Files
Xiaji-go/internal/bot/telegram.go
openclaw 0c1a4f06f7 feat: xiaji-go v1.0.0 - 智能记账机器人
- Telegram Bot + QQ Bot (WebSocket) 双平台支持
- 150+ 预设分类关键词,jieba 智能分词
- Web 管理后台(记录查看/删除/CSV导出)
- 金额精确存储(分/int64)
- 版本信息嵌入(编译时注入)
- Docker 支持
- 优雅关闭(context + signal)
2026-02-15 06:40:04 +08:00

123 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package bot
import (
"context"
"fmt"
"log"
"strings"
"time"
"xiaji-go/internal/service"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type TGBot struct {
api *tgbotapi.BotAPI
finance *service.FinanceService
}
func NewTGBot(token string, finance *service.FinanceService) (*TGBot, error) {
bot, err := tgbotapi.NewBotAPI(token)
if err != nil {
return nil, err
}
return &TGBot{api: bot, finance: finance}, nil
}
func (b *TGBot) Start(ctx context.Context) {
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates := b.api.GetUpdatesChan(u)
log.Println("🚀 Telegram Bot 已启动")
for {
select {
case <-ctx.Done():
log.Println("⏳ Telegram Bot 正在停止...")
b.api.StopReceivingUpdates()
return
case update, ok := <-updates:
if !ok {
return
}
if update.Message == nil || update.Message.Text == "" {
continue
}
b.handleMessage(update.Message)
}
}
}
func (b *TGBot) handleMessage(msg *tgbotapi.Message) {
text := msg.Text
chatID := msg.Chat.ID
userID := msg.From.ID
var reply string
switch {
case text == "/start":
reply = "🦞 欢迎使用虾记记账!\n\n直接发送消费描述即可记账例如\n• 午饭 25元\n• 打车 ¥30\n• 买咖啡15块\n\n命令\n/list - 查看最近记录\n/help - 帮助"
case text == "/help":
reply = "📖 使用说明:\n\n直接发送带金额的文本即可自动记账。\n系统会自动识别金额和消费分类。\n\n支持格式\n• 午饭 25元\n• ¥30 打车\n• 买水果15块\n\n命令\n/list - 最近10条记录\n/today - 今日汇总\n/start - 欢迎信息"
case text == "/today":
today := time.Now().Format("2006-01-02")
items, err := b.finance.GetTransactionsByDate(userID, today)
if err != nil {
reply = "❌ 查询失败"
} else if len(items) == 0 {
reply = fmt.Sprintf("📭 %s 暂无消费记录", today)
} else {
var sb strings.Builder
var total int64
sb.WriteString(fmt.Sprintf("📊 今日(%s消费\n\n", today))
for _, item := range items {
sb.WriteString(fmt.Sprintf("• %s%.2f元\n", item.Category, item.AmountYuan()))
total += item.Amount
}
sb.WriteString(fmt.Sprintf("\n💰 共 %d 笔,合计 %.2f 元", len(items), float64(total)/100.0))
reply = sb.String()
}
case text == "/list":
items, err := b.finance.GetTransactions(userID, 10)
if err != nil {
reply = "❌ 查询失败"
} else if len(items) == 0 {
reply = "📭 暂无记录"
} else {
var sb strings.Builder
sb.WriteString("📋 最近记录:\n\n")
for _, item := range items {
sb.WriteString(fmt.Sprintf("• [%s] %s%.2f元\n", item.Date, item.Category, item.AmountYuan()))
}
reply = sb.String()
}
case strings.HasPrefix(text, "/"):
reply = "❓ 未知命令,输入 /help 查看帮助"
default:
// 记账逻辑
amount, category, err := b.finance.AddTransaction(userID, text)
if err != nil {
reply = "❌ 记账失败,请稍后重试"
log.Printf("记账失败 user=%d: %v", userID, err)
} else if amount == 0 {
reply = "📍 没看到金额,这笔花了多少钱?"
} else {
amountYuan := float64(amount) / 100.0
reply = fmt.Sprintf("✅ 已记入【%s】%.2f元\n📝 备注:%s", category, amountYuan, text)
}
}
m := tgbotapi.NewMessage(chatID, reply)
if _, err := b.api.Send(m); err != nil {
log.Printf("发送消息失败 chat=%d: %v", chatID, err)
}
}