Remove PackedChat

In favour of using the session entity type consistently.
This commit is contained in:
Lonami Exo 2022-02-08 11:31:24 +01:00
parent 07faa53c5a
commit 9b4808a558
8 changed files with 86 additions and 74 deletions

View File

@ -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):

View File

@ -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.

View File

@ -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

View File

@ -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):

View File

@ -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('<Bqq', blob)
except struct.error:
raise ValueError(f'malformed entity data, got {string!r}') from None
return cls(EntityType(ty), id, hash)
def __str__(self):
return f'{chr(self.ty)}.{self.id}.{self.hash}'
def __bytes__(self):
return struct.pack('<Bqq', self.ty, self.id, self.hash)

View File

@ -1,2 +1,2 @@
from .entitycache import EntityCache, PackedChat
from .entitycache import EntityCache
from .messagebox import MessageBox

View File

@ -8,59 +8,6 @@ from .. import _tl
from .._sessions.types import EntityType, Entity
class PackedChat(namedtuple('PackedChat', 'ty id hash')):
__slots__ = ()
@property
def is_user(self):
return self.ty in (EntityType.USER, EntityType.BOT)
@property
def is_chat(self):
return self.ty in (EntityType.GROUP,)
@property
def is_channel(self):
return self.ty in (EntityType.CHANNEL, EntityType.MEGAGROUP, EntityType.GIGAGROUP)
def to_peer(self):
if self.is_user:
return _tl.PeerUser(user_id=self.id)
elif self.is_chat:
return _tl.PeerChat(chat_id=self.id)
elif self.is_channel:
return _tl.PeerChannel(channel_id=self.id)
def to_input_peer(self):
if self.is_user:
return _tl.InputPeerUser(user_id=self.id, access_hash=self.hash)
elif self.is_chat:
return _tl.InputPeerChat(chat_id=self.id)
elif self.is_channel:
return _tl.InputPeerChannel(channel_id=self.id, access_hash=self.hash)
def try_to_input_user(self):
if self.is_user:
return _tl.InputUser(user_id=self.id, access_hash=self.hash)
else:
return None
def try_to_chat_id(self):
if self.is_chat:
return self.id
else:
return None
def try_to_input_channel(self):
if self.is_channel:
return _tl.InputChannel(channel_id=self.id, access_hash=self.hash)
else:
return None
def __str__(self):
return f'{chr(self.ty.value)}.{self.id}.{self.hash}'
@dataclass
class EntityCache:
hash_map: dict = field(default_factory=dict) # id -> (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)

View File

@ -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