Stage 2 : Finish Alpha, Begin Beta #2

Merged
saqut merged 14 commits from alpha into stable 2022-11-27 18:59:22 +03:00
13 changed files with 5478 additions and 0 deletions

77
Source/Client.js Normal file
View File

@ -0,0 +1,77 @@
function Client()
{
/**
* @type {string}
*/
this.id = null;
/**
* @type {import("websocket").connection}
*/
this.socket = null;
/**
* @type {Date}
*/
this.created_at = null;
this.store = new Map();
this.rooms = new Set();
this.pairs = new Set();
this.requiredPair = false;
};
/**
* @type {Map<string, Client>}
*/
Client.clients = new Map();
/**
* @param {Client} client
*/
Client.prototype.peerRequest = function(client){
let info = {};
this.store.forEach((value, name) => info[name] = value);
this.pairs.add(client.id);
client.send([{
from: this.id,
info
},'request/pair']);
};
/**
* @param {Client} client
*/
Client.prototype.acceptPeerRequest = function(client){
this.pairs.add(client.id);
client.send([{
from: this.id
},'accepted/pair']);
};
/**
* @param {Client} client
*/
Client.prototype.rejectPeerRequest = function(client){
this.pairs.delete(client.id);
client.pairs.delete(this.id);
client.send([{
from: this.id
},'rejected/pair']);
};
/**
* @param {Client|string} client
* @returns {Boolean}
*/
Client.prototype.isPaired = function(client){
if(typeof client == "string")
{
return Client.clients.get(client)?.pairs.has(this.id) && this.pairs.has(client)
}
return client.pairs.has(this.id) && this.pairs.has(client.id);
};
Client.prototype.pairList = function(){
return [...this.pairs.values()].filter(e => this.isPaired(e));
};
Client.prototype.send = function(obj){
this.socket.sendUTF(JSON.stringify(obj));
};
exports.Client = Client;

24
Source/HTTPServer.js Normal file
View File

@ -0,0 +1,24 @@
"use strict";
let http = require("http");
let express = require("express");
let compression = require("compression");
let server = http.createServer();
let app = express();
server.addListener("request", app);
app.use(compression({
level: 9
}));
server.listen(7707,'0.0.0.0',() => {
console.log("HTTP Service Running...");
});
exports.http = server;
let {resolve} = require("path");
app.get("/script",(request, response)=>{
response.sendFile(resolve("./script/wsjs.js"))
});
app.get("*",(request, response)=>{
response.sendFile(resolve("./script/status.xml"))
});

151
Source/Services/Auth.js Normal file
View File

@ -0,0 +1,151 @@
const { Client } = require("../Client.js");
let {addService, addListener} = require("../WebSocket.js");
addListener('disconnect',(global, xClient)=>{
for (const pair of xClient.pairs) {
}
})
addService(({
client,
message,
end,
next
})=>{
let {type,username,password,to} = message;
switch(type)
{
case 'auth/public':{
client.requiredPair = false;
return end({
value: 'success',
mode: 'public'
})
}
case 'auth/private':{
client.requiredPair = true;
return end({
value: 'success',
mode: 'private'
})
}
case 'request/pair':{
if(Client.clients.has(to)){
return end({
status: 'fail',
message: 'CLIENT-NOT-FOUND'
})
};
let pairclient = Client.clients.get(to);
if(pairclient.pairs.has(client.id))
{
return end({
status: 'success',
message: 'ALREADY-PAIRED'
})
}
if(client.pairs.add(to))
{
return end({
status: 'fail',
message: 'ALREADY-REQUESTED'
})
}
client.peerRequest(pairclient);
break;
}
case 'accept/pair':{
if(Client.clients.has(to)){
return end({
status: 'fail',
message: 'CLIENT-NOT-FOUND'
})
};
let pairclient = Client.clients.get(to);
if(pairclient.pairs.has(client.id))
{
return end({
status: 'success',
message: 'ALREADY-PAIRED'
})
}
if(!client.pairs.has(to))
{
return end({
status: 'fail',
message: 'NOT-REQUESTED-PAIR'
})
}
client.acceptPeerRequest(pairclient);
break;
}
case 'reject/pair':{
if(Client.clients.has(to)){
return end({
status: 'fail',
message: 'CLIENT-NOT-FOUND'
})
};
let pairclient = Client.clients.get(to);
if(pairclient.pairs.has(client.id))
{
return end({
status: 'success',
message: 'ALREADY-PAIRED'
})
}
if(!client.pairs.has(to))
{
return end({
status: 'fail',
message: 'NOT-REQUESTED-PAIR'
})
}
client.rejectPeerRequest(pairclient);
break;
}
case 'pair/list':{
end({
type:'pair/list',
value: pairList
})
break;
}
case 'auth/check':{
let auth = client.store.has('user');
return end({
value: auth
})
}
case 'auth/login':{
if(username == '*' && password == '*')
{
return end({
status: 'success'
})
}else{
return end({
status: 'fail'
})
}
}
case 'auth/logout':{
let auth = client.store.has('user');
if(auth)
{
client.store.delete('user');
return end({
status: 'success'
})
}else{
return end({
status: 'fail'
})
}
}
default:{
next();
}
}
});

View File

@ -0,0 +1,75 @@
const { Client } = require("../Client.js");
let {randomUUID} = require("crypto");
let {addService,addListener} = require("../WebSocket.js");
const { Room } = require("./Room.js");
let term = require("terminal-kit").terminal;
/*
Peer to peer veri aktarımı
- Kişiden kişiye direkt veri aktarımı
- Kişiler arası tünelleme / Kişiler arası özel protokol
- Oda katılımcıları içerisinde veri aktarımı
- Oda katılımcıları arasında belli kişilere veri aktarımı
*/
addService(({
client,
end,
global,
message,
next,
response
}) => {
let {type} = message;
switch(type)
{
case "pack/to":{
let {to,pack,handshake} = message;
if(Client.clients.has(to))
{
let otherPeer = Client.clients.get(to);
otherPeer.send([{
from: client.id,
pack: pack
}, 'pack']);
handshake && end({
type: 'success'
})
}else{
handshake && end({
type: 'fail'
})
}
break;
}
case "pack/room":{
let {to,pack, handshake} = message;
if(Room.rooms.has(to))
{
if(!client.rooms.has(to))
{
return handshake && end({
type: 'fail'
})
};
Room.rooms.get(to).send([{
from: client.id,
pack: pack
}, 'pack']);
handshake && end({
type: 'success'
})
}else{
handshake && end({
type: 'fail'
})
}
break;
}
default:{
next();
}
};
});

506
Source/Services/Room.js Normal file
View File

