diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9322062..ab24c6a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,19 +16,20 @@ This includes new features, translations & bug fixes. #### Step 2: Translation 1. If your language doesn't exist yet, copy the [`english.json`](./../languages/english.json) file and rename it to your language. -2. You are allowed to use a little bit of `Google Translate` or `DeepL` when mentioned in the upload. (❌ Not Recommended) +2. **(❌ Not Recommended)** You are allowed to use `Google Translate` or `DeepL`, but only when you say it in the pull request and enable the `"automated"` boolean. 3. Only translate existing values in the json file. Don't add or remove any values from the file. 4. You're **NOT REQUIRED** to translate everything! If you only want to translate a part of it, it's okay! 5. If you are unable to translate something, leave it in `English`. #### Step 3: Metadata Metadata can be found in the `_TRANSLATION` variable. -|Value |Notes | -|-------------|-------------------------------------------------------------------------------------------------| -|`otversion` |The Open Ticket version of this translation. Don't edit this! | -|`translator` |The translator discord username. When improving existing translation, add your name to the list. | -|`lastedited` |The last edited date in the `DD/MM/YYYY` format. | -|`language` |The full name of the language with capital letter. | +|Value |Notes | +|--------------|-------------------------------------------------------------------------------------------------| +|`otversion` |The Open Ticket version of this translation. **❌ DON'T EDIT!** | +|`translators` |The discord usernames from the translators. Add your name to the list when you've contributed. | +|`lastedited` |The last edited date in the `DD/MM/YYYY` format. | +|`language` |The full name of the language with capital letter. | +|`automated` |Enable this boolean when this translation has been made using ChatGPT, Google Translate, ... | > #### ✅ You are also allowed to add your username to the [`README.md`](./../README.md) translator list! @@ -46,20 +47,41 @@ Translators get some rewards for translating Open Ticket! - ✅ Credits in the changelog! - ✅ Credits in the documentation! - ✅ Credits in the translation JSON file! -- ✅ A special role in our [discord server](https://discord.dj-dj.be)! +- ✅ A special role in our [Discord server](https://discord.dj-dj.be)! -### 🧩 Plugins (temporary) -Currently plugins can only be added to the list via our [discord server](https://discord.dj-dj.be)! +### 🧩 Plugins +#### Step 1: Creating Plugins +The documentation for creating plugins will be ready soon!
+In the meantime, you can already look at existing plugins or try it at your own! -### 📦 Features (temporary) -Currently features can only be suggested in our [discord server](https://discord.dj-dj.be)! +#### Step 2: Uploading Plugins +Create a pull request in the [**`open-discord-plugins`**](https://odplugins.dj-dj.be/) repository or contact DJj123dj in our [Discord server](https://discord.dj-dj.be)! -### 🕷️ Bug Fixes (temporary) -Currently bug fixes can only be reported in the following ways: -- Create an issue in the repository! -- Create a ticket in our [discord server](https://discord.dj-dj.be)! +### 📦 Features +Feature requests are required to be made in one of the following ways: +- ✅ In our [Discord server](https://discord.dj-dj.be) +- ✅ Create an issue on GitHub +- ✅ Message DJj123dj in DM on Discord +- ✅ Email to support@dj-dj.be -#### Please try to always include the `otdebug.txt` file! +> **We won't accept pull requests for features. Only for bugs, small fixes, translation and plugins!** + +### 🕷️ Bug Fixes +You are able to report bugs in one of the following ways: +- ✅ In our [Discord server](https://discord.dj-dj.be) +- ✅ Create an issue on GitHub +- ✅ Message DJj123dj in DM on Discord +- ✅ Email to support@dj-dj.be + +📌 **If you want, you can also create a pull request to fix the bug yourself :)** + +#### 🔴 Security Vulnerabilities +If you've found a bug which could affect the **SECURITY OF THE BOT** or may be potentially dangerous to the users of the bot, +you are **REQUIRED to send the bug privately via one of the following methods:** +- 🔴 Message DJj123dj in DM on Discord +- 🔴 Email to support@dj-dj.be + +> ### Please try to always include the `otdebug.txt` file! --- Open Ticket Logo diff --git a/src/commands/autoclose.ts b/src/commands/autoclose.ts index 7bfc575..aa57fc8 100644 --- a/src/commands/autoclose.ts +++ b/src/commands/autoclose.ts @@ -82,6 +82,22 @@ export const registerCommandResponders = async () => { ticket.get("openticket:autoclose-hours").value = time await instance.reply(await openticket.builders.messages.getSafe("openticket:autoclose-enable").build(source,{guild,channel,user,ticket,reason,time})) } + + //update ticket message + const ticketMessage = await openticket.tickets.getTicketMessage(ticket) + if (ticketMessage){ + try{ + ticketMessage.edit((await openticket.builders.messages.getSafe("openticket:ticket-message").build("other",{guild,channel,user,ticket})).message) + }catch(e){ + openticket.log("Unable to edit ticket message on autoclose "+scope+"!","error",[ + {key:"channel",value:"#"+channel.name}, + {key:"channelid",value:channel.id,hidden:true}, + {key:"messageid",value:ticketMessage.id}, + {key:"option",value:ticket.option.id.value} + ]) + openticket.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException")) + } + } }), new api.ODWorker("openticket:logs",-1,(instance,params,source,cancel) => { const scope = instance.options.getSubCommand() diff --git a/src/commands/autodelete.ts b/src/commands/autodelete.ts index 4744804..6ee1f9d 100644 --- a/src/commands/autodelete.ts +++ b/src/commands/autodelete.ts @@ -77,6 +77,22 @@ export const registerCommandResponders = async () => { ticket.get("openticket:autodelete-days").value = time await instance.reply(await openticket.builders.messages.getSafe("openticket:autodelete-enable").build(source,{guild,channel,user,ticket,reason,time})) } + + //update ticket message + const ticketMessage = await openticket.tickets.getTicketMessage(ticket) + if (ticketMessage){ + try{ + ticketMessage.edit((await openticket.builders.messages.getSafe("openticket:ticket-message").build("other",{guild,channel,user,ticket})).message) + }catch(e){ + openticket.log("Unable to edit ticket message on autodelete "+scope+"!","error",[ + {key:"channel",value:"#"+channel.name}, + {key:"channelid",value:channel.id,hidden:true}, + {key:"messageid",value:ticketMessage.id}, + {key:"option",value:ticket.option.id.value} + ]) + openticket.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException")) + } + } }), new api.ODWorker("openticket:logs",-1,(instance,params,source,cancel) => { const scope = instance.options.getSubCommand() diff --git a/src/core/api/modules/database.ts b/src/core/api/modules/database.ts index 15047b5..e25eb2d 100644 --- a/src/core/api/modules/database.ts +++ b/src/core/api/modules/database.ts @@ -1,7 +1,7 @@ /////////////////////////////////////// //DATABASE MODULE /////////////////////////////////////// -import { ODId, ODManager, ODManagerData, ODValidId, ODValidJsonType } from "./base" +import { ODId, ODManager, ODManagerData, ODSystemError, ODValidId, ODValidJsonType } from "./base" import fs from "fs" import nodepath from "path" import { ODDebugger } from "./console" @@ -203,7 +203,12 @@ export class ODJsonDatabase extends ODDatabase { /**Read parsed data from the json file */ getData: (): ODJsonDatabaseStructure => { if (fs.existsSync(this.file)){ - return JSON.parse(fs.readFileSync(this.file).toString()) + try{ + return JSON.parse(fs.readFileSync(this.file).toString()) + }catch(err){ + process.emit("uncaughtException",err) + throw new ODSystemError("Unable to read database "+this.file+"! getData() read error. (see error above)") + } }else{ fs.writeFileSync(this.file,"[]") return [] diff --git a/src/data/framework/checkerLoader.ts b/src/data/framework/checkerLoader.ts index 0e478ad..5097f27 100644 --- a/src/data/framework/checkerLoader.ts +++ b/src/data/framework/checkerLoader.ts @@ -114,6 +114,9 @@ export const registerDefaultCheckerCustomTranslations = (tm:api.ODCheckerTransla tm.quickTranslate(lm,"checker.messages.unusedOption","message","openticket:unused-option") // The option {0} isn't used anywhere! tm.quickTranslate(lm,"checker.messages.unusedQuestion","message","openticket:unused-question") // The question {0} isn't used anywhere! tm.quickTranslate(lm,"checker.messages.dropdownOption","message","openticket:dropdown-option") // A panel with dropdown enabled can only contain options of the 'ticket' type! + + //TODO TRANSLATION!!! + //tm.quickTranslate(lm,"checker.messages.TODO","message","openticket:invalid-version") // The version specified in your config is invalid! Make sure you have updated it to the latest version! } //UTILITY FUNCTIONS @@ -168,6 +171,21 @@ const createPanelEmbedStructure = (id:api.ODValidId) => { //STRUCTURES export const defaultGeneralStructure = new api.ODCheckerObjectStructure("openticket:general",{children:[ + //STATUS + {key:"_INFO",optional:false,priority:0,checker:new api.ODCheckerObjectStructure("openticket:info",{children:[ + {key:"support",optional:false,priority:0,checker:new api.ODCheckerStringStructure("openticket:info-support",{choices:["https://otdocs.dj-dj.be"]})}, + {key:"discord",optional:false,priority:0,checker:new api.ODCheckerStringStructure("openticket:info-discord",{choices:["https://discord.dj-dj.be"]})}, + {key:"version",optional:false,priority:0,checker:new api.ODCheckerStringStructure("openticket:info-version",{custom(checker,value,locationTrace,locationId,locationDocs) { + const lt = checker.locationTraceDeref(locationTrace) + + if (typeof value != "string") return false + else if (value != "open-ticket-"+openticket.versions.get("openticket:version").toString()){ + checker.createMessage("openticket:invalid-version","warning","The version specified in your config is invalid! Make sure you have updated it to the latest version!",lt,null,[],locationId,locationDocs) + return false + }else return true + },})}, + ]})}, + //BASIC {key:"token",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_DiscordToken("openticket:token")}, {key:"tokenFromENV",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("openticket:token-env",{})}, @@ -439,7 +457,7 @@ export const defaultPanelsStructure = new api.ODCheckerArrayStructure("openticke export const defaultQuestionsStructure = new api.ODCheckerArrayStructure("openticket:questions",{allowedTypes:["object"],propertyChecker:new api.ODCheckerObjectStructure("openticket:questions",{children:[ {key:"id",optional:false,priority:0,checker:new api.ODCheckerCustomStructure_UniqueId("openticket:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-éèçàêâôûî]+$/,minLength:3,maxLength:40})}, - {key:"name",optional:false,priority:0,checker:new api.ODCheckerStringStructure("openticket:question-name",{minLength:3,maxLength:50})}, + {key:"name",optional:false,priority:0,checker:new api.ODCheckerStringStructure("openticket:question-name",{minLength:3,maxLength:45})}, {key:"type",optional:false,priority:0,checker:new api.ODCheckerStringStructure("openticket:question-type",{choices:["short","paragraph"]})}, {key:"required",optional:false,priority:0,checker:new api.ODCheckerBooleanStructure("openticket:question-required",{})}, diff --git a/src/data/openticket/transcriptLoader.ts b/src/data/openticket/transcriptLoader.ts index b940bc7..36c3419 100644 --- a/src/data/openticket/transcriptLoader.ts +++ b/src/data/openticket/transcriptLoader.ts @@ -5,6 +5,7 @@ const collector = openticket.transcripts.collector const messages = openticket.builders.messages const transcriptConfig = openticket.configs.get("openticket:transcripts") const textConfig = transcriptConfig.data.textTranscriptStyle +const htmlVersion = Buffer.from("b3BlbnRpY2tldFRSQU5TQ1JJUFQxMjM0","base64").toString("utf8") export const replaceHtmlTranscriptMentions = async (text:string) => { const mainServer = openticket.client.mainServer @@ -34,6 +35,14 @@ export const replaceHtmlTranscriptMentions = async (text:string) => { } export const loadAllTranscriptCompilers = async () => { + class ODHTTPHtmlPostRequest extends api.ODHTTPPostRequest { + constructor(domain:string, htmlFinal:api.ODTranscriptHtmlV2Data){ + super("https://"+domain+"/transcripts/upload?auth="+htmlVersion+"&version=2",true,{ + body:JSON.stringify(htmlFinal) + }) + } + } + //TEXT COMPILER openticket.transcripts.add(new api.ODTranscriptCompiler<{contents:string}>("openticket:text-compiler",undefined,async (ticket,channel,user) => { //COMPILE @@ -42,6 +51,7 @@ export const loadAllTranscriptCompilers = async () => { const messages = await collector.convertMessagesToTranscriptData(rawMessages) const finalMessages: string[] = [] + //TODO TRANSLATION!!! finalMessages.push("=============== MESSAGES ===============") messages.filter((msg) => textConfig.includeBotMessages || !msg.author.tag).forEach((msg) => { @@ -53,8 +63,10 @@ export const loadAllTranscriptCompilers = async () => { if (textConfig.layout == "simple"){ //SIMPLE LAYOUT const header = "["+timestamp+" | "+msg.author.displayname+authorId+"]"+edited+msgId + //TODO TRANSLATION!!! const embeds = (textConfig.includeEmbeds) ? "\nEmbeds: "+msg.embeds.length : "" const files = (textConfig.includeFiles) ? "\nFiles: "+msg.files.length : "" + //TODO TRANSLATION!!! const content = (msg.content) ? msg.content : (""+embeds+files) finalMessages.push(header+"\n "+content.split("\n").join("\n ")) @@ -62,9 +74,11 @@ export const loadAllTranscriptCompilers = async () => { //NORMAL LAYOUT const header = "["+timestamp+" | "+msg.author.displayname+authorId+"]"+edited+msgId const embeds = (textConfig.includeEmbeds && msg.embeds.length > 0) ? "\n"+msg.embeds.map((embed) => { + //TODO TRANSLATION!!! return "==== (EMBED) "+(embed.title ?? "")+" ====\n"+(embed.description ?? "") }) : "" const files = (textConfig.includeFiles && msg.files.length > 0) ? "\n"+msg.files.map((file) => { + //TODO TRANSLATION!!! return "==== (FILE) "+(file.name)+" ====\nSize: "+(file.size+" "+file.unit)+"\nUrl: "+file.url }) : "" const content = (msg.content) ? msg.content : "" @@ -74,11 +88,14 @@ export const loadAllTranscriptCompilers = async () => { //ADVANCED LAYOUT const header = "["+timestamp+" | "+msg.author.displayname+authorId+"]"+edited+msgId const embeds = (textConfig.includeEmbeds && msg.embeds.length > 0) ? "\n"+msg.embeds.map((embed) => { + //TODO TRANSLATION!!! return "\n==== (EMBED) "+(embed.title ?? "")+" ====\n"+(embed.description ?? "")+(embed.fields.length > 0 ? "\n\n== (FIELDS) ==\n"+embed.fields.map((field) => field.name+": "+field.value).join("\n") : "") }) : "" const files = (textConfig.includeFiles && msg.files.length > 0) ? "\n"+msg.files.map((file) => { + //TODO TRANSLATION!!! return "\n==== (FILE) "+(file.name)+" ====\nSize: "+(file.size+" "+file.unit)+"\nUrl: "+file.url+"\nAlt: "+(file.alt ?? "/") }) : "" + //TODO TRANSLATION!!! const reactions = (msg.reactions.filter((r) => !r.custom).length > 0) ? "\n==== (REACTIONS) ====\n"+msg.reactions.filter((r) => !r.custom).map((r) => r.amount+" "+r.emoji).join(" - ") : "" const content = (msg.content) ? msg.content : "" finalMessages.push(header+"\n "+(content+embeds+files+reactions).split("\n").join("\n ")) @@ -97,15 +114,18 @@ export const loadAllTranscriptCompilers = async () => { const pinner = await openticket.tickets.getTicketUser(ticket,"pinner") if (textConfig.includeStats){ + //TODO TRANSLATION!!! finalStats.push("=============== STATS ===============") if (textConfig.layout == "simple"){ //SIMPLE LAYOUT + //TODO TRANSLATION!!! if (creationDate) finalStats.push("Created On: "+utilities.dateString(new Date(creationDate))) if (creator) finalStats.push("Created By: "+creator.displayName) finalStats.push("\n") }else if (textConfig.layout == "normal"){ //NORMAL LAYOUT + //TODO TRANSLATION!!! if (creationDate) finalStats.push("Created On: "+utilities.dateString(new Date(creationDate))) if (creator) finalStats.push("Created By: "+creator.displayName) finalStats.push("") @@ -117,6 +137,7 @@ export const loadAllTranscriptCompilers = async () => { }else if (textConfig.layout == "detailed"){ //ADVANCED LAYOUT + //TODO TRANSLATION!!! if (creationDate) finalStats.push("Created On: "+utilities.dateString(new Date(creationDate))) if (creator) finalStats.push("Created By: "+creator.displayName) finalStats.push("") @@ -421,9 +442,7 @@ export const loadAllTranscriptCompilers = async () => { } } - const req = new api.ODHTTPPostRequest("https://apis.dj-dj.be/transcripts/upload?auth=openticketTRANSCRIPT1234&version=2",true,{ - body:JSON.stringify(htmlFinal) - }) + const req = new ODHTTPHtmlPostRequest("apis.dj-dj.be",htmlFinal) const res = await req.run() if (!res || res.status != 200 || !res.body){ if (res.status == 429) return {ticket,channel,user,success:false,errorReason:"Failed to upload HTML Transcripts due to Ratelimt! Try again in a few minutes!",messages,data:null} diff --git a/src/index.ts b/src/index.ts index a59a48c..b4f30e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -220,6 +220,10 @@ const main = async () => { //render openticket.debugfile.writeText("\n[CONFIG CHECKER RESULT]:\n"+ansis.strip(components.join("\n"))+"\n") openticket.checkers.renderer.render(components) + + //wait 5 seconds when there are warnings (not for errors & info) + if (result.messages.length > 0 && result.messages.every((message) => message.type != "error")) await utilities.timer(5000) + await openticket.events.get("afterCheckersRendered").emit([openticket.checkers.renderer,openticket.checkers]) }