mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
Implement Telegram MCP tools for chat management and user interactions
- Developed various Telegram tools for managing chats, including archiving, unarchiving, muting, and unmuting chats, as well as inviting users to groups and managing chat photos. - Enhanced user management capabilities with tools for banning, unbanning, promoting, and demoting users in groups and channels. - Integrated robust error handling and input validation across all tools, ensuring clear feedback for invalid inputs and operational errors. - Improved logging and error formatting to streamline error management and enhance the reliability of chat and user operations. These updates significantly enhance the Telegram MCP tools, providing comprehensive functionalities for chat and user management while ensuring a smooth user experience through improved error handling and validation.
This commit is contained in:
@@ -1,20 +1,56 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'archive_chat',
|
||||
description: 'Archive a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function archiveChat(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('archive_chat');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputPeer = await client.getInputEntity(entity);
|
||||
await client.invoke(
|
||||
new Api.folders.EditPeerFolders({
|
||||
folderPeers: [
|
||||
new Api.InputFolderPeer({
|
||||
peer: inputPeer,
|
||||
folderId: 1, // 1 = Archive folder
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `Chat ${chatId} archived.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'archive_chat',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CHAT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,70 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'ban_user',
|
||||
description: 'Ban user from chat',
|
||||
description: 'Ban a user from a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
user_id: { type: 'string', description: 'User ID' },
|
||||
user_id: { type: 'string', description: 'User ID to ban' },
|
||||
},
|
||||
required: ['chat_id', 'user_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function banUser(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('ban_user');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const userId = validateId(args.user_id, 'user_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
if (chat.type !== 'channel' && chat.type !== 'supergroup') {
|
||||
return { content: [{ type: 'text', text: 'Ban is only available for channels/supergroups.' }], isError: true };
|
||||
}
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
const inputUser = await client.getInputEntity(userId);
|
||||
await client.invoke(
|
||||
new Api.channels.EditBanned({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
participant: inputUser as Api.TypeInputPeer,
|
||||
bannedRights: new Api.ChatBannedRights({
|
||||
viewMessages: true,
|
||||
sendMessages: true,
|
||||
sendMedia: true,
|
||||
sendStickers: true,
|
||||
sendGifs: true,
|
||||
sendGames: true,
|
||||
sendInline: true,
|
||||
embedLinks: true,
|
||||
untilDate: 0,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `User ${userId} banned from ${chat.title ?? chatId}.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'ban_user',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.ADMIN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,55 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
import { optString } from '../args';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'create_channel',
|
||||
description: 'Create a channel',
|
||||
description: 'Create a new channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
title: { type: 'string', description: 'Channel title' },
|
||||
description: { type: 'string', description: 'Channel description' },
|
||||
about: { type: 'string', description: 'Channel description' },
|
||||
megagroup: { type: 'boolean', description: 'Create as supergroup instead of channel', default: false },
|
||||
},
|
||||
required: ['title'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function createChannel(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('create_channel');
|
||||
try {
|
||||
const title = typeof args.title === 'string' ? args.title : '';
|
||||
if (!title) return { content: [{ type: 'text', text: 'title is required' }], isError: true };
|
||||
|
||||
const about = optString(args, 'about') ?? '';
|
||||
const megagroup = args.megagroup === true;
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(
|
||||
new Api.channels.CreateChannel({
|
||||
title,
|
||||
about,
|
||||
megagroup,
|
||||
broadcast: !megagroup,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
const channelId = (result as any)?.chats?.[0]?.id ?? 'unknown';
|
||||
const type = megagroup ? 'Supergroup' : 'Channel';
|
||||
return { content: [{ type: 'text', text: `${type} "${title}" created. ID: ${channelId}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'create_channel',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,54 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'create_group',
|
||||
description: 'Create a group',
|
||||
description: 'Create a new group chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
title: { type: 'string', description: 'Group title' },
|
||||
user_ids: { type: 'array', description: 'User IDs to add' },
|
||||
user_ids: { type: 'array', items: { type: 'string' }, description: 'User IDs to add' },
|
||||
},
|
||||
required: ['title'],
|
||||
required: ['title', 'user_ids'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function createGroup(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('create_group');
|
||||
try {
|
||||
const title = typeof args.title === 'string' ? args.title : '';
|
||||
if (!title) return { content: [{ type: 'text', text: 'title is required' }], isError: true };
|
||||
|
||||
const userIds = Array.isArray(args.user_ids) ? args.user_ids : [];
|
||||
if (userIds.length === 0) return { content: [{ type: 'text', text: 'user_ids must not be empty' }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const users: Api.TypeInputUser[] = [];
|
||||
for (const uid of userIds) {
|
||||
const inputUser = await client.getInputEntity(String(uid));
|
||||
users.push(inputUser as Api.TypeInputUser);
|
||||
}
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(
|
||||
new Api.messages.CreateChat({ title, users }),
|
||||
);
|
||||
});
|
||||
|
||||
const chatId = (result as any)?.chats?.[0]?.id ?? 'unknown';
|
||||
return { content: [{ type: 'text', text: `Group "${title}" created. Chat ID: ${chatId}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'create_group',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,63 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'delete_chat_photo',
|
||||
description: 'Delete chat photo',
|
||||
description: 'Delete the photo of a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function deleteChatPhoto(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('delete_chat_photo');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
if (chat.type === 'channel' || chat.type === 'supergroup') {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
await client.invoke(
|
||||
new Api.channels.EditPhoto({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
photo: new Api.InputChatPhotoEmpty(),
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
await client.invoke(
|
||||
new Api.messages.EditChatPhoto({
|
||||
chatId: BigInt(chat.id),
|
||||
photo: new Api.InputChatPhotoEmpty(),
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: `Chat photo deleted for ${chatId}.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'delete_chat_photo',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,61 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'demote_admin',
|
||||
description: 'Demote admin',
|
||||
description: 'Demote an admin in a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
user_id: { type: 'string', description: 'User ID' },
|
||||
user_id: { type: 'string', description: 'User ID to demote' },
|
||||
},
|
||||
required: ['chat_id', 'user_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function demoteAdmin(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('demote_admin');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const userId = validateId(args.user_id, 'user_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
if (chat.type !== 'channel' && chat.type !== 'supergroup') {
|
||||
return { content: [{ type: 'text', text: 'Admin demotion is only available for channels/supergroups.' }], isError: true };
|
||||
}
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
const inputUser = await client.getInputEntity(userId);
|
||||
await client.invoke(
|
||||
new Api.channels.EditAdmin({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
userId: inputUser as Api.TypeInputUser,
|
||||
adminRights: new Api.ChatAdminRights({}),
|
||||
rank: '',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `User ${userId} demoted in ${chat.title ?? chatId}.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'demote_admin',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.ADMIN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,66 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'edit_chat_title',
|
||||
description: 'Edit chat title',
|
||||
description: 'Edit the title of a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
new_title: { type: 'string', description: 'New title' },
|
||||
title: { type: 'string', description: 'New title' },
|
||||
},
|
||||
required: ['chat_id', 'new_title'],
|
||||
required: ['chat_id', 'title'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function editChatTitle(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('edit_chat_title');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const title = typeof args.title === 'string' ? args.title : '';
|
||||
if (!title) return { content: [{ type: 'text', text: 'title is required' }], isError: true };
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
if (chat.type === 'channel' || chat.type === 'supergroup') {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
await client.invoke(
|
||||
new Api.channels.EditTitle({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
title,
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
await client.invoke(
|
||||
new Api.messages.EditChatTitle({
|
||||
chatId: BigInt(chat.id),
|
||||
title,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: `Chat title updated to "${title}".` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'edit_chat_title',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,66 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
import { optString } from '../args';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'export_chat_invite',
|
||||
description: 'Create invite link for a chat',
|
||||
description: 'Export a new chat invite link',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
title: { type: 'string', description: 'Link title' },
|
||||
expire_date: { type: 'number', description: 'Expiration timestamp' },
|
||||
usage_limit: { type: 'number', description: 'Max number of uses' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function exportChatInvite(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('export_chat_invite');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const title = optString(args, 'title');
|
||||
const expireDate = typeof args.expire_date === 'number' ? args.expire_date : undefined;
|
||||
const usageLimit = typeof args.usage_limit === 'number' ? args.usage_limit : undefined;
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputPeer = await client.getInputEntity(entity);
|
||||
return client.invoke(
|
||||
new Api.messages.ExportChatInvite({
|
||||
peer: inputPeer,
|
||||
title: title ?? undefined,
|
||||
expireDate: expireDate ?? undefined,
|
||||
usageLimit: usageLimit ?? undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
const link = (result as any)?.link;
|
||||
if (!link) {
|
||||
return { content: [{ type: 'text', text: 'Could not create invite link.' }], isError: true };
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: `Invite link created: ${link}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'export_chat_invite',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'export_contacts',
|
||||
description: 'Export contacts',
|
||||
description: 'Export all contacts from Telegram',
|
||||
inputSchema: { type: 'object', properties: {} },
|
||||
};
|
||||
|
||||
@@ -12,5 +14,31 @@ export async function exportContacts(
|
||||
_args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('export_contacts');
|
||||
try {
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(new Api.contacts.GetContacts({ hash: BigInt(0) }));
|
||||
});
|
||||
|
||||
if (!result || !('users' in result) || !Array.isArray(result.users) || result.users.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'No contacts to export.' }] };
|
||||
}
|
||||
|
||||
const contacts = result.users.map((u: any) => ({
|
||||
id: String(u.id),
|
||||
firstName: u.firstName ?? '',
|
||||
lastName: u.lastName ?? '',
|
||||
username: u.username ?? '',
|
||||
phone: u.phone ?? '',
|
||||
}));
|
||||
|
||||
return { content: [{ type: 'text', text: JSON.stringify(contacts, null, 2) }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'export_contacts',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CONTACT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,81 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'get_admins',
|
||||
description: 'Get admins of a chat',
|
||||
description: 'Get admins of a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function getAdmins(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('get_admins');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
let admins: any[] = [];
|
||||
|
||||
if (chat.type === 'channel' || chat.type === 'supergroup') {
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
return client.invoke(
|
||||
new Api.channels.GetParticipants({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
filter: new Api.ChannelParticipantsAdmins(),
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
hash: BigInt(0),
|
||||
}),
|
||||
);
|
||||
});
|
||||
if (result && 'users' in result && Array.isArray(result.users)) {
|
||||
admins = result.users;
|
||||
}
|
||||
} else {
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(
|
||||
new Api.messages.GetFullChat({ chatId: BigInt(chat.id) }),
|
||||
);
|
||||
});
|
||||
if (result && 'users' in result && Array.isArray(result.users)) {
|
||||
admins = result.users;
|
||||
}
|
||||
}
|
||||
|
||||
if (admins.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'No admins found.' }] };
|
||||
}
|
||||
|
||||
const lines = admins.map((u: any) => {
|
||||
const name = [u.firstName, u.lastName].filter(Boolean).join(' ') || 'Unknown';
|
||||
const username = u.username ? `@${u.username}` : '';
|
||||
return `ID: ${u.id} | ${name} ${username}`.trim();
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `${lines.length} admins:\n${lines.join('\n')}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'get_admins',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.ADMIN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,72 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
import { optNumber } from '../args';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'get_banned_users',
|
||||
description: 'Get banned users in a chat',
|
||||
description: 'Get banned users in a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
limit: { type: 'number', description: 'Max results', default: 50 },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function getBannedUsers(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('get_banned_users');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const limit = optNumber(args, 'limit', 50);
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
if (chat.type !== 'channel' && chat.type !== 'supergroup') {
|
||||
return { content: [{ type: 'text', text: 'Banned users list is only available for channels/supergroups.' }], isError: true };
|
||||
}
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
return client.invoke(
|
||||
new Api.channels.GetParticipants({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
filter: new Api.ChannelParticipantsKicked({ q: '' }),
|
||||
offset: 0,
|
||||
limit,
|
||||
hash: BigInt(0),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
if (!result || !('users' in result) || !Array.isArray(result.users) || result.users.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'No banned users found.' }] };
|
||||
}
|
||||
|
||||
const lines = result.users.map((u: any) => {
|
||||
const name = [u.firstName, u.lastName].filter(Boolean).join(' ') || 'Unknown';
|
||||
const username = u.username ? `@${u.username}` : '';
|
||||
return `ID: ${u.id} | ${name} ${username}`.trim();
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `${lines.length} banned users:\n${lines.join('\n')}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'get_banned_users',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.ADMIN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,51 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
import { optNumber } from '../args';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'get_blocked_users',
|
||||
description: 'Get list of blocked users',
|
||||
inputSchema: { type: 'object', properties: {} },
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
limit: { type: 'number', description: 'Max results', default: 50 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export async function getBlockedUsers(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('get_blocked_users');
|
||||
try {
|
||||
const limit = optNumber(args, 'limit', 50);
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(
|
||||
new Api.contacts.GetBlocked({ offset: 0, limit }),
|
||||
);
|
||||
});
|
||||
|
||||
if (!result || !('users' in result) || !Array.isArray(result.users) || result.users.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'No blocked users.' }] };
|
||||
}
|
||||
|
||||
const lines = result.users.map((u: any) => {
|
||||
const name = [u.firstName, u.lastName].filter(Boolean).join(' ') || 'Unknown';
|
||||
const username = u.username ? `@${u.username}` : '';
|
||||
return `ID: ${u.id} | ${name} ${username}`.trim();
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'get_blocked_users',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CONTACT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,57 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'get_invite_link',
|
||||
description: 'Get invite link for a chat',
|
||||
description: 'Get the invite link for a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function getInviteLink(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('get_invite_link');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputPeer = await client.getInputEntity(entity);
|
||||
return client.invoke(
|
||||
new Api.messages.ExportChatInvite({
|
||||
peer: inputPeer,
|
||||
legacyRevokePermanent: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
const link = (result as any)?.link;
|
||||
if (!link) {
|
||||
return { content: [{ type: 'text', text: 'Could not generate invite link.' }], isError: true };
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: `Invite link: ${link}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'get_invite_link',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,84 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
import { optNumber } from '../args';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'get_participants',
|
||||
description: 'Get participants in a chat',
|
||||
description: 'Get participants of a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
limit: { type: 'number', description: 'Max participants', default: 100 },
|
||||
limit: { type: 'number', description: 'Max results', default: 50 },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function getParticipants(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('get_participants');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const limit = optNumber(args, 'limit', 50);
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
let participants: any[] = [];
|
||||
|
||||
if (chat.type === 'channel' || chat.type === 'supergroup') {
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
return client.invoke(
|
||||
new Api.channels.GetParticipants({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
filter: new Api.ChannelParticipantsRecent(),
|
||||
offset: 0,
|
||||
limit,
|
||||
hash: BigInt(0),
|
||||
}),
|
||||
);
|
||||
});
|
||||
if (result && 'users' in result && Array.isArray(result.users)) {
|
||||
participants = result.users;
|
||||
}
|
||||
} else {
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(
|
||||
new Api.messages.GetFullChat({ chatId: BigInt(chat.id) }),
|
||||
);
|
||||
});
|
||||
if (result && 'users' in result && Array.isArray(result.users)) {
|
||||
participants = result.users;
|
||||
}
|
||||
}
|
||||
|
||||
if (participants.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'No participants found.' }] };
|
||||
}
|
||||
|
||||
const lines = participants.map((u: any) => {
|
||||
const name = [u.firstName, u.lastName].filter(Boolean).join(' ') || 'Unknown';
|
||||
const username = u.username ? `@${u.username}` : '';
|
||||
return `ID: ${u.id} | ${name} ${username}`.trim();
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `${lines.length} participants:\n${lines.join('\n')}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'get_participants',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,42 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'import_chat_invite',
|
||||
description: 'Join chat via invite hash',
|
||||
description: 'Join a chat using an invite hash',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { invite_hash: { type: 'string', description: 'Invite hash' } },
|
||||
required: ['invite_hash'],
|
||||
properties: {
|
||||
hash: { type: 'string', description: 'Invite hash (from t.me/+HASH or t.me/joinchat/HASH)' },
|
||||
},
|
||||
required: ['hash'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function importChatInvite(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('import_chat_invite');
|
||||
try {
|
||||
const hash = typeof args.hash === 'string' ? args.hash : '';
|
||||
if (!hash) return { content: [{ type: 'text', text: 'hash is required' }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(new Api.messages.ImportChatInvite({ hash }));
|
||||
});
|
||||
|
||||
const chatTitle = (result as any)?.chats?.[0]?.title ?? 'unknown';
|
||||
return { content: [{ type: 'text', text: `Joined chat: ${chatTitle}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'import_chat_invite',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,65 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'import_contacts',
|
||||
description: 'Import contacts',
|
||||
description: 'Import contacts to Telegram',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { contacts: { type: 'array', description: 'Contacts to import' } },
|
||||
properties: {
|
||||
contacts: {
|
||||
type: 'array',
|
||||
description: 'Array of contacts: [{phone, first_name, last_name?}]',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
phone: { type: 'string' },
|
||||
first_name: { type: 'string' },
|
||||
last_name: { type: 'string' },
|
||||
},
|
||||
required: ['phone', 'first_name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['contacts'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function importContacts(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('import_contacts');
|
||||
try {
|
||||
const contactsArg = args.contacts;
|
||||
if (!Array.isArray(contactsArg) || contactsArg.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'contacts array is required and must not be empty.' }], isError: true };
|
||||
}
|
||||
|
||||
const inputContacts = contactsArg.map((c: any, i: number) =>
|
||||
new Api.InputPhoneContact({
|
||||
clientId: BigInt(i),
|
||||
phone: String(c.phone ?? ''),
|
||||
firstName: String(c.first_name ?? ''),
|
||||
lastName: String(c.last_name ?? ''),
|
||||
}),
|
||||
);
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(new Api.contacts.ImportContacts({ contacts: inputContacts }));
|
||||
});
|
||||
|
||||
const imported = (result as any)?.imported?.length ?? 0;
|
||||
return { content: [{ type: 'text', text: `Imported ${imported} of ${contactsArg.length} contacts.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'import_contacts',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CONTACT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,76 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'invite_to_group',
|
||||
description: 'Invite users to a group',
|
||||
description: 'Invite users to a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
user_ids: { type: 'array', description: 'User IDs to invite' },
|
||||
user_ids: { type: 'array', items: { type: 'string' }, description: 'User IDs to invite' },
|
||||
},
|
||||
required: ['chat_id', 'user_ids'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function inviteToGroup(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('invite_to_group');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const userIds = Array.isArray(args.user_ids) ? args.user_ids : [];
|
||||
if (userIds.length === 0) return { content: [{ type: 'text', text: 'user_ids must not be empty' }], isError: true };
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
const users: Api.TypeInputUser[] = [];
|
||||
for (const uid of userIds) {
|
||||
const inputUser = await client.getInputEntity(String(uid));
|
||||
users.push(inputUser as Api.TypeInputUser);
|
||||
}
|
||||
|
||||
const inputPeer = await client.getInputEntity(entity);
|
||||
|
||||
if (chat.type === 'channel' || chat.type === 'supergroup') {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
await client.invoke(
|
||||
new Api.channels.InviteToChannel({
|
||||
channel: inputPeer as Api.TypeInputChannel,
|
||||
users,
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
for (const user of users) {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
await client.invoke(
|
||||
new Api.messages.AddChatUser({
|
||||
chatId: BigInt(chat.id),
|
||||
userId: user,
|
||||
fwdLimit: 100,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: `Invited ${userIds.length} user(s) to ${chat.title ?? chatId}.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'invite_to_group',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,49 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'join_chat_by_link',
|
||||
description: 'Join chat via invite link',
|
||||
description: 'Join a chat using an invite link',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { invite_link: { type: 'string', description: 'Invite link URL' } },
|
||||
required: ['invite_link'],
|
||||
properties: {
|
||||
link: { type: 'string', description: 'Invite link (e.g. https://t.me/+HASH or https://t.me/joinchat/HASH)' },
|
||||
},
|
||||
required: ['link'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function joinChatByLink(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('join_chat_by_link');
|
||||
try {
|
||||
const link = typeof args.link === 'string' ? args.link : '';
|
||||
if (!link) return { content: [{ type: 'text', text: 'link is required' }], isError: true };
|
||||
|
||||
// Extract hash from link
|
||||
let hash = link;
|
||||
const plusMatch = link.match(/t\.me\/\+(.+)/);
|
||||
const joinMatch = link.match(/t\.me\/joinchat\/(.+)/);
|
||||
if (plusMatch) hash = plusMatch[1];
|
||||
else if (joinMatch) hash = joinMatch[1];
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(new Api.messages.ImportChatInvite({ hash }));
|
||||
});
|
||||
|
||||
const chatTitle = (result as any)?.chats?.[0]?.title ?? 'unknown';
|
||||
return { content: [{ type: 'text', text: `Joined chat: ${chatTitle}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'join_chat_by_link',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,63 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'leave_chat',
|
||||
description: 'Leave a chat',
|
||||
description: 'Leave a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function leaveChat(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('leave_chat');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
if (chat.type === 'channel' || chat.type === 'supergroup') {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
await client.invoke(
|
||||
new Api.channels.LeaveChannel({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const selfUser = await client.getMe();
|
||||
await client.invoke(
|
||||
new Api.messages.DeleteChatUser({
|
||||
chatId: BigInt(chat.id),
|
||||
userId: new Api.InputUserSelf(),
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: `Left chat ${chat.title ?? chatId}.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'leave_chat',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'list_contacts',
|
||||
@@ -12,5 +14,34 @@ export async function listContacts(
|
||||
_args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('list_contacts');
|
||||
try {
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(new Api.contacts.GetContacts({ hash: BigInt(0) }));
|
||||
});
|
||||
|
||||
if (!result || !('users' in result) || !Array.isArray(result.users)) {
|
||||
return { content: [{ type: 'text', text: 'No contacts found.' }] };
|
||||
}
|
||||
|
||||
const lines = result.users.map((u: any) => {
|
||||
const name = [u.firstName, u.lastName].filter(Boolean).join(' ') || 'Unknown';
|
||||
const username = u.username ? `@${u.username}` : '';
|
||||
const phone = u.phone ? `+${u.phone}` : '';
|
||||
return `ID: ${u.id} | ${name} ${username} ${phone}`.trim();
|
||||
});
|
||||
|
||||
if (lines.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'No contacts found.' }] };
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'list_contacts',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CONTACT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,58 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'mute_chat',
|
||||
description: 'Mute a chat',
|
||||
description: 'Mute notifications for a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
mute_for: { type: 'number', description: 'Mute duration in seconds' },
|
||||
duration: { type: 'number', description: 'Mute duration in seconds (0 = forever)', default: 0 },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function muteChat(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('mute_chat');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const duration = typeof args.duration === 'number' ? args.duration : 0;
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
const muteUntil = duration === 0 ? 2147483647 : Math.floor(Date.now() / 1000) + duration;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputPeer = await client.getInputEntity(entity);
|
||||
await client.invoke(
|
||||
new Api.account.UpdateNotifySettings({
|
||||
peer: new Api.InputNotifyPeer({ peer: inputPeer }),
|
||||
settings: new Api.InputPeerNotifySettings({
|
||||
muteUntil,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `Chat ${chatId} muted.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'mute_chat',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CHAT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,68 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'promote_admin',
|
||||
description: 'Promote user to admin',
|
||||
description: 'Promote a user to admin in a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
user_id: { type: 'string', description: 'User ID' },
|
||||
user_id: { type: 'string', description: 'User ID to promote' },
|
||||
},
|
||||
required: ['chat_id', 'user_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function promoteAdmin(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('promote_admin');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const userId = validateId(args.user_id, 'user_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
if (chat.type !== 'channel' && chat.type !== 'supergroup') {
|
||||
return { content: [{ type: 'text', text: 'Admin promotion is only available for channels/supergroups.' }], isError: true };
|
||||
}
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
const inputUser = await client.getInputEntity(userId);
|
||||
await client.invoke(
|
||||
new Api.channels.EditAdmin({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
userId: inputUser as Api.TypeInputUser,
|
||||
adminRights: new Api.ChatAdminRights({
|
||||
changeInfo: true,
|
||||
deleteMessages: true,
|
||||
banUsers: true,
|
||||
inviteUsers: true,
|
||||
pinMessages: true,
|
||||
manageCall: true,
|
||||
}),
|
||||
rank: 'Admin',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `User ${userId} promoted to admin in ${chat.title ?? chatId}.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'promote_admin',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.ADMIN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,55 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
import { optNumber } from '../args';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'search_contacts',
|
||||
description: 'Search contacts by name or phone',
|
||||
description: 'Search contacts by name or username',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { query: { type: 'string', description: 'Search query' } },
|
||||
properties: {
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
limit: { type: 'number', description: 'Max results', default: 20 },
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function searchContacts(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('search_contacts');
|
||||
try {
|
||||
const query = typeof args.query === 'string' ? args.query : '';
|
||||
if (!query) {
|
||||
return { content: [{ type: 'text', text: 'query is required' }], isError: true };
|
||||
}
|
||||
const limit = optNumber(args, 'limit', 20);
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
const result = await mtprotoService.withFloodWaitHandling(async () => {
|
||||
return client.invoke(new Api.contacts.Search({ q: query, limit }));
|
||||
});
|
||||
|
||||
if (!result || !('users' in result) || !Array.isArray(result.users) || result.users.length === 0) {
|
||||
return { content: [{ type: 'text', text: `No contacts found for "${query}".` }] };
|
||||
}
|
||||
|
||||
const lines = result.users.map((u: any) => {
|
||||
const name = [u.firstName, u.lastName].filter(Boolean).join(' ') || 'Unknown';
|
||||
const username = u.username ? `@${u.username}` : '';
|
||||
return `ID: ${u.id} | ${name} ${username}`.trim();
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'search_contacts',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CONTACT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,46 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'subscribe_public_channel',
|
||||
description: 'Subscribe to public channel by username',
|
||||
description: 'Subscribe to a public channel by username',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { username: { type: 'string', description: 'Channel username' } },
|
||||
properties: {
|
||||
username: { type: 'string', description: 'Channel username' },
|
||||
},
|
||||
required: ['username'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function subscribePublicChannel(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('subscribe_public_channel');
|
||||
try {
|
||||
const username = typeof args.username === 'string' ? args.username : '';
|
||||
if (!username) return { content: [{ type: 'text', text: 'username is required' }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(username);
|
||||
await client.invoke(
|
||||
new Api.channels.JoinChannel({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `Subscribed to channel: ${username}` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'subscribe_public_channel',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.GROUP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,56 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'unarchive_chat',
|
||||
description: 'Unarchive a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function unarchiveChat(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('unarchive_chat');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputPeer = await client.getInputEntity(entity);
|
||||
await client.invoke(
|
||||
new Api.folders.EditPeerFolders({
|
||||
folderPeers: [
|
||||
new Api.InputFolderPeer({
|
||||
peer: inputPeer,
|
||||
folderId: 0, // 0 = Main folder (unarchive)
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `Chat ${chatId} unarchived.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'unarchive_chat',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CHAT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,60 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'unban_user',
|
||||
description: 'Unban user from chat',
|
||||
description: 'Unban a user from a group or channel',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
user_id: { type: 'string', description: 'User ID' },
|
||||
user_id: { type: 'string', description: 'User ID to unban' },
|
||||
},
|
||||
required: ['chat_id', 'user_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function unbanUser(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('unban_user');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const userId = validateId(args.user_id, 'user_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
if (chat.type !== 'channel' && chat.type !== 'supergroup') {
|
||||
return { content: [{ type: 'text', text: 'Unban is only available for channels/supergroups.' }], isError: true };
|
||||
}
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputChannel = await client.getInputEntity(entity);
|
||||
const inputUser = await client.getInputEntity(userId);
|
||||
await client.invoke(
|
||||
new Api.channels.EditBanned({
|
||||
channel: inputChannel as Api.TypeInputChannel,
|
||||
participant: inputUser as Api.TypeInputPeer,
|
||||
bannedRights: new Api.ChatBannedRights({ untilDate: 0 }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `User ${userId} unbanned from ${chat.title ?? chatId}.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'unban_user',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.ADMIN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,43 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'unblock_user',
|
||||
description: 'Unblock a user',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { user_id: { type: 'string', description: 'User ID' } },
|
||||
properties: {
|
||||
user_id: { type: 'string', description: 'User ID to unblock' },
|
||||
},
|
||||
required: ['user_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function unblockUser(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('unblock_user');
|
||||
try {
|
||||
const userId = validateId(args.user_id, 'user_id');
|
||||
const client = mtprotoService.getClient();
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputUser = await client.getInputEntity(userId);
|
||||
await client.invoke(
|
||||
new Api.contacts.Unblock({ id: inputUser as Api.TypeInputPeer }),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `User ${userId} unblocked.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'unblock_user',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CONTACT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,54 @@
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import { notImplemented } from './notImplemented';
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { validateId } from '../../validation';
|
||||
import { getChatById } from '../telegramApi';
|
||||
import { mtprotoService } from '../../../../services/mtprotoService';
|
||||
import { Api } from 'telegram';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'unmute_chat',
|
||||
description: 'Unmute a chat',
|
||||
description: 'Unmute notifications for a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { chat_id: { type: 'string', description: 'Chat ID or username' } },
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'Chat ID or username' },
|
||||
},
|
||||
required: ['chat_id'],
|
||||
},
|
||||
};
|
||||
|
||||
export async function unmuteChat(
|
||||
_args: Record<string, unknown>,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
return notImplemented('unmute_chat');
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) return { content: [{ type: 'text', text: `Chat not found: ${chatId}` }], isError: true };
|
||||
|
||||
const client = mtprotoService.getClient();
|
||||
const entity = chat.username ? chat.username : chat.id;
|
||||
|
||||
await mtprotoService.withFloodWaitHandling(async () => {
|
||||
const inputPeer = await client.getInputEntity(entity);
|
||||
await client.invoke(
|
||||
new Api.account.UpdateNotifySettings({
|
||||
peer: new Api.InputNotifyPeer({ peer: inputPeer }),
|
||||
settings: new Api.InputPeerNotifySettings({
|
||||
muteUntil: 0,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: `Chat ${chatId} unmuted.` }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'unmute_chat',
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.CHAT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user