Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot obtain Channel via get_entity by it's ID #4084

Open
2 of 3 tasks
NotStatilko opened this issue Apr 9, 2023 · 20 comments
Open
2 of 3 tasks

Cannot obtain Channel via get_entity by it's ID #4084

NotStatilko opened this issue Apr 9, 2023 · 20 comments

Comments

@NotStatilko
Copy link
Contributor

NotStatilko commented Apr 9, 2023

Checklist

  • The error is in the library's code, and not in my own.
  • I have searched for this issue before posting it and there isn't a duplicate.
  • I ran pip install -U https://github.com/LonamiWebs/Telethon/archive/v1.zip and triggered the bug in the latest version.

Hi. Recently i bumped Telethon version from v1.25.4 to the most recent v1.28.2 and discovered that it's now impossible to obtain Channel entity via TelegramClient.get_entity. This behaviour started from the v1.28.0.

Maybe worth noting that i use a StringSession instead of default SQLite.

Code that causes the issue

...
# This doesn't work
await client.get_entity(channel_id)

# This doesn't work too
entity = await client.get_input_entity(channel_id)
await client.get_entity(entity)

# But this is OK
peer_channel = PeerChannel(channel_id)
channel_entity = await client.get_entity(entity)

Traceback

Traceback (most recent call last):
  File "/home/non/Scripts/py_projects/tgbox-env/tgbox/a_test.py", line 648, in <module>
    loop.run_until_complete(main())
  File "uvloop/loop.pyx", line 1501, in uvloop.loop.Loop.run_until_complete
  File "/home/non/Scripts/py_projects/tgbox-env/tgbox/a_test.py", line 587, in main
    print(await drb.tc.get_entity(ie))
  File "/home/non/Scripts/py_projects/tgbox-env/lib/python3.9/site-packages/telethon/client/users.py", line 332, in get_entity
    result.append(id_entity[utils.get_peer_id(x)])
KeyError: 1610806777

Is it OK?

@Lonami
Copy link
Member

Lonami commented Apr 10, 2023

The traceback seems cut, as the error is missing. Could you provide the full error log?

v1.28 did introduce some changes to the cache of entities, but the traceback doesn't look right.

@jw-star
Copy link

jw-star commented Apr 10, 2023

I also have this problem

@NotStatilko
Copy link
Contributor Author

NotStatilko commented Apr 10, 2023

Hi @Lonami. Sorry, i really forgot to include it xD

Here's full:
image

With some quick test i discovered that this error related to the IDs with removed -100 prefixes

channel_id_with_minus = -100XXXXXXXXXX # Works fine
channel_id = XXXXXXXXXX # Raises KeyError

This error will not be raised if we firstly call get_entity on channel_id_with_minus and then channel_id, most probably because it goes to cache. This statement is true only for SQLite Session.

It seems that utils.get_peer_id doesn't add -100 prefix.

@Lonami
Copy link
Member

Lonami commented May 4, 2023

That's the first time I'm seeing the above error. What does it show when you do the following?:

print(await client.get_input_entity(channel_id))

@NotStatilko
Copy link
Contributor Author

That's the first time I'm seeing the above error. What does it show when you do the following?:

print(await client.get_input_entity(channel_id))

print(await client.get_input_entity(1610806777)) ->
InputPeerUser(user_id=1610806777, access_hash=2328109287557450445)

print(await client.get_input_entity(-1001610806777)) ->
InputPeerChannel(channel_id=1610806777, access_hash=2328109287557450445)

(By using StringSession with the latest v1 commits)

@Lonami
Copy link
Member

Lonami commented May 4, 2023

For some reason, you seem to have the same ID in the cache with and without the mark. What does it show if you print(self.session._entities)?

@Lonami
Copy link
Member

Lonami commented May 4, 2023

It might also be interesting to print the traceback and objects in

def process_entities(self, tlo):
self._entities |= set(self._entities_to_rows(tlo))
, so that we know what is causing those to be saved that way:

    def process_entities(self, tlo):
        self._entities |= set(self._entities_to_rows(tlo))
        print('processing', tlo)
        import traceback
        for line in traceback.format_stack():
            print(line.strip())
        print('--')

