From 020435aca55a32b7a735dd5fda52997052f6419c Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Sun, 3 Sep 2023 10:37:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=88=B6app=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.example.yml | 5 +- src/index.ts | 116 +++-------------------------------- src/lib/app.ts | 146 ++++++++++++++++++++++++++++++++++++++++++++ src/types/app.ts | 32 ++++++++-- src/types/config.ts | 4 ++ src/util/fs.ts | 21 +++++-- 6 files changed, 207 insertions(+), 117 deletions(-) create mode 100644 src/lib/app.ts diff --git a/config.example.yml b/config.example.yml index 33e0612..07ff3ea 100644 --- a/config.example.yml +++ b/config.example.yml @@ -21,4 +21,7 @@ bot_sysadmin_id: bot_name: "琳酱" # TG风格: "/" QQ风格: "." -command_style: "/" \ No newline at end of file +command_style: "/" + +# 轮询超时消息不处理时间(秒) +outdate_seconds: 300 \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 351a8ff..e0a78b2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,120 +1,24 @@ // 读取配置和模块 import fs from 'fs'; -// import Promise from 'promise'; -import yaml from 'js-yaml'; -// import readline from 'readline' -import std from './lib/std.js'; -import fatalError from './lib/fatal_error.js'; -import db from './lib/db.js'; import { botOnOff, commandParser, getCommands, getGlobalMessageHandles, getReplyHandles, - registCommand, - registGlobalMessageHandle, - registReplyHandle -} from './lib/command.js'; -import { App } from './types/app.js'; -import { YamlConfig } from './types/config.js'; -import { exit } from 'process'; -import TelegramBot, { BotCommand } from 'node-telegram-bot-api'; -import { CreateBot } from './types/bridge.js'; -import { reverseReadFileIfExists } from './util/fs.js'; +} from '@/lib/command.js'; +import { BotCommand } from 'node-telegram-bot-api'; +import { Application } from '@/lib/app.js'; -// 创建app -const app: App = { - db, - std, - registCommand, - registGlobalMessageHandle, - registReplyHandle, - - _config: undefined, - - get config(): YamlConfig { - if (this._config) { - return this._config; - } else { - throw '未找到config'; - } - }, - get bot(): TelegramBot { - if (this._bot) { - return this._bot; - } else { - throw '无bot被初始化'; - } - }, - - - get version() { - return JSON.parse(fs.readFileSync('package.json', { encoding: 'utf-8' })).version; - }, - get configExample() { - return fs.readFileSync('config.example.yml', { encoding: 'utf-8' }).toString(); - }, - - async initConfig() { - // 读取配置文件 - const configContent = reverseReadFileIfExists('config.yml'); - if (!configContent) { - await std.questionSync( - '未找到配置文件。是否使用默认配置文件config.examle.yml覆盖config.yml (yes)', - (answer) => { - if (answer === 'yes') { - try { - app.setConfig(app.configExample); - console.log('写入完成,本程序自动退出。'); - } catch (err) { - fatalError(err, '写入配置文件失败。'); - process.exit(-1); - } - } else { - console.log('请正确配置config.yml. 本程序将自动退出.'); - } - process.exit(0); - }); - return; - } - this._config = yaml.load(configContent) as YamlConfig; - }, - writeConfig(config_data: string) { - fs.writeFileSync('./config.yml', config_data); - }, -}; - -if (process.argv[2] === '--version') { - console.log(`Linquebot ${app.version}`); - console.log('本程序具有超级琳力'); - process.exit(0); -} - -// 初始化配置 -await app.initConfig(); - -console.log(app.config); -console.log('---------------'); - -if (!app.config) { exit(-1); } - -// 创建bot -if (app.config.platform.settings[app.config.platform.enabled] === undefined) { - console.log(`未找到${app.config.platform.enabled}的配置,请检查config.yml`); - exit(-1); -} -console.log(`Laauching bot at Platform[${app.config.platform.enabled}]...`); -const createBot = (await import(`./bridges/${app.config.platform.enabled}/index.js`)).createBot as CreateBot; - -app._bot = createBot(app.config.platform.settings[app.config.platform.enabled]); - -if (!app._bot) { - console.log('bot创建失败,请检查bridge是否正确。'); - exit(-1); -} +/** 创建app */ +const app = new Application(); +/** 初始化app */ +await app.init(); app.bot.on('message', (msg) => { + if (Number(new Date)/1000 - msg.date > app.config.outdate_seconds) { + console.log('古老消息被忽略。详见 config.outdate_seconds'); + } console.log(msg); commandParser(app, msg); if (botOnOff(app, msg)) { diff --git a/src/lib/app.ts b/src/lib/app.ts new file mode 100644 index 0000000..51a99ae --- /dev/null +++ b/src/lib/app.ts @@ -0,0 +1,146 @@ +// 读取配置和模块 +import fs from 'fs'; +// import Promise from 'promise'; +import yaml from 'js-yaml'; +// import readline from 'readline' +import std from '@/lib/std.js'; +import fatalError from '@/lib/fatal_error.js'; +import db from '@/lib/db.js'; +import { + registCommand, + registGlobalMessageHandle, + registReplyHandle +} from '@/lib/command.js'; +import { App } from '@/types/app.js'; +import { YamlConfig } from '@/types/config.js'; +import process from 'process'; +import TelegramBot from 'node-telegram-bot-api'; +import { CreateBot } from '@/types/bridge.js'; +import { reverseReadFileIfExists } from '@/util/fs.js'; +import chalk from 'chalk'; + +/** + * `@/types/app.ts` 的 App 实现 + */ +export class Application implements App { + private _bot?: TelegramBot; + private _config?: YamlConfig; + private _configExample: string; + private _version: string; + + constructor() { + const packageJSON = reverseReadFileIfExists('package.json'); + if (packageJSON) { + this._version = JSON.parse(packageJSON).version; + } else { + throw 'no version found'; + } + try { + this._configExample = reverseReadFileIfExists('config.example.yml') || ''; + if (!this._configExample) { + throw 'no config example found'; + } + } catch(err) { + this._configExample = ''; + console.warn(chalk.yellow('No config.example.yml found. This program will not be able to provide a config example.')); + } + } + + async init(): Promise { + console.log('Reading config.yml ...'); + await this.initConfig(); + console.log(this.config); + console.log('---------------'); + console.log('Initializing bot ...'); + if (this.config.platform.settings[this.config.platform.enabled] === undefined) { + console.log(`未找到平台${this.config.platform.enabled}的配置,请检查config.yml`); + process.exit(-1); + } + console.log(`Launching bot at Platform[${this.config.platform.enabled}]...`); + + const createBot = (await import(`../bridges/${this.config.platform.enabled}/index.js`)).createBot as CreateBot; + + try { + this._bot = createBot(this.config.platform.settings[this.config.platform.enabled]); + if (!this._bot) { + throw 'bot is undefined'; + } + } catch (err) { + console.log(`-------${chalk.red('[ERROR]')}--------`); + console.log('Launching bot failed. Please check the error information below:'); + console.error(err); + process.exit(-1); + } + console.log('---------------'); + } + + get config(): YamlConfig { + if (this._config) { + return this._config; + } else { + throw '未找到config'; + } + } + + get bot(): TelegramBot { + if (this._bot) { + return this._bot; + } else { + throw '无bot被初始化'; + } + } + + get db() { + return db; + } + + get std() { + return std; + } + + get version(): string { + return this._version; + } + get configExample() { + return this._configExample; + } + + private async initConfig() { + // 读取配置文件 + const configContent = reverseReadFileIfExists('config.yml'); + if (!configContent) { + await std.questionSync( + '未找到配置文件。是否使用默认配置文件config.examle.yml覆盖config.yml (yes)', + (answer) => { + if (answer === 'yes') { + try { + this.writeConfig(this.configExample); + console.log('写入完成,本程序自动退出。'); + } catch (err) { + fatalError(err, '写入配置文件失败。'); + process.exit(-1); + } + } else { + console.log('请正确配置config.yml. 本程序将自动退出.'); + } + process.exit(0); + }); + return; + } + this._config = yaml.load(configContent) as YamlConfig; + }; + + private writeConfig(config_data: string) { + fs.writeFileSync('./config.yml', config_data); + }; + + get registCommand() { + return registCommand; + }; + get registGlobalMessageHandle() { + return registGlobalMessageHandle; + } + get registReplyHandle() { + return registReplyHandle; + } +} \ No newline at end of file diff --git a/src/types/app.ts b/src/types/app.ts index 9dbf293..0d4186e 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -1,12 +1,9 @@ import TelegramBot from 'node-telegram-bot-api'; import { YamlConfig } from './config.js'; import { registCommand, registGlobalMessageHandle, registReplyHandle } from '@/lib/command.js'; +import { Std } from '@/lib/std.js'; export interface App { - /** @hidden */ - _config?: YamlConfig; - /** @hidden */ - _bot?: TelegramBot; /** * 用户在 config.yml 的配置 */ @@ -15,11 +12,34 @@ export interface App { * Telegram bot */ get bot(): TelegramBot; + /** + * 初始化app。只应执行一次。 + */ + init(): Promise; + /** + * 数据库获取 + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get db(): any; + + /** + * 获得标准输入输出交互器 + */ + get std(): Std; + + /** + * 获取版本号 + */ + get version(): string; + + /** + * 获取配置示例 + */ + get configExample(): string; registCommand: typeof registCommand; registGlobalMessageHandle: typeof registGlobalMessageHandle; registReplyHandle: typeof registReplyHandle; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; + // [key: string]: any; } \ No newline at end of file diff --git a/src/types/config.ts b/src/types/config.ts index 9e3b394..7c2f824 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -19,6 +19,10 @@ export interface YamlConfig { * TG风格: "/" QQ风格: "." */ command_style: '/' | '.'; + /** + * 由于一场关机导致轮询时爬取古老消息,记作超时不处理的消息最短时间(秒) + */ + outdate_seconds: number; } export interface TelegramPlatform { diff --git a/src/util/fs.ts b/src/util/fs.ts index 7260602..88c4987 100644 --- a/src/util/fs.ts +++ b/src/util/fs.ts @@ -2,11 +2,17 @@ import fs from 'fs'; /** * 读取文件,如果文件不存在则返回null + * Default use { encoding: 'utf-8' } as `options` + * @param fileName filename or file descriptor + * @param options If the encoding option is specified then this function returns a string. Otherwise it returns a buffer. */ -export const readFileIfExists = (fileName: string) => { +export const readFileIfExists = ( + fileName: string, + options: { encoding: BufferEncoding; flag?: string | undefined; } | undefined = { encoding: 'utf-8' } +) => { const fileExist = fs.existsSync(fileName); if (!fileExist) {return null;} - return fs.readFileSync(fileName, { encoding: 'utf-8' }); + return fs.readFileSync(fileName, options); }; const defaultDirs = [ @@ -18,10 +24,17 @@ const defaultDirs = [ /** * 从里到外读取文件,如果文件不存在则返回null + * Default use { encoding: 'utf-8' } as `options` + * @param fileName filename or file descriptor + * @param options If the encoding option is specified then this function returns a string. Otherwise it returns a buffer. */ -export const reverseReadFileIfExists = (fileName: string, reverseDirs: string[] = defaultDirs) => { +export const reverseReadFileIfExists = ( + fileName: string, + reverseDirs: string[] = defaultDirs, + options: { encoding: BufferEncoding; flag?: string | undefined; } | undefined = { encoding: 'utf-8' } +) => { for (const dir of reverseDirs) { - const fileContent = readFileIfExists(dir + fileName); + const fileContent = readFileIfExists(dir + fileName, options); if (fileContent !== null) {return fileContent;} } return null;