@mikitasazan/notify 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/dist/cli.js +69 -60
- package/dist/events.d.ts +127 -117
- package/dist/events.js +30 -24
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/render.d.ts +57 -49
- package/dist/render.js +255 -221
- package/dist/routes.d.ts +31 -27
- package/dist/routes.js +25 -20
- package/dist/send.d.ts +1 -19
- package/dist/send.js +2 -33
- package/package.json +1 -1
package/dist/routes.d.ts
CHANGED
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Routing "event → where to send." The ONLY place that a new project
|
|
3
|
+
* touches: add a row to `ROUTES` — no new bot, no new secret, no edits in
|
|
4
|
+
* the projects themselves.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* The scheme: ONE FORUM PER PROJECT, with an "⚙️ Ops" tab (robot
|
|
7
|
+
* notifications) and a "💬 Dev" tab (people's live chat) inside it.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
9
|
+
* Why not one shared forum with a topic per project — that was the first
|
|
10
|
+
* version, and it turned out to be a mistake: Telegram cannot hide one
|
|
11
|
+
* topic from a member — whoever is in the group sees ALL the tabs. That
|
|
12
|
+
* means employees cannot be let in, they need their own chat, and one
|
|
13
|
+
* project ends up living in two places at once ("the owner's topic" +
|
|
14
|
+
* "the team's channel"). A forum per project removes the duplication: an
|
|
15
|
+
* employee is added to their own project's forum and does not see anyone
|
|
16
|
+
* else's, and the scheme is the same for everyone — people show up on a
|
|
17
|
+
* new project, just add them to the same forum.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Chat and topic ids are not a secret: without the bot's token they are
|
|
20
|
+
* useless. So they live in code, not in environment variables (otherwise
|
|
21
|
+
* "add a project" means edits in three places). There is exactly one
|
|
22
|
+
* secret — `OPS_BOT_TOKEN`.
|
|
21
23
|
*/
|
|
22
24
|
import type { NotifyEvent, Project } from './events.ts';
|
|
23
25
|
type Forum = {
|
|
24
|
-
/**
|
|
26
|
+
/** The id of the project's forum supergroup. */
|
|
25
27
|
chat: string;
|
|
26
|
-
/**
|
|
28
|
+
/** The "⚙️ Ops" tab — robots write here. */
|
|
27
29
|
ops: number;
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
31
|
+
* The "💬 Dev" tab — people. The bot does not write here; the field is
|
|
32
|
+
* kept for completeness. Optional: an infrastructure forum has no people
|
|
33
|
+
* and nothing to discuss, and an empty tab would read as a forgotten
|
|
34
|
+
* setting, not as room to grow into.
|
|
32
35
|
*/
|
|
33
36
|
dev?: number;
|
|
34
37
|
};
|
|
@@ -39,13 +42,14 @@ export type Target = {
|
|
|
39
42
|
silent: boolean;
|
|
40
43
|
};
|
|
41
44
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
45
|
+
* Where an event goes. Everything goes to its own project's "Ops" tab; a
|
|
46
|
+
* red one arrives there too, just with sound.
|
|
44
47
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
48
|
+
* There is no separate "incidents" topic any more: it made sense while the
|
|
49
|
+
* forum was shared across all projects. Now every project has its own
|
|
50
|
+
* group, and an incident is visible in that same feed — there is no reason
|
|
51
|
+
* to funnel them into one place, and the sound already tells an incident
|
|
52
|
+
* apart from an ordinary message.
|
|
49
53
|
*/
|
|
50
54
|
export declare const targets: (e: NotifyEvent) => Target[];
|
|
51
55
|
export {};
|
package/dist/routes.js
CHANGED
|
@@ -1,38 +1,43 @@
|
|
|
1
1
|
import { severity } from "./events.js";
|
|
2
2
|
export const ROUTES = {
|
|
3
|
-
// ops/dev = 22/23,
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// ops/dev = 22/23, not 3/4: the old tabs were deleted by hand on
|
|
4
|
+
// 27.07.2026, and Telegram removes a topic's messages along with it. A
|
|
5
|
+
// recreated topic gets a NEW id — a topic's id is the id of its first
|
|
6
|
+
// message, it is never reused.
|
|
6
7
|
arvent: { chat: '-1004299939100', ops: 22, dev: 23 },
|
|
7
8
|
playhub: { chat: '-1004418379613', ops: 3, dev: 4 },
|
|
8
9
|
'game-publisher': { chat: '-1004292453693', ops: 3, dev: 4 },
|
|
9
10
|
'one-q': { chat: '-1004466909784', ops: 3, dev: 4 },
|
|
10
|
-
//
|
|
11
|
-
//
|
|
11
|
+
// No `dev`: the safe is infrastructure, there is no one to discuss it
|
|
12
|
+
// with. Only reports from the weekly check, and only when something
|
|
13
|
+
// broke.
|
|
12
14
|
vault: { chat: '-1004459314999', ops: 3 },
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
+
// Also infrastructure, no `dev`: the daily task digest and the Monday
|
|
16
|
+
// stumbles summary. Before this row, both used to die silently on
|
|
17
|
+
// "unknown project."
|
|
15
18
|
'mac-config': { chat: '-1004442522004', ops: 2 },
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
19
|
+
// The owner's corporate project: only its own reports on "what the
|
|
20
|
+
// publish delivered" (ali98x-sentry). No `dev` — the project's team lives
|
|
21
|
+
// in other systems, there are no people here. Until 18.08.2026 reports
|
|
22
|
+
// were getting lost on "unknown project" for weeks.
|
|
20
23
|
alitools: { chat: '-1003904331479', ops: 3 }
|
|
21
24
|
};
|
|
22
25
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
26
|
+
* Where an event goes. Everything goes to its own project's "Ops" tab; a
|
|
27
|
+
* red one arrives there too, just with sound.
|
|
25
28
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
29
|
+
* There is no separate "incidents" topic any more: it made sense while the
|
|
30
|
+
* forum was shared across all projects. Now every project has its own
|
|
31
|
+
* group, and an incident is visible in that same feed — there is no reason
|
|
32
|
+
* to funnel them into one place, and the sound already tells an incident
|
|
33
|
+
* apart from an ordinary message.
|
|
30
34
|
*/
|
|
31
35
|
export const targets = (e) => {
|
|
32
36
|
const forum = ROUTES[e.project];
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
37
|
+
// An unknown project — a typo in `--project`, or a project forgotten in
|
|
38
|
+
// ROUTES. We return an empty list rather than throw: a notification has
|
|
39
|
+
// no right to bring down the deploy or cron job that called it (in bash
|
|
40
|
+
// with `set -e` a throw here would be fatal).
|
|
36
41
|
if (!forum) {
|
|
37
42
|
console.error(`[notify] unknown project "${e.project}" — known: ${Object.keys(ROUTES).join(', ')}`);
|
|
38
43
|
return [];
|
package/dist/send.d.ts
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
import type { NotifyEvent
|
|
1
|
+
import type { NotifyEvent } from './events.ts';
|
|
2
2
|
export type SendResult = 'sent' | 'skipped' | 'failed';
|
|
3
3
|
export declare const notify: (e: NotifyEvent) => Promise<SendResult>;
|
|
4
|
-
/**
|
|
5
|
-
* Готовый HTML во вкладку «Ops» проекта — ТОЛЬКО для дневных отчётов.
|
|
6
|
-
*
|
|
7
|
-
* Зачем исключение из правила «свободного текста в API нет». Отчёт — это не
|
|
8
|
-
* событие: в нём плотная строка вроде
|
|
9
|
-
* `🎮 1284 игр (🍎 412 iOS +3 · 🤖 890 Android +5) | 📈 +240 запусков`,
|
|
10
|
-
* и разложить её в `label=value` можно только испортив. Но транспорт у отчёта
|
|
11
|
-
* ТОТ ЖЕ: ретраи, 429, таймауты, curl-фолбэк, номер вкладки. Пока его копировали
|
|
12
|
-
* в каждый скрипт, один и тот же баг с дублями на таймауте пришлось чинить
|
|
13
|
-
* дважды — в пакете и в game-publisher (27.07.2026).
|
|
14
|
-
*
|
|
15
|
-
* Граница: формат событий по-прежнему задаёт только пакет, «своё» уведомление
|
|
16
|
-
* о деплое или упавшей задаче написать нельзя. Здесь стандартизирован транспорт,
|
|
17
|
-
* а не формат — и в этом весь смысл.
|
|
18
|
-
*
|
|
19
|
-
* Всегда беззвучно: отчёт читают утром, а не по звонку.
|
|
20
|
-
*/
|
|
21
|
-
export declare const sendReport: (project: Project, html: string, key?: string) => Promise<SendResult>;
|
package/dist/send.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { execFileSync } from 'node:child_process';
|
|
16
16
|
import { readFileSync } from 'node:fs';
|
|
17
17
|
import { basename } from 'node:path';
|
|
18
|
-
import {
|
|
18
|
+
import { render } from "./render.js";
|
|
19
19
|
import { ROUTES, targets } from "./routes.js";
|
|
20
20
|
const log = (msg) => {
|
|
21
21
|
// stderr, не stdout — stdout зарезервирован под возможный машинный вывод CLI.
|
|
@@ -133,7 +133,7 @@ const sendOne = async (token, target, text) => {
|
|
|
133
133
|
log('out of send attempts');
|
|
134
134
|
return 'failed';
|
|
135
135
|
};
|
|
136
|
-
/**
|
|
136
|
+
/** The shared tail of `notify()`: token, targets, delivery one after another. */
|
|
137
137
|
const deliver = async (where, text) => {
|
|
138
138
|
const token = process.env.OPS_BOT_TOKEN?.trim();
|
|
139
139
|
if (!token) {
|
|
@@ -285,34 +285,3 @@ export const notify = async (e) => {
|
|
|
285
285
|
}
|
|
286
286
|
return deliver(targets(e), render(e));
|
|
287
287
|
};
|
|
288
|
-
/**
|
|
289
|
-
* Готовый HTML во вкладку «Ops» проекта — ТОЛЬКО для дневных отчётов.
|
|
290
|
-
*
|
|
291
|
-
* Зачем исключение из правила «свободного текста в API нет». Отчёт — это не
|
|
292
|
-
* событие: в нём плотная строка вроде
|
|
293
|
-
* `🎮 1284 игр (🍎 412 iOS +3 · 🤖 890 Android +5) | 📈 +240 запусков`,
|
|
294
|
-
* и разложить её в `label=value` можно только испортив. Но транспорт у отчёта
|
|
295
|
-
* ТОТ ЖЕ: ретраи, 429, таймауты, curl-фолбэк, номер вкладки. Пока его копировали
|
|
296
|
-
* в каждый скрипт, один и тот же баг с дублями на таймауте пришлось чинить
|
|
297
|
-
* дважды — в пакете и в game-publisher (27.07.2026).
|
|
298
|
-
*
|
|
299
|
-
* Граница: формат событий по-прежнему задаёт только пакет, «своё» уведомление
|
|
300
|
-
* о деплое или упавшей задаче написать нельзя. Здесь стандартизирован транспорт,
|
|
301
|
-
* а не формат — и в этом весь смысл.
|
|
302
|
-
*
|
|
303
|
-
* Всегда беззвучно: отчёт читают утром, а не по звонку.
|
|
304
|
-
*/
|
|
305
|
-
export const sendReport = async (project, html, key) => {
|
|
306
|
-
if (!Object.hasOwn(ROUTES, project)) {
|
|
307
|
-
// Та же красная карточка, что у notify(): дверь свободного HTML — не
|
|
308
|
-
// лазейка для молчаливой потери (этот класс уже жил неделями дважды).
|
|
309
|
-
await reportLostProject(project, 'report');
|
|
310
|
-
return 'skipped';
|
|
311
|
-
}
|
|
312
|
-
const forum = ROUTES[project];
|
|
313
|
-
// Без проекта — как и render.ts: карточка уже лежит в форуме своего
|
|
314
|
-
// проекта, дублировать его в теге незачем. Строка тегов ПЕРВАЯ и до обрезки,
|
|
315
|
-
// как у любой другой карточки.
|
|
316
|
-
const tags = reportTags(key ?? 'daily');
|
|
317
|
-
return deliver([{ chat: forum.chat, thread: forum.ops, silent: true }], `${tags}\n${clampMessage(html, Math.max(64, 4000 - tags.length - 1))}`);
|
|
318
|
-
};
|
package/package.json
CHANGED