@imessaging/telegram-mtproto 0.8.2 → 0.8.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.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Telegram MTProto user-account transport for imessaging",
5
5
  "keywords": [
6
6
  "messaging",
@@ -31,7 +31,7 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@imessaging/core": "^0.8.2",
34
+ "@imessaging/core": "^0.8.3",
35
35
  "big-integer": "^1.6.52"
36
36
  },
37
37
  "peerDependencies": {
package/src/entities.ts CHANGED
@@ -22,6 +22,10 @@ export function toTelegramEntities(ft: FormattedText): Api.TypeMessageEntity[] {
22
22
  return new Api.MessageEntityPre({ ...base, language: e.language ?? "" });
23
23
  case "link":
24
24
  return new Api.MessageEntityTextUrl({ ...base, url: e.url ?? "" });
25
+ case "spoiler":
26
+ return new Api.MessageEntitySpoiler(base);
27
+ case "quote":
28
+ return new Api.MessageEntityBlockquote(base);
25
29
  }
26
30
  });
27
31
  }
package/src/incoming.ts CHANGED
@@ -1,4 +1,10 @@
1
- import type { IncomingAttachment, IncomingLink, IncomingMessage } from "@imessaging/core";
1
+ import type {
2
+ IncomingAttachment,
3
+ IncomingLink,
4
+ IncomingMessage,
5
+ TextEntity,
6
+ TextEntityType,
7
+ } from "@imessaging/core";
2
8
 
3
9
  /** Пир MTProto в той части, по которой определяется чат. Приходит из `Api.TypePeer`. */
4
10
  export type MtprotoPeerLike = {
@@ -93,6 +99,7 @@ export type MtprotoEntityLike = {
93
99
  offset?: number;
94
100
  length?: number;
95
101
  url?: string;
102
+ language?: string;
96
103
  };
97
104
 
98
105
  /** Кнопка под сообщением. Адрес несёт только `KeyboardButtonUrl`, у остальных его нет. */
@@ -233,6 +240,35 @@ export function collectLinks(message: MtprotoMessageLike): IncomingLink[] {
233
240
  return links;
234
241
  }
235
242
 
243
+ const FORMATTING: Record<string, TextEntityType> = {
244
+ MessageEntityBold: "bold",
245
+ MessageEntityItalic: "italic",
246
+ MessageEntityUnderline: "underline",
247
+ MessageEntityStrike: "strike",
248
+ MessageEntitySpoiler: "spoiler",
249
+ MessageEntityCode: "code",
250
+ MessageEntityPre: "pre",
251
+ MessageEntityTextUrl: "link",
252
+ MessageEntityBlockquote: "quote",
253
+ };
254
+
255
+ export function formattingEntities(message: MtprotoMessageLike): TextEntity[] {
256
+ const result: TextEntity[] = [];
257
+ for (const entity of message.entities ?? []) {
258
+ // Упоминания, хэштеги, голые адреса — не форматирование: пары в общей модели у них нет.
259
+ const type = FORMATTING[entity.className ?? ""];
260
+ if (!type || entity.offset === undefined || entity.length === undefined) continue;
261
+ result.push({
262
+ type,
263
+ offset: entity.offset,
264
+ length: entity.length,
265
+ ...(type === "link" && entity.url ? { url: entity.url } : {}),
266
+ ...(type === "pre" && entity.language ? { language: entity.language } : {}),
267
+ });
268
+ }
269
+ return result;
270
+ }
271
+
236
272
  /**
237
273
  * Сообщение живой учётной записи → общий вид.
238
274
  *
@@ -297,6 +333,7 @@ export function toIncomingMessage(
297
333
  sentAt: message.date ? new Date(message.date * 1000) : undefined,
298
334
  // Непоправленное сообщение приходит с `editDate: null` — это не правка в 1970 году.
299
335
  editedAt: message.editDate ? new Date(message.editDate * 1000) : undefined,
336
+ entities: formattingEntities(message),
300
337
  attachments: collectAttachments(message, chatId),
301
338
  links: collectLinks(message),
302
339
  mentionsMe: typeof message.mentioned === "boolean" ? message.mentioned : undefined,
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export {
7
7
  collectAttachments,
8
8
  collectLinks,
9
9
  downloadFromMessage,
10
+ formattingEntities,
10
11
  IncomingDispatcher,
11
12
  peerToChat,
12
13
  unmarkPeerId,
@@ -7,6 +7,7 @@ import type {
7
7
  ReadReceiptTransport,
8
8
  EditableMessage,
9
9
  EditableMessageTransport,
10
+ EditedMessageTransport,
10
11
  EditResult,
11
12
  MessageReaction,
12
13
  MessageRecipient,
@@ -27,6 +28,7 @@ import { Api, TelegramClient } from "telegram";
27
28
  import { CustomFile } from "telegram/client/uploads";
28
29
  import { CallbackQuery } from "telegram/events/CallbackQuery";
29
30
  import { NewMessage } from "telegram/events/NewMessage";
31
+ import { EditedMessage } from "telegram/events/EditedMessage";
30
32
  import type { TelegramClientParams } from "telegram/client/telegramBaseClient";
31
33
  import { StringSession } from "telegram/sessions";
32
34
 
@@ -89,6 +91,7 @@ function isChannel(chat: Api.TypeChat): chat is Api.Channel {
89
91
  export class TelegramMtprotoTransport
90
92
  implements
91
93
  EditableMessageTransport,
94
+ EditedMessageTransport,
92
95
  ButtonPressTransport,
93
96
  IncomingMessageTransport,
94
97
  ReactionTransport,
@@ -102,6 +105,8 @@ export class TelegramMtprotoTransport
102
105
  private lastActivityAt?: Date;
103
106
  private readonly incoming = new IncomingDispatcher();
104
107
  private incomingListener: ((event: unknown) => void) | null = null;
108
+ private readonly edits = new IncomingDispatcher();
109
+ private editListener: ((event: unknown) => void) | null = null;
105
110
  /** Отметка жизни подписки: срабатывает на любом обновлении, включая служебные. */
106
111
  private activityListener: ((event: unknown) => void) | null = null;
107
112
 
@@ -187,6 +192,34 @@ export class TelegramMtprotoTransport
187
192
  };
188
193
  }
189
194
 
195
+ onEdit(handler: (message: IncomingMessage) => void | Promise<void>): () => void {
196
+ const remove = this.edits.add(handler);
197
+ this.startEditListeningIfNeeded();
198
+ return () => {
199
+ remove();
200
+ this.stopEditListeningIfIdle();
201
+ };
202
+ }
203
+
204
+ private startEditListeningIfNeeded(): void {
205
+ if (this.editListener !== null || this.edits.size === 0) return;
206
+ const listener = (event: unknown): void => {
207
+ const message = (event as { message?: MtprotoMessageLike }).message;
208
+ if (!message) return;
209
+ this.lastActivityAt = new Date();
210
+ void this.rememberSender(message);
211
+ void this.edits.dispatch(message, this.id, this.options.accountId);
212
+ };
213
+ this.editListener = listener;
214
+ this.client.addEventHandler(listener, new EditedMessage({}));
215
+ }
216
+
217
+ private stopEditListeningIfIdle(): void {
218
+ if (this.edits.size > 0 || this.editListener === null) return;
219
+ this.client.removeEventHandler(this.editListener, new EditedMessage({}));
220
+ this.editListener = null;
221
+ }
222
+
190
223
  private startListeningIfNeeded(): void {
191
224
  if (this.incomingListener !== null || this.incoming.size === 0) return;
192
225
  const listener = (event: unknown): void => {