@helyx/bot 0.4.30 → 0.4.32
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 +18 -0
- package/dist/control-permissions.d.ts +38 -0
- package/dist/control-permissions.js +139 -0
- package/dist/control-server.d.ts +1 -15
- package/dist/control-server.js +112 -166
- package/dist/discord-guild-health.d.ts +174 -0
- package/dist/discord-guild-health.js +370 -0
- package/dist/events.d.ts +15 -0
- package/dist/events.js +338 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +26 -7
- package/dist/interactions.js +36 -6
- package/dist/modules.d.ts +2 -0
- package/dist/modules.js +10 -1
- package/dist/oauth-module.d.ts +2 -0
- package/dist/oauth-module.js +192 -0
- package/dist/oauth-module.test.d.ts +2 -0
- package/dist/oauth-module.test.js +172 -0
- package/dist/oauth-service.d.ts +16 -0
- package/dist/oauth-service.js +40 -0
- package/dist/shard-supervisor.js +6 -0
- package/dist/shard-worker.d.ts +5 -0
- package/dist/shard-worker.js +16 -1
- package/package.json +70 -70
package/dist/events.js
CHANGED
|
@@ -29,10 +29,26 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
29
29
|
moduleId: module.manifest.id,
|
|
30
30
|
contribution,
|
|
31
31
|
})));
|
|
32
|
+
const reactionRemoveAllContributions = modules.flatMap((module) => (module.events?.messageReactionRemoveAll ?? []).map((contribution) => ({
|
|
33
|
+
moduleId: module.manifest.id,
|
|
34
|
+
contribution,
|
|
35
|
+
})));
|
|
36
|
+
const reactionRemoveEmojiContributions = modules.flatMap((module) => (module.events?.messageReactionRemoveEmoji ?? []).map((contribution) => ({
|
|
37
|
+
moduleId: module.manifest.id,
|
|
38
|
+
contribution,
|
|
39
|
+
})));
|
|
40
|
+
const messageUpdateContributions = modules.flatMap((module) => (module.events?.guildMessageUpdate ?? []).map((contribution) => ({
|
|
41
|
+
moduleId: module.manifest.id,
|
|
42
|
+
contribution,
|
|
43
|
+
})));
|
|
32
44
|
const messageDeleteContributions = modules.flatMap((module) => (module.events?.messageDelete ?? []).map((contribution) => ({
|
|
33
45
|
moduleId: module.manifest.id,
|
|
34
46
|
contribution,
|
|
35
47
|
})));
|
|
48
|
+
const messageDeleteBulkContributions = modules.flatMap((module) => (module.events?.guildMessageDeleteBulk ?? []).map((contribution) => ({
|
|
49
|
+
moduleId: module.manifest.id,
|
|
50
|
+
contribution,
|
|
51
|
+
})));
|
|
36
52
|
const memberAddContributions = modules.flatMap((module) => (module.events?.guildMemberAdd ?? []).map((contribution) => ({
|
|
37
53
|
moduleId: module.manifest.id,
|
|
38
54
|
contribution,
|
|
@@ -70,7 +86,11 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
70
86
|
guildVoiceSessionContributions.length === 0 &&
|
|
71
87
|
reactionContributions.length === 0 &&
|
|
72
88
|
reactionRemoveContributions.length === 0 &&
|
|
89
|
+
reactionRemoveAllContributions.length === 0 &&
|
|
90
|
+
reactionRemoveEmojiContributions.length === 0 &&
|
|
91
|
+
messageUpdateContributions.length === 0 &&
|
|
73
92
|
messageDeleteContributions.length === 0 &&
|
|
93
|
+
messageDeleteBulkContributions.length === 0 &&
|
|
74
94
|
memberAddContributions.length === 0 &&
|
|
75
95
|
memberRemoveContributions.length === 0 &&
|
|
76
96
|
guildRoleDeleteContributions.length === 0 &&
|
|
@@ -101,6 +121,11 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
101
121
|
(await item.contribution.isEligible({
|
|
102
122
|
serverId: message.guildId,
|
|
103
123
|
channelId: message.channelId,
|
|
124
|
+
userId: message.author.id,
|
|
125
|
+
userIsBot: message.author.bot,
|
|
126
|
+
userIsSystem: message.author.system || message.system,
|
|
127
|
+
webhookId: message.webhookId,
|
|
128
|
+
content: message.content,
|
|
104
129
|
services,
|
|
105
130
|
}))) {
|
|
106
131
|
eligible.push(item);
|
|
@@ -156,12 +181,40 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
156
181
|
queue.enqueue(guildId, () => handleReactionRemove(reaction, user, details.burst));
|
|
157
182
|
});
|
|
158
183
|
}
|
|
184
|
+
if (reactionRemoveAllContributions.length > 0) {
|
|
185
|
+
client.on(Events.MessageReactionRemoveAll, (message) => {
|
|
186
|
+
if (message.guildId)
|
|
187
|
+
queue.enqueue(message.guildId, () => handleReactionRemoveAll(message));
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (reactionRemoveEmojiContributions.length > 0) {
|
|
191
|
+
client.on(Events.MessageReactionRemoveEmoji, (reaction) => {
|
|
192
|
+
const guildId = reaction.message.guildId;
|
|
193
|
+
if (guildId)
|
|
194
|
+
queue.enqueue(guildId, () => handleReactionRemoveEmoji(reaction));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (messageUpdateContributions.length > 0) {
|
|
198
|
+
client.on(Events.MessageUpdate, (_before, message) => {
|
|
199
|
+
if (message.guildId)
|
|
200
|
+
queue.enqueue(message.guildId, () => handleMessageUpdate(message));
|
|
201
|
+
});
|
|
202
|
+
}
|
|
159
203
|
if (messageDeleteContributions.length > 0) {
|
|
160
204
|
client.on(Events.MessageDelete, (message) => {
|
|
161
205
|
if (message.guildId)
|
|
162
206
|
queue.enqueue(message.guildId, () => handleMessageDelete(message));
|
|
163
207
|
});
|
|
164
208
|
}
|
|
209
|
+
if (messageDeleteBulkContributions.length > 0) {
|
|
210
|
+
client.on(Events.MessageBulkDelete, (messages, channel) => {
|
|
211
|
+
if (channel.isDMBased())
|
|
212
|
+
return;
|
|
213
|
+
queue.enqueue(channel.guild.id, () => handleMessageDeleteBulk(channel.guild.id, channel.id, [
|
|
214
|
+
...messages.keys(),
|
|
215
|
+
]));
|
|
216
|
+
});
|
|
217
|
+
}
|
|
165
218
|
if (memberAddContributions.length > 0) {
|
|
166
219
|
client.on(Events.GuildMemberAdd, (member) => {
|
|
167
220
|
queue.enqueue(member.guild.id, () => handleMemberAdd(member));
|
|
@@ -360,6 +413,7 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
360
413
|
const installations = services.get(HELYX_SERVICE_NAMES.installations);
|
|
361
414
|
const member = await message.guild?.members.fetch(user.id);
|
|
362
415
|
const userRoleIds = member ? [...member.roles.cache.keys()] : [];
|
|
416
|
+
const parentChannelId = messageParentChannelId(message);
|
|
363
417
|
for (const { moduleId, contribution } of reactionContributions) {
|
|
364
418
|
if (!(await installations.isModuleEnabled(message.guildId, moduleId))) {
|
|
365
419
|
continue;
|
|
@@ -368,12 +422,21 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
368
422
|
await contribution.execute({
|
|
369
423
|
serverId: message.guildId,
|
|
370
424
|
channelId: message.channelId,
|
|
425
|
+
...(parentChannelId ? { parentChannelId } : {}),
|
|
371
426
|
messageId: message.id,
|
|
372
427
|
userId: receivedUser.id,
|
|
373
428
|
userRoleIds,
|
|
374
429
|
emojiKey: reactionEmojiKey(reaction),
|
|
430
|
+
emojiIdentifier: reactionEmojiIdentifier(reaction),
|
|
431
|
+
reactionCount: Math.max(0, reaction.count ?? 0),
|
|
375
432
|
reactionType: burst ? "burst" : "normal",
|
|
433
|
+
sourceAuthorId: message.author?.id ?? "",
|
|
434
|
+
sourceAuthorIsBot: message.author?.bot ?? false,
|
|
435
|
+
sourceAuthorIsWebhook: Boolean(message.webhookId),
|
|
436
|
+
sourceAuthorIsSystem: (message.author?.system ?? false) || (message.system ?? false),
|
|
437
|
+
sourceCreatedAt: message.createdAt ?? new Date(0),
|
|
376
438
|
services,
|
|
439
|
+
fetchSourceMessage: () => sourceMessageSnapshot(message, messageClassificationAvailable),
|
|
377
440
|
removeAddedReaction: () => reaction.users.remove(user.id).then(() => undefined),
|
|
378
441
|
removeUserReaction: async (emojiIdentifier) => {
|
|
379
442
|
const refreshed = await message.fetch();
|
|
@@ -465,10 +528,16 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
465
528
|
try {
|
|
466
529
|
if (receivedUser.bot)
|
|
467
530
|
return;
|
|
468
|
-
const
|
|
531
|
+
const reaction = receivedReaction.partial
|
|
532
|
+
? await receivedReaction.fetch()
|
|
533
|
+
: receivedReaction;
|
|
534
|
+
const message = reaction.message.partial
|
|
535
|
+
? await reaction.message.fetch()
|
|
536
|
+
: reaction.message;
|
|
469
537
|
if (!message.guildId || !message.channelId)
|
|
470
538
|
return;
|
|
471
539
|
const installations = services.get(HELYX_SERVICE_NAMES.installations);
|
|
540
|
+
const parentChannelId = messageParentChannelId(message);
|
|
472
541
|
for (const { moduleId, contribution } of reactionRemoveContributions) {
|
|
473
542
|
if (!(await installations.isModuleEnabled(message.guildId, moduleId))) {
|
|
474
543
|
continue;
|
|
@@ -477,9 +546,12 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
477
546
|
await contribution.execute({
|
|
478
547
|
serverId: message.guildId,
|
|
479
548
|
channelId: message.channelId,
|
|
549
|
+
...(parentChannelId ? { parentChannelId } : {}),
|
|
480
550
|
messageId: message.id,
|
|
481
551
|
userId: receivedUser.id,
|
|
482
|
-
emojiKey: reactionEmojiKey(
|
|
552
|
+
emojiKey: reactionEmojiKey(reaction),
|
|
553
|
+
emojiIdentifier: reactionEmojiIdentifier(reaction),
|
|
554
|
+
reactionCount: Math.max(0, reaction.count ?? 0),
|
|
483
555
|
reactionType: burst ? "burst" : "normal",
|
|
484
556
|
services,
|
|
485
557
|
});
|
|
@@ -502,6 +574,101 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
502
574
|
}, "Module reaction-remove event failed");
|
|
503
575
|
}
|
|
504
576
|
}
|
|
577
|
+
async function handleReactionRemoveAll(receivedMessage) {
|
|
578
|
+
const message = receivedMessage.partial
|
|
579
|
+
? await receivedMessage.fetch().catch(() => receivedMessage)
|
|
580
|
+
: receivedMessage;
|
|
581
|
+
if (!message.guildId || !message.channelId)
|
|
582
|
+
return;
|
|
583
|
+
const installations = services.get(HELYX_SERVICE_NAMES.installations);
|
|
584
|
+
for (const { moduleId, contribution } of reactionRemoveAllContributions) {
|
|
585
|
+
try {
|
|
586
|
+
if (!(await installations.isModuleEnabled(message.guildId, moduleId)))
|
|
587
|
+
continue;
|
|
588
|
+
await contribution.execute({
|
|
589
|
+
serverId: message.guildId,
|
|
590
|
+
channelId: message.channelId,
|
|
591
|
+
messageId: message.id,
|
|
592
|
+
services,
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
logger.warn({
|
|
597
|
+
event: "module.reaction_remove_all_failed",
|
|
598
|
+
moduleId,
|
|
599
|
+
error: safeError(error),
|
|
600
|
+
messageId: message.id,
|
|
601
|
+
guildId: message.guildId,
|
|
602
|
+
}, "Module reaction-remove-all event failed");
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function handleReactionRemoveEmoji(receivedReaction) {
|
|
607
|
+
const reaction = receivedReaction.partial
|
|
608
|
+
? await receivedReaction.fetch().catch(() => receivedReaction)
|
|
609
|
+
: receivedReaction;
|
|
610
|
+
const message = reaction.message;
|
|
611
|
+
if (!message.guildId || !message.channelId)
|
|
612
|
+
return;
|
|
613
|
+
const installations = services.get(HELYX_SERVICE_NAMES.installations);
|
|
614
|
+
for (const { moduleId, contribution } of reactionRemoveEmojiContributions) {
|
|
615
|
+
try {
|
|
616
|
+
if (!(await installations.isModuleEnabled(message.guildId, moduleId)))
|
|
617
|
+
continue;
|
|
618
|
+
await contribution.execute({
|
|
619
|
+
serverId: message.guildId,
|
|
620
|
+
channelId: message.channelId,
|
|
621
|
+
messageId: message.id,
|
|
622
|
+
emojiKey: reactionEmojiKey(reaction),
|
|
623
|
+
emojiIdentifier: reactionEmojiIdentifier(reaction),
|
|
624
|
+
services,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
logger.warn({
|
|
629
|
+
event: "module.reaction_remove_emoji_failed",
|
|
630
|
+
moduleId,
|
|
631
|
+
error: safeError(error),
|
|
632
|
+
messageId: message.id,
|
|
633
|
+
guildId: message.guildId,
|
|
634
|
+
}, "Module reaction-remove-emoji event failed");
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async function handleMessageUpdate(receivedMessage) {
|
|
639
|
+
if (!receivedMessage.guildId || !receivedMessage.channelId)
|
|
640
|
+
return;
|
|
641
|
+
const installations = services.get(HELYX_SERVICE_NAMES.installations);
|
|
642
|
+
for (const { moduleId, contribution } of messageUpdateContributions) {
|
|
643
|
+
try {
|
|
644
|
+
if (!(await installations.isModuleEnabled(receivedMessage.guildId, moduleId)))
|
|
645
|
+
continue;
|
|
646
|
+
await contribution.execute({
|
|
647
|
+
serverId: receivedMessage.guildId,
|
|
648
|
+
channelId: receivedMessage.channelId,
|
|
649
|
+
messageId: receivedMessage.id,
|
|
650
|
+
services,
|
|
651
|
+
fetchSourceMessage: async () => {
|
|
652
|
+
const message = receivedMessage.partial
|
|
653
|
+
? await receivedMessage.fetch().catch(() => null)
|
|
654
|
+
: receivedMessage;
|
|
655
|
+
return message
|
|
656
|
+
? sourceMessageSnapshot(message, messageClassificationAvailable)
|
|
657
|
+
: null;
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
catch (error) {
|
|
662
|
+
logger.warn({
|
|
663
|
+
event: "module.message_update_failed",
|
|
664
|
+
moduleId,
|
|
665
|
+
error: safeError(error),
|
|
666
|
+
messageId: receivedMessage.id,
|
|
667
|
+
guildId: receivedMessage.guildId,
|
|
668
|
+
}, "Module message-update event failed");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
505
672
|
async function handleMessageDelete(message) {
|
|
506
673
|
if (!message.guildId || !message.channelId)
|
|
507
674
|
return;
|
|
@@ -529,6 +696,34 @@ export function installModuleEvents(client, modules, logger, services) {
|
|
|
529
696
|
}
|
|
530
697
|
}
|
|
531
698
|
}
|
|
699
|
+
async function handleMessageDeleteBulk(guildId, channelId, messageIds) {
|
|
700
|
+
if (messageIds.length === 0)
|
|
701
|
+
return;
|
|
702
|
+
const installations = services.get(HELYX_SERVICE_NAMES.installations);
|
|
703
|
+
const boundedIds = messageIds.slice(0, 100);
|
|
704
|
+
for (const { moduleId, contribution } of messageDeleteBulkContributions) {
|
|
705
|
+
try {
|
|
706
|
+
if (!(await installations.isModuleEnabled(guildId, moduleId)))
|
|
707
|
+
continue;
|
|
708
|
+
await contribution.execute({
|
|
709
|
+
serverId: guildId,
|
|
710
|
+
channelId,
|
|
711
|
+
messageIds: boundedIds,
|
|
712
|
+
services,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
catch (error) {
|
|
716
|
+
logger.warn({
|
|
717
|
+
event: "module.message_delete_bulk_failed",
|
|
718
|
+
moduleId,
|
|
719
|
+
error: safeError(error),
|
|
720
|
+
guildId,
|
|
721
|
+
channelId,
|
|
722
|
+
messageCount: boundedIds.length,
|
|
723
|
+
}, "Module message-delete-bulk event failed");
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
532
727
|
async function handleVoiceState(before, after) {
|
|
533
728
|
const memberId = after.id;
|
|
534
729
|
const key = `${after.guild.id}:${memberId}`;
|
|
@@ -676,6 +871,78 @@ export function reactionEmojiKey(reaction) {
|
|
|
676
871
|
? `custom:${reaction.emoji.id}`
|
|
677
872
|
: `unicode:${(reaction.emoji.name ?? "").normalize("NFKC")}`;
|
|
678
873
|
}
|
|
874
|
+
export function reactionEmojiIdentifier(reaction) {
|
|
875
|
+
if (!reaction.emoji.id)
|
|
876
|
+
return reaction.emoji.name ?? "";
|
|
877
|
+
return `${reaction.emoji.name ?? "emoji"}:${reaction.emoji.id}`;
|
|
878
|
+
}
|
|
879
|
+
async function sourceMessageSnapshot(receivedMessage, contentAvailable) {
|
|
880
|
+
const message = receivedMessage.partial
|
|
881
|
+
? await receivedMessage.fetch()
|
|
882
|
+
: receivedMessage;
|
|
883
|
+
const parentChannelId = message.channel.isThread()
|
|
884
|
+
? (message.channel.parentId ?? undefined)
|
|
885
|
+
: undefined;
|
|
886
|
+
const authorDisplayName = message.member?.displayName ??
|
|
887
|
+
message.author.globalName ??
|
|
888
|
+
message.author.username;
|
|
889
|
+
const attachments = contentAvailable
|
|
890
|
+
? [...message.attachments.values()].slice(0, 10).map((attachment) => ({
|
|
891
|
+
name: attachment.name.slice(0, 255),
|
|
892
|
+
url: attachment.url,
|
|
893
|
+
contentType: attachment.contentType,
|
|
894
|
+
size: attachment.size,
|
|
895
|
+
image: attachment.contentType === "image/png" ||
|
|
896
|
+
attachment.contentType === "image/jpeg" ||
|
|
897
|
+
attachment.contentType === "image/gif" ||
|
|
898
|
+
attachment.contentType === "image/webp",
|
|
899
|
+
spoiler: attachment.spoiler,
|
|
900
|
+
}))
|
|
901
|
+
: [];
|
|
902
|
+
return {
|
|
903
|
+
serverId: message.guildId,
|
|
904
|
+
channelId: message.channelId,
|
|
905
|
+
...(message.channel.isThread()
|
|
906
|
+
? message.channel.parent?.name
|
|
907
|
+
? { channelName: message.channel.parent.name }
|
|
908
|
+
: {}
|
|
909
|
+
: "name" in message.channel && typeof message.channel.name === "string"
|
|
910
|
+
? { channelName: message.channel.name }
|
|
911
|
+
: {}),
|
|
912
|
+
...(parentChannelId ? { parentChannelId } : {}),
|
|
913
|
+
messageId: message.id,
|
|
914
|
+
jumpUrl: message.url,
|
|
915
|
+
authorId: message.author.id,
|
|
916
|
+
authorDisplayName,
|
|
917
|
+
authorAvatarUrl: message.author.displayAvatarURL({ size: 128 }),
|
|
918
|
+
authorIsBot: message.author.bot,
|
|
919
|
+
authorIsWebhook: Boolean(message.webhookId),
|
|
920
|
+
authorIsSystem: message.author.system || message.system,
|
|
921
|
+
createdAt: message.createdAt,
|
|
922
|
+
editedAt: message.editedAt,
|
|
923
|
+
ageRestricted: messageChannelAgeRestricted(message),
|
|
924
|
+
contentAvailable,
|
|
925
|
+
content: contentAvailable ? message.content.slice(0, 4_000) : "",
|
|
926
|
+
attachments,
|
|
927
|
+
reactions: [...message.reactions.cache.values()]
|
|
928
|
+
.slice(0, 20)
|
|
929
|
+
.map((reaction) => ({
|
|
930
|
+
emojiKey: reactionEmojiKey(reaction),
|
|
931
|
+
emojiIdentifier: reactionEmojiIdentifier(reaction),
|
|
932
|
+
count: Math.max(0, reaction.count),
|
|
933
|
+
})),
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
function messageChannelAgeRestricted(message) {
|
|
937
|
+
const channel = message.channel;
|
|
938
|
+
if (channel.isThread())
|
|
939
|
+
return channel.parent?.nsfw ?? false;
|
|
940
|
+
return "nsfw" in channel && channel.nsfw === true;
|
|
941
|
+
}
|
|
942
|
+
function messageParentChannelId(message) {
|
|
943
|
+
const channel = message.channel;
|
|
944
|
+
return channel?.isThread?.() ? (channel.parentId ?? undefined) : undefined;
|
|
945
|
+
}
|
|
679
946
|
export function reactionCacheKey(emojiIdentifier) {
|
|
680
947
|
const custom = /^(?:[A-Za-z0-9_]{2,32}):(\d{17,20})$/u.exec(emojiIdentifier);
|
|
681
948
|
return custom?.[1] ?? emojiIdentifier;
|
|
@@ -767,6 +1034,34 @@ export class DiscordMessageDeliveryService {
|
|
|
767
1034
|
};
|
|
768
1035
|
}
|
|
769
1036
|
}
|
|
1037
|
+
async fetchSource(input) {
|
|
1038
|
+
validateSnowflake(input.guildId, "guild");
|
|
1039
|
+
validateSnowflake(input.channelId, "channel");
|
|
1040
|
+
validateSnowflake(input.messageId, "message");
|
|
1041
|
+
const guild = await this.client.guilds.fetch(input.guildId);
|
|
1042
|
+
if (guild.id !== input.guildId)
|
|
1043
|
+
throw new Error("Discord guild validation failed");
|
|
1044
|
+
const channel = await guild.channels.fetch(input.channelId);
|
|
1045
|
+
if (!channel ||
|
|
1046
|
+
channel.guild.id !== guild.id ||
|
|
1047
|
+
!channel.isTextBased() ||
|
|
1048
|
+
channel.isDMBased() ||
|
|
1049
|
+
!channel.viewable)
|
|
1050
|
+
throw new Error("Discord source-message channel is unavailable");
|
|
1051
|
+
try {
|
|
1052
|
+
const message = await channel.messages.fetch(input.messageId);
|
|
1053
|
+
if (message.guildId !== input.guildId ||
|
|
1054
|
+
message.channelId !== input.channelId ||
|
|
1055
|
+
message.id !== input.messageId)
|
|
1056
|
+
throw new Error("Discord source message validation failed");
|
|
1057
|
+
return sourceMessageSnapshot(message, this.client.options.intents.has(GatewayIntentBits.MessageContent));
|
|
1058
|
+
}
|
|
1059
|
+
catch (error) {
|
|
1060
|
+
if (isUnknownMessage(error))
|
|
1061
|
+
return null;
|
|
1062
|
+
throw error;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
770
1065
|
}
|
|
771
1066
|
export class DiscordMemberEnforcementService {
|
|
772
1067
|
client;
|
|
@@ -1162,6 +1457,43 @@ export class DiscordResourceInspection {
|
|
|
1162
1457
|
};
|
|
1163
1458
|
});
|
|
1164
1459
|
}
|
|
1460
|
+
async listMembers(input) {
|
|
1461
|
+
validateSnowflake(input.guildId, "guild");
|
|
1462
|
+
if (input.after)
|
|
1463
|
+
validateSnowflake(input.after, "member cursor");
|
|
1464
|
+
if (!Number.isSafeInteger(input.limit) ||
|
|
1465
|
+
input.limit < 1 ||
|
|
1466
|
+
input.limit > 100)
|
|
1467
|
+
throw new Error("Discord member list limit is invalid");
|
|
1468
|
+
if (!this.client.options.intents.has(GatewayIntentBits.GuildMembers))
|
|
1469
|
+
throw new Error("Discord guild-members intent is unavailable");
|
|
1470
|
+
const guild = await this.client.guilds.fetch(input.guildId);
|
|
1471
|
+
if (guild.id !== input.guildId)
|
|
1472
|
+
throw new Error("Discord guild validation failed");
|
|
1473
|
+
const members = await guild.members.fetch({
|
|
1474
|
+
limit: input.limit,
|
|
1475
|
+
...(input.after ? { after: input.after } : {}),
|
|
1476
|
+
});
|
|
1477
|
+
const ordered = [...members.values()].sort((left, right) => BigInt(left.id) < BigInt(right.id)
|
|
1478
|
+
? -1
|
|
1479
|
+
: BigInt(left.id) > BigInt(right.id)
|
|
1480
|
+
? 1
|
|
1481
|
+
: 0);
|
|
1482
|
+
const items = ordered.map((member) => {
|
|
1483
|
+
const avatarUrl = memberAvatarUrl(member);
|
|
1484
|
+
return {
|
|
1485
|
+
userId: member.id,
|
|
1486
|
+
displayName: member.displayName,
|
|
1487
|
+
...(avatarUrl ? { avatarUrl } : {}),
|
|
1488
|
+
isBot: member.user.bot,
|
|
1489
|
+
};
|
|
1490
|
+
});
|
|
1491
|
+
const nextCursor = items.at(-1)?.userId;
|
|
1492
|
+
return {
|
|
1493
|
+
items,
|
|
1494
|
+
...(items.length === input.limit && nextCursor ? { nextCursor } : {}),
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1165
1497
|
async #resolveMemberGuild(input) {
|
|
1166
1498
|
validateSnowflake(input.guildId, "guild");
|
|
1167
1499
|
if (input.userIds.length > 50 ||
|
|
@@ -1295,6 +1627,7 @@ export class DiscordResourceInspection {
|
|
|
1295
1627
|
addReactions: false,
|
|
1296
1628
|
readMessageHistory: false,
|
|
1297
1629
|
manageMessages: false,
|
|
1630
|
+
ageRestricted: false,
|
|
1298
1631
|
};
|
|
1299
1632
|
const guildMatches = channel.guild.id === guild.id;
|
|
1300
1633
|
const me = guild.members.me ?? (await guild.members.fetchMe());
|
|
@@ -1322,6 +1655,9 @@ export class DiscordResourceInspection {
|
|
|
1322
1655
|
(effective?.has(PermissionFlagsBits.ReadMessageHistory) ?? false),
|
|
1323
1656
|
manageMessages: guildMatches &&
|
|
1324
1657
|
(effective?.has(PermissionFlagsBits.ManageMessages) ?? false),
|
|
1658
|
+
ageRestricted: "nsfw" in channel && typeof channel.nsfw === "boolean"
|
|
1659
|
+
? channel.nsfw
|
|
1660
|
+
: false,
|
|
1325
1661
|
};
|
|
1326
1662
|
}
|
|
1327
1663
|
async inspectMessage(input) {
|
package/dist/index.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export interface BotHandle {
|
|
|
9
9
|
export declare function startBot(processEnvironment?: NodeJS.ProcessEnv): Promise<BotHandle>;
|
|
10
10
|
export declare function resolveBotWorkerEntryPoint(entryUrl?: string): string;
|
|
11
11
|
export declare function readBotVersion(): string;
|
|
12
|
-
export declare function gatewayIntentsForModules(modules: readonly ModuleDefinition[], messageContentEnabled?: boolean): GatewayIntentBits[];
|
|
12
|
+
export declare function gatewayIntentsForModules(modules: readonly ModuleDefinition[], messageContentEnabled?: boolean, guildMembersEnabled?: boolean): GatewayIntentBits[];
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,10 @@ import { parseShardWorkerLaunchContract, startShardWorker, } from "./shard-worke
|
|
|
27
27
|
import { createDeveloperOperationsModule } from "./developer-operations-module.js";
|
|
28
28
|
import { shardHealthSnapshotSchema } from "./shard-contracts.js";
|
|
29
29
|
import { createSupportModule } from "./support-module.js";
|
|
30
|
+
import { createOAuthModule } from "./oauth-module.js";
|
|
31
|
+
import { HttpOAuthService } from "./oauth-service.js";
|
|
30
32
|
import { DiscordOwnedMessageDeletionService } from "./discord-owned-message-service.js";
|
|
33
|
+
import { DiscordGuildHealthInspection } from "./discord-guild-health.js";
|
|
31
34
|
import { CoreResourceAuthorizationService } from "./resource-authorization.js";
|
|
32
35
|
import { createLocalScheduledTaskRoute, createScheduledTaskRunner, schedulerOperationalHealth, } from "./scheduled-task-runner.js";
|
|
33
36
|
import { createLocalNumericWriteBehind } from "./numeric-write-behind.js";
|
|
@@ -129,7 +132,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
129
132
|
}, "Module databases ready");
|
|
130
133
|
const featureModules = modulePackages.map(({ definition }) => definition);
|
|
131
134
|
const client = new Client({
|
|
132
|
-
intents: gatewayIntentsForModules(featureModules, environment.HELYX_MESSAGE_CONTENT_ENABLED),
|
|
135
|
+
intents: gatewayIntentsForModules(featureModules, environment.HELYX_MESSAGE_CONTENT_ENABLED, environment.HELYX_GUILD_MEMBERS_ENABLED),
|
|
133
136
|
partials: [Partials.Message, Partials.Channel, Partials.Reaction],
|
|
134
137
|
allowedMentions: { parse: [] },
|
|
135
138
|
});
|
|
@@ -152,10 +155,14 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
152
155
|
})
|
|
153
156
|
: undefined;
|
|
154
157
|
const supportModule = createSupportModule(cloudConnector);
|
|
158
|
+
const oauthModule = environment.HELYX_OAUTH_ENABLED
|
|
159
|
+
? createOAuthModule()
|
|
160
|
+
: undefined;
|
|
155
161
|
const publicModules = [
|
|
156
162
|
managementModule,
|
|
157
163
|
permissionsModule,
|
|
158
164
|
supportModule,
|
|
165
|
+
...(oauthModule ? [oauthModule] : []),
|
|
159
166
|
...(cloudManagementModule ? [cloudManagementModule] : []),
|
|
160
167
|
...featureModules,
|
|
161
168
|
];
|
|
@@ -172,6 +179,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
172
179
|
managementModule,
|
|
173
180
|
permissionsModule,
|
|
174
181
|
supportModule,
|
|
182
|
+
...(oauthModule ? [oauthModule] : []),
|
|
175
183
|
...(cloudManagementModule ? [cloudManagementModule] : []),
|
|
176
184
|
].map(({ manifest }) => manifest),
|
|
177
185
|
modules: featureModules.map(({ manifest }) => manifest),
|
|
@@ -257,6 +265,9 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
257
265
|
}));
|
|
258
266
|
platformServices.set(HELYX_SERVICE_NAMES.verificationMembers, new DiscordVerificationMemberService(client));
|
|
259
267
|
}
|
|
268
|
+
if (environment.HELYX_OAUTH_ENABLED &&
|
|
269
|
+
environment.HELYX_OAUTH_INTERNAL_SECRET)
|
|
270
|
+
platformServices.set(HELYX_SERVICE_NAMES.oauth, new HttpOAuthService(environment.HELYX_CLOUD_API_URL, environment.HELYX_OAUTH_INTERNAL_SECRET));
|
|
260
271
|
const numericWriteBehind = createLocalNumericWriteBehind(platformServices, publicModules);
|
|
261
272
|
platformServices.set(HELYX_SERVICE_NAMES.numericWriteBehind, numericWriteBehind);
|
|
262
273
|
platformServices.set(HELYX_SERVICE_NAMES.resourceAuthorization, new CoreResourceAuthorizationService(platformServices.get(HELYX_SERVICE_NAMES.permissions)));
|
|
@@ -265,6 +276,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
265
276
|
platformServices.set(HELYX_SERVICE_NAMES.memberEnforcement, new DiscordMemberEnforcementService(client));
|
|
266
277
|
platformServices.set(HELYX_SERVICE_NAMES.memberRoles, new DiscordMemberRoleService(client));
|
|
267
278
|
platformServices.set(HELYX_SERVICE_NAMES.discordResources, new DiscordResourceInspection(client));
|
|
279
|
+
platformServices.set(HELYX_SERVICE_NAMES.discordGuildHealth, new DiscordGuildHealthInspection(client, publicModules, serviceAccess));
|
|
268
280
|
if (featureModules.some(({ manifest }) => manifest.id === "helyx.logging")) {
|
|
269
281
|
platformServices.set(HELYX_SERVICE_NAMES.moduleLogging, new DiscordModuleLoggingService(client, database, publicModules, bootstrapLogger));
|
|
270
282
|
}
|
|
@@ -277,6 +289,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
277
289
|
managementModule.manifest.id,
|
|
278
290
|
permissionsModule.manifest.id,
|
|
279
291
|
supportModule.manifest.id,
|
|
292
|
+
...(oauthModule ? [oauthModule.manifest.id] : []),
|
|
280
293
|
...(cloudManagementModule ? [cloudManagementModule.manifest.id] : []),
|
|
281
294
|
...(operationsModule ? [operationsModule.manifest.id] : []),
|
|
282
295
|
]);
|
|
@@ -288,6 +301,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
288
301
|
prepare: prepareGuilds,
|
|
289
302
|
selfAuthorizingGuildModuleIds: new Set([
|
|
290
303
|
supportModule.manifest.id,
|
|
304
|
+
...(oauthModule ? [oauthModule.manifest.id] : []),
|
|
291
305
|
...(operationsModule ? [operationsModule.manifest.id] : []),
|
|
292
306
|
]),
|
|
293
307
|
}, bootstrapLogger, serviceAccess);
|
|
@@ -469,10 +483,12 @@ function deploymentReference(processEnvironment) {
|
|
|
469
483
|
processEnvironment.GIT_COMMIT_SHA;
|
|
470
484
|
return value?.trim().slice(0, 12) || undefined;
|
|
471
485
|
}
|
|
472
|
-
export function gatewayIntentsForModules(modules, messageContentEnabled = false) {
|
|
486
|
+
export function gatewayIntentsForModules(modules, messageContentEnabled = false, guildMembersEnabled = false) {
|
|
473
487
|
const intents = [GatewayIntentBits.Guilds];
|
|
474
488
|
if (modules.some((module) => module.events?.messageReactionAdd?.length ||
|
|
475
|
-
module.events?.messageReactionRemove?.length
|
|
489
|
+
module.events?.messageReactionRemove?.length ||
|
|
490
|
+
module.events?.messageReactionRemoveAll?.length ||
|
|
491
|
+
module.events?.messageReactionRemoveEmoji?.length)) {
|
|
476
492
|
intents.push(GatewayIntentBits.GuildMessageReactions);
|
|
477
493
|
}
|
|
478
494
|
const hasPrivilegedMessageCreate = modules.some((module) => module.events?.privilegedMessageCreate?.length);
|
|
@@ -480,16 +496,19 @@ export function gatewayIntentsForModules(modules, messageContentEnabled = false)
|
|
|
480
496
|
if (((hasPrivilegedMessageCreate || requiresMessageContent) &&
|
|
481
497
|
messageContentEnabled) ||
|
|
482
498
|
modules.some((module) => module.events?.guildMessageCreate?.length ||
|
|
483
|
-
module.events?.
|
|
499
|
+
module.events?.guildMessageUpdate?.length ||
|
|
500
|
+
module.events?.messageDelete?.length ||
|
|
501
|
+
module.events?.guildMessageDeleteBulk?.length)) {
|
|
484
502
|
intents.push(GatewayIntentBits.GuildMessages);
|
|
485
503
|
}
|
|
486
504
|
if ((hasPrivilegedMessageCreate || requiresMessageContent) &&
|
|
487
505
|
messageContentEnabled) {
|
|
488
506
|
intents.push(GatewayIntentBits.MessageContent);
|
|
489
507
|
}
|
|
490
|
-
if (
|
|
491
|
-
module.
|
|
492
|
-
|
|
508
|
+
if (guildMembersEnabled ||
|
|
509
|
+
modules.some((module) => module.requiredDiscordCapabilities?.includes("guild-members") ||
|
|
510
|
+
module.events?.guildMemberAdd?.length ||
|
|
511
|
+
module.events?.guildMemberRemove?.length)) {
|
|
493
512
|
intents.push(GatewayIntentBits.GuildMembers);
|
|
494
513
|
}
|
|
495
514
|
if (modules.some((module) => module.events?.guildVoiceSession?.length)) {
|
package/dist/interactions.js
CHANGED
|
@@ -6,6 +6,7 @@ const buttonStyles = {
|
|
|
6
6
|
secondary: ButtonStyle.Secondary,
|
|
7
7
|
success: ButtonStyle.Success,
|
|
8
8
|
danger: ButtonStyle.Danger,
|
|
9
|
+
link: ButtonStyle.Link,
|
|
9
10
|
};
|
|
10
11
|
export function installInteractions(client, modules, registration, logger, services) {
|
|
11
12
|
const commands = uniqueMap(modules.flatMap((module) => (module.interactions?.commands ?? []).map((contribution) => ({
|
|
@@ -702,9 +703,14 @@ export function toReplyOptions(response) {
|
|
|
702
703
|
const allowedMentions = toAllowedUserMentions(response.allowedUserMentionIds);
|
|
703
704
|
const selectRows = toSelectRows(response);
|
|
704
705
|
if (response.componentsV2) {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
706
|
+
if (response.content !== undefined) {
|
|
707
|
+
throw new Error("Components V2 responses cannot include plain content");
|
|
708
|
+
}
|
|
709
|
+
let flags = Number(MessageFlags.IsComponentsV2);
|
|
710
|
+
if (response.ephemeral)
|
|
711
|
+
flags |= MessageFlags.Ephemeral;
|
|
712
|
+
if (response.suppressEmbeds)
|
|
713
|
+
flags |= MessageFlags.SuppressEmbeds;
|
|
708
714
|
return {
|
|
709
715
|
flags,
|
|
710
716
|
...(allowedMentions ? { allowedMentions } : {}),
|
|
@@ -728,9 +734,11 @@ export function toReplyOptions(response) {
|
|
|
728
734
|
type: 1,
|
|
729
735
|
components: response.buttons.map((button) => ({
|
|
730
736
|
type: 2,
|
|
731
|
-
custom_id: button.customId,
|
|
732
737
|
label: button.label,
|
|
733
738
|
style: buttonStyles[button.style],
|
|
739
|
+
...(button.style === "link"
|
|
740
|
+
? { url: button.url }
|
|
741
|
+
: { custom_id: button.customId }),
|
|
734
742
|
...(button.emojiIdentifier
|
|
735
743
|
? { emoji: toButtonEmoji(button.emojiIdentifier) }
|
|
736
744
|
: {}),
|
|
@@ -750,13 +758,29 @@ export function toReplyOptions(response) {
|
|
|
750
758
|
: undefined;
|
|
751
759
|
if (embed && response.embed?.footer)
|
|
752
760
|
embed.setFooter({ text: response.embed.footer });
|
|
761
|
+
if (embed && response.embed?.url)
|
|
762
|
+
embed.setURL(response.embed.url);
|
|
763
|
+
if (embed && response.embed?.author)
|
|
764
|
+
embed.setAuthor({
|
|
765
|
+
name: response.embed.author.name,
|
|
766
|
+
...(response.embed.author.iconUrl
|
|
767
|
+
? { iconURL: response.embed.author.iconUrl }
|
|
768
|
+
: {}),
|
|
769
|
+
});
|
|
770
|
+
if (embed && response.embed?.timestamp)
|
|
771
|
+
embed.setTimestamp(new Date(response.embed.timestamp));
|
|
772
|
+
if (embed && response.embed?.imageUrl)
|
|
773
|
+
embed.setImage(response.embed.imageUrl);
|
|
753
774
|
const components = response.buttons?.length
|
|
754
775
|
? [
|
|
755
776
|
new ActionRowBuilder().addComponents(response.buttons.map((button) => {
|
|
756
777
|
const builder = new ButtonBuilder()
|
|
757
|
-
.setCustomId(button.customId)
|
|
758
778
|
.setLabel(button.label)
|
|
759
779
|
.setStyle(buttonStyles[button.style]);
|
|
780
|
+
if (button.style === "link")
|
|
781
|
+
builder.setURL(button.url);
|
|
782
|
+
else
|
|
783
|
+
builder.setCustomId(button.customId);
|
|
760
784
|
if (button.emojiIdentifier) {
|
|
761
785
|
builder.setEmoji(toButtonEmoji(button.emojiIdentifier));
|
|
762
786
|
}
|
|
@@ -769,8 +793,14 @@ export function toReplyOptions(response) {
|
|
|
769
793
|
: [];
|
|
770
794
|
components.push(...selectRows);
|
|
771
795
|
return {
|
|
772
|
-
...(response.ephemeral
|
|
796
|
+
...(response.ephemeral || response.suppressEmbeds
|
|
797
|
+
? {
|
|
798
|
+
flags: (response.ephemeral ? MessageFlags.Ephemeral : 0) |
|
|
799
|
+
(response.suppressEmbeds ? MessageFlags.SuppressEmbeds : 0),
|
|
800
|
+
}
|
|
801
|
+
: {}),
|
|
773
802
|
...(allowedMentions ? { allowedMentions } : {}),
|
|
803
|
+
...(response.content === undefined ? {} : { content: response.content }),
|
|
774
804
|
...(embed ? { embeds: [embed] } : {}),
|
|
775
805
|
components,
|
|
776
806
|
};
|