feat: Add telegram channel for Agent (#11947)

This commit is contained in:
CityFun
2026-02-25 16:38:34 +08:00
committed by GitHub
parent f9a70757a1
commit 87a39cab3a
20 changed files with 508 additions and 137 deletions
+61
View File
@@ -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)
}
+23
View File
@@ -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"`
}
+89 -2
View File
@@ -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 {
+3
View File
@@ -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)
}
}
+23
View File
@@ -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;
}
}
+12
View File
@@ -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.AgentTelegramConfig>(`/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);
};
+1 -1
View File
@@ -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/.',
+1 -1
View File
@@ -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/',
+1 -1
View File
@@ -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/ で始めてください',
+1 -1
View File
@@ -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/ 로 시작해야 합니다',
+1 -1
View File
@@ -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/',
+1 -1
View File
@@ -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/',
+1 -1
View File
@@ -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/',
+1 -1
View File
@@ -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',
+1 -1
View File
@@ -682,7 +682,7 @@ const message = {
pairingCodePlaceholder: '請輸入配對碼',
approvePairing: '批准配對',
feishuRequired: '請填寫 botName / appId / appSecret',
feishuSaveSuccess: '保存成功',
saveSuccess: '保存成功',
pairingCodeRequired: '請輸入配對碼',
pairingApproveSuccess: '配對成功',
customModelHelper: '自訂模型帳號模型需固定以 custom/ 開頭',
+1 -1
View File
@@ -684,7 +684,7 @@ const message = {
pairingCodePlaceholder: '请输入配对码',
approvePairing: '批准配对',
feishuRequired: '请填写 botName / appId / appSecret',
feishuSaveSuccess: '保存成功',
saveSuccess: '保存成功',
pairingCodeRequired: '请输入配对码',
pairingApproveSuccess: '配对成功',
customModelHelper: '自定义模型账号模型固定以 custom/ 开头',
@@ -1,13 +1,15 @@
<template>
<DrawerPro v-model="open" :header="header" size="large" @close="handleClose">
<el-tabs v-model="activeTab" tab-position="left" class="config-tabs" @tab-click="handleTabClick">
<el-tab-pane :label="t('aiTools.model.model')" name="model">
<ModelTab ref="modelRef" @updated="handleModelUpdated" />
</el-tab-pane>
<el-tab-pane :label="t('aiTools.agents.channelsTab')" name="channels">
<ChannelsTab ref="channelsRef" />
</el-tab-pane>
</el-tabs>
<template #content>
<el-tabs v-model="activeTab" tab-position="left" class="config-tabs" @tab-click="handleTabClick">
<el-tab-pane :label="t('aiTools.model.model')" name="model">
<ModelTab ref="modelRef" @updated="handleModelUpdated" />
</el-tab-pane>
<el-tab-pane :label="t('aiTools.agents.channelsTab')" name="channels">
<ChannelsTab ref="channelsRef" />
</el-tab-pane>
</el-tabs>
</template>
</DrawerPro>
</template>
@@ -1,134 +1,45 @@
<template>
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
<el-form-item :label="t('aiTools.agents.feishu')">
<el-switch v-model="form.enabled" />
</el-form-item>
<el-form-item>
<el-link type="primary" icon="Position" @click="toFeishuDoc">
{{ t('container.mirrorsHelper2') }}
</el-link>
</el-form-item>
<el-form-item :label="t('aiTools.agents.dmPolicy')" prop="dmPolicy">
<el-select v-model="form.dmPolicy">
<el-option label="pairing" value="pairing" />
</el-select>
</el-form-item>
<el-form-item :label="t('aiTools.agents.botName')" prop="botName">
<el-input v-model="form.botName" />
</el-form-item>
<el-form-item :label="t('aiTools.agents.appId')" prop="appId">
<el-input v-model="form.appId" />
</el-form-item>
<el-form-item :label="t('aiTools.agents.appSecret')" prop="appSecret">
<el-input v-model="form.appSecret" type="password" show-password />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveChannel">
{{ t('commons.button.save') }}
</el-button>
</el-form-item>
<el-divider />
<el-form-item :label="t('aiTools.agents.pairingCode')">
<el-input v-model="pairingCode" :placeholder="t('aiTools.agents.pairingCodePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="approving" @click="approvePairing">
{{ t('aiTools.agents.approvePairing') }}
</el-button>
</el-form-item>
</el-form>
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane :label="t('aiTools.agents.feishu')" name="feishu">
<FeishuTab ref="feishuRef" />
</el-tab-pane>
<el-tab-pane label="Telegram" name="telegram">
<TelegramTab ref="telegramRef" />
</el-tab-pane>
</el-tabs>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import type { FormInstance } from 'element-plus';
import { nextTick, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { AI } from '@/api/interface/ai';
import { approveAgentFeishuPairing, getAgentFeishuConfig, updateAgentFeishuConfig } from '@/api/modules/ai';
import { MsgSuccess, MsgWarning } from '@/utils/message';
import { Rules } from '@/global/form-rules';
import FeishuTab from './channels/feishu.vue';
import TelegramTab from './channels/telegram.vue';
const { t } = useI18n();
const saving = ref(false);
const approving = ref(false);
const activeTab = ref('feishu');
const agentId = ref(0);
const pairingCode = ref('');
const formRef = ref<FormInstance>();
const feishuRef = ref();
const telegramRef = ref();
const form = reactive<AI.AgentFeishuConfig>({
enabled: true,
dmPolicy: 'pairing',
botName: '',
appId: '',
appSecret: '',
});
const loadCurrentTab = async () => {
if (agentId.value <= 0) {
return;
}
await nextTick();
if (activeTab.value === 'telegram') {
await telegramRef.value?.load(agentId.value);
return;
}
await feishuRef.value?.load(agentId.value);
};
const rules = reactive({
dmPolicy: [Rules.requiredSelect],
botName: [Rules.requiredInput],
appId: [Rules.requiredInput],
appSecret: [Rules.requiredInput],
});
const toFeishuDoc = () => {
window.open('https://openclaw.club/guides/feishu-platform', '_blank');
const handleTabClick = async () => {
await loadCurrentTab();
};
const load = async (id: number) => {
agentId.value = id;
pairingCode.value = '';
const res = await getAgentFeishuConfig({ agentId: id });
Object.assign(form, res.data || {});
if (!form.dmPolicy) {
form.dmPolicy = 'pairing';
}
};
const saveChannel = async () => {
if (!agentId.value) {
return;
}
if (!formRef.value) {
return;
}
await formRef.value.validate();
saving.value = true;
try {
await updateAgentFeishuConfig({
agentId: agentId.value,
enabled: form.enabled,
dmPolicy: form.dmPolicy || 'pairing',
botName: form.botName,
appId: form.appId,
appSecret: form.appSecret,
});
MsgSuccess(t('aiTools.agents.feishuSaveSuccess'));
} finally {
saving.value = false;
}
};
const approvePairing = async () => {
if (!agentId.value) {
return;
}
if (!pairingCode.value) {
MsgWarning(t('aiTools.agents.pairingCodeRequired'));
return;
}
approving.value = true;
try {
await approveAgentFeishuPairing({
agentId: agentId.value,
pairingCode: pairingCode.value,
});
MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));
pairingCode.value = '';
} finally {
approving.value = false;
}
await loadCurrentTab();
};
defineExpose({
@@ -0,0 +1,135 @@
<template>
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
<el-form-item :label="t('commons.table.status')">
<el-switch v-model="form.enabled" />
</el-form-item>
<el-form-item>
<el-link type="primary" icon="Position" @click="toFeishuDoc">
{{ t('container.mirrorsHelper2') }}
</el-link>
</el-form-item>
<el-form-item :label="t('aiTools.agents.dmPolicy')" prop="dmPolicy">
<el-select v-model="form.dmPolicy">
<el-option label="pairing" value="pairing" />
</el-select>
</el-form-item>
<el-form-item :label="t('aiTools.agents.botName')" prop="botName">
<el-input v-model="form.botName" />
</el-form-item>
<el-form-item :label="t('aiTools.agents.appId')" prop="appId">
<el-input v-model="form.appId" />
</el-form-item>
<el-form-item :label="t('aiTools.agents.appSecret')" prop="appSecret">
<el-input v-model="form.appSecret" type="password" show-password />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveChannel">
{{ t('commons.button.save') }}
</el-button>
</el-form-item>
<el-divider />
<el-form-item :label="t('aiTools.agents.pairingCode')">
<el-input v-model="pairingCode" :placeholder="t('aiTools.agents.pairingCodePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="approving" @click="approvePairing">
{{ t('aiTools.agents.approvePairing') }}
</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import type { FormInstance } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { AI } from '@/api/interface/ai';
import { approveAgentChannelPairing, getAgentFeishuConfig, updateAgentFeishuConfig } from '@/api/modules/ai';
import { MsgSuccess, MsgWarning } from '@/utils/message';
import { Rules } from '@/global/form-rules';
const { t } = useI18n();
const saving = ref(false);
const approving = ref(false);
const agentId = ref(0);
const pairingCode = ref('');
const formRef = ref<FormInstance>();
const form = reactive<AI.AgentFeishuConfig>({
enabled: true,
dmPolicy: 'pairing',
botName: '',
appId: '',
appSecret: '',
});
const rules = reactive({
dmPolicy: [Rules.requiredSelect],
botName: [Rules.requiredInput],
appId: [Rules.requiredInput],
appSecret: [Rules.requiredInput],
});
const toFeishuDoc = () => {
window.open('https://openclaw.club/guides/feishu-platform', '_blank');
};
const load = async (id: number) => {
agentId.value = id;
pairingCode.value = '';
const res = await getAgentFeishuConfig({ agentId: id });
Object.assign(form, res.data || {});
if (!form.dmPolicy) {
form.dmPolicy = 'pairing';
}
};
const saveChannel = async () => {
if (!agentId.value || !formRef.value) {
return;
}
await formRef.value.validate();
saving.value = true;
try {
await updateAgentFeishuConfig({
agentId: agentId.value,
enabled: form.enabled,
dmPolicy: form.dmPolicy || 'pairing',
botName: form.botName,
appId: form.appId,
appSecret: form.appSecret,
});
MsgSuccess(t('aiTools.agents.saveSuccess'));
} finally {
saving.value = false;
}
};
const approvePairing = async () => {
if (!agentId.value) {
return;
}
if (!pairingCode.value) {
MsgWarning(t('aiTools.agents.pairingCodeRequired'));
return;
}
approving.value = true;
try {
await approveAgentChannelPairing({
agentId: agentId.value,
type: 'feishu',
pairingCode: pairingCode.value,
});
MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));
pairingCode.value = '';
} finally {
approving.value = false;
}
};
defineExpose({
load,
});
</script>
@@ -0,0 +1,114 @@
<template>
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
<el-form-item :label="t('commons.table.status')">
<el-switch v-model="form.enabled" />
</el-form-item>
<el-form-item :label="t('aiTools.agents.dmPolicy')" prop="dmPolicy">
<el-select v-model="form.dmPolicy">
<el-option label="pairing" value="pairing" />
</el-select>
</el-form-item>
<el-form-item label="Bot Token" prop="botToken">
<el-input v-model="form.botToken" type="password" show-password />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveChannel">
{{ t('commons.button.save') }}
</el-button>
</el-form-item>
<el-divider />
<el-form-item :label="t('aiTools.agents.pairingCode')">
<el-input v-model="pairingCode" :placeholder="t('aiTools.agents.pairingCodePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="approving" @click="approvePairing">
{{ t('aiTools.agents.approvePairing') }}
</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import type { FormInstance } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { AI } from '@/api/interface/ai';
import { approveAgentChannelPairing, getAgentTelegramConfig, updateAgentTelegramConfig } from '@/api/modules/ai';
import { MsgSuccess, MsgWarning } from '@/utils/message';
import { Rules } from '@/global/form-rules';
const { t } = useI18n();
const saving = ref(false);
const approving = ref(false);
const agentId = ref(0);
const pairingCode = ref('');
const formRef = ref<FormInstance>();
const form = reactive<AI.AgentTelegramConfig>({
enabled: true,
dmPolicy: 'pairing',
botToken: '',
});
const rules = reactive({
dmPolicy: [Rules.requiredSelect],
botToken: [Rules.requiredInput],
});
const load = async (id: number) => {
agentId.value = id;
pairingCode.value = '';
const res = await getAgentTelegramConfig({ agentId: id });
Object.assign(form, res.data || {});
if (!form.dmPolicy) {
form.dmPolicy = 'pairing';
}
};
const saveChannel = async () => {
if (!agentId.value || !formRef.value) {
return;
}
await formRef.value.validate();
saving.value = true;
try {
await updateAgentTelegramConfig({
agentId: agentId.value,
enabled: form.enabled,
dmPolicy: form.dmPolicy || 'pairing',
botToken: form.botToken,
});
MsgSuccess(t('aiTools.agents.saveSuccess'));
} finally {
saving.value = false;
}
};
const approvePairing = async () => {
if (!agentId.value) {
return;
}
if (!pairingCode.value) {
MsgWarning(t('aiTools.agents.pairingCodeRequired'));
return;
}
approving.value = true;
try {
await approveAgentChannelPairing({
agentId: agentId.value,
type: 'telegram',
pairingCode: pairingCode.value,
});
MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));
pairingCode.value = '';
} finally {
approving.value = false;
}
};
defineExpose({
load,
});
</script>