@mtcute/core 0.27.3 → 0.27.5

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.
@@ -2035,7 +2035,16 @@ export interface TelegramClient extends ITelegramClient {
2035
2035
  *
2036
2036
  * @param params Download parameters
2037
2037
  */
2038
- downloadAsIterable(input: FileDownloadLocation, params?: FileDownloadParameters): AsyncIterableIterator<Uint8Array>;
2038
+ downloadAsIterable(input: FileDownloadLocation, params?: FileDownloadParameters & {
2039
+ /**
2040
+ * A function to apply backpressure to the download.
2041
+ *
2042
+ * If the consumer isn't keeping up, the function is supposed to return a promise that resolves when the consumer is ready to receive more data.
2043
+ *
2044
+ * Note that this function will likely get called multiple times simultaneously, since the download is parallelized.
2045
+ */
2046
+ throttle?: (chunkSize: number) => MaybePromise<void>;
2047
+ }): AsyncIterableIterator<Uint8Array>;
2039
2048
  /**
2040
2049
  * Download a remote file as a Node.js Readable stream.
2041
2050
  *
@@ -2045,14 +2054,25 @@ export interface TelegramClient extends ITelegramClient {
2045
2054
  */
2046
2055
  downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters): import('node:stream').Readable;
2047
2056
  /**
2048
- * Download a file and return it as a `@fuman/io` stream,
2057
+ * Download a file and return it as a Web Stream,
2049
2058
  * streaming file contents.
2050
2059
  *
2051
2060
  * **Available**: ✅ both users and bots
2052
2061
  *
2053
2062
  * @param params File download parameters
2054
2063
  */
2055
- downloadAsStream(location: FileDownloadLocation, params?: FileDownloadParameters): ReadableStream<Uint8Array>;
2064
+ downloadAsStream(location: FileDownloadLocation, params?: FileDownloadParameters & {
2065
+ /**
2066
+ * If passed, the maximum number of bytes that can be buffered in the stream.
2067
+ *
2068
+ * If the consumer isn't keeping up, the stream will pause until the buffer is below this limit.
2069
+ *
2070
+ * Note that this parameter is not guaranteed to be strictly the maximum number of bytes
2071
+ * buffered at once, since the underlying download is parallelized, and is treated merely as a hint.
2072
+ * The actual number of bytes buffered at once can be as much as `partSize * numWorkers`
2073
+ */
2074
+ highWaterMark?: number;
2075
+ }): ReadableStream<Uint8Array>;
2056
2076
  /**
2057
2077
  * Normalize a {@link InputFileLike} to `InputFile`,
2058
2078
  * uploading it if needed.
@@ -2035,7 +2035,16 @@ export interface TelegramClient extends ITelegramClient {
2035
2035
  *
2036
2036
  * @param params Download parameters
2037
2037
  */
2038
- downloadAsIterable(input: FileDownloadLocation, params?: FileDownloadParameters): AsyncIterableIterator<Uint8Array>;
2038
+ downloadAsIterable(input: FileDownloadLocation, params?: FileDownloadParameters & {
2039
+ /**
2040
+ * A function to apply backpressure to the download.
2041
+ *
2042
+ * If the consumer isn't keeping up, the function is supposed to return a promise that resolves when the consumer is ready to receive more data.
2043
+ *
2044
+ * Note that this function will likely get called multiple times simultaneously, since the download is parallelized.
2045
+ */
2046
+ throttle?: (chunkSize: number) => MaybePromise<void>;
2047
+ }): AsyncIterableIterator<Uint8Array>;
2039
2048
  /**
2040
2049
  * Download a remote file as a Node.js Readable stream.
2041
2050
  *
@@ -2045,14 +2054,25 @@ export interface TelegramClient extends ITelegramClient {
2045
2054
  */
2046
2055
  downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters): import('node:stream').Readable;
2047
2056
  /**
2048
- * Download a file and return it as a `@fuman/io` stream,
2057
+ * Download a file and return it as a Web Stream,
2049
2058
  * streaming file contents.
2050
2059
  *
2051
2060
  * **Available**: ✅ both users and bots
2052
2061
  *
2053
2062
  * @param params File download parameters
2054
2063
  */
