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
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() {
|
|
|
|
}
|
|
}
|