@NotStatilko
Copy link
Contributor Author

For some reason, you seem to have the same ID in the cache with and without the mark. What does it show if you print(self.session._entities)?

{(-1001610806777, 2328109287557450445, None, None, 'TGBOX[01]: v1.0.sql'), (894756050, 1341376998091936395, 'ndhsunqze1cexlhtgc1vg9tz3', '<...>', '/home/non/test/')}

'<...>' is my phone number. If i understand something, there is no duplicates.


When i made a little investigations further i found out that id_entity in get_entity store ID with mark, like this:
{-1001610806777: <telethon.tl.types.Channel object at 0x7f0fd86396d0>}

And when we ask get_entity to give us Channel by the ID without -100 mark, it just can't find it in id_entity because is has only ID with mark.

The utils.get_peer_id doesn't add -100 mark to ID here (as i think should be expected?), so it's result in KeyError.

utils.get_peer_id just return ID as is if it's type is integer, but it should be prefixed with -100?

Based on my cursory research, it seems to me that the problem is in utils.get_peer_id. Maybe SQLite session store ID without -100 prefix, so it's presented in the id_entity (please note this). But StringSession seems to store only IDs with mark.

@NotStatilko
Copy link
Contributor Author

NotStatilko commented May 4, 2023

When i made a little investigations further i found out that id_entity in get_entity store ID with mark, like this:
{-1001610806777: <telethon.tl.types.Channel object at 0x7f0fd86396d0>}

Ok, now it seems that it's not true, id_entity is just empty dict when we call get_entity(1610806777), sorry :eyeroll

I will try to investigate it more later by myself.

@Lonami Lonami closed this as completed in ca68236 May 4, 2023
@Lonami
Copy link
Member

Lonami commented May 4, 2023

Nevermind, the above isn't gonna work.

@Lonami Lonami reopened this May 4, 2023
@Lonami Lonami closed this as completed in 980f8b3 May 4, 2023
@NotStatilko
Copy link
Contributor Author

Nevermind, the above isn't gonna work.

Ok. So to receive channel we should specify ID with -100 prefix?

@Lonami
Copy link
Member

Lonami commented May 4, 2023

The second commit linked should fix the issue.

@NotStatilko
Copy link
Contributor Author

NotStatilko commented May 5, 2023

The second commit linked should fix the issue.

Missed your commit :o


I tested it and it seems that this commit doesn't change anything. Try this code:

from telethon.sync import TelegramClient

API_ID = ...
API_HASH = ...

# @telegram channel ID, need to be subscribed
TELEGRAM_CHANNEL_ID = 1005640892

with TelegramClient('_session', API_ID, API_HASH) as client:
    print(client.get_entity(TELEGRAM_CHANNEL_ID))

Result:

Traceback (most recent call last):
  File "/home/non/Scripts/py_projects/tgbox-env/tgbox/d_test.py", line 10, in <module>
    print(client.get_entity(TELEGRAM_CHANNEL_ID))
  File "/home/non/Scripts/py_projects/tgbox-env/lib/python3.9/site-packages/telethon/sync.py", line 39, in syncified
    return loop.run_until_complete(coro)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/non/Scripts/py_projects/tgbox-env/lib/python3.9/site-packages/telethon/client/users.py", line 287, in get_entity
    inputs.append(await self.get_input_entity(x))
  File "/home/non/Scripts/py_projects/tgbox-env/lib/python3.9/site-packages/telethon/client/users.py", line 463, in get_input_entity
    raise ValueError(
ValueError: Could not find the input entity for PeerUser(user_id=1005640892) (PeerUser). Please read https://docs.telethon.dev/en/stable/concepts/entities.html to find out more details.

Getting Channel via ID without mark (1005640892) will work only if we firstly call get_entity(-1001005640892) (with mark), then [i think] library will pull out entity from the SQLite session file on get_entity(1005640892). This is the same situation that i described in my previous comment.

StringSession will fail (on ask without -100) even if we firstly ask for entity with marked ID.

@NotStatilko
Copy link
Contributor Author

Getting Channel via ID without mark (1005640892) will work only if we firstly call get_entity(-1001005640892)

The below additional to the get_input_entity (code) will fix this

...
        # If we're a bot and the user has messaged us privately users.getUsers
        # will work with access_hash = 0. Similar for channels.getChannels.
        # If we're not a bot but the user is in our contacts, it seems to work
        # regardless. These are the only two special-cased requests.
        peer = utils.get_peer(peer)
        if isinstance(peer, types.PeerUser):
            users = await self(functions.users.GetUsersRequest([
                types.InputUser(peer.user_id, access_hash=0)]))

            if users and not isinstance(users[0], types.UserEmpty):
                # If the user passed a valid ID they expect to work for
                # channels but would be valid for users, we get UserEmpty.
                # Avoid returning the invalid empty input peer for that.
                #
                # We *could* try to guess if it's a channel first, and if
                # it's not, work as a chat and try to validate it through
                # another request, but that becomes too much work.
                return utils.get_input_peer(users[0])
                
            # = This block ============================ #
            try:
                channels = await self(functions.channels.GetChannelsRequest([
                    types.InputChannel(peer.user_id, access_hash=0)]))
                return utils.get_input_peer(channels.chats[0])
            except errors.ChannelInvalidError:
                pass
             # ======================================= #
...

However i don't know if this is valid fix because comment states that

We could try to guess if it's a channel first, and if
it's not, work as a chat and try to validate it through
another request, but that becomes too much work.


While this seems to make a fix to the SQLite session, the String one still doesn't work. The problem is that for some reason StringSession cache Channel ID without mark as InputPeerUser, so we receive this exception.

The first mentioned here exception (KeyError) disappears if we remove this block (isn't good for sure :) with the addition of upper one (try/except) and all works just as expected (we can retrieve Channel by ID without mark).

That's all that i found as for now.

@Lonami
Copy link
Member

Lonami commented May 6, 2023

ValueError: Could not find the input entity for PeerUser(user_id=1005640892) (PeerUser). Please read https://docs.telethon.dev/en/stable/concepts/entities.html to find out more details.

is a different error from

KeyError: 1610806777

which is what 980f8b3 fixed. Note how this "new" error is on line 332 whereas the old one is in 287 (i.e. it's hitting a different error at a later point).

While this seems to make a fix to the SQLite session, the String one still doesn't work. The problem is that for some reason StringSession cache Channel ID without mark as InputPeerUser, so we receive this exception.

That doesn't seem to be the case. In #4084 (comment) you showed that print(self.session._entities) gave (-1001610806777, 2328109287557450445, ..., so with the -100 mark.

The first mentioned here exception (KeyError) disappears if we remove this block (isn't good for sure :) with the addition of upper one (try/except) and all works just as expected (we can retrieve Channel by ID without mark).

Trying to fetch the user or channel with access_hash of 0 is more of a hacky last-resort attempt at getting something that works. It's not something I would rely on. (I really dislike the fact that it works, as it defeats the purpose of access-hashes in the first place if the server knows what we can access on its own and the lines are blurry.)

If you know it's a channel, use the -100ID form or PeerChannel(ID) and the library will try its best. I don't think there's more to fix here.

@NotStatilko
Copy link
Contributor Author

That doesn't seem to be the case. In #4084 (comment) you showed that print(self.session._entities) gave (-1001610806777, 2328109287557450445, ..., so with the -100 mark.

Just to clarify (i don't think this is really important but maybe this will pop out later somewhere else):

from telethon.sync import TelegramClient
from telethon.sessions import StringSession

API_ID = ...
API_HASH = ...

SESSION = StringSession('...')

with TelegramClient(api_id=API_ID, api_hash=API_HASH, session=SESSION) as client:
    print(client.get_entity(-1001005640892))
    print(client.session.get_input_entity(1005640892))
    print(client.get_entity(1005640892))

Result in:

Channel(id=1005640892, title='Telegram News', photo=ChatPhoto(photo_id=4967709344246376726, dc_id=1, has_video=True, stripped_thumb=b'\x01\x08\x08\x7f\xc9\xe5\x9eN\xfa(\xa2\xbaN\x1b\x9f'), date=datetime.datetime(2023, 5, 5, 20, 57, 58, tzinfo=datetime.timezone.utc), creator=False, left=False, broadcast=True, verified=True, megagroup=False, restricted=False, signatures=False, min=False, scam=False, has_link=False, has_geo=False, slowmode_enabled=False, call_active=False, call_not_empty=False, fake=False, gigagroup=False, noforwards=False, join_to_send=False, join_request=False, forum=False, access_hash=7025306665282166182, username='telegram', restriction_reason=[], admin_rights=None, banned_rights=None, default_banned_rights=None, participants_count=None, usernames=[])

InputPeerUser(user_id=1005640892, access_hash=7025306665282166182)

Traceback (most recent call last):
  File "/home/non/Scripts/py_projects/tgbox-env/tgbox/d_test.py", line 12, in <module>
    print(client.get_entity(1005640892))
  File "/home/non/Scripts/py_projects/tgbox-env/lib/python3.9/site-packages/telethon/sync.py", line 39, in syncified
    return loop.run_until_complete(coro)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/non/Scripts/py_projects/tgbox-env/lib/python3.9/site-packages/telethon/client/users.py", line 334, in get_entity
    result.append(id_entity[utils.get_peer_id(x, add_mark=False)])
KeyError: 1005640892

The same code, tag v1.27.0:

Channel(id=1005640892, title='Telegram News', photo=ChatPhoto(photo_id=4967709344246376726, dc_id=1, has_video=True, stripped_thumb=b'\x01\x08\x08\x7f\xc9\xe5\x9eN\xfa(\xa2\xbaN\x1b\x9f'), date=datetime.datetime(2023, 5, 5, 20, 57, 58, tzinfo=datetime.timezone.utc), creator=False, left=False, broadcast=True, verified=True, megagroup=False, restricted=False, signatures=False, min=False, scam=False, has_link=False, has_geo=False, slowmode_enabled=False, call_active=False, call_not_empty=False, fake=False, gigagroup=False, noforwards=False, join_to_send=False, join_request=False, forum=False, access_hash=7025306665282166182, username='telegram', restriction_reason=[], admin_rights=None, banned_rights=None, default_banned_rights=None, participants_count=None, usernames=[])

InputPeerUser(user_id=1005640892, access_hash=7025306665282166182)

Channel(id=1005640892, title='Telegram News', photo=ChatPhoto(photo_id=4967709344246376726, dc_id=1, has_video=True, stripped_thumb=b'\x01\x08\x08\x7f\xc9\xe5\x9eN\xfa(\xa2\xbaN\x1b\x9f'), date=datetime.datetime(2023, 5, 5, 20, 57, 58, tzinfo=datetime.timezone.utc), creator=False, left=False, broadcast=True, verified=True, megagroup=False, restricted=False, signatures=False, min=False, scam=False, has_link=False, has_geo=False, slowmode_enabled=False, call_active=False, call_not_empty=False, fake=False, gigagroup=False, noforwards=False, join_to_send=False, join_request=False, forum=False, access_hash=7025306665282166182, username='telegram', restriction_reason=[], admin_rights=None, banned_rights=None, default_banned_rights=None, participants_count=None, usernames=[])

The best way to operate with Channel ID is wrap it in PeerChannel. That's make sense. Thanks, Lonami!

@Lonami
Copy link
Member

Lonami commented May 6, 2023

Yeah I don't understand how line 334 can fail if the dictionary is built with add_mark=False and accessed in the same manner. Maybe I'll revisit this at some point.

@Lonami Lonami reopened this May 6, 2023
Jisan09 added a commit to Jisan09/Telethon that referenced this issue May 28, 2023
* except and propagate TypeNotFoundError during update handling

* Better document breaking ToS will lead to bans

Closes LonamiWebs#4102.

* Fix KeyError when ID is in cache but queried without mark

Closes LonamiWebs#4084.

* Fix asyncio.CancelledError was being swallowed by inner except

Closes LonamiWebs#4104.

* Add check for asyncio event loop to remain the same

* Update FAQ with more common questions

---------

Co-authored-by: Lonami Exo <totufals@hotmail.com>
@speauty
Copy link

speauty commented Aug 4, 2023

interesting....i use this api to get channel by its id...emmm, the result of some groups which i'm not in is "Could not find the input entity for PeerChannel(channel_id=1273185727) (PeerChannel)", and other groups which i'm in is ok:

# channel_id is real-id of channel
await tg_client.get_entity(types.PeerChannel(channel_id))

@MelodyEars
Copy link

In this code, I accessed the channel through another account that was already subscribed to the channel and received the object

Channel(id=2222222222, title='NameChannel', photo=ChatPhoto(photo_id=5884109877490597299, dc_id=4, has_video=False, stripped_thumb=b'\x01\x08\x08\xc9\xc2\xec\xce~l\xf4\xa2\x8a(z\x81'), date=datetime.datetime(2023, 12, 20, 9, 44, 2, tzinfo=datetime.timezone.utc), creator=False, left=False, broadcast=True, verified=False, megagroup=False, restricted=False, signatures=False, min=False, scam=False, has_link=False, has_geo=False, slowmode_enabled=False, call_active=False, call_not_empty=False, fake=False, gigagroup=False, noforwards=False, join_to_send=False, join_request=False, forum=False, stories_hidden=False, stories_hidden_min=False, stories_unavailable=True, access_hash=2332663674881728592, username=None, restriction_reason=[], admin_rights=None, banned_rights=None, default_banned_rights=None, participants_count=None, usernames=[], stories_max_id=None, color=PeerColor(color=9, background_emoji_id=5298529109570239315))

Then through except InviteHashExpiredError used the following function

invite = await client(ExportChatInviteRequest(peer=InputPeerChannel(access_hash=channel_obj.access_hash, channel_id=channel_obj.id)))

Error:

Traceback (most recent call last):

  File "home\PycharmProjects\tg_actions_with_group\scripts\test.py", line 221, in tst_main
    new_join_info = await client(ImportChatInviteRequest(hash=channel_url))
                          │      │                            └ 'WbDFfZ2OOvdjY2M0'
                          │      └ <class 'telethon.tl.functions.messages.ImportChatInviteRequest'><telethon.client.telegramclient.TelegramClient object at 0x000001EF9F13EE10>

  File "home\PycharmProjects\tg_actions_with_group\venv\Lib\site-packages\telethon\client\users.py", line 30, in __call__
    return await self._call(self._sender, request, ordered=ordered)
                 │    │     │    │        │                └ False
                 │    │     │    │        └ <telethon.tl.functions.messages.ImportChatInviteRequest object at 0x000001EF9D69B5D0>
                 │    │     │    └ <telethon.network.mtprotosender.MTProtoSender object at 0x000001EF9EE07F50>
                 │    │     └ <telethon.client.telegramclient.TelegramClient object at 0x000001EF9F13EE10>
                 │    └ <function UserMethods._call at 0x000001EFFF448FE0><telethon.client.telegramclient.TelegramClient object at 0x000001EF9F13EE10>
  File "home\PycharmProjects\tg_actions_with_group\venv\Lib\site-packages\telethon\client\users.py", line 87, in _call
    result = await future<Future finished exception=InviteHashExpiredError('The chat the user tried to join has expired and is not valid anymore (caus...

telethon.errors.rpcerrorlist.InviteHashExpiredError: The chat the user tried to join has expired and is not valid anymore (caused by ImportChatInviteRequest)


During handling of the above exception, another exception occurred:


Traceback (most recent call last):

  File "home\PycharmProjects\tg_actions_with_group\scripts\test.py", line 253, in <module>
    asyncio.run(tst_main())
    │       │   └ <function tst_main at 0x000001EF9DCB2660>
    │       └ <function run at 0x000001EFFD69A200><module 'asyncio' from 'C:\\Users\\King\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\asyncio\\__init__.py'>

  File "home\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           │      │   └ <coroutine object tst_main at 0x000001EF9DCF1340>
           │      └ <function Runner.run at 0x000001EFFDB5DE40><asyncio.runners.Runner object at 0x000001EFFB7DFE50>
  File "C:\Users\King\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           │    │     │                  └ <Task pending name='Task-1' coro=<tst_main() running at home\PycharmProjects\tg_actions_with_group\venv\Lib\site-pac...
           │    │     └ <function BaseEventLoop.run_until_complete at 0x000001EFFDB57920>
           │    └ <ProactorEventLoop running=True closed=False debug=False><asyncio.runners.Runner object at 0x000001EFFB7DFE50>
  File "home\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 640, in run_until_complete
    self.run_forever()
    │    └ <function ProactorEventLoop.run_forever at 0x000001EFFDC27A60><ProactorEventLoop running=True closed=False debug=False>
  File "home\AppData\Local\Programs\Python\Python311\Lib\asyncio\windows_events.py", line 321, in run_forever
    super().run_forever()
  File "home\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 607, in run_forever
    self._run_once()
    │    └ <function BaseEventLoop._run_once at 0x000001EFFDB5D6C0><ProactorEventLoop running=True closed=False debug=False>
  File "home\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 1922, in _run_once
    handle._run()
    │      └ <function Handle._run at 0x000001EFFD69AD40><Handle Task.task_wakeup(<Future finished result=None>)>
> File "home\AppData\Local\Programs\Python\Python311\Lib\asyncio\events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
    │    │            │    │           │    └ <member '_args' of 'Handle' objects>
    │    │            │    │           └ <Handle Task.task_wakeup(<Future finished result=None>)>
    │    │            │    └ <member '_callback' of 'Handle' objects>
    │    │            └ <Handle Task.task_wakeup(<Future finished result=None>)>
    │    └ <member '_context' of 'Handle' objects><Handle Task.task_wakeup(<Future finished result=None>)>

  File "home\PycharmProjects\tg_actions_with_group\scripts\join_channel.py", line 231, in tst_main
    invite = await client(ExportChatInviteRequest(peer=InputPeerChannel(access_hash=channel_obj.access_hash, channel_id=channel_obj.id)))
                   │      │                            │                            │           │                       │           └ 2128475717
                   │      │                            │                            │           │                       └ <telethon.tl.types.Channel object at 0x000001EF9EE05FD0>
                   │      │                            │                            │           └ 2332663674881728592
                   │      │                            │                            └ <telethon.tl.types.Channel object at 0x000001EF9EE05FD0>
                   │      │                            └ <class 'telethon.tl.types.InputPeerChannel'>
                   │      └ <class 'telethon.tl.functions.messages.ExportChatInviteRequest'><telethon.client.telegramclient.TelegramClient object at 0x000001EF9F13EE10>

  File "home\PycharmProjects\tg_actions_with_group\venv\Lib\site-packages\telethon\client\users.py", line 87, in _call
    result = await future<Future finished exception=ChannelInvalidError('Invalid channel object. Make sure to pass the right types, for instance makin...

telethon.errors.rpcerrorlist.ChannelInvalidError: Invalid channel object. Make sure to pass the right types, for instance making sure that the request is designed for channels or otherwise look for a different one more suited (caused by ExportChatInviteRequest)

@Alnevis
Copy link

Alnevis commented May 11, 2024

interesting....i use this api to get channel by its id...emmm, the result of some groups which i'm not in is "Could not find the input entity for PeerChannel(channel_id=1273185727) (PeerChannel)", and other groups which i'm in is ok:

# channel_id is real-id of channel
await tg_client.get_entity(types.PeerChannel(channel_id))

is there any chance to get by ID the entity of channels or supergroups which you are not a member of?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants