mirror of https://github.com/Ale32bit/Soqet.git
let's just bury v1
This commit is contained in:
parent
80c8a60dd5
commit
7df6b9dc06
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"port": 3004,
|
||||
"tcp_port": 25555,
|
||||
"wildcard_channel": "*",
|
||||
"motd": "Welcome to the Soqet Network"
|
||||
}
|
|
@ -1,500 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Soqet Documentation</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #004d40;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.top {
|
||||
border-bottom: white solid 2px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.top a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.top h1 {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.top ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.top li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.top li a {
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.top li a:link,
|
||||
a:visited,
|
||||
a:hover,
|
||||
a:active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.container h3 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.container .sub {
|
||||
padding-left: 20px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.container table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.container th {
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.container td {
|
||||
text-align: left;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.container table,
|
||||
th,
|
||||
td {
|
||||
background-color: #263238;
|
||||
border: 1px solid #546e7a;
|
||||
}
|
||||
|
||||
.container code {
|
||||
background-color: #263238;
|
||||
padding: 2px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.codeblock {
|
||||
background-color: #263238;
|
||||
padding: 3px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
font-family: monospace;
|
||||
width: 450px;
|
||||
border: 1px solid #546e7a;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="top">
|
||||
<ul>
|
||||
<li><a style="font-size: 32px; font-weight: 600;">Soqet Docs</a></li>
|
||||
<li><a href="#polling">Polling</a></li>
|
||||
<li><a href="/ui.html">Soqet Viewer</a></li>
|
||||
<li><a href="https://raw.githubusercontent.com/Ale32bit/Soqet/master/soqet.lua">Soqet.lua</a></li>
|
||||
<li><a href="https://github.com/Ale32bit/Soqet">Source code</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2 id="sockets">WebSockets and TCP Sockets</h2>
|
||||
<div class="sub">
|
||||
<p>All communications with the server must be in JSON.</p>
|
||||
<p>Responses of client requests will always contain the following fields:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ok</td>
|
||||
<td><i>boolean</i></td>
|
||||
<td>Successful request</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>error*</td>
|
||||
<td><i>string</i></td>
|
||||
<td>Error message (Only if <b>ok</b> is <i>false</i>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>id</td>
|
||||
<td><i>number</i></td>
|
||||
<td>ID of the request provided by the client (defaults to <i>1</i>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>uuid</td>
|
||||
<td><i>string</i></td>
|
||||
<td>UUID of the client session (random if not authenticated)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p><i>* Not always included</i></p>
|
||||
</div>
|
||||
|
||||
<h3>Connection</h3>
|
||||
<div class="sub">
|
||||
<p>Connection is possible via WebSocket from the endpoint <code>wss://soqet.alexdevs.pw/</code> and from the
|
||||
TCP socket <code>soqet.alexdevs.pw:25555</code></p>
|
||||
</div>
|
||||
|
||||
<h2>Requests</h2>
|
||||
|
||||
<p>Requests can optionally have the <code>id</code> field to identify requests, this is to help clients manage
|
||||
better the server responses.</p>
|
||||
|
||||
<h3>Authentication</h3>
|
||||
<div class="sub">
|
||||
<p>It's possible to get unique and persistent UUIDs by authenticating with a token.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>"token"</td>
|
||||
<td>Required</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>token</td>
|
||||
<td><i>string</i></td>
|
||||
<td>Token for authentication</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3>Opening a channel</h3>
|
||||
<div class="sub">
|
||||
<p>Opening channels allows the receiving of messages from other clients.</p>
|
||||
<p>The <b>Wildcard</b> to listen to all channels is <code>*</code>.</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>"open"</td>
|
||||
<td>Required</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td><i>string | number</i></td>
|
||||
<td>Channel to open</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3>Closing a channel</h3>
|
||||
<div class="sub">
|
||||
<p>Closing channels will stop getting messages from other clients.</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>"close"</td>
|
||||
<td>Required</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td><i>stirng | number</i></td>
|
||||
<td>Channel to close</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3>Sending a message</h3>
|
||||
<div class="sub">
|
||||
<p>The main point of this server is to transmit messages remotely.</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>"send"</td>
|
||||
<td>Required</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td><i>string | number</i></td>
|
||||
<td>Channel to send to</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>message</td>
|
||||
<td><i>any</i></td>
|
||||
<td>The message itself, can be anything.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>meta</td>
|
||||
<td><i>object</i></td>
|
||||
<td>The metadata object. See <a href="#message-metadata">metadata</a> for default values.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2 id="polling">Polling</h2>
|
||||
|
||||
<div class="sub">
|
||||
<p>All communications with the server must be in JSON.</p>
|
||||
<p>Responses of client requests will always contain the following fields:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ok</td>
|
||||
<td><i>boolean</i></td>
|
||||
<td>Successful request</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>error*</td>
|
||||
<td><i>string</i></td>
|
||||
<td>Error message (Only if <b>ok</b> is <i>false</i>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>uuid</td>
|
||||
<td><i>string</i></td>
|
||||
<td>UUID of the client session (random if not authenticated)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p><i>* Not always included</i></p>
|
||||
</div>
|
||||
|
||||
<h3>Connection</h3>
|
||||
<div class="sub">
|
||||
<p>Polling connection is possible via the GET URL
|
||||
<code>https://soqet.alexdevs.pw/api/connect?token=[optional token here]</code>.</p>
|
||||
<p>Response is in <code>application/json</code> type and contains the session token used for requests in the
|
||||
<code>token</code> field.</p>
|
||||
<p>Field <code>motd</code> is also supplied upon connection.</p>
|
||||
|
||||
<p><b>Once connected you need to request <code>/api/update</code> at least once every 60 seconds to keep the session token alive!</b>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3>REST URLs</h3>
|
||||
<div class="sub">
|
||||
<p><b><code>token</code> must always be supplied for the following requests!</b></p>
|
||||
|
||||
<h4><b>POST</b> <code>/api/update</code></h4>
|
||||
<p>Fetch latest updates from the queue</p>
|
||||
<p><b>This API should be requested often</b></p>
|
||||
<p>Fields</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>queue</td>
|
||||
<td>array</td>
|
||||
<td>The queue contains all events that happened since the last update request.<br>See <a
|
||||
href="#events">events</a> for a list of possible values.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4><b>POST</b> <code>/api/send</code></h4>
|
||||
<p>Transmit a message to a channel</p>
|
||||
<p>Fields</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td>string | number</td>
|
||||
<td>The channel name.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>message</td>
|
||||
<td>any</td>
|
||||
<td>The message itself, can be anything.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>meta</td>
|
||||
<td>object</td>
|
||||
<td>The metadata object. See <a href="#message-metadata">metadata</a> for default values.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4><b>POST</b> <code>/api/open</code></h4>
|
||||
<p>Open a channel to listen to</p>
|
||||
<p>Fields</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td>string | number</td>
|
||||
<td>The channel name</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4><b>POST</b> <code>/api/close</code></h4>
|
||||
<p>Close a channel</p>
|
||||
<p>Fields</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td>string | number</td>
|
||||
<td>The channel name</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2 id="events">Events</h2>
|
||||
<div class="sub">
|
||||
<p>The events are messages sent by the server automatically and do not directly require any request (unless client is using polling).</p>
|
||||
<p>Currently the events are:</p>
|
||||
|
||||
<h4>Ping <code>ping</code></h4>
|
||||
<p>This event is sent every 10 seconds.</p>
|
||||
<p><i>Not available in polling clients.</i></p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>string</td>
|
||||
<td>ping</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>time</td>
|
||||
<td>number</td>
|
||||
<td><i>Unix Epoch in milliseconds</i></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4>Message <code>message</code></h4>
|
||||
<p>Message events can only be received by opening channels.</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>string</td>
|
||||
<td>message</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td>string / number</td>
|
||||
<td><i>Channel name</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>message</td>
|
||||
<td><i>any</i></td>
|
||||
<td><i>Message sent by a client</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>meta</td>
|
||||
<td>object</td>
|
||||
<td>Contains metadata</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="sub">
|
||||
<h4 id="message-metadata">Message metadata</h4>
|
||||
<p>The message metadata contains additional information about the message, any client can add any value
|
||||
to it.</p>
|
||||
<p>The server always sends these metadata by default (and overrides client's):</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>uuid</td>
|
||||
<td>string</td>
|
||||
<td>UUID of the sender</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>channel</td>
|
||||
<td>string / number</td>
|
||||
<td><i>Channel name</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>time</td>
|
||||
<td>number</td>
|
||||
<td><i>Unix epoch of the date the message was sent</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>guest</td>
|
||||
<td>boolean</td>
|
||||
<td>If the sender was authenticated</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h4>Message of the day <code>motd</code></h4>
|
||||
<p>This event is sent upon connection to the server</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>string</td>
|
||||
<td>motd</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>motd</td>
|
||||
<td>string</td>
|
||||
<td><i>Inspiring quotes to help the developer and the user get over problems and easily achieve life goals.</i></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,589 +0,0 @@
|
|||
//
|
||||
// Soqet V3 by AlexDevs
|
||||
// (c) 2020 Alessandro
|
||||
//
|
||||
// MIT License
|
||||
// https://github.com/Ale32bit/Soqet/blob/master/LICENSE
|
||||
//
|
||||
// https://github.com/Ale32bit/Soqet
|
||||
// https://soqet.alexdevs.pw/
|
||||
//
|
||||
|
||||
import express from "express";
|
||||
import expressWs from "express-ws";
|
||||
import * as net from "net";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
const { app } = expressWs(express());
|
||||
|
||||
const config = require("./config.json")
|
||||
|
||||
interface Client {
|
||||
send: (data: string | object) => void,
|
||||
uuid: string,
|
||||
id: string,
|
||||
auth: boolean,
|
||||
type: string,
|
||||
client: any,
|
||||
}
|
||||
|
||||
interface PollingClient extends Client {
|
||||
lastPing: number,
|
||||
queue: Array<any>,
|
||||
token: string,
|
||||
}
|
||||
|
||||
interface Channels {
|
||||
[key: string]: Array<string>,
|
||||
}
|
||||
|
||||
interface Clients {
|
||||
[key: string]: PollingClient | Client,
|
||||
}
|
||||
|
||||
interface PollingTokens {
|
||||
[key: string]: string,
|
||||
}
|
||||
|
||||
const channels: Channels = {};
|
||||
const clients: Clients = {};
|
||||
const pollingTokens: PollingTokens = {};
|
||||
|
||||
function sha256(str: string): string {
|
||||
return crypto.createHash("sha256").update(str).digest("hex");
|
||||
}
|
||||
|
||||
function sha256_raw(str: string): Buffer {
|
||||
return crypto.createHash("sha256").update(str).digest();
|
||||
}
|
||||
|
||||
function toBase36(input: number): string { // hexadecimal number to base 36
|
||||
const byte = 48 + Math.floor(input / 7);
|
||||
return String.fromCharCode(byte + 39 > 122 ? 101 : byte > 57 ? byte + 39 : byte);
|
||||
}
|
||||
|
||||
function random(len: number = 16, prefix: string = "g"): string { // Generate a random secure string in hex
|
||||
let bytes = crypto.randomBytes(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
prefix += toBase36(bytes[i]);
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
function createID(token: string, prefix: string = "a"): string { // New create ID algorithm
|
||||
let buff: Buffer = sha256_raw(token);
|
||||
let len = 32;
|
||||
let id: Array<string> = [];
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
buff = sha256_raw(buff.toString("hex"));
|
||||
let chunk = buff[0];
|
||||
|
||||
id[i] = toBase36(chunk);
|
||||
}
|
||||
|
||||
return prefix + id.join("");
|
||||
}
|
||||
|
||||
function disconnect(id: string) {
|
||||
for (let name in channels) {
|
||||
let ch: Array<string> = channels[name];
|
||||
|
||||
let index: number = ch.indexOf(id);
|
||||
if (index > -1) {
|
||||
ch.splice(index, 1);
|
||||
}
|
||||
|
||||
if (ch.length === 0) {
|
||||
delete channels[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transmit(id: string, channel: string | number, message: unknown, meta: object): void {
|
||||
try {
|
||||
if (channel === config.wildcard_channel) return;
|
||||
|
||||
if (channels[channel]) {
|
||||
channels[channel].forEach(cid => {
|
||||
if (id === cid) return;
|
||||
|
||||
let client = clients[cid];
|
||||
|
||||
if (client) {
|
||||
client.send({
|
||||
type: "message",
|
||||
channel: channel,
|
||||
message: message,
|
||||
meta: meta,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (channels[config.wildcard_channel]) {
|
||||
channels[config.wildcard_channel].forEach(cid => {
|
||||
let client = clients[cid];
|
||||
if (client) {
|
||||
client.send({
|
||||
type: "message",
|
||||
channel: config.wildcard_channel,
|
||||
message: message,
|
||||
meta: meta,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function send(client: Client | PollingClient, channel: string | number, message: any, meta: any) {
|
||||
meta = typeof meta === "object" && !Array.isArray(meta) ? meta : {};
|
||||
|
||||
meta.uuid = client.uuid; // sender uuid
|
||||
meta.time = Date.now(); // time of sending
|
||||
meta.channel = channel; // channel
|
||||
meta.guest = !client.auth; // if not authenticated
|
||||
|
||||
transmit(client.id, channel, message, meta);
|
||||
}
|
||||
|
||||
function openChannel(id: string, channel: string | number): void {
|
||||
|
||||
if (!channels[channel]) channels[channel] = [];
|
||||
channels[channel].push(id);
|
||||
}
|
||||
|
||||
function closeChannel(id: string, channel: string | number) {
|
||||
if (channels[channel]) { // remove uuid from the channel
|
||||
let index = channels[channel].indexOf(id);
|
||||
if (index >= 0) {
|
||||
channels[channel].splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onMessage(client: Client, message: any): void {
|
||||
let data;
|
||||
if (typeof message !== "string") message = message.toString("utf-8");
|
||||
try {
|
||||
data = JSON.parse(message);
|
||||
} catch (e) {
|
||||
return client.send({
|
||||
ok: false,
|
||||
error: "Invalid data format",
|
||||
uuid: client.uuid,
|
||||
})
|
||||
}
|
||||
|
||||
data.id = Number.parseInt(data.id) || 1;
|
||||
|
||||
if (!data.type) {
|
||||
return client.send({
|
||||
ok: false,
|
||||
error: "Invalid request",
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
})
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case "send": // Send a message to a channel
|
||||
if (!data.channel) {
|
||||
return client.send({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
})
|
||||
}
|
||||
|
||||
// proceed to send the message
|
||||
send(client, data.channel, data.message, data.meta || {});
|
||||
|
||||
client.send({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: client.uuid,
|
||||
});
|
||||
break;
|
||||
case "open": // Open channel
|
||||
if (!data.channel) {
|
||||
return client.send({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
})
|
||||
}
|
||||
|
||||
// limit of channel name:
|
||||
// Must be either a string long max 256 chars or a number
|
||||
|
||||
if ((typeof data.channel === "string" && data.channel.length <= 256) || typeof data.channel === "number") {
|
||||
|
||||
openChannel(client.id, data.channel)
|
||||
|
||||
client.send({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: client.uuid,
|
||||
});
|
||||
} else {
|
||||
return client.send({
|
||||
ok: false,
|
||||
error: "Invalid channel field",
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
})
|
||||
}
|
||||
|
||||
break;
|
||||
case "close": // close a channel
|
||||
if (!data.channel) {
|
||||
return client.send({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
})
|
||||
}
|
||||
|
||||
closeChannel(client.id, data.channel);
|
||||
|
||||
client.send({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: client.uuid,
|
||||
});
|
||||
|
||||
break;
|
||||
case "ping": // allow clients to ping the server if they want to
|
||||
client.send({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: client.uuid,
|
||||
});
|
||||
break;
|
||||
case "auth": // authentication
|
||||
if (!data.token) {
|
||||
return client.send({
|
||||
ok: false,
|
||||
error: "Missing token field",
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
})
|
||||
}
|
||||
|
||||
let authid = createID(data.token); // create the UUID from the token
|
||||
|
||||
let olduuid = client.uuid;
|
||||
client.uuid = authid; // set new UUID
|
||||
client.auth = true; // set as authenticated
|
||||
|
||||
console.log(`AUTH: ${olduuid} is now ${client.uuid}`);
|
||||
|
||||
return client.send({
|
||||
ok: true,
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
});
|
||||
|
||||
break;
|
||||
default: // if no request type is found send this as error
|
||||
client.send({
|
||||
ok: false,
|
||||
error: "Invalid request",
|
||||
uuid: client.uuid,
|
||||
id: data.id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const netServer = net.createServer();
|
||||
|
||||
app.ws("*", ws => {
|
||||
let client = {} as Client;
|
||||
|
||||
client.uuid = random();
|
||||
client.id = random(undefined, "S");
|
||||
client.auth = false;
|
||||
client.type = "websocket";
|
||||
client.client = ws;
|
||||
|
||||
client.send = function (data: string | object) {
|
||||
if (typeof data === "object") data = JSON.stringify(data);
|
||||
|
||||
if(client.client.readyState === ws.OPEN) {
|
||||
client.client.send(data)
|
||||
}
|
||||
}
|
||||
|
||||
clients[client.id] = client;
|
||||
|
||||
let pingInterval = setInterval(function () {
|
||||
client.send({
|
||||
type: "ping",
|
||||
uuid: client.uuid,
|
||||
ping: Date.now(),
|
||||
})
|
||||
}, 10000);
|
||||
|
||||
client.send({
|
||||
type: "motd",
|
||||
motd: config.motd || "soqet",
|
||||
uuid: client.uuid,
|
||||
});
|
||||
|
||||
console.log("[WS]", `Client connected: ${client.id}`);
|
||||
|
||||
ws.on("message", data => {
|
||||
onMessage(client, data);
|
||||
})
|
||||
|
||||
ws.on("close", (code, reason) => {
|
||||
console.log("[WS]", `Client disconnected ${client.id} (${code} ${reason})`);
|
||||
clearInterval(pingInterval);
|
||||
|
||||
disconnect(client.id);
|
||||
})
|
||||
|
||||
ws.on("error", err => {
|
||||
console.error("[TCP]", client.id, err) // it can happen
|
||||
})
|
||||
})
|
||||
|
||||
netServer.on("connection", socket => {
|
||||
let client = {} as Client;
|
||||
|
||||
client.uuid = random();
|
||||
client.id = random(undefined, "S");
|
||||
client.auth = false;
|
||||
client.type = "socket";
|
||||
client.client = socket as net.Socket;
|
||||
|
||||
client.send = function (data: string | object) {
|
||||
if (typeof data === "object") data = JSON.stringify(data);
|
||||
|
||||
client.client.write(data)
|
||||
}
|
||||
|
||||
clients[client.id] = client;
|
||||
|
||||
let pingInterval = setInterval(function () {
|
||||
client.send({
|
||||
type: "ping",
|
||||
uuid: client.uuid,
|
||||
ping: Date.now(),
|
||||
})
|
||||
}, 10000);
|
||||
|
||||
client.send({
|
||||
type: "motd",
|
||||
motd: config.motd || "soqet",
|
||||
uuid: client.uuid,
|
||||
});
|
||||
|
||||
console.log("[TCP]", `Client connected: ${client.id}`);
|
||||
|
||||
socket.on("data", data => {
|
||||
onMessage(client, data);
|
||||
})
|
||||
|
||||
socket.on("close", (code: any, reason: any) => { // WS Client disconnects
|
||||
console.log("[TCP]", `Client disconnected ${client.id} (${code} ${reason})`);
|
||||
clearInterval(pingInterval); // Clear Ping interval
|
||||
|
||||
// remove uuid from all channels
|
||||
disconnect(client.id);
|
||||
});
|
||||
|
||||
socket.on("error", (err) => {
|
||||
console.error("[TCP]", client.id, err) // it can happen
|
||||
});
|
||||
})
|
||||
|
||||
app.use(express.json({
|
||||
limit: "10mb"
|
||||
}));
|
||||
|
||||
let pollingRouter = express.Router();
|
||||
|
||||
// Create a polling connection
|
||||
pollingRouter.get("/connect", (req, res, next) => {
|
||||
let query = req.query;
|
||||
let uuid = random();
|
||||
if (query.token) {
|
||||
uuid = createID(query.token as string);
|
||||
}
|
||||
|
||||
let sessionToken = random(127, "$"); // will be used as ID
|
||||
|
||||
let client = {} as PollingClient;
|
||||
|
||||
client.uuid = uuid;
|
||||
client.id = random();
|
||||
client.auth = false;
|
||||
client.type = "polling";
|
||||
client.client = req;
|
||||
|
||||
client.queue = [];
|
||||
client.token = sessionToken;
|
||||
|
||||
client.send = function (data: string | object) {
|
||||
if (typeof data === "string") data = JSON.parse(data);
|
||||
|
||||
client.queue.push(data);
|
||||
}
|
||||
|
||||
pollingTokens[sessionToken] = client.id;
|
||||
clients[client.id] = client;
|
||||
|
||||
console.log("[POL]", `Client connected: ${client.id}`);
|
||||
|
||||
return res.json({
|
||||
ok: true,
|
||||
motd: config.motd || "soqet",
|
||||
uuid: client.uuid,
|
||||
token: sessionToken,
|
||||
})
|
||||
});
|
||||
|
||||
pollingRouter.use("*", function (req, res, next) {
|
||||
let sessionToken = req.body.token as string;
|
||||
if (!sessionToken || !pollingTokens[sessionToken]) {
|
||||
return res.status(401).json({
|
||||
ok: false,
|
||||
error: "Invalid token",
|
||||
})
|
||||
}
|
||||
|
||||
next();
|
||||
})
|
||||
|
||||
// Open a channel
|
||||
pollingRouter.post("/open", (req, res, next) => {
|
||||
let client = clients[pollingTokens[req.body.token]];
|
||||
let channel = req.body.channel as string | number;
|
||||
if (!channel) {
|
||||
return res.status(400).json({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: client.uuid,
|
||||
})
|
||||
}
|
||||
|
||||
if ((typeof channel === "string" && channel.length <= 256) || typeof channel === "number") {
|
||||
openChannel(client.id, channel);
|
||||
res.json({
|
||||
ok: true,
|
||||
uuid: client.uuid,
|
||||
});
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
ok: false,
|
||||
error: "Invalid channel field",
|
||||
uuid: client.uuid,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Close a channel
|
||||
pollingRouter.post("/close", (req, res, next) => {
|
||||
let client = clients[pollingTokens[req.body.token]];
|
||||
let channel = req.body.channel as string | number;
|
||||
if (!channel) {
|
||||
return res.status(400).json({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: client.uuid,
|
||||
})
|
||||
}
|
||||
|
||||
closeChannel(client.id, channel);
|
||||
|
||||
res.json({
|
||||
ok: true,
|
||||
uuid: client.uuid,
|
||||
});
|
||||
})
|
||||
|
||||
// Transmit a message
|
||||
pollingRouter.post("/send", (req, res, next) => {
|
||||
let client = clients[pollingTokens[req.body.token]];
|
||||
let channel = req.body.channel as string | number;
|
||||
if (!channel) {
|
||||
return res.status(400).json({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: client.uuid,
|
||||
})
|
||||
}
|
||||
|
||||
let message: any = req.body.message;
|
||||
let meta: any = req.body.meta;
|
||||
|
||||
// create the message meta
|
||||
|
||||
send(client, channel, message, meta || {}); // proceed to send the message
|
||||
|
||||
res.json({
|
||||
ok: true,
|
||||
uuid: client.uuid,
|
||||
});
|
||||
})
|
||||
|
||||
// Request queue
|
||||
pollingRouter.post("/update", (req, res, next) => {
|
||||
let client = clients[pollingTokens[req.body.token]] as PollingClient;
|
||||
|
||||
res.json({
|
||||
ok: true,
|
||||
uuid: client.uuid,
|
||||
queue: client.queue,
|
||||
});
|
||||
|
||||
client.lastPing = Date.now();
|
||||
client.queue = [];
|
||||
})
|
||||
|
||||
app.use("/api/", pollingRouter);
|
||||
|
||||
app.use(express.static("public"));
|
||||
|
||||
netServer.on("listening", () => {
|
||||
console.log("Listening on TCP port", config.tcp_port)
|
||||
})
|
||||
|
||||
app.listen(config.port, () => {
|
||||
console.log("Listening on HTTP port", config.port);
|
||||
|
||||
setInterval(function () {
|
||||
|
||||
|
||||
let pClients = Object.keys(clients)
|
||||
.filter(key => key.startsWith("$"))
|
||||
.reduce((obj, key) => {
|
||||
obj.push(key);
|
||||
return obj;
|
||||
}, [] as Array<string>);
|
||||
|
||||
|
||||
|
||||
pClients.forEach(id => {
|
||||
let v = clients[id] as PollingClient;
|
||||
|
||||
if ((Date.now() - v.lastPing) > 60000) {
|
||||
console.log("[POL]", `Client connected: ${id}`);
|
||||
let clientToken = (clients[id] as PollingClient).token;
|
||||
delete clients[id];
|
||||
delete pollingTokens[clientToken]
|
||||
}
|
||||
})
|
||||
}, 60000)
|
||||
});
|
||||
|
||||
netServer.listen(config.tcp_port);
|
|
@ -1,376 +0,0 @@
|
|||
/*
|
||||
Soqet Server by Ale32bit
|
||||
|
||||
MIT License
|
||||
|
||||
*/
|
||||
|
||||
const WebSocket = require("ws");
|
||||
const net = require("net");
|
||||
const crypto = require("crypto");
|
||||
const config = require("./config.json");
|
||||
|
||||
const channels = {};
|
||||
const clients = [];
|
||||
const WILDCARD = '*';
|
||||
|
||||
// ---- FUNCTIONS ----
|
||||
|
||||
function sha256(str) { // Hash a string using SHA256
|
||||
return crypto.createHash("sha256").update(str).digest("hex");
|
||||
}
|
||||
|
||||
function random(len = 16, prefix = "g") { // Generate a random secure string in hex
|
||||
return prefix + crypto.randomBytes(len).toString("hex");
|
||||
}
|
||||
|
||||
function randomToken() { // Same with random but in base64 and non-alphanumerical chars removed -- Currently not used
|
||||
return crypto.randomBytes(42).toString("base64").replace(/[^a-zA-Z0-9 -]/g, "")
|
||||
}
|
||||
|
||||
function incRandom(min, max) { // random number inclusive
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
}
|
||||
|
||||
function hexToBase36(input) { // hexadecimal number to base 36
|
||||
const byte = 48 + Math.floor(input / 7);
|
||||
return String.fromCharCode(byte + 39 > 122 ? 101 : byte > 57 ? byte + 39 : byte);
|
||||
}
|
||||
|
||||
function createID(token) { // From krist-utils, generate an ID from the token, like an HASH
|
||||
token = sha256(token);
|
||||
let prefix = "a";
|
||||
let len = 31;
|
||||
let hash = sha256(sha256(token));
|
||||
let chars = [];
|
||||
|
||||
for (let i = 0; i <= len; i++) {
|
||||
chars[i] = hash.substring(0, 2);
|
||||
hash = sha256(sha256(hash));
|
||||
}
|
||||
|
||||
for (let i = 0; i <= len;) {
|
||||
const index = parseInt(hash.substring(2 * i, 2 + (2 * i)), 16) % (len + 1);
|
||||
|
||||
if (!chars[index]) {
|
||||
hash = sha256(hash);
|
||||
} else {
|
||||
prefix += hexToBase36(parseInt(chars[index], 16));
|
||||
chars[index] = undefined;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
function disconnect(sID) { // remove a client UUID from all channels.
|
||||
for (let chn in channels) {
|
||||
let ch = channels[chn];
|
||||
for (let i = 0; i < ch.length; i++) {
|
||||
if (ch[i] === sID) {
|
||||
let index = ch.indexOf(sID);
|
||||
ch.splice(index, 1)
|
||||
}
|
||||
}
|
||||
if (ch.length === 0) {
|
||||
delete channels[chn]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const server = new WebSocket.Server({ // create the websocket server, port is from config.json
|
||||
port: config.port,
|
||||
});
|
||||
|
||||
const netServer = net.createServer();
|
||||
|
||||
function getClient(sID) { // just a for loop to get the ws client from the session ID
|
||||
for (let item of clients) {
|
||||
if (item.sID === sID) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transmit(channel, message, meta, ignore = null) { // transmit a message to the channel. WILDCARD channel is read-only
|
||||
try {
|
||||
if (channel === WILDCARD) { // prevents from sending a message directly to WILDCARD channel
|
||||
return;
|
||||
}
|
||||
|
||||
if(channels[channel]) {
|
||||
channels[channel].forEach(sID => {
|
||||
if (ignore === sID) return;
|
||||
|
||||
let ws = getClient(sID);
|
||||
if (ws) {
|
||||
ws.send(JSON.stringify({
|
||||
type: "message",
|
||||
channel: channel,
|
||||
message: message,
|
||||
meta: meta,
|
||||
}))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (channels[WILDCARD]) { // send message to WILDCARD channel
|
||||
channels[WILDCARD].forEach(sID => {
|
||||
let ws = getClient(sID);
|
||||
if (ws) {
|
||||
ws.send(JSON.stringify({
|
||||
type: "message",
|
||||
channel: WILDCARD,
|
||||
message: message,
|
||||
meta: meta,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) { // this code kept crashing, no longer happens
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function onMessage(ws) {
|
||||
return function(message) {
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(message);
|
||||
} catch (e) {
|
||||
return ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Invalid data format",
|
||||
uuid: ws.uuid,
|
||||
}));
|
||||
}
|
||||
|
||||
data.id = Number.parseInt(data.id) || 1; // if request ID is invalid or nonexistend, define it as 1
|
||||
|
||||
if (!data.type) { // requests require type field
|
||||
return ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Invalid request",
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}))
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case "send": // Send a message to a channel
|
||||
if (!data.channel) {
|
||||
return ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}))
|
||||
}
|
||||
|
||||
// create the message meta
|
||||
|
||||
let meta = typeof data.meta === "object" && !Array.isArray(data.meta) ? data.meta : {};
|
||||
|
||||
meta.uuid = ws.uuid; // sender uuid
|
||||
meta.time = Date.now(); // time of sending
|
||||
meta.channel = data.channel; // channel
|
||||
meta.guest = !ws.auth; // if not authenticated
|
||||
|
||||
|
||||
transmit(data.channel, data.message, meta, ws.sID); // proceed to send the message
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: ws.uuid,
|
||||
}));
|
||||
break;
|
||||
case "open": // Open channel
|
||||
if (!data.channel) {
|
||||
return ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}))
|
||||
}
|
||||
|
||||
// limit of channel name:
|
||||
// Must be either a string long max 256 chars or a number
|
||||
|
||||
if ((typeof data.channel === "string" && data.channel.length <= 256) || typeof data.channel === "number") {
|
||||
|
||||
if (!channels[data.channel]) channels[data.channel] = [];
|
||||
channels[data.channel].push(ws.sID);
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: ws.uuid,
|
||||
}));
|
||||
} else {
|
||||
return ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Invalid channel field",
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}))
|
||||
}
|
||||
|
||||
break;
|
||||
case "close": // close a channel
|
||||
if (!data.channel) {
|
||||
return ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Missing channel field",
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}))
|
||||
}
|
||||
|
||||
if (channels[data.channel]) { // remove uuid from the channel
|
||||
let index = channels[data.channel].indexOf(ws.sID);
|
||||
if (index >= 0) {
|
||||
channels[data.channel].splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: ws.uuid,
|
||||
}));
|
||||
|
||||
break;
|
||||
case "ping": // allow clients to ping the server if they want to
|
||||
ws.send(JSON.stringify({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
uuid: ws.uuid,
|
||||
}));
|
||||
break;
|
||||
case "auth": // authentication
|
||||
if (!data.token) {
|
||||
return ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Missing token field",
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}))
|
||||
}
|
||||
|
||||
let authid = createID(data.token); // create the UUID from the token
|
||||
|
||||
let olduuid = ws.uuid;
|
||||
ws.uuid = authid; // set new UUID
|
||||
ws.auth = true; // set as authenticated
|
||||
|
||||
console.log(`AUTH: ${olduuid} is now ${ws.uuid}`);
|
||||
|
||||
return ws.send(JSON.stringify({
|
||||
ok: true,
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}));
|
||||
|
||||
break;
|
||||
default: // if no request type is found send this as error
|
||||
ws.send(JSON.stringify({
|
||||
ok: false,
|
||||
error: "Invalid request",
|
||||
uuid: ws.uuid,
|
||||
id: data.id,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.on("connection", ws => { // Listen to clients connecting to the websocket server
|
||||
|
||||
ws.uuid = random(); // assign a random UUID as guest
|
||||
ws.sID = random(undefined, "S"); // Session ID
|
||||
ws.auth = false; // not authenticated by default
|
||||
ws.type = "websocket";
|
||||
|
||||
clients.push(ws);
|
||||
|
||||
console.log("Connect:", ws.uuid, ws.sID);
|
||||
|
||||
let pingInterval = setInterval(function () { // Send a ping to the client every 10 seconds to keep the connection alive
|
||||
ws.send(JSON.stringify({
|
||||
type: "ping",
|
||||
uuid: ws.uuid,
|
||||
ping: Date.now(),
|
||||
}))
|
||||
}, 10000);
|
||||
|
||||
ws.send(JSON.stringify({ // A friendly message upon connection
|
||||
type: "motd",
|
||||
motd: "Welcome to the Soqet network",
|
||||
uuid: ws.uuid,
|
||||
}));
|
||||
|
||||
ws.on("message", onMessage(ws));
|
||||
|
||||
ws.on("close", (code, reason) => { // WS Client disconnects
|
||||
console.log("Close:", ws.uuid, ws.sID, `(${code} ${reason})`);
|
||||
clearInterval(pingInterval); // Clear Ping interval
|
||||
|
||||
// remove uuid from all channels
|
||||
disconnect(ws.sID);
|
||||
});
|
||||
|
||||
ws.on("error", (err) => {
|
||||
console.error(ws.uuid, ws.sID, err) // it can happen
|
||||
});
|
||||
});
|
||||
|
||||
netServer.on("connection", socket => {
|
||||
socket.send = function(data) {
|
||||
return socket.write(data)
|
||||
}
|
||||
|
||||
socket.uuid = random();
|
||||
socket.sID = random(undefined, "S");
|
||||
socket.auth = false;
|
||||
socket.type = "socket"
|
||||
|
||||
clients.push(socket)
|
||||
|
||||
console.log("TCP Connect:", socket.uuid, socket.sID);
|
||||
|
||||
let pingInterval = setInterval(function () { // Send a ping to the client every 10 seconds to keep the connection alive
|
||||
socket.send(JSON.stringify({
|
||||
type: "ping",
|
||||
uuid: socket.uuid,
|
||||
ping: Date.now(),
|
||||
}))
|
||||
}, 10000);
|
||||
|
||||
socket.send(JSON.stringify({ // A friendly message upon connection
|
||||
type: "motd",
|
||||
motd: "Welcome to the Soqet network",
|
||||
uuid: socket.uuid,
|
||||
}));
|
||||
|
||||
socket.on("data", onMessage(socket));
|
||||
|
||||
socket.on("close", (code, reason) => { // WS Client disconnects
|
||||
console.log("Close:", socket.uuid, socket.sID, `(${code} ${reason})`);
|
||||
clearInterval(pingInterval); // Clear Ping interval
|
||||
|
||||
// remove uuid from all channels
|
||||
disconnect(socket.sID);
|
||||
});
|
||||
|
||||
socket.on("error", (err) => {
|
||||
console.error(socket.uuid, socket.sID, err) // it can happen
|
||||
});
|
||||
})
|
||||
|
||||
netServer.listen(config.tcp_port);
|
||||
|
||||
// hopefully this service will help cc communities
|
|
@ -1,290 +0,0 @@
|
|||
--[[
|
||||
-- OC_Soqet.lua --
|
||||
https://github.com/Ale32bit/Soqet/
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Alessandro
|
||||
|
||||
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.
|
||||
]] --
|
||||
|
||||
--[[
|
||||
-- json.lua --
|
||||
https://github.com/rxi/json.lua
|
||||
|
||||
Copyright (c) 2019 rxi
|
||||
|
||||
|
||||
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.
|
||||
|
||||
]] --
|
||||
|
||||
local http = require("internet")
|
||||
local fs = require("filesystem")
|
||||
|
||||
if not http then
|
||||
error("Missing internet card", 2)
|
||||
end
|
||||
|
||||
local function _request(...)
|
||||
local handle = http.request(...)
|
||||
|
||||
handle.finishConnect()
|
||||
|
||||
local content = ""
|
||||
for chunk in handle do
|
||||
content = content .. chunk
|
||||
end
|
||||
return content
|
||||
end
|
||||
|
||||
local function get(url)
|
||||
return _request(url)
|
||||
end
|
||||
|
||||
local function post(url, data, headers)
|
||||
return _request(url, data, headers, "POST")
|
||||
end
|
||||
|
||||
if not fs.exists("/lib/json.lua") then
|
||||
local con = get("https://raw.githubusercontent.com/rxi/json.lua/master/json.lua")
|
||||
local f = io.open("/lib/json.lua", "w")
|
||||
f:write(con)
|
||||
f:close()
|
||||
end
|
||||
|
||||
local function expect(index, value, ...)
|
||||
local types = {...}
|
||||
|
||||
local valueType = type(value)
|
||||
|
||||
local valid = false
|
||||
for _, v in ipairs(types) do
|
||||
if valueType == v then
|
||||
valid = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not valid then
|
||||
error(("bad argument #%d (expected %s, got %s)"):format(index, table.concat(types, ", "), valueType), 3)
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local soqet = {
|
||||
ENDPOINT = "soqet.alexdevs.pw",
|
||||
ssl = false,
|
||||
json = require("json"),
|
||||
credits = "OC_Soqet.lua by AlexDevs"
|
||||
}
|
||||
|
||||
local function postJson(url, data)
|
||||
return _request(
|
||||
url,
|
||||
soqet.json.encode(data),
|
||||
{
|
||||
["Content-Type"] = "application/json"
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function soqet.new()
|
||||
error("WebSocket client is not supported. Use long polling instead.", 2)
|
||||
end
|
||||
|
||||
function soqet.poll()
|
||||
local client = {
|
||||
channels = {},
|
||||
uuid = nil,
|
||||
sessionId = math.random(0xffffff),
|
||||
sessionToken = nil,
|
||||
ssl = soqet.ssl,
|
||||
connected = false,
|
||||
listening = true,
|
||||
updateInterval = 1
|
||||
}
|
||||
|
||||
if ssl then
|
||||
client.ENDPOINT = "https://" .. soqet.ENDPOINT
|
||||
else
|
||||
client.ENDPOINT = "http://" .. soqet.ENDPOINT
|
||||
end
|
||||
|
||||
local function send(path, body)
|
||||
return postJson(client.ENDPOINT .. "/api/" .. path, body)
|
||||
end
|
||||
|
||||
local function rawreceive()
|
||||
if not client.connected then
|
||||
client.connect()
|
||||
end
|
||||
while true do
|
||||
local h =
|
||||
send(
|
||||
"update",
|
||||
{
|
||||
token = client.sessionToken
|
||||
}
|
||||
)
|
||||
|
||||
local data = soqet.json.decode(h)
|
||||
|
||||
local queue = {}
|
||||
|
||||
for i, v in ipairs(data.queue) do
|
||||
if v.type == "message" then
|
||||
table.insert(
|
||||
queue,
|
||||
{
|
||||
channel = v.channel,
|
||||
message = v.message,
|
||||
meta = v.meta
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if #queue > 0 then
|
||||
return queue
|
||||
else
|
||||
os.sleep(client.updateInterval)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function client.connect(token)
|
||||
if nil and type(token) ~= "string" then
|
||||
error("bad argument #1", 2)
|
||||
end
|
||||
local h = get(client.ENDPOINT .. "/api/connect?token=" .. (token or ""))
|
||||
|
||||
local data = soqet.json.decode(h)
|
||||
|
||||
client.uuid = data.uuid
|
||||
client.sessionToken = data.token
|
||||
client.motd = data.motd
|
||||
|
||||
client.connected = true
|
||||
|
||||
for i, v in pairs(client.channels) do
|
||||
client.open(v)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function client.open(channel)
|
||||
expect(1, channel, "string", "number")
|
||||
|
||||
client.channels[#client.channels + 1] = channel
|
||||
|
||||
send(
|
||||
"open",
|
||||
{
|
||||
token = client.sessionToken,
|
||||
channel = channel
|
||||
}
|
||||
)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function client.close(channel)
|
||||
expect(1, channel, "string", "number")
|
||||
|
||||
for i, v in pairs(client.channels) do
|
||||
if v == channel then
|
||||
client.channels[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
send(
|
||||
"close",
|
||||
{
|
||||
token = client.sessionToken,
|
||||
channel = channel
|
||||
}
|
||||
)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function client.send(channel, message, meta)
|
||||
expect(1, channel, "string", "number")
|
||||
expect(3, meta, "nil", "table")
|
||||
|
||||
meta = meta or {}
|
||||
meta.library = meta.library or soqet.credits
|
||||
|
||||
send(
|
||||
"send",
|
||||
{
|
||||
token = client.sessionToken,
|
||||
channel = channel,
|
||||
message = message,
|
||||
meta = meta
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function client.receive()
|
||||
return rawreceive()
|
||||
end
|
||||
|
||||
function client.listen()
|
||||
client.listening = true
|
||||
while client.listening do
|
||||
local queue = rawreceive()
|
||||
|
||||
for i, v in ipairs(queue) do
|
||||
computer.pushSignal("soqet_message", v.channel, v.message, v.meta, client.sessionId)
|
||||
end
|
||||
|
||||
sleep(client.updateInterval)
|
||||
end
|
||||
end
|
||||
|
||||
function client.unlisten()
|
||||
client.listening = false
|
||||
return true
|
||||
end
|
||||
|
||||
return client
|
||||
end
|
||||
|
||||
return soqet
|
|
@ -1,558 +0,0 @@
|
|||
{
|
||||
"name": "soqet",
|
||||
"version": "3.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
|
||||
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz",
|
||||
"integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz",
|
||||
"integrity": "sha512-EaEdY+Dty1jEU7U6J4CUWwxL+hyEGMkO5jan5gplfegUgCUsIUWqXxqw47uGjimeT4Qgkz/XUfwoau08+fgvKA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-ws": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-ws/-/express-ws-3.0.0.tgz",
|
||||
"integrity": "sha512-GxsWec7Vp6h7sJuK0PwnZHeXNZnOwQn8kHAbCfvii66it5jXHTWzSg5cgHVtESwJfBLOe9SJ5wmM7C6gsDoyQw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*",
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/ws": "*"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
|
||||
"integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz",
|
||||
"integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/qs": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz",
|
||||
"integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz",
|
||||
"integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/mime": "*"
|
||||
}
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "7.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.6.tgz",
|
||||
"integrity": "sha512-Q07IrQUSNpr+cXU4E4LtkSIBPie5GLZyyMC1QtQYRLWz701+XcoVygGUZgvLqElq1nU4ICldMYPnexlBsg3dqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||
"requires": {
|
||||
"mime-types": "~2.1.24",
|
||||
"negotiator": "0.6.2"
|
||||
}
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||
"requires": {
|
||||
"bytes": "3.1.0",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "~2.3.0",
|
||||
"qs": "6.7.0",
|
||||
"raw-body": "2.4.0",
|
||||
"type-is": "~1.6.17"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||
},
|
||||
"destroy": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||
},
|
||||
"etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||
"requires": {
|
||||
"accepts": "~1.3.7",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.19.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "~1.1.2",
|
||||
"fresh": "0.5.2",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.5",
|
||||
"qs": "6.7.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.1.2",
|
||||
"send": "0.17.1",
|
||||
"serve-static": "1.14.1",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": "~1.5.0",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"express-ws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-ws/-/express-ws-4.0.0.tgz",
|
||||
"integrity": "sha512-KEyUw8AwRET2iFjFsI1EJQrJ/fHeGiJtgpYgEWG3yDv4l/To/m3a2GaYfeGyB3lsWdvbesjF5XCMx+SVBgAAYw==",
|
||||
"requires": {
|
||||
"ws": "^5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "~1.5.0",
|
||||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||
},
|
||||
"fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.3",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.0"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||
},
|
||||
"merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||
},
|
||||
"methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.27",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||
"requires": {
|
||||
"mime-db": "1.44.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||
"requires": {
|
||||
"forwarded": "~0.1.2",
|
||||
"ipaddr.js": "1.9.1"
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
||||
"requires": {
|
||||
"bytes": "3.1.0",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"send": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
||||
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"destroy": "~1.0.4",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "~1.7.2",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.1",
|
||||
"on-finished": "~2.3.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "~1.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve-static": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
|
||||
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
|
||||
"requires": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.17.1"
|
||||
}
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
|
||||
"integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.17",
|
||||
"yn": "3.1.1"
|
||||
}
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"requires": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz",
|
||||
"integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"name": "soqet",
|
||||
"version": "3.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"tsc": "tsc",
|
||||
"start": "tsc && node index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.17.1",
|
||||
"express-ws": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.8",
|
||||
"@types/express-ws": "^3.0.0",
|
||||
"@types/node": "^14.6.4",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.0.2"
|
||||
}
|
||||
}
|
|
@ -1,410 +0,0 @@
|
|||
--[[
|
||||
-- Soqet.lua --
|
||||
https://github.com/Ale32bit/Soqet/
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Alessandro
|
||||
|
||||
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.
|
||||
]] --
|
||||
|
||||
--[[
|
||||
-- json.lua --
|
||||
https://github.com/rxi/json.lua
|
||||
|
||||
Copyright (c) 2019 rxi
|
||||
|
||||
|
||||
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.
|
||||
|
||||
]] --
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
local soqet = {
|
||||
ENDPOINT = "soqet.alexdevs.pw",
|
||||
ssl = true,
|
||||
json = json,
|
||||
credits = "Soqet.lua v2 by AlexDevs"
|
||||
}
|
||||
|
||||
if not soqet.json then
|
||||
if not fs.exists("json.lua") then
|
||||
local h = http.get("https://raw.githubusercontent.com/rxi/json.lua/master/json.lua")
|
||||
local f = fs.open("json.lua", "w")
|
||||
f.write(h.readAll())
|
||||
f.close()
|
||||
h.close()
|
||||
end
|
||||
|
||||
soqet.json = require("json")
|
||||
end
|
||||
|
||||
function soqet.new()
|
||||
if not http then
|
||||
return false, "HTTP is not enabled!"
|
||||
end
|
||||
|
||||
if not http.websocket then
|
||||
return false, "HTTP WebSocket feature is not enabled!"
|
||||
end
|
||||
|
||||
local client = {
|
||||
channels = {},
|
||||
uuid = nil,
|
||||
socket = nil,
|
||||
sessionId = math.random(0xffffff),
|
||||
ssl = soqet.ssl
|
||||
}
|
||||
|
||||
local function rawsend(data)
|
||||
if not client.socket then
|
||||
return false
|
||||
end
|
||||
|
||||
client.socket.send(soqet.json.encode(data))
|
||||
return true
|
||||
end
|
||||
|
||||
local function rawreceive()
|
||||
if not client.socket then
|
||||
client.connect()
|
||||
end
|
||||
|
||||
while true do
|
||||
local data = client.socket.receive()
|
||||
|
||||
data = soqet.json.decode(data)
|
||||
|
||||
client.uuid = data.uuid
|
||||
|
||||
if data.type == "ping" then
|
||||
rawsend(
|
||||
{
|
||||
type = "ping",
|
||||
id = 99
|
||||
}
|
||||
)
|
||||
elseif data.type == "motd" then
|
||||
client.motd = data.motd
|
||||
elseif data.type == "message" then
|
||||
return data.channel, data.message, data.meta
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if soqet.ssl then
|
||||
client.ENDPOINT = "wss://" .. soqet.ENDPOINT .. "/" .. client.sessionId
|
||||
else
|
||||
client.ENDPOINT = "ws://" .. soqet.ENDPOINT .. "/" .. client.sessionId
|
||||
end
|
||||
|
||||
function client.connect()
|
||||
if client.socket then
|
||||
pcall(client.socket.close)
|
||||
end
|
||||
|
||||
local socket, err = http.websocket(client.ENDPOINT)
|
||||
if not socket then
|
||||
return false, err
|
||||
end
|
||||
|
||||
client.socket = socket
|
||||
|
||||
for i, v in pairs(client.channels) do
|
||||
client.open(v)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function client.open(channel)
|
||||
expect(1, channel, "string", "number")
|
||||
|
||||
client.channels[#client.channels + 1] = channel
|
||||
|
||||
return rawsend(
|
||||
{
|
||||
type = "open",
|
||||
channel = channel
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function client.close(channel)
|
||||
expect(1, channel, "string", "number")
|
||||
|
||||
for i, v in pairs(client.channels) do
|
||||
if v == channel then
|
||||
client.channels[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return rawsend(
|
||||
{
|
||||
type = "close",
|
||||
channel = channel
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function client.send(channel, message, meta)
|
||||
expect(1, channel, "string", "number")
|
||||
expect(3, meta, "nil", "table")
|
||||
|
||||
meta = meta or {}
|
||||
meta.library = meta.library or soqet.credits
|
||||
|
||||
return rawsend(
|
||||
{
|
||||
type = "send",
|
||||
channel = channel,
|
||||
message = message,
|
||||
meta = meta
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function client.auth(token)
|
||||
expect(1, token, "string")
|
||||
|
||||
return rawsend(
|
||||
{
|
||||
type = "auth",
|
||||
token = token
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function client.receive()
|
||||
return rawreceive()
|
||||
end
|
||||
|
||||
function client.listen()
|
||||
client.listening = true
|
||||
while client.listening do
|
||||
local channel, message, meta = rawreceive()
|
||||
os.queueEvent("soqet_message", channel, message, meta, client.sessionId)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function client.unlisten()
|
||||
client.listening = false
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return client, client.sessionId
|
||||
end
|
||||
|
||||
function soqet.poll(token)
|
||||
local client = {
|
||||
channels = {},
|
||||
uuid = nil,
|
||||
sessionId = math.random(0xffffff),
|
||||
sessionToken = nil,
|
||||
ssl = soqet.ssl,
|
||||
connected = false,
|
||||
listening = true,
|
||||
updateInterval = 1
|
||||
}
|
||||
|
||||
if ssl then
|
||||
client.ENDPOINT = "https://" .. soqet.ENDPOINT
|
||||
else
|
||||
client.ENDPOINT = "http://" .. soqet.ENDPOINT
|
||||
end
|
||||
|
||||
local function postJson(path, body)
|
||||
return http.post(
|
||||
client.ENDPOINT .. "/api/" .. path,
|
||||
soqet.json.encode(body),
|
||||
{
|
||||
["Content-Type"] = "application/json"
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
local function rawreceive()
|
||||
if not client.connected then
|
||||
client.connect()
|
||||
end
|
||||
while true do
|
||||
local h, err =
|
||||
postJson(
|
||||
"update",
|
||||
{
|
||||
token = client.sessionToken
|
||||
}
|
||||
)
|
||||
|
||||
if not h then
|
||||
error(err)
|
||||
end
|
||||
|
||||
local data = soqet.json.decode(h.readAll())
|
||||
h.close()
|
||||
|
||||
local queue = {}
|
||||
|
||||
for i, v in ipairs(data.queue) do
|
||||
if v.type == "message" then
|
||||
table.insert(
|
||||
queue,
|
||||
{
|
||||
channel = v.channel,
|
||||
message = v.message,
|
||||
meta = v.meta
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if #queue > 0 then
|
||||
return queue
|
||||
else
|
||||
sleep(client.updateInterval)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function client.connect(token)
|
||||
expect(1, token, "nil", "string")
|
||||
|
||||
local h, err, eh = http.get(client.ENDPOINT .. "/api/connect?token=" .. textutils.urlEncode(token or ""))
|
||||
|
||||
if not h then
|
||||
return false, err, eh
|
||||
end
|
||||
|
||||
local data = soqet.json.decode(h.readAll())
|
||||
|
||||
h.close()
|
||||
|
||||
client.uuid = data.uuid
|
||||
client.sessionToken = data.token
|
||||
client.motd = data.motd
|
||||
|
||||
client.connected = true
|
||||
|
||||
for i, v in pairs(client.channels) do
|
||||
client.open(v)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function client.open(channel)
|
||||
expect(1, channel, "string", "number")
|
||||
|
||||
client.channels[#client.channels + 1] = channel
|
||||
|
||||
postJson(
|
||||
"open",
|
||||
{
|
||||
token = client.sessionToken,
|
||||
channel = channel
|
||||
}
|
||||
)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function client.close(channel)
|
||||
expect(1, channel, "string", "number")
|
||||
|
||||
for i, v in pairs(client.channels) do
|
||||
if v == channel then
|
||||
client.channels[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
postJson(
|
||||
"close",
|
||||
{
|
||||
token = client.sessionToken,
|
||||
channel = channel
|
||||
}
|
||||
)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function client.send(channel, message, meta)
|
||||
expect(1, channel, "string", "number")
|
||||
expect(3, meta, "nil", "table")
|
||||
|
||||
meta = meta or {}
|
||||
meta.library = meta.library or soqet.credits
|
||||
|
||||
postJson(
|
||||
"send",
|
||||
{
|
||||
token = client.sessionToken,
|
||||
channel = channel,
|
||||
message = message,
|
||||
meta = meta
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function client.receive()
|
||||
return rawreceive()
|
||||
end
|
||||
|
||||
function client.listen()
|
||||
client.listening = true
|
||||
while client.listening do
|
||||
local queue = rawreceive()
|
||||
|
||||
for i, v in ipairs(queue) do
|
||||
os.queueEvent("soqet_message", v.channel, v.message, v.meta, client.sessionId)
|
||||
end
|
||||
|
||||
sleep(client.updateInterval)
|
||||
end
|
||||
end
|
||||
|
||||
function client.unlisten()
|
||||
client.listening = false
|
||||
return true
|
||||
end
|
||||
|
||||
return client
|
||||
end
|
||||
|
||||
return soqet
|
|
@ -1,69 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
}
|
||||
}
|
117
v1/public/ui.css
117
v1/public/ui.css
|
@ -1,117 +0,0 @@
|
|||
* { margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 13px BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
background-color: #2C2F33;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #3f84c5;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #3f84c5;
|
||||
}
|
||||
|
||||
.chat {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
word-wrap: break-word;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
.chat div {
|
||||
padding: 5px 10px;
|
||||
border-bottom: 1px solid #353535;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
/*.chat { margin-bottom: 40px; }*/
|
||||
.displaychat {
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 8px;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #484b52;
|
||||
border: none;
|
||||
color: #b9bbbe !important;
|
||||
padding: 10px 27px;
|
||||
margin-top: 6px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
outline:none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #3c3e44;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.textinput {
|
||||
bottom: 0px;
|
||||
position: fixed;
|
||||
padding: 3px;
|
||||
width: 100vw;
|
||||
background-color: #2c2f33;
|
||||
border-top: 1px solid #353535;
|
||||
/*display: inline-block;
|
||||
z-index: 1;
|
||||
overflow:hidden;*/
|
||||
}
|
||||
|
||||
.textinput input[type=text] {
|
||||
width: 40vw;
|
||||
padding: 5px 5px;
|
||||
margin: 0 0 0 3px;
|
||||
box-sizing: border-box;
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
background-color: #484b52;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
/*cursor: not-allowed;*/
|
||||
}
|
||||
|
||||
.textinput:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.textinput input[type=submit] {
|
||||
width: auto;
|
||||
padding: 6px;
|
||||
margin-bottom: 0;
|
||||
margin-left: 2px;
|
||||
color: #7f8186;
|
||||
background-color: #484b52;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
/*cursor: not-allowed;*/
|
||||
}
|
||||
|
||||
.textinput input[type=submit]:hover {
|
||||
background-color: #7f8186;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/*.container {
|
||||
display: grid;
|
||||
grid-column-gap: 50px;
|
||||
grid-row-gap: 50px;
|
||||
|
||||
grid-template-areas:
|
||||
'main main main menu'
|
||||
}*/
|
||||
|
||||
/*.chat { grid-area: main; overflow: scroll;}
|
||||
|
||||
.sidebar { grid-area: menu ; }*/
|
|
@ -1,106 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<!-- This code kinda sucks. Pardon me -->
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Soqet Viewer</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="ui.css">
|
||||
<noscript><a style="color:white;">JavaScript is required to run!</a></noscript>
|
||||
<script>if (typeof module === 'object') {
|
||||
window.module = module;
|
||||
module = undefined;
|
||||
}</script>
|
||||
<script type="text/javascript">
|
||||
let channels = {};
|
||||
|
||||
function addLine(message, latest) {
|
||||
var list = document.getElementById('chat'); // get list
|
||||
var entry = document.createElement('div'); // create <p>
|
||||
entry.innerHTML = message;
|
||||
entry.class = "chat"; // give class chat to the <p>
|
||||
list.appendChild(entry); // add <p> to list
|
||||
window.scrollTo(0, document.body.scrollHeight); // auto scroll to bottom
|
||||
}
|
||||
|
||||
function sendMessage() {
|
||||
//return false;
|
||||
var message = document.getElementById("sendMsg").value;
|
||||
var channel = document.getElementById("channel").value;
|
||||
channel = Number.parseInt(channel) || channel;
|
||||
message = (function(){
|
||||
try {
|
||||
return JSON.parse(message);
|
||||
} catch(e) {
|
||||
return message;
|
||||
}
|
||||
})();
|
||||
if(!channels[channel]) {
|
||||
ws.send(JSON.stringify({
|
||||
type: "open",
|
||||
channel: channel,
|
||||
}));
|
||||
channels[channel] = true
|
||||
}
|
||||
addLine("> "+message);
|
||||
|
||||
if (message !== "") {
|
||||
ws.send(JSON.stringify({
|
||||
type: "send",
|
||||
channel: channel,
|
||||
message: message,
|
||||
}));
|
||||
}
|
||||
document.getElementById("sendMsg").value = "";
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var ws;
|
||||
|
||||
function startWS() {
|
||||
ws = new WebSocket("wss://soqet.alexdevs.pw"); // Connect to WS server
|
||||
|
||||
ws.addEventListener("message", function (event) { // Listen to all messages from server
|
||||
var data = JSON.parse(event.data); // Parse plain text to JSON
|
||||
console.log(data);
|
||||
if(data.type === "message") {
|
||||
addLine(`< [${data.channel}] <a style="color: #31D3FF">${JSON.stringify(data.message)}</a> <a style="color: #8a8a8a">${JSON.stringify(data.meta)}</a>`);
|
||||
} else {
|
||||
if(data.type !== "ping") {
|
||||
addLine(`< <a style="color: #8a8a8a">${JSON.stringify(data)}</a>`);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
startWS(); // Start WS for the first time
|
||||
</script>
|
||||
<script>if (window.module) module = window.module;</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width: 100%">
|
||||
<a style="color: #8a8a8a; display:none;"></a>
|
||||
<div id="escape" style="display: none;"></div>
|
||||
|
||||
<div class="displaychat">
|
||||
<div id="container" style="display: block;">
|
||||
<div class="chat" id="chat">
|
||||
|
||||
</div>
|
||||
<div class="textinput" id="textinput">
|
||||
<form onsubmit="return sendMessage()">
|
||||
<input id="channel" type="text" placeholder="Channel" autocomplete="true" style="width: 20%">
|
||||
<input id="sendMsg" type="text" placeholder="Message" autocomplete="true" autofocus>
|
||||
<input type="submit" value="Send" id="submitMsg">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue