fix blacklist and add static create command, change node commands
This commit is contained in:
@@ -4,3 +4,5 @@ node_modules
|
|||||||
tests/
|
tests/
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.env.test
|
||||||
|
.env.example
|
||||||
|
|||||||
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
BOT_TOKEN=
|
||||||
|
CLIENT_ID=
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
BOT_TOKEN=
|
|
||||||
CLIENT_ID=
|
|
||||||
DB_NAME=
|
|
||||||
DB_PORT=
|
|
||||||
DB_HOST=
|
|
||||||
DB_USER=
|
|
||||||
DB_PASS=
|
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
.env
|
.env.prod
|
||||||
|
.env.test
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
src/database/config/
|
src/database/config/config.json
|
||||||
src/database/migrations/
|
src/database/migrations/
|
||||||
src/database/seeders/
|
src/database/seeders/
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ Its best to run the bot in a docker container
|
|||||||
- Blacklist a player with a given reason
|
- Blacklist a player with a given reason
|
||||||
- Updates a global message
|
- Updates a global message
|
||||||
- Can check against individual players
|
- Can check against individual players
|
||||||
|
- Event Reminder
|
||||||
|
- Reminds roles for ingame events 15min ahead of time
|
||||||
|
- Role assignment
|
||||||
|
- Assign your role by reacting to a message
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
@@ -32,5 +36,3 @@ Its best to run the bot in a docker container
|
|||||||
- Command that asks every user to confirm a set date and time for the static
|
- Command that asks every user to confirm a set date and time for the static
|
||||||
- Make it always repeat on the same time, or just once (have to re create every time)
|
- Make it always repeat on the same time, or just once (have to re create every time)
|
||||||
- Write easy to understand documentation and a HOWTO
|
- Write easy to understand documentation and a HOWTO
|
||||||
- Blacklist
|
|
||||||
- Limit command usage to admins, maybe go away from slash commands?
|
|
||||||
|
|||||||
@@ -14,17 +14,14 @@ export default [
|
|||||||
'indent': ['error', 2],
|
'indent': ['error', 2],
|
||||||
'linebreak-style': ['error', 'unix'],
|
'linebreak-style': ['error', 'unix'],
|
||||||
'comma-dangle': ['error', 'always-multiline'],
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
'no-unused-vars': ['error', {
|
|
||||||
vars: 'all',
|
|
||||||
args: 'after-used',
|
|
||||||
ignoreRestSiblings: true,
|
|
||||||
caughtErrors: 'none',
|
|
||||||
argsIgnorePattern: '_',
|
|
||||||
}],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['**/*.js'],
|
files: ['**/*.js'],
|
||||||
|
env:{
|
||||||
|
'node': true,
|
||||||
|
'commonjs': true,
|
||||||
|
},
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
sourceType: 'commonjs',
|
sourceType: 'commonjs',
|
||||||
ecmaVersion: 2021,
|
ecmaVersion: 2021,
|
||||||
@@ -52,7 +49,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: globals.browser,
|
globals: globals.node,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pluginJs.configs.recommended,
|
pluginJs.configs.recommended,
|
||||||
|
|||||||
@@ -2,13 +2,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"discord.js": "^14.15.3",
|
"discord.js": "^14.15.3",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
"dotenv-cli": "^7.4.2",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
"pg": "^8.12.0",
|
"pg": "^8.12.0",
|
||||||
"sequelize": "^6.37.3"
|
"sequelize": "^6.37.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest"
|
"test": "jest",
|
||||||
|
"dev": "NODE_ENV=development dotenv -e .env.test node src/index.js",
|
||||||
|
"prod": "NODE_ENV=production dotenv -e .env.prod node src/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.10.0",
|
"@eslint/js": "^9.10.0",
|
||||||
|
|||||||
23
src/database/config/config.example.json
Normal file
23
src/database/config/config.example.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"development": {
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"database": "",
|
||||||
|
"host": "",
|
||||||
|
"dialect": ""
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"database": "",
|
||||||
|
"host": "",
|
||||||
|
"dialect": ""
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"database": "",
|
||||||
|
"host": "",
|
||||||
|
"dialect": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const { Client, GatewayIntentBits, Partials, Routes } = require('discord.js')
|
const { Client, GatewayIntentBits, Partials, Routes, PermissionFlagsBits, PermissionsBitField, ChannelFlags, ChannelManager, ChannelFlagsBitField, ChannelType } = require('discord.js')
|
||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
const { REST } = require('@discordjs/rest')
|
const { REST } = require('@discordjs/rest')
|
||||||
const { SlashCommandBuilder } = require('@discordjs/builders')
|
const { SlashCommandBuilder } = require('@discordjs/builders')
|
||||||
@@ -22,11 +22,23 @@ const {
|
|||||||
handleBirthdayDelete,
|
handleBirthdayDelete,
|
||||||
} = require('../features/birthday')
|
} = require('../features/birthday')
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleStaticAdd,
|
||||||
|
handleStaticGet,
|
||||||
|
handleStaticDelete,
|
||||||
|
handleStaticUpdateName,
|
||||||
|
handleStaticUpdateUser,
|
||||||
|
handleStaticUpdateUsers,
|
||||||
|
handleStaticUpdateSize,
|
||||||
|
} = require('../features/static')
|
||||||
|
|
||||||
const { startBirthdayCheckCron } = require('../tasks/checkBirthday')
|
const { startBirthdayCheckCron } = require('../tasks/checkBirthday')
|
||||||
const { startEventCheckCron } = require('../tasks/eventReminder')
|
const { startEventCheckCron } = require('../tasks/eventReminder')
|
||||||
|
|
||||||
const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN)
|
const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN)
|
||||||
|
|
||||||
|
let server = null
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
intents: [
|
intents: [
|
||||||
GatewayIntentBits.Guilds,
|
GatewayIntentBits.Guilds,
|
||||||
@@ -44,7 +56,7 @@ client.on('interactionCreate', async interaction => {
|
|||||||
if (!interaction.isCommand()) return
|
if (!interaction.isCommand()) return
|
||||||
|
|
||||||
switch (interaction.commandName) {
|
switch (interaction.commandName) {
|
||||||
case 'blacklist':
|
case 'blacklist': {
|
||||||
const reportedUser = interaction.options.getString('player')
|
const reportedUser = interaction.options.getString('player')
|
||||||
const reason = interaction.options.getString('reason')
|
const reason = interaction.options.getString('reason')
|
||||||
const reportedByUser = interaction.user.username
|
const reportedByUser = interaction.user.username
|
||||||
@@ -55,13 +67,13 @@ client.on('interactionCreate', async interaction => {
|
|||||||
interaction.reply({ content: 'This user has already been reported', ephemeral: true })
|
interaction.reply({ content: 'This user has already been reported', ephemeral: true })
|
||||||
else {
|
else {
|
||||||
interaction.reply({ content: `Player ** ${reportedUser}** had been successfully reported for ${reason}`, ephemeral: true })
|
interaction.reply({ content: `Player ** ${reportedUser}** had been successfully reported for ${reason}`, ephemeral: true })
|
||||||
updateGlobalMessage(interaction)
|
updateGlobalMessage(client)
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
interaction.reply({ content: 'ERROR trying to add the player to the blacklist, please contact @Crylia', ephemeral: true })
|
interaction.reply({ content: 'ERROR trying to add the player to the blacklist, please contact @Crylia', ephemeral: true })
|
||||||
|
|
||||||
break
|
break
|
||||||
case 'blacklist-check-player':
|
} case 'blacklist-check-player': {
|
||||||
const player = interaction.options.getString('player')
|
const player = interaction.options.getString('player')
|
||||||
|
|
||||||
const reason2 = await handleBlacklistCheck(player)
|
const reason2 = await handleBlacklistCheck(player)
|
||||||
@@ -70,7 +82,7 @@ client.on('interactionCreate', async interaction => {
|
|||||||
await interaction.reply({ content: `** ${player}** is not blacklisted.`, ephemeral: true })
|
await interaction.reply({ content: `** ${player}** is not blacklisted.`, ephemeral: true })
|
||||||
|
|
||||||
break
|
break
|
||||||
case 'birthday':
|
} case 'birthday': {
|
||||||
const user = interaction.user.username
|
const user = interaction.user.username
|
||||||
const birthday = interaction.options.getString('birthday')
|
const birthday = interaction.options.getString('birthday')
|
||||||
|
|
||||||
@@ -104,30 +116,111 @@ client.on('interactionCreate', async interaction => {
|
|||||||
await interaction.reply({ content: `Set ${birthday} as your birthday.Everyone will be notified once the day arrives!`, ephemeral: true }) :
|
await interaction.reply({ content: `Set ${birthday} as your birthday.Everyone will be notified once the day arrives!`, ephemeral: true }) :
|
||||||
await interaction.reply({ content: 'Something went wrong when setting / updating your birthday, please contact @crylia', ephemeral: true })
|
await interaction.reply({ content: 'Something went wrong when setting / updating your birthday, please contact @crylia', ephemeral: true })
|
||||||
break
|
break
|
||||||
case 'birthday-check':
|
} case 'birthday-check': {
|
||||||
const birthdayCheck = await handleBirthdayCheck(interaction.user.username)
|
const birthdayCheck = await handleBirthdayCheck(interaction.user.username)
|
||||||
birthdayCheck ?
|
birthdayCheck ?
|
||||||
await interaction.reply({ content: `Your birthday is currently set to ${new Date(birthdayCheck[0].date).toLocaleDateString('de-DE')}.`, ephemeral: true }) :
|
await interaction.reply({ content: `Your birthday is currently set to ${new Date(birthdayCheck[0].date).toLocaleDateString('de-DE')}.`, ephemeral: true }) :
|
||||||
await interaction.reply({ content: 'You don\'t have a birthday set. Use the`birthday` command to set one.', ephemeral: true })
|
await interaction.reply({ content: 'You don\'t have a birthday set. Use the`birthday` command to set one.', ephemeral: true })
|
||||||
break
|
break
|
||||||
case 'birthday-delete':
|
} case 'birthday-delete': {
|
||||||
await handleBirthdayDelete(interaction.user.username) ?
|
await handleBirthdayDelete(interaction.user.username) ?
|
||||||
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':
|
} case 'static-create': {
|
||||||
|
const static_name = interaction.options.getString('name')
|
||||||
|
const static_size = interaction.options.getString('size')
|
||||||
|
let static_members = [interaction.user.username]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const static_role = await interaction.guild.roles.create({
|
||||||
|
name: static_name,
|
||||||
|
color: 'BLUE',
|
||||||
|
})
|
||||||
|
|
||||||
|
interaction.member.roles.add(static_role)
|
||||||
|
|
||||||
|
for (const username of (interaction.options.getString('members')).split(',').map(name => name.trim())) {
|
||||||
|
const member = interaction.guild.members.cache.find(member => member.user.username === username)
|
||||||
|
|
||||||
|
if (member) {
|
||||||
|
static_members.push(member)
|
||||||
|
member.roles.add(static_role)
|
||||||
|
} else
|
||||||
|
console.log(`WARNING: Creating static: ${static_name} member named ${username} not found`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let category = interaction.guild.channels.cache.find(channel => channel.name === 'Statics' && channel.type === ChannelType.GuildCategory)
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
console.log(`ERROR: Creating static, couldn't find category Statics, ABBORTING`)
|
||||||
|
|
||||||
|
interaction.guild.roles.remove(static_role)
|
||||||
|
}
|
||||||
|
|
||||||
|
const static_text_channel = await interaction.guild.channels.create({
|
||||||
|
name: static_name,
|
||||||
|
type: ChannelType.GuildText,
|
||||||
|
parent: category.id,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: interaction.guild.id, // @everyone role
|
||||||
|
deny: [PermissionsBitField.Flags.ViewChannel] // Deny view for everyone
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: static_role.id, // Allow view for the static role
|
||||||
|
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const static_voice_channel = await interaction.guild.channels.create({
|
||||||
|
name: static_name,
|
||||||
|
type: ChannelType.GuildVoice,
|
||||||
|
parent: category.id,
|
||||||
|
permissionOverwrites: [
|
||||||
|
{
|
||||||
|
id: interaction.guild.id, // @everyone role
|
||||||
|
deny: [PermissionsBitField.Flags.ViewChannel] // Deny view for everyone
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: static_role.id, // Allow view for the static role
|
||||||
|
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = handleStaticAdd(static_name, static_members[0], static_members, static_size, static_role, static_text_channel, static_voice_channel)
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
interaction.reply({
|
||||||
|
content: `Static ${static_name} created. Current members are ${static_members}.`,
|
||||||
|
ephemeral: true
|
||||||
|
})
|
||||||
|
} else
|
||||||
|
interaction.reply({
|
||||||
|
content: `Error creating static, please contact @Crylia for help`,
|
||||||
|
ephemeral: true
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating static or assigning roles:', error)
|
||||||
|
interaction.reply({
|
||||||
|
content: 'An error occurred while creating the static. Please try again or contact an admin.',
|
||||||
|
ephemeral: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case 'static-delete':
|
} case 'static-delete': {
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'static-show':
|
} case 'static-show': {
|
||||||
|
|
||||||
break
|
break
|
||||||
default:
|
} default: {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
client.on('messageReactionAdd', async (reaction, user) => {
|
client.on('messageReactionAdd', async (reaction, user) => {
|
||||||
@@ -166,6 +259,7 @@ const connectDiscord = async () => {
|
|||||||
.setName('birthday-delete')
|
.setName('birthday-delete')
|
||||||
.setDescription('Delete your birthday, nobody will know when your birthday arrives :('),
|
.setDescription('Delete your birthday, nobody will know when your birthday arrives :('),
|
||||||
new SlashCommandBuilder()
|
new SlashCommandBuilder()
|
||||||
|
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
|
||||||
.setName('blacklist')
|
.setName('blacklist')
|
||||||
.setDescription('Add a player to a blacklist with a reason')
|
.setDescription('Add a player to a blacklist with a reason')
|
||||||
.addStringOption(option =>
|
.addStringOption(option =>
|
||||||
@@ -186,6 +280,24 @@ const connectDiscord = async () => {
|
|||||||
.setDescription('The in-game name of the player')
|
.setDescription('The in-game name of the player')
|
||||||
.setRequired(true)
|
.setRequired(true)
|
||||||
),
|
),
|
||||||
|
new SlashCommandBuilder()
|
||||||
|
.setName('static-create')
|
||||||
|
.setDescription('Create a new static with a voice and text channel just for your members.')
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('name')
|
||||||
|
.setDescription('Name of the static, the voice and test channel will be named after this.')
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('size')
|
||||||
|
.setDescription('Number of members in the static.')
|
||||||
|
.setRequired(false)
|
||||||
|
)
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('members')
|
||||||
|
.setDescription('Optionally assign members here by a comma seperated list (user1,user2,user3...).')
|
||||||
|
.setRequired(false)
|
||||||
|
)
|
||||||
].map(command => command.toJSON())
|
].map(command => command.toJSON())
|
||||||
|
|
||||||
await rest.put(
|
await rest.put(
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ const createBlacklistEmbeds = (playerEntries, maxChars = 30) => {
|
|||||||
if (fieldCount > 0) {
|
if (fieldCount > 0) {
|
||||||
embeds.push(embed)
|
embeds.push(embed)
|
||||||
}
|
}
|
||||||
|
|
||||||
return embeds
|
return embeds
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,15 +78,17 @@ const updateGlobalMessage = async (client) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = await targetChannel.messages.fetch({ limit: 100 })
|
const embeds = createBlacklistEmbeds(
|
||||||
|
(await handleBlacklistShow()).map(
|
||||||
|
entry => [entry.name, entry.reason]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// The message count will increase/decrese with a maximum of 1, so pulling the new page
|
||||||
|
// count + 1 will guarantee that all previous embeds will be deleted
|
||||||
|
const messages = await targetChannel.messages.fetch({ limit: embeds.length + 1 })
|
||||||
await Promise.all(messages.map(msg => msg.delete()))
|
await Promise.all(messages.map(msg => msg.delete()))
|
||||||
|
|
||||||
const blacklistEntries = await handleBlacklistShow()
|
|
||||||
|
|
||||||
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] })
|
||||||
|
|
||||||
@@ -96,7 +97,6 @@ const updateGlobalMessage = async (client) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
handleBlacklistAdd,
|
handleBlacklistAdd,
|
||||||
handleBlacklistCheck,
|
handleBlacklistCheck,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const { CreateStatic, ReadStatic, DeleteStatic } = require('../database/staticdb')
|
const { CreateStatic, ReadStatic, DeleteStatic } = require('../database/staticdb')
|
||||||
|
|
||||||
const handleStaticAdd = async (name, creator, members, size) => {
|
const handleStaticAdd = async (name, creator, members, size) => {
|
||||||
if (!name || !createor || !members || !size) return false
|
if (!name || !creator || !members || !size) return false
|
||||||
|
|
||||||
const result = await CreateStatic(name, creator, members, size)
|
const result = await CreateStatic(name, creator, members, size)
|
||||||
|
|
||||||
@@ -24,8 +24,25 @@ const handleStaticDelete = async (name) => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleStaticUpdateName = async (newName) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
const handleStaticUpdateUser = async (user, action) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
const handleStaticUpdateUsers = async (users, action) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
const handleStaticUpdateSize = async (newSize) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
handleStaticAdd,
|
handleStaticAdd,
|
||||||
handleStaticGet,
|
handleStaticGet,
|
||||||
handleStaticDelete,
|
handleStaticDelete,
|
||||||
|
handleStaticUpdateName,
|
||||||
|
handleStaticUpdateUser,
|
||||||
|
handleStaticUpdateUsers,
|
||||||
|
handleStaticUpdateSize
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
const { Sequelize } = require('sequelize')
|
|
||||||
const { connectDiscord } = require('./discord/discordClient')
|
const { connectDiscord } = require('./discord/discordClient')
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
connectDiscord()
|
connectDiscord()
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing application:', error)
|
console.error('Error initializing application:', error)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user