@imessaging/telegram-mtproto 0.8.12 → 0.8.13
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/incoming.ts +4 -0
- package/src/telegram-mtproto-transport.ts +72 -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.13",
|
|
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.11",
|
|
35
35
|
"big-integer": "^1.6.52"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
package/src/incoming.ts
CHANGED
|
@@ -60,6 +60,8 @@ export type MtprotoMessageLike = {
|
|
|
60
60
|
lastName?: string;
|
|
61
61
|
title?: string;
|
|
62
62
|
accessHash?: { toString(): string } | string | number;
|
|
63
|
+
/** Есть только у пользователя (Api.User). */
|
|
64
|
+
mutualContact?: boolean;
|
|
63
65
|
};
|
|
64
66
|
/** Сущность САМОГО чата из обновления: канал, группа или собеседник. Может не прийти. */
|
|
65
67
|
chat?: {
|
|
@@ -325,6 +327,8 @@ export function toIncomingMessage(
|
|
|
325
327
|
// связывают человека со своей учётной записью.
|
|
326
328
|
senderUsername: message.sender?.username,
|
|
327
329
|
senderName,
|
|
330
|
+
senderMutualContact:
|
|
331
|
+
typeof message.sender?.mutualContact === "boolean" ? message.sender.mutualContact : undefined,
|
|
328
332
|
chatTitle,
|
|
329
333
|
chatUsername: message.chat?.username ?? undefined,
|
|
330
334
|
messageId: String(message.id),
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
OutgoingObserverTransport,
|
|
12
12
|
OutgoingReadReceipt,
|
|
13
13
|
OutgoingReadReceiptTransport,
|
|
14
|
+
PeerInfo,
|
|
15
|
+
PeerInfoTransport,
|
|
14
16
|
ButtonPress,
|
|
15
17
|
ButtonPressTransport,
|
|
16
18
|
IncomingAttachment,
|
|
@@ -108,6 +110,18 @@ const OWN_MEMORY_MS = 10 * 60 * 1000;
|
|
|
108
110
|
/** Номер не занят Telegram или не принят в этой форме — «не найдено», а не отказ. */
|
|
109
111
|
const PHONE_NOT_FOUND_ERRORS = new Set(["PHONE_NOT_OCCUPIED", "PHONE_NUMBER_INVALID"]);
|
|
110
112
|
|
|
113
|
+
/** @username не занят или записан неверно — «не найдено», а не отказ. */
|
|
114
|
+
const USERNAME_NOT_FOUND_ERRORS = new Set(["USERNAME_NOT_OCCUPIED", "USERNAME_INVALID"]);
|
|
115
|
+
|
|
116
|
+
function toPeerInfo(user: Api.User): PeerInfo {
|
|
117
|
+
return {
|
|
118
|
+
id: user.id.toString(),
|
|
119
|
+
username: user.username ?? undefined,
|
|
120
|
+
contact: Boolean(user.contact),
|
|
121
|
+
mutualContact: Boolean(user.mutualContact),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
111
125
|
/** Номер чата входного пира в том же маркированном виде, что chatId во входящем (peerToChat). */
|
|
112
126
|
function inputPeerChatId(peer: Api.TypeInputPeer): string {
|
|
113
127
|
if (peer instanceof Api.InputPeerChannel) return peerToChat({ channelId: peer.channelId }).chatId;
|
|
@@ -146,6 +160,7 @@ export class TelegramMtprotoTransport
|
|
|
146
160
|
IncomingMessageTransport,
|
|
147
161
|
OutgoingObserverTransport,
|
|
148
162
|
OutgoingReadReceiptTransport,
|
|
163
|
+
PeerInfoTransport,
|
|
149
164
|
PhoneLookupTransport,
|
|
150
165
|
ReactionTransport,
|
|
151
166
|
ReadReceiptTransport,
|
|
@@ -718,6 +733,60 @@ export class TelegramMtprotoTransport
|
|
|
718
733
|
this.lastActivityAt = new Date();
|
|
719
734
|
}
|
|
720
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Контакт ли собеседник и взаимно ли. Пир берётся из хранилища и кэша клиента, иначе по @username;
|
|
738
|
+
* по телефону не ищет — это findByPhone.
|
|
739
|
+
*/
|
|
740
|
+
async peerInfo(recipient: MessageRecipient): Promise<PeerInfo | null> {
|
|
741
|
+
this.assertConnected();
|
|
742
|
+
if (recipient.type !== "user" && recipient.type !== "username") return null;
|
|
743
|
+
const cached = await this.peerStore.get(this.options.accountId, recipient);
|
|
744
|
+
if (cached && cached.type !== "user") return null;
|
|
745
|
+
const peer = cached ? this.toInputPeer(cached) : await this.knownInputPeer(recipient);
|
|
746
|
+
if (peer instanceof Api.InputPeerUser) {
|
|
747
|
+
const [user] = await this.client.invoke(
|
|
748
|
+
new Api.users.GetUsers({
|
|
749
|
+
id: [new Api.InputUser({ userId: peer.userId, accessHash: peer.accessHash })],
|
|
750
|
+
}),
|
|
751
|
+
);
|
|
752
|
+
return user && isUser(user) ? toPeerInfo(user) : null;
|
|
753
|
+
}
|
|
754
|
+
if (peer) return null;
|
|
755
|
+
|
|
756
|
+
const username = recipient.username?.replace(/^@/, "");
|
|
757
|
+
if (!username) return null;
|
|
758
|
+
let result: Api.contacts.ResolvedPeer;
|
|
759
|
+
try {
|
|
760
|
+
result = await this.client.invoke(new Api.contacts.ResolveUsername({ username }));
|
|
761
|
+
} catch (error: unknown) {
|
|
762
|
+
if (USERNAME_NOT_FOUND_ERRORS.has((error as TelegramRpcError).errorMessage ?? ""))
|
|
763
|
+
return null;
|
|
764
|
+
throw error;
|
|
765
|
+
}
|
|
766
|
+
const resolved = result.peer;
|
|
767
|
+
if (!isPeerUser(resolved)) return null;
|
|
768
|
+
const user = result.users.find(
|
|
769
|
+
(candidate): candidate is Api.User =>
|
|
770
|
+
isUser(candidate) && candidate.id.equals(resolved.userId),
|
|
771
|
+
);
|
|
772
|
+
if (!user) return null;
|
|
773
|
+
if (user.accessHash) {
|
|
774
|
+
await this.peerStore.set(
|
|
775
|
+
this.options.accountId,
|
|
776
|
+
recipient,
|
|
777
|
+
{
|
|
778
|
+
type: "user",
|
|
779
|
+
id: user.id.toString(),
|
|
780
|
+
accessHash: user.accessHash.toString(),
|
|
781
|
+
username,
|
|
782
|
+
resolvedAt: new Date().toISOString(),
|
|
783
|
+
},
|
|
784
|
+
this.options.peerTtlSeconds,
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
return toPeerInfo(user);
|
|
788
|
+
}
|
|
789
|
+
|
|
721
790
|
/** Меню команд бота. Сессия обычной учётки получит отказ Telegram: меню бывает только у бота. */
|
|
722
791
|
async setCommands(commands: ChannelCommand[]): Promise<void> {
|
|
723
792
|
this.assertConnected();
|
|
@@ -907,10 +976,13 @@ export class TelegramMtprotoTransport
|
|
|
907
976
|
this.rememberOwn(this.ownSent, peer, file.id);
|
|
908
977
|
}
|
|
909
978
|
this.lastActivityAt = new Date();
|
|
979
|
+
// Получатель есть, только если ответ принёс users: короткий ответ лички (UpdateShortSentMessage) их не несёт.
|
|
980
|
+
const chat = sent.chat;
|
|
910
981
|
return {
|
|
911
982
|
transportId: this.id,
|
|
912
983
|
messageId: String(sent.id),
|
|
913
984
|
recipientId: this.getPeerId(peer),
|
|
985
|
+
...(chat instanceof Api.User ? { mutualContact: Boolean(chat.mutualContact) } : {}),
|
|
914
986
|
};
|
|
915
987
|
} catch (error: unknown) {
|
|
916
988
|
if (retried || !this.isInvalidPeerError(error)) throw error;
|