@imessaging/telegram-mtproto 0.6.1 → 0.6.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imessaging/telegram-mtproto",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Telegram MTProto user-account transport for imessaging",
5
5
  "keywords": [
6
6
  "messaging",
package/src/incoming.ts CHANGED
@@ -25,6 +25,15 @@ export type MtprotoMessageLike = {
25
25
  * `accessHash` живёт только здесь. Поле необязательное: в обновлении его может не быть.
26
26
  */
27
27
  sender?: { username?: string; accessHash?: { toString(): string } | string | number };
28
+ /**
29
+ * Пир отправителя ВМЕСТЕ с `accessHash` — так его отдаёт само обновление.
30
+ *
31
+ * Без него ответить по одному номеру нельзя: клиент знает только тех, кого уже разрешал, а
32
+ * `accessHash` из обновления больше нигде не хранится.
33
+ */
34
+ getInputSender?: () => Promise<{ userId?: { toString(): string }; accessHash?: { toString(): string } } | undefined>;
35
+ /** Отправитель целиком. Обращение к сущностям обновления, а не поход в сеть. */
36
+ getSender?: () => Promise<{ username?: string } | undefined>;
28
37
  };
29
38
 
30
39
  export type MtprotoMediaLike = {
@@ -42,18 +51,36 @@ export type MtprotoMediaLike = {
42
51
  *
43
52
  * `chatId` берётся из пира, а НЕ из отправителя: в группе они разные, и перепутав их, ответ уехал
44
53
  * бы в личку тому, кто написал в общий чат.
54
+ *
55
+ * ИДЕНТИФИКАТОР ОТДАЁТСЯ МАРКИРОВАННЫМ — тем самым, что знают Bot API, люди и соседние продукты:
56
+ * `-100…` у каналов и супергрупп, `-…` у обычных групп. Внутри MTProto он положительный, и если
57
+ * отдавать его как есть, один и тот же чат в разных системах называется двумя разными числами:
58
+ * замер 06.09.2026 — продукт хранит `-5386451338`, шлюз присылает `5386451338`, и предъявитель
59
+ * не находится. Тот же класс ошибки уже стоил продукту чата-призрака с потерянным знаком.
45
60
  */
46
61
  export function peerToChat(peer: MtprotoPeerLike | undefined): {
47
62
  chatId: string;
48
63
  chatType: IncomingMessage["chatType"];
49
64
  } {
50
65
  if (peer?.channelId !== undefined)
51
- return { chatId: peer.channelId.toString(), chatType: "channel" };
52
- if (peer?.chatId !== undefined) return { chatId: peer.chatId.toString(), chatType: "group" };
66
+ return { chatId: `-100${peer.channelId.toString()}`, chatType: "channel" };
67
+ if (peer?.chatId !== undefined) return { chatId: `-${peer.chatId.toString()}`, chatType: "group" };
53
68
  if (peer?.userId !== undefined) return { chatId: peer.userId.toString(), chatType: "user" };
54
69
  throw new Error("MTProto peer without userId, chatId or channelId");
55
70
  }
56
71
 
72
+ /**
73
+ * Маркированный идентификатор → тот, которым чат зовётся ВНУТРИ MTProto.
74
+ *
75
+ * Нужен на отправке: `InputPeerChat` и `InputPeerChannel` принимают положительное число, а снаружи
76
+ * ходит маркированное. Немаркированное число тоже принимается — тогда вид берётся из вызова.
77
+ */
78
+ export function unmarkPeerId(chatId: string): string {
79
+ if (chatId.startsWith("-100")) return chatId.slice(4);
80
+ if (chatId.startsWith("-")) return chatId.slice(1);
81
+ return chatId;
82
+ }
83
+
57
84
  /**
58
85
  * Что приехало файлом.
59
86
  *
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export {
7
7
  downloadFromMessage,
8
8
  IncomingDispatcher,
9
9
  peerToChat,
10
+ unmarkPeerId,
10
11
  toIncomingMessage,
11
12
  type DownloadingClient,
12
13
  type MtprotoMediaLike,
@@ -17,7 +17,7 @@ import type {
17
17
  TypingTransport,
18
18
  } from "@imessaging/core";
19
19
  import { isTelegramRecipient, MemoryPeerStore } from "@imessaging/core";
20
- import { peerToChat } from "./incoming";
20
+ import { peerToChat, unmarkPeerId } from "./incoming";
21
21
  import bigInt from "big-integer";
22
22
  import { Api, TelegramClient } from "telegram";
23
23
  import { CustomFile } from "telegram/client/uploads";
@@ -185,9 +185,12 @@ export class TelegramMtprotoTransport
185
185
  */
186
186
  private async rememberSender(message: MtprotoMessageLike): Promise<void> {
187
187
  try {
188
- const hash = message.sender?.accessHash;
189
- if (hash === undefined || hash === null) return;
190
188
  const { chatId, chatType } = peerToChat(message.peerId);
189
+ // Спрашиваем ПИР ОБНОВЛЕНИЯ, а не поле `sender`: у живого сообщения gramJS оно ленивое и
190
+ // чаще всего пустое, а `getInputSender` берёт собеседника из карты сущностей обновления.
191
+ const input = await message.getInputSender?.();
192
+ const hash = input?.accessHash ?? message.sender?.accessHash;
193
+ if (hash === undefined || hash === null) return;
191
194
  // В группе отправитель и чат — разные пиры, и запись сложила бы одно вместо другого.
192
195
  if (chatType !== "user") return;
193
196
  const recipient = { type: "user", id: chatId } as const;
@@ -198,7 +201,7 @@ export class TelegramMtprotoTransport
198
201
  type: "user",
199
202
  id: chatId,
200
203
  accessHash: String(hash),
201
- username: message.sender?.username,
204
+ username: message.sender?.username ?? (await message.getSender?.())?.username,
202
205
  resolvedAt: new Date().toISOString(),
203
206
  },
204
207
  this.options.peerTtlSeconds,
@@ -333,8 +336,12 @@ export class TelegramMtprotoTransport
333
336
  */
334
337
  private async knownInputPeer(recipient: MessageRecipient): Promise<Api.TypeInputPeer | null> {
335
338
  if (recipient.type === "email" || recipient.type === "username") return null;
339
+ // Обычной группе ни клиент, ни хранилище не нужны: `InputPeerChat` строится из одного номера.
340
+ if (recipient.type === "group") {
341
+ return new Api.InputPeerChat({ chatId: bigInt(unmarkPeerId(recipient.id)) });
342
+ }
336
343
  try {
337
- return await this.client.getInputEntity(bigInt(recipient.id));
344
+ return await this.client.getInputEntity(bigInt(unmarkPeerId(recipient.id)));
338
345
  } catch {
339
346
  return null;
340
347
  }
@@ -396,17 +403,18 @@ export class TelegramMtprotoTransport
396
403
 
397
404
  private toInputPeer(peer: ResolvedPeer): Api.TypeInputPeer {
398
405
  if (peer.type === "group") {
399
- return new Api.InputPeerChat({ chatId: bigInt(peer.id) });
406
+ // У обычной группы access hash не бывает вовсе — ей довольно номера.
407
+ return new Api.InputPeerChat({ chatId: bigInt(unmarkPeerId(peer.id)) });
400
408
  }
401
409
  if (!peer.accessHash) throw new Error(`Cached ${peer.type} peer has no accessHash`);
402
410
  if (peer.type === "user") {
403
411
  return new Api.InputPeerUser({
404
- userId: bigInt(peer.id),
412
+ userId: bigInt(unmarkPeerId(peer.id)),
405
413
  accessHash: bigInt(peer.accessHash),
406
414
  });
407
415
  }
408
416
  return new Api.InputPeerChannel({
409
- channelId: bigInt(peer.id),
417
+ channelId: bigInt(unmarkPeerId(peer.id)),
410
418
  accessHash: bigInt(peer.accessHash),
411
419
  });
412
420
  }