Started with CLI "Quick Setup" feature.

This commit is contained in:
DJj123dj
2025-04-16 21:44:23 +02:00
parent ad623f9650
commit e73c8e9b3b
4 changed files with 200 additions and 54 deletions
+75
View File
@@ -0,0 +1,75 @@
import {opendiscord, api, utilities} from "../../index"
import {Terminal, terminal} from "terminal-kit"
import ansis from "ansis"
const logo = [
" ██████╗ ██████╗ ███████╗███╗ ██╗ ████████╗██╗ ██████╗██╗ ██╗███████╗████████╗ ",
" ██╔═══██╗██╔══██╗██╔════╝████╗ ██║ ╚══██╔══╝██║██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝ ",
" ██║ ██║██████╔╝█████╗ ██╔██╗ ██║ ██║ ██║██║ █████╔╝ █████╗ ██║ ",
" ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║ ██║ ██║██║ ██╔═██╗ ██╔══╝ ██║ ",
" ╚██████╔╝██║ ███████╗██║ ╚████║ ██║ ██║╚██████╗██║ ██╗███████╗ ██║ ",
" ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝ "
]
/**A utility function to center text to a certain width. */
export function centerText(text:string,width:number){
if (width < text.length) return text
let newWidth = width-ansis.strip(text).length+1
let final = " ".repeat(newWidth/2)+text
return final
}
/**A utility function to terminate the interactive CLI. */
export async function terminate(){
terminal.grabInput(false)
terminal.clear()
terminal.green("👋 Exited the Open Ticket Interactive Setup CLI.\n")
process.exit(0)
}
terminal.on("key",(name,matches,data) => {
if (name == "CTRL_C") terminate()
})
/**Render the header of the interactive CLI. */
export function renderHeader(path:(string|number)[]|string){
terminal.grabInput(true)
terminal.clear().moveTo(1,1)
terminal(ansis.hex("#f8ba00")(logo.join("\n")+"\n"))
terminal.bold(centerText("Interactive Setup CLI - Version: "+opendiscord.versions.get("opendiscord:version").toString()+" - Support: https://discord.dj-dj.be\n",88))
if (typeof path == "string") terminal.cyan(centerText(path+"\n\n",88))
else if (path.length < 1) terminal.cyan(centerText("👋 Hi! Welcome to the Open Ticket Interactive Setup CLI! 👋\n\n",88))
else terminal.cyan(centerText("🌐 Current Location: "+path.map((v,i) => {
if (i == 0) return v.toString()
else if (typeof v == "string") return ".\""+v+"\""
else if (typeof v == "number") return "."+v
}).join("")+"\n\n",88))
}
async function renderCliModeSelector(backFn:(() => api.ODPromiseVoid)){
renderHeader([])
terminal(ansis.bold.green("Please select what CLI module you want to use.\n")+ansis.italic.gray("(use arrow keys to navigate, exit using escape)\n"))
const answer = await terminal.singleColumnMenu([
"✏️ Edit Config "+ansis.gray("=> Edit the current config, add/remove new tickets/questions/panels & more!"),
"⏱️ Quick Setup "+ansis.gray("=> A quick and easy way of setting up the bot in your Discord server."),
],{
leftPadding:"> ",
style:terminal.cyan,
selectedStyle:terminal.bgDefaultColor.bold,
submittedStyle:terminal.bgBlue,
extraLines:2,
cancelable:true
}).promise
if (answer.canceled) return await backFn()
else if (answer.selectedIndex == 0) await (await import("./editConfig.js")).renderEditConfig(async () => {await renderCliModeSelector(backFn)})
else if (answer.selectedIndex == 1) await (await import("./quickSetup.js")).renderQuickSetup(async () => {await renderCliModeSelector(backFn)})
}
export async function execute(){
if (terminal.width < 100 || terminal.height < 35){
terminal(ansis.red.bold("\n\nMake sure your console or cmd window has a "+ansis.cyan("minimum width & height")+" of "+ansis.cyan("100x35")+" characters."))
terminal(ansis.red.bold("\nOtherwise the Open Ticket Interactive Setup CLI will be rendered incorrectly."))
terminal(ansis.red.bold("\nThe current terminal dimensions are: "+ansis.cyan(terminal.width+"x"+terminal.height)+"."))
}else await renderCliModeSelector(terminate)
}
@@ -1,50 +1,9 @@
import {opendiscord, api, utilities} from "../../index" import {opendiscord, api, utilities} from "../../index"
import {Terminal, terminal} from "terminal-kit" import {Terminal, terminal} from "terminal-kit"
import ansis from "ansis" import ansis from "ansis"
import {renderHeader} from "./cli"
const logo = [ export async function renderEditConfig(backFn:(() => api.ODPromiseVoid)){
" ██████╗ ██████╗ ███████╗███╗ ██╗ ████████╗██╗ ██████╗██╗ ██╗███████╗████████╗ ",
" ██╔═══██╗██╔══██╗██╔════╝████╗ ██║ ╚══██╔══╝██║██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝ ",
" ██║ ██║██████╔╝█████╗ ██╔██╗ ██║ ██║ ██║██║ █████╔╝ █████╗ ██║ ",
" ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║ ██║ ██║██║ ██╔═██╗ ██╔══╝ ██║ ",
" ╚██████╔╝██║ ███████╗██║ ╚████║ ██║ ██║╚██████╗██║ ██╗███████╗ ██║ ",
" ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝ "
]
/**A utility function to center text to a certain width. */
function centerText(text:string,width:number){
if (width < text.length) return text
let newWidth = width-ansis.strip(text).length+1
let final = " ".repeat(newWidth/2)+text
return final
}
/**A utility function to terminate the interactive CLI. */
async function terminate(){
terminal.grabInput(false)
terminal.clear()
terminal.green("👋 Exited the Open Ticket Interactive Setup CLI.\n")
process.exit(0)
}
terminal.on("key",(name,matches,data) => {
if (name == "CTRL_C") terminate()
})
/**Render the header of the interactive CLI. */
function renderHeader(path:(string|number)[]){
terminal.grabInput(true)
terminal.clear().moveTo(1,1)
terminal(ansis.hex("#f8ba00")(logo.join("\n")+"\n"))
terminal.bold(centerText("Interactive Setup CLI - Version: "+opendiscord.versions.get("opendiscord:version").toString()+" - Support: https://discord.dj-dj.be\n",88))
if (path.length < 1) terminal.cyan(centerText("👋 Hi! Welcome to the Open Ticket Interactive Setup CLI! 👋\n\n",88))
else terminal.cyan(centerText("🌐 Current Location: "+path.map((v,i) => {
if (i == 0) return v.toString()
else if (typeof v == "string") return ".\""+v+"\""
else if (typeof v == "number") return "."+v
}).join("")+"\n\n",88))
}
async function renderConfigSelector(backFn:(() => api.ODPromiseVoid)){
renderHeader([]) renderHeader([])
terminal(ansis.bold.green("Please select which config you would like to edit.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n")) terminal(ansis.bold.green("Please select which config you would like to edit.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
@@ -65,7 +24,7 @@ async function renderConfigSelector(backFn:(() => api.ODPromiseVoid)){
if (answer.canceled) return await backFn() if (answer.canceled) return await backFn()
const checker = checkerList[answer.selectedIndex] const checker = checkerList[answer.selectedIndex]
const configData = checker.config.data as api.ODValidJsonType const configData = checker.config.data as api.ODValidJsonType
await chooseConfigStructure(checker,async () => {await renderConfigSelector(backFn)},checker.structure,configData,{},NaN,["("+checker.config.path+")"]) await chooseConfigStructure(checker,async () => {await renderEditConfig(backFn)},checker.structure,configData,{},NaN,["("+checker.config.path+")"])
} }
async function chooseConfigStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerStructure,data:api.ODValidJsonType,parent:object|any[],parentIndex:string|number,path:(string|number)[]){ async function chooseConfigStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerStructure,data:api.ODValidJsonType,parent:object|any[],parentIndex:string|number,path:(string|number)[]){
@@ -938,7 +897,6 @@ async function renderAdditionConfigArrayStructureSelector(checker:api.ODChecker,
else if (answer.selectedIndex == 5) await renderConfigArrayStructureDuplicateSelector(checker,backFnFunc,structure,structure.options.propertyChecker,localData,parent,parentIndex,path) else if (answer.selectedIndex == 5) await renderConfigArrayStructureDuplicateSelector(checker,backFnFunc,structure,structure.options.propertyChecker,localData,parent,parentIndex,path)
} }
async function renderAdditionConfigTypeSwitchStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerTypeSwitchStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){ async function renderAdditionConfigTypeSwitchStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerTypeSwitchStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){
renderHeader(path) renderHeader(path)
terminal(ansis.bold.green("You are now creating "+(typeof parentIndex == "string" ? "the property "+ansis.blue("\""+parentIndex+"\"") : "property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n")) terminal(ansis.bold.green("You are now creating "+(typeof parentIndex == "string" ? "the property "+ansis.blue("\""+parentIndex+"\"") : "property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
@@ -973,11 +931,3 @@ async function renderAdditionConfigTypeSwitchStructure(checker:api.ODChecker,bac
else if (answer.selectedText.startsWith("Create as array/list") && structure.options.array) await renderAdditionConfigArrayStructureSelector(checker,async () => {await renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.array,parent,parentIndex,path,localPath) else if (answer.selectedText.startsWith("Create as array/list") && structure.options.array) await renderAdditionConfigArrayStructureSelector(checker,async () => {await renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.array,parent,parentIndex,path,localPath)
else if (answer.selectedText.startsWith("Create as null") && structure.options.null) await renderAdditionConfigNullStructure(checker,async () => {await renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.null,parent,parentIndex,path,localPath) else if (answer.selectedText.startsWith("Create as null") && structure.options.null) await renderAdditionConfigNullStructure(checker,async () => {await renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.null,parent,parentIndex,path,localPath)
} }
export async function execute(){
if (terminal.width < 100 || terminal.height < 35){
terminal(ansis.red.bold("\n\nMake sure your console or cmd window has a "+ansis.cyan("minimum width & height")+" of "+ansis.cyan("100x35")+" characters."))
terminal(ansis.red.bold("\nOtherwise the Open Ticket Interactive Setup CLI will be rendered incorrectly."))
terminal(ansis.red.bold("\nThe current terminal dimensions are: "+ansis.cyan(terminal.width+"x"+terminal.height)+"."))
}else await renderConfigSelector(terminate)
}
+121
View File
@@ -0,0 +1,121 @@
import {opendiscord, api, utilities} from "../../index"
import {Terminal, terminal} from "terminal-kit"
import ansis from "ansis"
import {renderHeader} from "./cli"
export async function renderQuickSetup(backFn:() => api.ODPromiseVoid){
if (quickSetupRequiresReset()) await renderQuickSetupWarning(backFn)
else await renderQuickSetupWelcome(backFn)
}
function quickSetupRequiresReset(): boolean {
const generalConfig = opendiscord.configs.get("opendiscord:general")
if (generalConfig.data.token != "your bot token here! (or leave empty when using 'tokenFromENV')") return true
if (generalConfig.data.mainColor != "#f8ba00") return true
if (generalConfig.data.language != "english") return true
if (generalConfig.data.prefix != "!ticket ") return true
if (generalConfig.data.serverId != "discord server id") return true
return false
}
async function renderQuickSetupWarning(backFn:() => api.ODPromiseVoid) {
renderHeader("⏱️ Open Ticket Quick Setup: Warning")
terminal.bold(ansis.yellow("WARNING! ")+ansis.red("By using the 'Quick Setup' feature, your current config will be completely resetted!"))
terminal.gray("\n\nAre you sure you want to continue?")
const answer = await terminal.singleColumnMenu([
ansis.green("✅ No, take me back."),
ansis.red("🚨 Yes, continue and reset the config.")
],{
leftPadding:"> ",
style:terminal.cyan,
selectedStyle:terminal.bgDefaultColor.bold,
submittedStyle:terminal.bgBlue,
extraLines:2,
cancelable:true
}).promise
if (answer.canceled || answer.selectedIndex == 0) return await backFn()
if (answer.selectedIndex == 1) await renderQuickSetupWelcome(async () => {await renderQuickSetupWarning(backFn)})
}
async function renderQuickSetupWelcome(backFn:() => api.ODPromiseVoid){
renderHeader("⏱️ Open Ticket Quick Setup: Introduction")
terminal.bold.underline.blue("Open Ticket: Quick Setup\n")
terminal.gray([
"Hi there! Thank you for downloading and installing Open Ticket.",
"You have chosen to configure the bot using the 'Quick Setup CLI'.",
"",
"This program will help you with configuring Open Ticket using a step-by-step method.",
"If you've ever used Google Forms, then this will probably be very easy for you 😉.",
"",
ansis.magenta("The configuration should normally only take around 5 minutes."),
ansis.magenta("Once you've completed the form, the bot is technically ready for usage!")
].join("\n")+"\n\n")
const answer = await terminal.singleColumnMenu([
ansis.green("Press 'Enter' to start!")
],{
leftPadding:"> ",
style:terminal.cyan,
selectedStyle:terminal.bgDefaultColor.bold,
submittedStyle:terminal.bgBlue,
extraLines:2,
cancelable:true
}).promise
if (answer.canceled) return await backFn()
else if (answer.selectedIndex == 0) await renderQuickSetupDevPortalInfo(async () => {await renderQuickSetupWelcome(backFn)})
}
async function renderQuickSetupDevPortalInfo(backFn:() => api.ODPromiseVoid){
renderHeader("⏱️ Open Ticket Quick Setup: Dev Portal & Experience")
terminal.bold.blue("(Step 1) Have you already created a Discord bot to use for Open Ticket?\n")
const answer = await terminal.singleColumnMenu([
"✅ Yes I have, and it has been invited to the server.",
"❓ No not yet, I don't know how to create one.",
"👶 I've never seen a Discord bot before.",
],{
leftPadding:"> ",
style:terminal.gray,
selectedStyle:terminal.bgDefaultColor.bold,
submittedStyle:terminal.bgBlue,
extraLines:2,
cancelable:true
}).promise
if (answer.canceled) return await backFn()
else if (answer.selectedIndex == 0) await backFn() //TODO NEXT QUESTION
else if (answer.selectedIndex == 1) await renderQuickSetupDevPortalGuide(0,async () => {await renderQuickSetupDevPortalInfo(backFn)})
else if (answer.selectedIndex == 2) await renderQuickSetupDevPortalGuide(1,async () => {await renderQuickSetupDevPortalInfo(backFn)})
}
async function renderQuickSetupDevPortalGuide(variation:0|1,backFn:() => api.ODPromiseVoid){
renderHeader("⏱️ Open Ticket Quick Setup: Dev Portal & Experience")
if (variation == 0){
terminal.bold.blue("(Step 1.1) You've mentioned that you don't know how to create a Discord bot.\n\n")
terminal.gray("Please visit the following URL for a step-by-step guide on how to create a Discord bot.\nIf it still doesn't work, join our Discord server and we will help you further!\n"+ansis.magenta("=> https://otdocs.dj-dj.be/docs/guides/get-started#bot\n\n"))
}else{
terminal.bold.blue("(Step 1.2) You've mentioned that you've never seen Discord bot before.\n\n")
terminal.gray("How did you even download Open Ticket 🤪? But still, we have a step-by-step guide on how to create a Discord bot.\nIf it still doesn't work, join our Discord server and we will help you further!\n"+ansis.magenta("=> https://otdocs.dj-dj.be/docs/guides/get-started#bot\n\n"))
}
const answer = await terminal.singleColumnMenu([
ansis.green("✅ Alright, I've made the bot. Take me back please!")
],{
leftPadding:"> ",
style:terminal.cyan,
selectedStyle:terminal.bgDefaultColor.bold,
submittedStyle:terminal.bgBlue,
extraLines:2,
cancelable:true
}).promise
return await backFn()
}
+1 -1
View File
@@ -301,7 +301,7 @@ const main = async () => {
//switch to CLI context instead of running the bot //switch to CLI context instead of running the bot
if (useCliFlag && useCliFlag.value){ if (useCliFlag && useCliFlag.value){
await (await (import("./core/startup/cli.js"))).execute() await (await (import("./core/cli/cli.js"))).execute()
await utilities.timer(1000) await utilities.timer(1000)
console.log("\n\n"+ansis.red("❌ Something went wrong in the Interactive Setup CLI. Please try again or report a bug in our discord server.")) console.log("\n\n"+ansis.red("❌ Something went wrong in the Interactive Setup CLI. Please try again or report a bug in our discord server."))
process.exit(0) process.exit(0)