(v4.2.0) Added 5 new question types

Added support for dropdown, radio select, checkbox select, file upload & text display question types.
This commit is contained in:
DJj123dj
2026-05-23 14:30:56 +02:00
parent 87c034d215
commit 4af9b9e17f
26 changed files with 837 additions and 401 deletions
+2
View File
@@ -50,6 +50,7 @@
{"sectionId":"contributor","name":"Ashish5180","pictureUrl":"https://github.com/Ashish5180.png","profileUrl":"https://github.com/Ashish5180"},
{"sectionId":"contributor","name":"duboiss","pictureUrl":"https://github.com/duboiss.png","profileUrl":"https://github.com/duboiss"},
{"sectionId":"contributor","name":"sdehaarte","pictureUrl":"https://github.com/sdehaarte.png","profileUrl":"https://github.com/sdehaarte"},
{"sectionId":"contributor","name":"MauroDruwel","pictureUrl":"https://github.com/MauroDruwel.png","profileUrl":"https://github.com/MauroDruwel"},
{"sectionId":"support","name":"smetsliam","pictureUrl":"https://github.com/smetsliam.png","profileUrl":"https://github.com/smetsliam"},
{"sectionId":"support","name":"Sank34","pictureUrl":"https://github.com/Sank34.png","profileUrl":"https://github.com/Sank34"},
{"sectionId":"support","name":"FrankVissers","pictureUrl":"https://github.com/FrankVissers.png","profileUrl":"https://github.com/FrankVissers"},
@@ -58,6 +59,7 @@
{"sectionId":"plugin-dev","name":"Imperatorix17","pictureUrl":"https://github.com/imperatorix17.png","profileUrl":"https://github.com/imperatorix17"},
{"sectionId":"plugin-dev","name":"DanoGlez","pictureUrl":"https://github.com/DanoGlez.png","profileUrl":"https://github.com/DanoGlez"},
{"sectionId":"plugin-dev","name":"yowsef","pictureUrl":"https://github.com/Yow-sef.png","profileUrl":"https://github.com/Yow-sef"},
{"sectionId":"plugin-dev","name":"MeneerSouf","pictureUrl":"https://github.com/MeneerSouf.png","profileUrl":"https://github.com/MeneerSouf"},
{"sectionId":"plugin-dev","name":"challenger_nova","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"HanumeshGupta","pictureUrl":"https://github.com/HanumeshGupta.png","profileUrl":"https://github.com/HanumeshGupta"},
{"sectionId":"translator","name":"benzorich","pictureUrl":"https://github.com/benzorich.png","profileUrl":"https://github.com/benzorich"},
+100 -88
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

+1 -1
View File
@@ -27,7 +27,7 @@
"license": "GPL-3.0-only",
"dependencies": {
"@discordjs/rest": "^2.6.1",
"@open-discord-bots/framework": "^0.4.4",
"@open-discord-bots/framework": "^0.5.2",
"@types/node": "^22.5.0",
"@types/terminal-kit": "^2.5.7",
"ansis": "^4.2.0",
+11 -3
View File
@@ -626,11 +626,19 @@ const ticketEmbeds = () => {
if (generalConfig.data.ticketSystem.displayFieldsWithQuestions) instance.addFields(...embedOptions.fields)
const answers = ticket.get("opendiscord:answers").value
answers.forEach((answer) => {
if (!answer.value || answer.value.length == 0) return
for (const answer of answers){
if (answer.type == "file-upload"){
//render file upload fields
if (!answer.files) continue
const renderedFiles = answer.files.map((file) => `- [${file.name}](${file.url})`).join("\n")
instance.addFields({name:answer.name,value:renderedFiles,inline:false})
}else if (typeof answer.value == "string" && answer.value.length > 0){
//render other fields
if (generalConfig.data.ticketSystem.questionFieldsInCodeBlock) instance.addFields({name:answer.name,value:"```"+answer.value.slice(0,1024-6)+"```",inline:false})
else instance.addFields({name:answer.name,value:answer.value,inline:false})
})
}
}
}else if (embedOptions.fields){
instance.setFields(embedOptions.fields)
}
-173
View File
@@ -1,173 +0,0 @@
///////////////////////////////////////
//MODAL BUILDERS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index.js"
import * as discord from "discord.js"
const modals = opendiscord.builders.modals
const lang = opendiscord.languages
export async function registerAllModals(){
ticketModals()
}
const ticketModals = () => {
//TICKET QUESTIONS
modals.add(new api.ODModal("opendiscord:ticket-questions"))
modals.get("opendiscord:ticket-questions").workers.add(
new api.ODWorker("opendiscord:ticket-questions",0,async (instance,params,origin) => {
const {option} = params
instance.setCustomId("od:ticket-questions_"+option.id.value+"_"+origin)
instance.setTitle(option.exists("opendiscord:name") ? option.get("opendiscord:name").value : option.id.value)
const questionIds = option.get("opendiscord:questions").value
questionIds.forEach((id) => {
const question = opendiscord.questions.get(id)
if (!question) return
if (question instanceof api.ODShortQuestion) instance.addQuestion({
customId:question.id.value,
label:question.get("opendiscord:name").value,
style:"short",
required:question.get("opendiscord:required").value,
placeholder:(question.get("opendiscord:placeholder").value) ? question.get("opendiscord:placeholder").value : undefined,
minLength:(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-min").value : undefined,
maxLength:Math.min(1024-6,(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-max").value : (1024-6)) //embed field limit - 6x` characters
})
else if (question instanceof api.ODParagraphQuestion) instance.addQuestion({
customId:question.id.value,
label:question.get("opendiscord:name").value,
style:"paragraph",
required:question.get("opendiscord:required").value,
placeholder:(question.get("opendiscord:placeholder").value) ? question.get("opendiscord:placeholder").value : undefined,
minLength:(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-min").value : undefined,
maxLength:Math.min(1024-6,(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-max").value : (1024-6)) //embed field limit - 6x` characters
})
})
})
)
//CLOSE TICKET REASON
modals.add(new api.ODModal("opendiscord:close-ticket-reason"))
modals.get("opendiscord:close-ticket-reason").workers.add(
new api.ODWorker("opendiscord:close-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
instance.setCustomId("od:close-ticket-reason|"+channel.id+"|"+message.id)
instance.setTitle(lang.getTranslation("actions.buttons.close"))
instance.addQuestion({
customId:"reason",
label:lang.getTranslation("params.uppercase.reason"),
style:"paragraph",
required:true,
placeholder:lang.getTranslation("actions.modal.closePlaceholder")
})
})
)
//REOPEN TICKET REASON
modals.add(new api.ODModal("opendiscord:reopen-ticket-reason"))
modals.get("opendiscord:reopen-ticket-reason").workers.add(
new api.ODWorker("opendiscord:reopen-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
instance.setCustomId("od:reopen-ticket-reason|"+channel.id+"|"+message.id)
instance.setTitle(lang.getTranslation("actions.buttons.reopen"))
instance.addQuestion({
customId:"reason",
label:lang.getTranslation("params.uppercase.reason"),
style:"paragraph",
required:true,
placeholder:lang.getTranslation("actions.modal.reopenPlaceholder")
})
})
)
//DELETE TICKET REASON
modals.add(new api.ODModal("opendiscord:delete-ticket-reason"))
modals.get("opendiscord:delete-ticket-reason").workers.add(
new api.ODWorker("opendiscord:delete-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
instance.setCustomId("od:delete-ticket-reason|"+channel.id+"|"+message.id)
instance.setTitle(lang.getTranslation("actions.buttons.delete"))
instance.addQuestion({
customId:"reason",
label:lang.getTranslation("params.uppercase.reason"),
style:"paragraph",
required:true,
placeholder:lang.getTranslation("actions.modal.deletePlaceholder")
})
})
)
//CLAIM TICKET REASON
modals.add(new api.ODModal("opendiscord:claim-ticket-reason"))
modals.get("opendiscord:claim-ticket-reason").workers.add(
new api.ODWorker("opendiscord:claim-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
instance.setCustomId("od:claim-ticket-reason|"+channel.id+"|"+message.id)
instance.setTitle(lang.getTranslation("actions.buttons.claim"))
instance.addQuestion({
customId:"reason",
label:lang.getTranslation("params.uppercase.reason"),
style:"paragraph",
required:true,
placeholder:lang.getTranslation("actions.modal.claimPlaceholder")
})
})
)
//UNCLAIM TICKET REASON
modals.add(new api.ODModal("opendiscord:unclaim-ticket-reason"))
modals.get("opendiscord:unclaim-ticket-reason").workers.add(
new api.ODWorker("opendiscord:unclaim-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
instance.setCustomId("od:unclaim-ticket-reason|"+channel.id+"|"+message.id)
instance.setTitle(lang.getTranslation("actions.buttons.unclaim"))
instance.addQuestion({
customId:"reason",
label:lang.getTranslation("params.uppercase.reason"),
style:"paragraph",
required:true,
placeholder:lang.getTranslation("actions.modal.unclaimPlaceholder")
})
})
)
//PIN TICKET REASON
modals.add(new api.ODModal("opendiscord:pin-ticket-reason"))
modals.get("opendiscord:pin-ticket-reason").workers.add(
new api.ODWorker("opendiscord:pin-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
instance.setCustomId("od:pin-ticket-reason|"+channel.id+"|"+message.id)
instance.setTitle(lang.getTranslation("actions.buttons.pin"))
instance.addQuestion({
customId:"reason",
label:lang.getTranslation("params.uppercase.reason"),
style:"paragraph",
required:true,
placeholder:lang.getTranslation("actions.modal.pinPlaceholder")
})
})
)
//UNPIN TICKET REASON
modals.add(new api.ODModal("opendiscord:unpin-ticket-reason"))
modals.get("opendiscord:unpin-ticket-reason").workers.add(
new api.ODWorker("opendiscord:unpin-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
instance.setCustomId("od:unpin-ticket-reason|"+channel.id+"|"+message.id)
instance.setTitle(lang.getTranslation("actions.buttons.unpin"))
instance.addQuestion({
customId:"reason",
label:lang.getTranslation("params.uppercase.reason"),
style:"paragraph",
required:true,
placeholder:lang.getTranslation("actions.modal.unpinPlaceholder")
})
})
)
}
+1 -1
View File
@@ -169,7 +169,7 @@ export async function registerVerifyBars(){
}
}else if (params.selectedButtonId == "accept-with-reason"){
//CLAIM WITH REASON (MODAL)
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:claim-ticket-reason").build("other",{guild,channel,user,ticket,message}))
instance.modal(await opendiscord.components.modals.get("opendiscord:claim-ticket-reason").build("other",{guild,channel,user,ticket,message}))
}
})
])
+1 -1
View File
@@ -177,7 +177,7 @@ export async function registerVerifyBars(){
}
}else if (params.selectedButtonId == "accept-with-reason"){
//CLOSE WITH REASON (MODAL)
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:close-ticket-reason").build("other",{guild,channel,user,ticket,message}))
instance.modal(await opendiscord.components.modals.get("opendiscord:close-ticket-reason").build("other",{guild,channel,user,ticket,message}))
}
})
])
+1 -1
View File
@@ -202,7 +202,7 @@ export async function registerVerifyBars(){
}
}else if (params.selectedButtonId == "accept-with-reason"){
//DELETE WITH REASON (MODAL)
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:delete-ticket-reason").build("other",{guild,channel,user,ticket,message}))
instance.modal(await opendiscord.components.modals.get("opendiscord:delete-ticket-reason").build("other",{guild,channel,user,ticket,message}))
}
})
])
+1 -1
View File
@@ -169,7 +169,7 @@ export async function registerVerifyBars(){
}
}else if (params.selectedButtonId == "accept-with-reason"){
//PIN WITH REASON (MODAL)
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:pin-ticket-reason").build("other",{guild,channel,user,ticket,message}))
instance.modal(await opendiscord.components.modals.get("opendiscord:pin-ticket-reason").build("other",{guild,channel,user,ticket,message}))
}
})
])
+1 -1
View File
@@ -185,7 +185,7 @@ export async function registerVerifyBars(){
}
}else if (params.selectedButtonId == "accept-with-reason"){
//REOPEN WITH REASON (MODAL)
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:reopen-ticket-reason").build("other",{guild,channel,user,ticket,message}))
instance.modal(await opendiscord.components.modals.get("opendiscord:reopen-ticket-reason").build("other",{guild,channel,user,ticket,message}))
}
})
])
+39 -18
View File
@@ -50,7 +50,7 @@ export async function registerCommandResponders(){
//start ticket creation
if (option.exists("opendiscord:questions") && option.get("opendiscord:questions").value.length > 0){
//SEND MODAL
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:ticket-questions").build(origin,{guild,channel,user,option}))
instance.modal(await opendiscord.components.modals.get("opendiscord:ticket-questions").build(origin,{guild,channel,user,option}))
}else{
//check ticket permissions (modals need check after submit)
if (!(await checkTicketCreationPerms(instance,origin,guild,user,option))) return cancel()
@@ -99,7 +99,7 @@ export async function registerButtonResponders(){
//start ticket creation
if (option.exists("opendiscord:questions") && option.get("opendiscord:questions").value.length > 0){
//SEND MODAL
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:ticket-questions").build("panel-button",{guild,channel,user,option}))
instance.modal(await opendiscord.components.modals.get("opendiscord:ticket-questions").build("panel-button",{guild,channel,user,option}))
}else{
//check ticket permissions (modals need check after submit)
if (!(await checkTicketCreationPerms(instance,"panel-button",guild,user,option))) return cancel()
@@ -141,7 +141,7 @@ export async function registerDropdownResponders(){
//start ticket creation
if (option.exists("opendiscord:questions") && option.get("opendiscord:questions").value.length > 0){
//SEND MODAL
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:ticket-questions").build("panel-dropdown",{guild,channel,user,option}))
instance.modal(await opendiscord.components.modals.get("opendiscord:ticket-questions").build("panel-dropdown",{guild,channel,user,option}))
}else{
//check ticket permissions (modals need check after submit)
if (!(await checkTicketCreationPerms(instance,"panel-dropdown",guild,user,option))) return cancel()
@@ -193,26 +193,47 @@ export async function registerModalResponders(){
if (!(await checkTicketCreationPerms(instance,originalOrigin,guild,user,option))) return cancel()
//get answers
const answers: {id:string,name:string,type:"short"|"paragraph",value:string|null}[] = []
option.get("opendiscord:questions").value.forEach((id) => {
const question = opendiscord.questions.get(id)
if (!question) return
if (question instanceof api.ODShortQuestion){
answers.push({
id,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : id,
const answers: api.ODQuestionAnswer[] = []
for (const questionId of option.get("opendiscord:questions").value){
const question = opendiscord.questions.get(questionId)
if (!question) continue
if (question instanceof api.ODShortQuestion) answers.push({
id:questionId,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : questionId,
type:"short",
value:instance.values.getTextField(id,false)
value:instance.values.getTextField(questionId,false)
})
}else if (question instanceof api.ODParagraphQuestion){
answers.push({
id,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : id,
else if (question instanceof api.ODParagraphQuestion) answers.push({
id:questionId,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : questionId,
type:"paragraph",
value:instance.values.getTextField(id,false)
value:instance.values.getTextField(questionId,false)
})
else if (question instanceof api.ODDropdownQuestion) answers.push({
id:questionId,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : questionId,
type:"dropdown",
value:instance.values.getStringDropdownValues(questionId)[0] ?? null
})
else if (question instanceof api.ODRadioSelectQuestion) answers.push({
id:questionId,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : questionId,
type:"radio-select",
value:instance.values.getRadioGroup(questionId,false)
})
else if (question instanceof api.ODCheckboxSelectQuestion) answers.push({
id:questionId,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : questionId,
type:"checkbox-select",
value:(instance.values.getCheckboxGroup(questionId).length > 0) ? instance.values.getCheckboxGroup(questionId).map((opt) => "> "+opt).join("\n") : null
})
else if (question instanceof api.ODFileUploadQuestion) answers.push({
id:questionId,
name:question.exists("opendiscord:name") ? question.get("opendiscord:name")?.value : questionId,
type:"file-upload",
files:(instance.values.getUploadedFiles(questionId).length > 0) ? instance.values.getUploadedFiles(questionId).map(({id,url,name,title,description,contentType}) => ({id,url,name,title,description,contentType})) : []
})
}
})
await instance.defer((generalConfig.data.ticketSystem.replyOnTicketCreation) ? "reply" : "update",true)
+1 -1
View File
@@ -168,7 +168,7 @@ export async function registerVerifyBars(){
}
}else if (params.selectedButtonId == "accept-with-reason"){
//UNCLAIM WITH REASON (MODAL)
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:unclaim-ticket-reason").build("other",{guild,channel,user,ticket,message}))
instance.modal(await opendiscord.components.modals.get("opendiscord:unclaim-ticket-reason").build("other",{guild,channel,user,ticket,message}))
}
})
])
+1 -1
View File
@@ -168,7 +168,7 @@ export async function registerVerifyBars(){
}
}else if (params.selectedButtonId == "accept-with-reason"){
//UNPIN WITH REASON (MODAL)
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:unpin-ticket-reason").build("other",{guild,channel,user,ticket,message}))
instance.modal(await opendiscord.components.modals.get("opendiscord:unpin-ticket-reason").build("other",{guild,channel,user,ticket,message}))
}
})
])
+302
View File
@@ -0,0 +1,302 @@
///////////////////////////////////////
//REGISTER MODAL COMPONENTS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index.js"
import * as discord from "discord.js"
const modals = opendiscord.components.modals
const lang = opendiscord.languages
const generalConfig = opendiscord.configs.get("opendiscord:general")
export async function registerModalComponents(){
//TICKET QUESTIONS
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 modal = instance.setComponent(new api.ODModalComponent("opendiscord:questions-modal",{
customId:"od:ticket-questions|"+option.id.value+"|"+origin,
title:lang.getTranslation("params.uppercase.ticket")+": "+(option.exists("opendiscord:name")) ? option.get("opendiscord:name").value : option.id.value
}))
for (const questionId of option.get("opendiscord:questions").value){
const question = opendiscord.questions.get(questionId)
if (!question) continue
if (question instanceof api.ODShortQuestion){
//SHORT QUESTION
const component = new api.ODLabelComponent(question.id.value,{
title:question.get("opendiscord:name").value,
description:(question.get("opendiscord:description").value) ? question.get("opendiscord:description").value : undefined,
})
component.setComponent(new api.ODShortInputComponent(question.id.value,{
customId:question.id.value,
required:question.get("opendiscord:required").value,
placeholder:(question.get("opendiscord:placeholder").value) ? question.get("opendiscord:placeholder").value : undefined,
minLength:(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-min").value : undefined,
maxLength:Math.min(1024-6,(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-max").value : (1024-6)) //embed field limit - 6x` characters
}))
modal.addComponent(component,"end")
}else if (question instanceof api.ODParagraphQuestion){
//PARAGRAPH QUESTION
const component = new api.ODLabelComponent(question.id.value,{
title:question.get("opendiscord:name").value,
description:(question.get("opendiscord:description").value) ? question.get("opendiscord:description").value : undefined,
})
component.setComponent(new api.ODParagraphInputComponent(question.id.value,{
customId:question.id.value,
required:question.get("opendiscord:required").value,
placeholder:(question.get("opendiscord:placeholder").value) ? question.get("opendiscord:placeholder").value : undefined,
minLength:(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-min").value : undefined,
maxLength:Math.min(1024-6,(question.get("opendiscord:length-enabled").value) ? question.get("opendiscord:length-max").value : (1024-6)) //embed field limit - 6x` characters
}))
modal.addComponent(component,"end")
}else if (question instanceof api.ODDropdownQuestion){
//DROPDOWN QUESTION
const component = new api.ODLabelComponent(question.id.value,{
title:question.get("opendiscord:name").value,
description:(question.get("opendiscord:description").value) ? question.get("opendiscord:description").value : undefined,
})
component.setComponent(new api.ODDropdownComponent(question.id.value,{
type:"string",
customId:question.id.value,
required:question.get("opendiscord:required").value,
placeholder:(question.get("opendiscord:placeholder").value) ? question.get("opendiscord:placeholder").value : undefined,
maxValues:1,
options:question.get("opendiscord:choices").value.map((choice) => ({
label:choice.title,
emoji:choice.emoji,
description:choice.description,
value:choice.title
}))
}))
modal.addComponent(component,"end")
}else if (question instanceof api.ODRadioSelectQuestion){
//RADIO SELECT QUESTION
const component = new api.ODLabelComponent(question.id.value,{
title:question.get("opendiscord:name").value,
description:(question.get("opendiscord:description").value) ? question.get("opendiscord:description").value : undefined,
})
component.setComponent(new api.ODRadioGroupComponent(question.id.value,{
customId:question.id.value,
required:question.get("opendiscord:required").value,
options:question.get("opendiscord:choices").value.map((choice) => ({
label:choice.title,
default:choice.selectedByDefault,
description:choice.description,
value:choice.title
}))
}))
modal.addComponent(component,"end")
}else if (question instanceof api.ODCheckboxSelectQuestion){
//CHECKBOX SELECT QUESTION
const component = new api.ODLabelComponent(question.id.value,{
title:question.get("opendiscord:name").value,
description:(question.get("opendiscord:description").value) ? question.get("opendiscord:description").value : undefined,
})
component.setComponent(new api.ODCheckboxGroupComponent(question.id.value,{
customId:question.id.value,
required:question.get("opendiscord:required").value,
minValues:(question.get("opendiscord:limits-enabled").value) ? question.get("opendiscord:limits-min").value : undefined,
maxValues:(question.get("opendiscord:limits-enabled").value) ? question.get("opendiscord:limits-max").value : undefined,
options:question.get("opendiscord:choices").value.map((choice) => ({
label:choice.title,
default:choice.selectedByDefault,
description:choice.description,
value:choice.title
}))
}))
modal.addComponent(component,"end")
}else if (question instanceof api.ODFileUploadQuestion){
//FILE UPLOAD QUESTION
const component = new api.ODLabelComponent(question.id.value,{
title:question.get("opendiscord:name").value,
description:(question.get("opendiscord:description").value) ? question.get("opendiscord:description").value : undefined,
})
component.setComponent(new api.ODFileUploadComponent(question.id.value,{
customId:question.id.value,
required:question.get("opendiscord:required").value,
minAmount:(question.get("opendiscord:limits-enabled").value) ? question.get("opendiscord:limits-min").value : undefined,
maxAmount:(question.get("opendiscord:limits-enabled").value) ? question.get("opendiscord:limits-max").value : undefined,
}))
modal.addComponent(component,"end")
}else if (question instanceof api.ODTextDisplayQuestion){
//TEXT DISPLAY QUESTION
const component = new api.ODTextComponent(question.id.value,{
content:question.get("opendiscord:text-contents").value
})
modal.addComponent(component,"end")
}
}
})
)
//CLOSE TICKET REASON
modals.add(new api.ODComponentFactory("opendiscord:close-ticket-reason"))
modals.get("opendiscord:close-ticket-reason").workers.add(
new api.ODWorker("opendiscord:close-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
const modal = instance.setComponent(new api.ODModalComponent("opendiscord:close-ticket-reason",{
customId:"od:close-ticket-reason|"+channel.id+"|"+message.id,
title:lang.getTranslation("actions.buttons.close")
}))
const reasonComponent = new api.ODLabelComponent("reason",{
title:lang.getTranslation("params.uppercase.reason"),
description:lang.getTranslation("actions.modal.closePlaceholder")
})
reasonComponent.setComponent(new api.ODParagraphInputComponent("reason",{
customId:"reason",
required:true,
placeholder:lang.getTranslation("params.uppercase.reason")
}))
modal.addComponent(reasonComponent,"end")
})
)
//REOPEN TICKET REASON
modals.add(new api.ODComponentFactory("opendiscord:reopen-ticket-reason"))
modals.get("opendiscord:reopen-ticket-reason").workers.add(
new api.ODWorker("opendiscord:reopen-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
const modal = instance.setComponent(new api.ODModalComponent("opendiscord:reopen-ticket-reason",{
customId:"od:reopen-ticket-reason|"+channel.id+"|"+message.id,
title:lang.getTranslation("actions.buttons.reopen")
}))
const reasonComponent = new api.ODLabelComponent("reason",{
title:lang.getTranslation("params.uppercase.reason"),
description:lang.getTranslation("actions.modal.reopenPlaceholder")
})
reasonComponent.setComponent(new api.ODParagraphInputComponent("reason",{
customId:"reason",
required:true,
placeholder:lang.getTranslation("params.uppercase.reason")
}))
modal.addComponent(reasonComponent,"end")
})
)
//DELETE TICKET REASON
modals.add(new api.ODComponentFactory("opendiscord:delete-ticket-reason"))
modals.get("opendiscord:delete-ticket-reason").workers.add(
new api.ODWorker("opendiscord:delete-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
const modal = instance.setComponent(new api.ODModalComponent("opendiscord:delete-ticket-reason",{
customId:"od:delete-ticket-reason|"+channel.id+"|"+message.id,
title:lang.getTranslation("actions.buttons.delete")
}))
const reasonComponent = new api.ODLabelComponent("reason",{
title:lang.getTranslation("params.uppercase.reason"),
description:lang.getTranslation("actions.modal.deletePlaceholder")
})
reasonComponent.setComponent(new api.ODParagraphInputComponent("reason",{
customId:"reason",
required:true,
placeholder:lang.getTranslation("params.uppercase.reason")
}))
modal.addComponent(reasonComponent,"end")
})
)
//CLAIM TICKET REASON
modals.add(new api.ODComponentFactory("opendiscord:claim-ticket-reason"))
modals.get("opendiscord:claim-ticket-reason").workers.add(
new api.ODWorker("opendiscord:claim-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
const modal = instance.setComponent(new api.ODModalComponent("opendiscord:claim-ticket-reason",{
customId:"od:claim-ticket-reason|"+channel.id+"|"+message.id,
title:lang.getTranslation("actions.buttons.claim")
}))
const reasonComponent = new api.ODLabelComponent("reason",{
title:lang.getTranslation("params.uppercase.reason"),
description:lang.getTranslation("actions.modal.claimPlaceholder")
})
reasonComponent.setComponent(new api.ODParagraphInputComponent("reason",{
customId:"reason",
required:true,
placeholder:lang.getTranslation("params.uppercase.reason")
}))
modal.addComponent(reasonComponent,"end")
})
)
//UNCLAIM TICKET REASON
modals.add(new api.ODComponentFactory("opendiscord:unclaim-ticket-reason"))
modals.get("opendiscord:unclaim-ticket-reason").workers.add(
new api.ODWorker("opendiscord:unclaim-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
const modal = instance.setComponent(new api.ODModalComponent("opendiscord:unclaim-ticket-reason",{
customId:"od:unclaim-ticket-reason|"+channel.id+"|"+message.id,
title:lang.getTranslation("actions.buttons.unclaim")
}))
const reasonComponent = new api.ODLabelComponent("reason",{
title:lang.getTranslation("params.uppercase.reason"),
description:lang.getTranslation("actions.modal.unclaimPlaceholder")
})
reasonComponent.setComponent(new api.ODParagraphInputComponent("reason",{
customId:"reason",
required:true,
placeholder:lang.getTranslation("params.uppercase.reason")
}))
modal.addComponent(reasonComponent,"end")
})
)
//PIN TICKET REASON
modals.add(new api.ODComponentFactory("opendiscord:pin-ticket-reason"))
modals.get("opendiscord:pin-ticket-reason").workers.add(
new api.ODWorker("opendiscord:pin-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
const modal = instance.setComponent(new api.ODModalComponent("opendiscord:pin-ticket-reason",{
customId:"od:pin-ticket-reason|"+channel.id+"|"+message.id,
title:lang.getTranslation("actions.buttons.pin")
}))
const reasonComponent = new api.ODLabelComponent("reason",{
title:lang.getTranslation("params.uppercase.reason"),
description:lang.getTranslation("actions.modal.pinPlaceholder")
})
reasonComponent.setComponent(new api.ODParagraphInputComponent("reason",{
customId:"reason",
required:true,
placeholder:lang.getTranslation("params.uppercase.reason")
}))
modal.addComponent(reasonComponent,"end")
})
)
//UNPIN TICKET REASON
modals.add(new api.ODComponentFactory("opendiscord:unpin-ticket-reason"))
modals.get("opendiscord:unpin-ticket-reason").workers.add(
new api.ODWorker("opendiscord:unpin-ticket-reason",0,async (instance,params,origin) => {
const {channel,message} = params
const modal = instance.setComponent(new api.ODModalComponent("opendiscord:unpin-ticket-reason",{
customId:"od:unpin-ticket-reason|"+channel.id+"|"+message.id,
title:lang.getTranslation("actions.buttons.unpin")
}))
const reasonComponent = new api.ODLabelComponent("reason",{
title:lang.getTranslation("params.uppercase.reason"),
description:lang.getTranslation("actions.modal.unpinPlaceholder")
})
reasonComponent.setComponent(new api.ODParagraphInputComponent("reason",{
customId:"reason",
required:true,
placeholder:lang.getTranslation("params.uppercase.reason")
}))
modal.addComponent(reasonComponent,"end")
})
)
}
+196 -69
View File
@@ -2,6 +2,8 @@
//OPENTICKET OPTION MODULE
///////////////////////////////////////
import * as api from "@open-discord-bots/framework/api"
import * as discord from "discord.js"
import { ODQuestionsJsonConfig_BaseQuestion, ODQuestionsJsonConfig_DropdownChoice, ODQuestionsJsonConfig_RadioCheckboxChoice } from "../mappings/config.js"
/**## ODQuestionIdConstraint `type`
* The constraint/layout for id mappings/interfaces of the `ODQuestion` class.
@@ -14,9 +16,10 @@ export type ODQuestionIdConstraint = Record<string,ODQuestionData<api.ODValidJso
*/
export interface ODShortQuestionIdMappings extends ODQuestionIdConstraint {
"opendiscord:name":ODQuestionData<string>,
"opendiscord:description":ODQuestionData<string>,
"opendiscord:required":ODQuestionData<boolean>,
"opendiscord:placeholder":ODQuestionData<string>,
"opendiscord:placeholder":ODQuestionData<string>,
"opendiscord:length-enabled":ODQuestionData<boolean>,
"opendiscord:length-min":ODQuestionData<number>,
"opendiscord:length-max":ODQuestionData<number>
@@ -28,14 +31,77 @@ export interface ODShortQuestionIdMappings extends ODQuestionIdConstraint {
*/
export interface ODParagraphQuestionIdMappings extends ODQuestionIdConstraint {
"opendiscord:name":ODQuestionData<string>,
"opendiscord:description":ODQuestionData<string>,
"opendiscord:required":ODQuestionData<boolean>,
"opendiscord:placeholder":ODQuestionData<string>,
"opendiscord:placeholder":ODQuestionData<string>,
"opendiscord:length-enabled":ODQuestionData<boolean>,
"opendiscord:length-min":ODQuestionData<number>,
"opendiscord:length-max":ODQuestionData<number>
}
/**## ODDropdownQuestionIdMappings `interface`
* A list of all available IDs in the default `ODDropdownQuestion` class in `opendiscord`.
* It's used to generate typescript declarations for this class.
*/
export interface ODDropdownQuestionIdMappings extends ODQuestionIdConstraint {
"opendiscord:name":ODQuestionData<string>,
"opendiscord:description":ODQuestionData<string>,
"opendiscord:required":ODQuestionData<boolean>,
"opendiscord:placeholder":ODQuestionData<string>,
"opendiscord:choices":ODQuestionData<ODQuestionsJsonConfig_DropdownChoice[]>
}
/**## ODRadioSelectQuestionIdMappings `interface`
* A list of all available IDs in the default `ODRadioSelectQuestion` class in `opendiscord`.
* It's used to generate typescript declarations for this class.
*/
export interface ODRadioSelectQuestionIdMappings extends ODQuestionIdConstraint {
"opendiscord:name":ODQuestionData<string>,
"opendiscord:description":ODQuestionData<string>,
"opendiscord:required":ODQuestionData<boolean>,
"opendiscord:choices":ODQuestionData<ODQuestionsJsonConfig_RadioCheckboxChoice[]>
}
/**## ODCheckboxSelectQuestionIdMappings `interface`
* A list of all available IDs in the default `ODCheckboxSelectQuestion` class in `opendiscord`.
* It's used to generate typescript declarations for this class.
*/
export interface ODCheckboxSelectQuestionIdMappings extends ODQuestionIdConstraint {
"opendiscord:name":ODQuestionData<string>,
"opendiscord:description":ODQuestionData<string>,
"opendiscord:required":ODQuestionData<boolean>,
"opendiscord:limits-enabled":ODQuestionData<boolean>,
"opendiscord:limits-min":ODQuestionData<number>,
"opendiscord:limits-max":ODQuestionData<number>
"opendiscord:choices":ODQuestionData<ODQuestionsJsonConfig_RadioCheckboxChoice[]>
}
/**## ODFileUploadQuestionIdMappings `interface`
* A list of all available IDs in the default `ODFileUploadQuestion` class in `opendiscord`.
* It's used to generate typescript declarations for this class.
*/
export interface ODFileUploadQuestionIdMappings extends ODQuestionIdConstraint {
"opendiscord:name":ODQuestionData<string>,
"opendiscord:description":ODQuestionData<string>,
"opendiscord:required":ODQuestionData<boolean>,
"opendiscord:limits-enabled":ODQuestionData<boolean>,
"opendiscord:limits-min":ODQuestionData<number>,
"opendiscord:limits-max":ODQuestionData<number>
}
/**## ODTextDisplayQuestionIdMappings `interface`
* A list of all available IDs in the default `ODTextDisplayQuestion` class in `opendiscord`.
* It's used to generate typescript declarations for this class.
*/
export interface ODTextDisplayQuestionIdMappings extends ODQuestionIdConstraint {
"opendiscord:text-contents":ODQuestionData<string>
}
/**## ODQuestionManager `class`
* This is an Open Ticket question manager.
*
@@ -81,20 +147,19 @@ export interface ODQuestionJson {
/**## ODQuestion `class`
* This is an Open Ticket question.
*
* This class contains all data related to this question (parsed from the config).
* This class contains all question data parsed from the config.
*
* Use `ODShortQuestion` or `ODParagraphQuestion` instead!
* This is an abstract class. Use instances like `ODShortQuestion` or `ODParagraphQuestion` instead.
*/
export class ODQuestion extends api.ODManager<ODQuestionData<api.ODValidJsonType>> {
export abstract class ODQuestion<IdList extends ODQuestionIdConstraint = ODQuestionIdConstraint> extends api.ODManager<ODQuestionData<api.ODValidJsonType>> {
/**The id of this question. (from the config) */
id:api.ODId
/**The type of this question (e.g. `opendiscord:short` or `opendiscord:paragraph`) */
type: string
abstract readonly type: string
constructor(id:api.ODValidId, type:string, data:ODQuestionData<api.ODValidJsonType>[]){
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super()
this.id = new api.ODId(id)
this.type = type
data.forEach((data) => {
this.add(data)
})
@@ -117,9 +182,25 @@ export class ODQuestion extends api.ODManager<ODQuestionData<api.ODValidJsonType
}
}
/**Create a question from a JSON object in the database. */
static fromJson(json:ODQuestionJson): ODQuestion {
return new ODQuestion(json.id,json.type,json.data.map((data) => new ODQuestionData(data.id,data.value)))
get<QuestionId extends keyof api.ODNoGeneric<IdList>>(id:QuestionId): IdList[QuestionId]
get(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null
get(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null {
return super.get(id)
}
remove<QuestionId extends keyof api.ODNoGeneric<IdList>>(id:QuestionId): IdList[QuestionId]
remove(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null
remove(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null {
return super.remove(id)
}
exists(id:keyof api.ODNoGeneric<IdList>): boolean
exists(id:api.ODValidId): boolean
exists(id:api.ODValidId): boolean {
return super.exists(id)
}
}
@@ -153,40 +234,36 @@ export class ODQuestionData<DataType extends api.ODValidJsonType> extends api.OD
}
}
/**## ODQuestionAnswer `type`
* A question answer stored in the database.
*/
export type ODQuestionAnswer = {
id:string,
name:string,
type:Exclude<ODQuestionsJsonConfig_BaseQuestion["type"],"text-display"|"file-upload">,
value:string|null
}|{
id:string,
name:string,
type:"file-upload",
files:{
id:string,
url:string,
name:string,
title:string|null,
description:string|null,
contentType:string|null,
}[]
}
/**## ODShortQuestion `class`
* This is an Open Ticket short question.
*
* This class contains all data related to an Open Ticket short question (parsed from the config).
*
* Use this question in an option to add a short text field to the modal!
* An Open Ticket short question. It contains all config properties of the short question type.
*/
export class ODShortQuestion extends ODQuestion {
type: "opendiscord:short" = "opendiscord:short"
export class ODShortQuestion extends ODQuestion<ODShortQuestionIdMappings> {
readonly type: "opendiscord:short" = "opendiscord:short"
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super(id,"opendiscord:short",data)
}
get<QuestionId extends keyof api.ODNoGeneric<ODShortQuestionIdMappings>>(id:QuestionId): ODShortQuestionIdMappings[QuestionId]
get(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null
get(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null {
return super.get(id)
}
remove<QuestionId extends keyof api.ODNoGeneric<ODShortQuestionIdMappings>>(id:QuestionId): ODShortQuestionIdMappings[QuestionId]
remove(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null
remove(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null {
return super.remove(id)
}
exists(id:keyof api.ODNoGeneric<ODShortQuestionIdMappings>): boolean
exists(id:api.ODValidId): boolean
exists(id:api.ODValidId): boolean {
return super.exists(id)
super(id,data)
}
static fromJson(json: ODQuestionJson): ODShortQuestion {
@@ -195,41 +272,91 @@ export class ODShortQuestion extends ODQuestion {
}
/**## ODParagraphQuestion `class`
* This is an Open Ticket paragraph question.
*
* This class contains all data related to an Open Ticket paragraph question (parsed from the config).
*
* Use this question in an option to add a paragraph text field to the modal!
* An Open Ticket paragraph question. It contains all config properties of the paragraph question type.
*/
export class ODParagraphQuestion extends ODQuestion {
type: "opendiscord:paragraph" = "opendiscord:paragraph"
export class ODParagraphQuestion extends ODQuestion<ODParagraphQuestionIdMappings> {
readonly type: "opendiscord:paragraph" = "opendiscord:paragraph"
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super(id,"opendiscord:paragraph",data)
}
get<QuestionId extends keyof api.ODNoGeneric<ODParagraphQuestionIdMappings>>(id:QuestionId): ODParagraphQuestionIdMappings[QuestionId]
get(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null
get(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null {
return super.get(id)
}
remove<QuestionId extends keyof api.ODNoGeneric<ODParagraphQuestionIdMappings>>(id:QuestionId): ODParagraphQuestionIdMappings[QuestionId]
remove(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null
remove(id:api.ODValidId): ODQuestionData<api.ODValidJsonType>|null {
return super.remove(id)
}
exists(id:keyof api.ODNoGeneric<ODParagraphQuestionIdMappings>): boolean
exists(id:api.ODValidId): boolean
exists(id:api.ODValidId): boolean {
return super.exists(id)
super(id,data)
}
static fromJson(json: ODQuestionJson): ODParagraphQuestion {
return new ODParagraphQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
}
}
/**## ODDropdownQuestion `class`
* An Open Ticket dropdown question. It contains all config properties of the dropdown question type.
*/
export class ODDropdownQuestion extends ODQuestion<ODDropdownQuestionIdMappings> {
readonly type: "opendiscord:dropdown" = "opendiscord:dropdown"
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super(id,data)
}
static fromJson(json: ODQuestionJson): ODDropdownQuestion {
return new ODDropdownQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
}
}
/**## ODRadioSelectQuestion `class`
* An Open Ticket radio-select question. It contains all config properties of the radio-select question type.
*/
export class ODRadioSelectQuestion extends ODQuestion<ODRadioSelectQuestionIdMappings> {
readonly type: "opendiscord:radio-select" = "opendiscord:radio-select"
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super(id,data)
}
static fromJson(json: ODQuestionJson): ODRadioSelectQuestion {
return new ODRadioSelectQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
}
}
/**## ODCheckboxSelectQuestion `class`
* An Open Ticket checkbox-select question. It contains all config properties of the checkbox-select question type.
*/
export class ODCheckboxSelectQuestion extends ODQuestion<ODCheckboxSelectQuestionIdMappings> {
readonly type: "opendiscord:checkbox-select" = "opendiscord:checkbox-select"
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super(id,data)
}
static fromJson(json: ODQuestionJson): ODCheckboxSelectQuestion {
return new ODCheckboxSelectQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
}
}
/**## ODFileUploadQuestion `class`
* An Open Ticket file-upload question. It contains all config properties of the file-upload question type.
*/
export class ODFileUploadQuestion extends ODQuestion<ODFileUploadQuestionIdMappings> {
readonly type: "opendiscord:file-upload" = "opendiscord:file-upload"
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super(id,data)
}
static fromJson(json: ODQuestionJson): ODFileUploadQuestion {
return new ODFileUploadQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
}
}
/**## ODTextDisplayQuestion `class`
* An Open Ticket text-display question. It contains all config properties of the text-display question type.
*/
export class ODTextDisplayQuestion extends ODQuestion<ODTextDisplayQuestionIdMappings> {
readonly type: "opendiscord:text-display" = "opendiscord:text-display"
constructor(id:api.ODValidId, data:ODQuestionData<api.ODValidJsonType>[]){
super(id,data)
}
static fromJson(json: ODQuestionJson): ODTextDisplayQuestion {
return new ODTextDisplayQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
}
}
+2 -1
View File
@@ -4,6 +4,7 @@
import * as api from "@open-discord-bots/framework/api"
import { ODTicketOption } from "./option.js"
import * as discord from "discord.js"
import { ODQuestionAnswer } from "./question.js"
/**## ODTicketIdConstraint `type`
* The constraint/layout for id mappings/interfaces of the `ODTicket` class.
@@ -48,7 +49,7 @@ export interface ODTicketIdMappings extends ODTicketIdConstraint {
"opendiscord:autodelete-enabled":ODTicketData<boolean>,
"opendiscord:autodelete-days":ODTicketData<number>,
"opendiscord:answers":ODTicketData<{id:string,name:string,type:"short"|"paragraph",value:string|null}[]>,
"opendiscord:answers":ODTicketData<ODQuestionAnswer[]>,
"opendiscord:priority":ODTicketData<number>,
"opendiscord:topic":ODTicketData<string>,
"opendiscord:message-sent":ODTicketData<boolean>,
+13
View File
@@ -1411,6 +1411,19 @@ async function saveQuickSetupConfig(){
{title:"Choice D",description:"Fear",selectedByDefault:false}
]
},
{
id:"example-question-6",
name:"Example Question 6",
description:"This is a file upload question.",
type:"file-upload",
required:true,
limits:{
enabled:false,
min:0,
max:1
}
},
{
id:"example-text-display",
type:"text-display",
+2 -1
View File
@@ -8,6 +8,7 @@ import { ODTicket, ODTicketClearFilter } from "../api/ticket.js"
import { ODTranscriptCompiler, ODTranscriptCompilerCompileResult } from "../api/transcript.js"
import { ODRole, ODRoleUpdateMode, ODRoleUpdateResult } from "../api/role.js"
import { ODPriorityLevel } from "../api/priority.js"
import { ODQuestionAnswer } from "../api/question.js"
/**## ODActionManagerIdMappings `interface`
* A list of all available IDs in the default `ODActionManager` class in `opendiscord`.
@@ -28,7 +29,7 @@ export interface ODActionManagerIdMappings extends api.ODActionManagerIdConstrai
},
"opendiscord:create-ticket":{
origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",
params:{guild:discord.Guild,user:discord.User,option:ODTicketOption,answers:{id:string,name:string,type:"short"|"paragraph",value:string|null}[]},
params:{guild:discord.Guild,user:discord.User,option:ODTicketOption,answers:ODQuestionAnswer[]},
result:{channel:discord.GuildTextBasedChannel,ticket:ODTicket},
workers:"opendiscord:create-ticket"|"opendiscord:send-ticket-message"|"opendiscord:discord-logs"|"opendiscord:logs"
},
+1 -8
View File
@@ -235,14 +235,7 @@ export interface ODMessageManagerIdMappings extends api.ODMessageManagerIdConstr
* It's used to generate typescript declarations for this class.
*/
export interface ODModalManagerIdMappings extends api.ODModalManagerIdConstraint {
"opendiscord:ticket-questions":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,option:ODTicketOption},workers:"opendiscord:ticket-questions"}
"opendiscord:close-ticket-reason":{origin:"ticket-message"|"reopen-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:close-ticket-reason"}
"opendiscord:reopen-ticket-reason":{origin:"ticket-message"|"close-message"|"autoclose-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:reopen-ticket-reason"}
"opendiscord:delete-ticket-reason":{origin:"ticket-message"|"reopen-message"|"close-message"|"autoclose-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:delete-ticket-reason"}
"opendiscord:claim-ticket-reason":{origin:"ticket-message"|"unclaim-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:claim-ticket-reason"}
"opendiscord:unclaim-ticket-reason":{origin:"ticket-message"|"claim-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:unclaim-ticket-reason"}
"opendiscord:pin-ticket-reason":{origin:"ticket-message"|"unpin-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:pin-ticket-reason"}
"opendiscord:unpin-ticket-reason":{origin:"ticket-message"|"pin-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:unpin-ticket-reason"}
//Deprecated, moved to ODModalComponentManagerIdMappings
}
/////////////////////////////
+10 -1
View File
@@ -3,6 +3,8 @@
///////////////////////////////////////
import * as api from "@open-discord-bots/framework/api"
import * as discord from "discord.js"
import { ODTicketOption } from "../api/option.js"
import { ODTicket } from "../api/ticket.js"
/**## ODSharedComponentManagerIdMappings `interface`
* A list of all available IDs in the default `ODSharedComponentManager` class in `opendiscord`.
@@ -25,7 +27,14 @@ export interface ODMessageComponentManagerIdMappings extends api.ODComponentMana
* It's used to generate typescript declarations for this class.
*/
export interface ODModalComponentManagerIdMappings extends api.ODComponentManagerIdConstraint {
//"opendiscord:example-modal":{origin:"slash"|"text"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:example-modal"},
"opendiscord:ticket-questions":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,option:ODTicketOption},workers:"opendiscord:ticket-questions"},
"opendiscord:close-ticket-reason":{origin:"ticket-message"|"reopen-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:close-ticket-reason"}
"opendiscord:reopen-ticket-reason":{origin:"ticket-message"|"close-message"|"autoclose-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:reopen-ticket-reason"}
"opendiscord:delete-ticket-reason":{origin:"ticket-message"|"reopen-message"|"close-message"|"autoclose-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:delete-ticket-reason"}
"opendiscord:claim-ticket-reason":{origin:"ticket-message"|"unclaim-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:claim-ticket-reason"}
"opendiscord:unclaim-ticket-reason":{origin:"ticket-message"|"claim-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:unclaim-ticket-reason"}
"opendiscord:pin-ticket-reason":{origin:"ticket-message"|"unpin-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:pin-ticket-reason"}
"opendiscord:unpin-ticket-reason":{origin:"ticket-message"|"pin-message"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket,message:discord.Message},workers:"opendiscord:unpin-ticket-reason"}
}
/**## ODComponentModifierManagerIdMappings `interface`
+25 -5
View File
@@ -619,6 +619,18 @@ export interface ODQuestionsJsonConfig_CheckboxLimits {
max:number
}
/**## ODQuestionsJsonConfig_FileUploadLimits `interface`
* Verify the amount of uploaded files in a question.
*/
export interface ODQuestionsJsonConfig_FileUploadLimits {
/**Enable file limits. */
enabled:boolean,
/**The minimum amount of uploaded files. */
min:number,
/**The maximum amount of uploaded files. */
max:number
}
/**## ODQuestionsJsonConfig_DropdownChoice `interface`
* A dropdown choice used in `ODQuestionsJsonConfig_DropdownQuestion`
*/
@@ -654,7 +666,7 @@ export interface ODQuestionsJsonConfig_BaseQuestion {
/**The description of this question. (Leave empty for none) */
description:string,
/**The type of this question. */
type:"short"|"paragraph"|"text-display"|"dropdown"|"radio-select"|"checkbox-select",
type:"short"|"paragraph"|"text-display"|"dropdown"|"radio-select"|"checkbox-select"|"file-upload",
/**Is this question required? */
required:boolean,
}
@@ -662,9 +674,7 @@ export interface ODQuestionsJsonConfig_BaseQuestion {
/**## ODQuestionsJsonConfig_TextDisplayQuestion `interface`
* All properties for a text-display in the `questions.jsonc` config!
*/
export interface ODQuestionsJsonConfig_TextDisplayQuestion {
/**The id of this text-display. */
id:string,
export interface ODQuestionsJsonConfig_TextDisplayQuestion extends Omit<ODQuestionsJsonConfig_BaseQuestion,"name"|"description"|"required"> {
/**The type of this question. */
type:"text-display",
/**The text contents to show in the modal. */
@@ -724,6 +734,15 @@ export interface ODQuestionsJsonConfig_CheckboxSelectQuestion extends ODQuestion
choices:ODQuestionsJsonConfig_RadioCheckboxChoice[]
}
/**## ODQuestionsJsonConfig_FileUploadQuestion `interface`
* All properties for checkbox select questions in the `questions.jsonc` config!
*/
export interface ODQuestionsJsonConfig_FileUploadQuestion extends ODQuestionsJsonConfig_BaseQuestion {
type:"file-upload",
/**Verify the amount of uploaded files (minimum/maximum). */
limits:ODQuestionsJsonConfig_FileUploadLimits
}
/**## ODQuestionsJsonConfig_QuestionsData `type`
* All contents of the `questions.jsonc` config file.
*/
@@ -733,7 +752,8 @@ export type ODQuestionsJsonConfig_QuestionsData = (
ODQuestionsJsonConfig_TextDisplayQuestion|
ODQuestionsJsonConfig_DropdownQuestion|
ODQuestionsJsonConfig_RadioSelectQuestion|
ODQuestionsJsonConfig_CheckboxSelectQuestion
ODQuestionsJsonConfig_CheckboxSelectQuestion|
ODQuestionsJsonConfig_FileUploadQuestion
)[]
///////////////////////////////////////
+13
View File
@@ -311,6 +311,19 @@ export const migrations = [
{title:"Choice D",description:"Fear",selectedByDefault:false}
]
},
{
id:"example-file-upload",
name:"Example File Upload",
description:"With this question type, users can upload one or multiple files.",
type:"file-upload",
required:true,
limits:{
enabled:false,
min:0,
max:1
}
},
{
id:"example-text-display-question",
type:"text-display",
+15 -1
View File
@@ -686,7 +686,21 @@ export const defaultQuestionsStructure = new api.ODCheckerArrayStructure("opendi
],cliDisplayName:"Choice",cliDisplayDescription:"A choice for this checkbox select question."}),cliDisplayName:"Choices",cliDisplayPropertyName:"choice",cliDisplayDescription:"Manage all available choices of this checkbox select question."})}
],cliDisplayName:"Checkbox Select Question",cliDisplayDescription:"Manage, customise and configure the checkbox select question to your preference."})},
],cliDisplayName:"Question",cliDisplayDescription:"Manage a question of one of the 6 types: short, paragraph, text-display, dropdown, radio-select, checkbox-select."}),cliDisplayName:"Questions",cliDisplayDescription:"A list of all questions in the bot. Here you can add, modify & remove existing questions or customise them to your preference."})
//FILE UPLOAD QUESTION
{name:"File Upload Question",priority:0,properties:[{key:"type",value:"file-upload"}],checker:new api.ODCheckerObjectStructure("opendiscord:file-upload-question",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
{key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-éèçàêâôûî]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this question. Used in ticket options."})},
{key:"name",checker:new api.ODCheckerStringStructure("opendiscord:question-name",{minLength:3,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this question."})},
{key:"description",checker:new api.ODCheckerStringStructure("opendiscord:question-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this question."})},
{key:"required",checker:new api.ODCheckerBooleanStructure("opendiscord:question-required",{cliDisplayName:"Required",cliDisplayDescription:"Is this question required? If not, it can be left empty."})},
{key:"limits",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:amount",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:amount",{children:[
{key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:amount-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable checking the min/max amount of uploaded files."})},
{key:"min",checker:new api.ODCheckerNumberStructure("opendiscord:amount-min",{min:0,max:10,floatAllowed:false,cliDisplayName:"Min Amount",cliDisplayDescription:"The minimum amount of uploaded files required."})},
{key:"max",checker:new api.ODCheckerNumberStructure("opendiscord:amount-max",{min:1,max:10,floatAllowed:false,cliInitDefaultValue:10,cliDisplayName:"Max Amount",cliDisplayDescription:"The maximum amount of uploaded files allowed."})},
],cliDisplayName:"Upload Limits",cliDisplayDescription:"Verify the minimum or maximum amount of uploaded files."}),cliDisplayName:"Checkbox Limits",cliDisplayDescription:"Verify the minimum or maximum amount of uploaded files."})},
],cliDisplayName:"Checkbox Select Question",cliDisplayDescription:"Manage, customise and configure the file upload question to your preference."})},
],cliDisplayName:"Question",cliDisplayDescription:"Manage a question of one of the 7 types: short, paragraph, text-display, dropdown, radio-select, checkbox-select, file-upload."}),cliDisplayName:"Questions",cliDisplayDescription:"A list of all questions in the bot. Here you can add, modify & remove existing questions or customise them to your preference."})
export const defaultTranscriptsStructure = new api.ODCheckerObjectStructure("opendiscord:transcripts",{children:[
//GENERAL
+15
View File
@@ -286,6 +286,21 @@ export const defaultQuestionsFormatter = new fjs.TopLevelCommentFormatter(new fj
new fjs.PropertyFormatter("selectedByDefault"),
])),
])},
{key:"type",value:"file-upload",formatter:new fjs.ObjectFormatter(null,true,[
new fjs.MultiCommentFormatter("A file upload modal question where users can upload one or more files."),
new fjs.PropertyFormatter("id"),
new fjs.PropertyFormatter("name"),
new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.PropertyFormatter("type"),
new fjs.PropertyFormatter("required"),
new fjs.TextFormatter(""),
new fjs.ObjectFormatter("limits",true,[
new fjs.MultiCommentFormatter("Configure minimum/maximum amount of files to upload."),
new fjs.PropertyFormatter("enabled"),
new fjs.PropertyFormatter("min"),
new fjs.PropertyFormatter("max"),
]),
])},
])))
+72 -14
View File
@@ -4,13 +4,15 @@ export async function loadAllQuestions(){
const questionConfig = opendiscord.configs.get("opendiscord:questions")
if (!questionConfig) return
questionConfig.data.forEach((question) => {
if (question.type == "short"){
opendiscord.questions.add(loadShortQuestion(question))
}else if (question.type == "paragraph"){
opendiscord.questions.add(loadParagraphQuestion(question))
for (const question of questionConfig.data){
if (question.type === "short") opendiscord.questions.add(loadShortQuestion(question))
else if (question.type === "paragraph") opendiscord.questions.add(loadParagraphQuestion(question))
else if (question.type === "dropdown") opendiscord.questions.add(loadDropdownQuestion(question))
else if (question.type === "radio-select") opendiscord.questions.add(loadRadioSelectQuestion(question))
else if (question.type === "checkbox-select") opendiscord.questions.add(loadCheckboxSelectQuestion(question))
else if (question.type === "file-upload") opendiscord.questions.add(loadFileUploadQuestion(question))
else if (question.type === "text-display") opendiscord.questions.add(loadTextDisplayQuestion(question))
}
})
//update questions on config reload
questionConfig.onReload(async () => {
@@ -18,22 +20,25 @@ export async function loadAllQuestions(){
await opendiscord.questions.loopAll((data,id) => {opendiscord.questions.remove(id)})
//add new questions
questionConfig.data.forEach((question) => {
if (question.type == "short"){
opendiscord.questions.add(loadShortQuestion(question))
}else if (question.type == "paragraph"){
opendiscord.questions.add(loadParagraphQuestion(question))
for (const question of questionConfig.data){
if (question.type === "short") opendiscord.questions.add(loadShortQuestion(question))
else if (question.type === "paragraph") opendiscord.questions.add(loadParagraphQuestion(question))
else if (question.type === "dropdown") opendiscord.questions.add(loadDropdownQuestion(question))
else if (question.type === "radio-select") opendiscord.questions.add(loadRadioSelectQuestion(question))
else if (question.type === "checkbox-select") opendiscord.questions.add(loadCheckboxSelectQuestion(question))
else if (question.type === "file-upload") opendiscord.questions.add(loadFileUploadQuestion(question))
else if (question.type === "text-display") opendiscord.questions.add(loadTextDisplayQuestion(question))
}
})
})
}
export const loadShortQuestion = (option:api.ODQuestionsJsonConfig_ShortQuestion) => {
return new api.ODShortQuestion(option.id,[
new api.ODQuestionData("opendiscord:name",option.name),
new api.ODQuestionData("opendiscord:description",option.description),
new api.ODQuestionData("opendiscord:required",option.required),
new api.ODQuestionData("opendiscord:placeholder",option.placeholder),
new api.ODQuestionData("opendiscord:placeholder",option.placeholder),
new api.ODQuestionData("opendiscord:length-enabled",option.length.enabled),
new api.ODQuestionData("opendiscord:length-min",option.length.min),
new api.ODQuestionData("opendiscord:length-max",option.length.max),
@@ -43,11 +48,64 @@ export const loadShortQuestion = (option:api.ODQuestionsJsonConfig_ShortQuestion
export const loadParagraphQuestion = (option:api.ODQuestionsJsonConfig_ParagraphQuestion) => {
return new api.ODParagraphQuestion(option.id,[
new api.ODQuestionData("opendiscord:name",option.name),
new api.ODQuestionData("opendiscord:description",option.description),
new api.ODQuestionData("opendiscord:required",option.required),
new api.ODQuestionData("opendiscord:placeholder",option.placeholder),
new api.ODQuestionData("opendiscord:placeholder",option.placeholder),
new api.ODQuestionData("opendiscord:length-enabled",option.length.enabled),
new api.ODQuestionData("opendiscord:length-min",option.length.min),
new api.ODQuestionData("opendiscord:length-max",option.length.max),
])
}
export const loadDropdownQuestion = (option:api.ODQuestionsJsonConfig_DropdownQuestion) => {
return new api.ODDropdownQuestion(option.id,[
new api.ODQuestionData("opendiscord:name",option.name),
new api.ODQuestionData("opendiscord:description",option.description),
new api.ODQuestionData("opendiscord:required",option.required),
new api.ODQuestionData("opendiscord:placeholder",option.placeholder),
new api.ODQuestionData("opendiscord:choices",option.choices)
])
}
export const loadRadioSelectQuestion = (option:api.ODQuestionsJsonConfig_RadioSelectQuestion) => {
return new api.ODRadioSelectQuestion(option.id,[
new api.ODQuestionData("opendiscord:name",option.name),
new api.ODQuestionData("opendiscord:description",option.description),
new api.ODQuestionData("opendiscord:required",option.required),
new api.ODQuestionData("opendiscord:choices",option.choices)
])
}
export const loadCheckboxSelectQuestion = (option:api.ODQuestionsJsonConfig_CheckboxSelectQuestion) => {
return new api.ODCheckboxSelectQuestion(option.id,[
new api.ODQuestionData("opendiscord:name",option.name),
new api.ODQuestionData("opendiscord:description",option.description),
new api.ODQuestionData("opendiscord:required",option.required),
new api.ODQuestionData("opendiscord:limits-enabled",option.limits.enabled),
new api.ODQuestionData("opendiscord:limits-min",option.limits.min),
new api.ODQuestionData("opendiscord:limits-max",option.limits.max),
new api.ODQuestionData("opendiscord:choices",option.choices)
])
}
export const loadFileUploadQuestion = (option:api.ODQuestionsJsonConfig_FileUploadQuestion) => {
return new api.ODFileUploadQuestion(option.id,[
new api.ODQuestionData("opendiscord:name",option.name),
new api.ODQuestionData("opendiscord:description",option.description),
new api.ODQuestionData("opendiscord:required",option.required),
new api.ODQuestionData("opendiscord:limits-enabled",option.limits.enabled),
new api.ODQuestionData("opendiscord:limits-min",option.limits.min),
new api.ODQuestionData("opendiscord:limits-max",option.limits.max),
])
}
export const loadTextDisplayQuestion = (option:api.ODQuestionsJsonConfig_TextDisplayQuestion) => {
return new api.ODTextDisplayQuestion(option.id,[
new api.ODQuestionData("opendiscord:text-contents",option.textContents)
])
}
+2 -2
View File
@@ -647,7 +647,7 @@ const main = async () => {
//load modal builders
opendiscord.log("Loading modals...","system")
if (opendiscord.sharedFuses.getFuse("modalBuildersLoading")){
await (await import("./builders/modals.js")).registerAllModals()
//Deprecated, moved to "modal components"
}
await opendiscord.events.get("onModalBuilderLoad").emit([opendiscord.builders.modals,opendiscord.builders,opendiscord.actions])
await opendiscord.events.get("afterModalBuildersLoaded").emit([opendiscord.builders.modals,opendiscord.builders,opendiscord.actions])
@@ -671,7 +671,7 @@ const main = async () => {
//load modal components
opendiscord.log("Loading modal components...","system")
if (opendiscord.sharedFuses.getFuse("modalComponentsLoading")){
//TODO!!
await (await import("./components/modals.js")).registerModalComponents()
}
await opendiscord.events.get("onModalComponentLoad").emit([opendiscord.components.modals,opendiscord.components,opendiscord.actions])
await opendiscord.events.get("afterModalComponentsLoaded").emit([opendiscord.components.modals,opendiscord.components,opendiscord.actions])