@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 +2 -2
- package/src/entities.ts +4 -0
- package/src/incoming.ts +38 -1
- package/src/index.ts +1 -0
- package/src/telegram-mtproto-transport.ts +33 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imessaging/telegram-mtproto",
|
|
3
|
-
"version": "0.8.
|
|
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.
|
|
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 {
|
|
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 @@ 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 => {
|