fix: refresh complete reaction state
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"check": "deno fmt --check && deno lint && deno check src/index.ts && deno test",
|
"check": "deno fmt --check && deno lint && deno check src/index.ts && deno test",
|
||||||
"start": "deno run --allow-env=TOKEN,CLIENT_ID,CONFIG_PATH,NODE_V8_COVERAGE,UNDICI_NO_FG,JEST_WORKER_ID,SHARDS,SHARD_COUNT,SHARDING_MANAGER,SHARDING_MANAGER_MODE,DISCORD_TOKEN,WS_NO_BUFFER_UTIL,WS_NO_UTF_8_VALIDATE --allow-net=discord.com,gateway.discord.gg --allow-read=./config.json,/data/config.json --allow-write=.,/data src/index.ts"
|
"start": "deno run --allow-env=TOKEN,CLIENT_ID,CONFIG_PATH,NODE_V8_COVERAGE,UNDICI_NO_FG,JEST_WORKER_ID,SHARDS,SHARD_COUNT,SHARDING_MANAGER,SHARDING_MANAGER_MODE,DISCORD_TOKEN,WS_NO_BUFFER_UTIL,WS_NO_UTF_8_VALIDATE --allow-net=discord.com,gateway.discord.gg --allow-read=./config.json,./config.json.tmp,/data/config.json,/data/config.json.tmp --allow-write=.,/data src/index.ts"
|
||||||
},
|
},
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["deno.window"]
|
"lib": ["deno.window"]
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { EmbedBuilder, MessageReaction, PartialMessageReaction } from "discord.js";
|
import { EmbedBuilder, Message } from "discord.js";
|
||||||
|
|
||||||
export default function awardEmbed(
|
export default function awardEmbed(
|
||||||
reaction: MessageReaction | PartialMessageReaction,
|
message: Message,
|
||||||
score: number,
|
score: number,
|
||||||
): EmbedBuilder {
|
): EmbedBuilder {
|
||||||
const message = reaction.message;
|
|
||||||
const reactionSummary = message.reactions.cache
|
const reactionSummary = message.reactions.cache
|
||||||
.map((item) => `${item.emoji} ×${item.count}`)
|
.map((item) => `${item.emoji} ×${item.count}`)
|
||||||
.join(" · ");
|
.join(" · ");
|
||||||
|
|||||||
+68
-4
@@ -51,6 +51,7 @@ client.once(Events.ClientReady, (readyClient) => {
|
|||||||
`Logged in as ${readyClient.user.tag}; serving ${readyClient.guilds.cache.size} guild(s).`,
|
`Logged in as ${readyClient.user.tag}; serving ${readyClient.guilds.cache.size} guild(s).`,
|
||||||
);
|
);
|
||||||
readyClient.user.setActivity("👑 awarding funny messages");
|
readyClient.user.setActivity("👑 awarding funny messages");
|
||||||
|
void reconcileAwards().catch((error) => console.error("Failed to reconcile awards:", error));
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on(Events.InteractionCreate, async (interaction) => {
|
client.on(Events.InteractionCreate, async (interaction) => {
|
||||||
@@ -185,9 +186,13 @@ async function updateAward(
|
|||||||
reaction: MessageReaction | PartialMessageReaction,
|
reaction: MessageReaction | PartialMessageReaction,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (reaction.partial) await reaction.fetch();
|
if (reaction.partial) await reaction.fetch();
|
||||||
if (reaction.message.partial) await reaction.message.fetch();
|
// Reaction gateway events only populate the cache entry that changed. Always fetch the
|
||||||
|
// message so reactions that existed before this process started are included as well.
|
||||||
|
const message = await reaction.message.fetch(true);
|
||||||
|
await updateAwardForMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
const message = reaction.message;
|
async function updateAwardForMessage(message: Message): Promise<void> {
|
||||||
const entry = watchedEntry(message);
|
const entry = watchedEntry(message);
|
||||||
if (!message.guild || !entry) return;
|
if (!message.guild || !entry) return;
|
||||||
|
|
||||||
@@ -225,7 +230,7 @@ async function updateAward(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = { embeds: [awardEmbed(reaction, score)] };
|
const payload = { embeds: [awardEmbed(message, score)] };
|
||||||
if (oldAward) {
|
if (oldAward) {
|
||||||
await oldAward.edit(payload);
|
await oldAward.edit(payload);
|
||||||
if (entry.awards[message.id] !== oldAward.id) {
|
if (entry.awards[message.id] !== oldAward.id) {
|
||||||
@@ -239,6 +244,65 @@ async function updateAward(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function reconcileAwards(): Promise<void> {
|
||||||
|
let reconciled = 0;
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.output_channel) continue;
|
||||||
|
|
||||||
|
const guild = await client.guilds.fetch(entry.server).catch(() => undefined);
|
||||||
|
if (!guild) continue;
|
||||||
|
const outputChannel = await guild.channels.fetch(entry.output_channel).catch(() => undefined);
|
||||||
|
if (!(outputChannel instanceof BaseGuildTextChannel)) continue;
|
||||||
|
|
||||||
|
const sourceChannels = new Map<string, string>();
|
||||||
|
const recentAwards = await outputChannel.messages.fetch({ limit: 100 });
|
||||||
|
for (const award of recentAwards.values()) {
|
||||||
|
if (award.author.id !== client.user?.id) continue;
|
||||||
|
const source = getSourceMessageLocation(award);
|
||||||
|
if (source) sourceChannels.set(source.messageId, source.channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [sourceMessageId, awardMessageId] of Object.entries(entry.awards)) {
|
||||||
|
if (sourceChannels.has(sourceMessageId)) continue;
|
||||||
|
const award = await outputChannel.messages.fetch(awardMessageId).catch(() => undefined);
|
||||||
|
if (!award) continue;
|
||||||
|
const source = getSourceMessageLocation(award);
|
||||||
|
if (source?.messageId === sourceMessageId) {
|
||||||
|
sourceChannels.set(sourceMessageId, source.channelId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [sourceMessageId, sourceChannelId] of sourceChannels) {
|
||||||
|
const sourceChannel = await guild.channels.fetch(sourceChannelId).catch(() => undefined);
|
||||||
|
if (!(sourceChannel instanceof BaseGuildTextChannel)) continue;
|
||||||
|
|
||||||
|
const sourceMessage = await sourceChannel.messages.fetch({
|
||||||
|
message: sourceMessageId,
|
||||||
|
force: true,
|
||||||
|
}).catch(() => undefined);
|
||||||
|
if (!sourceMessage) continue;
|
||||||
|
|
||||||
|
await updateAwardForMessage(sourceMessage);
|
||||||
|
reconciled++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reconciled > 0) console.log(`Reconciled ${reconciled} existing award(s).`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSourceMessageLocation(
|
||||||
|
award: Message,
|
||||||
|
): { channelId: string; messageId: string } | undefined {
|
||||||
|
for (const embed of award.embeds) {
|
||||||
|
for (const field of embed.fields) {
|
||||||
|
const match = field.value.match(
|
||||||
|
/https:\/\/discord\.com\/channels\/\d+\/(\d+)\/(\d+)/,
|
||||||
|
);
|
||||||
|
if (match) return { channelId: match[1], messageId: match[2] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function watchedEntry(message: Message | PartialMessage): Entry | undefined {
|
function watchedEntry(message: Message | PartialMessage): Entry | undefined {
|
||||||
const entry = entries.find((item) => item.server === message.guildId);
|
const entry = entries.find((item) => item.server === message.guildId);
|
||||||
if (!entry?.output_channel) return undefined;
|
if (!entry?.output_channel) return undefined;
|
||||||
@@ -253,7 +317,7 @@ function listAllows(type: Type, values: string[], value: string): boolean {
|
|||||||
|
|
||||||
async function getMessageScore(
|
async function getMessageScore(
|
||||||
entry: Entry,
|
entry: Entry,
|
||||||
message: Message | PartialMessage,
|
message: Message,
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
const reactionCounts = new Map<string, number>();
|
const reactionCounts = new Map<string, number>();
|
||||||
for (const item of message.reactions.cache.values()) {
|
for (const item of message.reactions.cache.values()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user