From 9fd47761b0ee9dcf7d68e7dc57d22098f8611bee Mon Sep 17 00:00:00 2001 From: Rene Kievits Date: Sat, 14 Sep 2024 02:20:30 +0200 Subject: [PATCH] migrate from pg to sequelize --- .gitignore | 3 + eslint.config.mjs | 59 +++++ package.json | 7 + src/database/birthdaydb.js | 179 ++++++++------- src/database/blacklistdb.js | 156 ++++++------- src/database/database.js | 32 --- src/database/eventdb.js | 40 ++-- src/database/models/birthday.js | 37 ++++ src/database/models/blacklist.js | 36 +++ src/database/models/discorduser.js | 24 ++ src/database/models/event.js | 37 ++++ src/database/models/event_roles.js | 41 ++++ src/database/models/event_schedule.js | 45 ++++ src/database/models/index.js | 43 ++++ src/database/models/init-models.js | 49 +++++ src/database/models/static.js | 49 +++++ src/database/models/static_members.js | 38 ++++ src/database/staticdb.js | 108 +++++---- src/discord/discordClient.js | 304 +++++++++++++------------- src/features/birthday.js | 34 +-- src/features/blacklist.js | 132 +++++------ src/features/reactionPerRole.js | 286 ++++++++++++------------ src/features/static.js | 24 +- src/index.js | 19 +- src/tasks/checkBirthday.js | 68 +++--- src/tasks/eventReminder.js | 100 ++++----- 26 files changed, 1205 insertions(+), 745 deletions(-) create mode 100644 eslint.config.mjs delete mode 100644 src/database/database.js create mode 100644 src/database/models/birthday.js create mode 100644 src/database/models/blacklist.js create mode 100644 src/database/models/discorduser.js create mode 100644 src/database/models/event.js create mode 100644 src/database/models/event_roles.js create mode 100644 src/database/models/event_schedule.js create mode 100644 src/database/models/index.js create mode 100644 src/database/models/init-models.js create mode 100644 src/database/models/static.js create mode 100644 src/database/models/static_members.js diff --git a/.gitignore b/.gitignore index 28a76fd..2a57f54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .env node_modules/ package-lock.json +src/database/config/ +src/database/migrations/ +src/database/seeders/ diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..d3d9d61 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,59 @@ +import globals from 'globals' +import pluginJs from '@eslint/js' + +export default [ + { + files: ['eslint.config.mjs'], + languageOptions: { + sourceType: 'module', + ecmaVersion: 2021, + }, + rules: { + 'semi': ['error', 'never'], + 'quotes': ['error', 'single'], + 'indent': ['error', 2], + 'linebreak-style': ['error', 'unix'], + 'comma-dangle': ['error', 'always-multiline'], + 'no-unused-vars': ['error', { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: true, + caughtErrors: 'none', + argsIgnorePattern: '_', + }], + }, + }, + { + files: ['**/*.js'], + languageOptions: { + sourceType: 'commonjs', + ecmaVersion: 2021, + }, + rules: { + 'semi': ['error', 'never'], + 'quotes': ['error', 'single'], + 'indent': ['error', 2], + 'linebreak-style': ['error', 'unix'], + 'comma-dangle': ['error', { + arrays: 'always-multiline', + objects: 'always-multiline', + imports: 'always-multiline', + exports: 'always-multiline', + functions: 'never', + }], + 'no-unused-vars': ['error', { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: true, + caughtErrors: 'none', + argsIgnorePattern: '_', + }], + }, + }, + { + languageOptions: { + globals: globals.browser, + }, + }, + pluginJs.configs.recommended, +] diff --git a/package.json b/package.json index 2bfdcac..9c5ac1c 100644 --- a/package.json +++ b/package.json @@ -9,5 +9,12 @@ }, "scripts": { "test": "jest" + }, + "devDependencies": { + "@eslint/js": "^9.10.0", + "eslint": "^9.10.0", + "globals": "^15.9.0", + "sequelize-auto": "^0.8.8", + "sequelize-cli": "^6.6.2" } } diff --git a/src/database/birthdaydb.js b/src/database/birthdaydb.js index 9abb1cc..431bb67 100644 --- a/src/database/birthdaydb.js +++ b/src/database/birthdaydb.js @@ -1,100 +1,111 @@ -const { getClient } = require('./database') +const { + birthday: Birthday, + discorduser: DiscordUser, +} = require('./models') -const CreateBirthday = async (user, birthday) => { - try { - const client = getClient() - const userResult = await client.query(` - SELECT * FROM discorduser - WHERE name = $1;`, - [user] - ) +const CreateBirthday = async (userName, birthdayDate) => { + try { + const [user, _] = await DiscordUser.findOrCreate({ + where: { name: userName }, + defaults: { name: userName }, + }) - if (userResult.rows.length === 0) { - await client.query(` - INSERT INTO discorduser (name) - VALUES ($1);`, - [user] - ) - } + const formattedBirthday = new Date( + birthdayDate.split('.').reverse().join('-') + ).toISOString().slice(0, 10) - const res = await client.query(` - INSERT INTO birthday (date, discorduser) - VALUES ($1, $2);`, - [new Date(birthday.split('.').reverse().join('-')).toISOString().slice(0, 10), user] - ) + const birthday = await Birthday.create({ + date: formattedBirthday, + discorduser: user.name, + }) - return res.rowCount > 0 - } catch (error) { - console.error('Error creating birthday entry:', error) - return false - } + return birthday !== null + } catch (error) { + console.error('Error creating birthday entry:', error) + return false + } } -const ReadBirthday = async (user) => { - try { - const client = getClient() - - if (user) { - const res = await client.query(` - SELECT * FROM birthday - WHERE discorduser = $1;`, - [user] - ) - return res.rows - } else { - const res = await client.query(` - SELECT * FROM birthday`, - ) - return res.rows - } - } catch (error) { - console.error('Error reading birthday table:', error) - return [] - } +const ReadBirthday = async (userName) => { + try { + if (userName) { + const user = await DiscordUser.findOne({ + where: { + name: userName, + }, + }) + if (user) { + const birthdays = await Birthday.findAll({ + where: { + discorduser: user.name, + }, + }) + return birthdays + } else return [] + } else { + const birthdays = await Birthday.findAll() + return birthdays + } + } catch (error) { + console.error('Error reading birthday table:', error) + return [] + } } -const UpdateBirthday = async (user, birthday) => { - try { - const client = getClient() +const UpdateBirthday = async (userName, birthdayDate) => { + try { + const user = await DiscordUser.findOne({ + where: { + name: userName, + }, + }) - if (!birthday) return false - - await client.query(` - UPDATE birthday - SET birthday = $2 - WHERE discorduser = $1;`, - [user, birthday] - ) - - return true - } catch (error) { - console.error('Error updating birthday table:', error) - return false - } + if (user) { + const [updated] = await Birthday.update( + { + date: new Date( + birthdayDate.split('.').reverse().join('-') + ).toISOString().slice(0, 10), + }, + { + where: { + discorduser: user.name, + }, + } + ) + return updated > 0 + } else return false + } catch (error) { + console.error('Error updating birthday table:', error) + return false + } } -const DeleteBirthday = async (user) => { - try { - const client = getClient() +const DeleteBirthday = async (userName) => { + try { + const user = await DiscordUser.findOne({ + where: { + name: userName, + }, + }) - if (!user) return false - - await client.query(` - DELETE FROM birthday - WHERE discorduser = $1;`, - [user] - ) - - return true - } catch (error) { - console.error('Error deleting birthday table:', error) - return false - } + if (user) { + const deleted = await Birthday.destroy({ + where: { + discorduser: user.name, + }, + }) + return deleted > 0 + } else return false + } catch (error) { + console.error('Error deleting birthday table:', error) + return false + } } module.exports = { - CreateBirthday, - ReadBirthday, - UpdateBirthday, - DeleteBirthday, + CreateBirthday, + ReadBirthday, + UpdateBirthday, + DeleteBirthday, } diff --git a/src/database/blacklistdb.js b/src/database/blacklistdb.js index 1a886c2..bfceb7b 100644 --- a/src/database/blacklistdb.js +++ b/src/database/blacklistdb.js @@ -1,99 +1,99 @@ -const { getClient } = require('./database') +const { + blacklist: Blacklist, + discorduser: DiscordUser, +} = require('./models') const CreateBlacklist = async (reportedUser, reason, reportedByUser) => { - try { - const client = getClient() + try { + const existingBlacklist = await Blacklist.findOne({ + where: { + name: reportedUser, + }, + }) - const alreadyReportedResult = await client.query( - "SELECT name FROM blacklist WHERE name = $1", - [reportedUser] - ) + if (existingBlacklist) return existingBlacklist - if (alreadyReportedResult.rows.length === 1) - return alreadyReportedResult.rows[0] + const [reportingUser] = await DiscordUser.findOrCreate({ + where: { + name: reportedByUser, + }, + defaults: { + name: reportedByUser, + }, + }) - const userResult = await client.query( - "SELECT * FROM discorduser WHERE name = $1", - [reportedByUser] - ) + const blacklistEntry = await Blacklist.create({ + name: reportedUser, + reason, + reportedby: reportingUser.name, + }) - if (userResult.rows.length === 0) - await client.query( - "INSERT INTO discorduser (name) VALUES ($1)", - [reportedByUser] - ) - - await client.query( - "INSERT INTO blacklist (name, reason, reportedby) VALUES ($1, $2, $3)", - [reportedUser, reason, reportedByUser] - ) - - return true - } catch (error) { - console.error('Error creating blacklist entry:', error) - return false - } + return blacklistEntry !== null + } catch (error) { + console.error('Error creating blacklist entry:', error) + return false + } } -const ReadBlacklist = async (user) => { - try { - const client = getClient() - - if (user) { - const res = await client.query( - "SELECT * FROM blacklist WHERE name=$1", - [user] - ) - return res.rows[0] || false - } else { - const res = await client.query( - "SELECT * FROM blacklist" - ) - return res.rows - } - } catch (error) { - console.error('Error reading blacklist table:', error) - return [] - } +const ReadBlacklist = async (userName) => { + try { + if (userName) { + const blacklistEntry = await Blacklist.findOne({ + where: { + name: userName, + }, + }) + return blacklistEntry || false + } else { + const allBlacklistEntries = await Blacklist.findAll() + return allBlacklistEntries + } + } catch (error) { + console.error('Error reading blacklist table:', error) + return [] + } } const UpdateBlacklist = async (reportedUser, reason) => { - try { - const client = getClient() + try { + if (!reportedUser) return false - if (!reportedUser) return false + const [updated] = await Blacklist.update( + { reason }, + { + where: { + name: reportedUser, + }, + } + ) - await client.query( - "UPDATE blacklist SET reason = $2 WHERE name = $1", - [reportedUser, reason] - ) - return true - } catch (error) { - console.error('Error updating blacklist table:', error) - return false - } + return updated > 0 + } catch (error) { + console.error('Error updating blacklist table:', error) + return false + } } -const DeleteBlacklist = async (user) => { - try { - const client = getClient() +const DeleteBlacklist = async (userName) => { + try { + if (!userName) return false - if (!user) return false + const deleted = await Blacklist.destroy({ + where: { + name: userName, + }, + }) - await client.query( - "DELETE FROM blacklist WHERE name = $1", - [user] - ) - return true - } catch (error) { - console.error('Error deleting blacklist table:', error) - return false - } + return deleted > 0 + } catch (error) { + console.error('Error deleting blacklist table:', error) + return false + } } module.exports = { - CreateBlacklist, - ReadBlacklist, - UpdateBlacklist, - DeleteBlacklist, + CreateBlacklist, + ReadBlacklist, + UpdateBlacklist, + DeleteBlacklist, } diff --git a/src/database/database.js b/src/database/database.js deleted file mode 100644 index 073237a..0000000 --- a/src/database/database.js +++ /dev/null @@ -1,32 +0,0 @@ -const { Client } = require('pg') - - -let client = null - -const createClient = (config) => { - client = new Client(config); - return client; -}; - -const connectDatabase = async (client) => { - try { - await client.connect() - console.log('Database connected') - } catch (error) { - console.error('Database connection error:', error) - throw new Error('Connection failed'); - } -} - -const getClient = () => { - if (!client) { - throw new Error('Client has not been initialized. Call createClient first.'); - } - return client; -}; - -module.exports = { - createClient, - connectDatabase, - getClient, -} diff --git a/src/database/eventdb.js b/src/database/eventdb.js index 80c1af0..06352df 100644 --- a/src/database/eventdb.js +++ b/src/database/eventdb.js @@ -1,13 +1,13 @@ -const { getClient } = require('./database') +const { + event_schedule: EventTimes, + event_role_view: EventRoleView, + event_roles: EventRoles, +} = require('./models') const ReadEvents = async () => { try { - const client = getClient() - - const res = await client.query( - "SELECT * FROM event_times" - ) - return res.rows + const events = await EventTimes.findAll() + return events } catch (error) { console.error('Error reading event entries:', error) return false @@ -16,34 +16,30 @@ const ReadEvents = async () => { const GetEventRole = async () => { try { - const client = getClient() - - const res = await client.query( - "SELECT * FROM event_role_view" - ) + const eventRoles = await EventRoleView.findAll() const rolesEventMap = new Map() - res.rows.forEach(row => rolesEventMap.set(row.event_name, row.role)) + + eventRoles.forEach(row => rolesEventMap.set(row.event_name, row.role)) return rolesEventMap } catch (error) { - console.error(error) + console.error('Error fetching event roles:', error) return false } } const GetIconRole = async () => { try { - const client = getClient() + const eventIcons = await EventRoles.findAll({ + attributes: ['role', 'icon_name'], + }) + const rolesIconMap = new Map() - 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)) + eventIcons.forEach(row => rolesIconMap.set(row.icon_name, row.role)) - return rolesEventMap + return rolesIconMap } catch (error) { - console.error(error) + console.error('Error fetching icon roles:', error) return false } } diff --git a/src/database/models/birthday.js b/src/database/models/birthday.js new file mode 100644 index 0000000..38192fb --- /dev/null +++ b/src/database/models/birthday.js @@ -0,0 +1,37 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('birthday', { + id: { + autoIncrement: true, + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + }, + date: { + type: DataTypes.DATEONLY, + allowNull: false, + }, + discorduser: { + type: DataTypes.STRING(100), + allowNull: false, + references: { + model: 'discorduser', + key: 'name', + }, + }, + }, { + sequelize, + tableName: 'birthday', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'birthday_pkey', + unique: true, + fields: [ + { name: 'id' }, + ], + }, + ], + }) +} diff --git a/src/database/models/blacklist.js b/src/database/models/blacklist.js new file mode 100644 index 0000000..7473bc3 --- /dev/null +++ b/src/database/models/blacklist.js @@ -0,0 +1,36 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('blacklist', { + name: { + type: DataTypes.STRING(100), + allowNull: false, + primaryKey: true, + }, + reason: { + type: DataTypes.TEXT, + allowNull: false, + }, + reportedby: { + type: DataTypes.STRING(100), + allowNull: true, + references: { + model: 'discorduser', + key: 'name', + }, + }, + }, { + sequelize, + tableName: 'blacklist', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'blacklist_pkey', + unique: true, + fields: [ + { name: 'name' }, + ], + }, + ], + }) +} diff --git a/src/database/models/discorduser.js b/src/database/models/discorduser.js new file mode 100644 index 0000000..e0901e9 --- /dev/null +++ b/src/database/models/discorduser.js @@ -0,0 +1,24 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('discorduser', { + name: { + type: DataTypes.STRING(100), + allowNull: false, + primaryKey: true, + }, + }, { + sequelize, + tableName: 'discorduser', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'discorduser_pkey', + unique: true, + fields: [ + { name: 'name' }, + ], + }, + ], + }) +} diff --git a/src/database/models/event.js b/src/database/models/event.js new file mode 100644 index 0000000..44fe59e --- /dev/null +++ b/src/database/models/event.js @@ -0,0 +1,37 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('event', { + id: { + autoIncrement: true, + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + }, + name: { + type: DataTypes.STRING(255), + allowNull: false, + unique: 'event_name_key', + }, + }, { + sequelize, + tableName: 'event', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'event_name_key', + unique: true, + fields: [ + { name: 'name' }, + ], + }, + { + name: 'event_pkey', + unique: true, + fields: [ + { name: 'id' }, + ], + }, + ], + }) +} diff --git a/src/database/models/event_roles.js b/src/database/models/event_roles.js new file mode 100644 index 0000000..89654b7 --- /dev/null +++ b/src/database/models/event_roles.js @@ -0,0 +1,41 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('event_roles', { + event_id: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'event', + key: 'id', + }, + }, + role: { + type: DataTypes.STRING(50), + allowNull: false, + }, + icon_name: { + type: DataTypes.STRING(50), + allowNull: true, + }, + role_id: { + autoIncrement: true, + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + }, + }, { + sequelize, + tableName: 'event_roles', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'event_roles_pkey', + unique: true, + fields: [ + { name: 'role_id' }, + ], + }, + ], + }) +} diff --git a/src/database/models/event_schedule.js b/src/database/models/event_schedule.js new file mode 100644 index 0000000..b394238 --- /dev/null +++ b/src/database/models/event_schedule.js @@ -0,0 +1,45 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('event_schedule', { + id: { + autoIncrement: true, + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + }, + event_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'event', + key: 'id', + }, + }, + day_of_week: { + type: DataTypes.STRING(9), + allowNull: true, + }, + start_time: { + type: DataTypes.TIME, + allowNull: true, + }, + end_time: { + type: DataTypes.TIME, + allowNull: true, + }, + }, { + sequelize, + tableName: 'event_schedule', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'event_schedule_pkey', + unique: true, + fields: [ + { name: 'id' }, + ], + }, + ], + }) +} diff --git a/src/database/models/index.js b/src/database/models/index.js new file mode 100644 index 0000000..bd9a3a5 --- /dev/null +++ b/src/database/models/index.js @@ -0,0 +1,43 @@ +'use strict' + +const fs = require('fs') +const path = require('path') +const Sequelize = require('sequelize') +const process = require('process') +const basename = path.basename(__filename) +const env = process.env.NODE_ENV || 'development' +const config = require(__dirname + '/../config/config.json')[env] +const db = {} + +let sequelize +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config) +} else { + sequelize = new Sequelize(config.database, config.username, config.password, config) +} + +fs + .readdirSync(__dirname) + .filter(file => { + return ( + file.indexOf('.') !== 0 && + file !== basename && + file.slice(-3) === '.js' && + file.indexOf('.test.js') === -1 + ) + }) + .forEach(file => { + const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes) + db[model.name] = model + }) + +Object.keys(db).forEach(modelName => { + if (db[modelName].associate) { + db[modelName].associate(db) + } +}) + +db.sequelize = sequelize +db.Sequelize = Sequelize + +module.exports = db diff --git a/src/database/models/init-models.js b/src/database/models/init-models.js new file mode 100644 index 0000000..144d7fe --- /dev/null +++ b/src/database/models/init-models.js @@ -0,0 +1,49 @@ +var DataTypes = require('sequelize').DataTypes +var _birthday = require('./birthday') +var _blacklist = require('./blacklist') +var _discorduser = require('./discorduser') +var _event = require('./event') +var _event_roles = require('./event_roles') +var _event_schedule = require('./event_schedule') +var _static = require('./static') +var _static_members = require('./static_members') + +function initModels(sequelize) { + var birthday = _birthday(sequelize, DataTypes) + var blacklist = _blacklist(sequelize, DataTypes) + var discorduser = _discorduser(sequelize, DataTypes) + var event = _event(sequelize, DataTypes) + var event_roles = _event_roles(sequelize, DataTypes) + var event_schedule = _event_schedule(sequelize, DataTypes) + var static = _static(sequelize, DataTypes) + var static_members = _static_members(sequelize, DataTypes) + + discorduser.belongsToMany(static, { as: 'static_id_statics', through: static_members, foreignKey: 'username', otherKey: 'static_id' }) + static.belongsToMany(discorduser, { as: 'username_discordusers', through: static_members, foreignKey: 'static_id', otherKey: 'username' }) + birthday.belongsTo(discorduser, { as: 'discorduser_discorduser', foreignKey: 'discorduser'}) + discorduser.hasMany(birthday, { as: 'birthdays', foreignKey: 'discorduser'}) + blacklist.belongsTo(discorduser, { as: 'reportedby_discorduser', foreignKey: 'reportedby'}) + discorduser.hasMany(blacklist, { as: 'blacklists', foreignKey: 'reportedby'}) + static_members.belongsTo(discorduser, { as: 'username_discorduser', foreignKey: 'username'}) + discorduser.hasMany(static_members, { as: 'static_members', foreignKey: 'username'}) + event_roles.belongsTo(event, { as: 'event', foreignKey: 'event_id'}) + event.hasMany(event_roles, { as: 'event_roles', foreignKey: 'event_id'}) + event_schedule.belongsTo(event, { as: 'event', foreignKey: 'event_id'}) + event.hasMany(event_schedule, { as: 'event_schedules', foreignKey: 'event_id'}) + static_members.belongsTo(static, { as: 'static', foreignKey: 'static_id'}) + static.hasMany(static_members, { as: 'static_members', foreignKey: 'static_id'}) + + return { + birthday, + blacklist, + discorduser, + event, + event_roles, + event_schedule, + static, + static_members, + } +} +module.exports = initModels +module.exports.initModels = initModels +module.exports.default = initModels diff --git a/src/database/models/static.js b/src/database/models/static.js new file mode 100644 index 0000000..4377d62 --- /dev/null +++ b/src/database/models/static.js @@ -0,0 +1,49 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('static', { + id: { + autoIncrement: true, + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + }, + name: { + type: DataTypes.STRING(12), + allowNull: false, + unique: 'unique_name', + }, + creator: { + type: DataTypes.STRING(255), + allowNull: false, + }, + size: { + type: DataTypes.INTEGER, + allowNull: true, + }, + time: { + type: DataTypes.DATE, + allowNull: true, + }, + }, { + sequelize, + tableName: 'static', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'static_pkey', + unique: true, + fields: [ + { name: 'id' }, + ], + }, + { + name: 'unique_name', + unique: true, + fields: [ + { name: 'name' }, + ], + }, + ], + }) +} diff --git a/src/database/models/static_members.js b/src/database/models/static_members.js new file mode 100644 index 0000000..f15d624 --- /dev/null +++ b/src/database/models/static_members.js @@ -0,0 +1,38 @@ +const Sequelize = require('sequelize') +module.exports = function(sequelize, DataTypes) { + return sequelize.define('static_members', { + static_id: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + references: { + model: 'static', + key: 'id', + }, + }, + username: { + type: DataTypes.STRING(100), + allowNull: false, + primaryKey: true, + references: { + model: 'discorduser', + key: 'name', + }, + }, + }, { + sequelize, + tableName: 'static_members', + schema: 'public', + timestamps: false, + indexes: [ + { + name: 'static_members_pkey', + unique: true, + fields: [ + { name: 'static_id' }, + { name: 'username' }, + ], + }, + ], + }) +} diff --git a/src/database/staticdb.js b/src/database/staticdb.js index fd25752..5602f61 100644 --- a/src/database/staticdb.js +++ b/src/database/staticdb.js @@ -1,61 +1,81 @@ -const { getClient } = require('./database') +const { + static: Static, + static_members: StaticMembers, + discorduser: DiscordUser, +} = require('./models') const CreateStatic = async (name, creator, members, size) => { - try { - const client = getClient() + try { + const staticEntry = await Static.create({ + name, + creator, + size, + }) - const result = await client.query( - "INSERT INTO static (name, creator, size) VALUES ($1, $2, $3) RETURNING id", - [name, creator, size] - ) + const staticId = staticEntry.id - const staticId = result.rows[0].id + await Promise.all( + members.map(async member => { + const [user] = await DiscordUser.findOrCreate({ + where: { + name: member, + }, + defaults: { + name: member, + }, + }) - for (const member of members) { - await client.query( - "INSERT INTO static_members (static_id, member) VALUES ($1, $2)", - [staticId, member] - ) - } + await StaticMembers.create({ + static_id: staticId, + member: user.name, + }) + }) + ) - return true - } catch (error) { - console.error('Error creating static entry:', error) - return false - } + return true + } catch (error) { + console.error('Error creating static entry:', error) + return false + } } const ReadStatic = async (name) => { - try { - const client = getClient() - - 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 - } + try { + const staticEntry = await Static.findOne({ + where: { name }, + }) + return staticEntry || false + } catch (error) { + console.error('Error reading static entry:', error) + return false + } } const DeleteStatic = async (name) => { - try { - const client = getClient() + try { + const staticEntry = await Static.findOne({ + where: { name }, + }) - await client.query( - "DELETE FROM static WHERE name = $1", - [name] - ) - } catch (error) { - console.error('Error deleting static entry:', error) - return false - } + if (!staticEntry) return false + + await StaticMembers.destroy({ + where: { + static_id: staticEntry.id, + }, + }) + + await staticEntry.destroy() + + return true + } catch (error) { + console.error('Error deleting static entry:', error) + return false + } } module.exports = { - CreateStatic, - ReadStatic, - DeleteStatic, + CreateStatic, + ReadStatic, + DeleteStatic, } diff --git a/src/discord/discordClient.js b/src/discord/discordClient.js index da64304..8e5600c 100644 --- a/src/discord/discordClient.js +++ b/src/discord/discordClient.js @@ -3,23 +3,23 @@ require('dotenv').config() const { REST } = require('@discordjs/rest') const { SlashCommandBuilder } = require('@discordjs/builders') const { - initReactionPerRole, - messageReactionAdd, - messageReactionRemove, + initReactionPerRole, + messageReactionAdd, + messageReactionRemove, } = require('../features/reactionPerRole') const { - handleBlacklistAdd, - handleBlacklistCheck, - handleBlacklistShow, - updateGlobalMessage, + handleBlacklistAdd, + handleBlacklistCheck, + handleBlacklistShow, + updateGlobalMessage, } = require('../features/blacklist') const { - handleBirthdayAdd, - handleBirthdayUpdate, - handleBirthdayCheck, - handleBirthdayDelete, + handleBirthdayAdd, + handleBirthdayUpdate, + handleBirthdayCheck, + handleBirthdayDelete, } = require('../features/birthday') const { startBirthdayCheckCron } = require('../tasks/checkBirthday') @@ -28,179 +28,179 @@ const { startEventCheckCron } = require('../tasks/eventReminder') const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN) const client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.GuildMembers, - GatewayIntentBits.MessageContent, - GatewayIntentBits.GuildMessageReactions, - ], - partials: [ - Partials.Channel, - ] + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildMessageReactions, + ], + partials: [ + Partials.Channel, + ], }) client.on('interactionCreate', async interaction => { - if (!interaction.isCommand()) return + if (!interaction.isCommand()) return - switch (interaction.commandName) { - case 'blacklist': - const reportedUser = interaction.options.getString('player') - const reason = interaction.options.getString('reason') - const reportedByUser = interaction.user.username + switch (interaction.commandName) { + case 'blacklist': + const reportedUser = interaction.options.getString('player') + const reason = interaction.options.getString('reason') + const reportedByUser = interaction.user.username - const res = await handleBlacklistAdd(reportedUser, reason, reportedByUser) - if (res) { - if (res.name === reportedUser) - interaction.reply({ content: `This user has already been reported`, ephemeral: true }) - else { - interaction.reply({ content: `Player ** ${reportedUser}** had been successfully reported for ${reason}`, ephemeral: true }) - updateGlobalMessage(interaction) - } - } else - interaction.reply({ content: `ERROR trying to add the player to the blacklist, please contact @Crylia`, ephemeral: true }) + const res = await handleBlacklistAdd(reportedUser, reason, reportedByUser) + if (res) { + if (res.name === reportedUser) + interaction.reply({ content: 'This user has already been reported', ephemeral: true }) + else { + interaction.reply({ content: `Player ** ${reportedUser}** had been successfully reported for ${reason}`, ephemeral: true }) + updateGlobalMessage(interaction) + } + } else + interaction.reply({ content: 'ERROR trying to add the player to the blacklist, please contact @Crylia', ephemeral: true }) - break - case 'blacklist-check-player': - const player = interaction.options.getString('player') + break + case 'blacklist-check-player': + const player = interaction.options.getString('player') - const reason2 = await handleBlacklistCheck(player) - reason2 ? - 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 }) + const reason2 = await handleBlacklistCheck(player) + reason2 ? + 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 }) - break - case 'birthday': - const user = interaction.user.username - const birthday = interaction.options.getString('birthday') + break + case 'birthday': + const user = interaction.user.username + const birthday = interaction.options.getString('birthday') - // Matches format xx.xx.xxxx, later dd.mm.yyyy - const match = birthday.match(/^(\d{2})\.(\d{2})\.(\d{4})$/) - if (!match) { - await interaction.reply({ content: 'Invalid date format. Please use dd.mm.yyyy.', ephemeral: true }) - return - } + // Matches format xx.xx.xxxx, later dd.mm.yyyy + const match = birthday.match(/^(\d{2})\.(\d{2})\.(\d{4})$/) + if (!match) { + await interaction.reply({ content: 'Invalid date format. Please use dd.mm.yyyy.', ephemeral: true }) + return + } - const day = parseInt(match[1], 10) - const month = parseInt(match[2], 10) - const year = parseInt(match[3], 10) + const day = parseInt(match[1], 10) + const month = parseInt(match[2], 10) + const year = parseInt(match[3], 10) - // Validates dd.mm ae legit, year doesnt matter for the birthday - const isValidDate = (day, month, year) => { - if (month < 1 || month > 12) return false + // Validates dd.mm ae legit, year doesnt matter for the birthday + const isValidDate = (day, month, year) => { + if (month < 1 || month > 12) return false - const daysInMonth = [31, ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - return day > 0 && day <= daysInMonth[month - 1] - } + const daysInMonth = [31, ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + return day > 0 && day <= daysInMonth[month - 1] + } - if (!isValidDate(day, month, year)) { - await interaction.reply({ content: 'Invalid date. Please enter a valid birthday as dd.mm.yyyy.', ephemeral: true }) - return - } + if (!isValidDate(day, month, year)) { + await interaction.reply({ content: 'Invalid date. Please enter a valid birthday as dd.mm.yyyy.', ephemeral: true }) + return + } - await handleBirthdayCheck(user, birthday).length > 0 ? - await handleBirthdayUpdate(user, birthday) : - handleBirthdayAdd(user, birthday) ? - 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 }) - break - case 'birthday-check': - const birthdayCheck = await handleBirthdayCheck(interaction.user.username) - 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: "You don't have a birthday set. Use the`birthday` command to set one.", ephemeral: true }) - break - case 'birthday-delete': - await handleBirthdayDelete(interaction.user.username) ? - await interaction.reply({ content: "Your birthday has been deleted.", ephemeral: true }) : - await interaction.reply({ content: "You don't have a birthday set.", ephemeral: true }) - break - case 'static-create': + await handleBirthdayCheck(user, birthday).length > 0 ? + await handleBirthdayUpdate(user, birthday) : + handleBirthdayAdd(user, birthday) ? + 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 }) + break + case 'birthday-check': + const birthdayCheck = await handleBirthdayCheck(interaction.user.username) + 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: 'You don\'t have a birthday set. Use the`birthday` command to set one.', ephemeral: true }) + break + case 'birthday-delete': + await handleBirthdayDelete(interaction.user.username) ? + await interaction.reply({ content: 'Your birthday has been deleted.', ephemeral: true }) : + await interaction.reply({ content: 'You don\'t have a birthday set.', ephemeral: true }) + break + case 'static-create': - break - case 'static-delete': + break + case 'static-delete': - break + break - case 'static-show': + case 'static-show': - break - default: - break - } + break + default: + break + } }) client.on('messageReactionAdd', async (reaction, user) => { - messageReactionAdd(user, reaction) + messageReactionAdd(user, reaction) }) client.on('messageReactionRemove', async (reaction, user) => { - messageReactionRemove(user, reaction) + messageReactionRemove(user, reaction) }) client.once('ready', async () => { - console.log(`Logged in as ${client.user.tag} `) - startBirthdayCheckCron(client) - startEventCheckCron(client) - updateGlobalMessage(client) - initReactionPerRole(client) + console.log(`Logged in as ${client.user.tag} `) + startBirthdayCheckCron(client) + startEventCheckCron(client) + updateGlobalMessage(client) + initReactionPerRole(client) }) const connectDiscord = async () => { - try { - console.log('Started refreshing application (/) commands.') + try { + console.log('Started refreshing application (/) commands.') - const commands = [ - new SlashCommandBuilder() - .setName('birthday') - .setDescription('Set yourself a birthday') - .addStringOption(option => - option.setName('birthday') - .setDescription('Set (or overwrite) your birthday as dd.mm.yyyy (e.g. 01.12.1999)') - .setRequired(true) - ), - new SlashCommandBuilder() - .setName('birthday-check') - .setDescription('Check your set birthday (only you will see the date)'), - new SlashCommandBuilder() - .setName('birthday-delete') - .setDescription('Delete your birthday, nobody will know when your birthday arrives :('), - new SlashCommandBuilder() - .setName('blacklist') - .setDescription('Add a player to a blacklist with a reason') - .addStringOption(option => - option.setName('player') - .setDescription('The in-game name of the player') - .setRequired(true) - ) - .addStringOption(option => - option.setName('reason') - .setDescription('Explain what happened, why should this player be blacklisted') - .setRequired(true) - ), - new SlashCommandBuilder() - .setName('blacklist-check-player') - .setDescription('Check if a player is blacklisted') - .addStringOption(option => - option.setName('player') - .setDescription('The in-game name of the player') - .setRequired(true) - ) - ].map(command => command.toJSON()) + const commands = [ + new SlashCommandBuilder() + .setName('birthday') + .setDescription('Set yourself a birthday') + .addStringOption(option => + option.setName('birthday') + .setDescription('Set (or overwrite) your birthday as dd.mm.yyyy (e.g. 01.12.1999)') + .setRequired(true) + ), + new SlashCommandBuilder() + .setName('birthday-check') + .setDescription('Check your set birthday (only you will see the date)'), + new SlashCommandBuilder() + .setName('birthday-delete') + .setDescription('Delete your birthday, nobody will know when your birthday arrives :('), + new SlashCommandBuilder() + .setName('blacklist') + .setDescription('Add a player to a blacklist with a reason') + .addStringOption(option => + option.setName('player') + .setDescription('The in-game name of the player') + .setRequired(true) + ) + .addStringOption(option => + option.setName('reason') + .setDescription('Explain what happened, why should this player be blacklisted') + .setRequired(true) + ), + new SlashCommandBuilder() + .setName('blacklist-check-player') + .setDescription('Check if a player is blacklisted') + .addStringOption(option => + option.setName('player') + .setDescription('The in-game name of the player') + .setRequired(true) + ), + ].map(command => command.toJSON()) - await rest.put( - Routes.applicationCommands(process.env.CLIENT_ID), - { body: commands }, - ) - console.log('Successfully reloaded application (/) commands.') - } catch (error) { - console.error(error) - } - try { - client.login(process.env.BOT_TOKEN) - } catch (error) { - console.error('Error logging in to Discord:', error) - } + await rest.put( + Routes.applicationCommands(process.env.CLIENT_ID), + { body: commands } + ) + console.log('Successfully reloaded application (/) commands.') + } catch (error) { + console.error(error) + } + try { + client.login(process.env.BOT_TOKEN) + } catch (error) { + console.error('Error logging in to Discord:', error) + } } module.exports = { client, connectDiscord } diff --git a/src/features/birthday.js b/src/features/birthday.js index 942d7e7..530b1bd 100644 --- a/src/features/birthday.js +++ b/src/features/birthday.js @@ -1,43 +1,43 @@ const { CreateBirthday, ReadBirthday, UpdateBirthday, DeleteBirthday } = require('../database/birthdaydb') const handleBirthdayAdd = async (user, birthday) => { - if (!user || !birthday) return false + if (!user || !birthday) return false - const result = await CreateBirthday(user, birthday) + const result = await CreateBirthday(user, birthday) - return result + return result } const handleBirthdayCheck = async (user) => { - if (!user) return false + if (!user) return false - const result = await ReadBirthday(user) + const result = await ReadBirthday(user) - return result + return result } const handleBirthdayDelete = async (user) => { - if (!user) return false + if (!user) return false - const result = await DeleteBirthday(user) + const result = await DeleteBirthday(user) - return result + return result } const handleBirthdayUpdate = async (user, birthday) => { - if (!user || !birthday) return false + if (!user || !birthday) return false - const result = await UpdateBirthday(user, birthday) + const result = await UpdateBirthday(user, birthday) - return result + return result } const handleBirthdayGetAll = async () => await ReadBirthday() module.exports = { - handleBirthdayAdd, - handleBirthdayCheck, - handleBirthdayDelete, - handleBirthdayUpdate, - handleBirthdayGetAll, + handleBirthdayAdd, + handleBirthdayCheck, + handleBirthdayDelete, + handleBirthdayUpdate, + handleBirthdayGetAll, } diff --git a/src/features/blacklist.js b/src/features/blacklist.js index a885e25..93bd285 100644 --- a/src/features/blacklist.js +++ b/src/features/blacklist.js @@ -2,104 +2,104 @@ const { CreateBlacklist, ReadBlacklist, UpdateBlacklist, DeleteBlacklist } = req const { EmbedBuilder } = require('discord.js') const handleBlacklistAdd = async (reportedUser, reason, reportedByUser) => { - if (!reportedUser || !reason || !reportedByUser) return false + if (!reportedUser || !reason || !reportedByUser) return false - const result = await CreateBlacklist(reportedUser, reason, reportedByUser) + const result = await CreateBlacklist(reportedUser, reason, reportedByUser) - return result + return result } const handleBlacklistCheck = async (user) => { - if (!user) return false + if (!user) return false - const result = await ReadBlacklist(user) + const result = await ReadBlacklist(user) - return result + return result } const handleBlacklistShow = async () => await ReadBlacklist() const createBlacklistEmbeds = (playerEntries, maxChars = 30) => { - const embeds = [] - let embed = new EmbedBuilder() - .setColor('#0099ff') - .setTitle('Blacklist (Page 1)') - .setDescription('Players who have been blacklisted and the reasons.') + const embeds = [] + let embed = new EmbedBuilder() + .setColor('#0099ff') + .setTitle('Blacklist (Page 1)') + .setDescription('Players who have been blacklisted and the reasons.') - let fieldCount = 0 - let pageIndex = 1 + let fieldCount = 0 + let pageIndex = 1 - playerEntries.forEach(([playerName, reason]) => { - let splitReason = [] - while (reason.length > maxChars) { - let splitIndex = reason.lastIndexOf(' ', maxChars) - if (splitIndex === -1) splitIndex = maxChars - splitReason.push(reason.substring(0, splitIndex)) - reason = reason.substring(splitIndex + 1) - } - splitReason.push(reason) + playerEntries.forEach(([playerName, reason]) => { + let splitReason = [] + while (reason.length > maxChars) { + let splitIndex = reason.lastIndexOf(' ', maxChars) + if (splitIndex === -1) splitIndex = maxChars + splitReason.push(reason.substring(0, splitIndex)) + reason = reason.substring(splitIndex + 1) + } + splitReason.push(reason) - const nameField = playerName || '\u200B' - const valueField = splitReason.join('\n').trim() || '\u200B' + const nameField = playerName || '\u200B' + const valueField = splitReason.join('\n').trim() || '\u200B' - if (valueField !== '\u200B') { - embed.addFields({ name: nameField, value: valueField, inline: true }) - fieldCount++ - } + if (valueField !== '\u200B') { + embed.addFields({ name: nameField, value: valueField, inline: true }) + fieldCount++ + } - if (fieldCount >= 25) { - embeds.push(embed) - pageIndex++ - embed = new EmbedBuilder() - .setColor('#0099ff') - .setTitle(`Blacklist (Page ${pageIndex})`) - .setDescription('Players who have been blacklisted and the reasons.') - fieldCount = 0 - } - }) + if (fieldCount >= 25) { + embeds.push(embed) + pageIndex++ + embed = new EmbedBuilder() + .setColor('#0099ff') + .setTitle(`Blacklist (Page ${pageIndex})`) + .setDescription('Players who have been blacklisted and the reasons.') + fieldCount = 0 + } + }) - if (fieldCount > 0) { - embeds.push(embed) - } + if (fieldCount > 0) { + embeds.push(embed) + } - return embeds + return embeds } const updateGlobalMessage = async (client) => { - try { - let targetChannel = null + try { + let targetChannel = null - for (const [_, oauthGuild] of await client.guilds.fetch()) { - targetChannel = (await (await oauthGuild.fetch()).channels.fetch()).find(ch => ch.name === 'blacklist') - break - } + for (const [_, oauthGuild] of await client.guilds.fetch()) { + targetChannel = (await (await oauthGuild.fetch()).channels.fetch()).find(ch => ch.name === 'blacklist') + break + } - if (!targetChannel) { - console.error('Channel with name "blacklist" not found.') - return - } + if (!targetChannel) { + console.error('Channel with name "blacklist" not found.') + return + } - const messages = await targetChannel.messages.fetch({ limit: 100 }) - await Promise.all(messages.map(msg => msg.delete())) + const messages = await targetChannel.messages.fetch({ limit: 100 }) + await Promise.all(messages.map(msg => msg.delete())) - const blacklistEntries = await handleBlacklistShow() + const blacklistEntries = await handleBlacklistShow() - const playerEntries = blacklistEntries.map(entry => [entry.name, entry.reason]) + const playerEntries = blacklistEntries.map(entry => [entry.name, entry.reason]) - const embeds = createBlacklistEmbeds(playerEntries) + const embeds = createBlacklistEmbeds(playerEntries) - for (const embed of embeds) - await targetChannel.send({ embeds: [embed] }) + for (const embed of embeds) + await targetChannel.send({ embeds: [embed] }) - } catch (error) { - console.error('Error updating global message:', error) - } + } catch (error) { + console.error('Error updating global message:', error) + } } module.exports = { - handleBlacklistAdd, - handleBlacklistCheck, - handleBlacklistShow, - updateGlobalMessage, + handleBlacklistAdd, + handleBlacklistCheck, + handleBlacklistShow, + updateGlobalMessage, } diff --git a/src/features/reactionPerRole.js b/src/features/reactionPerRole.js index fcc4839..8009e95 100644 --- a/src/features/reactionPerRole.js +++ b/src/features/reactionPerRole.js @@ -1,206 +1,206 @@ const { GetIconRole } = require('../database/eventdb') -const cron = require('node-cron'); +const cron = require('node-cron') let reminderMessageID = null let rolesMap = new Map() const areMapsEqual = (map1, map2) => { - if (map1.size !== map2.size) return false + if (map1.size !== map2.size) return false - for (const [key, value] of map1) - if (map2.get(key) !== value) return false + for (const [key, value] of map1) + if (map2.get(key) !== value) return false - return true + 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') + 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) + if (!rolesChannel) { + console.log('Channel not found') + return + } + const message = await rolesChannel.messages.fetch(reminderMessageID) - const newRolesFromDB = await GetIconRole() + const newRolesFromDB = await GetIconRole() - if (areMapsEqual(rolesMap, newRolesFromDB)) { - console.log(`No changes to the roles, quitting`) - return - } + if (areMapsEqual(rolesMap, newRolesFromDB)) { + console.log('No changes to the roles, quitting') + return + } - rolesMap = newRolesFromDB + rolesMap = newRolesFromDB - const currentLines = message.content.split('\n').slice(1) + 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 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 = [] + const rolesToAdd = [], rolesToRemove = [] - for (const [iconName, roleName] of rolesMap) - if (!currentRoleMap.has(iconName)) - rolesToAdd.push({ icon: iconName, name: roleName }) + 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 }) + for (const [iconName, roleName] of currentRoleMap) + if (!rolesMap.has(iconName)) + rolesToRemove.push({ icon: iconName, name: roleName }) - const emojis = await guild.emojis.fetch() + 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\n` - else - console.log(`Couldn't find emoji ${iconName}`) - }) + 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\n` + else + console.log(`Couldn't find emoji ${iconName}`) + }) - await message.edit(updatedContent) + 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 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 + for (const role of rolesToRemove) { + const reaction = message.reactions.cache.find(r => r.emoji.name === role.icon) + if (!reaction) continue - await reaction.remove() + await reaction.remove() - const roleToRemove = message.guild.roles.cache.find(r => r.name === role.name) - if (!roleToRemove) continue + 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)); + 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) - } + 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') + 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 - } + 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 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() + 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\n` - else - console.log(`Emoji for ${iconName} not found`) - } - const sentMessage = await rolesChannel.send(message) + 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\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) - } + 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) - } + reminderMessageID = sentMessage.id + } + } catch (error) { + console.log(error) + } } const messageReactionAdd = async (user, reaction) => { - if (user.id === '1280557738530963506') return + if (user.id === '1280557738530963506') return - const { message, emoji } = reaction - const guild = message.guild - const member = await guild.members.fetch(user.id) - if (!member) 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() + 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) + 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) + if (role && !member.roles.cache.has(role.id)) + await member.roles.add(role) - } catch (error) { - console.error(`Error fetching role for ${roleName}`, error) - } - } - } + } catch (error) { + console.error(`Error fetching role for ${roleName}`, error) + } + } + } } const messageReactionRemove = async (user, reaction) => { - const { message, emoji } = reaction - const guild = message.guild + const { message, emoji } = reaction + const guild = message.guild - const member = await guild.members.fetch(user.id) - if (!member) return + const member = await guild.members.fetch(user.id) + if (!member) return - if (message.id === reminderMessageID) { - const roleMap = await GetIconRole() + 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) + 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) + if (role && member.roles.cache.has(role.id)) + await member.roles.remove(role) - } catch (error) { - console.error(`Error fetching role for ${roleName}`, error) - } - } - } + } catch (error) { + console.error(`Error fetching role for ${roleName}`, error) + } + } + } } const initReactionPerRole = async (client) => { - rolesMap = await GetIconRole() + rolesMap = await GetIconRole() - await createReactionMessage(client) + await createReactionMessage(client) - cron.schedule('0 * * * *', async () => { - await editReactionMessage(client) - }) + cron.schedule('0 * * * *', async () => { + await editReactionMessage(client) + }) } module.exports = { - initReactionPerRole, - messageReactionAdd, - messageReactionRemove, + initReactionPerRole, + messageReactionAdd, + messageReactionRemove, } diff --git a/src/features/static.js b/src/features/static.js index fe5efb9..0677914 100644 --- a/src/features/static.js +++ b/src/features/static.js @@ -1,31 +1,31 @@ const { CreateStatic, ReadStatic, DeleteStatic } = require('../database/staticdb') const handleStaticAdd = async (name, creator, members, size) => { - if (!name || !createor || !members || !size) return false + if (!name || !createor || !members || !size) return false - const result = await CreateStatic(name, creator, members, size) + const result = await CreateStatic(name, creator, members, size) - return result + return result } const handleStaticGet = async (name) => { - if (!name) return false; + if (!name) return false - const result = await ReadStatic(name) + const result = await ReadStatic(name) - return result + return result } const handleStaticDelete = async (name) => { - if (!name) return false + if (!name) return false - const result = await DeleteStatic(name) + const result = await DeleteStatic(name) - return result + return result } module.exports = { - handleStaticAdd, - handleStaticGet, - handleStaticDelete, + handleStaticAdd, + handleStaticGet, + handleStaticDelete, } diff --git a/src/index.js b/src/index.js index 2c2a076..bcda9df 100644 --- a/src/index.js +++ b/src/index.js @@ -1,16 +1,13 @@ +const { Sequelize } = require('sequelize') const { connectDiscord } = require('./discord/discordClient') -const { connectDatabase, createClient } = require('./database/database'); require('dotenv').config(); (async () => { - try { - const client = createClient({ - connectionString: `postgresql://${process.env.DB_USER}:${process.env.DB_PASS}@${process.env.DB_HOST}/${process.env.DB_NAME}` - }) - connectDatabase(client).then(() => { - connectDiscord() - }) - } catch (error) { - console.log(error) - } + try { + + connectDiscord() + + } catch (error) { + console.error('Error initializing application:', error) + } })() diff --git a/src/tasks/checkBirthday.js b/src/tasks/checkBirthday.js index 1fa660a..ec5d376 100644 --- a/src/tasks/checkBirthday.js +++ b/src/tasks/checkBirthday.js @@ -1,41 +1,41 @@ -const cron = require('node-cron'); -const { handleBirthdayGetAll } = require('../features/birthday'); +const cron = require('node-cron') +const { handleBirthdayGetAll } = require('../features/birthday') const startBirthdayCheckCron = async (client) => { - cron.schedule('0 20 * * *', async () => { - try { - console.log('Running birthday check...'); + cron.schedule('0 20 * * *', async () => { + try { + console.log('Running birthday check...') - for (const [guildId, oauthGuild] of await client.guilds.fetch()) { - const guild = await oauthGuild.fetch() - const birthdayChannel = (await guild.channels.fetch()).find(ch => ch.name === 'main') + for (const [guildId, oauthGuild] of await client.guilds.fetch()) { + const guild = await oauthGuild.fetch() + const birthdayChannel = (await guild.channels.fetch()).find(ch => ch.name === 'main') - if (!birthdayChannel) continue + if (!birthdayChannel) continue - const birthdays = await handleBirthdayGetAll(); - console.log("birthdays:", birthdays) - if (birthdays.length < 1) continue + const birthdays = await handleBirthdayGetAll() + console.log('birthdays:', birthdays) + if (birthdays.length < 1) continue - let message = '🎉 **Today\'s Birthdays!** 🎉\n\n'; - for (const birthday of birthdays) { - const birthdayDate = new Date(birthday.date); - if (birthdayDate.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }) === new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' })) { - (await guild.members.fetch()).forEach(user => { - if (user.user.username === birthday.discorduser) { - message += `🎂 **Happy Birthday, <@${user.id}>!** 🎂\nWishing you a fantastic day filled with joy and surprises! 🎁🎈\n\n**Everyone, make sure to wish <@${user.id}> a wonderful birthday!** 🎊🎉\n\n`; - } - }) - } - } - console.log(message) - if (message !== '🎉 **Today\'s Birthdays!** 🎉\n\n') { - await birthdayChannel.send(message); - } - } - } catch (error) { - console.log("Error in scheduled birthday check:", error); - } - }); -}; + let message = '🎉 **Today\'s Birthdays!** 🎉\n\n' + for (const birthday of birthdays) { + const birthdayDate = new Date(birthday.date) + if (birthdayDate.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }) === new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' })) { + (await guild.members.fetch()).forEach(user => { + if (user.user.username === birthday.discorduser) { + message += `🎂 **Happy Birthday, <@${user.id}>!** 🎂\nWishing you a fantastic day filled with joy and surprises! 🎁🎈\n\n**Everyone, make sure to wish <@${user.id}> a wonderful birthday!** 🎊🎉\n\n` + } + }) + } + } + console.log(message) + if (message !== '🎉 **Today\'s Birthdays!** 🎉\n\n') { + await birthdayChannel.send(message) + } + } + } catch (error) { + console.log('Error in scheduled birthday check:', error) + } + }) +} -module.exports = { startBirthdayCheckCron }; +module.exports = { startBirthdayCheckCron } diff --git a/src/tasks/eventReminder.js b/src/tasks/eventReminder.js index 059efd3..c722854 100644 --- a/src/tasks/eventReminder.js +++ b/src/tasks/eventReminder.js @@ -1,79 +1,79 @@ -const cron = require('node-cron'); +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() + const res = await ReadEvents() - eventCache = res + eventCache = res } const convertToISO = (time, date) => { - const utcDate = new Date(`${date}T${time}`); - utcDate.setHours(utcDate.getHours()); - return utcDate.toISOString().split('.')[0]; -}; + 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 [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 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 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 () => { - await FetchEvents() + cron.schedule('45 * * * *', async () => { + await FetchEvents() - for (const [_, oauthGuild] of await client.guilds.fetch()) { - const guild = await oauthGuild.fetch() - const channel = (await guild.channels.fetch()).find(ch => ch.name === 'reminder') + for (const [_, 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 + if (!channel) continue - eventCache.forEach(async (event) => { - let { schedule_id, event_name, start_time, end_time, day_of_week } = event - if (!((!day_of_week || day_of_week === getDayOfWeek()) && isReminderTime(convertToUTC(start_time)))) return + eventCache.forEach(async (event) => { + let { schedule_id, event_name, start_time, end_time, day_of_week } = event + if (!((!day_of_week || day_of_week === getDayOfWeek()) && isReminderTime(convertToUTC(start_time)))) return - const rolesEventMap = await GetEventRole() - const role = guild.roles.cache.find(r => r.name === rolesEventMap.get(event_name)) - const roleId = role ? role.id : null + const rolesEventMap = await GetEventRole() + const role = guild.roles.cache.find(r => r.name === rolesEventMap.get(event_name)) + const roleId = role ? role.id : null - const embed = new EmbedBuilder() - .setTitle(event_name) - .setDescription(`${event_name} starts at **${start_time} CEST** and ends at **${end_time} CEST**. \n ${roleId ? `<@&${roleId}>` : 'No role assigned'} \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](https://www.timeanddate.com/worldclock/fixedtime.html?iso=${convertToISO(start_time, new Date().toISOString().split('T')[0])}&msg=${encodeURIComponent(event_name)})`, - inline: true - } - ) - .setFooter({ text: `Schedule ID: ${schedule_id}` }) - .setColor('#00FF00') + const embed = new EmbedBuilder() + .setTitle(event_name) + .setDescription(`${event_name} starts at **${start_time} CEST** and ends at **${end_time} CEST**. \n ${roleId ? `<@&${roleId}>` : 'No role assigned'} \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](https://www.timeanddate.com/worldclock/fixedtime.html?iso=${convertToISO(start_time, new Date().toISOString().split('T')[0])}&msg=${encodeURIComponent(event_name)})`, + inline: true + } + ) + .setFooter({ text: `Schedule ID: ${schedule_id}` }) + .setColor('#00FF00') - await channel.send({ embeds: [embed] }).catch(console.error) - }) - } - }) + await channel.send({ embeds: [embed] }).catch(console.error) + }) + } + }) } module.exports = { - startEventCheckCron, + startEventCheckCron, }