Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cyan-masks-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@iqai/mcp-telegram": patch
---

Added support for messaging on topic channels of forum type groups
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Send a message to a Telegram chat or channel.

- `chatId`: Chat ID or username (e.g., @channelname or -1001234567890)
- `text`: Message text
- `topicId`: Optional topic ID for forum channels
- `parseMode`: Optional formatting (HTML, Markdown, MarkdownV2)
- `disableWebPagePreview`: Optional boolean
- `disableNotification`: Optional boolean for silent messages
Expand Down Expand Up @@ -311,6 +312,8 @@ The system uses customizable templates for different message types:
- **Contact**: Contact information
- **Poll**: Poll questions and options

All message templates include topic ID information for forum channels, allowing the AI to understand the context of which topic thread the message belongs to.

### Customization

To customize the sampling behavior:
Expand Down Expand Up @@ -367,6 +370,19 @@ Send a message to a Telegram channel:
}
```

Send a message to a specific topic in a forum channel:

```json
{
"tool_name": "SEND_MESSAGE",
"arguments": {
"chatId": "@mychannel",
"text": "Hello from the Telegram MCP Server!",
"topicId": 123
}
}
```

### GET_CHANNEL_INFO

Get information about a Telegram channel:
Expand Down
10 changes: 10 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Adding topic_id: {topicId} to each template is repetitive. A more significant concern is that topicId is optional, which will result in topic_id: undefined in the prompt when a message is not in a topic. This adds unnecessary noise for the AI.

Consider a more dynamic approach where the topic_id line is only included when a value is present. This could be achieved by refactoring templates into functions or by pre-processing the template string before formatting.

message_type: ${MessageType.TEXT}
content: {content}`,

Expand All @@ -165,6 +166,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.PHOTO}
caption: {caption}
photo_info: {photoInfo}`,
Expand All @@ -174,6 +176,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.DOCUMENT}
filename: {fileName}
mime_type: {mimeType}
Expand All @@ -184,6 +187,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.VOICE}
duration: {duration}s`,

Expand All @@ -192,6 +196,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.VIDEO}
caption: {caption}
duration: {duration}s`,
Expand All @@ -201,6 +206,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.STICKER}
emoji: {stickerEmoji}
set_name: {stickerSetName}`,
Expand All @@ -210,6 +216,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.LOCATION}
latitude: {latitude}
longitude: {longitude}`,
Expand All @@ -219,6 +226,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.CONTACT}
contact_name: {contactName}
phone_number: {phoneNumber}`,
Expand All @@ -228,6 +236,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: ${MessageType.POLL}
question: {pollQuestion}
options: {pollOptions}`,
Expand All @@ -237,6 +246,7 @@ user_id: {userId}
chat_id: {chatId}
isDM: {isDM}
message_id: {messageId}
topic_id: {topicId}
message_type: {messageType}
content: {content}`,
},
Expand Down
8 changes: 7 additions & 1 deletion src/sampling/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export class SamplingHandler {
await this.telegramService.sendMessage(
request.chatId,
"Sorry, the AI service is not available right now.",
request.templateData.topicId,
);
}
return;
Expand Down Expand Up @@ -212,7 +213,11 @@ export class SamplingHandler {

// Send response back to Telegram (unless in silent mode)
if (!samplingConfig.silentMode) {
await this.telegramService.sendMessage(request.chatId, responseText);
await this.telegramService.sendMessage(
request.chatId,
responseText,
request.templateData.topicId,
);
}

console.log(
Expand All @@ -225,6 +230,7 @@ export class SamplingHandler {
await this.telegramService.sendMessage(
request.chatId,
"Sorry, I encountered an error while processing your request. Please try again.",
request.templateData.topicId,
);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/sampling/message-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function handleTextMessage(ctx: Context): MessageTemplateData | null {
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.TEXT,
};
}
Expand All @@ -30,6 +31,7 @@ export function handlePhotoMessage(ctx: Context): MessageTemplateData | null {
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.PHOTO,
};
}
Expand All @@ -51,6 +53,7 @@ export function handleDocumentMessage(
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.DOCUMENT,
};
}
Expand All @@ -68,6 +71,7 @@ export function handleVoiceMessage(ctx: Context): MessageTemplateData | null {
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.VOICE,
};
}
Expand All @@ -86,6 +90,7 @@ export function handleVideoMessage(ctx: Context): MessageTemplateData | null {
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.VIDEO,
};
}
Expand All @@ -104,6 +109,7 @@ export function handleStickerMessage(ctx: Context): MessageTemplateData | null {
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.STICKER,
};
}
Expand All @@ -124,6 +130,7 @@ export function handleLocationMessage(
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.LOCATION,
};
}
Expand All @@ -142,6 +149,7 @@ export function handleContactMessage(ctx: Context): MessageTemplateData | null {
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.CONTACT,
};
}
Expand All @@ -160,6 +168,7 @@ export function handlePollMessage(ctx: Context): MessageTemplateData | null {
chatId: ctx.chat.id,
isDM: ctx.chat.id === msg.from.id,
messageId: msg.message_id,
topicId: msg.message_thread_id,
messageType: MessageType.POLL,
};
}
1 change: 1 addition & 0 deletions src/sampling/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface MessageTemplateData {
chatId: number;
isDM: boolean;
messageId: number;
topicId?: number;
messageType?: string;
caption?: string;
photoInfo?: string;
Expand Down
5 changes: 4 additions & 1 deletion src/services/telegram-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ export class TelegramService {
async sendMessage(
chatId: string | number,
text: string,
topicId?: number,
): Promise<MessageInfo> {
try {
const message = await this.bot.telegram.sendMessage(chatId, text);
const message = await this.bot.telegram.sendMessage(chatId, text, {
message_thread_id: topicId,
});

return {
messageId: message.message_id,
Expand Down
5 changes: 5 additions & 0 deletions src/tools/send-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const sendMessageParams = z.object({
"The chat ID or channel username (e.g., @channelname or -1001234567890)",
),
text: z.string().min(1).describe("The message text to send"),
topicId: z
.number()
.optional()
.describe("The topic ID for forum channels (optional)"),
});

type SendMessageParams = z.infer<typeof sendMessageParams>;
Expand All @@ -24,6 +28,7 @@ export const sendMessageTool = {
const messageInfo = await telegramService.sendMessage(
params.chatId,
params.text,
params.topicId,
);

return dedent`
Expand Down