异步改造Telethon-MongoDB代码遇下标错误求助
解决Telethon+MongoDB异步改造中的两个常见错误
让我们一步步拆解你遇到的问题,然后给出可行的修复方案:
第一个错误:pymongo+asyncio的TypeError: 'coroutine' object is not subscriptable
你写的await loop.run_until_complete(a.finddb()[0])犯了两个关键问题:
- 协程调用顺序错误:
a.finddb()是一个协程对象,你先尝试对它做[0]下标操作,再传给run_until_complete,这就相当于直接去取协程对象的第0个元素,当然会报错。正确顺序是先等待协程执行完成,再对返回结果做下标操作。 - pymongo本身不支持异步:pymongo是同步库,即使你用
run_until_complete包裹,它的数据库操作还是会阻塞整个异步事件循环,违背异步编程的初衷,直接换Motor才是正确选择。
第二个错误:Motor的TypeError: 'AsyncIOMotorCursor' object is not subscriptable
Motor的find()方法返回的是AsyncIOMotorCursor异步游标,不是同步pymongo里的可迭代游标,更不是列表,所以不能直接用[0]取第一个元素。要获取游标里的文档,需要用异步方法消费:
- 只需要第一个文档:用
await cursor.next()(注意处理无结果的异常) - 需要多个文档:用
await cursor.to_list(length=N)(N为要获取的数量,比如10)
另外还有几个优化点:
create_index不要放在每次查询的方法里,重复创建索引无意义,应该在程序启动时只执行一次。- Telethon代码里去掉
sync导入,你用的是异步事件处理,sync是给同步代码用的,会干扰异步逻辑。
修正后的完整Motor版本代码
from telethon import TelegramClient, events, Button, utils import re import logging import motor.motor_asyncio # 提前初始化MongoDB客户端,避免每次创建Search实例都重新连接 myclient = motor.motor_asyncio.AsyncIOMotorClient( "mongodb+srv://login:pass@cluster0.ye4cx.mongodb.net/info?retryWrites=true&w=majority&ssl=true&ssl_cert_reqs=CERT_NONE" ) mydb = myclient["info"] mycol = mydb["comics"] async def init_indexes(): # 程序启动时创建一次索引,不要每次查询都创建 await mycol.create_index([('title', 'text')], default_language='english') class Search(): def __init__(self, search): self.search = search # 直接使用全局的集合对象,避免重复创建客户端 self.mycol = mycol async def searchdb(self): cursor = self.mycol.find({"$text": {"$search": self.search}}) # Motor里count()是异步方法,必须await count = await cursor.count_documents() if count == 0: return [] # 把游标转换成列表,这里取前10个结果 return await cursor.to_list(length=10) async def finddb(self): cursor = self.mycol.find({"title": self.search}) try: # 获取第一个文档,无结果时会抛出StopAsyncIteration return await cursor.next() except StopAsyncIteration: # 返回None表示未找到 return None # 假设你的bot已经完成初始化,示例: # bot = TelegramClient('session_name', api_id, api_hash) @bot.on(events.NewMessage(pattern=r'(?<=|).*(?=|)')) async def find(event): search_text = event.text.replace("|", "") a = Search(search_text) find_result = await a.finddb() if not find_result: await event.respond("没有找到对应的漫画信息") return # 构造回复消息 response = ( f'**|{find_result.get("title")}|**\n\n' f'**Статус перевода**: {find_result.get("status")}\n' f'**Издатель**: {find_result.get("publisher")}\n\n' f'**Жанр**: {find_result.get("genres")}\n' f'**Описание**:\n{find_result.get("description")}' ) await event.respond(response, buttons=[[Button.inline('Ссылки на скачку', b'next')]]) # 程序启动入口 async def main(): await init_indexes() await bot.start() await bot.run_until_disconnected() if __name__ == "__main__": import asyncio asyncio.run(main())
关键修复点说明
- 全局MongoDB客户端:避免重复创建数据库连接,提升性能。
- 异步索引初始化:仅在程序启动时创建一次文本索引,避免冗余操作。
- 正确消费Motor游标:用异步方法获取游标结果,适配Motor的异步特性。
- 错误处理:增加未找到文档时的友好回复,避免后续逻辑报错。
- 移除冗余导入:去掉Telethon的
sync导入,符合异步编程规范。
内容的提问来源于stack exchange,提问作者fungalspores
相关产品推荐
相关产品推荐

