@leemour/cli-messaging 0.87.0 → 0.88.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 +1 -0
- package/dist/background/index.d.ts +5 -0
- package/dist/background/index.d.ts.map +1 -0
- package/dist/background/index.js +5 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/lock.d.ts +17 -0
- package/dist/background/lock.d.ts.map +1 -0
- package/dist/background/lock.js +34 -0
- package/dist/background/lock.js.map +1 -0
- package/dist/background/processes.d.ts.map +1 -0
- package/dist/background/processes.js.map +1 -0
- package/dist/background/system.d.ts +20 -0
- package/dist/background/system.d.ts.map +1 -0
- package/dist/background/system.js +39 -0
- package/dist/background/system.js.map +1 -0
- package/dist/background/units.d.ts +42 -0
- package/dist/background/units.d.ts.map +1 -0
- package/dist/background/units.js +178 -0
- package/dist/background/units.js.map +1 -0
- package/dist/cli/config-command.d.ts.map +1 -1
- package/dist/cli/config-command.js +17 -5
- package/dist/cli/config-command.js.map +1 -1
- package/dist/cli/context.d.ts +3 -0
- package/dist/cli/context.d.ts.map +1 -1
- package/dist/cli/context.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/messenger/admin-moderation-command.d.ts +11 -0
- package/dist/cli/messenger/admin-moderation-command.d.ts.map +1 -0
- package/dist/cli/messenger/admin-moderation-command.js +83 -0
- package/dist/cli/messenger/admin-moderation-command.js.map +1 -0
- package/dist/cli/messenger/after.d.ts +2 -2
- package/dist/cli/messenger/after.d.ts.map +1 -1
- package/dist/cli/messenger/after.js +3 -2
- package/dist/cli/messenger/after.js.map +1 -1
- package/dist/cli/messenger/ask.d.ts +2 -0
- package/dist/cli/messenger/ask.d.ts.map +1 -1
- package/dist/cli/messenger/ask.js +7 -0
- package/dist/cli/messenger/ask.js.map +1 -1
- package/dist/cli/messenger/backfill-jobs.d.ts.map +1 -1
- package/dist/cli/messenger/backfill-jobs.js +1 -1
- package/dist/cli/messenger/backfill-jobs.js.map +1 -1
- package/dist/cli/messenger/chats-command.d.ts.map +1 -1
- package/dist/cli/messenger/chats-command.js +3 -0
- package/dist/cli/messenger/chats-command.js.map +1 -1
- package/dist/cli/messenger/context.d.ts +5 -0
- package/dist/cli/messenger/context.d.ts.map +1 -1
- package/dist/cli/messenger/context.js +3 -0
- package/dist/cli/messenger/context.js.map +1 -1
- package/dist/cli/messenger/hearing-command.d.ts.map +1 -1
- package/dist/cli/messenger/hearing-command.js +4 -1
- package/dist/cli/messenger/hearing-command.js.map +1 -1
- package/dist/cli/messenger/messages-command.d.ts.map +1 -1
- package/dist/cli/messenger/messages-command.js +7 -4
- package/dist/cli/messenger/messages-command.js.map +1 -1
- package/dist/cli/messenger/port.d.ts +6 -1
- package/dist/cli/messenger/port.d.ts.map +1 -1
- package/dist/cli/messenger/port.js.map +1 -1
- package/dist/cli/messenger/serve-command.d.ts +1 -13
- package/dist/cli/messenger/serve-command.d.ts.map +1 -1
- package/dist/cli/messenger/serve-command.js +7 -32
- package/dist/cli/messenger/serve-command.js.map +1 -1
- package/dist/cli/messenger/server-command.d.ts +1 -17
- package/dist/cli/messenger/server-command.d.ts.map +1 -1
- package/dist/cli/messenger/server-command.js +6 -213
- package/dist/cli/messenger/server-command.js.map +1 -1
- package/dist/cli/messenger/store-maintenance-command.d.ts.map +1 -1
- package/dist/cli/messenger/store-maintenance-command.js +1 -1
- package/dist/cli/messenger/store-maintenance-command.js.map +1 -1
- package/dist/cli/messenger/transcribe-command.d.ts.map +1 -1
- package/dist/cli/messenger/transcribe-command.js +4 -1
- package/dist/cli/messenger/transcribe-command.js.map +1 -1
- package/dist/cli/settings.d.ts +25 -1
- package/dist/cli/settings.d.ts.map +1 -1
- package/dist/cli/settings.js +108 -34
- package/dist/cli/settings.js.map +1 -1
- package/dist/domain/models.d.ts +2 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/domain/send-time.d.ts +1 -1
- package/dist/domain/send-time.js +4 -4
- package/dist/domain/send-time.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools/moderation.d.ts +8 -0
- package/dist/mcp/tools/moderation.d.ts.map +1 -0
- package/dist/mcp/tools/moderation.js +36 -0
- package/dist/mcp/tools/moderation.js.map +1 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +2 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/moderation/check.d.ts +83 -0
- package/dist/moderation/check.d.ts.map +1 -0
- package/dist/moderation/check.js +227 -0
- package/dist/moderation/check.js.map +1 -0
- package/dist/moderation/rules.d.ts +105 -0
- package/dist/moderation/rules.d.ts.map +1 -0
- package/dist/moderation/rules.js +158 -0
- package/dist/moderation/rules.js.map +1 -0
- package/dist/sends/permissions.d.ts +5 -3
- package/dist/sends/permissions.d.ts.map +1 -1
- package/dist/sends/permissions.js +29 -5
- package/dist/sends/permissions.js.map +1 -1
- package/dist/services/deps.d.ts +7 -1
- package/dist/services/deps.d.ts.map +1 -1
- package/dist/services/deps.js +2 -1
- package/dist/services/deps.js.map +1 -1
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +3 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/messages.d.ts +2 -0
- package/dist/services/messages.d.ts.map +1 -1
- package/dist/services/messages.js +7 -1
- package/dist/services/messages.js.map +1 -1
- package/dist/services/moderation.d.ts +33 -0
- package/dist/services/moderation.d.ts.map +1 -0
- package/dist/services/moderation.js +99 -0
- package/dist/services/moderation.js.map +1 -0
- package/dist/speech/transcribe.d.ts +5 -1
- package/dist/speech/transcribe.d.ts.map +1 -1
- package/dist/speech/transcribe.js +2 -2
- package/dist/speech/transcribe.js.map +1 -1
- package/dist/store/sqlite/drizzle/bun.js +1 -1
- package/dist/store/sqlite/drizzle/core.d.ts +1 -1
- package/dist/store/sqlite/drizzle/core.d.ts.map +1 -1
- package/dist/store/sqlite/drizzle/core.js +5 -3
- package/dist/store/sqlite/drizzle/core.js.map +1 -1
- package/dist/store/sqlite/drizzle/node.js +1 -1
- package/dist/store/sqlite/drizzle/{shared-JAIU3GYB.js → shared-VIVPOB3Q.js} +5 -1
- package/dist/store/sqlite/drizzle/{shared-JAIU3GYB.js.map → shared-VIVPOB3Q.js.map} +1 -1
- package/dist/store/sqlite/manifest.d.ts.map +1 -1
- package/dist/store/sqlite/manifest.js +1 -0
- package/dist/store/sqlite/manifest.js.map +1 -1
- package/dist/store/sqlite/migrations.generated.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.generated.js +15 -0
- package/dist/store/sqlite/migrations.generated.js.map +1 -1
- package/dist/store/sqlite/schema.d.ts +494 -0
- package/dist/store/sqlite/schema.d.ts.map +1 -1
- package/dist/store/sqlite/schema.js +78 -1
- package/dist/store/sqlite/schema.js.map +1 -1
- package/package.json +5 -1
- package/parity.json +73 -88
- package/dist/cli/messenger/processes.d.ts.map +0 -1
- package/dist/cli/messenger/processes.js.map +0 -1
- /package/dist/{cli/messenger → background}/processes.d.ts +0 -0
- /package/dist/{cli/messenger → background}/processes.js +0 -0
package/dist/mcp/tools.js
CHANGED
|
@@ -12,6 +12,7 @@ import { messageDeleteTools } from "./tools/messages-delete.js";
|
|
|
12
12
|
import { messagesPhotoTools } from "./tools/messages-photo.js";
|
|
13
13
|
import { messageSendTools } from "./tools/messages-send.js";
|
|
14
14
|
import { messagesTranscribeTools } from "./tools/messages-transcribe.js";
|
|
15
|
+
import { moderationTools } from "./tools/moderation.js";
|
|
15
16
|
import { pollReadTools, pollWriteTools } from "./tools/polls.js";
|
|
16
17
|
import { reactionTools } from "./tools/reactions.js";
|
|
17
18
|
import { reviewTools } from "./tools/review.js";
|
|
@@ -41,6 +42,7 @@ export const sendTools = (messenger) => ({
|
|
|
41
42
|
...adminTools(messenger),
|
|
42
43
|
...folderTools(messenger),
|
|
43
44
|
...contactWriteTools(messenger),
|
|
45
|
+
...moderationTools(messenger),
|
|
44
46
|
...messageSendTools(messenger),
|
|
45
47
|
...messageActionTools(messenger),
|
|
46
48
|
...reactionTools(messenger),
|
package/dist/mcp/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,CAAC;IAC3E,GAAG,UAAU,CAAC,SAAS,CAAC;IACxB,GAAG,WAAW,CAAC,SAAS,CAAC;IACzB,GAAG,WAAW,CAAC,SAAS,CAAC;IACzB,GAAG,YAAY,CAAC,SAAS,CAAC;IAC1B,GAAG,UAAU,CAAC,SAAS,CAAC;IACxB,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,kBAAkB,CAAC,SAAS,CAAC;IAChC,GAAG,uBAAuB,CAAC,SAAS,CAAC;CACtC,CAAC,CAAA;AAEF,0GAA0G;AAC1G,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;AAEzG,kHAAkH;AAClH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAA;AAE3G,oFAAoF;AACpF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,CAAC;IAC3E,GAAG,UAAU,CAAC,SAAS,CAAC;IACxB,GAAG,WAAW,CAAC,SAAS,CAAC;IACzB,GAAG,iBAAiB,CAAC,SAAS,CAAC;IAC/B,GAAG,gBAAgB,CAAC,SAAS,CAAC;IAC9B,GAAG,kBAAkB,CAAC,SAAS,CAAC;IAChC,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,cAAc,CAAC,SAAS,CAAC;CAC7B,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,CAAC;IAC3E,GAAG,UAAU,CAAC,SAAS,CAAC;IACxB,GAAG,WAAW,CAAC,SAAS,CAAC;IACzB,GAAG,WAAW,CAAC,SAAS,CAAC;IACzB,GAAG,YAAY,CAAC,SAAS,CAAC;IAC1B,GAAG,UAAU,CAAC,SAAS,CAAC;IACxB,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,kBAAkB,CAAC,SAAS,CAAC;IAChC,GAAG,uBAAuB,CAAC,SAAS,CAAC;CACtC,CAAC,CAAA;AAEF,0GAA0G;AAC1G,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;AAEzG,kHAAkH;AAClH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAA;AAE3G,oFAAoF;AACpF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAAoB,EAA2B,EAAE,CAAC,CAAC;IAC3E,GAAG,UAAU,CAAC,SAAS,CAAC;IACxB,GAAG,WAAW,CAAC,SAAS,CAAC;IACzB,GAAG,iBAAiB,CAAC,SAAS,CAAC;IAC/B,GAAG,eAAe,CAAC,SAAS,CAAC;IAC7B,GAAG,gBAAgB,CAAC,SAAS,CAAC;IAC9B,GAAG,kBAAkB,CAAC,SAAS,CAAC;IAChC,GAAG,aAAa,CAAC,SAAS,CAAC;IAC3B,GAAG,cAAc,CAAC,SAAS,CAAC;CAC7B,CAAC,CAAA"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { MessengerAdapter } from "../cli/messenger/port.js";
|
|
2
|
+
import type { GroupMember, Id, Message } from "../domain/models.js";
|
|
3
|
+
import type { GroupRules } from "./rules.js";
|
|
4
|
+
export type Action = "report" | "delete" | "remove";
|
|
5
|
+
export type Outcome = "reported" | "done" | "planned" | "forbidden" | "declined" | "refused" | "failed" | "skipped";
|
|
6
|
+
export interface Finding {
|
|
7
|
+
/** A message, or somebody who joined. */
|
|
8
|
+
kind: "message" | "member";
|
|
9
|
+
rule: "blocked" | "invites" | "links" | "forwards" | "flood" | "newAccount";
|
|
10
|
+
personId: Id;
|
|
11
|
+
personName: string | null;
|
|
12
|
+
messageId?: Id;
|
|
13
|
+
action: Action;
|
|
14
|
+
}
|
|
15
|
+
export interface CheckRow extends Finding {
|
|
16
|
+
outcome: Outcome;
|
|
17
|
+
reason?: string;
|
|
18
|
+
/** What the owner types to do it by hand, on a row that was not done. */
|
|
19
|
+
command?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CheckInput {
|
|
22
|
+
rules: GroupRules;
|
|
23
|
+
messages: Message[];
|
|
24
|
+
joined: GroupMember[];
|
|
25
|
+
/** The group's admins; the owner's own messages are known by `outgoing`. */
|
|
26
|
+
answerers: ReadonlySet<Id>;
|
|
27
|
+
/** Service messages — someone joined, the title changed — which no rule judges. */
|
|
28
|
+
service: ReadonlySet<Id>;
|
|
29
|
+
now: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* What the rules say about what is new. Pure: nothing here talks to a messenger. One finding per
|
|
33
|
+
* message — the rule with the strongest action — and one removal per person, whatever else they did.
|
|
34
|
+
*/
|
|
35
|
+
export declare const judge: ({ rules, messages, joined, answerers, service, now }: CheckInput) => Finding[];
|
|
36
|
+
/** The two things a check may do, whoever does them: the personal account, or a bot. */
|
|
37
|
+
export interface Moderator {
|
|
38
|
+
deleteMessage(chatId: Id, messageId: Id): Promise<void>;
|
|
39
|
+
removePerson(chatId: Id, personId: Id): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export interface ActOptions {
|
|
42
|
+
chatId: Id;
|
|
43
|
+
rules: GroupRules;
|
|
44
|
+
/** `--allow-dangerous`: yes to every action at level `ask`. */
|
|
45
|
+
allowDangerous: boolean;
|
|
46
|
+
dryRun: boolean;
|
|
47
|
+
maxActions: number;
|
|
48
|
+
/** Asks the owner about one action: `undefined` from it, or no `confirm`, when nobody is there to answer. */
|
|
49
|
+
confirm?: (finding: Finding) => Promise<boolean | undefined>;
|
|
50
|
+
/** The program's word, for the command a row says to type by hand: `tg`, `max`. */
|
|
51
|
+
command: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Does what the findings ask, as far as the group's levels, the per-run limit and the guard let it.
|
|
55
|
+
* `deny` never acts, `readonly` only reports, `ask` asks — or `--allow-dangerous` says yes — and
|
|
56
|
+
* `allow` acts. Stops acting at the first hourly-limit refusal: the rest is `skipped`, never tried.
|
|
57
|
+
*/
|
|
58
|
+
export declare const act: (moderator: Moderator, findings: Finding[], options: ActOptions) => Promise<CheckRow[]>;
|
|
59
|
+
/** One action in words, for a question at the terminal. */
|
|
60
|
+
export declare const describe: (finding: Finding) => string;
|
|
61
|
+
export interface Gathered extends Omit<CheckInput, "rules" | "now"> {
|
|
62
|
+
/** The newest message read, ISO 8601: where the next check starts. */
|
|
63
|
+
until: string | null;
|
|
64
|
+
/** More history than one check reads. */
|
|
65
|
+
more: boolean;
|
|
66
|
+
notes: string[];
|
|
67
|
+
}
|
|
68
|
+
/** One check reads at most this many messages; the rest waits for the next. */
|
|
69
|
+
export declare const CHECK_READS = 1000;
|
|
70
|
+
/**
|
|
71
|
+
* What is new in the group since `since` (ms): its messages, who joined — from the chat's service
|
|
72
|
+
* messages — and who the admins are. Reads only.
|
|
73
|
+
*/
|
|
74
|
+
export declare const gather: (adapter: MessengerAdapter, chatId: Id, since: number) => Promise<Gathered>;
|
|
75
|
+
export declare const MAX_ACTIONS = 10;
|
|
76
|
+
/**
|
|
77
|
+
* Where the next check starts: after the newest message read, or just before the first message whose
|
|
78
|
+
* action is still undone — so it is judged again, and what came after it is still read. A person
|
|
79
|
+
* who joined and is still undone keeps the point where it was, since the join is not a row's message.
|
|
80
|
+
*/
|
|
81
|
+
export declare const nextPoint: (rows: CheckRow[], found: Gathered) => string | null;
|
|
82
|
+
export declare const someUndone: (rows: CheckRow[]) => boolean;
|
|
83
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/moderation/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
const INVITE = /max\.ru\/join\/|(t|telegram)\.me\/(\+|joinchat\/)|tg:\/\/join/i;
|
|
3
|
+
const LINK = /https?:\/\/\S+/i;
|
|
4
|
+
/** Which action wins when a message breaks several rules; the first rule listed breaks a tie. */
|
|
5
|
+
const STRENGTH = { report: 0, delete: 1, remove: 2 };
|
|
6
|
+
/**
|
|
7
|
+
* What the rules say about what is new. Pure: nothing here talks to a messenger. One finding per
|
|
8
|
+
* message — the rule with the strongest action — and one removal per person, whatever else they did.
|
|
9
|
+
*/
|
|
10
|
+
export const judge = ({ rules, messages, joined, answerers, service, now }) => {
|
|
11
|
+
const trusted = new Set(rules.trusted);
|
|
12
|
+
const blocked = (id, name) => (id !== null && rules.blocked.includes(id)) ||
|
|
13
|
+
(name !== null && rules.blockedNames.some((part) => name.toLocaleLowerCase().includes(part.toLocaleLowerCase())));
|
|
14
|
+
const young = (member) => rules.newAccount.days > 0 &&
|
|
15
|
+
typeof member.registeredAt === "string" &&
|
|
16
|
+
now - Date.parse(member.registeredAt) < rules.newAccount.days * 86_400_000;
|
|
17
|
+
const found = [];
|
|
18
|
+
const add = (finding) => {
|
|
19
|
+
if (finding.action === "remove" && found.some((f) => f.action === "remove" && f.personId === finding.personId)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
found.push(finding);
|
|
23
|
+
};
|
|
24
|
+
for (const member of joined) {
|
|
25
|
+
if (trusted.has(member.id) || answerers.has(member.id))
|
|
26
|
+
continue;
|
|
27
|
+
const person = { kind: "member", personId: member.id, personName: member.name };
|
|
28
|
+
if (blocked(member.id, member.name)) {
|
|
29
|
+
add({ ...person, rule: "blocked", action: rules.blockedPeople === "remove" ? "remove" : "report" });
|
|
30
|
+
}
|
|
31
|
+
else if (young(member)) {
|
|
32
|
+
add({ ...person, rule: "newAccount", action: rules.newAccount.action });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const flooding = floodOf(messages, rules.flood);
|
|
36
|
+
for (const message of messages) {
|
|
37
|
+
const id = message.senderId;
|
|
38
|
+
if (id === null || message.outgoing === true || answerers.has(id) || trusted.has(id))
|
|
39
|
+
continue;
|
|
40
|
+
if (service.has(message.id) || message.attachments.some((attachment) => attachment.kind === "control"))
|
|
41
|
+
continue;
|
|
42
|
+
const base = { kind: "message", personId: id, personName: message.senderName, messageId: message.id };
|
|
43
|
+
const text = message.text;
|
|
44
|
+
const broken = [];
|
|
45
|
+
if (blocked(id, message.senderName))
|
|
46
|
+
broken.push(["blocked", rules.blockedPeople]);
|
|
47
|
+
if (INVITE.test(text))
|
|
48
|
+
broken.push(["invites", rules.invites]);
|
|
49
|
+
if (LINK.test(text) || message.attachments.some((attachment) => attachment.kind === "share")) {
|
|
50
|
+
broken.push(["links", rules.links]);
|
|
51
|
+
}
|
|
52
|
+
if (message.forwardedFrom !== null)
|
|
53
|
+
broken.push(["forwards", rules.forwards]);
|
|
54
|
+
if (flooding.has(message.id))
|
|
55
|
+
broken.push(["flood", rules.flood.action]);
|
|
56
|
+
const strongest = broken.reduce((best, rule) => (best === undefined || STRENGTH[rule[1]] > STRENGTH[best[1]] ? rule : best), undefined);
|
|
57
|
+
if (strongest)
|
|
58
|
+
add({ ...base, rule: strongest[0], action: strongest[1] });
|
|
59
|
+
}
|
|
60
|
+
return found;
|
|
61
|
+
};
|
|
62
|
+
/** Messages past the limit: more than `messages` from one person within `minutes`. */
|
|
63
|
+
const floodOf = (messages, flood) => {
|
|
64
|
+
const window = flood.minutes * 60_000;
|
|
65
|
+
const over = new Set();
|
|
66
|
+
const bySender = new Map();
|
|
67
|
+
for (const message of messages) {
|
|
68
|
+
if (message.senderId === null)
|
|
69
|
+
continue;
|
|
70
|
+
const times = bySender.get(message.senderId) ?? [];
|
|
71
|
+
const at = Date.parse(message.timestamp);
|
|
72
|
+
times.push(at);
|
|
73
|
+
bySender.set(message.senderId, times);
|
|
74
|
+
if (times.filter((time) => at - time < window).length > flood.messages)
|
|
75
|
+
over.add(message.id);
|
|
76
|
+
}
|
|
77
|
+
return over;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Does what the findings ask, as far as the group's levels, the per-run limit and the guard let it.
|
|
81
|
+
* `deny` never acts, `readonly` only reports, `ask` asks — or `--allow-dangerous` says yes — and
|
|
82
|
+
* `allow` acts. Stops acting at the first hourly-limit refusal: the rest is `skipped`, never tried.
|
|
83
|
+
*/
|
|
84
|
+
export const act = async (moderator, findings, options) => {
|
|
85
|
+
const { chatId, rules, allowDangerous, dryRun, maxActions, confirm, command } = options;
|
|
86
|
+
const rows = [];
|
|
87
|
+
let acted = 0;
|
|
88
|
+
let stopped;
|
|
89
|
+
for (const finding of findings) {
|
|
90
|
+
if (finding.action === "report") {
|
|
91
|
+
rows.push({ ...finding, outcome: "reported" });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const consent = finding.action;
|
|
95
|
+
const level = rules.consent[consent];
|
|
96
|
+
const row = (outcome, reason) => ({
|
|
97
|
+
...finding,
|
|
98
|
+
outcome,
|
|
99
|
+
...(reason ? { reason } : {}),
|
|
100
|
+
...(outcome === "done" || outcome === "reported" ? {} : { command: commandFor(command, chatId, finding) }),
|
|
101
|
+
});
|
|
102
|
+
if (level === "deny") {
|
|
103
|
+
rows.push(row("forbidden", `consent.${consent} is deny`));
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (level === "readonly") {
|
|
107
|
+
rows.push(row("reported", `consent.${consent} is readonly`));
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (dryRun) {
|
|
111
|
+
rows.push(row("planned", "--dry-run"));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (stopped || acted >= maxActions) {
|
|
115
|
+
rows.push(row("skipped", stopped ?? `over the limit of ${maxActions} actions per check`));
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (level === "ask" && !allowDangerous) {
|
|
119
|
+
const yes = confirm ? await confirm(finding) : undefined;
|
|
120
|
+
if (yes === undefined) {
|
|
121
|
+
rows.push(row("planned", `consent.${consent} is ask, and nobody is there to answer — run with --allow-dangerous`));
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (!yes) {
|
|
125
|
+
rows.push(row("declined", "the answer was no"));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
acted += 1;
|
|
130
|
+
try {
|
|
131
|
+
if (finding.action === "delete" && finding.messageId) {
|
|
132
|
+
await moderator.deleteMessage(chatId, finding.messageId);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
await moderator.removePerson(chatId, finding.personId);
|
|
136
|
+
}
|
|
137
|
+
rows.push(row("done"));
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const failure = asCliError(error);
|
|
141
|
+
if (failure.code === "rate_limited")
|
|
142
|
+
stopped = failure.message;
|
|
143
|
+
const refused = ["rate_limited", "permission_error", "confirmation_required"].includes(failure.code);
|
|
144
|
+
rows.push(row(refused ? "refused" : "failed", failure.message));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return rows;
|
|
148
|
+
};
|
|
149
|
+
const asCliError = (error) => error instanceof CliError
|
|
150
|
+
? error
|
|
151
|
+
: new CliError("provider_error", error instanceof Error ? error.message : String(error));
|
|
152
|
+
const who = (finding) => finding.personName ?? finding.personId;
|
|
153
|
+
/** One action in words, for a question at the terminal. */
|
|
154
|
+
export const describe = (finding) => finding.action === "delete"
|
|
155
|
+
? `delete message ${finding.messageId} from ${who(finding)} for everyone (${finding.rule})`
|
|
156
|
+
: `remove ${who(finding)} from the group (${finding.rule})`;
|
|
157
|
+
const commandFor = (command, chatId, finding) => finding.action === "delete"
|
|
158
|
+
? `${command} messages delete ${chatId} ${finding.messageId} --for-everyone --allow-dangerous`
|
|
159
|
+
: finding.action === "remove"
|
|
160
|
+
? `${command} chats members remove ${chatId} ${finding.personId}`
|
|
161
|
+
: "";
|
|
162
|
+
/** One check reads at most this many messages; the rest waits for the next. */
|
|
163
|
+
export const CHECK_READS = 1000;
|
|
164
|
+
const PAGE = 100;
|
|
165
|
+
/**
|
|
166
|
+
* What is new in the group since `since` (ms): its messages, who joined — from the chat's service
|
|
167
|
+
* messages — and who the admins are. Reads only.
|
|
168
|
+
*/
|
|
169
|
+
export const gather = async (adapter, chatId, since) => {
|
|
170
|
+
const notes = [];
|
|
171
|
+
const historyAfter = adapter.historyAfter;
|
|
172
|
+
if (!historyAfter)
|
|
173
|
+
throw new CliError("validation_error", "this messenger cannot read a chat forward from a time");
|
|
174
|
+
const messages = [];
|
|
175
|
+
let more = true;
|
|
176
|
+
while (more && messages.length < CHECK_READS) {
|
|
177
|
+
const last = messages.at(-1);
|
|
178
|
+
const page = await historyAfter.call(adapter, chatId, {
|
|
179
|
+
limit: PAGE,
|
|
180
|
+
after: last ? { id: last.id } : { time: since },
|
|
181
|
+
});
|
|
182
|
+
messages.push(...page.items);
|
|
183
|
+
more = page.hasMore && page.items.length > 0;
|
|
184
|
+
}
|
|
185
|
+
const events = adapter.chatEvents ? (await adapter.chatEvents(chatId, { since })).events : [];
|
|
186
|
+
if (!adapter.chatEvents)
|
|
187
|
+
notes.push("this messenger does not say who joined, so only messages are judged");
|
|
188
|
+
const joinedIds = new Set(events
|
|
189
|
+
.filter((one) => one.event === "join" || one.event === "add")
|
|
190
|
+
.flatMap((one) => (one.people.length > 0 ? one.people.map((person) => person.id) : one.by.id ? [one.by.id] : [])));
|
|
191
|
+
const joined = joinedIds.size === 0 || !adapter.members
|
|
192
|
+
? []
|
|
193
|
+
: (await adapter.members(chatId, { offset: 0 })).items.filter((member) => joinedIds.has(member.id));
|
|
194
|
+
const admins = adapter.admins ? await adapter.admins(chatId) : null;
|
|
195
|
+
if (admins === null)
|
|
196
|
+
notes.push("the group's admins are not known, so only your own messages are exempt");
|
|
197
|
+
return {
|
|
198
|
+
messages,
|
|
199
|
+
joined,
|
|
200
|
+
answerers: new Set(admins ?? []),
|
|
201
|
+
service: new Set(events.map((one) => one.messageId)),
|
|
202
|
+
until: messages.at(-1)?.timestamp ?? null,
|
|
203
|
+
more,
|
|
204
|
+
notes,
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
export const MAX_ACTIONS = 10;
|
|
208
|
+
const undone = (row) => ["planned", "skipped", "failed"].includes(row.outcome);
|
|
209
|
+
/**
|
|
210
|
+
* Where the next check starts: after the newest message read, or just before the first message whose
|
|
211
|
+
* action is still undone — so it is judged again, and what came after it is still read. A person
|
|
212
|
+
* who joined and is still undone keeps the point where it was, since the join is not a row's message.
|
|
213
|
+
*/
|
|
214
|
+
export const nextPoint = (rows, found) => {
|
|
215
|
+
const waiting = rows.filter(undone);
|
|
216
|
+
if (waiting.some((row) => row.messageId === undefined))
|
|
217
|
+
return null;
|
|
218
|
+
const times = waiting
|
|
219
|
+
.map((row) => found.messages.find((message) => message.id === row.messageId)?.timestamp)
|
|
220
|
+
.filter((time) => time !== undefined)
|
|
221
|
+
.map(Date.parse);
|
|
222
|
+
if (times.length === 0)
|
|
223
|
+
return found.until;
|
|
224
|
+
return new Date(Math.min(...times) - 1).toISOString();
|
|
225
|
+
};
|
|
226
|
+
export const someUndone = (rows) => rows.some(undone);
|
|
227
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/moderation/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
import type { AppIdentity } from "../cli/app.js";
|
|
3
|
+
import type { Id } from "../domain/models.js";
|
|
4
|
+
/** What a rule does with what it finds; nothing but `report` acts until the group's level lets it. */
|
|
5
|
+
export declare const RULE_ACTIONS: readonly ["report", "delete", "remove"];
|
|
6
|
+
/** `requests` and `consent.accept|decline` were for join requests (max-cli `FIND-249`); a file with them loads and drops them. */
|
|
7
|
+
declare const groupRules: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
8
|
+
readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
9
|
+
readonly trusted: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a person id is digits">]>, undefined>;
|
|
10
|
+
readonly blocked: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a person id is digits">]>, undefined>;
|
|
11
|
+
readonly blockedNames: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
12
|
+
readonly links: v.PicklistSchema<readonly ["report", "delete", "remove"], undefined>;
|
|
13
|
+
readonly invites: v.PicklistSchema<readonly ["report", "delete", "remove"], undefined>;
|
|
14
|
+
readonly forwards: v.PicklistSchema<readonly ["report", "delete", "remove"], undefined>;
|
|
15
|
+
readonly blockedPeople: v.PicklistSchema<readonly ["report", "delete", "remove"], undefined>;
|
|
16
|
+
readonly flood: v.StrictObjectSchema<{
|
|
17
|
+
readonly messages: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, number, undefined>]>;
|
|
18
|
+
readonly minutes: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, number, undefined>]>;
|
|
19
|
+
readonly action: v.PicklistSchema<readonly ["report", "delete", "remove"], undefined>;
|
|
20
|
+
}, undefined>;
|
|
21
|
+
/** 0 days turns the rule off. */
|
|
22
|
+
readonly newAccount: v.StrictObjectSchema<{
|
|
23
|
+
readonly days: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, number, undefined>]>;
|
|
24
|
+
readonly action: v.PicklistSchema<["report", "remove"], undefined>;
|
|
25
|
+
}, undefined>;
|
|
26
|
+
readonly requests: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
27
|
+
readonly consent: v.StrictObjectSchema<{
|
|
28
|
+
readonly delete: v.SchemaWithPipe<readonly [v.PicklistSchema<["deny", "readonly", "ask", "allow", ...string[]], undefined>, v.TransformAction<string, "allow" | "ask" | "deny" | "readonly">]>;
|
|
29
|
+
readonly remove: v.SchemaWithPipe<readonly [v.PicklistSchema<["deny", "readonly", "ask", "allow", ...string[]], undefined>, v.TransformAction<string, "allow" | "ask" | "deny" | "readonly">]>;
|
|
30
|
+
readonly accept: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
31
|
+
readonly decline: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
32
|
+
}, undefined>;
|
|
33
|
+
}, undefined>, v.TransformAction<{
|
|
34
|
+
title: string | null;
|
|
35
|
+
trusted: string[];
|
|
36
|
+
blocked: string[];
|
|
37
|
+
blockedNames: string[];
|
|
38
|
+
links: "delete" | "remove" | "report";
|
|
39
|
+
invites: "delete" | "remove" | "report";
|
|
40
|
+
forwards: "delete" | "remove" | "report";
|
|
41
|
+
blockedPeople: "delete" | "remove" | "report";
|
|
42
|
+
flood: {
|
|
43
|
+
messages: number;
|
|
44
|
+
minutes: number;
|
|
45
|
+
action: "delete" | "remove" | "report";
|
|
46
|
+
};
|
|
47
|
+
newAccount: {
|
|
48
|
+
days: number;
|
|
49
|
+
action: "remove" | "report";
|
|
50
|
+
};
|
|
51
|
+
requests?: unknown;
|
|
52
|
+
consent: {
|
|
53
|
+
delete: "allow" | "ask" | "deny" | "readonly";
|
|
54
|
+
remove: "allow" | "ask" | "deny" | "readonly";
|
|
55
|
+
accept?: unknown;
|
|
56
|
+
decline?: unknown;
|
|
57
|
+
};
|
|
58
|
+
}, {
|
|
59
|
+
title: string | null;
|
|
60
|
+
trusted: string[];
|
|
61
|
+
blocked: string[];
|
|
62
|
+
blockedNames: string[];
|
|
63
|
+
links: "delete" | "remove" | "report";
|
|
64
|
+
invites: "delete" | "remove" | "report";
|
|
65
|
+
forwards: "delete" | "remove" | "report";
|
|
66
|
+
blockedPeople: "delete" | "remove" | "report";
|
|
67
|
+
flood: {
|
|
68
|
+
messages: number;
|
|
69
|
+
minutes: number;
|
|
70
|
+
action: "delete" | "remove" | "report";
|
|
71
|
+
};
|
|
72
|
+
newAccount: {
|
|
73
|
+
days: number;
|
|
74
|
+
action: "remove" | "report";
|
|
75
|
+
};
|
|
76
|
+
consent: {
|
|
77
|
+
delete: "allow" | "ask" | "deny" | "readonly";
|
|
78
|
+
remove: "allow" | "ask" | "deny" | "readonly";
|
|
79
|
+
};
|
|
80
|
+
}>]>;
|
|
81
|
+
export type GroupRules = v.InferOutput<typeof groupRules>;
|
|
82
|
+
/** Every key written out, so the file shows all there is to set (max-cli `NEED-314`). Nothing here acts. */
|
|
83
|
+
export declare const defaultRules: (title: string | null) => GroupRules;
|
|
84
|
+
export declare const RULE_KEYS: string[];
|
|
85
|
+
export declare const moderationPathFor: (app: AppIdentity, profile: string, env?: NodeJS.ProcessEnv) => string;
|
|
86
|
+
/**
|
|
87
|
+
* The rules of each group this profile moderates, one file per profile beside the recipient list —
|
|
88
|
+
* the path max-cli used, so its rules carry over. May be edited by hand; a file that does not check
|
|
89
|
+
* out refuses rather than being guessed at.
|
|
90
|
+
*/
|
|
91
|
+
export declare class ModerationRules {
|
|
92
|
+
#private;
|
|
93
|
+
readonly path: string;
|
|
94
|
+
constructor(path: string);
|
|
95
|
+
/** `undefined` when this group has no section yet. */
|
|
96
|
+
read(chatId: Id): GroupRules | undefined;
|
|
97
|
+
/** Writes the group's whole section — the defaults first, if it had none — with one key changed. */
|
|
98
|
+
set(chatId: Id, title: string | null, key: string, value: string): GroupRules;
|
|
99
|
+
/** Puts one key back to its default. */
|
|
100
|
+
unset(chatId: Id, title: string | null, key: string): GroupRules;
|
|
101
|
+
checkedUntil(chatId: Id): string | undefined;
|
|
102
|
+
markChecked(chatId: Id, at: string): void;
|
|
103
|
+
}
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/moderation/rules.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAG7C,sGAAsG;AACtG,eAAO,MAAM,YAAY,YAAI,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAA;AAgBnE,kIAAkI;AAClI,QAAA,MAAM,UAAU;;;;;;;;;;;;;;IAWZ,iCAAiC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAcpC,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,UAAU,CAAC,CAAA;AAQzD,4GAA4G;AAC5G,eAAO,MAAM,YAAY,UAAW,MAAM,GAAG,IAAI,KAAG,UAYlD,CAAA;AA4BF,eAAO,MAAM,SAAS,UAAoB,CAAA;AAE1C,eAAO,MAAM,iBAAiB,QAAS,WAAW,WAAW,MAAM,QAAO,MAAM,CAAC,UAAU,KAAiB,MAKzG,CAAA;AAEH;;;;GAIG;AACH,qBAAa,eAAe;;IACd,QAAQ,CAAC,IAAI,EAAE,MAAM;IAAjC,YAAqB,IAAI,EAAE,MAAM,EAAI;IAErC,sDAAsD;IACtD,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,UAAU,GAAG,SAAS,CAEvC;IAED,oGAAoG;IACpG,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAS5E;IAED,wCAAwC;IACxC,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAO/D;IAED,YAAY,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,GAAG,SAAS,CAE3C;IAED,WAAW,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAGxC;CAqBF"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { CliError, resolvePaths, writeSecurely } from "@leemour/cli-core";
|
|
4
|
+
import * as v from "valibot";
|
|
5
|
+
import { LEVELS } from "../sends/permissions.js";
|
|
6
|
+
/** What a rule does with what it finds; nothing but `report` acts until the group's level lets it. */
|
|
7
|
+
export const RULE_ACTIONS = ["report", "delete", "remove"];
|
|
8
|
+
/**
|
|
9
|
+
* max-cli's first words for a group's consent (`NEED-308`), read as the profile's levels
|
|
10
|
+
* (`NEED-462` B): `flag` and `confirm` both ask, `forbid` denies.
|
|
11
|
+
*/
|
|
12
|
+
const OLD_LEVELS = { forbid: "deny", flag: "ask", confirm: "ask", allow: "allow" };
|
|
13
|
+
const level = v.pipe(v.picklist([...LEVELS, ...Object.keys(OLD_LEVELS)]), v.transform((typed) => (OLD_LEVELS[typed] ?? typed)));
|
|
14
|
+
const action = v.picklist(RULE_ACTIONS);
|
|
15
|
+
const personIds = v.array(v.pipe(v.string(), v.regex(/^-?\d+$/, "a person id is digits")));
|
|
16
|
+
const atLeast = (min) => v.pipe(v.number(), v.integer(), v.minValue(min));
|
|
17
|
+
/** `requests` and `consent.accept|decline` were for join requests (max-cli `FIND-249`); a file with them loads and drops them. */
|
|
18
|
+
const groupRules = v.pipe(v.strictObject({
|
|
19
|
+
title: v.nullable(v.string()),
|
|
20
|
+
trusted: personIds,
|
|
21
|
+
blocked: personIds,
|
|
22
|
+
blockedNames: v.array(v.pipe(v.string(), v.minLength(1))),
|
|
23
|
+
links: action,
|
|
24
|
+
invites: action,
|
|
25
|
+
forwards: action,
|
|
26
|
+
blockedPeople: action,
|
|
27
|
+
flood: v.strictObject({ messages: atLeast(1), minutes: atLeast(1), action }),
|
|
28
|
+
/** 0 days turns the rule off. */
|
|
29
|
+
newAccount: v.strictObject({ days: atLeast(0), action: v.picklist(["report", "remove"]) }),
|
|
30
|
+
requests: v.optional(v.unknown()),
|
|
31
|
+
consent: v.strictObject({
|
|
32
|
+
delete: level,
|
|
33
|
+
remove: level,
|
|
34
|
+
accept: v.optional(v.unknown()),
|
|
35
|
+
decline: v.optional(v.unknown()),
|
|
36
|
+
}),
|
|
37
|
+
}), v.transform(({ requests: _, consent: { accept: _accept, decline: _decline, ...consent }, ...rules }) => ({
|
|
38
|
+
...rules,
|
|
39
|
+
consent,
|
|
40
|
+
})));
|
|
41
|
+
const file = v.strictObject({
|
|
42
|
+
groups: v.record(v.string(), groupRules),
|
|
43
|
+
/** Where each group's next `chats moderate` starts, ISO 8601. */
|
|
44
|
+
checkedUntil: v.optional(v.record(v.string(), v.string())),
|
|
45
|
+
});
|
|
46
|
+
/** Every key written out, so the file shows all there is to set (max-cli `NEED-314`). Nothing here acts. */
|
|
47
|
+
export const defaultRules = (title) => ({
|
|
48
|
+
title,
|
|
49
|
+
trusted: [],
|
|
50
|
+
blocked: [],
|
|
51
|
+
blockedNames: [],
|
|
52
|
+
links: "report",
|
|
53
|
+
invites: "report",
|
|
54
|
+
forwards: "report",
|
|
55
|
+
blockedPeople: "report",
|
|
56
|
+
flood: { messages: 5, minutes: 1, action: "report" },
|
|
57
|
+
newAccount: { days: 7, action: "report" },
|
|
58
|
+
consent: { delete: "ask", remove: "ask" },
|
|
59
|
+
});
|
|
60
|
+
const list = (value) => value
|
|
61
|
+
.split(",")
|
|
62
|
+
.map((item) => item.trim())
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
const whole = (value) => (/^\d+$/.test(value.trim()) ? Number(value) : Number.NaN);
|
|
65
|
+
/** What `chats rules set` accepts, and how its text becomes the stored value. */
|
|
66
|
+
const KEYS = {
|
|
67
|
+
trusted: { help: "person ids, comma-separated; never acted on", parse: list },
|
|
68
|
+
blocked: { help: "person ids, comma-separated", parse: list },
|
|
69
|
+
blockedNames: { help: "parts of a name, comma-separated, any case", parse: list },
|
|
70
|
+
links: { help: RULE_ACTIONS.join("|"), parse: String },
|
|
71
|
+
invites: { help: RULE_ACTIONS.join("|"), parse: String },
|
|
72
|
+
forwards: { help: RULE_ACTIONS.join("|"), parse: String },
|
|
73
|
+
blockedPeople: { help: RULE_ACTIONS.join("|"), parse: String },
|
|
74
|
+
"flood.messages": { help: "a whole number, 1 or more", parse: whole },
|
|
75
|
+
"flood.minutes": { help: "a whole number, 1 or more", parse: whole },
|
|
76
|
+
"flood.action": { help: RULE_ACTIONS.join("|"), parse: String },
|
|
77
|
+
"newAccount.days": { help: "a whole number; 0 turns it off", parse: whole },
|
|
78
|
+
"newAccount.action": { help: "report|remove", parse: String },
|
|
79
|
+
"consent.delete": { help: LEVELS.join("|"), parse: String },
|
|
80
|
+
"consent.remove": { help: LEVELS.join("|"), parse: String },
|
|
81
|
+
};
|
|
82
|
+
export const RULE_KEYS = Object.keys(KEYS);
|
|
83
|
+
export const moderationPathFor = (app, profile, env = process.env) => join(resolvePaths({ appName: app.appName, prefix: app.envPrefix, env }).state, "profiles", `${profile}.moderation.json`);
|
|
84
|
+
/**
|
|
85
|
+
* The rules of each group this profile moderates, one file per profile beside the recipient list —
|
|
86
|
+
* the path max-cli used, so its rules carry over. May be edited by hand; a file that does not check
|
|
87
|
+
* out refuses rather than being guessed at.
|
|
88
|
+
*/
|
|
89
|
+
export class ModerationRules {
|
|
90
|
+
path;
|
|
91
|
+
constructor(path) {
|
|
92
|
+
this.path = path;
|
|
93
|
+
}
|
|
94
|
+
/** `undefined` when this group has no section yet. */
|
|
95
|
+
read(chatId) {
|
|
96
|
+
return this.#file().groups[chatId];
|
|
97
|
+
}
|
|
98
|
+
/** Writes the group's whole section — the defaults first, if it had none — with one key changed. */
|
|
99
|
+
set(chatId, title, key, value) {
|
|
100
|
+
const known = KEYS[key];
|
|
101
|
+
if (!known)
|
|
102
|
+
throw invalid(`no rule ${key} — one of: ${RULE_KEYS.join(", ")}`);
|
|
103
|
+
const current = this.read(chatId) ?? defaultRules(title);
|
|
104
|
+
const checked = v.safeParse(groupRules, assign(current, key, known.parse(value)));
|
|
105
|
+
if (!checked.success)
|
|
106
|
+
throw invalid(`${key} ${JSON.stringify(value)} is not valid — ${known.help}`);
|
|
107
|
+
const saved = this.#file();
|
|
108
|
+
this.#write({ ...saved, groups: { ...saved.groups, [chatId]: checked.output } });
|
|
109
|
+
return checked.output;
|
|
110
|
+
}
|
|
111
|
+
/** Puts one key back to its default. */
|
|
112
|
+
unset(chatId, title, key) {
|
|
113
|
+
if (!KEYS[key])
|
|
114
|
+
throw invalid(`no rule ${key} — one of: ${RULE_KEYS.join(", ")}`);
|
|
115
|
+
const current = this.read(chatId) ?? defaultRules(title);
|
|
116
|
+
const changed = assign(current, key, lookup(defaultRules(title), key));
|
|
117
|
+
const saved = this.#file();
|
|
118
|
+
this.#write({ ...saved, groups: { ...saved.groups, [chatId]: changed } });
|
|
119
|
+
return changed;
|
|
120
|
+
}
|
|
121
|
+
checkedUntil(chatId) {
|
|
122
|
+
return this.#file().checkedUntil?.[chatId];
|
|
123
|
+
}
|
|
124
|
+
markChecked(chatId, at) {
|
|
125
|
+
const saved = this.#file();
|
|
126
|
+
this.#write({ ...saved, checkedUntil: { ...saved.checkedUntil, [chatId]: at } });
|
|
127
|
+
}
|
|
128
|
+
#file() {
|
|
129
|
+
if (!existsSync(this.path))
|
|
130
|
+
return { groups: {} };
|
|
131
|
+
let parsed;
|
|
132
|
+
try {
|
|
133
|
+
parsed = JSON.parse(readFileSync(this.path, "utf8"));
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
throw broken(this.path, error instanceof Error ? error.message : String(error));
|
|
137
|
+
}
|
|
138
|
+
const checked = v.safeParse(file, parsed);
|
|
139
|
+
if (!checked.success) {
|
|
140
|
+
const problems = checked.issues.map((issue) => `${v.getDotPath(issue) ?? "file"}: ${issue.message}`);
|
|
141
|
+
throw broken(this.path, problems.join("; "));
|
|
142
|
+
}
|
|
143
|
+
return checked.output;
|
|
144
|
+
}
|
|
145
|
+
#write(content) {
|
|
146
|
+
writeSecurely(this.path, `${JSON.stringify(content, null, 2)}\n`, 0o600);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const invalid = (message) => new CliError("validation_error", message);
|
|
150
|
+
const broken = (path, why) => new CliError("configuration_error", `the moderation rules ${path} cannot be read (${why}) — fix the file`);
|
|
151
|
+
const lookup = (rules, key) => key.split(".").reduce((value, part) => value[part], rules);
|
|
152
|
+
const assign = (rules, key, value) => {
|
|
153
|
+
const [head, tail] = key.split(".");
|
|
154
|
+
if (tail === undefined)
|
|
155
|
+
return { ...rules, [head]: value };
|
|
156
|
+
return { ...rules, [head]: { ...rules[head], [tail]: value } };
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=rules.js.map
|