diff --git a/config/general.json b/config/general.json index ec55506..10106b8 100644 --- a/config/general.json +++ b/config/general.json @@ -5,7 +5,7 @@ "version":"open-ticket-v4.0.7" }, - "token":"your bot token here! (or leave empty when using 'tokenFromENV')", + "token":"insert your bot token here! (or leave empty when using 'tokenFromENV')", "tokenFromENV":false, "mainColor":"#f8ba00", @@ -20,21 +20,35 @@ "status":{ "enabled":true, "type":"listening OR watching OR playing OR custom", + "mode":"online OR invisible OR idle OR dnd", "text":"/help", - "status":"online OR invisible OR idle OR dnd" + "state":"(additional text or leave empty)" }, "system":{ - "removeParticipantsOnClose":false, - "replyOnTicketCreation":true, - "replyOnReactionRole":true, - "useTranslatedConfigChecker":true, "preferSlashOverText":true, "sendErrorOnUnknownCommand":true, "questionFieldsInCodeBlock":true, + "displayFieldsWithQuestions":false, + "showGlobalAdminsInPanelRoles":false, "disableVerifyBars":false, "useRedErrorEmbeds":true, + "alwaysShowReason":false, "emojiStyle":"before (OR after OR double OR disabled)", + "pinEmoji":"📌", + + "replyOnTicketCreation":true, + "replyOnReactionRole":true, + "showPreAutocloseWarning":false, + "askPriorityOnTicketCreation":false, + "removeParticipantsOnClose":false, + "disableAutocloseAfterReopen":true, + "autodeleteRequiresClosedTicket":true, + "adminOnlyDeleteWithoutTranscript":true, + "allowCloseBeforeMessage":false, + "allowCloseBeforeAdminMessage":true, + "useTranslatedConfigChecker":true, + "pinFirstTicketMessage":false, "enableTicketClaimButtons":true, "enableTicketCloseButtons":true, @@ -54,6 +68,18 @@ "userMaximum":3 }, + "channelTopic":{ + "showOptionName":true, + "showOptionDescription":false, + "showOptionTopic":true, + "showClosed":true, + "showClaimed":false, + "showPinned":false, + "showPriority":false, + "showCreator":false, + "showParticipants":false + }, + "permissions":{ "help":"everyone (OR admin OR none OR role id)", "panel":"admin (OR everyone OR none OR role id)", @@ -73,23 +99,28 @@ "stats":"everyone (OR admin OR none OR role id)", "clear":"admin (OR everyone OR none OR role id)", "autoclose":"admin (OR everyone OR none OR role id)", - "autodelete":"admin (OR everyone OR none OR role id)" + "autodelete":"admin (OR everyone OR none OR role id)", + "transfer":"admin (OR everyone OR none OR role id)", + "topic":"admin (OR everyone OR none OR role id)", + "priority":"admin (OR everyone OR none OR role id)" }, "messages":{ - "creation":{"dm":false, "logs":true}, - "closing":{"dm":false, "logs":true}, - "deleting":{"dm":false, "logs":true}, - "reopening":{"dm":false, "logs":true}, - "claiming":{"dm":false, "logs":true}, - "pinning":{"dm":false, "logs":true}, - "adding":{"dm":false, "logs":true}, - "removing":{"dm":false, "logs":true}, - "renaming":{"dm":false, "logs":true}, - "moving":{"dm":false, "logs":true}, - "blacklisting":{"dm":false, "logs":true}, - "roleAdding":{"dm":false, "logs":true}, - "roleRemoving":{"dm":false, "logs":true} + "creation":{"dm":false,"logs":true}, + "closing":{"dm":false,"logs":true}, + "deleting":{"dm":false,"logs":true}, + "reopening":{"dm":false,"logs":true}, + "claiming":{"dm":false,"logs":true}, + "pinning":{"dm":false,"logs":true}, + "adding":{"dm":false,"logs":true}, + "removing":{"dm":false,"logs":true}, + "renaming":{"dm":false,"logs":true}, + "moving":{"dm":false,"logs":true}, + "blacklisting":{"dm":false,"logs":true}, + "transferring":{"dm":false,"logs":true}, + "topicChange":{"dm":false,"logs":true}, + "priorityChange":{"dm":false,"logs":true}, + "reactionRole":{"dm":false,"logs":true} } } } \ No newline at end of file diff --git a/config/options.json b/config/options.json index 277a5ff..981a1bc 100644 --- a/config/options.json +++ b/config/options.json @@ -25,7 +25,7 @@ "claimedCategory":[ {"user":"user id","category":"category id"} ], - "description":"This is a question ticket (or leave empty)" + "topic":"This is the topic of this ticket channel and is visible to everyone! (or leave empty)" }, "dmMessage":{ @@ -87,6 +87,10 @@ "enabled":false, "globalMaximum":20, "userMaximum":3 + }, + "slowMode":{ + "enabled":false, + "slowModeSeconds":20 } }, { diff --git a/index.js b/index.js index 8d77b92..c0995a6 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ process.argv.push(...flags) ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║ ██║ ██║██║ ██╔═██╗ ██╔══╝ ██║ ╚██████╔╝██║ ███████╗██║ ╚████║ ██║ ██║╚██████╗██║ ██╗███████╗ ██║ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝ - v4.0.7 - Made by DJj123dj & Contributors + v4.1.0 - Made by DJj123dj & Contributors Discord: https://discord.dj-dj.be Docs: https://otdocs.dj-dj.be diff --git a/src/actions/createTicket.ts b/src/actions/createTicket.ts index 0974e02..9bd1467 100644 --- a/src/actions/createTicket.ts +++ b/src/actions/createTicket.ts @@ -19,7 +19,7 @@ export const registerActions = async () => { const channelPrefix = option.get("opendiscord:channel-prefix").value const channelCategory = option.get("opendiscord:channel-category").value const channelBackupCategory = option.get("opendiscord:channel-category-backup").value - const channelDescription = option.get("opendiscord:channel-description").value + const channelTopic = option.get("opendiscord:channel-topic").value const channelSuffix = await opendiscord.options.suffix.getSuffixFromOption(option,user) const channelName = channelPrefix+channelSuffix @@ -108,7 +108,7 @@ export const registerActions = async () => { type:discord.ChannelType.GuildText, name:channelName, nsfw:false, - topic:channelDescription, + topic:channelTopic, parent:category, reason:"Ticket Created By "+user.displayName, permissionOverwrites:permissions @@ -131,6 +131,7 @@ export const registerActions = async () => { new api.ODTicketData("opendiscord:ticket-message",null), new api.ODTicketData("opendiscord:participants",participants), new api.ODTicketData("opendiscord:channel-suffix",channelSuffix), + new api.ODTicketData("opendiscord:previous-creators",[]), new api.ODTicketData("opendiscord:open",true), new api.ODTicketData("opendiscord:opened-by",user.id), @@ -138,6 +139,9 @@ export const registerActions = async () => { new api.ODTicketData("opendiscord:closed",false), new api.ODTicketData("opendiscord:closed-by",null), new api.ODTicketData("opendiscord:closed-on",null), + new api.ODTicketData("opendiscord:reopened",false), + new api.ODTicketData("opendiscord:reopened-by",null), + new api.ODTicketData("opendiscord:reopened-on",null), new api.ODTicketData("opendiscord:claimed",false), new api.ODTicketData("opendiscord:claimed-by",null), new api.ODTicketData("opendiscord:claimed-on",null), @@ -155,7 +159,11 @@ export const registerActions = async () => { new api.ODTicketData("opendiscord:autodelete-enabled",option.get("opendiscord:autodelete-enable-days").value), new api.ODTicketData("opendiscord:autodelete-days",(option.get("opendiscord:autodelete-enable-days").value ? option.get("opendiscord:autodelete-days").value : 0)), - new api.ODTicketData("opendiscord:answers",answers) + new api.ODTicketData("opendiscord:answers",answers), + new api.ODTicketData("opendiscord:priority",-1), + new api.ODTicketData("opendiscord:topic",option.get("opendiscord:channel-topic").value), + new api.ODTicketData("opendiscord:message-sent",false), + new api.ODTicketData("opendiscord:admin-message-sent",false), ]) //manage stats diff --git a/src/actions/moveTicket.ts b/src/actions/moveTicket.ts index 11c21e3..5a9ef35 100644 --- a/src/actions/moveTicket.ts +++ b/src/actions/moveTicket.ts @@ -28,7 +28,7 @@ export const registerActions = async () => { const rawClaimCategory = ticket.option.get("opendiscord:channel-categories-claimed").value.find((c) => c.user == user.id) const claimCategory = (rawClaimCategory) ? rawClaimCategory.category : null const closeCategory = ticket.option.get("opendiscord:channel-category-closed").value - const channelDescription = ticket.option.get("opendiscord:channel-description").value + const channelTopic = ticket.option.get("opendiscord:channel-topic").value const channelName = channelPrefix+channelSuffix //handle category @@ -164,7 +164,7 @@ export const registerActions = async () => { await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-move",{guild,channel,user,originalName,newName:channelName})).message) } try{ - if (channel.type == discord.ChannelType.GuildText) channel.setTopic(channelDescription) + if (channel.type == discord.ChannelType.GuildText) channel.setTopic(channelTopic) }catch{} //update ticket message diff --git a/src/actions/reactionRole.ts b/src/actions/reactionRole.ts index c9cff4b..84760e1 100644 --- a/src/actions/reactionRole.ts +++ b/src/actions/reactionRole.ts @@ -88,13 +88,13 @@ export const registerActions = async () => { if (!instance.role || !instance.result) return //to logs - if (generalConfig.data.system.logs.enabled && (generalConfig.data.system.messages.roleAdding.logs || generalConfig.data.system.messages.roleRemoving.logs)){ + if (generalConfig.data.system.logs.enabled && (generalConfig.data.system.messages.reactionRole.logs)){ const logChannel = opendiscord.posts.get("opendiscord:logs") if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:reaction-role-logs").build(source,{guild,user,role:instance.role,result:instance.result})) } //to dm - if (generalConfig.data.system.messages.roleAdding.dm || generalConfig.data.system.messages.roleRemoving.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:reaction-role-dm").build(source,{guild,user,role:instance.role,result:instance.result})) + if (generalConfig.data.system.messages.reactionRole.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:reaction-role-dm").build(source,{guild,user,role:instance.role,result:instance.result})) }), new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => { const {guild,user,option} = params diff --git a/src/core/api/defaults/config.ts b/src/core/api/defaults/config.ts index 008ab00..7a50dad 100644 --- a/src/core/api/defaults/config.ts +++ b/src/core/api/defaults/config.ts @@ -76,10 +76,12 @@ export interface ODJsonConfig_DefaultStatusType { enabled:boolean, /**The type of status (e.g. playing, listening, custom, ...) */ type:Exclude, + /**The mode/status of the bot (e.g. online, invisible, idle, do not disturb) */ + mode:ODClientActivityStatus /**The text for the status. */ text:string, - /**The status of the bot (e.g. online, invisible, idle, do not disturb) */ - status:ODClientActivityStatus + /**Additional text for the status. (visible below 'text') */ + state:string, } /**## ODJsonConfig_DefaultMessageSettingsType `interface` @@ -131,6 +133,30 @@ export interface ODJsonConfig_DefaultSystemLimits { userMaximum:number } +/**## ODJsonConfig_DefaultSystemChannelTopic `interface` + * All global channel topic settings. + */ +export interface ODJsonConfig_DefaultSystemChannelTopic { + /**Show the option name in the channel topic. */ + showOptionName:boolean, + /**Show the option description in the channel topic. */ + showOptionDescription:boolean, + /**Show the option topic text in the channel topic (configured in the options config). */ + showOptionTopic:boolean, + /**Show the current close/reopen status in the channel topic (auto-updated). */ + showClosed:boolean, + /**Show the current claim status in the channel topic (auto-updated). */ + showClaimed:boolean, + /**Show the current pin status in the channel topic (auto-updated). */ + showPinned:boolean, + /**Show the current priority in the channel topic (auto-updated). */ + showPriority:boolean, + /**Show the creator of the ticket in the channel topic (auto-updated on transfer). */ + showCreator:boolean, + /**Show the first 5 participants of the ticket in the channel topic (auto-updated). */ + showParticipants:boolean +} + /**## ODJsonConfig_DefaultSystemPermissions `interface` * Configure permissions for all Open Ticket commands & actions. */ @@ -153,7 +179,10 @@ export interface ODJsonConfig_DefaultSystemPermissions { stats:ODJsonConfig_DefaultCmdPermissionSettingsType, clear:ODJsonConfig_DefaultCmdPermissionSettingsType, autoclose:ODJsonConfig_DefaultCmdPermissionSettingsType, - autodelete:ODJsonConfig_DefaultCmdPermissionSettingsType + autodelete:ODJsonConfig_DefaultCmdPermissionSettingsType, + transfer:ODJsonConfig_DefaultCmdPermissionSettingsType, + topic:ODJsonConfig_DefaultCmdPermissionSettingsType, + priority:ODJsonConfig_DefaultCmdPermissionSettingsType, } /**## ODJsonConfig_DefaultSystemMessages `interface` @@ -171,35 +200,62 @@ export interface ODJsonConfig_DefaultSystemMessages { renaming:ODJsonConfig_DefaultMessageSettingsType, moving:ODJsonConfig_DefaultMessageSettingsType, blacklisting:ODJsonConfig_DefaultMessageSettingsType, - roleAdding:ODJsonConfig_DefaultMessageSettingsType, - roleRemoving:ODJsonConfig_DefaultMessageSettingsType + transferring:ODJsonConfig_DefaultMessageSettingsType, + topicChange:ODJsonConfig_DefaultMessageSettingsType, + priorityChange:ODJsonConfig_DefaultMessageSettingsType, + reactionRole:ODJsonConfig_DefaultMessageSettingsType, } /**## ODJsonConfig_DefaultSystem `interface` * All settings related to the ticket system. */ export interface ODJsonConfig_DefaultSystem { - /**Remove all participants (except admins) from the ticket when it's closed. */ - removeParticipantsOnClose:boolean, - /**Reply with an ephemeral message when a ticket is created. */ - replyOnTicketCreation:boolean, - /**Reply with an ephemeral message when reaction roles are changed. */ - replyOnReactionRole:boolean, - /**Use a translated config checker in the console. */ - useTranslatedConfigChecker:boolean, /**Prefer slash-commands over text-commands when displaying them in menu's and messages. */ preferSlashOverText:boolean, /**Reply with "unknown command" when the prefix is used without a valid command. */ sendErrorOnUnknownCommand:boolean, /**Display the question fields (in a ticket message) in code blocks. */ questionFieldsInCodeBlock:boolean, + /**Display embed fields together with question fields (in a ticket message). */ + displayFieldsWithQuestions:boolean, + /**Show global admins roles together with ticket admins in panel embeds. */ + showGlobalAdminsInPanelRoles:boolean, /**Disable the (✅❌) buttons and directly run the action. */ disableVerifyBars:boolean, /**Display error embeds/messages with red instead of the default bot color. */ useRedErrorEmbeds:boolean, + /**Always show the reason field in embeds, even when there is no reason provided. */ + alwaysShowReason:boolean, /**The emoji style used in the bot. This will affect all embeds, titles & messages in the bot. */ emojiStyle:"before"|"after"|"double"|"disabled", - + /**The emoji used when pinning tickets. This is '📌' by default. */ + pinEmoji:string, + + /**Reply with an ephemeral message when a ticket is created. */ + replyOnTicketCreation:boolean, + /**Reply with an ephemeral message when reaction roles are changed. */ + replyOnReactionRole:boolean, + /**Show a warning message before the ticket gets autoclosed. This will happen when only 1/4th of the autoclose time remains. */ + showPreAutocloseWarning:boolean, + /**Ask for the priority of this ticket on ticket creation. This will happen in a dropdown in the ticket message. */ + askPriorityOnTicketCreation:boolean, + /**Remove all participants (except admins) from the ticket when it's closed. */ + removeParticipantsOnClose:boolean, + /**Disable autoclose for a ticket when it has been closed and re-opened. */ + disableAutocloseAfterReopen:boolean, + /**Only allow autodelete when the ticket is already closed. */ + autodeleteRequiresClosedTicket:boolean, + /**When enabled, only global admins are able to delete a ticket without transcript. */ + adminOnlyDeleteWithoutTranscript:boolean, + /**Only allow ticket closing when at least 1 message has been sent by the creator. (admins are able to bypass) */ + allowCloseBeforeMessage:boolean, + /**Only allow ticket closing when at least 1 message has been sent by a global or ticket admin. (admins are able to bypass) */ + allowCloseBeforeAdminMessage:boolean, + /**Use a translated config checker in the console. */ + useTranslatedConfigChecker:boolean, + /**Pin the (first) ticket message in the channel. This simulates old behaviour like Open Ticket v1, v2 & v3. */ + pinFirstTicketMessage:boolean, + /**Enable/disable the ticket claim & unclaim button in the ticket message. */ enableTicketClaimButtons:boolean, /**Enable/disable the ticket close & re-open button in the ticket message. */ @@ -219,6 +275,9 @@ export interface ODJsonConfig_DefaultSystem { /**All settings related to global ticket limits. */ limits:ODJsonConfig_DefaultSystemLimits, + /**All global channel topic settings. */ + channelTopic:ODJsonConfig_DefaultSystemChannelTopic, + /**Configure permissions for all Open Ticket commands & actions. */ permissions:ODJsonConfig_DefaultSystemPermissions, @@ -366,8 +425,8 @@ export interface ODJsonConfig_DefaultOptionTicketChannelType { /**The category to move the ticket to when claimed by this user. */ category:string }[], - /**The channel description/topic shown at the top of the channel in discord. */ - description:string + /**The channel topic shown at the top of the channel in discord. */ + topic:string } /**## ODJsonConfig_DefaultOptionTicketType `interface` @@ -428,14 +487,14 @@ export interface ODJsonConfig_DefaultOptionTicketType extends ODJsonConfig_Defau /**Disable autodeleting when the ticket is claimed by someone. */ disableOnClaim:boolean }, - /**All settings related to the cooldown of this ticket type */ + /**All settings related to the cooldown of this ticket type. */ cooldown:{ /**Enable cooldown (per user) */ enabled:boolean, /**The amount of minutes a user needs to wait before being able to create a ticket again. */ cooldownMinutes:number }, - /**All settings related to the limits of this ticket type */ + /**All settings related to the limits of this ticket type. */ limits:{ /**Enable option ticket limits. */ enabled:boolean, @@ -443,6 +502,13 @@ export interface ODJsonConfig_DefaultOptionTicketType extends ODJsonConfig_Defau globalMaximum:number, /**The maximum amount of tickets of this type that a user is allowed to create at the same time. */ userMaximum:number + }, + /**All settings related to the slow mode of this ticket type. */ + slowMode:{ + /**Enable channel slow mode. */ + enabled:boolean, + /**The amount of seconds users need to wait between sending messages. */ + slowModeSeconds:number } } diff --git a/src/core/api/openticket/option.ts b/src/core/api/openticket/option.ts index 75e741a..6741616 100644 --- a/src/core/api/openticket/option.ts +++ b/src/core/api/openticket/option.ts @@ -156,7 +156,7 @@ export interface ODTicketOptionIds { "opendiscord:channel-category-closed":ODOptionData, "opendiscord:channel-category-backup":ODOptionData, "opendiscord:channel-categories-claimed":ODOptionData<{user:string,category:string}[]>, - "opendiscord:channel-description":ODOptionData, + "opendiscord:channel-topic":ODOptionData, "opendiscord:dm-message-enabled":ODOptionData, "opendiscord:dm-message-text":ODOptionData, @@ -183,6 +183,9 @@ export interface ODTicketOptionIds { "opendiscord:limits-enabled":ODOptionData, "opendiscord:limits-maximum-global":ODOptionData, "opendiscord:limits-maximum-user":ODOptionData + + "opendiscord:slowmode-enabled":ODOptionData, + "opendiscord:slowmode-seconds":ODOptionData, } /**## ODTicketOption `class` diff --git a/src/core/api/openticket/ticket.ts b/src/core/api/openticket/ticket.ts index 7b03243..2d8203a 100644 --- a/src/core/api/openticket/ticket.ts +++ b/src/core/api/openticket/ticket.ts @@ -158,6 +158,7 @@ export interface ODTicketIds { "opendiscord:ticket-message":ODTicketData, "opendiscord:participants":ODTicketData<{type:"role"|"user",id:string}[]>, "opendiscord:channel-suffix":ODTicketData, + "opendiscord:previous-creators":ODTicketData, "opendiscord:open":ODTicketData, "opendiscord:opened-by":ODTicketData, @@ -165,6 +166,9 @@ export interface ODTicketIds { "opendiscord:closed":ODTicketData, "opendiscord:closed-by":ODTicketData, "opendiscord:closed-on":ODTicketData, + "opendiscord:reopened":ODTicketData, + "opendiscord:reopened-by":ODTicketData, + "opendiscord:reopened-on":ODTicketData, "opendiscord:claimed":ODTicketData, "opendiscord:claimed-by":ODTicketData, "opendiscord:claimed-on":ODTicketData, @@ -183,6 +187,10 @@ export interface ODTicketIds { "opendiscord:autodelete-days":ODTicketData, "opendiscord:answers":ODTicketData<{id:string,name:string,type:"short"|"paragraph",value:string|null}[]>, + "opendiscord:priority":ODTicketData, + "opendiscord:topic":ODTicketData, + "opendiscord:message-sent":ODTicketData, + "opendiscord:admin-message-sent":ODTicketData, } /**## ODTicket `class` diff --git a/src/core/cli/quickSetup.ts b/src/core/cli/quickSetup.ts index 7378dad..8dc6312 100644 --- a/src/core/cli/quickSetup.ts +++ b/src/core/cli/quickSetup.ts @@ -457,11 +457,11 @@ async function renderQuickSetupStatusType(backFn:() => api.ODPromiseVoid){ }).promise if (answer.canceled) return await backFn() - else if (answer.selectedIndex == 0) quickSetupStorage.status = {enabled:false,status:"online",type:"custom",text:""} - else if (answer.selectedIndex == 1) quickSetupStorage.status = {enabled:true,status:"online",type:"custom",text:""} - else if (answer.selectedIndex == 2) quickSetupStorage.status = {enabled:true,status:"online",type:"listening",text:""} - else if (answer.selectedIndex == 3) quickSetupStorage.status = {enabled:true,status:"online",type:"watching",text:""} - else if (answer.selectedIndex == 4) quickSetupStorage.status = {enabled:true,status:"online",type:"playing",text:""} + else if (answer.selectedIndex == 0) quickSetupStorage.status = {enabled:false,mode:"online",type:"custom",text:"",state:""} + else if (answer.selectedIndex == 1) quickSetupStorage.status = {enabled:true,mode:"online",type:"custom",text:"",state:""} + else if (answer.selectedIndex == 2) quickSetupStorage.status = {enabled:true,mode:"online",type:"listening",text:"",state:""} + else if (answer.selectedIndex == 3) quickSetupStorage.status = {enabled:true,mode:"online",type:"watching",text:"",state:""} + else if (answer.selectedIndex == 4) quickSetupStorage.status = {enabled:true,mode:"online",type:"playing",text:"",state:""} if (answer.selectedIndex == 0) return await renderQuickSetupLogs(async () => {await renderQuickSetupStatusType(backFn)}) else return await renderQuickSetupStatusText(async () => {await renderQuickSetupStatusType(backFn)}) @@ -1223,19 +1223,32 @@ async function saveQuickSetupConfig(){ slashCommands:quickSetupStorage.slashCommands ?? false, textCommands:quickSetupStorage.textCommands ?? false, - status:quickSetupStorage.status ?? {enabled:false,status:"online",text:"",type:"custom"}, + status:quickSetupStorage.status ?? {enabled:false,mode:"online",type:"custom",text:"",state:""}, system:{ - removeParticipantsOnClose:quickSetupStorage.removeParticipantsOnClose ?? false, - replyOnTicketCreation:false, - replyOnReactionRole:true, - useTranslatedConfigChecker:true, preferSlashOverText:quickSetupStorage.slashCommands ?? false, sendErrorOnUnknownCommand:true, questionFieldsInCodeBlock:true, + displayFieldsWithQuestions:false, + showGlobalAdminsInPanelRoles:false, disableVerifyBars:false, useRedErrorEmbeds:true, + alwaysShowReason:false, emojiStyle:quickSetupStorage.emojiStyle ?? "before", + pinEmoji:"📌", + + replyOnTicketCreation:false, + replyOnReactionRole:true, + showPreAutocloseWarning:false, + askPriorityOnTicketCreation:false, + removeParticipantsOnClose:quickSetupStorage.removeParticipantsOnClose ?? false, + disableAutocloseAfterReopen:true, + autodeleteRequiresClosedTicket:true, + adminOnlyDeleteWithoutTranscript:true, + allowCloseBeforeMessage:false, + allowCloseBeforeAdminMessage:true, + useTranslatedConfigChecker:true, + pinFirstTicketMessage:false, enableTicketClaimButtons:true, enableTicketCloseButtons:true, @@ -1254,6 +1267,18 @@ async function saveQuickSetupConfig(){ globalMaximum:100, userMaximum:quickSetupStorage.globalUserLimit ?? 3 }, + + channelTopic:{ + showOptionName:true, + showOptionDescription:false, + showOptionTopic:true, + showClosed:true, + showClaimed:false, + showPinned:false, + showPriority:false, + showCreator:false, + showParticipants:false + }, permissions:{ help:"everyone", @@ -1274,7 +1299,10 @@ async function saveQuickSetupConfig(){ stats:"everyone", clear:"admin", autoclose:"admin", - autodelete:"admin" + autodelete:"admin", + transfer:"admin", + topic:"admin", + priority:"admin", }, messages:{ @@ -1289,8 +1317,10 @@ async function saveQuickSetupConfig(){ renaming:{dm:false,logs:true}, moving:{dm:true,logs:true}, blacklisting:{dm:true,logs:true}, - roleAdding:{dm:false,logs:true}, - roleRemoving:{dm:false,logs:true} + transferring:{dm:true,logs:true}, + topicChange:{dm:false,logs:true}, + priorityChange:{dm:false,logs:true}, + reactionRole:{dm:false,logs:true} } } } @@ -1360,7 +1390,7 @@ async function saveQuickSetupConfig(){ closedCategory:"", backupCategory:"", claimedCategory:[], - description:ticket.description + topic:ticket.description }, dmMessage:{ @@ -1418,6 +1448,10 @@ async function saveQuickSetupConfig(){ enabled:false, globalMaximum:20, userMaximum:3 + }, + slowMode:{ + enabled:false, + slowModeSeconds:20 } } }) diff --git a/src/core/startup/init.ts b/src/core/startup/init.ts index a243ebe..6d1478d 100644 --- a/src/core/startup/init.ts +++ b/src/core/startup/init.ts @@ -147,7 +147,8 @@ export class ODVersionMigration { try{ await this.#func() return true - }catch{ + }catch(err){ + process.emit("uncaughtException",err) return false } } @@ -156,7 +157,8 @@ export class ODVersionMigration { try{ await this.#afterInitFunc() return true - }catch{ + }catch(err){ + process.emit("uncaughtException",err) return false } } diff --git a/src/core/startup/manageMigration.ts b/src/core/startup/manageMigration.ts index 82499c1..cd0467b 100644 --- a/src/core/startup/manageMigration.ts +++ b/src/core/startup/manageMigration.ts @@ -1,4 +1,5 @@ import {opendiscord, api, utilities} from "../../index" +import fs from "fs" /**Check if migration is required. Returns the last version used in the database. */ async function isMigrationRequired(): Promise { @@ -81,6 +82,22 @@ async function unloadMigrationContext(){ opendiscord.debug.debug("-- MIGRATION CONTEXT END --") } +/**Create a backup of the (dev)config & database before migrating. */ +function createMigrationBackup(){ + if (fs.existsSync("./.backup/")) fs.rmSync("./.backup/",{force:true,recursive:true}) + fs.mkdirSync("./.backup/") + + const devconfigFlag = opendiscord.flags.get("opendiscord:dev-config") + const isDevConfig = devconfigFlag ? devconfigFlag.value : false + const devDatabaseFlag = opendiscord.flags.get("opendiscord:dev-database") + const isDevDatabase = devDatabaseFlag ? devDatabaseFlag.value : false + + if (isDevConfig) fs.cpSync("./devconfig/","./.backup/devconfig/",{force:true,recursive:true}) + else fs.cpSync("./config/","./.backup/config/",{force:true,recursive:true}) + if (isDevDatabase) fs.cpSync("./devdatabase/","./.backup/devdatabase/",{force:true,recursive:true}) + else fs.cpSync("./database/","./.backup/database/",{force:true,recursive:true}) +} + /**Execute all version migration functions which are handled in the restricted migration context. */ async function loadAllVersionMigrations(lastVersion:api.ODVersion){ const migrations = (await import("./migration.js")).migrations @@ -90,6 +107,11 @@ async function loadAllVersionMigrations(lastVersion:api.ODVersion){ else if (comparison == "higher") return 1 else return -1 }) + if (migrations.length > 0){ + //create backup of config & database + createMigrationBackup() + } + for (const migration of migrations){ if (migration.version.compare(lastVersion) == "higher"){ const success = await migration.migrate() @@ -97,6 +119,7 @@ async function loadAllVersionMigrations(lastVersion:api.ODVersion){ {key:"success",value:success ? "true" : "false"}, {key:"afterInit",value:"false"} ]) + else throw new api.ODSystemError("Migration Error: Unable to migrate database & config to the new version of the bot.") } } } @@ -110,6 +133,11 @@ export async function loadAllAfterInitVersionMigrations(lastVersion:api.ODVersio else if (comparison == "higher") return 1 else return -1 }) + if (migrations.length > 0){ + //create backup of config & database + createMigrationBackup() + } + for (const migration of migrations){ if (migration.version.compare(lastVersion) == "higher"){ const success = await migration.migrateAfterInit() @@ -117,6 +145,7 @@ export async function loadAllAfterInitVersionMigrations(lastVersion:api.ODVersio {key:"success",value:success ? "true" : "false"}, {key:"afterInit",value:"true"} ]) + else throw new api.ODSystemError("Migration Error: Unable to migrate database & config to the new version of the bot.") } } } \ No newline at end of file diff --git a/src/core/startup/migration.ts b/src/core/startup/migration.ts index 18675a0..64376bc 100644 --- a/src/core/startup/migration.ts +++ b/src/core/startup/migration.ts @@ -31,5 +31,109 @@ export const migrations = [ new utilities.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.6"),async () => {},async () => {}), //MIGRATE TO v4.0.7 - new utilities.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.7"),async () => {},async () => {}) + new utilities.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.7"),async () => {},async () => {}), + + //MIGRATE TO v4.1.0 + new utilities.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.0"),async () => {},async () => { + //AFTER INIT MIGRATION + + //migrate config + const generalConfig = opendiscord.configs.get("opendiscord:general") + const optionConfig = opendiscord.configs.get("opendiscord:options") + + if (!generalConfig.data.status.state){ + //only migrate config when it hasn't been done manually by the user. + + if (!generalConfig.data._INFO) throw new api.ODSystemError("Couldn't find general.json '_INFO' category.") + generalConfig.data._INFO.version = "open-ticket-v4.1.0" + + if (!generalConfig.data.status) throw new api.ODSystemError("Couldn't find general.json 'status' category.") + generalConfig.data.status.mode = generalConfig.data.status["status"] ?? "online" + generalConfig.data.status.state = "" + delete generalConfig.data.status["status"] + + if (!generalConfig.data.system) throw new api.ODSystemError("Couldn't find general.json 'system' category.") + generalConfig.data.system.displayFieldsWithQuestions = false + generalConfig.data.system.showGlobalAdminsInPanelRoles = false + generalConfig.data.system.alwaysShowReason = false + generalConfig.data.system.pinEmoji = "📌" + generalConfig.data.system.showPreAutocloseWarning = false + generalConfig.data.system.askPriorityOnTicketCreation = false + generalConfig.data.system.disableAutocloseAfterReopen = true + generalConfig.data.system.autodeleteRequiresClosedTicket = true + generalConfig.data.system.adminOnlyDeleteWithoutTranscript = true + generalConfig.data.system.allowCloseBeforeMessage = false + generalConfig.data.system.allowCloseBeforeAdminMessage = true + generalConfig.data.system.pinFirstTicketMessage = false + + generalConfig.data.system.channelTopic = { + showOptionName:true, + showOptionDescription:false, + showOptionTopic:true, + showClosed:true, + showClaimed:false, + showPinned:false, + showPriority:false, + showCreator:false, + showParticipants:false + } + + if (!generalConfig.data.system.permissions) throw new api.ODSystemError("Couldn't find general.json 'system.permissions' category.") + generalConfig.data.system.permissions.transfer = "admin" + generalConfig.data.system.permissions.topic = "admin" + generalConfig.data.system.permissions.priority = "admin" + + if (!generalConfig.data.system.messages) throw new api.ODSystemError("Couldn't find general.json 'system.messages' category.") + generalConfig.data.system.messages.transferring = {dm:false,logs:true} + generalConfig.data.system.messages.topicChange = {dm:false,logs:true} + generalConfig.data.system.messages.priorityChange = {dm:false,logs:true} + generalConfig.data.system.messages.reactionRole = generalConfig.data.system.messages["roleAdding"] ?? {dm:false,logs:true} + delete generalConfig.data.system.messages["roleAdding"] + delete generalConfig.data.system.messages["roleRemoving"] + + for (const option of optionConfig.data){ + if (option.type != "ticket") continue + option.channel.topic = option.channel["description"] ?? "" + delete option.channel["description"] + + option.slowMode = { + enabled:false, + slowModeSeconds:20 + } + } + + await generalConfig.save() + await optionConfig.save() + } + + //migrate database + const optionDatabase = opendiscord.databases.get("opendiscord:options") + const ticketDatabase = opendiscord.databases.get("opendiscord:tickets") + + for (const option of (await optionDatabase.getCategory("opendiscord:used-option") ?? [])){ + const optionData = option.value + + const topicData = optionData.data.find((d) => d.id == "opendiscord:channel-description") + if (topicData) topicData.id = "opendiscord:channel-topic" + if (!optionData.data.find((d) => d.id == "opendiscord:slowmode-enabled")) optionData.data.push({id:"opendiscord:slowmode-enabled",value:false}) + if (!optionData.data.find((d) => d.id == "opendiscord:slowmode-seconds")) optionData.data.push({id:"opendiscord:slowmode-seconds",value:20}) + + optionDatabase.set("opendiscord:used-option",option.key,optionData) + } + + for (const ticket of (await ticketDatabase.getCategory("opendiscord:ticket") ?? [])){ + const ticketData = ticket.value + + if (!ticketData.data.find((d) => d.id == "opendiscord:previous-creators")) ticketData.data.push({id:"opendiscord:previous-creators",value:[]}) + if (!ticketData.data.find((d) => d.id == "opendiscord:reopened")) ticketData.data.push({id:"opendiscord:reopened",value:false}) + if (!ticketData.data.find((d) => d.id == "opendiscord:reopened-by")) ticketData.data.push({id:"opendiscord:reopened-by",value:null}) + if (!ticketData.data.find((d) => d.id == "opendiscord:reopened-on")) ticketData.data.push({id:"opendiscord:reopened-on",value:null}) + if (!ticketData.data.find((d) => d.id == "opendiscord:priority")) ticketData.data.push({id:"opendiscord:priority",value:-1}) + if (!ticketData.data.find((d) => d.id == "opendiscord:topic")) ticketData.data.push({id:"opendiscord:topic",value:""}) + if (!ticketData.data.find((d) => d.id == "opendiscord:message-sent")) ticketData.data.push({id:"opendiscord:message-sent",value:true}) + if (!ticketData.data.find((d) => d.id == "opendiscord:admin-message-sent")) ticketData.data.push({id:"opendiscord:admin-message-sent",value:true}) + + ticketDatabase.set("opendiscord:ticket",ticket.key,ticketData) + } + }) ] \ No newline at end of file diff --git a/src/data/framework/checkerLoader.ts b/src/data/framework/checkerLoader.ts index 1698b00..adab440 100644 --- a/src/data/framework/checkerLoader.ts +++ b/src/data/framework/checkerLoader.ts @@ -245,8 +245,9 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis checker:new api.ODCheckerObjectStructure("opendiscord:status",{children:[ {key:"enabled",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:status-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable the status. When disabled, the bot will be online without any status."})}, {key:"type",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:status-type",{choices:["listening","watching","playing","custom"],cliDisplayName:"Type",cliDisplayDescription:"The type of status: Listening, Watching, Playing or Custom."})}, + {key:"mode",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:status-mode",{choices:["online","invisible","idle","dnd"],cliDisplayName:"Mode",cliDisplayDescription:"The profile status/mode of the bot: Online, Invisible, Idle or Do Not Disturb."})}, {key:"text",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:status-text",{minLength:1,maxLength:128,cliDisplayName:"Text",cliDisplayDescription:"The text displayed in the status."})}, - {key:"status",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:status-status",{choices:["online","invisible","idle","dnd"],cliDisplayName:"Status",cliDisplayDescription:"The profile status of the bot: Online, Invisible, Idle or Do Not Disturb."})}, + {key:"state",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:status-state",{maxLength:128,cliDisplayName:"State",cliDisplayDescription:"Additional text displayed below the status 'text'."})}, ],cliDisplayName:"Bot Status",cliDisplayDescription:"Manage the status of the bot."}), cliDisplayName:"Bot Status", cliDisplayDescription:"Manage the status of the bot." @@ -254,16 +255,29 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis //SYSTEM {key:"system",optional:false,priority:0,checker:new api.ODCheckerObjectStructure("opendiscord:system",{children:[ - {key:"removeParticipantsOnClose",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:remove-participants-on-close",{cliDisplayName:"Remove Participants On Close",cliDisplayDescription:"When enabled, all participants except admins will be removed from the ticket."})}, - {key:"replyOnTicketCreation",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:reply-on-ticket-creation",{cliDisplayName:"Reply On Ticket Creation",cliDisplayDescription:"When enabled, the bot will send an ephemeral reply in the channel of the panel when creating a ticket."})}, - {key:"replyOnReactionRole",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:reply-on-reaction-role",{cliDisplayName:"Reply On Reaction Role",cliDisplayDescription:"When enabled, the bot will send an ephemeral reply in the channel of the panel when using a role button."})}, - {key:"useTranslatedConfigChecker",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:use-translated-config-checker",{cliDisplayName:"Use Translated Config Checker",cliDisplayDescription:"Use a translated config checker to better understand the errors the bot gives."})}, {key:"preferSlashOverText",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:prefer-slash-over-text",{cliDisplayName:"Prefer Slash Over Text",cliDisplayDescription:"Prefer displaying slash commands over text commands in help menus."})}, {key:"sendErrorOnUnknownCommand",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:send-error-on-unknown-command",{cliDisplayName:"Send Error On Unknown Command",cliDisplayDescription:"Send an error when using the text-command prefix without a valid command."})}, {key:"questionFieldsInCodeBlock",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:question-fields-in-code-block",{cliDisplayName:"Questions Fields In Code Blocks",cliDisplayDescription:"Display question fields in code blocks instead of plain text."})}, + {key:"displayFieldsWithQuestions",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:display-fields-with-questions",{cliDisplayName:"Display Fields With Questions",cliDisplayDescription:"Display embed fields together with question fields (in a ticket message)."})}, + {key:"showGlobalAdminsInPanelRoles",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:global-admins-in-panel-roles",{cliDisplayName:"Show Global Admins In Panel Roles",cliDisplayDescription:"Show global admins roles together with ticket admins in panel embeds."})}, {key:"disableVerifyBars",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:disable-verify-bars",{cliDisplayName:"Disable Verifybars",cliDisplayDescription:"Disable the (✅/❌) verify buttons in all commands. (Not recommended)"})}, {key:"useRedErrorEmbeds",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:use-red-error-embeds",{cliDisplayName:"Use Red Error Embeds",cliDisplayDescription:"Display all error messages with a red border instead of the default color of the bot."})}, + {key:"alwaysShowReason",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:always-show-reason",{cliDisplayName:"Always Show Reason",cliDisplayDescription:"Always show the reason field in embeds, even when there is no reason provided."})}, {key:"emojiStyle",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:emoji-style",{choices:["before","after","double","disabled"],cliDisplayName:"Emoji Style",cliDisplayDescription:"Choose how the bot will display emojis in message titles. (Visit docs for more info)"})}, + {key:"pinEmoji",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:pin-emoji",1,1,false,{cliDisplayName:"Pin Emoji",cliDisplayDescription:"The emoji used when pinning tickets. This is '📌' by default."})}, + + {key:"replyOnTicketCreation",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:reply-on-ticket-creation",{cliDisplayName:"Reply On Ticket Creation",cliDisplayDescription:"When enabled, the bot will send an ephemeral reply in the channel of the panel when creating a ticket."})}, + {key:"replyOnReactionRole",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:reply-on-reaction-role",{cliDisplayName:"Reply On Reaction Role",cliDisplayDescription:"When enabled, the bot will send an ephemeral reply in the channel of the panel when using a role button."})}, + {key:"showPreAutocloseWarning",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:show-pre-autoclose-warning",{cliDisplayName:"Show Pre-Autoclose Warning",cliDisplayDescription:"Show a warning message before the ticket gets autoclosed. This will happen when only 1/4th of the autoclose time remains."})}, + {key:"askPriorityOnTicketCreation",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:ask-priority-creation",{cliDisplayName:"Ask Priority On Ticket Creation",cliDisplayDescription:"Ask for the priority of this ticket on ticket creation. This will happen in a dropdown in the ticket message."})}, + {key:"removeParticipantsOnClose",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:remove-participants-on-close",{cliDisplayName:"Remove Participants On Close",cliDisplayDescription:"When enabled, all participants except admins will be removed from the ticket."})}, + {key:"disableAutocloseAfterReopen",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:disable-autoclose-reopen",{cliDisplayName:"Disable Autoclose On Reopen",cliDisplayDescription:"Disable autoclose for a ticket when it has been closed and re-opened."})}, + {key:"autodeleteRequiresClosedTicket",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:autodelete-requires-closed-ticket",{cliDisplayName:"Autodelete Requires Closed Ticket",cliDisplayDescription:"Only allow autodelete when the ticket is already closed."})}, + {key:"adminOnlyDeleteWithoutTranscript",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:adminonly-delete-without-transcript",{cliDisplayName:"Admin-only Delete Without Transcript",cliDisplayDescription:"When enabled, only global admins are able to delete a ticket without transcript."})}, + {key:"allowCloseBeforeMessage",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:allow-close-before-message",{cliDisplayName:"Allow Close Before Message",cliDisplayDescription:"Only allow ticket closing when at least 1 message has been sent by the creator. (admins are able to bypass)"})}, + {key:"allowCloseBeforeAdminMessage",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:allow-close-before-admin-message",{cliDisplayName:"Allow Close Before Admin Message",cliDisplayDescription:"Only allow ticket closing when at least 1 message has been sent by a global or ticket admin. (admins are able to bypass)"})}, + {key:"useTranslatedConfigChecker",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:use-translated-config-checker",{cliDisplayName:"Use Translated Config Checker",cliDisplayDescription:"Use a translated config checker to better understand the errors the bot gives."})}, + {key:"pinFirstTicketMessage",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:pin-first-ticket-message",{cliDisplayName:"Pin First Ticket Message",cliDisplayDescription:"Pin the (first) ticket message in the channel. This simulates old behaviour like Open Ticket v1, v2 & v3."})}, {key:"enableTicketClaimButtons",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:enable-ticket-claim-buttons",{cliDisplayName:"Enable Ticket Claim Buttons",cliDisplayDescription:"Enable/disable buttons for claiming a ticket. Be aware that this doesn't disable the command!"})}, {key:"enableTicketCloseButtons",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:enable-ticket-close-buttons",{cliDisplayName:"Enable Ticket Close Buttons",cliDisplayDescription:"Enable/disable buttons for closing a ticket. Be aware that this doesn't disable the command!"})}, @@ -283,6 +297,19 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis {key:"userMaximum",optional:false,priority:0,checker:new api.ODCheckerNumberStructure("opendiscord:limits-user",{zeroAllowed:false,negativeAllowed:false,floatAllowed:false,min:1,cliDisplayName:"User Maximum",cliDisplayDescription:"The maximum amount of tickets from a specific user that are able to exist in the server at the same time."})} ],cliDisplayName:"Global Limits",cliDisplayDescription:"Manage global limits for ticket creation to reduce the workload on your support team."}),cliDisplayName:"Global Limits",cliDisplayDescription:"Manage global limits for ticket creation to reduce the workload on your support team."})}, + {key:"channelTopic",optional:false,priority:0,checker:new api.ODCheckerObjectStructure("opendiscord:channel-topic",{children:[ + {key:"showOptionName",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-option-name",{cliDisplayName:"Show Option Name",cliDisplayDescription:"Show the option name in the channel topic."})}, + {key:"showOptionDescription",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-option-description",{cliDisplayName:"Show Option Description",cliDisplayDescription:"Show the option description in the channel topic."})}, + {key:"showOptionTopic",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-option-topic",{cliDisplayName:"Show Option Topic",cliDisplayDescription:"Show the option topic text in the channel topic (configured in the options.json config)."})}, + {key:"showClosed",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-closed",{cliDisplayName:"Show Closed Status",cliDisplayDescription:"Show the current close/reopen status in the channel topic (auto-updated)."})}, + {key:"showClaimed",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-claimed",{cliDisplayName:"Show Claimed Status",cliDisplayDescription:"Show the current claim status in the channel topic (auto-updated)."})}, + {key:"showPinned",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-pinned",{cliDisplayName:"Show Pinned Status",cliDisplayDescription:"Show the current pin status in the channel topic (auto-updated)."})}, + {key:"showPriority",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-priority",{cliDisplayName:"Show Priority",cliDisplayDescription:"Show the current priority in the channel topic (auto-updated)."})}, + {key:"showCreator",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-creator",{cliDisplayName:"Show Creator",cliDisplayDescription:"Show the creator of the ticket in the channel topic (auto-updated on transfer)."})}, + {key:"showParticipants",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-participants",{cliDisplayName:"Show Participants",cliDisplayDescription:"Show the first 5 participants of the ticket in the channel topic (auto-updated)."})}, + + ],cliDisplayName:"Channel Topic",cliDisplayDescription:"Manage stats and text of ticket channel topics."})}, + {key:"permissions",optional:false,priority:0,checker:new api.ODCheckerObjectStructure("opendiscord:system-permissions",{children:[ {key:"help",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-help","role",false,["admin","everyone","none"],{cliDisplayName:"Help",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, {key:"panel",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-panel","role",false,["admin","everyone","none"],{cliDisplayName:"Panel",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, @@ -302,7 +329,10 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis {key:"stats",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-stats","role",false,["admin","everyone","none"],{cliDisplayName:"Stats",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, {key:"clear",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-clear","role",false,["admin","everyone","none"],{cliDisplayName:"Clear Tickets",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, {key:"autoclose",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-autoclose","role",false,["admin","everyone","none"],{cliDisplayName:"Autoclose",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, - {key:"autodelete",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-autodelete","role",false,["admin","everyone","none"],{cliDisplayName:"Autodelete",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})} + {key:"autodelete",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-autodelete","role",false,["admin","everyone","none"],{cliDisplayName:"Autodelete",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, + {key:"transfer",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-transfer","role",false,["admin","everyone","none"],{cliDisplayName:"Transfer",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, + {key:"topic",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-topic","role",false,["admin","everyone","none"],{cliDisplayName:"Topic",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, + {key:"priority",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-priority","role",false,["admin","everyone","none"],{cliDisplayName:"Priority",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})}, ],cliDisplayName:"Permissions",cliDisplayDescription:"Manage all button & command permissions in the bot. (Visit docs for more info)"})}, {key:"messages",optional:false,priority:0,checker:new api.ODCheckerObjectStructure("opendiscord:system-permissions",{children:[ @@ -317,8 +347,10 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis {key:"renaming",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-renaming","Ticket Renamed")}, {key:"moving",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-moving","Ticket Moved")}, {key:"blacklisting",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-blacklisting","User Blacklisted")}, - {key:"roleAdding",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-role-adding","Role Added")}, - {key:"roleRemoving",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-role-removing","Role Removed")} + {key:"transferring",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-transferring","Ticket Transferred")}, + {key:"topicChange",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-topic-change","Topic Changed")}, + {key:"priorityChange",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-priority-change","Priority Changed")}, + {key:"reactionRole",optional:false,priority:0,checker:createMsgStructure("opendiscord:msg-reaction-role","Reaction Role")}, ],cliDisplayName:"Messages",cliDisplayDescription:"Manage all messages & DM's for each action of the bot. (Visit docs for more info)"})}, ],cliDisplayName:"System",cliDisplayDescription:"Configure everything related to the ticket system."})} ],cliDisplayName:"General",cliDisplayDescription:"General settings for the bot."}) @@ -369,7 +401,7 @@ export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendisc {key:"user",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:ticket-channel-claimed-user","user",false,[],{cliDisplayName:"User",cliDisplayDescription:"A discord user ID of the ticket claimer."})}, {key:"category",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:ticket-channel-claimed-category","category",false,[],{cliDisplayName:"Category",cliDisplayDescription:"A discord category ID to move the ticket to."})} ],cliDisplayName:"Claimed Category",cliDisplayDescription:"A collection of a user ID and a category ID. The ticket will be moved to the category when this user claims the ticket."}),cliDisplayName:"Claimed Categories",cliDisplayDescription:"Add categories to move the ticket to when a user claims a ticket."})}, - {key:"description",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:ticket-channel-description",{cliDisplayName:"Channel Description",cliDisplayDescription:"The description of the ticket channel. Visible in the discord client."})}, + {key:"topic",optional:false,priority:0,checker:new api.ODCheckerStringStructure("opendiscord:ticket-channel-topic",{cliDisplayName:"Channel Topic",cliDisplayDescription:"The topic text of the ticket channel. Visible in the discord client when general.json 'channelTopic'.'showOptionTopic' is enabled."})}, ],cliDisplayName:"Channel",cliDisplayDescription:"Manage all settings related to the ticket channel and categories."})}, //DM MESSAGE @@ -415,6 +447,13 @@ export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendisc {key:"globalMaximum",optional:false,priority:0,checker:new api.ODCheckerNumberStructure("opendiscord:ticket-limits-global",{zeroAllowed:false,negativeAllowed:false,floatAllowed:false,min:1,cliInitDefaultValue:10,cliDisplayName:"Global Maximum",cliDisplayDescription:"The maximum amount of tickets of this type/option that are able to exist in the server at the same time."})}, {key:"userMaximum",optional:false,priority:0,checker:new api.ODCheckerNumberStructure("opendiscord:ticket-limits-user",{zeroAllowed:false,negativeAllowed:false,floatAllowed:false,min:1,cliInitDefaultValue:3,cliDisplayName:"User Maximum",cliDisplayDescription:"The maximum amount of tickets of this type/option from a specific user that are able to exist in the server at the same time."})} ],cliDisplayName:"Option Limits",cliDisplayDescription:"Manage option-based limits for ticket creation to reduce the workload on your support team."}),cliDisplayName:"Limits",cliDisplayDescription:"Manage option-based limits for ticket creation to reduce the workload on your support team."})}, + + //SLOW MODE + {key:"slowMode",optional:false,priority:0,checker:new api.ODCheckerEnabledObjectStructure("opendiscord:ticket-slowmode",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:ticket-limits",{children:[ + {key:"enabled",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("opendiscord:ticket-slowmode-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable ticket slow mode."})}, + {key:"slowModeSeconds",optional:false,priority:0,checker:new api.ODCheckerNumberStructure("opendiscord:ticket-slowmode-seconds",{zeroAllowed:false,negativeAllowed:false,floatAllowed:false,min:1,cliInitDefaultValue:20,cliDisplayName:"Slow Mode Seconds",cliDisplayDescription:"The amount of seconds users need to wait between sending messages."})}, + ],cliDisplayName:"Option Slow Mode",cliDisplayDescription:"Add slow mode to this ticket option to restrict the amount of message spam users can send."}),cliDisplayName:"Slow Mode",cliDisplayDescription:"Add slow mode to this ticket option to restrict the amount of message spam users can send."})}, + ],cliDisplayName:"Ticket Option",cliDisplayDescription:"Manage all ticket-specific settings of this option/type."})}, //WEBSITE diff --git a/src/data/framework/configLoader.ts b/src/data/framework/configLoader.ts index 54a0a63..f902249 100644 --- a/src/data/framework/configLoader.ts +++ b/src/data/framework/configLoader.ts @@ -55,21 +55,35 @@ export const defaultGeneralFormatter = new fjs.ObjectFormatter(null,true,[ new fjs.ObjectFormatter("status",true,[ new fjs.PropertyFormatter("enabled"), new fjs.PropertyFormatter("type"), + new fjs.PropertyFormatter("mode"), new fjs.PropertyFormatter("text"), - new fjs.PropertyFormatter("status"), + new fjs.PropertyFormatter("state"), ]), new fjs.TextFormatter(""), new fjs.ObjectFormatter("system",true,[ - new fjs.PropertyFormatter("removeParticipantsOnClose"), - new fjs.PropertyFormatter("replyOnTicketCreation"), - new fjs.PropertyFormatter("replyOnReactionRole"), - new fjs.PropertyFormatter("useTranslatedConfigChecker"), new fjs.PropertyFormatter("preferSlashOverText"), new fjs.PropertyFormatter("sendErrorOnUnknownCommand"), new fjs.PropertyFormatter("questionFieldsInCodeBlock"), + new fjs.PropertyFormatter("displayFieldsWithQuestions"), + new fjs.PropertyFormatter("showGlobalAdminsInPanelRoles"), new fjs.PropertyFormatter("disableVerifyBars"), new fjs.PropertyFormatter("useRedErrorEmbeds"), + new fjs.PropertyFormatter("alwaysShowReason"), new fjs.PropertyFormatter("emojiStyle"), + new fjs.PropertyFormatter("pinEmoji"), + new fjs.TextFormatter(""), + new fjs.PropertyFormatter("replyOnTicketCreation"), + new fjs.PropertyFormatter("replyOnReactionRole"), + new fjs.PropertyFormatter("showPreAutocloseWarning"), + new fjs.PropertyFormatter("askPriorityOnTicketCreation"), + new fjs.PropertyFormatter("removeParticipantsOnClose"), + new fjs.PropertyFormatter("disableAutocloseAfterReopen"), + new fjs.PropertyFormatter("autodeleteRequiresClosedTicket"), + new fjs.PropertyFormatter("adminOnlyDeleteWithoutTranscript"), + new fjs.PropertyFormatter("allowCloseBeforeMessage"), + new fjs.PropertyFormatter("allowCloseBeforeAdminMessage"), + new fjs.PropertyFormatter("useTranslatedConfigChecker"), + new fjs.PropertyFormatter("pinFirstTicketMessage"), new fjs.TextFormatter(""), new fjs.PropertyFormatter("enableTicketClaimButtons"), new fjs.PropertyFormatter("enableTicketCloseButtons"), @@ -89,6 +103,18 @@ export const defaultGeneralFormatter = new fjs.ObjectFormatter(null,true,[ new fjs.PropertyFormatter("userMaximum"), ]), new fjs.TextFormatter(""), + new fjs.ObjectFormatter("channelTopic",true,[ + new fjs.PropertyFormatter("showOptionName"), + new fjs.PropertyFormatter("showOptionDescription"), + new fjs.PropertyFormatter("showOptionTopic"), + new fjs.PropertyFormatter("showClosed"), + new fjs.PropertyFormatter("showClaimed"), + new fjs.PropertyFormatter("showPinned"), + new fjs.PropertyFormatter("showPriority"), + new fjs.PropertyFormatter("showCreator"), + new fjs.PropertyFormatter("showParticipants"), + ]), + new fjs.TextFormatter(""), new fjs.ObjectFormatter("permissions",true,[ new fjs.PropertyFormatter("help"), new fjs.PropertyFormatter("panel"), @@ -109,6 +135,9 @@ export const defaultGeneralFormatter = new fjs.ObjectFormatter(null,true,[ new fjs.PropertyFormatter("clear"), new fjs.PropertyFormatter("autoclose"), new fjs.PropertyFormatter("autodelete"), + new fjs.PropertyFormatter("transfer"), + new fjs.PropertyFormatter("topic"), + new fjs.PropertyFormatter("priority"), ]), new fjs.TextFormatter(""), new fjs.ObjectFormatter("messages",true,[ @@ -123,8 +152,10 @@ export const defaultGeneralFormatter = new fjs.ObjectFormatter(null,true,[ new fjs.DefaultFormatter("renaming",false), new fjs.DefaultFormatter("moving",false), new fjs.DefaultFormatter("blacklisting",false), - new fjs.DefaultFormatter("roleAdding",false), - new fjs.DefaultFormatter("roleRemoving",false), + new fjs.DefaultFormatter("transferring",false), + new fjs.DefaultFormatter("topicChange",false), + new fjs.DefaultFormatter("priorityChange",false), + new fjs.DefaultFormatter("reactionRole",false) ]), ]), ]) @@ -187,7 +218,7 @@ export const defaultOptionsFormatter = new fjs.ArrayFormatter(null,true,new fjs. new fjs.PropertyFormatter("user"), new fjs.PropertyFormatter("category"), ])), - new fjs.PropertyFormatter("description"), + new fjs.PropertyFormatter("topic"), ]), new fjs.TextFormatter(""), new fjs.ObjectFormatter("dmMessage",true,[ @@ -254,6 +285,10 @@ export const defaultOptionsFormatter = new fjs.ArrayFormatter(null,true,new fjs. new fjs.PropertyFormatter("globalMaximum"), new fjs.PropertyFormatter("userMaximum"), ]), + new fjs.ObjectFormatter("slowMode",true,[ + new fjs.PropertyFormatter("enabled"), + new fjs.PropertyFormatter("slowModeSeconds"), + ]), ])}, {key:"type",value:"website",formatter:new fjs.ObjectFormatter(null,true,[ new fjs.PropertyFormatter("id"), diff --git a/src/data/openticket/optionLoader.ts b/src/data/openticket/optionLoader.ts index 302f7d2..0124902 100644 --- a/src/data/openticket/optionLoader.ts +++ b/src/data/openticket/optionLoader.ts @@ -74,7 +74,7 @@ export const loadTicketOption = (option:api.ODJsonConfig_DefaultOptionTicketType new api.ODOptionData("opendiscord:channel-category-closed",option.channel.closedCategory), new api.ODOptionData("opendiscord:channel-category-backup",option.channel.backupCategory), new api.ODOptionData("opendiscord:channel-categories-claimed",option.channel.claimedCategory), - new api.ODOptionData("opendiscord:channel-description",option.channel.description), + new api.ODOptionData("opendiscord:channel-topic",option.channel.topic), new api.ODOptionData("opendiscord:dm-message-enabled",option.dmMessage.enabled), new api.ODOptionData("opendiscord:dm-message-text",option.dmMessage.text), @@ -100,7 +100,10 @@ export const loadTicketOption = (option:api.ODJsonConfig_DefaultOptionTicketType new api.ODOptionData("opendiscord:limits-enabled",option.limits.enabled), new api.ODOptionData("opendiscord:limits-maximum-global",option.limits.globalMaximum), - new api.ODOptionData("opendiscord:limits-maximum-user",option.limits.userMaximum) + new api.ODOptionData("opendiscord:limits-maximum-user",option.limits.userMaximum), + + new api.ODOptionData("opendiscord:slowmode-enabled",option.slowMode.enabled), + new api.ODOptionData("opendiscord:slowmode-seconds",option.slowMode.slowModeSeconds) ]) } diff --git a/src/data/openticket/ticketLoader.ts b/src/data/openticket/ticketLoader.ts index 33b7815..86b88ca 100644 --- a/src/data/openticket/ticketLoader.ts +++ b/src/data/openticket/ticketLoader.ts @@ -27,7 +27,10 @@ export const loadTicket = async (ticket:api.ODTicketJson) => { //manage backup option (+ sync version of options with latest OT version in database) if (configOption) await optionDatabase.set("opendiscord:used-option",configOption.id.value,configOption.toJson(opendiscord.versions.get("opendiscord:version"))) - else if (backupOption) opendiscord.options.add(backupOption) + else if (backupOption){ + opendiscord.options.add(backupOption) + await optionDatabase.set("opendiscord:used-option",backupOption.id.value,backupOption.toJson(opendiscord.versions.get("opendiscord:version"))) + } else throw new api.ODSystemError("Unable to use backup option! Normal option not found in config!") //load ticket & option diff --git a/src/index.ts b/src/index.ts index 587ae1c..e319bce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -412,7 +412,7 @@ const main = async () => { opendiscord.log("Loading client activity...","system") if (opendiscord.defaults.getDefault("clientActivityLoading")){ //load config status - if (generalConfig.data.status && generalConfig.data.status.enabled) opendiscord.client.activity.setStatus(generalConfig.data.status.type,generalConfig.data.status.text,generalConfig.data.status.status) + if (generalConfig.data.status && generalConfig.data.status.enabled) opendiscord.client.activity.setStatus(generalConfig.data.status.type,generalConfig.data.status.text,generalConfig.data.status.mode) } await opendiscord.events.get("onClientActivityLoad").emit([opendiscord.client.activity,opendiscord.client]) await opendiscord.events.get("afterClientActivityLoaded").emit([opendiscord.client.activity,opendiscord.client])