package handlers import ( "fmt" "net/http" "strconv" "time" "github.com/ctwj/urldb/db/converter" "github.com/ctwj/urldb/db/dto" "github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/repo" "github.com/ctwj/urldb/services" "github.com/ctwj/urldb/utils" "github.com/gin-gonic/gin" ) // TelegramHandler Telegram 处理器 type TelegramHandler struct { telegramChannelRepo repo.TelegramChannelRepository systemConfigRepo repo.SystemConfigRepository telegramBotService services.TelegramBotService } // NewTelegramHandler 创建 Telegram 处理器 func NewTelegramHandler( telegramChannelRepo repo.TelegramChannelRepository, systemConfigRepo repo.SystemConfigRepository, telegramBotService services.TelegramBotService, ) *TelegramHandler { return &TelegramHandler{ telegramChannelRepo: telegramChannelRepo, systemConfigRepo: systemConfigRepo, telegramBotService: telegramBotService, } } // GetBotConfig 获取机器人配置 func (h *TelegramHandler) GetBotConfig(c *gin.Context) { configs, err := h.systemConfigRepo.GetOrCreateDefault() if err != nil { ErrorResponse(c, "获取配置失败", http.StatusInternalServerError) return } botConfig := converter.SystemConfigToTelegramBotConfig(configs) SuccessResponse(c, botConfig) } // UpdateBotConfig 更新机器人配置 func (h *TelegramHandler) UpdateBotConfig(c *gin.Context) { var req dto.TelegramBotConfigRequest if err := c.ShouldBindJSON(&req); err != nil { ErrorResponse(c, "请求参数错误", http.StatusBadRequest) return } // 转换为系统配置实体 configs := converter.TelegramBotConfigRequestToSystemConfigs(req) // 保存配置 if len(configs) > 0 { err := h.systemConfigRepo.UpsertConfigs(configs) if err != nil { ErrorResponse(c, "保存配置失败", http.StatusInternalServerError) return } } // 重新加载配置缓存 if err := h.systemConfigRepo.SafeRefreshConfigCache(); err != nil { ErrorResponse(c, "刷新配置缓存失败", http.StatusInternalServerError) return } // 重新加载机器人服务配置 if err := h.telegramBotService.ReloadConfig(); err != nil { ErrorResponse(c, "重新加载机器人配置失败", http.StatusInternalServerError) return } // 配置更新完成后,尝试启动机器人(如果未运行且配置有效) if startErr := h.telegramBotService.Start(); startErr != nil { utils.Warn("[TELEGRAM:HANDLER] 配置更新后尝试启动机器人失败: %v", startErr) // 启动失败不影响配置保存,只记录警告 } // 返回成功 SuccessResponse(c, map[string]interface{}{ "success": true, "message": "配置更新成功,机器人已尝试启动", }) } // ValidateApiKey 校验 API Key func (h *TelegramHandler) ValidateApiKey(c *gin.Context) { var req dto.ValidateTelegramApiKeyRequest if err := c.ShouldBindJSON(&req); err != nil { ErrorResponse(c, "请求参数错误", http.StatusBadRequest) return } // 如果请求中包含代理配置,临时更新服务配置进行校验 if req.ProxyEnabled { // 这里只是为了校验,我们不应该修改全局配置 // 传递代理配置给服务进行校验 valid, botInfo, err := h.telegramBotService.ValidateApiKeyWithProxy( req.ApiKey, req.ProxyEnabled, req.ProxyType, req.ProxyHost, req.ProxyPort, req.ProxyUsername, req.ProxyPassword, ) if err != nil { ErrorResponse(c, "校验失败: "+err.Error(), http.StatusInternalServerError) return } response := dto.ValidateTelegramApiKeyResponse{ Valid: valid, BotInfo: botInfo, } if !valid { response.Error = "无效的 API Key" } SuccessResponse(c, response) } else { // 使用默认配置校验 valid, botInfo, err := h.telegramBotService.ValidateApiKey(req.ApiKey) if err != nil { ErrorResponse(c, "校验失败: "+err.Error(), http.StatusInternalServerError) return } response := dto.ValidateTelegramApiKeyResponse{ Valid: valid, BotInfo: botInfo, } if !valid { response.Error = "无效的 API Key" } SuccessResponse(c, response) } } // GetChannels 获取频道列表 func (h *TelegramHandler) GetChannels(c *gin.Context) { channels, err := h.telegramChannelRepo.FindAll() if err != nil { ErrorResponse(c, "获取频道列表失败", http.StatusInternalServerError) return } channelResponses := converter.TelegramChannelsToResponse(channels) SuccessResponse(c, channelResponses) } // CreateChannel 创建频道 func (h *TelegramHandler) CreateChannel(c *gin.Context) { var req dto.TelegramChannelRequest if err := c.ShouldBindJSON(&req); err != nil { ErrorResponse(c, "请求参数错误", http.StatusBadRequest) return } // 检查频道是否已存在 existing, err := h.telegramChannelRepo.FindByChatID(req.ChatID) if err == nil && existing != nil { ErrorResponse(c, "该频道/群组已注册", http.StatusBadRequest) return } // 获取当前用户信息作为注册者 username := getCurrentUsername(c) // 需要实现获取用户信息的方法 channel := converter.RequestToTelegramChannel(req, username) if err := h.telegramChannelRepo.Create(&channel); err != nil { ErrorResponse(c, "创建频道失败", http.StatusInternalServerError) return } response := converter.TelegramChannelToResponse(channel) SuccessResponse(c, response) } // UpdateChannel 更新频道 func (h *TelegramHandler) UpdateChannel(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { ErrorResponse(c, "无效的ID", http.StatusBadRequest) return } var req dto.TelegramChannelUpdateRequest if err := c.ShouldBindJSON(&req); err != nil { ErrorResponse(c, "请求参数错误", http.StatusBadRequest) return } utils.Info("[TELEGRAM:HANDLER] 接收到频道更新请求: ID=%s, ChatName=%s, PushStartTime=%s, PushEndTime=%s, ResourceStrategy=%s, TimeLimit=%s", idStr, req.ChatName, req.PushStartTime, req.PushEndTime, req.ResourceStrategy, req.TimeLimit) // 查找现有频道 channel, err := h.telegramChannelRepo.FindByID(uint(id)) if err != nil { ErrorResponse(c, "频道不存在", http.StatusNotFound) return } // 保存前的日志 utils.Info("[TELEGRAM:HANDLER] 更新前频道状态: PushStartTime=%s, PushEndTime=%s, ResourceStrategy=%s, TimeLimit=%s", channel.PushStartTime, channel.PushEndTime, channel.ResourceStrategy, channel.TimeLimit) // 如果前端传递了ChatID,验证它是否与现有频道匹配 if req.ChatID != 0 && req.ChatID != channel.ChatID { ErrorResponse(c, "ChatID不匹配,无法更新此频道", http.StatusBadRequest) return } // 更新频道信息 channel.ChatName = req.ChatName channel.ChatType = req.ChatType channel.PushEnabled = req.PushEnabled channel.PushFrequency = req.PushFrequency channel.PushStartTime = req.PushStartTime channel.PushEndTime = req.PushEndTime channel.ContentCategories = req.ContentCategories channel.ContentTags = req.ContentTags channel.IsActive = req.IsActive channel.ResourceStrategy = req.ResourceStrategy channel.TimeLimit = req.TimeLimit if err := h.telegramChannelRepo.Update(channel); err != nil { ErrorResponse(c, "更新频道失败", http.StatusInternalServerError) return } // 保存后的日志 utils.Info("[TELEGRAM:HANDLER] 更新后频道状态: PushStartTime=%s, PushEndTime=%s, ResourceStrategy=%s, TimeLimit=%s", channel.PushStartTime, channel.PushEndTime, channel.ResourceStrategy, channel.TimeLimit) response := converter.TelegramChannelToResponse(*channel) SuccessResponse(c, response) } // DeleteChannel 删除频道 func (h *TelegramHandler) DeleteChannel(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { ErrorResponse(c, "无效的ID", http.StatusBadRequest) return } // 检查频道是否存在 channel, err := h.telegramChannelRepo.FindByID(uint(id)) if err != nil { ErrorResponse(c, "频道不存在", http.StatusNotFound) return } // 删除频道 if err := h.telegramChannelRepo.Delete(uint(id)); err != nil { ErrorResponse(c, "删除频道失败", http.StatusInternalServerError) return } SuccessResponse(c, map[string]interface{}{ "success": true, "message": "频道 " + channel.ChatName + " 已成功移除", }) } // RegisterChannelByCommand 通过命令注册频道(供内部调用) func (h *TelegramHandler) RegisterChannelByCommand(chatID int64, chatName, chatType string) error { // 检查是否已注册 existing, err := h.telegramChannelRepo.FindByChatID(chatID) if err == nil && existing != nil { // 已存在,返回成功 return nil } // 创建新的频道记录 channel := entity.TelegramChannel{ ChatID: chatID, ChatName: chatName, ChatType: chatType, PushEnabled: true, PushFrequency: 15, // 默认15分钟 PushStartTime: "08:30", // 默认开始时间8:30 PushEndTime: "11:30", // 默认结束时间11:30 IsActive: true, RegisteredBy: "bot_command", RegisteredAt: time.Now(), ResourceStrategy: "random", // 默认纯随机 TimeLimit: "none", // 默认无限制 } return h.telegramChannelRepo.Create(&channel) } // HandleWebhook 处理 Telegram Webhook func (h *TelegramHandler) HandleWebhook(c *gin.Context) { // 将消息交给 bot 服务处理 // 这里可以根据需要添加身份验证 h.telegramBotService.HandleWebhookUpdate(c) } // GetBotStatus 获取机器人状态 func (h *TelegramHandler) GetBotStatus(c *gin.Context) { // 获取机器人运行时状态 runtimeStatus := h.telegramBotService.GetRuntimeStatus() // 获取配置状态 configs, err := h.systemConfigRepo.GetOrCreateDefault() if err != nil { ErrorResponse(c, "获取配置失败", http.StatusInternalServerError) return } // 解析配置状态 configStatus := map[string]interface{}{ "enabled": false, "auto_reply_enabled": false, "api_key_configured": false, } for _, config := range configs { switch config.Key { case "telegram_bot_enabled": configStatus["enabled"] = config.Value == "true" case "telegram_auto_reply_enabled": configStatus["auto_reply_enabled"] = config.Value == "true" case "telegram_bot_api_key": configStatus["api_key_configured"] = config.Value != "" } } // 合并状态信息 status := map[string]interface{}{ "config": configStatus, "runtime": runtimeStatus, "overall_status": runtimeStatus["is_running"].(bool), "status_text": func() string { if runtimeStatus["is_running"].(bool) { return "运行中" } else if configStatus["enabled"].(bool) { return "已启用但未运行" } else { return "已停止" } }(), } SuccessResponse(c, status) } // TestBotMessage 测试机器人消息发送 func (h *TelegramHandler) TestBotMessage(c *gin.Context) { var req struct { ChatID int64 `json:"chat_id" binding:"required"` Text string `json:"text" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { ErrorResponse(c, "请求参数错误", http.StatusBadRequest) return } err := h.telegramBotService.SendMessage(req.ChatID, req.Text, "") if err != nil { ErrorResponse(c, "发送消息失败: "+err.Error(), http.StatusInternalServerError) return } SuccessResponse(c, map[string]interface{}{ "success": true, "message": "测试消息已发送", }) } // ReloadBotConfig 重新加载机器人配置 func (h *TelegramHandler) ReloadBotConfig(c *gin.Context) { // 这里可以实现重新加载配置的逻辑 // 目前通过重启服务来实现配置重新加载 SuccessResponse(c, map[string]interface{}{ "success": true, "message": "请重启服务器以重新加载配置", "note": "当前版本需要重启服务器才能重新加载机器人配置", }) } // DebugBotConnection 调试机器人连接 func (h *TelegramHandler) DebugBotConnection(c *gin.Context) { // 获取机器人状态信息用于调试 botUsername := h.telegramBotService.GetBotUsername() debugInfo := map[string]interface{}{ "bot_username": botUsername, "is_running": botUsername != "", "timestamp": "2024-01-01T12:00:00Z", // 当前时间 "debugging_enabled": true, "expected_logs": []string{ "[TELEGRAM:SERVICE] Telegram Bot (@username) 已启动", "[TELEGRAM:MESSAGE] 开始监听 Telegram 消息更新...", "[TELEGRAM:MESSAGE] 消息监听循环已启动,等待消息...", "[TELEGRAM:MESSAGE] 收到消息: ChatID=xxx, Text='/register'", "[TELEGRAM:MESSAGE] 处理 /register 命令 from ChatID=xxx", }, "troubleshooting_steps": []string{ "1. 检查服务器日志中是否有 TELEGRAM 相关日志", "2. 确认机器人已添加到群组并设为管理员", "3. 验证 API Key 是否正确", "4. 检查自动回复是否已启用", "5. 重启服务器重新加载配置", }, } SuccessResponse(c, debugInfo) } // GetTelegramLogs 获取Telegram相关的日志 func (h *TelegramHandler) GetTelegramLogs(c *gin.Context) { // 解析查询参数 hoursStr := c.DefaultQuery("hours", "24") limitStr := c.DefaultQuery("limit", "100") hours, err := strconv.Atoi(hoursStr) if err != nil || hours <= 0 || hours > 720 { // 最多30天 hours = 24 } limit, err := strconv.Atoi(limitStr) if err != nil || limit <= 0 || limit > 1000 { limit = 100 } // 计算时间范围 endTime := time.Now() startTime := endTime.Add(-time.Duration(hours) * time.Hour) // 获取日志 logs, err := utils.GetTelegramLogs(&startTime, &endTime, limit) if err != nil { ErrorResponse(c, "获取日志失败: "+err.Error(), http.StatusInternalServerError) return } SuccessResponse(c, map[string]interface{}{ "logs": logs, "count": len(logs), "hours": hours, "limit": limit, "start": startTime.Format("2006-01-02 15:04:05"), "end": endTime.Format("2006-01-02 15:04:05"), }) } // GetTelegramLogStats 获取Telegram日志统计信息 func (h *TelegramHandler) GetTelegramLogStats(c *gin.Context) { hoursStr := c.DefaultQuery("hours", "24") hours, err := strconv.Atoi(hoursStr) if err != nil || hours <= 0 || hours > 720 { hours = 24 } stats, err := utils.GetTelegramLogStats(hours) if err != nil { ErrorResponse(c, "获取统计信息失败: "+err.Error(), http.StatusInternalServerError) return } SuccessResponse(c, map[string]interface{}{ "stats": stats, "hours": hours, }) } // ClearTelegramLogs 清理旧的Telegram日志 func (h *TelegramHandler) ClearTelegramLogs(c *gin.Context) { daysStr := c.DefaultQuery("days", "30") days, err := strconv.Atoi(daysStr) if err != nil || days <= 0 || days > 365 { days = 30 } err = utils.ClearOldTelegramLogs(days) if err != nil { ErrorResponse(c, "清理日志失败: "+err.Error(), http.StatusInternalServerError) return } SuccessResponse(c, map[string]interface{}{ "message": fmt.Sprintf("已清理 %d 天前的日志文件", days), "days": days, }) } // getCurrentUsername 获取当前用户名(临时实现) func getCurrentUsername(c *gin.Context) string { // 这里应该从中间件中获取用户信息 // 暂时返回默认值 return "admin" }