如何从事件循环外部(即来自 python-telegram-bot 线程)发送带有 discord.py 的消息?
How to send a message with discord.py from outside the event loop (i.e. from python-telegram-bot thread)?
我想使用库 python-telegram-bot 和 discord.py(版本 1.0.0)制作一个在 discord 和电报之间进行通信的机器人。然而,问题是 discord.py 使用异步函数和 python-telegram-bot 线程。使用下面的代码,对于在 discord 中发布的消息一切正常(机器人将它们正确发送到电报),但是反之则不起作用(机器人从电报中获取消息并将其发送到 discord)。我以前遇到 syntax/runtime 错误的问题,因为我试图 运行 同步函数中的 discords channel.send
函数(因此要么只返回一个生成器对象,要么抱怨我不能使用 await
在同步函数中)。然而,同时 python-telegram-bot 的 MessageHandler
需要一个同步函数,所以当给他一个异步函数时 Python 抱怨说 "await" 从来没有被调用过异步功能。
我现在尝试使用 asgiref 库中的 async_to_sync 方法到 运行 我的异步 broadcastMsg
来自 MessageHandler
,但是代码仍然没有将消息发送到 discord!它似乎正确地调用了该函数,但只到第 print('I get to here')
行为止。不显示任何错误,也不会在不和谐中弹出任何消息。我想这与我必须将函数注册为 discord.py 事件循环中的任务有关,但是注册仅在 botDiscord.run(TOKENDISCORD)
执行之前发生时才有效,当然必须在之前发生。
所以把我的问题归结为一个问题:
我如何能够与来自另一个线程(来自电报 MessageHandler
)的 discord.py 事件循环交互。或者,如果这不可能:如何在不进入 discord.py 事件循环的情况下使用 discord.py 发送消息?
感谢您的帮助
import asyncio
from asgiref.sync import async_to_sync
from telegram import Message as TMessage
from telegram.ext import (Updater,Filters,MessageHandler)
from discord.ext import commands
import discord
TChannelID = 'someTelegramChannelID'
DChannel = 'someDiscordChannelObject'
#%% define functions / commands
prefix = "?"
botDiscord = commands.Bot(command_prefix=prefix)
discordChannels = {}
async def broadcastMsg(medium,channel,message):
'''
Function to broadcast a message to all linked channels.
'''
if isinstance(message,TMessage):
fromMedium = 'Telegram'
author = message.from_user.username
channel = message.chat.title
content = message.text
elif isinstance(message,discord.Message):
fromMedium = 'Discord'
author = message.author
channel = message.channel.name
content = message.content
# check where message comes from
textToSend = '%s wrote on %s %s:\n%s'%(author,fromMedium,channel,content)
# go through channels and send the message
if 'telegram' in medium:
# transform channel to telegram chatID and send
updaterTelegram.bot.send_message(channel,textToSend)
elif 'discord' in medium:
print('I get to here')
await channel.send(textToSend)
print("I do not get there")
@botDiscord.event
async def on_message(message):
await broadcastMsg('telegram',TChannelID,message)
def on_TMessage(bot,update):
# check if this chat is already known, else save it
# get channels to send to and send message
async_to_sync(broadcastMsg)('discord',DChannel,update.message)
#%% initialize telegram and discord bot and run them
messageHandler = MessageHandler(Filters.text, on_TMessage)
updaterTelegram = Updater(token = TOKENTELEGRAM, request_kwargs={'read_timeout': 10, 'connect_timeout': 10})
updaterTelegram.dispatcher.add_handler(messageHandler)
updaterTelegram.start_polling()
botDiscord.run(TOKENDISCORD)
How can I send a message with discord.py without being within the discord.py event loop?
要从事件循环线程外部安全地安排协程,请使用 asyncio.run_coroutine_threadsafe
:
_loop = asyncio.get_event_loop()
def on_TMessage(bot, update):
asyncio.run_coroutine_threadsafe(
broadcastMsg('discord', DChannel, update.message), _loop)
您可以尝试将它们拆分为 2 个单独的 *.py 文件。
t2d.py #telegram to discor
import subprocess
from telethon import TelegramClient, events, sync
api_id = '...'
api_hash = '...'
with TelegramClient('name', api_id, api_hash) as client:
@client.on(events.NewMessage()) #inside .NewMessage() you can put specific channel like: chats="test_channel"
async def handler(event):
print('test_channel raw text: ', event.raw_text) #this row is not necessary
msg = event.raw_text
subprocess.call(["python", "s2d.py", msg])
client.run_until_disconnected()
s2d.py #发送到discord
import discord, sys
my_secret = '...'
clientdiscord = discord.Client()
@clientdiscord.event
async def on_ready():
#print('We have logged in as {0.user}'.format(clientdiscord)) #this row is not necessary
channel = clientdiscord.get_channel(123456789) # num of channel where you want to write message
msg = sys.argv[1] #grab message
msg = 's2d: ' + msg #only for test, you can delete this row
await channel.send(msg)
quit() # very important quit this bot
clientdiscord.run(my_secret)
会慢一点(子进程会延迟),但是很容易解决
我想使用库 python-telegram-bot 和 discord.py(版本 1.0.0)制作一个在 discord 和电报之间进行通信的机器人。然而,问题是 discord.py 使用异步函数和 python-telegram-bot 线程。使用下面的代码,对于在 discord 中发布的消息一切正常(机器人将它们正确发送到电报),但是反之则不起作用(机器人从电报中获取消息并将其发送到 discord)。我以前遇到 syntax/runtime 错误的问题,因为我试图 运行 同步函数中的 discords channel.send
函数(因此要么只返回一个生成器对象,要么抱怨我不能使用 await
在同步函数中)。然而,同时 python-telegram-bot 的 MessageHandler
需要一个同步函数,所以当给他一个异步函数时 Python 抱怨说 "await" 从来没有被调用过异步功能。
我现在尝试使用 asgiref 库中的 async_to_sync 方法到 运行 我的异步 broadcastMsg
来自 MessageHandler
,但是代码仍然没有将消息发送到 discord!它似乎正确地调用了该函数,但只到第 print('I get to here')
行为止。不显示任何错误,也不会在不和谐中弹出任何消息。我想这与我必须将函数注册为 discord.py 事件循环中的任务有关,但是注册仅在 botDiscord.run(TOKENDISCORD)
执行之前发生时才有效,当然必须在之前发生。
所以把我的问题归结为一个问题:
我如何能够与来自另一个线程(来自电报 MessageHandler
)的 discord.py 事件循环交互。或者,如果这不可能:如何在不进入 discord.py 事件循环的情况下使用 discord.py 发送消息?
感谢您的帮助
import asyncio
from asgiref.sync import async_to_sync
from telegram import Message as TMessage
from telegram.ext import (Updater,Filters,MessageHandler)
from discord.ext import commands
import discord
TChannelID = 'someTelegramChannelID'
DChannel = 'someDiscordChannelObject'
#%% define functions / commands
prefix = "?"
botDiscord = commands.Bot(command_prefix=prefix)
discordChannels = {}
async def broadcastMsg(medium,channel,message):
'''
Function to broadcast a message to all linked channels.
'''
if isinstance(message,TMessage):
fromMedium = 'Telegram'
author = message.from_user.username
channel = message.chat.title
content = message.text
elif isinstance(message,discord.Message):
fromMedium = 'Discord'
author = message.author
channel = message.channel.name
content = message.content
# check where message comes from
textToSend = '%s wrote on %s %s:\n%s'%(author,fromMedium,channel,content)
# go through channels and send the message
if 'telegram' in medium:
# transform channel to telegram chatID and send
updaterTelegram.bot.send_message(channel,textToSend)
elif 'discord' in medium:
print('I get to here')
await channel.send(textToSend)
print("I do not get there")
@botDiscord.event
async def on_message(message):
await broadcastMsg('telegram',TChannelID,message)
def on_TMessage(bot,update):
# check if this chat is already known, else save it
# get channels to send to and send message
async_to_sync(broadcastMsg)('discord',DChannel,update.message)
#%% initialize telegram and discord bot and run them
messageHandler = MessageHandler(Filters.text, on_TMessage)
updaterTelegram = Updater(token = TOKENTELEGRAM, request_kwargs={'read_timeout': 10, 'connect_timeout': 10})
updaterTelegram.dispatcher.add_handler(messageHandler)
updaterTelegram.start_polling()
botDiscord.run(TOKENDISCORD)
How can I send a message with discord.py without being within the discord.py event loop?
要从事件循环线程外部安全地安排协程,请使用 asyncio.run_coroutine_threadsafe
:
_loop = asyncio.get_event_loop()
def on_TMessage(bot, update):
asyncio.run_coroutine_threadsafe(
broadcastMsg('discord', DChannel, update.message), _loop)
您可以尝试将它们拆分为 2 个单独的 *.py 文件。
t2d.py #telegram to discor
import subprocess
from telethon import TelegramClient, events, sync
api_id = '...'
api_hash = '...'
with TelegramClient('name', api_id, api_hash) as client:
@client.on(events.NewMessage()) #inside .NewMessage() you can put specific channel like: chats="test_channel"
async def handler(event):
print('test_channel raw text: ', event.raw_text) #this row is not necessary
msg = event.raw_text
subprocess.call(["python", "s2d.py", msg])
client.run_until_disconnected()
s2d.py #发送到discord
import discord, sys
my_secret = '...'
clientdiscord = discord.Client()
@clientdiscord.event
async def on_ready():
#print('We have logged in as {0.user}'.format(clientdiscord)) #this row is not necessary
channel = clientdiscord.get_channel(123456789) # num of channel where you want to write message
msg = sys.argv[1] #grab message
msg = 's2d: ' + msg #only for test, you can delete this row
await channel.send(msg)
quit() # very important quit this bot
clientdiscord.run(my_secret)
会慢一点(子进程会延迟),但是很容易解决