From 182bccb6522c17dd608f977b7d0ef1d7040ff435 Mon Sep 17 00:00:00 2001 From: PHIDIAS Date: Fri, 17 Jan 2025 19:32:03 +0900 Subject: [PATCH] . --- .gitignore | 4 ++ .npmignore | 0 CHANGELOG.md | 6 +++ README.md | 6 ++- controller.js | 102 ++++++++++++++++++++++++++++++++++++++++++++++ info.js | 70 +++++++++++++++++++++++++++++++ instance.js | 19 +++++++++ package.json | 41 +++++++++++++++++++ web/index.jsx | 0 webpack.config.js | 28 +++++++++++++ 10 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 CHANGELOG.md create mode 100644 controller.js create mode 100644 info.js create mode 100644 instance.js create mode 100644 package.json create mode 100644 web/index.jsx create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b816ab1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# npm stuff +/node_modules/ +/package-lock.json +/dist diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e69de29 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c30e44c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## v1.0.0 + +- Initial + diff --git a/README.md b/README.md index 39c9f8c..ec47f0e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ -# ClusterChatSync -Clusterio plugin - One way discord chat sync +Cluster Chat Sync +======================== + +This is a custom chat sync, one-way only at this time. diff --git a/controller.js b/controller.js new file mode 100644 index 0000000..83cfb0c --- /dev/null +++ b/controller.js @@ -0,0 +1,102 @@ +"use strict"; +const Discord = require("discord.js"); +const { BaseControllerPlugin } = require("@clusterio/controller"); +const { InstanceActionEvent } = require("./info.js"); + +class ControllerPlugin extends BaseControllerPlugin { + async init() { + this.controller.config.on("fieldChanged", (field, curr, prev) => { + if (field === "chat_sync.discord_bot_token") { + this.connect().catch(err => { this.logger.error(`Unexpected error:\n${err.stack}`); }); + } + }); + + this.controller.handle(InstanceActionEvent, this.handleInstanceAction.bind(this)); + this.client = null; + await this.connect(); + } + + async connect() { + if (this.client) { + this.client.destroy(); + this.client = null; + } + + let token = this.controller.config.get("chat_sync.discord_bot_token"); + + if (!token) { + this.logger.warn("chat sync bot token not configured, so chat is offline"); + return; + } + + this.client = new Discord.Client({ + intents: [ + Discord.GatewayIntentBits.Guilds, + Discord.GatewayIntentBits.GuildMessages, + Discord.GatewayIntentBits.MessageContent, + ], + }); + + this.logger.info("chat sync is logging in to Discord"); + + try { + await this.client.login(this.controller.config.get("chat_sync.discord_bot_token")); + } catch (err) { + this.logger.error(`chat sync have error logging in to discord, chat is offline:\n${err.stack}`); + this.client.destroy(); + this.client = null; + return; + } + + this.logger.info("chat sync have successfully logged in"); + } + + async onShutdown() { + if (this.client) { + this.client.destroy(); + this.client = null; + } + } + + async handleInstanceAction(request, src) { + if (request.action === "CHAT" || request.action === "SHOUT") { + const channel_id = this.controller.config.get("chat_sync.discord_channel_mapping")[this.controller.instances.get(src.id) ?? ""]; + let channel = null; + + if (!channel_id) { + return; + } + + try { + channel = await this.client.channels.fetch(channel_id); + } catch (err) { + if (err.code !== 10003) { + throw err; + } + } + + if (channel === null) { + this.logger.error(`chat sync discord hannel ID ${channel_id} was not found`); + return; + } + + const nrc = request.content.replace(/\[special-item=.*?\]/g, '').replace(/<@/g, '<@\u200c>'); + const nrc_index = nrc.indexOf(":"); + const nrc_username = nrc.substring(0, nrc_index); + const nrc_message = nrc.substring(nrc_index + 1).trim(); + const nrc_msg = `**\`${nrc_username}\`** ${nrc_message}` + + if (this.controller.config.get("chat_sync.datetime_on_message")) { + let now = new Date(); + let dt = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`; + await channel.send(`${dt} ${nrc_msg}`, { allowedMentions: { parse: [] }}); + } else { + await channel.send(nrc_msg, { allowedMentions: { parse: [] }}); + } + } + } +} + +module.exports = { + ControllerPlugin +}; diff --git a/info.js b/info.js new file mode 100644 index 0000000..0fe6719 --- /dev/null +++ b/info.js @@ -0,0 +1,70 @@ +"use strict"; +const lib = require("@clusterio/lib"); + +class InstanceActionEvent { + static type = "event"; + static src = "instance"; + static dst = "controller"; + static plugin = "chat_sync"; + + constructor(instanceName, action, content) { + this.instanceName = instanceName; + this.action = action; + this.content = content; + } + + static jsonSchema = { + type: "object", + required: ["instanceName", "action", "content"], + properties: { + "instanceName": { type: "string" }, + "action": { type: "string" }, + "content": { type: "string" }, + }, + }; + + static fromJSON(json) { + return new this(json.instanceName, json.action, json.content); + } +} + +const plugin = { + name: "chat_sync", + title: "Chat Sync", + description: "One way chat sync.", + instanceEntrypoint: "instance", + controllerEntrypoint: "controller", + controllerConfigFields: { + "chat_sync.discord_bot_token": { + title: "Discord Bot Token", + description: "API Token", + type: "string", + optional: true, + }, + "chat_sync.datetime_on_message": { + title: "Message Datetime", + description: "Append datetime in front", + type: "boolean", + initialValue: true, + optional: true, + }, + "chat_sync.discord_channel_mapping": { + title: "Channels", + description: "Putting the discord channel id and instance relations here", + type: "object", + initialValue: { + "S1": "123", + "S2": "123", + }, + }, + }, + + messages: [ + InstanceActionEvent + ], +}; + +module.exports = { + plugin, + InstanceActionEvent +}; diff --git a/instance.js b/instance.js new file mode 100644 index 0000000..93f6749 --- /dev/null +++ b/instance.js @@ -0,0 +1,19 @@ +"use strict"; +const lib = require("@clusterio/lib"); +const { BaseInstancePlugin } = require("@clusterio/host"); + +class InstancePlugin extends BaseInstancePlugin { + async init() { + this.messageQueue = []; + } + + async onOutput(output) { + if (output.type == "action") { + this.messageQueue.push([output.action, output.message]); + } + } +} + +module.exports = { + InstancePlugin +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..0b75cfe --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "@phidias0303/chat_sync", + "version": "1.0.0", + "description": "One way chat sync", + "main": "info.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "prepare": "webpack-cli --env production" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/PHIDIAS0303" + }, + "keywords": [ + "clusterio", + "factorio", + "discord" + ], + "author": "PHIDIAS ", + "license": "MIT", + "bugs": { + "url": "https://github.com/PHIDIAS0303/issues" + }, + "homepage": "https://github.com/PHIDIAS0303#readme", + "peerDependencies": { + "@clusterio/lib": "^2.0.0-alpha.14" + }, + "devDependencies": { + "@clusterio/lib": "^2.0.0-alpha.14", + "@clusterio/web_ui": "^2.0.0-alpha.14", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4", + "webpack-merge": "^5.9.0" + }, + "dependencies": { + "discord.js": "^14.14.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/web/index.jsx b/web/index.jsx new file mode 100644 index 0000000..e69de29 diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..d0c5063 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,28 @@ +"use strict"; +const path = require("path"); +const webpack = require("webpack"); +const { merge } = require("webpack-merge"); + +const common = require("@clusterio/web_ui/webpack.common"); + +module.exports = (env = {}) => merge(common(env), { + context: __dirname, + entry: "./web/index.jsx", + output: { + path: path.resolve(__dirname, "dist", "web"), + }, + plugins: [ + new webpack.container.ModuleFederationPlugin({ + name: "chat_sync", + library: { type: "var", name: "plugin_chat_sync" }, + exposes: { + "./": "./info.js", + "./package.json": "./package.json", + }, + shared: { + "@clusterio/lib": { import: false }, + "@clusterio/web_ui": { import: false }, + }, + }), + ], +});