@helyx/bot 0.1.3 → 0.2.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.
@@ -0,0 +1,250 @@
1
+ import { assertCloudResponseSize, parseOperationInput, } from "@helyx/control-protocol";
2
+ import { CloudConnectorRepository } from "@helyx/database";
3
+ export class CloudControlDispatcher {
4
+ database;
5
+ control;
6
+ constructor(database, control) {
7
+ this.database = database;
8
+ this.control = control;
9
+ }
10
+ async execute(request, correlationId) {
11
+ const actorUserId = request.actor.kind === "customer"
12
+ ? request.actor.accountUserId
13
+ : undefined;
14
+ let result;
15
+ switch (request.operation) {
16
+ case "installation.health.read.v1":
17
+ parseOperationInput(request.operation, request.input);
18
+ result = {
19
+ status: "online",
20
+ observedAt: new Date().toISOString(),
21
+ };
22
+ break;
23
+ case "guild.state.read.v1": {
24
+ const input = parseOperationInput(request.operation, request.input);
25
+ result = await this.control.getGuildState(input.guildId);
26
+ break;
27
+ }
28
+ case "guild.module.status.write.v1": {
29
+ const input = parseOperationInput(request.operation, request.input);
30
+ result = await this.control.setModuleStatus({
31
+ ...input,
32
+ actorUserId: requiredActor(actorUserId),
33
+ correlationId,
34
+ });
35
+ break;
36
+ }
37
+ case "guild.module.configuration.write.v1": {
38
+ const input = parseOperationInput(request.operation, request.input);
39
+ result = await this.control.updateConfiguration({
40
+ ...input,
41
+ actorUserId: requiredActor(actorUserId),
42
+ correlationId,
43
+ });
44
+ break;
45
+ }
46
+ case "guild.module.permissions.write.v1": {
47
+ const input = parseOperationInput(request.operation, request.input);
48
+ result = await this.control.updatePermissions({
49
+ ...input,
50
+ actorUserId: requiredActor(actorUserId),
51
+ correlationId,
52
+ });
53
+ break;
54
+ }
55
+ case "guild.module.logging.write.v1": {
56
+ const input = parseOperationInput(request.operation, request.input);
57
+ result = await this.control.updateLoggingPolicy({
58
+ ...input,
59
+ actorUserId: requiredActor(actorUserId),
60
+ correlationId,
61
+ });
62
+ break;
63
+ }
64
+ case "guild.module.action.execute.v1": {
65
+ const input = parseOperationInput(request.operation, request.input);
66
+ result = await this.control.executeDashboardAction({
67
+ ...input,
68
+ actorUserId: requiredActor(actorUserId),
69
+ correlationId,
70
+ });
71
+ break;
72
+ }
73
+ case "support.guild.snapshot.read.v1": {
74
+ const input = parseOperationInput(request.operation, request.input);
75
+ if (request.actor.kind !== "helyx_staff")
76
+ throw new CloudControlDeniedError("staff_actor_required");
77
+ result = await this.#supportSnapshot({
78
+ ...input,
79
+ actorReference: request.actor.assignmentId,
80
+ purpose: request.actor.purpose,
81
+ correlationId,
82
+ });
83
+ break;
84
+ }
85
+ }
86
+ assertCloudResponseSize(result);
87
+ return result;
88
+ }
89
+ async #supportSnapshot(input) {
90
+ const repository = new CloudConnectorRepository(this.database);
91
+ const consent = await repository.getConsent();
92
+ const requested = input.dataClasses.filter((dataClass) => consent.supportDataClasses.includes(dataClass));
93
+ if (!consent.supportInspectionEnabled || requested.length === 0) {
94
+ await repository.appendAudit({
95
+ action: "support.snapshot.read",
96
+ source: "cloud",
97
+ outcome: "denied",
98
+ actorReference: input.actorReference,
99
+ guildId: input.guildId,
100
+ bindingId: input.bindingId,
101
+ purpose: input.purpose,
102
+ dataClasses: input.dataClasses,
103
+ safeReasonCode: "support_inspection_not_consented",
104
+ correlationId: input.correlationId,
105
+ });
106
+ throw new CloudControlDeniedError("support_inspection_not_consented");
107
+ }
108
+ try {
109
+ const state = await this.control.getGuildState(input.guildId);
110
+ const result = buildSupportSnapshot(state, requested, input.guildId);
111
+ await repository.appendAudit({
112
+ action: "support.snapshot.read",
113
+ source: "cloud",
114
+ outcome: "succeeded",
115
+ actorReference: input.actorReference,
116
+ guildId: input.guildId,
117
+ bindingId: input.bindingId,
118
+ purpose: input.purpose,
119
+ dataClasses: requested,
120
+ correlationId: input.correlationId,
121
+ });
122
+ return result;
123
+ }
124
+ catch (error) {
125
+ await repository.appendAudit({
126
+ action: "support.snapshot.read",
127
+ source: "cloud",
128
+ outcome: "failed",
129
+ actorReference: input.actorReference,
130
+ guildId: input.guildId,
131
+ bindingId: input.bindingId,
132
+ purpose: input.purpose,
133
+ dataClasses: requested,
134
+ safeReasonCode: "snapshot_failed",
135
+ correlationId: input.correlationId,
136
+ });
137
+ throw error;
138
+ }
139
+ }
140
+ }
141
+ export class CloudControlDeniedError extends Error {
142
+ safeReasonCode;
143
+ constructor(safeReasonCode) {
144
+ super("Cloud Connect request was denied by the local policy");
145
+ this.safeReasonCode = safeReasonCode;
146
+ this.name = "CloudControlDeniedError";
147
+ }
148
+ }
149
+ export function buildSupportSnapshot(state, dataClasses, guildId) {
150
+ const include = new Set(dataClasses);
151
+ const includeModules = [
152
+ "module_inventory",
153
+ "configuration",
154
+ "permissions",
155
+ "logging",
156
+ ].some((dataClass) => include.has(dataClass));
157
+ return {
158
+ guildId,
159
+ observedAt: new Date().toISOString(),
160
+ dataClasses,
161
+ ...(includeModules
162
+ ? {
163
+ modules: state.modules.map((module) => {
164
+ const configuration = state.configurations[module.id];
165
+ const fields = module.settings.map((setting) => {
166
+ const visibility = "supportVisibility" in Object(setting)
167
+ ? (setting.supportVisibility ?? "hidden")
168
+ : "hidden";
169
+ if (!include.has("configuration") || visibility === "hidden")
170
+ return {
171
+ key: settingKey(setting),
172
+ state: "omitted",
173
+ };
174
+ const key = settingKey(setting);
175
+ const value = configuration?.value[key];
176
+ if (visibility === "set_only")
177
+ return {
178
+ key,
179
+ state: "redacted",
180
+ isSet: value !== undefined && value !== "" && value !== null,
181
+ };
182
+ return value === undefined
183
+ ? { key, state: "unavailable" }
184
+ : { key, state: "value", value };
185
+ });
186
+ const permission = state.permissions[module.id];
187
+ return {
188
+ id: module.id,
189
+ version: module.version,
190
+ enabled: state.moduleStatuses[module.id] === "enabled",
191
+ ...(include.has("configuration")
192
+ ? {
193
+ configuration: {
194
+ version: configuration?.version ?? null,
195
+ fields,
196
+ },
197
+ }
198
+ : {}),
199
+ ...(include.has("permissions") && permission
200
+ ? {
201
+ permissions: {
202
+ modulePreset: permission.module.preset,
203
+ allowedRoleCount: permission.module.allowedRoleIds.length,
204
+ deniedRoleCount: permission.module.deniedRoleIds.length,
205
+ commands: permission.commands.map((command) => ({
206
+ commandId: command.commandId,
207
+ inherited: !command.enabled,
208
+ preset: command.policy.preset,
209
+ allowedRoleCount: command.policy.allowedRoleIds.length,
210
+ deniedRoleCount: command.policy.deniedRoleIds.length,
211
+ })),
212
+ },
213
+ }
214
+ : {}),
215
+ ...(include.has("logging")
216
+ ? {
217
+ logging: (state.logging[module.id] ?? []).map((event) => ({
218
+ eventId: event.eventId,
219
+ enabled: event.enabled,
220
+ version: event.version,
221
+ })),
222
+ }
223
+ : {}),
224
+ };
225
+ }),
226
+ }
227
+ : {}),
228
+ ...(include.has("health")
229
+ ? {
230
+ health: {
231
+ state: "available",
232
+ moduleCount: state.modules.length,
233
+ },
234
+ }
235
+ : {}),
236
+ };
237
+ }
238
+ function settingKey(setting) {
239
+ if (typeof setting !== "object" ||
240
+ setting === null ||
241
+ typeof setting.key !== "string")
242
+ return "unknown";
243
+ return setting.key;
244
+ }
245
+ function requiredActor(value) {
246
+ if (!value)
247
+ throw new CloudControlDeniedError("customer_actor_required");
248
+ return value;
249
+ }
250
+ //# sourceMappingURL=cloud-control-dispatcher.js.map
@@ -0,0 +1,9 @@
1
+ import { type ModuleDefinition } from "@helyx/sdk";
2
+ import type { Client } from "discord.js";
3
+ import type { CloudConnectorController } from "./cloud-connector.js";
4
+ export declare function createCloudManagementModule(input: {
5
+ connector: CloudConnectorController;
6
+ client: Client;
7
+ configuredOwnerUserIds: readonly string[];
8
+ }): ModuleDefinition;
9
+ //# sourceMappingURL=cloud-management-module.d.ts.map