2055
- downloadAsStream(location: FileDownloadLocation, params?: FileDownloadParameters): ReadableStream<Uint8Array>;
2064
+ downloadAsStream(location: FileDownloadLocation, params?: FileDownloadParameters & {
2065
+ /**
2066
+ * If passed, the maximum number of bytes that can be buffered in the stream.
2067
+ *
2068
+ * If the consumer isn't keeping up, the stream will pause until the buffer is below this limit.
2069
+ *
2070
+ * Note that this parameter is not guaranteed to be strictly the maximum number of bytes
2071
+ * buffered at once, since the underlying download is parallelized, and is treated merely as a hint.
2072
+ * The actual number of bytes buffered at once can be as much as `partSize * numWorkers`
2073
+ */
2074
+ highWaterMark?: number;
2075
+ }): ReadableStream<Uint8Array>;
2056
2076
  /**
2057
2077
  * Normalize a {@link InputFileLike} to `InputFile`,
2058
2078
  * uploading it if needed.
@@ -121,6 +121,10 @@ async function* downloadAsIterable(client, input, params) {
121
121
  if (ended) {
122
122
  return;
123
123
  }
124
+ const throttlePromise = params?.throttle?.(chunkSize);
125
+ if (throttlePromise) {
126
+ await throttlePromise;
127
+ }
124
128
  try {
125
129
  result = await client.call(
126
130
  {
@@ -1,3 +1,4 @@
1
+ import { MaybePromise } from '@fuman/utils';
1
2
  import { ITelegramClient } from '../../client.types.js';
2
3
  import { FileDownloadLocation, FileDownloadParameters } from '../../types/index.js';
3
4
  import { tl } from '@mtcute/tl';
@@ -13,4 +14,13 @@ export declare function _normalizeFileDownloadLocation(client: ITelegramClient,
13
14
  *
14
15
  * @param params Download parameters
15
16
  */
16
- export declare function downloadAsIterable(client: ITelegramClient, input: FileDownloadLocation, params?: FileDownloadParameters): AsyncIterableIterator<Uint8Array>;
17
+ export declare function downloadAsIterable(client: ITelegramClient, input: FileDownloadLocation, params?: FileDownloadParameters & {
18
+ /**
19
+ * A function to apply backpressure to the download.
20
+ *
21
+ * If the consumer isn't keeping up, the function is supposed to return a promise that resolves when the consumer is ready to receive more data.
22
+ *
23
+ * Note that this function will likely get called multiple times simultaneously, since the download is parallelized.
24
+ */
25
+ throttle?: (chunkSize: number) => MaybePromise<void>;
26
+ }): AsyncIterableIterator<Uint8Array>;
@@ -1,3 +1,4 @@
1
+ import { MaybePromise } from '@fuman/utils';
1
2
  import { ITelegramClient } from '../../client.types.js';
2
3
  import { FileDownloadLocation, FileDownloadParameters } from '../../types/index.js';
3
4
  import { tl } from '@mtcute/tl';
