@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.
Files changed (150) hide show
  1. package/README.md +43 -9
  2. package/dist/bot/client.d.ts +1 -1
  3. package/dist/bot/client.d.ts.map +1 -1
  4. package/dist/bot/client.js +2 -2
  5. package/dist/bot/client.js.map +1 -1
  6. package/dist/bot/keep.d.ts +25 -0
  7. package/dist/bot/keep.d.ts.map +1 -0
  8. package/dist/bot/keep.js +96 -0
  9. package/dist/bot/keep.js.map +1 -0
  10. package/dist/bot/registry.d.ts +3 -0
  11. package/dist/bot/registry.d.ts.map +1 -1
  12. package/dist/bot/registry.js +27 -10
  13. package/dist/bot/registry.js.map +1 -1
  14. package/dist/bot/uploads.d.ts +34 -0
  15. package/dist/bot/uploads.d.ts.map +1 -0
  16. package/dist/bot/uploads.js +122 -0
  17. package/dist/bot/uploads.js.map +1 -0
  18. package/dist/client.d.ts +19 -4
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +76 -39
  21. package/dist/client.js.map +1 -1
  22. package/dist/commands/bot-comments.d.ts +4 -0
  23. package/dist/commands/bot-comments.d.ts.map +1 -0
  24. package/dist/commands/bot-comments.js +97 -0
  25. package/dist/commands/bot-comments.js.map +1 -0
  26. package/dist/commands/bot-context.d.ts +9 -3
  27. package/dist/commands/bot-context.d.ts.map +1 -1
  28. package/dist/commands/bot-context.js +38 -15
  29. package/dist/commands/bot-context.js.map +1 -1
  30. package/dist/commands/bot-members.d.ts +12 -0
  31. package/dist/commands/bot-members.d.ts.map +1 -0
  32. package/dist/commands/bot-members.js +136 -0
  33. package/dist/commands/bot-members.js.map +1 -0
  34. package/dist/commands/bot-reads.d.ts.map +1 -1
  35. package/dist/commands/bot-reads.js +50 -8
  36. package/dist/commands/bot-reads.js.map +1 -1
  37. package/dist/commands/bot-sends.d.ts +5 -0
  38. package/dist/commands/bot-sends.d.ts.map +1 -1
  39. package/dist/commands/bot-sends.js +56 -11
  40. package/dist/commands/bot-sends.js.map +1 -1
  41. package/dist/commands/bot-setup.d.ts +5 -0
  42. package/dist/commands/bot-setup.d.ts.map +1 -0
  43. package/dist/commands/bot-setup.js +107 -0
  44. package/dist/commands/bot-setup.js.map +1 -0
  45. package/dist/commands/bot-updates.d.ts +3 -0
  46. package/dist/commands/bot-updates.d.ts.map +1 -0
  47. package/dist/commands/bot-updates.js +182 -0
  48. package/dist/commands/bot-updates.js.map +1 -0
  49. package/dist/commands/bot.d.ts.map +1 -1
  50. package/dist/commands/bot.js +15 -0
  51. package/dist/commands/bot.js.map +1 -1
  52. package/dist/commands/chats.d.ts.map +1 -1
  53. package/dist/commands/chats.js +19 -6
  54. package/dist/commands/chats.js.map +1 -1
  55. package/dist/commands/check.d.ts +8 -0
  56. package/dist/commands/check.d.ts.map +1 -0
  57. package/dist/commands/check.js +64 -0
  58. package/dist/commands/check.js.map +1 -0
  59. package/dist/commands/context.d.ts +5 -0
  60. package/dist/commands/context.d.ts.map +1 -1
  61. package/dist/commands/context.js +5 -1
  62. package/dist/commands/context.js.map +1 -1
  63. package/dist/commands/hearing.d.ts +25 -0
  64. package/dist/commands/hearing.d.ts.map +1 -0
  65. package/dist/commands/hearing.js +41 -0
  66. package/dist/commands/hearing.js.map +1 -0
  67. package/dist/commands/inbox.d.ts.map +1 -1
  68. package/dist/commands/inbox.js +19 -3
  69. package/dist/commands/inbox.js.map +1 -1
  70. package/dist/commands/mcp.d.ts +1 -0
  71. package/dist/commands/mcp.d.ts.map +1 -1
  72. package/dist/commands/mcp.js +7 -4
  73. package/dist/commands/mcp.js.map +1 -1
  74. package/dist/commands/messages.d.ts.map +1 -1
  75. package/dist/commands/messages.js +21 -5
  76. package/dist/commands/messages.js.map +1 -1
  77. package/dist/commands/paging.d.ts +3 -1
  78. package/dist/commands/paging.d.ts.map +1 -1
  79. package/dist/commands/paging.js +4 -1
  80. package/dist/commands/paging.js.map +1 -1
  81. package/dist/commands/review.d.ts.map +1 -1
  82. package/dist/commands/review.js +5 -7
  83. package/dist/commands/review.js.map +1 -1
  84. package/dist/domain/models.d.ts +4 -0
  85. package/dist/domain/models.d.ts.map +1 -1
  86. package/dist/domain/models.js.map +1 -1
  87. package/dist/generated/client.generated.d.ts +1 -0
  88. package/dist/generated/client.generated.d.ts.map +1 -1
  89. package/dist/generated/client.generated.js +1 -0
  90. package/dist/generated/client.generated.js.map +1 -1
  91. package/dist/generated/opcodes.generated.d.ts +1 -0
  92. package/dist/generated/opcodes.generated.d.ts.map +1 -1
  93. package/dist/generated/opcodes.generated.js +1 -0
  94. package/dist/generated/opcodes.generated.js.map +1 -1
  95. package/dist/generated/operations.generated.d.ts +13 -0
  96. package/dist/generated/operations.generated.d.ts.map +1 -1
  97. package/dist/generated/operations.generated.js +2 -1
  98. package/dist/generated/operations.generated.js.map +1 -1
  99. package/dist/mcp/instructions.d.ts +2 -1
  100. package/dist/mcp/instructions.d.ts.map +1 -1
  101. package/dist/mcp/instructions.js +2 -1
  102. package/dist/mcp/instructions.js.map +1 -1
  103. package/dist/mcp/server.d.ts +2 -1
  104. package/dist/mcp/server.d.ts.map +1 -1
  105. package/dist/mcp/server.js +4 -1
  106. package/dist/mcp/server.js.map +1 -1
  107. package/dist/mcp/tools.d.ts +5 -1
  108. package/dist/mcp/tools.d.ts.map +1 -1
  109. package/dist/mcp/tools.js +150 -11
  110. package/dist/mcp/tools.js.map +1 -1
  111. package/dist/moderation/check.d.ts +98 -0
  112. package/dist/moderation/check.d.ts.map +1 -0
  113. package/dist/moderation/check.js +306 -0
  114. package/dist/moderation/check.js.map +1 -0
  115. package/dist/review.d.ts +10 -4
  116. package/dist/review.d.ts.map +1 -1
  117. package/dist/review.js +35 -53
  118. package/dist/review.js.map +1 -1
  119. package/dist/sends/journal.d.ts +1 -1
  120. package/dist/sends/journal.d.ts.map +1 -1
  121. package/dist/session/store.d.ts +2 -0
  122. package/dist/session/store.d.ts.map +1 -1
  123. package/dist/session/store.js +2 -0
  124. package/dist/session/store.js.map +1 -1
  125. package/dist/spec/index.d.ts.map +1 -1
  126. package/dist/spec/index.js +2 -1
  127. package/dist/spec/index.js.map +1 -1
  128. package/dist/spec/operations/messages.d.ts.map +1 -1
  129. package/dist/spec/operations/messages.js +9 -4
  130. package/dist/spec/operations/messages.js.map +1 -1
  131. package/dist/spec/operations/uploads.d.ts +13 -0
  132. package/dist/spec/operations/uploads.d.ts.map +1 -1
  133. package/dist/spec/operations/uploads.js +21 -0
  134. package/dist/spec/operations/uploads.js.map +1 -1
  135. package/dist/transcribe/index.d.ts +36 -1
  136. package/dist/transcribe/index.d.ts.map +1 -1
  137. package/dist/transcribe/index.js +91 -14
  138. package/dist/transcribe/index.js.map +1 -1
  139. package/dist/upload.d.ts +3 -0
  140. package/dist/upload.d.ts.map +1 -1
  141. package/dist/upload.js +13 -7
  142. package/dist/upload.js.map +1 -1
  143. package/dist/version.d.ts +1 -1
  144. package/dist/version.js +1 -1
  145. package/dist/voice.d.ts +9 -0
  146. package/dist/voice.d.ts.map +1 -0
  147. package/dist/voice.js +36 -0
  148. package/dist/voice.js.map +1 -0
  149. package/package.json +5 -2
  150. package/skills/max-cli/SKILL.md +10 -3
