Fixes and improvements for database and other things

Also continued to add to ticket creation stuff - now includes opening questions
This commit is contained in:
Isaac 2021-04-25 15:40:07 +01:00
parent 674f29b403
commit db94bf6969
No known key found for this signature in database
GPG Key ID: F6812DBC6719B4E3
16 changed files with 245 additions and 189 deletions

View File

@ -3,12 +3,12 @@ module.exports = ({ config }, sequelize) => {
const { DB_TABLE_PREFIX } = process.env; const { DB_TABLE_PREFIX } = process.env;
sequelize.define('Category', { sequelize.define('Category', {
id: { id: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
primaryKey: true, primaryKey: true,
allowNull: false, allowNull: false,
}, },
guild: { guild: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
references: { references: {
model: DB_TABLE_PREFIX + 'guilds', model: DB_TABLE_PREFIX + 'guilds',

View File

@ -3,13 +3,13 @@ module.exports = (client, sequelize) => {
const { DB_TABLE_PREFIX } = process.env; const { DB_TABLE_PREFIX } = process.env;
sequelize.define('ChannelEntity', { sequelize.define('ChannelEntity', {
channel: { channel: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
unique: 'id-ticket' unique: 'id-ticket'
}, },
name: DataTypes.TEXT, name: DataTypes.TEXT,
ticket: { ticket: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
unique: 'id-ticket', unique: 'id-ticket',
references: { references: {

View File

@ -3,7 +3,7 @@ module.exports = ({ config }, sequelize) => {
const { DB_TABLE_PREFIX } = process.env; const { DB_TABLE_PREFIX } = process.env;
sequelize.define('Guild', { sequelize.define('Guild', {
id: { id: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
primaryKey: true, primaryKey: true,
allowNull: false, allowNull: false,
}, },

View File

@ -3,12 +3,12 @@ module.exports = (client, sequelize) => {
const { DB_TABLE_PREFIX } = process.env; const { DB_TABLE_PREFIX } = process.env;
sequelize.define('Message', { sequelize.define('Message', {
id: { id: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
primaryKey: true, primaryKey: true,
allowNull: false, allowNull: false,
}, },
author: { author: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
}, },
data: { data: {
@ -24,7 +24,7 @@ module.exports = (client, sequelize) => {
defaultValue: false, defaultValue: false,
}, },
ticket: { ticket: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
references: { references: {
model: DB_TABLE_PREFIX + 'tickets', model: DB_TABLE_PREFIX + 'tickets',

View File

@ -8,12 +8,12 @@ module.exports = (client, sequelize) => {
}, },
name: DataTypes.TEXT, name: DataTypes.TEXT,
role: { role: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
unique: 'id-ticket' unique: 'id-ticket'
}, },
ticket: { ticket: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
unique: 'id-ticket', unique: 'id-ticket',
references: { references: {

View File

@ -3,7 +3,7 @@ module.exports = (client, sequelize) => {
const { DB_TABLE_PREFIX } = process.env; const { DB_TABLE_PREFIX } = process.env;
sequelize.define('Survey', { sequelize.define('Survey', {
guild: { guild: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
references: { references: {
model: DB_TABLE_PREFIX + 'guilds', model: DB_TABLE_PREFIX + 'guilds',

View File

@ -7,17 +7,18 @@ module.exports = (client, sequelize) => {
allowNull: true, allowNull: true,
}, },
survey: { survey: {
type: DataTypes.CHAR(18), type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
unique: 'survey-ticket',
references: { references: {
model: DB_TABLE_PREFIX + 'surveys', model: DB_TABLE_PREFIX + 'surveys',
key: 'id' key: 'id'
}, },
}, },
ticket: { ticket: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
unique: 'id-ticket', unique: 'survey-ticket',
references: { references: {
model: DB_TABLE_PREFIX + 'tickets', model: DB_TABLE_PREFIX + 'tickets',
key: 'id' key: 'id'

View File

@ -3,12 +3,12 @@ module.exports = (client, sequelize) => {
const { DB_TABLE_PREFIX } = process.env; const { DB_TABLE_PREFIX } = process.env;
sequelize.define('Ticket', { sequelize.define('Ticket', {
id: { id: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
primaryKey: true, primaryKey: true,
allowNull: false, allowNull: false,
}, },
category: { category: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
references: { references: {
model: DB_TABLE_PREFIX + 'categories', model: DB_TABLE_PREFIX + 'categories',
@ -16,7 +16,7 @@ module.exports = (client, sequelize) => {
}, },
}, },
closed_by: { closed_by: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: true, allowNull: true,
}, },
closed_reason: { closed_reason: {
@ -24,7 +24,7 @@ module.exports = (client, sequelize) => {
allowNull: true, allowNull: true,
}, },
creator: { creator: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
}, },
first_response: { first_response: {
@ -32,7 +32,7 @@ module.exports = (client, sequelize) => {
allowNull: true, allowNull: true,
}, },
guild: { guild: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
references: { references: {
model: DB_TABLE_PREFIX + 'guilds', model: DB_TABLE_PREFIX + 'guilds',
@ -50,7 +50,7 @@ module.exports = (client, sequelize) => {
defaultValue: true defaultValue: true
}, },
opening_message: { opening_message: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: true, allowNull: true,
}, },
topic: { topic: {

View File

@ -8,7 +8,7 @@ module.exports = (client, sequelize) => {
discriminator: DataTypes.STRING, discriminator: DataTypes.STRING,
display_name: DataTypes.TEXT, display_name: DataTypes.TEXT,
ticket: { ticket: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
unique: 'id-ticket', unique: 'id-ticket',
references: { references: {
@ -17,7 +17,7 @@ module.exports = (client, sequelize) => {
}, },
}, },
user: { user: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(19),
allowNull: false, allowNull: false,
unique: 'id-ticket' unique: 'id-ticket'
}, },

View File

@ -6,40 +6,23 @@ module.exports = {
let settings = await message.guild.settings; let settings = await message.guild.settings;
if (!settings) settings = await message.guild.createSettings(); if (!settings) settings = await message.guild.createSettings();
let is_blacklisted = false; let t_row = await client.db.models.Ticket.findOne({
if (settings.blacklist.includes(message.author.id)) {
is_blacklisted = true;
client.log.info(`Ignoring blacklisted member ${message.author.tag}`);
} else {
settings.blacklist.forEach(element => {
if (message.guild.roles.cache.has(element) && message.member.roles.cache.has(element)) {
is_blacklisted = true;
client.log.info(`Ignoring member ${message.author.tag} with blacklisted role`);
}
});
}
if (is_blacklisted) {
try {
return message.react('❌');
} catch (error) {
return client.log.debug('Failed to react to a message');
}
}
if (settings.log_messages && !message.system) client.tickets.archives.addMessage(message); // add the message to the archives (if it is in a ticket channel)
let t_row = await client.db.Ticket.findOne({
where: { where: {
id: message.channel.id id: message.channel.id
} }
}); });
const ignore = [client.user.id, t_row.creator]; if (t_row) {
if (t_row && !t_row.first_response && !ignore.includes(message.author.id)) { if (settings.log_messages && !message.system) {
t_row.update({ client.tickets.archives.addMessage(message); // add the message to the archives (if it is in a ticket channel)
first_response: new Date() }
});
const ignore = [client.user.id, t_row.creator];
if (!t_row.first_response && !ignore.includes(message.author.id)) {
t_row.update({
first_response: new Date()
});
}
} }
client.commands.handle(message); // pass the message to the command handler client.commands.handle(message); // pass the message to the command handler

View File

@ -1 +0,0 @@
{}

View File

@ -102,6 +102,12 @@
}, },
"description": "Create a new ticket", "description": "Create a new ticket",
"name": "new", "name": "new",
"opening_message": {
"fields": {
"topic": "Topic"
}
},
"questions": "Please answer the following questions:\n\n%s",
"response": { "response": {
"created": { "created": {
"title": "✅ Ticket created", "title": "✅ Ticket created",
@ -128,14 +134,9 @@
"description": "You took too long to select the ticket category." "description": "You took too long to select the ticket category."
} }
}, },
"opening_message": {
"fields": {
"topic": "Topic"
}
},
"request_topic": { "request_topic": {
"title": "⚠️ Ticket topic", "title": "Ticket topic",
"description": "Please briefly state what this ticket is about in a short sentence." "description": "Please briefly state what this ticket is about in a a few words."
} }
}, },
"settings": { "settings": {

View File

@ -61,6 +61,27 @@ module.exports = class CommandManager {
* @param {Message} message - Command message * @param {Message} message - Command message
*/ */
async handle(message) { async handle(message) {
let is_blacklisted = false;
if (settings.blacklist.includes(message.author.id)) {
is_blacklisted = true;
this.client.log.info(`Ignoring blacklisted member ${message.author.tag}`);
} else {
settings.blacklist.forEach(element => {
if (message.guild.roles.cache.has(element) && message.member.roles.cache.has(element)) {
is_blacklisted = true;
this.client.log.info(`Ignoring member ${message.author.tag} with blacklisted role`);
}
});
}
if (is_blacklisted) {
try {
return message.react('❌');
} catch (error) {
return this.client.log.warn('Failed to react to a message');
}
}
let settings = await message.guild.settings; let settings = await message.guild.settings;
const i18n = this.client.i18n.get(settings.locale); const i18n = this.client.i18n.get(settings.locale);

View File

@ -17,150 +17,184 @@ module.exports = class TicketArchives {
} }
async addMessage(message) { async addMessage(message) {
let t_row = await this.client.db.models.Ticket.findOne({ try {
where: { await this.client.db.transaction(async t => {
id: message.channel.id let t_row = await this.client.db.models.Ticket.findOne({
} where: {
}); id: message.channel.id
},
transaction: t
});
if (t_row) { if (t_row) {
await this.client.db.models.Message.create({ await this.client.db.models.Message.create({
id: message.id, id: message.id,
ticket: t_row.id, ticket: t_row.id,
author: message.author.id, author: message.author.id,
data: this.encrypt(JSON.stringify({ data: this.encrypt(JSON.stringify({
content: message.content, content: message.content,
// time: message.createdTimestamp, embeds: message.embeds.map(embed => {
embeds: message.embeds.map(embed => { return { embed };
return { embed }; }),
}), attachments: [...message.attachments.values()]
attachments: [...message.attachments.values()] })),
})) createdAt: new Date(message.createdTimestamp)
}, { transaction: t });
this.updateEntities(message);
}
}); });
} catch (e) {
this.updateEntities(message); this.client.log.warn('Failed to add a message to the ticket archive');
this.client.log.error(e);
} }
} }
async updateMessage(message) { async updateMessage(message) {
let m_row = await this.client.db.models.Message.findOne({ try {
where: { await this.client.db.transaction(async t => {
id: message.id let m_row = await this.client.db.models.Message.findOne({
} where: {
}); id: message.id
},
transaction: t
});
if (m_row) { if (m_row) {
m_row.data = this.encrypt(JSON.stringify({ m_row.data = this.encrypt(JSON.stringify({
content: message.content, content: message.content,
embeds: message.embeds.map(embed => { embeds: message.embeds.map(embed => {
return { embed }; return { embed };
}), }),
attachments: [...message.attachments.values()] attachments: [...message.attachments.values()]
})); }));
if (message.editedTimestamp) { if (message.editedTimestamp) {
m_row.edited = true; m_row.edited = true;
this.updateEntities(message); this.updateEntities(message);
} }
await m_row.save(); // save changes await m_row.save({ transaction: t }); // save changes
}
});
} catch (e) {
this.client.log.warn('Failed to update message in the ticket archive');
this.client.log.error(e);
} }
} }
async deleteMessage(message) { async deleteMessage(message) {
let msg = await this.client.db.models.Message.findOne({ try {
where: { await this.client.db.transaction(async t => {
id: message.id let msg = await this.client.db.models.Message.findOne({
} where: {
}); id: message.id
},
transaction: t
});
if (msg) { if (msg) {
msg.deleted = true; msg.deleted = true;
await msg.save(); // save changes to message row await msg.save({ transaction: t }); // save changes to message row
}
});
} catch (e) {
this.client.log.warn('Failed to delete message in ticket archive');
this.client.log.error(e);
} }
} }
async updateEntities(message) { async updateEntities(message) {
let m_row = await this.client.db.models.Message.findOne({ try {
where: { await this.client.db.transaction(async t => {
id: message.id let m_row = await this.client.db.models.Message.findOne({
} where: {
}); id: message.id
},
transaction: t
});
if (!m_row) return; if (!m_row) return;
// message author // message author
let u_model_data = { let u_model_data = {
user: message.author.id, user: message.author.id,
ticket: message.channel.id ticket: message.channel.id
}; };
let [u_row] = await this.client.db.models.UserEntity.findOrCreate({ let [u_row] = await this.client.db.models.UserEntity.findOrCreate({
where: u_model_data, where: u_model_data,
defaults: u_model_data defaults: u_model_data,
}); transaction: t
await u_row.update({ });
avatar: message.author.displayAvatarURL(),
username: this.encrypt(message.author.username),
discriminator: message.author.discriminator,
display_name: this.encrypt(message.member.displayName),
colour: message.member.displayColor === 0 ? null : int2hex(message.member.displayColor),
bot: message.author.bot
});
// mentioned members await u_row.update({
message.mentions.members.forEach(async member => { avatar: message.author.displayAvatarURL(),
let m_model_data = { username: this.encrypt(message.author.username),
user: member.user.id, discriminator: message.author.discriminator,
ticket: message.channel.id display_name: this.encrypt(message.member.displayName),
}; colour: message.member.displayColor === 0 ? null : int2hex(message.member.displayColor),
let [m_row] = await this.client.db.models.UserEntity.findOrCreate({ bot: message.author.bot
where: m_model_data, }, { transaction: t });
defaults: m_model_data
});
await m_row.update({ // mentioned members
avatar: member.user.displayAvatarURL(), message.mentions.members.forEach(async member => {
username: this.encrypt(member.user.username), let m_model_data = {
discriminator: member.user.discriminator, user: member.user.id,
display_name: this.encrypt(member.displayName), ticket: message.channel.id
colour: member.displayColor === 0 ? null : int2hex(member.displayColor), };
bot: member.user.bot let [m_row] = await this.client.db.models.UserEntity.findOrCreate({
}); where: m_model_data,
}); defaults: m_model_data,
transaction: t
});
// mentioned channels await m_row.update({
message.mentions.channels.forEach(async channel => { avatar: member.user.displayAvatarURL(),
let c_model_data = { username: this.encrypt(member.user.username),
channel: channel.id, discriminator: member.user.discriminator,
ticket: message.channel.id display_name: this.encrypt(member.displayName),
}; colour: member.displayColor === 0 ? null : int2hex(member.displayColor),
let [c_row] = await this.client.db.models.ChannelEntity.findOrCreate({ bot: member.user.bot
where: c_model_data, }, { transaction: t });
defaults: c_model_data });
});
await c_row.update({
name: this.encrypt(channel.name)
});
});
// mentioned roles // mentioned channels
message.mentions.roles.forEach(async role => { message.mentions.channels.forEach(async channel => {
let r_model_data = { let c_model_data = {
role: role.id, channel: channel.id,
ticket: message.channel.id ticket: message.channel.id
}; };
let [r_row] = await this.client.db.models.RoleEntity.findOrCreate({ let [c_row] = await this.client.db.models.ChannelEntity.findOrCreate({
where: r_model_data, where: c_model_data,
defaults: r_model_data defaults: c_model_data,
transaction: t
});
await c_row.update({
name: this.encrypt(channel.name)
}, { transaction: t });
});
// mentioned roles
message.mentions.roles.forEach(async role => {
let r_model_data = {
role: role.id,
ticket: message.channel.id
};
let [r_row] = await this.client.db.models.RoleEntity.findOrCreate({
where: r_model_data,
defaults: r_model_data,
transaction: t
});
await r_row.update({
name: this.encrypt(role.name),
colour: role.color === 0 ? '7289DA' : int2hex(role.color) // 7289DA = 7506394
}, { transaction: t });
});
}); });
await r_row.update({ } catch (e) {
name: this.encrypt(role.name), this.client.log.warn('Failed to update message entities in ticket archive');
colour: role.color === 0 ? '7289DA' : int2hex(role.color) // 7289DA = 7506394 this.client.log.error(e);
}); }
});
} }
}; };

View File

@ -2,6 +2,7 @@ const EventEmitter = require('events');
const TicketArchives = require('./archives'); const TicketArchives = require('./archives');
const { MessageEmbed } = require('discord.js'); const { MessageEmbed } = require('discord.js');
const { int2hex } = require('../../utils'); const { int2hex } = require('../../utils');
const { footer } = require('../../utils/discord');
/** Manages tickets */ /** Manages tickets */
module.exports = class TicketManager extends EventEmitter { module.exports = class TicketManager extends EventEmitter {
@ -69,13 +70,17 @@ module.exports = class TicketManager extends EventEmitter {
.catch(() => this.client.log.warn('Failed to delete system pin message')); .catch(() => this.client.log.warn('Failed to delete system pin message'));
} }
let questions = cat_row.opening_questions
.map((q, index) => `**${index + 1}.** ${q}`)
.join('\n\n');
if (cat_row.require_topic && topic.length === 0) { if (cat_row.require_topic && topic.length === 0) {
let collector_message = await t_channel.send( let collector_message = await t_channel.send(
new MessageEmbed() new MessageEmbed()
.setColor(settings.colour) .setColor(settings.colour)
.setTitle(i18n('commands.new.request_topic.title')) .setTitle('⚠️ ' + i18n('commands.new.request_topic.title'))
.setDescription(i18n('commands.new.request_topic.description')) .setDescription(i18n('commands.new.request_topic.description'))
.setFooter(settings.footer) .setFooter(footer(settings.footer, i18n('collector_expires_in', 120)), guild.iconURL())
); );
const collector_filter = (message) => message.author.id === t_row.creator; const collector_filter = (message) => message.author.id === t_row.creator;
@ -99,17 +104,29 @@ module.exports = class TicketManager extends EventEmitter {
.setFooter(settings.footer) .setFooter(settings.footer)
); );
await message.react('✅'); await message.react('✅');
collector.stop();
}); });
collector.on('end', async (collected) => { collector.on('end', async () => {
if (collected.size === 0) { collector_message
collector_message .delete()
.delete() .catch(() => this.client.log.warn('Failed to delete topic collector message'));
.catch(() => this.client.log.warn('Failed to delete topic collector message')); await t_channel.send(
} new MessageEmbed()
.setColor(settings.colour)
.setDescription(i18n('commands.new.questions', questions))
.setFooter(settings.footer)
);
}); });
} else {
if (cat_row.opening_questions.length > 0) {
await t_channel.send(
new MessageEmbed()
.setColor(settings.colour)
.setDescription(i18n('commands.new.questions', questions))
.setFooter(settings.footer)
);
}
} }
} }

View File

@ -11,7 +11,7 @@ module.exports = {
*/ */
footer: (text, additional) => { footer: (text, additional) => {
if (text && additional) return `${text} | ${additional}`; if (text && additional) return `${text} | ${additional}`;
else return text || additional || ''; else return additional || text || '';
}, },
/** /**
* Select a presence from the config * Select a presence from the config