(v4.2.0) Large README overhaul (Part 1)
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"_INFO":"This document contains all Open Ticket sponsors.",
|
||||
"SPONSOR_TEMPLATE":{
|
||||
"name":"INSERT_NAME",
|
||||
"pictureUrl":"INSERT_PICTURE_URL",
|
||||
"profileUrl":"INSERT_PROFILE_URL",
|
||||
"sectionId":"INSERT_ID"
|
||||
},
|
||||
"SECTION_TEMPLATE":{
|
||||
"name":"INSERT_NAME",
|
||||
"id":"INSERT_ID",
|
||||
"pfpSize":50,
|
||||
"pfpColumns":5,
|
||||
"withNames":true
|
||||
},
|
||||
"sections":[
|
||||
{
|
||||
"name":"💎 Platinum Sponsors",
|
||||
"id":"platinum",
|
||||
"pfpSize":120,
|
||||
"pfpColumns":4,
|
||||
"withNames":true
|
||||
},
|
||||
{
|
||||
"name":"🥇 Gold Sponsors",
|
||||
"id":"gold",
|
||||
"pfpSize":60,
|
||||
"pfpColumns":8,
|
||||
"withNames":true
|
||||
},
|
||||
{
|
||||
"name":"🥈 Silver Sponsors",
|
||||
"id":"silver",
|
||||
"pfpSize":40,
|
||||
"pfpColumns":16,
|
||||
"withNames":false
|
||||
},
|
||||
{
|
||||
"name":"❤️ Past Sponsors",
|
||||
"id":"past",
|
||||
"pfpSize":30,
|
||||
"pfpColumns":20,
|
||||
"withNames":false
|
||||
}
|
||||
],
|
||||
"sponsors":[
|
||||
{
|
||||
"name":"Guillee3",
|
||||
"pictureUrl":"https://github.com/guillee3.png",
|
||||
"profileUrl":"https://github.com/guillee3",
|
||||
"sectionId":"platinum"
|
||||
},
|
||||
{
|
||||
"name":"Jacob Humston",
|
||||
"pictureUrl":"https://github.com/jacobhumston.png",
|
||||
"profileUrl":"https://github.com/jacobhumston",
|
||||
"sectionId":"platinum"
|
||||
},
|
||||
{
|
||||
"name":"SpyEye2",
|
||||
"pictureUrl":"https://github.com/SpyEye2.png",
|
||||
"profileUrl":"https://github.com/SpyEye2",
|
||||
"sectionId":"gold"
|
||||
},
|
||||
{
|
||||
"name":"DOSEV5",
|
||||
"pictureUrl":"https://github.com/DOSEV5.png",
|
||||
"profileUrl":"https://github.com/DOSEV5",
|
||||
"sectionId":"gold"
|
||||
},
|
||||
{
|
||||
"name":"YeeetSK",
|
||||
"pictureUrl":"https://github.com/YeeetSK.png",
|
||||
"profileUrl":"https://github.com/YeeetSK",
|
||||
"sectionId":"silver"
|
||||
},
|
||||
{
|
||||
"name":"BENZORICH",
|
||||
"pictureUrl":"https://github.com/BENZORICH.png",
|
||||
"profileUrl":"https://github.com/BENZORICH",
|
||||
"sectionId":"silver"
|
||||
},
|
||||
{
|
||||
"name":"Mods HD",
|
||||
"pictureUrl":"https://github.com/mods-hd.png",
|
||||
"profileUrl":"https://github.com/mods-hd",
|
||||
"sectionId":"silver"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 124 KiB |
+2
-4
@@ -19,9 +19,7 @@ otdebug.txt
|
||||
|
||||
.backup/*
|
||||
.tools/*
|
||||
!.tools/createDocs.js
|
||||
!.tools/mergeTranslations.js
|
||||
!.tools/typedoc-config.json
|
||||
!.tools/pterodactyl-eggs/
|
||||
!.tools/createSponsors.ts
|
||||
!.tools/mergeTranslations.ts
|
||||
!.tools/docker-compose.yml
|
||||
!.tools/dockerfile
|
||||
@@ -1,328 +0,0 @@
|
||||
//@ts-check
|
||||
const fs = require("fs")
|
||||
|
||||
/*////// OT DOCS GENERATOR ////////
|
||||
What does this file do?:
|
||||
Well, this file will parse the result returned from typedoc (the documentation generator).
|
||||
The resulting file will then be used in the OT Docs markdown generator to generate the Open Ticket API Reference.
|
||||
|
||||
In short:
|
||||
This file generates the documentation for Open Ticket :)
|
||||
|
||||
Contributing:
|
||||
If you want to contribute to the Open Ticket Docs, please run the following command:
|
||||
|
||||
> npm run docs
|
||||
|
||||
This will give you a "result.json" file which can then be used in the OT Docs.
|
||||
Further instructions can be found there.
|
||||
/////////////////////////////////*/
|
||||
|
||||
if (!fs.existsSync(".docs/typedoc-result.json")){
|
||||
console.log("Unable to generate documentation! Run typedoc first!")
|
||||
process.exit(1)
|
||||
}
|
||||
const result = JSON.parse(fs.readFileSync(".docs/typedoc-result.json").toString())
|
||||
const availableElements = []
|
||||
const skipElementNames = [
|
||||
"#opendiscord-types"
|
||||
]
|
||||
|
||||
const handleFunction = (memberType) => {
|
||||
if (!memberType || !memberType.signatures || !Array.isArray(memberType.signatures)) return {type:"unknown"}
|
||||
|
||||
//try to get signature
|
||||
let signature = memberType.signatures.find((s) => !s.typeParameters)
|
||||
if (!signature) signature = memberType.signatures[0]
|
||||
if (!signature) return {type:"unknown"}
|
||||
|
||||
//try to get comment
|
||||
let comment = signature.comment
|
||||
if (!comment) comment = memberType.signatures.find((s) => s.comment)?.comment
|
||||
if (!comment) comment = null
|
||||
|
||||
const signatureInherited = signature.flags.isInherited ? true : false
|
||||
const signatureComment = comment ? comment.summary?.map((c) => c.text).join("") : null
|
||||
const signatureParameters = signature.parameters?.map((p) => {
|
||||
return {name:p.name,details:handleType(p.type)}
|
||||
}) ?? []
|
||||
const signatureReturns = handleType(signature.type)
|
||||
|
||||
return {type:"function",inherited:signatureInherited,comment:signatureComment,parameters:signatureParameters,returns:signatureReturns}
|
||||
}
|
||||
|
||||
const handleObject = (memberType) => {
|
||||
if (!memberType || !memberType.children || !Array.isArray(memberType.children)) return {type:"unknown"}
|
||||
const propertyIds = memberType.groups?.find((g) => g.title == "Properties")?.children ?? []
|
||||
const methodIds = memberType.groups?.find((g) => g.title == "Methods")?.children ?? []
|
||||
|
||||
const objectChildren = []
|
||||
for (const member of (memberType.children ?? [])){
|
||||
const memberName = member.name
|
||||
const memberType = propertyIds.includes(member.id) ? "property" : methodIds.includes(member.id) ? "method" : "other"
|
||||
let memberComment = member.comment?.summary?.map((c) => c.text).join("") ?? null
|
||||
|
||||
const rawMemberSource = member.sources ? (member.sources[0] ?? null) : null
|
||||
let memberSource = rawMemberSource ? rawMemberSource.fileName+":"+rawMemberSource.line+":"+rawMemberSource.character : null
|
||||
|
||||
let memberDetails = null
|
||||
if (memberType == "property"){
|
||||
//INTERFACE => PROPERTY
|
||||
memberDetails = handleType(member.type)
|
||||
}else if (memberType == "method"){
|
||||
//INTERFACE => METHOD
|
||||
memberDetails = handleFunction(member)
|
||||
memberComment = memberDetails.comment
|
||||
}
|
||||
|
||||
objectChildren.push({
|
||||
type:memberType,
|
||||
name:memberName,
|
||||
comment:memberComment,
|
||||
source:memberSource,
|
||||
details:memberDetails
|
||||
})
|
||||
}
|
||||
return {type:"object",children:objectChildren}
|
||||
}
|
||||
|
||||
const handleType = (memberType) => {
|
||||
if (!memberType || !memberType.type) return {type:"unknown"}
|
||||
else if (memberType.type == "intrinsic") return {type:"primitive",name:memberType.name}
|
||||
else if (memberType.type == "array") return {type:"array",child:handleType(memberType.elementType)}
|
||||
else if (memberType.type == "union") return {type:"union",children:memberType.types.map((t) => handleType(t))}
|
||||
else if (memberType.type == "intersection") return {type:"intersection",children:memberType.types.map((t) => handleType(t))}
|
||||
else if (memberType.type == "reference"){
|
||||
const referenceTypeArguments = (memberType.typeArguments) ? memberType.typeArguments.map((ta) => handleType(ta)) : null
|
||||
let referenceType = availableElements.find((el) => el.name == memberType.name)?.type ?? null
|
||||
|
||||
if (referenceType){
|
||||
//member is known type (from Open Ticket)
|
||||
return {type:"reference",name:memberType.name,target:referenceType,typeArguments:referenceTypeArguments}
|
||||
}else if (memberType.package){
|
||||
//member is type from node.js or another package
|
||||
if (memberType.package == "typescript") return {type:"internal",name:memberType.name,typeArguments:referenceTypeArguments}
|
||||
if (memberType.package == "open-ticket" && memberType.refersToTypeParameter) return {type:"typeParam",name:memberType.name,typeArguments:referenceTypeArguments}
|
||||
else return {type:"external",package:memberType.package,name:memberType.name,typeArguments:referenceTypeArguments}
|
||||
}else return {type:"unknown"}
|
||||
}
|
||||
else if (memberType.type == "literal") return {type:"literal",name:(typeof memberType.value == "string") ? JSON.stringify(memberType.value) : String(memberType.value)}
|
||||
else if (memberType.type == "typeOperator"){
|
||||
if (memberType.operator == "keyof") return {type:"keyof",child:handleType(memberType.target)}
|
||||
else if (memberType.operator == "readonly") return {type:"readonly",child:handleType(memberType.target)}
|
||||
else if (memberType.operator == "unique") return {type:"unique",child:handleType(memberType.target)}
|
||||
}
|
||||
else if (memberType.type == "conditional") return {type:"conditional",checker:handleType(memberType.checkType),extends:handleType(memberType.extendsType),trueValue:handleType(memberType.trueType),falseValue:handleType(memberType.falseType)}
|
||||
else if (memberType.type == "indexedAccess") return {type:"index",index:handleType(memberType.indexType),object:handleType(memberType.objectType)}
|
||||
else if (memberType.type == "mapped") return {type:"mapped",parameterName:memberType.parameter,parameter:handleType(memberType.parameterType),template:handleType(memberType.templateType)}
|
||||
else if (memberType.type == "optional") return {type:"optional",child:handleType(memberType.elementType)}
|
||||
else if (memberType.type == "predicate") return {type:"predicate",name:memberType.name,target:handleType(memberType.targetType)}
|
||||
else if (memberType.type == "query") return {type:"query",target:handleType(memberType.queryType)}
|
||||
else if (memberType.type == "rest") return {type:"rest",child:handleType(memberType.elementType)}
|
||||
else if (memberType.type == "tuple") return {type:"tuple",children:memberType.elements.map((t) => handleType(t))}
|
||||
else if (memberType.type == "templateLiteral") return {type:"template",head:memberType.head,tails:memberType.tail.map((t) => {
|
||||
return {element:handleType(t[0]),text:t[1]}
|
||||
})}
|
||||
else if (memberType.type == "reflection" && memberType.declaration && memberType.declaration.signatures && memberType.declaration.signatures[0]){
|
||||
return handleFunction(memberType.declaration)
|
||||
}
|
||||
else if (memberType.type == "reflection"){
|
||||
return handleObject(memberType.declaration)
|
||||
}
|
||||
else return {type:"unknown"}
|
||||
}
|
||||
|
||||
for (const file of result.children){
|
||||
const classIds = file.groups?.find((g) => g.title == "Classes")?.children ?? []
|
||||
const interfaceIds = file.groups?.find((g) => g.title == "Interfaces")?.children ?? []
|
||||
const typeIds = file.groups?.find((g) => g.title == "Type Aliases")?.children ?? []
|
||||
const enumIds = file.groups?.find((g) => g.title == "Enumerations")?.children ?? []
|
||||
const varIds = file.groups?.find((g) => g.title == "Variables")?.children ?? []
|
||||
const funcIds = file.groups?.find((g) => g.title == "Functions")?.children ?? []
|
||||
|
||||
for (const declaration of file.children){
|
||||
const declarationName = declaration.name
|
||||
const declarationType = classIds.includes(declaration.id) ? "class" : interfaceIds.includes(declaration.id) ? "interface" : typeIds.includes(declaration.id) ? "type" : enumIds.includes(declaration.id) ? "enum" : varIds.includes(declaration.id) ? "variable" : funcIds.includes(declaration.id) ? "function" : "other"
|
||||
availableElements.push({name:declarationName,type:declarationType})
|
||||
}
|
||||
}
|
||||
|
||||
const exported = []
|
||||
for (const file of result.children){
|
||||
const classIds = file.groups?.find((g) => g.title == "Classes")?.children ?? []
|
||||
const interfaceIds = file.groups?.find((g) => g.title == "Interfaces")?.children ?? []
|
||||
const typeIds = file.groups?.find((g) => g.title == "Type Aliases")?.children ?? []
|
||||
const enumIds = file.groups?.find((g) => g.title == "Enumerations")?.children ?? []
|
||||
const varIds = file.groups?.find((g) => g.title == "Variables")?.children ?? []
|
||||
const funcIds = file.groups?.find((g) => g.title == "Functions")?.children ?? []
|
||||
|
||||
for (const declaration of (file.children ?? [])){
|
||||
const declarationName = declaration.name
|
||||
if (skipElementNames.includes(declarationName)) continue
|
||||
|
||||
const declarationType = classIds.includes(declaration.id) ? "class" : interfaceIds.includes(declaration.id) ? "interface" : typeIds.includes(declaration.id) ? "type" : enumIds.includes(declaration.id) ? "enum" : varIds.includes(declaration.id) ? "variable" : funcIds.includes(declaration.id) ? "function" : "other"
|
||||
const declarationTypeParams = (declaration.typeParameters) ? declaration.typeParameters.map((tp) => {
|
||||
return {name:tp.name,type:handleType(tp.type)}
|
||||
}) : null
|
||||
const declarationComment = declaration.comment?.summary?.map((c) => c.text).join("") ?? null
|
||||
const declarationConstant = declaration.flags.isConst ? true : false
|
||||
|
||||
const rawDeclarationSource = declaration.sources ? (declaration.sources[0] ?? null) : null
|
||||
const declarationSource = rawDeclarationSource ? rawDeclarationSource.fileName+":"+rawDeclarationSource.line+":"+rawDeclarationSource.character : null
|
||||
|
||||
const declarationChildren = []
|
||||
if (declarationType == "class"){
|
||||
//CLASS
|
||||
const constructorIds = declaration.groups?.find((g) => g.title == "Constructors")?.children ?? []
|
||||
const propertyIds = declaration.groups?.find((g) => g.title == "Properties")?.children ?? []
|
||||
const methodIds = declaration.groups?.find((g) => g.title == "Methods")?.children ?? []
|
||||
|
||||
for (const member of (declaration.children ?? [])){
|
||||
const memberName = member.name
|
||||
const memberType = constructorIds.includes(member.id) ? "constructor" : propertyIds.includes(member.id) ? "property" : methodIds.includes(member.id) ? "method" : "other"
|
||||
let memberComment = member.comment?.summary?.map((c) => c.text).join("") ?? null
|
||||
|
||||
const rawMemberSource = member.sources ? (member.sources[0] ?? null) : null
|
||||
let memberSource = rawMemberSource ? rawMemberSource.fileName+":"+rawMemberSource.line+":"+rawMemberSource.character : null
|
||||
|
||||
const memberInherited = member.flags.isInherited ? true : false
|
||||
const memberStatic = member.flags.isStatic ? true : false
|
||||
const memberProtected = member.flags.isProtected ? true : false
|
||||
const memberOptional = member.flags.isOptional ? true : false
|
||||
const memberReadonly = member.flags.isReadonly ? true : false
|
||||
|
||||
let memberDetails = null
|
||||
if (memberType == "property"){
|
||||
//CLASS => PROPERTY
|
||||
memberDetails = handleType(member.type)
|
||||
|
||||
}else if (memberType == "method"){
|
||||
//CLASS => METHOD
|
||||
memberDetails = handleFunction(member)
|
||||
memberComment = memberDetails.comment
|
||||
|
||||
}else if (memberType == "constructor"){
|
||||
//CLASS => CONSTRUCTOR
|
||||
memberDetails = handleFunction(member)
|
||||
memberComment = memberDetails.comment
|
||||
}
|
||||
|
||||
declarationChildren.push({
|
||||
type:memberType,
|
||||
name:memberName,
|
||||
comment:memberComment,
|
||||
source:memberSource,
|
||||
details:memberDetails,
|
||||
inherited:memberInherited,
|
||||
static:memberStatic,
|
||||
protected:memberProtected,
|
||||
optional:memberOptional,
|
||||
readonly:memberReadonly
|
||||
})
|
||||
}
|
||||
}else if (declarationType == "interface"){
|
||||
//INTERFACE
|
||||
const propertyIds = declaration.groups?.find((g) => g.title == "Properties")?.children ?? []
|
||||
const methodIds = declaration.groups?.find((g) => g.title == "Methods")?.children ?? []
|
||||
|
||||
for (const member of (declaration.children ?? [])){
|
||||
const memberName = member.name
|
||||
const memberType = propertyIds.includes(member.id) ? "property" : methodIds.includes(member.id) ? "method" : "other"
|
||||
let memberComment = member.comment?.summary?.map((c) => c.text).join("") ?? null
|
||||
|
||||
const rawMemberSource = member.sources ? (member.sources[0] ?? null) : null
|
||||
let memberSource = rawMemberSource ? rawMemberSource.fileName+":"+rawMemberSource.line+":"+rawMemberSource.character : null
|
||||
|
||||
const memberInherited = member.flags.isInherited ? true : false
|
||||
const memberStatic = member.flags.isStatic ? true : false
|
||||
const memberProtected = member.flags.isProtected ? true : false
|
||||
const memberOptional = member.flags.isOptional ? true : false
|
||||
const memberReadonly = member.flags.isReadonly ? true : false
|
||||
|
||||
let memberDetails = null
|
||||
if (memberType == "property"){
|
||||
//INTERFACE => PROPERTY
|
||||
memberDetails = handleType(member.type)
|
||||
}else if (memberType == "method"){
|
||||
//INTERFACE => METHOD
|
||||
memberDetails = handleFunction(member)
|
||||
memberComment = memberDetails.comment
|
||||
}
|
||||
|
||||
declarationChildren.push({
|
||||
type:memberType,
|
||||
name:memberName,
|
||||
comment:memberComment,
|
||||
source:memberSource,
|
||||
details:memberDetails,
|
||||
inherited:memberInherited,
|
||||
static:memberStatic,
|
||||
protected:memberProtected,
|
||||
optional:memberOptional,
|
||||
readonly:memberReadonly
|
||||
})
|
||||
}
|
||||
}else if (declarationType == "type"){
|
||||
//TYPE
|
||||
declarationChildren.push(handleType(declaration.type))
|
||||
}else if (declarationType == "enum"){
|
||||
//ENUM
|
||||
const enumerableIds = declaration.groups?.find((g) => g.title == "Enumeration Members")?.children ?? []
|
||||
|
||||
for (const member of (declaration.children ?? [])){
|
||||
const memberName = member.name
|
||||
const memberType = enumerableIds.includes(member.id) ? "enumerable" : "other"
|
||||
const memberComment = member.comment?.summary?.map((c) => c.text).join("") ?? null
|
||||
|
||||
const rawMemberSource = member.sources ? (member.sources[0] ?? null) : null
|
||||
let memberSource = rawMemberSource ? rawMemberSource.fileName+":"+rawMemberSource.line+":"+rawMemberSource.character : null
|
||||
|
||||
let memberDetails = null
|
||||
if (memberType == "enumerable"){
|
||||
//ENUM => ENUMERABLE
|
||||
memberDetails = handleType(member.type)
|
||||
}
|
||||
|
||||
const memberInherited = member.flags.isInherited ? true : false
|
||||
const memberStatic = member.flags.isStatic ? true : false
|
||||
const memberProtected = member.flags.isProtected ? true : false
|
||||
const memberOptional = member.flags.isOptional ? true : false
|
||||
const memberReadonly = member.flags.isReadonly ? true : false
|
||||
|
||||
declarationChildren.push({
|
||||
type:memberType,
|
||||
name:memberName,
|
||||
comment:memberComment,
|
||||
source:memberSource,
|
||||
details:memberDetails,
|
||||
inherited:memberInherited,
|
||||
static:memberStatic,
|
||||
protected:memberProtected,
|
||||
optional:memberOptional,
|
||||
readonly:memberReadonly
|
||||
})
|
||||
}
|
||||
}else if (declarationType == "variable"){
|
||||
//VARIABLE
|
||||
declarationChildren.push(handleType(declaration.type))
|
||||
|
||||
}else if (declarationType == "function"){
|
||||
//FUNCTION
|
||||
declarationChildren.push(handleFunction(declaration))
|
||||
|
||||
}
|
||||
|
||||
exported.push({
|
||||
type:declarationType,
|
||||
name:declarationName,
|
||||
comment:declarationComment,
|
||||
constant:declarationConstant,
|
||||
source:declarationSource,
|
||||
children:declarationChildren,
|
||||
typeParams:declarationTypeParams
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(".docs/result.json",JSON.stringify(exported,null,"\t"))
|
||||
fs.rmSync(".docs/typedoc-result.json")
|
||||
@@ -0,0 +1,131 @@
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import crypto from "crypto"
|
||||
|
||||
const sponsorData: {sponsors:Sponsor[],sections:Section[]} = JSON.parse(fs.readFileSync(path.join(process.cwd(),"./.github/SPONSORS.json")).toString())
|
||||
|
||||
//CONSTANTS
|
||||
const CORNER_RADIUS = 10
|
||||
const SPACE_MULTIPLIER = 1.2
|
||||
const SVG_WIDTH = 1000
|
||||
|
||||
//TYPES
|
||||
interface Sponsor {
|
||||
name:string,
|
||||
pictureUrl:string,
|
||||
profileUrl:string,
|
||||
sectionId:string
|
||||
}
|
||||
interface Section {
|
||||
name:string,
|
||||
id:string,
|
||||
pfpSize:number,
|
||||
pfpColumns:number,
|
||||
withNames:boolean
|
||||
}
|
||||
|
||||
|
||||
//FUNCTIONS
|
||||
async function downloadPfpToBase64URL(url:string){
|
||||
const res = await fetch(url,{method:"GET"})
|
||||
if (!res.ok) return null
|
||||
const buffer = Buffer.from(await res.arrayBuffer())
|
||||
console.log("Downloaded picture URL:",url)
|
||||
return "data:image/png;base64,"+buffer.toString("base64")
|
||||
}
|
||||
|
||||
function createTitle(yPos:number,name:string){
|
||||
return `<text x="${Math.round(SVG_WIDTH/2)}" y="${yPos+20}" text-anchor="middle" class="sponsor-tier-title">${name}</text>`
|
||||
}
|
||||
|
||||
async function createPfp(yPos:number,xPos:number,size:number,sponsor:Sponsor,withNames:boolean){
|
||||
const randomId = crypto.randomBytes(8).toString("hex")
|
||||
const nameElement = (withNames) ? `<text x="${Math.round(xPos+(size/2))}" y="${yPos+size+20}" text-anchor="middle" fill="currentColor">${sponsor.name}</text>` : ""
|
||||
|
||||
return (`<a href="${sponsor.profileUrl}" class="sponsor-link" target="_blank">
|
||||
<clipPath id="clipPath-${randomId}">
|
||||
<rect x="${xPos}" y="${yPos}" width="${size}" height="${size}" rx="${CORNER_RADIUS}" ry="${CORNER_RADIUS}"/>
|
||||
</clipPath>
|
||||
<image x="${xPos}" y="${yPos}" width="${size}" height="${size}" href="${await downloadPfpToBase64URL(sponsor.pictureUrl)}" clip-path="url(#clipPath-${randomId})"/>
|
||||
${nameElement}
|
||||
</a>`)
|
||||
}
|
||||
|
||||
async function generateSection(yPos:number,section:Section,sponsors:Sponsor[]){
|
||||
let sectionHtml: string = ""
|
||||
sectionHtml += createTitle(yPos,section.name)
|
||||
const nameOffset = (section.withNames) ? 20 : 0
|
||||
|
||||
//divide sponsors in rows
|
||||
const groupedSponsors: Sponsor[][] = []
|
||||
let currentGroup: Sponsor[] = []
|
||||
for (const sponsor of sponsors){
|
||||
currentGroup.push(sponsor)
|
||||
if (currentGroup.length == section.pfpColumns){
|
||||
groupedSponsors.push(currentGroup)
|
||||
currentGroup = []
|
||||
}
|
||||
}
|
||||
if (currentGroup.length > 0) groupedSponsors.push(currentGroup)
|
||||
|
||||
let y = 0
|
||||
for (const sponsorGroup of groupedSponsors){
|
||||
const xOffset = Math.round((SVG_WIDTH - (sponsorGroup.length * section.pfpSize * SPACE_MULTIPLIER))/2)
|
||||
let x = 0
|
||||
for (const sponsor of sponsorGroup){
|
||||
const pfpYPos = 40 + yPos + (y * ((section.pfpSize * SPACE_MULTIPLIER) + nameOffset))
|
||||
const pfpXPos = xOffset + (x * section.pfpSize * SPACE_MULTIPLIER)
|
||||
sectionHtml += await createPfp(pfpYPos,pfpXPos,section.pfpSize,sponsor,section.withNames)
|
||||
x++
|
||||
}
|
||||
y++
|
||||
}
|
||||
|
||||
let sectionHeight: number = 40 + (y * ((section.pfpSize * SPACE_MULTIPLIER) + nameOffset))
|
||||
return {sectionHtml,sectionHeight}
|
||||
}
|
||||
|
||||
async function generateSections(sections:Section[],sponsors:Sponsor[]){
|
||||
let finalHeight: number = 10
|
||||
let finalHtml: string = ""
|
||||
for (const section of sections){
|
||||
const sectionSponsors = sponsors.filter((s) => s.sectionId === section.id)
|
||||
if (sectionSponsors.length < 1) continue
|
||||
const {sectionHtml,sectionHeight} = await generateSection(finalHeight,section,sectionSponsors)
|
||||
|
||||
finalHeight += sectionHeight
|
||||
finalHtml += sectionHtml
|
||||
}
|
||||
|
||||
finalHeight += 10
|
||||
return {finalHeight,finalHtml}
|
||||
}
|
||||
|
||||
function generateFinalHtml(sectionsHtml:string,sectionHeight:number){
|
||||
return (`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${SVG_WIDTH} ${sectionHeight}" width="${SVG_WIDTH}" height="${sectionHeight}">
|
||||
<style>
|
||||
text {
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
fill: #777777;
|
||||
font-family: 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
.sponsor-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sponsor-tier-title {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
}
|
||||
</style>
|
||||
<rect x="2" y="2" width="${SVG_WIDTH-4}" height="${sectionHeight-4}" rx="20" ry="20" style="fill:transparent;stroke:#f8ba00;stroke-width:3"></rect>
|
||||
${sectionsHtml}
|
||||
</svg>`)
|
||||
}
|
||||
|
||||
//GENERATE SPONSORS SVG
|
||||
async function main(){
|
||||
const {finalHeight,finalHtml} = await generateSections(sponsorData.sections,sponsorData.sponsors)
|
||||
fs.writeFileSync(path.join(process.cwd(),"./.github/SPONSORS.svg"),generateFinalHtml(finalHtml,finalHeight))
|
||||
}
|
||||
main()
|
||||
@@ -1,6 +1,7 @@
|
||||
//@ts-check
|
||||
const fjs = require("formatted-json-stringify")
|
||||
const fs = require("fs")
|
||||
|
||||
import fjs from "formatted-json-stringify"
|
||||
import fs from "fs"
|
||||
|
||||
const formatter = new fjs.ObjectFormatter(null,true,[
|
||||
new fjs.ObjectFormatter("_TRANSLATION",true,[
|
||||
new fjs.PropertyFormatter("otversion"),
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"$schema": "https://typedoc.org/schema.json",
|
||||
"entryPoints": [
|
||||
"../src/core/api/modules/",
|
||||
"../src/core/api/defaults/",
|
||||
"../src/core/api/openticket/",
|
||||
"../src/core/api/main.ts",
|
||||
"../src/core/startup/init.ts",
|
||||
],
|
||||
"entryPointStrategy": "expand",
|
||||
"json": "./typedoc-result.json",
|
||||
"basePath": "../"
|
||||
}
|
||||
@@ -8,104 +8,110 @@
|
||||
<a href="https://otdocs.dj-dj.be"><img alt="Open Ticket Documentation" src="https://img.shields.io/badge/discord.js-v14-CB3837.svg?style=flat-square&logo=npm"></img></a>
|
||||
<a href="https://github.com/open-discord-bots/open-ticket/blob/main/LICENSE"><img alt="Open Ticket License" src="https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square"></img></a>
|
||||
<a href="https://otdocs.dj-dj.be"><img alt="Open Ticket Stars" src="https://img.shields.io/github/stars/djj123dj/open-ticket?color=yellow&label=stars&logo=github&style=flat-square"></img></a>
|
||||
<br>
|
||||
<a href="https://github.com/sponsors/DJj123dj"><img alt="Sponsor DJj123dj" src="https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors"></img></a>
|
||||
<a href="https://hub.docker.com/repository/docker/djj123dj/open-ticket"><img alt="Open Ticket supports Docker!" src="https://img.shields.io/badge/docker-supported-2496ED?style=flat-square&logo=docker"></img></a>
|
||||
<a href=".eggs/README.md"><img alt="Open Ticket supports Pterodactyl Eggs!" src="https://img.shields.io/badge/pterodactyl-supported-10539F?style=flat-square&logo=pterodactyl"></img></a>
|
||||
<a href=".github/pterodactyl-eggs/README.md"><img alt="Open Ticket supports Pterodactyl Eggs!" src="https://img.shields.io/badge/pterodactyl-supported-10539F?style=flat-square&logo=pterodactyl"></img></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Open Ticket is the most <b>advanced and customizable</b> Discord ticket bot available. With <b>350+ configurable settings</b>, you have full control over every aspect of your ticket system!
|
||||
From <code>HTML transcripts</code> and <code>Advanced Plugins</code> to <code>Claiming & Pinning</code>, <code>Questions & Modals</code>, <code>Detailed Statistics</code>, and much more.<br><br>
|
||||
The bot is fully translated into <b>37+ languages</b> and has been battle-tested in large Discord servers.<br>
|
||||
Open Ticket is the most <b>advanced and customizable</b> Discord ticket bot available right now. With <b>350+ configurable settings</b>, you have full control over almost every aspect of your ticket system!
|
||||
From <code>HTML transcripts</code> and <code>Advanced Plugins</code> to <code>Claiming & Pinning</code>, <code>Modal Questions & Limits</code>, <code>Detailed Statistics</code>, and much more.<br><br>
|
||||
The bot is fully translated into <b>38+ languages</b> and has been battle-tested in large Discord servers.<br>
|
||||
Need help or want to get involved? Feel free to join our <a href="https://discord.dj-dj.be"><b>Discord server</b></a>.
|
||||
</p>
|
||||
|
||||
<h3 align="center"><b>⭐️ Support Open Ticket’s growth by starring this repo! ⭐️</b></h3>
|
||||
<p align="center"><sup>❤️ Love Open Ticket? <a href="https://github.com/sponsors/DJj123dj">Sponsorships</a> help fuel our HTML transcript servers and future features! ❤️</sup></p>
|
||||
<p align="center"><sup>❤️ Love Open Ticket? <a href="https://github.com/sponsors/DJj123dj">Sponsorships</a> help fuel our HTML transcript servers and future features! ❤️</sup><br>
|
||||
<img align="center" src=".github/SPONSORS.svg" alt="Open Ticket" width="800px">
|
||||
</p>
|
||||
|
||||
---
|
||||
> **[-> Navigate to (⏱️ Quick Setup)](#️-quick-start-using-interactive-cli-tool)**
|
||||
> **[-> Navigate to (⏱️ Quick Setup)](#️-quick-start)**
|
||||
> **[-> Navigate to (📚 Documentation)](https://otdocs.dj-dj.be)**
|
||||
> **[-> Navigate to (📞 Support Server)](https://discord.dj-dj.be)**
|
||||
> **[-> Navigate to (🧩 Plugins/Addons)](https://odplugins.dj-dj.be)**
|
||||
> **[-> Navigate to (🦇 Pterodactyl Eggs)](.github/pterodactyl-eggs/README.md)**
|
||||
|
||||
### 📌 Features
|
||||
- **⏳ Quick Setup** - Using the interactive Quick Setup CLI, you can **configure Open Ticket in less than 5min!**
|
||||
- **🦇 Pterodactyl Support** - Open Ticket works perfect on Pterodactyl based panels. [(Download official eggs)](.eggs/README.md)
|
||||
- **💩 No Credits** - Your bot won't contain any form of bloat or credits. It's all yours!
|
||||
- **🔒 Private & Secure** - It has been battletested by thousands of servers and **respects security & privacy.**
|
||||
- **📈 Scalable** - Made to handle huge servers and has already been **tested in servers with 100k members.**
|
||||
- **📄 HTML Transcripts** - The **built-in HTML Transcripts Service** provides beautiful & easy-to-use transcripts.
|
||||
- **✅ Ticket Status** - Close, reopen, delete, claim, pin, rename or move tickets in your server.
|
||||
- **🇬🇧 Translation** - Every message has been translated in more than **37 languages** by our community.
|
||||
- **🎨 Customisation** - More than **200+ settings** are related to customisation & advanced features.
|
||||
- **🖥️ Interactions** - The bot has full support for buttons, dropdowns, slash/text commands & modals.
|
||||
- **∞ Unlimited Possibilities** - Create an infinite amount of tickets, questions & panels.
|
||||
- **📝 Advanced Plugins** - Create advanced plugins or use [**pre-made plugins**](#-plugins) by our community.
|
||||
- **👥 Participants** - Add or remove participants & transfer ownership from one user to another.
|
||||
- **📊 Detailed Statistics** - With more than **50+ statistics** for tickets, users & the server.
|
||||
- **🚫 Blacklist** - Blacklist users to prevent them from creating new tickets.
|
||||
- **🚨 Priorities** - Assign different **priority levels** to tickets to mark them as important.
|
||||
- **❓ Modal Questions** - Give users the ability to **answer questions** in a modal before their ticket is created.
|
||||
- **✨ Commands** - Manage all your tickets with more than 28+ commands.
|
||||
- **🤖 Automation** - Automate ticket handling with **autoclose, autodelete** & slow mode.
|
||||
- **😎 Additional Features** - For some weird reason, the bot also supports Reaction Role & URL Buttons.
|
||||
#### Core Features
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-management.svg"></img> **Ticket Management** - Close, reopen, delete, claim or pin tickets with ease.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-commands.svg"></img> **Powerful Commands** - Manage your support system with **30+ commands** for staff & users.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-questions.svg"></img> **Modal Questions** - Ask users **custom questions** before a ticket is created.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-priorities.svg"></img> **Priorities** - Assign **priority levels** to tickets to highlight urgent requests.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-participants.svg"></img> **Participants** - Add or remove participants & transfer ownership from one user to another.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-adjustments.svg"></img> **Adjustments** - Rename tickets, change ticket types or transfer ownership.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-blacklist.svg"></img> **Blacklist & Limits** - Prevent users from creating tickets and set per-user or global ticket limits.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-customisable.svg"></img> **Highly Customisable** - Configure **350+ settings** covering appearance and behaviour.
|
||||
|
||||
#### And even more using [pre-made community plugins](#-plugins)!
|
||||
- **💬 Reviews** - Create & manage a support review system.
|
||||
- **📢 Feedback** - Collect feedback & create forms for users to answer.
|
||||
- **⏰ Reminders** - Create & manage customisable reminders.
|
||||
- **🏷️ Tags** - Create tags & answer questions automatically using keywords.
|
||||
- **📝 Forms** - Create advanced forms and automatically ask for repetitive questions.
|
||||
- **🔄 Channel Display** - Create a voice channel with realtime statistics from the ticket system.
|
||||
- **💾 SQLite Database** - Use an `SQLite` database for increased performance.
|
||||
- **🎉 Custom Embeds** - Create your own embeds and send them using a command.
|
||||
- **🎨 Customisation** - Yep, you heard it right. Even more customisation!
|
||||
- **😁 And so much more...**
|
||||
#### Ticket Automation & Workflows
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-unlimited.svg"></img> **Unlimited Possibilities** - Create unlimited tickets, panels & question flows.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-autoclose.svg"></img> **Autoclose Tickets** - Automatically **close tickets** after predefined conditions.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-autodelete.svg"></img> **Autodelete Tickets** - Automatically **delete closed tickets** to keep channels clean.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-categories.svg"></img> **Category Routing** - Move tickets between categories based on claim or close state.
|
||||
|
||||
### ⏱️ Quick Start (Using Interactive CLI Tool)
|
||||
> 1. Download the latest version of Open Ticket on [Github](https://github.com/open-discord-bots/open-ticket).
|
||||
> 2. Make sure Node.js & Npm are installed using `node -v` (minimum `v20`).
|
||||
#### Transcripts & Insights
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/insights-transcripts.svg"></img> **HTML Transcripts** - Generate beautiful, easy-to-read **HTML transcripts** for every ticket.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/insights-stats.svg"></img> **Detailed Statistics** - Track **50+ statistics** for tickets, users and server activity.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/insights-logs.svg"></img> **Ticket Logs** - Track **all ticket events** such as creation, closures, and staff actions.
|
||||
|
||||
#### User Experience
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-translated.svg"></img> **Fully Translated** - Available in **38+ languages**, translated and maintained by the community.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-interactions.svg"></img> **Modern Interactions** - Full support for buttons, dropdowns, slash/text commands & modals.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-panels.svg"></img> **Panels** - Create messages with buttons or a dropdown for users to open tickets.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-sub-panels.svg"></img> **Sub-Panels** - One panel not enough? Use multiple panels to offer more choices.
|
||||
|
||||
#### Plugins & Ecosystem
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-plugins.svg"></img> **Plugin System** - Use custom plugins to **add new features** or **modify existing behavior** of the bot.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-community.svg"></img> **Community Plugins** - Use and share plugins built by the community.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-api.svg"></img> **Advanced API** - Build advanced plugins with access to ticket events and internal systems.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-integrations.svg"></img> **Integrations** - Connect Open Ticket with external services to automate workflows across platforms.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-bonus.svg"></img> **Bonus Features** - Somehow, we included Reaction Roles and URL Button support as well.
|
||||
|
||||
#### Deployment
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-quick.svg"></img> **Quick Setup** - Easy **5-minute configuration** using the Interactive Setup CLI.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-scale.svg"></img> **Scalable & Reliable** - Battle-tested in servers with **100k+ members**.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-secure.svg"></img> **Private & Secure** - Used by thousands of servers with respect for security & privacy.
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-pterodactyl.svg"></img> **Pterodactyl Support** - 100% compatible with Pterodactyl panels. [(Download official eggs)](.eggs/README.md)
|
||||
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-docker.svg"></img> **Docker Support** - Deploy Open Ticket in minutes with Docker containers.
|
||||
|
||||
#### Extend functionality even more with our [pre-made community plugins](#-plugins)!
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-reviews.svg"></img> **Reviews** - Create and manage a support review system for tickets.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-tags.svg"></img> **Tags** - Define keywords that automatically trigger predefined responses.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-reminders.svg"></img> **Reminders** - Create and manage custom reminders for users or staff.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-ai.svg"></img> **AI Integrations** - Connect to AI providers such as ChatGPT, Claude, or Gemini.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-display.svg"></img> **Channel Display** - Create voice channels that display real-time ticket system statistics.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-forms.svg"></img> **Forms** - Build advanced forms for collecting structured information from users.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-embeds.svg"></img> **Custom Embeds** - Create and send custom embeds via commands.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-customisable.svg"></img> **Customization Tools** - Additional configuration options for advanced behavior and styling.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-dashboard.svg"></img> **Web Dashboard** - Configure and manage the bot through a remote web dashboard.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-feedback.svg"></img> **Feedback** - Collect user feedback after ticket deletion using forms.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-sqlite.svg"></img> **SQLite Database** - Use an SQLite backend for improved performance and lightweight storage.
|
||||
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-more.svg"></img> **And more** - Additional community plugins are available and actively expanding.
|
||||
|
||||
## ⏱️ Quick Start
|
||||
> 1. Download the [latest version of Open Ticket](https://github.com/open-discord-bots/open-ticket/releases/latest).
|
||||
> 2. Make sure you have installed Node.js on your system (check using `node -v`, minimum `v20`).
|
||||
> 3. Install any required dependencies using `npm install`.
|
||||
> 4. Start the **Quick Setup CLI** using `npm run setup`.
|
||||
> 5. Click on `> ⏱️ Quick Setup` and follow the instructions.
|
||||
> 4. Configure the bot in one of the following ways:
|
||||
> - Method 1 (Easy): Start the **Quick Setup CLI** using `npm run setup`.
|
||||
> - Method 2 (Hard): Manual **JSON configuration** in `./config/...`
|
||||
> 5. If using the **Quick Setup CLI**, click on `> ⏱️ Quick Setup` and follow the instructions.
|
||||
> 6. Start the bot using `npm start` or `node index.js`
|
||||
> - If required, the bot will give a report of errors that must be solved.
|
||||
> - If any config errors occur, the bot will give you a report of how to solve them.
|
||||
> - Follow the instructions and restart the bot.
|
||||
> 7. Enjoy using Open Ticket!
|
||||
> 8. Install plugins from the [**Official Plugin Repository**](https://github.com/open-discord-bots/plugins)
|
||||
>
|
||||
> #### 🚦 Navigation
|
||||
> #### 🚦 Next Steps
|
||||
> **[-> Navigate to (📚 Documentation)](https://otdocs.dj-dj.be)**
|
||||
> **[-> Navigate to (📞 Support Server)](https://discord.dj-dj.be)**
|
||||
> **[-> Navigate to (🧩 Download Plugins)](https://odplugins.dj-dj.be)**
|
||||
> **[-> Navigate to (🧩 Plugins/Addons)](https://odplugins.dj-dj.be)**
|
||||
> **[-> Navigate to (🦇 Pterodactyl Eggs)](.github/pterodactyl-eggs/README.md)**
|
||||
>
|
||||
> #### 🖥️ Recommended Hosting
|
||||
> - **A VPS (Virtual Private Server)** - Extra customisation & more stability. Recommended for most servers.
|
||||
> - **Any Pterodactyl-Based Panel** - Easy installation & configuration.
|
||||
|
||||
### ❤️ Sponsors
|
||||
Huge thanks to our sponsors for making this project possible. Your support means everything to us.
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://github.com/guillee3.png" alt="Profile Picture" width="100px"></td>
|
||||
<td><img src="https://github.com/yeeetSK.png" alt="Profile Picture" width="100px"></td>
|
||||
<td><img src="https://github.com/jacobhumston.png" alt="Profile Picture" width="100px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/guillee3"><b>guillee3</b></a></td>
|
||||
<td align="center"><a href="https://github.com/yeeetSK"><b>yeeetSK</b></a></td>
|
||||
<td align="center"><a href="https://github.com/jacobhumston"><b>jacobhumston</b></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**Past Sponsors:**<br>
|
||||
<a href="https://github.com/sponsors/DJj123dj">
|
||||
<img src="https://github.com/SpyEye2.png" alt="SpyEye" width="40px">
|
||||
<img src="https://github.com/mods-hd.png" alt="Mods HD" width="40px">
|
||||
<img src="https://github.com/DOSEV5.png" alt="DOSEV5" width="40px">
|
||||
<img src="https://github.com/BENZORICH.png" alt="BENZORICH" width="40px">
|
||||
</a>
|
||||
|
||||
## 📸 Preview
|
||||
<img alt="An example of a panel." src="https://apis.dj-dj.be/cdn/openticket/preview-v4/panel-examples.png">
|
||||
<img alt="An example of a ticket message." src="https://apis.dj-dj.be/cdn/openticket/preview-v4/ticket-example.png">
|
||||
@@ -134,14 +140,14 @@ A list of amazing people who have contributed or provided supported for **Open T
|
||||
</table>
|
||||
|
||||
### 💬 Translators
|
||||
With the amazing support of our translators, we've been able to translate Open Ticket in more than **37 languages**!
|
||||
With the amazing support of our translators, we've been able to translate Open Ticket in more than **38 languages**!
|
||||
#### Categories:
|
||||
- **🟢 Available**
|
||||
- **🤖 Partially Made Using AI**
|
||||
- **🟠 Incomplete**
|
||||
- **🔴 Unavailable/Outdated**
|
||||
|
||||
|🔍 |Languages (37) |Maintainer (Github/Discord) |
|
||||
|🔍 |Languages (38) |Maintainer (Github/Discord) |
|
||||
|----|---------------------|--------------------------------|
|
||||
|🟢 |🇬🇧 English |djj123dj |
|
||||
|🟢 |🇳🇱 Dutch |djj123dj |
|
||||
@@ -168,6 +174,7 @@ With the amazing support of our translators, we've been able to translate Open T
|
||||
|🟢 |🇧🇩 Bengali |HanumeshGupta |
|
||||
|🟢 |❓ Catalan |guillee3 |
|
||||
|🟢 |🇨🇳 Traditional Chinese|me.october |
|
||||
|🟢 |🇰🇭 Khmer (Cambodia) |yuuslokrobjakkroval |
|
||||
|🤖 |🇪🇪 Estonian |iamnotmega |
|
||||
|🤖 |🇫🇮 Finnish |iamnotmega |
|
||||
|🤖 |🇯🇵 Japanese |HanumeshGupta |
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@
|
||||
"test": "node index.js --dev-config --dev-database --soft-plugins",
|
||||
"testsetup": "node index.js --cli --dev-config --dev-database --soft-plugins",
|
||||
"testnc": "node index.js --no-compile --dev-config --dev-database --soft-plugins",
|
||||
"docs": "npx typedoc --options .tools/typedoc-config.json && node .tools/createDocs.js",
|
||||
"mergelang": "node .tools/mergeTranslations.js"
|
||||
"tools:mergelang": "bun run .tools/mergeTranslations.js",
|
||||
"tools:sponsors": "bun run .tools/createSponsors.ts"
|
||||
},
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
|
||||
Reference in New Issue
Block a user