349 lines
10 KiB
TypeScript
349 lines
10 KiB
TypeScript
import "dotenv/config";
|
|
const TOKEN = process.env.TOKEN;
|
|
const CLIENT_ID = process.env.CLIENT_ID;
|
|
if (!TOKEN || !CLIENT_ID) {
|
|
console.error(`No ${TOKEN ? "CLIENT_ID" : "TOKEN"} provided`);
|
|
process.exit();
|
|
}
|
|
import commands from "./commands.ts";
|
|
|
|
import {
|
|
REST,
|
|
Routes,
|
|
Client,
|
|
Partials,
|
|
GatewayIntentBits,
|
|
Events,
|
|
TextChannel,
|
|
Message,
|
|
PartialMessage,
|
|
} from "discord.js";
|
|
|
|
const rest = new REST({ version: "10" }).setToken(TOKEN);
|
|
try {
|
|
console.log("refreshing slash commands...");
|
|
await rest.put(Routes.applicationCommands(CLIENT_ID), { body: commands });
|
|
console.log("OK");
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
|
|
import { writeConfig, readConfig } from "./config.ts";
|
|
const client = new Client({
|
|
intents: [
|
|
GatewayIntentBits.Guilds,
|
|
GatewayIntentBits.GuildMessages,
|
|
GatewayIntentBits.GuildMessageReactions,
|
|
GatewayIntentBits.MessageContent,
|
|
],
|
|
partials: [Partials.Message, Partials.Channel, Partials.Reaction],
|
|
});
|
|
|
|
client.on("ready", () => {
|
|
if (!client.user) {
|
|
console.error("Failed to login");
|
|
process.exit();
|
|
}
|
|
|
|
console.log(`Logged in as ${client.user.tag}!`);
|
|
client.user.setActivity("👑let's award funny messages!");
|
|
});
|
|
|
|
import { Entry, Type } from "./entry.ts";
|
|
const entries: Entry[] = readConfig();
|
|
writeConfig(entries);
|
|
|
|
client.on("interactionCreate", async (interaction) => {
|
|
if (interaction.isChatInputCommand()) {
|
|
if (interaction.commandName === "config") {
|
|
if (interaction.user.id !== interaction.guild?.ownerId) {
|
|
await interaction.reply({
|
|
content: "Only the server owner can configure the bot",
|
|
ephemeral: true,
|
|
});
|
|
return;
|
|
}
|
|
const subcommand = interaction.options.getSubcommand(true);
|
|
if (subcommand === "output") {
|
|
const channel = interaction.options.getChannel("channel", true);
|
|
try {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
let entry = entries.find(
|
|
(entry) => entry.server === interaction.guildId
|
|
);
|
|
if (!entry) {
|
|
entry = new Entry(interaction.guildId!);
|
|
entries.push(entry);
|
|
}
|
|
entry.output_channel = channel.id;
|
|
writeConfig(entries);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
await interaction.followUp({
|
|
content: `Set output to <#${channel.id}>`,
|
|
});
|
|
}
|
|
} else if (subcommand === "channels") {
|
|
const channel = interaction.options.getChannel("channel", true);
|
|
const mode = interaction.options.getString("add_remove", true);
|
|
try {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
let entry = entries.find(
|
|
(entry) => entry.server === interaction.guildId
|
|
);
|
|
if (!entry) {
|
|
entry = new Entry(interaction.guildId!);
|
|
entries.push(entry);
|
|
}
|
|
if (mode === "add") entry.channels.push(channel.id);
|
|
else entry.channels = entry.channels.filter((c) => c !== channel.id);
|
|
writeConfig(entries);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
await interaction.followUp({
|
|
content: `Set to watch for reactions in <#${channel.id}>`,
|
|
});
|
|
}
|
|
} else if (subcommand === "channels_type") {
|
|
const channel_type = interaction.options.getString(
|
|
"channels_type",
|
|
true
|
|
);
|
|
try {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
let entry = entries.find(
|
|
(entry) => entry.server === interaction.guildId
|
|
);
|
|
if (!entry) {
|
|
entry = new Entry(interaction.guildId!);
|
|
entries.push(entry);
|
|
}
|
|
entry.channel_type = channel_type as Type;
|
|
writeConfig(entries);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
await interaction.followUp({
|
|
content: `channel list type set to ${channel_type}`,
|
|
ephemeral: true,
|
|
});
|
|
}
|
|
} else if (subcommand === "emojis") {
|
|
const emoji = interaction.options.getString("emoji", true);
|
|
const mode = interaction.options.getString("add_remove", true);
|
|
try {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
let entry = entries.find(
|
|
(entry) => entry.server === interaction.guildId
|
|
);
|
|
if (!entry) {
|
|
entry = new Entry(interaction.guildId!);
|
|
entries.push(entry);
|
|
}
|
|
if (mode === "add") entry.emojis.push(emoji);
|
|
else entry.emojis = entry.emojis.filter((e) => e !== emoji);
|
|
await writeConfig(entries);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
await interaction.followUp({
|
|
content: `Set to watch for ${emoji}`,
|
|
ephemeral: true,
|
|
});
|
|
}
|
|
} else if (subcommand === "emojis_type") {
|
|
const emoji_type = interaction.options.getString("emojis_type", true);
|
|
try {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
let entry = entries.find(
|
|
(entry) => entry.server === interaction.guildId
|
|
);
|
|
if (!entry) {
|
|
entry = new Entry(interaction.guildId!);
|
|
entries.push(entry);
|
|
}
|
|
entry.emoji_type = emoji_type as Type;
|
|
writeConfig(entries);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
await interaction.followUp({
|
|
content: `emoji list type set to ${emoji_type}`,
|
|
ephemeral: true,
|
|
});
|
|
}
|
|
} else if (subcommand === "score") {
|
|
const score = interaction.options.getInteger("score", true);
|
|
try {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
let entry = entries.find(
|
|
(entry) => entry.server === interaction.guildId
|
|
);
|
|
if (!entry) {
|
|
entry = new Entry(interaction.guildId!);
|
|
entries.push(entry);
|
|
}
|
|
entry.score = score;
|
|
await writeConfig(entries);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
await interaction.followUp({
|
|
content: `The score needed to trigger is now ${score}`,
|
|
ephemeral: true,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
import awardEmbed from "./award_message.ts";
|
|
client.on(Events.MessageReactionAdd, async (reaction, user) => {
|
|
if (reaction.partial) {
|
|
try {
|
|
await reaction.fetch();
|
|
} catch (error) {
|
|
console.error("Something went wrong when fetching the message:", error);
|
|
return;
|
|
}
|
|
}
|
|
if (!reaction.message.guild) return;
|
|
|
|
const entry = entries.find(
|
|
(entry) => entry.server === reaction.message.guildId
|
|
);
|
|
if (entry === undefined) return;
|
|
if (
|
|
(entry.channel_type === Type.EXCLUDE) ===
|
|
entry.channels.includes(reaction.message.channelId)
|
|
)
|
|
return;
|
|
if (
|
|
(entry.emoji_type === Type.EXCLUDE) ===
|
|
entry.emojis.includes(`${reaction.emoji.id || reaction.emoji.name}`)
|
|
)
|
|
return;
|
|
|
|
console.log(`reaction added: ${reaction.emoji.id || reaction.emoji.name}`);
|
|
const totalScore = await getMessageScore(entry, reaction.message);
|
|
|
|
if (totalScore >= entry.score) {
|
|
const channel = reaction.message.guild.channels.cache.get(
|
|
entry.output_channel
|
|
) as TextChannel;
|
|
channel.messages.fetch({ limit: 10 }).then((messages) => {
|
|
messages.find((message) => {
|
|
if (message.embeds[0]?.fields[0].value.includes(reaction.message.id)) {
|
|
message.delete();
|
|
console.log("old message deleted.");
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
channel.send({
|
|
embeds: [awardEmbed(reaction, totalScore)],
|
|
});
|
|
console.log("message sent.");
|
|
});
|
|
}
|
|
});
|
|
|
|
client.on(Events.MessageReactionRemove, async (reaction, user) => {
|
|
if (reaction.partial) {
|
|
try {
|
|
await reaction.fetch();
|
|
} catch (error) {
|
|
console.error("Something went wrong when fetching the message:", error);
|
|
return;
|
|
}
|
|
}
|
|
if (!reaction.message.guild) return;
|
|
|
|
const entry = entries.find(
|
|
(entry) => entry.server === reaction.message.guildId
|
|
);
|
|
if (entry === undefined) return;
|
|
if (
|
|
(entry.channel_type === Type.EXCLUDE) ===
|
|
entry.channels.includes(reaction.message.channelId)
|
|
)
|
|
return;
|
|
if (
|
|
(entry.emoji_type === Type.EXCLUDE) ===
|
|
entry.emojis.includes(`${reaction.emoji.id || reaction.emoji.name}`)
|
|
)
|
|
return;
|
|
|
|
console.log(`reaction removed: ${reaction.emoji.id || reaction.emoji.name}`);
|
|
const totalScore = await getMessageScore(entry, reaction.message);
|
|
|
|
const channel = reaction.message.guild.channels.cache.get(
|
|
entry.output_channel
|
|
) as TextChannel;
|
|
channel.messages.fetch({ limit: 10 }).then((messages) => {
|
|
messages.find((message) => {
|
|
if (message.embeds[0]?.fields[0].value.includes(reaction.message.id)) {
|
|
message.delete();
|
|
console.log("old message deleted.");
|
|
if (totalScore >= entry.score) {
|
|
channel.send({
|
|
embeds: [awardEmbed(reaction, totalScore)],
|
|
});
|
|
console.log("message sent.");
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
});
|
|
});
|
|
|
|
client.login(TOKEN);
|
|
|
|
async function getMessageScore(
|
|
entry: Entry,
|
|
message: Message | PartialMessage
|
|
): Promise<number> {
|
|
/* Message Score Calculation *
|
|
* Per user: 1 / (-1 + 2x) */
|
|
|
|
const allReactions: Map<string, number> = new Map();
|
|
const reactions = message.reactions.cache;
|
|
for (const r of reactions.values()) {
|
|
if (
|
|
(entry.emoji_type === Type.EXCLUDE) ===
|
|
entry.emojis.includes(`${r.emoji.id || r.emoji.name}`)
|
|
)
|
|
continue;
|
|
const users = await r.users.fetch();
|
|
for (const user of users.values()) {
|
|
if (user.bot) continue;
|
|
const score = allReactions.get(user.id) || 0;
|
|
allReactions.set(user.id, score + 1 / (-1 + 2 * (score + 1)));
|
|
}
|
|
}
|
|
|
|
let totalScore = 0;
|
|
allReactions.forEach((value) => {
|
|
totalScore += value;
|
|
});
|
|
|
|
// "message" by "user"
|
|
// id : score
|
|
// ----------------
|
|
// Total: 1.5
|
|
|
|
console.log(`"${message.id}" by "${message.author?.username}"`);
|
|
allReactions.forEach((value, key) => {
|
|
console.log(`${key} : ${value}`);
|
|
});
|
|
console.log(`----------------`);
|
|
console.log(`Total: ${totalScore}`);
|
|
|
|
return totalScore;
|
|
}
|