diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml
new file mode 100644
index 000000000..dd8744e09
--- /dev/null
+++ b/.github/workflows/test_build.yml
@@ -0,0 +1,29 @@
+on:
+ workflow_dispatch:
+ push:
+ paths:
+ - "**"
+
+name: Test Build
+
+jobs:
+ insiders-build:
+ strategy:
+ matrix:
+ os: [windows, macos, ubuntu]
+ include:
+ - os: windows
+ - os: macos
+ - os: ubuntu
+ runs-on: ${{ matrix.os }}-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v2
+ env:
+ MONGOMS_VERSION: 4.4.3
+ with:
+ node-version: 18
+ - run: |
+ cd bundle
+ npm run setup
+ npm run build clean logerrors pretty-errors propagate-err
diff --git a/scripts/build.js b/scripts/build.js
new file mode 100644
index 000000000..7421aa4fb
--- /dev/null
+++ b/scripts/build.js
@@ -0,0 +1,116 @@
+const { execSync } = require("child_process");
+const path = require("path");
+const fs = require("fs");
+const { argv, stdout, exit } = require("process");
+const { execIn, parts, getDirs, walk, sanitizeVarName } = require("./utils");
+
+if (argv.includes("help")) {
+ console.log(`Fosscord build script help:
+Arguments:
+ clean Cleans up previous builds
+ verbose Enable verbose logging
+ logerrors Log build errors to console
+ pretty-errors Pretty-print build errors
+ silent No output to console or files.
+ propagate-err Exit script with error code if build fails.`);
+ exit(0);
+}
+
+let steps = 5,
+ i = 0;
+if (argv.includes("clean")) steps++;
+
+const verbose = argv.includes("verbose") || argv.includes("v");
+const logerr = argv.includes("logerrors");
+const pretty = argv.includes("pretty-errors");
+const silent = argv.includes("silent");
+
+if (silent) console.error = console.log = function () {};
+
+if (argv.includes("clean")) {
+ console.log(`[${++i}/${steps}] Cleaning...`);
+ let d = "../" + "/dist";
+ if (fs.existsSync(d)) {
+ fs.rmSync(d, { recursive: true });
+ if (verbose) console.log(`Deleted ${d}!`);
+ }
+}
+
+console.log(`[${++i}/${steps}] Compiling src files ...`);
+
+let buildFlags = "";
+if (pretty) buildFlags += "--pretty ";
+
+console.log(`[${++i}/${steps}] Building plugin index...`);
+let pluginDir = path.join(__dirname, "..", "src", "plugins");
+let output = 'import { Plugin } from "util/plugin";\n';
+
+const dirs = fs.readdirSync(pluginDir).filter((x) => {
+ try {
+ fs.readdirSync(path.join(pluginDir, x));
+ return true;
+ } catch (e) {
+ return false;
+ }
+});
+dirs.forEach((x) => {
+ let pluginManifest = require(path.join(pluginDir, x, "plugin.json"));
+ output += `import * as ${sanitizeVarName(x)} from "./${x}/${pluginManifest.mainClass}";\n`;
+});
+output += `\nexport const PluginIndex: any = {\n`;
+dirs.forEach((x) => {
+ output += ` "${x}": new ${sanitizeVarName(x)}.default(),\n`; //ctor test: '${path.resolve(path.join(pluginDir, x))}', require('./${x}/plugin.json')
+});
+output += `};`;
+
+fs.writeFileSync(path.join(__dirname, "..", "src", "plugins", "PluginIndex.ts"), output);
+
+if (!argv.includes("copyonly")) {
+ console.log(`[${++i}/${steps}] Compiling source code...`);
+
+ let buildFlags = "";
+ if (pretty) buildFlags += "--pretty ";
+
+ try {
+ execSync(
+ 'node "' +
+ path.join(__dirname, "..", "node_modules", "typescript", "lib", "tsc.js") +
+ '" -p "' +
+ path.join(__dirname, "..") +
+ '" ' +
+ buildFlags,
+ {
+ cwd: path.join(__dirname, ".."),
+ shell: true,
+ env: process.env,
+ encoding: "utf8"
+ }
+ );
+ } catch (error) {
+ if (verbose || logerr) {
+ error.stdout.split(/\r?\n/).forEach((line) => {
+ let _line = line.replace("dist/", "", 1);
+ if (!pretty && _line.includes(".ts(")) {
+ //reformat file path for easy jumping
+ _line = _line.replace("(", ":", 1).replace(",", ":", 1).replace(")", "", 1);
+ }
+ console.error(_line);
+ });
+ }
+ console.error(`Build failed! Please check build.log for info!`);
+ if (!silent) {
+ if (pretty) fs.writeFileSync("build.log.ansi", error.stdout);
+ fs.writeFileSync(
+ "build.log",
+ error.stdout.replaceAll(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "")
+ );
+ }
+ throw error;
+ }
+}
+
+console.log(`[${++i}/${steps}] Copying plugin data...`);
+let pluginFiles = walk(pluginDir).filter((x) => !x.endsWith(".ts"));
+pluginFiles.forEach((x) => {
+ fs.copyFileSync(x, x.replace("src", "dist"));
+});
diff --git a/src/Server.ts b/src/Server.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/plugins/example-plugin/ExamplePlugin.ts b/src/plugins/example-plugin/ExamplePlugin.ts
new file mode 100644
index 000000000..e6f706574
--- /dev/null
+++ b/src/plugins/example-plugin/ExamplePlugin.ts
@@ -0,0 +1,7 @@
+import { Plugin } from "@fosscord/util";
+
+export default class TestPlugin extends Plugin {
+ onPluginLoaded(): void {
+ console.log("Hello from test plugin! IT WORKS!!!!!!!");
+ }
+}
\ No newline at end of file
diff --git a/src/plugins/example-plugin/plugin.json b/src/plugins/example-plugin/plugin.json
new file mode 100644
index 000000000..2fcb7a008
--- /dev/null
+++ b/src/plugins/example-plugin/plugin.json
@@ -0,0 +1,10 @@
+{
+ "id": "example-plugin",
+ "name": "Fosscord example plugin",
+ "authors": [
+ "The Arcane Brony"
+ ],
+ "repository": "https://github.com/fosscord/fosscord-server",
+ "license": "",
+ "index": "ExamplePlugin.js"
+}
diff --git a/src/util/index.ts b/src/util/index.ts
index 9a84d1af2..802e6f5b2 100644
--- a/src/util/index.ts
+++ b/src/util/index.ts
@@ -1,17 +1,17 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
-
+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
-
+
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
@@ -29,3 +29,5 @@ export * from "./imports";
export * from "./config";
export * from "./connections";
export * from "./Signing";
+export * from "./schemas";
+export * from "./plugin";
\ No newline at end of file
diff --git a/src/util/plugin/Plugin.ts b/src/util/plugin/Plugin.ts
index 96e058436..1c86a0067 100644
--- a/src/util/plugin/Plugin.ts
+++ b/src/util/plugin/Plugin.ts
@@ -6,7 +6,14 @@ type PluginEvents = {
loaded: () => void;
};
-export class Plugin extends (EventEmitter as new () => TypedEventEmitter) {
+//this doesnt work, check later:
+ //(EventEmitter as new () => TypedEventEmitter) {
+export class Plugin extends EventEmitter {
+ private _untypedOn = this.on
+ private _untypedEmit = this.emit
+ public on = (event: K, listener: PluginEvents[K]): this => this._untypedOn(event, listener)
+ public emit = (event: K, ...args: Parameters): boolean => this._untypedEmit(event, ...args)
+
async init() {
// insert default config into database?
}
diff --git a/src/util/plugin/PluginLoader.ts b/src/util/plugin/PluginLoader.ts
index b46ef269a..e69cb4998 100644
--- a/src/util/plugin/PluginLoader.ts
+++ b/src/util/plugin/PluginLoader.ts
@@ -2,7 +2,7 @@ import path from "path";
import fs from "fs";
import { Plugin, PluginManifest } from "./";
-const root = process.env.PLUGIN_LOCATION || "../plugins";
+const root = process.env.PLUGIN_LOCATION || "dist/plugins";
let pluginsLoaded = false;
export class PluginLoader {
@@ -24,7 +24,7 @@ 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, "dist", "index.js")) as Plugin;
+ const module_ = require(path.join(modPath, manifest.index)) as Plugin;
try {
await module_.init();
module_.emit("loaded");
diff --git a/src/util/plugin/PluginManifest.ts b/src/util/plugin/PluginManifest.ts
index b171956b4..01b2b0844 100644
--- a/src/util/plugin/PluginManifest.ts
+++ b/src/util/plugin/PluginManifest.ts
@@ -6,4 +6,5 @@ export class PluginManifest {
license: string;
version: string // semver
versionCode: number // integer
+ index: string;
}
\ No newline at end of file