mirror of
https://github.com/Hessenuk/DiscordTickets.git
synced 2024-12-23 00:03:09 +02:00
Progress on message archiving
This commit is contained in:
parent
9682fcf22b
commit
460138fb73
@ -22,7 +22,7 @@ model ArchivedChannel {
|
|||||||
model ArchivedMessage {
|
model ArchivedMessage {
|
||||||
author ArchivedUser @relation(fields: [ticketId, authorId], references: [ticketId, userId], onDelete: Cascade)
|
author ArchivedUser @relation(fields: [ticketId, authorId], references: [ticketId, userId], onDelete: Cascade)
|
||||||
authorId String @db.VarChar(19)
|
authorId String @db.VarChar(19)
|
||||||
content Json
|
content String @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
deleted Boolean @default(false)
|
deleted Boolean @default(false)
|
||||||
edited Boolean @default(false)
|
edited Boolean @default(false)
|
||||||
@ -50,17 +50,17 @@ model ArchivedRole {
|
|||||||
|
|
||||||
model ArchivedUser {
|
model ArchivedUser {
|
||||||
archivedMessages ArchivedMessage[]
|
archivedMessages ArchivedMessage[]
|
||||||
avatar String
|
avatar String?
|
||||||
bot Boolean @default(false)
|
bot Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
discriminator String @db.Char(4)
|
discriminator String? @db.Char(4)
|
||||||
displayName String @db.Text
|
displayName String? @db.Text
|
||||||
role ArchivedRole @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade)
|
role ArchivedRole? @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade)
|
||||||
roleId String @db.VarChar(19)
|
roleId String? @db.VarChar(19)
|
||||||
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
||||||
ticketId String @db.VarChar(19)
|
ticketId String @db.VarChar(19)
|
||||||
userId String @db.VarChar(19)
|
userId String @db.VarChar(19)
|
||||||
username String @db.Text
|
username String? @db.Text
|
||||||
|
|
||||||
@@id([ticketId, userId])
|
@@id([ticketId, userId])
|
||||||
@@unique([ticketId, userId])
|
@@unique([ticketId, userId])
|
||||||
|
@ -22,7 +22,7 @@ model ArchivedChannel {
|
|||||||
model ArchivedMessage {
|
model ArchivedMessage {
|
||||||
author ArchivedUser @relation(fields: [ticketId, authorId], references: [ticketId, userId], onDelete: Cascade)
|
author ArchivedUser @relation(fields: [ticketId, authorId], references: [ticketId, userId], onDelete: Cascade)
|
||||||
authorId String @db.VarChar(19)
|
authorId String @db.VarChar(19)
|
||||||
content Json
|
content String @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
deleted Boolean @default(false)
|
deleted Boolean @default(false)
|
||||||
edited Boolean @default(false)
|
edited Boolean @default(false)
|
||||||
@ -50,17 +50,17 @@ model ArchivedRole {
|
|||||||
|
|
||||||
model ArchivedUser {
|
model ArchivedUser {
|
||||||
archivedMessages ArchivedMessage[]
|
archivedMessages ArchivedMessage[]
|
||||||
avatar String
|
avatar String?
|
||||||
bot Boolean @default(false)
|
bot Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
discriminator String @db.Char(4)
|
discriminator String? @db.Char(4)
|
||||||
displayName String @db.Text
|
displayName String? @db.Text
|
||||||
role ArchivedRole @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade)
|
role ArchivedRole? @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade)
|
||||||
roleId String @db.VarChar(19)
|
roleId String? @db.VarChar(19)
|
||||||
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
||||||
ticketId String @db.VarChar(19)
|
ticketId String @db.VarChar(19)
|
||||||
userId String @db.VarChar(19)
|
userId String @db.VarChar(19)
|
||||||
username String @db.Text
|
username String? @db.Text
|
||||||
|
|
||||||
@@id([ticketId, userId])
|
@@id([ticketId, userId])
|
||||||
@@unique([ticketId, userId])
|
@@unique([ticketId, userId])
|
||||||
|
@ -50,17 +50,17 @@ model ArchivedRole {
|
|||||||
|
|
||||||
model ArchivedUser {
|
model ArchivedUser {
|
||||||
archivedMessages ArchivedMessage[]
|
archivedMessages ArchivedMessage[]
|
||||||
avatar String
|
avatar String?
|
||||||
bot Boolean @default(false)
|
bot Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
discriminator String
|
discriminator String?
|
||||||
displayName String
|
displayName String?
|
||||||
role ArchivedRole @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade)
|
role ArchivedRole? @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade)
|
||||||
roleId String
|
roleId String?
|
||||||
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
||||||
ticketId String
|
ticketId String
|
||||||
userId String
|
userId String
|
||||||
username String
|
username String?
|
||||||
|
|
||||||
@@id([ticketId, userId])
|
@@id([ticketId, userId])
|
||||||
@@unique([ticketId, userId])
|
@@unique([ticketId, userId])
|
||||||
|
@ -18,7 +18,6 @@ module.exports = class CreateMessageCommand extends MessageCommand {
|
|||||||
* @param {import("discord.js").MessageContextMenuCommandInteraction} interaction
|
* @param {import("discord.js").MessageContextMenuCommandInteraction} interaction
|
||||||
*/
|
*/
|
||||||
async run(interaction) {
|
async run(interaction) {
|
||||||
// TODO: archive message
|
|
||||||
await useGuild(this.client, interaction, { referencesMessage: interaction.targetMessage.channelId + '/' + interaction.targetId });
|
await useGuild(this.client, interaction, { referencesMessage: interaction.targetMessage.channelId + '/' + interaction.targetId });
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -1,11 +1,255 @@
|
|||||||
const Cryptr = require('cryptr');
|
const Cryptr = require('cryptr');
|
||||||
// const cryptr = new Cryptr(process.env.ENCRYPTION_KEY);
|
const cryptr = new Cryptr(process.env.ENCRYPTION_KEY);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns highest (roles.highest) hoisted role , or everyone
|
||||||
|
* @param {import("discord.js").GuildMember} member
|
||||||
|
* @returns {import("discord.js").Role}
|
||||||
|
*/
|
||||||
|
const hoistedRole = member => member.roles.hoist || member.guild.roles.everyone;
|
||||||
|
|
||||||
module.exports = class TicketArchiver {
|
module.exports = class TicketArchiver {
|
||||||
constructor(client) {
|
constructor(client) {
|
||||||
/** @type {import("client")} */
|
/** @type {import("client")} */
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
this.encrypt = cryptr.encrypt;
|
||||||
|
this.decrypt = cryptr.decrypt;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addMessage() {}
|
/** Add or update a message
|
||||||
|
* @param {string} ticketId
|
||||||
|
* @param {import("discord.js").Message} message
|
||||||
|
* @param {boolean?} external
|
||||||
|
* @returns {import("@prisma/client").ArchivedMessage|boolean}
|
||||||
|
*/
|
||||||
|
async saveMessage(ticketId, message, external = false) {
|
||||||
|
if (this.client.config.overrides.disableArchives) return false;
|
||||||
|
|
||||||
|
if (!message.member) {
|
||||||
|
try {
|
||||||
|
message.member = await message.guild.members.fetch(message.author.id);
|
||||||
|
} catch {
|
||||||
|
this.client.log.verbose('Failed to fetch member %s of %s', message.author.id, message.guild.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const channels = message.mentions.channels;
|
||||||
|
const members = [...message.mentions.members];
|
||||||
|
const roles = [...message.mentions.roles];
|
||||||
|
|
||||||
|
if (message.member) {
|
||||||
|
members.push(message.member);
|
||||||
|
roles.push(hoistedRole(message.member));
|
||||||
|
} else {
|
||||||
|
this.client.log.warn('Message member does not exist');
|
||||||
|
await this.client.prisma.archivedUser.upsert({
|
||||||
|
create: {},
|
||||||
|
update: {},
|
||||||
|
where: {
|
||||||
|
ticketId_userId: {
|
||||||
|
ticketId,
|
||||||
|
userId: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const role of roles) {
|
||||||
|
const data = {
|
||||||
|
colour: role.hexColor.slice(1),
|
||||||
|
name: role.name,
|
||||||
|
roleId: role.id,
|
||||||
|
ticket: { connect: { id: ticketId } },
|
||||||
|
};
|
||||||
|
await this.client.prisma.archivedRole.upsert({
|
||||||
|
create: data,
|
||||||
|
update: data,
|
||||||
|
where: {
|
||||||
|
ticketId_roleId: {
|
||||||
|
roleId: role.id,
|
||||||
|
ticketId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const member of members) {
|
||||||
|
const data = {
|
||||||
|
avatar: member.avatar || member.user.avatar, // TODO: save avatar in user/avatars/
|
||||||
|
bot: member.user.bot,
|
||||||
|
discriminator: member.user.discriminator,
|
||||||
|
displayName: member.displayName ? this.encrypt(member.displayName) : null,
|
||||||
|
roleId: !!member && hoistedRole(member).id,
|
||||||
|
ticketId,
|
||||||
|
userId: member.user.id,
|
||||||
|
username: this.encrypt(member.user.username),
|
||||||
|
};
|
||||||
|
await this.client.prisma.archivedUser.upsert({
|
||||||
|
create: data,
|
||||||
|
update: data,
|
||||||
|
where: {
|
||||||
|
ticketId_userId: {
|
||||||
|
ticketId,
|
||||||
|
userId: member.user.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageD = {
|
||||||
|
author: {
|
||||||
|
connect: {
|
||||||
|
ticketId_userId: {
|
||||||
|
ticketId,
|
||||||
|
userId: message.author?.id || 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
content: cryptr.encrypt(
|
||||||
|
JSON.stringify({
|
||||||
|
attachments: [...message.attachments.values()],
|
||||||
|
components: [...message.components.values()],
|
||||||
|
content: message.content,
|
||||||
|
embeds: message.embeds.map(embed => ({ embed })),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
createdAt: message.createdAt,
|
||||||
|
edited: !!message.editedAt,
|
||||||
|
external,
|
||||||
|
id: message.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.client.prisma.ticket.update({
|
||||||
|
data: {
|
||||||
|
archivedChannels: {
|
||||||
|
upsert: channels.map(channel => {
|
||||||
|
const data = {
|
||||||
|
channelId: channel.id,
|
||||||
|
name: channel.name,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
create: data,
|
||||||
|
update: data,
|
||||||
|
where: {
|
||||||
|
ticketId_channelId: {
|
||||||
|
channelId: channel.id,
|
||||||
|
ticketId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
archivedMessages: {
|
||||||
|
upsert: {
|
||||||
|
create: messageD,
|
||||||
|
update: messageD,
|
||||||
|
where: { id: message.id },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
where: { id: ticketId },
|
||||||
|
});
|
||||||
|
|
||||||
|
// await this.client.prisma.ticket.update({
|
||||||
|
// data: {
|
||||||
|
// archivedChannels: {
|
||||||
|
// upsert: channels.map(channel => {
|
||||||
|
// const data = {
|
||||||
|
// channelId: channel.id,
|
||||||
|
// name: channel.name,
|
||||||
|
// };
|
||||||
|
// return {
|
||||||
|
// create: data,
|
||||||
|
// update: data,
|
||||||
|
// where: {
|
||||||
|
// ticketId_channelId: {
|
||||||
|
// channelId: channel.id,
|
||||||
|
// ticketId,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// }),
|
||||||
|
// },
|
||||||
|
// archivedRoles: {
|
||||||
|
// upsert: roles.map(role => {
|
||||||
|
// const data = {
|
||||||
|
// colour: role.hexColor.slice(1),
|
||||||
|
// name: role.name,
|
||||||
|
// roleId: role.id,
|
||||||
|
// };
|
||||||
|
// return {
|
||||||
|
// create: data,
|
||||||
|
// update: data,
|
||||||
|
// where: {
|
||||||
|
// ticketId_roleId: {
|
||||||
|
// roleId: role.id,
|
||||||
|
// ticketId,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// }),
|
||||||
|
// },
|
||||||
|
// archivedUsers: {
|
||||||
|
// upsert: members.map(member => {
|
||||||
|
// // message author might have left the server (this message could be external/referenced)
|
||||||
|
// const data = {
|
||||||
|
// avatar: member?.avatar || member.user?.avatar, // TODO: save avatar in user/avatars/
|
||||||
|
// bot: member.user?.bot,
|
||||||
|
// discriminator: member.user?.discriminator,
|
||||||
|
// displayName: member?.displayName ? this.encrypt(member?.displayName) : null,
|
||||||
|
// // role: !!member && {
|
||||||
|
// // connectOrCreate: {
|
||||||
|
// // create: {
|
||||||
|
// // colour: hoistedRole(member).hexColor.slice(1),
|
||||||
|
// // name: hoistedRole(member).name,
|
||||||
|
// // roleId: hoistedRole(member).id,
|
||||||
|
// // ticket: { connect: { id: ticketId } },
|
||||||
|
// // },
|
||||||
|
// // where: {
|
||||||
|
// // roleId: hoistedRole(member).id,
|
||||||
|
// // ticketId,
|
||||||
|
// // },
|
||||||
|
// // },
|
||||||
|
// // },
|
||||||
|
// roleId: !!member && hoistedRole(member).id,
|
||||||
|
// userId: member.user?.id || 'default',
|
||||||
|
// username: member.user?.username ? this.encrypt(member.user.username) : null,
|
||||||
|
// };
|
||||||
|
// return {
|
||||||
|
// create: data,
|
||||||
|
// update: data,
|
||||||
|
// where: {
|
||||||
|
// ticketId_userId: {
|
||||||
|
// ticketId,
|
||||||
|
// userId: member.user.id,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// }),
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// where: { id: ticketId },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const messageD = {
|
||||||
|
// author: { connect: { id: message.author?.id || 'default' } },
|
||||||
|
// content: cryptr.encrypt(
|
||||||
|
// JSON.stringify({
|
||||||
|
// attachments: [...message.attachments.values()],
|
||||||
|
// components: [...message.components.values()],
|
||||||
|
// content: message.content,
|
||||||
|
// embeds: message.embeds.map(embed => ({ embed })),
|
||||||
|
// }),
|
||||||
|
// ),
|
||||||
|
// createdAt: message.createdAt,
|
||||||
|
// edited: !!message.editedAt,
|
||||||
|
// external,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return await this.client.prisma.archivedMessage.upsert({
|
||||||
|
// create: messageD,
|
||||||
|
// update: messageD,
|
||||||
|
// where: { id: message.id },
|
||||||
|
// });
|
||||||
|
}
|
||||||
};
|
};
|
@ -474,13 +474,24 @@ module.exports = class TicketManager {
|
|||||||
.catch(() => this.client.log.warn('Failed to delete system pin message'));
|
.catch(() => this.client.log.warn('Failed to delete system pin message'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: referenced msg or ticket
|
/** @type {import("discord.js").Message|undefined} */
|
||||||
|
let message;
|
||||||
if (referencesMessage) {
|
if (referencesMessage) {
|
||||||
referencesMessage = referencesMessage.split('/');
|
referencesMessage = referencesMessage.split('/');
|
||||||
/** @type {import("discord.js").Message} */
|
/** @type {import("discord.js").Message} */
|
||||||
const message = await (await this.client.channels.fetch(referencesMessage[0]))?.messages.fetch(referencesMessage[1]);
|
message = await (await this.client.channels.fetch(referencesMessage[0]))?.messages.fetch(referencesMessage[1]);
|
||||||
if (message) {
|
if (message) {
|
||||||
|
// not worth the effort of making system messages work atm
|
||||||
|
if (message.system) {
|
||||||
|
referencesMessage = null;
|
||||||
|
message = null;
|
||||||
|
} else {
|
||||||
|
if (!message.member) {
|
||||||
|
try {
|
||||||
|
message.member = await message.guild.members.fetch(message.author.id);
|
||||||
|
} catch {
|
||||||
|
this.client.log.verbose('Failed to fetch member %s of %s', message.author.id, message.guild.id);
|
||||||
|
}
|
||||||
await channel.send({
|
await channel.send({
|
||||||
embeds: [
|
embeds: [
|
||||||
new ExtendedEmbedBuilder()
|
new ExtendedEmbedBuilder()
|
||||||
@ -501,10 +512,13 @@ module.exports = class TicketManager {
|
|||||||
iconURL: message.member?.displayAvatarURL(),
|
iconURL: message.member?.displayAvatarURL(),
|
||||||
name: message.member?.displayName || 'Unknown',
|
name: message.member?.displayName || 'Unknown',
|
||||||
})
|
})
|
||||||
.setDescription(message.content.substring(0, 1000) + message.content.length > 1000 ? '...' : ''),
|
.setDescription(message.content.substring(0, 1000) + (message.content.length > 1000 ? '...' : '')),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
} else if (referencesTicketId) {
|
} else if (referencesTicketId) {
|
||||||
// TODO: add portal url
|
// TODO: add portal url
|
||||||
const ticket = await this.client.prisma.ticket.findUnique({ where: { id: referencesTicketId } });
|
const ticket = await this.client.prisma.ticket.findUnique({ where: { id: referencesTicketId } });
|
||||||
@ -554,9 +568,6 @@ module.exports = class TicketManager {
|
|||||||
topic: topic ? cryptr.encrypt(topic) : null,
|
topic: topic ? cryptr.encrypt(topic) : null,
|
||||||
};
|
};
|
||||||
if (referencesTicketId) data.referencesTicket = { connect: { id: referencesTicketId } };
|
if (referencesTicketId) data.referencesTicket = { connect: { id: referencesTicketId } };
|
||||||
let message;
|
|
||||||
if (referencesMessage) message = await this.client.prisma.archivedMessage.findUnique({ where: { id: referencesMessage[1] } });
|
|
||||||
if (message) data.referencesMessage = { connect: { id: referencesMessage[0] } }; // only add if the message has been archived ^^
|
|
||||||
if (answers) data.questionAnswers = { createMany: { data: answers } };
|
if (answers) data.questionAnswers = { createMany: { data: answers } };
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
components: [],
|
components: [],
|
||||||
@ -583,6 +594,16 @@ module.exports = class TicketManager {
|
|||||||
await this.client.keyv.set(cacheKey, expiresAt, TTL);
|
await this.client.keyv.set(cacheKey, expiresAt, TTL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (category.guild.archive && message) {
|
||||||
|
const row = await this.archiver.saveMessage(ticket.id, message, true);
|
||||||
|
if (row) {
|
||||||
|
await this.client.prisma.ticket.update({
|
||||||
|
data: { referencesMessageId: row.id },
|
||||||
|
where: { id: ticket.id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logTicketEvent(this.client, {
|
logTicketEvent(this.client, {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
target: {
|
target: {
|
||||||
|
@ -177,11 +177,34 @@ module.exports = class extends Listener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: archive messages in tickets
|
let ticket = await client.prisma.ticket.findUnique({
|
||||||
// TODO: first response
|
include: { guild: true },
|
||||||
|
where: { id: message.channel.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ticket) {
|
||||||
|
if (ticket.guild.archive) {
|
||||||
|
try {
|
||||||
|
await client.tickets.archiver.saveMessage(ticket.id, message);
|
||||||
|
} catch (error) {
|
||||||
|
client.log.warn('Failed to archive message', message.id);
|
||||||
|
client.log.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticket.firstResponseAt === null) {
|
||||||
|
ticket = await client.prisma.ticket.update({
|
||||||
|
data: { firstResponseAt: new Date() },
|
||||||
|
where: { id: ticket.id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: lastMessageAt
|
// TODO: lastMessageAt
|
||||||
// TODO: auto tag
|
|
||||||
// TODO: staff status alert, working hours alerts
|
// TODO: staff status alert, working hours alerts
|
||||||
|
} else {
|
||||||
|
// TODO: auto tag
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user