You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同步实例方法中调用Discord异步消息发送方法?

问题概述

同步方法send_message()在线程中调用,需要触发Discord的异步方法my_channel.send(),尝试asyncio.run、async_to_sync等多种实现方式后,先后遇到两类错误:

  • TypeError: type of the return value must be str; got coroutine instead
  • RuntimeError: Timeout context manager should be used inside a task

相关代码

#@typechecked
class MessageCog(Cog):

    @typechecked
    async def asend_message(self, message:str)->str:
        my_channel:TextChannel    = self.bot.get_channel(self.channel)
        assert isinstance(my_channel, TextChannel)
        embed     :Embed          = Embed(title="Response", description=message, color=pepe_green)
        assert isinstance(embed, Embed)
        my_message:Message        = await my_channel.send(embed=embed)
        assert isinstance(my_message, Message)
        result    :str            = str(my_message) # ??
        assert isinstance(result, str)
        if not isinstance(result, str): raise Exception()
        return result

    @typechecked
    def send_message(self, message:str)->str:
        #
        #f:Coroutine = lambda m: self.asend_message(m)
        #result:str = run(f)
        #
        #result:str = run(self.asend_message(message))
        #
        #result:str = get_running_loop().run_untilcomplete(self.asend_message(message))
        #
        #f:Callable[[str],str] = async_to_sync(MessageCog.asend_message)
        #
        #f = async_to_sync(MessageCog.asend_message)
        #result:str = f(self, message)
        #
        #result:str = create_task(self.asend_message(message))
        #
        #f = partial(MessageCog.asend_message, self)
        #result:str = run(f(message))
        assert isinstance(result, str)
        if not isinstance(result, str): raise Exception()
        return result

错误信息

初始错误

[chain/error] [chain:AgentExecutor] [82.96s] Chain run errored with error:
...
TypeError: type of the return value must be str; got coroutine instead

更新后错误

[tool/error] [chain:AgentExecutor > tool:send_message] [3ms] Tool run errored with error:
RuntimeError('Timeout context manager should be used inside a task')Traceback (most recent call last):

  File "/home/user/.local/lib/python3.10/site-packages/langchain_core/tools.py", line 409, in run
    context.run(

  File "/home/user/.local/lib/python3.10/site-packages/langchain_core/tools.py", line 750, in _run
    else self.func(*args, **kwargs)

  File "/home/user/enumerate_bot/./eris.py", line 380, in <lambda>
    func       =lambda message: self. send_message(message),

  File "/usr/lib/python3/dist-packages/typeguard.py", line 494, in wrapper
    retval = func(*args, **kwargs)

  File "/home/user/enumerate_bot/./eris.py", line 335, in send_message
    result:str = run(g)

  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)

  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()

  File "/home/user/enumerate_bot/./eris.py", line 306, in asend_message
    my_message:Message        = await my_channel.send(embed=embed)

  File "/home/user/.local/lib/python3.10/site-packages/discord/abc.py", line 1618, in send
    data = await state.http.send_message(channel.id, params=params)

  File "/home/user/.local/lib/python3.10/site-packages/discord/http.py", line 638, in request
    async with self.__session.request(method, url, **kwargs) as response:

  File "/home/user/.local/lib/python3.10/site-packages/aiohttp/client.py", line 1197, in __aenter__
    self._resp = await self._coro

  File "/home/user/.local/lib/python3.10/site-packages/aiohttp/client.py", line 507, in _request
    with timer:

  File "/home/user/.local/lib/python3.10/site-packages/aiohttp/helpers.py", line 715, in __enter__
    raise RuntimeError(

RuntimeError: Timeout context manager should be used inside a task

解决方案

核心原因

  1. 直接返回未执行的协程对象会触发TypeError,因为同步方法要求返回str类型,而协程不是字符串。
  2. asyncio.run会创建全新的事件循环,但Discord的HTTP客户端绑定在bot的原循环中,新循环无法兼容aiohttp的超时上下文,导致RuntimeError。
  3. create_task仅能将任务提交到当前运行的循环,但同步线程中可能没有活跃循环,或无法直接获取执行结果。

正确实现代码

使用Discord.py自带的async_to_sync工具,它会自动处理循环切换和线程安全问题,适配Discord内部上下文:

from discord.ext.commands import async_to_sync

# ... 其他代码保持不变 ...

@typechecked
def send_message(self, message:str)->str:
    # 用async_to_sync包装异步方法
    sync_send = async_to_sync(self.asend_message)
    result = sync_send(message)
    assert isinstance(result, str)
    return result

关键注意事项

  • 必须导入discord.ext.commands下的async_to_sync,而非第三方库的同名工具,它专门适配Discord.py的线程和循环模型。
  • 禁止在同步线程中创建新事件循环(如asyncio.run),必须复用bot已有的事件循环。
  • 即使send_message在非bot主线程中调用,async_to_sync也会自动完成线程间的循环切换,确保操作在bot的事件循环中执行。

内容的提问来源于stack exchange,提问作者Innovations Anonymous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:32:03