Add roles management per reaction on a message and event reminder with role ping
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
BOT_TOKEN=
|
BOT_TOKEN=
|
||||||
CLIENT_ID=
|
CLIENT_ID=
|
||||||
SHEET_ID=
|
|
||||||
DB_NAME=
|
DB_NAME=
|
||||||
DB_PORT=
|
DB_PORT=
|
||||||
DB_HOST=
|
DB_HOST=
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Tochi's Discord Bot
|
# Tochi's Discord Bot
|
||||||
|
|
||||||
A simple discord bot for the Limited Edition legion in the game Aion Classic that interfaces with a PostgreSQL database
|
A simple discord bot for the Limited Edition guild in the game Aion Classic that interfaces with a PostgreSQL database
|
||||||
|
|
||||||
The bot is made to run on a single server and will thus share the same database
|
The bot is made to run on a single server and will thus share the same database
|
||||||
|
|
||||||
|
|||||||
49
src/database/eventdb.js
Normal file
49
src/database/eventdb.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
const { client } = require('./database')
|
||||||
|
|
||||||
|
const ReadEvents = async () => {
|
||||||
|
try {
|
||||||
|
const res = await client.query(`
|
||||||
|
SELECT * FROM event_times;`
|
||||||
|
)
|
||||||
|
return res.rows
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading event entries:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetEventRole = async () => {
|
||||||
|
try {
|
||||||
|
const res = await client.query(`
|
||||||
|
SELECT * FROM event_role_view;`
|
||||||
|
)
|
||||||
|
const rolesEventMap = new Map()
|
||||||
|
res.rows.forEach(row => rolesEventMap.set(row.event_name, row.role))
|
||||||
|
|
||||||
|
return rolesEventMap
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetIconRole = async () => {
|
||||||
|
try {
|
||||||
|
const res = await client.query(`
|
||||||
|
SELECT role, icon_name FROM event_roles;`
|
||||||
|
)
|
||||||
|
const rolesEventMap = new Map()
|
||||||
|
res.rows.forEach(row => rolesEventMap.set(row.icon_name, row.role))
|
||||||
|
|
||||||
|
return rolesEventMap
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ReadEvents,
|
||||||
|
GetEventRole,
|
||||||
|
GetIconRole,
|
||||||
|
}
|
||||||
59
src/database/staticdb.js
Normal file
59
src/database/staticdb.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
const { client } = require('./database')
|
||||||
|
|
||||||
|
const CreateStatic = async (name, creator, members, size) => {
|
||||||
|
try {
|
||||||
|
await client.query(`
|
||||||
|
INSERT INTO static (name, creator, size)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
RETURNING id;`,
|
||||||
|
[name, creator, size]
|
||||||
|
)
|
||||||
|
|
||||||
|
const staticId = result.rows[0].id;
|
||||||
|
|
||||||
|
const memberValues = members.map(member => `(${staticId}, '${member}')`).join(',')
|
||||||
|
|
||||||
|
await client.query(`
|
||||||
|
INSERT INTO static_members (static_id, member)
|
||||||
|
VALUES ${memberValues};`
|
||||||
|
)
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating static entry:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReadStatic = async (name) => {
|
||||||
|
try {
|
||||||
|
const res = await client.query(`
|
||||||
|
SELECT * FROM static
|
||||||
|
WHERE name = $1;`,
|
||||||
|
[name]
|
||||||
|
)
|
||||||
|
return res.rows
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading static entry:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeleteStatic = async (name) => {
|
||||||
|
try {
|
||||||
|
await client.query(`
|
||||||
|
DELETE FROM static
|
||||||
|
WHERE name = $1;`,
|
||||||
|
[name]
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting static entry:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CreateStatic,
|
||||||
|
ReadStatic,
|
||||||
|
DeleteStatic,
|
||||||
|
}
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
const { Client, GatewayIntentBits, EmbedBuilder, Partials, Routes } = require('discord.js')
|
const { Client, GatewayIntentBits, EmbedBuilder, Partials, Routes } = require('discord.js')
|
||||||
const { REST } = require('@discordjs/rest')
|
const { REST } = require('@discordjs/rest')
|
||||||
const { SlashCommandBuilder } = require('@discordjs/builders')
|
const { SlashCommandBuilder } = require('@discordjs/builders')
|
||||||
|
const {
|
||||||
|
initReactionPerRole,
|
||||||
|
messageReactionAdd,
|
||||||
|
messageReactionRemove,
|
||||||
|
} = require('../features/reactionPerRole')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handleBlacklistAdd,
|
handleBlacklistAdd,
|
||||||
handleBlacklistCheck,
|
handleBlacklistCheck,
|
||||||
handleBlacklistShow,
|
handleBlacklistShow,
|
||||||
} = require('../features/blacklist')
|
} = require('../features/blacklist')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handleBirthdayAdd,
|
handleBirthdayAdd,
|
||||||
handleBirthdayUpdate,
|
handleBirthdayUpdate,
|
||||||
@@ -15,172 +21,97 @@ const {
|
|||||||
} = require('../features/birthday')
|
} = require('../features/birthday')
|
||||||
|
|
||||||
const { startBirthdayCheckCron } = require('../tasks/checkBirthday')
|
const { startBirthdayCheckCron } = require('../tasks/checkBirthday')
|
||||||
|
const { startEventCheckCron } = require('../tasks/eventReminder')
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
intents: [
|
intents: [
|
||||||
GatewayIntentBits.Guilds,
|
GatewayIntentBits.Guilds,
|
||||||
GatewayIntentBits.GuildMessages,
|
GatewayIntentBits.GuildMessages,
|
||||||
GatewayIntentBits.GuildMembers,
|
GatewayIntentBits.GuildMembers,
|
||||||
|
GatewayIntentBits.MessageContent,
|
||||||
|
GatewayIntentBits.GuildMessageReactions,
|
||||||
],
|
],
|
||||||
partials: [
|
partials: [
|
||||||
Partials.Channel,
|
Partials.Channel,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
const createBlacklistEmbeds = (entries) => {
|
const createBlacklistEmbeds = (playerEntries, maxChars = 30) => {
|
||||||
const embeds = [];
|
const embeds = []
|
||||||
let currentEmbed = new EmbedBuilder()
|
let embed = new EmbedBuilder()
|
||||||
.setTitle('Blacklisted Players')
|
.setColor('#0099ff')
|
||||||
.setColor('#EF9A9A');
|
.setTitle('Blacklist (Page 1)')
|
||||||
|
.setDescription('Players who have been blacklisted and the reasons.')
|
||||||
|
|
||||||
let currentEntries = [];
|
let fieldCount = 0
|
||||||
let first = true;
|
let pageIndex = 1
|
||||||
|
|
||||||
const breakTextIntoLines = (text, maxLength) => {
|
playerEntries.forEach(([playerName, reason]) => {
|
||||||
const words = text.split(' ');
|
let splitReason = []
|
||||||
const lines = [];
|
while (reason.length > maxChars) {
|
||||||
let currentLine = '';
|
let splitIndex = reason.lastIndexOf(' ', maxChars)
|
||||||
|
if (splitIndex === -1) splitIndex = maxChars
|
||||||
|
splitReason.push(reason.substring(0, splitIndex))
|
||||||
|
reason = reason.substring(splitIndex + 1)
|
||||||
|
}
|
||||||
|
splitReason.push(reason)
|
||||||
|
|
||||||
for (let i = 0; i < words.length; i++) {
|
const nameField = playerName || '\u200B'
|
||||||
if (words[i].length > maxLength) {
|
const valueField = splitReason.join('\n').trim() || '\u200B'
|
||||||
const splitWord = words[i].match(new RegExp(`.{1,${maxLength}}`, 'g')) || [];
|
|
||||||
for (let j = 0; j < splitWord.length; j++) {
|
if (valueField !== '\u200B') {
|
||||||
if (j === 0 && currentLine) {
|
embed.addFields({ name: nameField, value: valueField, inline: true })
|
||||||
lines.push(currentLine.trim());
|
fieldCount++
|
||||||
currentLine = '';
|
|
||||||
}
|
|
||||||
lines.push(splitWord[j]);
|
|
||||||
}
|
|
||||||
} else if ((currentLine + words[i]).length <= maxLength) {
|
|
||||||
currentLine += words[i] + ' ';
|
|
||||||
} else {
|
|
||||||
lines.push(currentLine.trim());
|
|
||||||
currentLine = words[i] + ' ';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLine) {
|
if (fieldCount >= 25) {
|
||||||
lines.push(currentLine.trim());
|
embeds.push(embed)
|
||||||
|
pageIndex++
|
||||||
|
embed = new EmbedBuilder()
|
||||||
|
.setColor('#0099ff')
|
||||||
|
.setTitle(`Blacklist (Page ${pageIndex})`)
|
||||||
|
.setDescription('Players who have been blacklisted and the reasons.')
|
||||||
|
fieldCount = 0
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return lines;
|
if (fieldCount > 0) {
|
||||||
};
|
embeds.push(embed)
|
||||||
|
|
||||||
const addEntryToEmbed = (entriesGroup) => {
|
|
||||||
let playerNames = '';
|
|
||||||
let reasons = '';
|
|
||||||
let reportedBys = '';
|
|
||||||
|
|
||||||
for (let i = 0; i < entriesGroup.length; i++) {
|
|
||||||
const entry = entriesGroup[i];
|
|
||||||
const name = entry.name || 'Unknown';
|
|
||||||
const reason = entry.reason || 'No reason provided';
|
|
||||||
|
|
||||||
const nameLines = breakTextIntoLines(name, 20);
|
|
||||||
const reasonLines = breakTextIntoLines(reason, 30);
|
|
||||||
|
|
||||||
const maxLines = Math.max(nameLines.length, reasonLines.length);
|
|
||||||
|
|
||||||
for (let j = 0; j < maxLines; j++) {
|
|
||||||
playerNames += (j < nameLines.length ? nameLines[j] : '\u200B') + '\n';
|
|
||||||
reasons += (j < reasonLines.length ? reasonLines[j] : '\u200B') + '\n';
|
|
||||||
reportedBys += (j === 0 ? entry.reportedby || 'Unknown' : '\u200B') + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let newFields = [];
|
|
||||||
if (first) {
|
|
||||||
newFields = [
|
|
||||||
{ name: 'Player', value: playerNames, inline: true },
|
|
||||||
{ name: 'Reason', value: reasons, inline: true },
|
|
||||||
{ name: 'Reported By', value: reportedBys, inline: true }
|
|
||||||
];
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
newFields = [
|
|
||||||
{ name: '\u200B', value: playerNames, inline: true },
|
|
||||||
{ name: '\u200B', value: reasons, inline: true },
|
|
||||||
{ name: '\u200B', value: reportedBys, inline: true }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const field of newFields) {
|
|
||||||
currentEmbed.addFields(field);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = 0; i < entries.length; i++) {
|
|
||||||
const entry = entries[i];
|
|
||||||
|
|
||||||
const tempEntries = [...currentEntries, entry];
|
|
||||||
let tempPlayerNames = '';
|
|
||||||
let tempReasons = '';
|
|
||||||
let tempReportedBys = '';
|
|
||||||
|
|
||||||
for (let j = 0; j < tempEntries.length; j++) {
|
|
||||||
const name = tempEntries[j].name || 'Unknown';
|
|
||||||
const reason = tempEntries[j].reason || 'No reason provided';
|
|
||||||
const nameLines = breakTextIntoLines(name, 20);
|
|
||||||
const reasonLines = breakTextIntoLines(reason, 30);
|
|
||||||
|
|
||||||
const maxLines = Math.max(nameLines.length, reasonLines.length);
|
|
||||||
|
|
||||||
for (let k = 0; k < maxLines; k++) {
|
|
||||||
tempPlayerNames += (k < nameLines.length ? nameLines[k] : '\u200B') + '\n';
|
|
||||||
tempReasons += (k < reasonLines.length ? reasonLines[k] : '\u200B') + '\n';
|
|
||||||
tempReportedBys += (k === 0 ? tempEntries[j].reportedby || 'Unknown' : '\u200B') + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const exceedsLimit = (str) => str.length > 1024;
|
|
||||||
|
|
||||||
if (exceedsLimit(tempPlayerNames) || exceedsLimit(tempReasons) || exceedsLimit(tempReportedBys)) {
|
|
||||||
if (currentEntries.length > 0) {
|
|
||||||
addEntryToEmbed(currentEntries);
|
|
||||||
currentEntries = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentEntries.push(entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentEntries.length > 0) {
|
return embeds
|
||||||
addEntryToEmbed(currentEntries);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
embeds.push(currentEmbed);
|
|
||||||
|
|
||||||
return embeds;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const updateGlobalMessage = async () => {
|
const updateGlobalMessage = async () => {
|
||||||
try {
|
try {
|
||||||
let targetChannel = null;
|
let targetChannel = null
|
||||||
|
|
||||||
for (const [_, oauthGuild] of await client.guilds.fetch()) {
|
for (const [_, oauthGuild] of await client.guilds.fetch()) {
|
||||||
targetChannel = (await (await oauthGuild.fetch()).channels.fetch()).find(ch => ch.name === 'blacklist')
|
targetChannel = (await (await oauthGuild.fetch()).channels.fetch()).find(ch => ch.name === 'blacklist')
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetChannel) {
|
if (!targetChannel) {
|
||||||
console.error('Channel with name "blacklist" not found.');
|
console.error('Channel with name "blacklist" not found.')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = await targetChannel.messages.fetch({ limit: 100 });
|
const messages = await targetChannel.messages.fetch({ limit: 100 })
|
||||||
await Promise.all(messages.map(msg => msg.delete()));
|
await Promise.all(messages.map(msg => msg.delete()))
|
||||||
|
|
||||||
const blacklistEntries = await handleBlacklistShow();
|
const blacklistEntries = await handleBlacklistShow()
|
||||||
const embeds = createBlacklistEmbeds(blacklistEntries);
|
|
||||||
|
const playerEntries = blacklistEntries.map(entry => [entry.name, entry.reason])
|
||||||
|
|
||||||
|
const embeds = createBlacklistEmbeds(playerEntries)
|
||||||
|
|
||||||
for (const embed of embeds)
|
for (const embed of embeds)
|
||||||
await targetChannel.send({ embeds: [embed] });
|
await targetChannel.send({ embeds: [embed] })
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating global message:', error);
|
console.error('Error updating global message:', error)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
client.on('interactionCreate', async interaction => {
|
client.on('interactionCreate', async interaction => {
|
||||||
if (!interaction.isCommand()) return
|
if (!interaction.isCommand()) return
|
||||||
@@ -208,7 +139,7 @@ client.on('interactionCreate', async interaction => {
|
|||||||
|
|
||||||
const reason2 = await handleBlacklistCheck(player)
|
const reason2 = await handleBlacklistCheck(player)
|
||||||
reason2 ?
|
reason2 ?
|
||||||
await interaction.reply({ content: `** ${reason2.name}** is blacklisted for: ** ${reason2.reason || 'No reason provided.'}** (by ${reason2.reportedby || 'unknown'}).`, ephemeral: true }) :
|
await interaction.reply({ content: `** ${reason2.name}** is blacklisted for: ** ${reason2.reason || 'No reason provided.'}**`, ephemeral: true }) :
|
||||||
await interaction.reply({ content: `** ${player}** is not blacklisted.`, ephemeral: true })
|
await interaction.reply({ content: `** ${player}** is not blacklisted.`, ephemeral: true })
|
||||||
|
|
||||||
break
|
break
|
||||||
@@ -257,17 +188,38 @@ client.on('interactionCreate', async interaction => {
|
|||||||
await interaction.reply({ content: "Your birthday has been deleted.", ephemeral: true }) :
|
await interaction.reply({ content: "Your birthday has been deleted.", ephemeral: true }) :
|
||||||
await interaction.reply({ content: "You don't have a birthday set.", ephemeral: true })
|
await interaction.reply({ content: "You don't have a birthday set.", ephemeral: true })
|
||||||
break
|
break
|
||||||
|
case 'static-create':
|
||||||
|
|
||||||
|
break
|
||||||
|
case 'static-delete':
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'static-show':
|
||||||
|
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
client.on('messageReactionAdd', async (reaction, user) => {
|
||||||
|
messageReactionAdd(user, reaction)
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on('messageReactionRemove', async (reaction, user) => {
|
||||||
|
messageReactionRemove(user, reaction)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN)
|
const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN)
|
||||||
|
|
||||||
client.once('ready', async () => {
|
client.once('ready', async () => {
|
||||||
console.log(`Logged in as ${client.user.tag} `)
|
console.log(`Logged in as ${client.user.tag} `)
|
||||||
startBirthdayCheckCron(client)
|
startBirthdayCheckCron(client)
|
||||||
|
startEventCheckCron(client)
|
||||||
updateGlobalMessage()
|
updateGlobalMessage()
|
||||||
|
initReactionPerRole(client)
|
||||||
})
|
})
|
||||||
|
|
||||||
const connectDiscord = async () => {
|
const connectDiscord = async () => {
|
||||||
|
|||||||
206
src/features/reactionPerRole.js
Normal file
206
src/features/reactionPerRole.js
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
const { GetIconRole } = require('../database/eventdb')
|
||||||
|
const cron = require('node-cron');
|
||||||
|
|
||||||
|
let reminderMessageID = null
|
||||||
|
let rolesMap = new Map()
|
||||||
|
|
||||||
|
const areMapsEqual = (map1, map2) => {
|
||||||
|
if (map1.size !== map2.size) return false
|
||||||
|
|
||||||
|
for (const [key, value] of map1)
|
||||||
|
if (map2.get(key) !== value) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const editReactionMessage = async (client) => {
|
||||||
|
try {
|
||||||
|
for (const [_, oauthGuild] of await client.guilds.fetch()) {
|
||||||
|
const guild = await oauthGuild.fetch()
|
||||||
|
const rolesChannel = (await guild.channels.fetch()).find(ch => ch.name === 'roles')
|
||||||
|
|
||||||
|
if (!rolesChannel) {
|
||||||
|
console.log("Channel not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const message = await rolesChannel.messages.fetch(reminderMessageID)
|
||||||
|
|
||||||
|
const newRolesFromDB = await GetIconRole()
|
||||||
|
|
||||||
|
if (areMapsEqual(rolesMap, newRolesFromDB)) {
|
||||||
|
console.log(`No changes to the roles, quitting`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rolesMap = newRolesFromDB
|
||||||
|
|
||||||
|
const currentLines = message.content.split('\n').slice(1)
|
||||||
|
|
||||||
|
const currentRoleMap = new Map()
|
||||||
|
currentLines.forEach(line => {
|
||||||
|
if (!line.trim()) return
|
||||||
|
const [icon, roleName] = line.split(': ')
|
||||||
|
const iconName = icon.match(/<:(\w+):\d+>/)[1]
|
||||||
|
currentRoleMap.set(iconName, roleName.replace(/`/g, ''))
|
||||||
|
})
|
||||||
|
|
||||||
|
const rolesToAdd = [], rolesToRemove = []
|
||||||
|
|
||||||
|
for (const [iconName, roleName] of rolesMap)
|
||||||
|
if (!currentRoleMap.has(iconName))
|
||||||
|
rolesToAdd.push({ icon: iconName, name: roleName })
|
||||||
|
|
||||||
|
for (const [iconName, roleName] of currentRoleMap)
|
||||||
|
if (!rolesMap.has(iconName))
|
||||||
|
rolesToRemove.push({ icon: iconName, name: roleName })
|
||||||
|
|
||||||
|
const emojis = await guild.emojis.fetch()
|
||||||
|
|
||||||
|
let updatedContent = 'React to get your roles!\n\n'
|
||||||
|
rolesMap.forEach((roleName, iconName) => {
|
||||||
|
const emoji = emojis.find(e => e.name === iconName)
|
||||||
|
if (emoji)
|
||||||
|
updatedContent += `${emoji}: \`${roleName}\`\n`
|
||||||
|
else
|
||||||
|
console.log(`Couldn't find emoji ${iconName}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await message.edit(updatedContent)
|
||||||
|
|
||||||
|
for (const role of rolesToAdd) {
|
||||||
|
const icon = emojis.find(e => e.name === role.icon)
|
||||||
|
if (icon)
|
||||||
|
await message.react(`${icon}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const role of rolesToRemove) {
|
||||||
|
const reaction = message.reactions.cache.find(r => r.emoji.name === role.icon)
|
||||||
|
if (!reaction) continue
|
||||||
|
|
||||||
|
await reaction.remove()
|
||||||
|
|
||||||
|
const roleToRemove = message.guild.roles.cache.find(r => r.name === role.name)
|
||||||
|
if (!roleToRemove) continue
|
||||||
|
|
||||||
|
const members = await message.guild.members.fetch();
|
||||||
|
const membersWithRole = members.filter(member => member.roles.cache.has(roleToRemove.id));
|
||||||
|
|
||||||
|
for (const member of membersWithRole.values())
|
||||||
|
await member.roles.remove(roleToRemove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createReactionMessage = async (client) => {
|
||||||
|
try {
|
||||||
|
for (const [_, oauthGuild] of await client.guilds.fetch()) {
|
||||||
|
const guild = await oauthGuild.fetch()
|
||||||
|
const rolesChannel = (await guild.channels.fetch()).find(ch => ch.name === 'roles')
|
||||||
|
|
||||||
|
if (!rolesChannel) {
|
||||||
|
console.log("Channel not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchedMessage = await rolesChannel.messages.fetch({ limit: 1 })
|
||||||
|
if (fetchedMessage.size > 0) {
|
||||||
|
reminderMessageID = fetchedMessage.first().id
|
||||||
|
console.log('Reminder MessageID: ', reminderMessageID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojis = await guild.emojis.fetch()
|
||||||
|
|
||||||
|
let message = 'React to get your roles!\n\n'
|
||||||
|
for (const [iconName, roleName] of rolesMap) {
|
||||||
|
const emoji = emojis.find(e => e.name === iconName)
|
||||||
|
if (emoji)
|
||||||
|
message += `<:${emoji.name}:${emoji.id}> : \`${roleName}\`\n`
|
||||||
|
else
|
||||||
|
console.log(`Emoji for ${iconName} not found`)
|
||||||
|
}
|
||||||
|
const sentMessage = await rolesChannel.send(message)
|
||||||
|
|
||||||
|
for (const [iconName, _] of rolesMap) {
|
||||||
|
const emoji = emojis.find(e => e.name === iconName)
|
||||||
|
if (emoji) await sentMessage.react(emoji.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
reminderMessageID = sentMessage.id
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageReactionAdd = async (user, reaction) => {
|
||||||
|
if (user.id === '1280557738530963506') return
|
||||||
|
|
||||||
|
const { message, emoji } = reaction
|
||||||
|
const guild = message.guild
|
||||||
|
const member = await guild.members.fetch(user.id)
|
||||||
|
if (!member) return
|
||||||
|
|
||||||
|
if (message.id === reminderMessageID) {
|
||||||
|
const roleMap = await GetIconRole()
|
||||||
|
|
||||||
|
const roleName = roleMap.get(emoji.name)
|
||||||
|
if (roleName) {
|
||||||
|
try {
|
||||||
|
const roles = await guild.roles.fetch()
|
||||||
|
const role = roles.find(r => r.name === roleName)
|
||||||
|
|
||||||
|
if (role && !member.roles.cache.has(role.id))
|
||||||
|
await member.roles.add(role)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching role for ${roleName}`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageReactionRemove = async (user, reaction) => {
|
||||||
|
const { message, emoji } = reaction
|
||||||
|
const guild = message.guild
|
||||||
|
|
||||||
|
const member = await guild.members.fetch(user.id)
|
||||||
|
if (!member) return
|
||||||
|
|
||||||
|
if (message.id === reminderMessageID) {
|
||||||
|
const roleMap = await GetIconRole()
|
||||||
|
|
||||||
|
const roleName = roleMap.get(emoji.name)
|
||||||
|
if (roleName) {
|
||||||
|
try {
|
||||||
|
const roles = await guild.roles.fetch()
|
||||||
|
const role = roles.find(r => r.name === roleName)
|
||||||
|
|
||||||
|
if (role && member.roles.cache.has(role.id))
|
||||||
|
await member.roles.remove(role)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching role for ${roleName}`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initReactionPerRole = async (client) => {
|
||||||
|
rolesMap = await GetIconRole()
|
||||||
|
|
||||||
|
await createReactionMessage(client)
|
||||||
|
|
||||||
|
cron.schedule('0 * * * *', async () => {
|
||||||
|
await editReactionMessage(client)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initReactionPerRole,
|
||||||
|
messageReactionAdd,
|
||||||
|
messageReactionRemove,
|
||||||
|
}
|
||||||
31
src/features/static.js
Normal file
31
src/features/static.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const { CreateStatic, ReadStatic, DeleteStatic } = require('../database/staticdb')
|
||||||
|
|
||||||
|
const handleStaticAdd = async (name, creator, members, size) => {
|
||||||
|
if (!name || !createor || !members || !size) return false
|
||||||
|
|
||||||
|
const result = await CreateStatic(name, creator, members, size)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStaticGet = async (name) => {
|
||||||
|
if (!name) return false;
|
||||||
|
|
||||||
|
const result = await ReadStatic(name)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStaticDelete = async (name) => {
|
||||||
|
if (!name) return false
|
||||||
|
|
||||||
|
const result = await DeleteStatic(name)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
handleStaticAdd,
|
||||||
|
handleStaticGet,
|
||||||
|
handleStaticDelete,
|
||||||
|
}
|
||||||
80
src/tasks/eventReminder.js
Normal file
80
src/tasks/eventReminder.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
const cron = require('node-cron');
|
||||||
|
const { ReadEvents, GetEventRole } = require('../database/eventdb')
|
||||||
|
const { EmbedBuilder } = require('discord.js')
|
||||||
|
|
||||||
|
let eventCache = []
|
||||||
|
|
||||||
|
const FetchEvents = async () => {
|
||||||
|
const res = await ReadEvents()
|
||||||
|
|
||||||
|
eventCache = res
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertToISO = (time, date) => {
|
||||||
|
const utcDate = new Date(`${date}T${time}`);
|
||||||
|
utcDate.setHours(utcDate.getHours());
|
||||||
|
return utcDate.toISOString().split('.')[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
const convertToUTC = (time) => {
|
||||||
|
const [hours, minutes, seconds] = time.split(':').map(Number);
|
||||||
|
const date = new Date();
|
||||||
|
date.setUTCHours(hours - 2, minutes, seconds);
|
||||||
|
return date.toISOString().split('.')[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
const isReminderTime = (eventStartUTC) => {
|
||||||
|
const now = new Date();
|
||||||
|
const [eventHours, eventMinutes] = eventStartUTC.split('T')[1].split(':').map(Number);
|
||||||
|
const eventStartDateTime = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), eventHours, eventMinutes));
|
||||||
|
const reminderTime = new Date(eventStartDateTime.getTime() - 15 * 60 * 1000);
|
||||||
|
console.log(now, reminderTime, eventStartDateTime)
|
||||||
|
return now >= reminderTime && now < eventStartDateTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDayOfWeek = () => new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), new Date().getUTCDate())).toLocaleDateString('en-US', { weekday: 'long' });
|
||||||
|
|
||||||
|
const startEventCheckCron = async (client) => {
|
||||||
|
cron.schedule('45 * * * *', async () => {
|
||||||
|
console.log('Checking for events...')
|
||||||
|
if (!eventCache.length) {
|
||||||
|
await FetchEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
const rolesEventMap = await GetEventRole()
|
||||||
|
|
||||||
|
for (const [guildId, oauthGuild] of await client.guilds.fetch()) {
|
||||||
|
const guild = await oauthGuild.fetch()
|
||||||
|
const channel = (await guild.channels.fetch()).find(ch => ch.name === 'reminder')
|
||||||
|
|
||||||
|
if (!channel) continue
|
||||||
|
|
||||||
|
eventCache.forEach(event => {
|
||||||
|
let { schedule_id, event_name, start_time, end_time, day_of_week } = event;
|
||||||
|
console.log(!((!day_of_week || day_of_week === getDayOfWeek()) && isReminderTime(convertToUTC(start_time))))
|
||||||
|
// Abbort if its not time to send a reminder
|
||||||
|
if (!((!day_of_week || day_of_week === getDayOfWeek()) && isReminderTime(convertToUTC(start_time)))) return;
|
||||||
|
|
||||||
|
const timeComparisonLink = `https://www.timeanddate.com/worldclock/fixedtime.html?iso=${convertToISO(start_time, new Date().toISOString().split('T')[0])}&msg=${encodeURIComponent(event_name)}`;
|
||||||
|
|
||||||
|
console.log("Sending event...")
|
||||||
|
console.log(rolesEventMap.get(event_name))
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(event_name)
|
||||||
|
.setDescription(`${event_name} starts at **${start_time} CEST** and ends at **${end_time} CEST**. \n<@${rolesEventMap.get(event_name)}>\n`)
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Day of Week', value: day_of_week ? day_of_week : 'Daily', inline: true },
|
||||||
|
{ name: 'Compare Time', value: `[Click here to compare event time to your local time](${timeComparisonLink})`, inline: true }
|
||||||
|
)
|
||||||
|
.setFooter({ text: `Schedule ID: ${schedule_id}` })
|
||||||
|
.setColor('#00FF00');
|
||||||
|
|
||||||
|
channel.send({ embeds: [embed] }).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
startEventCheckCron,
|
||||||
|
}
|
||||||
0
src/tasks/staticReminder.js
Normal file
0
src/tasks/staticReminder.js
Normal file
Reference in New Issue
Block a user