From 87a39cab3aa0d4540bae466aca5ebd8f6a7b06ff Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Wed, 25 Feb 2026 16:38:34 +0800 Subject: [PATCH] feat: Add telegram channel for Agent (#11947) --- agent/app/api/v2/agents.go | 61 ++++++++ agent/app/dto/agents.go | 23 +++ agent/app/service/agents.go | 91 ++++++++++- agent/router/ro_ai.go | 3 + frontend/src/api/interface/ai.ts | 23 +++ frontend/src/api/modules/ai.ts | 12 ++ frontend/src/lang/modules/en.ts | 2 +- frontend/src/lang/modules/es-es.ts | 2 +- frontend/src/lang/modules/ja.ts | 2 +- frontend/src/lang/modules/ko.ts | 2 +- frontend/src/lang/modules/ms.ts | 2 +- frontend/src/lang/modules/pt-br.ts | 2 +- frontend/src/lang/modules/ru.ts | 2 +- frontend/src/lang/modules/tr.ts | 2 +- frontend/src/lang/modules/zh-Hant.ts | 2 +- frontend/src/lang/modules/zh.ts | 2 +- .../views/ai/agents/agent/config/index.vue | 18 ++- .../ai/agents/agent/config/tabs/channels.vue | 145 ++++-------------- .../agent/config/tabs/channels/feishu.vue | 135 ++++++++++++++++ .../agent/config/tabs/channels/telegram.vue | 114 ++++++++++++++ 20 files changed, 508 insertions(+), 137 deletions(-) create mode 100644 frontend/src/views/ai/agents/agent/config/tabs/channels/feishu.vue create mode 100644 frontend/src/views/ai/agents/agent/config/tabs/channels/telegram.vue diff --git a/agent/app/api/v2/agents.go b/agent/app/api/v2/agents.go index f13d3b33a..5a9861250 100644 --- a/agent/app/api/v2/agents.go +++ b/agent/app/api/v2/agents.go @@ -271,6 +271,47 @@ func (b *BaseApi) UpdateAgentFeishuConfig(c *gin.Context) { helper.Success(c) } +// @Tags AI +// @Summary Get Agent Telegram channel config +// @Accept json +// @Param request body dto.AgentTelegramConfigReq true "request" +// @Success 200 {object} dto.AgentTelegramConfig +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/channel/telegram/get [post] +func (b *BaseApi) GetAgentTelegramConfig(c *gin.Context) { + var req dto.AgentTelegramConfigReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + data, err := agentService.GetTelegramConfig(req) + if err != nil { + helper.BadRequest(c, err) + return + } + helper.SuccessWithData(c, data) +} + +// @Tags AI +// @Summary Update Agent Telegram channel config +// @Accept json +// @Param request body dto.AgentTelegramConfigUpdateReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/channel/telegram/update [post] +func (b *BaseApi) UpdateAgentTelegramConfig(c *gin.Context) { + var req dto.AgentTelegramConfigUpdateReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := agentService.UpdateTelegramConfig(req); err != nil { + helper.BadRequest(c, err) + return + } + helper.Success(c) +} + // @Tags AI // @Summary Approve Agent Feishu pairing code // @Accept json @@ -290,3 +331,23 @@ func (b *BaseApi) ApproveAgentFeishuPairing(c *gin.Context) { } helper.Success(c) } + +// @Tags AI +// @Summary Approve Agent channel pairing code +// @Accept json +// @Param request body dto.AgentChannelPairingApproveReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/channel/pairing/approve [post] +func (b *BaseApi) ApproveAgentChannelPairing(c *gin.Context) { + var req dto.AgentChannelPairingApproveReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := agentService.ApproveChannelPairing(req); err != nil { + helper.BadRequest(c, err) + return + } + helper.Success(c) +} diff --git a/agent/app/dto/agents.go b/agent/app/dto/agents.go index 266564a0f..b73324f84 100644 --- a/agent/app/dto/agents.go +++ b/agent/app/dto/agents.go @@ -170,3 +170,26 @@ type AgentFeishuConfig struct { AppID string `json:"appId"` AppSecret string `json:"appSecret"` } + +type AgentTelegramConfigReq struct { + AgentID uint `json:"agentId" validate:"required"` +} + +type AgentTelegramConfigUpdateReq struct { + AgentID uint `json:"agentId" validate:"required"` + Enabled bool `json:"enabled"` + DmPolicy string `json:"dmPolicy" validate:"required"` + BotToken string `json:"botToken" validate:"required"` +} + +type AgentTelegramConfig struct { + Enabled bool `json:"enabled"` + DmPolicy string `json:"dmPolicy"` + BotToken string `json:"botToken"` +} + +type AgentChannelPairingApproveReq struct { + AgentID uint `json:"agentId" validate:"required"` + Type string `json:"type" validate:"required,oneof=feishu telegram"` + PairingCode string `json:"pairingCode" validate:"required"` +} diff --git a/agent/app/service/agents.go b/agent/app/service/agents.go index cf8f42da6..2f1733b5e 100644 --- a/agent/app/service/agents.go +++ b/agent/app/service/agents.go @@ -43,6 +43,9 @@ type IAgentService interface { DeleteAccount(req dto.AgentAccountDeleteReq) error GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error + GetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.AgentTelegramConfig, error) + UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error + ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error } @@ -570,14 +573,58 @@ func (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) err return nil } -func (a AgentService) ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error { +func (a AgentService) GetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.AgentTelegramConfig, error) { + agent, _, err := a.loadAgentAndInstall(req.AgentID) + if err != nil { + return nil, err + } + conf, err := readOpenclawConfig(agent.ConfigPath) + if err != nil { + return nil, err + } + result := extractTelegramConfig(conf) + return &result, nil +} + +func (a AgentService) UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error { + agent, _, err := a.loadAgentAndInstall(req.AgentID) + if err != nil { + return err + } + conf, err := readOpenclawConfig(agent.ConfigPath) + if err != nil { + return err + } + if req.DmPolicy == "" { + req.DmPolicy = "pairing" + } + setTelegramConfig(conf, dto.AgentTelegramConfig{ + Enabled: req.Enabled, + DmPolicy: req.DmPolicy, + BotToken: req.BotToken, + }) + if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil { + return err + } + return nil +} + +func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error { _, install, err := a.loadAgentAndInstall(req.AgentID) if err != nil { return err } + channelType := strings.ToLower(strings.TrimSpace(req.Type)) + if channelType == "" { + channelType = "feishu" + } + if channelType != "feishu" && channelType != "telegram" { + return fmt.Errorf("unsupported channel type: %s", channelType) + } if err := cmd.RunDefaultBashCf( - "docker exec %s openclaw pairing approve feishu %q", + "docker exec %s openclaw pairing approve %s %q", install.ContainerName, + channelType, strings.TrimSpace(req.PairingCode), ); err != nil { return err @@ -585,6 +632,14 @@ func (a AgentService) ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) return nil } +func (a AgentService) ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error { + return a.ApproveChannelPairing(dto.AgentChannelPairingApproveReq{ + AgentID: req.AgentID, + Type: "feishu", + PairingCode: req.PairingCode, + }) +} + func (a AgentService) loadAgentAndInstall(agentID uint) (*model.Agent, *model.AppInstall, error) { agent, err := agentRepo.GetFirst(repo.WithByID(agentID)) if err != nil { @@ -688,6 +743,38 @@ func setFeishuPluginEnabled(conf map[string]interface{}, enabled bool) { feishu["enabled"] = enabled } +func extractTelegramConfig(conf map[string]interface{}) dto.AgentTelegramConfig { + result := dto.AgentTelegramConfig{Enabled: true, DmPolicy: "pairing"} + channels, ok := conf["channels"].(map[string]interface{}) + if !ok { + return result + } + telegram, ok := channels["telegram"].(map[string]interface{}) + if !ok { + return result + } + if enabled, ok := telegram["enabled"].(bool); ok { + result.Enabled = enabled + } + if dmPolicy, ok := telegram["dmPolicy"].(string); ok && strings.TrimSpace(dmPolicy) != "" { + result.DmPolicy = dmPolicy + } + if botToken, ok := telegram["botToken"].(string); ok { + result.BotToken = botToken + } + return result +} + +func setTelegramConfig(conf map[string]interface{}, config dto.AgentTelegramConfig) { + channels := ensureChildMap(conf, "channels") + telegram := map[string]interface{}{ + "enabled": config.Enabled, + "dmPolicy": config.DmPolicy, + "botToken": config.BotToken, + } + channels["telegram"] = telegram +} + func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error { agents, err := agentRepo.List(repo.WithByAccountID(account.ID)) if err != nil { diff --git a/agent/router/ro_ai.go b/agent/router/ro_ai.go index e426a58fb..01797d8ad 100644 --- a/agent/router/ro_ai.go +++ b/agent/router/ro_ai.go @@ -54,5 +54,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) { aiToolsRouter.POST("/agents/channel/feishu/get", baseApi.GetAgentFeishuConfig) aiToolsRouter.POST("/agents/channel/feishu/update", baseApi.UpdateAgentFeishuConfig) aiToolsRouter.POST("/agents/channel/feishu/approve", baseApi.ApproveAgentFeishuPairing) + aiToolsRouter.POST("/agents/channel/telegram/get", baseApi.GetAgentTelegramConfig) + aiToolsRouter.POST("/agents/channel/telegram/update", baseApi.UpdateAgentTelegramConfig) + aiToolsRouter.POST("/agents/channel/pairing/approve", baseApi.ApproveAgentChannelPairing) } } diff --git a/frontend/src/api/interface/ai.ts b/frontend/src/api/interface/ai.ts index ebb5dff50..c35315c63 100644 --- a/frontend/src/api/interface/ai.ts +++ b/frontend/src/api/interface/ai.ts @@ -404,4 +404,27 @@ export namespace AI { agentId: number; pairingCode: string; } + + export interface AgentTelegramConfigReq { + agentId: number; + } + + export interface AgentTelegramConfig { + enabled: boolean; + dmPolicy: string; + botToken: string; + } + + export interface AgentTelegramConfigUpdateReq { + agentId: number; + enabled: boolean; + dmPolicy: string; + botToken: string; + } + + export interface AgentChannelPairingApproveReq { + agentId: number; + type: 'feishu' | 'telegram'; + pairingCode: string; + } } diff --git a/frontend/src/api/modules/ai.ts b/frontend/src/api/modules/ai.ts index 58373fb97..14c768040 100644 --- a/frontend/src/api/modules/ai.ts +++ b/frontend/src/api/modules/ai.ts @@ -147,3 +147,15 @@ export const updateAgentFeishuConfig = (req: AI.AgentFeishuConfigUpdateReq) => { export const approveAgentFeishuPairing = (req: AI.AgentFeishuPairingApproveReq) => { return http.post(`/ai/agents/channel/feishu/approve`, req); }; + +export const getAgentTelegramConfig = (req: AI.AgentTelegramConfigReq) => { + return http.post(`/ai/agents/channel/telegram/get`, req); +}; + +export const updateAgentTelegramConfig = (req: AI.AgentTelegramConfigUpdateReq) => { + return http.post(`/ai/agents/channel/telegram/update`, req); +}; + +export const approveAgentChannelPairing = (req: AI.AgentChannelPairingApproveReq) => { + return http.post(`/ai/agents/channel/pairing/approve`, req); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 66eab3046..e493edac6 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -710,7 +710,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: 'For custom model accounts, model names must start with custom/.', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index fcab71e8b..272d4d147 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -706,7 +706,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: 'En la cuenta de modelo personalizada, el nombre del modelo debe empezar por custom/', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index a0cfe0d48..5369e9212 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -695,7 +695,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: 'カスタムモデルアカウントでは、モデル名は必ず custom/ で始めてください', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 63f7b143c..f4fb9db07 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -692,7 +692,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: '사용자 정의 모델 계정의 모델명은 반드시 custom/ 로 시작해야 합니다', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 9dcd65a81..1a1e0bd18 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -707,7 +707,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: 'Akaun model tersuai, nama model mesti bermula dengan custom/', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 7411759df..3f623cbad 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -704,7 +704,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: 'Conta de modelo personalizada: o nome do modelo deve começar com custom/', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index f0ff2fda3..17d3fd1be 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -700,7 +700,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: 'Для пользовательской учетной записи модели имя модели должно начинаться с custom/', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 1d353773d..e944776a4 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -714,7 +714,7 @@ const message = { pairingCodePlaceholder: 'Enter pairing code', approvePairing: 'Approve Pairing', feishuRequired: 'Please fill botName / appId / appSecret', - feishuSaveSuccess: 'Saved successfully', + saveSuccess: 'Saved successfully', pairingCodeRequired: 'Please enter pairing code', pairingApproveSuccess: 'Pairing approved successfully', customModelHelper: 'Özel model hesabında model adı custom/ ile başlamalıdır', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 11f3e5943..b29c3bcd7 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -682,7 +682,7 @@ const message = { pairingCodePlaceholder: '請輸入配對碼', approvePairing: '批准配對', feishuRequired: '請填寫 botName / appId / appSecret', - feishuSaveSuccess: '保存成功', + saveSuccess: '保存成功', pairingCodeRequired: '請輸入配對碼', pairingApproveSuccess: '配對成功', customModelHelper: '自訂模型帳號,模型需固定以 custom/ 開頭', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index a13dee2e0..62b1f1ab6 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -684,7 +684,7 @@ const message = { pairingCodePlaceholder: '请输入配对码', approvePairing: '批准配对', feishuRequired: '请填写 botName / appId / appSecret', - feishuSaveSuccess: '保存成功', + saveSuccess: '保存成功', pairingCodeRequired: '请输入配对码', pairingApproveSuccess: '配对成功', customModelHelper: '自定义模型账号,模型固定以 custom/ 开头', diff --git a/frontend/src/views/ai/agents/agent/config/index.vue b/frontend/src/views/ai/agents/agent/config/index.vue index be58f4b97..60264945d 100644 --- a/frontend/src/views/ai/agents/agent/config/index.vue +++ b/frontend/src/views/ai/agents/agent/config/index.vue @@ -1,13 +1,15 @@ diff --git a/frontend/src/views/ai/agents/agent/config/tabs/channels.vue b/frontend/src/views/ai/agents/agent/config/tabs/channels.vue index a958bd121..c48f43c5d 100644 --- a/frontend/src/views/ai/agents/agent/config/tabs/channels.vue +++ b/frontend/src/views/ai/agents/agent/config/tabs/channels.vue @@ -1,134 +1,45 @@ diff --git a/frontend/src/views/ai/agents/agent/config/tabs/channels/telegram.vue b/frontend/src/views/ai/agents/agent/config/tabs/channels/telegram.vue new file mode 100644 index 000000000..d170e1108 --- /dev/null +++ b/frontend/src/views/ai/agents/agent/config/tabs/channels/telegram.vue @@ -0,0 +1,114 @@ + + +