@ -0,0 +1,506 @@
const { Client } = require("../Client.js");
let {randomUUID,createHash} = require("crypto");
const joi = require("joi");
let {addService,addListener} = require("../WebSocket.js");
let term = require("terminal-kit").terminal;
function Sha256(update)
{
return createHash("sha256").update(update).digest("hex");
};
function Room()
{
/**
* @type {string}
*/
this.id = randomUUID();
/**
* @type {string}
*/
this.name = "";
/**
* @type {string}
*/
this.description = "";
/**
* @type {Client}
*/
this.owner = null;
/**
* @type {Date}
*/
this.createdAt = new Date();
/**
* @type {Map<string, Client>}
*/
this.clients = new Map();
/**
* @type {"public"|"private"}
*/
this.accessType = "";
/**
* @type {"free"|"invite"|"password"|"lock"}
*/
this.joinType = "invite";
/**
* @type {boolean}
*/
this.notifyActionInvite = false;
/**
* @type {boolean}
*/
this.notifyActionJoined = true;
/**
* @type {boolean}
*/
this.notifyActionEjected = true;
/**
* @type {string}
*/
this.credential = null;
/**
* @type {string[]}
*/
this.waitingInvited = new Set();
}
/**
* @param {Room} room
*/
Room.prototype.publish = function(room){
Room.rooms.set(this.id, this);
term.green("Room Published ").white(this.name," in ").yellow(this.clients.size).white(" clients")('\n');
};
Room.prototype.toJSON = function(){
let obj = {};
obj.id = this.id;
obj.accessType = this.accessType;
obj.createdAt = this.createdAt;
obj.description = this.description;
obj.joinType = this.joinType;
obj.name = this.name;
obj.owner = this.owner.id;
obj.waitingInvited = [...this.waitingInvited];
return obj;
};
Room.prototype.send = function(obj){
for (const client of this.clients.values()) {
client.send(obj);
}
term.green("Room bulk message ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
};
/**
* @param {Client} client
*/
Room.prototype.join = function(client){
if(this.notifyActionJoined)
{
this.send([{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},'room/joined']);
};
client.rooms.add(this.id);
this.clients.set(client.id, client);
term.green("Client Room joined ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
};
Room.prototype.down = function(){
term.red("Room is downed ").red(this.name," in ").yellow(this.clients.size + "").red(" clients")('\n');
this.send([{
roomid: this.id,
ownerid: this.owner.id
},'room/closed']);
Room.rooms.delete(this.id);
};
/**
* @param {Client} client
*/
Room.prototype.eject = function(client){
if(this.notifyActionEjected)
{
this.send([{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},'room/ejected']);
}
client.rooms.delete(this.id);
this.clients.delete(client.id);
if(this.clients.size == 0)
{
this.down();
term.red("Client Room closed ").red(this.name," at 0 clients")('\n');
}
term.red("Client Room ejected ").red(this.name," in ").yellow(this.clients.size + "").red(" clients")('\n');
};
/**
* @type {Map<string, Room>}
*/
Room.rooms = new Map();
addListener('connect',(global, client)=>{
let room = new Room();
room.accessType = "private";
room.joinType = "notify";
room.description = 'Yourself private room, you can invite friends';
room.id = client.id;
room.name = "Your Room | " + client.id;
room.owner = client;
room.join(client);
room.publish();
});
addListener('disconnect',(global, client)=>{
Room.rooms.get(client.id).eject(client);
for (const roomId of client.rooms) {
Room.rooms.get(roomId).eject(client);
}
});
let CreateRoomVerify = joi.object({
type: joi.any().required(),
accessType: joi.string().pattern(/^public$|private$/).required(),
notifyActionInvite: joi.boolean().required(),
notifyActionJoined: joi.boolean().required(),
notifyActionEjected: joi.boolean().required(),
joinType: joi.string().pattern(/^free$|^invite$|^password$|^lock$/).required(),
description: joi.string().required(),
name: joi.string().required(),
credential: joi.string().optional()
});
addService(({
client,
end,
global,
message,
next,
response
})=>{
let {type} = message;
switch(type)
{
case 'myroom-info':{
let room = Room.rooms.get(client.id);
end({
status: "success",
room: room.toJSON()
})
break;
}
case 'room-peers':{
let {roomId} = message;
if(Room.rooms.has(roomId))
{
end({
status: 'success',
peers: [
...Room.rooms.get(roomId).clients.values()
].map(i => i.id)
});
}else{
end({
status: 'fail'
})
}
break;
}
case 'room-info':{
let {name} = message;
for (const [roomId,{name:RoomName}] of Room.rooms) {
if(name == RoomName)
{
return end({
status : "success",
room : Room.rooms.get(roomId).toJSON()
})
}
};
end({
status : "fail",
message : "NOT-FOUND-ROOM"
})
break;
}
case 'joinedrooms':{
let data = [
...client.rooms
].map(e => {
return Room.rooms.get(e).toJSON()
});
end(data)
break;
}
case 'closeroom':{
let {roomId} = message;
if(Room.rooms.has(roomId))
{
let room = Room.rooms.get(roomId);
if(room.owner === client.id)
{
room.down();
end({
status: 'success'
});
}else{
end({
status: 'fail'
});
}
}else{
end({
status: 'fail'
})
}
break;
}
case 'create-room':{
let {error} = CreateRoomVerify.validate(message);
if(error)
{
return end({
status: 'fail',
messages: error.message
})
}else{
let {name} = message;
for (const [,{name:RoomName}] of Room.rooms) {
if(name == RoomName)
{
return end({
status : "fail",
message : "ALREADY-EXISTS"
})
}
}
let room = new Room();
room.accessType = message.accessType;
room.notifyActionInvite = message.notifyActionInvite;
room.notifyActionJoined = message.notifyActionJoined;
room.notifyActionEjected = message.notifyActionEjected;
room.joinType = message.joinType;
room.description = message.description;
room.name = message.name;
room.owner = client;
if(message.credential)
{
room.credential = Sha256(message.credential + "");
}
room.join(client);
room.publish();
end({
status: "success",
room: room.toJSON()
});
}
break;
}
case 'joinroom':{
let {name} = message;
let roomId;
for (const [_roomId,{name:RoomName}] of Room.rooms) {
if(name == RoomName)
{
roomId = _roomId
break;
}
}
let isRoom = Room.rooms.has(roomId);
if(isRoom)
{
let room = Room.rooms.get(roomId);
if(room.joinType == "lock")
{
return end({
status : "fail",
message : "LOCKED-ROOM"
})
}else if(room.joinType == "password")
{
if(room.credential == Sha256(message.credential + ""))
{
room.join(client);
return end({
status : "success",
room: room.toJSON()
})
}else return end({
status : "fail",
message : "WRONG-PASSWORD",
area: "credential"
})
}else if(room.joinType == "free"){
room.join(client);
return end({
status : "success",
room: room.toJSON()
})
}else if(room.joinType == "invite"){
room.waitingInvited.add(client.id);
if(room.notifyActionInvite)
{
room.send([{
id: client.id
},"room/invite"]);
}else{
room.owner.send([{
id: client.id
},"room/invite"]);
}
};
}else{
return end({
status : "fail",
message : "NOT-FOUND-ROOM"
})
}
break;
}
case 'accept/invite-room':{
let {roomId, clientId} = message;
// Odanın varlığının kontrolü
if(!Room.rooms.has(roomId))
{
end({
status : "fail",
message : "NOT-FOUND-ROOM"
})
};
let room = Room.rooms.get(roomId);
// erişim kontrolü
if(!client.rooms.has(room.id))
{
return end({
status : "fail",
message : "FORBIDDEN-INVITE-ACTIONS"
})
}
// Odaya katılma şeklinin doğruluğu
if(room.joinType == 'invite')
{
return end({
status : "fail",
message : "INVALID-DATA"
})
};
// Odaya katılma talebinin doğruluğu
if(!room.waitingInvited.includes(clientId))
{
return end({
status : "fail",
message : "NO-WAITING-INVITED"
})
};
// Odaya katılan kişinin varlığı
if(!Client.clients.has(clientId))
{
return end({
status : "fail",
message : "NO-CLIENT"
})
};
// Odaya kişiyi kabul etme
let JoinClient = Client.clients.get(clientId)
room.join(JoinClient);
JoinClient.send([{
status:"accepted"
},'room/invite/status']);
return end({
status : "success"
});
}
case 'reject/invite-room':{
let {roomId, clientId} = message;
// Odanın varlığının kontrolü
if(!Room.rooms.has(roomId))
{
end({
status : "fail",
message : "NOT-FOUND-ROOM"
})
};
let room = Room.rooms.get(roomId);
// erişim kontrolü
if(!client.rooms.has(room.id))
{
return end({
status : "fail",
message : "FORBIDDEN-INVITE-ACTIONS"
})
}
// Odaya katılma şeklinin doğruluğu
if(room.joinType == 'invite')
{
return end({
status : "fail",
message : "INVALID-DATA"
})
};
// Odaya katılma talebinin doğruluğu
if(!room.waitingInvited.includes(clientId))
{
return end({
status : "fail",
message : "NO-WAITING-INVITED"
})
};
// Odaya katılan kişinin varlığı
if(!Client.clients.has(clientId))
{
return end({
status : "fail",
message : "NO-CLIENT"
})
};
// Odaya davet edilen kişiyi reddetme
let JoinClient = Client.clients.get(clientId)
room.waitingInvited = room.waitingInvited.filter(e => e != clientId);
room.send([{id:clientId,roomId:room.id},'room/invite/status'])
JoinClient.send([{
status:"rejected"
},'room/invite/status']);
return end({
status : "success"
});
}
case 'room/list':{
let rooms = [];
for (const [id, {accessType,name,joinType,description}] of Room.rooms) if(accessType == "public"){
rooms.push({
name,
joinType,
description,
id
})
}
end({
type:'public/rooms',
rooms
});
}
default:{
next();
}
}
});
exports.Room = Room;

View File

@ -0,0 +1,8 @@
let {addListener} = require("../WebSocket.js");
addListener('connect',(global, client)=>{
client.send([{
type: 'id',
value: client.id
},'id'])
});

124
Source/WebSocket.js Normal file
View File

@ -0,0 +1,124 @@
"use strict";
let websocket = require("websocket");
let {http} = require("./HTTPServer");
let {randomUUID} = require("crypto");
const { Client } = require("./Client.js");
console.log("Web Socket Protocol is ready");
http.addListener("upgrade",() => {
console.log("HTTP Upgrading to WebSocket");
})
let wsServer = new websocket.server({
httpServer: http,
autoAcceptConnections: true
});
let global = new Map();
let clients = new Map();
wsServer.addListener("connect",(socket) => {
let xClient = new Client();
let id = randomUUID();
socket.id = id;
xClient.id = id;
xClient.socket = socket;
xClient.created_at = new Date();
Client.clients.set(id, xClient);
clients.set(id, xClient);
emit("connect", global, xClient);
socket.addListener("close",()=>{
emit("disconnect", global, xClient);
Client.clients.set(id, xClient);
});
socket.addListener("message",({type,utf8Data}) => {
if(type == "utf8")
{
let json;
try{
json = JSON.parse(utf8Data);
emit('services', global, xClient, json);
let [payload, id, action] = json;
if(typeof id === "string")
{
action = id;
}
emitService(global, xClient, id, payload, action);
}catch{
emit("messageError", global, xClient, utf8Data);
}
}
});
});
/**
* @type {Map<string, Function[]>}
*/
let events = new Map();
/**
* @type {Map<string, Function[]>}
*/
let services = []
/**
*
* @param {string} event
* @param {(global:Map<string, any>, client:Client, data:any) => any} func
*/
exports.addListener = (event, func) => {
if(!events.has(event))
{
events.set(event,[]);
};
events.get(event).push(func);
};
/**
*
* @param {string} event
* @param {(data:{global:Map<string, any>, client:Client, message:any,response:Function,end:Function,next:Function}) => any} func
*/
exports.addService = (func) => {
services.push(func);
};
function emit(event,...args)
{
if(events.has(event))
{
for (const callback of events.get(event)) {
callback(...args);
}
};
};
/**
*
* @param {Map} global
* @param {Client} local
* @param {number} id
* @param {{[key:string]:any}} payload
* @param {"R"|"S"} action [R]EQUEST flag or [S]TREAM flag
*/
async function emitService(global, client, id, payload, action)
{
let willContinue = false;
for (const callback of services) {
await callback({
message: payload,
action,
client,
global,
response:(obj)=>{
client.send([obj, id, 'C']) // continue ([C]ONTINUE flag)
},
end:(obj)=>{
client.send([obj, id, 'E']) // stopped data stream (this channel) ([E]ND flag)
},
next:function(){
willContinue = true;
}
});
if(willContinue === false) break;
}
};
exports.websocket = wsServer;

7
Source/index.js Normal file
View File

@ -0,0 +1,7 @@
require("./HTTPServer.js");
require("./WebSocket.js");
require("./Services/YourID.js");
require("./Services/Auth.js");
require("./Services/Room.js");
require("./Services/DataTransfer.js");

4124
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "mwse",
"version": "0.1.0",
"description": "Mikro WebSocket Engine",
"main": "Source/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "http://git.saqut.com/saqut/MWSE"
},
"keywords": [
"WebSocket",
"server",
"microservice",
"ws"
],
"author": "Abdussamed ULUTAŞ <abdussamedulutas@yandex.com.tr>",
"license": "MIT",
"dependencies": {
"compression": "^1.7.4",
"express": "^4.18.2",
"joi": "^17.7.0",
"knex": "^2.3.0",
"sqlite3": "^5.1.2",
"terminal-kit": "^3.0.0",
"websocket": "^1.0.34"
}
}

