@imessaging/telegram-mtproto 0.7.3 → 0.8.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.
- package/package.json +2 -2
- package/src/entities.ts +27 -0
- package/src/incoming.ts +24 -7
- package/src/telegram-mtproto-transport.ts +17 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imessaging/telegram-mtproto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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.
|
|
34
|
+
"@imessaging/core": "^0.8.0",
|
|
35
35
|
"big-integer": "^1.6.52"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
package/src/entities.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Api } from "telegram";
|
|
2
|
+
import type { FormattedText } from "@imessaging/core";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Выделения FormattedText → сущности MTProto. Смещения у обоих в UTF-16, переводить их не нужно.
|
|
6
|
+
*/
|
|
7
|
+
export function toTelegramEntities(ft: FormattedText): Api.TypeMessageEntity[] {
|
|
8
|
+
return ft.entities.map((e) => {
|
|
9
|
+
const base = { offset: e.offset, length: e.length };
|
|
10
|
+
switch (e.type) {
|
|
11
|
+
case "bold":
|
|
12
|
+
return new Api.MessageEntityBold(base);
|
|
13
|
+
case "italic":
|
|
14
|
+
return new Api.MessageEntityItalic(base);
|
|
15
|
+
case "underline":
|
|
16
|
+
return new Api.MessageEntityUnderline(base);
|
|
17
|
+
case "strike":
|
|
18
|
+
return new Api.MessageEntityStrike(base);
|
|
19
|
+
case "code":
|
|
20
|
+
return new Api.MessageEntityCode(base);
|
|
21
|
+
case "pre":
|
|
22
|
+
return new Api.MessageEntityPre({ ...base, language: e.language ?? "" });
|
|
23
|
+
case "link":
|
|
24
|
+
return new Api.MessageEntityTextUrl({ ...base, url: e.url ?? "" });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
package/src/incoming.ts
CHANGED
|
@@ -14,9 +14,10 @@ export type MtprotoMessageLike = {
|
|
|
14
14
|
message?: string;
|
|
15
15
|
/** `true` — сообщение отправлено САМОЙ учётной записью. */
|
|
16
16
|
out?: boolean;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
// gramjs отдаёт отсутствующее поле как `null`, а не `undefined`: проверки ниже — на оба.
|
|
18
|
+
date?: number | null;
|
|
19
|
+
editDate?: number | null;
|
|
20
|
+
groupedId?: { toString(): string } | null;
|
|
20
21
|
peerId?: MtprotoPeerLike;
|
|
21
22
|
fromId?: MtprotoPeerLike;
|
|
22
23
|
media?: MtprotoMediaLike;
|
|
@@ -31,7 +32,7 @@ export type MtprotoMessageLike = {
|
|
|
31
32
|
replyMarkup?: { rows?: { buttons?: MtprotoButtonLike[] }[] };
|
|
32
33
|
replyTo?: {
|
|
33
34
|
replyToMsgId?: number | string;
|
|
34
|
-
replyToTopId?: number | string;
|
|
35
|
+
replyToTopId?: number | string | null;
|
|
35
36
|
forumTopic?: boolean;
|
|
36
37
|
};
|
|
37
38
|
/** Заголовок форварда. Пусто — сообщение своё, не пересланное. */
|
|
@@ -53,6 +54,13 @@ export type MtprotoMessageLike = {
|
|
|
53
54
|
title?: string;
|
|
54
55
|
accessHash?: { toString(): string } | string | number;
|
|
55
56
|
};
|
|
57
|
+
/** Сущность САМОГО чата из обновления: канал, группа или собеседник. Может не прийти. */
|
|
58
|
+
chat?: {
|
|
59
|
+
username?: string;
|
|
60
|
+
firstName?: string;
|
|
61
|
+
lastName?: string;
|
|
62
|
+
title?: string;
|
|
63
|
+
};
|
|
56
64
|
/**
|
|
57
65
|
* Пир отправителя ВМЕСТЕ с `accessHash` — так его отдаёт само обновление.
|
|
58
66
|
*
|
|
@@ -251,12 +259,18 @@ export function toIncomingMessage(
|
|
|
251
259
|
else if (message.sender.title) senderName = message.sender.title;
|
|
252
260
|
}
|
|
253
261
|
|
|
262
|
+
const chatName = [message.chat?.firstName, message.chat?.lastName]
|
|
263
|
+
.filter(Boolean)
|
|
264
|
+
.join(" ")
|
|
265
|
+
.trim();
|
|
266
|
+
const chatTitle = message.chat?.title || chatName || undefined;
|
|
267
|
+
|
|
254
268
|
let threadId: number | undefined;
|
|
255
269
|
let replyToMessageId: string | undefined;
|
|
256
270
|
if (message.replyTo?.forumTopic === true) {
|
|
257
271
|
threadId = Number(message.replyTo.replyToTopId ?? message.replyTo.replyToMsgId);
|
|
258
272
|
replyToMessageId =
|
|
259
|
-
message.replyTo.replyToTopId
|
|
273
|
+
message.replyTo.replyToTopId == null ? undefined : String(message.replyTo.replyToMsgId);
|
|
260
274
|
} else {
|
|
261
275
|
replyToMessageId =
|
|
262
276
|
message.replyTo?.replyToMsgId === undefined
|
|
@@ -274,11 +288,14 @@ export function toIncomingMessage(
|
|
|
274
288
|
// связывают человека со своей учётной записью.
|
|
275
289
|
senderUsername: message.sender?.username,
|
|
276
290
|
senderName,
|
|
291
|
+
chatTitle,
|
|
292
|
+
chatUsername: message.chat?.username ?? undefined,
|
|
277
293
|
messageId: String(message.id),
|
|
278
294
|
albumId: message.groupedId?.toString(),
|
|
279
295
|
text: message.message ?? "",
|
|
280
|
-
sentAt: message.date
|
|
281
|
-
|
|
296
|
+
sentAt: message.date ? new Date(message.date * 1000) : undefined,
|
|
297
|
+
// Непоправленное сообщение приходит с `editDate: null` — это не правка в 1970 году.
|
|
298
|
+
editedAt: message.editDate ? new Date(message.editDate * 1000) : undefined,
|
|
282
299
|
attachments: collectAttachments(message, chatId),
|
|
283
300
|
links: collectLinks(message),
|
|
284
301
|
replyToMessageId,
|
|
@@ -17,7 +17,8 @@ import type {
|
|
|
17
17
|
TransportStatus,
|
|
18
18
|
TypingTransport,
|
|
19
19
|
} from "@imessaging/core";
|
|
20
|
-
import { isTelegramRecipient, MemoryPeerStore } from "@imessaging/core";
|
|
20
|
+
import { isTelegramRecipient, MemoryPeerStore, formattedOf } from "@imessaging/core";
|
|
21
|
+
import { toTelegramEntities } from "./entities";
|
|
21
22
|
import { peerToChat, unmarkPeerId } from "./incoming";
|
|
22
23
|
import bigInt from "big-integer";
|
|
23
24
|
import { Api, TelegramClient } from "telegram";
|
|
@@ -442,9 +443,15 @@ export class TelegramMtprotoTransport
|
|
|
442
443
|
private async sendWithRetry(message: OutboundMessage, retried: boolean): Promise<SendResult> {
|
|
443
444
|
try {
|
|
444
445
|
const peer = await this.getInputPeer(message.recipient);
|
|
446
|
+
// С `format` уходят готовые выделения: gramjs тогда не разбирает текст своим markdown вовсе.
|
|
447
|
+
const ft = formattedOf(message);
|
|
448
|
+
const text = ft ? ft.text : message.text;
|
|
449
|
+
const parseMode = ft ? undefined : message.parseMode?.toLowerCase();
|
|
450
|
+
const formattingEntities = ft ? toTelegramEntities(ft) : undefined;
|
|
445
451
|
const sent = await this.client.sendMessage(peer, {
|
|
446
|
-
message:
|
|
447
|
-
parseMode
|
|
452
|
+
message: text,
|
|
453
|
+
parseMode,
|
|
454
|
+
formattingEntities,
|
|
448
455
|
replyTo: message.replyToMessageId ?? message.threadId,
|
|
449
456
|
});
|
|
450
457
|
for (const document of message.documents ?? []) {
|
|
@@ -604,12 +611,17 @@ export class TelegramMtprotoTransport
|
|
|
604
611
|
let peer: Api.TypeInputPeer | null = null;
|
|
605
612
|
try {
|
|
606
613
|
peer = await this.getInputPeer(message.recipient);
|
|
614
|
+
const ft = formattedOf(message);
|
|
615
|
+
const text = ft ? ft.text : message.text;
|
|
616
|
+
const parseMode = ft ? undefined : message.parseMode?.toLowerCase();
|
|
617
|
+
const formattingEntities = ft ? toTelegramEntities(ft) : undefined;
|
|
607
618
|
// Кнопки здесь не передаются — ровно как и при отправке этим транспортом: inline-клавиатуру
|
|
608
619
|
// MTProto-аккаунт не рисует. Передать их сюда значило бы обещать разметку, которой не будет.
|
|
609
620
|
await this.client.editMessage(peer, {
|
|
610
621
|
message: messageId,
|
|
611
|
-
text
|
|
612
|
-
parseMode
|
|
622
|
+
text,
|
|
623
|
+
parseMode,
|
|
624
|
+
formattingEntities,
|
|
613
625
|
});
|
|
614
626
|
this.lastActivityAt = new Date();
|
|
615
627
|
return {
|