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

异步函数中如何让while True循环返回循环起始位置?

问题原因

你的脚本只能触发一次的核心原因有3个:

  1. await telegramclient.run_until_disconnected()是永久阻塞调用,只要Telegram客户端不主动断开连接,这行代码永远不会返回,外层的while True自然永远走不到下一轮循环。
  2. 你每次检测到门铃信号就重新创建Telegram客户端实例、重复注册回调,逻辑设计完全错误,Telethon的客户端本身就是常驻运行的,不需要每次触发事件都重建。
  3. 异步回调里用了同步的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 12:27:27