@craftsamo/telegram-tools 0.1.0 → 0.2.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/dist/cli.js CHANGED
@@ -20229,6 +20229,7 @@ function acquireSyncLock(path) {
20229
20229
  }
20230
20230
 
20231
20231
  // src/core/time.ts
20232
+ var MS_PER_S = 1e3;
20232
20233
  var MS_PER_MINUTE = 6e4;
20233
20234
  var MINUTES_PER_HOUR = 60;
20234
20235
  var localOffsetMinutes = (ms) => -new Date(ms).getTimezoneOffset();
@@ -21229,7 +21230,6 @@ async function callService(socketPath, method, params, options) {
21229
21230
  // src/core/chat/live.ts
21230
21231
  var REFRESH_TIMEOUT_MS = 12e4;
21231
21232
  var FETCH_TIMEOUT_MS = 18e4;
21232
- var MS_PER_S = 1e3;
21233
21233
  function requestHistory(socket, params, signal) {
21234
21234
  return callService(socket, SERVICE_REQUESTS.history, params, { effect: "read", signal });
21235
21235
  }
@@ -21357,113 +21357,6 @@ var messageEntry = external_exports.object({
21357
21357
  private_note: external_exports.string().optional()
21358
21358
  });
21359
21359
 
21360
- // src/core/chat/schema.ts
21361
- var METHODS = ["list", "view", "topics", "search", "around", "fetch"];
21362
- var GENERAL_TOPIC2 = 1;
21363
- var LIMITS = {
21364
- list: { default: 30, max: 200 },
21365
- view: { default: 50, max: 300 },
21366
- topics: { default: 30, max: TOPICS_LIMIT },
21367
- search: { default: 30, max: 200 }
21368
- };
21369
- var AROUND_COUNT = { default: 5, max: AROUND_LIMIT };
21370
- var FETCH_PAGES = { default: 1, max: FETCH_PAGE_LIMIT };
21371
- var accountArgument = external_exports.string().optional().describe("The account label; may be left out when only one account is logged in.");
21372
- var input3 = external_exports.object({
21373
- method: external_exports.enum(METHODS).describe(
21374
- "`list` finds chats; `view` reads the newest messages of one chat between two times, oldest first; `topics` lists the topics of a forum chat; `search` finds messages in the mirror by words; `around` reads the messages around one; `fetch` adds older history of a mirrored chat to the mirror."
21375
- ),
21376
- account: accountArgument,
21377
- query: external_exports.string().optional().describe(
21378
- "list: only chats whose name or username holds this text; a leading @ is ignored. search (required): the words to look for, all of which a message must hold in its text, caption or file name, in any order. Both ignore case and letter width."
21379
- ),
21380
- unread: external_exports.boolean().optional().describe("list: only chats with unread messages."),
21381
- last: external_exports.boolean().optional().describe("list: add each chat's newest mirrored message as `last_message`."),
21382
- refresh: external_exports.boolean().optional().describe(
21383
- "list: first take the chat list from Telegram (needs the sync service), then read it, and report when in `refreshed_at`."
21384
- ),
21385
- chat: external_exports.string().optional().describe(
21386
- "view, topics, around, fetch (required) and search (optional, to search one chat): the chat id, from `list`, such as 123456789 or -1001234567890. A name, username or phone number is not a chat."
21387
- ),
21388
- topic: external_exports.number().int().positive().optional().describe(
21389
- `view, around: only messages of this topic of a forum chat (ids from \`topics\`); the General topic is ${GENERAL_TOPIC2}. Refused for a chat without topics.`
21390
- ),
21391
- after: external_exports.string().optional().describe(
21392
- "view, search: only messages after this time (exclusive): RFC 3339 with an offset, or a date or time without one, read as local time, such as 2026-11-07 or 2026-11-07T14:03."
21393
- ),
21394
- before: external_exports.string().optional().describe("view, search: only messages before this time (exclusive); the same forms as `after`."),
21395
- limit: external_exports.number().int().optional().describe(
21396
- `How many to return. list: default ${LIMITS.list.default}, at most ${LIMITS.list.max}. view: default ${LIMITS.view.default}, at most ${LIMITS.view.max} (${HISTORY_LIMIT} for a chat read live). topics: default ${LIMITS.topics.default}, at most ${LIMITS.topics.max}. search: default ${LIMITS.search.default}, at most ${LIMITS.search.max}. A larger value is lowered to the most; a result may hold fewer to stay a size a model can read.`
21397
- ),
21398
- cursor: external_exports.string().optional().describe("The `next` of the previous result, to continue where it stopped."),
21399
- id: external_exports.number().int().positive().optional().describe("around (required): the id of the message to read around."),
21400
- before_count: external_exports.number().int().optional().describe(
21401
- `around: how many older messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21402
- ),
21403
- after_count: external_exports.number().int().optional().describe(
21404
- `around: how many newer messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21405
- ),
21406
- pages: external_exports.number().int().optional().describe(
21407
- `fetch: how many pages of ${FETCH_PAGE_SIZE} older messages to add, default ${FETCH_PAGES.default}, at most ${FETCH_PAGES.max}.`
21408
- )
21409
- });
21410
- var nameCut = external_exports.literal(true).optional().describe("Set when the name was longer than the most a result carries and was cut.");
21411
- var chatEntry2 = external_exports.object({
21412
- id: external_exports.string().describe("The chat id: Telegram's signed id as text, such as `123456789` or `-1001234567890`."),
21413
- kind: external_exports.enum(CHAT_KINDS).describe("`self` is Saved Messages."),
21414
- name: external_exports.string().nullable().describe("The person's or chat's name; null when none is known."),
21415
- name_cut: nameCut,
21416
- username: external_exports.string().optional(),
21417
- mirrored: external_exports.boolean().describe("Whether the mirror keeps this chat's messages."),
21418
- unread: external_exports.number().int().describe("Incoming messages not read yet."),
21419
- last_activity_at: localTimeSchema.nullable().describe("The latest message's time, in local time with offset; null when none is known."),
21420
- forum: external_exports.literal(true).optional().describe("Set for a supergroup with topics."),
21421
- archived: external_exports.literal(true).optional(),
21422
- left: external_exports.literal(true).optional().describe("Set for a group or channel the user has left or been removed from."),
21423
- can_send: external_exports.literal(false).optional().describe("Set when the user is known not to be able to write here."),
21424
- auto_delete_seconds: external_exports.number().int().optional().describe("The chat's auto-delete period, when on."),
21425
- last_message: messageEntry.nullable().optional().describe("With `last`: the newest mirrored message of the chat, or null when none is mirrored.")
21426
- });
21427
- var topicEntry = external_exports.object({
21428
- id: external_exports.number().int().describe(`The topic id, to pass as \`topic\` to \`view\`; ${GENERAL_TOPIC2} is the General topic.`),
21429
- title: external_exports.string().nullable().describe("The topic's title; null when none is known."),
21430
- closed: external_exports.literal(true).optional().describe("Set for a topic that is closed."),
21431
- pinned: external_exports.literal(true).optional().describe("Set for a topic that is pinned."),
21432
- unread: external_exports.number().int().describe("Messages in it not read yet."),
21433
- last_activity_at: localTimeSchema.nullable().describe("The newest message's time, in local time with offset; null when none is known.")
21434
- });
21435
- var output3 = external_exports.object({
21436
- chats: external_exports.array(chatEntry2).optional().describe("list: the chats, newest activity first."),
21437
- refreshed_at: localTimeSchema.optional().describe("list with `refresh`: when the chat list was taken from Telegram, in local time with offset."),
21438
- chat: chatEntry2.optional().describe("view, topics, around, fetch: the chat read."),
21439
- messages: external_exports.array(messageEntry).optional().describe("view, around: the messages, oldest first. search: the hits, newest first."),
21440
- live: external_exports.literal(true).optional().describe(
21441
- "view, around: set when the chat is not mirrored, so the messages were read from Telegram and stored nowhere."
21442
- ),
21443
- topics: external_exports.array(topicEntry).optional().describe("topics: the topics, most recently active first."),
21444
- anchor: external_exports.number().int().optional().describe("around: the id of the message read around, among `messages`."),
21445
- trimmed_before: external_exports.number().int().optional().describe(
21446
- "around: how many older messages were left out, farthest first, to keep the result a size that can be read."
21447
- ),
21448
- trimmed_after: external_exports.number().int().optional().describe(
21449
- "around: how many newer messages were left out, farthest first, to keep the result a size that can be read."
21450
- ),
21451
- scanned_to: localTimeSchema.optional().describe(
21452
- "search: set when the search stopped before the end of the history it covers: the time of the oldest message looked at, in local time with offset. `next` continues from there."
21453
- ),
21454
- scope: external_exports.string().optional().describe("search: what the search covers and what it does not."),
21455
- fetched: external_exports.object({
21456
- added: external_exports.number().int().describe("How many messages the mirror gained."),
21457
- pages: external_exports.number().int().describe("How many pages were read."),
21458
- complete: external_exports.boolean().describe("Whether Telegram has nothing older for this chat."),
21459
- oldest_at: localTimeSchema.nullable().describe(
21460
- "The time of the oldest message the mirror now holds of this chat, in local time with offset; null when none."
21461
- )
21462
- }).optional().describe("fetch: what the history fetch stored."),
21463
- next: external_exports.string().optional().describe("Pass back as `cursor` to continue; absent when there is no more."),
21464
- note: external_exports.string().describe("Says that what the result carries is data written by other people.")
21465
- });
21466
-
21467
21360
  // src/core/chat/message-rows.ts
21468
21361
  var MESSAGE_COLUMNS = "chat, id, topic, ts, from_me, sender, sender_name, kind, body, entities, media, action, reply, forward, grouped, reactions, edited, expires, view_once, self_destruct";
21469
21362
  function liveMessages(now) {
@@ -21471,7 +21364,7 @@ function liveMessages(now) {
21471
21364
  }
21472
21365
  function inTopic(topic) {
21473
21366
  return {
21474
- condition: topic === GENERAL_TOPIC2 ? "(topic IS NULL OR topic = ?)" : "topic = ?",
21367
+ condition: topic === GENERAL_TOPIC ? "(topic IS NULL OR topic = ?)" : "topic = ?",
21475
21368
  params: [topic]
21476
21369
  };
21477
21370
  }
@@ -21696,6 +21589,112 @@ var cuttableMessage = {
21696
21589
  drop: dropMetadata
21697
21590
  };
21698
21591
 
21592
+ // src/core/chat/schema.ts
21593
+ var METHODS = ["list", "view", "topics", "search", "around", "fetch"];
21594
+ var LIMITS = {
21595
+ list: { default: 30, max: 200 },
21596
+ view: { default: 50, max: 300 },
21597
+ topics: { default: 30, max: TOPICS_LIMIT },
21598
+ search: { default: 30, max: 200 }
21599
+ };
21600
+ var AROUND_COUNT = { default: 5, max: AROUND_LIMIT };
21601
+ var FETCH_PAGES = { default: 1, max: FETCH_PAGE_LIMIT };
21602
+ var accountArgument = external_exports.string().optional().describe("The account label; may be left out when only one account is logged in.");
21603
+ var input3 = external_exports.object({
21604
+ method: external_exports.enum(METHODS).describe(
21605
+ "`list` finds chats; `view` reads the newest messages of one chat between two times, oldest first; `topics` lists the topics of a forum chat; `search` finds messages in the mirror by words; `around` reads the messages around one; `fetch` adds older history of a mirrored chat to the mirror."
21606
+ ),
21607
+ account: accountArgument,
21608
+ query: external_exports.string().optional().describe(
21609
+ "list: only chats whose name or username holds this text; a leading @ is ignored. search (required): the words to look for, all of which a message must hold in its text, caption or file name, in any order. Both ignore case and letter width."
21610
+ ),
21611
+ unread: external_exports.boolean().optional().describe("list: only chats with unread messages."),
21612
+ last: external_exports.boolean().optional().describe("list: add each chat's newest mirrored message as `last_message`."),
21613
+ refresh: external_exports.boolean().optional().describe(
21614
+ "list: first take the chat list from Telegram (needs the sync service), then read it, and report when in `refreshed_at`."
21615
+ ),
21616
+ chat: external_exports.string().optional().describe(
21617
+ "view, topics, around, fetch (required) and search (optional, to search one chat): the chat id, from `list`, such as 123456789 or -1001234567890. A name, username or phone number is not a chat."
21618
+ ),
21619
+ topic: external_exports.number().int().positive().optional().describe(
21620
+ `view, around: only messages of this topic of a forum chat (ids from \`topics\`); the General topic is ${GENERAL_TOPIC}. Refused for a chat without topics.`
21621
+ ),
21622
+ after: external_exports.string().optional().describe(
21623
+ "view, search: only messages after this time (exclusive): RFC 3339 with an offset, or a date or time without one, read as local time, such as 2026-11-07 or 2026-11-07T14:03."
21624
+ ),
21625
+ before: external_exports.string().optional().describe("view, search: only messages before this time (exclusive); the same forms as `after`."),
21626
+ limit: external_exports.number().int().optional().describe(
21627
+ `How many to return. list: default ${LIMITS.list.default}, at most ${LIMITS.list.max}. view: default ${LIMITS.view.default}, at most ${LIMITS.view.max} (${HISTORY_LIMIT} for a chat read live). topics: default ${LIMITS.topics.default}, at most ${LIMITS.topics.max}. search: default ${LIMITS.search.default}, at most ${LIMITS.search.max}. A larger value is lowered to the most; a result may hold fewer to stay a size a model can read.`
21628
+ ),
21629
+ cursor: external_exports.string().optional().describe("The `next` of the previous result, to continue where it stopped."),
21630
+ id: external_exports.number().int().positive().optional().describe("around (required): the id of the message to read around."),
21631
+ before_count: external_exports.number().int().optional().describe(
21632
+ `around: how many older messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21633
+ ),
21634
+ after_count: external_exports.number().int().optional().describe(
21635
+ `around: how many newer messages to return, default ${AROUND_COUNT.default}, at most ${AROUND_COUNT.max}; 0 asks for none.`
21636
+ ),
21637
+ pages: external_exports.number().int().optional().describe(
21638
+ `fetch: how many pages of ${FETCH_PAGE_SIZE} older messages to add, default ${FETCH_PAGES.default}, at most ${FETCH_PAGES.max}.`
21639
+ )
21640
+ });
21641
+ var nameCut = external_exports.literal(true).optional().describe("Set when the name was longer than the most a result carries and was cut.");
21642
+ var chatEntry2 = external_exports.object({
21643
+ id: external_exports.string().describe("The chat id: Telegram's signed id as text, such as `123456789` or `-1001234567890`."),
21644
+ kind: external_exports.enum(CHAT_KINDS).describe("`self` is Saved Messages."),
21645
+ name: external_exports.string().nullable().describe("The person's or chat's name; null when none is known."),
21646
+ name_cut: nameCut,
21647
+ username: external_exports.string().optional(),
21648
+ mirrored: external_exports.boolean().describe("Whether the mirror keeps this chat's messages."),
21649
+ unread: external_exports.number().int().describe("Incoming messages not read yet."),
21650
+ last_activity_at: localTimeSchema.nullable().describe("The latest message's time, in local time with offset; null when none is known."),
21651
+ forum: external_exports.literal(true).optional().describe("Set for a supergroup with topics."),
21652
+ archived: external_exports.literal(true).optional(),
21653
+ left: external_exports.literal(true).optional().describe("Set for a group or channel the user has left or been removed from."),
21654
+ can_send: external_exports.literal(false).optional().describe("Set when the user is known not to be able to write here."),
21655
+ auto_delete_seconds: external_exports.number().int().optional().describe("The chat's auto-delete period, when on."),
21656
+ last_message: messageEntry.nullable().optional().describe("With `last`: the newest mirrored message of the chat, or null when none is mirrored.")
21657
+ });
21658
+ var topicEntry = external_exports.object({
21659
+ id: external_exports.number().int().describe(`The topic id, to pass as \`topic\` to \`view\`; ${GENERAL_TOPIC} is the General topic.`),
21660
+ title: external_exports.string().nullable().describe("The topic's title; null when none is known."),
21661
+ closed: external_exports.literal(true).optional().describe("Set for a topic that is closed."),
21662
+ pinned: external_exports.literal(true).optional().describe("Set for a topic that is pinned."),
21663
+ unread: external_exports.number().int().describe("Messages in it not read yet."),
21664
+ last_activity_at: localTimeSchema.nullable().describe("The newest message's time, in local time with offset; null when none is known.")
21665
+ });
21666
+ var output3 = external_exports.object({
21667
+ chats: external_exports.array(chatEntry2).optional().describe("list: the chats, newest activity first."),
21668
+ refreshed_at: localTimeSchema.optional().describe("list with `refresh`: when the chat list was taken from Telegram, in local time with offset."),
21669
+ chat: chatEntry2.optional().describe("view, topics, around, fetch: the chat read."),
21670
+ messages: external_exports.array(messageEntry).optional().describe("view, around: the messages, oldest first. search: the hits, newest first."),
21671
+ live: external_exports.literal(true).optional().describe(
21672
+ "view, around: set when the chat is not mirrored, so the messages were read from Telegram and stored nowhere."
21673
+ ),
21674
+ topics: external_exports.array(topicEntry).optional().describe("topics: the topics, most recently active first."),
21675
+ anchor: external_exports.number().int().optional().describe("around: the id of the message read around, among `messages`."),
21676
+ trimmed_before: external_exports.number().int().optional().describe(
21677
+ "around: how many older messages were left out, farthest first, to keep the result a size that can be read."
21678
+ ),
21679
+ trimmed_after: external_exports.number().int().optional().describe(
21680
+ "around: how many newer messages were left out, farthest first, to keep the result a size that can be read."
21681
+ ),
21682
+ scanned_to: localTimeSchema.optional().describe(
21683
+ "search: set when the search stopped before the end of the history it covers: the time of the oldest message looked at, in local time with offset. `next` continues from there."
21684
+ ),
21685
+ scope: external_exports.string().optional().describe("search: what the search covers and what it does not."),
21686
+ fetched: external_exports.object({
21687
+ added: external_exports.number().int().describe("How many messages the mirror gained."),
21688
+ pages: external_exports.number().int().describe("How many pages were read."),
21689
+ complete: external_exports.boolean().describe("Whether Telegram has nothing older for this chat."),
21690
+ oldest_at: localTimeSchema.nullable().describe(
21691
+ "The time of the oldest message the mirror now holds of this chat, in local time with offset; null when none."
21692
+ )
21693
+ }).optional().describe("fetch: what the history fetch stored."),
21694
+ next: external_exports.string().optional().describe("Pass back as `cursor` to continue; absent when there is no more."),
21695
+ note: external_exports.string().describe("Says that what the result carries is data written by other people.")
21696
+ });
21697
+
21699
21698
  // src/core/chat/around.ts
21700
21699
  var TRIMMED_RESERVE = 100;
21701
21700
  var NO_MESSAGE = "No message with this id in this chat.";
@@ -21898,7 +21897,7 @@ function listChats(mirror, args, now) {
21898
21897
  return result;
21899
21898
  }
21900
21899
 
21901
- // src/core/chat/open.ts
21900
+ // src/core/open.ts
21902
21901
  async function pickPaths(ctx, account2) {
21903
21902
  if (!ctx.stateDir) throw new ToolError("internal_error", "The entry point did not supply a state directory.");
21904
21903
  return pickLabel(ctx.stateDir, account2);
@@ -22318,6 +22317,56 @@ var CHAT_FACT_TRANSLATIONS = {
22318
22317
  }
22319
22318
  };
22320
22319
 
22320
+ // src/core/chat/media-facts.ts
22321
+ var MEDIA_KIND_WORDS = {
22322
+ photo: "Photo",
22323
+ document: "Document",
22324
+ audio: "Audio",
22325
+ voice: "Voice message",
22326
+ video: "Video",
22327
+ sticker: "Sticker",
22328
+ dice: "Dice",
22329
+ contact: "Contact",
22330
+ venue: "Venue",
22331
+ geo: "Location",
22332
+ geo_live: "Live location",
22333
+ game: "Game",
22334
+ invoice: "Invoice",
22335
+ webpage: "Link preview",
22336
+ poll: "Poll",
22337
+ todo: "Checklist",
22338
+ story: "Story",
22339
+ paid: "Paid media",
22340
+ unsupported: "Unsupported media"
22341
+ };
22342
+ function mediaKindValue(type3) {
22343
+ return Object.hasOwn(MEDIA_KIND_WORDS, type3) ? { text: MEDIA_KIND_WORDS[type3] } : type3;
22344
+ }
22345
+ var MEDIA_FACT_TRANSLATIONS = {
22346
+ ja: {
22347
+ Media: "\u30E1\u30C7\u30A3\u30A2",
22348
+ Photo: "\u5199\u771F",
22349
+ Document: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
22350
+ Audio: "\u97F3\u58F0",
22351
+ "Voice message": "\u30DC\u30A4\u30B9\u30E1\u30C3\u30BB\u30FC\u30B8",
22352
+ Video: "\u52D5\u753B",
22353
+ Sticker: "\u30B9\u30BF\u30F3\u30D7",
22354
+ Dice: "\u30B5\u30A4\u30B3\u30ED",
22355
+ Contact: "\u9023\u7D61\u5148",
22356
+ Venue: "\u5834\u6240",
22357
+ Location: "\u4F4D\u7F6E\u60C5\u5831",
22358
+ "Live location": "\u30E9\u30A4\u30D6\u4F4D\u7F6E\u60C5\u5831",
22359
+ Game: "\u30B2\u30FC\u30E0",
22360
+ Invoice: "\u8ACB\u6C42\u66F8",
22361
+ "Link preview": "\u30EA\u30F3\u30AF\u306E\u30D7\u30EC\u30D3\u30E5\u30FC",
22362
+ Poll: "\u6295\u7968",
22363
+ Checklist: "\u30C1\u30A7\u30C3\u30AF\u30EA\u30B9\u30C8",
22364
+ Story: "\u30B9\u30C8\u30FC\u30EA\u30FC",
22365
+ "Paid media": "\u6709\u6599\u30E1\u30C7\u30A3\u30A2",
22366
+ "Unsupported media": "\u672A\u5BFE\u5FDC\u306E\u30E1\u30C7\u30A3\u30A2"
22367
+ }
22368
+ };
22369
+
22321
22370
  // src/core/facts.ts
22322
22371
  function sizeFact(bytes2) {
22323
22372
  return { label: "Size", value: { text: bytes2 === 1 ? "{bytes} byte" : "{bytes} bytes", values: { bytes: bytes2 } } };
@@ -24319,14 +24368,6 @@ function namesOf(file2, id) {
24319
24368
  const name = safeFileName(file2.name, { kind: file2.kind, id, type: file2.type });
24320
24369
  return { name, shown: file2.name === null ? file2.kind : name };
24321
24370
  }
24322
- var FILE_KIND_WORDS = {
24323
- photo: "Photo",
24324
- document: "Document",
24325
- audio: "Audio",
24326
- voice: "Voice message",
24327
- video: "Video",
24328
- sticker: "Sticker"
24329
- };
24330
24371
  function saveHeadline(file2, chat) {
24331
24372
  if (file2 !== void 0 && chat !== null) return { text: "About to save {file} from {chat}", values: { file: file2, chat } };
24332
24373
  if (chat !== null) return { text: "About to save a file from {chat}", values: { chat } };
@@ -24334,9 +24375,8 @@ function saveHeadline(file2, chat) {
24334
24375
  return void 0;
24335
24376
  }
24336
24377
  function fileFacts(media, unpack) {
24337
- const kind = media && Object.hasOwn(FILE_KIND_WORDS, media.type) ? FILE_KIND_WORDS[media.type] : void 0;
24338
24378
  return [
24339
- ...media ? [{ label: "File", value: kind ? { text: kind } : media.type }] : [],
24379
+ ...media ? [{ label: "File", value: mediaKindValue(media.type) }] : [],
24340
24380
  ...unpack ? [{ label: "Unpack", value: { text: "Yes" } }] : [],
24341
24381
  ...media?.size !== void 0 && Number.isSafeInteger(media.size) && media.size > 0 ? [sizeFact(media.size)] : []
24342
24382
  ];
@@ -24851,9 +24891,8 @@ async function checkArchive(path, format2, limits, signal) {
24851
24891
  }
24852
24892
 
24853
24893
  // src/core/outgoing/limits.ts
24854
- var BYTES_PER_MB2 = 1024 * 1024;
24855
24894
  var MAX_FILES = 10;
24856
- var MAX_TOTAL_BYTES = 100 * BYTES_PER_MB2;
24895
+ var MAX_TOTAL_BYTES = 100 * BYTES_PER_MB;
24857
24896
 
24858
24897
  // src/core/file/send/constants.ts
24859
24898
  var SEND_FILES_TIMEOUT_MS = 30 * 60 * 1e3;
@@ -25575,39 +25614,46 @@ async function wholeCallDescription(spec, args, ctx) {
25575
25614
  // src/core/message/action-describe.ts
25576
25615
  var EDIT_HEADLINES = {
25577
25616
  named: "About to edit message {id} in {chat}",
25578
- saved: "About to edit message {id} in Saved Messages"
25617
+ saved: "About to edit message {id} in Saved Messages",
25618
+ bare: "About to edit message {id}"
25579
25619
  };
25580
25620
  var DELETE_HEADLINES = {
25581
- named: "About to delete message {id} from {chat}",
25582
- saved: "About to delete message {id} from Saved Messages"
25621
+ named: "About to delete message {id} from {chat} for everyone",
25622
+ saved: "About to delete message {id} from Saved Messages",
25623
+ bare: "About to delete message {id} for everyone"
25583
25624
  };
25584
25625
  var REACT_HEADLINES = {
25585
25626
  named: "About to react {emoji} to message {id} in {chat}",
25586
- saved: "About to react {emoji} to message {id} in Saved Messages"
25627
+ saved: "About to react {emoji} to message {id} in Saved Messages",
25628
+ bare: "About to react {emoji} to message {id}"
25587
25629
  };
25588
25630
  var UNREACT_HEADLINES = {
25589
25631
  named: "About to remove your reaction from message {id} in {chat}",
25590
- saved: "About to remove your reaction from message {id} in Saved Messages"
25632
+ saved: "About to remove your reaction from message {id} in Saved Messages",
25633
+ bare: "About to remove your reaction from message {id}"
25591
25634
  };
25592
25635
  var PIN_HEADLINES = {
25593
25636
  named: "About to pin message {id} in {chat}",
25594
- saved: "About to pin message {id} in Saved Messages"
25637
+ saved: "About to pin message {id} in Saved Messages",
25638
+ bare: "About to pin message {id}"
25595
25639
  };
25596
25640
  var UNPIN_HEADLINES = {
25597
25641
  named: "About to unpin message {id} in {chat}",
25598
- saved: "About to unpin message {id} in Saved Messages"
25642
+ saved: "About to unpin message {id} in Saved Messages",
25643
+ bare: "About to unpin message {id}"
25599
25644
  };
25600
25645
  var MARK_READ_HEADLINES = {
25601
25646
  named: "About to mark {chat} read up to message {id}",
25602
- saved: "About to mark Saved Messages read up to message {id}"
25647
+ saved: "About to mark Saved Messages read up to message {id}",
25648
+ bare: "About to mark messages read up to message {id}"
25603
25649
  };
25604
25650
  function actionHeadline(headlines, { kind, name }, id, values) {
25605
25651
  if (kind === "self") return { text: headlines.saved, values: { id: String(id), ...values } };
25606
- return name === null ? void 0 : { text: headlines.named, values: { id: String(id), chat: name, ...values } };
25652
+ return name === null ? { text: headlines.bare, values: { id: String(id), ...values } } : { text: headlines.named, values: { id: String(id), chat: name, ...values } };
25607
25653
  }
25608
25654
  function currentFacts({ body, media }) {
25609
- const shown = body || media?.type;
25610
- return shown ? [{ label: "Current", value: cut2(shown, MAX_VALUE_LENGTH) }] : [];
25655
+ if (body) return [{ label: "Current", value: cut2(body, MAX_VALUE_LENGTH) }];
25656
+ return media ? [{ label: "Media", value: mediaKindValue(media.type) }] : [];
25611
25657
  }
25612
25658
  function describeAction(headlines, chat, id, message2, extras = {}) {
25613
25659
  const headline = actionHeadline(headlines, chat, id, extras.values ?? {});
@@ -25843,7 +25889,7 @@ var telegramMessageDelete = defineTool({
25843
25889
  "A result means Telegram deleted the message for everyone. `already` means it was gone before this call.",
25844
25890
  "A failure `uncertain` means the delete may have gone through: check the chat in Telegram and never ask for it again without the user checking."
25845
25891
  ].join(" "),
25846
- intent: "About to delete a message",
25892
+ intent: "About to delete a message for everyone",
25847
25893
  toolset: "actions",
25848
25894
  class: "administrative",
25849
25895
  input: deleteInput,
@@ -26449,12 +26495,6 @@ var FILE_TRANSLATIONS = {
26449
26495
  "About to save a file from {chat}": "{chat} \u306E\u30D5\u30A1\u30A4\u30EB\u3092\u4FDD\u5B58\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26450
26496
  "About to save {file}": "{file} \u3092\u4FDD\u5B58\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26451
26497
  File: "\u30D5\u30A1\u30A4\u30EB",
26452
- Photo: "\u5199\u771F",
26453
- Document: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
26454
- Audio: "\u97F3\u58F0",
26455
- "Voice message": "\u30DC\u30A4\u30B9\u30E1\u30C3\u30BB\u30FC\u30B8",
26456
- Video: "\u52D5\u753B",
26457
- Sticker: "\u30B9\u30BF\u30F3\u30D7",
26458
26498
  Unpack: "\u5C55\u958B",
26459
26499
  Yes: "\u3059\u308B"
26460
26500
  }
@@ -26469,24 +26509,31 @@ var ACTION_TRANSLATIONS = {
26469
26509
  "Pin or unpin a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u30D4\u30F3\u7559\u3081\u3059\u308B\u30FB\u89E3\u9664\u3059\u308B",
26470
26510
  "Mark a chat read": "\u30C1\u30E3\u30C3\u30C8\u3092\u65E2\u8AAD\u306B\u3059\u308B",
26471
26511
  "About to edit a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26472
- "About to delete a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26512
+ "About to delete a message for everyone": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u5168\u54E1\u5206\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26473
26513
  "About to change a reaction": "\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5909\u66F4\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26474
26514
  "About to pin or unpin a message": "\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30D4\u30F3\u7559\u3081\u3092\u5909\u66F4\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26475
26515
  "About to mark messages read": "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26476
26516
  "About to edit message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26477
- "About to edit message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26478
- "About to delete message {id} from {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26479
- "About to delete message {id} from Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26517
+ "About to edit message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26518
+ "About to edit message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u7DE8\u96C6\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26519
+ "About to delete message {id} from {chat} for everyone": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u5168\u54E1\u5206\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26520
+ "About to delete message {id} from Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26521
+ "About to delete message {id} for everyone": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u5168\u54E1\u5206\u524A\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26480
26522
  "About to react {emoji} to message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26481
- "About to react {emoji} to message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26523
+ "About to react {emoji} to message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26524
+ "About to react {emoji} to message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306B {emoji} \u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u4ED8\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26482
26525
  "About to remove your reaction from message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26483
- "About to remove your reaction from message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26526
+ "About to remove your reaction from message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26527
+ "About to remove your reaction from message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u304B\u3089\u81EA\u5206\u306E\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u5916\u305D\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26484
26528
  "About to pin message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26485
- "About to pin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26529
+ "About to pin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26530
+ "About to pin message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u3092\u30D4\u30F3\u7559\u3081\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26486
26531
  "About to unpin message {id} in {chat}": "{chat} \u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26487
- "About to unpin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26532
+ "About to unpin message {id} in Saved Messages": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26533
+ "About to unpin message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u306E\u30D4\u30F3\u7559\u3081\u3092\u89E3\u9664\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26488
26534
  "About to mark {chat} read up to message {id}": "{chat} \u3092\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u307E\u3067\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26489
26535
  "About to mark Saved Messages read up to message {id}": "\u4FDD\u5B58\u6E08\u307F\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u307E\u3067\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26536
+ "About to mark messages read up to message {id}": "\u30E1\u30C3\u30BB\u30FC\u30B8 {id} \u307E\u3067\u65E2\u8AAD\u306B\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059",
26490
26537
  Message: "\u30E1\u30C3\u30BB\u30FC\u30B8",
26491
26538
  Current: "\u73FE\u5728\u306E\u5185\u5BB9",
26492
26539
  Replaces: "\u7F6E\u304D\u63DB\u3048\u308B\u30EA\u30A2\u30AF\u30B7\u30E7\u30F3",
@@ -26530,6 +26577,7 @@ var SYNC_TRANSLATIONS = {
26530
26577
  var ALL_TRANSLATIONS = mergeTranslations(
26531
26578
  FACT_TRANSLATIONS,
26532
26579
  CHAT_FACT_TRANSLATIONS,
26580
+ MEDIA_FACT_TRANSLATIONS,
26533
26581
  MESSAGE_TRANSLATIONS,
26534
26582
  ACTION_TRANSLATIONS,
26535
26583
  FILE_TRANSLATIONS,
@@ -94531,7 +94579,7 @@ function createTelegramClient(settings) {
94531
94579
  function deviceInfo() {
94532
94580
  return {
94533
94581
  model: `${PROJECT.name} (unofficial)`,
94534
- appVersion: true ? "0.1.0" : DEV_VERSION,
94582
+ appVersion: true ? "0.2.0" : DEV_VERSION,
94535
94583
  systemVersion: `${type2()} ${release()}`
94536
94584
  };
94537
94585
  }
@@ -96219,9 +96267,8 @@ function applyRead(mirror, read) {
96219
96267
  }
96220
96268
 
96221
96269
  // src/core/telegram/ingest-messages.ts
96222
- var MS_PER_S2 = 1e3;
96223
96270
  function expiryOf(msg) {
96224
- return isCount(msg.ttl) && msg.ttl > 0 ? msg.ts + msg.ttl * MS_PER_S2 : null;
96271
+ return isCount(msg.ttl) && msg.ttl > 0 ? msg.ts + msg.ttl * MS_PER_S : null;
96225
96272
  }
96226
96273
  function findVersion(mirror, chat, id) {
96227
96274
  const row = mirror.prepare("SELECT ts, body, entities, media, edited, expires FROM messages WHERE chat = ? AND id = ?").get(chat, id);
@@ -96759,7 +96806,6 @@ import { Buffer as Buffer8 } from "node:buffer";
96759
96806
  import { open as open6 } from "node:fs/promises";
96760
96807
 
96761
96808
  // src/core/telegram/sync-download-file.ts
96762
- var MS_PER_S3 = 1e3;
96763
96809
  var FILE_KINDS = ["photo", "document", "audio", "voice", "video", "sticker"];
96764
96810
  function isFileMedia(media) {
96765
96811
  return media !== null && FILE_KINDS.includes(media.type);
@@ -96773,7 +96819,7 @@ function messageFile(message2) {
96773
96819
  }
96774
96820
  function disappearsAtOf(message2) {
96775
96821
  const ttl = message2.ttlPeriod;
96776
- return ttl ? message2.date.getTime() + ttl * MS_PER_S3 : void 0;
96822
+ return ttl ? message2.date.getTime() + ttl * MS_PER_S : void 0;
96777
96823
  }
96778
96824
 
96779
96825
  // src/core/telegram/sync-download-refusal.ts
@@ -96801,11 +96847,10 @@ function tooLargeReason(size, maxBytes) {
96801
96847
  // src/core/telegram/sync-download-stream.ts
96802
96848
  import { open as open4 } from "node:fs/promises";
96803
96849
  var FILE_MODE3 = 384;
96804
- var MS_PER_S4 = 1e3;
96805
96850
  function stalled(stallMs) {
96806
96851
  return new ToolError(
96807
96852
  "upstream_error",
96808
- `The download received nothing for ${stallMs / MS_PER_S4} seconds; try again later.`
96853
+ `The download received nothing for ${stallMs / MS_PER_S} seconds; try again later.`
96809
96854
  );
96810
96855
  }
96811
96856
  async function writeAll2(handle, chunk) {
@@ -96814,7 +96859,7 @@ async function writeAll2(handle, chunk) {
96814
96859
  }
96815
96860
  async function writeDownload(ctx, signal, media, file2, max3) {
96816
96861
  const env = { endSession: ctx.endSession, log: ctx.settings.log };
96817
- const stallMs = ctx.settings.downloadStallMs;
96862
+ const stallMs = ctx.settings.transferStallMs;
96818
96863
  const failure2 = (error62) => {
96819
96864
  if (signal.aborted) return cancelled();
96820
96865
  if (error62 instanceof MtTimeoutError && error62.timeout === stallMs) return stalled(stallMs);
@@ -97144,6 +97189,202 @@ var FileKeeper = class {
97144
97189
  }
97145
97190
  };
97146
97191
 
97192
+ // src/core/telegram/sync-options.ts
97193
+ var SECOND_MS2 = 1e3;
97194
+ var MINUTE_MS = 60 * SECOND_MS2;
97195
+ var HOUR_MS = 60 * MINUTE_MS;
97196
+ var DAY_MS = 24 * HOUR_MS;
97197
+ var TRANSFER_STALL_MS = 6e4;
97198
+ var DEFAULT_HEARTBEAT_EVERY_MS = 5 * SECOND_MS2;
97199
+ var DEFAULT_GAP_AFTER_MS = 30 * SECOND_MS2;
97200
+ var DEFAULT_OFFLINE_LIMIT_MS = 10 * MINUTE_MS;
97201
+ var DEFAULT_PURGE_EVERY_MS = HOUR_MS;
97202
+ var DEFAULT_REFRESH_EVERY_MS = 30 * MINUTE_MS;
97203
+ var DEFAULT_REFRESH_RETRY_MS = MINUTE_MS;
97204
+ var DEFAULT_ROUND_EVERY_MS = 5 * SECOND_MS2;
97205
+ var DEFAULT_STOP_WAIT_MS = 10 * SECOND_MS2;
97206
+ var DEFAULT_SEED_WITHIN_MS = 30 * DAY_MS;
97207
+ var DEFAULT_SEED_COUNT = 50;
97208
+ var DEFAULT_SEED_CHATS_PER_ROUND = 20;
97209
+ var DEFAULT_SEED_ATTEMPTS = 3;
97210
+ var DEFAULT_FILL_LIMIT = 200;
97211
+ var DEFAULT_PACE_MS = [500, 1500];
97212
+ function resolveOptions(options) {
97213
+ return {
97214
+ ...options,
97215
+ now: options.now ?? Date.now,
97216
+ pause: options.pause ?? realPause,
97217
+ heartbeatEveryMs: options.heartbeatEveryMs ?? DEFAULT_HEARTBEAT_EVERY_MS,
97218
+ gapAfterMs: options.gapAfterMs ?? DEFAULT_GAP_AFTER_MS,
97219
+ offlineLimitMs: options.offlineLimitMs ?? DEFAULT_OFFLINE_LIMIT_MS,
97220
+ purgeEveryMs: options.purgeEveryMs ?? DEFAULT_PURGE_EVERY_MS,
97221
+ refreshEveryMs: options.refreshEveryMs ?? DEFAULT_REFRESH_EVERY_MS,
97222
+ refreshRetryMs: options.refreshRetryMs ?? DEFAULT_REFRESH_RETRY_MS,
97223
+ roundEveryMs: options.roundEveryMs ?? DEFAULT_ROUND_EVERY_MS,
97224
+ stopWaitMs: options.stopWaitMs ?? DEFAULT_STOP_WAIT_MS,
97225
+ seedWithinMs: options.seedWithinMs ?? DEFAULT_SEED_WITHIN_MS,
97226
+ seedCount: options.seedCount ?? DEFAULT_SEED_COUNT,
97227
+ seedChatsPerRound: options.seedChatsPerRound ?? DEFAULT_SEED_CHATS_PER_ROUND,
97228
+ seedAttempts: options.seedAttempts ?? DEFAULT_SEED_ATTEMPTS,
97229
+ fillLimit: options.fillLimit ?? DEFAULT_FILL_LIMIT,
97230
+ paceMs: options.paceMs ?? DEFAULT_PACE_MS,
97231
+ transferStallMs: options.transferStallMs ?? TRANSFER_STALL_MS
97232
+ };
97233
+ }
97234
+
97235
+ // src/core/telegram/sync-message.ts
97236
+ function readMessage(message2, channels) {
97237
+ const input8 = messageInputOf(message2);
97238
+ if (!input8) return null;
97239
+ const peer = readOrNull(() => message2.chat);
97240
+ const chat = peer ? chatInputOf(peer) : null;
97241
+ if (chat && !keepsKind(chat.kind, channels)) return null;
97242
+ return { chat, message: input8, topic: topicInputOfAction(message2) };
97243
+ }
97244
+ function writeMessage(mirror, ctx, read, channels) {
97245
+ if (read.chat) rememberChats(mirror, [read.chat], ctx.now, channels);
97246
+ if (read.topic) upsertTopics(mirror, [read.topic], ctx.now);
97247
+ return storeMessage(mirror, ctx, read.message);
97248
+ }
97249
+
97250
+ // src/core/telegram/sync-receive.ts
97251
+ var SKIPPED = "skipped";
97252
+ function countsOf(outcomes) {
97253
+ const counts = /* @__PURE__ */ new Map();
97254
+ for (const outcome of outcomes) counts.set(outcome, (counts.get(outcome) ?? 0) + 1);
97255
+ return [...counts].map(([outcome, count]) => `${outcome}=${count}`).join(" ") || "none";
97256
+ }
97257
+ var Receiver = class {
97258
+ deps;
97259
+ keeper;
97260
+ halted = false;
97261
+ constructor(deps) {
97262
+ this.deps = deps;
97263
+ }
97264
+ /**
97265
+ * Registers the handlers on the client's emitters; call it before the client
97266
+ * connects. `keeper` keeps the files of the disappearing messages that
97267
+ * arrive, when `keepDisappearing` is on.
97268
+ */
97269
+ attach(client, keeper) {
97270
+ this.keeper = keeper;
97271
+ client.onNewMessage.add((message2) => this.onMessage("message", message2));
97272
+ client.onEditMessage.add((message2) => this.onMessage("edit", message2));
97273
+ client.onDeleteMessage.add((update) => this.onDelete(update));
97274
+ client.onHistoryRead.add((update) => this.onRead(update));
97275
+ }
97276
+ /** Stops storing: whatever arrives from here on is ignored. */
97277
+ halt() {
97278
+ this.halted = true;
97279
+ }
97280
+ onMessage(label, message2) {
97281
+ const { channels, keepDisappearing } = this.deps;
97282
+ const done = this.process(
97283
+ label,
97284
+ () => readMessage(message2, channels),
97285
+ (read, ctx) => writeMessage(this.deps.mirror, ctx, read, channels)
97286
+ );
97287
+ if (keepDisappearing && label === "message" && done?.outcome === "stored") {
97288
+ this.keeper?.keep(message2, done.prepared.message);
97289
+ }
97290
+ }
97291
+ onDelete(update) {
97292
+ const { mirror, coverage, keepDisappearing } = this.deps;
97293
+ const done = this.process(
97294
+ "delete",
97295
+ () => deleteInputOf(update),
97296
+ (input8, ctx) => {
97297
+ if (input8.chat !== null && !findChat(mirror, input8.chat)?.followed) return SKIPPED;
97298
+ coverage.note(ctx.now);
97299
+ return countsOf(deleteMessages2(mirror, ctx, input8));
97300
+ }
97301
+ );
97302
+ if (keepDisappearing && done && done.outcome !== SKIPPED) {
97303
+ this.keeper?.forgetMessages(done.prepared.chat, done.prepared.ids);
97304
+ }
97305
+ }
97306
+ onRead(update) {
97307
+ this.process(
97308
+ "read",
97309
+ () => readInputOf(update),
97310
+ (input8) => {
97311
+ applyRead(this.deps.mirror, input8);
97312
+ return "applied";
97313
+ }
97314
+ );
97315
+ }
97316
+ /**
97317
+ * Turns the update into `prepared` outside the transaction, so that what
97318
+ * fails there is only mtcute's data, then writes it. `store` names the
97319
+ * outcome for the log. Resolves with `prepared` and the outcome once it is
97320
+ * written, and with null when the update was not (unreadable, not for the
97321
+ * mirror, or a storage failure).
97322
+ */
97323
+ process(label, prepare2, store) {
97324
+ if (this.halted) return null;
97325
+ const { mirror, now, keepDisappearing, log } = this.deps;
97326
+ let prepared;
97327
+ try {
97328
+ prepared = prepare2();
97329
+ } catch (error62) {
97330
+ log(`event: ${label} unreadable (${errorLabel(error62)})`);
97331
+ return null;
97332
+ }
97333
+ if (prepared === null) {
97334
+ log(`event: ${label} ${SKIPPED}`);
97335
+ return null;
97336
+ }
97337
+ let outcome;
97338
+ try {
97339
+ const received = now();
97340
+ outcome = inTransaction(mirror, () => {
97341
+ const result = store(prepared, { now: received, keepDisappearing });
97342
+ if (result !== SKIPPED) setMeta(mirror, { [META.lastReceivedAt]: received });
97343
+ return result;
97344
+ });
97345
+ } catch {
97346
+ this.failStorage();
97347
+ return null;
97348
+ }
97349
+ log(`event: ${label} ${outcome}`);
97350
+ return { prepared, outcome };
97351
+ }
97352
+ failStorage() {
97353
+ if (this.halted) return;
97354
+ this.halted = true;
97355
+ this.deps.onStorageFailure();
97356
+ this.deps.log(STORAGE_FAILURE_REASON);
97357
+ }
97358
+ };
97359
+
97360
+ // src/core/telegram/sync-request-queue.ts
97361
+ var RequestQueue = class {
97362
+ /** Settles when the last job in line is done. It never rejects. */
97363
+ tail = Promise.resolve();
97364
+ /**
97365
+ * Runs `work` once the jobs ahead are done. Rejects as `cancelled()`, without
97366
+ * running `work`, when `signal` aborts first; the jobs behind it then wait
97367
+ * for the ones ahead of it, as they would have.
97368
+ */
97369
+ async run(signal, work) {
97370
+ const ahead = this.tail;
97371
+ let done = () => {
97372
+ };
97373
+ this.tail = new Promise((resolve8) => done = resolve8);
97374
+ try {
97375
+ await abortable(ahead, signal);
97376
+ } catch (error62) {
97377
+ void ahead.then(done);
97378
+ throw error62;
97379
+ }
97380
+ try {
97381
+ return await work();
97382
+ } finally {
97383
+ done();
97384
+ }
97385
+ }
97386
+ };
97387
+
97147
97388
  // src/core/telegram/sync-request-calls.ts
97148
97389
  async function viaTelegram(ctx, call, what, work) {
97149
97390
  const signal = AbortSignal.any([call.signal, ctx.signal]);
@@ -97404,7 +97645,6 @@ function expectNoParams(params, what) {
97404
97645
  }
97405
97646
 
97406
97647
  // src/core/telegram/sync-download.ts
97407
- var DOWNLOAD_STALL_MS = 6e4;
97408
97648
  function fileOf(message2, id) {
97409
97649
  const file2 = messageFile(message2);
97410
97650
  if (!file2) {
@@ -97478,201 +97718,6 @@ function downloadRequest(ctx, queue, raw, call) {
97478
97718
  return keptCopy(ctx, params).then((kept) => kept ?? queue.run(signal, () => downloadFile(ctx, call, signal, params)));
97479
97719
  }
97480
97720
 
97481
- // src/core/telegram/sync-options.ts
97482
- var SECOND_MS2 = 1e3;
97483
- var MINUTE_MS = 60 * SECOND_MS2;
97484
- var HOUR_MS = 60 * MINUTE_MS;
97485
- var DAY_MS = 24 * HOUR_MS;
97486
- var DEFAULT_HEARTBEAT_EVERY_MS = 5 * SECOND_MS2;
97487
- var DEFAULT_GAP_AFTER_MS = 30 * SECOND_MS2;
97488
- var DEFAULT_OFFLINE_LIMIT_MS = 10 * MINUTE_MS;
97489
- var DEFAULT_PURGE_EVERY_MS = HOUR_MS;
97490
- var DEFAULT_REFRESH_EVERY_MS = 30 * MINUTE_MS;
97491
- var DEFAULT_REFRESH_RETRY_MS = MINUTE_MS;
97492
- var DEFAULT_ROUND_EVERY_MS = 5 * SECOND_MS2;
97493
- var DEFAULT_STOP_WAIT_MS = 10 * SECOND_MS2;
97494
- var DEFAULT_SEED_WITHIN_MS = 30 * DAY_MS;
97495
- var DEFAULT_SEED_COUNT = 50;
97496
- var DEFAULT_SEED_CHATS_PER_ROUND = 20;
97497
- var DEFAULT_SEED_ATTEMPTS = 3;
97498
- var DEFAULT_FILL_LIMIT = 200;
97499
- var DEFAULT_PACE_MS = [500, 1500];
97500
- function resolveOptions(options) {
97501
- return {
97502
- ...options,
97503
- now: options.now ?? Date.now,
97504
- pause: options.pause ?? realPause,
97505
- heartbeatEveryMs: options.heartbeatEveryMs ?? DEFAULT_HEARTBEAT_EVERY_MS,
97506
- gapAfterMs: options.gapAfterMs ?? DEFAULT_GAP_AFTER_MS,
97507
- offlineLimitMs: options.offlineLimitMs ?? DEFAULT_OFFLINE_LIMIT_MS,
97508
- purgeEveryMs: options.purgeEveryMs ?? DEFAULT_PURGE_EVERY_MS,
97509
- refreshEveryMs: options.refreshEveryMs ?? DEFAULT_REFRESH_EVERY_MS,
97510
- refreshRetryMs: options.refreshRetryMs ?? DEFAULT_REFRESH_RETRY_MS,
97511
- roundEveryMs: options.roundEveryMs ?? DEFAULT_ROUND_EVERY_MS,
97512
- stopWaitMs: options.stopWaitMs ?? DEFAULT_STOP_WAIT_MS,
97513
- seedWithinMs: options.seedWithinMs ?? DEFAULT_SEED_WITHIN_MS,
97514
- seedCount: options.seedCount ?? DEFAULT_SEED_COUNT,
97515
- seedChatsPerRound: options.seedChatsPerRound ?? DEFAULT_SEED_CHATS_PER_ROUND,
97516
- seedAttempts: options.seedAttempts ?? DEFAULT_SEED_ATTEMPTS,
97517
- fillLimit: options.fillLimit ?? DEFAULT_FILL_LIMIT,
97518
- paceMs: options.paceMs ?? DEFAULT_PACE_MS,
97519
- downloadStallMs: options.downloadStallMs ?? DOWNLOAD_STALL_MS
97520
- };
97521
- }
97522
-
97523
- // src/core/telegram/sync-message.ts
97524
- function readMessage(message2, channels) {
97525
- const input8 = messageInputOf(message2);
97526
- if (!input8) return null;
97527
- const peer = readOrNull(() => message2.chat);
97528
- const chat = peer ? chatInputOf(peer) : null;
97529
- if (chat && !keepsKind(chat.kind, channels)) return null;
97530
- return { chat, message: input8, topic: topicInputOfAction(message2) };
97531
- }
97532
- function writeMessage(mirror, ctx, read, channels) {
97533
- if (read.chat) rememberChats(mirror, [read.chat], ctx.now, channels);
97534
- if (read.topic) upsertTopics(mirror, [read.topic], ctx.now);
97535
- return storeMessage(mirror, ctx, read.message);
97536
- }
97537
-
97538
- // src/core/telegram/sync-receive.ts
97539
- var SKIPPED = "skipped";
97540
- function countsOf(outcomes) {
97541
- const counts = /* @__PURE__ */ new Map();
97542
- for (const outcome of outcomes) counts.set(outcome, (counts.get(outcome) ?? 0) + 1);
97543
- return [...counts].map(([outcome, count]) => `${outcome}=${count}`).join(" ") || "none";
97544
- }
97545
- var Receiver = class {
97546
- deps;
97547
- keeper;
97548
- halted = false;
97549
- constructor(deps) {
97550
- this.deps = deps;
97551
- }
97552
- /**
97553
- * Registers the handlers on the client's emitters; call it before the client
97554
- * connects. `keeper` keeps the files of the disappearing messages that
97555
- * arrive, when `keepDisappearing` is on.
97556
- */
97557
- attach(client, keeper) {
97558
- this.keeper = keeper;
97559
- client.onNewMessage.add((message2) => this.onMessage("message", message2));
97560
- client.onEditMessage.add((message2) => this.onMessage("edit", message2));
97561
- client.onDeleteMessage.add((update) => this.onDelete(update));
97562
- client.onHistoryRead.add((update) => this.onRead(update));
97563
- }
97564
- /** Stops storing: whatever arrives from here on is ignored. */
97565
- halt() {
97566
- this.halted = true;
97567
- }
97568
- onMessage(label, message2) {
97569
- const { channels, keepDisappearing } = this.deps;
97570
- const done = this.process(
97571
- label,
97572
- () => readMessage(message2, channels),
97573
- (read, ctx) => writeMessage(this.deps.mirror, ctx, read, channels)
97574
- );
97575
- if (keepDisappearing && label === "message" && done?.outcome === "stored") {
97576
- this.keeper?.keep(message2, done.prepared.message);
97577
- }
97578
- }
97579
- onDelete(update) {
97580
- const { mirror, coverage, keepDisappearing } = this.deps;
97581
- const done = this.process(
97582
- "delete",
97583
- () => deleteInputOf(update),
97584
- (input8, ctx) => {
97585
- if (input8.chat !== null && !findChat(mirror, input8.chat)?.followed) return SKIPPED;
97586
- coverage.note(ctx.now);
97587
- return countsOf(deleteMessages2(mirror, ctx, input8));
97588
- }
97589
- );
97590
- if (keepDisappearing && done && done.outcome !== SKIPPED) {
97591
- this.keeper?.forgetMessages(done.prepared.chat, done.prepared.ids);
97592
- }
97593
- }
97594
- onRead(update) {
97595
- this.process(
97596
- "read",
97597
- () => readInputOf(update),
97598
- (input8) => {
97599
- applyRead(this.deps.mirror, input8);
97600
- return "applied";
97601
- }
97602
- );
97603
- }
97604
- /**
97605
- * Turns the update into `prepared` outside the transaction, so that what
97606
- * fails there is only mtcute's data, then writes it. `store` names the
97607
- * outcome for the log. Resolves with `prepared` and the outcome once it is
97608
- * written, and with null when the update was not (unreadable, not for the
97609
- * mirror, or a storage failure).
97610
- */
97611
- process(label, prepare2, store) {
97612
- if (this.halted) return null;
97613
- const { mirror, now, keepDisappearing, log } = this.deps;
97614
- let prepared;
97615
- try {
97616
- prepared = prepare2();
97617
- } catch (error62) {
97618
- log(`event: ${label} unreadable (${errorLabel(error62)})`);
97619
- return null;
97620
- }
97621
- if (prepared === null) {
97622
- log(`event: ${label} ${SKIPPED}`);
97623
- return null;
97624
- }
97625
- let outcome;
97626
- try {
97627
- const received = now();
97628
- outcome = inTransaction(mirror, () => {
97629
- const result = store(prepared, { now: received, keepDisappearing });
97630
- if (result !== SKIPPED) setMeta(mirror, { [META.lastReceivedAt]: received });
97631
- return result;
97632
- });
97633
- } catch {
97634
- this.failStorage();
97635
- return null;
97636
- }
97637
- log(`event: ${label} ${outcome}`);
97638
- return { prepared, outcome };
97639
- }
97640
- failStorage() {
97641
- if (this.halted) return;
97642
- this.halted = true;
97643
- this.deps.onStorageFailure();
97644
- this.deps.log(STORAGE_FAILURE_REASON);
97645
- }
97646
- };
97647
-
97648
- // src/core/telegram/sync-request-queue.ts
97649
- var RequestQueue = class {
97650
- /** Settles when the last job in line is done. It never rejects. */
97651
- tail = Promise.resolve();
97652
- /**
97653
- * Runs `work` once the jobs ahead are done. Rejects as `cancelled()`, without
97654
- * running `work`, when `signal` aborts first; the jobs behind it then wait
97655
- * for the ones ahead of it, as they would have.
97656
- */
97657
- async run(signal, work) {
97658
- const ahead = this.tail;
97659
- let done = () => {
97660
- };
97661
- this.tail = new Promise((resolve8) => done = resolve8);
97662
- try {
97663
- await abortable(ahead, signal);
97664
- } catch (error62) {
97665
- void ahead.then(done);
97666
- throw error62;
97667
- }
97668
- try {
97669
- return await work();
97670
- } finally {
97671
- done();
97672
- }
97673
- }
97674
- };
97675
-
97676
97721
  // src/core/telegram/sync-list-requests.ts
97677
97722
  function expectListable({ mirror, settings }, chats) {
97678
97723
  for (const id of chats) {
@@ -97724,7 +97769,6 @@ async function syncRemove(ctx, raw) {
97724
97769
  }
97725
97770
 
97726
97771
  // src/core/telegram/sync-send-outcome.ts
97727
- var MS_PER_S5 = 1e3;
97728
97772
  var RANDOM_ID_DUPLICATE = "RANDOM_ID_DUPLICATE";
97729
97773
  var CALL_TIMEOUT = "TIMEOUT";
97730
97774
  var REFUSAL_CODES = [400, 403, 406];
@@ -97807,7 +97851,7 @@ function sentMessageOf(response, randomId) {
97807
97851
  return {
97808
97852
  form: "short",
97809
97853
  id: response.id,
97810
- date: response.date * MS_PER_S5,
97854
+ date: response.date * MS_PER_S,
97811
97855
  ttl: response.ttlPeriod ?? null,
97812
97856
  entities: response.entities ?? []
97813
97857
  };
@@ -98075,12 +98119,27 @@ function sendRequest(ctx, queue, raw, call) {
98075
98119
  }
98076
98120
 
98077
98121
  // src/core/telegram/sync-action.ts
98078
- function performAction(ctx, signal, refusals, call) {
98122
+ function runAction(ctx, queue, raw, call, parse6, act) {
98123
+ const params = parse6(raw);
98124
+ const signal = AbortSignal.any([call.signal, ctx.signal]);
98125
+ return queue.run(signal, () => act(params, signal));
98126
+ }
98127
+ async function resolveChatPeer(ctx, call, signal, what, chat) {
98128
+ const peer = await viaTelegram(ctx, call, what, () => ctx.client.resolvePeer(Number(chat)));
98129
+ if (signal.aborted) throw cancelled();
98130
+ return peer;
98131
+ }
98132
+ function inputChannelOf(peer) {
98133
+ if (peer._ !== "inputPeerChannel") return void 0;
98134
+ const { channelId, accessHash } = peer;
98135
+ return { _: "inputChannel", channelId, accessHash };
98136
+ }
98137
+ function performAction(ctx, signal, refusals, request) {
98079
98138
  return callOnce(
98080
98139
  ctx,
98081
98140
  signal,
98082
98141
  refusals.words.name,
98083
- call,
98142
+ (options) => ctx.client.call(request, options),
98084
98143
  (error62, trouble) => actionFailureOf(error62, trouble, refusals)
98085
98144
  );
98086
98145
  }
@@ -98161,10 +98220,9 @@ var DELETE_REFUSALS = {
98161
98220
  }
98162
98221
  ]
98163
98222
  };
98164
- function requestOf(peer, id) {
98165
- if (peer._ !== "inputPeerChannel") return { _: "messages.deleteMessages", revoke: true, id: [id] };
98166
- const { channelId, accessHash } = peer;
98167
- return { _: "channels.deleteMessages", channel: { _: "inputChannel", channelId, accessHash }, id: [id] };
98223
+ function requestOf(channel, id) {
98224
+ if (!channel) return { _: "messages.deleteMessages", revoke: true, id: [id] };
98225
+ return { _: "channels.deleteMessages", channel, id: [id] };
98168
98226
  }
98169
98227
  function recordDelete(ctx, { id: chat, kind }, id) {
98170
98228
  recordAction(ctx, "delete", () => {
@@ -98180,22 +98238,17 @@ function recordDelete(ctx, { id: chat, kind }, id) {
98180
98238
  async function deleteOwn(ctx, call, signal, params) {
98181
98239
  const { chat, message: message2 } = targetOf(ctx.mirror, params.chat, params.id, DELETE_WORDS);
98182
98240
  if (!message2) return { id: params.id, deleted: true, already: true };
98183
- const peer = await viaTelegram(ctx, call, "delete", () => ctx.client.resolvePeer(Number(params.chat)));
98184
- if (signal.aborted) throw cancelled();
98185
- const request = requestOf(peer, params.id);
98186
- const affected = await performAction(ctx, signal, DELETE_REFUSALS, (options) => ctx.client.call(request, options));
98187
- if (affected) handOverAffected(ctx, "delete", affected, peer._ === "inputPeerChannel" ? peer.channelId : void 0);
98241
+ const channel = inputChannelOf(await resolveChatPeer(ctx, call, signal, "delete", params.chat));
98242
+ const affected = await performAction(ctx, signal, DELETE_REFUSALS, requestOf(channel, params.id));
98243
+ if (affected) handOverAffected(ctx, "delete", affected, channel?.channelId);
98188
98244
  recordDelete(ctx, chat, params.id);
98189
98245
  return { id: params.id, deleted: true, ...!affected || affected.ptsCount === 0 ? { already: true } : {} };
98190
98246
  }
98191
98247
  function deleteRequest(ctx, queue, raw, call) {
98192
- const params = parseDeleteParams(raw);
98193
- const signal = AbortSignal.any([call.signal, ctx.signal]);
98194
- return queue.run(signal, () => deleteOwn(ctx, call, signal, params));
98248
+ return runAction(ctx, queue, raw, call, parseDeleteParams, (params, signal) => deleteOwn(ctx, call, signal, params));
98195
98249
  }
98196
98250
 
98197
98251
  // src/core/telegram/sync-edit.ts
98198
- var MS_PER_S6 = 1e3;
98199
98252
  var EDIT_WORDS = {
98200
98253
  name: "edit",
98201
98254
  gerund: "editing",
@@ -98255,25 +98308,22 @@ function acceptedResult2(ctx, params, response) {
98255
98308
  throw uncertainAction(EDIT_WORDS, "Telegram's answer did not name the edited message");
98256
98309
  }
98257
98310
  recordEdit(ctx, params, raw, group);
98258
- return { id: params.id, edited: raw.editDate * MS_PER_S6 };
98311
+ return { id: params.id, edited: raw.editDate * MS_PER_S };
98259
98312
  }
98260
98313
  async function editText(ctx, call, signal, params) {
98261
98314
  refuseUneditable(targetOf(ctx.mirror, params.chat, params.id, EDIT_WORDS), params.text);
98262
- const peer = await viaTelegram(ctx, call, "edit", () => ctx.client.resolvePeer(Number(params.chat)));
98263
- if (signal.aborted) throw cancelled();
98315
+ const peer = await resolveChatPeer(ctx, call, signal, "edit", params.chat);
98264
98316
  const request = {
98265
98317
  _: "messages.editMessage",
98266
98318
  peer,
98267
98319
  id: params.id,
98268
98320
  message: params.text
98269
98321
  };
98270
- const response = await performAction(ctx, signal, EDIT_REFUSALS, (options) => ctx.client.call(request, options));
98322
+ const response = await performAction(ctx, signal, EDIT_REFUSALS, request);
98271
98323
  return response === void 0 ? alreadyResult(ctx, params) : acceptedResult2(ctx, params, response);
98272
98324
  }
98273
98325
  function editRequest(ctx, queue, raw, call) {
98274
- const params = parseEditParams(raw);
98275
- const signal = AbortSignal.any([call.signal, ctx.signal]);
98276
- return queue.run(signal, () => editText(ctx, call, signal, params));
98326
+ return runAction(ctx, queue, raw, call, parseEditParams, (params, signal) => editText(ctx, call, signal, params));
98277
98327
  }
98278
98328
 
98279
98329
  // src/core/telegram/sync-mark-read.ts
@@ -98290,9 +98340,9 @@ var MARK_READ_REFUSALS = {
98290
98340
  };
98291
98341
  function requestOf2(peer, { up_to, topic }) {
98292
98342
  if (topic !== void 0) return { _: "messages.readDiscussion", peer, msgId: topic, readMaxId: up_to };
98293
- if (peer._ !== "inputPeerChannel") return { _: "messages.readHistory", peer, maxId: up_to };
98294
- const { channelId, accessHash } = peer;
98295
- return { _: "channels.readHistory", channel: { _: "inputChannel", channelId, accessHash }, maxId: up_to };
98343
+ const channel = inputChannelOf(peer);
98344
+ if (!channel) return { _: "messages.readHistory", peer, maxId: up_to };
98345
+ return { _: "channels.readHistory", channel, maxId: up_to };
98296
98346
  }
98297
98347
  function unreadPast(mirror, chat, readMax) {
98298
98348
  const row = mirror.prepare("SELECT COUNT(*) AS unread FROM messages WHERE chat = ? AND from_me = 0 AND id > ?").get(chat, readMax);
@@ -98318,23 +98368,19 @@ async function markRead(ctx, call, signal, params) {
98318
98368
  checkInTopic(message2.topic, topic, MARK_READ_WORDS.past);
98319
98369
  const done = { chat, up_to, ...topic === void 0 ? {} : { topic } };
98320
98370
  if (topic === void 0 && row.read_max !== null && row.read_max >= up_to) return { ...done, already: true };
98321
- const peer = await viaTelegram(ctx, call, "mark read", () => ctx.client.resolvePeer(Number(chat)));
98322
- if (signal.aborted) throw cancelled();
98323
- const request = requestOf2(peer, params);
98371
+ const peer = await resolveChatPeer(ctx, call, signal, "mark read", chat);
98324
98372
  const answer = await performAction(
98325
98373
  ctx,
98326
98374
  signal,
98327
98375
  MARK_READ_REFUSALS,
98328
- (options) => ctx.client.call(request, options)
98376
+ requestOf2(peer, params)
98329
98377
  );
98330
98378
  if (typeof answer === "object") handOverAffected(ctx, "mark read", answer);
98331
98379
  if (topic === void 0) recordRead(ctx, params);
98332
98380
  return done;
98333
98381
  }
98334
98382
  function markReadRequest(ctx, queue, raw, call) {
98335
- const params = parseMarkReadParams(raw);
98336
- const signal = AbortSignal.any([call.signal, ctx.signal]);
98337
- return queue.run(signal, () => markRead(ctx, call, signal, params));
98383
+ return runAction(ctx, queue, raw, call, parseMarkReadParams, (params, signal) => markRead(ctx, call, signal, params));
98338
98384
  }
98339
98385
 
98340
98386
  // src/core/telegram/sync-pin.ts
@@ -98384,8 +98430,7 @@ async function pinMessage2(ctx, call, signal, params) {
98384
98430
  const unpin = params.unpin === true;
98385
98431
  const { chat } = anyMessageOf(ctx.mirror, params.chat, params.id, unpin ? UNPIN_WORDS : PIN_WORDS);
98386
98432
  checkBothSides(chat.kind, params.both_sides);
98387
- const peer = await viaTelegram(ctx, call, "pin", () => ctx.client.resolvePeer(Number(params.chat)));
98388
- if (signal.aborted) throw cancelled();
98433
+ const peer = await resolveChatPeer(ctx, call, signal, "pin", params.chat);
98389
98434
  const request = {
98390
98435
  _: "messages.updatePinnedMessage",
98391
98436
  peer,
@@ -98394,21 +98439,14 @@ async function pinMessage2(ctx, call, signal, params) {
98394
98439
  ...unpin ? { unpin } : {},
98395
98440
  ...isPrivateChat(chat.kind) && !params.both_sides ? { pmOneside: true } : {}
98396
98441
  };
98397
- const response = await performAction(
98398
- ctx,
98399
- signal,
98400
- unpin ? UNPIN_REFUSALS : PIN_REFUSALS,
98401
- (options) => ctx.client.call(request, options)
98402
- );
98442
+ const response = await performAction(ctx, signal, unpin ? UNPIN_REFUSALS : PIN_REFUSALS, request);
98403
98443
  if (response === void 0) return { id: params.id, pinned: !unpin, already: true };
98404
98444
  handOver(ctx, response, "pin");
98405
98445
  recordServiceMessages(ctx, params.chat, response);
98406
98446
  return { id: params.id, pinned: !unpin };
98407
98447
  }
98408
98448
  function pinRequest(ctx, queue, raw, call) {
98409
- const params = parsePinParams(raw);
98410
- const signal = AbortSignal.any([call.signal, ctx.signal]);
98411
- return queue.run(signal, () => pinMessage2(ctx, call, signal, params));
98449
+ return runAction(ctx, queue, raw, call, parsePinParams, (params, signal) => pinMessage2(ctx, call, signal, params));
98412
98450
  }
98413
98451
 
98414
98452
  // src/core/telegram/sync-react.ts
@@ -98474,24 +98512,21 @@ async function reactTo(ctx, call, signal, params) {
98474
98512
  if (params.emoji !== void 0 && ownReaction(storedReactions(message2.reactions)) === params.emoji) {
98475
98513
  return { ...doneOf(params), already: true };
98476
98514
  }
98477
- const peer = await viaTelegram(ctx, call, "react", () => ctx.client.resolvePeer(Number(params.chat)));
98478
- if (signal.aborted) throw cancelled();
98515
+ const peer = await resolveChatPeer(ctx, call, signal, "react", params.chat);
98479
98516
  const request = {
98480
98517
  _: "messages.sendReaction",
98481
98518
  peer,
98482
98519
  msgId: params.id,
98483
98520
  reaction: params.emoji === void 0 ? [] : [{ _: "reactionEmoji", emoticon: params.emoji }]
98484
98521
  };
98485
- const response = await performAction(ctx, signal, REACT_REFUSALS, (options) => ctx.client.call(request, options));
98522
+ const response = await performAction(ctx, signal, REACT_REFUSALS, request);
98486
98523
  if (response === void 0) return { ...doneOf(params), already: true };
98487
98524
  handOver(ctx, response, "react");
98488
98525
  recordReaction(ctx, params, response);
98489
98526
  return doneOf(params);
98490
98527
  }
98491
98528
  function reactRequest(ctx, queue, raw, call) {
98492
- const params = parseReactParams(raw);
98493
- const signal = AbortSignal.any([call.signal, ctx.signal]);
98494
- return queue.run(signal, () => reactTo(ctx, call, signal, params));
98529
+ return runAction(ctx, queue, raw, call, parseReactParams, (params, signal) => reactTo(ctx, call, signal, params));
98495
98530
  }
98496
98531
 
98497
98532
  // src/core/telegram/sync-send-outbox.ts
@@ -98567,7 +98602,7 @@ function uploadFailure(ctx, signal, error62, name) {
98567
98602
  return refusal ?? new ToolError("upstream_error", `The upload of ${name} failed (${errorLabel(error62)}); nothing was sent.`);
98568
98603
  }
98569
98604
  async function uploadOne(ctx, signal, file2) {
98570
- const stallMs = ctx.settings.downloadStallMs;
98605
+ const stallMs = ctx.settings.transferStallMs;
98571
98606
  const stall = new AbortController();
98572
98607
  const uploadSignal = AbortSignal.any([signal, stall.signal]);
98573
98608
  let stalled2 = false;