如何在Nestjs中结合@nestjs/schedule的Cron正常使用Telegraf bot?
错误原因
你现在的报错是因为同一个方法同时绑定了@On('message')和@Cron两个装饰器,两个触发逻辑的入参不匹配:
- 当用户给Bot发消息触发
@On('message')时,Telegraf会自动传入包含当前会话信息的ctx参数,可以正常调用ctx.reply - 当定时任务触发
@Cron时,调度器不会给方法传入任何参数,此时ctx为undefined,调用ctx.reply就会抛出你看到的报错
调整方案
你需要把消息监听逻辑和定时推送逻辑拆分,同时主动注入Bot实例来完成定时推送,还要提前存储需要接收消息的聊天ID,具体修改如下:
1. 调整bot.service代码
import { Cron } from '@nestjs/schedule'; import { InjectRepository } from '@nestjs/typeorm'; import { On, Update, InjectBot } from 'nestjs-telegraf'; import { Context, Telegraf } from 'telegraf'; import { Repository } from 'typeorm'; import { TelegramBot } from './entities/bot.entity'; @Update() export class BotService { constructor( @InjectRepository(TelegramBot) private telegramRepo: Repository<TelegramBot>, // 注入Telegraf Bot实例 @InjectBot() private bot: Telegraf<Context> ) {} // 单独处理用户消息逻辑:存储用户的聊天ID到数据库 @On('message') async handleMessage(ctx: Context): Promise<void> { const chatId = ctx.chat.id; // 避免重复存储相同的聊天ID const existed = await this.telegramRepo.findOne({ where: { chatId } }); if (!existed) { await this.telegramRepo.save(this.telegramRepo.create({ chatId })); } await ctx.reply('Hello there'); } // 单独处理定时推送逻辑 @Cron('*/30 * * * * *') async sendScheduledMessage(): Promise<void> { // 从数据库查询所有需要推送的聊天ID const chatList = await this.telegramRepo.find(); for (const chat of chatList) { // 主动调用bot的sendMessage方法推送消息 await this.bot.telegram.sendMessage(chat.chatId, 'Hello there'); } } }
2. 确保TelegramBot实体包含chatId字段
你的TelegramBot实体需要至少有存储聊天ID的字段,示例如下:
// src/bot/entities/bot.entity.ts import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class TelegramBot { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) chatId: number; }
补充说明
如果你只需要给固定的单个聊天推送消息,不需要存储多用户的聊天ID,可以直接把目标聊天ID写到环境变量里,定时任务里直接读取这个ID推送即可,不需要操作数据库。
内容的提问来源于stack exchange,提问作者Roman Savka
相关产品推荐
相关产品推荐

