@helyx/bot 1.2.1 → 1.3.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 +14 -0
- package/dist/control-server.js +7 -36
- package/dist/dashboard-discord-settings.d.ts +20 -0
- package/dist/dashboard-discord-settings.js +67 -0
- package/dist/dashboard-feature-control.d.ts +2 -1
- package/dist/dashboard-feature-control.js +3 -0
- package/dist/discord-automod-execution-ids.d.ts +12 -0
- package/dist/discord-automod-execution-ids.js +62 -0
- package/dist/discord-violation-evidence-message.d.ts +12 -0
- package/dist/discord-violation-evidence-message.js +63 -0
- package/dist/discord-violation-layout-message.d.ts +13 -0
- package/dist/discord-violation-layout-message.js +156 -0
- package/dist/discord-violation-notification-users.d.ts +12 -0
- package/dist/discord-violation-notification-users.js +37 -0
- package/dist/discord-violation-thread-close.d.ts +5 -0
- package/dist/discord-violation-thread-close.js +71 -0
- package/dist/discord-violation-thread-service.d.ts +9 -0
- package/dist/discord-violation-thread-service.js +188 -50
- package/dist/events.js +13 -13
- package/dist/management-module.js +36 -2
- package/dist/moderation-action-registry.js +41 -2
- package/dist/privacy-provider-registry.d.ts +1 -1
- package/dist/privacy-provider-registry.js +35 -6
- package/dist/shard-router.js +2 -13
- package/package.json +3 -3
|
@@ -105,6 +105,7 @@ export class BotModerationActionRegistry {
|
|
|
105
105
|
const evidenceClaims = {
|
|
106
106
|
caseIds: new Set(),
|
|
107
107
|
threadIds: new Set(),
|
|
108
|
+
openingMessageIds: new Set(),
|
|
108
109
|
};
|
|
109
110
|
const requestServices = createRequestBoundServices(this.services, evidenceClaims);
|
|
110
111
|
return await this.#actionProvider.execute({
|
|
@@ -117,15 +118,30 @@ export class BotModerationActionRegistry {
|
|
|
117
118
|
!OPERATION_KEY_PATTERN.test(delivery.caseId) ||
|
|
118
119
|
!OPERATION_KEY_PATTERN.test(delivery.operationKey) ||
|
|
119
120
|
!evidenceClaims.caseIds.has(delivery.caseId) ||
|
|
120
|
-
!evidenceClaims.threadIds.has(delivery.threadId)
|
|
121
|
+
!evidenceClaims.threadIds.has(delivery.threadId) ||
|
|
122
|
+
(delivery.openingMessageId !== undefined &&
|
|
123
|
+
(!ID_PATTERN.test(delivery.openingMessageId) ||
|
|
124
|
+
!evidenceClaims.openingMessageIds.has(`${delivery.threadId}:${delivery.openingMessageId}`))))
|
|
121
125
|
return { outcome: "evidence_unavailable" };
|
|
122
126
|
if (!this.evidenceDelivery)
|
|
123
127
|
return { outcome: "evidence_unavailable" };
|
|
124
128
|
return await this.evidenceDelivery.deliverEvidence({
|
|
125
129
|
guildId: delivery.guildId,
|
|
126
130
|
threadId: delivery.threadId,
|
|
131
|
+
...(delivery.openingMessageId
|
|
132
|
+
? { openingMessageId: delivery.openingMessageId }
|
|
133
|
+
: {}),
|
|
127
134
|
evidenceReference: input.request.evidenceReference,
|
|
128
135
|
operationKey: delivery.operationKey,
|
|
136
|
+
context: {
|
|
137
|
+
severity: input.request.severity,
|
|
138
|
+
occurredAt: input.request.occurredAt,
|
|
139
|
+
...(policy.ruleName ? { ruleName: policy.ruleName } : {}),
|
|
140
|
+
confidenceClass: input.request.confidenceClass,
|
|
141
|
+
...(input.request.confidenceScore !== undefined
|
|
142
|
+
? { confidenceScore: input.request.confidenceScore }
|
|
143
|
+
: {}),
|
|
144
|
+
},
|
|
129
145
|
});
|
|
130
146
|
},
|
|
131
147
|
},
|
|
@@ -168,6 +184,11 @@ function isValidRequest(request) {
|
|
|
168
184
|
SAFE_CODE_PATTERN.test(request.reasonCode) &&
|
|
169
185
|
["low", "medium", "high", "critical"].includes(request.severity) &&
|
|
170
186
|
["exact", "native_equivalent", "high", "medium", "low"].includes(request.confidenceClass) &&
|
|
187
|
+
(request.confidenceScore === undefined ||
|
|
188
|
+
(request.detectionSource === "enhanced" &&
|
|
189
|
+
Number.isInteger(request.confidenceScore) &&
|
|
190
|
+
request.confidenceScore >= 0 &&
|
|
191
|
+
request.confidenceScore <= 100)) &&
|
|
171
192
|
(request.sourceMessage === null ||
|
|
172
193
|
(ID_PATTERN.test(request.sourceMessage.channelId) &&
|
|
173
194
|
(request.sourceMessage.messageId === null ||
|
|
@@ -234,9 +255,20 @@ function createRequestBoundServices(services, claims) {
|
|
|
234
255
|
},
|
|
235
256
|
addMember: (input) => service.addMember(input),
|
|
236
257
|
removeMember: (input) => service.removeMember(input),
|
|
237
|
-
send: (input) =>
|
|
258
|
+
send: async (input) => {
|
|
259
|
+
const result = await service.send(input);
|
|
260
|
+
if (claims.threadIds.has(input.threadId) &&
|
|
261
|
+
(result.outcome === "sent" || result.outcome === "already_sent"))
|
|
262
|
+
claims.openingMessageIds.add(`${input.threadId}:${result.messageId}`);
|
|
263
|
+
return result;
|
|
264
|
+
},
|
|
238
265
|
close: (input) => service.close(input),
|
|
239
266
|
reconcile: (input) => service.reconcile(input),
|
|
267
|
+
...(service.editControls
|
|
268
|
+
? {
|
|
269
|
+
editControls: (input) => service.editControls(input),
|
|
270
|
+
}
|
|
271
|
+
: {}),
|
|
240
272
|
};
|
|
241
273
|
return bound;
|
|
242
274
|
}
|
|
@@ -254,6 +286,12 @@ function isValidPolicy(policy) {
|
|
|
254
286
|
policy.windowStartsAt instanceof Date &&
|
|
255
287
|
!Number.isNaN(policy.windowStartsAt.getTime()) &&
|
|
256
288
|
typeof policy.openViolationThread === "boolean" &&
|
|
289
|
+
(policy.deleteSourceMessage === undefined ||
|
|
290
|
+
typeof policy.deleteSourceMessage === "boolean") &&
|
|
291
|
+
(policy.ruleName === undefined ||
|
|
292
|
+
(typeof policy.ruleName === "string" &&
|
|
293
|
+
policy.ruleName.trim().length > 0 &&
|
|
294
|
+
policy.ruleName.length <= 100)) &&
|
|
257
295
|
validRoleIds(policy.globalIgnoredRoleIds, MODERATION_CONTRACT_LIMITS.ignoredRolesPerScope) &&
|
|
258
296
|
validRoleIds(policy.ruleIgnoredRoleIds, MODERATION_CONTRACT_LIMITS.ignoredRolesPerScope) &&
|
|
259
297
|
Boolean(policy.actionPolicy) &&
|
|
@@ -325,6 +363,7 @@ function samePolicy(expected, current) {
|
|
|
325
363
|
expected.configurationRevision === current.configurationRevision &&
|
|
326
364
|
expected.windowStartsAt.getTime() === current.windowStartsAt.getTime() &&
|
|
327
365
|
expected.openViolationThread === current.openViolationThread &&
|
|
366
|
+
expected.deleteSourceMessage === current.deleteSourceMessage &&
|
|
328
367
|
sameStrings(expected.globalIgnoredRoleIds, current.globalIgnoredRoleIds) &&
|
|
329
368
|
sameStrings(expected.ruleIgnoredRoleIds, current.ruleIgnoredRoleIds) &&
|
|
330
369
|
sameAction(expected.actionPolicy.first, current.actionPolicy.first) &&
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ModuleDefinition, type PrivacyProvider, type PrivacyProviderExecutor, type ServiceAccess } from "@helyx/sdk";
|
|
2
2
|
export declare function createPrivacyProviderRegistry(modules: readonly ModuleDefinition[], executor: PrivacyProviderExecutor | undefined, services: ServiceAccess): ReadonlyMap<string, PrivacyProvider>;
|
|
3
3
|
export declare function describePrivacyProviders(providers: ReadonlyMap<string, PrivacyProvider>): readonly {
|
|
4
4
|
id: string;
|
|
@@ -1,12 +1,41 @@
|
|
|
1
|
+
import { createPrivacyProvider, } from "@helyx/sdk";
|
|
1
2
|
export function createPrivacyProviderRegistry(modules, executor, services) {
|
|
2
3
|
if (!executor)
|
|
3
4
|
return new Map();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const configurationDeclaration = {
|
|
6
|
+
containsPersonalData: true,
|
|
7
|
+
providerVersion: 1,
|
|
8
|
+
collections: [
|
|
9
|
+
{
|
|
10
|
+
name: "moderation_notification_memberships",
|
|
11
|
+
createdAtField: "updated_at",
|
|
12
|
+
subjectFields: [
|
|
13
|
+
{
|
|
14
|
+
field: "notification_user_id",
|
|
15
|
+
role: "staff-notification-recipient",
|
|
16
|
+
erasure: "set-null",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
const configurationProvider = createPrivacyProvider({
|
|
23
|
+
id: "helyx.configuration",
|
|
24
|
+
declaration: configurationDeclaration,
|
|
25
|
+
executor,
|
|
26
|
+
});
|
|
27
|
+
if (modules.some(({ manifest }) => manifest.id === configurationProvider.id)) {
|
|
28
|
+
throw new Error("Core configuration privacy identity cannot be a module identity");
|
|
29
|
+
}
|
|
30
|
+
return new Map([
|
|
31
|
+
[configurationProvider.id, configurationProvider],
|
|
32
|
+
...modules.flatMap((module) => {
|
|
33
|
+
if (!module.privacy)
|
|
34
|
+
return [];
|
|
35
|
+
const provider = module.privacy.create(executor, services);
|
|
36
|
+
return [[provider.id, provider]];
|
|
37
|
+
}),
|
|
38
|
+
]);
|
|
10
39
|
}
|
|
11
40
|
export function describePrivacyProviders(providers) {
|
|
12
41
|
return [...providers.values()]
|
package/dist/shard-router.js
CHANGED
|
@@ -8,7 +8,7 @@ import { SHARD_CONTROL_TIMEOUT_MS, controlOperationGuildId, guildIdToShardId, sh
|
|
|
8
8
|
// deadline turned healthy leases into deterministic 503s. Keep the check
|
|
9
9
|
// bounded while allowing normal network variance.
|
|
10
10
|
const LEASE_VALIDATION_TIMEOUT_MS = 3_000;
|
|
11
|
-
const ECKO_RESULT_TIMEOUT_MS = AUTOMOD_SCANNER_LIMITS.
|
|
11
|
+
const ECKO_RESULT_TIMEOUT_MS = AUTOMOD_SCANNER_LIMITS.resultAcknowledgementSeconds * 1_000;
|
|
12
12
|
export class ShardControlRouter {
|
|
13
13
|
input;
|
|
14
14
|
constructor(input) {
|
|
@@ -30,7 +30,7 @@ export class ShardControlRouter {
|
|
|
30
30
|
}
|
|
31
31
|
/** Routes an authenticated Ecko result to the current guild owner. */
|
|
32
32
|
acceptHostedEckoResult(input) {
|
|
33
|
-
return this.#route({ kind: "automod.enhanced.result.accept", ...input },
|
|
33
|
+
return this.#route({ kind: "automod.enhanced.result.accept", ...input }, ECKO_RESULT_TIMEOUT_MS);
|
|
34
34
|
}
|
|
35
35
|
/** Reads the fenced leader's current Enhanced configuration snapshot. */
|
|
36
36
|
getHostedEckoConfigurationPage(input) {
|
|
@@ -336,17 +336,6 @@ async function executeLeaderOperation(input, operation) {
|
|
|
336
336
|
throw new BotControlTransportError(503, "The shard leader is temporarily unavailable");
|
|
337
337
|
return executePrivacy(input.privacy, operation);
|
|
338
338
|
}
|
|
339
|
-
function remainingEnhancedDeadline(result) {
|
|
340
|
-
if (!result || typeof result !== "object")
|
|
341
|
-
return ECKO_RESULT_TIMEOUT_MS;
|
|
342
|
-
const deadlineAt = result["deadlineAt"];
|
|
343
|
-
if (typeof deadlineAt !== "string")
|
|
344
|
-
return ECKO_RESULT_TIMEOUT_MS;
|
|
345
|
-
const parsed = Date.parse(deadlineAt);
|
|
346
|
-
if (!Number.isFinite(parsed))
|
|
347
|
-
return ECKO_RESULT_TIMEOUT_MS;
|
|
348
|
-
return Math.max(1, Math.min(ECKO_RESULT_TIMEOUT_MS, parsed - Date.now()));
|
|
349
|
-
}
|
|
350
339
|
async function executePrivacy(privacy, operation) {
|
|
351
340
|
if (operation.kind === "privacy.providers.list")
|
|
352
341
|
return (await privacy.listPrivacyProviders());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helyx/bot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Production Discord runtime for the modular Helyx platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helyx",
|
|
@@ -55,9 +55,9 @@
|
|
|
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.11.0 <0.12.0",
|
|
59
59
|
"@helyx/platform": ">=1.0.0 <2.0.0",
|
|
60
|
-
"@helyx/sdk": ">=0.
|
|
60
|
+
"@helyx/sdk": ">=0.13.0 <0.14.0",
|
|
61
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",
|