mirror of
https://github.com/spacebarchat/server.git
synced 2026-10-24 17:13:13 +00:00
Plugins finally load!
This commit is contained in:
23 files changed
+249
-28
No files matched your search
@@ -0,0 +1,6 @@
|
||||
import { Plugin } from "util/plugin";
|
||||
import * as example_plugin from "./example-plugin/TestPlugin";
|
||||
|
||||
export const PluginIndex: any = {
|
||||
"example-plugin": new example_plugin.default(),
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Plugin } from "@fosscord/util";
|
||||
|
||||
export default class TestPlugin extends Plugin {
|
||||
onPluginLoaded(): void {
|
||||
console.log("Hello from test plugin! IT WORKS!!!!!!!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Plugin } from "@fosscord/util";
|
||||
|
||||
export default class TestPlugin implements Plugin {
|
||||
pluginPath: string;
|
||||
async initConfig(): Promise<void> {
|
||||
|
||||
}
|
||||
onPluginLoaded() {
|
||||
console.log("Test plugin active!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,5 +6,5 @@
|
||||
],
|
||||
"repository": "https://github.com/fosscord/fosscord-server",
|
||||
"license": "",
|
||||
"index": "ExamplePlugin.js"
|
||||
"mainClass": "TestPlugin"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Column, Entity } from "typeorm";
|
||||
import { BaseClassWithoutId, PrimaryIdColumn } from "./BaseClass";
|
||||
|
||||
@Entity("plugin_config")
|
||||
export class PluginConfigEntity extends BaseClassWithoutId {
|
||||
@PrimaryIdColumn()
|
||||
key: string;
|
||||
|
||||
@Column({ type: "simple-json", nullable: true })
|
||||
value: number | boolean | null | string | undefined;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import EventEmitter from "events";
|
||||
import { TypedEventEmitter } from "@fosscord/util";
|
||||
import { PluginLoadedEventArgs, TypedEventEmitter } from "@fosscord/util";
|
||||
|
||||
type PluginEvents = {
|
||||
error: (error: Error | unknown) => void;
|
||||
@@ -7,14 +7,31 @@ type PluginEvents = {
|
||||
};
|
||||
|
||||
//this doesnt work, check later:
|
||||
//(EventEmitter as new () => TypedEventEmitter<PluginEvents>) {
|
||||
export class Plugin extends EventEmitter {
|
||||
private _untypedOn = this.on
|
||||
private _untypedEmit = this.emit
|
||||
public on = <K extends keyof PluginEvents>(event: K, listener: PluginEvents[K]): this => this._untypedOn(event, listener)
|
||||
public emit = <K extends keyof PluginEvents>(event: K, ...args: Parameters<PluginEvents[K]>): boolean => this._untypedEmit(event, ...args)
|
||||
|
||||
async init() {
|
||||
//EventEmitter as new () => TypedEventEmitter<PluginEvents>
|
||||
export class Plugin {
|
||||
/**
|
||||
* Path the plugin resides in.
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Plugin
|
||||
*/
|
||||
pluginPath: string;
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @memberof Plugin
|
||||
*/
|
||||
async initConfig() {
|
||||
// insert default config into database?
|
||||
console.log("did you forget to implement initConfig?");
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {PluginLoadedEventArgs} args Info about plugin environment
|
||||
* @memberof Plugin
|
||||
*/
|
||||
onPluginLoaded?(args?: PluginLoadedEventArgs) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { ConfigEntity } from "../entities/Config";
|
||||
import fs from "fs";
|
||||
import { OrmUtils, Environment } from "..";
|
||||
|
||||
// TODO: yaml instead of json
|
||||
const overridePath = process.env.PLUGIN_CONFIG_PATH ?? "";
|
||||
|
||||
let config: any;
|
||||
let pairs: ConfigEntity[];
|
||||
|
||||
// TODO: use events to inform about config updates
|
||||
// Config keys are separated with _
|
||||
|
||||
export const Config = {
|
||||
init: async function init() {
|
||||
if (config) return config;
|
||||
console.log('[Config] Loading configuration...')
|
||||
pairs = await ConfigEntity.find();
|
||||
config = pairsToConfig(pairs);
|
||||
//config = (config || {}).merge(new ConfigValue());
|
||||
//config = OrmUtils.mergeDeep(new ConfigValue(), config)
|
||||
|
||||
if(process.env.CONFIG_PATH)
|
||||
try {
|
||||
const overrideConfig = JSON.parse(fs.readFileSync(overridePath, { encoding: "utf8" }));
|
||||
config = overrideConfig.merge(config);
|
||||
} catch (error) {
|
||||
fs.writeFileSync(overridePath, JSON.stringify(config, null, 4));
|
||||
}
|
||||
|
||||
return this.set(config);
|
||||
},
|
||||
get: function get() {
|
||||
if(!config) {
|
||||
if(Environment.isDebug)
|
||||
console.log("Oops.. trying to get config without config existing... Returning defaults... (Is the database still initialising?)");
|
||||
return {};
|
||||
}
|
||||
return config;
|
||||
},
|
||||
set: function set(val: Partial<any>) {
|
||||
if (!config || !val) return;
|
||||
config = val.merge(config);
|
||||
|
||||
return applyConfig(config);
|
||||
},
|
||||
};
|
||||
|
||||
function applyConfig(val: any) {
|
||||
async function apply(obj: any, key = ""): Promise<any> {
|
||||
if (typeof obj === "object" && obj !== null)
|
||||
return Promise.all(Object.keys(obj).map((k) => apply(obj[k], key ? `${key}_${k}` : k)));
|
||||
|
||||
let pair = pairs.find((x) => x.key === key);
|
||||
if (!pair) pair = new ConfigEntity();
|
||||
|
||||
pair.key = key;
|
||||
pair.value = obj;
|
||||
return pair.save();
|
||||
}
|
||||
if(process.env.CONFIG_PATH) {
|
||||
if(Environment.isDebug)
|
||||
console.log(`Writing config: ${process.env.CONFIG_PATH}`)
|
||||
fs.writeFileSync(overridePath, JSON.stringify(val, null, 4));
|
||||
}
|
||||
|
||||
return apply(val);
|
||||
}
|
||||
|
||||
function pairsToConfig(pairs: ConfigEntity[]) {
|
||||
let value: any = {};
|
||||
|
||||
pairs.forEach((p) => {
|
||||
const keys = p.key.split("_");
|
||||
let obj = value;
|
||||
let prev = "";
|
||||
let prevObj = obj;
|
||||
let i = 0;
|
||||
|
||||
for (const key of keys) {
|
||||
if (!isNaN(Number(key)) && !prevObj[prev]?.length) prevObj[prev] = obj = [];
|
||||
if (i++ === keys.length - 1) obj[key] = p.value;
|
||||
else if (!obj[key]) obj[key] = {};
|
||||
|
||||
prev = key;
|
||||
prevObj = obj;
|
||||
obj = obj[key];
|
||||
}
|
||||
});
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { Plugin, PluginManifest } from "./";
|
||||
import { Plugin, PluginLoadedEventArgs, PluginManifest } from "./";
|
||||
import { PluginIndex } from "plugins/PluginIndex";
|
||||
|
||||
const root = process.env.PLUGIN_LOCATION || "dist/plugins";
|
||||
|
||||
let pluginsLoaded = false;
|
||||
export class PluginLoader {
|
||||
public static plugins: Plugin[] = [];
|
||||
public static loadPlugins() {
|
||||
console.log(`Plugin root directory: ${path.resolve(root)}`);
|
||||
const dirs = fs.readdirSync(root).filter((x) => {
|
||||
@@ -17,6 +19,9 @@ export class PluginLoader {
|
||||
}
|
||||
});
|
||||
console.log(dirs);
|
||||
PluginIndex.forEach((x: any)=>{
|
||||
console.log(x.onPluginLoaded)
|
||||
})
|
||||
dirs.forEach(async (x) => {
|
||||
let modPath = path.resolve(path.join(root, x));
|
||||
console.log(`Trying to load plugin: ${modPath}`);
|
||||
@@ -24,16 +29,13 @@ export class PluginLoader {
|
||||
console.log(
|
||||
`Plugin info: ${manifest.name} (${manifest.id}), written by ${manifest.authors}, available at ${manifest.repository}`
|
||||
);
|
||||
const module_ = require(path.join(modPath, manifest.index)) as Plugin;
|
||||
try {
|
||||
await module_.init();
|
||||
module_.emit("loaded");
|
||||
} catch (error) {
|
||||
module_.emit("error", error);
|
||||
}
|
||||
const module_ = PluginIndex["example-plugin"];
|
||||
|
||||
module_.pluginPath = modPath;
|
||||
if(module_.onPluginLoaded) module_.onPluginLoaded({} as PluginLoadedEventArgs);
|
||||
this.plugins.push(module_);
|
||||
});
|
||||
|
||||
//
|
||||
//module_.pluginPath =
|
||||
console.log(`Done loading ${this.plugins.length} plugins!`)
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,5 @@ export class PluginManifest {
|
||||
license: string;
|
||||
version: string // semver
|
||||
versionCode: number // integer
|
||||
index: string;
|
||||
mainClass: string;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./Plugin";
|
||||
export * from "./PluginLoader";
|
||||
export * from "./PluginManifest";
|
||||
export * from "./plugin_data_objects/index";
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface PreChannelCreateEventArgs {
|
||||
|
||||
}
|
||||
|
||||
export interface OnChannelCreateEventArgs {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface PreGuildCreateEventArgs {
|
||||
|
||||
}
|
||||
|
||||
export interface OnGuildCreateEventArgs {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface PreLoginEventArgs {
|
||||
|
||||
}
|
||||
|
||||
export interface OnLoginEventArgs {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface PreMessageEventArgs {
|
||||
|
||||
}
|
||||
|
||||
export interface OnMessageEventArgs {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface PluginLoadedEventArgs {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface PreRegisterEventArgs {
|
||||
|
||||
}
|
||||
|
||||
export interface OnRegisterEventArgs {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface PreTypingEventArgs {
|
||||
|
||||
}
|
||||
|
||||
export interface OnTypingEventArgs {
|
||||
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
while read event
|
||||
do
|
||||
if [ ! -f "${event}EventArgs.ts" ]
|
||||
then
|
||||
echo Making event $event...
|
||||
(
|
||||
echo "export interface Pre${event}EventArgs {"
|
||||
echo ' '
|
||||
echo '}'
|
||||
echo ''
|
||||
echo "export interface On${event}EventArgs {"
|
||||
echo ' '
|
||||
echo '}'
|
||||
) > ${event}EventArgs.ts
|
||||
fi
|
||||
done < _pdo
|
||||
|
||||
echo ''
|
||||
|
||||
node ../../../../scripts/gen_index.js .. --recursive
|
||||
@@ -0,0 +1,7 @@
|
||||
Register
|
||||
Message
|
||||
Login
|
||||
GuildCreate
|
||||
ChannelCreate
|
||||
Typing
|
||||
StatusChange
|
||||
@@ -0,0 +1 @@
|
||||
Voice events when we do voice.
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from "./ChannelCreateEventArgs";
|
||||
export * from "./GuildCreateEventArgs";
|
||||
export * from "./LoginEventArgs";
|
||||
export * from "./MessageEventArgs";
|
||||
export * from "./PluginLoadedEventArgs";
|
||||
export * from "./RegisterEventArgs";
|
||||
export * from "./TypingEventArgs";
|
||||
@@ -0,0 +1,3 @@
|
||||
export class Environment {
|
||||
static isDebug: boolean = /--debug|--inspect/.test(process.execArgv.join(' '));
|
||||
}
|
||||
@@ -23,3 +23,6 @@ export * from "./Snowflake";
|
||||
export * from "./String";
|
||||
export * from "./Token";
|
||||
export * from "./TraverseDirectory";
|
||||
export * from "./InvisibleCharacters";
|
||||
export * from "./Environment";
|
||||
export * from "./Paths";
|
||||
Reference in new issue
Block a user