Open Ticket v4 Beta
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
///////////////////////////////////////
|
||||
//OPENTICKET BLACKLIST MODULE
|
||||
///////////////////////////////////////
|
||||
import { ODManager, ODManagerData, ODValidId } from "../modules/base"
|
||||
import { ODDebugger } from "../modules/console"
|
||||
|
||||
export class ODBlacklist extends ODManagerData {
|
||||
#reason: string|null
|
||||
|
||||
constructor(id:ODValidId,reason:string|null){
|
||||
super(id)
|
||||
this.#reason = reason
|
||||
}
|
||||
|
||||
set reason(reason:string|null) {
|
||||
this.#reason = reason
|
||||
this._change()
|
||||
}
|
||||
get reason(){
|
||||
return this.#reason
|
||||
}
|
||||
}
|
||||
|
||||
export class ODBlacklistManager extends ODManager<ODBlacklist> {
|
||||
constructor(debug:ODDebugger){
|
||||
super(debug,"blacklist")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
///////////////////////////////////////
|
||||
//OPENTICKET OPTION MODULE
|
||||
///////////////////////////////////////
|
||||
import { ODDatabase } from "../modules/database"
|
||||
import { ODJsonConfig_DefaultOptionEmbedSettingsType, ODJsonConfig_DefaultOptionPingSettingsType } from "../defaults/config"
|
||||
import { ODId, ODManager, ODValidJsonType, ODValidId, ODVersion, ODValidButtonColor, ODManagerData, ODSystemError } from "../modules/base"
|
||||
import { ODDebugger } from "../modules/console"
|
||||
import * as discord from "discord.js"
|
||||
import * as crypto from "crypto"
|
||||
import { OTRoleUpdateMode } from "./role"
|
||||
|
||||
export class ODOptionManager extends ODManager<ODOption> {
|
||||
#debug: ODDebugger
|
||||
suffix: ODOptionSuffixManager
|
||||
|
||||
constructor(debug:ODDebugger){
|
||||
super(debug,"option")
|
||||
this.#debug = debug
|
||||
this.suffix = new ODOptionSuffixManager(debug)
|
||||
}
|
||||
|
||||
add(data:ODOption, overwrite?:boolean): boolean {
|
||||
data.useDebug(this.#debug,"option data")
|
||||
return super.add(data,overwrite)
|
||||
}
|
||||
}
|
||||
|
||||
export interface ODOptionDataJson {
|
||||
id:string,
|
||||
value:ODValidJsonType
|
||||
}
|
||||
|
||||
export interface ODOptionJson {
|
||||
id:string,
|
||||
type:string,
|
||||
version:string,
|
||||
data:ODOptionDataJson[]
|
||||
}
|
||||
|
||||
export class ODOption extends ODManager<ODOptionData<ODValidJsonType>> {
|
||||
id:ODId
|
||||
type: string
|
||||
|
||||
constructor(id:ODValidId, type:string, data:ODOptionData<ODValidJsonType>[]){
|
||||
super()
|
||||
this.id = new ODId(id)
|
||||
this.type = type
|
||||
data.forEach((data) => {
|
||||
this.add(data)
|
||||
})
|
||||
}
|
||||
|
||||
toJson(version:ODVersion): ODOptionJson {
|
||||
const data = this.getAll().map((data) => {
|
||||
return {
|
||||
id:data.id.toString(),
|
||||
value:data.value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id:this.id.toString(),
|
||||
type:this.type,
|
||||
version:version.toString(),
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
static fromJson(json:ODOptionJson): ODOption {
|
||||
return new ODOption(json.id,json.type,json.data.map((data) => new ODOptionData(data.id,data.value)))
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionData<DataType extends ODValidJsonType> extends ODManagerData {
|
||||
#value: DataType
|
||||
|
||||
constructor(id:ODValidId, value:DataType){
|
||||
super(id)
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
set value(value:DataType){
|
||||
this.#value = value
|
||||
this._change()
|
||||
}
|
||||
get value(): DataType {
|
||||
return this.#value
|
||||
}
|
||||
/**Refresh the database. Is only required to be used when updating `ODOptionData` with an object/array as value. */
|
||||
refreshDatabase(){
|
||||
this._change()
|
||||
}
|
||||
}
|
||||
|
||||
/**## ODTicketOptionIds `type`
|
||||
* This type is an array of ids available in the `ODTicketOption` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODTicketOptionIds {
|
||||
"openticket:name":ODOptionData<string>,
|
||||
"openticket:description":ODOptionData<string>,
|
||||
|
||||
"openticket:button-emoji":ODOptionData<string>,
|
||||
"openticket:button-label":ODOptionData<string>,
|
||||
"openticket:button-color":ODOptionData<ODValidButtonColor>,
|
||||
|
||||
"openticket:admins":ODOptionData<string[]>,
|
||||
"openticket:admins-readonly":ODOptionData<string[]>,
|
||||
"openticket:allow-blacklisted-users":ODOptionData<boolean>,
|
||||
"openticket:questions":ODOptionData<string[]>,
|
||||
|
||||
"openticket:channel-prefix":ODOptionData<string>,
|
||||
"openticket:channel-suffix":ODOptionData<"user-name"|"user-id"|"random-number"|"random-hex"|"counter-dynamic"|"counter-fixed">,
|
||||
"openticket:channel-category":ODOptionData<string>,
|
||||
"openticket:channel-category-closed":ODOptionData<string>,
|
||||
"openticket:channel-category-backup":ODOptionData<string>,
|
||||
"openticket:channel-categories-claimed":ODOptionData<{user:string,category:string}[]>,
|
||||
"openticket:channel-description":ODOptionData<string>,
|
||||
|
||||
"openticket:dm-message-enabled":ODOptionData<boolean>,
|
||||
"openticket:dm-message-text":ODOptionData<string>,
|
||||
"openticket:dm-message-embed":ODOptionData<ODJsonConfig_DefaultOptionEmbedSettingsType>,
|
||||
|
||||
"openticket:ticket-message-enabled":ODOptionData<boolean>,
|
||||
"openticket:ticket-message-text":ODOptionData<string>,
|
||||
"openticket:ticket-message-embed":ODOptionData<ODJsonConfig_DefaultOptionEmbedSettingsType>,
|
||||
"openticket:ticket-message-ping":ODOptionData<ODJsonConfig_DefaultOptionPingSettingsType>,
|
||||
|
||||
"openticket:autoclose-enable-hours":ODOptionData<boolean>,
|
||||
"openticket:autoclose-enable-leave":ODOptionData<boolean>,
|
||||
"openticket:autoclose-disable-claim":ODOptionData<boolean>,
|
||||
"openticket:autoclose-hours":ODOptionData<number>,
|
||||
|
||||
"openticket:autodelete-enable-days":ODOptionData<boolean>,
|
||||
"openticket:autodelete-enable-leave":ODOptionData<boolean>,
|
||||
"openticket:autodelete-disable-claim":ODOptionData<boolean>,
|
||||
"openticket:autodelete-days":ODOptionData<number>,
|
||||
|
||||
"openticket:cooldown-enabled":ODOptionData<boolean>,
|
||||
"openticket:cooldown-minutes":ODOptionData<number>,
|
||||
|
||||
"openticket:limits-enabled":ODOptionData<boolean>,
|
||||
"openticket:limits-maximum-global":ODOptionData<number>,
|
||||
"openticket:limits-maximum-user":ODOptionData<number>
|
||||
}
|
||||
|
||||
export class ODTicketOption extends ODOption {
|
||||
type: "openticket:ticket" = "openticket:ticket"
|
||||
|
||||
constructor(id:ODValidId, data:ODOptionData<ODValidJsonType>[]){
|
||||
super(id,"openticket:ticket",data)
|
||||
}
|
||||
|
||||
get<OptionId extends keyof ODTicketOptionIds>(id:OptionId): ODTicketOptionIds[OptionId]
|
||||
get(id:ODValidId): ODOptionData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODOptionData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<OptionId extends keyof ODTicketOptionIds>(id:OptionId): ODTicketOptionIds[OptionId]
|
||||
remove(id:ODValidId): ODOptionData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODOptionData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODTicketOptionIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
|
||||
static fromJson(json: ODOptionJson): ODTicketOption {
|
||||
return new ODTicketOption(json.id,json.data.map((data) => new ODOptionData(data.id,data.value)))
|
||||
}
|
||||
}
|
||||
|
||||
/**## ODWebsiteOptionIds `type`
|
||||
* This type is an array of ids available in the `ODWebsiteOption` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODWebsiteOptionIds {
|
||||
"openticket:name":ODOptionData<string>,
|
||||
"openticket:description":ODOptionData<string>,
|
||||
|
||||
"openticket:button-emoji":ODOptionData<string>,
|
||||
"openticket:button-label":ODOptionData<string>,
|
||||
|
||||
"openticket:url":ODOptionData<string>,
|
||||
}
|
||||
|
||||
export class ODWebsiteOption extends ODOption {
|
||||
type: "openticket:website" = "openticket:website"
|
||||
|
||||
constructor(id:ODValidId, data:ODOptionData<ODValidJsonType>[]){
|
||||
super(id,"openticket:website",data)
|
||||
}
|
||||
|
||||
get<OptionId extends keyof ODWebsiteOptionIds>(id:OptionId): ODWebsiteOptionIds[OptionId]
|
||||
get(id:ODValidId): ODOptionData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODOptionData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<OptionId extends keyof ODWebsiteOptionIds>(id:OptionId): ODWebsiteOptionIds[OptionId]
|
||||
remove(id:ODValidId): ODOptionData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODOptionData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODWebsiteOptionIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
|
||||
static fromJson(json: ODOptionJson): ODWebsiteOption {
|
||||
return new ODWebsiteOption(json.id,json.data.map((data) => new ODOptionData(data.id,data.value)))
|
||||
}
|
||||
}
|
||||
|
||||
/**## ODRoleOptionIds `type`
|
||||
* This type is an array of ids available in the `ODRoleOption` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODRoleOptionIds {
|
||||
"openticket:name":ODOptionData<string>,
|
||||
"openticket:description":ODOptionData<string>,
|
||||
|
||||
"openticket:button-emoji":ODOptionData<string>,
|
||||
"openticket:button-label":ODOptionData<string>,
|
||||
"openticket:button-color":ODOptionData<ODValidButtonColor>,
|
||||
|
||||
"openticket:roles":ODOptionData<string[]>,
|
||||
"openticket:mode":ODOptionData<OTRoleUpdateMode>,
|
||||
"openticket:remove-roles-on-add":ODOptionData<string[]>,
|
||||
"openticket:add-on-join":ODOptionData<boolean>
|
||||
}
|
||||
|
||||
export class ODRoleOption extends ODOption {
|
||||
type: "openticket:role" = "openticket:role"
|
||||
|
||||
constructor(id:ODValidId, data:ODOptionData<ODValidJsonType>[]){
|
||||
super(id,"openticket:role",data)
|
||||
}
|
||||
|
||||
get<OptionId extends keyof ODRoleOptionIds>(id:OptionId): ODRoleOptionIds[OptionId]
|
||||
get(id:ODValidId): ODOptionData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODOptionData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<OptionId extends keyof ODRoleOptionIds>(id:OptionId): ODRoleOptionIds[OptionId]
|
||||
remove(id:ODValidId): ODOptionData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODOptionData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODRoleOptionIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
|
||||
static fromJson(json:ODOptionJson): ODRoleOption {
|
||||
return new ODRoleOption(json.id,json.data.map((data) => new ODOptionData(data.id,data.value)))
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionSuffixManager extends ODManager<ODOptionSuffix> {
|
||||
/**Alias to open ticket global database. */
|
||||
database: ODDatabase|null = null
|
||||
|
||||
constructor(debug:ODDebugger){
|
||||
super(debug,"ticket suffix")
|
||||
}
|
||||
|
||||
useDatabase(database:ODDatabase){
|
||||
this.database = database
|
||||
}
|
||||
/**Instantly get the suffix from an option. */
|
||||
getSuffixFromOption(option:ODTicketOption,user:discord.User): string|null {
|
||||
const suffix = this.getAll().find((suffix) => suffix.option.id.value == option.id.value)
|
||||
if (!suffix) return null
|
||||
return suffix.getSuffix(user)
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionSuffix extends ODManagerData {
|
||||
/**The option of this suffix. */
|
||||
option: ODTicketOption
|
||||
|
||||
constructor(id:ODValidId, option:ODTicketOption){
|
||||
super(id)
|
||||
this.option = option
|
||||
}
|
||||
|
||||
/**Get the suffix for a new ticket. */
|
||||
getSuffix(user:discord.User): string {
|
||||
throw new ODSystemError("Tried to use an unimplemented ODOptionSuffix!")
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionUserNameSuffix extends ODOptionSuffix {
|
||||
getSuffix(user:discord.User): string {
|
||||
return user.username
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionUserIdSuffix extends ODOptionSuffix {
|
||||
getSuffix(user:discord.User): string {
|
||||
return user.id
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionCounterDynamicSuffix extends ODOptionSuffix {
|
||||
/**The database where the value of this counter is stored. */
|
||||
database: ODDatabase
|
||||
|
||||
constructor(id:ODValidId, option:ODTicketOption, database:ODDatabase){
|
||||
super(id,option)
|
||||
this.database = database
|
||||
if (!this.database.exists("openticket:option-suffix-counter",option.id.value)) this.database.set("openticket:option-suffix-counter",this.option.id.value,0)
|
||||
}
|
||||
|
||||
getSuffix(user:discord.User): string {
|
||||
const rawCurrentValue = this.database.get("openticket:option-suffix-counter",this.option.id.value)
|
||||
const currentValue = (typeof rawCurrentValue != "number") ? 0 : rawCurrentValue
|
||||
const newValue = currentValue+1
|
||||
this.database.set("openticket:option-suffix-counter",this.option.id.value,newValue)
|
||||
return newValue.toString()
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionCounterFixedSuffix extends ODOptionSuffix {
|
||||
/**The database where the value of this counter is stored. */
|
||||
database: ODDatabase
|
||||
|
||||
constructor(id:ODValidId, option:ODTicketOption, database:ODDatabase){
|
||||
super(id,option)
|
||||
this.database = database
|
||||
if (!this.database.exists("openticket:option-suffix-counter",option.id.value)) this.database.set("openticket:option-suffix-counter",this.option.id.value,0)
|
||||
}
|
||||
|
||||
getSuffix(user:discord.User): string {
|
||||
const rawCurrentValue = this.database.get("openticket:option-suffix-counter",this.option.id.value)
|
||||
const currentValue = (typeof rawCurrentValue != "number") ? 0 : rawCurrentValue
|
||||
const newValue = (currentValue >= 9999) ? 0 : currentValue+1
|
||||
|
||||
this.database.set("openticket:option-suffix-counter",this.option.id.value,newValue)
|
||||
if (newValue.toString().length == 1) return "000"+newValue.toString()
|
||||
else if (newValue.toString().length == 2) return "00"+newValue.toString()
|
||||
else if (newValue.toString().length == 3) return "0"+newValue.toString()
|
||||
else return newValue.toString()
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionRandomNumberSuffix extends ODOptionSuffix {
|
||||
/**The database where previous random numbers are stored. */
|
||||
database: ODDatabase
|
||||
|
||||
constructor(id:ODValidId, option:ODTicketOption, database:ODDatabase){
|
||||
super(id,option)
|
||||
this.database = database
|
||||
if (!this.database.exists("openticket:option-suffix-history",option.id.value)) this.database.set("openticket:option-suffix-history",this.option.id.value,[])
|
||||
}
|
||||
|
||||
#generateUniqueValue(history:string[]): string {
|
||||
const rawNumber = Math.round(Math.random()*1000).toString()
|
||||
let number = rawNumber
|
||||
if (rawNumber.length == 1) number = "000"+rawNumber
|
||||
else if (rawNumber.length == 2) number = "00"+rawNumber
|
||||
else if (rawNumber.length == 3) number = "0"+rawNumber
|
||||
|
||||
if (history.includes(number)) return this.#generateUniqueValue(history)
|
||||
else return number
|
||||
}
|
||||
getSuffix(user:discord.User): string {
|
||||
const rawCurrentValues = this.database.get("openticket:option-suffix-history",this.option.id.value)
|
||||
const currentValues = ((Array.isArray(rawCurrentValues)) ? rawCurrentValues : []) as string[]
|
||||
const newValue = this.#generateUniqueValue(currentValues)
|
||||
currentValues.push(newValue)
|
||||
if (currentValues.length > 50) currentValues.shift()
|
||||
this.database.set("openticket:option-suffix-history",this.option.id.value,currentValues)
|
||||
return newValue
|
||||
}
|
||||
}
|
||||
|
||||
export class ODOptionRandomHexSuffix extends ODOptionSuffix {
|
||||
/**The database where previous random hexes are stored. */
|
||||
database: ODDatabase
|
||||
|
||||
constructor(id:ODValidId, option:ODTicketOption, database:ODDatabase){
|
||||
super(id,option)
|
||||
this.database = database
|
||||
if (!this.database.exists("openticket:option-suffix-history",option.id.value)) this.database.set("openticket:option-suffix-history",this.option.id.value,[])
|
||||
}
|
||||
|
||||
#generateUniqueValue(history:string[]): string {
|
||||
const hex = crypto.randomBytes(2).toString("hex")
|
||||
if (history.includes(hex)) return this.#generateUniqueValue(history)
|
||||
else return hex
|
||||
}
|
||||
getSuffix(user:discord.User): string {
|
||||
const rawCurrentValues = this.database.get("openticket:option-suffix-history",this.option.id.value)
|
||||
const currentValues = ((Array.isArray(rawCurrentValues)) ? rawCurrentValues : []) as string[]
|
||||
const newValue = this.#generateUniqueValue(currentValues)
|
||||
currentValues.push(newValue)
|
||||
if (currentValues.length > 50) currentValues.shift()
|
||||
this.database.set("openticket:option-suffix-history",this.option.id.value,currentValues)
|
||||
return newValue
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
///////////////////////////////////////
|
||||
//OPENTICKET PANEL MODULE
|
||||
///////////////////////////////////////
|
||||
import { ODJsonConfig_DefaultPanelEmbedSettingsType } from "../defaults/config"
|
||||
import { ODId, ODManager, ODValidJsonType, ODValidId, ODVersion, ODValidButtonColor, ODManagerData } from "../modules/base"
|
||||
import { ODDebugger } from "../modules/console"
|
||||
|
||||
export class ODPanelManager extends ODManager<ODPanel> {
|
||||
#debug: ODDebugger
|
||||
|
||||
constructor(debug:ODDebugger){
|
||||
super(debug,"option")
|
||||
this.#debug = debug
|
||||
}
|
||||
|
||||
add(data:ODPanel, overwrite?:boolean): boolean {
|
||||
data.useDebug(this.#debug,"option data")
|
||||
return super.add(data,overwrite)
|
||||
}
|
||||
}
|
||||
|
||||
export interface ODPanelDataJson {
|
||||
id:string,
|
||||
value:ODValidJsonType
|
||||
}
|
||||
|
||||
export interface ODPanelJson {
|
||||
id:string,
|
||||
version:string,
|
||||
data:ODPanelDataJson[]
|
||||
}
|
||||
|
||||
/**## ODPanelIds `type`
|
||||
* This type is an array of ids available in the `ODPanel` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODPanelIds {
|
||||
"openticket:name":ODPanelData<string>,
|
||||
"openticket:options":ODPanelData<string[]>,
|
||||
"openticket:dropdown":ODPanelData<boolean>,
|
||||
|
||||
"openticket:text":ODPanelData<string>,
|
||||
"openticket:embed":ODPanelData<ODJsonConfig_DefaultPanelEmbedSettingsType>,
|
||||
|
||||
"openticket:dropdown-placeholder":ODPanelData<string>,
|
||||
|
||||
"openticket:enable-max-tickets-warning-text":ODPanelData<boolean>,
|
||||
"openticket:enable-max-tickets-warning-embed":ODPanelData<boolean>,
|
||||
|
||||
"openticket:describe-options-layout":ODPanelData<"simple"|"normal"|"detailed">,
|
||||
"openticket:describe-options-custom-title":ODPanelData<string>,
|
||||
"openticket:describe-options-in-text":ODPanelData<boolean>,
|
||||
"openticket:describe-options-in-embed-fields":ODPanelData<boolean>,
|
||||
"openticket:describe-options-in-embed-description":ODPanelData<boolean>
|
||||
}
|
||||
|
||||
export class ODPanel extends ODManager<ODPanelData<ODValidJsonType>> {
|
||||
id:ODId
|
||||
|
||||
constructor(id:ODValidId, data:ODPanelData<ODValidJsonType>[]){
|
||||
super()
|
||||
this.id = new ODId(id)
|
||||
data.forEach((data) => {
|
||||
this.add(data)
|
||||
})
|
||||
}
|
||||
|
||||
toJson(version:ODVersion): ODPanelJson {
|
||||
const data = this.getAll().map((data) => {
|
||||
return {
|
||||
id:data.id.toString(),
|
||||
value:data.value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id:this.id.toString(),
|
||||
version:version.toString(),
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
static fromJson(json:ODPanelJson): ODPanel {
|
||||
return new ODPanel(json.id,json.data.map((data) => new ODPanelData(data.id,data.value)))
|
||||
}
|
||||
|
||||
get<PanelId extends keyof ODPanelIds>(id:PanelId): ODPanelIds[PanelId]
|
||||
get(id:ODValidId): ODPanelData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODPanelData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<PanelId extends keyof ODPanelIds>(id:PanelId): ODPanelIds[PanelId]
|
||||
remove(id:ODValidId): ODPanelData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODPanelData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODPanelIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
}
|
||||
|
||||
export class ODPanelData<DataType extends ODValidJsonType> extends ODManagerData {
|
||||
#value: DataType
|
||||
|
||||
constructor(id:ODValidId, value:DataType){
|
||||
super(id)
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
set value(value:DataType){
|
||||
this.#value = value
|
||||
this._change()
|
||||
}
|
||||
get value(): DataType {
|
||||
return this.#value
|
||||
}
|
||||
/**Refresh the database. Is only required to be used when updating `ODPanelData` with an object/array as value. */
|
||||
refreshDatabase(){
|
||||
this._change()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
///////////////////////////////////////
|
||||
//OPENTICKET OPTION MODULE
|
||||
///////////////////////////////////////
|
||||
import { ODId, ODManager, ODValidJsonType, ODValidId, ODVersion, ODManagerData } from "../modules/base"
|
||||
import { ODDebugger } from "../modules/console"
|
||||
|
||||
export class ODQuestionManager extends ODManager<ODQuestion> {
|
||||
#debug: ODDebugger
|
||||
|
||||
constructor(debug:ODDebugger){
|
||||
super(debug,"question")
|
||||
this.#debug = debug
|
||||
}
|
||||
|
||||
add(data:ODQuestion, overwrite?:boolean): boolean {
|
||||
data.useDebug(this.#debug,"question data")
|
||||
return super.add(data,overwrite)
|
||||
}
|
||||
}
|
||||
|
||||
export interface ODQuestionDataJson {
|
||||
id:string,
|
||||
value:ODValidJsonType
|
||||
}
|
||||
|
||||
export interface ODQuestionJson {
|
||||
id:string,
|
||||
type:string,
|
||||
version:string,
|
||||
data:ODQuestionDataJson[]
|
||||
}
|
||||
|
||||
export class ODQuestion extends ODManager<ODQuestionData<ODValidJsonType>> {
|
||||
id:ODId
|
||||
type: string
|
||||
|
||||
constructor(id:ODValidId, type:string, data:ODQuestionData<ODValidJsonType>[]){
|
||||
super()
|
||||
this.id = new ODId(id)
|
||||
this.type = type
|
||||
data.forEach((data) => {
|
||||
this.add(data)
|
||||
})
|
||||
}
|
||||
|
||||
toJson(version:ODVersion): ODQuestionJson {
|
||||
const data = this.getAll().map((data) => {
|
||||
return {
|
||||
id:data.id.toString(),
|
||||
value:data.value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id:this.id.toString(),
|
||||
type:this.type,
|
||||
version:version.toString(),
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
static fromJson(json:ODQuestionJson): ODQuestion {
|
||||
return new ODQuestion(json.id,json.type,json.data.map((data) => new ODQuestionData(data.id,data.value)))
|
||||
}
|
||||
}
|
||||
|
||||
export class ODQuestionData<DataType extends ODValidJsonType> extends ODManagerData {
|
||||
#value: DataType
|
||||
|
||||
constructor(id:ODValidId, value:DataType){
|
||||
super(id)
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
set value(value:DataType){
|
||||
this.#value = value
|
||||
this._change()
|
||||
}
|
||||
get value(): DataType {
|
||||
return this.#value
|
||||
}
|
||||
/**Refresh the database. Is only required to be used when updating `ODQuestionData` with an object/array as value. */
|
||||
refreshDatabase(){
|
||||
this._change()
|
||||
}
|
||||
}
|
||||
|
||||
/**## ODShortQuestionIds `type`
|
||||
* This type is an array of ids available in the `ODShortQuestion` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODShortQuestionIds {
|
||||
"openticket:name":ODQuestionData<string>,
|
||||
"openticket:required":ODQuestionData<boolean>,
|
||||
"openticket:placeholder":ODQuestionData<string>,
|
||||
|
||||
"openticket:length-enabled":ODQuestionData<boolean>,
|
||||
"openticket:length-min":ODQuestionData<number>,
|
||||
"openticket:length-max":ODQuestionData<number>
|
||||
}
|
||||
|
||||
export class ODShortQuestion extends ODQuestion {
|
||||
type: "openticket:short" = "openticket:short"
|
||||
|
||||
constructor(id:ODValidId, data:ODQuestionData<ODValidJsonType>[]){
|
||||
super(id,"openticket:short",data)
|
||||
}
|
||||
|
||||
get<QuestionId extends keyof ODShortQuestionIds>(id:QuestionId): ODShortQuestionIds[QuestionId]
|
||||
get(id:ODValidId): ODQuestionData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODQuestionData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<QuestionId extends keyof ODShortQuestionIds>(id:QuestionId): ODShortQuestionIds[QuestionId]
|
||||
remove(id:ODValidId): ODQuestionData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODQuestionData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODShortQuestionIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
|
||||
static fromJson(json: ODQuestionJson): ODShortQuestion {
|
||||
return new ODShortQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
|
||||
}
|
||||
}
|
||||
|
||||
/**## ODParagraphQuestionIds `type`
|
||||
* This type is an array of ids available in the `ODParagraphQuestion` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODParagraphQuestionIds {
|
||||
"openticket:name":ODQuestionData<string>,
|
||||
"openticket:required":ODQuestionData<boolean>,
|
||||
"openticket:placeholder":ODQuestionData<string>,
|
||||
|
||||
"openticket:length-enabled":ODQuestionData<boolean>,
|
||||
"openticket:length-min":ODQuestionData<number>,
|
||||
"openticket:length-max":ODQuestionData<number>
|
||||
}
|
||||
|
||||
export class ODParagraphQuestion extends ODQuestion {
|
||||
type: "openticket:paragraph" = "openticket:paragraph"
|
||||
|
||||
constructor(id:ODValidId, data:ODQuestionData<ODValidJsonType>[]){
|
||||
super(id,"openticket:paragraph",data)
|
||||
}
|
||||
|
||||
get<QuestionId extends keyof ODParagraphQuestionIds>(id:QuestionId): ODParagraphQuestionIds[QuestionId]
|
||||
get(id:ODValidId): ODQuestionData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODQuestionData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<QuestionId extends keyof ODParagraphQuestionIds>(id:QuestionId): ODParagraphQuestionIds[QuestionId]
|
||||
remove(id:ODValidId): ODQuestionData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODQuestionData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODParagraphQuestionIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
|
||||
static fromJson(json: ODQuestionJson): ODParagraphQuestion {
|
||||
return new ODParagraphQuestion(json.id,json.data.map((data) => new ODQuestionData(data.id,data.value)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
///////////////////////////////////////
|
||||
//OPENTICKET ROLE MODULE
|
||||
///////////////////////////////////////
|
||||
import { ODId, ODManager, ODValidJsonType, ODValidId, ODVersion, ODManagerData } from "../modules/base"
|
||||
import { ODDebugger } from "../modules/console"
|
||||
import * as discord from "discord.js"
|
||||
|
||||
export class ODRoleManager extends ODManager<ODRole> {
|
||||
#debug: ODDebugger
|
||||
|
||||
constructor(debug:ODDebugger){
|
||||
super(debug,"role")
|
||||
this.#debug = debug
|
||||
}
|
||||
|
||||
add(data:ODRole, overwrite?:boolean): boolean {
|
||||
data.useDebug(this.#debug,"role data")
|
||||
return super.add(data,overwrite)
|
||||
}
|
||||
}
|
||||
|
||||
export interface ODRoleDataJson {
|
||||
id:string,
|
||||
value:ODValidJsonType
|
||||
}
|
||||
|
||||
export interface ODRoleJson {
|
||||
id:string,
|
||||
version:string,
|
||||
data:ODRoleDataJson[]
|
||||
}
|
||||
|
||||
export class ODRoleData<DataType extends ODValidJsonType> extends ODManagerData {
|
||||
#value: DataType
|
||||
|
||||
constructor(id:ODValidId, value:DataType){
|
||||
super(id)
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
set value(value:DataType){
|
||||
this.#value = value
|
||||
this._change()
|
||||
}
|
||||
get value(): DataType {
|
||||
return this.#value
|
||||
}
|
||||
/**Refresh the database. Is only required to be used when updating `ODRoleData` with an object/array as value. */
|
||||
refreshDatabase(){
|
||||
this._change()
|
||||
}
|
||||
}
|
||||
|
||||
/**## ODRoleIds `type`
|
||||
* This type is an array of ids available in the `ODRole` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODRoleIds {
|
||||
"openticket:roles":ODRoleData<string[]>,
|
||||
"openticket:mode":ODRoleData<OTRoleUpdateMode>,
|
||||
"openticket:remove-roles-on-add":ODRoleData<string[]>,
|
||||
"openticket:add-on-join":ODRoleData<boolean>
|
||||
}
|
||||
|
||||
export class ODRole extends ODManager<ODRoleData<ODValidJsonType>> {
|
||||
id:ODId
|
||||
|
||||
constructor(id:ODValidId, data:ODRoleData<ODValidJsonType>[]){
|
||||
super()
|
||||
this.id = new ODId(id)
|
||||
data.forEach((data) => {
|
||||
this.add(data)
|
||||
})
|
||||
}
|
||||
|
||||
toJson(version:ODVersion): ODRoleJson {
|
||||
const data = this.getAll().map((data) => {
|
||||
return {
|
||||
id:data.id.toString(),
|
||||
value:data.value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id:this.id.toString(),
|
||||
version:version.toString(),
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
static fromJson(json:ODRoleJson): ODRole {
|
||||
return new ODRole(json.id,json.data.map((data) => new ODRoleData(data.id,data.value)))
|
||||
}
|
||||
|
||||
get<OptionId extends keyof ODRoleIds>(id:OptionId): ODRoleIds[OptionId]
|
||||
get(id:ODValidId): ODRoleData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODRoleData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<OptionId extends keyof ODRoleIds>(id:OptionId): ODRoleIds[OptionId]
|
||||
remove(id:ODValidId): ODRoleData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODRoleData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODRoleIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
}
|
||||
|
||||
export interface OTRoleUpdateResult {
|
||||
role:discord.Role,
|
||||
action:"added"|"removed"|null
|
||||
}
|
||||
|
||||
export type OTRoleUpdateMode = "add&remove"|"add"|"remove"
|
||||
@@ -0,0 +1,251 @@
|
||||
///////////////////////////////////////
|
||||
//OPENTICKET TICKET MODULE
|
||||
///////////////////////////////////////
|
||||
import { ODId, ODManager, ODValidJsonType, ODValidId, ODVersion, ODManagerData } from "../modules/base"
|
||||
import { ODDebugger } from "../modules/console"
|
||||
import { ODClientManager_Default } from "../defaults/client"
|
||||
import { ODOption, ODOptionJson, ODTicketOption } from "./option"
|
||||
import * as discord from "discord.js"
|
||||
|
||||
export class ODTicketManager extends ODManager<ODTicket> {
|
||||
/**A reference to the main server of the bot */
|
||||
#guild: discord.Guild|null = null
|
||||
#client: ODClientManager_Default
|
||||
#debug: ODDebugger
|
||||
|
||||
constructor(debug:ODDebugger, client:ODClientManager_Default){
|
||||
super(debug,"ticket")
|
||||
this.#debug = debug
|
||||
this.#client = client
|
||||
}
|
||||
|
||||
add(data:ODTicket, overwrite?:boolean): boolean {
|
||||
data.useDebug(this.#debug,"ticket data")
|
||||
return super.add(data,overwrite)
|
||||
}
|
||||
/**Use a specific guild in this class for fetching the channel*/
|
||||
useGuild(guild:discord.Guild|null){
|
||||
this.#guild = guild
|
||||
}
|
||||
/**Get the discord channel for a specific ticket. */
|
||||
async getTicketChannel(ticket:ODTicket): Promise<discord.GuildTextBasedChannel|null> {
|
||||
if (!this.#guild) return null
|
||||
try {
|
||||
const channel = await this.#guild.channels.fetch(ticket.id.value)
|
||||
if (!channel || !channel.isTextBased()) return null
|
||||
return channel
|
||||
}catch{
|
||||
return null
|
||||
}
|
||||
}
|
||||
/**Get the main ticket message of a ticket channel when found. */
|
||||
async getTicketMessage(ticket:ODTicket): Promise<discord.Message<true>|null> {
|
||||
const msgId = ticket.get("openticket:ticket-message").value
|
||||
if (!this.#guild || !msgId) return null
|
||||
try {
|
||||
const channel = await this.getTicketChannel(ticket)
|
||||
if (!channel) return null
|
||||
return await channel.messages.fetch(msgId)
|
||||
}catch{
|
||||
return null
|
||||
}
|
||||
}
|
||||
/**Shortcut for getting a discord.js user within a ticket. */
|
||||
async getTicketUser(ticket:ODTicket, user:"creator"|"closer"|"claimer"|"pinner"): Promise<discord.User|null> {
|
||||
if (!this.#guild) return null
|
||||
try {
|
||||
if (user == "creator"){
|
||||
const creatorId = ticket.get("openticket:opened-by").value
|
||||
if (!creatorId) return null
|
||||
else return (await this.#guild.client.users.fetch(creatorId))
|
||||
|
||||
}else if (user == "closer"){
|
||||
const closerId = ticket.get("openticket:closed-by").value
|
||||
if (!closerId) return null
|
||||
else return (await this.#guild.client.users.fetch(closerId))
|
||||
|
||||
}else if (user == "claimer"){
|
||||
const claimerId = ticket.get("openticket:claimed-by").value
|
||||
if (!claimerId) return null
|
||||
else return (await this.#guild.client.users.fetch(claimerId))
|
||||
|
||||
}else if (user == "pinner"){
|
||||
const pinnerId = ticket.get("openticket:pinned-by").value
|
||||
if (!pinnerId) return null
|
||||
else return (await this.#guild.client.users.fetch(pinnerId))
|
||||
|
||||
}else return null
|
||||
}catch {return null}
|
||||
}
|
||||
/**Shortcut for getting all users that are able to view a ticket. */
|
||||
async getAllTicketParticipants(ticket:ODTicket): Promise<{user:discord.User,role:"creator"|"participant"|"admin"}[]|null> {
|
||||
if (!this.#guild) return null
|
||||
const final: {user:discord.User,role:"creator"|"participant"|"admin"}[] = []
|
||||
const channel = await this.getTicketChannel(ticket)
|
||||
if (!channel) return null
|
||||
|
||||
//add creator
|
||||
const creatorId = ticket.get("openticket:opened-by").value
|
||||
if (creatorId){
|
||||
const creator = await this.#client.fetchUser(creatorId)
|
||||
if (creator) final.push({user:creator,role:"creator"})
|
||||
}
|
||||
|
||||
//add participants
|
||||
const participants = ticket.get("openticket:participants").value.filter((p) => p.type == "user")
|
||||
for (const p of participants){
|
||||
if (!final.find((u) => u.user.id == p.id)){
|
||||
const participant = await this.#client.fetchUser(p.id)
|
||||
if (participant) final.push({user:participant,role:"participant"})
|
||||
}
|
||||
}
|
||||
|
||||
//add admin roles
|
||||
const roles = ticket.get("openticket:participants").value.filter((p) => p.type == "role")
|
||||
for (const r of roles){
|
||||
const role = await this.#client.fetchGuildRole(channel.guild,r.id)
|
||||
if (role){
|
||||
role.members.forEach((member) => {
|
||||
if (final.find((u) => u.user.id == member.id)) return
|
||||
final.push({user:member.user,role:"admin"})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return final
|
||||
}
|
||||
}
|
||||
|
||||
export interface ODTicketDataJson {
|
||||
id:string,
|
||||
value:ODValidJsonType
|
||||
}
|
||||
|
||||
export interface ODTicketJson {
|
||||
id:string,
|
||||
option:string,
|
||||
version:string,
|
||||
data:ODTicketDataJson[]
|
||||
}
|
||||
|
||||
/**## ODTicketIds `type`
|
||||
* This type is an array of ids available in the `ODTicket` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODTicketIds {
|
||||
"openticket:busy":ODTicketData<boolean>,
|
||||
"openticket:ticket-message":ODTicketData<string|null>,
|
||||
"openticket:participants":ODTicketData<{type:"role"|"user",id:string}[]>,
|
||||
"openticket:channel-suffix":ODTicketData<string>,
|
||||
|
||||
"openticket:open":ODTicketData<boolean>,
|
||||
"openticket:opened-by":ODTicketData<string|null>,
|
||||
"openticket:opened-on":ODTicketData<number|null>,
|
||||
"openticket:closed":ODTicketData<boolean>,
|
||||
"openticket:closed-by":ODTicketData<string|null>,
|
||||
"openticket:closed-on":ODTicketData<number|null>,
|
||||
"openticket:claimed":ODTicketData<boolean>,
|
||||
"openticket:claimed-by":ODTicketData<string|null>,
|
||||
"openticket:claimed-on":ODTicketData<number|null>,
|
||||
"openticket:pinned":ODTicketData<boolean>,
|
||||
"openticket:pinned-by":ODTicketData<string|null>,
|
||||
"openticket:pinned-on":ODTicketData<number|null>,
|
||||
"openticket:for-deletion":ODTicketData<boolean>,
|
||||
|
||||
"openticket:category":ODTicketData<string|null>,
|
||||
"openticket:category-mode":ODTicketData<null|"normal"|"closed"|"backup"|"claimed">,
|
||||
|
||||
"openticket:autoclose-enabled":ODTicketData<boolean>,
|
||||
"openticket:autoclose-hours":ODTicketData<number>,
|
||||
"openticket:autoclosed":ODTicketData<boolean>,
|
||||
"openticket:autodelete-enabled":ODTicketData<boolean>,
|
||||
"openticket:autodelete-days":ODTicketData<number>,
|
||||
|
||||
"openticket:answers":ODTicketData<{id:string,name:string,type:"short"|"paragraph",value:string|null}[]>,
|
||||
}
|
||||
|
||||
export class ODTicket extends ODManager<ODTicketData<ODValidJsonType>> {
|
||||
id:ODId
|
||||
#option: ODTicketOption
|
||||
|
||||
constructor(id:ODValidId, option:ODTicketOption, data:ODTicketData<ODValidJsonType>[]){
|
||||
super()
|
||||
this.id = new ODId(id)
|
||||
this.#option = option
|
||||
data.forEach((data) => {
|
||||
this.add(data)
|
||||
})
|
||||
}
|
||||
|
||||
set option(option:ODTicketOption){
|
||||
this.#option = option
|
||||
this._change()
|
||||
}
|
||||
get option(){
|
||||
return this.#option
|
||||
}
|
||||
|
||||
toJson(version:ODVersion): ODTicketJson {
|
||||
const data = this.getAll().map((data) => {
|
||||
return {
|
||||
id:data.id.toString(),
|
||||
value:data.value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id:this.id.toString(),
|
||||
option:this.option.id.value,
|
||||
version:version.toString(),
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
static fromJson(json:ODTicketJson, option:ODTicketOption): ODTicket {
|
||||
return new ODTicket(json.id,option,json.data.map((data) => new ODTicketData(data.id,data.value)))
|
||||
}
|
||||
|
||||
get<OptionId extends keyof ODTicketIds>(id:OptionId): ODTicketIds[OptionId]
|
||||
get(id:ODValidId): ODTicketData<ODValidJsonType>|null
|
||||
|
||||
get(id:ODValidId): ODTicketData<ODValidJsonType>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<OptionId extends keyof ODTicketIds>(id:OptionId): ODTicketIds[OptionId]
|
||||
remove(id:ODValidId): ODTicketData<ODValidJsonType>|null
|
||||
|
||||
remove(id:ODValidId): ODTicketData<ODValidJsonType>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODTicketIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
}
|
||||
|
||||
export class ODTicketData<DataType extends ODValidJsonType> extends ODManagerData {
|
||||
#value: DataType
|
||||
|
||||
constructor(id:ODValidId, value:DataType){
|
||||
super(id)
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
set value(value:DataType){
|
||||
this.#value = value
|
||||
this._change()
|
||||
}
|
||||
get value(): DataType {
|
||||
return this.#value
|
||||
}
|
||||
/**Refresh the database. Is only required to be used when updating `ODTicketData` with an object/array as value. */
|
||||
refreshDatabase(){
|
||||
this._change()
|
||||
}
|
||||
}
|
||||
|
||||
export type ODTicketClearFilter = "all"|"open"|"closed"|"claimed"|"unclaimed"|"pinned"|"unpinned"|"autoclosed"
|
||||
@@ -0,0 +1,802 @@
|
||||
///////////////////////////////////////
|
||||
//OPENTICKET TRANSCRIPT MODULE
|
||||
///////////////////////////////////////
|
||||
import { ODId, ODManager, ODValidJsonType, ODValidId, ODManagerData, ODValidButtonColor } from "../modules/base"
|
||||
import { ODDebugger } from "../modules/console"
|
||||
import { ODTicket, ODTicketManager } from "./ticket"
|
||||
import { ODMessageBuildResult } from "../modules/builder"
|
||||
import * as discord from "discord.js"
|
||||
|
||||
export class ODTranscriptManager extends ODManager<ODTranscriptCompiler<any>> {
|
||||
/**The manager responsible for collecting all messages in a channel. */
|
||||
collector: ODTranscriptCollector
|
||||
|
||||
constructor(debug:ODDebugger, tickets:ODTicketManager){
|
||||
super(debug,"transcript compiler")
|
||||
this.collector = new ODTranscriptCollector(tickets)
|
||||
}
|
||||
}
|
||||
|
||||
export type ODTranscriptCompilerInitFunction = (ticket:ODTicket, channel:discord.TextChannel, user:discord.User) => (ODTranscriptCompilerInitResult)|Promise<ODTranscriptCompilerInitResult>
|
||||
export type ODTranscriptCompilerCompileFunction<Data extends object> = (ticket:ODTicket, channel:discord.TextChannel, user:discord.User) => ODTranscriptCompilerCompileResult<Data>|Promise<ODTranscriptCompilerCompileResult<Data>>
|
||||
export type ODTranscriptCompilerReadyFunction<Data extends object> = (result:ODTranscriptCompilerCompileResult<Data>) => ODTranscriptCompilerReadyResult|Promise<ODTranscriptCompilerReadyResult>
|
||||
|
||||
export interface ODTranscriptCompilerInitResult {
|
||||
success:boolean,
|
||||
errorReason:string|null,
|
||||
pendingMessage:ODMessageBuildResult|null
|
||||
}
|
||||
|
||||
export interface ODTranscriptCompilerCompileResult<Data extends object> {
|
||||
ticket:ODTicket,
|
||||
channel:discord.TextChannel,
|
||||
user:discord.User
|
||||
success:boolean,
|
||||
errorReason:string|null,
|
||||
messages:ODTranscriptMessageData[]|null,
|
||||
data:Data|null
|
||||
}
|
||||
export interface ODTranscriptCompilerReadyResult {
|
||||
channelMessage?:ODMessageBuildResult,
|
||||
creatorDmMessage?:ODMessageBuildResult,
|
||||
participantDmMessage?:ODMessageBuildResult,
|
||||
activeAdminDmMessage?:ODMessageBuildResult,
|
||||
everyAdminDmMessage?:ODMessageBuildResult
|
||||
}
|
||||
|
||||
export class ODTranscriptCompiler<Data extends object> extends ODManagerData {
|
||||
/*Initialise the system every time a transcript is created. Returns optional "pending" message to display while the transcript is being compiled. */
|
||||
init: ODTranscriptCompilerInitFunction|null
|
||||
/*Compile or create the transcript. Returns data to give to the ready() function for message creation. */
|
||||
compile: ODTranscriptCompilerCompileFunction<Data>|null
|
||||
/*Unload the system & create the final transcript message that will be sent. */
|
||||
ready: ODTranscriptCompilerReadyFunction<Data>|null
|
||||
|
||||
constructor(id:ODValidId, init?:ODTranscriptCompilerInitFunction, compile?:ODTranscriptCompilerCompileFunction<Data>, ready?:ODTranscriptCompilerReadyFunction<Data>|null){
|
||||
super(id)
|
||||
this.init = init ?? null
|
||||
this.compile = compile ?? null
|
||||
this.ready = ready ?? null
|
||||
}
|
||||
}
|
||||
|
||||
/**## ODTranscriptCompilerIds `type`
|
||||
* This type is an array of ids available in the `ODTranscriptCompiler` class.
|
||||
* It's used to generate typescript declarations for this class.
|
||||
*/
|
||||
export interface ODTranscriptCompilerIds {
|
||||
"openticket:html-compiler":ODTranscriptCompiler<{url:string}>,
|
||||
"openticket:text-compiler":ODTranscriptCompiler<{contents:string}>,
|
||||
}
|
||||
|
||||
/**## ODTranscriptManager_Default `default_class`
|
||||
* This is a special class that adds type definitions & typescript to the ODTranscriptManager class.
|
||||
* It doesn't add any extra features!
|
||||
*
|
||||
* This default class is made for the global variable `openticket.transcripts`!
|
||||
*/
|
||||
export class ODTranscriptManager_Default extends ODTranscriptManager {
|
||||
get<QuestionId extends keyof ODTranscriptCompilerIds>(id:QuestionId): ODTranscriptCompilerIds[QuestionId]
|
||||
get(id:ODValidId): ODTranscriptCompiler<any>|null
|
||||
|
||||
get(id:ODValidId): ODTranscriptCompiler<any>|null {
|
||||
return super.get(id)
|
||||
}
|
||||
|
||||
remove<QuestionId extends keyof ODTranscriptCompilerIds>(id:QuestionId): ODTranscriptCompilerIds[QuestionId]
|
||||
remove(id:ODValidId): ODTranscriptCompiler<any>|null
|
||||
|
||||
remove(id:ODValidId): ODTranscriptCompiler<any>|null {
|
||||
return super.remove(id)
|
||||
}
|
||||
|
||||
exists(id:keyof ODTranscriptCompilerIds): boolean
|
||||
exists(id:ODValidId): boolean
|
||||
|
||||
exists(id:ODValidId): boolean {
|
||||
return super.exists(id)
|
||||
}
|
||||
}
|
||||
|
||||
export class ODTranscriptCollector {
|
||||
/**Alias for the ticket manager. */
|
||||
#tickets: ODTicketManager
|
||||
|
||||
constructor(tickets:ODTicketManager){
|
||||
this.#tickets = tickets
|
||||
}
|
||||
|
||||
/**Collect all messages from a given ticket channel. It may not include all messages depending on the ratelimit. */
|
||||
async collectAllMessages(ticket:ODTicket, include?:ODTranscriptCollectorIncludeSettings): Promise<discord.Message<true>[]|null> {
|
||||
const newInclude: ODTranscriptCollectorIncludeSettings = include ?? {users:true,bots:true,client:true}
|
||||
const channel = await this.#tickets.getTicketChannel(ticket)
|
||||
if (!channel) return null
|
||||
|
||||
const final: discord.Message<true>[] = []
|
||||
const limit: number = 2000
|
||||
let lastId: string = ""
|
||||
while (true){
|
||||
const options: discord.FetchMessagesOptions = {limit:100}
|
||||
if (lastId) options.before = lastId
|
||||
|
||||
const messages = await channel.messages.fetch(options)
|
||||
messages.forEach(msg => {
|
||||
if (msg.author.id == msg.client.user.id && newInclude.client) final.push(msg)
|
||||
else if ((msg.author.bot || msg.author.system) && newInclude.bots) final.push(msg)
|
||||
else if (newInclude.users) final.push(msg)
|
||||
})
|
||||
|
||||
const lastMessage = messages.last()
|
||||
if (messages.size != 100 || final.length >= limit || !lastMessage) break
|
||||
else lastId = lastMessage.id
|
||||
}
|
||||
return final.reverse()
|
||||
}
|
||||
/**Count all messages from a given ticket channel. It may not include all messages depending on the ratelimit. */
|
||||
async countAllMessages(ticket:ODTicket, include?:ODTranscriptCollectorIncludeSettings){
|
||||
const messages = await this.collectAllMessages(ticket,include)
|
||||
if (!messages) return null
|
||||
return messages.length
|
||||
}
|
||||
async convertMessagesToTranscriptData(messages:discord.Message<true>[]): Promise<ODTranscriptMessageData[]> {
|
||||
const final: ODTranscriptMessageData[] = []
|
||||
for (const msg of messages){
|
||||
const {guild,channel,id,createdTimestamp} = msg
|
||||
|
||||
//create message author
|
||||
const author = this.#handleUserData(msg.author,msg.member)
|
||||
|
||||
//create message type
|
||||
let type: ODTranscriptMessageType = "default"
|
||||
if (msg.mentions.everyone || msg.content.includes("@here")) type = "important"
|
||||
else if (msg.flags.has("Ephemeral")) type = "ephemeral"
|
||||
else if (msg.type == discord.MessageType.UserJoin) type = "welcome.message"
|
||||
else if (msg.type == discord.MessageType.ChannelPinnedMessage) type = "pinned.message"
|
||||
else if (msg.type == discord.MessageType.GuildBoost || msg.type == discord.MessageType.GuildBoostTier1 || msg.type == discord.MessageType.GuildBoostTier2 || msg.type == discord.MessageType.GuildBoostTier3) type = "boost.message"
|
||||
else if (msg.type == discord.MessageType.ThreadCreated) type = "thread.message"
|
||||
|
||||
//create message embeds
|
||||
const embeds: ODTranscriptEmbedData[] = []
|
||||
msg.embeds.forEach((embed) => {
|
||||
embeds.push({
|
||||
title:embed.title,
|
||||
description:embed.description,
|
||||
authorimg:(embed.author && embed.author.iconURL) ? embed.author.iconURL : null,
|
||||
authortext:(embed.author) ? embed.author.name : null,
|
||||
footerimg:(embed.footer && embed.footer.iconURL) ? embed.footer.iconURL : null,
|
||||
footertext:(embed.footer) ? embed.footer.text : null,
|
||||
color:(embed.hexColor as discord.HexColorString) ?? "#000000",
|
||||
image:(embed.image) ? embed.image.url : null,
|
||||
thumbnail:(embed.thumbnail) ? embed.thumbnail.url : null,
|
||||
url:embed.url,
|
||||
fields:embed.fields.map((field) => {return {name:field.name,value:field.value,inline:field.inline ?? false}})
|
||||
})
|
||||
})
|
||||
|
||||
//create message files
|
||||
const files: ODTranscriptFileData[] = []
|
||||
msg.attachments.forEach((attachment) => {
|
||||
const {size,unit} = this.calculateFileSize(attachment.size)
|
||||
files.push({
|
||||
type:attachment.contentType ?? "unknown",
|
||||
size,
|
||||
unit,
|
||||
name:attachment.name,
|
||||
url:attachment.url,
|
||||
spoiler:attachment.spoiler,
|
||||
alt:attachment.description
|
||||
})
|
||||
})
|
||||
|
||||
//create message components
|
||||
const rows: ODTranscriptComponentRowData[] = []
|
||||
msg.components.forEach((row) => {
|
||||
const components: ODTranscriptComponentRowData["components"] = []
|
||||
row.components.forEach((component) => {
|
||||
if (component.type == discord.ComponentType.Button){
|
||||
components.push({
|
||||
id:component.customId,
|
||||
disabled:component.disabled,
|
||||
type:"button",
|
||||
label:component.label,
|
||||
emoji:this.#handleComponentEmoji(msg,component.emoji),
|
||||
color:this.#handleButtonComponentStyle(component.style),
|
||||
mode:(component.style == discord.ButtonStyle.Link) ? "url" : "button",
|
||||
url:component.url
|
||||
})
|
||||
}else if (component.type == discord.ComponentType.StringSelect){
|
||||
components.push({
|
||||
id:component.customId,
|
||||
disabled:component.disabled,
|
||||
type:"dropdown",
|
||||
placeholder:component.placeholder,
|
||||
options:component.options.map((option) => {
|
||||
return {
|
||||
id:option.value,
|
||||
label:option.label,
|
||||
description:option.description ?? null,
|
||||
emoji:this.#handleComponentEmoji(msg,option.emoji ?? null)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
rows.push({components})
|
||||
})
|
||||
|
||||
//create message reply
|
||||
let reply: ODTranscriptMessageReplyData|ODTranscriptInteractionReplyData|null = null
|
||||
if (msg.reference && msg.reference.messageId && msg.reference.guildId){
|
||||
//normal message reply
|
||||
try{
|
||||
const replyChannel = await msg.client.channels.fetch(msg.reference.channelId)
|
||||
if (replyChannel && !replyChannel.isDMBased() && replyChannel.isTextBased()){
|
||||
const replyMessage = await replyChannel.messages.fetch(msg.reference.messageId)
|
||||
if (replyMessage){
|
||||
const replyUser = this.#handleUserData(replyMessage.author,replyMessage.member)
|
||||
|
||||
reply = {
|
||||
type:"message",
|
||||
guild:msg.reference.guildId,
|
||||
channel:msg.reference.channelId,
|
||||
id:msg.reference.messageId,
|
||||
user:replyUser,
|
||||
content:(replyMessage.content != "") ? replyMessage.content : null
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch{}
|
||||
}else if (msg.interaction && msg.interaction.type == discord.InteractionType.ApplicationCommand){
|
||||
//slash command reply
|
||||
const member = await msg.guild.members.fetch(msg.interaction.user.id)
|
||||
reply = {
|
||||
type:"interaction",
|
||||
name:msg.interaction.commandName,
|
||||
user:this.#handleUserData(msg.interaction.user,member)
|
||||
}
|
||||
}
|
||||
|
||||
//create message reactions
|
||||
const reactions: ODTranscriptReactionData[] = []
|
||||
msg.reactions.cache.forEach((reaction) => {
|
||||
const {count,emoji} = reaction
|
||||
|
||||
if (emoji instanceof discord.ReactionEmoji && !emoji.id && emoji.name){
|
||||
//build-in emoji
|
||||
reactions.push({
|
||||
id:null,
|
||||
name:null,
|
||||
custom:false,
|
||||
animated:false,
|
||||
emoji:emoji.name,
|
||||
amount:count,
|
||||
super:false //unimplemented in discord.js
|
||||
})
|
||||
}else if (emoji instanceof discord.ReactionEmoji && emoji.id){
|
||||
//custom emoji
|
||||
reactions.push({
|
||||
id:emoji.id,
|
||||
name:emoji.name,
|
||||
custom:true,
|
||||
animated:emoji.animated ?? false,
|
||||
emoji:(emoji.animated ? emoji.imageURL({extension:"gif"}) : emoji.imageURL({extension:"png"})) ?? "❌",
|
||||
amount:count,
|
||||
super:false //unimplemented in discord.js
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
//create message
|
||||
final.push({
|
||||
author,
|
||||
guild:guild.id,
|
||||
channel:channel.id,
|
||||
id,
|
||||
edited:(msg.editedAt) ? true : false,
|
||||
timestamp:createdTimestamp,
|
||||
type,
|
||||
content:(msg.content != "") ? msg.content : null,
|
||||
embeds,
|
||||
files,
|
||||
components:rows,
|
||||
reply,
|
||||
reactions
|
||||
})
|
||||
}
|
||||
return final
|
||||
}
|
||||
/**Calculate a human-readable file size. Used in transcripts. */
|
||||
calculateFileSize(bytes:number): {size:number, unit:"B"|"KB"|"MB"|"GB"|"TB"} {
|
||||
if (bytes >= 1000000000000) return {size:(Math.round((bytes/1000000000000)*100)/100),unit:"TB"}
|
||||
if (bytes >= 1000000000) return {size:(Math.round((bytes/1000000000)*100)/100),unit:"GB"}
|
||||
else if (bytes >= 1000000) return {size:(Math.round((bytes/1000000)*100)/100),unit:"MB"}
|
||||
else if (bytes >= 1000) return {size:(Math.round((bytes/1000)*100)/100),unit:"KB"}
|
||||
else return {size:bytes,unit:"B"}
|
||||
}
|
||||
/**Get the `ODTranscriptEmojiData` from a discord.js component emoji. */
|
||||
#handleComponentEmoji(message:discord.Message<true>, rawEmoji:discord.APIMessageComponentEmoji|null): ODTranscriptEmojiData|null {
|
||||
if (!rawEmoji) return null
|
||||
//return built-in emoji
|
||||
if (rawEmoji.name) return {
|
||||
id:null,
|
||||
name:null,
|
||||
custom:false,
|
||||
animated:false,
|
||||
emoji:rawEmoji.name
|
||||
}
|
||||
if (!rawEmoji.id) return null
|
||||
const emoji = message.client.emojis.resolve(rawEmoji.id)
|
||||
if (!emoji) return null
|
||||
//return custom emoji
|
||||
return {
|
||||
id:emoji.id,
|
||||
name:emoji.name,
|
||||
custom:true,
|
||||
animated:emoji.animated ?? false,
|
||||
emoji:(emoji.animated ? emoji.imageURL({extension:"gif"}) : emoji.imageURL({extension:"png"}))
|
||||
}
|
||||
}
|
||||
/**Create the `ODValidButtonColor` from the discord.js button style. */
|
||||
#handleButtonComponentStyle(style:discord.ButtonStyle): ODValidButtonColor {
|
||||
if (style == discord.ButtonStyle.Danger) return "red"
|
||||
else if (style == discord.ButtonStyle.Success) return "green"
|
||||
else if (style == discord.ButtonStyle.Primary) return "blue"
|
||||
else return "gray"
|
||||
}
|
||||
/**Create the `ODTranscriptUserData` from a discord.js user. */
|
||||
#handleUserData(user:discord.User, member?:discord.GuildMember|null): ODTranscriptUserData {
|
||||
const userData: ODTranscriptUserData = {
|
||||
id:user.id,
|
||||
username:user.username,
|
||||
displayname:user.displayName,
|
||||
pfp:user.displayAvatarURL(),
|
||||
tag:null,
|
||||
color:"#ffffff"
|
||||
}
|
||||
if (user.flags && user.flags.has("VerifiedBot")) userData.tag = "verified"
|
||||
else if (user.system) userData.tag = "system"
|
||||
else if (user.bot) userData.tag = "app"
|
||||
if (member) userData.color = member.roles.highest.hexColor.replace("#000000","#ffffff") as discord.HexColorString
|
||||
|
||||
return userData
|
||||
}
|
||||
}
|
||||
|
||||
export interface ODTranscriptCollectorIncludeSettings {
|
||||
/**Collect messages from normal discord users. */
|
||||
users: boolean,
|
||||
/**Collect messages from bots & system users. */
|
||||
bots: boolean,
|
||||
/**Collect messages from this bot. */
|
||||
client: boolean
|
||||
}
|
||||
|
||||
export interface ODTranscriptMessageData {
|
||||
/**The message author. */
|
||||
author: ODTranscriptUserData,
|
||||
/**The server this message originated from. */
|
||||
guild: string,
|
||||
/**The channel this message originated from. */
|
||||
channel: string,
|
||||
/**The id of this message. */
|
||||
id: string,
|
||||
/**Is this message edited? */
|
||||
edited: boolean,
|
||||
/**The unix timestamp of the creation of this message. */
|
||||
timestamp: number,
|
||||
/**The type of message. */
|
||||
type: ODTranscriptMessageType,
|
||||
/**The contents of this message. */
|
||||
content: string|null,
|
||||
/**The embeds of this message. */
|
||||
embeds: ODTranscriptEmbedData[],
|
||||
/**The files of this message. */
|
||||
files: ODTranscriptFileData[],
|
||||
/**The components (buttons & dropdowns) of this message. */
|
||||
components: ODTranscriptComponentRowData[],
|
||||
/**When this message is a reply to something, the data will be here. */
|
||||
reply: (ODTranscriptMessageReplyData|ODTranscriptInteractionReplyData|null),
|
||||
/**All reactions of htis message. */
|
||||
reactions: ODTranscriptReactionData[]
|
||||
}
|
||||
|
||||
export type ODTranscriptMessageType = "default"|"important"|"ephemeral"|"pinned.message"|"welcome.message"|"boost.message"|"thread.message"
|
||||
|
||||
export interface ODTranscriptUserData {
|
||||
/**The id of this user. */
|
||||
id: string,
|
||||
/**The username of this user. */
|
||||
username: string,
|
||||
/**The display name of this user. */
|
||||
displayname: string,
|
||||
/**The profile picture url of this user. */
|
||||
pfp: string,
|
||||
/**The additional tag of this user. */
|
||||
tag: "app"|"verified"|"system"|null,
|
||||
/**When in a server, this is the color of the highest role. */
|
||||
color: discord.HexColorString
|
||||
}
|
||||
|
||||
export interface ODTranscriptEmbedData {
|
||||
/**The title of this embed. */
|
||||
title: string|null,
|
||||
/**The description of this embed. */
|
||||
description: string|null,
|
||||
/**The author image of this embed. */
|
||||
authorimg: string|null,
|
||||
/**The author text of this embed. */
|
||||
authortext: string|null,
|
||||
/**The footer image of this embed. */
|
||||
footerimg: string|null
|
||||
/**The footer text of this embed. */
|
||||
footertext: string|null,
|
||||
/**The color of this embed (hex color). */
|
||||
color: discord.HexColorString,
|
||||
/**The image of this embed. */
|
||||
image: string|null,
|
||||
/**The thumbnail of this embed. */
|
||||
thumbnail: string|null,
|
||||
/**The url in the title of this embed. */
|
||||
url: string|null,
|
||||
/**All fields available in this embed. */
|
||||
fields: ODTranscriptEmbedFieldData[]
|
||||
}
|
||||
|
||||
export interface ODTranscriptEmbedFieldData {
|
||||
/**The name of this embed field. */
|
||||
name: string,
|
||||
/**The value or contents of this embed field. */
|
||||
value: string,
|
||||
/**Is this field rendered inline? */
|
||||
inline: boolean
|
||||
}
|
||||
|
||||
export interface ODTranscriptFileData {
|
||||
/**The file type or extension. */
|
||||
type: string,
|
||||
/**The size of this file. */
|
||||
size: number,
|
||||
/**The unit used for the size of this file. */
|
||||
unit: "B"|"KB"|"MB"|"GB"|"TB",
|
||||
/**The name of this file. */
|
||||
name: string,
|
||||
/**The url of this file. */
|
||||
url: string,
|
||||
/**Is this file a spoiler? */
|
||||
spoiler: boolean,
|
||||
/**The alternative text for this file. */
|
||||
alt: string|null
|
||||
}
|
||||
|
||||
export interface ODTranscriptComponentRowData {
|
||||
/**All components (buttons & dropdowns) present in this action row. */
|
||||
components: (ODTranscriptButtonComponentData|ODTranscriptDropdownComponentData)[]
|
||||
}
|
||||
|
||||
export interface ODTranscriptComponentData {
|
||||
/**The custom id of this component. */
|
||||
id: string|null,
|
||||
/**Is this component disabled? */
|
||||
disabled: boolean
|
||||
/**The type of this component. */
|
||||
type: "button"|"dropdown"
|
||||
}
|
||||
|
||||
export interface ODTranscriptButtonComponentData extends ODTranscriptComponentData {
|
||||
/**The type of this component. */
|
||||
type: "button"
|
||||
/**The label of this button. */
|
||||
label: string|null,
|
||||
/**The emoji of this button. */
|
||||
emoji: ODTranscriptEmojiData|null,
|
||||
/**The color of this button. */
|
||||
color: ODValidButtonColor,
|
||||
/**Is this button a url or button? */
|
||||
mode: "url"|"button",
|
||||
/**The url of this button. */
|
||||
url: string|null
|
||||
}
|
||||
|
||||
export interface ODTranscriptDropdownComponentData extends ODTranscriptComponentData {
|
||||
/**The type of this component. */
|
||||
type: "dropdown"
|
||||
/**The placeholder in this dropdown. */
|
||||
placeholder: string|null,
|
||||
/**All options available in this dropdown. */
|
||||
options: ODTranscriptDropdownComponentOptionData[]
|
||||
}
|
||||
|
||||
export interface ODTranscriptDropdownComponentOptionData {
|
||||
/**The custom id of this dropdown option. */
|
||||
id: string,
|
||||
/**The label of this dropdown option. */
|
||||
label: string|null,
|
||||
/**The description of this dropdown option. */
|
||||
description: string|null,
|
||||
/**The emoji of this dropdown option. */
|
||||
emoji: ODTranscriptEmojiData|null
|
||||
}
|
||||
|
||||
export interface ODTranscriptReplyData {
|
||||
/**The type of reply. */
|
||||
type: "interaction"|"message",
|
||||
}
|
||||
|
||||
export interface ODTranscriptInteractionReplyData extends ODTranscriptReplyData {
|
||||
/**The type of reply. */
|
||||
type: "interaction",
|
||||
/**The bot used for this interaction. */
|
||||
user: ODTranscriptUserData,
|
||||
/**The name of the slash command used. */
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface ODTranscriptMessageReplyData extends ODTranscriptReplyData {
|
||||
/**The type of reply. */
|
||||
type: "message",
|
||||
/**The server this message originated from. */
|
||||
guild: string,
|
||||
/**The channel this message originated from. */
|
||||
channel: string,
|
||||
/**The id of this message. */
|
||||
id: string,
|
||||
/**The author of this message. */
|
||||
user: ODTranscriptUserData,
|
||||
/**The content of this message. */
|
||||
content: string|null
|
||||
}
|
||||
|
||||
export interface ODTranscriptEmojiData {
|
||||
/**The id of this emoji. */
|
||||
id: string|null,
|
||||
/**The name of this emoji. */
|
||||
name: string|null,
|
||||
/**Is this emoji animated? */
|
||||
animated: boolean,
|
||||
/**Is this a custom emoji (img/gif)? */
|
||||
custom: boolean,
|
||||
/**The url or emoji. */
|
||||
emoji: string,
|
||||
}
|
||||
|
||||
export interface ODTranscriptReactionData extends ODTranscriptEmojiData {
|
||||
/**The amount of emojis for this reaction. */
|
||||
amount: number,
|
||||
/**Is this a super (nitro) reaction? */
|
||||
super: boolean
|
||||
}
|
||||
|
||||
export interface ODTranscriptHtmlV2Data {
|
||||
version:"2",
|
||||
otversion:string,
|
||||
language:string,
|
||||
style:{
|
||||
background:{
|
||||
enableCustomBackground:boolean,
|
||||
backgroundModus:"color"|"image",
|
||||
backgroundData:string
|
||||
},
|
||||
header:{
|
||||
enableCustomHeader:boolean,
|
||||
decoColor:string,
|
||||
backgroundColor:string,
|
||||
textColor:string
|
||||
},
|
||||
stats:{
|
||||
enableCustomStats:boolean,
|
||||
backgroundColor:string,
|
||||
keyTextColor:string,
|
||||
valueTextColor:string,
|
||||
hideBackgroundColor:string,
|
||||
hideTextColor:string
|
||||
},
|
||||
favicon:{
|
||||
enableCustomFavicon:boolean,
|
||||
imageUrl:string
|
||||
}
|
||||
},
|
||||
bot:{
|
||||
name:string,
|
||||
id:string,
|
||||
pfp:string
|
||||
},
|
||||
ticket:{
|
||||
name:string,
|
||||
id:string,
|
||||
guildname:string,
|
||||
guildid:string,
|
||||
guildinvite:string|false,
|
||||
|
||||
creatorname:string,
|
||||
creatorid:string,
|
||||
|
||||
closedbyname:string,
|
||||
closedbyid:string,
|
||||
|
||||
claimedname:string,
|
||||
claimedid:string,
|
||||
|
||||
creatorpfp:string,
|
||||
closedbypfp:string,
|
||||
claimedpfp:string,
|
||||
|
||||
closedtime:number,
|
||||
openedtime:number,
|
||||
|
||||
components:{
|
||||
messages:number,
|
||||
files:number,
|
||||
embeds:number,
|
||||
reactions:number,
|
||||
interactions:{
|
||||
dropdowns:number,
|
||||
buttons:number,
|
||||
total:number
|
||||
},
|
||||
attachments:{
|
||||
invites:number,
|
||||
images:number,
|
||||
gifs:number,
|
||||
stickers:number
|
||||
}
|
||||
},
|
||||
roleColors:{id:string,color:string}[]
|
||||
},
|
||||
messages:{
|
||||
author:{
|
||||
name:string,
|
||||
id:string,
|
||||
color:string,
|
||||
pfp:string,
|
||||
bot:boolean,
|
||||
verifiedBot:boolean,
|
||||
system:boolean
|
||||
},
|
||||
content:string|false,
|
||||
timestamp:number,
|
||||
type:"normal",
|
||||
important:boolean,
|
||||
edited:boolean,
|
||||
embeds:{
|
||||
title:string|false,
|
||||
description:string|false,
|
||||
authorimg:string|false,
|
||||
authortext:string|false,
|
||||
footerimg:string|false,
|
||||
footertext:string|false,
|
||||
color:string,
|
||||
image:string|false,
|
||||
thumbnail:string|false,
|
||||
url:string|false,
|
||||
fields:{name:string,value:string,inline:boolean}[]
|
||||
}[],
|
||||
attachments:(
|
||||
{
|
||||
type:"FILE",
|
||||
name:string,
|
||||
fileType:string,
|
||||
size:string,
|
||||
url:string
|
||||
}|{
|
||||
type:"FILE:image",
|
||||
name:string,
|
||||
fileType:string,
|
||||
size:string,
|
||||
url:string
|
||||
}|{
|
||||
type:"URL:invite",
|
||||
guildId:string,
|
||||
guildName:string,
|
||||
guildSize:Number,
|
||||
guildOnline:Number,
|
||||
image:string,
|
||||
url:string
|
||||
}|{
|
||||
type:"URL:gif",
|
||||
url:string
|
||||
}|{
|
||||
type:"BUILDIN:sticker",
|
||||
name:string,
|
||||
url:string
|
||||
}
|
||||
)[],
|
||||
components:(
|
||||
{
|
||||
type:"buttons",
|
||||
buttons:{
|
||||
type:"interaction"|"url",
|
||||
label:string|false,
|
||||
icon:string|false,
|
||||
color:"gray"|"green"|"red"|"blue",
|
||||
id:string|false,
|
||||
url:string|false,
|
||||
disabled:boolean
|
||||
}[]
|
||||
}|{
|
||||
type:"dropdown",
|
||||
placeholder:string|false,
|
||||
options:{
|
||||
label:string|false,
|
||||
icon:string|false,
|
||||
description:string|false,
|
||||
id:string|false
|
||||
}[]
|
||||
}|{
|
||||
type:"reactions",
|
||||
reactions:{
|
||||
amount:number,
|
||||
emoji:string,
|
||||
type:"svg"|"image"|"gif"
|
||||
}[]
|
||||
}
|
||||
)[],
|
||||
reply:{
|
||||
type:"reply"|"command"|false,
|
||||
user?:{
|
||||
name:string,
|
||||
id:string,
|
||||
color:string,
|
||||
pfp:string,
|
||||
bot:Boolean,
|
||||
verifiedBot:Boolean,
|
||||
system:Boolean
|
||||
},
|
||||
replyOptions?:{
|
||||
content:string,
|
||||
messageId:string,
|
||||
channelId:string,
|
||||
guildId:string
|
||||
},
|
||||
commandOptions?:{
|
||||
interactionName:string,
|
||||
interactionId:string
|
||||
}
|
||||
}
|
||||
}[],
|
||||
premium:{
|
||||
enabled:boolean,
|
||||
premiumToken:string,
|
||||
customCredits:{
|
||||
enable:boolean,
|
||||
replaceText:string,
|
||||
replaceURL:string,
|
||||
enableReportBug:boolean
|
||||
},
|
||||
customHeaderUrl:{
|
||||
enabled:boolean,
|
||||
url:string,
|
||||
text:string
|
||||
},
|
||||
customTranscriptUrl:{
|
||||
enabled:boolean,
|
||||
name:string
|
||||
},
|
||||
customFavicon:{
|
||||
enabled:boolean,
|
||||
image:string
|
||||
},
|
||||
additionalFlags:string[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface ODTranscriptHtmlV2Response {
|
||||
status:"success"|"error",
|
||||
id:string,
|
||||
time:number,
|
||||
estimated:{
|
||||
lastdump: number,
|
||||
processtime:number,
|
||||
waittime:number
|
||||
},
|
||||
transcriptstatus:{
|
||||
value:2,
|
||||
data:{
|
||||
fetchedmsgs:true,
|
||||
uploaded:true,
|
||||
inqueue:true,
|
||||
processed:false,
|
||||
waiting:false,
|
||||
available:false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user