Co-authored-by: PineappleFan <PineaFan@users.noreply.github.com>
Co-authored-by: Skyler <skyler3665@gmail.com>
pull/11/head
TheCodedProf 3 years ago
parent 44f0289275
commit cfe8e9aebe

@ -9,7 +9,6 @@ export default async function (interaction: Discord.CommandInteraction | ButtonI
if (!interaction.guild) return;
const config = await client.database.guilds.read(interaction.guild.id);
const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger;
//FIXME
const ticketChannel = config.tickets.category;
if (!("parent" in interaction.channel!)) {
return await interaction.reply({
@ -84,7 +83,8 @@ export default async function (interaction: Discord.CommandInteraction | ButtonI
embeds: [
new EmojiEmbed()
.setTitle("Archived Ticket")
.setDescription(`This ticket has been Archived. Type ${getCommandMentionByName("ticket/close")} to delete it.` +
.setDescription(`This ticket has been Archived. Type ${getCommandMentionByName("ticket/close")} to delete it.\n` +
"Creating a transcript will delete all messages in this ticket" +
await client.database.premium.hasPremium(interaction.guild.id) ?
`\n\nFor more info on transcripts, check ${getCommandMentionByName("privacy")}` :
"")

@ -1,4 +1,4 @@
import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, GuildMember, StringSelectMenuBuilder } from "discord.js";
import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, StringSelectMenuBuilder } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import client from "../../utils/client.js";
@ -10,7 +10,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
builder.setName("premium").setDescription("Information about Nucleus Premium");
//TODO: Allow User to remove Premium
const dmcallback = async (interaction: CommandInteraction, member: GuildMember, dbUser: PremiumSchema | null, firstDescription: string): Promise<void> => {
const dmcallback = async (interaction: CommandInteraction, dbUser: PremiumSchema | null, firstDescription: string): Promise<void> => {
if(!dbUser) {
await interaction.editReply({embeds: [
@ -103,7 +103,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
premium = `**You can't give servers premium anymore because your subscription ended or was cancelled.** To get premium again please subscribe in the Clicks server`
count = 0;
}
if(!interaction.guild) return await dmcallback(interaction, member, dbMember, firstDescription);
if(!interaction.guild) return await dmcallback(interaction, dbMember, firstDescription);
const hasPremium = await client.database.premium.hasPremium(interaction.guild!.id);
let premiumGuild = ""
if (hasPremium) {

@ -8,7 +8,8 @@ import {
TextChannel,
ButtonStyle,
User,
ComponentType
ComponentType,
ThreadChannel
} from "discord.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import getEmojiByName from "../utils/getEmojiByName.js";
@ -78,14 +79,21 @@ interface Transcript {
type: "ticket" | "purge"
guild: string;
channel: string;
for: TranscriptAuthor
messages: TranscriptMessage[];
createdTimestamp: number;
createdBy: TranscriptAuthor;
}
const noTopic = new EmojiEmbed()
.setTitle("User not found")
.setDescription("There is no user associated with this ticket.")
.setStatus("Danger")
.setEmoji("CONTROL.BLOCKCROSS")
export default async function (interaction: CommandInteraction | MessageComponentInteraction) {
if (interaction.channel === null) return;
if (!(interaction.channel instanceof TextChannel)) return;
if (!(interaction.channel instanceof TextChannel || interaction.channel instanceof ThreadChannel)) return;
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
let messages: Message[] = [];
@ -98,6 +106,13 @@ export default async function (interaction: CommandInteraction | MessageComponen
messages = messages.concat(Array.from(deleted.values() as Iterable<Message>));
if (messages.length === 1) messageException(interaction.guild!.id, interaction.channel.id, messages[0]!.id)
} while (deletedCount === 100);
messages = messages.filter(message => !(
message.components.some(
component => component.components.some(
child => child.customId?.includes("transcript") ?? false
)
)
));
let out = "";
messages.reverse().forEach((message) => {
@ -116,8 +131,30 @@ export default async function (interaction: CommandInteraction | MessageComponen
const interactionMember = await interaction.guild?.members.fetch(interaction.user.id)
let topic
let member: GuildMember | null = null;
if (interaction.channel instanceof TextChannel) {
topic = interaction.channel.topic;
if (topic === null) return await interaction.reply({ embeds: [noTopic] });
member = interaction.guild!.members.cache.get(topic.split(" ")[1]!) ?? null;
} else {
topic = interaction.channel.name;
const split = topic.split("-").map(p => p.trim()) as [string, string, string];
member = interaction.guild!.members.cache.get(split[1]) ?? null;
if (member === null) return await interaction.reply({ embeds: [noTopic] });
}
const newOut: Transcript = {
type: "ticket",
for: {
username: member!.user.username,
discriminator: parseInt(member!.user.discriminator),
id: member!.user.id,
topRole: {
color: member!.roles.highest.color
}
},
guild: interaction.guild!.id,
channel: interaction.channel!.id,
messages: [],
@ -184,14 +221,8 @@ export default async function (interaction: CommandInteraction | MessageComponen
newOut.messages.push(msg);
});
console.log(newOut);
const topic = interaction.channel.topic;
let member: GuildMember | null = null;
if (topic !== null) {
const part = topic.split(" ")[0] ?? null;
if (part !== null) member = interaction.guild!.members.cache.get(part) ?? null;
}
const code = await client.database.transcripts.create(newOut);
if(!code) return await interaction.reply({embeds: [new EmojiEmbed().setTitle("Error").setDescription("An error occurred while creating the transcript.").setStatus("Danger").setEmoji("CONTROL.BLOCKCROSS")]})
let m: Message;
if (out !== "") {
const url = await pbClient.createPaste({
@ -200,7 +231,7 @@ export default async function (interaction: CommandInteraction | MessageComponen
name:
`Ticket Transcript ${
member ? "for " + member.user.username + "#" + member.user.discriminator + " " : ""
}` + `(Created at ${new Date(interaction.channel.createdTimestamp).toDateString()})`,
}` + `(Created at ${new Date(interaction.channel.createdTimestamp!).toDateString()})`,
publicity: Publicity.Unlisted
});
const guildConfig = await client.database.guilds.read(interaction.guild!.id);

@ -2,7 +2,7 @@ import Discord, { Client, Interaction, AutocompleteInteraction, Collection } fro
import { Logger } from "../utils/log.js";
import Memory from "../utils/memory.js";
import type { VerifySchema } from "../reflex/verify.js";
import { Guilds, History, ModNotes, Premium, PerformanceTest, ScanCache } from "../utils/database.js";
import { Guilds, History, ModNotes, Premium, PerformanceTest, ScanCache, Transcript } from "../utils/database.js";
import EventScheduler from "../utils/eventScheduler.js";
import type { RoleMenuSchema } from "../actions/roleMenu.js";
import config from "../config/main.js";
@ -23,6 +23,7 @@ class NucleusClient extends Client {
eventScheduler: EventScheduler;
performanceTest: PerformanceTest;
scanCache: ScanCache;
transcripts: Transcript
};
preloadPage: Record<string, {command: string, argument: string}> = {}; // e.g. { channelID: { command: privacy, page: 3}}
commands: Record<string, [{
@ -46,7 +47,8 @@ const client = new NucleusClient({
premium: new Premium(),
eventScheduler: new EventScheduler(),
performanceTest: new PerformanceTest(),
scanCache: new ScanCache()
scanCache: new ScanCache(),
transcripts: new Transcript()
});
export default client;

@ -1,4 +1,4 @@
import type { GuildMember } from "discord.js";
import type { ButtonStyle, GuildMember } from "discord.js";
import type Discord from "discord.js";
import { Collection, MongoClient } from "mongodb";
import config from "../config/main.js";
@ -101,6 +101,96 @@ export class Guilds {
}
}
interface TranscriptEmbed {
title?: string;
description?: string;
fields?: {
name: string;
value: string;
inline: boolean;
}[];
footer?: {
text: string;
iconURL?: string;
};
}
interface TranscriptComponent {
type: number;
style?: ButtonStyle;
label?: string;
description?: string;
placeholder?: string;
emojiURL?: string;
}
interface TranscriptAuthor {
username: string;
discriminator: number;
nickname?: string;
id: string;
iconURL?: string;
topRole: {
color: number;
badgeURL?: string;
}
}
interface TranscriptAttachment {
url: string;
filename: string;
size: number;
log?: string;
}
interface TranscriptMessage {
id: string;
author: TranscriptAuthor;
content?: string;
embeds?: TranscriptEmbed[];
components?: TranscriptComponent[][];
editedTimestamp?: number;
createdTimestamp: number;
flags?: string[];
attachments?: TranscriptAttachment[];
stickerURLs?: string[];
referencedMessage?: string | [string, string, string];
}
interface TranscriptSchema {
code: string;
for: TranscriptAuthor;
type: "ticket" | "purge"
guild: string;
channel: string;
messages: TranscriptMessage[];
createdTimestamp: number;
createdBy: TranscriptAuthor;
}
export class Transcript {
transcripts: Collection<TranscriptSchema>;
constructor() {
this.transcripts = database.collection<TranscriptSchema>("transcripts");
}
async create(transcript: Omit<TranscriptSchema, "code">) {
let code;
do {
code = Math.random().toString(36).substring(2, 16) + Math.random().toString(36).substring(2, 16);
} while (await this.transcripts.findOne({ code: code }));
const doc = await this.transcripts.insertOne(Object.assign(transcript, { code: code }));
if(doc.acknowledged) return code;
else return null;
}
async read(code: string) {
return await this.transcripts.findOne({ code: code });
}
}
export class History {
histories: Collection<HistorySchema>;

Loading…
Cancel
Save