diff --git a/utils/Env.ts b/utils/Env.ts new file mode 100644 index 0000000..fa2b80a --- /dev/null +++ b/utils/Env.ts @@ -0,0 +1,113 @@ +/* + * @Author: renxia + * @Date: 2024-02-20 10:31:21 + * @LastEditors: renxia + * @LastEditTime: 2024-04-08 11:18:27 + * @Description: + */ +import { type AnyObject, Request } from '@lzwme/fe-utils'; +import { strip, redBright } from 'console-log-colors'; +import { getCacheStorage, sendNotify } from './common'; + +interface EnvOptions { + /** 多账号分隔符。默认为 &、\n */ + sep?: string[]; + /** 是否开启消息通知。默认为 true */ + notifyFlag?: boolean; +} + +export class Env { + public index = 0; + private startTime = Date.now(); + private msgs: string[] = []; + private options: EnvOptions = { + sep: ['&', '\n'], + }; + public hasError: boolean | number = 0; + public req = new Request(undefined, { 'content-type': 'application/json' }); + public storage: ReturnType>; + constructor(public name: string, options?: EnvOptions) { + this.log(`[${this.name}]开始运行\n`, 'debug'); + this.storage = getCacheStorage(name); + if (options) Object.assign(this.options, options); + } + public async init(Task?: any, envName?: string, envValue?: string) { + await this.storage.ready(); + + if (Task) { + if (!envValue && envName) envName.split('|').some((eName) => (envValue = process.env[eName])); + if (envValue) { + const users = this.parse(envValue, this.options.sep); + await this.runTask(Task, users); + } else { + this.log(`环境变量 ${redBright(envName)} 未定义`, 'error'); + } + } + return this; + } + public async runTask(Task: any, usersConfig: any[]) { + try { + for (let [idx, userConfig] of Object.entries(usersConfig)) { + try { + this.index = +idx + 1; + let desc = ''; + if (typeof userConfig === 'string') [userConfig, desc = ''] = userConfig.split('##'); // 支持以 ## 隔离描述,可主要用于唯一 uid 标记 + this.log(`🆔账号${this.index}:${desc || ''}`); + if (typeof Task.prototype?.start === 'function') { + const t = new Task(userConfig, this.index, desc); + await t.start(); + } else await Task(userConfig, this.index, desc); + } catch (error) { + console.error(error); + this.log(`❌账号 ${this.index} 运行异常:${(error as Error).message}`, 'error'); + } + } + } catch (e) { + const error = e as Error; + console.error(error); + this.log(`❌运行异常:${error.message}`, 'error'); + } + this.done(); + } + public parse(envValue: string, mutiAccountSeps = this.options.sep!) { + if (!envValue) return []; + + const sep = mutiAccountSeps.find((d) => envValue.includes(d)) || mutiAccountSeps[0]; + const arr = envValue.split(sep).filter(Boolean); + if (arr.length > 1) this.log(`共找到了 ${arr.length} 个账号`); + return arr; + } + public log(msg: string, type: 'error' | 'info' | 'warn' | 'log' | 'debug' | 'D' = 'info') { + if (type === 'D') type = 'debug'; + if (type === 'error') { + this.hasError = true; + if (!msg.startsWith('❌') && !/^[\ud800-\udbff][\udc00-\udfff]/.test(msg)) msg = `❌ ${msg}`; + } + if (type !== 'debug') this.msgs.push(strip(msg)); + console[type](msg); + } + uuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16); + }); + } + wait(delay: number, gap = 0, showTip = true) { + if (gap > 0) delay += Math.floor(Math.random() * gap); + if (showTip) this.log(`等待 ${delay}ms 后继续...`, 'debug'); + return new Promise((rs) => setTimeout(rs, delay)); + } + public getMsgs() { + return this.msgs.join('\n'); + } + private end = false; + public async done() { + if (this.end) return; + this.end = true; + if (this.options.notifyFlag !== false && this.msgs.length) { + await sendNotify(this.name, this.getMsgs(), { hasError: this.hasError, isPrint: false, exit: false }); + } + this.log(`运行结束,共运行了 ${Math.ceil((Date.now() - this.startTime) / 1000)} 秒`); + process.exit(this.hasError ? 1 : 0); + } +}