@helyx/module-reactionroles 1.0.15 → 1.0.18
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 +20 -0
- package/README.md +37 -53
- package/dist/builder-interactions.d.ts +11 -0
- package/dist/builder-interactions.js +293 -0
- package/dist/builder-presentation.d.ts +30 -0
- package/dist/builder-presentation.js +343 -0
- package/dist/builder.d.ts +3 -0
- package/dist/builder.js +22 -16
- package/dist/cleanup-repository.d.ts +24 -0
- package/dist/cleanup-repository.js +118 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +4 -1
- package/dist/health.d.ts +16 -0
- package/dist/health.js +85 -0
- package/dist/index.js +66 -1147
- package/dist/logging.d.ts +33 -0
- package/dist/logging.js +40 -0
- package/dist/managed-resource.d.ts +2 -0
- package/dist/managed-resource.js +72 -0
- package/dist/member-interactions.d.ts +27 -0
- package/dist/member-interactions.js +360 -0
- package/dist/module-lifecycle.d.ts +3 -0
- package/dist/module-lifecycle.js +48 -0
- package/dist/panel-cache.d.ts +30 -0
- package/dist/panel-cache.js +197 -0
- package/dist/panel-create.d.ts +10 -0
- package/dist/panel-create.js +80 -0
- package/dist/panel-lifecycle.d.ts +47 -0
- package/dist/panel-lifecycle.js +272 -0
- package/dist/panel-managed.d.ts +14 -0
- package/dist/panel-managed.js +27 -0
- package/dist/panel-service.d.ts +13 -36
- package/dist/panel-service.js +48 -722
- package/dist/panel-support.d.ts +35 -0
- package/dist/panel-support.js +291 -0
- package/dist/permissions.d.ts +2 -0
- package/dist/permissions.js +8 -0
- package/dist/privacy.d.ts +5 -0
- package/dist/privacy.js +12 -0
- package/dist/record-mapping.d.ts +28 -0
- package/dist/record-mapping.js +161 -0
- package/dist/record-schema.d.ts +203 -0
- package/dist/record-schema.js +137 -0
- package/dist/records.d.ts +9 -201
- package/dist/records.js +32 -383
- package/dist/runtime-state.d.ts +9 -0
- package/dist/runtime-state.js +30 -0
- package/manifest.json +1 -1
- package/package.json +3 -3
package/dist/records.js
CHANGED
|
@@ -1,151 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
fields: {
|
|
7
|
-
panel_id: { type: "string" },
|
|
8
|
-
guild_id: { type: "string" },
|
|
9
|
-
channel_id: { type: "string" },
|
|
10
|
-
message_id: { type: "string", optional: true },
|
|
11
|
-
title: { type: "string" },
|
|
12
|
-
message_body: { type: "string" },
|
|
13
|
-
message_style: { type: "string" },
|
|
14
|
-
control_type: { type: "string" },
|
|
15
|
-
selection_mode: { type: "string" },
|
|
16
|
-
persistent_reactions: { type: "boolean" },
|
|
17
|
-
embed_colour: { type: "number", optional: true },
|
|
18
|
-
dropdown_placeholder: { type: "string", optional: true },
|
|
19
|
-
status: { type: "string" },
|
|
20
|
-
revision: { type: "number" },
|
|
21
|
-
created_by_user_id: { type: "string", optional: true },
|
|
22
|
-
updated_by_user_id: { type: "string", optional: true },
|
|
23
|
-
created_at: { type: "date" },
|
|
24
|
-
updated_at: { type: "date" },
|
|
25
|
-
},
|
|
26
|
-
uniqueKeys: [["panel_id"], ["guild_id", "message_id"]],
|
|
27
|
-
readableFields: [
|
|
28
|
-
"panel_id",
|
|
29
|
-
"guild_id",
|
|
30
|
-
"channel_id",
|
|
31
|
-
"message_id",
|
|
32
|
-
"title",
|
|
33
|
-
"message_body",
|
|
34
|
-
"message_style",
|
|
35
|
-
"control_type",
|
|
36
|
-
"selection_mode",
|
|
37
|
-
"persistent_reactions",
|
|
38
|
-
"embed_colour",
|
|
39
|
-
"dropdown_placeholder",
|
|
40
|
-
"status",
|
|
41
|
-
"revision",
|
|
42
|
-
"created_by_user_id",
|
|
43
|
-
"updated_by_user_id",
|
|
44
|
-
"created_at",
|
|
45
|
-
"updated_at",
|
|
46
|
-
],
|
|
47
|
-
writableFields: [
|
|
48
|
-
"channel_id",
|
|
49
|
-
"message_id",
|
|
50
|
-
"title",
|
|
51
|
-
"message_body",
|
|
52
|
-
"message_style",
|
|
53
|
-
"control_type",
|
|
54
|
-
"selection_mode",
|
|
55
|
-
"persistent_reactions",
|
|
56
|
-
"embed_colour",
|
|
57
|
-
"dropdown_placeholder",
|
|
58
|
-
"status",
|
|
59
|
-
"revision",
|
|
60
|
-
"updated_by_user_id",
|
|
61
|
-
"updated_at",
|
|
62
|
-
],
|
|
63
|
-
orderKeys: [["updated_at", "panel_id"]],
|
|
64
|
-
groupableFields: ["status"],
|
|
65
|
-
},
|
|
66
|
-
entries: {
|
|
67
|
-
table: "reactionroles_entries",
|
|
68
|
-
fields: {
|
|
69
|
-
entry_id: { type: "string" },
|
|
70
|
-
panel_id: { type: "string" },
|
|
71
|
-
position: { type: "number" },
|
|
72
|
-
role_id: { type: "string" },
|
|
73
|
-
nameplate: { type: "string" },
|
|
74
|
-
emoji_kind: { type: "string", optional: true },
|
|
75
|
-
emoji_name: { type: "string", optional: true },
|
|
76
|
-
emoji_id: { type: "string", optional: true },
|
|
77
|
-
emoji_animated: { type: "boolean", optional: true },
|
|
78
|
-
button_style: { type: "string", optional: true },
|
|
79
|
-
created_at: { type: "date" },
|
|
80
|
-
updated_at: { type: "date" },
|
|
81
|
-
},
|
|
82
|
-
uniqueKeys: [
|
|
83
|
-
["entry_id"],
|
|
84
|
-
["panel_id", "position"],
|
|
85
|
-
["panel_id", "role_id"],
|
|
86
|
-
],
|
|
87
|
-
readableFields: [
|
|
88
|
-
"entry_id",
|
|
89
|
-
"panel_id",
|
|
90
|
-
"position",
|
|
91
|
-
"role_id",
|
|
92
|
-
"nameplate",
|
|
93
|
-
"emoji_kind",
|
|
94
|
-
"emoji_name",
|
|
95
|
-
"emoji_id",
|
|
96
|
-
"emoji_animated",
|
|
97
|
-
"button_style",
|
|
98
|
-
"created_at",
|
|
99
|
-
"updated_at",
|
|
100
|
-
],
|
|
101
|
-
orderKeys: [["position", "entry_id"]],
|
|
102
|
-
},
|
|
103
|
-
message_cleanup: {
|
|
104
|
-
table: "reactionroles_message_cleanup",
|
|
105
|
-
fields: {
|
|
106
|
-
cleanup_id: { type: "string" },
|
|
107
|
-
guild_id: { type: "string" },
|
|
108
|
-
panel_id: { type: "string" },
|
|
109
|
-
channel_id: { type: "string" },
|
|
110
|
-
message_id: { type: "string" },
|
|
111
|
-
reason: { type: "string" },
|
|
112
|
-
attempts: { type: "number" },
|
|
113
|
-
next_attempt_at: { type: "date" },
|
|
114
|
-
created_at: { type: "date" },
|
|
115
|
-
completed_at: { type: "date", optional: true },
|
|
116
|
-
},
|
|
117
|
-
uniqueKeys: [
|
|
118
|
-
["cleanup_id"],
|
|
119
|
-
["guild_id", "channel_id", "message_id", "reason"],
|
|
120
|
-
],
|
|
121
|
-
readableFields: [
|
|
122
|
-
"cleanup_id",
|
|
123
|
-
"guild_id",
|
|
124
|
-
"panel_id",
|
|
125
|
-
"channel_id",
|
|
126
|
-
"message_id",
|
|
127
|
-
"reason",
|
|
128
|
-
"attempts",
|
|
129
|
-
"next_attempt_at",
|
|
130
|
-
"created_at",
|
|
131
|
-
"completed_at",
|
|
132
|
-
],
|
|
133
|
-
writableFields: ["attempts", "next_attempt_at", "completed_at"],
|
|
134
|
-
orderKeys: [["next_attempt_at", "cleanup_id"]],
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
const panelSelect = reactionRoleRecords.panels.readableFields;
|
|
138
|
-
const entrySelect = reactionRoleRecords.entries.readableFields;
|
|
139
|
-
const cleanupSelect = reactionRoleRecords.message_cleanup.readableFields;
|
|
1
|
+
import { REACTION_ROLES_MODULE_ID } from "./constants.js";
|
|
2
|
+
import { MessageCleanupRepository } from "./cleanup-repository.js";
|
|
3
|
+
import { entrySelect, panelSelect } from "./record-schema.js";
|
|
4
|
+
import { entryValues, mapPanel, panelValues, requiredDate, requiredEnum, requiredString, } from "./record-mapping.js";
|
|
5
|
+
export { reactionRoleRecords, } from "./record-schema.js";
|
|
140
6
|
export class ReactionRolePanelRepository {
|
|
141
7
|
records;
|
|
8
|
+
#cleanup;
|
|
142
9
|
constructor(records) {
|
|
143
10
|
this.records = records;
|
|
11
|
+
this.#cleanup = new MessageCleanupRepository(records);
|
|
144
12
|
}
|
|
145
13
|
async create(input) {
|
|
146
14
|
const now = input.now ?? new Date();
|
|
147
15
|
const result = await this.records.replaceOwnedSet({
|
|
148
|
-
moduleId:
|
|
16
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
149
17
|
collection: "entries",
|
|
150
18
|
owner: { panel_id: input.panelId },
|
|
151
19
|
values: input.entries.map((entry, position) => entryValues(input.panelId, entry, position, now)),
|
|
@@ -171,7 +39,7 @@ export class ReactionRolePanelRepository {
|
|
|
171
39
|
const now = input.now ?? new Date();
|
|
172
40
|
const revision = input.panel.revision + 1;
|
|
173
41
|
const result = await this.records.replaceOwnedSet({
|
|
174
|
-
moduleId:
|
|
42
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
175
43
|
collection: "entries",
|
|
176
44
|
owner: { panel_id: input.panel.panelId },
|
|
177
45
|
values: input.entries.map((entry, position) => entryValues(input.panel.panelId, entry, position, now)),
|
|
@@ -208,7 +76,7 @@ export class ReactionRolePanelRepository {
|
|
|
208
76
|
}
|
|
209
77
|
async findById(guildId, panelId) {
|
|
210
78
|
const panel = await this.records.findOne({
|
|
211
|
-
moduleId:
|
|
79
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
212
80
|
collection: "panels",
|
|
213
81
|
select: panelSelect,
|
|
214
82
|
where: { panel_id: panelId },
|
|
@@ -219,7 +87,7 @@ export class ReactionRolePanelRepository {
|
|
|
219
87
|
}
|
|
220
88
|
async findByMessage(guildId, messageId) {
|
|
221
89
|
const panel = await this.records.findOne({
|
|
222
|
-
moduleId:
|
|
90
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
223
91
|
collection: "panels",
|
|
224
92
|
select: panelSelect,
|
|
225
93
|
where: { guild_id: guildId, message_id: messageId },
|
|
@@ -228,7 +96,7 @@ export class ReactionRolePanelRepository {
|
|
|
228
96
|
}
|
|
229
97
|
async list(input) {
|
|
230
98
|
const page = await this.records.findMany({
|
|
231
|
-
moduleId:
|
|
99
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
232
100
|
collection: "panels",
|
|
233
101
|
select: panelSelect,
|
|
234
102
|
where: { guild_id: input.guildId },
|
|
@@ -246,7 +114,7 @@ export class ReactionRolePanelRepository {
|
|
|
246
114
|
}
|
|
247
115
|
async listReferences(input) {
|
|
248
116
|
const page = await this.records.findMany({
|
|
249
|
-
moduleId:
|
|
117
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
250
118
|
collection: "panels",
|
|
251
119
|
select: ["panel_id", "title", "status", "updated_at"],
|
|
252
120
|
where: { guild_id: input.guildId },
|
|
@@ -277,7 +145,7 @@ export class ReactionRolePanelRepository {
|
|
|
277
145
|
}
|
|
278
146
|
async listByChannel(guildId, channelId, limit = 100) {
|
|
279
147
|
const page = await this.records.findMany({
|
|
280
|
-
moduleId:
|
|
148
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
281
149
|
collection: "panels",
|
|
282
150
|
select: panelSelect,
|
|
283
151
|
where: { guild_id: guildId, channel_id: channelId },
|
|
@@ -297,7 +165,7 @@ export class ReactionRolePanelRepository {
|
|
|
297
165
|
}
|
|
298
166
|
async listInteractive(input) {
|
|
299
167
|
const page = await this.records.findMany({
|
|
300
|
-
moduleId:
|
|
168
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
301
169
|
collection: "panels",
|
|
302
170
|
select: panelSelect,
|
|
303
171
|
where: { status: input.status },
|
|
@@ -315,7 +183,7 @@ export class ReactionRolePanelRepository {
|
|
|
315
183
|
}
|
|
316
184
|
async markStatus(input) {
|
|
317
185
|
return (await this.records.updateOne({
|
|
318
|
-
moduleId:
|
|
186
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
319
187
|
collection: "panels",
|
|
320
188
|
where: { panel_id: input.panelId },
|
|
321
189
|
expected: { field: "revision", value: input.expectedRevision },
|
|
@@ -345,94 +213,36 @@ export class ReactionRolePanelRepository {
|
|
|
345
213
|
}
|
|
346
214
|
async deleteClaimed(panelId) {
|
|
347
215
|
return ((await this.records.deleteMany({
|
|
348
|
-
moduleId:
|
|
216
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
349
217
|
collection: "panels",
|
|
350
218
|
where: { panel_id: panelId, status: "deleting" },
|
|
351
219
|
limit: 1,
|
|
352
220
|
})).deleted === 1);
|
|
353
221
|
}
|
|
354
222
|
async recordCleanup(input) {
|
|
355
|
-
|
|
356
|
-
moduleId: MODULE_ID,
|
|
357
|
-
collection: "message_cleanup",
|
|
358
|
-
values: {
|
|
359
|
-
cleanup_id: randomUUID(),
|
|
360
|
-
guild_id: input.guildId,
|
|
361
|
-
panel_id: input.panelId,
|
|
362
|
-
channel_id: input.channelId,
|
|
363
|
-
message_id: input.messageId,
|
|
364
|
-
reason: input.reason,
|
|
365
|
-
attempts: 0,
|
|
366
|
-
next_attempt_at: input.now ?? new Date(),
|
|
367
|
-
created_at: input.now ?? new Date(),
|
|
368
|
-
},
|
|
369
|
-
uniqueBy: ["guild_id", "channel_id", "message_id", "reason"],
|
|
370
|
-
});
|
|
371
|
-
return created.created;
|
|
223
|
+
return this.#cleanup.record(input);
|
|
372
224
|
}
|
|
373
225
|
async listPendingCleanup(limit = 100) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
{ field: "cleanup_id", direction: "asc" },
|
|
382
|
-
],
|
|
383
|
-
limit: Math.min(100, Math.max(1, limit)),
|
|
384
|
-
});
|
|
385
|
-
return page.records.map((row) => ({
|
|
386
|
-
cleanupId: requiredString(row, "cleanup_id"),
|
|
387
|
-
guildId: requiredString(row, "guild_id"),
|
|
388
|
-
panelId: requiredString(row, "panel_id"),
|
|
389
|
-
channelId: requiredString(row, "channel_id"),
|
|
390
|
-
messageId: requiredString(row, "message_id"),
|
|
391
|
-
reason: requiredEnum(row, "reason", [
|
|
392
|
-
"replacement",
|
|
393
|
-
"rollback",
|
|
394
|
-
"delete",
|
|
395
|
-
]),
|
|
396
|
-
attempts: requiredNumber(row, "attempts"),
|
|
397
|
-
nextAttemptAt: requiredDate(row, "next_attempt_at"),
|
|
398
|
-
}));
|
|
226
|
+
return this.#cleanup.list(limit);
|
|
227
|
+
}
|
|
228
|
+
listPendingCleanupPage(limit = 100, cursor) {
|
|
229
|
+
return this.#cleanup.listPage(limit, cursor);
|
|
230
|
+
}
|
|
231
|
+
findPendingCleanup(cleanupId) {
|
|
232
|
+
return this.#cleanup.find(cleanupId);
|
|
399
233
|
}
|
|
400
234
|
async claimCleanup(cleanup, now = new Date()) {
|
|
401
|
-
|
|
402
|
-
cleanup.nextAttemptAt.getTime() > now.getTime())
|
|
403
|
-
return null;
|
|
404
|
-
const attempts = cleanup.attempts + 1;
|
|
405
|
-
const claimed = await this.records.updateOne({
|
|
406
|
-
moduleId: MODULE_ID,
|
|
407
|
-
collection: "message_cleanup",
|
|
408
|
-
where: { cleanup_id: cleanup.cleanupId },
|
|
409
|
-
expected: { field: "attempts", value: cleanup.attempts },
|
|
410
|
-
values: {
|
|
411
|
-
attempts,
|
|
412
|
-
next_attempt_at: new Date(now.getTime() + Math.min(3_600_000, 30_000 * 2 ** cleanup.attempts)),
|
|
413
|
-
},
|
|
414
|
-
});
|
|
415
|
-
return claimed.updated ? { ...cleanup, attempts } : null;
|
|
235
|
+
return this.#cleanup.claim(cleanup, now);
|
|
416
236
|
}
|
|
417
237
|
async completeCleanup(cleanup, now = new Date()) {
|
|
418
|
-
return
|
|
419
|
-
moduleId: MODULE_ID,
|
|
420
|
-
collection: "message_cleanup",
|
|
421
|
-
where: { cleanup_id: cleanup.cleanupId },
|
|
422
|
-
expected: { field: "attempts", value: cleanup.attempts },
|
|
423
|
-
values: { completed_at: now },
|
|
424
|
-
})).updated;
|
|
238
|
+
return this.#cleanup.complete(cleanup, now);
|
|
425
239
|
}
|
|
426
240
|
pendingCleanupCount(panelId) {
|
|
427
|
-
return this.
|
|
428
|
-
moduleId: MODULE_ID,
|
|
429
|
-
collection: "message_cleanup",
|
|
430
|
-
where: { panel_id: panelId, completed_at: null },
|
|
431
|
-
});
|
|
241
|
+
return this.#cleanup.count(panelId);
|
|
432
242
|
}
|
|
433
243
|
async #withEntries(panel) {
|
|
434
244
|
const entries = await this.records.findMany({
|
|
435
|
-
moduleId:
|
|
245
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
436
246
|
collection: "entries",
|
|
437
247
|
select: entrySelect,
|
|
438
248
|
where: { panel_id: requiredString(panel, "panel_id") },
|
|
@@ -449,7 +259,7 @@ export class ReactionRolePanelRepository {
|
|
|
449
259
|
return [];
|
|
450
260
|
const panelIds = panels.map((panel) => requiredString(panel, "panel_id"));
|
|
451
261
|
const entries = await this.records.findManyByValues({
|
|
452
|
-
moduleId:
|
|
262
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
453
263
|
collection: "entries",
|
|
454
264
|
select: entrySelect,
|
|
455
265
|
where: {},
|
|
@@ -472,7 +282,7 @@ export class ReactionRolePanelRepository {
|
|
|
472
282
|
}
|
|
473
283
|
async #listByEntryField(guildId, field, value, limit) {
|
|
474
284
|
const entries = await this.records.findMany({
|
|
475
|
-
moduleId:
|
|
285
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
476
286
|
collection: "entries",
|
|
477
287
|
select: ["panel_id"],
|
|
478
288
|
where: { [field]: value },
|
|
@@ -487,7 +297,7 @@ export class ReactionRolePanelRepository {
|
|
|
487
297
|
if (uniquePanelIds.length === 0)
|
|
488
298
|
return [];
|
|
489
299
|
const panels = await this.records.findManyByValues({
|
|
490
|
-
moduleId:
|
|
300
|
+
moduleId: REACTION_ROLES_MODULE_ID,
|
|
491
301
|
collection: "panels",
|
|
492
302
|
select: panelSelect,
|
|
493
303
|
where: { guild_id: guildId },
|
|
@@ -505,164 +315,3 @@ export class ReactionRolePanelRepository {
|
|
|
505
315
|
});
|
|
506
316
|
}
|
|
507
317
|
}
|
|
508
|
-
function panelValues(input) {
|
|
509
|
-
return {
|
|
510
|
-
panel_id: input.panelId,
|
|
511
|
-
guild_id: input.guildId,
|
|
512
|
-
channel_id: input.draft.channelId,
|
|
513
|
-
...(input.messageId ? { message_id: input.messageId } : {}),
|
|
514
|
-
title: input.draft.title,
|
|
515
|
-
message_body: input.draft.messageBody,
|
|
516
|
-
message_style: input.draft.messageStyle,
|
|
517
|
-
control_type: input.draft.controlType,
|
|
518
|
-
selection_mode: input.draft.selectionMode,
|
|
519
|
-
persistent_reactions: input.draft.persistentReactions,
|
|
520
|
-
...(input.draft.embedColour === undefined
|
|
521
|
-
? {}
|
|
522
|
-
: { embed_colour: input.draft.embedColour }),
|
|
523
|
-
...(input.draft.dropdownPlaceholder === undefined
|
|
524
|
-
? {}
|
|
525
|
-
: { dropdown_placeholder: input.draft.dropdownPlaceholder }),
|
|
526
|
-
status: input.status,
|
|
527
|
-
revision: input.revision,
|
|
528
|
-
created_by_user_id: input.actorUserId,
|
|
529
|
-
updated_by_user_id: input.actorUserId,
|
|
530
|
-
created_at: input.now,
|
|
531
|
-
updated_at: input.now,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
function entryValues(panelId, entry, position, now) {
|
|
535
|
-
return {
|
|
536
|
-
entry_id: entry.entryId,
|
|
537
|
-
panel_id: panelId,
|
|
538
|
-
position,
|
|
539
|
-
role_id: entry.roleId,
|
|
540
|
-
nameplate: entry.nameplate,
|
|
541
|
-
...(entry.emoji
|
|
542
|
-
? {
|
|
543
|
-
emoji_kind: entry.emoji.kind,
|
|
544
|
-
emoji_name: entry.emoji.name,
|
|
545
|
-
...(entry.emoji.id ? { emoji_id: entry.emoji.id } : {}),
|
|
546
|
-
emoji_animated: entry.emoji.animated,
|
|
547
|
-
}
|
|
548
|
-
: {}),
|
|
549
|
-
...(entry.buttonStyle ? { button_style: entry.buttonStyle } : {}),
|
|
550
|
-
created_at: now,
|
|
551
|
-
updated_at: now,
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
function mapPanel(row, entries) {
|
|
555
|
-
return {
|
|
556
|
-
panelId: requiredString(row, "panel_id"),
|
|
557
|
-
guildId: requiredString(row, "guild_id"),
|
|
558
|
-
channelId: requiredString(row, "channel_id"),
|
|
559
|
-
messageId: nullableString(row, "message_id"),
|
|
560
|
-
title: requiredString(row, "title"),
|
|
561
|
-
messageBody: requiredString(row, "message_body"),
|
|
562
|
-
messageStyle: requiredEnum(row, "message_style", ["text", "embed"]),
|
|
563
|
-
controlType: requiredEnum(row, "control_type", [
|
|
564
|
-
"buttons",
|
|
565
|
-
"dropdown",
|
|
566
|
-
"reactions",
|
|
567
|
-
]),
|
|
568
|
-
selectionMode: requiredEnum(row, "selection_mode", [
|
|
569
|
-
"multiple",
|
|
570
|
-
"one_only",
|
|
571
|
-
]),
|
|
572
|
-
persistentReactions: optionalBoolean(row, "persistent_reactions", true),
|
|
573
|
-
...(row.embed_colour === null
|
|
574
|
-
? {}
|
|
575
|
-
: { embedColour: requiredNumber(row, "embed_colour") }),
|
|
576
|
-
...(row.dropdown_placeholder === null
|
|
577
|
-
? {}
|
|
578
|
-
: {
|
|
579
|
-
dropdownPlaceholder: requiredString(row, "dropdown_placeholder"),
|
|
580
|
-
}),
|
|
581
|
-
entries: entries.map((entry) => {
|
|
582
|
-
const emojiKind = nullableString(entry, "emoji_kind");
|
|
583
|
-
const emojiName = nullableString(entry, "emoji_name");
|
|
584
|
-
const emojiId = nullableString(entry, "emoji_id");
|
|
585
|
-
const emoji = emojiKind && emojiName
|
|
586
|
-
? {
|
|
587
|
-
kind: requiredLiteral(emojiKind, ["unicode", "custom"]),
|
|
588
|
-
name: emojiName,
|
|
589
|
-
...(emojiId ? { id: emojiId } : {}),
|
|
590
|
-
animated: entry.emoji_animated === true,
|
|
591
|
-
identifier: emojiId ? `${emojiName}:${emojiId}` : emojiName,
|
|
592
|
-
key: emojiId ? `custom:${emojiId}` : `unicode:${emojiName}`,
|
|
593
|
-
}
|
|
594
|
-
: null;
|
|
595
|
-
return {
|
|
596
|
-
entryId: requiredString(entry, "entry_id"),
|
|
597
|
-
roleId: requiredString(entry, "role_id"),
|
|
598
|
-
nameplate: requiredString(entry, "nameplate"),
|
|
599
|
-
...(emoji ? { emoji: emoji.identifier } : {}),
|
|
600
|
-
...(entry.button_style
|
|
601
|
-
? {
|
|
602
|
-
buttonStyle: requiredEnum(entry, "button_style", [
|
|
603
|
-
"primary",
|
|
604
|
-
"secondary",
|
|
605
|
-
"success",
|
|
606
|
-
"danger",
|
|
607
|
-
]),
|
|
608
|
-
}
|
|
609
|
-
: {}),
|
|
610
|
-
};
|
|
611
|
-
}),
|
|
612
|
-
status: requiredEnum(row, "status", [
|
|
613
|
-
"pending",
|
|
614
|
-
"active",
|
|
615
|
-
"disabled",
|
|
616
|
-
"message_missing",
|
|
617
|
-
"needs_attention",
|
|
618
|
-
"cleanup_pending",
|
|
619
|
-
"deleting",
|
|
620
|
-
]),
|
|
621
|
-
revision: requiredNumber(row, "revision"),
|
|
622
|
-
createdByUserId: nullableString(row, "created_by_user_id"),
|
|
623
|
-
updatedByUserId: nullableString(row, "updated_by_user_id"),
|
|
624
|
-
createdAt: requiredDate(row, "created_at"),
|
|
625
|
-
updatedAt: requiredDate(row, "updated_at"),
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
function requiredString(row, field) {
|
|
629
|
-
const value = row[field];
|
|
630
|
-
if (typeof value !== "string" || value.length === 0)
|
|
631
|
-
throw new Error(`Invalid Reaction Roles record field: ${field}`);
|
|
632
|
-
return value;
|
|
633
|
-
}
|
|
634
|
-
function nullableString(row, field) {
|
|
635
|
-
const value = row[field];
|
|
636
|
-
if (value === null || value === undefined)
|
|
637
|
-
return null;
|
|
638
|
-
return requiredString(row, field);
|
|
639
|
-
}
|
|
640
|
-
function requiredNumber(row, field) {
|
|
641
|
-
const value = Number(row[field]);
|
|
642
|
-
if (!Number.isSafeInteger(value))
|
|
643
|
-
throw new Error(`Invalid Reaction Roles record field: ${field}`);
|
|
644
|
-
return value;
|
|
645
|
-
}
|
|
646
|
-
function optionalBoolean(row, field, fallback) {
|
|
647
|
-
const value = row[field];
|
|
648
|
-
if (value === null || value === undefined)
|
|
649
|
-
return fallback;
|
|
650
|
-
if (typeof value !== "boolean")
|
|
651
|
-
throw new Error(`Invalid Reaction Roles record field: ${field}`);
|
|
652
|
-
return value;
|
|
653
|
-
}
|
|
654
|
-
function requiredDate(row, field) {
|
|
655
|
-
const value = row[field];
|
|
656
|
-
const date = value instanceof Date ? value : new Date(String(value));
|
|
657
|
-
if (Number.isNaN(date.getTime()))
|
|
658
|
-
throw new Error(`Invalid Reaction Roles record field: ${field}`);
|
|
659
|
-
return date;
|
|
660
|
-
}
|
|
661
|
-
function requiredEnum(row, field, values) {
|
|
662
|
-
return requiredLiteral(requiredString(row, field), values);
|
|
663
|
-
}
|
|
664
|
-
function requiredLiteral(value, values) {
|
|
665
|
-
if (!values.includes(value))
|
|
666
|
-
throw new Error("Invalid Reaction Roles record enum");
|
|
667
|
-
return value;
|
|
668
|
-
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReactionRolesHealthTracker } from "./health.js";
|
|
2
|
+
import type { ReactionRolesHealthSource } from "./health.js";
|
|
3
|
+
import { PanelSnapshotCache, ReactionRolePanelService } from "./panel-service.js";
|
|
4
|
+
export declare const reactionRoleCache: PanelSnapshotCache;
|
|
5
|
+
export declare const reactionRolePanels: ReactionRolePanelService;
|
|
6
|
+
export declare const reactionRoleMemberLocks: Map<string, Promise<void>>;
|
|
7
|
+
export declare const reactionRoleHealth: ReactionRolesHealthTracker;
|
|
8
|
+
export declare function runReactionRolePrimary<T>(operation: () => Promise<T>, source?: ReactionRolesHealthSource): Promise<T>;
|
|
9
|
+
export declare function runReactionRoleFailureOnly<T>(operation: () => Promise<T>, source: ReactionRolesHealthSource): Promise<T>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DashboardActionValidationError } from "@helyx/sdk";
|
|
2
|
+
import { ReactionRolesHealthTracker } from "./health.js";
|
|
3
|
+
import { PanelSnapshotCache, ReactionRolePanelService, } from "./panel-service.js";
|
|
4
|
+
// Guild-worker ownership serializes dashboard and Discord mutations in one
|
|
5
|
+
// runtime; the short TTL remains the correctness fence after worker turnover.
|
|
6
|
+
export const reactionRoleCache = new PanelSnapshotCache();
|
|
7
|
+
export const reactionRolePanels = new ReactionRolePanelService(reactionRoleCache);
|
|
8
|
+
export const reactionRoleMemberLocks = new Map();
|
|
9
|
+
export const reactionRoleHealth = new ReactionRolesHealthTracker();
|
|
10
|
+
export async function runReactionRolePrimary(operation, source = "resource") {
|
|
11
|
+
try {
|
|
12
|
+
const result = await operation();
|
|
13
|
+
reactionRoleHealth.primarySuccess(source);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (!(error instanceof DashboardActionValidationError))
|
|
18
|
+
reactionRoleHealth.primaryFailure(source);
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function runReactionRoleFailureOnly(operation, source) {
|
|
23
|
+
try {
|
|
24
|
+
return await operation();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
reactionRoleHealth.primaryFailure(source);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/manifest.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"healthExposure": "direct",
|
|
11
11
|
"id": "helyx.reactionroles",
|
|
12
12
|
"name": "Reaction Roles",
|
|
13
|
-
"version": "1.0.
|
|
13
|
+
"version": "1.0.18",
|
|
14
14
|
"description": "Let members select approved roles themselves.",
|
|
15
15
|
"entry": "./dist/index.js",
|
|
16
16
|
"framework": "^0.1.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helyx/module-reactionroles",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Configurable self-service Discord role panels for Helyx.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helyx",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"build": "tsc -b"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@helyx/module-manifest": ">=0.
|
|
53
|
-
"@helyx/sdk": ">=0.
|
|
52
|
+
"@helyx/module-manifest": ">=0.9.0 <0.10.0",
|
|
53
|
+
"@helyx/sdk": ">=0.11.0 <0.12.0"
|
|
54
54
|
}
|
|
55
55
|
}
|