我一直在开发一个Discord机器人,它可以像character.ai角色一样对话。我主要使用了一个非官方的characterai API (https://github.com/kramcat/CharacterAI) 和discord.py库。代码如下:
import tracemalloctracemalloc.start()import asyncioimport discordfrom discord.ext import commandsimport characteraicharacter_ai_token = "character ai token here"intents = discord.Intents.all()client = characterai.pyCAI(character_ai_token)bot = commands.Bot(command_prefix='!', intents=intents)@bot.eventasync def on_ready(): print(f'Bot is ready. Logged in as {bot.user.name}')target_channel_id = "channel_id_here"@bot.eventasync def on_message(message): data = await client.chat.send_message('CHAR', message, wait=True) if message.author == bot.user: return if message.channel.id != target_channel_id: return if message.content.startswith(bot.command_prefix): await bot.process_commands(message) return response = await data['replies'][0]['text'] await message.channel.send(response)async def start_bot(): await bot.start("bot token")async def main(): await asyncio.create_task(start_bot())asyncio.run(main())
我尝试使用了asyncio.create_task()、asyncio.get_event_loop()、loop.create_task()。我还尝试在不将start_bot函数放入main()的情况下使用asyncio.run()。
Traceback (most recent call last): File "C:\Users\hp\Desktop\projects\discoBotchai\main.py", line 43, in <module> asyncio.run(main()) File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\asyncio\runners.py", line 33, in run raise RuntimeError(RuntimeError: asyncio.run() cannot be called from a running event loopsys:1: RuntimeWarning: coroutine 'main' was never awaitedObject allocated at (most recent call last): File "C:\Users\hp\Desktop\projects\discoBotchai\main.py", lineno 43Process finished with exit code 1
回答:
我看到你试图使用await bot.start('...')
在start_bot
函数中启动你的Discord机器人。此外,我注意到你在on_message
函数中使用client
变量来以”character.ai角色”的身份回应。我发现了一些与启动你的Discord机器人无关的问题。让我们先看看这些问题,然后再看看如何解决它们。
一般问题
- 启动你的
bot
目前是多余的。 - 你应该使用监听器来处理on_message,而不是
@bot.event
。这样做可以避免在处理命令时遇到问题,并且不需要在消息监听器中手动处理命令。 - 你的目标频道ID是一个字符串,Discord.py使用整数作为频道ID。
- 在调用
client.chat.send_message
时,你将discord.Message
对象传递给了message
参数。这会导致问题,你需要传递消息的清理内容,如果有的话。 - 你为你的客户端创建了一个
pyCAI
实例,而不是pyAsyncCAI
实例。pyCAI
不是异步的,你需要使用异步客户端实例。 - 在从你的
client
获取响应时,你等待了data
。这会在运行时导致问题。
启动机器人
让我们看看你当前用于启动机器人的代码块,然后详细说明如何修复它以使其运行。
async def start_bot(): await bot.start("bot token")async def main(): await asyncio.create_task(start_bot())asyncio.run(main())
在你当前的示例中,你的main()
函数被调用,它会生成一个任务来运行start_bot
函数。然后这个函数调用await bot.start('bot token')
。不幸的是,你当前的实现忽略了启动你的异步characterAI客户端,如Github上所示。为了实现这一点,我们需要调整你的main
函数以启动机器人和客户端。
async def wrapped_start_bot(): async with bot: await bot.start('bot token')async def main(): tasks = [ asyncio.create_task(wrapped_start_bot()), asyncio.create_task(client.start(headless=True) ] await asyncio.wait(tasks)
在我们调整后的版本中,我们创建了两个任务:一个启动机器人,另一个启动我们的异步character AI客户端。之后,我们使用asyncio.wait
,它会暂停main
协程,直到所有任务完成,或者在我们的例子中,直到机器人的生命周期结束。很好,让我们将所有这些更改整合在一起。
最终修订
import asyncioimport characteraiimport discordfrom discord.ext import commandsimport tracemalloctracemalloc.start()character_ai_token = "character ai token here"intents = discord.Intents.all()client = characterai.pyAsyncCAI(character_ai_token)bot = commands.Bot(command_prefix='!', intents=intents)@bot.eventasync def on_ready(): print(f'Bot is ready. Logged in as {bot.user.name}')# 将目标频道ID设置为整数:target_channel_id: int = 000000001# 使用bot.listen()而不是@bot.event,以免担心手动处理命令。@bot.listen('on_message')async def on_message(message: discord.Message): if not message.content: # 这条消息没有内容! return if message.author.bot: # 这条消息是由机器人发送的! return if message.channel.id != target_channel_id: # 这不在正确的频道中! return # 但我们怎么知道这不是一个命令呢?让我们确保这不是一个有效的命令! context = await bot.get_context(message) if context.valid: # 这是一个有效的命令,这意味着我们的机器人不应该对此做出回应! return # 我们知道以下所有项目: # - 这条消息有内容。 # - 这条消息不是由机器人发送的。 # - 这条消息在正确的频道中。 # - 这条消息不是机器人命令。 data = await client.chat.send_message('CHAR', message.clean_content, wait=True) response = data['replies'][0]['text'] await message.reply(response, mention_author=False)async def wrapped_start_bot(): async with bot: await bot.start('bot token')async def main(): tasks = [ asyncio.create_task(wrapped_start_bot()), asyncio.create_task(client.start(headless=True) ] await asyncio.wait(tasks)
很好。我们已经修正了启动bot
和client
的问题,并修复了你当前实现中的一些其他一般问题!