如何用C#实现Telegram Bot通过Webhook处理问答(含/userinfo示例)
用C#开发Telegram Bot(Webhook模式)实现问答流程
我来一步步带你搞定这个需求——用C#做一个Telegram Bot,通过Webhook接收消息,当用户发/userinfo时,依次询问名字、姓氏,最后汇总信息。
准备工作
先搞定几个前置条件:
- 去Telegram的BotFather那里创建一个Bot,拿到专属的
API Token(这个是核心,别随便泄露)。 - 要有一个公网可访问的HTTPS地址,因为Telegram的Webhook只支持HTTPS。本地开发的话,可以用
ngrok把本地端口转发成公网HTTPS地址,比如执行ngrok http 5000就能生成临时地址。 - 创建一个ASP.NET Core Web API项目(Webhook需要一个HTTP端点来接收Telegram的请求)。
安装依赖
在你的项目里安装官方的Telegram Bot NuGet包:
Install-Package Telegram.Bot
或者用.NET CLI命令:
dotnet add package Telegram.Bot
配置Webhook和Bot客户端
首先在appsettings.json里添加你的Bot Token:
{ "TelegramBotToken": "你的BotToken这里填" }
然后在Program.cs里配置Telegram Bot客户端,把它注册成服务:
var builder = WebApplication.CreateBuilder(args); // 注册Telegram Bot客户端 var botToken = builder.Configuration["TelegramBotToken"]; builder.Services.AddHttpClient<TelegramBotClient>(client => { client.BaseAddress = new Uri($"https://api.telegram.org/bot{botToken}/"); }); builder.Services.AddControllers(); var app = builder.Build(); // 启动后自动设置Webhook(替换成你的公网HTTPS地址+webhook端点) using (var scope = app.Services.CreateScope()) { var botClient = scope.ServiceProvider.GetRequiredService<TelegramBotClient>(); var webhookUrl = new Uri("https://你的ngrok地址/webhook"); await botClient.SetWebhookAsync(webhookUrl); } app.MapControllers(); app.Run();
核心:状态管理(跟踪用户问答步骤)
要记住每个用户当前处于哪个提问环节,咱们得用线程安全的存储结构。这里用ConcurrentDictionary来存用户的状态和输入的数据:
先定义一个枚举表示用户当前的步骤:
public enum UserInfoStep { Start, // 初始状态 WaitingForFirstName, // 等待用户输入名字 WaitingForLastName // 等待用户输入姓氏 }
再定义一个存储用户信息的模型:
public class UserInfo { public string FirstName { get; set; } public string LastName { get; set; } }
然后在控制器里声明两个静态的ConcurrentDictionary:
private static readonly ConcurrentDictionary<long, UserInfoStep> _userSteps = new(); private static readonly ConcurrentDictionary<long, UserInfo> _userData = new();
实现Webhook端点和消息处理
创建一个TelegramBotController,添加Webhook的POST端点,处理Telegram发来的消息更新:
[ApiController] [Route("[controller]")] public class TelegramBotController : ControllerBase { private readonly TelegramBotClient _botClient; public TelegramBotController(TelegramBotClient botClient) { _botClient = botClient; } [HttpPost("webhook")] public async Task<IActionResult> Post([FromBody] Update update) { if (update.Message == null) return Ok(); var chatId = update.Message.Chat.Id; var messageText = update.Message.Text; // 获取用户当前状态,默认是初始状态 _userSteps.TryGetValue(chatId, out var currentStep); switch (currentStep) { case UserInfoStep.Start: await HandleStartCommand(chatId, messageText); break; case UserInfoStep.WaitingForFirstName: await HandleFirstNameInput(chatId, messageText); break; case UserInfoStep.WaitingForLastName: await HandleLastNameInput(chatId, messageText); break; } return Ok(); } private async Task HandleStartCommand(long chatId, string messageText) { if (messageText.Equals("/userinfo", StringComparison.OrdinalIgnoreCase)) { // 发送第一个问题 await _botClient.SendTextMessageAsync(chatId, "你的名字?"); // 更新用户状态为等待名字输入 _userSteps[chatId] = UserInfoStep.WaitingForFirstName; // 初始化用户数据存储 _userData[chatId] = new UserInfo(); } else { // 提示用户输入正确命令 await _botClient.SendTextMessageAsync(chatId, "请发送 /userinfo 命令开始填写信息~"); } } private async Task HandleFirstNameInput(long chatId, string firstName) { // 保存用户输入的名字 _userData[chatId].FirstName = firstName; // 发送第二个问题 await _botClient.SendTextMessageAsync(chatId, "你的姓氏?"); // 更新状态为等待姓氏输入 _userSteps[chatId] = UserInfoStep.WaitingForLastName; } private async Task HandleLastNameInput(long chatId, string lastName) { // 保存用户输入的姓氏 _userData[chatId].LastName = lastName; // 汇总信息回复用户 var userInfo = _userData[chatId]; await _botClient.SendTextMessageAsync(chatId, $"已收到你的信息:\n名字:{userInfo.FirstName}\n姓氏:{userInfo.LastName}"); // 重置用户状态和数据,准备下一次交互 _userSteps.TryRemove(chatId, out _); _userData.TryRemove(chatId, out _); } }
测试流程
- 启动你的ASP.NET Core项目,默认端口一般是5000。
- 用ngrok转发端口:执行
ngrok http 5000,拿到类似https://xxxx-xx-xx-xx-xx.ngrok.io的临时公网地址。 - 设置Webhook:在浏览器里访问
https://api.telegram.org/bot<你的BotToken>/setWebhook?url=https://xxxx-xx-xx-xx-xx.ngrok.io/telegrambot/webhook(替换成你的Token和ngrok地址)。 - 打开Telegram,给你的Bot发
/userinfo,然后按照提示回复名字和姓氏,就能看到汇总信息啦!
注意事项
- 线程安全:因为Web API是多线程的,必须用
ConcurrentDictionary来存用户状态和数据,避免并发冲突。 - 持久化:如果Bot重启后不想丢失用户的未完成状态,可以把状态和数据存到数据库(比如SQLite、Redis),而不是内存里的字典。
- 异常处理:可以在代码里添加try-catch块,处理Telegram API调用失败的情况,比如网络波动问题。
内容的提问来源于stack exchange,提问作者probaumak _
相关产品推荐
相关产品推荐

