@leemour/max-cli 0.14.0 → 0.15.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/README.md +43 -9
- package/dist/bot/client.d.ts +1 -1
- package/dist/bot/client.d.ts.map +1 -1
- package/dist/bot/client.js +2 -2
- package/dist/bot/client.js.map +1 -1
- package/dist/bot/keep.d.ts +25 -0
- package/dist/bot/keep.d.ts.map +1 -0
- package/dist/bot/keep.js +96 -0
- package/dist/bot/keep.js.map +1 -0
- package/dist/bot/registry.d.ts +3 -0
- package/dist/bot/registry.d.ts.map +1 -1
- package/dist/bot/registry.js +27 -10
- package/dist/bot/registry.js.map +1 -1
- package/dist/bot/uploads.d.ts +34 -0
- package/dist/bot/uploads.d.ts.map +1 -0
- package/dist/bot/uploads.js +122 -0
- package/dist/bot/uploads.js.map +1 -0
- package/dist/client.d.ts +19 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +76 -39
- package/dist/client.js.map +1 -1
- package/dist/commands/bot-comments.d.ts +4 -0
- package/dist/commands/bot-comments.d.ts.map +1 -0
- package/dist/commands/bot-comments.js +97 -0
- package/dist/commands/bot-comments.js.map +1 -0
- package/dist/commands/bot-context.d.ts +9 -3
- package/dist/commands/bot-context.d.ts.map +1 -1
- package/dist/commands/bot-context.js +38 -15
- package/dist/commands/bot-context.js.map +1 -1
- package/dist/commands/bot-members.d.ts +12 -0
- package/dist/commands/bot-members.d.ts.map +1 -0
- package/dist/commands/bot-members.js +136 -0
- package/dist/commands/bot-members.js.map +1 -0
- package/dist/commands/bot-reads.d.ts.map +1 -1
- package/dist/commands/bot-reads.js +50 -8
- package/dist/commands/bot-reads.js.map +1 -1
- package/dist/commands/bot-sends.d.ts +5 -0
- package/dist/commands/bot-sends.d.ts.map +1 -1
- package/dist/commands/bot-sends.js +56 -11
- package/dist/commands/bot-sends.js.map +1 -1
- package/dist/commands/bot-setup.d.ts +5 -0
- package/dist/commands/bot-setup.d.ts.map +1 -0
- package/dist/commands/bot-setup.js +107 -0
- package/dist/commands/bot-setup.js.map +1 -0
- package/dist/commands/bot-updates.d.ts +3 -0
- package/dist/commands/bot-updates.d.ts.map +1 -0
- package/dist/commands/bot-updates.js +182 -0
- package/dist/commands/bot-updates.js.map +1 -0
- package/dist/commands/bot.d.ts.map +1 -1
- package/dist/commands/bot.js +15 -0
- package/dist/commands/bot.js.map +1 -1
- package/dist/commands/chats.d.ts.map +1 -1
- package/dist/commands/chats.js +19 -6
- package/dist/commands/chats.js.map +1 -1
- package/dist/commands/check.d.ts +8 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +64 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/context.d.ts +5 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +5 -1
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/hearing.d.ts +25 -0
- package/dist/commands/hearing.d.ts.map +1 -0
- package/dist/commands/hearing.js +41 -0
- package/dist/commands/hearing.js.map +1 -0
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +19 -3
- package/dist/commands/inbox.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +7 -4
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/messages.d.ts.map +1 -1
- package/dist/commands/messages.js +21 -5
- package/dist/commands/messages.js.map +1 -1
- package/dist/commands/paging.d.ts +3 -1
- package/dist/commands/paging.d.ts.map +1 -1
- package/dist/commands/paging.js +4 -1
- package/dist/commands/paging.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +5 -7
- package/dist/commands/review.js.map +1 -1
- package/dist/domain/models.d.ts +4 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/domain/models.js.map +1 -1
- package/dist/generated/client.generated.d.ts +1 -0
- package/dist/generated/client.generated.d.ts.map +1 -1
- package/dist/generated/client.generated.js +1 -0
- package/dist/generated/client.generated.js.map +1 -1
- package/dist/generated/opcodes.generated.d.ts +1 -0
- package/dist/generated/opcodes.generated.d.ts.map +1 -1
- package/dist/generated/opcodes.generated.js +1 -0
- package/dist/generated/opcodes.generated.js.map +1 -1
- package/dist/generated/operations.generated.d.ts +13 -0
- package/dist/generated/operations.generated.d.ts.map +1 -1
- package/dist/generated/operations.generated.js +2 -1
- package/dist/generated/operations.generated.js.map +1 -1
- package/dist/mcp/instructions.d.ts +2 -1
- package/dist/mcp/instructions.d.ts.map +1 -1
- package/dist/mcp/instructions.js +2 -1
- package/dist/mcp/instructions.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +4 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +5 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +150 -11
- package/dist/mcp/tools.js.map +1 -1
- package/dist/moderation/check.d.ts +98 -0
- package/dist/moderation/check.d.ts.map +1 -0
- package/dist/moderation/check.js +306 -0
- package/dist/moderation/check.js.map +1 -0
- package/dist/review.d.ts +10 -4
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +35 -53
- package/dist/review.js.map +1 -1
- package/dist/sends/journal.d.ts +1 -1
- package/dist/sends/journal.d.ts.map +1 -1
- package/dist/session/store.d.ts +2 -0
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +2 -0
- package/dist/session/store.js.map +1 -1
- package/dist/spec/index.d.ts.map +1 -1
- package/dist/spec/index.js +2 -1
- package/dist/spec/index.js.map +1 -1
- package/dist/spec/operations/messages.d.ts.map +1 -1
- package/dist/spec/operations/messages.js +9 -4
- package/dist/spec/operations/messages.js.map +1 -1
- package/dist/spec/operations/uploads.d.ts +13 -0
- package/dist/spec/operations/uploads.d.ts.map +1 -1
- package/dist/spec/operations/uploads.js +21 -0
- package/dist/spec/operations/uploads.js.map +1 -1
- package/dist/transcribe/index.d.ts +36 -1
- package/dist/transcribe/index.d.ts.map +1 -1
- package/dist/transcribe/index.js +91 -14
- package/dist/transcribe/index.js.map +1 -1
- package/dist/upload.d.ts +3 -0
- package/dist/upload.d.ts.map +1 -1
- package/dist/upload.js +13 -7
- package/dist/upload.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/voice.d.ts +9 -0
- package/dist/voice.d.ts.map +1 -0
- package/dist/voice.js +36 -0
- package/dist/voice.js.map +1 -0
- package/package.json +5 -2
- package/skills/max-cli/SKILL.md +10 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CacheStore, PersonOrder, SyncSummary } from "./cache/store.js";
|
|
2
|
-
import type { AccountSession, AttachmentLink, Chat, ChatCard, ChatEvents, ChatKind, Contact, ContactImport, Deletion, Folder, GroupCard, GroupMembers, GroupSettings, Id, Inbox, Message, MessageChange, MessageHit, Page, PersonCard, Pin, Profile, Reactions, ReadMark, Review, WindowedMessage } from "./domain/models.js";
|
|
2
|
+
import type { AccountSession, AttachmentLink, Chat, ChatCard, ChatEvents, ChatKind, Contact, ContactImport, Deletion, Folder, GroupCard, GroupMember, GroupMembers, GroupSettings, Id, Inbox, Message, MessageChange, MessageHit, Page, PersonCard, Pin, Profile, Reactions, ReadMark, Review, WindowedMessage } from "./domain/models.js";
|
|
3
3
|
import { type Wire } from "./protocol/connection.js";
|
|
4
4
|
import { type Payload } from "./protocol/frame.js";
|
|
5
5
|
import { type DiagnosticEvent } from "./runs/events.js";
|
|
@@ -124,9 +124,17 @@ export declare class MaxClient {
|
|
|
124
124
|
* Who joined, left, was added or removed since a point: the service messages in the chat's
|
|
125
125
|
* history, read forward from `since` without reactions. At most `EVENTS_READ` messages, the oldest.
|
|
126
126
|
*/
|
|
127
|
-
events: (reference: string, { since }
|
|
128
|
-
since
|
|
127
|
+
events: (reference: string, { since }?: {
|
|
128
|
+
since?: number;
|
|
129
129
|
}) => Promise<ChatEvents>;
|
|
130
|
+
/**
|
|
131
|
+
* A chat's history after `since`, oldest first and without reactions, at most `EVENTS_READ`
|
|
132
|
+
* messages; `more` when there was more than that.
|
|
133
|
+
*/
|
|
134
|
+
since: (chatId: Id, since: number) => Promise<{
|
|
135
|
+
messages: Message[];
|
|
136
|
+
more: boolean;
|
|
137
|
+
}>;
|
|
130
138
|
/** What a link leads to, without joining it. */
|
|
131
139
|
inspect: (link: string) => Promise<GroupCard>;
|
|
132
140
|
join: (link: string) => Promise<GroupCard>;
|
|
@@ -160,7 +168,8 @@ export declare class MaxClient {
|
|
|
160
168
|
remove: (reference: string, person: string) => Promise<ChatChange>;
|
|
161
169
|
};
|
|
162
170
|
requests: {
|
|
163
|
-
|
|
171
|
+
/** Each with the age of their account, read like a member's — the answer's `{contact}` is the same shape. */
|
|
172
|
+
list: (reference: string) => Promise<GroupMember[]>;
|
|
164
173
|
accept: (reference: string, people: string[]) => Promise<ChatChange>;
|
|
165
174
|
decline: (reference: string, people: string[]) => Promise<ChatChange>;
|
|
166
175
|
};
|
|
@@ -345,6 +354,10 @@ export declare class MaxClient {
|
|
|
345
354
|
replyTo?: Id;
|
|
346
355
|
markdown?: boolean;
|
|
347
356
|
files?: string[];
|
|
357
|
+
/** Send every file as a plain file, a video included — how a video went before `MAX-23`. */
|
|
358
|
+
asFile?: boolean;
|
|
359
|
+
/** An Ogg Opus file sent as a voice message, alone in its message. */
|
|
360
|
+
voice?: string;
|
|
348
361
|
anyFile?: boolean;
|
|
349
362
|
at?: number;
|
|
350
363
|
}) => Promise<Message>;
|
|
@@ -543,6 +556,8 @@ export interface PageRequest {
|
|
|
543
556
|
* `undefined` for anything that is not a message id.
|
|
544
557
|
*/
|
|
545
558
|
export declare const timeOfMessageId: (id: Id) => number | undefined;
|
|
559
|
+
/** How far back `chats events` looks without `since`. */
|
|
560
|
+
export declare const EVENTS_DAYS = 7;
|
|
546
561
|
/** What `readLikeTab` sends back to MAX on the next login. */
|
|
547
562
|
export interface TabSync {
|
|
548
563
|
folders: number;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAc5E,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,OAAO,EACP,aAAa,EACb,QAAQ,EACR,MAAM,EACN,SAAS,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -14,7 +14,8 @@ import { loginPausedUntil, withLoginRefused, withoutLoginPause, } from "./sessio
|
|
|
14
14
|
import { WEB_USER_AGENT } from "./spec/identity.js";
|
|
15
15
|
import { buildRequest, checkResponse } from "./spec/index.js";
|
|
16
16
|
import { ASSET_TYPES } from "./spec/operations/assets.js";
|
|
17
|
-
import { isImage, readUpload, uploadFile, uploadPhoto } from "./upload.js";
|
|
17
|
+
import { isImage, isVideo, readUpload, uploadFile, uploadMedia, uploadPhoto } from "./upload.js";
|
|
18
|
+
import { voiceOf } from "./voice.js";
|
|
18
19
|
/**
|
|
19
20
|
* **The only thing above this line that knows MAX exists.** Commands speak the domain model; the
|
|
20
21
|
* opcodes and the frames stop here, so a different transport underneath changes this file and
|
|
@@ -209,41 +210,18 @@ export class MaxClient {
|
|
|
209
210
|
* come from the local copy.
|
|
210
211
|
*/
|
|
211
212
|
adminIds: async (chatId) => {
|
|
212
|
-
await this.#
|
|
213
|
-
|
|
214
|
-
if (!raw || (raw.owner === undefined && raw.admins === undefined && raw.adminParticipants === undefined)) {
|
|
215
|
-
return undefined;
|
|
216
|
-
}
|
|
217
|
-
const ids = [
|
|
218
|
-
asId(raw.owner),
|
|
219
|
-
...(Array.isArray(raw.admins) ? raw.admins.map(asId) : []),
|
|
220
|
-
...Object.keys(record(raw.adminParticipants) ?? {}),
|
|
221
|
-
];
|
|
222
|
-
return [...new Set(ids.filter((id) => id !== undefined))];
|
|
213
|
+
const roles = await this.#roles(chatId);
|
|
214
|
+
return roles && [...new Set([...(roles.owner ? [roles.owner] : []), ...roles.admins])];
|
|
223
215
|
},
|
|
224
216
|
/**
|
|
225
217
|
* Who joined, left, was added or removed since a point: the service messages in the chat's
|
|
226
218
|
* history, read forward from `since` without reactions. At most `EVENTS_READ` messages, the oldest.
|
|
227
219
|
*/
|
|
228
|
-
events: async (reference, { since }) => {
|
|
220
|
+
events: async (reference, { since = Date.now() - EVENTS_DAYS * 86_400_000 } = {}) => {
|
|
229
221
|
if (this.#offline)
|
|
230
222
|
throw new CliError("validation_error", "`--offline` has no history to read events from");
|
|
231
223
|
const chatId = await this.chats.resolve(reference);
|
|
232
|
-
const read =
|
|
233
|
-
let from = since;
|
|
234
|
-
let more = false;
|
|
235
|
-
while (true) {
|
|
236
|
-
const page = (await this.#history(chatId, { from, backward: 0, forward: REVIEW_PAGE + 1 }, { reactions: false })).filter((message) => Date.parse(message.timestamp) > from);
|
|
237
|
-
read.push(...page);
|
|
238
|
-
const last = page.at(-1);
|
|
239
|
-
if (page.length < REVIEW_PAGE || !last)
|
|
240
|
-
break;
|
|
241
|
-
if (read.length >= EVENTS_READ) {
|
|
242
|
-
more = true;
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
from = Date.parse(last.timestamp);
|
|
246
|
-
}
|
|
224
|
+
const { messages: read, more } = await this.chats.since(chatId, since);
|
|
247
225
|
const found = read.flatMap((message) => message.attachments
|
|
248
226
|
.filter((attachment) => attachment.kind === "control" && attachment.event !== undefined)
|
|
249
227
|
.map((attachment) => ({ message, attachment })));
|
|
@@ -270,6 +248,24 @@ export class MaxClient {
|
|
|
270
248
|
})),
|
|
271
249
|
};
|
|
272
250
|
},
|
|
251
|
+
/**
|
|
252
|
+
* A chat's history after `since`, oldest first and without reactions, at most `EVENTS_READ`
|
|
253
|
+
* messages; `more` when there was more than that.
|
|
254
|
+
*/
|
|
255
|
+
since: async (chatId, since) => {
|
|
256
|
+
const read = [];
|
|
257
|
+
let from = since;
|
|
258
|
+
while (true) {
|
|
259
|
+
const page = (await this.#history(chatId, { from, backward: 0, forward: REVIEW_PAGE + 1 }, { reactions: false })).filter((message) => Date.parse(message.timestamp) > from);
|
|
260
|
+
read.push(...page);
|
|
261
|
+
const last = page.at(-1);
|
|
262
|
+
if (page.length < REVIEW_PAGE || !last)
|
|
263
|
+
return { messages: read, more: false };
|
|
264
|
+
if (read.length >= EVENTS_READ)
|
|
265
|
+
return { messages: read, more: true };
|
|
266
|
+
from = Date.parse(last.timestamp);
|
|
267
|
+
}
|
|
268
|
+
},
|
|
273
269
|
/** What a link leads to, without joining it. */
|
|
274
270
|
inspect: async (link) => {
|
|
275
271
|
if (this.#offline)
|
|
@@ -367,7 +363,14 @@ export class MaxClient {
|
|
|
367
363
|
}
|
|
368
364
|
marker = next;
|
|
369
365
|
}
|
|
370
|
-
|
|
366
|
+
const roles = await this.#roles(chatId);
|
|
367
|
+
const role = (id) => (id === roles?.owner ? "owner" : roles?.admins.has(id) ? "admin" : "member");
|
|
368
|
+
return {
|
|
369
|
+
chatId,
|
|
370
|
+
members: [...members.values()].map((member) => (roles ? { ...member, role: role(member.id) } : member)),
|
|
371
|
+
complete,
|
|
372
|
+
rolesKnown: roles !== undefined,
|
|
373
|
+
};
|
|
371
374
|
},
|
|
372
375
|
/** No history unless asked (`NEED-272`): what was said before somebody joined is not theirs by default. */
|
|
373
376
|
add: (reference, people, { history = false } = {}) => this.#updateMembers(reference, people, "members.add", { operation: "add", showHistory: history }),
|
|
@@ -382,13 +385,14 @@ export class MaxClient {
|
|
|
382
385
|
remove: (reference, person) => this.#updateMembers(reference, [person], "admins.remove", { operation: "remove", type: "ADMIN" }),
|
|
383
386
|
},
|
|
384
387
|
requests: {
|
|
388
|
+
/** Each with the age of their account, read like a member's — the answer's `{contact}` is the same shape. */
|
|
385
389
|
list: async (reference) => {
|
|
386
390
|
if (this.#offline)
|
|
387
391
|
throw new CliError("validation_error", "`--offline` reads what was recorded; join requests never are");
|
|
388
392
|
const chatId = await this.chats.resolve(reference);
|
|
389
393
|
await this.#connectOnce();
|
|
390
394
|
const answer = await this.#wire.chats.members({ chatId, type: "JOIN_REQUEST", count: JOIN_REQUESTS });
|
|
391
|
-
return asArray(answer.members).map(
|
|
395
|
+
return asArray(answer.members).map(toGroupMember);
|
|
392
396
|
},
|
|
393
397
|
accept: (reference, people) => this.#updateMembers(reference, people, "requests.accept", {
|
|
394
398
|
operation: "add",
|
|
@@ -791,6 +795,10 @@ export class MaxClient {
|
|
|
791
795
|
send: async (chatId, text, options = {}) => {
|
|
792
796
|
if (this.#offline)
|
|
793
797
|
throw new CliError("validation_error", "`--offline` reads what was recorded; it cannot send");
|
|
798
|
+
if (options.voice !== undefined && (text !== "" || (options.files ?? []).length > 0)) {
|
|
799
|
+
// MAX kept a caption beside a voice message (measured 2026-09-27), but web.max.ru never sends one; §34.
|
|
800
|
+
throw new CliError("validation_error", "a voice message goes alone — no text and no --file beside it");
|
|
801
|
+
}
|
|
794
802
|
if (options.at !== undefined && options.notify === false) {
|
|
795
803
|
// The web client always sends a scheduled message with `notify: true`; §34.
|
|
796
804
|
throw new CliError("validation_error", "a scheduled message cannot be silent — MAX's own client never sends one");
|
|
@@ -802,11 +810,16 @@ export class MaxClient {
|
|
|
802
810
|
const files = await Promise.all((options.files ?? []).map(async (path) => ({
|
|
803
811
|
path,
|
|
804
812
|
bytes: await readUpload(path, { anyFile: options.anyFile === true }),
|
|
805
|
-
|
|
813
|
+
kind: isImage(path) ? "photo" : isVideo(path) && options.asFile !== true ? "video" : "file",
|
|
806
814
|
})));
|
|
815
|
+
if (options.voice !== undefined) {
|
|
816
|
+
const bytes = await readUpload(options.voice, { anyFile: options.anyFile === true });
|
|
817
|
+
files.push({ path: options.voice, bytes, kind: "voice", voice: await voiceOf(options.voice, bytes) });
|
|
818
|
+
}
|
|
807
819
|
// Measured 2026-09-24: photos share a message, but a file with anything beside it is refused `proto.payload`.
|
|
808
|
-
|
|
809
|
-
|
|
820
|
+
// A video is kept alone the same way; a mixed message was never measured.
|
|
821
|
+
if (files.some((file) => file.kind !== "photo") && files.length > 1) {
|
|
822
|
+
throw new CliError("validation_error", "a file or a video goes in a message of its own — photos can share one; send them apart");
|
|
810
823
|
}
|
|
811
824
|
// Before connecting: a refused send never opens a socket when the chat was given as an id.
|
|
812
825
|
try {
|
|
@@ -822,10 +835,7 @@ export class MaxClient {
|
|
|
822
835
|
throw error;
|
|
823
836
|
}
|
|
824
837
|
const cid = options.cid ?? this.#nextCid();
|
|
825
|
-
const attachments = files.map(({ bytes,
|
|
826
|
-
kind: photo ? "photo" : "file",
|
|
827
|
-
bytes: bytes.length,
|
|
828
|
-
}));
|
|
838
|
+
const attachments = files.map(({ bytes, kind }) => ({ kind, bytes: bytes.length }));
|
|
829
839
|
const summary = {
|
|
830
840
|
...(attachments.length > 0 ? { attachments } : {}),
|
|
831
841
|
...(options.at === undefined ? {} : { scheduledFor: new Date(options.at).toISOString() }),
|
|
@@ -1703,6 +1713,20 @@ export class MaxClient {
|
|
|
1703
1713
|
forwardedFrom: message.forwardedFrom && name(message.forwardedFrom),
|
|
1704
1714
|
}));
|
|
1705
1715
|
}
|
|
1716
|
+
/** The chat's owner and admins as the login carried them; `undefined` when it did not. */
|
|
1717
|
+
async #roles(chatId) {
|
|
1718
|
+
await this.#connectOnce();
|
|
1719
|
+
const raw = asArray(this.#session().chats).find((chat) => asId(chat.id) === chatId);
|
|
1720
|
+
if (!raw || (raw.owner === undefined && raw.admins === undefined && raw.adminParticipants === undefined)) {
|
|
1721
|
+
return undefined;
|
|
1722
|
+
}
|
|
1723
|
+
const admins = [
|
|
1724
|
+
...(Array.isArray(raw.admins) ? raw.admins.map(asId) : []),
|
|
1725
|
+
...Object.keys(record(raw.adminParticipants) ?? {}),
|
|
1726
|
+
].filter((id) => id !== undefined);
|
|
1727
|
+
const owner = asId(raw.owner);
|
|
1728
|
+
return { ...(owner ? { owner } : {}), admins: new Set(admins.filter((id) => id !== owner)) };
|
|
1729
|
+
}
|
|
1706
1730
|
/** Names we hold first, the rest from `CONTACT_INFO`, kept for next time. A refusal costs the names only. */
|
|
1707
1731
|
async #namesOf(ids) {
|
|
1708
1732
|
const names = this.#cache?.people.names(ids) ?? new Map();
|
|
@@ -1832,14 +1856,25 @@ export class MaxClient {
|
|
|
1832
1856
|
* Uploads one file and answers what the message attaches (measured 2026-09-24). An upload is never
|
|
1833
1857
|
* retried: a failure here happens before `MSG_SEND`, so nothing was sent.
|
|
1834
1858
|
*/
|
|
1835
|
-
async #upload({ path, bytes,
|
|
1859
|
+
async #upload({ path, bytes, kind, voice }) {
|
|
1836
1860
|
const request = { count: 1, type: 0, uploaderType: 0, profile: false };
|
|
1837
|
-
if (photo) {
|
|
1861
|
+
if (kind === "photo") {
|
|
1838
1862
|
const { url } = await this.#wire.uploads.photo(request);
|
|
1839
1863
|
if (typeof url !== "string")
|
|
1840
1864
|
throw new CliError("provider_error", "MAX gave no address to upload the photo to");
|
|
1841
1865
|
return { _type: "PHOTO", photoToken: await uploadPhoto(url, path, bytes) };
|
|
1842
1866
|
}
|
|
1867
|
+
if (kind === "video" || kind === "voice") {
|
|
1868
|
+
const slot = voice ? { ...request, type: 2, uploaderType: 1 } : request;
|
|
1869
|
+
const info = record(asArray((await this.#wire.uploads.video(slot)).info)[0]) ?? {};
|
|
1870
|
+
if (typeof info.url !== "string" || info.videoId === undefined) {
|
|
1871
|
+
throw new CliError("provider_error", `MAX gave no address to upload the ${kind} to`);
|
|
1872
|
+
}
|
|
1873
|
+
await uploadMedia(voice ? "voice message" : "video", info.url, path, bytes);
|
|
1874
|
+
return voice
|
|
1875
|
+
? { _type: "AUDIO", audioId: info.videoId, duration: voice.durationMs, wave: voice.wave, token: info.token }
|
|
1876
|
+
: { _type: "VIDEO", videoId: info.videoId, token: info.token, videoType: 0 };
|
|
1877
|
+
}
|
|
1843
1878
|
const info = record(asArray((await this.#wire.uploads.file(request)).info)[0]) ?? {};
|
|
1844
1879
|
if (typeof info.url !== "string" || info.fileId === undefined) {
|
|
1845
1880
|
throw new CliError("provider_error", "MAX gave no address to upload the file to");
|
|
@@ -2161,6 +2196,8 @@ const REVIEW_CHATS = 50;
|
|
|
2161
2196
|
const REVIEW_PAGE = 100;
|
|
2162
2197
|
const REVIEW_PER_CHAT = 500;
|
|
2163
2198
|
const EVENTS_READ = 2000;
|
|
2199
|
+
/** How far back `chats events` looks without `since`. */
|
|
2200
|
+
export const EVENTS_DAYS = 7;
|
|
2164
2201
|
export const FIRST_TAB_SYNC = { folders: 0, calls: 0, assets: {} };
|
|
2165
2202
|
const numberOr = (value, fallback) => (typeof value === "number" ? value : fallback);
|
|
2166
2203
|
const eventTime = (chat) => {
|