@mtcute/core 0.30.0 → 0.30.2

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.
Files changed (148) hide show
  1. package/client.cjs +8 -0
  2. package/client.js +8 -0
  3. package/highlevel/client.d.cts +63 -1
  4. package/highlevel/client.d.ts +63 -1
  5. package/highlevel/methods/files/download-iterable.cjs +80 -45
  6. package/highlevel/methods/files/download-iterable.js +80 -45
  7. package/highlevel/methods/files/normalize-input-media.cjs +9 -1
  8. package/highlevel/methods/files/normalize-input-media.js +10 -2
  9. package/highlevel/methods/files/upload-file.cjs +6 -1
  10. package/highlevel/methods/files/upload-file.js +6 -1
  11. package/highlevel/methods/messages/create-rich-streaming-draft.cjs +39 -0
  12. package/highlevel/methods/messages/create-rich-streaming-draft.d.cts +42 -0
  13. package/highlevel/methods/messages/create-rich-streaming-draft.d.ts +42 -0
  14. package/highlevel/methods/messages/create-rich-streaming-draft.js +34 -0
  15. package/highlevel/methods/messages/normalize-rich-message.cjs +168 -0
  16. package/highlevel/methods/messages/normalize-rich-message.d.cts +10 -0
  17. package/highlevel/methods/messages/normalize-rich-message.d.ts +10 -0
  18. package/highlevel/methods/messages/normalize-rich-message.js +163 -0
  19. package/highlevel/methods/messages/send-common.cjs +3 -2
  20. package/highlevel/methods/messages/send-common.d.cts +4 -1
  21. package/highlevel/methods/messages/send-common.d.ts +4 -1
  22. package/highlevel/methods/messages/send-common.js +3 -2
  23. package/highlevel/methods/messages/send-rich-message.cjs +49 -0
  24. package/highlevel/methods/messages/send-rich-message.d.cts +38 -0
  25. package/highlevel/methods/messages/send-rich-message.d.ts +38 -0
  26. package/highlevel/methods/messages/send-rich-message.js +44 -0
  27. package/highlevel/methods.d.cts +3 -0
  28. package/highlevel/methods.d.ts +3 -0
  29. package/highlevel/types/bots/inline-message/factories.cjs +14 -0
  30. package/highlevel/types/bots/inline-message/factories.d.cts +9 -1
  31. package/highlevel/types/bots/inline-message/factories.d.ts +9 -1
  32. package/highlevel/types/bots/inline-message/factories.js +14 -0
  33. package/highlevel/types/bots/inline-message/types.d.cts +17 -1
  34. package/highlevel/types/bots/inline-message/types.d.ts +17 -1
  35. package/highlevel/types/files/utils.d.cts +9 -0
  36. package/highlevel/types/files/utils.d.ts +9 -0
  37. package/highlevel/types/media/input-media/types.d.cts +1 -1
  38. package/highlevel/types/media/input-media/types.d.ts +1 -1
  39. package/highlevel/types/messages/index.d.cts +1 -1
  40. package/highlevel/types/messages/index.d.ts +1 -1
  41. package/highlevel/types/messages/message.cjs +1 -1
  42. package/highlevel/types/messages/message.d.cts +1 -1
  43. package/highlevel/types/messages/message.d.ts +1 -1
  44. package/highlevel/types/messages/message.js +1 -1
  45. package/highlevel/types/messages/rich/index.d.cts +4 -0
  46. package/highlevel/types/messages/rich/index.d.ts +4 -0
  47. package/highlevel/types/messages/rich/inner.cjs +219 -0
  48. package/highlevel/types/messages/rich/inner.d.cts +127 -0
  49. package/highlevel/types/messages/rich/inner.d.ts +127 -0
  50. package/highlevel/types/messages/rich/inner.js +214 -0
  51. package/highlevel/types/messages/{rich-message.cjs → rich/rich-message.cjs} +6 -6
  52. package/highlevel/types/messages/{rich-message.d.cts → rich/rich-message.d.cts} +5 -5
  53. package/highlevel/types/messages/{rich-message.d.ts → rich/rich-message.d.ts} +5 -5
  54. package/highlevel/types/messages/{rich-message.js → rich/rich-message.js} +6 -6
  55. package/highlevel/types/messages/rich/types.d.cts +76 -0
  56. package/highlevel/types/messages/rich/types.d.ts +76 -0
  57. package/highlevel/updates/manager.cjs +10 -169
  58. package/highlevel/updates/manager.js +10 -169
  59. package/highlevel/utils/entities.cjs +176 -0
  60. package/highlevel/utils/entities.d.cts +18 -0
  61. package/highlevel/utils/entities.d.ts +18 -0
  62. package/highlevel/utils/entities.js +177 -1
  63. package/highlevel/utils/index.d.cts +1 -0
  64. package/highlevel/utils/index.d.ts +1 -0
  65. package/highlevel/utils/walk-page-blocks.cjs +164 -0
  66. package/highlevel/utils/walk-page-blocks.d.cts +9 -0
  67. package/highlevel/utils/walk-page-blocks.d.ts +9 -0
  68. package/highlevel/utils/walk-page-blocks.js +159 -0
  69. package/index.cjs +3 -1
  70. package/index.js +3 -1
  71. package/methods.cjs +4 -0
  72. package/methods.js +4 -0
  73. package/network/authorization.cjs +1 -1
  74. package/network/authorization.js +1 -1
  75. package/network/delay-gate.cjs +29 -0
  76. package/network/delay-gate.d.cts +5 -0
  77. package/network/delay-gate.d.ts +5 -0
  78. package/network/delay-gate.js +24 -0
  79. package/network/delay-gate.test.d.cts +1 -0
  80. package/network/delay-gate.test.d.ts +1 -0
  81. package/network/message-id-store.cjs +30 -0
  82. package/network/message-id-store.d.cts +7 -0
  83. package/network/message-id-store.d.ts +7 -0
  84. package/network/message-id-store.js +25 -0
  85. package/network/message-id-store.test.d.cts +1 -0
  86. package/network/message-id-store.test.d.ts +1 -0
  87. package/network/middlewares/bundle.cjs +2 -0
  88. package/network/middlewares/bundle.d.cts +1 -0
  89. package/network/middlewares/bundle.d.ts +1 -0
  90. package/network/middlewares/bundle.js +2 -0
  91. package/network/middlewares/default.cjs +6 -1
  92. package/network/middlewares/default.js +6 -1
  93. package/network/middlewares/flood-waiter.test.d.cts +1 -0
  94. package/network/middlewares/flood-waiter.test.d.ts +1 -0
  95. package/network/middlewares/internal-errors.test.d.cts +1 -0
  96. package/network/middlewares/internal-errors.test.d.ts +1 -0
  97. package/network/middlewares/media-throttle.cjs +65 -0
  98. package/network/middlewares/media-throttle.d.cts +5 -0
  99. package/network/middlewares/media-throttle.d.ts +5 -0
  100. package/network/middlewares/media-throttle.js +60 -0
  101. package/network/middlewares/media-throttle.test.d.cts +1 -0
  102. package/network/middlewares/media-throttle.test.d.ts +1 -0
  103. package/network/middlewares/on-error.test.d.cts +1 -0
  104. package/network/middlewares/on-error.test.d.ts +1 -0
  105. package/network/middlewares/on-method.test.d.cts +1 -0
  106. package/network/middlewares/on-method.test.d.ts +1 -0
  107. package/network/mtproto-session.cjs +9 -2
  108. package/network/mtproto-session.d.cts +5 -2
  109. package/network/mtproto-session.d.ts +5 -2
  110. package/network/mtproto-session.js +9 -2
  111. package/network/mtproxy/_fake-tls.cjs +169 -107
  112. package/network/mtproxy/_fake-tls.js +169 -107
  113. package/network/mtproxy/_fake-tls.test.d.cts +1 -0
  114. package/network/mtproxy/_fake-tls.test.d.ts +1 -0
  115. package/network/multi-session-connection.cjs +7 -0
  116. package/network/multi-session-connection.d.cts +2 -0
  117. package/network/multi-session-connection.d.ts +2 -0
  118. package/network/multi-session-connection.js +7 -0
  119. package/network/network-manager.cjs +7 -1
  120. package/network/network-manager.js +7 -1
  121. package/network/resource-limiter.cjs +60 -0
  122. package/network/resource-limiter.d.cts +11 -0
  123. package/network/resource-limiter.d.ts +11 -0
  124. package/network/resource-limiter.js +55 -0
  125. package/network/resource-limiter.test.d.cts +1 -0
  126. package/network/resource-limiter.test.d.ts +1 -0
  127. package/network/server-salt.cjs +7 -3
  128. package/network/server-salt.d.cts +2 -0
  129. package/network/server-salt.d.ts +2 -0
  130. package/network/server-salt.js +7 -3
  131. package/network/session-connection.cjs +106 -20
  132. package/network/session-connection.d.cts +11 -0
  133. package/network/session-connection.d.ts +11 -0
  134. package/network/session-connection.js +106 -20
  135. package/package.json +1 -1
  136. package/tl/api-schema.json +1 -1
  137. package/tl/binary/reader.cjs +115 -109
  138. package/tl/binary/reader.js +115 -109
  139. package/tl/binary/writer.cjs +243 -245
  140. package/tl/binary/writer.js +243 -245
  141. package/tl/index.d.cts +227 -216
  142. package/tl/index.d.ts +227 -216
  143. package/tl/inner-tl.cjs +3 -1
  144. package/tl/inner-tl.js +3 -1
  145. package/types/utils.d.cts +4 -0
  146. package/types/utils.d.ts +4 -0
  147. package/utils.cjs +4 -0
  148. package/utils.js +5 -1
