diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..14b98b1 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,5 @@ +nodeLinker: node-modules + +plugins: + - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs + spec: "@yarnpkg/plugin-typescript" diff --git a/build.js b/build.js new file mode 100644 index 0000000..3d9ac77 --- /dev/null +++ b/build.js @@ -0,0 +1,9 @@ +import esbuild from "esbuild"; + +esbuild.buildSync({ + entryPoints: ["src/index.ts"], + outfile: "dist/discovery-irc.js", + format: "cjs", + bundle: true, + platform: "node", +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..c44ced0 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "@lumeweb/relay-plugin-discovery-irc", + "version": "0.1.0", + "type": "module", + "scripts": { + "build": "node build.js" + }, + "devDependencies": { + "@lumeweb/peer-discovery": "https://git.lumeweb.com/LumeWeb/peer-discovery.git", + "@lumeweb/relay-types": "https://git.lumeweb.com/LumeWeb/relay-types.git", + "@types/b4a": "^1.6.0", + "@types/prettier": "^2", + "esbuild": "^0.16.10", + "prettier": "^2.8.1", + "rollup": "^3.7.5" + }, + "dependencies": { + "@ctrl/irc": "^2.1.0", + "b4a": "^1.6.1", + "json-stringify-deterministic": "^1.0.8" + } +} diff --git a/pkg/discovery-irc.json b/pkg/discovery-irc.json new file mode 100644 index 0000000..8380712 --- /dev/null +++ b/pkg/discovery-irc.json @@ -0,0 +1,13 @@ +{ + "plugin": { + "discoveryirc": { + "networks": [ + { + "host": "irc.ergo.chat", + "port": 6697, + "ssl" : true + } + ] + } + } +} diff --git a/pkg/load-discovery-irc.json b/pkg/load-discovery-irc.json new file mode 100644 index 0000000..71a35e4 --- /dev/null +++ b/pkg/load-discovery-irc.json @@ -0,0 +1,3 @@ +{ + "plugins": ["discovery-irc"] +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..dfda376 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,68 @@ +import type { Plugin, PluginAPI } from "@lumeweb/relay-types"; +import type { Peer } from "@lumeweb/peer-discovery"; +import { IrcClient } from "@ctrl/irc"; +import b4a from "b4a"; +import jsonStringify from "json-stringify-deterministic"; +import type { Message } from "@ctrl/irc/dist/src/parseMessage"; + +interface SignedPeerResponse extends Peer { + timestamp: number; + signature?: string; +} + +interface Network { + host: string; + port: number; + ssl: boolean; +} + +const plugin: Plugin = { + name: "discovery-irc", + async plugin(api: PluginAPI): Promise { + for (const network of api.pluginConfig.array("networks", []) as Network[]) { + let client = new IrcClient(undefined, api.identity.fingerprintHex, { + host: network.host, + port: network.port, + secure: network.ssl, + channels: ["#lumeweb"], + }); + client.addListener("message", function (from, to, message) { + const pubkey = b4a.from(message, "hex"); + if (!b4a.equals(api.identity.publicKeyRaw, pubkey)) { + return; + } + + const host = api.config.str("domain"); + const port = api.config.uint("port"); + const timestamp = Date.now(); + + let data = { + host, + port, + timestamp, + } as SignedPeerResponse; + + let json = jsonStringify(data); + + data.signature = b4a + .from(api.identity.sign(b4a.from(jsonStringify(data)))) + .toString("hex"); + + api.logger.info(api.identity.verify(b4a.from(json), data.signature)); + + client.say(from, jsonStringify(data)); + }); + client.addListener("error", (message: Message) => { + api.logger.error(message); + }); + + client.connect(); + + process.on("SIGTERM", () => { + client.end(); + }); + } + }, +}; + +export default plugin; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..09c3093 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "esnext", + "esModuleInterop": true, + "outDir": "dist", + "moduleResolution": "node" + }, + "include": ["src"], + "exclude": [ + "node_modules" + ] +}