mirror of
https://github.com/ReVanced/revanced-bots.git
synced 2026-01-18 08:43:57 +00:00
feat(bots/discord): add a better way to manage databases
This commit is contained in:
@@ -1,72 +1,97 @@
|
|||||||
import { Database, type SQLQueryBindings, type Statement } from 'bun:sqlite'
|
import { Database } from 'bun:sqlite'
|
||||||
|
|
||||||
export class LabeledResponseDatabase {
|
type BasicSQLBindings = string | number | null
|
||||||
readonly tableName = 'labeledResponses'
|
|
||||||
readonly tableStruct = `
|
|
||||||
reply TEXT PRIMARY KEY NOT NULL,
|
|
||||||
channel TEXT NOT NULL,
|
|
||||||
guild TEXT NOT NULL,
|
|
||||||
referenceMessage TEXT NOT NULL,
|
|
||||||
label TEXT NOT NULL,
|
|
||||||
text TEXT NOT NULL,
|
|
||||||
correctedBy TEXT,
|
|
||||||
CHECK (
|
|
||||||
typeof("text") = 'text' AND
|
|
||||||
length("text") > 0 AND
|
|
||||||
length("text") <= 280
|
|
||||||
)
|
|
||||||
`
|
|
||||||
|
|
||||||
#statements: {
|
export class BasicDatabase<T extends Record<string, BasicSQLBindings>> {
|
||||||
save: Statement
|
#db: Database
|
||||||
edit: Statement
|
#table: string
|
||||||
get: Statement<LabeledResponse, SQLQueryBindings[]>
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor(file: string, struct: string, tableName = 'data') {
|
||||||
// TODO: put in config
|
const db = new Database(file, {
|
||||||
const db = new Database('responses.db', {
|
|
||||||
create: true,
|
create: true,
|
||||||
readwrite: true,
|
readwrite: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
this.#db = db
|
||||||
${this.tableStruct}
|
this.#table = tableName
|
||||||
);`)
|
|
||||||
|
|
||||||
this.#statements = {
|
db.run(`CREATE TABLE IF NOT EXISTS ${tableName} (${struct});`)
|
||||||
save: db.prepare(
|
}
|
||||||
`INSERT INTO ${this.tableName} VALUES ($reply, $channel, $guild, $reference, $label, $text, NULL);`,
|
|
||||||
),
|
run(statement: string) {
|
||||||
edit: db.prepare(
|
this.#db.run(statement)
|
||||||
`UPDATE ${this.tableName} SET label = $label, correctedBy = $correctedBy WHERE reply = $reply`,
|
}
|
||||||
),
|
|
||||||
get: db.prepare(`SELECT * FROM ${this.tableName} WHERE reply = $reply`),
|
prepare(statement: string) {
|
||||||
} as const
|
return this.#db.prepare<T, BasicSQLBindings[]>(statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
query(statement: string) {
|
||||||
|
return this.#db.query<T, BasicSQLBindings[]>(statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(...values: BasicSQLBindings[]) {
|
||||||
|
this.run(`INSERT INTO ${this.#table} VALUES (${values.map(this.#encodeValue).join(', ')});`)
|
||||||
|
}
|
||||||
|
|
||||||
|
update(what: Partial<T>, where: string) {
|
||||||
|
const set = Object.entries(what)
|
||||||
|
.map(([key, value]) => `${key} = ${this.#encodeValue(value)}`)
|
||||||
|
.join(', ')
|
||||||
|
|
||||||
|
this.run(`UPDATE ${this.#table} SET ${set} WHERE ${where};`)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(where: string) {
|
||||||
|
this.run(`DELETE FROM ${this.#table} WHERE ${where};`)
|
||||||
|
}
|
||||||
|
|
||||||
|
select(columns: string[] | string, where: string) {
|
||||||
|
const realColumns = Array.isArray(columns) ? columns.join(', ') : columns
|
||||||
|
return this.query(`SELECT ${realColumns} FROM ${this.#table} WHERE ${where};`).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
#encodeValue(value: unknown) {
|
||||||
|
if (typeof value === 'string') return `'${value}'`
|
||||||
|
if (typeof value === 'number') return value
|
||||||
|
if (typeof value === 'boolean') return value ? 1 : 0
|
||||||
|
if (value === null) return 'NULL'
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LabeledResponseDatabase {
|
||||||
|
#db: BasicDatabase<LabeledResponse>
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#db = new BasicDatabase<LabeledResponse>(
|
||||||
|
'responses.db',
|
||||||
|
`reply TEXT PRIMARY KEY NOT NULL,
|
||||||
|
channel TEXT NOT NULL,
|
||||||
|
guild TEXT NOT NULL,
|
||||||
|
referenceMessage TEXT KEY NOT NULL,
|
||||||
|
label TEXT NOT NULL,
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
correctedBy TEXT,
|
||||||
|
CHECK (
|
||||||
|
typeof("text") = 'text' AND
|
||||||
|
length("text") > 0 AND
|
||||||
|
length("text") <= 280
|
||||||
|
)`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
save({ reply, channel, guild, referenceMessage, label, text }: Omit<LabeledResponse, 'correctedBy'>) {
|
save({ reply, channel, guild, referenceMessage, label, text }: Omit<LabeledResponse, 'correctedBy'>) {
|
||||||
const actualText = text.slice(0, 280)
|
const actualText = text.slice(0, 280)
|
||||||
this.#statements.save.run({
|
this.#db.insert(reply, channel, guild, referenceMessage, label, actualText, null)
|
||||||
$reply: reply,
|
|
||||||
$channel: channel,
|
|
||||||
$guild: guild,
|
|
||||||
$reference: referenceMessage,
|
|
||||||
$label: label,
|
|
||||||
$text: actualText,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get(reply: string) {
|
get(reply: string) {
|
||||||
return this.#statements.get.get({ $reply: reply })
|
return this.#db.select('*', `reply = ${reply}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
edit(reply: string, { label, correctedBy }: Pick<LabeledResponse, 'label' | 'correctedBy'>) {
|
edit(reply: string, { label, correctedBy }: Pick<LabeledResponse, 'label' | 'correctedBy'>) {
|
||||||
this.#statements.edit.run({
|
this.#db.update({ label, correctedBy }, `reply = ${reply}`)
|
||||||
$reply: reply,
|
|
||||||
$label: label,
|
|
||||||
$correctedBy: correctedBy,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user