Skip to content

Commit

Permalink
feat: enhance configuration handling and add support for custom confi…
Browse files Browse the repository at this point in the history
…g files
  • Loading branch information
ptsgrn committed Jan 8, 2025
1 parent d1027ec commit cfc7b25
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 27 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ node_modules/
node_modules
logs/
dist/
config.toml
configstore/
.*.sw*
.env
Expand All @@ -15,3 +14,7 @@ user-password.py
apicache/
*.lwp
*/throttle.ctrl


config.toml
config-*.toml
Binary file modified bun.lockb
Binary file not shown.
3 changes: 2 additions & 1 deletion core/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class Bot extends ServiceBase {
id: string;
name: string;
description: string;
frequency?: string;
}

public job?: Cron
Expand Down Expand Up @@ -87,7 +88,7 @@ export class Bot extends ServiceBase {

async schedule(options: {
pattern: string | Date;
options: CronOptions;
options?: CronOptions;
}) {
this.job = new Cron(options.pattern, {
name: this.info.id,
Expand Down
23 changes: 20 additions & 3 deletions core/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { z } from 'zod'
import { parseArgs } from "util";
import { join } from "path"

if (!Bun.file('../config.toml').exists()) {
throw new Error('Please create config.toml')
const { values, positionals } = parseArgs({
args: Bun.argv,
options: {
config: {
type: 'string',
default: "config.toml",
}
},
strict: false,
});

let configFile = join(import.meta.dir, "../", typeof values.config === 'string' ? values.config : "config.toml")

if (!Bun.file(configFile).exists()) {
throw new Error(`Config file not found: ${configFile}`)
}

export const config = z.object({
Expand Down Expand Up @@ -43,4 +58,6 @@ export const config = z.object({
webhook: z.string().optional(),
}),
}),
}).parse(await import('../config.toml'))
}).parse(await import(configFile))

process.env.TZ = config.bot.timezone
47 changes: 40 additions & 7 deletions core/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { ServiceBase } from './base'

class ScriptRunner extends ServiceBase {
private cli = new Command()
private config = {
logLevel: "info",
configFile: "config.toml"
}

async scriptModule(scriptName: string) {
if (!scriptName) {
Expand Down Expand Up @@ -38,29 +42,54 @@ class ScriptRunner extends ServiceBase {
.name('patsabot')
.version(version)
.enablePositionalOptions()
.configureHelp({
showGlobalOptions: true,
sortOptions: true,
sortSubcommands: true,
})

this.cli
.command('run')
.description('Run a script')
.argument('<script>', 'Script name')
.argument('[args...]', 'Script arguments')
.passThroughOptions()
.action(async (scriptName, options) => {
.action(async (scriptName) => {
scriptName = scriptName.replace(/^scripts\//, "").replace(/\.ts$/, "")
const scriptModule = await this.scriptModule(scriptName)
scriptModule.cli
.name('run ' + scriptName)
.description(scriptModule.scriptDescription)
// Global cli options
scriptModule.cli.option("-v, --verbose", "Show debug logging")

// Ensure that we pass the global options correctly to the script
scriptModule.cli.addOption(new Option("-l, --log-level <level>", "Log level")
.choices(['debug', 'info', 'warn', 'error'])
.default(this.config.logLevel)
)
scriptModule.cli.addOption(new Option("-c, --config <file>", "Config file")
.default(this.config.configFile)
)

// Handle global options within the script
scriptModule.cli.opts = () => ({
logLevel: this.config.logLevel,
configFile: this.config.configFile,
...scriptModule.cli.opts()
})

// Pass the correct arguments to parse
scriptModule.cli.parse(process.argv.slice(2))

scriptModule.log.defaultMeta = {
script: scriptName,
rid: createId(),
}
// @ts-ignore
if (scriptModule.cli.opts().verbose) {

// Apply global log level
if (this.config.logLevel === 'debug') {
scriptModule.log.level = 'debug'
}

try {
await scriptModule.beforeRun()
await scriptModule.run()
Expand All @@ -69,6 +98,7 @@ class ScriptRunner extends ServiceBase {
}
await scriptModule.afterRun()
});

this.cli
.command('schedule <script>')
.description('Schedule a script for cron')
Expand Down Expand Up @@ -100,6 +130,7 @@ class ScriptRunner extends ServiceBase {
}
})
})

this.cli
.command("replica-tunnel")
.description("Set up an SSH tunnel to Wikimedia Replica with specific wiki database")
Expand All @@ -111,8 +142,10 @@ class ScriptRunner extends ServiceBase {
.action(async (wiki, options) => {
Replica.createReplicaTunnel(wiki, options.cluster, options.port)
})
this.cli.parse(process.argv)

// Ensure that we parse the correct arguments from process.argv
this.cli.parse()
}
}

new ScriptRunner().run();
new ScriptRunner().run();
25 changes: 16 additions & 9 deletions core/web.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import { Elysia } from "elysia"
import { $ } from "bun"
import { Elysia, t } from "elysia"
import { jwt } from '@elysiajs/jwt'
import { swagger } from '@elysiajs/swagger'
import { $, ShellPromise, type Shell } from "bun"
import { config } from '@core/config'

const now = () => new Date().toISOString()

export const app = new Elysia()
.get("/deploy/:token", async function* ({ params }) {
const { token } = params
if (!token) return { status: 401, body: "Unauthorized" }
if (token !== config.toolforge.deploykey) return { status: 403, body: "Forbidden" }
.use(swagger())
.use(
jwt({
name: "jwt",
secret: config.toolforge.deploykey
})
)
.get("/deploy", async function* ({ jwt, query }) {
const data = await jwt.verify(query.token)
const commands = [
[$`git fetch`, "git fetch"],
[$`git reset --hard origin/main`, "git reset --hard origin/main"],
[$`git pull`, "git pull"],
[$`bun i`, "bun i"],
]
] as [ShellPromise, string][]

// yield simple html response prepend for simple style
yield `[${now()}] Starting deployment...\n`
const start = Date.now()
for (const command of commands) {
yield `[${now()}] Running: ${command[Symbol]}\n`
for (const [command, rawCommand] of commands) {
yield `[${now()}] Running: ${rawCommand}\n`
for await (const line of command.lines()) {
console.log(`[${now()}]`, line.trim())
yield `[${now()}] ${line.trim()}\n`
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"module": "core/run.ts",
"dependencies": {
"@commander-js/extra-typings": "^13.0.0",
"@elysiajs/jwt": "^1.2.0",
"@elysiajs/swagger": "^1.2.0",
"@paralleldrive/cuid2": "^2.2.2",
"chalk": "^5.4.1",
"commander": "^13.0.0",
Expand Down
3 changes: 2 additions & 1 deletion scripts/afccat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Bot } from '@core/bot';
import { Command } from '@commander-js/extra-typings';

export default class Afccat extends Bot {
info = {
public info: Bot['info'] = {
id: "afccat",
name: "AfC Category Creator",
description: "Create categories for AfC submissions",
frequency: "@daily",
}

cli = new Command()
Expand Down
30 changes: 29 additions & 1 deletion scripts/database-reports.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Command, Option } from '@commander-js/extra-typings';
import { Bot } from '@core/bot';
import { readdir } from "node:fs/promises";
import { join } from 'node:path';
import chalk from 'chalk';

export class DatabaseReportBot extends Bot {
Expand Down Expand Up @@ -84,6 +86,32 @@ export class DatabaseReportBot extends Bot {
formatRow(row: any, index: number, rows?: any[]): string[] {
return []
}

async schedule() {
await super.schedule({
pattern: this.reportFrequency,
options: {
name: this.id
}
})
}
}

export default class RunScheduleDatabaseReport extends Bot { }
export default class RunScheduleDatabaseReport extends Bot {
allReports: Record<string, DatabaseReportBot> = {}

async run() {
this.log.info('Running all scheduled database reports')
const files = await readdir(join(import.meta.dir, '../scripts/database-reports'))
for (const file of files) {
if (!file.endsWith('.ts')) {
continue
}
const module = await import(`@scripts/database-reports/${file}`)
const report = new module.default() as DatabaseReportBot
this.log.info(`Scheduled ${report.id} (${report.reportFrequency})`)
this.allReports[report.id] = report
await report.schedule()
}
}
}
2 changes: 1 addition & 1 deletion scripts/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { version } from '../package.json'
import moment from 'moment';

export default class Login extends Bot {
info = {
info: Bot['info'] = {
id: "login",
name: "Login",
description: "Get site and user info"
Expand Down
2 changes: 1 addition & 1 deletion scripts/topedits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface UserEdit {
}

export default class TopEdits extends Bot {
info = {
info: Bot['info'] = {
id: "topedits",
name: "TopEdits",
description: "อัปเดตตาราง[[วิกิพีเดีย:รายชื่อชาววิกิพีเดียที่แก้ไขมากที่สุด 500 อันดับ]] และ[[วิกิพีเดีย:รายชื่อชาววิกิพีเดียที่แก้ไขมากที่สุด 500 อันดับ (รวมบอต)]]",
Expand Down
2 changes: 1 addition & 1 deletion scripts/update-status.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Bot } from '@core/bot';

export default class UpdateStatusBot extends Bot {
public info: { id: string; name: string; description: string; } = {
public info: Bot['info'] = {
id: 'updatestatus',
description: 'อัปเดตสถานะบอต',
name: 'updatestatus',
Expand Down
2 changes: 1 addition & 1 deletion scripts/update-tasks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Bot } from '@core/bot';

export default class UpdateBotTaskBot extends Bot {
public info: { id: string; name: string; description: string; } = {
public info: Bot['info'] = {
id: 'update-tasks',
description: 'อัปเดตงานบอต',
name: 'update-tasks',
Expand Down

0 comments on commit cfc7b25

Please sign in to comment.