Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.0.0 Features #172

Merged
merged 15 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ The documentation is available [here](https://doc.ticket.pm/)
## ⚠️ Incompatibility
This new source code you're seeing are completely refactored and will be incompatible with the older version.
I recommend you finish up all of your remaining support ticket and start migrating to the newer version.
If you prefer to stay in the older version, you can download anything that is less than `3.0.0` from the release
or clone from the `old` branch (i.e. `git clone -b old https://github.com/Sayrix/Ticket-Bot.git`)
If you prefer to stay in the older version, here is the doc for the old version: https://doc.ticket.pm/docs/oldDoc/intro

## 💬 Discord

Expand All @@ -23,6 +22,11 @@ You can come on the discord: https://discord.gg/VasYV6MEJy

Contributions are welcome! Please read the [contributing guidelines](https://github.com/Sayrix/Ticket-Bot/blob/main/CONTRIBUTING.md) first.

## 👨‍💻 Maintainers
Our current project maintainers:
* [Sayrix](https://github.com/Sayrix)
* [小兽兽/zhiyan114](https://github.com/zhiyan114)

## 💎 Sponsors
Thanks to all our sponsors! 🙏
You can see all perks here: https://github.com/sponsors/Sayrix
Expand Down
28 changes: 17 additions & 11 deletions config/config.example.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"mainColor": "#f6c42f", // The hex color of the embeds by default
"lang": "main", // If you want to set english please set "main"

"closeTicketCategoryId": "", // The id of the category where a closed ticket will be moved to. Leave blank to disable this feature

"openTicketChannelId": "1111111111111111111", // The id of the channel where the message to create a ticket will be sent

"ticketTypes": [
Expand Down Expand Up @@ -58,7 +56,14 @@
}
],
"ticketNameOption": "Ticket-TICKETCOUNT", // Here is all parameter: USERNAME, USERID, TICKETCOUNT
"ticketNamePrefixWhenClaimed": "✔️", // With ✔️ as prefix the name of the ticket will be like this: ✔️ticket-1

// Ticket Claim Options
"claimOption": {
"claimButton": true, // Whether to enable ticket claim button or not
// The X can be replaced with S (The staff that claimed the ticket) or U (The user that created the ticket)
"nameWhenClaimed": "✔️ Ticket-TICKETCOUNT", // Here is all parameter: X_USERNAME, X_USERID, TICKETCOUNT
"categoryWhenClaimed": "" // The category the ticket is moved to when claimed
},

"rolesWhoHaveAccessToTheTickets": ["1111111111111111111", "2222222222222222222"], // Roles who can access to the tickets (Like the staff)

Expand All @@ -69,14 +74,15 @@

"logs": true,
"logsChannelId": "1111111111111111111", // The id of the channel where the logs will be sent

"claimButton": true,

"whoCanCloseTicket": "STAFFONLY", // STAFFONLY (roles configured at "rolesWhoHaveAccessToTheTickets") or EVERYONE
"closeButton": true, // If false the ticket can be closed only by doing /closes
"askReasonWhenClosing": true, // If false the ticket will be closed without asking the reason

"createTranscript": true, // If set to true, when the ticket is closed a transcript will be generated and sent in the logs channel

"closeOption": {
"closeButton": true, // If false the ticket can be closed only by doing /closes
"dmUser": true, // Whether to DM the user when the ticket is closed
"createTranscript": true, // If set to true, when the ticket is closed a transcript will be generated and sent in the logs channel
"askReason": true, // If false the ticket will be closed without asking the reason
"whoCanCloseTicket": "STAFFONLY", // STAFFONLY (roles configured at "rolesWhoHaveAccessToTheTickets") or EVERYONE
"closeTicketCategoryId": "" // The id of the category where a closed ticket will be moved to. Leave blank to disable this feature
},
"uuidType": "uuid", // uuid or emoji

"status": {
Expand Down
2 changes: 1 addition & 1 deletion prisma/compatible.sql
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS tickets (
id SERIAL PRIMARY KEY,
channelid TEXT NOT NULL UNIQUE,
messageid TEXT NOT NULL UNIQUE,
category TEXT NOT NULL,
category LONGTEXT NOT NULL,
invited TEXT NOT NULL DEFAULT '[]',
reason TEXT NOT NULL,
creator TEXT NOT NULL,
Expand Down
2 changes: 1 addition & 1 deletion prisma/postgre.sql
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ this will be used for

CREATE TABLE IF NOT EXISTS config (
key VARCHAR(256) PRIMARY KEY,
value TEXT
value LONGTEXT
);

/*
Expand Down
21 changes: 14 additions & 7 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,26 @@ export type config = {
openTicketChannelId: string;
ticketTypes: TicketType[];
ticketNameOption: string;
ticketNamePrefixWhenClaimed: string;
claimOption: {
claimButton: boolean;
nameWhenClaimed?: string;
categoryWhenClaimed?: string;
};
rolesWhoHaveAccessToTheTickets: string[];
rolesWhoCanNotCreateTickets: string[];
pingRoleWhenOpened: boolean;
roleToPingWhenOpenedId: string[];
logs: boolean;
logsChannelId: string;
claimButton: boolean;
whoCanCloseTicket: "STAFFONLY" | "EVERYONE";
closeButton: boolean;
askReasonWhenClosing: boolean;
createTranscript: boolean;
uuidType: string,
closeOption: {
closeButton: boolean;
dmUser: boolean;
createTranscript: boolean;
askReason: boolean;
whoCanCloseTicket: "STAFFONLY" | "EVERYONE";
closeTicketCategoryId?: string;
};
uuidType: "uuid" | "emoji";
status: {
enabled: boolean;
text: string;
Expand Down
4 changes: 2 additions & 2 deletions src/commands/close.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default {
data: new SlashCommandBuilder().setName("close").setDescription("Close the ticket"),
async execute(interaction: CommandInteraction, client: DiscordClient) {
if (
client.config.whoCanCloseTicket === "STAFFONLY" &&
client.config.closeOption.whoCanCloseTicket === "STAFFONLY" &&
!(interaction.member as GuildMember | null)?.roles.cache.some((r) => client.config.rolesWhoHaveAccessToTheTickets.includes(r.id))
)
return interaction
Expand All @@ -33,7 +33,7 @@ export default {
})
.catch((e) => console.log(e));

if (client.config.askReasonWhenClosing) {
if (client.config.closeOption.askReason) {
closeAskReason(interaction, client);
} else {
await interaction.deferReply().catch((e) => console.log(e));
Expand Down
10 changes: 8 additions & 2 deletions src/events/ready.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,16 @@ export default {
embeds: [embed],
components: [row]
}).then((rMsg) => {
client.prisma.config.create({
data: {
client.prisma.config.upsert({
create: {
key: "openTicketMessageId",
value: rMsg.id
},
update: {
value: rMsg.id
},
where: {
key: "openTicketMessageId"
}
}).then(); // I need .then() for it to execute?!?!??
});
Expand Down
26 changes: 21 additions & 5 deletions src/utils/claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, CommandInteraction, EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ChannelType, CommandInteraction, EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import { DiscordClient } from "../Types";
import { log } from "./logs";

Expand All @@ -24,7 +24,7 @@ import { log } from "./logs";
* @param {Discord.Client} client
*/
export const claim = async(interaction: ButtonInteraction | CommandInteraction, client: DiscordClient) => {
const ticket = await client.prisma.tickets.findUnique({
let ticket = await client.prisma.tickets.findUnique({
where: {
channelid: interaction.channel?.id
}
Expand Down Expand Up @@ -66,7 +66,7 @@ export const claim = async(interaction: ButtonInteraction | CommandInteraction,
client
);

await client.prisma.tickets.update({
ticket = await client.prisma.tickets.update({
data: {
claimedby: interaction.user.id,
claimedat: Date.now()
Expand Down Expand Up @@ -101,9 +101,25 @@ export const claim = async(interaction: ButtonInteraction | CommandInteraction,
})
.catch((e) => console.log(e));

if (client.config.ticketNamePrefixWhenClaimed) {
(interaction.channel as TextChannel | null)?.setName(`${client.config.ticketNamePrefixWhenClaimed}${(interaction.channel as TextChannel | null)?.name}`).catch((e) => console.log(e));
const defaultName = client.config.claimOption.nameWhenClaimed;
if (defaultName && defaultName.trim() !== "") {
const creatorUser = await client.users.fetch(ticket.creator);
const newName = defaultName
.replaceAll("S_USERNAME", interaction.user.username)
.replaceAll("U_USERNAME", creatorUser.username)
.replaceAll("S_USERID", interaction.user.id)
.replaceAll("U_USERID", creatorUser.id)
.replaceAll("TICKETCOUNT", ticket.id.toString());
await (interaction.channel as TextChannel | null)?.setName(newName).catch((e) => console.log(e));
}

const categoryID = client.config.claimOption.categoryWhenClaimed;
if(categoryID && categoryID.trim() !== "") {
const category = await interaction.guild?.channels.fetch(categoryID);
if(category?.type !== ChannelType.GuildCategory)
return console.error("claim.ts: USER ERROR - Invalid categoryWhenClaimed ID. Channel must be a category.");
await (interaction.channel as TextChannel | null)?.setParent(category);
}
};
/*
Copyright 2023 Sayrix (github.com/Sayrix)
Expand Down
42 changes: 26 additions & 16 deletions src/utils/close.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,26 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

type ticketType = {
id: number;
channelid: string;
messageid: string;
category: string;
invited: string;
reason: string;
creator: string;
createdat: bigint;
claimedby: string | null;
claimedat: bigint | null;
closedby: string | null;
closedat: bigint | null;
closereason: string | null;
transcript: string | null;
}

export async function close(interaction: ButtonInteraction | CommandInteraction | ModalSubmitInteraction, client: DiscordClient, reason?: string) {
if (!client.config.createTranscript) domain = client.locales.other.unavailable;
if (!client.config.closeOption.createTranscript) domain = client.locales.other.unavailable;

const ticket = await client.prisma.tickets.findUnique({
where: {
Expand All @@ -33,7 +51,7 @@ export async function close(interaction: ButtonInteraction | CommandInteraction
if (!ticket) return interaction.editReply({ content: "Ticket not found" }).catch((e) => console.log(e));

if (
client.config.whoCanCloseTicket === "STAFFONLY" &&
client.config.closeOption.whoCanCloseTicket === "STAFFONLY" &&
!(interaction.member as GuildMember | null)?.roles.cache.some((r) => client.config.rolesWhoHaveAccessToTheTickets.includes(r.id))
)
return interaction
Expand Down Expand Up @@ -84,19 +102,9 @@ export async function close(interaction: ButtonInteraction | CommandInteraction
content: client.locales.ticketCreatingTranscript
})
.catch((e) => console.log(e));

async function _close(id: string) {
async function _close(id: string, ticket: ticketType) {
if (client.config.closeTicketCategoryId) (interaction.channel as TextChannel | null)?.setParent(client.config.closeTicketCategoryId).catch((e) => console.log(e));

// We can guarantee this is not null because it's already checked above.
// Should re-write this to prevent nested functions :/
let ticket = (await client.prisma.tickets.findUnique({
where: {
channelid: interaction.channel?.id
}
}));
if(!ticket) return console.error("close.ts: UNEXPECTED ERROR - _close func encountered null ticket");

const msg = await interaction.channel?.messages.fetch(ticket.messageid);
const embed = new EmbedBuilder(msg?.embeds[0].data);

Expand Down Expand Up @@ -152,6 +160,8 @@ export async function close(interaction: ButtonInteraction | CommandInteraction
})
.catch((e) => console.log(e));


if(!client.config.closeOption.dmUser) return;
const footer = lEmbed.ticketClosedDM.footer.text.replace("ticket.pm", "");
const ticketClosedDMEmbed = new EmbedBuilder({
...lEmbed,
Expand Down Expand Up @@ -181,8 +191,8 @@ export async function close(interaction: ButtonInteraction | CommandInteraction
});
}

if (!client.config.createTranscript) {
_close("");
if (!client.config.closeOption.createTranscript) {
_close("", ticket);
return;
}

Expand Down Expand Up @@ -223,7 +233,7 @@ export async function close(interaction: ButtonInteraction | CommandInteraction
}
})
.catch(console.error);
_close(ts?.data);
_close(ts?.data, ticket);
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils/close_askReason.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { DiscordClient } from "../Types";

export const closeAskReason = async(interaction: CommandInteraction | ButtonInteraction, client: DiscordClient) => {
if (
client.config.whoCanCloseTicket === "STAFFONLY" &&
client.config.closeOption.whoCanCloseTicket === "STAFFONLY" &&
!(interaction.member as GuildMember | null)?.roles.cache.some((r) => client.config.rolesWhoHaveAccessToTheTickets.includes(r.id))
)
return interaction
Expand Down
6 changes: 3 additions & 3 deletions src/utils/createTicket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ export const createTicket = async (interaction: StringSelectMenuInteraction | Mo

const row = new ActionRowBuilder<ButtonBuilder>();

if (client.config.closeButton) {
if (client.config.askReasonWhenClosing) {
if (client.config.closeOption.closeButton) {
if (client.config.closeOption.askReason) {
row.addComponents(
new ButtonBuilder()
.setCustomId("close_askReason")
Expand All @@ -176,7 +176,7 @@ export const createTicket = async (interaction: StringSelectMenuInteraction | Mo
}
}

if (client.config.claimButton) {
if (client.config.claimOption.claimButton) {
row.addComponents(
new ButtonBuilder()
.setCustomId("claim")
Expand Down