@@ -10,7 +10,7 @@ import { _normalizeInputText } from "../misc/normalize-text.js";
10
10
  import { resolvePeer } from "../users/resolve-peer.js";
11
11
  import { _normalizeInputFile } from "./normalize-input-file.js";
12
12
  import { uploadFile } from "./upload-file.js";
13
- import { isAnyInputMedia, isAnyPollAnswer, isAnyInputFile } from "../../../tl/inner-tl.js";
13
+ import { isAnyInputMedia, isAnyInputPhoto, isAnyInputDocument, isAnyPollAnswer, isAnyInputFile } from "../../../tl/inner-tl.js";
14
14
  async function _normalizeInputMedia(client, media, params = {}, uploadMedia = false) {
15
15
  if (isAnyInputMedia(media)) {
16
16
  switch (media._) {
@@ -22,6 +22,14 @@ async function _normalizeInputMedia(client, media, params = {}, uploadMedia = fa
22
22
  }
23
23
  return media;
24
24
  }
25
+ if (isAnyInputPhoto(media)) {
26
+ assertTypeIs("normalizeInputMedia (@ photo)", media, "inputPhoto");
27
+ return { _: "inputMediaPhoto", id: media };
28
+ }
29
+ if (isAnyInputDocument(media)) {
30
+ assertTypeIs("normalizeInputMedia (@ document)", media, "inputDocument");
31
+ return { _: "inputMediaDocument", id: media };
32
+ }
25
33
  if (media.type === "venue") {
26
34
  return {
27
35
  _: "inputMediaVenue",
@@ -268,7 +276,7 @@ async function _normalizeInputMedia(client, media, params = {}, uploadMedia = fa
268
276
  fileReference: res.photo.fileReference
269
277
  },
270
278
  ttlSeconds: media.ttlSeconds,
271
- spoiler: media.type === "video" && media.spoiler,
279
+ spoiler: "spoiler" in media ? media.spoiler : false,
272
280
  livePhoto: Boolean(livePhotoVideo),
273
281
  video: livePhotoVideo
274
282
  };
@@ -174,7 +174,12 @@ async function uploadFile(client, params) {
174
174
  filePart: thisIdx,
175
175
  bytes: part
176
176
  };
177
- const result = await client.call(request, { kind: connectionKind, abortSignal });
177
+ const result = await client.call(request, {
178
+ kind: connectionKind,
179
+ maxRetryCount: Infinity,
180
+ floodSleepThreshold: Infinity,
181
+ abortSignal
182
+ });
178
183
  if (!result) throw new Error(`Failed to upload part ${idx}`);
179
184
  if (abortSignal?.aborted) return;
180
185
  pos += part.length;
@@ -167,7 +167,12 @@ async function uploadFile(client, params) {
167
167
  filePart: thisIdx,
168
168
  bytes: part
169
169
  };
170
- const result = await client.call(request, { kind: connectionKind, abortSignal });
170
+ const result = await client.call(request, {
171
+ kind: connectionKind,
172
+ maxRetryCount: Infinity,
173
+ floodSleepThreshold: Infinity,
174
+ abortSignal
175
+ });
171
176
  if (!result) throw new Error(`Failed to upload part ${idx}`);
172
177
  if (abortSignal?.aborted) return;
173
178
  pos += part.length;
@@ -0,0 +1,39 @@
1
+ if (typeof globalThis !== "undefined" && !globalThis._MTCUTE_CJS_DEPRECATION_WARNED) {
2
+ globalThis._MTCUTE_CJS_DEPRECATION_WARNED = true;
3
+ console.warn("[@mtcute/core] CommonJS bundles are deprecated. They will be removed completely in one of the upcoming releases. No support is provided for CommonJS users. Please consider switching to ESM, it's " + (/* @__PURE__ */ new Date()).getFullYear() + " already.");
4
+ console.warn("[@mtcute/core] Learn more about switching to ESM: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c");
5
+ }
6
+ "use strict";
7
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
8
+ const longUtils = require("../../../utils/long-utils.cjs");
9
+ const typeAssertions = require("../../../utils/type-assertions.cjs");
10
+ const resolvePeer = require("../users/resolve-peer.cjs");
11
+ const normalizeRichMessage = require("./normalize-rich-message.cjs");
12
+ async function createRichStreamingDraft(client, chatId, params) {
13
+ const { threadId, businessConnectionId, progressCallback } = params ?? {};
14
+ const randomId = longUtils.randomLong();
15
+ const peer = await resolvePeer.resolvePeer(client, chatId);
16
+ const uploadCache = params?.disableUploadCache ? void 0 : /* @__PURE__ */ new Map();
17
+ const sendDraft = async (content) => {
18
+ const richMessage = await normalizeRichMessage._normalizeInputRichMessage(client, peer, content, { progressCallback, uploadCache });
19
+ const r = await client.call({
20
+ _: "messages.setTyping",
21
+ peer,
22
+ action: {
23
+ _: "inputSendMessageRichMessageDraftAction",
24
+ randomId,
25
+ richMessage
26
+ },
27
+ topMsgId: threadId
28
+ }, {
29
+ businessConnectionId
30
+ });
31
+ typeAssertions.assertTrue("messages.setTyping", r);
32
+ };
33
+ return {
34
+ randomId,
35
+ send: sendDraft,
36
+ uploadCache
37
+ };
38
+ }
39
+ exports.createRichStreamingDraft = createRichStreamingDraft;
@@ -0,0 +1,42 @@
1
+ import { default as Long } from 'long';
2
+ import { ITelegramClient } from '../../client.types.js';
3
+ import { InputPeerLike } from '../../types/index.js';
4
+ import { InputRichMessage, RichMediaUploadCache } from '../../types/messages/rich/types.js';
5
+ export interface RichStreamingDraft {
6
+ /** Random ID of the draft */
7
+ readonly randomId: Long;
8
+ /** Replace the draft with the given rich message */
9
+ send(content: InputRichMessage): Promise<void>;
10
+ /**
11
+ * Cache of media uploaded while streaming the draft (unless disabled).
12
+ *
13
+ * Pass it to the final {@link sendRichMessage} call to avoid re-uploading the media.
14
+ */
15
+ readonly uploadCache?: RichMediaUploadCache;
16
+ }
17
+ /**
18
+ * Create a streaming rich message draft
19
+ *
20
+ * @param chatId Chat ID
21
+ * @param params
22
+ */
23
+ export declare function createRichStreamingDraft(client: ITelegramClient, chatId: InputPeerLike, params?: {
24
+ /**
25
+ * Unique identifier of the business connection on behalf of which the action will be sent
26
+ */
27
+ businessConnectionId?: string;
28
+ /** Whether to disable upload cache (exists to prevent uploading the same media multiple times) */
29
+ disableUploadCache?: boolean;
30
+ /**
31
+ * For comment threads, ID of the thread (i.e. top message)
32
+ */
33
+ threadId?: number;
34
+ /**
35
+ * Function that will be called after some part of the media has been uploaded.
36
+ *
37
+ * @param id ID of the file being uploaded
38
+ * @param uploaded Number of bytes already uploaded
39
+ * @param total Total file size
40
+ */
41
+ progressCallback?: (id: string, uploaded: number, total: number) => void;
42
+ }): Promise<RichStreamingDraft>;
@@ -0,0 +1,42 @@
1
+ import { default as Long } from 'long';
2
+ import { ITelegramClient } from '../../client.types.js';
3
+ import { InputPeerLike } from '../../types/index.js';
4
+ import { InputRichMessage, RichMediaUploadCache } from '../../types/messages/rich/types.js';
5
+ export interface RichStreamingDraft {
6
+ /** Random ID of the draft */
7
+ readonly randomId: Long;
8
+ /** Replace the draft with the given rich message */
9
+ send(content: InputRichMessage): Promise<void>;
10
+ /**
11
+ * Cache of media uploaded while streaming the draft (unless disabled).
12
+ *
13
+ * Pass it to the final {@link sendRichMessage} call to avoid re-uploading the media.
14
+ */
15
+ readonly uploadCache?: RichMediaUploadCache;
16
+ }
17
+ /**
18
+ * Create a streaming rich message draft
19
+ *
20
+ * @param chatId Chat ID
21
+ * @param params
22
+ */
23
+ export declare function createRichStreamingDraft(client: ITelegramClient, chatId: InputPeerLike, params?: {
24
+ /**
25
+ * Unique identifier of the business connection on behalf of which the action will be sent
26
+ */
27
+ businessConnectionId?: string;
28
+ /** Whether to disable upload cache (exists to prevent uploading the same media multiple times) */
29
+ disableUploadCache?: boolean;
30
+ /**
31
+ * For comment threads, ID of the thread (i.e. top message)
32
+ */
33
+ threadId?: number;
34
+ /**
35
+ * Function that will be called after some part of the media has been uploaded.
36
+ *
37
+ * @param id ID of the file being uploaded
38
+ * @param uploaded Number of bytes already uploaded
39
+ * @param total Total file size
40
+ */
41
+ progressCallback?: (id: string, uploaded: number, total: number) => void;
42
+ }): Promise<RichStreamingDraft>;
@@ -0,0 +1,34 @@
1
+ import { randomLong } from "../../../utils/long-utils.js";
2
+ import { assertTrue } from "../../../utils/type-assertions.js";
3
+ import { resolvePeer } from "../users/resolve-peer.js";
4
+ import { _normalizeInputRichMessage } from "./normalize-rich-message.js";
5
+ async function createRichStreamingDraft(client, chatId, params) {
6
+ const { threadId, businessConnectionId, progressCallback } = params ?? {};
7
+ const randomId = randomLong();
8
+ const peer = await resolvePeer(client, chatId);
9
+ const uploadCache = params?.disableUploadCache ? void 0 : /* @__PURE__ */ new Map();
10
+ const sendDraft = async (content) => {
11
+ const richMessage = await _normalizeInputRichMessage(client, peer, content, { progressCallback, uploadCache });
12
+ const r = await client.call({
13
+ _: "messages.setTyping",
14
+ peer,
15
+ action: {
16
+ _: "inputSendMessageRichMessageDraftAction",
17
+ randomId,
18
+ richMessage
19
+ },
20
+ topMsgId: threadId
21
+ }, {
22
+ businessConnectionId
23
+ });
24
+ assertTrue("messages.setTyping", r);
25
+ };
26
+ return {
27
+ randomId,
28
+ send: sendDraft,
29
+ uploadCache
30
+ };
31
+ }
32
+ export {
33
+ createRichStreamingDraft
34
+ };
@@ -0,0 +1,168 @@
1
+ if (typeof globalThis !== "undefined" && !globalThis._MTCUTE_CJS_DEPRECATION_WARNED) {
2
+ globalThis._MTCUTE_CJS_DEPRECATION_WARNED = true;
3
+ console.warn("[@mtcute/core] CommonJS bundles are deprecated. They will be removed completely in one of the upcoming releases. No support is provided for CommonJS users. Please consider switching to ESM, it's " + (/* @__PURE__ */ new Date()).getFullYear() + " already.");
4
+ console.warn("[@mtcute/core] Learn more about switching to ESM: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c");
5
+ }
6
+ "use strict";
7
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
8
+ const utils = require("@fuman/utils");
9
+ const errors = require("../../../types/errors.cjs");
10
+ const typeAssertions = require("../../../utils/type-assertions.cjs");
11
+ const peerUtils = require("../../utils/peer-utils.cjs");
12
+ const walkPageBlocks = require("../../utils/walk-page-blocks.cjs");
13
+ const normalizeInputMedia = require("../files/normalize-input-media.cjs");
14
+ const resolvePeerMany = require("../users/resolve-peer-many.cjs");
15
+ function _richMediaCacheKey(media) {
16
+ if (typeof media === "string") return `auto:${media}`;
17
+ if ("_" in media) return void 0;
18
+ if (typeof media.file === "string") return `${media.type}:${media.file}`;
19
+ return media;
20
+ }
21
+ async function _uploadRichMedia(client, peer, id, media, params) {
22
+ const cacheKey = params.uploadCache != null ? _richMediaCacheKey(media) : void 0;
23
+ if (cacheKey !== void 0) {
24
+ const cached = params.uploadCache.get(cacheKey);
25
+ if (cached != null) return cached;
26
+ }
27
+ const input = typeof media === "string" ? { type: "auto", file: media } : media;
28
+ const normalized = await normalizeInputMedia._normalizeInputMedia(client, input, {
29
+ uploadPeer: peer,
30
+ abortSignal: params.abortSignal,
31
+ progressCallback: params.progressCallback?.bind(null, id)
32
+ }, true);
33
+ if (normalized._ !== "inputMediaPhoto" && normalized._ !== "inputMediaDocument") {
34
+ throw new errors.MtTypeAssertionError("sendRichMessage (upload)", "inputMediaDocument | inputMediaPhoto", normalized._);
35
+ }
36
+ if (cacheKey !== void 0) params.uploadCache.set(cacheKey, normalized);
37
+ return normalized;
38
+ }
39
+ async function _normalizeInputRichMessage(client, peer, input, params) {
40
+ if ("_" in input) return input;
41
+ if (input.type !== "blocks") {
42
+ let richFiles;
43
+ if (input.attachments) {
44
+ const medias = [...Object.entries(input.attachments)];
45
+ const normalized = await utils.parallelMap(
46
+ medias,
47
+ async ([id, media]) => [id, await _uploadRichMedia(client, peer, id, media, params)],
48
+ { limit: 4, signal: params.abortSignal }
49
+ );
50
+ richFiles = normalized.map(
51
+ ([id, media]) => media._ === "inputMediaDocument" ? { _: "inputRichFileDocument", id, document: media.id } : { _: "inputRichFilePhoto", id, photo: media.id }
52
+ );
53
+ }
54
+ return input.type === "markdown" ? {
55
+ _: "inputRichMessageMarkdown",
56
+ rtl: input.rtl,
57
+ noautolink: input.skipEntityDetection,
58
+ files: richFiles,
59
+ markdown: input.content
60
+ } : {
61
+ _: "inputRichMessageHTML",
62
+ rtl: input.rtl,
63
+ noautolink: input.skipEntityDetection,
64
+ files: richFiles,
65
+ html: input.content
66
+ };
67
+ }
68
+ const photos = [];
69
+ const documents = [];
70
+ const fileBlocks = [];
71
+ function processBlock(node) {
72
+ if ("file" in node) {
73
+ const block = processBlock(node.block);
74
+ fileBlocks.push({ block, media: node.file });
75
+ return block;
76
+ }
77
+ switch (node._) {
78
+ case "pageBlockCover":
79
+ return { ...node, cover: processBlock(node.cover) };
80
+ case "pageBlockCollage":
81
+ case "pageBlockSlideshow":
82
+ return { ...node, items: node.items.map(processBlock) };
83
+ case "pageBlockDetails":
84
+ case "pageBlockEmbedPost":
85
+ case "pageBlockBlockquoteBlocks":
86
+ return { ...node, blocks: node.blocks.map(processBlock) };
87
+ case "pageBlockList":
88
+ return {
89
+ ...node,
90
+ items: node.items.map(
91
+ (item) => item._ === "pageListItemBlocks" ? {
92
+ ...item,
93
+ blocks: item.blocks.map(processBlock)
94
+ } : item
95
+ )
96
+ };
97
+ case "pageBlockOrderedList":
98
+ return {
99
+ ...node,
100
+ items: node.items.map(
101
+ (item) => item._ === "pageListOrderedItemBlocks" ? { ...item, blocks: item.blocks.map(processBlock) } : item
102
+ )
103
+ };
104
+ default:
105
+ return node;
106
+ }
107
+ }
108
+ const blocks = input.blocks.map(processBlock);
109
+ const mentionIds = /* @__PURE__ */ new Set();
110
+ walkPageBlocks.walkPageBlocks(blocks, {
111
+ richText: (text) => {
112
+ if (text._ === "textMentionName") mentionIds.add(text.userId);
113
+ }
114
+ });
115
+ const users = mentionIds.size ? await resolvePeerMany.resolvePeerMany(client, [...mentionIds], peerUtils.toInputUser) : void 0;
116
+ const uploaded = await utils.parallelMap(
117
+ fileBlocks,
118
+ async ({ block, media }, idx) => {
119
+ const uploaded2 = await _uploadRichMedia(client, peer, `block-${idx}`, media, params);
120
+ return { block, media: uploaded2 };
121
+ },
122
+ { limit: 4, signal: params.abortSignal }
123
+ );
124
+ for (const { block, media } of uploaded) {
125
+ if (media._ === "inputMediaPhoto") {
126
+ const photo = media.id;
127
+ typeAssertions.assertTypeIs("sendRichMessage (upload)", photo, "inputPhoto");
128
+ photos.push(photo);
129
+ switch (block._) {
130
+ case "pageBlockPhoto":
131
+ block.photoId = photo.id;
132
+ break;
133
+ case "pageBlockEmbed":
134
+ block.posterPhotoId = photo.id;
135
+ break;
136
+ case "pageBlockEmbedPost":
137
+ block.authorPhotoId = photo.id;
138
+ break;
139
+ default:
140
+ throw new errors.MtArgumentError(`sendRichMessage: expected photo ${photo.id} to be used in a photo block, but got ${block._}`);
141
+ }
142
+ } else {
143
+ const document = media.id;
144
+ typeAssertions.assertTypeIs("sendRichMessage (upload)", document, "inputDocument");
145
+ documents.push(document);
146
+ switch (block._) {
147
+ case "pageBlockVideo":
148
+ block.videoId = document.id;
149
+ break;
150
+ case "pageBlockAudio":
151
+ block.audioId = document.id;
152
+ break;
153
+ default:
154
+ throw new errors.MtArgumentError(`sendRichMessage: expected document ${document.id} to be used in a document block, but got ${block._}`);
155
+ }
156
+ }
157
+ }
158
+ return {
159
+ _: "inputRichMessage",
160
+ rtl: input.rtl,
161
+ noautolink: input.skipEntityDetection,
162
+ blocks,
163
+ photos: photos.length ? photos : void 0,
164
+ documents: documents.length ? documents : void 0,
165
+ users: users?.length ? users : void 0
166
+ };
167
+ }
168
+ exports._normalizeInputRichMessage = _normalizeInputRichMessage;
@@ -0,0 +1,10 @@
1
+ import { tl } from '../../../tl/index.js';
2
+ import { ITelegramClient } from '../../client.types.js';
3
+ import { InputRichMessage, RichMediaUploadCache } from '../../types/messages/rich/types.js';
4
+ interface NormalizeRichMessageParams {
5
+ abortSignal?: AbortSignal;
6
+ progressCallback?: (id: string, uploaded: number, total: number) => void;
7
+ uploadCache?: RichMediaUploadCache;
8
+ }
9
+ export declare function _normalizeInputRichMessage(client: ITelegramClient, peer: tl.TypeInputPeer, input: InputRichMessage, params: NormalizeRichMessageParams): Promise<tl.TypeInputRichMessage>;
10
+ export {};
@@ -0,0 +1,10 @@
1
+ import { tl } from '../../../tl/index.js';
2
+ import { ITelegramClient } from '../../client.types.js';
3
+ import { InputRichMessage, RichMediaUploadCache } from '../../types/messages/rich/types.js';
4
+ interface NormalizeRichMessageParams {
5
+ abortSignal?: AbortSignal;
6
+ progressCallback?: (id: string, uploaded: number, total: number) => void;
7
+ uploadCache?: RichMediaUploadCache;
8
+ }
9
+ export declare function _normalizeInputRichMessage(client: ITelegramClient, peer: tl.TypeInputPeer, input: InputRichMessage, params: NormalizeRichMessageParams): Promise<tl.TypeInputRichMessage>;
10
+ export {};
@@ -0,0 +1,163 @@
1
+ import { parallelMap } from "@fuman/utils";
2
+ import { MtArgumentError, MtTypeAssertionError } from "../../../types/errors.js";
3
+ import { assertTypeIs } from "../../../utils/type-assertions.js";
4
+ import { toInputUser } from "../../utils/peer-utils.js";
5
+ import { walkPageBlocks } from "../../utils/walk-page-blocks.js";
6
+ import { _normalizeInputMedia } from "../files/normalize-input-media.js";
7
+ import { resolvePeerMany } from "../users/resolve-peer-many.js";
8
+ function _richMediaCacheKey(media) {
9
+ if (typeof media === "string") return `auto:${media}`;
10
+ if ("_" in media) return void 0;
11
+ if (typeof media.file === "string") return `${media.type}:${media.file}`;
12
+ return media;
13
+ }
14
+ async function _uploadRichMedia(client, peer, id, media, params) {
15
+ const cacheKey = params.uploadCache != null ? _richMediaCacheKey(media) : void 0;
16
+ if (cacheKey !== void 0) {
17
+ const cached = params.uploadCache.get(cacheKey);
18
+ if (cached != null) return cached;
19
+ }
20
+ const input = typeof media === "string" ? { type: "auto", file: media } : media;
21
+ const normalized = await _normalizeInputMedia(client, input, {
22
+ uploadPeer: peer,
23
+ abortSignal: params.abortSignal,
24
+ progressCallback: params.progressCallback?.bind(null, id)
25
+ }, true);
26
+ if (normalized._ !== "inputMediaPhoto" && normalized._ !== "inputMediaDocument") {
27
+ throw new MtTypeAssertionError("sendRichMessage (upload)", "inputMediaDocument | inputMediaPhoto", normalized._);
28
+ }
29
+ if (cacheKey !== void 0) params.uploadCache.set(cacheKey, normalized);
30
+ return normalized;
31
+ }
32
+ async function _normalizeInputRichMessage(client, peer, input, params) {
33
+ if ("_" in input) return input;
34
+ if (input.type !== "blocks") {
35
+ let richFiles;
36
+ if (input.attachments) {
37
+ const medias = [...Object.entries(input.attachments)];
38
+ const normalized = await parallelMap(
39
+ medias,
40
+ async ([id, media]) => [id, await _uploadRichMedia(client, peer, id, media, params)],
41
+ { limit: 4, signal: params.abortSignal }
42
+ );
43
+ richFiles = normalized.map(
44
+ ([id, media]) => media._ === "inputMediaDocument" ? { _: "inputRichFileDocument", id, document: media.id } : { _: "inputRichFilePhoto", id, photo: media.id }
45
+ );
46
+ }
47
+ return input.type === "markdown" ? {
48
+ _: "inputRichMessageMarkdown",
49
+ rtl: input.rtl,
50
+ noautolink: input.skipEntityDetection,
51
+ files: richFiles,
52
+ markdown: input.content
53
+ } : {
54
+ _: "inputRichMessageHTML",
55
+ rtl: input.rtl,
56
+ noautolink: input.skipEntityDetection,
57
+ files: richFiles,
58
+ html: input.content
59
+ };
60
+ }
61
+ const photos = [];
62
+ const documents = [];
63
+ const fileBlocks = [];
64
+ function processBlock(node) {
65
+ if ("file" in node) {
66
+ const block = processBlock(node.block);
67
+ fileBlocks.push({ block, media: node.file });
68
+ return block;
69
+ }
70
+ switch (node._) {
71
+ case "pageBlockCover":
72
+ return { ...node, cover: processBlock(node.cover) };
73
+ case "pageBlockCollage":
74
+ case "pageBlockSlideshow":
75
+ return { ...node, items: node.items.map(processBlock) };
76
+ case "pageBlockDetails":
77
+ case "pageBlockEmbedPost":
78
+ case "pageBlockBlockquoteBlocks":
79
+ return { ...node, blocks: node.blocks.map(processBlock) };
80
+ case "pageBlockList":
81
+ return {
82
+ ...node,
83
+ items: node.items.map(
84
+ (item) => item._ === "pageListItemBlocks" ? {
85
+ ...item,
86
+ blocks: item.blocks.map(processBlock)
87
+ } : item
88
+ )
89
+ };
90
+ case "pageBlockOrderedList":
91
+ return {
92
+ ...node,
93
+ items: node.items.map(
94
+ (item) => item._ === "pageListOrderedItemBlocks" ? { ...item, blocks: item.blocks.map(processBlock) } : item
95
+ )
96
+ };
97
+ default:
98
+ return node;
99
+ }
100
+ }
101
+ const blocks = input.blocks.map(processBlock);
102
+ const mentionIds = /* @__PURE__ */ new Set();
103
+ walkPageBlocks(blocks, {
104
+ richText: (text) => {
105
+ if (text._ === "textMentionName") mentionIds.add(text.userId);
106
+ }
107
+ });
108
+ const users = mentionIds.size ? await resolvePeerMany(client, [...mentionIds], toInputUser) : void 0;
109
+ const uploaded = await parallelMap(
110
+ fileBlocks,
111
+ async ({ block, media }, idx) => {
112
+ const uploaded2 = await _uploadRichMedia(client, peer, `block-${idx}`, media, params);
113
+ return { block, media: uploaded2 };
114
+ },
115
+ { limit: 4, signal: params.abortSignal }
116
+ );
117
+ for (const { block, media } of uploaded) {
118
+ if (media._ === "inputMediaPhoto") {
119
+ const photo = media.id;
120
+ assertTypeIs("sendRichMessage (upload)", photo, "inputPhoto");
121
+ photos.push(photo);
122
+ switch (block._) {
123
+ case "pageBlockPhoto":
124
+ block.photoId = photo.id;
125
+ break;
126
+ case "pageBlockEmbed":
127
+ block.posterPhotoId = photo.id;
128
+ break;
129
+ case "pageBlockEmbedPost":
130
+ block.authorPhotoId = photo.id;
131
+ break;
132
+ default:
133
+ throw new MtArgumentError(`sendRichMessage: expected photo ${photo.id} to be used in a photo block, but got ${block._}`);
134
+ }
135
+ } else {
136
+ const document = media.id;
137
+ assertTypeIs("sendRichMessage (upload)", document, "inputDocument");
138
+ documents.push(document);
139
+ switch (block._) {
140
+ case "pageBlockVideo":
141
+ block.videoId = document.id;
142
+ break;
143
+ case "pageBlockAudio":
144
+ block.audioId = document.id;
145
+ break;
146
+ default:
147
+ throw new MtArgumentError(`sendRichMessage: expected document ${document.id} to be used in a document block, but got ${block._}`);
148
+ }
149
+ }
150
+ }
151
+ return {
152
+ _: "inputRichMessage",
153
+ rtl: input.rtl,
154
+ noautolink: input.skipEntityDetection,
155
+ blocks,
156
+ photos: photos.length ? photos : void 0,
157
+ documents: documents.length ? documents : void 0,
158
+ users: users?.length ? users : void 0
159
+ };
160
+ }
161
+ export {
162
+ _normalizeInputRichMessage
163
+ };
@@ -48,11 +48,12 @@ async function _processCommonSendParameters(client, chatId, params) {
48
48
  throw new errors.MtArgumentError("replyTo/commentTo and replyToStory cannot be used together");
49
49
  }
50
50
  let tlReplyTo;
51
- if (replyTo) {
51
+ if (replyTo || params.threadId) {
52
52
  tlReplyTo = {
53
53
  _: "inputReplyToMessage",
54
- replyToMsgId: replyTo,
54
+ replyToMsgId: replyTo ?? params.threadId,
55
55
  replyToPeerId: replyToPeer,
56
+ topMsgId: params.threadId,
56
57
  quoteText: params.quote?.text,
57
58
  quoteEntities: params.quote?.entities,
58
59
  quoteOffset: params.quoteOffset,
@@ -7,11 +7,14 @@ export interface CommonSendParams {
7
7
  /**
8
8
  * Message to reply to. Either a message object or message ID.
9
9
  *
10
- * For forums - can also be an ID of the topic (i.e. its top message ID)
10
+ * For forums - can also be an ID of the topic (i.e. its top message ID),
11
+ * but prefer using {@link threadId} for that instead.
11
12
  *
12
13
  * Can also be a message from another chat, in which case a quote will be sent.
13
14
  */
14
15
  replyTo?: number | Message;
16
+ /** ID of the thread to reply to */
17
+ threadId?: number;
15
18
  /**
16
19
  * If this is a reply to a specific todo item, ID of that item
17
20
  */
@@ -7,11 +7,14 @@ export interface CommonSendParams {
7
7
  /**
8
8
  * Message to reply to. Either a message object or message ID.
9
9
  *
10
- * For forums - can also be an ID of the topic (i.e. its top message ID)
10
+ * For forums - can also be an ID of the topic (i.e. its top message ID),
11
+ * but prefer using {@link threadId} for that instead.
11
12
  *
12
13
  * Can also be a message from another chat, in which case a quote will be sent.
13
14
  */
14
15
  replyTo?: number | Message;
16
+ /** ID of the thread to reply to */
17
+ threadId?: number;
15
18
  /**
16
19
  * If this is a reply to a specific todo item, ID of that item
17
20
  */
@@ -41,11 +41,12 @@ async function _processCommonSendParameters(client, chatId, params) {
41
41
  throw new MtArgumentError("replyTo/commentTo and replyToStory cannot be used together");
42
42
  }
43
43
  let tlReplyTo;
44
- if (replyTo) {
44
+ if (replyTo || params.threadId) {
45
45
  tlReplyTo = {
46
46
  _: "inputReplyToMessage",
47
- replyToMsgId: replyTo,
47
+ replyToMsgId: replyTo ?? params.threadId,
48
48
  replyToPeerId: replyToPeer,
49
+ topMsgId: params.threadId,
49
50
  quoteText: params.quote?.text,
50
51
  quoteEntities: params.quote?.entities,
51
52
  quoteOffset: params.quoteOffset,