diff --git a/controller.js b/controller.js new file mode 100644 index 0000000..9d94d06 --- /dev/null +++ b/controller.js @@ -0,0 +1,193 @@ +'use strict'; +const Discord = require('discord.js'); +const fetch = require('node-fetch'); +const {BaseControllerPlugin} = require('@clusterio/controller'); +const {InstanceActionEvent} = require('./info.js'); +const {ChatEvent} = require("./message.js"); + +const MAX_DISCORD_MESSAGE_LENGTH = 1950; +const MIN_CONFIDENCE_SCORE = 10.0; + +class LibreTranslateAPI { + constructor(url, apiKey, logger = console) { + if (!url || !apiKey) this.logger.error('[Chat Sync] LibreTranslate API configuration is incomplete.'); + try {new URL(url);} catch {this.logger.error('[Chat Sync] LibreTranslate url is invalid');} + this.url = url.endsWith('/') ? url : url + '/'; + this.apiKey = apiKey; + this.logger = logger; + } + + async handleResponse(response) { + if (!response.ok) this.logger.error(`[Chat Sync] API Request got HTTP ${response.status}`); + return response.json(); + } + + async init() { + try { + this.allowedLanguages = (await this.handleResponse(await fetch(`${this.url}languages?api_key=${this.apiKey}`, {method: 'GET'})))?.[0]?.targets || []; + } catch (err) { + this.logger.error(`[Chat Sync] failed to initialize languages:\n${err.stack}`); + } + } + + async translateRequest(q, source, target) { + try { + return (await this.handleResponse(await fetch(`${this.url}translate`, {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({q: q, api_key: this.apiKey, source: source, target: target})})))?.translatedText; + } catch (err) { + this.logger.error(`[Chat Sync] Translation failed:\n${err.stack}`); + } + } + + async detectLanguage(q) { + try { + return (await this.handleResponse(await fetch(`${this.url}detect`, {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({q: q, api_key: this.apiKey})})))?.[0]; + } catch (err) { + this.logger.error(`[Chat Sync] Detection failed:\n${err.stack}`); + } + } + + async translate(query, targetLanguages) { + console.log(query); + const result = {action: false, passage: []}; + + try { + const detection = await this.detectLanguage(query); + + if (!detection || typeof detection !== 'object' || !detection.confidence || !detection.language) { + this.logger.warn('[Chat Sync] Invalid language detection result:', detection); + return result; + } + + if (detection.confidence > MIN_CONFIDENCE_SCORE) { + for (const targetLang of targetLanguages) { + if (!((detection.language === 'zh-Hans' || detection.language === 'zh-Hant') && (targetLang === 'zh-Hans' || targetLang === 'zh-Hant')) && detection.language !== targetLang && this.allowedLanguages.includes(detection.language) && this.allowedLanguages.includes(targetLang)) { + result.action = true; + const translated = await this.translateRequest(query, detection.language, targetLang); + result.passage.push(`[${detection.language} -> ${targetLang}] ${translated}`); + } + } + } + + return result; + } catch (err) { + this.logger.error(`[Chat Sync] translation failed:\n${err.stack}`); + } + } +} + +class ControllerPlugin extends BaseControllerPlugin { + async init() { + this.controller.config.on('fieldChanged', (field, curr, prev) => { + if (field === 'ClusterChatSync.discord_bot_token') { + this.connect().catch(err => { + this.logger.error(`[Chat Sync] Discord bot token:\n${err.stack}`); + }); + } + }); + this.controller.handle(InstanceActionEvent, this.handleInstanceAction.bind(this)); + this.client = null; + await this.connect(); + } + + async clientDestroy() { + if (this.client) { + this.client.destroy(); + this.client = null; + } + } + + async connect() { + await this.clientDestroy(); + + if (!this.controller.config.get('ClusterChatSync.discord_bot_token')) { + this.logger.error('[Chat Sync] Discord bot token not configured.'); + return; + } + + this.client = new Discord.Client({intents: [Discord.GatewayIntentBits.Guilds, Discord.GatewayIntentBits.GuildMessages, Discord.GatewayIntentBits.MessageContent]}); + this.logger.info('[Chat Sync] Logging into Discord.'); + + try { + await this.client.login(this.controller.config.get('ClusterChatSync.discord_bot_token')); + } catch (err) { + this.logger.error(`[Chat Sync] Discord login error:\n${err.stack}`); + await this.clientDestroy(); + return; + } + + this.logger.info('[Chat Sync] Logged in Discord successfully.'); + + if (this.controller.config.get('ClusterChatSync.use_libretranslate')) { + this.translator = new LibreTranslateAPI(this.controller.config.get('ClusterChatSync.libretranslate_url'), this.controller.config.get('ClusterChatSync.libretranslate_key'), this.logger); + await this.translator.init(); + this.translator_language = this.controller.config.get('ClusterChatSync.libretranslate_language').trim().split(/\s+/) || ['zh-Hant', 'en']; + } + } + + async onShutdown() { + await this.clientDestroy(); + } + + async sendMessage(request, nrc_msg) { + const channel_id = this.controller.config.get('ClusterChatSync.discord_channel_mapping')[request.instanceName]; + if (!channel_id) return; + let channel; + + try { + channel = await this.client.channels.fetch(channel_id); + + if (channel === null) { + this.logger.error(`[Chat Sync] Discord Channel ID ${channel_id} not found.`); + return; + } + } catch (err) { + if (err.code !== 10003) { + this.logger.error(`[Chat Sync] Discord channel fetch error:\n${err.stack}`); + } + } + + if (this.controller.config.get('ClusterChatSync.datetime_on_message')) { + let now = new Date(); + nrc_msg = `${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')} ${nrc_msg}` + } + + if (nrc_msg.length <= MAX_DISCORD_MESSAGE_LENGTH) { + await channel.send(nrc_msg, {allowedMentions: {parse: []}}); + } else { + while (nrc_msg.length > 0) { + let nrc_cmsg = nrc_msg.slice(0, MAX_DISCORD_MESSAGE_LENGTH); + let nrc_lindex = nrc_cmsg.lastIndexOf(' '); + + if (nrc_lindex !== -1) { + nrc_cmsg = nrc_cmsg.slice(0, nrc_lindex); + nrc_msg = nrc_msg.slice(nrc_lindex).trim(); + } else { + nrc_msg = nrc_msg.slice(MAX_DISCORD_MESSAGE_LENGTH).trim(); + } + + await channel.send(nrc_cmsg, {allowedMentions: {parse: []}}); + } + } + } + + async handleInstanceAction(request, src) { + if (request.action === 'CHAT' || request.action === 'SHOUT') { + 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(); + await this.sendMessage(request, `**\`${nrc_username}\`**: ${nrc_message}`); + + if (this.controller.config.get('ClusterChatSync.use_libretranslate')) { + const result = await this.translator.translate(nrc_message, this.translator_language); + + if (result && result.action) { + await this.sendMessage(request, `**\`${nrc_username}\`**: ${result.passage}`); + this.instance.sendTo({ instanceId: this.instance.id }, new ChatEvent(this.instance.name, `[color=255,255,255]\`${nrc_username}\`: ${result}[/color]`)); + } + } + } + } +} + +module.exports = {ControllerPlugin}; diff --git a/controller.ts b/controller.ts deleted file mode 100644 index 2a2fdde..0000000 --- a/controller.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { Client, GatewayIntentBits } from 'discord.js'; -import fetch from 'node-fetch'; -import { BaseControllerPlugin } from '@clusterio/controller'; -import { InstanceActionEvent } from './info'; -import { ChatEvent } from './message'; - -const MAX_DISCORD_MESSAGE_LENGTH = 1950; -const MIN_CONFIDENCE_SCORE = 10.0; - -interface TranslationResult { - action: boolean; - passage: string[]; -} - -interface LanguageDetection { - confidence: number; - language: string; - [key: string]: unknown; -} - -class LibreTranslateAPI { - private url: string; - private apiKey: string; - private logger: Console; - private allowedLanguages: string[] = []; - - constructor(url: string, apiKey: string, logger: Console = console) { - if (!url || !apiKey) { - logger.error('[Chat Sync] LibreTranslate API configuration is incomplete.'); - } - try { - new URL(url); - } catch { - logger.error('[Chat Sync] LibreTranslate url is invalid'); - } - this.url = url.endsWith('/') ? url : url + '/'; - this.apiKey = apiKey; - this.logger = logger; - } - - private async handleResponse(response: fetch.Response): Promise { - if (!response.ok) { - this.logger.error(`[Chat Sync] API Request got HTTP ${response.status}`); - } - return response.json(); - } - - async init(): Promise { - try { - const languages = await this.handleResponse( - await fetch(`${this.url}languages?api_key=${this.apiKey}`, { method: 'GET' }) - ); - this.allowedLanguages = languages?.[0]?.targets || []; - } catch (err) { - this.logger.error(`[Chat Sync] failed to initialize languages:\n${err.stack}`); - } - } - - async translateRequest(q: string, source: string, target: string): Promise { - try { - const response = await this.handleResponse( - await fetch(`${this.url}translate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ q, api_key: this.apiKey, source, target }) - }) - ); - return response?.translatedText; - } catch (err) { - this.logger.error(`[Chat Sync] Translation failed:\n${err.stack}`); - } - } - - async detectLanguage(q: string): Promise { - try { - const response = await this.handleResponse( - await fetch(`${this.url}detect`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ q, api_key: this.apiKey }) - }) - ); - return response?.[0]; - } catch (err) { - this.logger.error(`[Chat Sync] Detection failed:\n${err.stack}`); - } - } - - async translate(query: string, targetLanguages: string[]): Promise { - const result: TranslationResult = { action: false, passage: [] }; - try { - const detection = await this.detectLanguage(query); - if (!detection || typeof detection !== 'object' || !detection.confidence || !detection.language) { - this.logger.warn('[Chat Sync] Invalid language detection result:', detection); - return result; - } - - if (detection.confidence > MIN_CONFIDENCE_SCORE) { - for (const targetLang of targetLanguages) { - if ( - !((detection.language === 'zh-Hans' || detection.language === 'zh-Hant') && - (targetLang === 'zh-Hans' || targetLang === 'zh-Hant')) && - detection.language !== targetLang && - this.allowedLanguages.includes(detection.language) && - this.allowedLanguages.includes(targetLang) - ) { - result.action = true; - const translated = await this.translateRequest(query, detection.language, targetLang); - if (translated) { - result.passage.push(`[${detection.language} -> ${targetLang}] ${translated}`); - } - } - } - } - } catch (err) { - this.logger.error(`[Chat Sync] translation failed:\n${err.stack}`); - } - return result; - } -} - -interface ControllerConfig { - get(key: string): any; - on(event: string, callback: (field: string, curr: any, prev: any) => void); -} - -interface InstanceInfo { - name: string; - id: number; - sendTo(target: string | { instanceId: number }, message: any): void; -} - -export class ControllerPlugin extends BaseControllerPlugin { - private client: Client | null = null; - private translator?: LibreTranslateAPI; - private translator_language: string[] = []; - - constructor( - private controller: { - config: ControllerConfig; - handle(event: any, handler: Function): void; - logger: Console; - }, - private instance: InstanceInfo - ) { - super(); - } - - async init(): Promise { - this.controller.config.on('fieldChanged', (field: string, curr: any, prev: any) => { - if (field === 'ClusterChatSync.discord_bot_token') { - this.connect().catch(err => { - this.controller.logger.error(`[Chat Sync] Discord bot token:\n${err.stack}`); - }); - } - }); - this.controller.handle(InstanceActionEvent, this.handleInstanceAction.bind(this)); - await this.connect(); - } - - private async clientDestroy(): Promise { - if (this.client) { - this.client.destroy(); - this.client = null; - } - } - - private async connect(): Promise { - await this.clientDestroy(); - const token = this.controller.config.get('ClusterChatSync.discord_bot_token'); - if (!token) { - this.controller.logger.error('[Chat Sync] Discord bot token not configured.'); - return; - } - - this.client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent - ] - }); - - this.controller.logger.info('[Chat Sync] Logging into Discord.'); - try { - await this.client.login(token); - } catch (err) { - this.controller.logger.error(`[Chat Sync] Discord login error:\n${err.stack}`); - await this.clientDestroy(); - return; - } - - this.controller.logger.info('[Chat Sync] Logged in Discord successfully.'); - - if (this.controller.config.get('ClusterChatSync.use_libretranslate')) { - this.translator = new LibreTranslateAPI( - this.controller.config.get('ClusterChatSync.libretranslate_url'), - this.controller.config.get('ClusterChatSync.libretranslate_key'), - this.controller.logger - ); - await this.translator.init(); - this.translator_language = this.controller.config.get('ClusterChatSync.libretranslate_language') - .trim() - .split(/\s+/) || ['zh-Hant', 'en']; - } - } - - async onShutdown(): Promise { - await this.clientDestroy(); - } - - private async sendMessage(request: { instanceName: string }, message: string): Promise { - if (!this.client) return; - - const channelMapping = this.controller.config.get('ClusterChatSync.discord_channel_mapping'); - const channel_id = channelMapping[request.instanceName]; - if (!channel_id) return; - - try { - const channel = await this.client.channels.fetch(channel_id); - if (!channel || !channel.isTextBased()) { - this.controller.logger.error(`[Chat Sync] Discord Channel ID ${channel_id} not found or not text channel.`); - return; - } - - if (this.controller.config.get('ClusterChatSync.datetime_on_message')) { - const now = new Date(); - const timestamp = `${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')}`; - message = `${timestamp} ${message}`; - } - - while (message.length > 0) { - let chunk = message.slice(0, MAX_DISCORD_MESSAGE_LENGTH); - const lastSpace = chunk.lastIndexOf(' '); - - if (lastSpace !== -1 && chunk.length === MAX_DISCORD_MESSAGE_LENGTH) { - chunk = chunk.slice(0, lastSpace); - message = message.slice(lastSpace).trim(); - } else { - message = message.slice(chunk.length).trim(); - } - - await channel.send(chunk, { allowedMentions: { parse: [] } }); - } - } catch (err: any) { - if (err.code !== 10003) { // Unknown Channel error - this.controller.logger.error(`[Chat Sync] Discord channel error:\n${err.stack}`); - } - } - } - - private async handleInstanceAction(request: { - action: string; - content: string; - instanceName: string - }): Promise { - if (request.action !== 'CHAT' && request.action !== 'SHOUT') return; - - const sanitizedContent = request.content - .replace(/\[special-item=.*?\]/g, '') - .replace(/<@/g, '<@\u200c>'); - const colonIndex = sanitizedContent.indexOf(':'); - const username = sanitizedContent.substring(0, colonIndex); - const message = sanitizedContent.substring(colonIndex + 1).trim(); - - await this.sendMessage(request, `**\`${username}\`**: ${message}`); - - if (this.translator && this.controller.config.get('ClusterChatSync.use_libretranslate')) { - const result = await this.translator.translate(message, this.translator_language); - if (result?.action) { - await this.sendMessage(request, `**\`${username}\`**: ${result.passage.join('\n')}`); - this.instance.sendTo( - { instanceId: this.instance.id }, - new ChatEvent(this.instance.name, `[color=255,255,255]\`${username}\`: ${result.passage.join('\n')}[/color]`) - ); - } - } - } -} diff --git a/info.js b/info.js new file mode 100644 index 0000000..a3652cc --- /dev/null +++ b/info.js @@ -0,0 +1,81 @@ +'use strict'; +const lib = require('@clusterio/lib'); + +class InstanceActionEvent { + static type = 'event'; + static src = 'instance'; + static dst = 'controller'; + static plugin = 'ClusterChatSync'; + + 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: 'ClusterChatSync', + title: 'Cluster Chat Sync', + description: 'One way chat sync.', + instanceEntrypoint: 'instance', + controllerEntrypoint: 'controller', + controllerConfigFields: { + 'ClusterChatSync.discord_bot_token': { + title: 'Discord Bot Token', + description: 'API Token', + type: 'string' + }, + 'ClusterChatSync.datetime_on_message': { + title: 'Message Datetime', + description: 'Append datetime in front', + type: 'boolean', + initialValue: true + }, + 'ClusterChatSync.discord_channel_mapping': { + title: 'Channels', + description: 'Putting the discord channel id and instance relations here', + type: 'object', + initialValue: { + 'S1': '123' + }, + }, + 'ClusterChatSync.use_libretranslate': { + title: 'Translate Message', + description: 'Using self host or paid service of libretranslate', + type: 'boolean', + initialValue: false + }, + 'ClusterChatSync.libretranslate_url': { + title: 'Translate Server URL', + description: 'Including http protocol, and the port if needed', + type: 'string', + initialValue: 'http://localhost:5000' + }, + 'ClusterChatSync.libretranslate_key': { + title: 'Translate Server API Key', + description: 'The API key for the translate server', + type: 'string', + initialValue: '123456' + }, + 'ClusterChatSync.libretranslate_language': { + title: 'Translate Server Target Language', + description: 'Put a space between each language, using ISO 639-1 codes', + type: 'string', + initialValue: 'zh-Hant en' + }, + }, + messages: [InstanceActionEvent], +}; + +module.exports = {plugin, InstanceActionEvent}; diff --git a/info.ts b/info.ts deleted file mode 100644 index 71798c3..0000000 --- a/info.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { PluginConfigFields, SerializedEvent } from '@clusterio/lib'; - -export class InstanceActionEvent { - static type = 'event'; - static src = 'instance'; - static dst = 'controller'; - static plugin = 'ClusterChatSync'; - - instanceName: string; - action: string; - content: string; - - constructor(instanceName: string, action: string, content: string) { - this.instanceName = instanceName; - this.action = action; - this.content = content; - } - - static jsonSchema = { - type: 'object', - required: ['instanceName', 'action', 'content'] as const, - properties: { - instanceName: { type: 'string' }, - action: { type: 'string' }, - content: { type: 'string' } - } - }; - - static fromJSON(json: SerializedEvent): InstanceActionEvent { - return new this(json.instanceName, json.action, json.content); - } -} - -interface ChannelMapping { - [instanceName: string]: string; -} - -const pluginConfigFields: PluginConfigFields = { - 'ClusterChatSync.discord_bot_token': { - title: 'Discord Bot Token', - description: 'API Token', - type: 'string', - }, - 'ClusterChatSync.datetime_on_message': { - title: 'Message Datetime', - description: 'Append datetime in front', - type: 'boolean', - initialValue: true, - }, - 'ClusterChatSync.discord_channel_mapping': { - title: 'Channels', - description: 'Putting the discord channel id and instance relations here', - type: 'object', - initialValue: { - 'S1': '123' - } as ChannelMapping, - }, - 'ClusterChatSync.use_libretranslate': { - title: 'Translate Message', - description: 'Using self host or paid service of libretranslate', - type: 'boolean', - initialValue: false, - }, - 'ClusterChatSync.libretranslate_url': { - title: 'Translate Server URL', - description: 'Including http protocol, and the port if needed', - type: 'string', - initialValue: 'http://localhost:5000', - }, - 'ClusterChatSync.libretranslate_key': { - title: 'Translate Server API Key', - description: 'The API key for the translate server', - type: 'string', - initialValue: '123456', - }, - 'ClusterChatSync.libretranslate_language': { - title: 'Translate Server Target Language', - description: 'Put a space between each language, using ISO 639-1 codes', - type: 'string', - initialValue: 'zh-Hant en', - }, -}; - -export const plugin = { - name: 'ClusterChatSync', - title: 'Cluster Chat Sync', - description: 'One way chat sync.', - instanceEntrypoint: 'instance', - controllerEntrypoint: 'controller', - controllerConfigFields: pluginConfigFields, - messages: [InstanceActionEvent], -}; diff --git a/instance.js b/instance.js new file mode 100644 index 0000000..5e2e83d --- /dev/null +++ b/instance.js @@ -0,0 +1,34 @@ +"use strict"; +const lib = require("@clusterio/lib"); +const {BaseInstancePlugin} = require("@clusterio/host"); +const {InstanceActionEvent} = require("./info.js"); + +class InstancePlugin extends BaseInstancePlugin { + async init() { + this.messageQueue = []; + } + + onControllerConnectionEvent(event) { + if (event === 'connect') { + for (const [action, content] of this.messageQueue) { + try { + this.instance.sendTo('controller', new InstanceActionEvent(this.instance.name, action, content)); + } catch (err) { + this.messageQueue.push([output.action, output.message]); + } + } + this.messageQueue = []; + } + } + + async onOutput(output) { + if (output.type !== 'action') return; + if (this.host.connector.connected) { + this.instance.sendTo('controller', new InstanceActionEvent(this.instance.name, output.action, output.message)); + } else { + this.messageQueue.push([output.action, output.message]); + } + } +} + +module.exports = {InstancePlugin}; diff --git a/instance.ts b/instance.ts deleted file mode 100644 index 4a5cc44..0000000 --- a/instance.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as lib from "@clusterio/lib"; -import { BaseInstancePlugin } from "@clusterio/host"; -import { InstanceActionEvent } from "./info"; - -type MessageQueueItem = [string, unknown]; // [action, content] -type ControllerEvent = 'connect' | 'disconnect' | string; -type OutputMessage = { - type: string; - action: string; - message: unknown; -}; - -export class InstancePlugin extends BaseInstancePlugin { - private messageQueue: MessageQueueItem[] = []; - - async init(): Promise { - this.messageQueue = []; - } - - onControllerConnectionEvent(event: ControllerEvent): void { - if (event === 'connect') { - for (const [action, content] of this.messageQueue) { - try { - this.instance.sendTo('controller', - new InstanceActionEvent(this.instance.name, action, content)); - } catch (err) { - this.messageQueue.push([action, content]); - - // Optional: Log the error - console.error('Failed to send queued message:', err); - } - } - this.messageQueue = []; - } - } - - async onOutput(output: OutputMessage): Promise { - if (output.type !== 'action') return; - - if (this.host.connector.connected) { - this.instance.sendTo('controller', - new InstanceActionEvent(this.instance.name, output.action, output.message)); - } else { - this.messageQueue.push([output.action, output.message]); - } - } -} diff --git a/message.js b/message.js new file mode 100644 index 0000000..4408e2e --- /dev/null +++ b/message.js @@ -0,0 +1,33 @@ +// import { Type, Static } from "@sinclair/typebox"; +const {Type, Static} = require("@sinclair/typebox"); + +export class ChatEvent { + // declare ["constructor"]: typeof ChatEvent; + // as const + static type = "event"; + // as const + static src = ["control", "instance"]; + // as const + static dst = "instance"; + // as const + static plugin = "global_chat"; + static permission = null; + + /* + constructor( + public instanceName: string, + public content: string, + ) { + } + */ + + static jsonSchema = Type.Object({ + "instanceName": Type.String(), + "content": Type.String(), + }); + + // json: Static + static fromJSON(json) { + return new this(json.instanceName, json.content); + } +} \ No newline at end of file diff --git a/message.ts b/message.ts deleted file mode 100644 index a6c8b5b..0000000 --- a/message.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Type, Static } from "@sinclair/typebox"; - -export class ChatEvent { - declare ["constructor"]: typeof ChatEvent; - static type = "event" as const; - static src = ["control", "instance"] as const; - static dst = "instance" as const; - static plugin = "global_chat" as const; - static permission = null; - - constructor( - public instanceName: string, - public content: string, - ) { - } - - static jsonSchema = Type.Object({ - "instanceName": Type.String(), - "content": Type.String(), - }); - - static fromJSON(json: Static) { - return new this(json.instanceName, json.content); - } -} \ No newline at end of file diff --git a/package.json b/package.json index ac853ae..8deb9ca 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@phidias0303/clusterio_chat_sync", "version": "1.0.2", "description": "One way chat sync", - "main": "info.ts", + "main": "info.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "prepare": "webpack-cli --env production" @@ -25,19 +25,19 @@ }, "homepage": "https://github.com/PHIDIAS0303/ClusterChatSync#readme", "peerDependencies": { - "@clusterio/lib": "^2.0.0-alpha.21" + "@clusterio/lib": "catalog:" }, "devDependencies": { - "@clusterio/lib": "^2.0.0-alpha.21", - "@clusterio/web_ui": "^2.0.0-alpha.21", - "webpack": "^5.99.9", - "webpack-cli": "^6.0.1", - "webpack-merge": "^6.0.1" + "@clusterio/lib": "catalog:", + "@clusterio/web_ui": "catalog:", + "webpack": "catalog:", + "webpack-cli": "catalog:", + "webpack-merge": "catalog:" }, "dependencies": { - "discord.js": "^14.19.3", - "@sinclair/typebox": "^0.30.4", - "node-fetch": "^3.3.2" + "discord.js": "catalog:", + "@sinclair/typebox": "catalog:", + "node-fetch": "catalog:" }, "publishConfig": { "access": "public" diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e54b3c5 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,12 @@ +packages: + - "*" + +catalog: + "@clusterio/lib": ^2.0.0-alpha.21 + "@clusterio/web_ui": ^2.0.0-alpha.21 + "@sinclair/typebox": ^0.34.39 + "discord.js": ^14.21.0 + "webpack": ^5.101.2 + "webpack-cli": ^6.0.1 + "webpack-merge": ^6.0.1 + "node-fetch": ^3.3.2 diff --git a/tsconfig.browser.json b/tsconfig.browser.json deleted file mode 100644 index cc77ead..0000000 --- a/tsconfig.browser.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../tsconfig.browser.json", - "include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ], -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index fe5912a..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "files": [], - "references": [ - { "path": "./tsconfig.node.json" } - ] -} \ No newline at end of file diff --git a/tsconfig.node.json b/tsconfig.node.json deleted file mode 100644 index 7087bbf..0000000 --- a/tsconfig.node.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../tsconfig.node.json", - "include": ["./**/*.ts"], - "exclude": ["test/*", "./dist/*"], -} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 6abcf45..13fb006 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,7 +16,7 @@ module.exports = (env = {}) => merge(common(env), { name: 'ClusterChatSync', library: {type: 'var', name: 'plugin_ClusterChatSync' }, exposes: { - './': './info.ts', + './': './info.js', './package.json': './package.json', }, shared: {