@leemour/cli-messaging 0.146.0 → 0.148.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background/index.d.ts +1 -1
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js +1 -1
- package/dist/background/index.js.map +1 -1
- package/dist/background/lock.d.ts +6 -0
- package/dist/background/lock.d.ts.map +1 -1
- package/dist/background/lock.js +31 -1
- package/dist/background/lock.js.map +1 -1
- package/dist/background/units.d.ts +1 -0
- package/dist/background/units.d.ts.map +1 -1
- package/dist/background/units.js +9 -0
- package/dist/background/units.js.map +1 -1
- package/dist/cli/bot/people.js +1 -1
- package/dist/cli/bot/people.js.map +1 -1
- package/dist/cli/config-command.d.ts.map +1 -1
- package/dist/cli/config-command.js +17 -3
- package/dist/cli/config-command.js.map +1 -1
- package/dist/cli/index.d.ts +6 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/messenger/backfill-command.d.ts.map +1 -1
- package/dist/cli/messenger/backfill-command.js +4 -0
- package/dist/cli/messenger/backfill-command.js.map +1 -1
- package/dist/cli/messenger/chats-command.d.ts.map +1 -1
- package/dist/cli/messenger/chats-command.js +2 -2
- package/dist/cli/messenger/chats-command.js.map +1 -1
- package/dist/cli/messenger/chats-members-command.d.ts.map +1 -1
- package/dist/cli/messenger/chats-members-command.js +35 -1
- package/dist/cli/messenger/chats-members-command.js.map +1 -1
- package/dist/cli/messenger/chats-stats-command.js +1 -1
- package/dist/cli/messenger/chats-stats-command.js.map +1 -1
- package/dist/cli/messenger/contacts-command.d.ts.map +1 -1
- package/dist/cli/messenger/contacts-command.js +45 -1
- package/dist/cli/messenger/contacts-command.js.map +1 -1
- package/dist/cli/messenger/context.d.ts +10 -1
- package/dist/cli/messenger/context.d.ts.map +1 -1
- package/dist/cli/messenger/context.js +21 -2
- package/dist/cli/messenger/context.js.map +1 -1
- package/dist/cli/messenger/conversations-command.d.ts.map +1 -1
- package/dist/cli/messenger/conversations-command.js +6 -20
- package/dist/cli/messenger/conversations-command.js.map +1 -1
- package/dist/cli/messenger/doctor-command.d.ts +14 -0
- package/dist/cli/messenger/doctor-command.d.ts.map +1 -1
- package/dist/cli/messenger/doctor-command.js +117 -6
- package/dist/cli/messenger/doctor-command.js.map +1 -1
- package/dist/cli/messenger/flood-command.d.ts +8 -0
- package/dist/cli/messenger/flood-command.d.ts.map +1 -0
- package/dist/cli/messenger/flood-command.js +31 -0
- package/dist/cli/messenger/flood-command.js.map +1 -0
- package/dist/cli/messenger/flooded.d.ts +12 -0
- package/dist/cli/messenger/flooded.d.ts.map +1 -0
- package/dist/cli/messenger/flooded.js +77 -0
- package/dist/cli/messenger/flooded.js.map +1 -0
- package/dist/cli/messenger/inbox.d.ts +1 -0
- package/dist/cli/messenger/inbox.d.ts.map +1 -1
- package/dist/cli/messenger/inbox.js +4 -3
- package/dist/cli/messenger/inbox.js.map +1 -1
- package/dist/cli/messenger/mcp-command.d.ts +12 -0
- package/dist/cli/messenger/mcp-command.d.ts.map +1 -1
- package/dist/cli/messenger/mcp-command.js +49 -3
- package/dist/cli/messenger/mcp-command.js.map +1 -1
- package/dist/cli/messenger/messages-search-command.d.ts +2 -0
- package/dist/cli/messenger/messages-search-command.d.ts.map +1 -1
- package/dist/cli/messenger/messages-search-command.js +49 -16
- package/dist/cli/messenger/messages-search-command.js.map +1 -1
- package/dist/cli/messenger/messages-stats-command.d.ts +2 -0
- package/dist/cli/messenger/messages-stats-command.d.ts.map +1 -1
- package/dist/cli/messenger/messages-stats-command.js +35 -11
- package/dist/cli/messenger/messages-stats-command.js.map +1 -1
- package/dist/cli/messenger/points.d.ts +2 -1
- package/dist/cli/messenger/points.d.ts.map +1 -1
- package/dist/cli/messenger/points.js +1 -0
- package/dist/cli/messenger/points.js.map +1 -1
- package/dist/cli/messenger/port.d.ts +25 -1
- package/dist/cli/messenger/port.d.ts.map +1 -1
- package/dist/cli/messenger/port.js.map +1 -1
- package/dist/cli/messenger/private-files.d.ts +25 -0
- package/dist/cli/messenger/private-files.d.ts.map +1 -0
- package/dist/cli/messenger/private-files.js +28 -0
- package/dist/cli/messenger/private-files.js.map +1 -0
- package/dist/cli/messenger/replies-command.d.ts +5 -0
- package/dist/cli/messenger/replies-command.d.ts.map +1 -0
- package/dist/cli/messenger/replies-command.js +78 -0
- package/dist/cli/messenger/replies-command.js.map +1 -0
- package/dist/cli/messenger/report.d.ts +5 -0
- package/dist/cli/messenger/report.d.ts.map +1 -1
- package/dist/cli/messenger/report.js +25 -7
- package/dist/cli/messenger/report.js.map +1 -1
- package/dist/cli/messenger/review.d.ts.map +1 -1
- package/dist/cli/messenger/review.js +5 -3
- package/dist/cli/messenger/review.js.map +1 -1
- package/dist/cli/messenger/searches-command.d.ts +5 -0
- package/dist/cli/messenger/searches-command.d.ts.map +1 -0
- package/dist/cli/messenger/searches-command.js +117 -0
- package/dist/cli/messenger/searches-command.js.map +1 -0
- package/dist/cli/messenger/serve-command.d.ts.map +1 -1
- package/dist/cli/messenger/serve-command.js +87 -8
- package/dist/cli/messenger/serve-command.js.map +1 -1
- package/dist/cli/messenger/server-command.d.ts.map +1 -1
- package/dist/cli/messenger/server-command.js +25 -2
- 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 +105 -4
- package/dist/cli/messenger/store-maintenance-command.js.map +1 -1
- package/dist/cli/messenger/tags-command.d.ts +5 -0
- package/dist/cli/messenger/tags-command.d.ts.map +1 -0
- package/dist/cli/messenger/tags-command.js +76 -0
- package/dist/cli/messenger/tags-command.js.map +1 -0
- package/dist/cli/messenger/watch-command.d.ts +4 -1
- package/dist/cli/messenger/watch-command.d.ts.map +1 -1
- package/dist/cli/messenger/watch-command.js +2 -1
- package/dist/cli/messenger/watch-command.js.map +1 -1
- package/dist/cli/store-settings.d.ts +26 -0
- package/dist/cli/store-settings.d.ts.map +1 -0
- package/dist/cli/store-settings.js +59 -0
- package/dist/cli/store-settings.js.map +1 -0
- package/dist/domain/models.d.ts +11 -1
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/domain/tags.d.ts +6 -0
- package/dist/domain/tags.d.ts.map +1 -0
- package/dist/domain/tags.js +15 -0
- package/dist/domain/tags.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/kit/contract.d.ts.map +1 -1
- package/dist/kit/contract.js +6 -2
- package/dist/kit/contract.js.map +1 -1
- package/dist/mcp/personal.d.ts.map +1 -1
- package/dist/mcp/personal.js +2 -1
- package/dist/mcp/personal.js.map +1 -1
- package/dist/mcp/prompts.d.ts.map +1 -1
- package/dist/mcp/prompts.js +27 -8
- package/dist/mcp/prompts.js.map +1 -1
- package/dist/mcp/tool.d.ts +5 -0
- package/dist/mcp/tool.d.ts.map +1 -1
- package/dist/mcp/tool.js +12 -1
- package/dist/mcp/tool.js.map +1 -1
- package/dist/mcp/tools/chats.d.ts.map +1 -1
- package/dist/mcp/tools/chats.js +22 -4
- package/dist/mcp/tools/chats.js.map +1 -1
- package/dist/mcp/tools/contacts.d.ts.map +1 -1
- package/dist/mcp/tools/contacts.js +21 -1
- package/dist/mcp/tools/contacts.js.map +1 -1
- package/dist/mcp/tools/conversations.d.ts.map +1 -1
- package/dist/mcp/tools/conversations.js +2 -7
- package/dist/mcp/tools/conversations.js.map +1 -1
- package/dist/mcp/tools/inbox.d.ts +2 -0
- package/dist/mcp/tools/inbox.d.ts.map +1 -1
- package/dist/mcp/tools/inbox.js +27 -4
- package/dist/mcp/tools/inbox.js.map +1 -1
- package/dist/mcp/tools/messages.d.ts.map +1 -1
- package/dist/mcp/tools/messages.js +8 -2
- package/dist/mcp/tools/messages.js.map +1 -1
- package/dist/mcp/tools/review.d.ts.map +1 -1
- package/dist/mcp/tools/review.js +23 -2
- package/dist/mcp/tools/review.js.map +1 -1
- package/dist/mcp/tools/search.d.ts +6 -3
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/mcp/tools/search.js +48 -11
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/mcp/tools/searches.d.ts +4 -0
- package/dist/mcp/tools/searches.d.ts.map +1 -0
- package/dist/mcp/tools/searches.js +77 -0
- package/dist/mcp/tools/searches.js.map +1 -0
- package/dist/mcp/tools/tags.d.ts +4 -0
- package/dist/mcp/tools/tags.d.ts.map +1 -0
- package/dist/mcp/tools/tags.js +57 -0
- package/dist/mcp/tools/tags.js.map +1 -0
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +7 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/replies/decide.d.ts +37 -0
- package/dist/replies/decide.d.ts.map +1 -0
- package/dist/replies/decide.js +112 -0
- package/dist/replies/decide.js.map +1 -0
- package/dist/replies/dry-run.d.ts +51 -0
- package/dist/replies/dry-run.d.ts.map +1 -0
- package/dist/replies/dry-run.js +116 -0
- package/dist/replies/dry-run.js.map +1 -0
- package/dist/replies/rules.d.ts +139 -0
- package/dist/replies/rules.d.ts.map +1 -0
- package/dist/replies/rules.js +128 -0
- package/dist/replies/rules.js.map +1 -0
- package/dist/replies/serve.d.ts +44 -0
- package/dist/replies/serve.d.ts.map +1 -0
- package/dist/replies/serve.js +70 -0
- package/dist/replies/serve.js.map +1 -0
- package/dist/replies/state.d.ts +36 -0
- package/dist/replies/state.d.ts.map +1 -0
- package/dist/replies/state.js +67 -0
- package/dist/replies/state.js.map +1 -0
- package/dist/search/lucene/automaton.d.ts +5 -0
- package/dist/search/lucene/automaton.d.ts.map +1 -1
- package/dist/search/lucene/automaton.js +116 -0
- package/dist/search/lucene/automaton.js.map +1 -1
- package/dist/search/lucene/parser.d.ts.map +1 -1
- package/dist/search/lucene/parser.js +6 -2
- package/dist/search/lucene/parser.js.map +1 -1
- package/dist/search/lucene/registry.d.ts +5 -5
- package/dist/search/lucene/registry.d.ts.map +1 -1
- package/dist/search/lucene/registry.js +12 -7
- package/dist/search/lucene/registry.js.map +1 -1
- package/dist/search/search.d.ts.map +1 -1
- package/dist/search/search.js +4 -3
- package/dist/search/search.js.map +1 -1
- package/dist/sends/flood.d.ts +57 -0
- package/dist/sends/flood.d.ts.map +1 -0
- package/dist/sends/flood.js +90 -0
- package/dist/sends/flood.js.map +1 -0
- package/dist/sends/guard.d.ts +4 -1
- package/dist/sends/guard.d.ts.map +1 -1
- package/dist/sends/guard.js +9 -1
- package/dist/sends/guard.js.map +1 -1
- package/dist/sends/index.d.ts +1 -0
- package/dist/sends/index.d.ts.map +1 -1
- package/dist/sends/index.js +1 -0
- package/dist/sends/index.js.map +1 -1
- package/dist/sends/journal.d.ts +2 -0
- package/dist/sends/journal.d.ts.map +1 -1
- package/dist/sends/journal.js.map +1 -1
- package/dist/sends/permissions.d.ts +1 -1
- package/dist/sends/permissions.d.ts.map +1 -1
- package/dist/sends/permissions.js +13 -1
- package/dist/sends/permissions.js.map +1 -1
- package/dist/services/archive.d.ts.map +1 -1
- package/dist/services/archive.js +4 -2
- package/dist/services/archive.js.map +1 -1
- package/dist/services/chat-stats.d.ts +3 -0
- package/dist/services/chat-stats.d.ts.map +1 -1
- package/dist/services/chat-stats.js +3 -0
- package/dist/services/chat-stats.js.map +1 -1
- package/dist/services/chats.d.ts +13 -3
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.js +63 -2
- package/dist/services/chats.js.map +1 -1
- package/dist/services/deps.d.ts +2 -0
- package/dist/services/deps.d.ts.map +1 -1
- package/dist/services/deps.js.map +1 -1
- package/dist/services/inbox.d.ts +0 -2
- package/dist/services/inbox.d.ts.map +1 -1
- package/dist/services/inbox.js +13 -8
- package/dist/services/inbox.js.map +1 -1
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +6 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/members-audit.d.ts +44 -0
- package/dist/services/members-audit.d.ts.map +1 -0
- package/dist/services/members-audit.js +111 -0
- package/dist/services/members-audit.js.map +1 -0
- package/dist/services/messages-search.d.ts +6 -4
- package/dist/services/messages-search.d.ts.map +1 -1
- package/dist/services/messages-search.js +24 -8
- package/dist/services/messages-search.js.map +1 -1
- package/dist/services/messages.d.ts +10 -3
- package/dist/services/messages.d.ts.map +1 -1
- package/dist/services/messages.js +48 -5
- package/dist/services/messages.js.map +1 -1
- package/dist/services/people.d.ts +7 -0
- package/dist/services/people.d.ts.map +1 -1
- package/dist/services/people.js +18 -0
- package/dist/services/people.js.map +1 -1
- package/dist/services/person-context.d.ts +69 -0
- package/dist/services/person-context.d.ts.map +1 -0
- package/dist/services/person-context.js +130 -0
- package/dist/services/person-context.js.map +1 -0
- package/dist/services/searches.d.ts +50 -0
- package/dist/services/searches.d.ts.map +1 -0
- package/dist/services/searches.js +127 -0
- package/dist/services/searches.js.map +1 -0
- package/dist/services/tags.d.ts +40 -0
- package/dist/services/tags.d.ts.map +1 -0
- package/dist/services/tags.js +56 -0
- package/dist/services/tags.js.map +1 -0
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js.map +1 -1
- package/dist/store/normalize.d.ts +2 -0
- package/dist/store/normalize.d.ts.map +1 -1
- package/dist/store/normalize.js +7 -5
- package/dist/store/normalize.js.map +1 -1
- package/dist/store/repair.d.ts +55 -0
- package/dist/store/repair.d.ts.map +1 -0
- package/dist/store/repair.js +313 -0
- package/dist/store/repair.js.map +1 -0
- package/dist/store/sqlite/accounts.d.ts.map +1 -1
- package/dist/store/sqlite/accounts.js +3 -1
- package/dist/store/sqlite/accounts.js.map +1 -1
- package/dist/store/sqlite/chats.d.ts.map +1 -1
- package/dist/store/sqlite/chats.js +3 -1
- package/dist/store/sqlite/chats.js.map +1 -1
- package/dist/store/sqlite/completeness.d.ts +10 -1
- package/dist/store/sqlite/completeness.d.ts.map +1 -1
- package/dist/store/sqlite/completeness.js +15 -3
- package/dist/store/sqlite/completeness.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 +3 -1
- 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-WZFJ4TMA.js → shared-IZ6QGA7N.js} +2 -1
- package/dist/store/sqlite/lucene.d.ts.map +1 -1
- package/dist/store/sqlite/lucene.js +17 -3
- package/dist/store/sqlite/lucene.js.map +1 -1
- package/dist/store/sqlite/manifest.d.ts.map +1 -1
- package/dist/store/sqlite/manifest.js +6 -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 +53 -0
- package/dist/store/sqlite/migrations.generated.js.map +1 -1
- package/dist/store/sqlite/person-links.d.ts +18 -0
- package/dist/store/sqlite/person-links.d.ts.map +1 -0
- package/dist/store/sqlite/person-links.js +122 -0
- package/dist/store/sqlite/person-links.js.map +1 -0
- package/dist/store/sqlite/roster.d.ts +57 -0
- package/dist/store/sqlite/roster.d.ts.map +1 -0
- package/dist/store/sqlite/roster.js +179 -0
- package/dist/store/sqlite/roster.js.map +1 -0
- package/dist/store/sqlite/schema.d.ts +753 -0
- package/dist/store/sqlite/schema.d.ts.map +1 -1
- package/dist/store/sqlite/schema.js +99 -0
- package/dist/store/sqlite/schema.js.map +1 -1
- package/dist/store/sqlite/search-index.d.ts +3 -0
- package/dist/store/sqlite/search-index.d.ts.map +1 -1
- package/dist/store/sqlite/search-index.js +8 -6
- package/dist/store/sqlite/search-index.js.map +1 -1
- package/dist/store/sqlite/search.d.ts +2 -0
- package/dist/store/sqlite/search.d.ts.map +1 -1
- package/dist/store/sqlite/search.js +5 -0
- package/dist/store/sqlite/search.js.map +1 -1
- package/dist/store/sqlite/searches.d.ts +35 -0
- package/dist/store/sqlite/searches.d.ts.map +1 -0
- package/dist/store/sqlite/searches.js +77 -0
- package/dist/store/sqlite/searches.js.map +1 -0
- package/dist/store/sqlite/stems.d.ts +61 -0
- package/dist/store/sqlite/stems.d.ts.map +1 -0
- package/dist/store/sqlite/stems.js +215 -0
- package/dist/store/sqlite/stems.js.map +1 -0
- package/dist/store/sqlite/tags.d.ts +38 -0
- package/dist/store/sqlite/tags.d.ts.map +1 -0
- package/dist/store/sqlite/tags.js +85 -0
- package/dist/store/sqlite/tags.js.map +1 -0
- package/dist/store/store.d.ts +99 -1
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +115 -2
- package/dist/store/store.js.map +1 -1
- package/package.json +1 -1
- package/parity.json +254 -30
- /package/dist/store/sqlite/drizzle/{shared-WZFJ4TMA.js.map → shared-IZ6QGA7N.js.map} +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
import { formatLocator } from "../domain/locator.js";
|
|
3
|
+
import { decide } from "./decide.js";
|
|
4
|
+
import { isTester } from "./rules.js";
|
|
5
|
+
import { emptyState, recordReply } from "./state.js";
|
|
6
|
+
export const DRY_RUN_DAYS = 7;
|
|
7
|
+
const PAGE = 500;
|
|
8
|
+
/**
|
|
9
|
+
* What the rules would have answered in the stored messages since `since`, as `serve` would have met
|
|
10
|
+
* them: oldest first, rules in file order, the first that replies wins, the limits counted as it goes.
|
|
11
|
+
* The state starts empty and lives only here, so a run changes no file and sends nothing. A rule that
|
|
12
|
+
* is off is tried as if on: trying it before turning it on is the point.
|
|
13
|
+
*/
|
|
14
|
+
export const dryRun = async (store, account, rules, { since, until = Date.now(), only, testers, }) => {
|
|
15
|
+
const tried = only === undefined ? rules : rules.filter((rule) => rule.id === only);
|
|
16
|
+
if (only !== undefined && tried.length === 0) {
|
|
17
|
+
throw new CliError("not_found", `no reply rule "${only}" — the rules are: ${rules.map((one) => one.id).join(", ")}`);
|
|
18
|
+
}
|
|
19
|
+
const chats = (await store.chats(account, {})).items.filter((chat) => chat.kind === "dialog" || chat.kind === "group");
|
|
20
|
+
const arrived = [];
|
|
21
|
+
for (const chat of chats) {
|
|
22
|
+
for (const message of await storedSince(store, account, chat.id, since)) {
|
|
23
|
+
if (Date.parse(message.timestamp) <= until)
|
|
24
|
+
arrived.push({ message, chat });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
arrived.sort((a, b) => Date.parse(a.message.timestamp) - Date.parse(b.message.timestamp));
|
|
28
|
+
const people = await store.people(account.provider, { account: account.account });
|
|
29
|
+
const { isContact, botOf, botFlags } = await senderFacts(store, account);
|
|
30
|
+
const results = tried.map((rule) => ({
|
|
31
|
+
id: rule.id,
|
|
32
|
+
would: [],
|
|
33
|
+
skipped: {},
|
|
34
|
+
}));
|
|
35
|
+
let state = emptyState();
|
|
36
|
+
for (const { message, chat } of arrived) {
|
|
37
|
+
const sender = message.senderId;
|
|
38
|
+
const isBot = sender === null ? null : await botOf(sender);
|
|
39
|
+
const now = Date.parse(message.timestamp);
|
|
40
|
+
for (const [index, rule] of tried.entries()) {
|
|
41
|
+
const result = results[index];
|
|
42
|
+
const decision = decide({ ...rule, on: true }, {
|
|
43
|
+
message,
|
|
44
|
+
chat,
|
|
45
|
+
owner: { id: account.account },
|
|
46
|
+
sender: {
|
|
47
|
+
isBot: isBot === true,
|
|
48
|
+
isContact: sender !== null && isContact(sender),
|
|
49
|
+
isTester: isTester(testers, account.provider, sender),
|
|
50
|
+
},
|
|
51
|
+
since,
|
|
52
|
+
}, state, now);
|
|
53
|
+
if ("skip" in decision) {
|
|
54
|
+
result.skipped[decision.skip] = (result.skipped[decision.skip] ?? 0) + 1;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
result.would.push({
|
|
58
|
+
locator: formatLocator({
|
|
59
|
+
provider: account.provider,
|
|
60
|
+
account: account.account,
|
|
61
|
+
chat: chat.id,
|
|
62
|
+
message: message.id,
|
|
63
|
+
}),
|
|
64
|
+
chatId: chat.id,
|
|
65
|
+
chatTitle: chat.title,
|
|
66
|
+
to: { id: sender, name: message.senderName ?? people.get(sender)?.name ?? null },
|
|
67
|
+
at: message.timestamp,
|
|
68
|
+
text: decision.reply.text,
|
|
69
|
+
asReply: decision.reply.asReply,
|
|
70
|
+
});
|
|
71
|
+
state = recordReply(state, rule, message, now);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const senders = new Set(arrived.map(({ message }) => message.senderId).filter((id) => id !== null));
|
|
76
|
+
return {
|
|
77
|
+
since: new Date(since).toISOString(),
|
|
78
|
+
until: new Date(until).toISOString(),
|
|
79
|
+
rules: results,
|
|
80
|
+
botUnknown: [...senders].filter((id) => botFlags.get(id) === null).length,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* What a message does not say about its sender, from the store: a contact is someone with a
|
|
85
|
+
* one-to-one chat, as `contacts list` counts them; `null` when the store has no bot flag.
|
|
86
|
+
*/
|
|
87
|
+
export const senderFacts = async (store, account) => {
|
|
88
|
+
const contacts = new Set((await store.contacts(account, { order: "recent", limit: Number.MAX_SAFE_INTEGER })).items.map((one) => one.id));
|
|
89
|
+
const botFlags = new Map();
|
|
90
|
+
const botOf = async (id) => {
|
|
91
|
+
if (!botFlags.has(id)) {
|
|
92
|
+
const person = await store.personOf({ provider: account.provider, id });
|
|
93
|
+
const own = person?.identities.find((one) => one.provider === account.provider && one.id === id);
|
|
94
|
+
botFlags.set(id, own?.isBot ?? null);
|
|
95
|
+
}
|
|
96
|
+
return botFlags.get(id) ?? null;
|
|
97
|
+
};
|
|
98
|
+
return { isContact: (id) => contacts.has(id), botOf, botFlags };
|
|
99
|
+
};
|
|
100
|
+
const storedSince = async (store, account, chat, since) => {
|
|
101
|
+
const found = [];
|
|
102
|
+
let before;
|
|
103
|
+
while (true) {
|
|
104
|
+
const page = await store.messages(account, chat, {
|
|
105
|
+
limit: PAGE,
|
|
106
|
+
since: new Date(since).toISOString(),
|
|
107
|
+
...(before === undefined ? {} : { before }),
|
|
108
|
+
});
|
|
109
|
+
found.unshift(...page.items);
|
|
110
|
+
const oldest = page.items[0];
|
|
111
|
+
if (!page.hasMore || oldest === undefined)
|
|
112
|
+
return found;
|
|
113
|
+
before = oldest.id;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=dry-run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dry-run.js","sourceRoot":"","sources":["../../src/replies/dry-run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
import type { AppIdentity } from "../cli/app.js";
|
|
3
|
+
export declare const REPLY_ACTIONS: readonly ["reply", "task"];
|
|
4
|
+
export declare const REPLY_MODELS: readonly ["fill-only", "may-reword"];
|
|
5
|
+
/** Channels, saved messages and chats of unknown kind are never answered, so a rule cannot name them. */
|
|
6
|
+
export declare const REPLY_KINDS: readonly ["dialog", "group"];
|
|
7
|
+
export declare const PLACEHOLDERS: readonly ["firstName", "name"];
|
|
8
|
+
export declare const DAYS: readonly ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
9
|
+
export type Day = (typeof DAYS)[number];
|
|
10
|
+
/** Minutes after midnight, `[from, to)`; `to` below `from` crosses midnight. */
|
|
11
|
+
export interface Window {
|
|
12
|
+
from: number;
|
|
13
|
+
to: number;
|
|
14
|
+
}
|
|
15
|
+
/** At most `count` sends inside any `ms`. */
|
|
16
|
+
export interface Limit {
|
|
17
|
+
count: number;
|
|
18
|
+
ms: number;
|
|
19
|
+
}
|
|
20
|
+
declare const rule: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
21
|
+
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a rule id is lower-case letters, digits and -">]>;
|
|
22
|
+
readonly on: v.BooleanSchema<undefined>;
|
|
23
|
+
readonly do: v.SchemaWithPipe<readonly [v.ArraySchema<v.PicklistSchema<readonly ["reply", "task"], undefined>, undefined>, v.MinLengthAction<("reply" | "task")[], 1, "a rule has to do something: reply">]>;
|
|
24
|
+
readonly where: v.StrictObjectSchema<{
|
|
25
|
+
readonly kinds: v.ArraySchema<v.PicklistSchema<readonly ["dialog", "group"], undefined>, undefined>;
|
|
26
|
+
readonly chats: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
27
|
+
readonly notChats: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
28
|
+
}, undefined>;
|
|
29
|
+
readonly when: v.StrictObjectSchema<{
|
|
30
|
+
readonly hours: v.NullableSchema<v.StrictObjectSchema<{
|
|
31
|
+
readonly outside: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a window is written \"09:00-19:00\", 24-hour">, v.TransformAction<string, Window>, v.CheckAction<Window, "a window has to start and end at different times">]>;
|
|
32
|
+
readonly days: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TransformAction<string, ("fri" | "mon" | "sat" | "sun" | "thu" | "tue" | "wed")[] | undefined>, v.CheckAction<("fri" | "mon" | "sat" | "sun" | "thu" | "tue" | "wed")[] | undefined, `days are written "mon-fri" or "sat,sun", from ${string}`>, v.TransformAction<("fri" | "mon" | "sat" | "sun" | "thu" | "tue" | "wed")[] | undefined, ("fri" | "mon" | "sat" | "sun" | "thu" | "tue" | "wed")[]>]>;
|
|
33
|
+
readonly timezone: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "a time zone is written as \"Europe/Madrid\"">]>;
|
|
34
|
+
}, undefined>, undefined>;
|
|
35
|
+
readonly words: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
36
|
+
readonly question: v.BooleanSchema<undefined>;
|
|
37
|
+
readonly mentionsMe: v.BooleanSchema<undefined>;
|
|
38
|
+
readonly from: v.StrictObjectSchema<{
|
|
39
|
+
readonly people: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
40
|
+
readonly notPeople: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
41
|
+
readonly contactsOnly: v.BooleanSchema<undefined>;
|
|
42
|
+
}, undefined>;
|
|
43
|
+
}, undefined>;
|
|
44
|
+
readonly reply: v.StrictObjectSchema<{
|
|
45
|
+
readonly template: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "a reply template cannot be empty">, v.CheckAction<string, `a template fills only ${string}`>]>;
|
|
46
|
+
readonly model: v.PicklistSchema<readonly ["fill-only", "may-reword"], undefined>;
|
|
47
|
+
readonly asReply: v.BooleanSchema<undefined>;
|
|
48
|
+
}, undefined>;
|
|
49
|
+
readonly limits: v.StrictObjectSchema<{
|
|
50
|
+
readonly perChat: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a limit is written \"1/12h\": this many replies in this long — m, h or d">, v.TransformAction<string, Limit>]>;
|
|
51
|
+
readonly perPerson: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a limit is written \"1/12h\": this many replies in this long — m, h or d">, v.TransformAction<string, Limit>]>;
|
|
52
|
+
}, undefined>;
|
|
53
|
+
}, undefined>, v.CheckAction<{
|
|
54
|
+
id: string;
|
|
55
|
+
on: boolean;
|
|
56
|
+
do: ("reply" | "task")[];
|
|
57
|
+
where: {
|
|
58
|
+
kinds: ("dialog" | "group")[];
|
|
59
|
+
chats: string[];
|
|
60
|
+
notChats: string[];
|
|
61
|
+
};
|
|
62
|
+
when: {
|
|
63
|
+
hours: {
|
|
64
|
+
outside: Window;
|
|
65
|
+
days: ("fri" | "mon" | "sat" | "sun" | "thu" | "tue" | "wed")[];
|
|
66
|
+
timezone: string;
|
|
67
|
+
} | null;
|
|
68
|
+
words: string[];
|
|
69
|
+
question: boolean;
|
|
70
|
+
mentionsMe: boolean;
|
|
71
|
+
from: {
|
|
72
|
+
people: string[];
|
|
73
|
+
notPeople: string[];
|
|
74
|
+
contactsOnly: boolean;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
reply: {
|
|
78
|
+
template: string;
|
|
79
|
+
model: "fill-only" | "may-reword";
|
|
80
|
+
asReply: boolean;
|
|
81
|
+
};
|
|
82
|
+
limits: {
|
|
83
|
+
perChat: Limit;
|
|
84
|
+
perPerson: Limit;
|
|
85
|
+
};
|
|
86
|
+
}, "\"task\" waits for the tasks package; only \"reply\" works now">]>;
|
|
87
|
+
export type ReplyRule = v.InferOutput<typeof rule>;
|
|
88
|
+
declare const tester: v.StrictObjectSchema<{
|
|
89
|
+
readonly provider: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
90
|
+
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
91
|
+
}, undefined>;
|
|
92
|
+
export type Tester = v.InferOutput<typeof tester>;
|
|
93
|
+
/**
|
|
94
|
+
* Every key written out, so the file shows all there is to set, as moderation's does (max-cli
|
|
95
|
+
* `NEED-314`). Off until the owner turns it on, and the limits are the tightest worth having.
|
|
96
|
+
*/
|
|
97
|
+
export declare const defaultRule: (id: string) => {
|
|
98
|
+
id: string;
|
|
99
|
+
on: boolean;
|
|
100
|
+
do: string[];
|
|
101
|
+
where: {
|
|
102
|
+
kinds: string[];
|
|
103
|
+
chats: never[];
|
|
104
|
+
notChats: never[];
|
|
105
|
+
};
|
|
106
|
+
when: {
|
|
107
|
+
hours: null;
|
|
108
|
+
words: never[];
|
|
109
|
+
question: boolean;
|
|
110
|
+
mentionsMe: boolean;
|
|
111
|
+
from: {
|
|
112
|
+
people: never[];
|
|
113
|
+
notPeople: never[];
|
|
114
|
+
contactsOnly: boolean;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
reply: {
|
|
118
|
+
template: string;
|
|
119
|
+
model: string;
|
|
120
|
+
asReply: boolean;
|
|
121
|
+
};
|
|
122
|
+
limits: {
|
|
123
|
+
perChat: string;
|
|
124
|
+
perPerson: string;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
export declare const repliesPathFor: (app: AppIdentity, profile: string, env?: NodeJS.ProcessEnv) => string;
|
|
128
|
+
export interface Replies {
|
|
129
|
+
testers: Tester[];
|
|
130
|
+
rules: ReplyRule[];
|
|
131
|
+
}
|
|
132
|
+
/** The owner's rules, in file order. No file is no rules; a file that does not check out refuses, naming the field. */
|
|
133
|
+
export declare const readReplyRules: (path: string) => ReplyRule[];
|
|
134
|
+
export declare const readReplies: (path: string) => Replies;
|
|
135
|
+
export declare const parseReplyRules: (parsed: unknown, path: string) => ReplyRule[];
|
|
136
|
+
export declare const parseReplies: (parsed: unknown, path: string) => Replies;
|
|
137
|
+
export declare const isTester: (testers: readonly Tester[], provider: string, id: string | null) => boolean;
|
|
138
|
+
export {};
|
|
139
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/replies/rules.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,eAAO,MAAM,aAAa,YAAI,OAAO,EAAE,MAAM,CAAU,CAAA;AACvD,eAAO,MAAM,YAAY,YAAI,WAAW,EAAE,YAAY,CAAU,CAAA;AAChE,yGAAyG;AACzG,eAAO,MAAM,WAAW,YAAI,QAAQ,EAAE,OAAO,CAAU,CAAA;AACvD,eAAO,MAAM,YAAY,YAAI,WAAW,EAAE,MAAM,CAAU,CAAA;AAC1D,eAAO,MAAM,IAAI,YAAI,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAA;AAE9E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;AAMvC,gFAAgF;AAChF,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED,6CAA6C;AAC7C,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,MAAM,CAAA;CACX;AAoED,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sEAkBT,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,IAAI,CAAC,CAAA;AAElD,QAAA,MAAM,MAAM;;;aAGV,CAAA;AAEF,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,MAAM,CAAC,CAAA;AAWjD;;;GAGG;AACH,eAAO,MAAM,WAAW,OAAQ,MAAM;;;;;QAI3B,KAAK;QAAc,KAAK;QAAM,QAAQ;;;QAE7C,KAAK;QACL,KAAK;QACL,QAAQ;QACR,UAAU;QACV,IAAI;YAAI,MAAM;YAAM,SAAS;YAAM,YAAY;;;;QAExC,QAAQ;QAAM,KAAK;QAAe,OAAO;;;QACxC,OAAO;QAAU,SAAS;;CACpC,CAAA;AAEF,eAAO,MAAM,cAAc,QAAS,WAAW,WAAW,MAAM,QAAO,MAAM,CAAC,UAAU,KAAiB,MACG,CAAA;AAE5G,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,EAAE,SAAS,EAAE,CAAA;CACnB;AAED,uHAAuH;AACvH,eAAO,MAAM,cAAc,SAAU,MAAM,KAAG,SAAS,EAA6B,CAAA;AAEpF,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,OAS1C,CAAA;AAED,eAAO,MAAM,eAAe,WAAY,OAAO,QAAQ,MAAM,KAAG,SAAS,EAAsC,CAAA;AAE/G,eAAO,MAAM,YAAY,WAAY,OAAO,QAAQ,MAAM,KAAG,OAI5D,CAAA;AAED,eAAO,MAAM,QAAQ,YAAa,SAAS,MAAM,EAAE,YAAY,MAAM,MAAM,MAAM,GAAG,IAAI,KAAG,OACuB,CAAA"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { CliError, resolvePaths } from "@leemour/cli-core";
|
|
4
|
+
import * as v from "valibot";
|
|
5
|
+
export const REPLY_ACTIONS = ["reply", "task"];
|
|
6
|
+
export const REPLY_MODELS = ["fill-only", "may-reword"];
|
|
7
|
+
/** Channels, saved messages and chats of unknown kind are never answered, so a rule cannot name them. */
|
|
8
|
+
export const REPLY_KINDS = ["dialog", "group"];
|
|
9
|
+
export const PLACEHOLDERS = ["firstName", "name"];
|
|
10
|
+
export const DAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
11
|
+
const WINDOW = /^([01]\d|2[0-3]):([0-5]\d)-([01]\d|2[0-3]):([0-5]\d)$/;
|
|
12
|
+
const LIMIT = /^([1-9]\d*)\/([1-9]\d*)(m|h|d)$/;
|
|
13
|
+
const UNIT_MS = { m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
14
|
+
const minutes = (hours, mins) => Number(hours) * 60 + Number(mins);
|
|
15
|
+
const window = v.pipe(v.string(), v.regex(WINDOW, 'a window is written "09:00-19:00", 24-hour'), v.transform((typed) => {
|
|
16
|
+
const [, h1, m1, h2, m2] = WINDOW.exec(typed);
|
|
17
|
+
return { from: minutes(h1, m1), to: minutes(h2, m2) };
|
|
18
|
+
}), v.check((one) => one.from !== one.to, "a window has to start and end at different times"));
|
|
19
|
+
const dayOf = (typed) => DAYS.indexOf(typed);
|
|
20
|
+
/** `mon-fri`, `sat,sun`, `fri-mon` across the week's end. */
|
|
21
|
+
const days = v.pipe(v.string(), v.transform((typed) => {
|
|
22
|
+
const found = new Set();
|
|
23
|
+
for (const part of typed.split(",").map((one) => one.trim().toLowerCase())) {
|
|
24
|
+
const [first = "", last = first] = part.split("-");
|
|
25
|
+
const from = dayOf(first);
|
|
26
|
+
const to = dayOf(last);
|
|
27
|
+
if (from < 0 || to < 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
for (let at = from;; at = (at + 1) % 7) {
|
|
30
|
+
found.add(DAYS[at]);
|
|
31
|
+
if (at === to)
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return DAYS.filter((day) => found.has(day));
|
|
36
|
+
}), v.check((one) => one !== undefined, `days are written "mon-fri" or "sat,sun", from ${DAYS.join(", ")}`), v.transform((one) => one));
|
|
37
|
+
const knownZone = (zone) => {
|
|
38
|
+
try {
|
|
39
|
+
new Intl.DateTimeFormat("en-US", { timeZone: zone });
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const timezone = v.pipe(v.string(), v.check(knownZone, 'a time zone is written as "Europe/Madrid"'));
|
|
47
|
+
const limit = v.pipe(v.string(), v.regex(LIMIT, 'a limit is written "1/12h": this many replies in this long — m, h or d'), v.transform((typed) => {
|
|
48
|
+
const [, count, amount, unit] = LIMIT.exec(typed);
|
|
49
|
+
return { count: Number(count), ms: Number(amount) * (UNIT_MS[unit ?? ""] ?? 0) };
|
|
50
|
+
}));
|
|
51
|
+
const ids = v.array(v.pipe(v.string(), v.minLength(1)));
|
|
52
|
+
const template = v.pipe(v.string(), v.minLength(1, "a reply template cannot be empty"), v.check((text) => [...text.matchAll(/\{(\w*)\}/g)].every(([, name]) => PLACEHOLDERS.includes(name)), `a template fills only ${PLACEHOLDERS.map((name) => `{${name}}`).join(", ")}`));
|
|
53
|
+
const rule = v.pipe(v.strictObject({
|
|
54
|
+
id: v.pipe(v.string(), v.regex(/^[a-z0-9][a-z0-9-]*$/, "a rule id is lower-case letters, digits and -")),
|
|
55
|
+
on: v.boolean(),
|
|
56
|
+
do: v.pipe(v.array(v.picklist(REPLY_ACTIONS)), v.minLength(1, "a rule has to do something: reply")),
|
|
57
|
+
where: v.strictObject({ kinds: v.array(v.picklist(REPLY_KINDS)), chats: ids, notChats: ids }),
|
|
58
|
+
when: v.strictObject({
|
|
59
|
+
hours: v.nullable(v.strictObject({ outside: window, days, timezone })),
|
|
60
|
+
words: v.array(v.pipe(v.string(), v.trim(), v.minLength(1))),
|
|
61
|
+
question: v.boolean(),
|
|
62
|
+
mentionsMe: v.boolean(),
|
|
63
|
+
from: v.strictObject({ people: ids, notPeople: ids, contactsOnly: v.boolean() }),
|
|
64
|
+
}),
|
|
65
|
+
reply: v.strictObject({ template, model: v.picklist(REPLY_MODELS), asReply: v.boolean() }),
|
|
66
|
+
limits: v.strictObject({ perChat: limit, perPerson: limit }),
|
|
67
|
+
}),
|
|
68
|
+
// NEED-582: a rule may open a task, but there is no tasks package to open it in yet.
|
|
69
|
+
v.check((one) => !one.do.includes("task"), '"task" waits for the tasks package; only "reply" works now'));
|
|
70
|
+
const tester = v.strictObject({
|
|
71
|
+
provider: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
72
|
+
id: v.pipe(v.string(), v.minLength(1)),
|
|
73
|
+
});
|
|
74
|
+
const file = v.pipe(
|
|
75
|
+
// NEED-601: rules answer test accounts only, until the owner rules otherwise; no list, nobody.
|
|
76
|
+
v.strictObject({ testers: v.optional(v.array(tester), []), rules: v.array(rule) }), v.check(({ rules }) => new Set(rules.map((one) => one.id)).size === rules.length, "two rules share an id; each needs its own"));
|
|
77
|
+
/**
|
|
78
|
+
* Every key written out, so the file shows all there is to set, as moderation's does (max-cli
|
|
79
|
+
* `NEED-314`). Off until the owner turns it on, and the limits are the tightest worth having.
|
|
80
|
+
*/
|
|
81
|
+
export const defaultRule = (id) => ({
|
|
82
|
+
id,
|
|
83
|
+
on: false,
|
|
84
|
+
do: ["reply"],
|
|
85
|
+
where: { kinds: ["dialog"], chats: [], notChats: [] },
|
|
86
|
+
when: {
|
|
87
|
+
hours: null,
|
|
88
|
+
words: [],
|
|
89
|
+
question: false,
|
|
90
|
+
mentionsMe: false,
|
|
91
|
+
from: { people: [], notPeople: [], contactsOnly: false },
|
|
92
|
+
},
|
|
93
|
+
reply: { template: "", model: "fill-only", asReply: true },
|
|
94
|
+
limits: { perChat: "1/1d", perPerson: "1/1d" },
|
|
95
|
+
});
|
|
96
|
+
export const repliesPathFor = (app, profile, env = process.env) => join(resolvePaths({ appName: app.appName, prefix: app.envPrefix, env }).config, `${profile}.replies.json`);
|
|
97
|
+
/** The owner's rules, in file order. No file is no rules; a file that does not check out refuses, naming the field. */
|
|
98
|
+
export const readReplyRules = (path) => readReplies(path).rules;
|
|
99
|
+
export const readReplies = (path) => {
|
|
100
|
+
if (!existsSync(path))
|
|
101
|
+
return { testers: [], rules: [] };
|
|
102
|
+
let parsed;
|
|
103
|
+
try {
|
|
104
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
throw broken(path, error instanceof Error ? error.message : String(error));
|
|
108
|
+
}
|
|
109
|
+
return parseReplies(parsed, path);
|
|
110
|
+
};
|
|
111
|
+
export const parseReplyRules = (parsed, path) => parseReplies(parsed, path).rules;
|
|
112
|
+
export const parseReplies = (parsed, path) => {
|
|
113
|
+
const checked = v.safeParse(file, parsed);
|
|
114
|
+
if (!checked.success)
|
|
115
|
+
throw broken(path, checked.issues.map(problem).join("; "));
|
|
116
|
+
return checked.output;
|
|
117
|
+
};
|
|
118
|
+
export const isTester = (testers, provider, id) => id !== null && testers.some((one) => one.id === id && (one.provider === undefined || one.provider === provider));
|
|
119
|
+
const problem = (issue) => {
|
|
120
|
+
const at = v.getDotPath(issue) ?? "the file";
|
|
121
|
+
if (issue.kind === "schema" && issue.expected === "never")
|
|
122
|
+
return `${at} is not a field a rule has`;
|
|
123
|
+
if (issue.expected === "Object" && issue.received === "undefined")
|
|
124
|
+
return `${at} is missing`;
|
|
125
|
+
return `${at}: ${issue.message}`;
|
|
126
|
+
};
|
|
127
|
+
const broken = (path, why) => new CliError("configuration_error", `the reply rules ${path} cannot be read (${why}) — fix the file`);
|
|
128
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/replies/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Chat, Id, Message } from "../domain/models.js";
|
|
2
|
+
export declare const NOT_ALLOWED = "replies.send is not allow";
|
|
3
|
+
export declare const NO_RULES = "no rules";
|
|
4
|
+
export type Replied = {
|
|
5
|
+
sent: string;
|
|
6
|
+
} | {
|
|
7
|
+
skip: string;
|
|
8
|
+
};
|
|
9
|
+
export interface Replier {
|
|
10
|
+
/** The profile's rules and state files, read on every message so an edit or `replies pause` applies at once. */
|
|
11
|
+
rulesPath: string;
|
|
12
|
+
statePath: string;
|
|
13
|
+
provider: string;
|
|
14
|
+
owner: {
|
|
15
|
+
id: Id | null;
|
|
16
|
+
username?: string;
|
|
17
|
+
};
|
|
18
|
+
/** ms: when `serve` began; what is older came in the catch-up and is never answered. */
|
|
19
|
+
since: number;
|
|
20
|
+
/** Whether the profile's `replies.send` is `allow`. `ask` is no: `serve` has nobody to ask. */
|
|
21
|
+
allowed: () => boolean;
|
|
22
|
+
chatOf: (chat: Id) => Promise<Pick<Chat, "id" | "kind">>;
|
|
23
|
+
senderOf: (person: Id) => Promise<{
|
|
24
|
+
isBot: boolean;
|
|
25
|
+
isContact: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
send: (reply: {
|
|
28
|
+
chat: Id;
|
|
29
|
+
text: string;
|
|
30
|
+
replyTo?: Id;
|
|
31
|
+
sendId: string;
|
|
32
|
+
origin: string;
|
|
33
|
+
}) => Promise<unknown>;
|
|
34
|
+
newSendId: () => string;
|
|
35
|
+
now?: () => number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* One arriving message through the rules, in file order, the first that answers sending — only to a
|
|
39
|
+
* sender named in `testers` (NEED-601), only with `replies.send` at `allow`. A send that fails is
|
|
40
|
+
* tried once more with the same send id, so the messenger delivers at most one; a message whose
|
|
41
|
+
* reply may have gone is kept as answered.
|
|
42
|
+
*/
|
|
43
|
+
export declare const replyTo: (deps: Replier, message: Message) => Promise<Replied>;
|
|
44
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/replies/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAM5D,eAAO,MAAM,WAAW,8BAA8B,CAAA;AACtD,eAAO,MAAM,QAAQ,aAAa,CAAA;AAElC,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD,MAAM,WAAW,OAAO;IACtB,gHAAgH;IAChH,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE;QAAE,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,wFAAwF;IACxF,KAAK,EAAE,MAAM,CAAA;IACb,+FAA+F;IAC/F,OAAO,EAAE,MAAM,OAAO,CAAA;IACtB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACzE,IAAI,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3G,SAAS,EAAE,MAAM,MAAM,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAKD;;;;;GAKG;AACH,eAAO,MAAM,OAAO,SAAgB,OAAO,WAAW,OAAO,KAAG,OAAO,CAAC,OAAO,CAkD9E,CAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { codeOf } from "../sends/guarded.js";
|
|
2
|
+
import { decide } from "./decide.js";
|
|
3
|
+
import { isTester, readReplies } from "./rules.js";
|
|
4
|
+
import { readRepliesState, recordReply, writeRepliesState } from "./state.js";
|
|
5
|
+
export const NOT_ALLOWED = "replies.send is not allow";
|
|
6
|
+
export const NO_RULES = "no rules";
|
|
7
|
+
/** Refusals that a second try with the same id would only repeat. */
|
|
8
|
+
const FINAL = new Set(["permission_error", "confirmation_required", "rate_limited", "validation_error", "not_found"]);
|
|
9
|
+
/**
|
|
10
|
+
* One arriving message through the rules, in file order, the first that answers sending — only to a
|
|
11
|
+
* sender named in `testers` (NEED-601), only with `replies.send` at `allow`. A send that fails is
|
|
12
|
+
* tried once more with the same send id, so the messenger delivers at most one; a message whose
|
|
13
|
+
* reply may have gone is kept as answered.
|
|
14
|
+
*/
|
|
15
|
+
export const replyTo = async (deps, message) => {
|
|
16
|
+
const { rules, testers } = readReplies(deps.rulesPath);
|
|
17
|
+
if (rules.length === 0)
|
|
18
|
+
return { skip: NO_RULES };
|
|
19
|
+
let state = readRepliesState(deps.statePath);
|
|
20
|
+
const now = deps.now?.() ?? Date.now();
|
|
21
|
+
const tester = isTester(testers, deps.provider, message.senderId);
|
|
22
|
+
const chat = tester ? await deps.chatOf(message.chatId) : { id: message.chatId, kind: "unknown" };
|
|
23
|
+
const facts = tester && message.senderId !== null ? await deps.senderOf(message.senderId) : { isBot: false, isContact: false };
|
|
24
|
+
const incoming = {
|
|
25
|
+
message,
|
|
26
|
+
chat,
|
|
27
|
+
owner: deps.owner,
|
|
28
|
+
sender: { ...facts, isTester: tester },
|
|
29
|
+
since: deps.since,
|
|
30
|
+
};
|
|
31
|
+
let why = "no rule matched";
|
|
32
|
+
for (const rule of rules) {
|
|
33
|
+
const decision = decide(rule, incoming, state, now);
|
|
34
|
+
if ("skip" in decision) {
|
|
35
|
+
why = decision.skip;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!deps.allowed())
|
|
39
|
+
return { skip: NOT_ALLOWED };
|
|
40
|
+
const sendId = deps.newSendId();
|
|
41
|
+
const reply = {
|
|
42
|
+
chat: message.chatId,
|
|
43
|
+
text: decision.reply.text,
|
|
44
|
+
...(decision.reply.asReply ? { replyTo: message.id } : {}),
|
|
45
|
+
sendId,
|
|
46
|
+
origin: `rule:${rule.id}`,
|
|
47
|
+
};
|
|
48
|
+
let failed;
|
|
49
|
+
try {
|
|
50
|
+
await deps.send(reply);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (FINAL.has(codeOf(error)))
|
|
54
|
+
return { skip: codeOf(error) };
|
|
55
|
+
try {
|
|
56
|
+
await deps.send(reply);
|
|
57
|
+
}
|
|
58
|
+
catch (again) {
|
|
59
|
+
failed = again;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
state = recordReply(readRepliesState(deps.statePath), rule, message, now);
|
|
63
|
+
writeRepliesState(deps.statePath, state);
|
|
64
|
+
if (failed !== undefined)
|
|
65
|
+
throw failed;
|
|
66
|
+
return { sent: rule.id };
|
|
67
|
+
}
|
|
68
|
+
return { skip: why };
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/replies/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
import type { AppIdentity } from "../cli/app.js";
|
|
3
|
+
import type { Message } from "../domain/models.js";
|
|
4
|
+
import type { ReplyRule } from "./rules.js";
|
|
5
|
+
/** Enough to cover any restart's catch-up; older ids are past the catch-up start and never answered anyway. */
|
|
6
|
+
export declare const ANSWERED_KEPT = 1000;
|
|
7
|
+
declare const stateFile: v.ObjectSchema<{
|
|
8
|
+
readonly paused: v.BooleanSchema<undefined>;
|
|
9
|
+
/** `<chat>:<message>`, oldest first. */
|
|
10
|
+
readonly answered: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
11
|
+
/** Per rule, when it replied, by chat and by person: what its limits count. */
|
|
12
|
+
readonly sent: v.RecordSchema<v.StringSchema<undefined>, v.ObjectSchema<{
|
|
13
|
+
readonly chats: v.RecordSchema<v.StringSchema<undefined>, v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
14
|
+
readonly people: v.RecordSchema<v.StringSchema<undefined>, v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
15
|
+
}, undefined>, undefined>;
|
|
16
|
+
}, undefined>;
|
|
17
|
+
export type RepliesState = v.InferOutput<typeof stateFile>;
|
|
18
|
+
export declare const emptyState: () => RepliesState;
|
|
19
|
+
export declare const answeredKey: (message: Pick<Message, "chatId" | "id">) => string;
|
|
20
|
+
/**
|
|
21
|
+
* What the rules did, apart from the rules: editing the rules file never forgets what was already
|
|
22
|
+
* answered. Read on every message, so `pause` stops a running `serve` without a restart.
|
|
23
|
+
*/
|
|
24
|
+
export declare const repliesStatePathFor: (app: AppIdentity, profile: string, env?: NodeJS.ProcessEnv) => string;
|
|
25
|
+
/** A file that does not check out refuses: guessing would forget what was answered, and answer it twice. */
|
|
26
|
+
export declare const readRepliesState: (path: string) => RepliesState;
|
|
27
|
+
export declare const writeRepliesState: (path: string, state: RepliesState) => void;
|
|
28
|
+
export declare const paused: (state: RepliesState, on: boolean) => RepliesState;
|
|
29
|
+
/**
|
|
30
|
+
* The state after `rule` replied to `message` at `now`: the message answered, the reply counted for
|
|
31
|
+
* its chat and its sender. Counts older than the rule's longest limit are dropped, since no limit
|
|
32
|
+
* reads them.
|
|
33
|
+
*/
|
|
34
|
+
export declare const recordReply: (state: RepliesState, rule: Pick<ReplyRule, "id" | "limits">, message: Pick<Message, "chatId" | "id" | "senderId">, now: number) => RepliesState;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/replies/state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,KAAK,EAAM,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C,+GAA+G;AAC/G,eAAO,MAAM,aAAa,OAAO,CAAA;AAIjC,QAAA,MAAM,SAAS;;IAEb,wCAAwC;;IAExC,+EAA+E;;;;;aAE/E,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,SAAS,CAAC,CAAA;AAE1D,eAAO,MAAM,UAAU,QAAO,YAA2D,CAAA;AAEzF,eAAO,MAAM,WAAW,YAAa,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC,KAAG,MAA2C,CAAA;AAEjH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAS,WAAW,WAAW,MAAM,QAAO,MAAM,CAAC,UAAU,KAAiB,MACG,CAAA;AAEjH,4GAA4G;AAC5G,eAAO,MAAM,gBAAgB,SAAU,MAAM,KAAG,YAa/C,CAAA;AAED,eAAO,MAAM,iBAAiB,SAAU,MAAM,SAAS,YAAY,KAAG,IACZ,CAAA;AAE1D,eAAO,MAAM,MAAM,UAAW,YAAY,MAAM,OAAO,KAAG,YAA0C,CAAA;AAEpG;;;;GAIG;AACH,eAAO,MAAM,WAAW,UACf,YAAY,QACb,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,QAAQ,CAAC,WAC7B,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI,GAAG,UAAU,CAAC,OAC/C,MAAM,KACV,YAsBF,CAAA"}
|