@@ -13,4 +14,13 @@ export declare function _normalizeFileDownloadLocation(client: ITelegramClient,
13
14
  *
14
15
  * @param params Download parameters
15
16
  */
16
- export declare function downloadAsIterable(client: ITelegramClient, input: FileDownloadLocation, params?: FileDownloadParameters): AsyncIterableIterator<Uint8Array>;
17
+ export declare function downloadAsIterable(client: ITelegramClient, input: FileDownloadLocation, params?: FileDownloadParameters & {
18
+ /**
19
+ * A function to apply backpressure to the download.
20
+ *
21
+ * If the consumer isn't keeping up, the function is supposed to return a promise that resolves when the consumer is ready to receive more data.
22
+ *
23
+ * Note that this function will likely get called multiple times simultaneously, since the download is parallelized.
24
+ */
25
+ throttle?: (chunkSize: number) => MaybePromise<void>;
26
+ }): AsyncIterableIterator<Uint8Array>;
@@ -114,6 +114,10 @@ async function* downloadAsIterable(client, input, params) {
114
114
  if (ended) {
115
115
  return;
116
116
  }
117
+ const throttlePromise = params?.throttle?.(chunkSize);
118
+ if (throttlePromise) {
119
+ await throttlePromise;
120
+ }
117
121
  try {
118
122
  result = await client.call(
119
123
  {
@@ -5,6 +5,7 @@ if (typeof globalThis !== "undefined" && !globalThis._MTCUTE_CJS_DEPRECATION_WAR
5
5
  }
6
6
  "use strict";
7
7
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
8
+ const utils = require("@fuman/utils");
8
9
  const downloadIterable = require("./download-iterable.cjs");
9
10
  const fileLocation = require("../../types/files/file-location.cjs");
10
11
  function downloadAsStream(client, location, params) {
@@ -23,18 +24,31 @@ function downloadAsStream(client, location, params) {
23
24
  cancel.abort();
24
25
  });
25
26
  }
27
+ const backpressureCv = new utils.ConditionVariable();
26
28
  return new ReadableStream({
27
29
  start(controller) {
28
30
  (async () => {
31
+ const params_ = params ?? {};
32
+ params_.throttle = () => {
33
+ if (controller.desiredSize != null && controller.desiredSize <= 0) {
34
+ return backpressureCv.wait();
35
+ }
36
+ };
29
37
  for await (const chunk of downloadIterable.downloadAsIterable(client, location, params)) {
30
38
  controller.enqueue(chunk);
31
39
  }
32
40
  controller.close();
33
41
  })().catch((e) => controller.error(e));
34
42
  },
43
+ pull() {
44
+ backpressureCv.notify();
45
+ },
35
46
  cancel() {
36
47
  cancel.abort();
37
48
  }
49
+ }, {
50
+ highWaterMark: params?.highWaterMark,
51
+ size: (chunk) => chunk?.length ?? 0
38
52
  });
39
53
  }
40
54
  exports.downloadAsStream = downloadAsStream;
@@ -1,9 +1,20 @@
1
1
  import { ITelegramClient } from '../../client.types.js';
2
2
  import { FileDownloadLocation, FileDownloadParameters } from '../../types/index.js';
3
3
  /**
4
- * Download a file and return it as a `@fuman/io` stream,
4
+ * Download a file and return it as a Web Stream,
5
5
  * streaming file contents.
6
6
  *
7
7
  * @param params File download parameters
8
8
  */
9
- export declare function downloadAsStream(client: ITelegramClient, location: FileDownloadLocation, params?: FileDownloadParameters): ReadableStream<Uint8Array>;
9
+ export declare function downloadAsStream(client: ITelegramClient, location: FileDownloadLocation, params?: FileDownloadParameters & {
10
+ /**
11
+ * If passed, the maximum number of bytes that can be buffered in the stream.
12
+ *
13
+ * If the consumer isn't keeping up, the stream will pause until the buffer is below this limit.
14
+ *
15
+ * Note that this parameter is not guaranteed to be strictly the maximum number of bytes
16
+ * buffered at once, since the underlying download is parallelized, and is treated merely as a hint.
17
+ * The actual number of bytes buffered at once can be as much as `partSize * numWorkers`
18
+ */
19
+ highWaterMark?: number;
20
+ }): ReadableStream<Uint8Array>;
@@ -1,9 +1,20 @@
1
1
  import { ITelegramClient } from '../../client.types.js';
2
2
  import { FileDownloadLocation, FileDownloadParameters } from '../../types/index.js';
3
3
  /**
4
- * Download a file and return it as a `@fuman/io` stream,
4
+ * Download a file and return it as a Web Stream,
5
5
  * streaming file contents.
6
6
  *
7
7
  * @param params File download parameters
8
8
  */
9
- export declare function downloadAsStream(client: ITelegramClient, location: FileDownloadLocation, params?: FileDownloadParameters): ReadableStream<Uint8Array>;
9
+ export declare function downloadAsStream(client: ITelegramClient, location: FileDownloadLocation, params?: FileDownloadParameters & {
10
+ /**
11
+ * If passed, the maximum number of bytes that can be buffered in the stream.
12
+ *
13
+ * If the consumer isn't keeping up, the stream will pause until the buffer is below this limit.
14
+ *
15
+ * Note that this parameter is not guaranteed to be strictly the maximum number of bytes
16
+ * buffered at once, since the underlying download is parallelized, and is treated merely as a hint.
17
+ * The actual number of bytes buffered at once can be as much as `partSize * numWorkers`
18
+ */
19
+ highWaterMark?: number;
20
+ }): ReadableStream<Uint8Array>;
@@ -1,3 +1,4 @@
1
+ import { ConditionVariable } from "@fuman/utils";
1
2
  import { downloadAsIterable } from "./download-iterable.js";
2
3
  import { FileLocation as FileLocation$1 } from "../../types/files/file-location.js";
3
4
  function downloadAsStream(client, location, params) {
@@ -16,18 +17,31 @@ function downloadAsStream(client, location, params) {
16
17
  cancel.abort();
17
18
  });
18
19
  }
20
+ const backpressureCv = new ConditionVariable();
19
21
  return new ReadableStream({
20
22
  start(controller) {
21
23
  (async () => {
24
+ const params_ = params ?? {};
25
+ params_.throttle = () => {
26
+ if (controller.desiredSize != null && controller.desiredSize <= 0) {
27
+ return backpressureCv.wait();
28
+ }
29
+ };
22
30
  for await (const chunk of downloadAsIterable(client, location, params)) {
23
31
  controller.enqueue(chunk);
24
32
  }
25
33
  controller.close();
26
34
  })().catch((e) => controller.error(e));
27
35
  },
36
+ pull() {
37
+ backpressureCv.notify();
38
+ },
28
39
  cancel() {
29
40
  cancel.abort();
30
41
  }
42
+ }, {
43
+ highWaterMark: params?.highWaterMark,
44
+ size: (chunk) => chunk?.length ?? 0
31
45
  });
32
46
  }
33
47
  export {
@@ -26,6 +26,7 @@ async function uploadMedia(client, media, params = {}) {
26
26
  case "inputMediaDice":
27
27
  case "inputMediaPaidMedia":
28
28
  case "inputMediaTodo":
29
+ case "inputMediaStakeDice":
29
30
  throw new errors.MtArgumentError(`This media (${normMedia._}) can't be uploaded`);
30
31
  }
31
32
  const res = await client.call({
@@ -19,6 +19,7 @@ async function uploadMedia(client, media, params = {}) {
19
19
  case "inputMediaDice":
20
20
  case "inputMediaPaidMedia":
21
21
  case "inputMediaTodo":
22
+ case "inputMediaStakeDice":
22
23
  throw new MtArgumentError(`This media (${normMedia._}) can't be uploaded`);
23
24
  }
24
25
  const res = await client.call({
@@ -28,7 +28,7 @@ function parseDocument(doc, media) {
28
28
  switch (attr._) {
29
29
  case "documentAttributeAudio":
30
30
  if (attr.voice) {
31
- return new voice.Voice(doc, attr);
31
+ return new voice.Voice(doc, attr, media);
32
32
  }
33
33
  return new audio.Audio(doc, attr);
34
34
  case "documentAttributeVideo":
@@ -21,7 +21,7 @@ function parseDocument(doc, media) {
21
21
  switch (attr._) {
22
22
  case "documentAttributeAudio":
23
23
  if (attr.voice) {
24
- return new Voice$2(doc, attr);
24
+ return new Voice$2(doc, attr, media);
25
25
  }
26
26
  return new Audio$2(doc, attr);
27
27
  case "documentAttributeVideo":
@@ -11,9 +11,10 @@ const voiceUtils = require("../../utils/voice-utils.cjs");
11
11
  const document = require("./document.cjs");
12
12
  const inspectable = require("../../utils/inspectable.cjs");
13
13
  class Voice extends document.RawDocument {
14
- constructor(doc, attr) {
14
+ constructor(doc, attr, media) {
15
15
  super(doc);
16
16
  this.attr = attr;
17
+ this.media = media;
17
18
  }
18
19
  type = "voice";
19
20
  _fileIdType() {
@@ -34,6 +35,10 @@ class Voice extends document.RawDocument {
34
35
  get waveform() {
35
36
  return voiceUtils.decodeWaveform(this.attr.waveform);
36
37
  }
38
+ /** For self-destructing voice notes, TTL in seconds */
39
+ get ttlSeconds() {
40
+ return this.media?.ttlSeconds ?? null;
41
+ }
37
42
  }
38
43
  const Voice$1 = /* @__PURE__ */ memoize.memoizeGetters(Voice, ["fileName", "thumbnails", "fileId", "uniqueFileId", "waveform"]);
39
44
  const Voice$2 = /* @__PURE__ */ inspectable.makeInspectable(Voice$1, ["fileSize", "dcId"], ["inputMedia", "inputDocument", "waveform"]);
@@ -6,9 +6,10 @@ import { RawDocument } from './document.js';
6
6
  */
7
7
  export declare class Voice extends RawDocument {
8
8
  readonly attr: tl.RawDocumentAttributeAudio;
9
+ readonly media?: tl.RawMessageMediaDocument | undefined;
9
10
  readonly type: "voice";
10
11
  protected _fileIdType(): tdFileId.FileType;
11
- constructor(doc: tl.RawDocument, attr: tl.RawDocumentAttributeAudio);
12
+ constructor(doc: tl.RawDocument, attr: tl.RawDocumentAttributeAudio, media?: tl.RawMessageMediaDocument | undefined);
12
13
  /**
13
14
  * Duration of the voice note in seconds.
14
15
  */
@@ -20,4 +21,6 @@ export declare class Voice extends RawDocument {
20
21
  * usually has length of 100
21
22
  */
22
23
  get waveform(): number[];
24
+ /** For self-destructing voice notes, TTL in seconds */
25
+ get ttlSeconds(): number | null;
23
26
  }
@@ -6,9 +6,10 @@ import { RawDocument } from './document.js';
6
6
  */
7
7
  export declare class Voice extends RawDocument {
8
8
  readonly attr: tl.RawDocumentAttributeAudio;
9
+ readonly media?: tl.RawMessageMediaDocument | undefined;
9
10
  readonly type: "voice";
10
11
  protected _fileIdType(): tdFileId.FileType;
11
- constructor(doc: tl.RawDocument, attr: tl.RawDocumentAttributeAudio);
12
+ constructor(doc: tl.RawDocument, attr: tl.RawDocumentAttributeAudio, media?: tl.RawMessageMediaDocument | undefined);
12
13
  /**
13
14
  * Duration of the voice note in seconds.
14
15
  */
@@ -20,4 +21,6 @@ export declare class Voice extends RawDocument {
20
21
  * usually has length of 100
21
22
  */
22
23
  get waveform(): number[];
24
+ /** For self-destructing voice notes, TTL in seconds */
25
+ get ttlSeconds(): number | null;
23
26
  }
@@ -4,9 +4,10 @@ import { decodeWaveform } from "../../utils/voice-utils.js";
4
4
  import { RawDocument } from "./document.js";
5
5
  import { makeInspectable } from "../../utils/inspectable.js";
6
6
  class Voice extends RawDocument {
7
- constructor(doc, attr) {
7
+ constructor(doc, attr, media) {
8
8
  super(doc);
9
9
  this.attr = attr;
10
+ this.media = media;
10
11
  }
11
12
  type = "voice";
12
13
  _fileIdType() {
@@ -27,6 +28,10 @@ class Voice extends RawDocument {
27
28
  get waveform() {
28
29
  return decodeWaveform(this.attr.waveform);
29
30
  }
31
+ /** For self-destructing voice notes, TTL in seconds */
32
+ get ttlSeconds() {
33
+ return this.media?.ttlSeconds ?? null;
34
+ }
30
35
  }
31
36
  const Voice$1 = /* @__PURE__ */ memoizeGetters(Voice, ["fileName", "thumbnails", "fileId", "uniqueFileId", "waveform"]);
32
37
  const Voice$2 = /* @__PURE__ */ makeInspectable(Voice$1, ["fileSize", "dcId"], ["inputMedia", "inputDocument", "waveform"]);
@@ -225,7 +225,7 @@ class NetworkManager {
225
225
  _: "initConnection",
226
226
  deviceModel,
227
227
  systemVersion: "1.0",
228
- appVersion: "0.27.3",
228
+ appVersion: "0.27.5",
229
229
  systemLangCode: "en",
230
230
  langPack: "",
231
231
  // "langPacks are for official apps only"
@@ -218,7 +218,7 @@ class NetworkManager {
218
218
  _: "initConnection",
219
219
  deviceModel,
220
220
  systemVersion: "1.0",
221
- appVersion: "0.27.3",
221
+ appVersion: "0.27.5",
222
222
  systemLangCode: "en",
223
223
  langPack: "",
224
224
  // "langPacks are for official apps only"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mtcute/core",
3
3
  "type": "module",
4
- "version": "0.27.3",
4
+ "version": "0.27.5",
5
5
  "description": "Type-safe library for MTProto (Telegram API)",
6
6
  "license": "MIT",
7
7
  "scripts": {},
@@ -10,7 +10,7 @@
10
10
  "@fuman/net": "0.0.17",
11
11
  "@fuman/utils": "0.0.17",
12
12
  "@mtcute/file-id": "^0.27.0",
13
- "@mtcute/tl": "^220.0.0",
13
+ "@mtcute/tl": "^221.0.0",
14
14
  "@mtcute/tl-runtime": "^0.24.3",
15
15
  "@types/events": "3.0.0",
16
16
  "long": "5.2.3"
@@ -92,6 +92,8 @@ function mapCompatMessageMedia(obj) {
92
92
  switch (obj._) {
93
93
  case "messageMediaDocument_layer197":
94
94
  return replaceType(obj, "messageMediaDocument");
95
+ case "messageMediaDice_layer220":
96
+ return replaceType(obj, "messageMediaDice");
95
97
  default:
96
98
  return obj;
97
99
  }
@@ -169,6 +171,7 @@ function mapCompatMessage(obj) {
169
171
  case "message_layer199":
170
172
  case "message_layer204":
171
173
  case "message_layer216":
174
+ case "message_layer220":
172
175
  return {
173
176
  ...obj,
174
177
  _: "message",
@@ -207,6 +210,7 @@ function mapCompatObject(obj) {
207
210
  case "emojiStatusUntil_layer214":
208
211
  return mapCompatEmojiStatus(obj);
209
212
  case "messageMediaDocument_layer197":
213
+ case "messageMediaDice_layer220":
210
214
  return mapCompatMessageMedia(obj);
211
215
  case "channelFull_layer197":
212
216
  case "channelFull_layer204":
@@ -256,6 +260,7 @@ function mapCompatObject(obj) {
256
260
  case "message_layer199":
257
261
  case "message_layer204":
258
262
  case "message_layer216":
263
+ case "message_layer220":
259
264
  case "messageService_layer204":
260
265
  return mapCompatMessage(obj);
261
266
  case "messageReplyHeader_layer206":
@@ -85,6 +85,8 @@ function mapCompatMessageMedia(obj) {
85
85
  switch (obj._) {
86
86
  case "messageMediaDocument_layer197":
87
87
  return replaceType(obj, "messageMediaDocument");
88
+ case "messageMediaDice_layer220":
89
+ return replaceType(obj, "messageMediaDice");
88
90
  default:
89
91
  return obj;
90
92
  }
@@ -162,6 +164,7 @@ function mapCompatMessage(obj) {
162
164
  case "message_layer199":
163
165
  case "message_layer204":
164
166
  case "message_layer216":
167
+ case "message_layer220":
165
168
  return {
166
169
  ...obj,
167
170
  _: "message",
@@ -200,6 +203,7 @@ function mapCompatObject(obj) {
200
203
  case "emojiStatusUntil_layer214":
201
204
  return mapCompatEmojiStatus(obj);
202
205
  case "messageMediaDocument_layer197":
206
+ case "messageMediaDice_layer220":
203
207
  return mapCompatMessageMedia(obj);
204
208
  case "channelFull_layer197":
205
209
  case "channelFull_layer204":
@@ -249,6 +253,7 @@ function mapCompatObject(obj) {
249
253
  case "message_layer199":
250
254
  case "message_layer204":
251
255
  case "message_layer216":
256
+ case "message_layer220":
252
257
  case "messageService_layer204":
253
258
  return mapCompatMessage(obj);
254
259
  case "messageReplyHeader_layer206":