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

Telegram机器人AttributeError修复求助:NoneType无from_user属性

问题描述

我正在开发一款Telegram托管服务机器人(PayEscrowBOT),功能逻辑如下:

  • 用户发送/start指令后,机器人发送带两个Inline Keyboard按钮的欢迎消息;
  • 点击“ℹ️ INSTRUCTIONS”按钮,欢迎消息会切换为带“⬅️ BACK”按钮的说明消息;
  • 期望点击BACK按钮时切回原欢迎消息,但尝试后触发报错:AttributeError: 'NoneType' object has no attribute 'from_user'

以下是完整代码及报错栈:

from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler

# Ihr Bot-Token
TOKEN = "Bot_Token"

# Ein globaler Wert, um den Zustand des Bots zu verfolgen
START, INSTRUCTIONS = range(2)

def start(update, context):
    user = update.message.from_user
    
    # Text der Willkommensnachricht
    welcome_message = """⚜️ **PayEscrowBOT** ⚜️ v.1.1
#1 Automated Telegram Escrow Service

Welcome to **PayEscrowBOT**™️. This bot provides a safe escrow service for your business on Telegram. Never get ripped again, The bot secures your funds throughout the deal. If you have any issues, kindly type /contact and an arbitrator will join the group chat within 24 hours.

🎟 **ESCROW FEE:**
5% if over $100
5$ if under $100

☑️ **DEALS DONE:** 1200
🧑‍⚖️ **DISPUTES HANDLED:** 280

💬 **Declare the seller or buyer with** /seller or /buyer [BTC/LTC ADDRESS] (**Your BTC/LTC address =** [BTC/LTC ADDRESS])

💡 Type /menu **to summon a menu with all bot's features**"""

    # Erstes Inline-Keyboard
    keyboard1 = [[InlineKeyboardButton("❓ WHAT IS ESCROW", callback_data="what_is_escrow"),
                 InlineKeyboardButton("ℹ️ INSTRUCTIONS", callback_data="instructions")]]
    
    # Inline-Keyboard-Markup für das erste Keyboard
    reply_markup1 = InlineKeyboardMarkup(keyboard1)

    # Senden der Willkommensnachricht mit dem Inline-Keyboard
    update.message.reply_markdown(welcome_message, reply_markup=reply_markup1)
    
    # Setzen des Bots in den START-Zustand
    context.user_data["state"] = START

def instructions(update, context):
    query = update.callback_query
    query.answer()
    
    # Nachricht mit den Anweisungen
    instructions_message = """💬 INSTRUCTIONS

1️⃣ Add the bot to a new Group Chat with buyer + seller

2️⃣ Declare the seller with /seller [BTC/LTC ADDRESS]
        Replace [BTC/LTC ADDRESS] with receive wallet

3️⃣ Declare the buyer with /buyer [BTC/LTC ADDRESS]
        Replace [BTC/LTC ADDRESS] with refund wallet

4️⃣ Generate a wallet with /generate

5️⃣ Buyer sends agreed amount to generated BTC wallet

6️⃣ Check balance with /balance

7️⃣ Seller releases product / service to buyer when balance is confirmed

8️⃣ When buyer is satisfied, release payment with /release
    The seller can also /refund the buyer.

9️⃣ If any issues, contact arbitrator with /contact

❗️ For video example, type /video

☎️ If you need help, type /contact [INVITE LINK TO GROUP]
Replace [INVITE LINK TO GROUP] with the invite link of the group chat."""

    # Inline-Keyboard für den Zurück-Button
    back_keyboard = [[InlineKeyboardButton("⬅️ BACK", callback_data="back")]]
    back_markup = InlineKeyboardMarkup(back_keyboard)

    # Senden der Anweisungsnachricht mit dem Zurück-Button
    query.edit_message_text(text=instructions_message, reply_markup=back_markup)
    
    # Setzen des Bots in den INSTRUCTIONS-Zustand
    context.user_data["state"] = INSTRUCTIONS

def back(update, context):
    query = update.callback_query
    query.answer()
    
    if context.user_data["state"] == INSTRUCTIONS:
        # Zurück zur Willkommensnachricht mit den Optionen
        start(update, context)

def button(update, context):
    query = update.callback_query
    query.answer()

    if query.data == "instructions":
        instructions(update, context)
    elif query.data == "back":
        start(update, context)
    else:
        query.edit_message_text(text=f"Du hast {query.data} ausgewählt")

def main():
    updater = Updater(token=TOKEN, use_context=True)
    dp = updater.dispatcher

    # Definieren Sie den /start-Handler
    dp.add_handler(CommandHandler("start", start))

    # Definieren Sie den Callback-Handler für die Inline-Keyboards
    dp.add_handler(CallbackQueryHandler(button))

    updater.start_polling()
    updater.idle()

if __name__ == "__main__":
    main()

报错信息:

