@imessaging/core 0.1.0 → 0.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imessaging/core",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Core messaging transport contracts and Telegram peer storage",
5
5
  "keywords": [
6
6
  "messaging",
package/src/index.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  export { MemoryPeerStore } from "./memory-peer-store";
2
2
  export { createPeerKey } from "./peer-key";
3
+ export { canEdit, isTelegramRecipient } from "./types";
3
4
  export type {
5
+ EditableMessage,
6
+ EditableMessageTransport,
7
+ EditResult,
8
+ EmailRecipient,
4
9
  MessageRecipient,
5
10
  MessageTransport,
6
11
  OutboundButton,
@@ -9,5 +14,6 @@ export type {
9
14
  ResolvedPeer,
10
15
  SendResult,
11
16
  TelegramPeerStore,
17
+ TelegramRecipient,
12
18
  TransportStatus,
13
19
  } from "./types";
package/src/peer-key.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { MessageRecipient } from "./types";
1
+ import { isTelegramRecipient, type MessageRecipient } from "./types";
2
2
 
3
3
  function normalizeUsername(username: string): string {
4
4
  return username.trim().replace(/^@/, "").toLowerCase();
@@ -10,6 +10,12 @@ export function createPeerKey(accountId: string, recipient: MessageRecipient): s
10
10
  throw new Error("accountId must not be empty");
11
11
  }
12
12
 
13
+ // Кеш пиров — телеграмный. Адрес почты сюда не кладут: ключ вида `telegram-peer:…:email:…`
14
+ // выглядел бы настоящим и молча жил бы в хранилище, ничего не адресуя.
15
+ if (!isTelegramRecipient(recipient)) {
16
+ throw new Error("peer key is Telegram-only and cannot be built for an email recipient");
17
+ }
18
+
13
19
  if (recipient.type === "username") {
14
20
  const username = normalizeUsername(recipient.username);
15
21
  if (!username) {
package/src/types.ts CHANGED
@@ -9,7 +9,36 @@ export type TelegramIdRecipient = {
9
9
  username?: string;
10
10
  };
11
11
 
12
- export type MessageRecipient = UsernameRecipient | TelegramIdRecipient;
12
+ /**
13
+ * Получатель письма.
14
+ *
15
+ * Адрес — самостоятельный вид получателя, а не telegram-имя с собачкой. Соблазн подставить почту
16
+ * в `UsernameRecipient` велик (поле тоже строковое), но тогда `createPeerKey` завёл бы для письма
17
+ * ключ telegram-пира, а mtproto-транспорт попытался бы разрешить адрес как @username и упал бы с
18
+ * сообщением про Telegram. Отдельный вариант заставляет каждый транспорт сказать вслух, работает
19
+ * он с этим получателем или нет.
20
+ */
21
+ export type EmailRecipient = {
22
+ type: "email";
23
+ address: string;
24
+ /** Отображаемое имя: `Имя <адрес>`. Необязательно — без него уходит голый адрес. */
25
+ name?: string;
26
+ };
27
+
28
+ export type MessageRecipient = UsernameRecipient | TelegramIdRecipient | EmailRecipient;
29
+
30
+ /** Получатель, адресуемый в Telegram. Сужение для транспортов, которые кроме него ничего не умеют. */
31
+ export type TelegramRecipient = UsernameRecipient | TelegramIdRecipient;
32
+
33
+ /**
34
+ * Телеграмный ли это получатель.
35
+ *
36
+ * Нужен транспортам как ЕДИНСТВЕННАЯ точка отказа от чужого канала: без него проверка расползётся
37
+ * по местам использования `recipient.id` и в каждом будет своей.
38
+ */
39
+ export function isTelegramRecipient(recipient: MessageRecipient): recipient is TelegramRecipient {
40
+ return recipient.type !== "email";
41
+ }
13
42
 
14
43
  export type OutboundDocument = {
15
44
  data: Uint8Array;
@@ -41,6 +70,29 @@ export type SendResult = {
41
70
  recipientId: string;
42
71
  };
43
72
 
73
+ /**
74
+ * Правка уже отправленного сообщения.
75
+ *
76
+ * Адресуется парой «получатель + идентификатор сообщения»: сам по себе идентификатор в Telegram
77
+ * не уникален, он нумеруется В ПРЕДЕЛАХ чата, и без получателя правка ушла бы не туда.
78
+ *
79
+ * Вложения не правятся: у отправленного сообщения меняется текст и разметка, а документы уже
80
+ * доставлены отдельными сообщениями. Обещать здесь `documents` значило бы обещать невыполнимое.
81
+ */
82
+ export type EditableMessage = {
83
+ recipient: MessageRecipient;
84
+ messageId: string;
85
+ text: string;
86
+ buttons?: OutboundButton[][];
87
+ parseMode?: "HTML" | "Markdown" | "MarkdownV2";
88
+ };
89
+
90
+ export type EditResult = {
91
+ transportId: string;
92
+ messageId: string;
93
+ recipientId: string;
94
+ };
95
+
44
96
  export type TransportStatus = {
45
97
  connected: boolean;
46
98
  transportId: string;
@@ -57,6 +109,21 @@ export interface MessageTransport {
57
109
  getStatus(): Promise<TransportStatus>;
58
110
  }
59
111
 
112
+ /**
113
+ * Транспорт, умеющий править отправленное.
114
+ *
115
+ * Отдельный интерфейс, а не метод в `MessageTransport`: править умеет не всякий канал, и обещать
116
+ * это всем транспортам значит заставить каждый новый бросать «не поддерживается». Потребитель
117
+ * спрашивает возможность типом (`canEdit`), а не ловит исключение.
118
+ */
119
+ export interface EditableMessageTransport extends MessageTransport {
120
+ edit(message: EditableMessage): Promise<EditResult>;
121
+ }
122
+
123
+ export function canEdit(transport: MessageTransport): transport is EditableMessageTransport {
124
+ return typeof (transport as EditableMessageTransport).edit === "function";
125
+ }
126
+
60
127
  export type ResolvedPeer = {
61
128
  type: "user" | "group" | "channel";
62
129
  id: string;