mirror of https://github.com/Nofated095/Q2TG.git
refactor: 用 zod parse 环境变量
This commit is contained in:
parent
df9fd35a32
commit
6b80afca58
|
@ -48,7 +48,8 @@
|
|||
"telegram": "^2.19.10",
|
||||
"tmp-promise": "^3.0.3",
|
||||
"undici": "^6.3.0",
|
||||
"zincsearch-node": "^2.1.0"
|
||||
"zincsearch-node": "^2.1.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.13.1 || >=16.0.0"
|
||||
|
|
|
@ -80,6 +80,9 @@ dependencies:
|
|||
zincsearch-node:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.1(undici@6.3.0)
|
||||
zod:
|
||||
specifier: ^3.22.4
|
||||
version: 3.22.4
|
||||
|
||||
devDependencies:
|
||||
'@types/cli-progress':
|
||||
|
@ -3721,6 +3724,10 @@ packages:
|
|||
undici: 6.3.0
|
||||
dev: false
|
||||
|
||||
/zod@3.22.4:
|
||||
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
|
||||
dev: false
|
||||
|
||||
'@github.com/Clansty/quote-api/archive/37a0e48a434b94bb04c04c7d86d9f0d2295df869.tar.gz':
|
||||
resolution: {tarball: https://github.com/Clansty/quote-api/archive/37a0e48a434b94bb04c04c7d86d9f0d2295df869.tar.gz}
|
||||
name: quote-api
|
||||
|
|
|
@ -20,8 +20,9 @@ import { Converter, Image, rand2uuid } from 'icqq/lib/message';
|
|||
import { randomBytes } from 'crypto';
|
||||
import { escapeXml, gzip, timestamp } from 'icqq/lib/common';
|
||||
import { pb } from 'icqq/lib/core';
|
||||
import env from '../models/env';
|
||||
|
||||
const LOG_LEVEL: LogLevel = process.env.LOG_LEVEL as LogLevel || 'warn';
|
||||
const LOG_LEVEL: LogLevel = env.OICQ_LOG_LEVEL;
|
||||
|
||||
type MessageHandler = (event: PrivateMessageEvent | GroupMessageEvent) => Promise<boolean | void>
|
||||
|
||||
|
@ -123,10 +124,10 @@ export default class OicqClient extends Client {
|
|||
platform: params.platform,
|
||||
data_dir: dataPath(params.uin.toString()),
|
||||
log_level: LOG_LEVEL,
|
||||
ffmpeg_path: process.env.FFMPEG_PATH,
|
||||
ffprobe_path: process.env.FFPROBE_PATH,
|
||||
sign_api_addr: params.signApi || process.env.SIGN_API,
|
||||
ver: params.signVer || process.env.SIGN_VER,
|
||||
ffmpeg_path: env.FFMPEG_PATH,
|
||||
ffprobe_path: env.FFPROBE_PATH,
|
||||
sign_api_addr: params.signApi || env.SIGN_API,
|
||||
ver: params.signVer || env.SIGN_VER,
|
||||
}, params.signDockerId);
|
||||
client.on('system.login.device', loginDeviceHandler);
|
||||
client.on('system.login.slider', loginSliderHandler);
|
||||
|
|
|
@ -15,6 +15,7 @@ import { BigInteger } from 'big-integer';
|
|||
import { EditMessageParams, IterMessagesParams } from 'telegram/client/messages';
|
||||
import { PromisedNetSockets, PromisedWebSockets } from 'telegram/extensions';
|
||||
import { ConnectionTCPFull, ConnectionTCPObfuscated } from 'telegram/network';
|
||||
import env from '../models/env';
|
||||
|
||||
type MessageHandler = (message: Api.Message) => Promise<boolean | void>;
|
||||
type ServiceMessageHandler = (message: Api.MessageService) => Promise<boolean | void>;
|
||||
|
@ -41,24 +42,24 @@ export default class Telegram {
|
|||
private constructor(appName: string, sessionId?: number) {
|
||||
this.client = new TelegramClient(
|
||||
new TelegramSession(sessionId),
|
||||
parseInt(process.env.TG_API_ID),
|
||||
process.env.TG_API_HASH,
|
||||
env.TG_API_ID,
|
||||
env.TG_API_HASH,
|
||||
{
|
||||
connectionRetries: 20,
|
||||
langCode: 'zh',
|
||||
deviceModel: `${appName} On ${os.hostname()}`,
|
||||
appVersion: 'rainbowcat',
|
||||
useIPV6: !!process.env.IPV6,
|
||||
proxy: process.env.PROXY_IP ? {
|
||||
useIPV6: !!env.IPV6,
|
||||
proxy: env.PROXY_IP ? {
|
||||
socksType: 5,
|
||||
ip: process.env.PROXY_IP,
|
||||
port: parseInt(process.env.PROXY_PORT),
|
||||
...(process.env.PROXY_USERNAME && { username: process.env.PROXY_USERNAME }),
|
||||
...(process.env.PROXY_PASSWORD && { password: process.env.PROXY_PASSWORD }),
|
||||
ip: env.PROXY_IP,
|
||||
port: env.PROXY_PORT,
|
||||
username: env.PROXY_USERNAME,
|
||||
password: env.PROXY_PASSWORD,
|
||||
} : undefined,
|
||||
autoReconnect: true,
|
||||
networkSocket: process.env.TG_CONNECTION === 'websocket' ? PromisedWebSockets : PromisedNetSockets,
|
||||
connection: process.env.TG_CONNECTION === 'websocket' ? ConnectionTCPObfuscated : ConnectionTCPFull,
|
||||
networkSocket: env.TG_CONNECTION === 'websocket' ? PromisedWebSockets : PromisedNetSockets,
|
||||
connection: env.TG_CONNECTION === 'websocket' ? ConnectionTCPObfuscated : ConnectionTCPFull,
|
||||
},
|
||||
);
|
||||
// this.client.logger.setLevel(LogLevel.WARN);
|
||||
|
|
|
@ -12,6 +12,7 @@ import BigInteger from 'big-integer';
|
|||
import { getAvatarUrl } from '../utils/urls';
|
||||
import convert from '../helpers/convert';
|
||||
import { Pair } from '../models/Pair';
|
||||
import env from '../models/env';
|
||||
|
||||
export default class {
|
||||
private readonly log: Logger;
|
||||
|
@ -280,7 +281,7 @@ export default class {
|
|||
throw new Error('不支持的消息类型');
|
||||
}
|
||||
const res = await quotly({
|
||||
botToken: process.env.TG_BOT_TOKEN,
|
||||
botToken: env.TG_BOT_TOKEN,
|
||||
type,
|
||||
format,
|
||||
backgroundColor,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { WorkMode } from '../types/definitions';
|
|||
import OicqClient from '../client/OicqClient';
|
||||
import { md5Hex } from '../utils/hashing';
|
||||
import Instance from '../models/Instance';
|
||||
import env from '../models/env';
|
||||
|
||||
export default class SetupController {
|
||||
private readonly setupService: SetupService;
|
||||
|
@ -106,7 +107,7 @@ export default class SetupController {
|
|||
|
||||
let signApi: string;
|
||||
|
||||
if (!process.env.SIGN_API) {
|
||||
if (!env.SIGN_API) {
|
||||
signApi = await this.setupService.waitForOwnerInput('请输入签名服务器地址', [
|
||||
[Button.text('不需要签名服务器', true, true)],
|
||||
]);
|
||||
|
@ -115,7 +116,7 @@ export default class SetupController {
|
|||
|
||||
let signVer: string;
|
||||
|
||||
if (signApi && !process.env.SIGN_VER) {
|
||||
if (signApi && !env.SIGN_VER) {
|
||||
signVer = await this.setupService.waitForOwnerInput('请输入签名服务器版本', [
|
||||
[Button.text('8.9.63', true, true),
|
||||
Button.text('8.9.68', true, true)],
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { spawn } from 'child_process';
|
||||
import env from '../models/env';
|
||||
|
||||
export default function tgsToGif(tgsPath: string) {
|
||||
return new Promise(resolve => {
|
||||
spawn(process.env.TGS_TO_GIF || 'tgs_to_gif', [tgsPath]).on('exit', () => {
|
||||
spawn(env.TGS_TO_GIF, [tgsPath]).on('exit', () => {
|
||||
resolve(tgsPath + '.gif');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ import axios from 'axios';
|
|||
import { CustomFile } from 'telegram/client/uploads';
|
||||
import fsP from 'fs/promises';
|
||||
import { file } from 'tmp-promise';
|
||||
import env from '../models/env';
|
||||
|
||||
export default class {
|
||||
private readonly log: Logger;
|
||||
|
@ -197,21 +198,26 @@ export default class {
|
|||
}
|
||||
break;
|
||||
case 'forward':
|
||||
try {
|
||||
const messages = await this.pair.qq.getForwardMsg(result.resId);
|
||||
const hash = md5Hex(result.resId);
|
||||
text += `转发的消息记录 ${process.env.CRV_API}/?hash=${hash}`;
|
||||
// 传到 Cloudflare
|
||||
axios.post(`${process.env.CRV_API}/add`, {
|
||||
auth: process.env.CRV_KEY,
|
||||
key: hash,
|
||||
data: messages,
|
||||
})
|
||||
.then(data => this.log.trace('上传消息记录到 Cloudflare', data.data))
|
||||
.catch(e => this.log.error('上传消息记录到 Cloudflare 失败', e));
|
||||
if (env.CRV_API) {
|
||||
try {
|
||||
const messages = await this.pair.qq.getForwardMsg(result.resId);
|
||||
const hash = md5Hex(result.resId);
|
||||
text += `转发的消息记录 ${env.CRV_API}/?hash=${hash}`;
|
||||
// 传到 Cloudflare
|
||||
axios.post(`${env.CRV_API}/add`, {
|
||||
auth: env.CRV_KEY,
|
||||
key: hash,
|
||||
data: messages,
|
||||
})
|
||||
.then(data => this.log.trace('上传消息记录到 Cloudflare', data.data))
|
||||
.catch(e => this.log.error('上传消息记录到 Cloudflare 失败', e));
|
||||
}
|
||||
catch (e) {
|
||||
text += '[转发多条消息(无法获取)]';
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
text += '[转发多条消息(无法获取)]';
|
||||
else {
|
||||
text += '[转发多条消息]';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -255,11 +261,11 @@ export default class {
|
|||
ext: 'tgs',
|
||||
mime: 'application/x-tgsticker',
|
||||
} : await fileTypeFromFile(filePath);
|
||||
if(!type){
|
||||
if (!type) {
|
||||
type = {
|
||||
ext: 'bin',
|
||||
mime: 'application/octet-stream',
|
||||
}
|
||||
};
|
||||
}
|
||||
let media: Api.TypeInputMedia;
|
||||
if (['.webp', '.tgs'].includes(path.extname(filePath))) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import path from 'path';
|
||||
|
||||
const DATA_DIR = process.env.DATA_DIR || path.resolve('./data');
|
||||
import env from '../models/env';
|
||||
|
||||
// Wrap of path.join, add base DATA_DIR
|
||||
export default (...paths: string[]) =>
|
||||
path.join(DATA_DIR, ...paths);
|
||||
path.join(env.DATA_DIR, ...paths);
|
||||
|
|
|
@ -24,6 +24,7 @@ import StatusReportController from '../controllers/StatusReportController';
|
|||
import HugController from '../controllers/HugController';
|
||||
import QuotLyController from '../controllers/QuotLyController';
|
||||
import MiraiSkipFilterController from '../controllers/MiraiSkipFilterController';
|
||||
import env from './env';
|
||||
|
||||
export default class Instance {
|
||||
private _owner = 0;
|
||||
|
@ -96,7 +97,7 @@ export default class Instance {
|
|||
this.tgBot = await Telegram.connect(this._botSessionId);
|
||||
}
|
||||
else {
|
||||
const token = this.id === 0 ? process.env.TG_BOT_TOKEN : botToken;
|
||||
const token = this.id === 0 ? env.TG_BOT_TOKEN : botToken;
|
||||
if (!token) {
|
||||
throw new Error('botToken 未指定');
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { MemorySession } from 'telegram/sessions';
|
|||
import db from './db';
|
||||
import { AuthKey } from 'telegram/crypto/AuthKey';
|
||||
import { getLogger, Logger } from 'log4js';
|
||||
import env from './env';
|
||||
|
||||
const PASS = () => 0;
|
||||
|
||||
|
@ -19,19 +20,19 @@ export default class TelegramSession extends MemorySession {
|
|||
|
||||
async load() {
|
||||
this.log.trace('load');
|
||||
if (process.env.TG_INITIAL_DCID) {
|
||||
this._dcId = Number(process.env.TG_INITIAL_DCID);
|
||||
if (env.TG_INITIAL_DCID) {
|
||||
this._dcId = env.TG_INITIAL_DCID;
|
||||
}
|
||||
if (process.env.TG_INITIAL_SERVER) {
|
||||
this._serverAddress = process.env.TG_INITIAL_SERVER;
|
||||
if (env.TG_INITIAL_SERVER) {
|
||||
this._serverAddress = env.TG_INITIAL_SERVER;
|
||||
}
|
||||
if (!this._dbId) {
|
||||
this.log.debug('Session 不存在,创建');
|
||||
// 创建并返回
|
||||
const newDbEntry = await db.session.create({
|
||||
data: {
|
||||
dcId: process.env.TG_INITIAL_DCID ? Number(process.env.TG_INITIAL_DCID) : null,
|
||||
serverAddress: process.env.TG_INITIAL_SERVER,
|
||||
dcId: env.TG_INITIAL_DCID,
|
||||
serverAddress: env.TG_INITIAL_SERVER,
|
||||
},
|
||||
});
|
||||
this._dbId = newDbEntry.id;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import z from 'zod';
|
||||
import path from 'path';
|
||||
|
||||
const configParsed = z.object({
|
||||
DATA_DIR: z.string().default(path.resolve('./data')),
|
||||
OICQ_LOG_LEVEL: z.enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal', 'mark', 'off']).default('warn'),
|
||||
FFMPEG_PATH: z.string().optional(),
|
||||
FFPROBE_PATH: z.string().optional(),
|
||||
SIGN_API: z.string().url().optional(),
|
||||
SIGN_VER: z.string().optional(),
|
||||
TG_API_ID: z.string().regex(/^\d+$/).transform(Number),
|
||||
TG_API_HASH: z.string(),
|
||||
TG_BOT_TOKEN: z.string(),
|
||||
TG_CONNECTION: z.enum(['websocket', 'tcp']).default('tcp'),
|
||||
TG_INITIAL_DCID: z.string().regex(/^\d+$/).transform(Number).optional(),
|
||||
TG_INITIAL_SERVER: z.string().ip().optional(),
|
||||
IPV6: z.string().transform((v) => ['true', '1', 'yes'].includes(v.toLowerCase())).default('false'),
|
||||
PROXY_IP: z.string().ip().optional(),
|
||||
PROXY_PORT: z.string().regex(/^\d+$/).transform(Number).optional(),
|
||||
PROXY_USERNAME: z.string().optional(),
|
||||
PROXY_PASSWORD: z.string().optional(),
|
||||
TGS_TO_GIF: z.string().default('tgs_to_gif'),
|
||||
CRV_API: z.string().url().optional(),
|
||||
CRV_KEY: z.string().optional(),
|
||||
ZINC_URL: z.string().url().optional(),
|
||||
ZINC_USERNAME: z.string().optional(),
|
||||
ZINC_PASSWORD: z.string().optional(),
|
||||
BAIDU_APP_ID: z.string().optional(),
|
||||
BAIDU_API_KEY: z.string().optional(),
|
||||
BAIDU_SECRET_KEY: z.string().optional(),
|
||||
DISABLE_FILE_UPLOAD_TIP: z.string().transform((v) => ['true', '1', 'yes'].includes(v.toLowerCase())).default('false'),
|
||||
}).safeParse(process.env);
|
||||
|
||||
if (!configParsed.success) {
|
||||
console.error('环境变量解析错误:', (configParsed as any).error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
export default configParsed.data;
|
|
@ -41,6 +41,7 @@ import random from '../utils/random';
|
|||
import { escapeXml } from 'icqq/lib/common';
|
||||
import Docker from 'dockerode';
|
||||
import ReplyKeyboardHide = Api.ReplyKeyboardHide;
|
||||
import env from '../models/env';
|
||||
|
||||
const NOT_CHAINABLE_ELEMENTS = ['flash', 'record', 'video', 'location', 'share', 'json', 'xml', 'poke'];
|
||||
|
||||
|
@ -55,18 +56,18 @@ export default class ForwardService {
|
|||
private readonly tgBot: Telegram,
|
||||
private readonly oicq: OicqClient) {
|
||||
this.log = getLogger(`ForwardService - ${instance.id}`);
|
||||
if (process.env.ZINC_URL) {
|
||||
if (env.ZINC_URL) {
|
||||
this.zincSearch = new ZincSearch({
|
||||
url: process.env.ZINC_URL,
|
||||
user: process.env.ZINC_USERNAME,
|
||||
password: process.env.ZINC_PASSWORD,
|
||||
url: env.ZINC_URL,
|
||||
user: env.ZINC_USERNAME,
|
||||
password: env.ZINC_PASSWORD,
|
||||
});
|
||||
}
|
||||
if (process.env.BAIDU_APP_ID) {
|
||||
if (env.BAIDU_APP_ID) {
|
||||
this.speechClient = new AipSpeechClient(
|
||||
process.env.BAIDU_APP_ID,
|
||||
process.env.BAIDU_API_KEY,
|
||||
process.env.BAIDU_SECRET_KEY,
|
||||
env.BAIDU_APP_ID,
|
||||
env.BAIDU_API_KEY,
|
||||
env.BAIDU_SECRET_KEY,
|
||||
);
|
||||
}
|
||||
if (oicq.signDockerId) {
|
||||
|
@ -110,22 +111,27 @@ export default class ForwardService {
|
|||
}
|
||||
};
|
||||
const useForward = async (resId: string) => {
|
||||
try {
|
||||
const messages = await pair.qq.getForwardMsg(resId);
|
||||
message = helper.generateForwardBrief(messages);
|
||||
const hash = md5Hex(resId);
|
||||
buttons.push(Button.url('📃查看', `${process.env.CRV_API}/?hash=${hash}`));
|
||||
// 传到 Cloudflare
|
||||
axios.post(`${process.env.CRV_API}/add`, {
|
||||
auth: process.env.CRV_KEY,
|
||||
key: hash,
|
||||
data: messages,
|
||||
})
|
||||
.then(data => this.log.trace('上传消息记录到 Cloudflare', data.data))
|
||||
.catch(e => this.log.error('上传消息记录到 Cloudflare 失败', e));
|
||||
if(env.CRV_API) {
|
||||
try {
|
||||
const messages = await pair.qq.getForwardMsg(resId);
|
||||
message = helper.generateForwardBrief(messages);
|
||||
const hash = md5Hex(resId);
|
||||
buttons.push(Button.url('📃查看', `${env.CRV_API}/?hash=${hash}`));
|
||||
// 传到 Cloudflare
|
||||
axios.post(`${env.CRV_API}/add`, {
|
||||
auth: env.CRV_KEY,
|
||||
key: hash,
|
||||
data: messages,
|
||||
})
|
||||
.then(data => this.log.trace('上传消息记录到 Cloudflare', data.data))
|
||||
.catch(e => this.log.error('上传消息记录到 Cloudflare 失败', e));
|
||||
}
|
||||
catch (e) {
|
||||
message = '[<i>转发多条消息(无法获取)</i>]';
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
message = '[<i>转发多条消息(无法获取)</i>]';
|
||||
else {
|
||||
message = '[<i>转发多条消息(未配置)</i>]';
|
||||
}
|
||||
};
|
||||
for (const elem of event.message) {
|
||||
|
@ -541,7 +547,7 @@ export default class ForwardService {
|
|||
}
|
||||
}
|
||||
brief += '[文件]';
|
||||
if (process.env.DISABLE_FILE_UPLOAD_TIP) {
|
||||
if (env.DISABLE_FILE_UPLOAD_TIP) {
|
||||
chain = [];
|
||||
}
|
||||
}
|
||||
|
@ -713,10 +719,10 @@ export default class ForwardService {
|
|||
nick: string,
|
||||
}) {
|
||||
if (!this.zincSearch) return;
|
||||
const existsReq = await fetch(process.env.ZINC_URL + `/api/index/q2tg-${pairId}`, {
|
||||
const existsReq = await fetch(env.ZINC_URL + `/api/index/q2tg-${pairId}`, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: 'Basic ' + Buffer.from(process.env.ZINC_USERNAME + ':' + process.env.ZINC_PASSWORD).toString('base64'),
|
||||
Authorization: 'Basic ' + Buffer.from(env.ZINC_USERNAME + ':' + env.ZINC_PASSWORD).toString('base64'),
|
||||
},
|
||||
});
|
||||
if (existsReq.status === 404) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import db from '../models/db';
|
|||
import { Friend, Group } from 'icqq';
|
||||
import { format } from 'date-and-time';
|
||||
import ZincSearch from 'zincsearch-node';
|
||||
import env from '../models/env';
|
||||
|
||||
export default class InChatCommandsService {
|
||||
private readonly log: Logger;
|
||||
|
@ -20,11 +21,11 @@ export default class InChatCommandsService {
|
|||
private readonly tgBot: Telegram,
|
||||
private readonly oicq: OicqClient) {
|
||||
this.log = getLogger(`InChatCommandsService - ${instance.id}`);
|
||||
if (process.env.ZINC_URL) {
|
||||
if (env.ZINC_URL) {
|
||||
this.zincSearch = new ZincSearch({
|
||||
url: process.env.ZINC_URL,
|
||||
user: process.env.ZINC_USERNAME,
|
||||
password: process.env.ZINC_PASSWORD,
|
||||
url: env.ZINC_URL,
|
||||
user: env.ZINC_USERNAME,
|
||||
password: env.ZINC_PASSWORD,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue