@helyx/bot 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +16 -1
- package/dist/auto-moderation-scanner-registry.d.ts +27 -0
- package/dist/auto-moderation-scanner-registry.js +172 -0
- package/dist/cloud-connector.js +3 -0
- package/dist/cloud-control-dispatcher.js +32 -0
- package/dist/command-path-contract.js +1 -0
- package/dist/control-permissions.d.ts +2 -2
- package/dist/control-permissions.js +4 -2
- package/dist/control-server.d.ts +72 -16
- package/dist/control-server.js +150 -250
- package/dist/dashboard-action-control.d.ts +25 -0
- package/dist/dashboard-action-control.js +73 -0
- package/dist/dashboard-action-routes.d.ts +9 -0
- package/dist/dashboard-action-routes.js +36 -0
- package/dist/dashboard-feature-control.d.ts +37 -0
- package/dist/dashboard-feature-control.js +206 -0
- package/dist/dashboard-module-projection.d.ts +9 -0
- package/dist/dashboard-module-projection.js +15 -0
- package/dist/dashboard-preview-import-routes.d.ts +6 -0
- package/dist/dashboard-preview-import-routes.js +121 -0
- package/dist/discord-analytics.d.ts +3 -0
- package/dist/discord-analytics.js +49 -5
- package/dist/discord-auto-moderation-service.d.ts +21 -0
- package/dist/discord-auto-moderation-service.js +425 -0
- package/dist/discord-guild-health.d.ts +2 -2
- package/dist/discord-member-enforcement-service.d.ts +26 -0
- package/dist/discord-member-enforcement-service.js +590 -0
- package/dist/discord-message-enforcement-service.d.ts +9 -0
- package/dist/discord-message-enforcement-service.js +102 -0
- package/dist/discord-violation-thread-service.d.ts +33 -0
- package/dist/discord-violation-thread-service.js +789 -0
- package/dist/ecko-evidence-receiver.d.ts +32 -0
- package/dist/ecko-evidence-receiver.js +173 -0
- package/dist/ecko-transport-auth.d.ts +19 -0
- package/dist/ecko-transport-auth.js +80 -0
- package/dist/ecko-transport-routes.d.ts +19 -0
- package/dist/ecko-transport-routes.js +130 -0
- package/dist/embedded-auto-moderation-pool.d.ts +46 -0
- package/dist/embedded-auto-moderation-pool.js +324 -0
- package/dist/embedded-auto-moderation-scanner.d.ts +34 -0
- package/dist/embedded-auto-moderation-scanner.js +263 -0
- package/dist/embedded-auto-moderation-worker-runtime.d.ts +37 -0
- package/dist/embedded-auto-moderation-worker-runtime.js +37 -0
- package/dist/embedded-auto-moderation-worker.d.ts +2 -0
- package/dist/embedded-auto-moderation-worker.js +13 -0
- package/dist/events.d.ts +5 -1
- package/dist/events.js +243 -4
- package/dist/guilds.d.ts +2 -2
- package/dist/guilds.js +14 -8
- package/dist/hosted-server-moderation.d.ts +16 -5
- package/dist/hosted-server-moderation.js +32 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +84 -14
- package/dist/moderation-action-registry.d.ts +11 -0
- package/dist/moderation-action-registry.js +375 -0
- package/dist/moderation-case-disassociation.d.ts +13 -0
- package/dist/moderation-case-disassociation.js +83 -0
- package/dist/moderation-platform-services.d.ts +27 -0
- package/dist/moderation-platform-services.js +47 -0
- package/dist/modules.d.ts +6 -0
- package/dist/modules.js +65 -3
- package/dist/privileged-message-metadata-cache.d.ts +35 -0
- package/dist/privileged-message-metadata-cache.js +70 -0
- package/dist/scheduled-task-runner.js +24 -1
- package/dist/shard-contracts.d.ts +151 -1
- package/dist/shard-contracts.js +142 -1
- package/dist/shard-coordinator.d.ts +1 -1
- package/dist/shard-ecko-scanner-registry.d.ts +38 -0
- package/dist/shard-ecko-scanner-registry.js +96 -0
- package/dist/shard-router.d.ts +23 -0
- package/dist/shard-router.js +69 -5
- package/dist/shard-supervisor.d.ts +11 -0
- package/dist/shard-supervisor.js +67 -4
- package/dist/shard-worker.d.ts +15 -1
- package/dist/shard-worker.js +95 -17
- package/dist/transient-violation-evidence.d.ts +39 -0
- package/dist/transient-violation-evidence.js +132 -0
- package/documentation/README.md +2 -0
- package/documentation/examples/.env.example +12 -0
- package/documentation/guides/self-hosting.md +17 -1
- package/documentation/modules/auto-moderation.md +51 -0
- package/documentation/modules/moderation.md +42 -0
- package/documentation/reference/environment.md +11 -0
- package/package.json +17 -5
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { VIOLATION_THREAD_LIMITS, } from "@helyx/sdk";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
const MAX_EVIDENCE_ENTRIES = 10_000;
|
|
4
|
+
const MAX_EVIDENCE_TOTAL_BYTES = 16 * 1_024 * 1_024;
|
|
5
|
+
/**
|
|
6
|
+
* Bot-private, memory-only hand-off for message content. The opaque reference is
|
|
7
|
+
* safe to cross module boundaries; the content and this vault are not.
|
|
8
|
+
*/
|
|
9
|
+
export class TransientViolationEvidenceStore {
|
|
10
|
+
now;
|
|
11
|
+
createKey;
|
|
12
|
+
#records = new Map();
|
|
13
|
+
#maximumEntries;
|
|
14
|
+
#maximumTotalBytes;
|
|
15
|
+
#totalBytes = 0;
|
|
16
|
+
constructor(now = Date.now, createKey = randomUUID, limits = {}) {
|
|
17
|
+
this.now = now;
|
|
18
|
+
this.createKey = createKey;
|
|
19
|
+
this.#maximumEntries = boundedPositiveInteger(limits.entries ?? MAX_EVIDENCE_ENTRIES, MAX_EVIDENCE_ENTRIES, "entry limit");
|
|
20
|
+
this.#maximumTotalBytes = boundedPositiveInteger(limits.totalBytes ?? MAX_EVIDENCE_TOTAL_BYTES, MAX_EVIDENCE_TOTAL_BYTES, "byte limit");
|
|
21
|
+
}
|
|
22
|
+
capture(input) {
|
|
23
|
+
validateIdentity(input.guildId);
|
|
24
|
+
validateIdentity(input.channelId);
|
|
25
|
+
if (input.messageId !== null)
|
|
26
|
+
validateIdentity(input.messageId);
|
|
27
|
+
validateIdentity(input.authorUserId);
|
|
28
|
+
validateBinding(input.sourceEventId, "source event");
|
|
29
|
+
if (input.source === "enhanced_scanner")
|
|
30
|
+
validateBinding(input.configurationRevision, "configuration revision");
|
|
31
|
+
const contentBytes = Buffer.byteLength(input.content, "utf8");
|
|
32
|
+
if (input.content.length < 1 ||
|
|
33
|
+
input.content.length > VIOLATION_THREAD_LIMITS.evidenceCharacters ||
|
|
34
|
+
contentBytes > VIOLATION_THREAD_LIMITS.evidenceUtf8Bytes)
|
|
35
|
+
return null;
|
|
36
|
+
this.#pruneExpired();
|
|
37
|
+
if (this.#records.size >= this.#maximumEntries ||
|
|
38
|
+
this.#totalBytes + contentBytes > this.#maximumTotalBytes)
|
|
39
|
+
return null;
|
|
40
|
+
const key = this.createKey();
|
|
41
|
+
if (this.#records.has(key))
|
|
42
|
+
return null;
|
|
43
|
+
const base = {
|
|
44
|
+
key,
|
|
45
|
+
sourceEventId: input.sourceEventId,
|
|
46
|
+
guildId: input.guildId,
|
|
47
|
+
channelId: input.channelId,
|
|
48
|
+
messageId: input.messageId,
|
|
49
|
+
authorUserId: input.authorUserId,
|
|
50
|
+
};
|
|
51
|
+
const reference = Object.freeze(input.source === "enhanced_scanner"
|
|
52
|
+
? {
|
|
53
|
+
...base,
|
|
54
|
+
source: input.source,
|
|
55
|
+
configurationRevision: input.configurationRevision,
|
|
56
|
+
}
|
|
57
|
+
: { ...base, source: input.source });
|
|
58
|
+
this.#records.set(reference.key, {
|
|
59
|
+
reference,
|
|
60
|
+
content: input.content,
|
|
61
|
+
bytes: contentBytes,
|
|
62
|
+
expiresAt: this.now() + VIOLATION_THREAD_LIMITS.evidenceLifetimeSeconds * 1_000,
|
|
63
|
+
});
|
|
64
|
+
this.#totalBytes += contentBytes;
|
|
65
|
+
return reference;
|
|
66
|
+
}
|
|
67
|
+
consume(reference) {
|
|
68
|
+
const result = this.read(reference);
|
|
69
|
+
if (result.outcome === "available")
|
|
70
|
+
this.discard(reference);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
/** Read without discarding so a failed Discord send can be retried. */
|
|
74
|
+
read(reference) {
|
|
75
|
+
const record = this.#records.get(reference.key);
|
|
76
|
+
if (!record)
|
|
77
|
+
return { outcome: "unavailable" };
|
|
78
|
+
if (record.expiresAt <= this.now()) {
|
|
79
|
+
this.#delete(reference.key, record);
|
|
80
|
+
return { outcome: "unavailable" };
|
|
81
|
+
}
|
|
82
|
+
if (!sameReference(record.reference, reference))
|
|
83
|
+
return { outcome: "mismatch" };
|
|
84
|
+
return { outcome: "available", content: record.content };
|
|
85
|
+
}
|
|
86
|
+
discard(reference) {
|
|
87
|
+
const record = this.#records.get(reference.key);
|
|
88
|
+
if (record && sameReference(record.reference, reference))
|
|
89
|
+
this.#delete(reference.key, record);
|
|
90
|
+
}
|
|
91
|
+
get size() {
|
|
92
|
+
this.#pruneExpired();
|
|
93
|
+
return this.#records.size;
|
|
94
|
+
}
|
|
95
|
+
#pruneExpired() {
|
|
96
|
+
const now = this.now();
|
|
97
|
+
for (const [key, record] of this.#records)
|
|
98
|
+
if (record.expiresAt <= now)
|
|
99
|
+
this.#delete(key, record);
|
|
100
|
+
}
|
|
101
|
+
#delete(key, record) {
|
|
102
|
+
if (!this.#records.delete(key))
|
|
103
|
+
return;
|
|
104
|
+
this.#totalBytes = Math.max(0, this.#totalBytes - record.bytes);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function boundedPositiveInteger(value, maximum, subject) {
|
|
108
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > maximum)
|
|
109
|
+
throw new RangeError(`Violation evidence ${subject} is invalid`);
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
function sameReference(left, right) {
|
|
113
|
+
return (left.source === right.source &&
|
|
114
|
+
left.key === right.key &&
|
|
115
|
+
left.sourceEventId === right.sourceEventId &&
|
|
116
|
+
left.guildId === right.guildId &&
|
|
117
|
+
left.channelId === right.channelId &&
|
|
118
|
+
left.messageId === right.messageId &&
|
|
119
|
+
left.authorUserId === right.authorUserId &&
|
|
120
|
+
(left.source !== "enhanced_scanner" ||
|
|
121
|
+
(right.source === "enhanced_scanner" &&
|
|
122
|
+
left.configurationRevision === right.configurationRevision)));
|
|
123
|
+
}
|
|
124
|
+
function validateIdentity(value) {
|
|
125
|
+
if (!/^\d{17,20}$/u.test(value))
|
|
126
|
+
throw new Error("Violation evidence identity is invalid");
|
|
127
|
+
}
|
|
128
|
+
function validateBinding(value, subject) {
|
|
129
|
+
if (value.trim().length < 1 || value.length > 160 || /[\r\n]/u.test(value))
|
|
130
|
+
throw new Error(`Violation evidence ${subject} is invalid`);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=transient-violation-evidence.js.map
|
package/documentation/README.md
CHANGED
|
@@ -20,6 +20,8 @@ and module guides.
|
|
|
20
20
|
- [Build an independent module](modules/building-a-module.md)
|
|
21
21
|
- [Test a module](modules/testing-a-module.md)
|
|
22
22
|
- [Prepare a module package](modules/publishing-a-module.md)
|
|
23
|
+
- [Operate the Moderation module](modules/moderation.md)
|
|
24
|
+
- [Operate the Auto Moderation module](modules/auto-moderation.md)
|
|
23
25
|
|
|
24
26
|
The complete working example is published separately as
|
|
25
27
|
`@helyx/module-example`. It is intentionally independent: module-specific
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
# REQUIRED — EDIT THESE VALUES
|
|
2
2
|
|
|
3
|
+
HELYX_RUNTIME_PROFILE=self-hosted
|
|
4
|
+
HELYX_DEPLOYMENT_ENVIRONMENT=self-hosted
|
|
5
|
+
|
|
3
6
|
# Discord
|
|
4
7
|
DISCORD_TOKEN=replace-me
|
|
5
8
|
DISCORD_CLIENT_ID=12345678901234567
|
|
6
9
|
# Explicit privileged gateway opt-in. Keep false unless a reviewed loaded module
|
|
7
10
|
# requires message content and it is enabled in the Discord developer portal.
|
|
8
11
|
HELYX_MESSAGE_CONTENT_ENABLED=false
|
|
12
|
+
# Enhanced Auto Moderation stays inside this bot process. One worker is the
|
|
13
|
+
# recommended default; increase only after measuring local CPU and memory.
|
|
14
|
+
HELYX_AUTOMOD_EMBEDDED_WORKERS=1
|
|
15
|
+
# Optional conservative queue overrides (hard ceilings: 5000 items, 64 MiB,
|
|
16
|
+
# 100 items per server). Defaults are used when these remain commented.
|
|
17
|
+
# HELYX_AUTOMOD_EMBEDDED_QUEUE_ITEMS=5000
|
|
18
|
+
# HELYX_AUTOMOD_EMBEDDED_QUEUE_BYTES=67108864
|
|
19
|
+
# HELYX_AUTOMOD_EMBEDDED_QUEUE_ITEMS_PER_GUILD=100
|
|
20
|
+
# HELYX_AUTOMOD_EMBEDDED_REFRESH_SECONDS=300
|
|
9
21
|
# Enable only when member inspection modules are loaded and the Discord Server
|
|
10
22
|
# Members Intent is enabled in the Discord developer portal.
|
|
11
23
|
HELYX_GUILD_MEMBERS_ENABLED=false
|
|
@@ -23,6 +23,21 @@ project is the source of truth for its pinned public packages and module
|
|
|
23
23
|
catalogue. Do not copy source trees from the Helyx repository into an
|
|
24
24
|
installation.
|
|
25
25
|
|
|
26
|
+
### Enhanced Auto Moderation resources
|
|
27
|
+
|
|
28
|
+
Enhanced Auto Moderation remains inside the existing bot process and uses the
|
|
29
|
+
installation's existing PostgreSQL database. It does not require Helyx Ecko,
|
|
30
|
+
Redis, another bot, another database, a public endpoint or a Premium heartbeat.
|
|
31
|
+
Standard mode starts no scanning worker.
|
|
32
|
+
|
|
33
|
+
Enhanced mode requires the privileged Message Content intent in both the Discord
|
|
34
|
+
developer portal and `HELYX_MESSAGE_CONTENT_ENABLED=true`. Start with the default
|
|
35
|
+
single worker and measure process CPU, memory, queue depth and module health on
|
|
36
|
+
the real host before considering two to four workers. Queue overflow, stale
|
|
37
|
+
configuration or repeated worker failure falls back to Discord-native Standard
|
|
38
|
+
coverage; it does not make unsafe actions on delayed content. Use `npm run doctor`
|
|
39
|
+
after environment changes and the dashboard module-health view for diagnosis.
|
|
40
|
+
|
|
26
41
|
Every implemented catalogue entry carries a manifest-backed deployment label
|
|
27
42
|
and credential-risk label. A planned entry without a canonical v0.4 manifest
|
|
28
43
|
is explicitly `unclassified`; that label makes no promise about its future
|
|
@@ -47,7 +62,8 @@ downloaded manifest.
|
|
|
47
62
|
|
|
48
63
|
- Run `npm run doctor` after configuration or package changes.
|
|
49
64
|
- Watch process health, database capacity, command deployment errors, and module
|
|
50
|
-
health.
|
|
65
|
+
health. When Enhanced Auto Moderation is active, also watch CPU, memory,
|
|
66
|
+
scanner queue pressure and Standard-fallback guidance.
|
|
51
67
|
- Back up both PostgreSQL and the environment or key material needed to decrypt
|
|
52
68
|
its records.
|
|
53
69
|
- Test recovery in a disposable environment.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Auto Moderation module
|
|
2
|
+
|
|
3
|
+
Auto Moderation is an unpublished Testing module that manages Helyx-owned Discord Auto Moderation rules and optional Enhanced matching. It supports Hosted and Self-Hosted Helyx, but version `1.0.0` must be explicitly included in a reviewed test composition and is not available from the public npm catalogue.
|
|
4
|
+
|
|
5
|
+
## Supported Self-Hosted shape
|
|
6
|
+
|
|
7
|
+
Use the same Helyx bot process and PostgreSQL database as the rest of the installation. Standard mode starts no content-scanning worker. Enhanced starts a bounded worker pool inside the bot process. Self-hosters do not deploy Helyx Ecko, Redis, a second bot connection or any Helyx-hosted scanner.
|
|
8
|
+
|
|
9
|
+
The matching engine is the deliberate `@helyx/module-automod/engine` package export. Hosted Ecko and the embedded worker use the same versioned engine and result contract; module code remains free of Discord.js, database, network and environment access.
|
|
10
|
+
|
|
11
|
+
## Discord setup
|
|
12
|
+
|
|
13
|
+
Grant **Manage Server** so Helyx can inspect and manage only its mapped rules. Grant **Moderate Members** when a native rule uses Discord timeout. Standard consumes Discord's Auto Moderation configuration and execution Gateway events and does not require Message Content.
|
|
14
|
+
|
|
15
|
+
Enhanced requires Message Content. In the Discord Developer Portal for the Self-Hosted application, enable the **Message Content Intent**, set `HELYX_MESSAGE_CONTENT_ENABLED=true` in the supported environment file and restart the bot. Keep it false when only Standard is used. Do not expose the bot token or use Message Content for unrelated modules.
|
|
16
|
+
|
|
17
|
+
## Standard and Enhanced
|
|
18
|
+
|
|
19
|
+
Only one mode is effective at a time. Standard delegates content evaluation to Discord native rules. Enhanced also evaluates eligible Gateway message observations using deterministic exact, phrase, wildcard, reviewed normalisation and explicitly opted-in bounded fuzzy matching. Custom regex remains on Discord's native Rust engine.
|
|
20
|
+
|
|
21
|
+
Self-Hosted Enhanced is locally included and never requires a Helyx Premium slot. Hosted Enhanced is a Premium feature inside the free module and uses private Ecko. Losing Enhanced capability falls back to Standard without deleting saved rules or thresholds.
|
|
22
|
+
|
|
23
|
+
## Resource profile and backpressure
|
|
24
|
+
|
|
25
|
+
Enhanced compiles immutable rule snapshots into bounded process memory. Terms, phrases, allow lists and observation text disappear on invalidation, eviction and stop. The worker pool preserves per-server ordering, limits queue size, applies a circuit breaker after worker failure and rejects overflow into effective Standard fallback. It performs no REST fetch for missing message content.
|
|
26
|
+
|
|
27
|
+
Size the bot for actual Enhanced traffic and begin with Balanced confidence. Watch event-loop delay, queue depth, rejected work, scan latency and memory. A degraded worker must not prevent Discord-native rules from enforcing Standard coverage.
|
|
28
|
+
|
|
29
|
+
## Data and privacy
|
|
30
|
+
|
|
31
|
+
PostgreSQL stores logical rules, Discord mapping/fingerprint, structural action receipts, durable enforcement claims and anonymous daily counts. It never stores matched content, matched terms, bodies, attachments, embeds or transient evidence. Native receipts expire after 90 days; settled claim/count history is bounded to 365 days.
|
|
32
|
+
|
|
33
|
+
Discord native alerts and optional Moderation violation-thread evidence are Discord-owned content. Restrict those channels separately. Logging, Audit, metrics and traces must remain content-free.
|
|
34
|
+
|
|
35
|
+
## Health and recovery
|
|
36
|
+
|
|
37
|
+
External Discord edits mark a rule Drifted; deletion marks it Missing. Adopt or restore only after review, and never silently recreate a missing rule. Disabling the module disables mapped owned rules while retaining definitions and privacy/recovery records.
|
|
38
|
+
|
|
39
|
+
The module is unhealthy when required records, configuration, installation, Discord Auto Moderation or authorisation services are unavailable. Optional Moderation or Logging failure degrades only that integration. Enhanced worker failure reports degraded effective mode and preserves Standard coverage.
|
|
40
|
+
|
|
41
|
+
## Verify a test installation
|
|
42
|
+
|
|
43
|
+
1. Confirm the private Testing module is present in the test catalogue.
|
|
44
|
+
2. Apply migrations with the migration role and start the bot with Standard.
|
|
45
|
+
3. Publish one fictional native rule and verify block/alert plus a content-free receipt.
|
|
46
|
+
4. Enable Message Content, switch to Enhanced and test one obfuscated phrase plus one safe near-match.
|
|
47
|
+
5. Restart the bot and confirm snapshots rebuild without retained message content.
|
|
48
|
+
6. Exercise worker failure or overflow and confirm effective Standard fallback.
|
|
49
|
+
7. If Moderation is installed, prove duplicate native and Enhanced results create one claim, ordinal, case and action.
|
|
50
|
+
|
|
51
|
+
For the current Hosted customer workflow, see <https://docs.helyx.gg/modules/auto-moderation/>.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Moderation module
|
|
2
|
+
|
|
3
|
+
Moderation is an unpublished Testing module for accountable Discord staff actions. It supports Hosted and Self-Hosted Helyx, but version `1.0.0` must be explicitly included in a reviewed test composition and is not available from the public npm catalogue.
|
|
4
|
+
|
|
5
|
+
## Supported Self-Hosted shape
|
|
6
|
+
|
|
7
|
+
Run the normal Helyx bot process and PostgreSQL only. Moderation needs no Redis, scanner service, Cloud Connect or Helyx-hosted dependency. Install its migrations through the normal reviewed module lifecycle; never run migration credentials in the bot process.
|
|
8
|
+
|
|
9
|
+
## Discord setup
|
|
10
|
+
|
|
11
|
+
Grant only the permissions used by your enabled workflows: **Moderate Members**, **Kick Members**, **Ban Members**, **Manage Roles**, and the channel/thread permissions needed for violation conversations. Helyx does not need Administrator, Message Content or a privileged intent for Moderation.
|
|
12
|
+
|
|
13
|
+
For violation threads, choose a normal text channel and a manageable low-privilege access role. Use the dashboard's **Check channel** action before **Configure channel**. The role grants parent visibility; private-thread membership grants case access. Thread visibility never grants staff action controls.
|
|
14
|
+
|
|
15
|
+
## Commands and authority
|
|
16
|
+
|
|
17
|
+
Moderation contributes `/mod warn`, `/mod timeout`, `/mod timeout-remove`, `/mod kick`, `/mod ban`, `/mod demote`, `/mod unban` and `/mod history`. Each path has a separate Helyx permission and defaults to Administrators. Configure policies through the dashboard or the paired Cloud Connect dashboard; there is no command that bypasses module policy.
|
|
18
|
+
|
|
19
|
+
Kick, ban, demotion, unban and timeouts longer than 24 hours require an actor-bound 60-second confirmation. Every action rechecks current target state, moderator hierarchy, bot hierarchy and Discord permission immediately before dispatch.
|
|
20
|
+
|
|
21
|
+
## Data and recovery
|
|
22
|
+
|
|
23
|
+
The module creates a pending numbered case and immutable attempt before changing Discord. An ambiguous outcome enters Review and must be reconciled from current Discord state; do not blindly repeat the action. Warning DMs and violation-thread delivery are secondary and cannot roll back a successful case.
|
|
24
|
+
|
|
25
|
+
PostgreSQL stores case, action, identity, safe outcome and Discord-reference metadata. It does not store offending message bodies. Transient evidence can be copied directly to a Discord-owned private thread with mentions disabled and is never persisted or queued.
|
|
26
|
+
|
|
27
|
+
Settled retention is configurable from 30 to 730 days. Dashboard disassociation clears the subject and future repeat-count association while preserving an anonymous tombstone. It is not hard deletion or Privacy Service erasure.
|
|
28
|
+
|
|
29
|
+
## Health and shutdown
|
|
30
|
+
|
|
31
|
+
Health is unhealthy while the module is stopped or a required platform boundary is missing. Durable review or delivery recovery can make it degraded without invalidating already applied cases. Disabling stops new commands and automation while retaining cases, privacy visibility and protected recovery state. Existing Discord punishments and threads remain.
|
|
32
|
+
|
|
33
|
+
## Verify a test installation
|
|
34
|
+
|
|
35
|
+
1. Confirm the module is private, Testing and explicitly selected in the test catalogue.
|
|
36
|
+
2. Apply the current database baseline and module migrations with the migration role.
|
|
37
|
+
3. Start the bot and verify module health.
|
|
38
|
+
4. Test a warning, a confirmed high-impact action, a hierarchy rejection and a case history read with fictional accounts.
|
|
39
|
+
5. Exercise channel inspection, one private violation thread, staff-control denial for the affected user and safe Close visibility.
|
|
40
|
+
6. Restart the bot and reconcile one deliberately interrupted test outcome.
|
|
41
|
+
|
|
42
|
+
For the current Hosted customer workflow, see <https://docs.helyx.gg/modules/moderation/>.
|
|
@@ -16,12 +16,23 @@ also replace the example PostgreSQL password consistently in `PGPASSWORD` and
|
|
|
16
16
|
|
|
17
17
|
## Safe defaults
|
|
18
18
|
|
|
19
|
+
The generated Self-Hosted environment fixes `HELYX_RUNTIME_PROFILE` to
|
|
20
|
+
`self-hosted`. Keep that value unchanged: it selects the embedded scanning
|
|
21
|
+
boundary and prevents Hosted-only Helyx Ecko calls from entering the local
|
|
22
|
+
runtime.
|
|
23
|
+
|
|
19
24
|
Privileged Discord intents, privacy execution, hosted OAuth, status reporting,
|
|
20
25
|
guild command writes, and advanced sharding stay disabled until their complete
|
|
21
26
|
requirements are deliberately configured. An empty variable still counts as a
|
|
22
27
|
configured value in many environments, so leave unused optional lines
|
|
23
28
|
commented.
|
|
24
29
|
|
|
30
|
+
`HELYX_AUTOMOD_EMBEDDED_WORKERS` controls only the in-process Self-Hosted
|
|
31
|
+
Enhanced Auto Moderation pool. Keep the default of one unless local measurement
|
|
32
|
+
justifies a value from two through four. Its queue settings may be lowered but
|
|
33
|
+
cannot exceed 5,000 items, 64 MiB total or 100 queued items per server. These
|
|
34
|
+
settings never create or contact a Hosted Ecko service.
|
|
35
|
+
|
|
25
36
|
`HELYX_CLOUD_CONNECT_ENABLED=true` makes an installation ready to be paired; it
|
|
26
37
|
does not pair the installation by itself. Keep official endpoint overrides
|
|
27
38
|
commented unless Helyx support directs otherwise.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helyx/bot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Production Discord runtime for the modular Helyx platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helyx",
|
|
@@ -51,14 +51,14 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@helyx/config": ">=1.0.0 <2.0.0",
|
|
54
|
-
"@helyx/control-protocol": ">=0.
|
|
54
|
+
"@helyx/control-protocol": ">=0.5.0 <0.6.0",
|
|
55
55
|
"@helyx/core": ">=0.1.0 <0.2.0",
|
|
56
56
|
"@helyx/database": ">=2.0.0 <3.0.0",
|
|
57
57
|
"@helyx/framework": ">=0.1.0 <0.2.0",
|
|
58
|
-
"@helyx/module-manifest": ">=0.
|
|
58
|
+
"@helyx/module-manifest": ">=0.10.0 <0.11.0",
|
|
59
59
|
"@helyx/platform": ">=1.0.0 <2.0.0",
|
|
60
|
-
"@helyx/sdk": ">=0.
|
|
61
|
-
"@helyx/status-client": ">=0.
|
|
60
|
+
"@helyx/sdk": ">=0.12.0 <0.13.0",
|
|
61
|
+
"@helyx/status-client": ">=0.2.0 <0.3.0",
|
|
62
62
|
"@helyx/verification-contracts": ">=0.3.0 <0.4.0",
|
|
63
63
|
"discord.js": "14.27.0",
|
|
64
64
|
"fastify": "5.12.3",
|
|
@@ -68,6 +68,18 @@
|
|
|
68
68
|
"ws": "8.21.1",
|
|
69
69
|
"zod": "4.4.3"
|
|
70
70
|
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"@helyx/module-automod": ">=1.0.0 <2.0.0",
|
|
73
|
+
"@helyx/module-moderation": ">=1.0.0 <2.0.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependenciesMeta": {
|
|
76
|
+
"@helyx/module-automod": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"@helyx/module-moderation": {
|
|
80
|
+
"optional": true
|
|
81
|
+
}
|
|
82
|
+
},
|
|
71
83
|
"devDependencies": {
|
|
72
84
|
"@types/ws": "8.18.1"
|
|
73
85
|
}
|