@helyx/module-moderation 1.0.1
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 +41 -0
- package/LICENSE +725 -0
- package/README.md +107 -0
- package/dist/action-support.d.ts +48 -0
- package/dist/action-support.js +201 -0
- package/dist/case-actions.d.ts +9 -0
- package/dist/case-actions.js +311 -0
- package/dist/case-naming.d.ts +4 -0
- package/dist/case-naming.js +9 -0
- package/dist/case-repository.d.ts +29 -0
- package/dist/case-repository.js +199 -0
- package/dist/channel-actions.d.ts +3 -0
- package/dist/channel-actions.js +267 -0
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +309 -0
- package/dist/components.d.ts +15 -0
- package/dist/components.js +381 -0
- package/dist/configuration.d.ts +16 -0
- package/dist/configuration.js +94 -0
- package/dist/constants.d.ts +53 -0
- package/dist/constants.js +53 -0
- package/dist/contracts.d.ts +109 -0
- package/dist/contracts.js +84 -0
- package/dist/domain.d.ts +29 -0
- package/dist/domain.js +101 -0
- package/dist/events.d.ts +3 -0
- package/dist/events.js +60 -0
- package/dist/health.d.ts +10 -0
- package/dist/health.js +56 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +92 -0
- package/dist/moderation-cases-resource.d.ts +14 -0
- package/dist/moderation-cases-resource.js +226 -0
- package/dist/presentation.d.ts +8 -0
- package/dist/presentation.js +69 -0
- package/dist/privacy.d.ts +8 -0
- package/dist/privacy.js +38 -0
- package/dist/provider.d.ts +4 -0
- package/dist/provider.js +335 -0
- package/dist/receipt-repository.d.ts +9 -0
- package/dist/receipt-repository.js +36 -0
- package/dist/records.d.ts +399 -0
- package/dist/records.js +303 -0
- package/dist/repository-model.d.ts +131 -0
- package/dist/repository-model.js +318 -0
- package/dist/repository.d.ts +21 -0
- package/dist/repository.js +41 -0
- package/dist/service.d.ts +75 -0
- package/dist/service.js +329 -0
- package/dist/tasks.d.ts +49 -0
- package/dist/tasks.js +391 -0
- package/dist/thread-controls.d.ts +23 -0
- package/dist/thread-controls.js +376 -0
- package/dist/thread-deletion-recovery.d.ts +13 -0
- package/dist/thread-deletion-recovery.js +46 -0
- package/dist/thread-delivery.d.ts +11 -0
- package/dist/thread-delivery.js +427 -0
- package/dist/thread-reconciliation.d.ts +24 -0
- package/dist/thread-reconciliation.js +181 -0
- package/dist/thread-repository.d.ts +16 -0
- package/dist/thread-repository.js +70 -0
- package/manifest.json +999 -0
- package/migrations/0001_moderation_foundation.sql +552 -0
- package/migrations/0002_staff_attempt_parameters.sql +27 -0
- package/package.json +56 -0
package/dist/service.js
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { HELYX_SERVICE_NAMES, } from "@helyx/sdk";
|
|
2
|
+
import { MODERATION_MODULE_ID } from "./constants.js";
|
|
3
|
+
import { appendModerationAudit, atomicCases as cases, moderationApplied as isApplied, moderationAuditReason as auditReason, moderationConfiguration as getConfiguration, moderationEnabled as moduleEnabled, emitModerationActionLog, moderationEnforcement as memberEnforcement, moderationRetentionExpiresAt as retentionExpiresAt, scheduleActionReconciliation, sendWarningDirectMessage, staffAction, } from "./action-support.js";
|
|
4
|
+
export class ModerationService {
|
|
5
|
+
#threads = null;
|
|
6
|
+
attachThreadRuntime(runtime) {
|
|
7
|
+
this.#threads = runtime;
|
|
8
|
+
}
|
|
9
|
+
canExecuteStaffAction(services, draft) {
|
|
10
|
+
return this.#revalidateStaffAction(services, draft);
|
|
11
|
+
}
|
|
12
|
+
async executeStaffAction(services, draft) {
|
|
13
|
+
if (!(await this.#revalidateStaffAction(services, draft)))
|
|
14
|
+
return { outcome: "rejected", safeCode: "authorization_changed" };
|
|
15
|
+
const configuration = await getConfiguration(services, draft.guildId);
|
|
16
|
+
const occurredAt = new Date();
|
|
17
|
+
const pending = await cases(services).createPendingCase({
|
|
18
|
+
operationKey: draft.operationKey,
|
|
19
|
+
guildId: draft.guildId,
|
|
20
|
+
subjectUserId: draft.subjectUserId,
|
|
21
|
+
actorUserId: draft.actor.userId,
|
|
22
|
+
source: "staff_command",
|
|
23
|
+
sourceModuleId: MODERATION_MODULE_ID,
|
|
24
|
+
action: draft.action,
|
|
25
|
+
reason: draft.reason,
|
|
26
|
+
occurredAt,
|
|
27
|
+
...(draft.privateNote ? { privateNote: draft.privateNote } : {}),
|
|
28
|
+
...(draft.evidenceMessage
|
|
29
|
+
? { evidenceMessage: draft.evidenceMessage }
|
|
30
|
+
: {}),
|
|
31
|
+
...(draft.requestedDurationSeconds
|
|
32
|
+
? { requestedDurationSeconds: draft.requestedDurationSeconds }
|
|
33
|
+
: {}),
|
|
34
|
+
...(draft.deleteMessageSeconds !== undefined
|
|
35
|
+
? { deleteMessageSeconds: draft.deleteMessageSeconds }
|
|
36
|
+
: {}),
|
|
37
|
+
...(draft.demoteRoleIds ? { demoteRoleIds: draft.demoteRoleIds } : {}),
|
|
38
|
+
});
|
|
39
|
+
if (pending.outcome === "rejected")
|
|
40
|
+
return { outcome: "rejected", safeCode: pending.code };
|
|
41
|
+
if (pending.outcome === "replayed") {
|
|
42
|
+
if (pending.attemptOutcome === "pending" ||
|
|
43
|
+
pending.attemptOutcome === "ambiguous")
|
|
44
|
+
await scheduleActionReconciliation(services, {
|
|
45
|
+
guildId: draft.guildId,
|
|
46
|
+
caseId: pending.caseId,
|
|
47
|
+
attemptId: pending.attemptId,
|
|
48
|
+
operationKey: draft.operationKey,
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
outcome: pending.attemptOutcome === "succeeded" ||
|
|
52
|
+
pending.attemptOutcome === "already_applied"
|
|
53
|
+
? "applied"
|
|
54
|
+
: pending.attemptOutcome === "pending" ||
|
|
55
|
+
pending.attemptOutcome === "ambiguous" ||
|
|
56
|
+
pending.attemptOutcome === "review_required"
|
|
57
|
+
? "review_required"
|
|
58
|
+
: "failed",
|
|
59
|
+
caseId: pending.caseId,
|
|
60
|
+
caseNumber: pending.caseNumber,
|
|
61
|
+
revision: pending.revision,
|
|
62
|
+
safeCode: `replayed_${pending.attemptOutcome}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
await appendModerationAudit(services, {
|
|
67
|
+
guildId: draft.guildId,
|
|
68
|
+
actorUserId: draft.actor.userId,
|
|
69
|
+
action: "moderation.action-attempt",
|
|
70
|
+
source: "discord",
|
|
71
|
+
correlationId: draft.operationKey,
|
|
72
|
+
idempotencyKey: `${draft.operationKey}:audit`,
|
|
73
|
+
targetType: "moderation_case",
|
|
74
|
+
targetId: pending.caseId,
|
|
75
|
+
metadata: { action: draft.action, caseNumber: pending.caseNumber },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
const settledAt = new Date();
|
|
80
|
+
await cases(services).settleActionAttempt({
|
|
81
|
+
operationKey: `${draft.operationKey}:audit-failed`,
|
|
82
|
+
guildId: draft.guildId,
|
|
83
|
+
caseId: pending.caseId,
|
|
84
|
+
attemptId: pending.attemptId,
|
|
85
|
+
expectedRevision: pending.revision,
|
|
86
|
+
outcome: "failed",
|
|
87
|
+
safeCode: "audit_unavailable",
|
|
88
|
+
retentionExpiresAt: retentionExpiresAt(settledAt, configuration.caseRetentionDays),
|
|
89
|
+
occurredAt: settledAt,
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
outcome: "failed",
|
|
93
|
+
caseId: pending.caseId,
|
|
94
|
+
caseNumber: pending.caseNumber,
|
|
95
|
+
safeCode: "audit_unavailable",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const effect = await this.dispatchAction(services, {
|
|
99
|
+
guildId: draft.guildId,
|
|
100
|
+
subjectUserId: draft.subjectUserId,
|
|
101
|
+
actorUserId: draft.actor.userId,
|
|
102
|
+
action: staffAction(draft),
|
|
103
|
+
reason: auditReason(pending.caseNumber, draft.reason),
|
|
104
|
+
operationKey: `${draft.operationKey}:discord`,
|
|
105
|
+
protectedRoleIds: configuration.violationAccessRoleId
|
|
106
|
+
? [configuration.violationAccessRoleId]
|
|
107
|
+
: [],
|
|
108
|
+
revalidate: () => this.#revalidateStaffAction(services, draft),
|
|
109
|
+
}).catch(() => ({
|
|
110
|
+
outcome: "ambiguous",
|
|
111
|
+
safeCode: "enforcement_unavailable",
|
|
112
|
+
}));
|
|
113
|
+
const settledAt = new Date();
|
|
114
|
+
const settlement = await cases(services)
|
|
115
|
+
.settleActionAttempt({
|
|
116
|
+
operationKey: `${draft.operationKey}:settle`,
|
|
117
|
+
guildId: draft.guildId,
|
|
118
|
+
caseId: pending.caseId,
|
|
119
|
+
attemptId: pending.attemptId,
|
|
120
|
+
expectedRevision: pending.revision,
|
|
121
|
+
outcome: effect.outcome,
|
|
122
|
+
...(effect.safeCode ? { safeCode: effect.safeCode } : {}),
|
|
123
|
+
...("removedRoleIds" in effect && effect.removedRoleIds
|
|
124
|
+
? { removedRoleIds: effect.removedRoleIds }
|
|
125
|
+
: {}),
|
|
126
|
+
retentionExpiresAt: retentionExpiresAt(settledAt, configuration.caseRetentionDays),
|
|
127
|
+
occurredAt: settledAt,
|
|
128
|
+
})
|
|
129
|
+
.catch(() => null);
|
|
130
|
+
if (!settlement) {
|
|
131
|
+
await scheduleActionReconciliation(services, {
|
|
132
|
+
guildId: draft.guildId,
|
|
133
|
+
caseId: pending.caseId,
|
|
134
|
+
attemptId: pending.attemptId,
|
|
135
|
+
operationKey: draft.operationKey,
|
|
136
|
+
}).catch(() => undefined);
|
|
137
|
+
return {
|
|
138
|
+
outcome: "review_required",
|
|
139
|
+
caseId: pending.caseId,
|
|
140
|
+
caseNumber: pending.caseNumber,
|
|
141
|
+
safeCode: "settlement_unavailable",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (settlement.outcome === "rejected")
|
|
145
|
+
return {
|
|
146
|
+
outcome: "review_required",
|
|
147
|
+
caseId: pending.caseId,
|
|
148
|
+
caseNumber: pending.caseNumber,
|
|
149
|
+
safeCode: `settlement_${settlement.code}`,
|
|
150
|
+
};
|
|
151
|
+
if (effect.outcome === "ambiguous")
|
|
152
|
+
await scheduleActionReconciliation(services, {
|
|
153
|
+
guildId: draft.guildId,
|
|
154
|
+
caseId: pending.caseId,
|
|
155
|
+
attemptId: pending.attemptId,
|
|
156
|
+
operationKey: draft.operationKey,
|
|
157
|
+
});
|
|
158
|
+
if (draft.action === "warn" && isApplied(effect.outcome))
|
|
159
|
+
await sendWarningDirectMessage(services, {
|
|
160
|
+
guildId: draft.guildId,
|
|
161
|
+
caseId: pending.caseId,
|
|
162
|
+
caseNumber: pending.caseNumber,
|
|
163
|
+
subjectUserId: draft.subjectUserId,
|
|
164
|
+
}).catch(() => undefined);
|
|
165
|
+
if (isApplied(effect.outcome)) {
|
|
166
|
+
await emitModerationActionLog(services, {
|
|
167
|
+
guildId: draft.guildId,
|
|
168
|
+
action: draft.action,
|
|
169
|
+
caseId: pending.caseId,
|
|
170
|
+
caseNumber: pending.caseNumber,
|
|
171
|
+
subjectUserId: draft.subjectUserId,
|
|
172
|
+
actorUserId: draft.actor.userId,
|
|
173
|
+
...(draft.requestedDurationSeconds
|
|
174
|
+
? { durationSeconds: draft.requestedDurationSeconds }
|
|
175
|
+
: {}),
|
|
176
|
+
operationKey: draft.operationKey,
|
|
177
|
+
});
|
|
178
|
+
await this.deliverViolationThread({
|
|
179
|
+
services,
|
|
180
|
+
guildId: draft.guildId,
|
|
181
|
+
caseId: pending.caseId,
|
|
182
|
+
caseNumber: pending.caseNumber,
|
|
183
|
+
caseRevision: settlement.revision,
|
|
184
|
+
subjectUserId: draft.subjectUserId,
|
|
185
|
+
severity: "medium",
|
|
186
|
+
operationKey: draft.operationKey,
|
|
187
|
+
}).catch(() => "review_required");
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
outcome: isApplied(effect.outcome)
|
|
191
|
+
? "applied"
|
|
192
|
+
: effect.outcome === "ambiguous" || effect.outcome === "review_required"
|
|
193
|
+
? "review_required"
|
|
194
|
+
: "failed",
|
|
195
|
+
caseId: pending.caseId,
|
|
196
|
+
caseNumber: pending.caseNumber,
|
|
197
|
+
revision: settlement.revision,
|
|
198
|
+
safeCode: effect.safeCode ?? effect.outcome,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async dispatchAction(services, input) {
|
|
202
|
+
const enforcement = memberEnforcement(services);
|
|
203
|
+
const common = {
|
|
204
|
+
guildId: input.guildId,
|
|
205
|
+
memberUserId: input.subjectUserId,
|
|
206
|
+
...(input.actorUserId ? { actorUserId: input.actorUserId } : {}),
|
|
207
|
+
moduleId: MODERATION_MODULE_ID,
|
|
208
|
+
reason: input.reason,
|
|
209
|
+
operationKey: input.operationKey,
|
|
210
|
+
revalidate: () => input.revalidate(),
|
|
211
|
+
};
|
|
212
|
+
if (input.action.type === "case_only" || input.action.type === "warn")
|
|
213
|
+
return { outcome: "succeeded" };
|
|
214
|
+
if (input.action.type === "timeout")
|
|
215
|
+
return enforcement.timeout({
|
|
216
|
+
...common,
|
|
217
|
+
durationSeconds: input.action.durationSeconds,
|
|
218
|
+
});
|
|
219
|
+
if (input.action.type === "timeout_remove")
|
|
220
|
+
return enforcement.removeTimeout(common);
|
|
221
|
+
if (input.action.type === "kick")
|
|
222
|
+
return enforcement.kickMember(common);
|
|
223
|
+
if (input.action.type === "ban")
|
|
224
|
+
return enforcement.banMember({
|
|
225
|
+
...common,
|
|
226
|
+
deleteMessageSeconds: input.action.deleteMessageSeconds,
|
|
227
|
+
});
|
|
228
|
+
if (input.action.type === "unban")
|
|
229
|
+
return enforcement.unban({
|
|
230
|
+
guildId: input.guildId,
|
|
231
|
+
userId: input.subjectUserId,
|
|
232
|
+
...(input.actorUserId ? { actorUserId: input.actorUserId } : {}),
|
|
233
|
+
moduleId: MODERATION_MODULE_ID,
|
|
234
|
+
reason: input.reason,
|
|
235
|
+
operationKey: input.operationKey,
|
|
236
|
+
revalidate: () => input.revalidate(),
|
|
237
|
+
});
|
|
238
|
+
if (input.action.type === "demote") {
|
|
239
|
+
const result = await enforcement.removeConfiguredRoles({
|
|
240
|
+
...common,
|
|
241
|
+
roleIds: input.action.roleIds,
|
|
242
|
+
protectedRoleIds: [...new Set(input.protectedRoleIds)],
|
|
243
|
+
});
|
|
244
|
+
return "removedRoleIds" in result
|
|
245
|
+
? { outcome: result.outcome, removedRoleIds: result.removedRoleIds }
|
|
246
|
+
: result;
|
|
247
|
+
}
|
|
248
|
+
if (input.action.type === "delete_message") {
|
|
249
|
+
const source = input.sourceMessage;
|
|
250
|
+
if (!source?.messageId)
|
|
251
|
+
return { outcome: "review_required", safeCode: "source_missing" };
|
|
252
|
+
const result = await services
|
|
253
|
+
.get(HELYX_SERVICE_NAMES.messageEnforcement)
|
|
254
|
+
.deleteMemberMessage({
|
|
255
|
+
guildId: input.guildId,
|
|
256
|
+
channelId: source.channelId,
|
|
257
|
+
messageId: source.messageId,
|
|
258
|
+
expectedAuthorUserId: input.subjectUserId,
|
|
259
|
+
callerModuleId: MODERATION_MODULE_ID,
|
|
260
|
+
reason: input.reason,
|
|
261
|
+
operationKey: input.operationKey,
|
|
262
|
+
revalidate: () => input.revalidate(),
|
|
263
|
+
});
|
|
264
|
+
return {
|
|
265
|
+
outcome: result.outcome === "deleted"
|
|
266
|
+
? "succeeded"
|
|
267
|
+
: result.outcome === "already_absent"
|
|
268
|
+
? "already_applied"
|
|
269
|
+
: result.outcome === "ambiguous"
|
|
270
|
+
? "ambiguous"
|
|
271
|
+
: "failed",
|
|
272
|
+
...(result.safeCode ? { safeCode: result.safeCode } : {}),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return { outcome: "review_required", safeCode: "unsupported_action" };
|
|
276
|
+
}
|
|
277
|
+
async #revalidateStaffAction(services, draft) {
|
|
278
|
+
if (!(await moduleEnabled(services, draft.guildId)))
|
|
279
|
+
return false;
|
|
280
|
+
const enforcement = memberEnforcement(services);
|
|
281
|
+
const actor = await enforcement.inspectMember({
|
|
282
|
+
guildId: draft.guildId,
|
|
283
|
+
memberUserId: draft.actor.userId,
|
|
284
|
+
});
|
|
285
|
+
if (actor.outcome !== "present")
|
|
286
|
+
return false;
|
|
287
|
+
const authorised = await services
|
|
288
|
+
.get(HELYX_SERVICE_NAMES.resourceAuthorization)
|
|
289
|
+
.canUse({
|
|
290
|
+
guildId: draft.guildId,
|
|
291
|
+
moduleId: MODERATION_MODULE_ID,
|
|
292
|
+
permissionId: draft.permissionId,
|
|
293
|
+
actor: {
|
|
294
|
+
userId: draft.actor.userId,
|
|
295
|
+
roleIds: actor.roleIds,
|
|
296
|
+
permissions: actor.effectivePermissionNames,
|
|
297
|
+
isServerOwner: actor.isGuildOwner,
|
|
298
|
+
isAdministrator: actor.isAdministrator,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
if (!authorised || draft.subjectUserId === draft.actor.userId)
|
|
302
|
+
return false;
|
|
303
|
+
if (draft.action === "unban")
|
|
304
|
+
return true;
|
|
305
|
+
const subject = await enforcement.inspectMember({
|
|
306
|
+
guildId: draft.guildId,
|
|
307
|
+
memberUserId: draft.subjectUserId,
|
|
308
|
+
actorUserId: draft.actor.userId,
|
|
309
|
+
});
|
|
310
|
+
if (draft.action === "ban" && subject.outcome === "member_missing")
|
|
311
|
+
return true;
|
|
312
|
+
if (subject.outcome !== "present")
|
|
313
|
+
return false;
|
|
314
|
+
if (subject.isGuildOwner ||
|
|
315
|
+
subject.helyxHierarchy !== "above" ||
|
|
316
|
+
subject.actorHierarchy !== "above")
|
|
317
|
+
return false;
|
|
318
|
+
if (draft.action === "timeout" &&
|
|
319
|
+
(subject.isAdministrator || subject.isBot))
|
|
320
|
+
return false;
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
deliverViolationThread(input) {
|
|
324
|
+
return this.#threads
|
|
325
|
+
? this.#threads.deliver(input)
|
|
326
|
+
: Promise.resolve("review_required");
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=service.js.map
|
package/dist/tasks.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ModuleScheduledTaskContribution, ScheduledTaskExecutionResult, ServiceAccess } from "@helyx/sdk";
|
|
2
|
+
export interface ModerationTaskRuntime {
|
|
3
|
+
deliverPendingThread(services: ServiceAccess, input: {
|
|
4
|
+
guildId: string;
|
|
5
|
+
caseId: string;
|
|
6
|
+
}): Promise<ScheduledTaskExecutionResult>;
|
|
7
|
+
reconcileAction(services: ServiceAccess, input: {
|
|
8
|
+
guildId: string;
|
|
9
|
+
caseId: string;
|
|
10
|
+
attemptId: string;
|
|
11
|
+
}): Promise<ScheduledTaskExecutionResult>;
|
|
12
|
+
reconcileThreadAccess(services: ServiceAccess, input: {
|
|
13
|
+
guildId: string;
|
|
14
|
+
}): Promise<ScheduledTaskExecutionResult>;
|
|
15
|
+
}
|
|
16
|
+
export interface ModerationTaskHealth {
|
|
17
|
+
recordTaskSuccess(taskKind: string): void;
|
|
18
|
+
recordTaskFailure(taskKind: string, safeCode: string): void;
|
|
19
|
+
}
|
|
20
|
+
export declare function createModerationTasks(runtime: ModerationTaskRuntime, health?: ModerationTaskHealth): readonly ModuleScheduledTaskContribution[];
|
|
21
|
+
export declare function validateThreadDeliveryPayload(input: unknown): {
|
|
22
|
+
caseId: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function validateActionReconcilePayload(input: unknown): {
|
|
25
|
+
caseId: string;
|
|
26
|
+
attemptId: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function validateEmptyPayload(input: unknown): {};
|
|
29
|
+
export declare function reconcileModerationAction(services: ServiceAccess, input: {
|
|
30
|
+
guildId: string;
|
|
31
|
+
caseId: string;
|
|
32
|
+
attemptId: string;
|
|
33
|
+
actorUserId?: string | null;
|
|
34
|
+
operationKey?: string;
|
|
35
|
+
}): Promise<ScheduledTaskExecutionResult>;
|
|
36
|
+
export declare function scheduleModerationThreadRetry(services: ServiceAccess, input: {
|
|
37
|
+
guildId: string;
|
|
38
|
+
caseId: string;
|
|
39
|
+
operationKey: string;
|
|
40
|
+
}): Promise<void>;
|
|
41
|
+
export declare function reconcileDashboardCase(input: {
|
|
42
|
+
guildId: string;
|
|
43
|
+
caseId: string;
|
|
44
|
+
expectedRevision: number;
|
|
45
|
+
actorUserId: string;
|
|
46
|
+
operationKey: string;
|
|
47
|
+
services: ServiceAccess;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
//# sourceMappingURL=tasks.d.ts.map
|