@overpod/mcp-telegram 1.15.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.
@@ -267,4 +267,21 @@ export declare class TelegramService {
267
267
  type: string;
268
268
  inviteLink?: string;
269
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>;
270
287
  }
@@ -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));
@@ -1261,4 +1262,179 @@ export class TelegramService {
1261
1262
  throw new Error("Failed to create group");
1262
1263
  return { id: chat.id.toString(), title, type: "group" };
1263
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
+ }
1264
1440
  }
@@ -145,4 +145,164 @@ export function registerChatTools(server, telegram) {
145
145
  return fail(e);
146
146
  }
147
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
+ });
148
308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overpod/mcp-telegram",
3
- "version": "1.15.0",
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",