diff --git a/telethon/_client/users.py b/telethon/_client/users.py index d47fab67..4717d6b1 100644 --- a/telethon/_client/users.py +++ b/telethon/_client/users.py @@ -241,11 +241,11 @@ async def get_input_entity( entity = await self._session.get_entity(None, peer_id) if entity: if entity.ty in (Entity.USER, Entity.BOT): - return _tl.InputPeerUser(entity.id, entity.access_hash) + return _tl.InputPeerUser(entity.id, entity.hash) elif entity.ty in (Entity.GROUP): return _tl.InputPeerChat(peer.chat_id) elif entity.ty in (Entity.CHANNEL, Entity.MEGAGROUP, Entity.GIGAGROUP): - return _tl.InputPeerChannel(entity.id, entity.access_hash) + return _tl.InputPeerChannel(entity.id, entity.hash) # Only network left to try if isinstance(peer, str): diff --git a/telethon/_sessions/abstract.py b/telethon/_sessions/abstract.py index cdb747a4..328998f3 100644 --- a/telethon/_sessions/abstract.py +++ b/telethon/_sessions/abstract.py @@ -64,15 +64,15 @@ class Session(ABC): Get the `Entity` with matching ``ty`` and ``id``. The following groups of ``ty`` should be treated to be equivalent, that is, for a given - ``ty`` and ``id``, if the ``ty`` is in a given group, a matching ``access_hash`` with - that ``id`` from within any ``ty`` in that group should be returned. + ``ty`` and ``id``, if the ``ty`` is in a given group, a matching ``hash`` with that ``id`` + from within any ``ty`` in that group should be returned. * `EntityType.USER` and `EntityType.BOT`. * `EntityType.GROUP`. * `EntityType.CHANNEL`, `EntityType.MEGAGROUP` and `EntityType.GIGAGROUP`. For example, if a ``ty`` representing a bot is stored but the asking ``ty`` is a user, - the corresponding ``access_hash`` should still be returned. + the corresponding ``hash`` should still be returned. You may use ``EntityType.canonical`` to find out the canonical type. diff --git a/telethon/_sessions/memory.py b/telethon/_sessions/memory.py index 1c86aff7..7e87b194 100644 --- a/telethon/_sessions/memory.py +++ b/telethon/_sessions/memory.py @@ -34,12 +34,12 @@ class MemorySession(Session): return list(self.channel_states.values()) async def insert_entities(self, entities: List[Entity]): - self.entities.update((e.id, (e.ty, e.access_hash)) for e in entities) + self.entities.update((e.id, (e.ty, e.hash)) for e in entities) async def get_entity(self, ty: Optional[int], id: int) -> Optional[Entity]: try: - ty, access_hash = self.entities[id] - return Entity(ty, id, access_hash) + ty, hash = self.entities[id] + return Entity(ty, id, hash) except KeyError: return None diff --git a/telethon/_sessions/sqlite.py b/telethon/_sessions/sqlite.py index 2ea419be..7b0e5849 100644 --- a/telethon/_sessions/sqlite.py +++ b/telethon/_sessions/sqlite.py @@ -127,7 +127,7 @@ class SQLiteSession(Session): limit 1 ''') c.execute(''' - insert into entity (id, access_hash, ty) + insert into entity (id, hash, ty) select case when id < -1000000000000 then -(id + 1000000000000) @@ -173,7 +173,7 @@ class SQLiteSession(Session): )''', '''entity ( id integer primary key, - access_hash integer not null, + hash integer not null, ty integer not null )''', ) @@ -245,13 +245,13 @@ class SQLiteSession(Session): try: c.executemany( 'insert or replace into entity values (?,?,?)', - [(e.id, e.access_hash, e.ty) for e in entities] + [(e.id, e.hash, e.ty) for e in entities] ) finally: c.close() async def get_entity(self, ty: Optional[int], id: int) -> Optional[Entity]: - row = self._execute('select ty, id, access_hash from entity where id = ?', id) + row = self._execute('select ty, id, hash from entity where id = ?', id) return Entity(*row) if row else None async def save(self): diff --git a/telethon/_sessions/types.py b/telethon/_sessions/types.py index a9738709..3aca03e4 100644 --- a/telethon/_sessions/types.py +++ b/telethon/_sessions/types.py @@ -105,12 +105,74 @@ class Entity: """ Stores the information needed to use a certain user, chat or channel with the API. - * ty: 8-bit number indicating the type of the entity. + * ty: 8-bit number indicating the type of the entity (of type `EntityType`). * id: 64-bit number uniquely identifying the entity among those of the same type. - * access_hash: 64-bit number needed to use this entity with the API. + * hash: 64-bit signed number needed to use this entity with the API. + + The string representation of this class is considered to be stable, for as long as + Telegram doesn't need to add more fields to the entities. It can also be converted + to bytes with ``bytes(entity)``, for a more compact representation. """ - __slots__ = ('ty', 'id', 'access_hash') + __slots__ = ('ty', 'id', 'hash') ty: EntityType id: int - access_hash: int + hash: int + + @property + def is_user(self): + """ + ``True`` if the entity is either a user or a bot. + """ + return self.ty in (EntityType.USER, EntityType.BOT) + + @property + def is_group(self): + """ + ``True`` if the entity is a small group chat or `megagroup`_. + + .. _megagroup: https://telegram.org/blog/supergroups5k + """ + return self.ty in (EntityType.GROUP, EntityType.MEGAGROUP) + + @property + def is_channel(self): + """ + ``True`` if the entity is a broadcast channel or `broadcast group`_. + + .. _broadcast group: https://telegram.org/blog/autodelete-inv2#groups-with-unlimited-members + """ + return self.ty in (EntityType.CHANNEL, EntityType.GIGAGROUP) + + @classmethod + def from_str(cls, string: str): + """ + Convert the string into an `Entity`. + """ + try: + ty, id, hash = string.split('.') + ty, id, hash = ord(ty), int(id), int(hash) + except AttributeError: + raise TypeError(f'expected str, got {string!r}') from None + except (TypeError, ValueError): + raise ValueError(f'malformed entity str (must be T.id.hash), got {string!r}') from None + + return cls(EntityType(ty), id, hash) + + @classmethod + def from_bytes(cls, blob): + """ + Convert the bytes into an `Entity`. + """ + try: + ty, id, hash = struct.unpack(' (hash, ty) @@ -72,8 +19,11 @@ class EntityCache: self.self_bot = bot def get(self, id): - value = self.hash_map.get(id) - return PackedChat(ty=value[1], id=id, hash=value[0]) if value else None + try: + hash, ty = self.hash_map[id] + return Entity(ty, id, hash) + except KeyError: + return None def extend(self, users, chats): # See https://core.telegram.org/api/min for "issues" with "min constructors". @@ -100,4 +50,4 @@ class EntityCache: return [Entity(ty, id, hash) for id, (hash, ty) in self.hash_map.items()] def put(self, entity): - self.hash_map[entity.id] = (entity.access_hash, entity.ty) + self.hash_map[entity.id] = (entity.hash, entity.ty) diff --git a/telethon/_updates/messagebox.py b/telethon/_updates/messagebox.py index d2bdd174..d25e0d09 100644 --- a/telethon/_updates/messagebox.py +++ b/telethon/_updates/messagebox.py @@ -530,7 +530,7 @@ class MessageBox: return _tl.fn.updates.GetChannelDifference( force=False, - channel=packed.try_to_input_channel(), + channel=_tl.InputChannel(packed.id, packed.hash), filter=_tl.ChannelMessagesFilterEmpty(), pts=state.pts, limit=BOT_CHANNEL_DIFF_LIMIT if chat_hashes.self_bot else USER_CHANNEL_DIFF_LIMIT