Skip to content

Commit 2b07dd7

Browse files
authored
feat(telegram): add action to send telegram message (#147)
Signed-off-by: mudler <mudler@localai.io>
1 parent 864bf8b commit 2b07dd7

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

services/actions.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const (
4646
ActionCounter = "counter"
4747
ActionCallAgents = "call_agents"
4848
ActionShellcommand = "shell-command"
49+
ActionSendTelegramMessage = "send-telegram-message"
4950
)
5051

5152
var AvailableActions = []string{
@@ -79,6 +80,7 @@ var AvailableActions = []string{
7980
ActionCounter,
8081
ActionCallAgents,
8182
ActionShellcommand,
83+
ActionSendTelegramMessage,
8284
}
8385

8486
func Actions(actionsConfigs map[string]string) func(a *state.AgentConfig) func(ctx context.Context, pool *state.AgentPool) []types.Action {
@@ -177,6 +179,8 @@ func Action(name, agentName string, config map[string]string, pool *state.AgentP
177179
a = actions.NewCallAgent(config, agentName, pool.InternalAPI())
178180
case ActionShellcommand:
179181
a = actions.NewShell(config)
182+
case ActionSendTelegramMessage:
183+
a = actions.NewSendTelegramMessageRunner(config)
180184
default:
181185
xlog.Error("Action not found", "name", name)
182186
return nil, fmt.Errorf("Action not found")
@@ -341,5 +345,10 @@ func ActionsConfigMeta() []config.FieldGroup {
341345
Label: "Call Agents",
342346
Fields: actions.CallAgentConfigMeta(),
343347
},
348+
{
349+
Name: "send-telegram-message",
350+
Label: "Send Telegram Message",
351+
Fields: actions.SendTelegramMessageConfigMeta(),
352+
},
344353
}
345354
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package actions
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strconv"
7+
8+
"github.com/go-telegram/bot"
9+
"github.com/go-telegram/bot/models"
10+
"github.com/mudler/LocalAGI/core/types"
11+
"github.com/mudler/LocalAGI/pkg/config"
12+
"github.com/mudler/LocalAGI/pkg/xstrings"
13+
"github.com/sashabaranov/go-openai/jsonschema"
14+
)
15+
16+
const (
17+
MetadataTelegramMessageSent = "telegram_message_sent"
18+
telegramMaxMessageLength = 3000
19+
)
20+
21+
type SendTelegramMessageRunner struct {
22+
token string
23+
chatID int64
24+
bot *bot.Bot
25+
}
26+
27+
func NewSendTelegramMessageRunner(config map[string]string) *SendTelegramMessageRunner {
28+
token := config["token"]
29+
if token == "" {
30+
return nil
31+
}
32+
33+
// Parse chat ID from config if present
34+
var chatID int64
35+
if configChatID := config["chat_id"]; configChatID != "" {
36+
var err error
37+
chatID, err = strconv.ParseInt(configChatID, 10, 64)
38+
if err != nil {
39+
return nil
40+
}
41+
}
42+
43+
b, err := bot.New(token)
44+
if err != nil {
45+
return nil
46+
}
47+
48+
return &SendTelegramMessageRunner{
49+
token: token,
50+
chatID: chatID,
51+
bot: b,
52+
}
53+
}
54+
55+
type TelegramMessageParams struct {
56+
ChatID int64 `json:"chat_id"`
57+
Message string `json:"message"`
58+
}
59+
60+
func (s *SendTelegramMessageRunner) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) {
61+
var messageParams TelegramMessageParams
62+
err := params.Unmarshal(&messageParams)
63+
if err != nil {
64+
return types.ActionResult{}, fmt.Errorf("failed to unmarshal params: %w", err)
65+
}
66+
67+
if s.chatID != 0 {
68+
messageParams.ChatID = s.chatID
69+
}
70+
71+
if messageParams.ChatID == 0 {
72+
return types.ActionResult{}, fmt.Errorf("chat_id is required either in config or parameters")
73+
}
74+
75+
if messageParams.Message == "" {
76+
return types.ActionResult{}, fmt.Errorf("message is required")
77+
}
78+
79+
// Split the message if it's too long
80+
messages := xstrings.SplitParagraph(messageParams.Message, telegramMaxMessageLength)
81+
82+
if len(messages) == 0 {
83+
return types.ActionResult{}, fmt.Errorf("empty message after splitting")
84+
}
85+
86+
// Send each message part
87+
for i, msg := range messages {
88+
_, err = s.bot.SendMessage(ctx, &bot.SendMessageParams{
89+
ChatID: messageParams.ChatID,
90+
Text: msg,
91+
ParseMode: models.ParseModeMarkdown,
92+
})
93+
if err != nil {
94+
return types.ActionResult{}, fmt.Errorf("failed to send telegram message part %d: %w", i+1, err)
95+
}
96+
}
97+
98+
return types.ActionResult{
99+
Result: fmt.Sprintf("Message sent successfully to chat ID %d in %d parts", messageParams.ChatID, len(messages)),
100+
Metadata: map[string]interface{}{
101+
MetadataTelegramMessageSent: true,
102+
},
103+
}, nil
104+
}
105+
106+
func (s *SendTelegramMessageRunner) Definition() types.ActionDefinition {
107+
if s.chatID != 0 {
108+
return types.ActionDefinition{
109+
Name: "send_telegram_message",
110+
Description: "Send a message to a Telegram user or group",
111+
Properties: map[string]jsonschema.Definition{
112+
"message": {
113+
Type: jsonschema.String,
114+
Description: "The message to send",
115+
},
116+
},
117+
Required: []string{"message"},
118+
}
119+
}
120+
121+
return types.ActionDefinition{
122+
Name: "send_telegram_message",
123+
Description: "Send a message to a Telegram user or group",
124+
Properties: map[string]jsonschema.Definition{
125+
"chat_id": {
126+
Type: jsonschema.Number,
127+
Description: "The Telegram chat ID to send the message to (optional if configured in config)",
128+
},
129+
"message": {
130+
Type: jsonschema.String,
131+
Description: "The message to send",
132+
},
133+
},
134+
Required: []string{"message", "chat_id"},
135+
}
136+
}
137+
138+
func (s *SendTelegramMessageRunner) Plannable() bool {
139+
return true
140+
}
141+
142+
// SendTelegramMessageConfigMeta returns the metadata for Send Telegram Message action configuration fields
143+
func SendTelegramMessageConfigMeta() []config.Field {
144+
return []config.Field{
145+
{
146+
Name: "token",
147+
Label: "Telegram Token",
148+
Type: config.FieldTypeText,
149+
Required: true,
150+
HelpText: "Telegram bot token for sending messages",
151+
},
152+
{
153+
Name: "chat_id",
154+
Label: "Default Chat ID",
155+
Type: config.FieldTypeText,
156+
Required: false,
157+
HelpText: "Default Telegram chat ID to send messages to (can be overridden in parameters)",
158+
},
159+
}
160+
}

0 commit comments

Comments
 (0)