29
script/status.xml Normal file
View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<Server mask="255.255.255.255" of="55.67.239.30" to="0.0.0.0">
<unit name="sdp://74.50.50.1" cluster="0">
<service md5="cbaaa7f6899508277f0a1579b2c869b9" checkout="true" status="verified">
304e17d899c067afe3c439f92c5bd29869dfbde06ceca4f65e2128a93e7366c078bb0af1b14cb516008683df14c00107cadcce4d980f149da932bdba4a9892841b004ae877b2c7733e2d326d5586a4b2225384afbab6d00900852f5feb47ebd234ad95ec6957dbbbe585c86126f02e47cff3dc6d4ac9d321efbbd39a28655110fa850caca24a9b82984b60d0c517f3d8400594a17ba4a5fd0fcf19711c4b0ef503d698e8597c841eb099118d23e8cc8bc4db907d5093ca73e68f85c3dae629876b5d2fa49ed283591972559ae7da1b99489715c355f068c689c7cb4690ea4d4f3d1f364367b352bd74a0a57a541bfac90fe69d9b6d2fe941707c7316b067f5287c6aff01c86cbb23c2ac5e202cb497c203437c80159454b20dcadec7f2080b286c6b4e76826952b963f856e3c7e9ad89e50b76e2dd22f7bc1fbc8366a965cb76dc862d5c9b5b52802e431d2490079a7396db1bc279099a68a9b250135020e3906bc8c159b773a7dbb3d5beb54a30746fc776a07aeeb4f1f170d688b15742f530901959e4a4cd8ed869249e95a58c3cc45bc323aee66aec9090f4c938411c348b72d6ae4c3169c0049d84e88f4c26318136353db77519d7c060df07f7da78df71f13ed7be5916b2973fa712b4d826527911a04923dc763630306d04e09229584370e670c24d6af48bee2ead5525f55b8bd66083314b11d1dede385bfbdcaacd0b577c1d37f7bbcbb8d24f461723713570537abbcdfd8b96c9dcfc0f8c0afd7e6e43620dbfb88442cacd15a6cb28ee2d318e56d023af40a832010e8e2cb1d2d5223fd465616094fca6fecd92ff66e398f3ac8ac091beb3635dfacea5147848514c94ecdc011180cda61f977f9c59265682c77dc7af662e1c50386ac50835c6802adb86f941ec3d05316e505f7183bf8dc048685af48f6514726d993e789cfe13e28bd0ce7ea0e0e97f19364ec8567e63f28afcdf07de02fa8c79a850ffc4e9da3786ecec269aff4995ed5e3029a2abcba65ba62990e0842714688ca48162510449ebaf80878405c7373534aa6e688f4c56569f15e349272db874f3846eb3cea77f8de261628c285e93ff7c38aba3d65df19bf8dd8732068e817e90b92ddf7be29ec844f6b90af2547daf00093e4f2241c2d419e60c9b6d64e4278a2baa0a0396e98ccafebd131a6c5374baaf4a3e715c6c4d2c647de1cd0610cf9bcf3a9e857bef77a0ed51dc297ba914a5c41c5243cc72308b9de7908ec8955b52c9328b1d8efdd28d570c010f1329b7f0c40669004dc77cc87a34348310f444ba799fb0ed00687ae16681dc7eab7d1884213b59ff1e2e3cefa64676587fd37db308f4de795cb46b468948e38d340981e32ca2222cbff42bc3852f1657a690bfba8b918cfa05bd3f6a7bbc284e15ba39b114e8619b666748325e0b87514eff2b48a70ea906ca0ecf6f0f2c6438ffa8106239b94231f008339069fd420978e0f8103c94fcfb8a48018353443e8cbe131dc4261ef79945471cdaec9868954e420f4f43f738c098e3ee3378c5890409bc9e4f191e85f3318c5a125f48e8bc3a9eb67f413742c2b030847b3461bd9659e62af48aca3a456ae4f99beacd3df943608956fbadde816733edc9f2a107a8d92592b9690aeb69b48bfd9231ba345f1c71dcb6aae1c13cf1ab91942938db056a1569c6399cf57dd02032ef43f64f6e3ee3de56d11b7dc07f3bdb899714b912d6e8a45be8e2f6413ad51ef93aa75c5bd9c4a8c86231b31a5e68892bc8ec01de40db5d6ec87160c8d5d14b3725160407a9a7484bb794dee73e0a87cceeb874ed6d96941406f18b8634886c23d5be'
</service>
<service md5="cbaaa7f6899508277f0a1579b2c869b9" checkout="true" status="verified">
304e17d899c067afe3c439f92c5bd29869dfbde06ceca4f65e2128a93e7366c078bb0af1b14cb516008683df14c00107cadcce4d980f149da932bdba4a9892841b004ae877b2c7733e2d326d5586a4b2225384afbab6d00900852f5feb47ebd234ad95ec6957dbbbe585c86126f02e47cff3dc6d4ac9d321efbbd39a28655110fa850caca24a9b82984b60d0c517f3d8400594a17ba4a5fd0fcf19711c4b0ef503d698e8597c841eb099118d23e8cc8bc4db907d5093ca73e68f85c3dae629876b5d2fa49ed283591972559ae7da1b99489715c355f068c689c7cb4690ea4d4f3d1f364367b352bd74a0a57a541bfac90fe69d9b6d2fe941707c7316b067f5287c6aff01c86cbb23c2ac5e202cb497c203437c80159454b20dcadec7f2080b286c6b4e76826952b963f856e3c7e9ad89e50b76e2dd22f7bc1fbc8366a965cb76dc862d5c9b5b52802e431d2490079a7396db1bc279099a68a9b250135020e3906bc8c159b773a7dbb3d5beb54a30746fc776a07aeeb4f1f170d688b15742f530901959e4a4cd8ed869249e95a58c3cc45bc323aee66aec9090f4c938411c348b72d6ae4c3169c0049d84e88f4c26318136353db77519d7c060df07f7da78df71f13ed7be5916b2973fa712b4d826527911a04923dc763630306d04e09229584370e670c24d6af48bee2ead5525f55b8bd66083314b11d1dede385bfbdcaacd0b577c1d37f7bbcbb8d24f461723713570537abbcdfd8b96c9dcfc0f8c0afd7e6e43620dbfb88442cacd15a6cb28ee2d318e56d023af40a832010e8e2cb1d2d5223fd465616094fca6fecd92ff66e398f3ac8ac091beb3635dfacea5147848514c94ecdc011180cda61f977f9c59265682c77dc7af662e1c50386ac50835c6802adb86f941ec3d05316e505f7183bf8dc048685af48f6514726d993e789cfe13e28bd0ce7ea0e0e97f19364ec8567e63f28afcdf07de02fa8c79a850ffc4e9da3786ecec269aff4995ed5e3029a2abcba65ba62990e0842714688ca48162510449ebaf80878405c7373534aa6e688f4c56569f15e349272db874f3846eb3cea77f8de261628c285e93ff7c38aba3d65df19bf8dd8732068e817e90b92ddf7be29ec844f6b90af2547daf00093e4f2241c2d419e60c9b6d64e4278a2baa0a0396e98ccafebd131a6c5374baaf4a3e715c6c4d2c647de1cd0610cf9bcf3a9e857bef77a0ed51dc297ba914a5c41c5243cc72308b9de7908ec8955b52c9328b1d8efdd28d570c010f1329b7f0c40669004dc77cc87a34348310f444ba799fb0ed00687ae16681dc7eab7d1884213b59ff1e2e3cefa64676587fd37db308f4de795cb46b468948e38d340981e32ca2222cbff42bc3852f1657a690bfba8b918cfa05bd3f6a7bbc284e15ba39b114e8619b666748325e0b87514eff2b48a70ea906ca0ecf6f0f2c6438ffa8106239b94231f008339069fd420978e0f8103c94fcfb8a48018353443e8cbe131dc4261ef79945471cdaec9868954e420f4f43f738c098e3ee3378c5890409bc9e4f191e85f3318c5a125f48e8bc3a9eb67f413742c2b030847b3461bd9659e62af48aca3a456ae4f99beacd3df943608956fbadde816733edc9f2a107a8d92592b9690aeb69b48bfd9231ba345f1c71dcb6aae1c13cf1ab91942938db056a1569c6399cf57dd02032ef43f64f6e3ee3de56d11b7dc07f3bdb899714b912d6e8a45be8e2f6413ad51ef93aa75c5bd9c4a8c86231b31a5e68892bc8ec01de40db5d6ec87160c8d5d14b3725160407a9a7484bb794dee73e0a87cceeb874ed6d96941406f18b8634886c23d5be'
</service>
<service md5="2836d60b7e3a9a41f75d662d63c65197" checkout="true" status="verified">
35b9ea4d98445d6fda595e42c921269e0699ed78a2655b169fcaffef66521e649c1818cb71fb5a2ef4c0f453786de08c9cf81cdc4f6bcb8a7af68f23d9a4b3dc4a84950a13261e929ef4aa41a53819b8fc98582250533b476e46d6d0894a0003f315a6d8612d2d46eebe367330ae4587635daa4d4b929a33de91dfdb30b1aa3b3bded0ab06741786b19564b74e3b0d86a3d056bf9662fa004d2609a62b862caea7f6448a5257689a0de69798ac8eaf6186799c1e5de7ba124493365a76ded957d0f133190608b49c940cd7db6bdcb6b98739dfe6dc44b4736ebb10a3563ac7a4c6c63177cb292f32be6be3041cd2c27fef4c9f3ec2005041f80df142941185c57114d90500c744d9442f4507e43c91bbacc4205ee8dc4dbbc0656a6bf948309c446ffc67844f8e49171672ebbf380368979e20dcd114f889936f5a6ae0fdc393d4292659ee8f00070c0b6f01781e9c9d64a1ef7dc43912de5666d2f3495d9e4f3b478621e0e7e2ade6728e2de162aaf06b000ef5c514ba8004ed28cdbb9893a2b9e024a3ffedf98b920ff94130f31775cbc6a4f7a0dd361c40a8ca0956bf7fa4047bb00ab51c8aae729cce29a5427b8f80e28fcd5163a6f070c654ef05b85671fd1c9a2a2e52e8737b3cc04d6e95e0690b5e7d711b23c45a065f66193ab6d070a23efa56654f9427ec05e4e32a79e43869db3f3b61ca5c4fed04f2926e4bdb99367b15068868d0fb232359ea3c9e9c64150b4058a592b59943a7e23b69813bf08e637cca4ea1e5e996613a1db2684386a5e5f0dcbbea04b312f69512bab31db7e9ef94b6905a79690193f7289851821d135974af3696a2e0439585a960b3d8a31568bca8de1271a14cd30ed1a7b85adf23f9d76f5b62149242296677fea61288a4b58d2e38889b2bbd9926672df36b46fd6463bf0c0f7b1db1a7312071ef8e39a5a7deea4e47b6cda985de2cc5133337633b3db6d0cb54d56795a8a76fef2edf93debc3cbea6d0b902a0c710e73fa443c4842ec1646b4934514d2700eaa48b1320f0477f992ac0849bfd1fa628fd2b85a8997c5c2128bcb8135718b62180ede1ee8d4bf375c053a5419173b6adf54ab6f5943a377f7b2b4b81807fba3460b6acd28ef6d4be2bb015d9d5ea9b3c2d22389d7c688dd871e0a9fe295389711bf19804eeb68a86204fed691fe7493db9e0d2bce3f6ae4df5debe4cd4b4c668111146dc82b638080da9fa26d47d00f37538ee2a4a2930d39d1f734537a79d0d34f6cfab7893e2ebe71ec110e82f63a7d5c246737acadf1fc75c777faaa150164fc204c7ad3261fc9563a1b5bac908905d53fb7f37060dc1c5750634b200b31fbfaf4de1fefbc687b4c808968065e9cb567f003e1bd06b77e6442a7e5d3f54ff98d12d03d3f8705fdeb9614764acd153fc15a8763e5dc95e2c19ce478b8da93744680f2bfca75e50bebabc0cbb609575bb1000141dc35da3f4b5f5141899ff9ef856b7904baf5a828d2e4f576a58e505c5c9f3e5bc0e1ecfaf4ef36a7dee842aaf155db65912cd8dfccd11b188f5da5ea10035d47f17e0996791b0aacefa38799708198e4780846c7b1cf60aa9172a3a5935338bdb71e33a20b96181e487dd5caeeeff0446bc945ac2a83c3ca0ded82939d314073711c2afce8cc2a94fc6d46e28ea5bbafcd6adb517dedc9fa99446be727718bc41122214b6b788641e785ba0bee6530c61f9a9a58c14c6a5c477fd6f2117c67b1a2d8ca20e3aaef5c2d4e1f2b525fc8516ac90654880e72b4d87916e76592691d154a89f4125db6d9635e553955797f77ec00797f130f43b6245bf97cb88c93a641c78cbc352cf4816b075c81133cff364f4120399e3beb8cb6aae362a33de38c0d14d919a1939c00d46cd7ec2368620378fa490dd7d746361df3ee49b19a848035f619b08a66271fd97cb805ec834d85355495f003e39b6284b1ff52d5fbfeca7f7676f2a780c0b649abe6574da5867b2949af7d01e33fb9ffdee
</service>
<service md5="ae9965260e1098a43543a9a79fa1dac4" checkout="true" status="verified">
2fe9827b8286e6e5329cc9af2bb910f78b57ec5d073e6ef061360d7103f1c38245e04f804f431da3b51887a6e16cfee2a69534f4ab8ded57b74029beba7ccd2347b962d8f0d2d8bc93771a52ef4688fdf9759d33be9ba18b306a86fd3848b8ca8fe61abdd9ea2b9018c31554122172488166517e67276384b23239f02f4b5c4d8290dc542f0af54a66874a34bb8cd6114d7d5bce7404294f2c41ec20b85b942e5dca6e7b94fdea2e3dcd98777d0d7b631246c2222b69bd0547dc56d4c7990e0f803c32593cd218f4d655998ddd90e488a5d12aa7ef868f7641dc6a6df0bb0abfc0e76335ab682d909d117d863c90f6e21ebdb3ff8a7126bde4b14c524afe31fad20d1b000356cd25ab2276a2adf82638a5bf20304e2ad1a2f28cd7490e818fbec3566514cdeb412ce63d421738b038e40f70073d50352307de058b40d6215f6af1ce09cf6b954be69c7ef0f8030449a20faa5c2dd9b8d0f1adf911371156dc65b470fb02a092975af2f9080f689dd56cc3cd0f4c0434537e32b80cc22bd19743b1145d713422fed681b3bb9a09c5e5ec8f739631a8ffe2a49cebb2f13634b9414da48845948473616f18814447f63e018015b3d385cc592cc80c1f5a54d04029766572be94991d3e96b952961813e35fdc3f86a2d5fbfcd48a12d531be0f68ffcf48f1eb0e14623a6cfbdea026dd766fab9bbbc442468e6559d934959f0643c963c0799fcc3f98ccfa38b84cfb962a77cf5008037c81934425f0d7f4d1b7093adfc109918a7a130f1b3d5073a1517ac1c5b6dfe962917c8704e0d94d68795355f4ac341f84124bddd270c1da1a180ca101fb5f0bd4204e11461dce54d96827dcaa3e3a3889a621734268cf27ac18fe8c0df23108f3ef949fc68fd54122f3ea5fd5b2131b1ab7264006d730a7c4b90493b6858d78f5dfcf2cddbe8f1aa2b5da1de0580862ae16e01c5bce21a537ce0a8dfec8379c5152c0a3af41e09b7d35ed8920ad0f01abf1495b6e3e565cc8734ecf22a839212626f8f46cee5b90a742d43c2de2a280a2b1ffbe7e8285d8632bbf1137c28590f0420b3e75809c71f8b0e9bd8ed8995ba81003386d9c206ff5d3db014353d9565d1ac859f9335146cd3d2c8a04b4c3a1e7fa2e37a067ea2cb8fd4826010ecc5915114301fd7b2c7c72a222618c424797fe690cc9af7bc0a609c17a1dc8de4603df8ecbcc08d09bdd382b440fb29d11c822319b8572727592915fdcc1d9f7f3a025bec58834d5c55505c8013a5daf7f6e312b509500c2adc8e664c56031666124ab3f997185ae4f9286e7e6760ca8a106484fc68e50da961819fce231fca20330729b80be767c75006b1f8b796d60da0636d8fec8bd4c69671a8681f11f21f2b104ab4961c50b50f6b2b5fc59f78b674de891652d91f32ba60deaabf554158a38989438b67fb1713a14b4a2b58843a8b2df832d0969309f23ce4e07504a6be8aba05d8d79af93f41cf70fe0ef7b20ad603e4bd4c9261b0c3a007b46663c7b16ad77e8b8029d4d76fc4941205134d4cc9b7b7f9cc961ce5651afe8b308fa982cfdd57e9c21e1c65b2e906001f98f63cea267c44032c28348655541242b2092bfe8cb521bc491d376d989baa75c8f3c588089956f0736a71245c7acf0654dbaaad04803f8d8096d9a2c786a4c96ab99e4bc38119171839b718cffba4f8503d624cdfc2209597505da520878e2a10a9a7b0cd1e54ca09cf8c1ec9898735ea38e0886c940d8390d4581d205e4d9eb03d280071c6428bcee962122094af10e3784150651c5c4a583d8814c9e14810e021c659bf8cb66f82141d9a87248b20cba5cc212b2c0cd6dd085e690e549b31376babf1f3b19581956e26b1107f198975502
</service>
<service md5="7c72644fd27fad50a9a932107bbf9321" checkout="true" status="verified">
74f4e96bbc0f84de1f5f481c7e3257243edfcec173663557469a90ef23c067b8606774b867568db7dfe5209aada24cc96fcf93faa7a95180ffe2a20154898e82b27e27d01358c42c1857f9bdcb4a83ee1709226e2fc117c3a93cd3cb139777fc9295b4602c5d7c76f5a47d2f574009959a274f49d36e7435ed61e755768551859dfc34277df224c2dc9365112b9459e26235e7ce8619d0d1457954de93f604a200b0e9368b53aa7a3313b4f020d28625c646b6ca7bc6feadc7297b5a51f7eeb77adb016f346ca3bd2f4878a92dcef4abad57b0e5c8a10087f717e813892f55ed4e248cc6bdf0ea1edcaa1d42adbec7d2832c184d2185fdb66f0ee0043ce0a6035a151b22d81eafcf4cf9a8ce962e6ce1f6571b6df0a86dc221b770974f5bbfe960efbfb8c148f590584fbefae0b5cb95545bf69e4e5c9a39b431155649468ef093127aba424654ee0bb46d194ad17e61b022c43671dfef0eae98ec6ba6cd2924279fb592ac1d3babb61c0befddf6521efc38d4d75007499864cced0c8ee14cb5fd37b79626f636753b585ccfcf583ee92784850f0cacd3389c3546827bf3e7ef2719b25819f0208dcf7c3493edfc048a6fa3d35511e6f9c2a367cb87ad74c0f41bf853b6ba35f51fadfd5632012bde0180585dff6a7f114a2a958ce7ecbe484304dba2f138a86c4b0aad337c0047b1cb9dcd358300155d09b72d6c0ed4c53778e86fbdc7674d37a1441c1d38bf8f4c9976540090575863c41a8211c9b9450f28edd514ed8d51e3565de2fd30d6f0bf02fcfb79319ff04e7c4ad452be372fd9fe64021c8af6e566076b78f9c6d553848f9e1bf2ad3955c94470cfb8b6ba4aa81c53acc82395870ef9677b634515c4e4bd8de971292c3aa0c150516c546f8b172613dd1e314d4caf46ebd1770bb0d73b6ea2d53969
</service>
<service md5="5ca1eafd97774b352b06fe6a5f2d537a" checkout="true" status="verified">
8305f8dd0b412a4b95ef71a44a2cb069890895668054436640b9dffd2b0db4d84e21788b2cf8d609eb95773b4ef3c926335bc93bdc4df971a95f4c4c68ee627402c8935aa619ad276a6994004babf0bb41a8ca9fd5d22e76ec68f662eeb449ad9cdbd37f78b52fb88101e86a54323be7cd4777fdab26ada1cbba1ff0b08b6f451aa45daadcf9ef35a0a24039f2b1d5ca88ad64057afdbfe7c2bba6033380208d8d7c6c5f822a5f8bf19b5ee4f16efe068bd914d2e6ca831af24d53fa330c5d8150eb52fdd29518c14ecbc9af7e68a6e5f7a866150ecccf2b598fd6ba78521d5fe815797a6430ea09d6b394abe630df9510046bc983d9c41e3213868aeaa9dd531220959dd6305a1c582bde1a3e01d9b4be5d09150c1f4a84aa35c995103e6c6e3b6bb5345759b2219a31d05626373653d8151130ba1abef2aa78494f736792c0a462f54ebd86d2a110ae27ad1ad413baca5c7c4a302252da1423e44647cd57399e92cc805a60ce43bbda7a74797be1ad0275c0b270be2f117bd9ccc07bee42e247d178b15ba233535bffbb1c641cdd9ca3d387985b7980e8aed9752dbe30313f8a4b911f0ac7d8fd9352e3be80cdfaa3fd20ac5771c9f918d456b88cd3155de13a3f71bbcc627e766a16a86cac17f83fde98a2e7742bee4144ad235e23c76459522a96812819585155b6fcb6a08dafb19ccf93de37cb6738c803f73b0876e583d47ea3c59d6aba7b168c8de113a8f04cb813847a79682574ee71f6d9a5987b5e3be7169bf3bd82fbda4f5899e31b55b8c257f989516bbc879e60a49a4c5370230b4f728dd9389a059b481ea61046230e1382046955dae636005db580ec4a0540ee382fbc74b78b00ec821a19778fc119b673496df2bc2f11639922141d4cbb8fe22833f6fb210801419ab1b43f77a2baf892055a41c57e529026d4784aad469a4d2b530e81780f2aaea6b37b44243fa0f5e60a99391c6cf2e8a0f263a38566bfa56f761684b754bb8fa95aed80511332bff4673d600b6a09852cba34faca550f0652241a9eb04ca9652c4a19c20d37d6772604c3bbd34c82fa913a1a95e33b811f33952f4618d3600165574e29c44f7f748c05f90105791906e5920b356c4a8ce133dc2a809f0e47aa0ca2537a09688d2b92d49fa6b1f25165b6d2905ea99d1d6d56b2c39b08167df0cf18ce0f593228abbdbba718f05de4700ffa54067cb01fa65d9e378c0bc88d5f4d7bc153db964920e3c0dab30c02e87d957c369c6492bba7b240c27541d80bf0d39cbe62c077bdf6b1fb252d470cc39f9e43a13a95c08788dd5fe4e62fa3b07be01496bc977c6e1fe62977a2a8b72197fb3ad812369e96e3dd2ad329f134aaa4baee81c2ebd1b2c39e55fd9afcc554633ffb04a827ac75f7b26e49bcc341ea062ba13f672cd0a1dfd79a093273a5187e3adb6ece0e460555dc872c49b95e5054ffb772a4301feecdb072d6087345814b4af54de55d413eee562be25741a041bbdcb2850281
</service>
<service md5="a35aeef398a27556cf9e08aa6e25650c" checkout="true" status="verified">
8305f8dd0b412a4b95ef71a44a2cb069890895668054436640b9dffd2b0db4d84e21788b2cf8d609eb95773b4ef3c926335bc93bdc4df971a95f4c4c68ee627402c8935aa619ad276a6994004babf0bb41a8ca9fd5d22e76ec68f662eeb449ad9cdbd37f78b52fb88101e86a54323be7cd4777fdab26ada1cbba1ff0b08b6f451aa45daadcf9ef35a0a24039f2b1d5ca88ad64057afdbfe7c2bba6033380208d8d7c6c5f822a5f8bf19b5ee4f16efe068bd914d2e6ca831af24d53fa330c5d8150eb52fdd29518c14ecbc9af7e68a6e5f7a866150ecccf2b598fd6ba78521d5fe815797a6430ea09d6b394abe630df9510046bc983d9c41e3213868aeaa9dd531220959dd6305a1c582bde1a3e01d9b4be5d09150c1f4a84aa35c995103e6c6e3b6bb5345759b2219a31d05626373653d8151130ba1abef2aa78494f736792c0a462f54ebd86d2a110ae27ad1ad413baca5c7c4a302252da1423e44647cd57399e92cc805a60ce43bbda7a74797be1ad0275c0b270be2f117bd9ccc07bee42e247d178b15ba233535bffbb1c641cdd9ca3d387985b7980e8aed9752dbe30313f8a4b911f0ac7d8fd9352e3be80cdfaa3fd20ac5771c9f918d456b88cd3155de13a3f71bbcc627e766a16a86cac17f83fde98a2e7742bee4144ad235e23c76459522a96812819585155b6fcb6a08dafb19ccf93de37cb6738c803f73b0876e583d47ea3c59d6aba7b168c8de113a8f04cb813847a79682574ee71f6d9a5987b5e3be7169bf3bd82fbda4f5899e31b55b8c257f989516bbc879e60a49a4c5370230b4f728dd9389a059b481ea61046230e1382046955dae636005db580ec4a0540ee382fbc74b78b00ec821a19778fc119b673496df2bc2f11639922141d4cbb8fe22833f6fb210801419ab1b43f77a2baf892055a41c57e529026d4784aad469a4d2b530e81780f2aaea6b37b44243fa0f5e60a99391c6cf2e8a0f263a38566bfa56f761684b754bb8fa95aed80511332bff4673d600b6a09852cba34faca550f0652241a9eb04ca9652c4a19c20d37d6772604c3bbd34c82fa913a1a95e33b811f33952f4618d3600165574e29c44f7f748c05f90105791906e5920b356c4a8ce133dc2a809f0e47aa0ca2537a09688d2b92d49fa6b1f25165b6d2905ea99d1d6d56b2c39b08167df0cf18ce0f593228abbdbba718f05de4700ffa54067cb01fa65d9e378c0bc88d5f4d7bc153db964920e3c0dab30c02e87d957c369c6492bba7b240c27541d80bf0d39cbe62c077bdf6b1fb252d470cc39f9e43a13a95c08788dd5fe4e62fa3b07be01496bc977c6e1fe62977a2a8b72197fb3ad812369e96e3dd2ad329f134aaa4baee81c2ebd1b2c39e55fd9afcc554633ffb04a827ac75f7b26e49bcc341ea062ba13f672cd0a1dfd79a093273a5187e3adb6ece0e460555dc872c49b95e5054ffb772a4301feecdb072d6087345814b4af54de55d413eee562be25741a041bbdcb2850281
</service>
<service md5="af214a23bf109f45d423f0fdcad927f4" checkout="true" status="verified">
fb7d736ac8ee7a3a74f952551e8856a34c05d564ee0408ca0eea47a86e833b5b30a732b9d3ca954e56231908beb9022aec02c756c3e075bd9086f9ade0381b953185b4d3ed332d5c4b454abe5d2ccbeb48b473f956d543bde4f48c1978f129da7bf481b629a8da09941e5ea66d069617f6c82ac3a685b9e6cf75d8d4faf0b2ca0b52df201372c6e2d08dbcb2b724da94c733590165282e6faefd7bb89bb5be76d452f84b50a557d4b0f45192fc562f3682aba1098818ed3ddfaf72181d66f1f7b59be2b31759b66c
</service>
</unit>
</Server>

194
script/wsjs.js Normal file
View File

@ -0,0 +1,194 @@
function WSJS()
{
this.isActive = false;
this.ws = WSJS.activeWS || null;
this.endpoint = null;
};
WSJS.activeWS = null;
WSJS.prototype.connect = function(url){
this.ws = new WebSocket(url);
this.addListeners();
}
WSJS.prototype.addListeners = function(){
this.ws.addEventListener("close", this.closedEvent.bind(this));
this.ws.addEventListener("message", this.messageEvent.bind(this));
this.ws.addEventListener("open", this.openMessage.bind(this));
}
WSJS.prototype.closedEvent = function(){
WSJS.activeWS = null;
this.isActive = false;
this.ws = null;
this.events.dispatchEvent(new Event("close"));
}
WSJS.prototype.openMessage = function(){
WSJS.activeWS = this.ws;
this.isActive = true;
this.events.dispatchEvent(new Event("open"));
}
WSJS.prototype.messageEvent = function({data}){
let [payload, id, action] = JSON.parse(data);
if(typeof id === 'number')
{
if(this.requests.has(id))
{
this.requests.get(id)(payload, action);
switch(action)
{
case 'E':{ // [E]ND flag
this.requests.delete(id);
break;
}
case 'S': // [S]TREAM flag
default:{
break;
}
}
}else console.warn("Missing event sended from server");
}else{
if(this.signals.has(id))
{
for (const callback of this.signals.get(id)) {
callback(payload);
}
}else console.warn("Missing event sended from server");
}
}
WSJS.prototype.events = new EventTarget();
WSJS.prototype.scope = function(func){
if(this.isActive)
{
func();
}else this.events.addEventListener("open", func);
}
WSJS.prototype.sendOnly = function(obj){
if(this.isActive)
{
this.sendRaw([obj,'R']);
}else throw new Error(`socket could be a active`);
}
WSJS.prototype.requests = new Map();
WSJS.prototype.requestCount = 0;
WSJS.prototype.request = async function(obj){
if(this.isActive)
{
return await new Promise(ok => {
let id = ++this.requestCount;
this.sendRaw([obj,id,'R']);
this.requests.set(id,data => {
ok(data);
});
})
}else throw new Error(`socket could be a active`);
}
WSJS.prototype.stream = async function(obj,callback){
if(this.isActive)
{
let id = ++this.requestCount;
this.sendRaw([obj, id, 'S']);
this.requests.set(id,data => {
callback(data);
});
}else throw new Error(`socket could be a active`);
}
WSJS.prototype.signals = new Map();
WSJS.prototype.signal = async function(name, callback){
if(!this.signals.has(name))
{
this.signals.set(name, [callback]);
}else{
this.signals.get(name).push(callback);
}
}
WSJS.prototype.slot = async function(name, obj){
if(this.isActive)
{
if(typeof name == "string")
{
this.sendOnly([obj,name]);
}else{
throw new Error(`name could be a string, gived ${typeof name}`);
}
}else throw new Error(`socket could be a active`);
}
WSJS.prototype.sendRaw = function(obj){
if(this.isActive)
{
this.ws.send(JSON.stringify(obj))
};
};
WSJS.prototype.authWith = async function(username, password){
await this.request({
type: 'auth/login',
username,
password
});
};
WSJS.prototype.fetchMyRoomInfo = async function(){
return await this.request({
type: 'myroom-info'
});
};
WSJS.prototype.createRoom = async function(options){
let result = await this.request({
type: 'create-room',
accessType: options.accessType || "private",
notifyActionInvite: options.notifyActionInvite === undefined ? true : options.notifyActionInvite,
notifyActionJoined: options.notifyActionJoined === undefined ? true : options.notifyActionJoined,
notifyActionEjected: options.notifyActionEjected === undefined ? true : options.notifyActionEjected,
joinType: options.joinType || "free",
description: options.description || "No Description",
name: options.name || "No",
credential: options.credential || undefined
});
return result;
};
WSJS.prototype.roomInfo = async function(name){
let result = await this.request({
type: 'room-info',
name
});
return result;
};
WSJS.prototype.joinRoom = async function(options){
let result = await this.request({
...options,
type: 'joinroom'
});
return result;
};
WSJS.prototype.joinedRooms = new Map();
WSJS.prototype.getJoinedRooms = async function(){
return await this.request({
type: 'joinedrooms'
});
};
WSJS.prototype.getRoomPeers = async function(id){
return await this.request({
type: 'room-peers',
roomId: id
});
};
WSJS.prototype.sendPackToPeer = async function(roomId, pack){
return await this.sendOnly({
type: 'pack/to',
to: roomId,
pack,
handshake: false
});
};
WSJS.prototype.sendPackToRoom = async function(roomId, pack){
return await this.sendOnly({
type: 'pack/room',
to: roomId,
pack,
handshake: false
});
};

129
test.html Normal file
View File

@ -0,0 +1,129 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./wsjs.js"></script>
<script>
/* let wsjs = new WSJS({
endpoint: "ws://localhost:8282"
});
let room = wsjs.room({
name: "MY-ROOM",
description: "Gizli Odam",
joinType: "password",
credential: "123456Kc",
ifexistsJoin: true
});
room.on('joinpeer',(id)=>{
});
room.on('invitepeer',(id)=>{
});
room.on('acceptedinvite',(id)=>{
});
room.on('rejectedinvite',(id)=>{
});
peer.on('message',(pack)=>{
})
room.info();
room.send({
message: "HI!"
});
room.closeRoom();
room.ejectRoom();
room.fetchPeerList().then(e => console.log("e id listesi"))
let peer = wsjs.peer('07781835-0e96-4fc3-b7e1-8e3c67f9c5b7');
peer.pairRequest();
peer.on('accept',()=>{
peer.send({
message: "HI!"
});
});
peer.on('reject',()=>{
});
peer.on('message',(data)=>{
});
peer.on('close',(data)=>{
});
peer.close();
*/
let ws = new WSJS();
ws.connect('ws://localhost:8282');
ws.scope(async ()=>{
let secretRoom;
console.log("Connected ws")
let roomInfo = await ws.roomInfo("MY-ROOM");
console.log("Room Info", roomInfo)
let type = "";
if(roomInfo.status == 'fail' && roomInfo.message == "NOT-FOUND-ROOM")
{
type = "owner";
roomInfo = await ws.createRoom({
name: "MY-ROOM",
description: "Gizli Odam",
joinType: "password",
credential: "123456Kc"
});
setInterval(()=>{
ws.sendPackToRoom( roomInfo.room.id,"Merhaba");
}, 5000);
}else{
roomInfo = await ws.joinRoom({name:"MY-ROOM",credential:"123456Kc"});
setInterval(()=>{
ws.sendPackToPeer( roomInfo.room.owner,"Merhaba Yönetici");
}, 5000);
type = "member";
};
console.log("Oda bilgisi: ",roomInfo);
let Peers = await ws.getRoomPeers( roomInfo.room.id);
console.log("Odadaki eşlerin bilgisi: ",Peers)
console.log(await ws.getJoinedRooms());
});
ws.signal('id',(data)=>{
console.log("Your id is ", data.value)
});
ws.signal('pack',(pack)=>{
console.log("Recaived Package :", pack)
});
ws.signal("room/joined",(joinStatus)=>{
console.log("Room joined", joinStatus)
});
ws.signal("room/ejected",(ejectStatus)=>{
console.log("Room ejected", ejectStatus)
})
</script>
</body>
</html>