1- import type { ChatInputCommandInteraction } from "discord.js" ;
1+ import { TextChannel , type ChatInputCommandInteraction } from "discord.js" ;
22import {
33 ChannelType ,
44 ComponentType ,
@@ -25,13 +25,38 @@ import type {
2525 SlashCommand ,
2626} from "#~/helpers/discord" ;
2727import { quoteMessageContent } from "#~/helpers/discord" ;
28+ import db from "#~/db.server.js" ;
2829
2930const rest = new REST ( { version : "10" } ) . setToken ( discordToken ) ;
3031
32+ const DEFAULT_BUTTON_TEXT = "Open a private ticket with the moderators" ;
33+
3134export default [
3235 {
3336 command : new SlashCommandBuilder ( )
3437 . setName ( "tickets-channel" )
38+ . addRoleOption ( ( o ) => {
39+ o . setName ( "role" ) ;
40+ o . setDescription (
41+ "Which role (if any) should be pinged when a ticket is created?" ,
42+ ) ;
43+ o . setRequired ( false ) ;
44+ return o ;
45+ } )
46+ . addStringOption ( ( o ) => {
47+ o . setName ( "button-text" ) ;
48+ o . setDescription (
49+ `What should the button say? If left blank, it will say "${ DEFAULT_BUTTON_TEXT } "` ,
50+ ) ;
51+ return o ;
52+ } )
53+ . addChannelOption ( ( o ) => {
54+ o . setName ( "channel" ) ;
55+ o . setDescription (
56+ "Which channel (if not this one) should tickets be created in?" ,
57+ ) ;
58+ return o ;
59+ } )
3560 . setDescription (
3661 "Set up a new button for creating private tickets with moderators" ,
3762 )
@@ -42,21 +67,56 @@ export default [
4267 handler : async ( interaction : ChatInputCommandInteraction ) => {
4368 if ( ! interaction . guild ) throw new Error ( "Interaction has no guild" ) ;
4469
45- await interaction . reply ( {
46- components : [
47- {
48- type : ComponentType . ActionRow ,
49- components : [
50- {
51- type : ComponentType . Button ,
52- label : "Open a private ticket with the moderators" ,
53- style : ButtonStyle . Primary ,
54- customId : "open-ticket" ,
55- } ,
56- ] ,
57- } ,
58- ] ,
59- } ) ;
70+ const pingableRole = interaction . options . getRole ( "role" ) ;
71+ const ticketChannel = interaction . options . getChannel ( "channel" ) ;
72+ const buttonText =
73+ interaction . options . getString ( "button-text" ) || DEFAULT_BUTTON_TEXT ;
74+
75+ if ( ticketChannel && ticketChannel . type !== ChannelType . GuildText ) {
76+ await interaction . reply ( {
77+ content : `The channel configured must be a text channel! Tickets will be created as private threads.` ,
78+ } ) ;
79+ return ;
80+ }
81+
82+ try {
83+ const interactionResponse = await interaction . reply ( {
84+ components : [
85+ {
86+ type : ComponentType . ActionRow ,
87+ components : [
88+ {
89+ type : ComponentType . Button ,
90+ label : buttonText ,
91+ style : ButtonStyle . Primary ,
92+ customId : "open-ticket" ,
93+ } ,
94+ ] ,
95+ } ,
96+ ] ,
97+ } ) ;
98+ const producedMessage = await interactionResponse . fetch ( ) ;
99+
100+ let roleId = pingableRole ?. id ;
101+ if ( ! roleId ) {
102+ const { [ SETTINGS . moderator ] : mod } = await fetchSettings (
103+ interaction . guild ,
104+ [ SETTINGS . moderator , SETTINGS . modLog ] ,
105+ ) ;
106+ roleId = mod ;
107+ }
108+
109+ await db
110+ . insertInto ( "tickets_config" )
111+ . values ( {
112+ message_id : producedMessage . id ,
113+ channel_id : ticketChannel ?. id ,
114+ role_id : roleId ,
115+ } )
116+ . execute ( ) ;
117+ } catch ( e ) {
118+ console . error ( `error:` , e ) ;
119+ }
60120 } ,
61121 } as SlashCommand ,
62122 {
@@ -87,7 +147,8 @@ export default [
87147 ! interaction . channel ||
88148 interaction . channel . type !== ChannelType . GuildText ||
89149 ! interaction . user ||
90- ! interaction . guild
150+ ! interaction . guild ||
151+ ! interaction . message
91152 ) {
92153 await interaction . reply ( {
93154 content : "Something went wrong while creating a ticket" ,
@@ -98,19 +159,43 @@ export default [
98159 const { channel, fields, user } = interaction ;
99160 const concern = fields . getField ( "concern" ) . value ;
100161
101- const { [ SETTINGS . moderator ] : mod } = await fetchSettings (
102- interaction . guild ,
103- [ SETTINGS . moderator , SETTINGS . modLog ] ,
104- ) ;
105- const thread = await channel . threads . create ( {
162+ let config = await db
163+ . selectFrom ( "tickets_config" )
164+ . selectAll ( )
165+ . where ( "message_id" , "=" , interaction . message . id )
166+ . executeTakeFirst ( ) ;
167+ // If there's no config, that means that the button was set up before the db was set up. Add one with default values
168+ if ( ! config ) {
169+ const { [ SETTINGS . moderator ] : mod } = await fetchSettings (
170+ interaction . guild ,
171+ [ SETTINGS . moderator , SETTINGS . modLog ] ,
172+ ) ;
173+ config = await db
174+ . insertInto ( "tickets_config" )
175+ . returningAll ( )
176+ . values ( { message_id : interaction . message . id , role_id : mod } )
177+ . executeTakeFirst ( ) ;
178+ if ( ! config ) {
179+ throw new Error ( "Something went wrong while fixing tickets config" ) ;
180+ }
181+ }
182+
183+ const ticketsChannel = config . channel_id
184+ ? ( ( await interaction . guild . channels . fetch (
185+ config . channel_id ,
186+ ) ) as TextChannel ) || channel
187+ : channel ;
188+
189+ const thread = await ticketsChannel . threads . create ( {
106190 name : `${ user . username } – ${ format ( new Date ( ) , "PP kk:mmX" ) } ` ,
107191 autoArchiveDuration : 60 * 24 * 7 ,
108192 type : ChannelType . PrivateThread ,
109193 } ) ;
110194 await thread . send ( {
111- content : `<@${ user . id } >, this is a private space only visible to you and the <@&${ mod } > role.` ,
195+ content : `<@${ user . id } >, this is a private space only visible to you and the <@&${ config . role_id } > role.` ,
112196 } ) ;
113- await thread . send ( quoteMessageContent ( concern ) ) ;
197+ await thread . send ( `${ user . displayName } said:
198+ ${ quoteMessageContent ( concern ) } `) ;
114199 await thread . send ( {
115200 content : "When you’ve finished, please close the ticket." ,
116201 components : [
@@ -143,7 +228,6 @@ export default [
143228 command : { type : InteractionType . MessageComponent , name : "close-ticket" } ,
144229 handler : async ( interaction ) => {
145230 const [ , ticketOpenerUserId , feedback ] = interaction . customId . split ( "||" ) ;
146- console . log ( ticketOpenerUserId , feedback , interaction . customId ) ;
147231 const threadId = interaction . channelId ;
148232 if ( ! interaction . member || ! interaction . guild ) {
149233 console . error (
0 commit comments