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

如何用discord.py实现CMD输入消息发送?附代码报错求助

问题解决方案与代码优化建议

一、解决两个核心错误

1. AttributeError: 'NoneType' object has no attribute 'send'

出现这个问题的核心原因是bot.get_channel(channelid)未获取到有效频道对象,常见诱因及修复方式:

  • 频道ID格式错误:确保传入的channelid是整数类型(Discord频道ID是纯数字字符串,需转成int),而非字符串。
  • 机器人未完成初始化:必须在bot.on_ready()事件触发后,再执行获取频道、发送消息的操作——此时机器人已完成与Discord网关的连接,能正确拉取服务器/频道数据。
  • 权限或服务器归属问题:检查机器人是否已加入目标频道所在的服务器,且拥有发送消息权限。

2. RuntimeWarning: coroutine 'reciever' was never awaited

这个警告是因为你在子线程中直接调用了协程函数,但未正确触发其执行。协程必须通过await或asyncio.run()来启动,在子线程中需要单独创建asyncio事件循环:

import asyncio

def input_thread(bot, target_channel_id):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(send_input_messages(bot, target_channel_id))
    loop.close()

async def send_input_messages(bot, target_channel_id):
    channel = bot.get_channel(target_channel_id)
    if not channel:
        print("无效频道ID,请检查后重试")
        return
    while True:
        msg = input("输入要发送的消息(输入exit退出):")
        if msg.lower() == "exit":
            await bot.close()
            break
        await channel.send(msg)

二、完整可行代码示例

整合三个模块的功能,同时处理输入与机器人事件:

主模块(main.py)

import discord
from discord.ext import commands
import threading
import asyncio

# 初始化机器人
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix="!", intents=intents)

# 消息监听逻辑
@bot.event
async def on_message(message):
    # 忽略机器人自身消息
    if message.author == bot.user:
        return
    print(f"[{message.channel.name}] {message.author.name}: {message.content}")
    await bot.process_commands(message)

@bot.event
async def on_ready():
    print(f"已登录为 {bot.user.name}")
    # 启动输入线程
    target_channel_id = int(input("输入目标频道ID:"))
    threading.Thread(target=input_thread, args=(bot, target_channel_id), daemon=True).start()

def input_thread(bot, target_channel_id):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(send_input_messages(bot, target_channel_id))
    loop.close()

async def send_input_messages(bot, target_channel_id):
    channel = bot.get_channel(target_channel_id)
    if not channel:
        print("错误:无法找到目标频道,请检查ID或机器人权限")
        await bot.close()
        return
    print(f"已绑定频道:{channel.name},输入消息即可发送")
    while True:
        try:
            msg = input()
            if msg.lower() == "exit":
                await bot.close()
                break
            await channel.send(msg)
        except Exception as e:
            print(f"发送失败:{str(e)}")

if __name__ == "__main__":
    # 用户协议确认
    print("=== CMD Discord 客户端 ===")
    agree = input("是否同意使用条款?(y/n):")
    if agree.lower() != "y":
        print("程序退出")
        exit()
    token = input("输入Discord机器人Token:")
    bot.run(token)

三、代码优化建议

  1. 用异步队列替代直接线程调用:避免线程与asyncio事件循环的冲突,通过asyncio.Queue在输入线程和机器人事件循环间传递消息,更符合discord.py的异步模型:
    # 在主模块初始化队列
    msg_queue = asyncio.Queue()
    
    # 输入线程只负责把消息放入队列
    def input_thread():
        while True:
            msg = input()
            if msg.lower() == "exit":
                msg_queue.put_nowait("exit")
                break
            msg_queue.put_nowait(msg)
    
    # 在bot.on_ready()中启动消费队列的任务
    @bot.event
    async def on_ready():
        print(f"已登录为 {bot.user.name}")
        target_channel_id = int(input("输入目标频道ID:"))
        threading.Thread(target=input_thread, daemon=True).start()
        await process_msg_queue(target_channel_id)
    
    async def process_msg_queue(target_channel_id):
        channel = bot.get_channel(target_channel_id)
        if not channel:
            print("错误:无法找到目标频道")
            await bot.close()
            return
        while True:
            msg = await msg_queue.get()
            if msg == "exit":
                await bot.close()
                break
            await channel.send(msg)
    
  2. 参数验证与错误处理:对输入的Token、频道ID做格式校验,捕获网络异常、权限不足等错误并给出明确提示。
  3. 模块化拆分:将消息监听、发送逻辑拆分为单独的函数/模块,但确保所有异步逻辑都在同一个asyncio事件循环中处理,避免跨线程直接操作bot实例。
  4. 日志记录:使用logging模块替代print,方便调试和记录运行状态。
  5. 弃用sys.addaudithook:该API主要用于审计Python运行时行为,不适合处理用户输入,属于误用场景,建议放弃这种实现方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 09:45:34