package/README.md CHANGED
@@ -6,7 +6,8 @@
6
6
 
7
7
  Ваши боты [MAX](https://max.ru) и ваш личный аккаунт — в терминале и в ИИ-агентах. `max` —
8
8
  программа для терминала и MCP-сервер: пишите от имени бота через официальный Bot API, а в свои
9
- чаты — сами или руками Claude, Codex, Cursor и других агентов.
9
+ чаты — сами или руками Claude, Codex, Cursor и других агентов. Для групп, которые вы ведёте, —
10
+ вопросы без ответа, новенькие и уборка спама по вашим правилам.
10
11
 
11
12
  ```sh
12
13
  max sales bot messages send "Команда продаж" "Сборка готова" # от имени бота
@@ -22,15 +23,21 @@ max messages send "Иван Петров" "Опаздываю на 15 минут
22
23
  ```sh
23
24
  max sales bot auth set # токен — в скрытом вводе, MAX его проверит
24
25
  max sales bot me # какой это бот
25
- max sales bot messages send -72894839451 "Сборка готова"
26
- max sales bot messages list -72894839451
27
- max sales bot recipients add -72894839451 # писать только сюда
26
+ max sales bot api get-updates --limit 10 # номер группы — в поле chat_id
27
+ max sales bot messages send "Команда продаж" "Сборка готова" --file report.pdf
28
+ max sales bot recipients add "Команда продаж" # писать только сюда
28
29
  max bot list --check # все боты на этом компьютере
29
30
  ```
30
31
 
32
+ Чат называют по номеру или, когда бот его уже видел, по названию; человеку пишут как
33
+ `user:<номер>`. Где взять номер чата — [docs/bot.md](docs/bot.md#где-взять-номер-чата).
34
+
31
35
  - **Сообщения и чаты.** Отправка в чат по номеру, человеку как `user:<номер>` или в чат по
32
36
  названию; правка, удаление, чтение, закрепление. `chats list` — чаты, которые бот видел: списка
33
37
  чатов бота у MAX нет.
38
+ - **Файлы, участники, комментарии, кнопки, меню команд, вебхуки.** `messages send --file`,
39
+ `members`, `admins`, `comments`, `callbacks answer`, `commands`, `webhooks` — у каждой записи тот
40
+ же список получателей и журнал.
34
41
  - **Любая операция API.** Все операции Bot API доступны как `max bot api <операция>` — с флагами
35
42
  для параметров и JSON-телом, которое сверяется со схемой до отправки.
36
43
  - **Бот не напишет лишнего.** У каждого бота свой список чатов, куда ему можно писать, и журнал
@@ -76,6 +83,9 @@ max messages send "Иван Петров" "Опаздываю на 15 минут
76
83
  - **Черновики ответов.** Агент предлагает текст, а отправляете вы — или он сам, если вы разрешили
77
84
  писать в этот чат.
78
85
  - **Поиск.** «Когда мы договорились о встрече с Иваном?» — ответ с датой и самим сообщением.
86
+ - **Группа, которую вы ведёте.** Какие вопросы висят без ответа, кто вступил за неделю и с каким
87
+ аккаунтом, удалить приглашения в чужие чаты — по вашим правилам и с вашим «да»
88
+ ([ниже](#группы-которые-вы-ведёте)).
79
89
 
80
90
  Как это выглядит:
81
91
 
@@ -98,6 +108,21 @@ max messages send "Иван Петров" "Опаздываю на 15 минут
98
108
  агента под каждую задачу — готовые запросы, расписание и ограничения:
99
109
  [docs/recipes.md](docs/recipes.md).
100
110
 
111
+ ## Группы, которые вы ведёте
112
+
113
+ ```sh
114
+ max review --chat "Поход" --unanswered # вопросы, на которые сутки никто не ответил
115
+ max chats events "Поход" # кто вступил и вышел за неделю
116
+ max chats members list "Поход" # все участники: когда заведён аккаунт
117
+ max chats rules set "Поход" invites delete # приглашения в чужие чаты — удалять
118
+ max chats check "Поход" --allow-dangerous # проверить группу и сделать, что разрешено
119
+ ```
120
+
121
+ Проверка сверяет новое в группе с вашими правилами: заблокированные люди, приглашения, ссылки,
122
+ пересланное, флуд, совсем новые аккаунты. По умолчанию она только отмечает. Для каждого действия
123
+ вы решаете сами: никогда, только с флагом, с вашим подтверждением или сразу. Сценарии с агентом,
124
+ все правила и ограничения — [docs/groups.md](docs/groups.md).
125
+
101
126
  ## Как это работает
102
127
 
103
128
  `max` работает от вашего имени, как ещё одно ваше устройство, а не как бот. Вы входите один раз
@@ -129,10 +154,12 @@ max messages send "Иван Петров" "Опаздываю на 15 минут
129
154
  расшифровка голосовых в текст на вашем компьютере, выгрузка переписки в JSONL или Markdown.
130
155
  - **Искать.** Сообщения — по тексту всего прочитанного, без подключения к MAX. Чаты и контакты —
131
156
  по части названия или имени.
132
- - **Писать.** Текст и разметка, ответы, фото и файлы, правка, пересылка, закрепление, реакции,
157
+ - **Писать.** Текст и разметка, ответы, фото, видео, файлы и голосовые, правка, пересылка, закрепление, реакции,
133
158
  отложенная отправка, удаление — у себя или у всех.
134
159
  - **Вести группы и каналы.** Создать группу, вступить по ссылке, выйти, участники и админы,
135
- название и настройки, заявки на вступление, ссылка-приглашение, папки.
160
+ название и настройки, заявки на вступление, ссылка-приглашение, папки. Для админа — вопросы без
161
+ ответа, кто вступил и вышел, все участники с возрастом аккаунта, правила и проверка группы по ним
162
+ ([docs/groups.md](docs/groups.md)).
136
163
  - **Контакты и аккаунт.** Найти человека по номеру, добавить, удалить, импортировать из файла;
137
164
  изменить профиль; посмотреть и завершить другие сеансы.
138
165
  - **Входить удобным способом.** QR-код прямо в терминале, QR-код или SMS через web.max.ru в
@@ -145,6 +172,10 @@ max messages send "Иван Петров" "Опаздываю на 15 минут
145
172
  - **Агент не напишет лишнего.** Есть профиль только для чтения, список чатов, в которые можно
146
173
  писать, и лимит сообщений в час. MCP-сервер может показывать вам каждое действие до того, как его выполнить.
147
174
  Если имя подходит к двум чатам, `max` не выбирает сам, а показывает оба.
175
+ - **Модерация по вашим правилам, а не по чужим.** Проверка группы по умолчанию только отмечает.
176
+ Удалить сообщение или человека она может, только если вы разрешили: для каждого действия —
177
+ никогда, с флагом, с вашим подтверждением или сразу. Не больше 10 действий за раз, и ничего не
178
+ следит за группой в фоне.
148
179
  - **Сообщение не уйдёт дважды.** Если связь оборвалась во время отправки, `max` прямо говорит, что
149
180
  не знает, дошло ли сообщение, и даёт команду для повтора. MAX узнаёт повтор и второго сообщения не
150
181
  создаёт.
@@ -192,9 +223,11 @@ max messages send "Иван Петров" "Опаздываю на 15 минут
192
223
  | удаление сообщений — у себя или у всех, с лимитом | ✅ | ✅ | — |
193
224
  | отметка «прочитано» по запросу | ✅ | ✅ | — |
194
225
  | группы и каналы: создать, вступить, участники, админы | ✅ | ✅ | чтение каналов |
226
+ | для админа группы: вопросы без ответа, новенькие, проверка по правилам | ✅ | — | — |
195
227
  | контакты, профиль, другие сеансы | ✅ | ✅ | — |
196
- | отправка видео и кружков | — | ✅ | — |
197
- | отправка голосовых | — | ⚠️ в 2.4.1 не проходит ([#102](https://github.com/MaxApiTeam/PyMax/issues/102), [#103](https://github.com/MaxApiTeam/PyMax/issues/103)) | — |
228
+ | отправка видео | ✅ | ✅ | — |
229
+ | отправка кружков | — | ✅ | — |
230
+ | отправка голосовых | ✅ | ⚠️ в 2.4.1 не проходит ([#102](https://github.com/MaxApiTeam/PyMax/issues/102), [#103](https://github.com/MaxApiTeam/PyMax/issues/103)) | — |
198
231
  | опросы | — | ✅ | — |
199
232
  | пароль и двухфакторный вход, фото профиля | — | ✅ | — |
200
233
 
@@ -205,6 +238,7 @@ max messages send "Иван Петров" "Опаздываю на 15 минут
205
238
 
206
239
  - [Бот](#бот)
207
240
  - [Личный аккаунт](#личный-аккаунт)
241
+ - [Группы, которые вы ведёте](#группы-которые-вы-ведёте)
208
242
  - [Установка](#установка)
209
243
  - [Вход](#вход)
210
244
  - [Использование](#использование)
@@ -380,6 +414,7 @@ max chats list --json
380
414
  - [docs/sessions.md](docs/sessions.md) — токен, хранилище паролей, профили
381
415
  - [docs/configuration.md](docs/configuration.md) — настройки, переменные, порядок разрешения
382
416
  - [docs/mcp.md](docs/mcp.md) — MCP-сервер: подключение, отправка, соединение с MAX
417
+ - [docs/groups.md](docs/groups.md) — группы, которые вы ведёте: сценарии с агентом, правила, проверка
383
418
  - [docs/recipes.md](docs/recipes.md) — рецепты для агентов: сводки, отчёты, долги, напоминания, расписание
384
419
  - [docs/diagnostics.md](docs/diagnostics.md) — `--trace`, `--record`, `max runs`
385
420
  - [docs/ROADMAP.md](docs/ROADMAP.md) — что планируется
@@ -412,7 +447,6 @@ pnpm generate # переписать сгенериро
412
447
 
413
448
  Ближайшее:
414
449
 
415
- - голосовые сообщения и видео;
416
450
  - опросы: показывать при чтении и голосовать;
417
451
  - локальная копия по желанию — можно работать совсем без неё.
418
452
 
@@ -11,7 +11,7 @@ export declare class BotApiClient {
11
11
  me(): Promise<BotInfoType>;
12
12
  chat(chatId: string): Promise<Chat>;
13
13
  message(messageId: string, selfId?: string): Promise<Message>;
14
- /** Newest last, as MAX returns them. */
14
+ /** Oldest first, like `max messages list`; MAX answers newest first (`BUG-59`). */
15
15
  messages(chatId: string, count: number, selfId?: string): Promise<Message[]>;
16
16
  /** A message from any answer — a send's result, an update — decoded the same way as a read. */
17
17
  decodeMessage(raw: unknown, selfId?: string): Message;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/bot/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAI3D,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAyB,MAAM,sBAAsB,CAAA;AAEzF,OAAO,EAAgB,KAAK,SAAS,EAAa,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAE/F,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,SAAS,iBAAiB,EAAe,CAAA;AAUrE,wEAAwE;AACxE,qBAAa,YAAY;;IAGvB,YAAY,OAAO,EAAE,gBAAgB,EAEpC;IAEK,EAAE,IAAI,OAAO,CAAC,WAAW,CAAC,CAU/B;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxC;IAEK,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGlE;IAED,wCAAwC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAKjF;IAED,+FAA+F;IAC/F,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAEpD;IAED,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAErE;CAyCF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/bot/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAI3D,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAyB,MAAM,sBAAsB,CAAA;AAEzF,OAAO,EAAgB,KAAK,SAAS,EAAa,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAE/F,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,SAAS,iBAAiB,EAAe,CAAA;AAUrE,wEAAwE;AACxE,qBAAa,YAAY;;IAGvB,YAAY,OAAO,EAAE,gBAAgB,EAEpC;IAEK,EAAE,IAAI,OAAO,CAAC,WAAW,CAAC,CAU/B;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxC;IAEK,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGlE;IAED,mFAAmF;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAKjF;IAED,+FAA+F;IAC/F,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAEpD;IAED,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAErE;CAyCF"}
@@ -38,12 +38,12 @@ export class BotApiClient {
38
38
  const answer = await this.#transport.call(required("getMessageById"), { path: { messageId } });
39
39
  return this.#message(answer, selfId);
40
40
  }
41
- /** Newest last, as MAX returns them. */
41
+ /** Oldest first, like `max messages list`; MAX answers newest first (`BUG-59`). */
42
42
  async messages(chatId, count, selfId) {
43
43
  const answer = (await this.#transport.call(required("getMessages"), {
44
44
  query: { chat_id: chatId, count: String(count) },
45
45
  }));
46
- return (answer?.messages ?? []).map((raw) => this.#message(raw, selfId));
46
+ return (answer?.messages ?? []).map((raw) => this.#message(raw, selfId)).reverse();
47
47
  }
48
48
  /** A message from any answer — a send's result, an update — decoded the same way as a read. */
49
49
  decodeMessage(raw, selfId) {
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/bot/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/bot/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Chat, Message } from "@leemour/cli-messaging";
2
+ import { type AccountKey, type IngestedVia, type MessageStore } from "@leemour/cli-messaging/store";
3
+ import type { ChatRegistry } from "./registry.js";
4
+ /** How a bot's messages are told apart from a personal account's in the shared store and its locators. */
5
+ export declare const PROVIDER = "max-bot";
6
+ export declare const accountOf: (botId: string) => AccountKey;
7
+ export declare const keep: (botId: string, messages: readonly Message[], via: IngestedVia, warn: (message: string) => void) => Promise<boolean>;
8
+ /**
9
+ * Only a chat MAX has just described in full: the store replaces a title it is not given
10
+ * (`RISK-53`), and the partial facts stay in the registry, which merges them.
11
+ */
12
+ export declare const keepChat: (botId: string | undefined, chat: Chat, warn: (message: string) => void) => Promise<void>;
13
+ /**
14
+ * A message MAX says was deleted. The store keeps it as a tombstone: reads, `--offline` and
15
+ * search stop returning it.
16
+ */
17
+ export declare const forget: (botId: string, removals: readonly {
18
+ chatId: string;
19
+ messageId: string;
20
+ }[], warn: (message: string) => void) => Promise<boolean>;
21
+ /** A send's answer names the bot as its sender, so the store needs no `me()` to know whose it is. */
22
+ export declare const keepSent: (registry: ChatRegistry, sent: unknown, warn: (message: string) => void) => Promise<void>;
23
+ /** For `--offline` and `search`: here the store is the answer, so a failure is the command's failure. */
24
+ export declare const fromStore: <T>(read: (store: MessageStore) => T) => Promise<T>;
25
+ //# sourceMappingURL=keep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keep.d.ts","sourceRoot":"","sources":["../../src/bot/keep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAa,MAAM,8BAA8B,CAAA;AAC9G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,0GAA0G;AAC1G,eAAO,MAAM,QAAQ,YAAY,CAAA;AAIjC,eAAO,MAAM,SAAS,UAAW,MAAM,KAAG,UAAsD,CAAA;AAkChG,eAAO,MAAM,IAAI,UACR,MAAM,YACH,SAAS,OAAO,EAAE,OACvB,WAAW,QACV,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,KAC9B,OAAO,CAAC,OAAO,CASjB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,UAAiB,MAAM,GAAG,SAAS,QAAQ,IAAI,QAAQ,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,kBAEpG,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,MAAM,UACV,MAAM,YACH,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,QACpD,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,KAC9B,OAAO,CAAC,OAAO,CAMjB,CAAA;AAED,qGAAqG;AACrG,eAAO,MAAM,QAAQ,aAAoB,YAAY,QAAQ,OAAO,QAAQ,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,kBAKpG,CAAA;AAED,yGAAyG;AACzG,eAAO,MAAM,SAAS,GAAU,CAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,KAAK,CAAC,KAAG,OAAO,CAAC,CAAC,CAY9E,CAAA"}
@@ -0,0 +1,96 @@
1
+ import { CliError } from "@leemour/cli-core";
2
+ import { openStore } from "@leemour/cli-messaging/store";
3
+ /** How a bot's messages are told apart from a personal account's in the shared store and its locators. */
4
+ export const PROVIDER = "max-bot";
5
+ const CHAT_ID = /^-?\d+$/;
6
+ export const accountOf = (botId) => ({ provider: PROVIDER, account: botId });
7
+ /**
8
+ * Only a real chat and a real message: the fallback decoder says `unknown`, and `user:<id>` is a
9
+ * send address — stored, either would make a chat that does not exist (`RISK-54`).
10
+ */
11
+ const storable = (message) => CHAT_ID.test(message.chatId) && message.id.startsWith("mid.");
12
+ /** The reason, never the path — a home directory is nobody else's business. */
13
+ const reasonOf = (error) => {
14
+ const code = error?.code;
15
+ if (typeof code === "string")
16
+ return code;
17
+ return error instanceof Error ? error.name : "an unknown problem";
18
+ };
19
+ /**
20
+ * **A store that fails never fails the command** — MAX answered, and the answer is printed either
21
+ * way; the reason goes to stderr. Returns whether it was saved, for `updates watch`, which must not
22
+ * move its marker past what it failed to keep.
23
+ */
24
+ const quietly = async (write, warn) => {
25
+ let store;
26
+ try {
27
+ store = await openStore();
28
+ write(store);
29
+ return true;
30
+ }
31
+ catch (error) {
32
+ warn(`the local copy was not updated: ${reasonOf(error)}`);
33
+ return false;
34
+ }
35
+ finally {
36
+ store?.close();
37
+ }
38
+ };
39
+ export const keep = async (botId, messages, via, warn) => {
40
+ const byChat = new Map();
41
+ for (const message of messages.filter(storable)) {
42
+ byChat.set(message.chatId, [...(byChat.get(message.chatId) ?? []), message]);
43
+ }
44
+ if (byChat.size === 0)
45
+ return true;
46
+ return quietly((store) => {
47
+ for (const [chatId, chatMessages] of byChat)
48
+ store.saveMessages(accountOf(botId), chatId, chatMessages, { via });
49
+ }, warn);
50
+ };
51
+ /**
52
+ * Only a chat MAX has just described in full: the store replaces a title it is not given
53
+ * (`RISK-53`), and the partial facts stay in the registry, which merges them.
54
+ */
55
+ export const keepChat = async (botId, chat, warn) => {
56
+ if (botId && CHAT_ID.test(chat.id))
57
+ await quietly((store) => store.saveChats(accountOf(botId), [chat]), warn);
58
+ };
59
+ /**
60
+ * A message MAX says was deleted. The store keeps it as a tombstone: reads, `--offline` and
61
+ * search stop returning it.
62
+ */
63
+ export const forget = async (botId, removals, warn) => {
64
+ const known = removals.filter((removal) => CHAT_ID.test(removal.chatId));
65
+ if (known.length === 0)
66
+ return true;
67
+ return quietly((store) => {
68
+ for (const { chatId, messageId } of known)
69
+ store.markDeleted(accountOf(botId), [messageId], { chatId });
70
+ }, warn);
71
+ };
72
+ /** A send's answer names the bot as its sender, so the store needs no `me()` to know whose it is. */
73
+ export const keepSent = async (registry, sent, warn) => {
74
+ const message = sent;
75
+ if (typeof message?.senderId !== "string" || typeof message.id !== "string")
76
+ return;
77
+ registry.rememberBot(message.senderId);
78
+ await keep(message.senderId, [message], "send", warn);
79
+ };
80
+ /** For `--offline` and `search`: here the store is the answer, so a failure is the command's failure. */
81
+ export const fromStore = async (read) => {
82
+ let store;
83
+ try {
84
+ store = await openStore();
85
+ }
86
+ catch (error) {
87
+ throw new CliError("configuration_error", `the local copy cannot be opened: ${reasonOf(error)}`);
88
+ }
89
+ try {
90
+ return read(store);
91
+ }
92
+ finally {
93
+ store.close();
94
+ }
95
+ };
96
+ //# sourceMappingURL=keep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keep.js","sourceRoot":"","sources":["../../src/bot/keep.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,9 @@ export declare class ChatRegistry {
15
15
  #private;
16
16
  constructor(profile: string, env?: NodeJS.ProcessEnv);
17
17
  list(): SeenChat[];
18
+ /** The bot's own user id, once any command has learned it — the key its messages are stored under. */
19
+ botId(): string | undefined;
20
+ rememberBot(id: string): void;
18
21
  /** Makes this bot known to `max bot list` before it has seen any chat. */
19
22
  touch(): void;
20
23
  /** Newer facts win; a chat seen only as an id keeps the title it had. */
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/bot/registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAA;AAElD,MAAM,WAAW,QAAS,SAAQ,IAAI;IACpC,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,8FAA8F;AAC9F,eAAO,MAAM,aAAa,SAAS,MAAM,CAAC,UAAU,KAAiB,MACS,CAAA;AAE9E;;;;;GAKG;AACH,qBAAa,YAAY;;IAGvB,YAAY,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,EAEhE;IAED,IAAI,IAAI,QAAQ,EAAE,CAQjB;IAED,0EAA0E;IAC1E,KAAK,IAAI,IAAI,CAEZ;IAED,yEAAyE;IACzE,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,EAAE,GAAG,GAAE,IAAiB,GAAG,IAAI,CAuBxF;CACF;AAED,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAS,MAAM,CAAC,UAAU,KAAiB,MAAM,EAO7E,CAAA"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/bot/registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAA;AAElD,MAAM,WAAW,QAAS,SAAQ,IAAI;IACpC,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,8FAA8F;AAC9F,eAAO,MAAM,aAAa,SAAS,MAAM,CAAC,UAAU,KAAiB,MACS,CAAA;AAE9E;;;;;GAKG;AACH,qBAAa,YAAY;;IAGvB,YAAY,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,EAEhE;IAED,IAAI,IAAI,QAAQ,EAAE,CAGjB;IAED,sGAAsG;IACtG,KAAK,IAAI,MAAM,GAAG,SAAS,CAG1B;IAED,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAG5B;IAED,0EAA0E;IAC1E,KAAK,IAAI,IAAI,CAEZ;IAED,yEAAyE;IACzE,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,EAAE,GAAG,GAAE,IAAiB,GAAG,IAAI,CAuBxF;CAeF;AAED,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAS,MAAM,CAAC,UAAU,KAAiB,MAAM,EAO7E,CAAA"}
@@ -15,15 +15,18 @@ export class ChatRegistry {
15
15
  this.#path = join(botsDirectory(env), `${profile}.json`);
16
16
  }
17
17
  list() {
18
- if (!existsSync(this.#path))
19
- return [];
20
- try {
21
- const parsed = JSON.parse(readFileSync(this.#path, "utf8"));
22
- return Array.isArray(parsed.chats) ? parsed.chats : [];
23
- }
24
- catch {
25
- return [];
26
- }
18
+ const { chats } = this.#read();
19
+ return Array.isArray(chats) ? chats : [];
20
+ }
21
+ /** The bot's own user id, once any command has learned it — the key its messages are stored under. */
22
+ botId() {
23
+ const { botId } = this.#read();
24
+ return typeof botId === "string" ? botId : undefined;
25
+ }
26
+ rememberBot(id) {
27
+ const file = this.#read();
28
+ if (file.botId !== id)
29
+ this.#write({ ...file, botId: id });
27
30
  }
28
31
  /** Makes this bot known to `max bot list` before it has seen any chat. */
29
32
  touch() {
@@ -52,7 +55,21 @@ export class ChatRegistry {
52
55
  lastSeenAt: at,
53
56
  });
54
57
  }
55
- writeSecurely(this.#path, `${JSON.stringify({ chats: [...byId.values()] }, null, 2)}\n`, 0o600);
58
+ this.#write({ ...this.#read(), chats: [...byId.values()] });
59
+ }
60
+ #read() {
61
+ if (!existsSync(this.#path))
62
+ return {};
63
+ try {
64
+ const parsed = JSON.parse(readFileSync(this.#path, "utf8"));
65
+ return parsed && typeof parsed === "object" ? parsed : {};
66
+ }
67
+ catch {
68
+ return {};
69
+ }
70
+ }
71
+ #write(file) {
72
+ writeSecurely(this.#path, `${JSON.stringify(file, null, 2)}\n`, 0o600);
56
73
  }
57
74
  }
58
75
  /** Every profile that has ever kept a registry — the bots this machine has used. */
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/bot/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/bot/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type SleepLike } from "@leemour/cli-core";
2
+ import { type FetchLike } from "@leemour/cli-core/http";
3
+ export declare const UPLOAD_TYPES: readonly ["image", "video", "audio", "file"];
4
+ export type UploadType = (typeof UPLOAD_TYPES)[number];
5
+ export declare const uploadTypeOf: (path: string) => UploadType;
6
+ /** What goes into a message body's `attachments`. */
7
+ export interface Attachment {
8
+ type: UploadType;
9
+ payload: Record<string, unknown>;
10
+ }
11
+ export interface UploadEndpoint {
12
+ url: string;
13
+ token?: string | null;
14
+ }
15
+ export declare const endpointOf: (answer: unknown) => UploadEndpoint;
16
+ /**
17
+ * The second step: the bytes, streamed from disk as multipart field `data`. The upload URL is not
18
+ * the Bot API host and is itself a credential, so it gets no token and never appears in an error.
19
+ * Video and audio answer XML and are named by step one's token; image and file by the answer.
20
+ */
21
+ export declare const uploadFile: (options: {
22
+ path: string;
23
+ type: UploadType;
24
+ endpoint: UploadEndpoint;
25
+ fetch: FetchLike;
26
+ signal?: AbortSignal;
27
+ timeoutMs?: number;
28
+ }) => Promise<Attachment>;
29
+ /**
30
+ * Repeats a send only while MAX refuses it as not ready — a refusal, so nothing was sent. Any
31
+ * other failure, an unclear outcome above all, goes straight back to the caller.
32
+ */
33
+ export declare const whenAttachmentReady: <T>(send: () => Promise<T>, signal?: AbortSignal, sleep?: SleepLike) => Promise<T>;
34
+ //# sourceMappingURL=uploads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../src/bot/uploads.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACvF,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,wBAAwB,CAAA;AAIrE,eAAO,MAAM,YAAY,YAAI,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAA;AACxE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AAwBtD,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,UAA0E,CAAA;AAEtH,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,eAAO,MAAM,UAAU,WAAY,OAAO,KAAG,cAQ5C,CAAA;AAID;;;;GAIG;AACH,eAAO,MAAM,UAAU,YAAmB;IACxC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,SAAS,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,KAAG,OAAO,CAAC,UAAU,CAkDrB,CAAA;AAWD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAU,CAAC,QACnC,MAAM,OAAO,CAAC,CAAC,CAAC,WACb,WAAW,UACb,SAAS,KACf,OAAO,CAAC,CAAC,CAUX,CAAA"}
@@ -0,0 +1,122 @@
1
+ import { openAsBlob, statSync } from "node:fs";
2
+ import { basename, extname } from "node:path";
3
+ import { CliError, realSleep } from "@leemour/cli-core";
4
+ import { statusToCode } from "@leemour/cli-core/http";
5
+ import { parse } from "lossless-json";
6
+ import { plainJson } from "./transport.js";
7
+ export const UPLOAD_TYPES = ["image", "video", "audio", "file"];
8
+ const BY_EXTENSION = {
9
+ jpg: "image",
10
+ jpeg: "image",
11
+ png: "image",
12
+ gif: "image",
13
+ tif: "image",
14
+ tiff: "image",
15
+ bmp: "image",
16
+ heic: "image",
17
+ mp4: "video",
18
+ mov: "video",
19
+ mkv: "video",
20
+ webm: "video",
21
+ mp3: "audio",
22
+ wav: "audio",
23
+ m4a: "audio",
24
+ ogg: "audio",
25
+ opus: "audio",
26
+ aac: "audio",
27
+ flac: "audio",
28
+ };
29
+ export const uploadTypeOf = (path) => BY_EXTENSION[extname(path).slice(1).toLowerCase()] ?? "file";
30
+ export const endpointOf = (answer) => {
31
+ const endpoint = plainJson(answer);
32
+ if (typeof endpoint?.url !== "string") {
33
+ throw new CliError("invalid_response", "MAX answered getUploadUrl with no upload URL", {
34
+ operation: "getUploadUrl",
35
+ });
36
+ }
37
+ return { url: endpoint.url, token: endpoint.token ?? null };
38
+ };
39
+ const UPLOAD_TIMEOUT_MS = 10 * 60_000;
40
+ /**
41
+ * The second step: the bytes, streamed from disk as multipart field `data`. The upload URL is not
42
+ * the Bot API host and is itself a credential, so it gets no token and never appears in an error.
43
+ * Video and audio answer XML and are named by step one's token; image and file by the answer.
44
+ */
45
+ export const uploadFile = async (options) => {
46
+ const { path, type, endpoint } = options;
47
+ const name = basename(path);
48
+ const stat = (() => {
49
+ try {
50
+ return statSync(path);
51
+ }
52
+ catch {
53
+ return undefined;
54
+ }
55
+ })();
56
+ if (!stat?.isFile())
57
+ throw new CliError("not_found", `no file at ${path}`);
58
+ const form = new FormData();
59
+ form.append("data", await openAsBlob(path), name);
60
+ const signals = [
61
+ AbortSignal.timeout(options.timeoutMs ?? UPLOAD_TIMEOUT_MS),
62
+ ...(options.signal ? [options.signal] : []),
63
+ ];
64
+ let response;
65
+ try {
66
+ response = await options.fetch(endpoint.url, { method: "POST", body: form, signal: AbortSignal.any(signals) });
67
+ }
68
+ catch {
69
+ throw new CliError("network_error", `the upload of ${name} did not finish; nothing was sent to the chat`, {
70
+ retryable: false,
71
+ });
72
+ }
73
+ const text = await response.text();
74
+ if (!response.ok) {
75
+ throw new CliError(statusToCode(response.status), `the upload server refused ${name} (${response.status}); nothing was sent to the chat`, { status: response.status });
76
+ }
77
+ if (type === "video" || type === "audio") {
78
+ if (!endpoint.token) {
79
+ throw new CliError("invalid_response", `MAX gave no token for a ${type} upload`, { operation: "getUploadUrl" });
80
+ }
81
+ return { type, payload: { token: endpoint.token } };
82
+ }
83
+ let answer;
84
+ try {
85
+ answer = plainJson(parse(text));
86
+ }
87
+ catch {
88
+ throw new CliError("invalid_response", `the upload server answered ${name} with something that is not JSON`);
89
+ }
90
+ if (type === "image" && answer.photos)
91
+ return { type, payload: { photos: answer.photos } };
92
+ if (typeof answer.token !== "string") {
93
+ throw new CliError("invalid_response", `the upload server answered ${name} with no token`);
94
+ }
95
+ return { type, payload: { token: answer.token } };
96
+ };
97
+ /** maxigo-client's `DefaultRetryIntervals` (`options.go`): MAX is still processing the file. */
98
+ const NOT_READY_WAITS_MS = [500, 1000, 2000, 5000];
99
+ const notReady = (error) => {
100
+ const failure = error;
101
+ if (failure.code === "outcome_unknown")
102
+ return false;
103
+ return /not\.ready|not\.processed/.test(`${failure.details?.maxCode ?? ""} ${failure.message ?? ""}`);
104
+ };
105
+ /**
106
+ * Repeats a send only while MAX refuses it as not ready — a refusal, so nothing was sent. Any
107
+ * other failure, an unclear outcome above all, goes straight back to the caller.
108
+ */
109
+ export const whenAttachmentReady = async (send, signal, sleep = realSleep) => {
110
+ for (let attempt = 0;; attempt++) {
111
+ try {
112
+ return await send();
113
+ }
114
+ catch (error) {
115
+ const wait = NOT_READY_WAITS_MS[attempt];
116
+ if (wait === undefined || !notReady(error))
117
+ throw error;
118
+ await sleep(wait, signal, "retry");
119
+ }
120
+ }
121
+ };
122
+ //# sourceMappingURL=uploads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploads.js","sourceRoot":"","sources":["../../src/bot/uploads.ts"],"names":[],"mappings":"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"}