异步函数中如何让while True循环返回循环起始位置?
问题原因
你的脚本只能触发一次的核心原因有3个:
await telegramclient.run_until_disconnected()是永久阻塞调用,只要Telegram客户端不主动断开连接,这行代码永远不会返回,外层的while True自然永远走不到下一轮循环。- 你每次检测到门铃信号就重新创建Telegram客户端实例、重复注册回调,逻辑设计完全错误,Telethon的客户端本身就是常驻运行的,不需要每次触发事件都重建。
- 异步回调里用了同步的
time.sleep(5),会直接卡住整个异步事件循环,导致所有后续消息、事件都无法处理。另外你代码里还有个低级bug:开门按钮绑定的data是b'open',回调判断里写的是b'openen',永远匹配不到开门操作。
修复步骤
- 程序启动时只初始化一次Telegram客户端,只注册一次回调,不要把这部分逻辑塞到信号检测的循环里
- 把GPIO信号检测做成独立的异步任务,和Telegram客户端事件循环并行跑
- 所有异步场景下的等待替换成
asyncio.sleep(),禁止用同步的time.sleep() - 修正按钮data的匹配错误
修复后参考代码
import asyncio from telethon import TelegramClient, events, Button # 导入你自己用的GPIO、继电器相关库,比如RPi.GPIO或者gpiozero # 提前初始化好detect(信号检测引脚)、relay(继电器控制引脚)对象 # 替换成你自己的账号、机器人参数 api_id = 你的API_ID api_hash = '你的API_HASH' bot_token = '你的BOT_TOKEN' notichannel = 你的通知频道ID # 全局标记,避免信号触发时重复发消息 is_waiting_response = False async def detect_doorbell(): """独立异步任务,持续检测门铃信号""" global is_waiting_response print("开始检测门铃信号") while True: if detect.value == 1 and not is_waiting_response: print("检测到门铃信号") is_waiting_response = True # 发送带操作按钮的通知消息 await telegramclient.send_message( notichannel, '有人在门口', buttons=[[ Button.inline('开门', b'open'), Button.inline('忽略', b'ignore') ]] ) # 轮询间隔设为0.1秒,避免占满CPU await asyncio.sleep(0.1) async def main(): global telegramclient # 整个程序生命周期只初始化一次客户端 telegramclient = await TelegramClient("Test", api_id, api_hash).start(bot_token=bot_token) # 只注册一次按钮回调 @telegramclient.on(events.CallbackQuery) async def callback(event): global is_waiting_response if event.data == b'open': await event.answer("已开门!", alert=True) relay.on() print("执行开门操作") # 异步等待,不会阻塞整个事件循环 await asyncio.sleep(5) relay.off() print("门已关闭") else: await event.answer("已忽略开门请求", alert=True) print("忽略本次开门请求") # 重置状态标记,允许下一次门铃触发 is_waiting_response = False # 清空已处理消息的按钮,避免重复点击旧消息 await event.edit("本次请求已处理", buttons=None) # 启动门铃检测后台任务 asyncio.create_task(detect_doorbell()) # 常驻运行Telegram客户端,作为程序主阻塞逻辑 await telegramclient.run_until_disconnected() if __name__ == "__main__": asyncio.run(main())
额外说明
- 新增的
is_waiting_response标记可以避免门铃信号持续触发时,短时间内重复发送大量通知消息 - 信号检测用0.1秒的异步轮询间隔,资源占用极低,也不会卡住其他消息、事件的处理
- 如果不需要保留历史通知,可以在处理完回调后直接删除对应消息,进一步减少误触可能
内容的提问来源于stack exchange,提问作者Raymond L
相关产品推荐
相关产品推荐