No error handlers are registered, logging exception.
Traceback (most recent call last):
  File "/Users/philippalbrecht/.pyenv/versions/3.11.1/lib/python3.11/site-packages/telegram/ext/dispatcher.py", line 340, in process_update
    handler.handle_update(update, self, check, context)
  File "/Users/philippalbrecht/.pyenv/versions/3.11.1/lib/python3.11/site-packages/telegram/ext/handler.py", line 119, in handle_update
    return self.callback(update, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/philippalbrecht/Desktop/Telegram/Telegram_escrow", line 101, in button
    back(update, context)
  File "/Users/philippalbrecht/Desktop/Telegram/Telegram_escrow", line 92, in back
    start(update, context)
  File "/Users/philippalbrecht/Desktop/Telegram/Telegram_escrow", line 11, in start
    user = update.message.from_user
           ^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'from_user'

错误原因

点击BACK按钮触发的是回调查询(CallbackQuery),此时update.message为None——回调类型的更新不包含message对象,只有callback_query。而你直接调用依赖update.message的start函数,自然会触发属性不存在的错误。

修复方案

单独编写处理返回逻辑的函数,通过callback_query.edit_message_text直接修改当前消息内容为欢迎界面,避免调用依赖命令消息的start函数。同时提取重复内容为全局常量,减少代码冗余。

修改后的完整代码

from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler

# 你的Bot Token
TOKEN = "Bot_Token"

# 状态常量
START, INSTRUCTIONS = range(2)

# 复用欢迎消息和键盘定义,避免重复代码
WELCOME_MESSAGE = """⚜️ **PayEscrowBOT** ⚜️ v.1.1
#1 Automated Telegram Escrow Service

Welcome to **PayEscrowBOT**™️. This bot provides a safe escrow service for your business on Telegram. Never get ripped again, The bot secures your funds throughout the deal. If you have any issues, kindly type /contact and an arbitrator will join the group chat within 24 hours.

🎟 **ESCROW FEE:**
5% if over $100
5$ if under $100

☑️ **DEALS DONE:** 1200
🧑‍⚖️ **DISPUTES HANDLED:** 280

💬 **Declare the seller or buyer with** /seller or /buyer [BTC/LTC ADDRESS] (**Your BTC/LTC address =** [BTC/LTC ADDRESS])

💡 Type /menu **to summon a menu with all bot's features**"""

WELCOME_KEYBOARD = InlineKeyboardMarkup([
    [InlineKeyboardButton("❓ WHAT IS ESCROW", callback_data="what_is_escrow"),
     InlineKeyboardButton("ℹ️ INSTRUCTIONS", callback_data="instructions")]
])

def start(update, context):
    # 处理/start命令,此时update包含message对象
    user = update.message.from_user
    
    # 发送欢迎消息
    update.message.reply_markdown(WELCOME_MESSAGE, reply_markup=WELCOME_KEYBOARD)
    
    # 设置状态为START
    context.user_data["state"] = START

def instructions(update, context):
    query = update.callback_query
    query.answer()
    
    instructions_message = """💬 INSTRUCTIONS

1️⃣ Add the bot to a new Group Chat with buyer + seller

2️⃣ Declare the seller with /seller [BTC/LTC ADDRESS]
        Replace [BTC/LTC ADDRESS] with receive wallet

3️⃣ Declare the buyer with /buyer [BTC/LTC ADDRESS]
        Replace [BTC/LTC ADDRESS] with refund wallet

4️⃣ Generate a wallet with /generate

5️⃣ Buyer sends agreed amount to generated BTC wallet

6️⃣ Check balance with /balance

7️⃣ Seller releases product / service to buyer when balance is confirmed

8️⃣ When buyer is satisfied, release payment with /release
    The seller can also /refund the buyer.

9️⃣ If any issues, contact arbitrator with /contact

❗️ For video example, type /video

☎️ If you need help, type /contact [INVITE LINK TO GROUP]
Replace [INVITE LINK TO GROUP] with the invite link of the group chat."""

    back_keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("⬅️ BACK", callback_data="back")]])
    
    # 修改当前消息为说明内容
    query.edit_message_text(text=instructions_message, reply_markup=back_keyboard)
    
    context.user_data["state"] = INSTRUCTIONS

def back_to_welcome(update, context):
    query = update.callback_query
    query.answer()
    
    # 修改当前消息为欢迎内容和对应键盘
    query.edit_message_text(text=WELCOME_MESSAGE, reply_markup=WELCOME_KEYBOARD, parse_mode='Markdown')
    
    context.user_data["state"] = START

def button(update, context):
    query = update.callback_query
    query.answer()

    if query.data == "instructions":
        instructions(update, context)
    elif query.data == "back":
        back_to_welcome(update, context)
    else:
        query.edit_message_text(text=f"你选择了 {query.data}")

def main():
    updater = Updater(token=TOKEN, use_context=True)
    dp = updater.dispatcher

    dp.add_handler(CommandHandler("start", start))
    dp.add_handler(CallbackQueryHandler(button))

    updater.start_polling()
    updater.idle()

if __name__ == "__main__":
    main()

核心修改点

  1. 将欢迎消息和键盘提取为全局常量,避免重复编写;
  2. 新增back_to_welcome函数,专门处理BACK按钮回调,通过query.edit_message_text直接修改当前消息,不依赖update.message;
  3. 在button函数中,点击back时调用back_to_welcome而非start函数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:03:10