@mtcute/core 0.30.1 → 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.
- package/highlevel/methods/files/download-iterable.cjs +80 -45
- package/highlevel/methods/files/download-iterable.js +80 -45
- package/highlevel/methods/files/normalize-input-media.cjs +1 -1
- package/highlevel/methods/files/normalize-input-media.js +1 -1
- package/highlevel/methods/files/upload-file.cjs +6 -1
- package/highlevel/methods/files/upload-file.js +6 -1
- package/highlevel/methods/messages/normalize-rich-message.cjs +12 -1
- package/highlevel/methods/messages/normalize-rich-message.js +12 -1
- package/highlevel/methods/messages/send-common.cjs +3 -2
- package/highlevel/methods/messages/send-common.d.cts +4 -1
- package/highlevel/methods/messages/send-common.d.ts +4 -1
- package/highlevel/methods/messages/send-common.js +3 -2
- package/highlevel/types/files/utils.d.cts +9 -0
- package/highlevel/types/files/utils.d.ts +9 -0
- package/highlevel/updates/manager.cjs +6 -169
- package/highlevel/updates/manager.js +6 -169
- package/highlevel/utils/index.d.cts +1 -0
- package/highlevel/utils/index.d.ts +1 -0
- package/highlevel/utils/walk-page-blocks.cjs +164 -0
- package/highlevel/utils/walk-page-blocks.d.cts +9 -0
- package/highlevel/utils/walk-page-blocks.d.ts +9 -0
- package/highlevel/utils/walk-page-blocks.js +159 -0
- package/network/authorization.cjs +1 -1
- package/network/authorization.js +1 -1
- package/network/delay-gate.cjs +29 -0
- package/network/delay-gate.d.cts +5 -0
- package/network/delay-gate.d.ts +5 -0
- package/network/delay-gate.js +24 -0
- package/network/delay-gate.test.d.cts +1 -0
- package/network/delay-gate.test.d.ts +1 -0
- package/network/message-id-store.cjs +30 -0
- package/network/message-id-store.d.cts +7 -0
- package/network/message-id-store.d.ts +7 -0
- package/network/message-id-store.js +25 -0
- package/network/message-id-store.test.d.cts +1 -0
- package/network/message-id-store.test.d.ts +1 -0
- package/network/middlewares/bundle.cjs +2 -0
- package/network/middlewares/bundle.d.cts +1 -0
- package/network/middlewares/bundle.d.ts +1 -0
- package/network/middlewares/bundle.js +2 -0
- package/network/middlewares/default.cjs +6 -1
- package/network/middlewares/default.js +6 -1
- package/network/middlewares/flood-waiter.test.d.cts +1 -0
- package/network/middlewares/flood-waiter.test.d.ts +1 -0
- package/network/middlewares/internal-errors.test.d.cts +1 -0
- package/network/middlewares/internal-errors.test.d.ts +1 -0
- package/network/middlewares/media-throttle.cjs +65 -0
- package/network/middlewares/media-throttle.d.cts +5 -0
- package/network/middlewares/media-throttle.d.ts +5 -0
- package/network/middlewares/media-throttle.js +60 -0
- package/network/middlewares/media-throttle.test.d.cts +1 -0
- package/network/middlewares/media-throttle.test.d.ts +1 -0
- package/network/middlewares/on-error.test.d.cts +1 -0
- package/network/middlewares/on-error.test.d.ts +1 -0
- package/network/middlewares/on-method.test.d.cts +1 -0
- package/network/middlewares/on-method.test.d.ts +1 -0
- package/network/mtproto-session.cjs +9 -2
- package/network/mtproto-session.d.cts +5 -2
- package/network/mtproto-session.d.ts +5 -2
- package/network/mtproto-session.js +9 -2
- package/network/multi-session-connection.cjs +7 -0
- package/network/multi-session-connection.d.cts +2 -0
- package/network/multi-session-connection.d.ts +2 -0
- package/network/multi-session-connection.js +7 -0
- package/network/network-manager.cjs +7 -1
- package/network/network-manager.js +7 -1
- package/network/resource-limiter.cjs +60 -0
- package/network/resource-limiter.d.cts +11 -0
- package/network/resource-limiter.d.ts +11 -0
- package/network/resource-limiter.js +55 -0
- package/network/resource-limiter.test.d.cts +1 -0
- package/network/resource-limiter.test.d.ts +1 -0
- package/network/server-salt.cjs +7 -3
- package/network/server-salt.d.cts +2 -0
- package/network/server-salt.d.ts +2 -0
- package/network/server-salt.js +7 -3
- package/network/session-connection.cjs +106 -20
- package/network/session-connection.d.cts +11 -0
- package/network/session-connection.d.ts +11 -0
- package/network/session-connection.js +106 -20
- package/package.json +1 -1
- package/utils.cjs +2 -0
- package/utils.js +2 -0
|
@@ -7,7 +7,9 @@ if (typeof globalThis !== "undefined" && !globalThis._MTCUTE_CJS_DEPRECATION_WAR
|
|
|
7
7
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
8
8
|
const utils = require("@fuman/utils");
|
|
9
9
|
const fileId = require("@mtcute/file-id");
|
|
10
|
+
const delayGate = require("../../../network/delay-gate.cjs");
|
|
10
11
|
const errors$1 = require("../../../types/errors.cjs");
|
|
12
|
+
const abortSignal = require("../../../utils/abort-signal.cjs");
|
|
11
13
|
const peerUtils = require("../../../utils/peer-utils.cjs");
|
|
12
14
|
const convertFileId = require("../../utils/convert-file-id.cjs");
|
|
13
15
|
const fileUtils = require("../../utils/file-utils.cjs");
|
|
@@ -90,8 +92,17 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
90
92
|
let dcId = params?.dcId ?? normalized?.dcId;
|
|
91
93
|
const primaryDcId = await client.getPrimaryDcId();
|
|
92
94
|
if (!dcId) dcId = primaryDcId;
|
|
93
|
-
const
|
|
95
|
+
const stallTimeout = params?.stallTimeout;
|
|
96
|
+
const stallAbort = stallTimeout != null ? new AbortController() : void 0;
|
|
97
|
+
const abortSignal$1 = stallAbort ? params?.abortSignal ? abortSignal.combineAbortSignals(params.abortSignal, stallAbort.signal) : stallAbort.signal : params?.abortSignal;
|
|
94
98
|
let ended = false;
|
|
99
|
+
let stallTimer;
|
|
100
|
+
const resetStallTimer = stallAbort && stallTimeout != null ? () => {
|
|
101
|
+
if (stallTimer) utils.timers.clearTimeout(stallTimer);
|
|
102
|
+
stallTimer = utils.timers.setTimeout(() => {
|
|
103
|
+
stallAbort.abort(new errors$1.MtTimeoutError(stallTimeout));
|
|
104
|
+
}, stallTimeout);
|
|
105
|
+
} : void 0;
|
|
95
106
|
const partSizeKb = params?.partSize ?? (fileSize ? fileUtils.determinePartSize(fileSize) : 64);
|
|
96
107
|
if (partSizeKb % 4 !== 0) {
|
|
97
108
|
throw new errors$1.MtArgumentError(`Invalid part size: ${partSizeKb}. Must be divisible by 4.`);
|
|
@@ -120,6 +131,7 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
120
131
|
connectionKind = "download";
|
|
121
132
|
}
|
|
122
133
|
const poolSize = await client.getPoolSize(connectionKind, dcId);
|
|
134
|
+
const delayGate$1 = isSmall ? void 0 : new delayGate.DownloadDelayGate();
|
|
123
135
|
client.log.debug(
|
|
124
136
|
"Downloading file of size %d from dc %d using %s connection pool (pool size: %d)",
|
|
125
137
|
limitBytes,
|
|
@@ -127,6 +139,44 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
127
139
|
connectionKind,
|
|
128
140
|
poolSize
|
|
129
141
|
);
|
|
142
|
+
const callPart = async (chunk) => {
|
|
143
|
+
while (true) {
|
|
144
|
+
try {
|
|
145
|
+
return await client.call(
|
|
146
|
+
{
|
|
147
|
+
_: isWeb ? "upload.getWebFile" : "upload.getFile",
|
|
148
|
+
// eslint-disable-next-line
|
|
149
|
+
location,
|
|
150
|
+
offset: offset + chunkSize * chunk,
|
|
151
|
+
limit: chunkSize
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
dcId,
|
|
155
|
+
kind: connectionKind,
|
|
156
|
+
maxRetryCount: Infinity,
|
|
157
|
+
// retry until explicitly aborted (or finished)
|
|
158
|
+
floodSleepThreshold: Infinity,
|
|
159
|
+
abortSignal: abortSignal$1
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
} catch (e) {
|
|
163
|
+
if (e instanceof DOMException && e.name === "AbortError") return void 0;
|
|
164
|
+
if (!innerTl.RpcError.is(e)) throw e;
|
|
165
|
+
if (e.is("FILE_MIGRATE_%d")) {
|
|
166
|
+
dcId = e.newDc;
|
|
167
|
+
continue;
|
|
168
|
+
} else if (e.is("FILEREF_UPGRADE_NEEDED")) {
|
|
169
|
+
throw new errors$1.MtUnsupportedError("File ref expired!");
|
|
170
|
+
} else if (e.is("MTPROTO_CLUSTER_INVALID") && dcId != null) {
|
|
171
|
+
client.log.debug("received cluster invalid error for dc %d, recreating dc", dcId);
|
|
172
|
+
await client.recreateDc(dcId);
|
|
173
|
+
continue;
|
|
174
|
+
} else {
|
|
175
|
+
throw e;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
130
180
|
const downloadChunk = async (chunk = nextWorkerChunkIdx++) => {
|
|
131
181
|
let result;
|
|
132
182
|
if (ended) {
|
|
@@ -136,40 +186,20 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
136
186
|
if (throttlePromise) {
|
|
137
187
|
await throttlePromise;
|
|
138
188
|
}
|
|
189
|
+
if (ended) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
139
192
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
_: isWeb ? "upload.getWebFile" : "upload.getFile",
|
|
143
|
-
// eslint-disable-next-line
|
|
144
|
-
location,
|
|
145
|
-
offset: offset + chunkSize * chunk,
|
|
146
|
-
limit: chunkSize
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
dcId,
|
|
150
|
-
kind: connectionKind,
|
|
151
|
-
maxRetryCount: Infinity,
|
|
152
|
-
// retry until explicitly aborted (or finished)
|
|
153
|
-
floodSleepThreshold: Infinity,
|
|
154
|
-
abortSignal
|
|
155
|
-
}
|
|
156
|
-
);
|
|
193
|
+
if (delayGate$1) await delayGate$1.wait(abortSignal$1);
|
|
194
|
+
result = await callPart(chunk);
|
|
157
195
|
} catch (e) {
|
|
158
196
|
if (e instanceof DOMException && e.name === "AbortError") return;
|
|
159
|
-
|
|
160
|
-
if (e.is("FILE_MIGRATE_%d")) {
|
|
161
|
-
dcId = e.newDc;
|
|
162
|
-
return downloadChunk(chunk);
|
|
163
|
-
} else if (e.is("FILEREF_UPGRADE_NEEDED")) {
|
|
164
|
-
throw new errors$1.MtUnsupportedError("File ref expired!");
|
|
165
|
-
} else if (e.is("MTPROTO_CLUSTER_INVALID") && dcId != null) {
|
|
166
|
-
client.log.debug("received cluster invalid error for dc %d, recreating dc", dcId);
|
|
167
|
-
await client.recreateDc(dcId);
|
|
168
|
-
return downloadChunk(chunk);
|
|
169
|
-
} else {
|
|
170
|
-
throw e;
|
|
171
|
-
}
|
|
197
|
+
throw e;
|
|
172
198
|
}
|
|
199
|
+
if (!result) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
resetStallTimer?.();
|
|
173
203
|
if (result._ === "upload.fileCdnRedirect") {
|
|
174
204
|
throw new errors$1.MtUnsupportedError("Received CDN redirect, which is not supported (yet)");
|
|
175
205
|
}
|
|
@@ -189,6 +219,7 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
189
219
|
}
|
|
190
220
|
};
|
|
191
221
|
let error;
|
|
222
|
+
resetStallTimer?.();
|
|
192
223
|
void Promise.all(Array.from({
|
|
193
224
|
length: Math.min(poolSize * (isSmall ? 1 : REQUESTS_PER_CONNECTION), numChunks)
|
|
194
225
|
}, downloadChunk)).catch((e) => {
|
|
@@ -199,27 +230,31 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
199
230
|
}).then(() => {
|
|
200
231
|
client.log.debug("download workers finished");
|
|
201
232
|
});
|
|
202
|
-
abortSignal?.addEventListener("abort", () => {
|
|
233
|
+
abortSignal$1?.addEventListener("abort", () => {
|
|
203
234
|
client.log.debug("download aborted");
|
|
204
|
-
error = abortSignal.reason;
|
|
235
|
+
error = abortSignal$1.reason;
|
|
205
236
|
ended = true;
|
|
206
237
|
nextChunkCv.notify();
|
|
207
238
|
});
|
|
208
239
|
let position = offset;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
240
|
+
try {
|
|
241
|
+
while (position < limitBytes) {
|
|
242
|
+
await nextChunkCv.wait();
|
|
243
|
+
if (error) throw error;
|
|
244
|
+
while (nextChunkIdx in buffer) {
|
|
245
|
+
const buf = buffer[nextChunkIdx];
|
|
246
|
+
delete buffer[nextChunkIdx];
|
|
247
|
+
position += buf.length;
|
|
248
|
+
params?.progressCallback?.(position, limitBytes);
|
|
249
|
+
yield buf;
|
|
250
|
+
nextChunkIdx++;
|
|
251
|
+
if (buf.length < chunkSize) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
221
254
|
}
|
|
222
255
|
}
|
|
256
|
+
} finally {
|
|
257
|
+
if (stallTimer) utils.timers.clearTimeout(stallTimer);
|
|
223
258
|
}
|
|
224
259
|
}
|
|
225
260
|
exports._normalizeFileDownloadLocation = _normalizeFileDownloadLocation;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { ConditionVariable } from "@fuman/utils";
|
|
1
|
+
import { ConditionVariable, timers } from "@fuman/utils";
|
|
2
2
|
import { parseFileId } from "@mtcute/file-id";
|
|
3
|
-
import {
|
|
3
|
+
import { DownloadDelayGate } from "../../../network/delay-gate.js";
|
|
4
|
+
import { MtArgumentError, MtTimeoutError, MtUnsupportedError } from "../../../types/errors.js";
|
|
5
|
+
import { combineAbortSignals } from "../../../utils/abort-signal.js";
|
|
4
6
|
import { toggleChannelIdMark } from "../../../utils/peer-utils.js";
|
|
5
7
|
import { fileIdToInputWebFileLocation, fileIdToInputFileLocation } from "../../utils/convert-file-id.js";
|
|
6
8
|
import { determinePartSize } from "../../utils/file-utils.js";
|
|
@@ -83,8 +85,17 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
83
85
|
let dcId = params?.dcId ?? normalized?.dcId;
|
|
84
86
|
const primaryDcId = await client.getPrimaryDcId();
|
|
85
87
|
if (!dcId) dcId = primaryDcId;
|
|
86
|
-
const
|
|
88
|
+
const stallTimeout = params?.stallTimeout;
|
|
89
|
+
const stallAbort = stallTimeout != null ? new AbortController() : void 0;
|
|
90
|
+
const abortSignal = stallAbort ? params?.abortSignal ? combineAbortSignals(params.abortSignal, stallAbort.signal) : stallAbort.signal : params?.abortSignal;
|
|
87
91
|
let ended = false;
|
|
92
|
+
let stallTimer;
|
|
93
|
+
const resetStallTimer = stallAbort && stallTimeout != null ? () => {
|
|
94
|
+
if (stallTimer) timers.clearTimeout(stallTimer);
|
|
95
|
+
stallTimer = timers.setTimeout(() => {
|
|
96
|
+
stallAbort.abort(new MtTimeoutError(stallTimeout));
|
|
97
|
+
}, stallTimeout);
|
|
98
|
+
} : void 0;
|
|
88
99
|
const partSizeKb = params?.partSize ?? (fileSize ? determinePartSize(fileSize) : 64);
|
|
89
100
|
if (partSizeKb % 4 !== 0) {
|
|
90
101
|
throw new MtArgumentError(`Invalid part size: ${partSizeKb}. Must be divisible by 4.`);
|
|
@@ -113,6 +124,7 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
113
124
|
connectionKind = "download";
|
|
114
125
|
}
|
|
115
126
|
const poolSize = await client.getPoolSize(connectionKind, dcId);
|
|
127
|
+
const delayGate = isSmall ? void 0 : new DownloadDelayGate();
|
|
116
128
|
client.log.debug(
|
|
117
129
|
"Downloading file of size %d from dc %d using %s connection pool (pool size: %d)",
|
|
118
130
|
limitBytes,
|
|
@@ -120,6 +132,44 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
120
132
|
connectionKind,
|
|
121
133
|
poolSize
|
|
122
134
|
);
|
|
135
|
+
const callPart = async (chunk) => {
|
|
136
|
+
while (true) {
|
|
137
|
+
try {
|
|
138
|
+
return await client.call(
|
|
139
|
+
{
|
|
140
|
+
_: isWeb ? "upload.getWebFile" : "upload.getFile",
|
|
141
|
+
// eslint-disable-next-line
|
|
142
|
+
location,
|
|
143
|
+
offset: offset + chunkSize * chunk,
|
|
144
|
+
limit: chunkSize
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
dcId,
|
|
148
|
+
kind: connectionKind,
|
|
149
|
+
maxRetryCount: Infinity,
|
|
150
|
+
// retry until explicitly aborted (or finished)
|
|
151
|
+
floodSleepThreshold: Infinity,
|
|
152
|
+
abortSignal
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
if (e instanceof DOMException && e.name === "AbortError") return void 0;
|
|
157
|
+
if (!RpcError.is(e)) throw e;
|
|
158
|
+
if (e.is("FILE_MIGRATE_%d")) {
|
|
159
|
+
dcId = e.newDc;
|
|
160
|
+
continue;
|
|
161
|
+
} else if (e.is("FILEREF_UPGRADE_NEEDED")) {
|
|
162
|
+
throw new MtUnsupportedError("File ref expired!");
|
|
163
|
+
} else if (e.is("MTPROTO_CLUSTER_INVALID") && dcId != null) {
|
|
164
|
+
client.log.debug("received cluster invalid error for dc %d, recreating dc", dcId);
|
|
165
|
+
await client.recreateDc(dcId);
|
|
166
|
+
continue;
|
|
167
|
+
} else {
|
|
168
|
+
throw e;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
123
173
|
const downloadChunk = async (chunk = nextWorkerChunkIdx++) => {
|
|
124
174
|
let result;
|
|
125
175
|
if (ended) {
|
|
@@ -129,40 +179,20 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
129
179
|
if (throttlePromise) {
|
|
130
180
|
await throttlePromise;
|
|
131
181
|
}
|
|
182
|
+
if (ended) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
132
185
|
try {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
_: isWeb ? "upload.getWebFile" : "upload.getFile",
|
|
136
|
-
// eslint-disable-next-line
|
|
137
|
-
location,
|
|
138
|
-
offset: offset + chunkSize * chunk,
|
|
139
|
-
limit: chunkSize
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
dcId,
|
|
143
|
-
kind: connectionKind,
|
|
144
|
-
maxRetryCount: Infinity,
|
|
145
|
-
// retry until explicitly aborted (or finished)
|
|
146
|
-
floodSleepThreshold: Infinity,
|
|
147
|
-
abortSignal
|
|
148
|
-
}
|
|
149
|
-
);
|
|
186
|
+
if (delayGate) await delayGate.wait(abortSignal);
|
|
187
|
+
result = await callPart(chunk);
|
|
150
188
|
} catch (e) {
|
|
151
189
|
if (e instanceof DOMException && e.name === "AbortError") return;
|
|
152
|
-
|
|
153
|
-
if (e.is("FILE_MIGRATE_%d")) {
|
|
154
|
-
dcId = e.newDc;
|
|
155
|
-
return downloadChunk(chunk);
|
|
156
|
-
} else if (e.is("FILEREF_UPGRADE_NEEDED")) {
|
|
157
|
-
throw new MtUnsupportedError("File ref expired!");
|
|
158
|
-
} else if (e.is("MTPROTO_CLUSTER_INVALID") && dcId != null) {
|
|
159
|
-
client.log.debug("received cluster invalid error for dc %d, recreating dc", dcId);
|
|
160
|
-
await client.recreateDc(dcId);
|
|
161
|
-
return downloadChunk(chunk);
|
|
162
|
-
} else {
|
|
163
|
-
throw e;
|
|
164
|
-
}
|
|
190
|
+
throw e;
|
|
165
191
|
}
|
|
192
|
+
if (!result) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
resetStallTimer?.();
|
|
166
196
|
if (result._ === "upload.fileCdnRedirect") {
|
|
167
197
|
throw new MtUnsupportedError("Received CDN redirect, which is not supported (yet)");
|
|
168
198
|
}
|
|
@@ -182,6 +212,7 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
182
212
|
}
|
|
183
213
|
};
|
|
184
214
|
let error;
|
|
215
|
+
resetStallTimer?.();
|
|
185
216
|
void Promise.all(Array.from({
|
|
186
217
|
length: Math.min(poolSize * (isSmall ? 1 : REQUESTS_PER_CONNECTION), numChunks)
|
|
187
218
|
}, downloadChunk)).catch((e) => {
|
|
@@ -199,20 +230,24 @@ async function* downloadAsIterable(client, input, params) {
|
|
|
199
230
|
nextChunkCv.notify();
|
|
200
231
|
});
|
|
201
232
|
let position = offset;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
233
|
+
try {
|
|
234
|
+
while (position < limitBytes) {
|
|
235
|
+
await nextChunkCv.wait();
|
|
236
|
+
if (error) throw error;
|
|
237
|
+
while (nextChunkIdx in buffer) {
|
|
238
|
+
const buf = buffer[nextChunkIdx];
|
|
239
|
+
delete buffer[nextChunkIdx];
|
|
240
|
+
position += buf.length;
|
|
241
|
+
params?.progressCallback?.(position, limitBytes);
|
|
242
|
+
yield buf;
|
|
243
|
+
nextChunkIdx++;
|
|
244
|
+
if (buf.length < chunkSize) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
214
247
|
}
|
|
215
248
|
}
|
|
249
|
+
} finally {
|
|
250
|
+
if (stallTimer) timers.clearTimeout(stallTimer);
|
|
216
251
|
}
|
|
217
252
|
}
|
|
218
253
|
export {
|
|
@@ -283,7 +283,7 @@ async function _normalizeInputMedia(client, media, params = {}, uploadMedia = fa
|
|
|
283
283
|
fileReference: res.photo.fileReference
|
|
284
284
|
},
|
|
285
285
|
ttlSeconds: media.ttlSeconds,
|
|
286
|
-
spoiler:
|
|
286
|
+
spoiler: "spoiler" in media ? media.spoiler : false,
|
|
287
287
|
livePhoto: Boolean(livePhotoVideo),
|
|
288
288
|
video: livePhotoVideo
|
|
289
289
|
};
|
|
@@ -276,7 +276,7 @@ async function _normalizeInputMedia(client, media, params = {}, uploadMedia = fa
|
|
|
276
276
|
fileReference: res.photo.fileReference
|
|
277
277
|
},
|
|
278
278
|
ttlSeconds: media.ttlSeconds,
|
|
279
|
-
spoiler:
|
|
279
|
+
spoiler: "spoiler" in media ? media.spoiler : false,
|
|
280
280
|
livePhoto: Boolean(livePhotoVideo),
|
|
281
281
|
video: livePhotoVideo
|
|
282
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, {
|
|
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, {
|
|
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;
|
|
@@ -8,7 +8,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
8
8
|
const utils = require("@fuman/utils");
|
|
9
9
|
const errors = require("../../../types/errors.cjs");
|
|
10
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");
|
|
11
13
|
const normalizeInputMedia = require("../files/normalize-input-media.cjs");
|
|
14
|
+
const resolvePeerMany = require("../users/resolve-peer-many.cjs");
|
|
12
15
|
function _richMediaCacheKey(media) {
|
|
13
16
|
if (typeof media === "string") return `auto:${media}`;
|
|
14
17
|
if ("_" in media) return void 0;
|
|
@@ -103,6 +106,13 @@ async function _normalizeInputRichMessage(client, peer, input, params) {
|
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
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;
|
|
106
116
|
const uploaded = await utils.parallelMap(
|
|
107
117
|
fileBlocks,
|
|
108
118
|
async ({ block, media }, idx) => {
|
|
@@ -151,7 +161,8 @@ async function _normalizeInputRichMessage(client, peer, input, params) {
|
|
|
151
161
|
noautolink: input.skipEntityDetection,
|
|
152
162
|
blocks,
|
|
153
163
|
photos: photos.length ? photos : void 0,
|
|
154
|
-
documents: documents.length ? documents : void 0
|
|
164
|
+
documents: documents.length ? documents : void 0,
|
|
165
|
+
users: users?.length ? users : void 0
|
|
155
166
|
};
|
|
156
167
|
}
|
|
157
168
|
exports._normalizeInputRichMessage = _normalizeInputRichMessage;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { parallelMap } from "@fuman/utils";
|
|
2
2
|
import { MtArgumentError, MtTypeAssertionError } from "../../../types/errors.js";
|
|
3
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";
|
|
4
6
|
import { _normalizeInputMedia } from "../files/normalize-input-media.js";
|
|
7
|
+
import { resolvePeerMany } from "../users/resolve-peer-many.js";
|
|
5
8
|
function _richMediaCacheKey(media) {
|
|
6
9
|
if (typeof media === "string") return `auto:${media}`;
|
|
7
10
|
if ("_" in media) return void 0;
|
|
@@ -96,6 +99,13 @@ async function _normalizeInputRichMessage(client, peer, input, params) {
|
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
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;
|
|
99
109
|
const uploaded = await parallelMap(
|
|
100
110
|
fileBlocks,
|
|
101
111
|
async ({ block, media }, idx) => {
|
|
@@ -144,7 +154,8 @@ async function _normalizeInputRichMessage(client, peer, input, params) {
|
|
|
144
154
|
noautolink: input.skipEntityDetection,
|
|
145
155
|
blocks,
|
|
146
156
|
photos: photos.length ? photos : void 0,
|
|
147
|
-
documents: documents.length ? documents : void 0
|
|
157
|
+
documents: documents.length ? documents : void 0,
|
|
158
|
+
users: users?.length ? users : void 0
|
|
148
159
|
};
|
|
149
160
|
}
|
|
150
161
|
export {
|
|
@@ -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,
|
|
@@ -93,4 +93,13 @@ export interface FileDownloadParameters {
|
|
|
93
93
|
* Abort signal that can be used to cancel the download.
|
|
94
94
|
*/
|
|
95
95
|
abortSignal?: AbortSignal;
|
|
96
|
+
/**
|
|
97
|
+
* If set, the download will be aborted (and an `MtTimeoutError` will be thrown)
|
|
98
|
+
* if no new chunk has been received within this amount of time (in milliseconds).
|
|
99
|
+
*
|
|
100
|
+
* Useful for bulk downloads where some files might be unavailable server-side
|
|
101
|
+
* (and would otherwise cause the download to hang indefinitely due to the server
|
|
102
|
+
* repeatedly responding with `FLOOD_WAIT` or `-503 Timeout`).
|
|
103
|
+
*/
|
|
104
|
+
stallTimeout?: number;
|
|
96
105
|
}
|
|
@@ -93,4 +93,13 @@ export interface FileDownloadParameters {
|
|
|
93
93
|
* Abort signal that can be used to cancel the download.
|
|
94
94
|
*/
|
|
95
95
|
abortSignal?: AbortSignal;
|
|
96
|
+
/**
|
|
97
|
+
* If set, the download will be aborted (and an `MtTimeoutError` will be thrown)
|
|
98
|
+
* if no new chunk has been received within this amount of time (in milliseconds).
|
|
99
|
+
*
|
|
100
|
+
* Useful for bulk downloads where some files might be unavailable server-side
|
|
101
|
+
* (and would otherwise cause the download to hang indefinitely due to the server
|
|
102
|
+
* repeatedly responding with `FLOOD_WAIT` or `-503 Timeout`).
|
|
103
|
+
*/
|
|
104
|
+
stallTimeout?: number;
|
|
96
105
|
}
|