Add CLI setup and QQ webhook integration

This commit is contained in:
zenord
2026-08-15 16:02:05 +08:00
parent 00325efb50
commit d79264b11a
22 changed files with 1389 additions and 99 deletions
+73
View File
@@ -0,0 +1,73 @@
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
export interface PromptSession {
ask(question: string, defaultValue?: string): Promise<string>;
askBoolean(question: string, defaultValue?: boolean): Promise<boolean>;
askList(question: string, defaultValues?: string[]): Promise<string[]>;
choose<T>(question: string, choices: Choice<T>[], defaultIndex?: number): Promise<T>;
close(): void;
}
export interface Choice<T> {
label: string;
value: T;
hint?: string;
}
export function createPromptSession(): PromptSession {
const rl = readline.createInterface({ input, output });
return {
ask: (question, defaultValue) => ask(rl, question, defaultValue),
askBoolean: (question, defaultValue) => askBoolean(rl, question, defaultValue),
askList: (question, defaultValues) => askList(rl, question, defaultValues),
choose: (question, choices, defaultIndex) => choose(rl, question, choices, defaultIndex),
close: () => rl.close()
};
}
export async function ask(rl: readline.Interface, question: string, defaultValue?: string): Promise<string> {
const suffix = defaultValue !== undefined && defaultValue !== "" ? ` [${defaultValue}]` : "";
const answer = (await rl.question(`${question}${suffix}: `)).trim();
return answer || defaultValue || "";
}
export async function askBoolean(rl: readline.Interface, question: string, defaultValue = false): Promise<boolean> {
const suffix = defaultValue ? " [Y/n]" : " [y/N]";
for (;;) {
const answer = (await rl.question(`${question}${suffix}: `)).trim().toLowerCase();
if (!answer) return defaultValue;
if (["y", "yes"].includes(answer)) return true;
if (["n", "no"].includes(answer)) return false;
console.log("Please answer yes or no.");
}
}
export async function askList(rl: readline.Interface, question: string, defaultValues: string[] = []): Promise<string[]> {
const answer = await ask(rl, question, defaultValues.join(","));
return answer.split(",").map((value) => value.trim()).filter(Boolean);
}
export async function choose<T>(
rl: readline.Interface,
question: string,
choices: Choice<T>[],
defaultIndex = 0
): Promise<T> {
if (choices.length === 0) throw new Error("No choices available");
console.log(question);
choices.forEach((choice, index) => {
const marker = index === defaultIndex ? "*" : " ";
const hint = choice.hint ? ` - ${choice.hint}` : "";
console.log(` ${marker} ${index + 1}) ${choice.label}${hint}`);
});
for (;;) {
const answer = (await rl.question(`Choose 1-${choices.length} [${defaultIndex + 1}]: `)).trim();
if (!answer) return choices[defaultIndex]?.value ?? choices[0].value;
const index = Number(answer) - 1;
if (Number.isInteger(index) && choices[index]) return choices[index].value;
console.log(`Please enter a number from 1 to ${choices.length}.`);
}
}