From 45f15a2df6b448e68ed95d67da25b4e8a7226214 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Sun, 24 May 2026 21:53:54 +0200 Subject: [PATCH] (v4.2.0) Create ticket for another user (admin only) --- src/commands/ticket.ts | 36 ++++++++++++++++++++++------- src/components/modals.ts | 4 ++-- src/data/framework/commandLoader.ts | 31 +++++++++++++++---------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/commands/ticket.ts b/src/commands/ticket.ts index de83f8c..2b0e67a 100644 --- a/src/commands/ticket.ts +++ b/src/commands/ticket.ts @@ -29,24 +29,42 @@ export async function registerCommandResponders(){ instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-option-unknown").build(origin,{guild:instance.guild,channel:instance.channel,user:instance.user})) return cancel() } + + //don't allow createTicketForOtherUser to non-global-admins when enabled + const otherUser = (generalConfig.data.ticketSystem.enableCreateTicketForOtherUser) ? instance.options.getUser("user",false) : null + if (otherUser && generalConfig.data.ticketSystem.enableCreateTicketForOtherUser){ + if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild,{allowChannelRoleScope:false,allowChannelUserScope:false,allowGlobalRoleScope:true,allowGlobalUserScope:true}))){ + instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:["support"]})) + return cancel() + } + } + if (otherUser) opendiscord.log(instance.user.displayName+" created a ticket for "+otherUser.displayName+" using the 'ticket' command!","warning",[ + {key:"commanduser",value:user.username}, + {key:"commanduserid",value:user.id,hidden:true}, + {key:"ticketuser",value:otherUser.username}, + {key:"ticketuserid",value:otherUser.id,hidden:true}, + {key:"channelid",value:instance.channel.id,hidden:true}, + {key:"method",value:origin} + ]) //start ticket creation + const ticketUser = otherUser ?? user if (option.exists("opendiscord:questions") && option.get("opendiscord:questions").value.length > 0){ //SEND MODAL - instance.modal(await opendiscord.components.modals.get("opendiscord:ticket-questions").build(origin,{guild,channel,user,option})) + instance.modal(await opendiscord.components.modals.get("opendiscord:ticket-questions").build(origin,{guild,channel,user:ticketUser,option})) }else{ //check ticket permissions (modals need check after submit) - if (!(await openticketUtils.checkTicketCreationPerms(instance,origin,guild,user,option))) return cancel() + if (!(await openticketUtils.checkTicketCreationPerms(instance,origin,guild,ticketUser,option))) return cancel() //CREATE TICKET await instance.defer(true) - const res = await opendiscord.actions.get("opendiscord:create-ticket").run(origin,{guild,user,answers:[],option}) + const res = await opendiscord.actions.get("opendiscord:create-ticket").run(origin,{guild,user:ticketUser,answers:[],option}) if (!res.channel || !res.ticket){ //error - await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel:instance.channel,user,error:"Unable to receive ticket or channel from callback! #1",layout:"advanced"})) + await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel:instance.channel,user:ticketUser,error:"Unable to receive ticket or channel from callback! #1",layout:"advanced"})) return cancel() } - await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build(origin,{guild,channel:res.channel,user,ticket:res.ticket})) + await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build(origin,{guild,channel:res.channel,user:ticketUser,ticket:res.ticket})) } }), new api.ODWorker("opendiscord:logs",-1,(instance,params,origin,cancel) => { @@ -115,15 +133,17 @@ export async function registerButtonResponders(){ export async function registerModalResponders(){ //TICKET QUESTIONS RESPONDER - opendiscord.responders.modals.add(new api.ODModalResponder("opendiscord:ticket-questions",/^od:ticket-questions\|([^|]+)\|([^|]+)/)) + opendiscord.responders.modals.add(new api.ODModalResponder("opendiscord:ticket-questions",/^od:ticket-questions\|([^|]+)\|([^|]+)\|([^|]+)/)) opendiscord.responders.modals.get("opendiscord:ticket-questions").workers.add([ new api.ODWorker("opendiscord:ticket-questions",0,async (instance,params,origin,cancel) => { - const {guild,channel,user} = instance + const {guild,channel} = instance - const match = /^od:ticket-questions\|([^|]+)\|([^|]+)/.exec(instance.interaction.customId) + const match = /^od:ticket-questions\|([^|]+)\|([^|]+)\|([^|]+)/.exec(instance.interaction.customId) if (!match) return cancel() const optionId = match[1] const originalOrigin = match[2] as ("panel-button"|"panel-dropdown"|"slash"|"text"|"other") + const user = await opendiscord.client.fetchUser(match[3]) + if (!user) return cancel() //responder checks const isInGuild = await openticketUtils.replyIsInGuild(instance,origin) diff --git a/src/components/modals.ts b/src/components/modals.ts index c1ed285..93d7c3d 100644 --- a/src/components/modals.ts +++ b/src/components/modals.ts @@ -13,10 +13,10 @@ export async function registerModalComponents(){ modals.add(new api.ODComponentFactory("opendiscord:ticket-questions")) modals.get("opendiscord:ticket-questions").workers.add( new api.ODWorker("opendiscord:ticket-questions",0,(instance,params,origin) => { - const {option} = params + const {option,user} = params const modal = instance.setComponent(new api.ODModalComponent("opendiscord:questions-modal",{ - customId:"od:ticket-questions|"+option.id.value+"|"+origin, + customId:"od:ticket-questions|"+option.id.value+"|"+origin+"|"+user.id, //add user ID for creating tickets for other users title:lang.getTranslation("params.uppercase.ticket")+": "+(option.exists("opendiscord:name")) ? option.get("opendiscord:name").value : option.id.value })) diff --git a/src/data/framework/commandLoader.ts b/src/data/framework/commandLoader.ts index ec97ed0..161a33d 100644 --- a/src/data/framework/commandLoader.ts +++ b/src/data/framework/commandLoader.ts @@ -58,7 +58,22 @@ export async function loadAllSlashCommands(){ description:lang.getTranslation("commands.ticket"), contexts:[discord.InteractionContextType.Guild], integrationTypes:[discord.ApplicationIntegrationType.GuildInstall], - options:[ + options:(generalConfig.data.ticketSystem.enableCreateTicketForOtherUser) ? [ + { + name:"id", + description:lang.getTranslation("commands.ticketId"), + type:acot.String, + required:true, + autocomplete:true + }, + { + name:"user", + //TODO TRANSLATION!!! + description:"Create a ticket for another user.", + type:acot.User, + required:false, + } + ] : [ { name:"id", description:lang.getTranslation("commands.ticketId"), @@ -87,13 +102,13 @@ export async function loadAllSlashCommands(){ })) //DELETE - if (allowedCommands.includes("delete") && generalConfig.data.ticketSystem.enableDeleteWithoutTranscript) commands.add(new api.ODSlashCommand("opendiscord:delete",{ + if (allowedCommands.includes("delete")) commands.add(new api.ODSlashCommand("opendiscord:delete",{ type:act.ChatInput, name:"delete", description:lang.getTranslation("commands.delete"), contexts:[discord.InteractionContextType.Guild], integrationTypes:[discord.ApplicationIntegrationType.GuildInstall], - options:[ + options:(generalConfig.data.ticketSystem.enableDeleteWithoutTranscript) ? [ { name:"reason", description:lang.getTranslation("commands.reason"), @@ -106,15 +121,7 @@ export async function loadAllSlashCommands(){ type:acot.Boolean, required:false } - ] - })) - else if (allowedCommands.includes("delete")) commands.add(new api.ODSlashCommand("opendiscord:delete",{ - type:act.ChatInput, - name:"delete", - description:lang.getTranslation("commands.delete"), - contexts:[discord.InteractionContextType.Guild], - integrationTypes:[discord.ApplicationIntegrationType.GuildInstall], - options:[ + ] : [ { name:"reason", description:lang.getTranslation("commands.reason"),