@overpod/mcp-telegram 1.39.0 → 1.39.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.39.1](https://github.com/mcp-telegram/mcp-telegram/compare/v1.39.0...v1.39.1) (2026-06-21)
9
+
10
+
11
+ ### Fixed
12
+
13
+ * **resolve:** recover bare numeric peer IDs that lack access_hash ([#62](https://github.com/mcp-telegram/mcp-telegram/issues/62)) ([678077a](https://github.com/mcp-telegram/mcp-telegram/commit/678077a78ce3a5852e07d5d831f664c3e08a81e3))
14
+
8
15
  ## [1.39.0](https://github.com/mcp-telegram/mcp-telegram/compare/v1.38.2...v1.39.0) (2026-06-21)
9
16
 
10
17
 
@@ -276,6 +276,13 @@ export declare class TelegramService {
276
276
  * Handles display names by searching dialogs.
277
277
  */
278
278
  private resolvePeer;
279
+ /**
280
+ * Resolve a bare numeric ID to a cached/dialog entity so GramJS can build a
281
+ * valid InputPeer. Falls back to the raw ID string if no dialog matches —
282
+ * GramJS may still resolve it (e.g. a contact or a peer it has messaged),
283
+ * and we must not regress that path.
284
+ */
285
+ private resolveNumericPeer;
279
286
  getChatInfo(chatId: string): Promise<{
280
287
  id: string;
281
288
  name: string;
@@ -1191,12 +1191,78 @@ export class TelegramService {
1191
1191
  // Normalize '@me' — GramJS only intercepts the plain 'me' string as InputPeerSelf
1192
1192
  if (chatId === "@me")
1193
1193
  return "me";
1194
- // Numeric IDs and @usernames work directly
1195
- if (/^-?\d+$/.test(chatId) || chatId.startsWith("@"))
1194
+ // @usernames resolve directly via contacts.ResolveUsername
1195
+ if (chatId.startsWith("@"))
1196
1196
  return chatId;
1197
- // Everything else resolve via dialogs
1197
+ // Bare numeric IDs need an entity with access_hash. GramJS can build an
1198
+ // InputPeer from a raw number only if it's already cached or the account
1199
+ // is a contact / has messaged us — otherwise getInputEntity throws
1200
+ // "Could not find the input entity". A bare positive number is also
1201
+ // ambiguous (GramJS assumes PeerUser, so channel IDs fail outright).
1202
+ // Recover by looking the ID up among dialogs, which yields a full entity
1203
+ // (with access_hash) for both users and channels.
1204
+ if (/^-?\d+$/.test(chatId))
1205
+ return this.resolveNumericPeer(chatId);
1206
+ // Everything else — resolve display name via dialogs
1198
1207
  return this.resolveChat(chatId);
1199
1208
  }
1209
+ /**
1210
+ * Resolve a bare numeric ID to a cached/dialog entity so GramJS can build a
1211
+ * valid InputPeer. Falls back to the raw ID string if no dialog matches —
1212
+ * GramJS may still resolve it (e.g. a contact or a peer it has messaged),
1213
+ * and we must not regress that path.
1214
+ */
1215
+ async resolveNumericPeer(chatId) {
1216
+ if (!this.client)
1217
+ throw new Error(NOT_CONNECTED_ERROR);
1218
+ const cached = this.entityCache.get(chatId);
1219
+ if (cached)
1220
+ return cached;
1221
+ // Direct resolve first — succeeds when GramJS already knows the peer.
1222
+ try {
1223
+ const entity = await this.client.getEntity(chatId);
1224
+ this.entityCache.set(chatId, entity);
1225
+ return entity;
1226
+ }
1227
+ catch {
1228
+ // Fall through to dialog scan.
1229
+ }
1230
+ // Scan dialogs for a matching entity. IDs reach us in two shapes:
1231
+ // • bare positive (e.g. 1004294063929 for a channel, 8959122940 for a
1232
+ // user) — this is what list-chats/search emit and what GramJS can't
1233
+ // disambiguate; match it against any entity's bare id.
1234
+ // • marked (-100<id> for channels, -<id> for basic groups) — the
1235
+ // sign/-100 prefix carries the type, so require the entity to match that
1236
+ // exact marked form, otherwise a group "-123" could wrongly match a user
1237
+ // with bare id 123.
1238
+ const isMarked = chatId.startsWith("-");
1239
+ try {
1240
+ const dialogs = await this.client.getDialogs({ limit: 100 });
1241
+ const match = dialogs.find((d) => {
1242
+ const entity = d.entity;
1243
+ if (!entity?.id)
1244
+ return false;
1245
+ const bare = entity.id.toString();
1246
+ if (!isMarked)
1247
+ return chatId === bare;
1248
+ // Marked input must match the entity's marked form.
1249
+ if (entity instanceof Api.Channel)
1250
+ return chatId === `-100${bare}`;
1251
+ if (entity instanceof Api.Chat)
1252
+ return chatId === `-${bare}`;
1253
+ return false;
1254
+ });
1255
+ if (match?.entity) {
1256
+ this.entityCache.set(chatId, match.entity);
1257
+ return match.entity;
1258
+ }
1259
+ }
1260
+ catch {
1261
+ // Dialog fetch failed — fall back to the raw ID below.
1262
+ }
1263
+ // Last resort: hand the raw ID to GramJS and let it try GetUsers/GetChannels.
1264
+ return chatId;
1265
+ }
1200
1266
  async getChatInfo(chatId) {
1201
1267
  if (!this.client || !this.connected)
1202
1268
  throw new Error(NOT_CONNECTED_ERROR);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overpod/mcp-telegram",
3
- "version": "1.39.0",
3
+ "version": "1.39.1",
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",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "devDependencies": {
74
74
  "@biomejs/biome": "^2.5.0",
75
- "@types/node": "^25.9.3",
75
+ "@types/node": "^26.0.0",
76
76
  "@types/qrcode": "^1.5.6",
77
77
  "c8": "^11.0.0",
78
78
  "tsx": "^4.22.4",