@overpod/mcp-telegram 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/telegram-client.d.ts +29 -0
- package/dist/telegram-client.js +251 -1
- package/dist/tools/chats.js +187 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -532,12 +532,31 @@ src/
|
|
|
532
532
|
- **[tsx](https://tsx.is/)** -- TypeScript execution without a build step
|
|
533
533
|
- **[dotenv](https://github.com/motdotla/dotenv)** -- Environment variable management
|
|
534
534
|
|
|
535
|
+
## Troubleshooting
|
|
536
|
+
|
|
537
|
+
### AUTH_KEY_DUPLICATED
|
|
538
|
+
|
|
539
|
+
A Telegram session can only be used by **one process at a time**. If you get `AUTH_KEY_DUPLICATED`, it means another process is already using the same session file.
|
|
540
|
+
|
|
541
|
+
**Solution**: Create separate sessions for each environment:
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
# Local development
|
|
545
|
+
TELEGRAM_SESSION_PATH=~/.mcp-telegram/session-local npx @overpod/mcp-telegram login
|
|
546
|
+
|
|
547
|
+
# Production server
|
|
548
|
+
TELEGRAM_SESSION_PATH=~/.mcp-telegram/session-prod npx @overpod/mcp-telegram login
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Then set `TELEGRAM_SESSION_PATH` in each environment's MCP config accordingly.
|
|
552
|
+
|
|
535
553
|
## Security
|
|
536
554
|
|
|
537
555
|
- API credentials are stored in `.env` (gitignored)
|
|
538
556
|
- Session is stored in `~/.mcp-telegram/session` with `0600` permissions (owner-only access)
|
|
539
557
|
- Session directory is created with `0700` permissions
|
|
540
558
|
- Phone number is **not required** -- QR-only authentication
|
|
559
|
+
- **One session per process** -- using the same session in multiple processes simultaneously causes `AUTH_KEY_DUPLICATED` errors (see [Troubleshooting](#troubleshooting))
|
|
541
560
|
- This is a **userbot** (personal account), not a bot -- respect the [Telegram Terms of Service](https://core.telegram.org/api/terms)
|
|
542
561
|
|
|
543
562
|
## License
|
|
@@ -255,4 +255,33 @@ export declare class TelegramService {
|
|
|
255
255
|
title: string;
|
|
256
256
|
type: string;
|
|
257
257
|
}>;
|
|
258
|
+
createGroup(options: {
|
|
259
|
+
title: string;
|
|
260
|
+
users: string[];
|
|
261
|
+
supergroup?: boolean;
|
|
262
|
+
forum?: boolean;
|
|
263
|
+
description?: string;
|
|
264
|
+
}): Promise<{
|
|
265
|
+
id: string;
|
|
266
|
+
title: string;
|
|
267
|
+
type: string;
|
|
268
|
+
inviteLink?: string;
|
|
269
|
+
}>;
|
|
270
|
+
inviteToGroup(chatId: string, users: string[]): Promise<{
|
|
271
|
+
invited: string[];
|
|
272
|
+
failed: string[];
|
|
273
|
+
}>;
|
|
274
|
+
kickUser(chatId: string, userId: string): Promise<void>;
|
|
275
|
+
banUser(chatId: string, userId: string): Promise<void>;
|
|
276
|
+
unbanUser(chatId: string, userId: string): Promise<void>;
|
|
277
|
+
editGroup(chatId: string, options: {
|
|
278
|
+
title?: string;
|
|
279
|
+
description?: string;
|
|
280
|
+
photoPath?: string;
|
|
281
|
+
}): Promise<void>;
|
|
282
|
+
leaveGroup(chatId: string): Promise<void>;
|
|
283
|
+
setAdmin(chatId: string, userId: string, options?: {
|
|
284
|
+
title?: string;
|
|
285
|
+
}): Promise<void>;
|
|
286
|
+
removeAdmin(chatId: string, userId: string): Promise<void>;
|
|
258
287
|
}
|
package/dist/telegram-client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { chmod, readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import bigInt from "big-integer";
|
|
7
7
|
import QRCode from "qrcode";
|
|
8
8
|
import { TelegramClient } from "telegram";
|
|
9
|
+
import { CustomFile } from "telegram/client/uploads.js";
|
|
9
10
|
import { StringSession } from "telegram/sessions/index.js";
|
|
10
11
|
import { Api } from "telegram/tl/index.js";
|
|
11
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1187,4 +1188,253 @@ export class TelegramService {
|
|
|
1187
1188
|
}
|
|
1188
1189
|
throw new Error("Target is not a group or channel. Use username, @username, or invite link.");
|
|
1189
1190
|
}
|
|
1191
|
+
async createGroup(options) {
|
|
1192
|
+
if (!this.client)
|
|
1193
|
+
throw new Error("Not connected");
|
|
1194
|
+
const { title, users, supergroup = false, forum = false, description } = options;
|
|
1195
|
+
if (supergroup || forum) {
|
|
1196
|
+
// Create supergroup/channel via channels.CreateChannel
|
|
1197
|
+
const result = await this.client.invoke(new Api.channels.CreateChannel({
|
|
1198
|
+
title,
|
|
1199
|
+
about: description ?? "",
|
|
1200
|
+
megagroup: true,
|
|
1201
|
+
forum: forum || undefined,
|
|
1202
|
+
}));
|
|
1203
|
+
const chat = result.chats?.[0];
|
|
1204
|
+
if (!chat)
|
|
1205
|
+
throw new Error("Failed to create supergroup");
|
|
1206
|
+
const channelId = chat.id.toString();
|
|
1207
|
+
// Invite users
|
|
1208
|
+
if (users.length > 0) {
|
|
1209
|
+
const inputUsers = [];
|
|
1210
|
+
for (const u of users) {
|
|
1211
|
+
try {
|
|
1212
|
+
const entity = await this.client.getEntity(u);
|
|
1213
|
+
if (entity instanceof Api.User) {
|
|
1214
|
+
inputUsers.push(new Api.InputUser({ userId: entity.id, accessHash: entity.accessHash ?? bigInt.zero }));
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
catch {
|
|
1218
|
+
// Skip unresolvable users
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
if (inputUsers.length > 0) {
|
|
1222
|
+
await this.client.invoke(new Api.channels.InviteToChannel({
|
|
1223
|
+
channel: chat,
|
|
1224
|
+
users: inputUsers,
|
|
1225
|
+
}));
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
// Get invite link
|
|
1229
|
+
let inviteLink;
|
|
1230
|
+
try {
|
|
1231
|
+
const exported = await this.client.invoke(new Api.messages.ExportChatInvite({ peer: chat }));
|
|
1232
|
+
if (exported instanceof Api.ChatInviteExported) {
|
|
1233
|
+
inviteLink = exported.link;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
catch { }
|
|
1237
|
+
return { id: channelId, title, type: forum ? "forum" : "supergroup", inviteLink };
|
|
1238
|
+
}
|
|
1239
|
+
// Create basic group via messages.CreateChat
|
|
1240
|
+
const inputUsers = [];
|
|
1241
|
+
for (const u of users) {
|
|
1242
|
+
try {
|
|
1243
|
+
const entity = await this.client.getEntity(u);
|
|
1244
|
+
if (entity instanceof Api.User) {
|
|
1245
|
+
inputUsers.push(new Api.InputUser({ userId: entity.id, accessHash: entity.accessHash ?? bigInt.zero }));
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
catch {
|
|
1249
|
+
// Skip unresolvable users
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
if (inputUsers.length === 0) {
|
|
1253
|
+
throw new Error("At least one valid user is required to create a basic group");
|
|
1254
|
+
}
|
|
1255
|
+
const result = await this.client.invoke(new Api.messages.CreateChat({
|
|
1256
|
+
title,
|
|
1257
|
+
users: inputUsers,
|
|
1258
|
+
}));
|
|
1259
|
+
const updates = result;
|
|
1260
|
+
const chat = updates.chats?.[0];
|
|
1261
|
+
if (!chat)
|
|
1262
|
+
throw new Error("Failed to create group");
|
|
1263
|
+
return { id: chat.id.toString(), title, type: "group" };
|
|
1264
|
+
}
|
|
1265
|
+
async inviteToGroup(chatId, users) {
|
|
1266
|
+
if (!this.client)
|
|
1267
|
+
throw new Error("Not connected");
|
|
1268
|
+
const entity = await this.client.getEntity(chatId);
|
|
1269
|
+
const invited = [];
|
|
1270
|
+
const failed = [];
|
|
1271
|
+
for (const u of users) {
|
|
1272
|
+
try {
|
|
1273
|
+
const user = await this.client.getEntity(u);
|
|
1274
|
+
if (!(user instanceof Api.User)) {
|
|
1275
|
+
failed.push(u);
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
const inputUser = new Api.InputUser({ userId: user.id, accessHash: user.accessHash ?? bigInt.zero });
|
|
1279
|
+
if (entity instanceof Api.Channel) {
|
|
1280
|
+
await this.client.invoke(new Api.channels.InviteToChannel({ channel: entity, users: [inputUser] }));
|
|
1281
|
+
}
|
|
1282
|
+
else if (entity instanceof Api.Chat) {
|
|
1283
|
+
await this.client.invoke(new Api.messages.AddChatUser({ chatId: entity.id, userId: inputUser, fwdLimit: 50 }));
|
|
1284
|
+
}
|
|
1285
|
+
invited.push(u);
|
|
1286
|
+
}
|
|
1287
|
+
catch {
|
|
1288
|
+
failed.push(u);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
return { invited, failed };
|
|
1292
|
+
}
|
|
1293
|
+
async kickUser(chatId, userId) {
|
|
1294
|
+
if (!this.client)
|
|
1295
|
+
throw new Error("Not connected");
|
|
1296
|
+
const entity = await this.client.getEntity(chatId);
|
|
1297
|
+
const user = await this.client.getEntity(userId);
|
|
1298
|
+
if (!(user instanceof Api.User))
|
|
1299
|
+
throw new Error("Target is not a user");
|
|
1300
|
+
const inputUser = new Api.InputUser({ userId: user.id, accessHash: user.accessHash ?? bigInt.zero });
|
|
1301
|
+
if (entity instanceof Api.Channel) {
|
|
1302
|
+
// Kick = ban + unban (removes without permanent ban)
|
|
1303
|
+
await this.client.invoke(new Api.channels.EditBanned({
|
|
1304
|
+
channel: entity,
|
|
1305
|
+
participant: inputUser,
|
|
1306
|
+
bannedRights: new Api.ChatBannedRights({ untilDate: 0, viewMessages: true }),
|
|
1307
|
+
}));
|
|
1308
|
+
await this.client.invoke(new Api.channels.EditBanned({
|
|
1309
|
+
channel: entity,
|
|
1310
|
+
participant: inputUser,
|
|
1311
|
+
bannedRights: new Api.ChatBannedRights({ untilDate: 0 }),
|
|
1312
|
+
}));
|
|
1313
|
+
}
|
|
1314
|
+
else if (entity instanceof Api.Chat) {
|
|
1315
|
+
await this.client.invoke(new Api.messages.DeleteChatUser({ chatId: entity.id, userId: inputUser }));
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
async banUser(chatId, userId) {
|
|
1319
|
+
if (!this.client)
|
|
1320
|
+
throw new Error("Not connected");
|
|
1321
|
+
const entity = await this.client.getEntity(chatId);
|
|
1322
|
+
const user = await this.client.getEntity(userId);
|
|
1323
|
+
if (!(user instanceof Api.User))
|
|
1324
|
+
throw new Error("Target is not a user");
|
|
1325
|
+
if (!(entity instanceof Api.Channel))
|
|
1326
|
+
throw new Error("Ban is only supported for supergroups and channels");
|
|
1327
|
+
const inputUser = new Api.InputUser({ userId: user.id, accessHash: user.accessHash ?? bigInt.zero });
|
|
1328
|
+
await this.client.invoke(new Api.channels.EditBanned({
|
|
1329
|
+
channel: entity,
|
|
1330
|
+
participant: inputUser,
|
|
1331
|
+
bannedRights: new Api.ChatBannedRights({ untilDate: 0, viewMessages: true }),
|
|
1332
|
+
}));
|
|
1333
|
+
}
|
|
1334
|
+
async unbanUser(chatId, userId) {
|
|
1335
|
+
if (!this.client)
|
|
1336
|
+
throw new Error("Not connected");
|
|
1337
|
+
const entity = await this.client.getEntity(chatId);
|
|
1338
|
+
const user = await this.client.getEntity(userId);
|
|
1339
|
+
if (!(user instanceof Api.User))
|
|
1340
|
+
throw new Error("Target is not a user");
|
|
1341
|
+
if (!(entity instanceof Api.Channel))
|
|
1342
|
+
throw new Error("Unban is only supported for supergroups and channels");
|
|
1343
|
+
const inputUser = new Api.InputUser({ userId: user.id, accessHash: user.accessHash ?? bigInt.zero });
|
|
1344
|
+
await this.client.invoke(new Api.channels.EditBanned({
|
|
1345
|
+
channel: entity,
|
|
1346
|
+
participant: inputUser,
|
|
1347
|
+
bannedRights: new Api.ChatBannedRights({ untilDate: 0 }),
|
|
1348
|
+
}));
|
|
1349
|
+
}
|
|
1350
|
+
async editGroup(chatId, options) {
|
|
1351
|
+
if (!this.client)
|
|
1352
|
+
throw new Error("Not connected");
|
|
1353
|
+
const entity = await this.client.getEntity(chatId);
|
|
1354
|
+
if (options.title) {
|
|
1355
|
+
if (entity instanceof Api.Channel) {
|
|
1356
|
+
await this.client.invoke(new Api.channels.EditTitle({ channel: entity, title: options.title }));
|
|
1357
|
+
}
|
|
1358
|
+
else if (entity instanceof Api.Chat) {
|
|
1359
|
+
await this.client.invoke(new Api.messages.EditChatTitle({ chatId: entity.id, title: options.title }));
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
if (options.description != null) {
|
|
1363
|
+
await this.client.invoke(new Api.messages.EditChatAbout({ peer: entity, about: options.description }));
|
|
1364
|
+
}
|
|
1365
|
+
if (options.photoPath) {
|
|
1366
|
+
const fileData = readFileSync(options.photoPath);
|
|
1367
|
+
const uploaded = await this.client.uploadFile({
|
|
1368
|
+
file: new CustomFile(options.photoPath, fileData.length, options.photoPath, fileData),
|
|
1369
|
+
workers: 1,
|
|
1370
|
+
});
|
|
1371
|
+
const inputPhoto = new Api.InputChatUploadedPhoto({ file: uploaded });
|
|
1372
|
+
if (entity instanceof Api.Channel) {
|
|
1373
|
+
await this.client.invoke(new Api.channels.EditPhoto({ channel: entity, photo: inputPhoto }));
|
|
1374
|
+
}
|
|
1375
|
+
else if (entity instanceof Api.Chat) {
|
|
1376
|
+
await this.client.invoke(new Api.messages.EditChatPhoto({ chatId: entity.id, photo: inputPhoto }));
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
async leaveGroup(chatId) {
|
|
1381
|
+
if (!this.client)
|
|
1382
|
+
throw new Error("Not connected");
|
|
1383
|
+
const entity = await this.client.getEntity(chatId);
|
|
1384
|
+
if (entity instanceof Api.Channel) {
|
|
1385
|
+
await this.client.invoke(new Api.channels.LeaveChannel({ channel: entity }));
|
|
1386
|
+
}
|
|
1387
|
+
else if (entity instanceof Api.Chat) {
|
|
1388
|
+
await this.client.invoke(new Api.messages.DeleteChatUser({
|
|
1389
|
+
chatId: entity.id,
|
|
1390
|
+
userId: new Api.InputUserSelf(),
|
|
1391
|
+
}));
|
|
1392
|
+
}
|
|
1393
|
+
else {
|
|
1394
|
+
throw new Error("Target is not a group or channel");
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
async setAdmin(chatId, userId, options) {
|
|
1398
|
+
if (!this.client)
|
|
1399
|
+
throw new Error("Not connected");
|
|
1400
|
+
const entity = await this.client.getEntity(chatId);
|
|
1401
|
+
if (!(entity instanceof Api.Channel))
|
|
1402
|
+
throw new Error("Set admin is only supported for supergroups and channels");
|
|
1403
|
+
const user = await this.client.getEntity(userId);
|
|
1404
|
+
if (!(user instanceof Api.User))
|
|
1405
|
+
throw new Error("Target is not a user");
|
|
1406
|
+
const inputUser = new Api.InputUser({ userId: user.id, accessHash: user.accessHash ?? bigInt.zero });
|
|
1407
|
+
await this.client.invoke(new Api.channels.EditAdmin({
|
|
1408
|
+
channel: entity,
|
|
1409
|
+
userId: inputUser,
|
|
1410
|
+
adminRights: new Api.ChatAdminRights({
|
|
1411
|
+
changeInfo: true,
|
|
1412
|
+
postMessages: true,
|
|
1413
|
+
editMessages: true,
|
|
1414
|
+
deleteMessages: true,
|
|
1415
|
+
banUsers: true,
|
|
1416
|
+
inviteUsers: true,
|
|
1417
|
+
pinMessages: true,
|
|
1418
|
+
manageCall: true,
|
|
1419
|
+
}),
|
|
1420
|
+
rank: options?.title ?? "",
|
|
1421
|
+
}));
|
|
1422
|
+
}
|
|
1423
|
+
async removeAdmin(chatId, userId) {
|
|
1424
|
+
if (!this.client)
|
|
1425
|
+
throw new Error("Not connected");
|
|
1426
|
+
const entity = await this.client.getEntity(chatId);
|
|
1427
|
+
if (!(entity instanceof Api.Channel))
|
|
1428
|
+
throw new Error("Remove admin is only supported for supergroups and channels");
|
|
1429
|
+
const user = await this.client.getEntity(userId);
|
|
1430
|
+
if (!(user instanceof Api.User))
|
|
1431
|
+
throw new Error("Target is not a user");
|
|
1432
|
+
const inputUser = new Api.InputUser({ userId: user.id, accessHash: user.accessHash ?? bigInt.zero });
|
|
1433
|
+
await this.client.invoke(new Api.channels.EditAdmin({
|
|
1434
|
+
channel: entity,
|
|
1435
|
+
userId: inputUser,
|
|
1436
|
+
adminRights: new Api.ChatAdminRights({}),
|
|
1437
|
+
rank: "",
|
|
1438
|
+
}));
|
|
1439
|
+
}
|
|
1190
1440
|
}
|
package/dist/tools/chats.js
CHANGED
|
@@ -100,6 +100,33 @@ export function registerChatTools(server, telegram) {
|
|
|
100
100
|
return fail(e);
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
+
server.registerTool("telegram-create-group", {
|
|
104
|
+
description: "Create a new Telegram group or supergroup",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
title: z.string().describe("Group name"),
|
|
107
|
+
users: z.array(z.string()).describe("Usernames or IDs to invite"),
|
|
108
|
+
supergroup: z.boolean().default(false).describe("Create as supergroup (supports >200 members, admin features)"),
|
|
109
|
+
forum: z.boolean().default(false).describe("Enable topics (requires supergroup)"),
|
|
110
|
+
description: z.string().optional().describe("Group description"),
|
|
111
|
+
},
|
|
112
|
+
annotations: WRITE,
|
|
113
|
+
}, async ({ title, users, supergroup, forum, description }) => {
|
|
114
|
+
const err = await requireConnection(telegram);
|
|
115
|
+
if (err)
|
|
116
|
+
return fail(new Error(err));
|
|
117
|
+
try {
|
|
118
|
+
const result = await telegram.createGroup({ title, users, supergroup, forum, description });
|
|
119
|
+
const lines = [
|
|
120
|
+
`Created ${result.type}: ${result.title}`,
|
|
121
|
+
`ID: ${result.id}`,
|
|
122
|
+
...(result.inviteLink ? [`Invite link: ${result.inviteLink}`] : []),
|
|
123
|
+
];
|
|
124
|
+
return ok(lines.join("\n"));
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
return fail(e);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
103
130
|
server.registerTool("telegram-join-chat", {
|
|
104
131
|
description: "Join a Telegram group or channel by username or invite link",
|
|
105
132
|
inputSchema: {
|
|
@@ -118,4 +145,164 @@ export function registerChatTools(server, telegram) {
|
|
|
118
145
|
return fail(e);
|
|
119
146
|
}
|
|
120
147
|
});
|
|
148
|
+
server.registerTool("telegram-leave-group", {
|
|
149
|
+
description: "Leave a Telegram group or channel",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
152
|
+
},
|
|
153
|
+
annotations: WRITE,
|
|
154
|
+
}, async ({ chatId }) => {
|
|
155
|
+
const err = await requireConnection(telegram);
|
|
156
|
+
if (err)
|
|
157
|
+
return fail(new Error(err));
|
|
158
|
+
try {
|
|
159
|
+
await telegram.leaveGroup(chatId);
|
|
160
|
+
return ok(`Left chat ${chatId}`);
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
return fail(e);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
server.registerTool("telegram-invite-to-group", {
|
|
167
|
+
description: "Invite users to a Telegram group or channel",
|
|
168
|
+
inputSchema: {
|
|
169
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
170
|
+
users: z.array(z.string()).describe("Usernames or IDs to invite"),
|
|
171
|
+
},
|
|
172
|
+
annotations: WRITE,
|
|
173
|
+
}, async ({ chatId, users }) => {
|
|
174
|
+
const err = await requireConnection(telegram);
|
|
175
|
+
if (err)
|
|
176
|
+
return fail(new Error(err));
|
|
177
|
+
try {
|
|
178
|
+
const result = await telegram.inviteToGroup(chatId, users);
|
|
179
|
+
const lines = [];
|
|
180
|
+
if (result.invited.length > 0)
|
|
181
|
+
lines.push(`Invited: ${result.invited.join(", ")}`);
|
|
182
|
+
if (result.failed.length > 0)
|
|
183
|
+
lines.push(`Failed: ${result.failed.join(", ")}`);
|
|
184
|
+
return ok(lines.join("\n") || "No users processed");
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
return fail(e);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
server.registerTool("telegram-kick-user", {
|
|
191
|
+
description: "Kick a user from a Telegram group (removes without permanent ban)",
|
|
192
|
+
inputSchema: {
|
|
193
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
194
|
+
userId: z.string().describe("User ID or username to kick"),
|
|
195
|
+
},
|
|
196
|
+
annotations: WRITE,
|
|
197
|
+
}, async ({ chatId, userId }) => {
|
|
198
|
+
const err = await requireConnection(telegram);
|
|
199
|
+
if (err)
|
|
200
|
+
return fail(new Error(err));
|
|
201
|
+
try {
|
|
202
|
+
await telegram.kickUser(chatId, userId);
|
|
203
|
+
return ok(`Kicked ${userId} from ${chatId}`);
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
return fail(e);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
server.registerTool("telegram-ban-user", {
|
|
210
|
+
description: "Ban a user from a supergroup or channel (permanent until unbanned)",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
213
|
+
userId: z.string().describe("User ID or username to ban"),
|
|
214
|
+
},
|
|
215
|
+
annotations: WRITE,
|
|
216
|
+
}, async ({ chatId, userId }) => {
|
|
217
|
+
const err = await requireConnection(telegram);
|
|
218
|
+
if (err)
|
|
219
|
+
return fail(new Error(err));
|
|
220
|
+
try {
|
|
221
|
+
await telegram.banUser(chatId, userId);
|
|
222
|
+
return ok(`Banned ${userId} from ${chatId}`);
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
return fail(e);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
server.registerTool("telegram-unban-user", {
|
|
229
|
+
description: "Unban a previously banned user from a supergroup or channel",
|
|
230
|
+
inputSchema: {
|
|
231
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
232
|
+
userId: z.string().describe("User ID or username to unban"),
|
|
233
|
+
},
|
|
234
|
+
annotations: WRITE,
|
|
235
|
+
}, async ({ chatId, userId }) => {
|
|
236
|
+
const err = await requireConnection(telegram);
|
|
237
|
+
if (err)
|
|
238
|
+
return fail(new Error(err));
|
|
239
|
+
try {
|
|
240
|
+
await telegram.unbanUser(chatId, userId);
|
|
241
|
+
return ok(`Unbanned ${userId} in ${chatId}`);
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
return fail(e);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
server.registerTool("telegram-edit-group", {
|
|
248
|
+
description: "Edit a group's title, description, or photo",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
251
|
+
title: z.string().optional().describe("New group title"),
|
|
252
|
+
description: z.string().optional().describe("New group description (supergroups only)"),
|
|
253
|
+
photoPath: z.string().optional().describe("Absolute path to new group photo image file"),
|
|
254
|
+
},
|
|
255
|
+
annotations: WRITE,
|
|
256
|
+
}, async ({ chatId, title, description, photoPath }) => {
|
|
257
|
+
const err = await requireConnection(telegram);
|
|
258
|
+
if (err)
|
|
259
|
+
return fail(new Error(err));
|
|
260
|
+
try {
|
|
261
|
+
await telegram.editGroup(chatId, { title, description, photoPath });
|
|
262
|
+
const changed = [title && "title", description != null && "description", photoPath && "photo"].filter(Boolean);
|
|
263
|
+
return ok(`Updated ${changed.join(", ")} for ${chatId}`);
|
|
264
|
+
}
|
|
265
|
+
catch (e) {
|
|
266
|
+
return fail(e);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
server.registerTool("telegram-set-admin", {
|
|
270
|
+
description: "Promote a user to admin in a supergroup or channel with full permissions",
|
|
271
|
+
inputSchema: {
|
|
272
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
273
|
+
userId: z.string().describe("User ID or username to promote"),
|
|
274
|
+
title: z.string().optional().describe("Custom admin title"),
|
|
275
|
+
},
|
|
276
|
+
annotations: WRITE,
|
|
277
|
+
}, async ({ chatId, userId, title }) => {
|
|
278
|
+
const err = await requireConnection(telegram);
|
|
279
|
+
if (err)
|
|
280
|
+
return fail(new Error(err));
|
|
281
|
+
try {
|
|
282
|
+
await telegram.setAdmin(chatId, userId, { title });
|
|
283
|
+
return ok(`Promoted ${userId} to admin in ${chatId}${title ? ` (${title})` : ""}`);
|
|
284
|
+
}
|
|
285
|
+
catch (e) {
|
|
286
|
+
return fail(e);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
server.registerTool("telegram-remove-admin", {
|
|
290
|
+
description: "Remove admin rights from a user in a supergroup or channel",
|
|
291
|
+
inputSchema: {
|
|
292
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
293
|
+
userId: z.string().describe("User ID or username to demote"),
|
|
294
|
+
},
|
|
295
|
+
annotations: WRITE,
|
|
296
|
+
}, async ({ chatId, userId }) => {
|
|
297
|
+
const err = await requireConnection(telegram);
|
|
298
|
+
if (err)
|
|
299
|
+
return fail(new Error(err));
|
|
300
|
+
try {
|
|
301
|
+
await telegram.removeAdmin(chatId, userId);
|
|
302
|
+
return ok(`Removed admin rights from ${userId} in ${chatId}`);
|
|
303
|
+
}
|
|
304
|
+
catch (e) {
|
|
305
|
+
return fail(e);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
121
308
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@overpod/mcp-telegram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "MCP server for Telegram userbot — messages, media, reactions, polls & more. Built on GramJS/MTProto.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"zod": "^4.3.6"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@biomejs/biome": "^2.4.
|
|
59
|
+
"@biomejs/biome": "^2.4.9",
|
|
60
60
|
"@types/node": "^25.5.0",
|
|
61
61
|
"@types/qrcode": "^1.5.6",
|
|
62
62
|
"tsx": "^4.21.0",
|