From d7c4bc16f406af9a6437d932fbd3ddb57927be8b Mon Sep 17 00:00:00 2001 From: areimann Date: Tue, 30 Jun 2026 00:30:34 +0200 Subject: [PATCH] Initial commit --- .gitignore | 2 + LICENSE | 21 +++++ README.md | 105 ++++++++++++++++++++++++ docs/comment-color-coding.js | 127 +++++++++++++++++++++++++++++ index.js | 153 +++++++++++++++++++++++++++++++++++ package-lock.json | 37 +++++++++ package.json | 24 ++++++ start.bat | 13 +++ start.sh | 21 +++++ 9 files changed, 503 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/comment-color-coding.js create mode 100644 index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 start.bat create mode 100644 start.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6693ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +logs/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8053121 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Andreas R. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0188015 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# WebSocket Test Server + +This is a simple WebSocket test tool designed for verifying connections, testing latency, and measuring Round-Trip Time (RTT). + +--- + +- [WebSocket Test Server](#websocket-test-server) + - [How it works:](#how-it-works) + - [Logging:](#logging) + - [Setup \& Installation:](#setup--installation) + - [How to Run](#how-to-run) + - [Windows](#windows) + - [Linux / MacOS](#linux--macos) + - [Manual Execution (Advanced)](#manual-execution-advanced) + - [Notes](#notes) + - [Recommended Custom Test Servers](#recommended-custom-test-servers) + - [Acknowledgements:](#acknowledgements) + +--- + +## How it works: +- **Dual Mode**: The tester can connect to either the Twitch IRC WebSocket server or a custom WebSocket server (e.g. an echo test server, see [Recommended CTS](#recommended-custom-test-servers) for more). +- **Interactive Start**: Run the starter script [start.bat](#windows) on Windows or [start.sh](#linux--macos) on Linux and choose the run mode and target directly in the terminal. +- **Ping Cycle**: Once connected, the tester will send 5 test PINGs (1 every 5 seconds) to the chosen server: + - *Custom servers*: Sends a `PING` message and waits for the echo response to measure RTT (Real-Time-Text). + - *Twitch*: Sends `PING :tmi.twitch.tv` and logs the incoming Twitch server data. + +--- + +## Logging: +- All sent and received messages, including timestamps and RTTs (in milliseconds), are tracked in real-time. +- After 5 successful ping cycles, a log file is automatically created in the "logs" folder, containing all relevant details. +- The program displays the final log file path and exit automatically after 3 seconds. + +--- + +## Setup & Installation: + +1. **Install Node.js**: + - Download and install [**Node.js**](https://nodejs.org/) + - Verify installation by running `node -v` and `npm -v` in your terminal. + +2. **Prepare the Project**: + - Clone or download this repository and open a terminal in the project folder. + +3. **Install dependencies**: + - Run the following command to install the required packages: + ```bash + npm install + ``` + +--- + +## How to Run + +Instead of typing long commands, use the provided interactive starter scripts: + +### Windows +Double-click `start.bat`or run it via CMD with: +```cmd +start.bat +``` + +### Linux / MacOS +Firstly you have to give the `start.sh` execution permissions with: +```bash +chmod +x start.sh +``` + +Then you can start the script with: +```bash +./start.sh +``` + +### Manual Execution (Advanced) +If you prefer to bypass the interactive scripts, you can run the node application directly: +- Custom server mode (default): `node index.js custom [custom_url]` +- Twitch mode: `node index.js twitch [channel_name]` + +### Notes +- **Defaults**: If you leave the `target` blank in custom mode, it will automatically fallback to `wss://echo.websocket.org` +- **Scope**: This tester is intended purely for development and network diagnostics. It does **NOT** interact with Twitch chat features beyond authentication and standard PING/PONG messages. + +--- + +## Recommended Custom Test Servers + +If you want to test the `custom` mode with different public endpoints, you can use these reliable and free WebSocket echo services: + +- **`wss://echo.websocket.org`** (Default) + - *Provider:* Built-in fallback + - *Behavior:* Standard high-availability echo server that mirrors back any text or PING sent to it. +- **`wss://echo.websocket.in`** + - *Provider:* PieSocket + - *Behavior:* A fast and secure public testing endpoint, ideal for checking raw text frame stability. +- **`wss://ws.postman-echo.com/raw`** + - *Provider:* Postman + - *Behavior:* Part of Postman's official utility stack. It returns an exact copy of the sent payloads and is perfect for latency testing. + +*Note: Make sure your firewall or network security settings allow outgoing connections on port 443 (SSL) for these addresses.* + +--- + +## Acknowledgements: +Thank you for using this tester! I hope it helps you quickly and reliably test your WebSocket servers. Feedback, improvements, or contributions are always welcome. Happy testing! \ No newline at end of file diff --git a/docs/comment-color-coding.js b/docs/comment-color-coding.js new file mode 100644 index 0000000..f41f4c4 --- /dev/null +++ b/docs/comment-color-coding.js @@ -0,0 +1,127 @@ +/* + VS-Code Extension: Colorful Comments + + This Plugin normally only has: + ! Red (!) + ? Blue (?) + * Green (*) + ^ Yellow (^) + & Pink (&) + ~ Purple (~) + todo Mustard (todo) + // Grey (//) + but was customized by me for better and more colors. + + URL: https://marketplace.visualstudio.com/items?itemName=ParthR2031.colorful-comments + + + My Custom Colors: + ! HEX: #FF2D00, Color: RED, Background: NONE, ST: false > file paths and module headers + ? HEX: #0076FF, Color: BLUE, Background: NONE, ST: false > general information / explanations + ?? HEX: #00CCAA, Color: TURQUOISE, Background: NONE, ST: false > usage declaration + + % HEX: #00FFFF, Color: LIGHTBLUE, Background: NONE, ST: false > variable headers + $ HEX: #BFFF00, Color: LIME, Background: NONE, ST: false > function headers + ~ HEX: #BE00FF, Color: PURPLE, Background: NONE, ST: false > in-function headers + § HEX: #808080, Color: GREY, Background: NONE, ST: false > other headers + + TODO HEX: #FFB84D, Color: ORANGE, Background: NONE, ST: false > todo documentation + DONE HEX: #28FF00, Color: LIGHTGREEN, Background: NONE, ST: false > optimization ideas / improvements + = HEX: #FF8C00, Color: ORANGE, Background: NONE, ST: false > temporary solutions + & HEX: #FF06A0, Color: PINK, Background: NONE, ST: false > experimental features / ideas + + ^ HEX: #F8FF00, Color: YELLOW, Background: NONE, ST: false > warnings + BUG HEX: #FF3333, Color: RED, Background: NONE, ST: false > bug documentation + // HEX: #474747, Color: GRAY, Background: NONE, ST: true > removed comments or fixed bugs + + HEX: #5C6370, Color: GRAY, Background: NONE, ST: false > normal comments (based on "Atom One Dark Theme") + + + My settings.json (to change yours, simply go to the plugin, hit the cog-symbol and click on "Edit in settings.json", then find the following section at the bottom of the file and paste my config then reload extensions or restart VSCode): + "colorful-comments.tags": [ + { + "tag": "!", + "color": "#FF2D00", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "??", + "color": "#00CCAA", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "?", + "color": "#0076FF", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "//", + "color": "#474747", + "strikethrough": true, + "backgroundColor": "transparent" + }, + { + "tag": "^", + "color": "#F8FF00", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "*", + "color": "#28FF00", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "&", + "color": "#FF06A0", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "~", + "color": "#BE00FF", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "todo", + "color": "#FFB84D", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "bug", + "color": "#FF3333", + "strikethrough": false, + "backgroundColor": "#330000" + }, + { + "tag": "$", + "color": "#BFFF00", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "%", + "color": "#00FFFF", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "=", + "color": "#FF8C00", + "strikethrough": false, + "backgroundColor": "transparent" + }, + { + "tag": "§", + "color": "#808080", + "strikethrough": false, + "backgroundColor": "transparent" + } + ] +*/ \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..9c94f8c --- /dev/null +++ b/index.js @@ -0,0 +1,153 @@ +// % import nodejs dependencies +const WebSocket = require('ws'); +const fs = require('fs'); +const path = require('path'); + +// % get arguments +const args = process.argv.slice(2); +const mode = args[0] || 'custom'; +const channel = args[1]; + +// % set nickname for twitch testing +const nick = 'justinfan' + Math.floor(Math.random() * 100000); + +// % declare external websocket urls and set mode +const customUrl = channel || 'wss://echo.websocket.org'; +const twitchUrl = 'wss://irc-ws.chat.twitch.tv:443'; +const url = mode === 'twitch' ? twitchUrl : customUrl; +console.log(`▶ Using ${mode} endpoint: ${url}`); + +// $ establish new websocket connection +const ws = new WebSocket(url); + +// % configure websocket settings +let lastPingTimestamp = null; +let testCount = 0; +let logBuffer = []; +let pingTimeout = null; + +// % configure logging +const logDir = path.join(__dirname, 'logs'); +if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true }); +const logFile = path.join(logDir, `wslog_${mode}_${Date.now()}.txt`); + +// $ push log buffer +function addLog(entry) { logBuffer.push(entry); } + +// $ finalize testing script +function finalizeAndExit() { + // % clear ping timeout if exists + if (pingTimeout) clearTimeout(pingTimeout); + + // ~ write log file + fs.writeFileSync(logFile, `=== WebSocket Log (${mode}) at ${new Date().toISOString()} ===\n` + logBuffer.join('\n')); + + // ~ Log success to console + console.log(`\n📂 Log file created: ${logFile}`); + console.log(`✅ Program will close in 3 seconds...`); + + // ~ close the tester + setTimeout(() => process.exit(0), 3000); +} + +// $ activate ping requests +function scheduleTestPing() { + // ~ end script after tester ran 5 times + if (testCount >= 5) return finalizeAndExit(); + + // ~ set a timeout for pings of 5 seconds + pingTimeout = setTimeout(() => { + // % create last ping timestamp + lastPingTimestamp = Date.now(); + + // % create reference for a message + let msg; + + // ~ send a websocket message to a server + if (mode === 'twitch') { + msg = 'PING :tmi.twitch.tv'; + ws.send(msg); + } else { + msg = 'PING'; + ws.send(msg); + } + + // % create a log message + const logMsg = `${new Date().toISOString()} ➡️ Sent ${msg}`; + + // ~ log in console and file + console.log(logMsg); + addLog(logMsg); + + // ~ add a count of +1 to testCount variable + testCount++; + + // ~ run test ping again till counter exceeds 5 + scheduleTestPing(); + }, 5000); +} + +// $ websocket configuration when connection opens +ws.on('open', () => { + // ~ console logging of connection + console.log(`${new Date().toISOString()} ✅ Connected to ${mode}`); + + // ~ configure websocket authentication to twitch + if (mode === 'twitch') { + ws.send('PASS SCHMOOPIIE'); + ws.send(`NICK ${nick}`); + ws.send(`JOIN ${channel}`); + console.log(`${new Date().toISOString()} ➡️ Logged in as ${nick}, Channel: ${channel}`); + } + + // ~ run test ping + scheduleTestPing(); +}); + +// $ websocket configuration on receiving a message +ws.on('message', (data) => { + // % convert message to string and get timestamp + const msg = data.toString(); + const now = new Date().toISOString(); + + // § check active run mode + if (mode === 'custom') { + // § check if a ping arrives + if (msg.toLowerCase().includes('ping')) { + // ~ send a pong + ws.send('PONG'); + + // ~ log message to console and file + const logMsg = `${now} ⬅️ Echo PING received → PONG sent`; + console.log(logMsg); + addLog(logMsg); + + // ~ log receive duration to console and file + const rtt = Date.now() - lastPingTimestamp; + const rttMsg = `${now} ⬅️ PONG received (RTT: ${rtt} ms)`; + console.log(rttMsg); + addLog(rttMsg); + } + } else { + // ~ log everything on twitch mode to console and file + const logMsg = `${now} ⬅️ ${msg.trim()}`; + console.log(logMsg); + addLog(logMsg); + } +}); + +// $ websocket configuration on error +ws.on('error', (err) => { + // ~ log error to console and file + const msg = `${new Date().toISOString()} ❌ Error: ${err.message}`; + console.error(msg); + addLog(msg); +}); + +// $ websocket configuration on close +ws.on('close', () => { + // ~ log closing to console and file + const msg = `${new Date().toISOString()} ⚠️ Connection closed`; + console.log(msg); + addLog(msg); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1f3f314 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,37 @@ +{ + "name": "ws-test", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ws-test", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "ws": "^8.18.3" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2c32aea --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "@lupinexlabs/websocket-tester", + "version": "1.0.0", + "description": "A simple interactive WebSocket testing tool for verifying connections, measuring RTT latency, and logging diagnostics for Twitch and custom servers.", + "main": "index.js", + "scripts": { + "start": "node index.js" + }, + "keywords": [ + "websocket", + "tester", + "twitch", + "rtt", + "latency", + "diagnostics", + "ws" + ], + "author": "LupiNex Media", + "license": "MIT", + "type": "commonjs", + "dependencies": { + "ws": "^8.18.3" + } +} diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..bdbdb3d --- /dev/null +++ b/start.bat @@ -0,0 +1,13 @@ +@echo off +title "NodeJS WebSocket Test Server" + +rem Get run mode or set default if nothing was provided +set /p mode="Enter mode (custom / twitch) [default: custom]: " +if "%mode%"=="" set mode=custom + +rem Get target information or set default if nothing was provided +set /p target="Enter target (custom url / twitch channel): " +if "%target%"=="" if "%mode%"=="custom" set target=wss://echo.websocket.org + +rem Execute the script with given information +node index.js %mode% %target% \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..cef168e --- /dev/null +++ b/start.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Set terminal title (wird von vielen Terminal-Emulatoren unterstützt) +echo -ne "\033]0;NodeJS WebSocket Test Server\007" + +# Get run mode +read -p "Enter mode (custom / twitch) [default: custom]: " mode +mode=${mode:-custom} + +# Get target information +read -p "Enter target (custom url / twitch channel): " target + +# Falls target leer ist und der Modus custom ist, nimm die Default-URL +if [ -z "$target" ]; then + if [ "$mode" == "custom" ]; then + target="wss://echo.websocket.org" + fi +fi + +# Execute the script with given information +node index.js "$mode" "$target" \ No newline at end of file