LonamiWebs / Telethon

Pure Python 3 MTProto API Telegram client library, for bots too!
https://docs.telethon.dev
MIT License
9.72k stars 1.39k forks source link

Unable to get the username of the public channel in a received message after telethon update #392

Closed primer99 closed 6 years ago

primer99 commented 6 years ago

Hi, earlier I was using the below code with telethon version 0.12.*

from telethon.tl.types import UpdatesTg

def update_handler(self, update_object):
    if type(update_object) is UpdatesTg:
        update_object = update_object.to_dict()
        user = update_object['chats'][0]['username'].upper() # username of the public channel
        msg = update_object['updates'][0]['message']['message']

In the user variable, I used to get the username of the public channel from which I received the message.

I updated telethon today to 0.15.3.2 and UpdatesTg seem not a part of it (or I'm unable to figure it out). So, I modified the code as follows: from telethon.tl.types import UpdateNewChannelMessage

from telethon.tl.types import UpdateNewChannelMessage

def update_handler(self, update_object):
    if type(update_object) is UpdateNewChannelMessage:
        update_object = update_object.to_dict()
        msg = update_object['message']['message']
        dt = update_object['message']['date']
        user = # how to get the username of the public channel as in previous version

username of the channel doesn't seem to be part of the UpdateNewChannelMessage object.

Please tell me how do I get the username of the channel which sent the message.

Thank you very much :)

expectocode commented 6 years ago

You can use the update_object.to_id, which is a PeerChannel. From this PeerChannel, you can get an input entity from the local database using client.get_input_entity, and you can use a GetChannelsRequest to get the username. There might be an simpler way. Also, you can ask these questions in https://t.me/TelethonChat as well :)

Lonami commented 6 years ago

Thanks @expectocode, that's correct. For a more in-depth explanation check the wiki: https://github.com/LonamiWebs/Telethon/wiki/Retrieving-an-entity

primer99 commented 6 years ago

Thank you.

HFDave commented 6 years ago

Lonami, can you please provide me with a working wiki link? The one posted does not actually work.

I was using 0.15.5.7 where I could use UpdateNewChannelMessage.Message.to_id value on any UpdateNewChannelMessage. I always passed the value, e.g. PeerChannel(channel_id=123456789), to get_entity() and got returned an object containing the channel title and so on.

I had extreme problems with receiving messages too late. Like described here and in several other threads: https://github.com/LonamiWebs/Telethon/issues/237

After reading this recent thread (237) I thought the problem could be fixed so I tried to update to 0.16.0.6 using pip yesterday. Unfortunately, it completely broke all my code.

Previously working:

if isinstance(update, UpdateNewChannelMessage):
    if hasattr(message, 'message'):
        chan = client.get_entity(message.to_id)

        # Finally, I need this ...
        print(chan.title)

Now, this just throws:

