feat(telegram): add business owner configuration and message handling
- Introduced `business_owner` field in Telegram settings to specify the owner ID. - Updated `handleBusinessMessage` to ignore messages from the business owner. - Added tests to verify that messages from the owner are correctly ignored. - Enhanced Telegram form to include input for business owner ID. - Updated localization files for English, Portuguese, and Chinese to include business owner labels.
This commit is contained in:
parent
4aa6ffb043
commit
d170c057d9
9 changed files with 77 additions and 0 deletions
|
|
@ -787,10 +787,28 @@ func (c *TelegramChannel) handleBusinessMessage(ctx context.Context, message *te
|
||||||
if businessConnectionID == "" {
|
if businessConnectionID == "" {
|
||||||
return fmt.Errorf("business message missing business_connection_id")
|
return fmt.Errorf("business message missing business_connection_id")
|
||||||
}
|
}
|
||||||
|
if c.isBusinessOwnerMessage(message) {
|
||||||
|
logger.DebugCF("telegram", "Business message ignored from configured owner", map[string]any{
|
||||||
|
"business_connection_id": businessConnectionID,
|
||||||
|
"user_id": fmt.Sprintf("%d", message.From.ID),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
c.markBusinessMessageRead(ctx, businessConnectionID, message.Chat.ID, message.MessageID)
|
c.markBusinessMessageRead(ctx, businessConnectionID, message.Chat.ID, message.MessageID)
|
||||||
return c.handleTelegramMessage(ctx, message, businessConnectionID)
|
return c.handleTelegramMessage(ctx, message, businessConnectionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *TelegramChannel) isBusinessOwnerMessage(message *telego.Message) bool {
|
||||||
|
if c == nil || c.tgCfg == nil || message == nil || message.From == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ownerID := strings.TrimSpace(c.tgCfg.BusinessOwner)
|
||||||
|
if ownerID == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ownerID == fmt.Sprintf("%d", message.From.ID)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *TelegramChannel) markBusinessMessageRead(
|
func (c *TelegramChannel) markBusinessMessageRead(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
businessConnectionID string,
|
businessConnectionID string,
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,43 @@ func TestHandleBusinessMessage_DisabledBusinessModeIgnoresMessage(t *testing.T)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleBusinessMessage_BusinessOwnerIgnoresMessage(t *testing.T) {
|
||||||
|
messageBus := bus.NewMessageBus()
|
||||||
|
ch := &TelegramChannel{
|
||||||
|
BaseChannel: channels.NewBaseChannel("telegram", nil, messageBus, nil),
|
||||||
|
chatIDs: make(map[string]int64),
|
||||||
|
ctx: context.Background(),
|
||||||
|
tgCfg: &config.TelegramSettings{
|
||||||
|
BusinessMode: true,
|
||||||
|
BusinessOwner: "42",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &telego.Message{
|
||||||
|
Text: "owner should be ignored",
|
||||||
|
MessageID: 19,
|
||||||
|
BusinessConnectionID: "biz-conn-1",
|
||||||
|
Chat: telego.Chat{
|
||||||
|
ID: 777,
|
||||||
|
Type: "private",
|
||||||
|
},
|
||||||
|
From: &telego.User{
|
||||||
|
ID: 42,
|
||||||
|
FirstName: "Owner",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ch.handleBusinessMessage(context.Background(), msg); err != nil {
|
||||||
|
t.Fatalf("handleBusinessMessage error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case inbound := <-messageBus.InboundChan():
|
||||||
|
t.Fatalf("expected owner business message to be ignored, got %#v", inbound)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTelegramAllowedUpdates_BusinessMode(t *testing.T) {
|
func TestTelegramAllowedUpdates_BusinessMode(t *testing.T) {
|
||||||
disabled := strings.Join(telegramAllowedUpdates(false), ",")
|
disabled := strings.Join(telegramAllowedUpdates(false), ",")
|
||||||
if strings.Contains(disabled, telego.BusinessMessageUpdates) {
|
if strings.Contains(disabled, telego.BusinessMessageUpdates) {
|
||||||
|
|
|
||||||
|
|
@ -364,6 +364,7 @@ type TelegramSettings struct {
|
||||||
Proxy string `json:"proxy" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_PROXY"`
|
Proxy string `json:"proxy" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_PROXY"`
|
||||||
Streaming StreamingConfig `json:"streaming,omitempty" yaml:"-"`
|
Streaming StreamingConfig `json:"streaming,omitempty" yaml:"-"`
|
||||||
BusinessMode bool `json:"business_mode" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_BUSINESS_MODE"`
|
BusinessMode bool `json:"business_mode" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_BUSINESS_MODE"`
|
||||||
|
BusinessOwner string `json:"business_owner" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_BUSINESS_OWNER"`
|
||||||
UseMarkdownV2 bool `json:"use_markdown_v2" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_USE_MARKDOWN_V2"`
|
UseMarkdownV2 bool `json:"use_markdown_v2" yaml:"-" env:"PICOCLAW_CHANNELS_TELEGRAM_USE_MARKDOWN_V2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ type testTelegramConfig struct {
|
||||||
BaseURL string `json:"base_url" yaml:"-"`
|
BaseURL string `json:"base_url" yaml:"-"`
|
||||||
Proxy string `json:"proxy" yaml:"-"`
|
Proxy string `json:"proxy" yaml:"-"`
|
||||||
BusinessMode bool `json:"business_mode" yaml:"-"`
|
BusinessMode bool `json:"business_mode" yaml:"-"`
|
||||||
|
BusinessOwner string `json:"business_owner" yaml:"-"`
|
||||||
UseMarkdownV2 bool `json:"use_markdown_v2" yaml:"-"`
|
UseMarkdownV2 bool `json:"use_markdown_v2" yaml:"-"`
|
||||||
Streaming StreamingConfig `json:"streaming,omitempty" yaml:"-"`
|
Streaming StreamingConfig `json:"streaming,omitempty" yaml:"-"`
|
||||||
Token SecureString `json:"token,omitzero" yaml:"token,omitempty"`
|
Token SecureString `json:"token,omitzero" yaml:"token,omitempty"`
|
||||||
|
|
@ -109,6 +110,7 @@ func TestChannel_JSON_Unmarshal(t *testing.T) {
|
||||||
"settings": {
|
"settings": {
|
||||||
"base_url": "https://custom-api.example.com",
|
"base_url": "https://custom-api.example.com",
|
||||||
"business_mode": true,
|
"business_mode": true,
|
||||||
|
"business_owner": "42",
|
||||||
"use_markdown_v2": true,
|
"use_markdown_v2": true,
|
||||||
"streaming": {"enabled": true, "throttle_seconds": 2},
|
"streaming": {"enabled": true, "throttle_seconds": 2},
|
||||||
"token": "[NOT_HERE]"
|
"token": "[NOT_HERE]"
|
||||||
|
|
@ -129,6 +131,7 @@ func TestChannel_JSON_Unmarshal(t *testing.T) {
|
||||||
require.NoError(t, ch.Decode(&cfg))
|
require.NoError(t, ch.Decode(&cfg))
|
||||||
assert.Equal(t, "https://custom-api.example.com", cfg.BaseURL)
|
assert.Equal(t, "https://custom-api.example.com", cfg.BaseURL)
|
||||||
assert.True(t, cfg.BusinessMode)
|
assert.True(t, cfg.BusinessMode)
|
||||||
|
assert.Equal(t, "42", cfg.BusinessOwner)
|
||||||
assert.True(t, cfg.UseMarkdownV2)
|
assert.True(t, cfg.UseMarkdownV2)
|
||||||
assert.True(t, cfg.Streaming.Enabled)
|
assert.True(t, cfg.Streaming.Enabled)
|
||||||
assert.Equal(t, 2, cfg.Streaming.ThrottleSeconds)
|
assert.Equal(t, 2, cfg.Streaming.ThrottleSeconds)
|
||||||
|
|
|
||||||
|
|
@ -498,6 +498,7 @@ func defaultChannels() ChannelsConfig {
|
||||||
"settings": map[string]any{
|
"settings": map[string]any{
|
||||||
"streaming": map[string]any{"enabled": true, "throttle_seconds": 3, "min_growth_chars": 200},
|
"streaming": map[string]any{"enabled": true, "throttle_seconds": 3, "min_growth_chars": 200},
|
||||||
"business_mode": false,
|
"business_mode": false,
|
||||||
|
"business_owner": "",
|
||||||
"use_markdown_v2": false,
|
"use_markdown_v2": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,17 @@ export function TelegramForm({
|
||||||
ariaLabel={t("channels.field.businessMode")}
|
ariaLabel={t("channels.field.businessMode")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
label={t("channels.field.businessOwner")}
|
||||||
|
hint={t("channels.form.desc.businessOwner")}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={asString(config.business_owner)}
|
||||||
|
onChange={(e) => onChange("business_owner", e.target.value)}
|
||||||
|
placeholder="123456789"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,7 @@
|
||||||
"encryptKey": "Encrypt Key",
|
"encryptKey": "Encrypt Key",
|
||||||
"baseUrl": "API Base URL",
|
"baseUrl": "API Base URL",
|
||||||
"businessMode": "Business Mode",
|
"businessMode": "Business Mode",
|
||||||
|
"businessOwner": "Business Owner",
|
||||||
"proxy": "HTTP Proxy",
|
"proxy": "HTTP Proxy",
|
||||||
"mentionOnly": "Mention Only",
|
"mentionOnly": "Mention Only",
|
||||||
"typingEnabled": "Typing Indicator",
|
"typingEnabled": "Typing Indicator",
|
||||||
|
|
@ -416,6 +417,7 @@
|
||||||
"encryptKey": "Encryption key used to decrypt callback payloads.",
|
"encryptKey": "Encryption key used to decrypt callback payloads.",
|
||||||
"baseUrl": "Platform API base URL. Official endpoint is used by default.",
|
"baseUrl": "Platform API base URL. Official endpoint is used by default.",
|
||||||
"businessMode": "Receive and reply to Telegram Business messages for connected business accounts.",
|
"businessMode": "Receive and reply to Telegram Business messages for connected business accounts.",
|
||||||
|
"businessOwner": "Telegram user ID of the business account owner. Business messages from this user are ignored.",
|
||||||
"proxy": "HTTP proxy address for outbound network access.",
|
"proxy": "HTTP proxy address for outbound network access.",
|
||||||
"mentionOnly": "Only respond when the bot is explicitly mentioned in group chats.",
|
"mentionOnly": "Only respond when the bot is explicitly mentioned in group chats.",
|
||||||
"typingEnabled": "Display typing status while the assistant is generating a response.",
|
"typingEnabled": "Display typing status while the assistant is generating a response.",
|
||||||
|
|
|
||||||
|
|
@ -368,6 +368,7 @@
|
||||||
"encryptKey": "Chave de Criptografia",
|
"encryptKey": "Chave de Criptografia",
|
||||||
"baseUrl": "URL Base da API",
|
"baseUrl": "URL Base da API",
|
||||||
"businessMode": "Modo Business",
|
"businessMode": "Modo Business",
|
||||||
|
"businessOwner": "Proprietário Business",
|
||||||
"proxy": "Proxy HTTP",
|
"proxy": "Proxy HTTP",
|
||||||
"mentionOnly": "Apenas com Menção",
|
"mentionOnly": "Apenas com Menção",
|
||||||
"typingEnabled": "Indicador de Digitação",
|
"typingEnabled": "Indicador de Digitação",
|
||||||
|
|
@ -408,6 +409,7 @@
|
||||||
"encryptKey": "Chave de criptografia usada para descriptografar payloads de callback.",
|
"encryptKey": "Chave de criptografia usada para descriptografar payloads de callback.",
|
||||||
"baseUrl": "URL base da API da plataforma. O endpoint oficial é usado por padrão.",
|
"baseUrl": "URL base da API da plataforma. O endpoint oficial é usado por padrão.",
|
||||||
"businessMode": "Receber e responder mensagens do Telegram Business para contas comerciais conectadas.",
|
"businessMode": "Receber e responder mensagens do Telegram Business para contas comerciais conectadas.",
|
||||||
|
"businessOwner": "ID de usuário do Telegram do proprietário da conta comercial. Mensagens Business desse usuário são ignoradas.",
|
||||||
"proxy": "Endereço de proxy HTTP para acesso de rede de saída.",
|
"proxy": "Endereço de proxy HTTP para acesso de rede de saída.",
|
||||||
"mentionOnly": "Responder apenas quando o bot for explicitamente mencionado em chats em grupo.",
|
"mentionOnly": "Responder apenas quando o bot for explicitamente mencionado em chats em grupo.",
|
||||||
"typingEnabled": "Exibir status de digitação enquanto o assistente está gerando uma resposta.",
|
"typingEnabled": "Exibir status de digitação enquanto o assistente está gerando uma resposta.",
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,7 @@
|
||||||
"encryptKey": "Encrypt Key",
|
"encryptKey": "Encrypt Key",
|
||||||
"baseUrl": "API Base URL",
|
"baseUrl": "API Base URL",
|
||||||
"businessMode": "Business Mode",
|
"businessMode": "Business Mode",
|
||||||
|
"businessOwner": "Business Owner",
|
||||||
"proxy": "HTTP 代理",
|
"proxy": "HTTP 代理",
|
||||||
"mentionOnly": "仅提及时响应",
|
"mentionOnly": "仅提及时响应",
|
||||||
"typingEnabled": "输入中提示",
|
"typingEnabled": "输入中提示",
|
||||||
|
|
@ -416,6 +417,7 @@
|
||||||
"encryptKey": "消息加密密钥,用于解密回调内容",
|
"encryptKey": "消息加密密钥,用于解密回调内容",
|
||||||
"baseUrl": "平台 API 地址,默认使用官方地址",
|
"baseUrl": "平台 API 地址,默认使用官方地址",
|
||||||
"businessMode": "接收并回复已连接商业账号的 Telegram Business 消息",
|
"businessMode": "接收并回复已连接商业账号的 Telegram Business 消息",
|
||||||
|
"businessOwner": "商业账号所有者的 Telegram 用户 ID。来自该用户的 Business 消息会被忽略。",
|
||||||
"proxy": "HTTP 代理地址,用于网络访问",
|
"proxy": "HTTP 代理地址,用于网络访问",
|
||||||
"mentionOnly": "在群聊中仅当明确提及时才响应",
|
"mentionOnly": "在群聊中仅当明确提及时才响应",
|
||||||
"typingEnabled": "在生成回复时显示“正在输入”状态",
|
"typingEnabled": "在生成回复时显示“正在输入”状态",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue