You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
5.3 KiB
TypeScript

import fs from 'fs/promises';
import path from 'path';
import Yaml from 'yaml';
import chokidar from 'chokidar';
import { debounce } from 'throttle-debounce';
import EventEmitter from 'events';
import App from './App';
import { BaseProvider } from './base/provider/BaseProvider';
import { ChannelConfig } from './Config';
export class ChannelManager extends EventEmitter {
private app: App;
private channelPath: string;
private loadChannelCallback: (file: string) => any;
private removeChannelCallback: (file: string) => any;
private setLoading?: debounce<Function>;
private watcher!: chokidar.FSWatcher;
public channels: { [key: string]: BaseProvider };
public channelName: { [key: string]: string };
constructor(app: App, channelPath: string) {
super();
this.app = app;
this.channelPath = channelPath;
this.channels = {};
this.channelName = {};
this.loadChannelCallback = this.loadChannel.bind(this);
this.removeChannelCallback = this.removeChannel.bind(this);
}
/**
* 加载所有Channel
*/
async initialize() {
this.watcher = chokidar.watch(this.channelPath, {
ignored: '*.bak',
ignorePermissionErrors: true,
persistent: true
});
this.watcher.on('add', this.loadChannelCallback);
this.watcher.on('change', this.loadChannelCallback);
this.watcher.on('unlink', this.removeChannelCallback);
}
/**
* 获取Channel ID
* @param {string} file - channel config file
* @returns
*/
getChannelId(file: string): string {
let channelPath = path.relative(this.channelPath, file).replace(/\\/g, "/").replace(/\..*?$/, "");
return channelPath;
}
/**
* 获取Channel的全名
* @param {string} channelId - channel ID
*/
getChannelFullName(channelId: string): string {
// 从最顶层开始查找
let pathList = channelId.split("/");
let nameList: string[] = [];
for (let i = 0; i < pathList.length; i++) {
let currentPath = pathList.slice(0, i + 1).join("/");
let findedName = this.channelName[currentPath];
if (findedName) {
nameList.push(findedName);
} else {
nameList.push(pathList[i]);
}
}
return nameList.join("/");
}
/**
* 获取Provider名
* @param config
* @returns
*/
getProviderName(config: ChannelConfig): string {
return config.provider;
}
/**
* 读取或更新Channel配置文件
* @param {string} file
*/
async loadChannel(file: string) {
try {
let content = await fs.readFile(file, { encoding: 'utf-8' });
let config = Yaml.parse(content);
let channelId = this.getChannelId(file);
if (path.basename(channelId) === "_group") {
// 只是group标记
channelId = path.dirname(channelId);
this.channelName[channelId] = config?.name;
} else {
if (BaseProvider.checkConfig(config)) {
// console.log(`正在加载Channel: ${channelId}`);
// 处理channel
let providerName = this.getProviderName(config);
let isReload = false;
if (channelId in this.channels) {
// 重载配置
isReload = true;
await this.channels[channelId].destory();
}
// 读取配置
let channel = this.app.createChannel(providerName, channelId, config);
if (channel) {
await channel.initialize();
// 更新列表
this.channels[channelId] = channel;
this.channelName[channelId] = config?.name;
if (isReload) {
this.emit('reload', channelId);
console.log(`已重载Channel: ${channelId}`);
} else {
this.emit('add', channelId);
console.log(`已加载Channel: ${channelId}`);
}
}
} else {
console.error(`配置文件: ${file} 格式错误`);
}
}
return true;
} catch (err) {
console.error(err);
return false;
}
}
/**
* 移除Channel
* @param {string} file
*/
async removeChannel(file: string) {
let channelId = this.getChannelId(file);
if (path.basename(channelId) === "_group") {
// 仅删除组名
channelId = path.basename(channelId);
delete this.channelName[channelId];
} else {
let channel = this.channels[channelId];
if (channel) {
await channel.destory();
delete this.channels[channelId];
delete this.channelName[channelId];
this.emit('remove', channelId);
console.log("已移除Channel: ", this.getChannelFullName(channelId));
}
}
}
onLoad() {
}
}