telethon.errors.rpc_error_list.ChannelInvalidError: (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')

While message.to_id contains object like:

PeerChannel(channel_id=123456789)

As I was progressively getting more and more clueless, I tried the following approaches along with many other things:

chan = client.get_entity(message.to_id)
chan = client.get_entity(PeerChannel(message.to_id))
chan = client.get_entity(PeerChannel(message.to_id.channel_id))
chan = client.get_input_entity(message.to_id)
chan = client.get_input_entity(PeerChannel(message.to_id))
chan = client.get_input_entity(PeerChannel(message.to_id.channel_id))

chan = client.GetChannelsRequest(client.get_input_entity(message.to_id))
...

... none of which worked for me. I am pretty running out of ideas.

I have come here in the search for any help because my project is actually ruined. My bookmarks of Telethon docs working just a few days back do not work anymore and it looks like a lot of things have changed in Telethon and I am failing to find the info what to change to restore previous functionality.

I have just seen someone asking exactly the same question on TelethonChat and other people having the same issue since November. Can someone help me please? Thank you in advance.

Lonami commented 6 years ago

working wiki link? The one posted does not actually work

That's because now we use ReadTheDocs.

I have just seen someone asking exactly the same question on TelethonChat and other people having the same issue since November. Can someone help me please?

Yes not sure what the issue is. So I would need the channel name to test myself and see if I can reproduce the issue.

HFDave commented 6 years ago

Looks like it fails with every update on any channel (with any name) I am on.

So, is my original approach still current and right?

chan = client.get_entity(message.to_id)

I can do some tests and provide you with results if I know which approach to focus on.

Lonami commented 6 years ago

Yes, that's the way it should be done. Nothing to change. There may be something wrong with the library. But need to test.

HFDave commented 6 years ago

OK, thanks. My project is quite complex. I will try to reproduce the problem on a fresh code, as simple and short as possible, and return to you with all details very shortly. Thank you very much for taking care.

HFDave commented 6 years ago

I wrote a short code completely aside of my project. After making a successful connection to Telegram, I run this code:

def UpdateHandler(update):
    from telethon.tl.types import Message, UpdateNewChannelMessage

    if isinstance(update, UpdateNewChannelMessage):
        message = update.message
        print(message, '\n')

        chan = client.get_entity(message.to_id)
        print(chan)

while True:
    update = client.updates.poll()
    if update: UpdateHandler(update)

Here comes the result:

Message(out=False, mentioned=False, media_unread=False, silent=False, post=False, id=254008, from_id=480074149, to_id=PeerChannel(channel_id=1129575249), fwd_from=None, via_bot_id=None, reply_to_msg_id=None, date=datetime.utcfromtimestamp(1515242115), message='We gonna fly', media=None, reply_markup=None, entities=None, views=None, edit_date=None, post_author=None, grouped_id=None) 

Traceback (most recent call last):
  File "./problem.py", line 78, in <module>
    if update: UpdateHandler(update)
  File "./problem.py", line 71, in UpdateHandler
    chan = client.get_entity(message.to_id)
  File "/usr/local/lib/python3.6/dist-packages/telethon/telegram_client.py", line 1085, in get_entity
    channels = self(GetChannelsRequest(channels)).chats
  File "/usr/local/lib/python3.6/dist-packages/telethon/telegram_bare_client.py", line 477, in __call__
    sender, call_receive, update_state, *requests
  File "/usr/local/lib/python3.6/dist-packages/telethon/telegram_bare_client.py", line 564, in _invoke
    raise next(x.rpc_error for x in requests if x.rpc_error)
telethon.errors.rpc_error_list.ChannelInvalidError: (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')

Please let me know if I can help any further as I will be glad to do so.

Lonami commented 6 years ago

After a small test I noticed that if the ID or hash changes even by a single unit, it will raise ChannelInvalidError. So maybe the channel hash in the database is somehow invalid. Do you know which channel this is? Can you compare the results as follows?:

from telethon import utils
print(client.get_input_entity(PeerChannel(1129575249)))
print(utils.get_input_peer(client.get_entity('username')))
HFDave commented 6 years ago

Please note that the problem shows up probably with any channel I am on, not only with this particular one.

from telethon.tl.types import PeerChannel
from telethon import utils

print('1:')
print(client.get_input_entity(PeerChannel(1129575249)))
print('\n') 

print('2:')
print(utils.get_input_entity(client.get_entity('username')))
print('\n')
1:
InputPeerChannel(channel_id=1129575249, access_hash=0)

2:
Traceback (most recent call last):
  File "./problem.py", line 91, in <module>
    print(utils.get_input_entity(client.get_entity('username')))
AttributeError: module 'telethon.utils' has no attribute 'get_input_entity'

I hope I am doing what you wanted me to.

Lonami commented 6 years ago

Sorry, utils.get_input_peer. .get_input_entity is for the client.

HFDave commented 6 years ago
2:
InputPeerChannel(channel_id=1066197625, access_hash=-6252777769346851166)

I am not sure but probably only super groups are affected. I tried to set up a channel, join and post a message there. It was working well just like it used to. I got a Channel() object including title and everything as normal. But big channels I am on do not work. Most probably none of them.

Lonami commented 6 years ago

Well as you can see one has ID zero and the other has the right access hash. So of course it's failing. The database probably saved invalid IDs. DB Browser for SQLite is a nice GUI application if you want to ensure the database saved it wrong.

Lonami commented 6 years ago

Although channel_id is also different, strange. I save 0 as access_hash on purpose for chats. Maybe 1129575249 is actually a chat? Can you try client.get_entity(PeerChat(1129575249))?

HFDave commented 6 years ago

I must say that I really do not fully understand this Telegram's mess about chat, channels, groups, super groups and their combinations. :)

I am also not sure where I can check and/or clean up the database and why it would went wrong anyway. On the other hand, I am quite able to copy with an SQL server from a SQL console so I will do anything you will need if you provide me with more instructions. For example, would some sort of DB purging help?

Here follows the requested output:

print(client.get_entity(PeerChat(1129575249)))

Traceback (most recent call last):
  File "./problem.py", line 87, in <module>
    print(client.get_entity(PeerChat(1129575249)))
  File "/usr/local/lib/python3.6/dist-packages/telethon/telegram_client.py", line 1083, in get_entity
    chats = self(GetChatsRequest(chats)).chats
  File "/usr/local/lib/python3.6/dist-packages/telethon/telegram_bare_client.py", line 477, in __call__
    sender, call_receive, update_state, *requests
  File "/usr/local/lib/python3.6/dist-packages/telethon/telegram_bare_client.py", line 564, in _invoke
    raise next(x.rpc_error for x in requests if x.rpc_error)
telethon.errors.rpc_error_list.ChatIdInvalidError: (ChatIdInvalidError(...), 'Invalid object ID for a chat. Make sure to pass the right types, for instance making sure that the request is designed for chats (not channels/megagroups) or otherwise look for a different one more suited\\nAn example working with a megagroup and AddChatUserRequest, it will fail because megagroups are channels. Use InviteToChannelRequest instead')

The channel/chat we are actually testing this on is CRYPTO TROLLBOX, 1129575249. But like I said, probably all big ones are affected.

Lonami commented 6 years ago

I pushed v0.16.0.7 to PyPi, perhaps you should delete all existing records off the database to clean it up.

HFDave commented 6 years ago

Well, that's just unbelievable, I have no words for your speed and cooperation. Really, thank you very much, Lonami.

/usr/bin/python3 -m pip install --upgrade telethon
Requirement already up-to-date: telethon in /usr/local/lib/python3.6/dist-packages

Waiting for the new version to show up. I will report back right then.

Sorry for the novice question, where can I clean up the DB? Any hint will be appreciated.

Lonami commented 6 years ago

No way to clean up the DB currently, just client.session._conn.execute('delete from entities'). Try --no-cache with pip, if I recall correctly.

HFDave commented 6 years ago

I was trying --no-cache but it did not help. Also uninstalling and installing back brings the old version.

/usr/bin/python3 -m pip install --upgrade --no-cache telethon
Collecting telethon
  Downloading Telethon-0.16.0.6-py3-none-any.whl (243kB)
    100% |████████████████████████████████| 245kB 5.3MB/s 
Requirement already up-to-date: rsa in /usr/local/lib/python3.6/dist-packages (from telethon)
Requirement already up-to-date: pyaes in /usr/local/lib/python3.6/dist-packages (from telethon)
Requirement already up-to-date: pyasn1>=0.1.3 in /usr/local/lib/python3.6/dist-packages (from rsa->telethon)
Installing collected packages: telethon
Successfully installed telethon-0.16.0.6

Thanks, I will commit the delete once I am on 16.0.7.

HFDave commented 6 years ago

Looks like there is still the old version even on the web ... https://pypi.python.org/pypi/Telethon

Lonami commented 6 years ago

My bad, try now?

HFDave commented 6 years ago

Excellent, I have got the .7. Thanks, I will report back shortly ...

HFDave commented 6 years ago

Lonami, thank you very much for your outstanding help.

As you noted, it was really necessary to purge the entities table. Looks like it is all perfectly working now again.

Unfortunately, the issue with delayed updates is still present. I will post more on this topic in the original thread: https://github.com/LonamiWebs/Telethon/issues/237

HFDave commented 6 years ago

I am not sure if it is related to the recent changes but I have just seen this running the .7 version. .get_entity(message.to_id) was called and there was some kind of network error. Not sure if it should lead to a traceback. I have never seen Telethon to do this before.

  File "/usr/local/lib/python3.5/dist-packages/telethon/telegram_client.py", line 1157, in get_entity
    channels = self(GetChannelsRequest(channels)).chats
  File "/usr/local/lib/python3.5/dist-packages/telethon/telegram_bare_client.py", line 452, in __call__
    sender.connect()
  File "/usr/local/lib/python3.5/dist-packages/telethon/network/mtproto_sender.py", line 58, in connect
    self.connection.connect(self.session.server_address, self.session.port)
  File "/usr/local/lib/python3.5/dist-packages/telethon/network/connection.py", line 97, in connect
    self.conn.connect(ip, port)
  File "/usr/local/lib/python3.5/dist-packages/telethon/extensions/tcp_client.py", line 75, in connect
    self._socket.connect(address)
ConnectionRefusedError: [Errno 111] Connection refused
Lonami commented 6 years ago

Not sure if we should just handle all OSError in the same way to be honest. Have talked about this with some other people but answer is unclear.

HFDave commented 6 years ago

Connection refused is a common network error and, as a such, it should not cause a crash. Enclosing any and all telethon method calls in 'try' blocks is not the best idea so it should be made safe internally.

There is nothing you can do about such error. Maybe one immediate retry. Delaying method exit is generally not good so the best solution would be returning a fail like False / None / 'Success': False ...

Lonami commented 6 years ago

Connection refused is a common network error and, as a such, it should not cause a crash.

I agree. Hence why I wrapped the error around another exception that the libraries does except and upon which it will retry. .connect() returns False if it fails.