mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 19:37:33 +08:00
update: tg
This commit is contained in:
@@ -17,6 +17,8 @@ func TelegramChannelToResponse(channel entity.TelegramChannel) dto.TelegramChann
|
|||||||
ChatType: channel.ChatType,
|
ChatType: channel.ChatType,
|
||||||
PushEnabled: channel.PushEnabled,
|
PushEnabled: channel.PushEnabled,
|
||||||
PushFrequency: channel.PushFrequency,
|
PushFrequency: channel.PushFrequency,
|
||||||
|
PushStartTime: channel.PushStartTime,
|
||||||
|
PushEndTime: channel.PushEndTime,
|
||||||
ContentCategories: channel.ContentCategories,
|
ContentCategories: channel.ContentCategories,
|
||||||
ContentTags: channel.ContentTags,
|
ContentTags: channel.ContentTags,
|
||||||
IsActive: channel.IsActive,
|
IsActive: channel.IsActive,
|
||||||
@@ -43,6 +45,8 @@ func RequestToTelegramChannel(req dto.TelegramChannelRequest, registeredBy strin
|
|||||||
ChatType: req.ChatType,
|
ChatType: req.ChatType,
|
||||||
PushEnabled: req.PushEnabled,
|
PushEnabled: req.PushEnabled,
|
||||||
PushFrequency: req.PushFrequency,
|
PushFrequency: req.PushFrequency,
|
||||||
|
PushStartTime: req.PushStartTime,
|
||||||
|
PushEndTime: req.PushEndTime,
|
||||||
ContentCategories: req.ContentCategories,
|
ContentCategories: req.ContentCategories,
|
||||||
ContentTags: req.ContentTags,
|
ContentTags: req.ContentTags,
|
||||||
IsActive: req.IsActive,
|
IsActive: req.IsActive,
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ type TelegramChannelRequest struct {
|
|||||||
ChatType string `json:"chat_type" binding:"required"` // channel 或 group
|
ChatType string `json:"chat_type" binding:"required"` // channel 或 group
|
||||||
PushEnabled bool `json:"push_enabled"`
|
PushEnabled bool `json:"push_enabled"`
|
||||||
PushFrequency int `json:"push_frequency"`
|
PushFrequency int `json:"push_frequency"`
|
||||||
|
PushStartTime string `json:"push_start_time"`
|
||||||
|
PushEndTime string `json:"push_end_time"`
|
||||||
ContentCategories string `json:"content_categories"`
|
ContentCategories string `json:"content_categories"`
|
||||||
ContentTags string `json:"content_tags"`
|
ContentTags string `json:"content_tags"`
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
@@ -22,6 +24,8 @@ type TelegramChannelResponse struct {
|
|||||||
ChatType string `json:"chat_type"`
|
ChatType string `json:"chat_type"`
|
||||||
PushEnabled bool `json:"push_enabled"`
|
PushEnabled bool `json:"push_enabled"`
|
||||||
PushFrequency int `json:"push_frequency"`
|
PushFrequency int `json:"push_frequency"`
|
||||||
|
PushStartTime string `json:"push_start_time"`
|
||||||
|
PushEndTime string `json:"push_end_time"`
|
||||||
ContentCategories string `json:"content_categories"`
|
ContentCategories string `json:"content_categories"`
|
||||||
ContentTags string `json:"content_tags"`
|
ContentTags string `json:"content_tags"`
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ type TelegramChannel struct {
|
|||||||
// 推送配置
|
// 推送配置
|
||||||
PushEnabled bool `json:"push_enabled" gorm:"default:true;comment:是否启用推送"`
|
PushEnabled bool `json:"push_enabled" gorm:"default:true;comment:是否启用推送"`
|
||||||
PushFrequency int `json:"push_frequency" gorm:"default:24;comment:推送频率(小时)"`
|
PushFrequency int `json:"push_frequency" gorm:"default:24;comment:推送频率(小时)"`
|
||||||
|
PushStartTime string `json:"push_start_time" gorm:"size:10;comment:推送开始时间,格式HH:mm"`
|
||||||
|
PushEndTime string `json:"push_end_time" gorm:"size:10;comment:推送结束时间,格式HH:mm"`
|
||||||
ContentCategories string `json:"content_categories" gorm:"type:text;comment:推送的内容分类,用逗号分隔"`
|
ContentCategories string `json:"content_categories" gorm:"type:text;comment:推送的内容分类,用逗号分隔"`
|
||||||
ContentTags string `json:"content_tags" gorm:"type:text;comment:推送的标签,用逗号分隔"`
|
ContentTags string `json:"content_tags" gorm:"type:text;comment:推送的标签,用逗号分隔"`
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,29 @@ func (r *TelegramChannelRepositoryImpl) UpdateLastPushAt(id uint, lastPushAt tim
|
|||||||
func (r *TelegramChannelRepositoryImpl) FindDueForPush() ([]entity.TelegramChannel, error) {
|
func (r *TelegramChannelRepositoryImpl) FindDueForPush() ([]entity.TelegramChannel, error) {
|
||||||
var channels []entity.TelegramChannel
|
var channels []entity.TelegramChannel
|
||||||
// 查找活跃、启用推送的频道,且距离上次推送已超过推送频率小时的记录
|
// 查找活跃、启用推送的频道,且距离上次推送已超过推送频率小时的记录
|
||||||
err := r.db.Where("is_active = ? AND push_enabled = ? AND (last_push_at IS NULL OR last_push_at < DATE_SUB(NOW(), INTERVAL push_frequency HOUR))",
|
|
||||||
true, true).Find(&channels).Error
|
// 先获取所有活跃且启用推送的频道
|
||||||
return channels, err
|
err := r.db.Where("is_active = ? AND push_enabled = ?", true, true).Find(&channels).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在内存中过滤出需要推送的频道(更可靠的跨数据库方案)
|
||||||
|
var dueChannels []entity.TelegramChannel
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for _, channel := range channels {
|
||||||
|
// 如果从未推送过,或者距离上次推送已超过推送频率小时
|
||||||
|
if channel.LastPushAt == nil {
|
||||||
|
dueChannels = append(dueChannels, channel)
|
||||||
|
} else {
|
||||||
|
// 计算下次推送时间:上次推送时间 + 推送频率小时
|
||||||
|
nextPushTime := channel.LastPushAt.Add(time.Duration(channel.PushFrequency) * time.Hour)
|
||||||
|
if now.After(nextPushTime) {
|
||||||
|
dueChannels = append(dueChannels, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dueChannels, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,12 +74,11 @@ func (h *TelegramHandler) UpdateBotConfig(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 这里应该启动或停止 Telegram bot 服务
|
// 重新加载机器人服务配置
|
||||||
// if req.BotEnabled != nil && *req.BotEnabled {
|
if err := h.telegramBotService.ReloadConfig(); err != nil {
|
||||||
// go h.telegramBotService.Start()
|
ErrorResponse(c, "重新加载机器人配置失败", http.StatusInternalServerError)
|
||||||
// } else {
|
return
|
||||||
// go h.telegramBotService.Stop()
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// 返回成功
|
// 返回成功
|
||||||
SuccessResponse(c, map[string]interface{}{
|
SuccessResponse(c, map[string]interface{}{
|
||||||
@@ -182,6 +181,8 @@ func (h *TelegramHandler) UpdateChannel(c *gin.Context) {
|
|||||||
channel.ChatType = req.ChatType
|
channel.ChatType = req.ChatType
|
||||||
channel.PushEnabled = req.PushEnabled
|
channel.PushEnabled = req.PushEnabled
|
||||||
channel.PushFrequency = req.PushFrequency
|
channel.PushFrequency = req.PushFrequency
|
||||||
|
channel.PushStartTime = req.PushStartTime
|
||||||
|
channel.PushEndTime = req.PushEndTime
|
||||||
channel.ContentCategories = req.ContentCategories
|
channel.ContentCategories = req.ContentCategories
|
||||||
channel.ContentTags = req.ContentTags
|
channel.ContentTags = req.ContentTags
|
||||||
channel.IsActive = req.IsActive
|
channel.IsActive = req.IsActive
|
||||||
@@ -255,16 +256,48 @@ func (h *TelegramHandler) HandleWebhook(c *gin.Context) {
|
|||||||
|
|
||||||
// GetBotStatus 获取机器人状态
|
// GetBotStatus 获取机器人状态
|
||||||
func (h *TelegramHandler) GetBotStatus(c *gin.Context) {
|
func (h *TelegramHandler) GetBotStatus(c *gin.Context) {
|
||||||
// 这里可以返回机器人运行状态、最后活动时间等信息
|
// 获取机器人运行时状态
|
||||||
// 暂时返回基本状态信息
|
runtimeStatus := h.telegramBotService.GetRuntimeStatus()
|
||||||
|
|
||||||
botUsername := h.telegramBotService.GetBotUsername()
|
// 获取配置状态
|
||||||
|
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{}{
|
status := map[string]interface{}{
|
||||||
"bot_username": botUsername,
|
"config": configStatus,
|
||||||
"service_running": botUsername != "",
|
"runtime": runtimeStatus,
|
||||||
"webhook_mode": false, // 当前使用长轮询模式
|
"overall_status": runtimeStatus["is_running"].(bool),
|
||||||
"polling_mode": true,
|
"status_text": func() string {
|
||||||
|
if runtimeStatus["is_running"].(bool) {
|
||||||
|
return "运行中"
|
||||||
|
} else if configStatus["enabled"].(bool) {
|
||||||
|
return "已启用但未运行"
|
||||||
|
} else {
|
||||||
|
return "已停止"
|
||||||
|
}
|
||||||
|
}(),
|
||||||
}
|
}
|
||||||
|
|
||||||
SuccessResponse(c, status)
|
SuccessResponse(c, status)
|
||||||
@@ -306,6 +339,35 @@ func (h *TelegramHandler) ReloadBotConfig(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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相关的日志
|
// GetTelegramLogs 获取Telegram相关的日志
|
||||||
func (h *TelegramHandler) GetTelegramLogs(c *gin.Context) {
|
func (h *TelegramHandler) GetTelegramLogs(c *gin.Context) {
|
||||||
// 解析查询参数
|
// 解析查询参数
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -348,14 +348,15 @@ func main() {
|
|||||||
api.GET("/telegram/bot-status", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetBotStatus)
|
api.GET("/telegram/bot-status", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetBotStatus)
|
||||||
api.POST("/telegram/reload-config", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.ReloadBotConfig)
|
api.POST("/telegram/reload-config", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.ReloadBotConfig)
|
||||||
api.POST("/telegram/test-message", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.TestBotMessage)
|
api.POST("/telegram/test-message", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.TestBotMessage)
|
||||||
|
api.GET("/telegram/debug-connection", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.DebugBotConnection)
|
||||||
api.GET("/telegram/channels", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetChannels)
|
api.GET("/telegram/channels", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetChannels)
|
||||||
api.POST("/telegram/channels", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.CreateChannel)
|
api.POST("/telegram/channels", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.CreateChannel)
|
||||||
api.PUT("/telegram/channels/:id", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.UpdateChannel)
|
api.PUT("/telegram/channels/:id", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.UpdateChannel)
|
||||||
api.DELETE("/telegram/channels/:id", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.DeleteChannel)
|
api.DELETE("/telegram/channels/:id", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.DeleteChannel)
|
||||||
api.POST("/telegram/webhook", telegramHandler.HandleWebhook)
|
|
||||||
api.GET("/telegram/logs", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetTelegramLogs)
|
api.GET("/telegram/logs", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetTelegramLogs)
|
||||||
api.GET("/telegram/logs/stats", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetTelegramLogStats)
|
api.GET("/telegram/logs/stats", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.GetTelegramLogStats)
|
||||||
api.POST("/telegram/logs/clear", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.ClearTelegramLogs)
|
api.POST("/telegram/logs/clear", middleware.AuthMiddleware(), middleware.AdminMiddleware(), telegramHandler.ClearTelegramLogs)
|
||||||
|
api.POST("/telegram/webhook", telegramHandler.HandleWebhook)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 静态文件服务
|
// 静态文件服务
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func (m *MeilisearchService) HealthCheck() error {
|
|||||||
// 使用官方SDK的健康检查
|
// 使用官方SDK的健康检查
|
||||||
_, err := m.client.Health()
|
_, err := m.client.Health()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Error("Meilisearch健康检查失败: %v", err)
|
// utils.Error("Meilisearch健康检查失败: %v", err)
|
||||||
return fmt.Errorf("Meilisearch健康检查失败: %v", err)
|
return fmt.Errorf("Meilisearch健康检查失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ctwj/urldb/db/entity"
|
"github.com/ctwj/urldb/db/entity"
|
||||||
"github.com/ctwj/urldb/db/repo"
|
"github.com/ctwj/urldb/db/repo"
|
||||||
"github.com/ctwj/urldb/utils"
|
"github.com/ctwj/urldb/utils"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
@@ -16,6 +19,9 @@ import (
|
|||||||
type TelegramBotService interface {
|
type TelegramBotService interface {
|
||||||
Start() error
|
Start() error
|
||||||
Stop() error
|
Stop() error
|
||||||
|
IsRunning() bool
|
||||||
|
ReloadConfig() error
|
||||||
|
GetRuntimeStatus() map[string]interface{}
|
||||||
ValidateApiKey(apiKey string) (bool, map[string]interface{}, error)
|
ValidateApiKey(apiKey string) (bool, map[string]interface{}, error)
|
||||||
GetBotUsername() string
|
GetBotUsername() string
|
||||||
SendMessage(chatID int64, text string) error
|
SendMessage(chatID int64, text string) error
|
||||||
@@ -42,6 +48,12 @@ type TelegramBotConfig struct {
|
|||||||
AutoReplyTemplate string
|
AutoReplyTemplate string
|
||||||
AutoDeleteEnabled bool
|
AutoDeleteEnabled bool
|
||||||
AutoDeleteInterval int // 分钟
|
AutoDeleteInterval int // 分钟
|
||||||
|
ProxyEnabled bool
|
||||||
|
ProxyType string // http, https, socks5
|
||||||
|
ProxyHost string
|
||||||
|
ProxyPort int
|
||||||
|
ProxyUsername string
|
||||||
|
ProxyPassword string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTelegramBotService(
|
func NewTelegramBotService(
|
||||||
@@ -75,6 +87,13 @@ func (s *TelegramBotServiceImpl) loadConfig() error {
|
|||||||
s.config.AutoReplyTemplate = "您好!我可以帮您搜索网盘资源,请输入您要搜索的内容。"
|
s.config.AutoReplyTemplate = "您好!我可以帮您搜索网盘资源,请输入您要搜索的内容。"
|
||||||
s.config.AutoDeleteEnabled = false
|
s.config.AutoDeleteEnabled = false
|
||||||
s.config.AutoDeleteInterval = 60
|
s.config.AutoDeleteInterval = 60
|
||||||
|
// 初始化代理默认值
|
||||||
|
s.config.ProxyEnabled = false
|
||||||
|
s.config.ProxyType = "http"
|
||||||
|
s.config.ProxyHost = ""
|
||||||
|
s.config.ProxyPort = 8080
|
||||||
|
s.config.ProxyUsername = ""
|
||||||
|
s.config.ProxyPassword = ""
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
switch config.Key {
|
switch config.Key {
|
||||||
@@ -100,6 +119,26 @@ func (s *TelegramBotServiceImpl) loadConfig() error {
|
|||||||
fmt.Sscanf(config.Value, "%d", &s.config.AutoDeleteInterval)
|
fmt.Sscanf(config.Value, "%d", &s.config.AutoDeleteInterval)
|
||||||
}
|
}
|
||||||
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s (AutoDeleteInterval: %d)", config.Key, config.Value, s.config.AutoDeleteInterval)
|
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s (AutoDeleteInterval: %d)", config.Key, config.Value, s.config.AutoDeleteInterval)
|
||||||
|
case "telegram_proxy_enabled":
|
||||||
|
s.config.ProxyEnabled = config.Value == "true"
|
||||||
|
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s (ProxyEnabled: %v)", config.Key, config.Value, s.config.ProxyEnabled)
|
||||||
|
case "telegram_proxy_type":
|
||||||
|
s.config.ProxyType = config.Value
|
||||||
|
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s (ProxyType: %s)", config.Key, config.Value, s.config.ProxyType)
|
||||||
|
case "telegram_proxy_host":
|
||||||
|
s.config.ProxyHost = config.Value
|
||||||
|
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s", config.Key, "[HIDDEN]")
|
||||||
|
case "telegram_proxy_port":
|
||||||
|
if config.Value != "" {
|
||||||
|
fmt.Sscanf(config.Value, "%d", &s.config.ProxyPort)
|
||||||
|
}
|
||||||
|
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s (ProxyPort: %d)", config.Key, config.Value, s.config.ProxyPort)
|
||||||
|
case "telegram_proxy_username":
|
||||||
|
s.config.ProxyUsername = config.Value
|
||||||
|
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s", config.Key, "[HIDDEN]")
|
||||||
|
case "telegram_proxy_password":
|
||||||
|
s.config.ProxyPassword = config.Value
|
||||||
|
utils.Info("[TELEGRAM:CONFIG] 加载配置 %s = %s", config.Key, "[HIDDEN]")
|
||||||
default:
|
default:
|
||||||
utils.Debug("未知配置: %s = %s", config.Key, config.Value)
|
utils.Debug("未知配置: %s = %s", config.Key, config.Value)
|
||||||
}
|
}
|
||||||
@@ -128,9 +167,71 @@ func (s *TelegramBotServiceImpl) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Bot 实例
|
// 创建 Bot 实例
|
||||||
bot, err := tgbotapi.NewBotAPI(s.config.ApiKey)
|
var bot *tgbotapi.BotAPI
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("创建 Telegram Bot 失败: %v", err)
|
if s.config.ProxyEnabled && s.config.ProxyHost != "" {
|
||||||
|
// 配置代理
|
||||||
|
utils.Info("[TELEGRAM:PROXY] 配置代理: %s://%s:%d", s.config.ProxyType, s.config.ProxyHost, s.config.ProxyPort)
|
||||||
|
|
||||||
|
var httpClient *http.Client
|
||||||
|
|
||||||
|
if s.config.ProxyType == "socks5" {
|
||||||
|
// SOCKS5 代理配置
|
||||||
|
var auth *proxy.Auth
|
||||||
|
if s.config.ProxyUsername != "" {
|
||||||
|
auth = &proxy.Auth{
|
||||||
|
User: s.config.ProxyUsername,
|
||||||
|
Password: s.config.ProxyPassword,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer, proxyErr := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", s.config.ProxyHost, s.config.ProxyPort), auth, proxy.Direct)
|
||||||
|
if proxyErr != nil {
|
||||||
|
return fmt.Errorf("创建 SOCKS5 代理失败: %v", proxyErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Dial: dialer.Dial,
|
||||||
|
},
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// HTTP/HTTPS 代理配置
|
||||||
|
proxyURL := &url.URL{
|
||||||
|
Scheme: s.config.ProxyType,
|
||||||
|
Host: fmt.Sprintf("%s:%d", s.config.ProxyHost, s.config.ProxyPort),
|
||||||
|
User: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.config.ProxyUsername != "" {
|
||||||
|
proxyURL.User = url.UserPassword(s.config.ProxyUsername, s.config.ProxyPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxyURL),
|
||||||
|
},
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
botInstance, botErr := tgbotapi.NewBotAPIWithClient(s.config.ApiKey, tgbotapi.APIEndpoint, httpClient)
|
||||||
|
if botErr != nil {
|
||||||
|
return fmt.Errorf("创建 Telegram Bot (代理模式) 失败: %v", botErr)
|
||||||
|
}
|
||||||
|
bot = botInstance
|
||||||
|
|
||||||
|
utils.Info("[TELEGRAM:PROXY] Telegram Bot 已配置代理连接")
|
||||||
|
} else {
|
||||||
|
// 直接连接(无代理)
|
||||||
|
var err error
|
||||||
|
bot, err = tgbotapi.NewBotAPI(s.config.ApiKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建 Telegram Bot 失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Info("[TELEGRAM:PROXY] Telegram Bot 使用直连模式")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.bot = bot
|
s.bot = bot
|
||||||
@@ -168,13 +269,104 @@ func (s *TelegramBotServiceImpl) Stop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsRunning 检查机器人服务是否正在运行
|
||||||
|
func (s *TelegramBotServiceImpl) IsRunning() bool {
|
||||||
|
return s.isRunning && s.bot != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadConfig 重新加载机器人配置
|
||||||
|
func (s *TelegramBotServiceImpl) ReloadConfig() error {
|
||||||
|
utils.Info("[TELEGRAM:SERVICE] 开始重新加载配置...")
|
||||||
|
|
||||||
|
// 重新加载配置
|
||||||
|
if err := s.loadConfig(); err != nil {
|
||||||
|
utils.Error("[TELEGRAM:SERVICE] 重新加载配置失败: %v", err)
|
||||||
|
return fmt.Errorf("重新加载配置失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Info("[TELEGRAM:SERVICE] 配置重新加载完成: Enabled=%v, AutoReplyEnabled=%v",
|
||||||
|
s.config.Enabled, s.config.AutoReplyEnabled)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuntimeStatus 获取机器人运行时状态
|
||||||
|
func (s *TelegramBotServiceImpl) GetRuntimeStatus() map[string]interface{} {
|
||||||
|
status := map[string]interface{}{
|
||||||
|
"is_running": s.IsRunning(),
|
||||||
|
"bot_initialized": s.bot != nil,
|
||||||
|
"config_loaded": s.config != nil,
|
||||||
|
"cron_running": s.cronScheduler != nil,
|
||||||
|
"username": "",
|
||||||
|
"uptime": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.bot != nil {
|
||||||
|
status["username"] = s.GetBotUsername()
|
||||||
|
}
|
||||||
|
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateApiKey 验证 API Key
|
// ValidateApiKey 验证 API Key
|
||||||
func (s *TelegramBotServiceImpl) ValidateApiKey(apiKey string) (bool, map[string]interface{}, error) {
|
func (s *TelegramBotServiceImpl) ValidateApiKey(apiKey string) (bool, map[string]interface{}, error) {
|
||||||
if apiKey == "" {
|
if apiKey == "" {
|
||||||
return false, nil, fmt.Errorf("API Key 不能为空")
|
return false, nil, fmt.Errorf("API Key 不能为空")
|
||||||
}
|
}
|
||||||
|
|
||||||
bot, err := tgbotapi.NewBotAPI(apiKey)
|
var bot *tgbotapi.BotAPI
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// 如果启用了代理,使用代理验证
|
||||||
|
if s.config.ProxyEnabled && s.config.ProxyHost != "" {
|
||||||
|
var httpClient *http.Client
|
||||||
|
|
||||||
|
if s.config.ProxyType == "socks5" {
|
||||||
|
var auth *proxy.Auth
|
||||||
|
if s.config.ProxyUsername != "" {
|
||||||
|
auth = &proxy.Auth{
|
||||||
|
User: s.config.ProxyUsername,
|
||||||
|
Password: s.config.ProxyPassword,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer, proxyErr := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", s.config.ProxyHost, s.config.ProxyPort), auth, proxy.Direct)
|
||||||
|
if proxyErr != nil {
|
||||||
|
// 如果代理失败,回退到直连
|
||||||
|
utils.Warn("[TELEGRAM:PROXY] SOCKS5 代理验证失败,回退到直连: %v", proxyErr)
|
||||||
|
bot, err = tgbotapi.NewBotAPI(apiKey)
|
||||||
|
} else {
|
||||||
|
httpClient = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Dial: dialer.Dial,
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
bot, err = tgbotapi.NewBotAPIWithClient(apiKey, tgbotapi.APIEndpoint, httpClient)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
proxyURL := &url.URL{
|
||||||
|
Scheme: s.config.ProxyType,
|
||||||
|
Host: fmt.Sprintf("%s:%d", s.config.ProxyHost, s.config.ProxyPort),
|
||||||
|
User: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.config.ProxyUsername != "" {
|
||||||
|
proxyURL.User = url.UserPassword(s.config.ProxyUsername, s.config.ProxyPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxyURL),
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
bot, err = tgbotapi.NewBotAPIWithClient(apiKey, tgbotapi.APIEndpoint, httpClient)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直连验证
|
||||||
|
bot, err = tgbotapi.NewBotAPI(apiKey)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, fmt.Errorf("无效的 API Key: %v", err)
|
return false, nil, fmt.Errorf("无效的 API Key: %v", err)
|
||||||
}
|
}
|
||||||
@@ -323,17 +515,50 @@ func (s *TelegramBotServiceImpl) handleSearchRequest(message *tgbotapi.Message)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 这里使用简单的资源搜索,实际项目中需要完善搜索逻辑
|
utils.Info("[TELEGRAM:SEARCH] 处理搜索请求: %s", query)
|
||||||
// resources, err := s.resourceRepo.Search(query, nil, 0, 10)
|
|
||||||
// 暂时模拟一个搜索结果
|
// 使用资源仓库进行搜索
|
||||||
results := []string{
|
resources, total, err := s.resourceRepo.Search(query, nil, 1, 5) // 限制为5个结果
|
||||||
fmt.Sprintf("🔍 搜索关键词: %s", query),
|
if err != nil {
|
||||||
"暂无相关资源,请尝试其他关键词。",
|
utils.Error("[TELEGRAM:SEARCH] 搜索失败: %v", err)
|
||||||
"",
|
s.sendReply(message, "搜索服务暂时不可用,请稍后重试")
|
||||||
fmt.Sprintf("💡 提示:如需精确搜索,请使用更具体的关键词。"),
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if total == 0 {
|
||||||
|
response := fmt.Sprintf("🔍 *搜索结果*\n\n关键词: `%s`\n\n❌ 未找到相关资源\n\n💡 建议:\n• 尝试使用更通用的关键词\n• 检查拼写是否正确\n• 减少关键词数量", query)
|
||||||
|
s.sendReply(message, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建搜索结果消息
|
||||||
|
resultText := fmt.Sprintf("🔍 *搜索结果*\n\n关键词: `%s`\n总共找到: %d 个资源\n\n", query, total)
|
||||||
|
|
||||||
|
// 显示前5个结果
|
||||||
|
for i, resource := range resources {
|
||||||
|
if i >= 5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
title := resource.Title
|
||||||
|
if len(title) > 50 {
|
||||||
|
title = title[:47] + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
description := resource.Description
|
||||||
|
if len(description) > 100 {
|
||||||
|
description = description[:97] + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
resultText += fmt.Sprintf("%d. *%s*\n%s\n\n", i+1, title, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有更多结果,添加提示
|
||||||
|
if total > 5 {
|
||||||
|
resultText += fmt.Sprintf("... 还有 %d 个结果\n\n", total-5)
|
||||||
|
resultText += "💡 如需查看更多结果,请访问网站搜索"
|
||||||
}
|
}
|
||||||
|
|
||||||
resultText := strings.Join(results, "\n")
|
|
||||||
s.sendReply(message, resultText)
|
s.sendReply(message, resultText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
@@ -51,6 +51,7 @@ declare module 'vue' {
|
|||||||
NTag: typeof import('naive-ui')['NTag']
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
NText: typeof import('naive-ui')['NText']
|
NText: typeof import('naive-ui')['NText']
|
||||||
NThing: typeof import('naive-ui')['NThing']
|
NThing: typeof import('naive-ui')['NThing']
|
||||||
|
NTimePicker: typeof import('naive-ui')['NTimePicker']
|
||||||
NUpload: typeof import('naive-ui')['NUpload']
|
NUpload: typeof import('naive-ui')['NUpload']
|
||||||
NUploadDragger: typeof import('naive-ui')['NUploadDragger']
|
NUploadDragger: typeof import('naive-ui')['NUploadDragger']
|
||||||
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
||||||
|
|||||||
@@ -13,14 +13,36 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<!-- 机器人启用开关 -->
|
<!-- 机器人启用开关 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div class="flex-1">
|
||||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">启用 Telegram 机器人</label>
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">启用 Telegram 机器人</label>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">开启后机器人将开始工作</p>
|
<p class="text-xs text-gray-500 dark:text-gray-400">开启后机器人将开始工作</p>
|
||||||
</div>
|
</div>
|
||||||
<n-switch
|
<div class="flex items-center space-x-3">
|
||||||
v-model:value="telegramBotConfig.bot_enabled"
|
<n-switch
|
||||||
@update:value="handleBotConfigChange"
|
v-model:value="telegramBotConfig.bot_enabled"
|
||||||
/>
|
@update:value="handleBotConfigChange"
|
||||||
|
/>
|
||||||
|
<!-- 运行状态指示器 -->
|
||||||
|
<div v-if="botStatus" class="flex items-center space-x-2">
|
||||||
|
<n-tag
|
||||||
|
:type="botStatus.overall_status ? 'success' : (telegramBotConfig.bot_enabled ? 'warning' : 'default')"
|
||||||
|
size="small"
|
||||||
|
class="min-w-16 text-center"
|
||||||
|
>
|
||||||
|
{{ botStatus.status_text }}
|
||||||
|
</n-tag>
|
||||||
|
<n-button
|
||||||
|
size="small"
|
||||||
|
@click="refreshBotStatus"
|
||||||
|
:loading="statusRefreshing"
|
||||||
|
circle
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<i class="fas fa-sync-alt"></i>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- API Key 配置 -->
|
<!-- API Key 配置 -->
|
||||||
@@ -83,12 +105,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<n-switch
|
<n-switch
|
||||||
v-model:value="telegramBotConfig.auto_reply_enabled"
|
v-model:value="telegramBotConfig.auto_reply_enabled"
|
||||||
|
:disabled="telegramBotConfig.bot_enabled"
|
||||||
@update:value="handleBotConfigChange"
|
@update:value="handleBotConfigChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 回复模板 -->
|
<!-- 回复模板 -->
|
||||||
<div v-if="telegramBotConfig.auto_reply_enabled">
|
<div v-if="telegramBotConfig.auto_reply_enabled || telegramBotConfig.bot_enabled">
|
||||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">回复模板</label>
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">回复模板</label>
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="telegramBotConfig.auto_reply_template"
|
v-model:value="telegramBotConfig.auto_reply_template"
|
||||||
@@ -124,6 +147,107 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 代理配置 -->
|
||||||
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||||
|
<div class="flex items-center mb-6">
|
||||||
|
<div class="w-8 h-8 bg-purple-600 text-white rounded-full flex items-center justify-center mr-3">
|
||||||
|
<span class="text-sm font-bold">3</span>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">代理配置</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- 启用代理 -->
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">启用代理</label>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400">通过代理服务器连接 Telegram API</p>
|
||||||
|
</div>
|
||||||
|
<n-switch
|
||||||
|
v-model:value="telegramBotConfig.proxy_enabled"
|
||||||
|
@update:value="handleBotConfigChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 代理设置 -->
|
||||||
|
<div v-if="telegramBotConfig.proxy_enabled" class="space-y-4">
|
||||||
|
<!-- 代理类型 -->
|
||||||
|
<div>
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">代理类型</label>
|
||||||
|
<n-select
|
||||||
|
v-model:value="telegramBotConfig.proxy_type"
|
||||||
|
:options="[
|
||||||
|
{ label: 'HTTP', value: 'http' },
|
||||||
|
{ label: 'HTTPS', value: 'https' },
|
||||||
|
{ label: 'SOCKS5', value: 'socks5' }
|
||||||
|
]"
|
||||||
|
placeholder="选择代理类型"
|
||||||
|
@update:value="handleBotConfigChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 代理主机和端口 -->
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">代理主机</label>
|
||||||
|
<n-input
|
||||||
|
v-model:value="telegramBotConfig.proxy_host"
|
||||||
|
placeholder="例如: 127.0.0.1 或 proxy.example.com"
|
||||||
|
@input="handleBotConfigChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">代理端口</label>
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="telegramBotConfig.proxy_port"
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
placeholder="例如: 8080"
|
||||||
|
@update:value="handleBotConfigChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 代理认证 -->
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">用户名 (可选)</label>
|
||||||
|
<n-input
|
||||||
|
v-model:value="telegramBotConfig.proxy_username"
|
||||||
|
placeholder="代理用户名"
|
||||||
|
@input="handleBotConfigChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">密码 (可选)</label>
|
||||||
|
<n-input
|
||||||
|
v-model:value="telegramBotConfig.proxy_password"
|
||||||
|
type="password"
|
||||||
|
placeholder="代理密码"
|
||||||
|
@input="handleBotConfigChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 代理状态提示 -->
|
||||||
|
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4">
|
||||||
|
<div class="flex items-start space-x-3">
|
||||||
|
<i class="fas fa-info-circle text-blue-600 dark:text-blue-400 mt-1"></i>
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-medium text-blue-800 dark:text-blue-200 mb-2">代理配置说明</h4>
|
||||||
|
<ul class="text-sm text-blue-700 dark:text-blue-300 space-y-1">
|
||||||
|
<li>• HTTP/HTTPS 代理支持基本的认证</li>
|
||||||
|
<li>• SOCKS5 代理支持用户名/密码认证</li>
|
||||||
|
<li>• 配置完成后需要重启机器人服务</li>
|
||||||
|
<li>• 确保代理服务器稳定可靠</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 频道和群组管理 -->
|
<!-- 频道和群组管理 -->
|
||||||
<div v-if="telegramBotConfig.bot_enabled" class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
<div v-if="telegramBotConfig.bot_enabled" class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="flex items-center justify-between mb-6">
|
||||||
@@ -147,15 +271,6 @@
|
|||||||
</template>
|
</template>
|
||||||
刷新
|
刷新
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button
|
|
||||||
@click="testBotConnection"
|
|
||||||
:loading="testingConnection"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-robot"></i>
|
|
||||||
</template>
|
|
||||||
测试连接
|
|
||||||
</n-button>
|
|
||||||
<n-button
|
<n-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="showRegisterChannelDialog = true"
|
@click="showRegisterChannelDialog = true"
|
||||||
@@ -238,7 +353,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end p-2">
|
<div class="flex justify-end p-2 gap-2">
|
||||||
|
<n-button
|
||||||
|
@click="testBotConnection"
|
||||||
|
:loading="testingConnection"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<i class="fas fa-robot"></i>
|
||||||
|
</template>
|
||||||
|
测试连接
|
||||||
|
</n-button>
|
||||||
|
<n-button
|
||||||
|
@click="debugBotConnection"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<i class="fas fa-bug"></i>
|
||||||
|
</template>
|
||||||
|
调试
|
||||||
|
</n-button>
|
||||||
<n-button @click="showLogDrawer = true">
|
<n-button @click="showLogDrawer = true">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="fas fa-list-alt"></i>
|
<i class="fas fa-list-alt"></i>
|
||||||
@@ -261,9 +393,9 @@
|
|||||||
v-model:show="showRegisterChannelDialog"
|
v-model:show="showRegisterChannelDialog"
|
||||||
preset="card"
|
preset="card"
|
||||||
title="注册频道/群组"
|
title="注册频道/群组"
|
||||||
size="huge"
|
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:segmented="false"
|
:segmented="false"
|
||||||
|
:style="{ width: '800px' }"
|
||||||
>
|
>
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-400">
|
<div class="text-sm text-gray-600 dark:text-gray-400">
|
||||||
@@ -305,6 +437,148 @@
|
|||||||
</div>
|
</div>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
|
<!-- 编辑频道对话框 -->
|
||||||
|
<n-modal
|
||||||
|
v-model:show="showEditChannelDialog"
|
||||||
|
preset="card"
|
||||||
|
:title="`编辑频道 - ${editingChannel?.chat_name || ''}`"
|
||||||
|
size="large"
|
||||||
|
:bordered="false"
|
||||||
|
:segmented="false"
|
||||||
|
>
|
||||||
|
<div v-if="editingChannel" class="space-y-6">
|
||||||
|
<!-- 频道基本信息 -->
|
||||||
|
<div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-4">
|
||||||
|
<h4 class="text-sm font-medium text-gray-900 dark:text-white mb-2">频道信息</h4>
|
||||||
|
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-600 dark:text-gray-400">频道名称:</span>
|
||||||
|
<span class="ml-2 text-gray-900 dark:text-white">{{ editingChannel.chat_name }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-600 dark:text-gray-400">频道ID:</span>
|
||||||
|
<span class="ml-2 text-gray-900 dark:text-white">{{ editingChannel.chat_id }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-600 dark:text-gray-400">类型:</span>
|
||||||
|
<span class="ml-2 text-gray-900 dark:text-white">{{ editingChannel.chat_type === 'channel' ? '频道' : '群组' }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-600 dark:text-gray-400">状态:</span>
|
||||||
|
<n-tag :type="editingChannel.is_active ? 'success' : 'warning'" size="small" class="ml-2">
|
||||||
|
{{ editingChannel.is_active ? '活跃' : '非活跃' }}
|
||||||
|
</n-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 推送设置 -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h4 class="text-base font-medium text-gray-900 dark:text-white">推送设置</h4>
|
||||||
|
|
||||||
|
<!-- 启用推送 -->
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">启用推送</label>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400">是否向此频道推送内容</p>
|
||||||
|
</div>
|
||||||
|
<n-switch
|
||||||
|
v-model:value="editingChannel.push_enabled"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 推送频率 -->
|
||||||
|
<div v-if="editingChannel.push_enabled">
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">推送频率</label>
|
||||||
|
<n-select
|
||||||
|
v-model:value="editingChannel.push_frequency"
|
||||||
|
:options="[
|
||||||
|
{ label: '每5分钟', value: 0.0833 },
|
||||||
|
{ label: '每10分钟', value: 0.1667 },
|
||||||
|
{ label: '每15分钟', value: 0.25 },
|
||||||
|
{ label: '每30分钟', value: 0.5 },
|
||||||
|
{ label: '每小时', value: 1 },
|
||||||
|
{ label: '每2小时', value: 2 },
|
||||||
|
{ label: '每3小时', value: 3 },
|
||||||
|
{ label: '每6小时', value: 6 },
|
||||||
|
{ label: '每12小时', value: 12 },
|
||||||
|
{ label: '每天', value: 24 },
|
||||||
|
{ label: '每2天', value: 48 },
|
||||||
|
{ label: '每周', value: 168 }
|
||||||
|
]"
|
||||||
|
placeholder="选择推送频率"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 推送时间段 -->
|
||||||
|
<div v-if="editingChannel.push_enabled">
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">推送时间段</label>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="text-xs text-gray-600 dark:text-gray-400">开始时间</label>
|
||||||
|
<n-time-picker
|
||||||
|
v-model:value="editingChannel.push_start_time"
|
||||||
|
format="HH:mm"
|
||||||
|
placeholder="选择开始时间"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-xs text-gray-600 dark:text-gray-400">结束时间</label>
|
||||||
|
<n-time-picker
|
||||||
|
v-model:value="editingChannel.push_end_time"
|
||||||
|
format="HH:mm"
|
||||||
|
placeholder="选择结束时间"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||||
|
留空表示全天推送,不设置时间限制
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容分类 -->
|
||||||
|
<div v-if="editingChannel.push_enabled">
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">内容分类</label>
|
||||||
|
<n-input
|
||||||
|
v-model:value="editingChannel.content_categories"
|
||||||
|
placeholder="输入内容分类,多个用逗号分隔 (如: 电影,电视剧,动漫)"
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
/>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">留空表示推送所有分类的内容</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 标签过滤 -->
|
||||||
|
<div v-if="editingChannel.push_enabled">
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">标签过滤</label>
|
||||||
|
<n-input
|
||||||
|
v-model:value="editingChannel.content_tags"
|
||||||
|
placeholder="输入标签关键词,多个用逗号分隔 (如: 高清,1080p,蓝光)"
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
/>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">留空表示推送所有标签的内容</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<div class="flex justify-end space-x-3 pt-4 border-t border-gray-200 dark:border-gray-600">
|
||||||
|
<n-button @click="showEditChannelDialog = false">
|
||||||
|
取消
|
||||||
|
</n-button>
|
||||||
|
<n-button
|
||||||
|
type="primary"
|
||||||
|
:loading="savingChannel"
|
||||||
|
@click="saveChannelSettings"
|
||||||
|
>
|
||||||
|
保存设置
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-modal>
|
||||||
|
|
||||||
<!-- Telegram 日志抽屉 -->
|
<!-- Telegram 日志抽屉 -->
|
||||||
<n-drawer
|
<n-drawer
|
||||||
v-model:show="showLogDrawer"
|
v-model:show="showLogDrawer"
|
||||||
@@ -313,9 +587,9 @@
|
|||||||
placement="right"
|
placement="right"
|
||||||
>
|
>
|
||||||
<n-drawer-content>
|
<n-drawer-content>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4 h-full overflow-y-auto flex flex-col">
|
||||||
<!-- 日志控制栏 -->
|
<!-- 日志控制栏 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex-0 flex items-center justify-between">
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-400">时间范围:</span>
|
<span class="text-sm text-gray-600 dark:text-gray-400">时间范围:</span>
|
||||||
<n-select
|
<n-select
|
||||||
@@ -342,7 +616,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 日志列表 -->
|
<!-- 日志列表 -->
|
||||||
<div class="space-y-2 max-h-96 overflow-y-auto">
|
<div class="h-1 flex-1 space-y-2 overflow-y-auto">
|
||||||
<div v-if="telegramLogs.length === 0 && !loadingLogs" class="text-center py-8">
|
<div v-if="telegramLogs.length === 0 && !loadingLogs" class="text-center py-8">
|
||||||
<i class="fas fa-list-alt text-4xl text-gray-400 mb-4"></i>
|
<i class="fas fa-list-alt text-4xl text-gray-400 mb-4"></i>
|
||||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">暂无日志</h3>
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">暂无日志</h3>
|
||||||
@@ -376,7 +650,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 日志统计 -->
|
<!-- 日志统计 -->
|
||||||
<div class="flex justify-between items-center text-sm text-gray-600 dark:text-gray-400">
|
<div class="flex-0 flex justify-between items-center text-sm text-gray-600 dark:text-gray-400">
|
||||||
<span>显示 {{ telegramLogs.length }} 条日志</span>
|
<span>显示 {{ telegramLogs.length }} 条日志</span>
|
||||||
<span v-if="telegramLogs.length > 0">
|
<span v-if="telegramLogs.length > 0">
|
||||||
加载于 {{ formatTimestamp(new Date().toISOString()) }}
|
加载于 {{ formatTimestamp(new Date().toISOString()) }}
|
||||||
@@ -400,6 +674,12 @@ const telegramBotConfig = ref<any>({
|
|||||||
auto_reply_template: '您好!我可以帮您搜索网盘资源,请输入您要搜索的内容。',
|
auto_reply_template: '您好!我可以帮您搜索网盘资源,请输入您要搜索的内容。',
|
||||||
auto_delete_enabled: false,
|
auto_delete_enabled: false,
|
||||||
auto_delete_interval: 60,
|
auto_delete_interval: 60,
|
||||||
|
proxy_enabled: false,
|
||||||
|
proxy_type: 'http',
|
||||||
|
proxy_host: '',
|
||||||
|
proxy_port: 8080,
|
||||||
|
proxy_username: '',
|
||||||
|
proxy_password: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const telegramChannels = ref<any[]>([])
|
const telegramChannels = ref<any[]>([])
|
||||||
@@ -408,12 +688,19 @@ const savingBotConfig = ref(false)
|
|||||||
const apiKeyValidationResult = ref<any>(null)
|
const apiKeyValidationResult = ref<any>(null)
|
||||||
const hasBotConfigChanges = ref(false)
|
const hasBotConfigChanges = ref(false)
|
||||||
const showRegisterChannelDialog = ref(false)
|
const showRegisterChannelDialog = ref(false)
|
||||||
|
const showEditChannelDialog = ref(false)
|
||||||
const showLogDrawer = ref(false)
|
const showLogDrawer = ref(false)
|
||||||
const refreshingChannels = ref(false)
|
const refreshingChannels = ref(false)
|
||||||
const testingConnection = ref(false)
|
const testingConnection = ref(false)
|
||||||
const telegramLogs = ref<any[]>([])
|
const telegramLogs = ref<any[]>([])
|
||||||
const loadingLogs = ref(false)
|
const loadingLogs = ref(false)
|
||||||
const logHours = ref(24)
|
const logHours = ref(24)
|
||||||
|
const editingChannel = ref<any>(null)
|
||||||
|
const savingChannel = ref(false)
|
||||||
|
|
||||||
|
// 机器人状态相关变量
|
||||||
|
const botStatus = ref<any>(null)
|
||||||
|
const statusRefreshing = ref(false)
|
||||||
|
|
||||||
// 使用统一的Telegram API
|
// 使用统一的Telegram API
|
||||||
const telegramApi = useTelegramApi()
|
const telegramApi = useTelegramApi()
|
||||||
@@ -423,6 +710,10 @@ const fetchTelegramConfig = async () => {
|
|||||||
try {
|
try {
|
||||||
const data = await telegramApi.getBotConfig() as any
|
const data = await telegramApi.getBotConfig() as any
|
||||||
if (data) {
|
if (data) {
|
||||||
|
// 确保当机器人启用时,自动回复始终为true
|
||||||
|
if (data.bot_enabled) {
|
||||||
|
data.auto_reply_enabled = true
|
||||||
|
}
|
||||||
telegramBotConfig.value = { ...data }
|
telegramBotConfig.value = { ...data }
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -434,9 +725,12 @@ const fetchTelegramConfig = async () => {
|
|||||||
const fetchTelegramChannels = async () => {
|
const fetchTelegramChannels = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await telegramApi.getChannels() as any[]
|
const data = await telegramApi.getChannels() as any[]
|
||||||
if (data) {
|
if (data !== undefined && data !== null) {
|
||||||
telegramChannels.value = data
|
telegramChannels.value = Array.isArray(data) ? data : []
|
||||||
|
} else {
|
||||||
|
telegramChannels.value = []
|
||||||
}
|
}
|
||||||
|
console.log('频道列表已更新:', telegramChannels.value.length, '个频道')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('获取频道列表失败:', error)
|
console.error('获取频道列表失败:', error)
|
||||||
// 如果是表不存在的错误,给出更友好的提示
|
// 如果是表不存在的错误,给出更友好的提示
|
||||||
@@ -460,6 +754,10 @@ const fetchTelegramChannels = async () => {
|
|||||||
|
|
||||||
// 处理机器人配置变更
|
// 处理机器人配置变更
|
||||||
const handleBotConfigChange = () => {
|
const handleBotConfigChange = () => {
|
||||||
|
// 当机器人启用时,自动回复必须为true
|
||||||
|
if (telegramBotConfig.value.bot_enabled) {
|
||||||
|
telegramBotConfig.value.auto_reply_enabled = true
|
||||||
|
}
|
||||||
hasBotConfigChanges.value = true
|
hasBotConfigChanges.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,17 +848,24 @@ const saveBotConfig = async () => {
|
|||||||
const config = telegramBotConfig.value as any
|
const config = telegramBotConfig.value as any
|
||||||
configRequest.bot_enabled = config.bot_enabled
|
configRequest.bot_enabled = config.bot_enabled
|
||||||
configRequest.bot_api_key = config.bot_api_key
|
configRequest.bot_api_key = config.bot_api_key
|
||||||
configRequest.auto_reply_enabled = config.auto_reply_enabled
|
// 当机器人启用时,自动回复必须为true
|
||||||
|
configRequest.auto_reply_enabled = config.bot_enabled ? true : config.auto_reply_enabled
|
||||||
configRequest.auto_reply_template = config.auto_reply_template
|
configRequest.auto_reply_template = config.auto_reply_template
|
||||||
configRequest.auto_delete_enabled = config.auto_delete_enabled
|
configRequest.auto_delete_enabled = config.auto_delete_enabled
|
||||||
configRequest.auto_delete_interval = config.auto_delete_interval
|
configRequest.auto_delete_interval = config.auto_delete_interval
|
||||||
|
configRequest.proxy_enabled = config.proxy_enabled
|
||||||
|
configRequest.proxy_type = config.proxy_type
|
||||||
|
configRequest.proxy_host = config.proxy_host
|
||||||
|
configRequest.proxy_port = config.proxy_port
|
||||||
|
configRequest.proxy_username = config.proxy_username
|
||||||
|
configRequest.proxy_password = config.proxy_password
|
||||||
}
|
}
|
||||||
|
|
||||||
await telegramApi.updateBotConfig(configRequest)
|
await telegramApi.updateBotConfig(configRequest)
|
||||||
|
|
||||||
notification.success({
|
notification.success({
|
||||||
content: '配置保存成功',
|
content: '配置保存成功,机器人服务已重新加载配置',
|
||||||
duration: 2000
|
duration: 3000
|
||||||
})
|
})
|
||||||
hasBotConfigChanges.value = false
|
hasBotConfigChanges.value = false
|
||||||
// 重新获取配置以确保同步
|
// 重新获取配置以确保同步
|
||||||
@@ -577,8 +882,8 @@ const saveBotConfig = async () => {
|
|||||||
|
|
||||||
// 编辑频道
|
// 编辑频道
|
||||||
const editChannel = (channel: any) => {
|
const editChannel = (channel: any) => {
|
||||||
// TODO: 实现编辑频道功能
|
editingChannel.value = { ...channel }
|
||||||
console.log('编辑频道:', channel)
|
showEditChannelDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注销频道(带确认)
|
// 注销频道(带确认)
|
||||||
@@ -612,6 +917,9 @@ const performUnregisterChannel = async (channel: any) => {
|
|||||||
duration: 3000
|
duration: 3000
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 添加短暂延迟确保数据库事务完成
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
|
||||||
// 重新获取频道列表,更新UI
|
// 重新获取频道列表,更新UI
|
||||||
await fetchTelegramChannels()
|
await fetchTelegramChannels()
|
||||||
|
|
||||||
@@ -694,14 +1002,20 @@ const testBotConnection = async () => {
|
|||||||
testingConnection.value = true
|
testingConnection.value = true
|
||||||
try {
|
try {
|
||||||
const data = await telegramApi.getBotStatus() as any
|
const data = await telegramApi.getBotStatus() as any
|
||||||
if (data && data.service_running) {
|
if (data && data.overall_status) {
|
||||||
notification.success({
|
notification.success({
|
||||||
content: `机器人连接正常!用户名:@${data.bot_username}`,
|
content: `机器人连接正常!用户名:@${data.runtime?.username || '未知'}`,
|
||||||
duration: 3000
|
duration: 3000
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
let warningMessage = '机器人服务未运行或未配置'
|
||||||
|
if (data?.config?.enabled) {
|
||||||
|
warningMessage = '机器人已启用但未运行,请检查 API Key 配置'
|
||||||
|
} else if (!data?.config?.api_key_configured) {
|
||||||
|
warningMessage = 'API Key 未配置,请先配置有效的 API Key'
|
||||||
|
}
|
||||||
notification.warning({
|
notification.warning({
|
||||||
content: '机器人服务未运行或未配置',
|
content: warningMessage,
|
||||||
duration: 3000
|
duration: 3000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -814,10 +1128,115 @@ const getCategoryLabel = (category: string): string => {
|
|||||||
const notification = useNotification()
|
const notification = useNotification()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
|
|
||||||
|
// 保存频道设置
|
||||||
|
const saveChannelSettings = async () => {
|
||||||
|
if (!editingChannel.value) return
|
||||||
|
|
||||||
|
savingChannel.value = true
|
||||||
|
try {
|
||||||
|
const updateData = {
|
||||||
|
chat_name: editingChannel.value.chat_name,
|
||||||
|
chat_type: editingChannel.value.chat_type,
|
||||||
|
push_enabled: editingChannel.value.push_enabled,
|
||||||
|
push_frequency: editingChannel.value.push_frequency,
|
||||||
|
content_categories: editingChannel.value.content_categories,
|
||||||
|
content_tags: editingChannel.value.content_tags,
|
||||||
|
is_active: editingChannel.value.is_active,
|
||||||
|
push_start_time: editingChannel.value.push_start_time,
|
||||||
|
push_end_time: editingChannel.value.push_end_time
|
||||||
|
}
|
||||||
|
|
||||||
|
await telegramApi.updateChannel(editingChannel.value.id, updateData)
|
||||||
|
|
||||||
|
notification.success({
|
||||||
|
content: `频道 "${editingChannel.value.chat_name}" 设置已更新`,
|
||||||
|
duration: 3000
|
||||||
|
})
|
||||||
|
|
||||||
|
// 关闭对话框
|
||||||
|
showEditChannelDialog.value = false
|
||||||
|
|
||||||
|
// 刷新频道列表
|
||||||
|
await fetchTelegramChannels()
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
notification.error({
|
||||||
|
content: `保存频道设置失败: ${error?.message || '请稍后重试'}`,
|
||||||
|
duration: 3000
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
savingChannel.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新机器人状态
|
||||||
|
const refreshBotStatus = async () => {
|
||||||
|
statusRefreshing.value = true
|
||||||
|
try {
|
||||||
|
const data = await telegramApi.getBotStatus() as any
|
||||||
|
botStatus.value = data
|
||||||
|
notification.success({
|
||||||
|
content: '机器人状态已刷新',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
} catch (error: any) {
|
||||||
|
notification.error({
|
||||||
|
content: '刷新状态失败:' + (error?.message || '请稍后重试'),
|
||||||
|
duration: 3000
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
statusRefreshing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试机器人连接
|
||||||
|
const debugBotConnection = async () => {
|
||||||
|
try {
|
||||||
|
const data = await telegramApi.getBotStatus() as any
|
||||||
|
|
||||||
|
let message = `🔍 **Telegram 机器人调试信息**\n\n`
|
||||||
|
message += `🤖 机器人状态: ${data.runtime?.is_running ? '✅ 运行中' : '❌ 未运行'}\n`
|
||||||
|
message += `👤 用户名: @${data.runtime?.username || '未知'}\n`
|
||||||
|
message += `⚡ 工作模式: 长轮询\n\n`
|
||||||
|
|
||||||
|
message += `📋 **故障排查步骤:**\n`
|
||||||
|
message += `1. 检查服务器控制台是否有 [TELEGRAM] 日志\n`
|
||||||
|
message += `2. 确认机器人已添加到群组并设为管理员\n`
|
||||||
|
message += `3. 验证 API Key 配置是否正确\n`
|
||||||
|
message += `4. 确认自动回复功能已启用\n`
|
||||||
|
message += `5. 重启服务器重新加载配置\n\n`
|
||||||
|
|
||||||
|
message += `🔧 **预期日志输出:**\n`
|
||||||
|
message += `• [TELEGRAM:SERVICE] Telegram Bot (@用户名) 已启动\n`
|
||||||
|
message += `• [TELEGRAM:MESSAGE] 收到消息: ChatID=xxx, Text='/register'\n`
|
||||||
|
message += `• [TELEGRAM:MESSAGE] 处理 /register 命令 from ChatID=xxx\n`
|
||||||
|
message += `• [TELEGRAM:MESSAGE:SUCCESS] 消息发送成功\n\n`
|
||||||
|
|
||||||
|
message += `💡 **如果没有日志输出:**\n`
|
||||||
|
message += `• 服务器可能未正确启动机器人服务\n`
|
||||||
|
message += `• API Key 可能有误\n`
|
||||||
|
message += `• 数据库配置可能有问题`
|
||||||
|
|
||||||
|
notification.info({
|
||||||
|
title: '🤖 机器人连接调试',
|
||||||
|
content: message,
|
||||||
|
duration: 15000,
|
||||||
|
keepAliveOnHover: true
|
||||||
|
})
|
||||||
|
} catch (error: any) {
|
||||||
|
notification.error({
|
||||||
|
title: '🔧 调试失败',
|
||||||
|
content: `无法获取机器人状态: ${error?.message || '网络错误或服务未运行'}`,
|
||||||
|
duration: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 页面加载时获取配置
|
// 页面加载时获取配置
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await fetchTelegramConfig()
|
await fetchTelegramConfig()
|
||||||
await fetchTelegramChannels()
|
await fetchTelegramChannels()
|
||||||
|
await refreshBotStatus() // 初始化机器人状态
|
||||||
console.log('Telegram 机器人标签已加载')
|
console.log('Telegram 机器人标签已加载')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ export const useTelegramApi = () => {
|
|||||||
const updateBotConfig = (data: any) => useApiFetch('/telegram/bot-config', { method: 'PUT', body: data }).then(parseApiResponse)
|
const updateBotConfig = (data: any) => useApiFetch('/telegram/bot-config', { method: 'PUT', body: data }).then(parseApiResponse)
|
||||||
const validateApiKey = (data: any) => useApiFetch('/telegram/validate-api-key', { method: 'POST', body: data }).then(parseApiResponse)
|
const validateApiKey = (data: any) => useApiFetch('/telegram/validate-api-key', { method: 'POST', body: data }).then(parseApiResponse)
|
||||||
const getBotStatus = () => useApiFetch('/telegram/bot-status').then(parseApiResponse)
|
const getBotStatus = () => useApiFetch('/telegram/bot-status').then(parseApiResponse)
|
||||||
|
const debugBotConnection = () => useApiFetch('/telegram/debug-connection').then(parseApiResponse)
|
||||||
const reloadBotConfig = () => useApiFetch('/telegram/reload-config', { method: 'POST' }).then(parseApiResponse)
|
const reloadBotConfig = () => useApiFetch('/telegram/reload-config', { method: 'POST' }).then(parseApiResponse)
|
||||||
const testBotMessage = (data: any) => useApiFetch('/telegram/test-message', { method: 'POST', body: data }).then(parseApiResponse)
|
const testBotMessage = (data: any) => useApiFetch('/telegram/test-message', { method: 'POST', body: data }).then(parseApiResponse)
|
||||||
const getChannels = () => useApiFetch('/telegram/channels').then(parseApiResponse)
|
const getChannels = () => useApiFetch('/telegram/channels').then(parseApiResponse)
|
||||||
@@ -283,6 +284,7 @@ export const useTelegramApi = () => {
|
|||||||
updateBotConfig,
|
updateBotConfig,
|
||||||
validateApiKey,
|
validateApiKey,
|
||||||
getBotStatus,
|
getBotStatus,
|
||||||
|
debugBotConnection,
|
||||||
reloadBotConfig,
|
reloadBotConfig,
|
||||||
testBotMessage,
|
testBotMessage,
|
||||||
getChannels,
|
getChannels,
|
||||||
|
|||||||
Reference in New Issue
Block a user