From 8c4b7a1ac8460c0d17e2b0a4de2e31d6ab7b3e08 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 4 Dec 2018 02:02:40 -0300 Subject: [PATCH] pubsub.lazy_guild: fix list_id for non-everyone lists - utils: add murmurhash3 implementation --- litecord/pubsub/lazy_guild.py | 29 +++++++++++++- litecord/utils.py | 73 +++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/litecord/pubsub/lazy_guild.py b/litecord/pubsub/lazy_guild.py index fcbd153..cc0bf5c 100644 --- a/litecord/pubsub/lazy_guild.py +++ b/litecord/pubsub/lazy_guild.py @@ -13,6 +13,7 @@ from litecord.permissions import ( Permissions, overwrite_find_mix, get_permissions, role_permissions ) from litecord.utils import index_by_func +from litecord.utils import mmh3 log = Logger(__name__) @@ -267,7 +268,33 @@ class GuildMemberList: """get the id of the member list.""" return ('everyone' if self.channel_id == self.guild_id - else str(self.channel_id)) + else self._calculated_id) + + @property + def _calculated_id(self): + """Calculate an id used by the client.""" + + if not self.list: + return str(self.channel_id) + + # list of strings holding the hash input + ovs_i = [] + + print(self.list.overwrites) + + for actor_id, overwrite in self.list.overwrites.items(): + allow, deny = ( + Permissions(overwrite['allow']), + Permissions(overwrite['deny']) + ) + + if allow.bits.read_messages: + ovs_i.append(f'allow:{actor_id}') + elif deny.bits.read_messages: + ovs_i.append(f'deny:{actor_id}') + + hash_in = ','.join(ovs_i) + return str(mmh3(hash_in)) def _set_empty_list(self): """Set the member list as being empty.""" diff --git a/litecord/utils.py b/litecord/utils.py index 2fda9d5..26c6ba2 100644 --- a/litecord/utils.py +++ b/litecord/utils.py @@ -37,3 +37,76 @@ def index_by_func(function, indexable: iter) -> int: return index return None + + +def _u(val): + """convert to unsigned.""" + return val % 0x100000000 + + +def mmh3(key: str, seed: int = 0): + """MurMurHash3 implementation. + + This seems to match Discord's JavaScript implementaiton. + + Based off + https://github.com/garycourt/murmurhash-js/blob/master/murmurhash3_gc.js + """ + key = [ord(c) for c in key] + + remainder = len(key) & 3 + bytecount = len(key) - remainder + h1 = seed + + # mm3 constants + c1 = 0xcc9e2d51 + c2 = 0x1b873593 + i = 0 + + while i < bytecount: + k1 = ( + (key[i] & 0xff) | + ((key[i + 1] & 0xff) << 8) | + ((key[i + 2] & 0xff) << 16) | + ((key[i + 3] & 0xff) << 24) + ) + + i += 4 + + k1 = ((((k1 & 0xffff) * c1) + ((((_u(k1) >> 16) * c1) & 0xffff) << 16))) & 0xffffffff + k1 = (k1 << 15) | (_u(k1) >> 17) + k1 = ((((k1 & 0xffff) * c2) + ((((_u(k1) >> 16) * c2) & 0xffff) << 16))) & 0xffffffff; + + h1 ^= k1 + h1 = (h1 << 13) | (_u(h1) >> 19); + h1b = ((((h1 & 0xffff) * 5) + ((((_u(h1) >> 16) * 5) & 0xffff) << 16))) & 0xffffffff; + h1 = (((h1b & 0xffff) + 0x6b64) + ((((_u(h1b) >> 16) + 0xe654) & 0xffff) << 16)) + + + k1 = 0 + v = None + + if remainder == 3: + v = (key[i + 2] & 0xff) << 16 + elif remainder == 2: + v = (key[i + 1] & 0xff) << 8 + elif remainder == 1: + v = (key[i] & 0xff) + + if v is not None: + k1 ^= v + + k1 = (((k1 & 0xffff) * c1) + ((((_u(k1) >> 16) * c1) & 0xffff) << 16)) & 0xffffffff + k1 = (k1 << 15) | (_u(k1) >> 17) + k1 = (((k1 & 0xffff) * c2) + ((((_u(k1) >> 16) * c2) & 0xffff) << 16)) & 0xffffffff + h1 ^= k1 + + h1 ^= len(key) + + h1 ^= _u(h1) >> 16 + h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((_u(h1) >> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff + h1 ^= _u(h1) >> 13 + h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((_u(h1) >> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff + h1 ^= _u(h1) >> 16 + + return _u(h1) >> 0