telethon.sync.TelegramClient 和 pyTelegramBotAPI 的异步问题
Async problem with telethon.sync.TelegramClient and pyTelegramBotAPI
首先,我必须开发 Telegram Bot 来检查用户是否订阅了该频道。我使用 pyTelegramBotAPI==3.6.6
创建一个机器人并使用 Telethon==1.9.0
检查用户是否订阅。
我有 @bot.message_handler
,我用 telethon.sync
函数调用 Class 的全局实例。看起来像:
from telebot import TeleBot
from telethon.sync import TelegramClient
import config # my module with constants
class TeleHelper:
def __init__(self, api_id, api_hash, phone, channel, session_name='session'):
self._client = TelegramClient(session_name, api_id, api_hash)
self._client.connect()
self._setup(phone)
self._channel = channel
def _setup(self, phone): # just setup
if not self._client.is_user_authorized():
self._client.send_code_request(phone)
self._client.sign_in(phone, input('Enter the code: '))
@staticmethod
def get_target(user): # get username or full name
if user.username:
return user.username
else:
return user.first_name + (f' {user.last_name}' if user.last_name else '')
def check_subscription(self, user): # search user in channel members, there is a problem
target = self.get_target(user)
participants = self._client.iter_participants(self._channel, search=target)
ids = [member.id for member in participants]
return user.id in ids
bot = TeleBot(config.bot_token) # bot instance
tg = TeleHelper(config.api_id, config.api_hash, config.phone, config.channel) # instance of the class above
@bot.message_handler(commands=['command'])
def handle_join(message):
if tg.check_subscription(message.from_user): # here problems start
text = 'All is good!'
bot.send_message(message.chat.id, text)
else:
text = 'You have to subscribe @python_lounge'
bot.send_message(message.chat.id, text)
if __name__ == '__main__':
bot.polling()
我从 telethon.sync
而不是从 telethon 导入了 TelegramClient
,所以一切看起来都很好,但不小心我得到了一个错误:
2019-08-24 10:31:07,342 (main.py:65 WorkerThread1) ERROR - TeleBot: "RuntimeError occurred, args=('You must use "async for" if the event loop is running (i.e. you are inside an "async def")',)
Traceback (most recent call last):
File "/root/ContestBot/.venv/lib/python3.7/site-packages/telebot/util.py", line 59, in run
task(*args, **kwargs)
File "main.py", line 99, in handle_join
if tg.check_subscription(message.from_user):
File "/root/ContestBot/main.py", line 25, in check_subscription
ids = [member.id for member in participants]
File "/root/ContestBot/.venv/lib/python3.7/site-packages/telethon/requestiter.py", line 102, in __iter__
'You must use "async for" if the event loop '
RuntimeError: You must use "async for" if the event loop is running (i.e. you are inside an "async def")
"
我试过"async for",但我是异步编程的新手,我写的是:
async def check_subscription(self, user):
ids = []
async for member in self._client.iter_participants(self._channel, search=self.get_target(user)):
await ids.append(member.id)
return user.id in ids
显然我想要,但程序仍然无法运行:
<coroutine object TeleHelper.check_subscription at 0x7ff9bc57f3c8>
/root/ContestBot/.venv/lib/python3.7/site-packages/telebot/util.py:59: RuntimeWarning: coroutine 'TeleHelper.check_subscription' was never awaited
task(*args, **kwargs)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
我用的是Python3.7.3
我的问题是我没有深入理解就把threads
和asyncio
混在一起了。解决方案之一是使用 aiogram
甚至 telethon
等异步模块来管理机器人。
实际上,我不必使用 telethon 来检查用户是否也订阅了该频道。 Bot API
中有个方法叫getChatMember
,所以pyTelegramBotAPI
就够了。它 returns None
或 ChatMember
对象 status
可以是“创建者”,“管理员”,“成员”,“受限”,“离开”或“被踢” .
所以,我的解决方案只有 pyTelegramBotAPI
:
@bot.message_handler(func=lambda msg: msg.text == 'Участвовать')
def handle_join(message):
member = bot.get_chat_member(config.channel_id, message.from_user.id). # right way to check if user subscribed
if member is not None and member.status in ('creator', 'administrator', 'member'):
text = 'All is good!'
bot.send_message(message.chat.id, text)
else:
text = 'You have to subscribe @python_lounge'
bot.send_message(message.chat.id, text)
顺便说一下,我发现 telethon.sync
不是真的。对于不知道 asyncio 是如何工作的人来说,这只是一个小技巧。所以 telethon.sync
只在有限的情况下有效,你不应该也可能不能将它用于除快速脚本之外的任何东西。
首先,我必须开发 Telegram Bot 来检查用户是否订阅了该频道。我使用 pyTelegramBotAPI==3.6.6
创建一个机器人并使用 Telethon==1.9.0
检查用户是否订阅。
我有 @bot.message_handler
,我用 telethon.sync
函数调用 Class 的全局实例。看起来像:
from telebot import TeleBot
from telethon.sync import TelegramClient
import config # my module with constants
class TeleHelper:
def __init__(self, api_id, api_hash, phone, channel, session_name='session'):
self._client = TelegramClient(session_name, api_id, api_hash)
self._client.connect()
self._setup(phone)
self._channel = channel
def _setup(self, phone): # just setup
if not self._client.is_user_authorized():
self._client.send_code_request(phone)
self._client.sign_in(phone, input('Enter the code: '))
@staticmethod
def get_target(user): # get username or full name
if user.username:
return user.username
else:
return user.first_name + (f' {user.last_name}' if user.last_name else '')
def check_subscription(self, user): # search user in channel members, there is a problem
target = self.get_target(user)
participants = self._client.iter_participants(self._channel, search=target)
ids = [member.id for member in participants]
return user.id in ids
bot = TeleBot(config.bot_token) # bot instance
tg = TeleHelper(config.api_id, config.api_hash, config.phone, config.channel) # instance of the class above
@bot.message_handler(commands=['command'])
def handle_join(message):
if tg.check_subscription(message.from_user): # here problems start
text = 'All is good!'
bot.send_message(message.chat.id, text)
else:
text = 'You have to subscribe @python_lounge'
bot.send_message(message.chat.id, text)
if __name__ == '__main__':
bot.polling()
我从 telethon.sync
而不是从 telethon 导入了 TelegramClient
,所以一切看起来都很好,但不小心我得到了一个错误:
2019-08-24 10:31:07,342 (main.py:65 WorkerThread1) ERROR - TeleBot: "RuntimeError occurred, args=('You must use "async for" if the event loop is running (i.e. you are inside an "async def")',)
Traceback (most recent call last):
File "/root/ContestBot/.venv/lib/python3.7/site-packages/telebot/util.py", line 59, in run
task(*args, **kwargs)
File "main.py", line 99, in handle_join
if tg.check_subscription(message.from_user):
File "/root/ContestBot/main.py", line 25, in check_subscription
ids = [member.id for member in participants]
File "/root/ContestBot/.venv/lib/python3.7/site-packages/telethon/requestiter.py", line 102, in __iter__
'You must use "async for" if the event loop '
RuntimeError: You must use "async for" if the event loop is running (i.e. you are inside an "async def")
"
我试过"async for",但我是异步编程的新手,我写的是:
async def check_subscription(self, user):
ids = []
async for member in self._client.iter_participants(self._channel, search=self.get_target(user)):
await ids.append(member.id)
return user.id in ids
显然我想要,但程序仍然无法运行:
<coroutine object TeleHelper.check_subscription at 0x7ff9bc57f3c8>
/root/ContestBot/.venv/lib/python3.7/site-packages/telebot/util.py:59: RuntimeWarning: coroutine 'TeleHelper.check_subscription' was never awaited
task(*args, **kwargs)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
我用的是Python3.7.3
我的问题是我没有深入理解就把threads
和asyncio
混在一起了。解决方案之一是使用 aiogram
甚至 telethon
等异步模块来管理机器人。
实际上,我不必使用 telethon 来检查用户是否也订阅了该频道。 Bot API
中有个方法叫getChatMember
,所以pyTelegramBotAPI
就够了。它 returns None
或 ChatMember
对象 status
可以是“创建者”,“管理员”,“成员”,“受限”,“离开”或“被踢” .
所以,我的解决方案只有 pyTelegramBotAPI
:
@bot.message_handler(func=lambda msg: msg.text == 'Участвовать')
def handle_join(message):
member = bot.get_chat_member(config.channel_id, message.from_user.id). # right way to check if user subscribed
if member is not None and member.status in ('creator', 'administrator', 'member'):
text = 'All is good!'
bot.send_message(message.chat.id, text)
else:
text = 'You have to subscribe @python_lounge'
bot.send_message(message.chat.id, text)
顺便说一下,我发现 telethon.sync
不是真的。对于不知道 asyncio 是如何工作的人来说,这只是一个小技巧。所以 telethon.sync
只在有限的情况下有效,你不应该也可能不能将它用于除快速脚本之外的任何东西。