2024-02-26 09:33:55 -05:00
|
|
|
const channelSocket = new WebSocket("ws://localhost:9091/connect");
|
2024-02-27 17:50:05 -05:00
|
|
|
const __messengerjs__ = {
|
|
|
|
"sessionID": null,
|
|
|
|
"imTheChannelOwner": false,
|
|
|
|
"myNickname": "",
|
|
|
|
"onloadfunction": null,
|
|
|
|
"onloadargs": null,
|
|
|
|
"fade": null
|
|
|
|
}
|
|
|
|
__messengerjs__.fade = document.createElement("div");
|
|
|
|
__messengerjs__.fade.style.cssText = "position: absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.75);color:white;text-align:center;font-family:sans-serif;";
|
|
|
|
__messengerjs__.fade.textContent = "connecting to WebSocket server...";
|
2024-02-27 10:23:34 -05:00
|
|
|
|
2024-02-28 08:41:20 -05:00
|
|
|
const beforeUnloadHandler = function(ev) {
|
2024-02-27 09:14:08 -05:00
|
|
|
Channel_OnAppClose();
|
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["disconnect"])));
|
2024-02-28 08:41:20 -05:00
|
|
|
channelSocket.close(1000, "unloading");
|
2024-02-27 09:14:08 -05:00
|
|
|
};
|
2024-02-28 08:41:20 -05:00
|
|
|
|
2024-02-26 09:33:55 -05:00
|
|
|
/** @section guacamole **/
|
|
|
|
const ParseCommandString = function(instruction) {
|
|
|
|
if(typeof instruction !== "string") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let position = -1;
|
|
|
|
let sections = new Array();
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
let length = instruction.indexOf('.', position + 1);
|
|
|
|
|
|
|
|
if(length === -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
position = (parseInt(instruction.slice(position + 1, length)) + length) + 1; // todo: not overflow
|
|
|
|
sections.push(instruction.slice(length + 1, position)
|
|
|
|
.replace(/'/g, "'")
|
|
|
|
.replace(/"/g, '"')
|
|
|
|
.replace(///g, '/')
|
|
|
|
.replace(/</g, '<')
|
|
|
|
.replace(/>/g, '>')
|
|
|
|
.replace(/&/g, '&')
|
|
|
|
);
|
|
|
|
|
|
|
|
if(instruction.slice(position, position + 1) === ';') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sections;
|
|
|
|
}
|
|
|
|
const EncodeCommandString = function(sections) {
|
|
|
|
if(Array.isArray(sections) === false) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let instruction = new String();
|
|
|
|
const argv = sections;
|
|
|
|
for (let argc = 0; argc < sections.length ; argc++) {
|
|
|
|
if(typeof sections[argc] !== "string") {
|
|
|
|
argv[argc] = sections[argc].toString();
|
|
|
|
}
|
|
|
|
argv[argc] = argv[argc]
|
|
|
|
.replace(/'/g, "'")
|
|
|
|
.replace(/"/g, '"')
|
|
|
|
.replace(/\//g, '/' )
|
|
|
|
.replace(/</g, '<' )
|
|
|
|
.replace(/>/g, '>' )
|
|
|
|
.replace(/&/g, '&' )
|
|
|
|
;
|
|
|
|
instruction = instruction.concat(argv[argc].length.toString(), ".", argv[argc])
|
|
|
|
instruction += (argc === sections.length - 1) ? ';' : ',';
|
|
|
|
}
|
|
|
|
return instruction;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** window.external **/
|
|
|
|
window.external.CloseApp = function() {
|
2024-02-28 08:41:20 -05:00
|
|
|
Channel_OnAppClose();
|
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["disconnect"])));
|
|
|
|
channelSocket.close(1000, "we called CloseApp");
|
2024-02-26 09:33:55 -05:00
|
|
|
}
|
|
|
|
// this is important because things expect it
|
|
|
|
/** window.external.Channel **/
|
2024-02-28 08:41:20 -05:00
|
|
|
window.external.Channel = {"Data": "", "SendData": null, "Initialize": null, "Error": {"Data": "", "Type": 0}}; // "Type" is a defined property
|
|
|
|
// things may want Channel.IM: string and Channel.FileInfo: object {Path: string, Size: number, Progress: number<0-100>, Incoming: bool, Status: num<0-3>}
|
2024-02-26 09:33:55 -05:00
|
|
|
window.external.Channel.SendData = function(d) {channelSocket.send(d)}; // this is dumb but trying to redirect this gives you TypeError's
|
|
|
|
window.external.Channel.Initialize = function() {
|
2024-02-28 08:41:20 -05:00
|
|
|
// no idea we're already pretty initalized by the time most things call this
|
|
|
|
Channel_OnTypeChanged(); // make client check if its even connected still
|
2024-02-26 09:33:55 -05:00
|
|
|
};
|
|
|
|
Object.defineProperty(window.external.Channel, "Type", {
|
|
|
|
get: function Type() {
|
|
|
|
if(channelSocket.readyState === 1) {
|
|
|
|
return 1; // Indirect;
|
|
|
|
} else {
|
|
|
|
return 2; // Disconnected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
/** window.external.Users **/
|
2024-02-28 08:41:20 -05:00
|
|
|
// yes i define these as the default users... should be created a bit more dynamically
|
|
|
|
// problem with that is that i'm yet to find a 1 user activity that calls any of these... ReplaceIM abuse maybe?
|
|
|
|
// avaliablity of these properties depends on the permission flags, especially UserProperties being enabled
|
2024-02-26 09:33:55 -05:00
|
|
|
window.external.yellows111_Users = [
|
2024-02-27 09:14:08 -05:00
|
|
|
{"Name": "Initiator", "Email": "initiator@messenger.js", "GlobalIP": "0.0.0.0", "LocalIP": "0.0.0.0", "PUID": "0"},
|
|
|
|
{"Name": "Target", "Email": "target@messenger.js", "GlobalIP": "0.0.0.0", "LocalIP": "0.0.0.1", "PUID": "1"}
|
2024-02-26 09:33:55 -05:00
|
|
|
];
|
2024-02-27 09:14:08 -05:00
|
|
|
|
|
|
|
window.external.Users = {"Me": null, "Inviter": null, "Item": null}; // "Count" is a defined property
|
|
|
|
/** window.external.Users._NewEnum **/
|
|
|
|
window.external.Users[Symbol.iterator] = function*() {
|
|
|
|
yield window.external.yellows111_Users[0];
|
|
|
|
yield window.external.yellows111_Users[1];
|
|
|
|
};
|
2024-02-26 09:33:55 -05:00
|
|
|
window.external.Users.Item = function(which){
|
|
|
|
return window.external.yellows111_Users[which];
|
|
|
|
}
|
|
|
|
Object.defineProperty(window.external.Users, "Count", {
|
|
|
|
get: function Count() {
|
2024-02-27 09:14:08 -05:00
|
|
|
return window.external.yellows111_Users.length; // 2
|
|
|
|
}
|
|
|
|
});
|
|
|
|
/** Some early activities use a lower case "Me", support that like this... **/
|
|
|
|
Object.defineProperty(window.external.Users, "me", {
|
|
|
|
get: function me() {
|
|
|
|
return window.external.Users.Me;
|
2024-02-26 09:33:55 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-02-28 08:41:20 -05:00
|
|
|
/*
|
|
|
|
window.external.Messenger exists but all it does is:
|
|
|
|
* allow you to open the Options dialog (on a certain page: number)
|
|
|
|
* allow you to open the phone dialer (with a predetermined number: string)
|
|
|
|
so why would i even bother?
|
|
|
|
*/
|
2024-02-26 09:33:55 -05:00
|
|
|
/** websocket stuff **/
|
2024-02-27 09:14:08 -05:00
|
|
|
channelSocket.binaryType = "arraybuffer"; // the alternative is blob.arrayBuffer(); IT'S A PROMISE CALL AND PROMISES SUXX
|
2024-02-26 09:33:55 -05:00
|
|
|
channelSocket.onmessage = function(event) {
|
|
|
|
switch(event.data.constructor.name) {
|
|
|
|
case "String": {
|
|
|
|
// is a Activity message
|
|
|
|
window.external.Channel.Data = event.data;
|
|
|
|
Channel_OnDataReceived();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "ArrayBuffer": {
|
|
|
|
// is a Command message
|
|
|
|
let args = ParseCommandString(new TextDecoder("utf8").decode(event.data));
|
|
|
|
switch(args[0]) {
|
|
|
|
case "rename": {
|
2024-02-27 17:50:05 -05:00
|
|
|
__messengerjs__.myNickname = args[3];
|
2024-02-26 09:33:55 -05:00
|
|
|
break;
|
|
|
|
}
|
2024-02-28 08:41:20 -05:00
|
|
|
case "ready": {
|
|
|
|
console.log("joined channel successfully.");
|
|
|
|
__messengerjs__.fade.textContent = "waiting for opponent to join this channel...";
|
|
|
|
window.addEventListener("beforeunload", beforeUnloadHandler);
|
|
|
|
__messengerjs__.imTheChannelOwner = (args[1] !== "1");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "error": {
|
|
|
|
window.external.Channel.Error.Data = args[1];
|
|
|
|
window.external.Channel.Error.Type = parseInt(args[2]);
|
|
|
|
switch(args[2]) {
|
|
|
|
case "0": { // if we're getting an error with STATUS_SUCCESSFUL... which sounds stupid, but i'm using it as SERVER_CLOSING)
|
|
|
|
__messengerjs__.fade.textContent = "the channel server was closed. please try again later.";
|
|
|
|
__messengerjs__.fade.style.display = "block";
|
|
|
|
channelSocket.close(1000, "server shutdown acknoledged.");
|
|
|
|
Channel_OnTypeChanged();
|
|
|
|
Channel_OnRemoteAppClosed(); // they're gone
|
|
|
|
Channel_OnAppClose(); // we're gone too
|
2024-02-26 09:33:55 -05:00
|
|
|
break;
|
|
|
|
}
|
2024-02-28 08:41:20 -05:00
|
|
|
case "513": { // SERVER_BUSY = channel was full
|
|
|
|
console.warn("channel was full, generating new room");
|
|
|
|
__messengerjs__.sessionID = Math.random().toString(36).split('.')[1].substring(0,8);
|
|
|
|
history.pushState(null, "", `#channel=${__messengerjs__.sessionID}`);
|
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["connect", __messengerjs__.sessionID])));
|
2024-02-26 09:33:55 -05:00
|
|
|
break;
|
|
|
|
}
|
2024-02-28 08:41:20 -05:00
|
|
|
default: {
|
|
|
|
__messengerjs__.fade.textContent = "server error:" + args[1] + "code (" + args[2] + ")";
|
|
|
|
__messengerjs__.fade.style.display = "block";
|
|
|
|
channelSocket.close(1000, "server sent non-zero error");
|
|
|
|
Channel_OnTypeChanged();
|
|
|
|
Channel_OnDataError(); // this may just attempt to retry to send the failed data... when we're already closed;
|
2024-02-26 09:33:55 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "adduser": {
|
2024-02-27 17:50:05 -05:00
|
|
|
__messengerjs__.fade.style.display = "none";
|
|
|
|
if(__messengerjs__.imTheChannelOwner === false) {
|
|
|
|
window.external.yellows111_Users[1].Name = __messengerjs__.myNickname;
|
2024-02-27 10:23:34 -05:00
|
|
|
window.external.yellows111_Users[0].Name = args[2];
|
|
|
|
} else {
|
|
|
|
window.external.yellows111_Users[1].Name = args[2];
|
2024-02-27 17:50:05 -05:00
|
|
|
window.external.yellows111_Users[0].Name = __messengerjs__.myNickname;
|
2024-02-27 10:23:34 -05:00
|
|
|
}
|
2024-02-27 17:50:05 -05:00
|
|
|
if(__messengerjs__.imTheChannelOwner === true) {
|
2024-02-27 10:23:34 -05:00
|
|
|
window.external.Users.Me = window.external.yellows111_Users[0];
|
|
|
|
window.external.Users.Inviter = window.external.yellows111_Users[0];
|
|
|
|
} else {
|
|
|
|
window.external.Users.Me = window.external.yellows111_Users[1];
|
|
|
|
window.external.Users.Inviter = window.external.yellows111_Users[0];
|
|
|
|
}
|
2024-02-27 17:50:05 -05:00
|
|
|
document.title = __messengerjs__.myNickname + ": " + document.title;
|
2024-02-27 10:23:34 -05:00
|
|
|
/** beyond even hack territory **/
|
2024-02-27 17:50:05 -05:00
|
|
|
if(typeof __messengerjs__.onloadfunction === "function") {
|
|
|
|
__messengerjs__.onloadfunction(__messengerjs__.onloadargs);
|
2024-02-26 09:33:55 -05:00
|
|
|
}
|
|
|
|
Channel_OnRemoteAppLoaded();
|
|
|
|
Channel_OnTypeChanged();
|
|
|
|
break;
|
|
|
|
}
|
2024-02-27 09:14:08 -05:00
|
|
|
case "remuser": {
|
2024-02-27 17:50:05 -05:00
|
|
|
__messengerjs__.fade.textContent = "opponent (" + args[1] + ") disconnected. refresh to get a new session!";
|
|
|
|
__messengerjs__.fade.style.display = "block";
|
2024-02-27 09:14:08 -05:00
|
|
|
window.removeEventListener("beforeunload", beforeUnloadHandler);
|
|
|
|
Channel_OnRemoteAppClosed();
|
2024-02-28 08:41:20 -05:00
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["disconnect"])));
|
|
|
|
channelSocket.close(1000, "opponent left");
|
2024-02-26 09:33:55 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "sync": {
|
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["sync", (new Date).getTime()])));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
console.warn("unknown instruction sequence:", args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-27 09:14:08 -05:00
|
|
|
channelSocket.onerror = function(ev) {
|
|
|
|
window.external.Channel.Error.Data = "WebSocket generic error";
|
2024-02-27 17:50:05 -05:00
|
|
|
__messengerjs__.fade.textContent = "encountered a WebSocket error"
|
|
|
|
__messengerjs__.fade.style.display = "block";
|
2024-02-26 09:33:55 -05:00
|
|
|
Channel_OnDataError();
|
|
|
|
}
|
|
|
|
channelSocket.onclose = function() {
|
2024-02-27 09:14:08 -05:00
|
|
|
Channel_OnTypeChanged();
|
2024-02-26 09:33:55 -05:00
|
|
|
}
|
2024-02-27 10:23:34 -05:00
|
|
|
|
2024-02-26 09:33:55 -05:00
|
|
|
function createWSEventsNowThatImReady() {
|
|
|
|
if(channelSocket.readyState === 1) {
|
|
|
|
// if we're already there
|
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["rename"])));
|
2024-02-27 17:50:05 -05:00
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["connect", __messengerjs__.sessionID])));
|
2024-02-26 09:33:55 -05:00
|
|
|
}
|
|
|
|
channelSocket.onopen = function() {
|
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["rename"])));
|
2024-02-27 17:50:05 -05:00
|
|
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["connect", __messengerjs__.sessionID])));
|
2024-02-26 09:33:55 -05:00
|
|
|
}
|
2024-02-27 10:23:34 -05:00
|
|
|
}
|
|
|
|
/** pure evil **/
|
2024-02-27 17:50:05 -05:00
|
|
|
__messengerjs__.OnLoadInterrupt = function(ev) {
|
|
|
|
document.body.appendChild(__messengerjs__.fade);
|
|
|
|
if(new URL(location.href).hash.substring(1).split("=")[0] === "channel") {
|
|
|
|
__messengerjs__.sessionID = new URL(location.href).hash.substring(1).split("=")[1];
|
2024-02-27 10:23:34 -05:00
|
|
|
} else {
|
2024-02-27 17:50:05 -05:00
|
|
|
__messengerjs__.sessionID = Math.random().toString(36).split('.')[1].substring(0,8);
|
|
|
|
history.pushState(null, "", `#channel=${__messengerjs__.sessionID}`);
|
2024-02-27 10:23:34 -05:00
|
|
|
}
|
|
|
|
createWSEventsNowThatImReady();
|
2024-02-27 17:50:05 -05:00
|
|
|
__messengerjs__.onloadargs = ev;
|
2024-02-27 10:23:34 -05:00
|
|
|
}
|
2024-02-28 08:41:20 -05:00
|
|
|
document.addEventListener("DOMContentLoaded", function(event) {
|
|
|
|
// do document.body.onload too since some things suck and like doing that
|
2024-02-27 17:50:05 -05:00
|
|
|
if(typeof document.body.onload === "function") {
|
|
|
|
__messengerjs__.onloadfunction = document.body.onload;
|
2024-02-27 10:23:34 -05:00
|
|
|
}
|
2024-02-27 17:50:05 -05:00
|
|
|
window.onload = __messengerjs__.OnLoadInterrupt;
|
|
|
|
Object.defineProperty(window, "onload", {
|
|
|
|
set: function onload(fn) {
|
|
|
|
__messengerjs__.onloadfunction = fn;
|
|
|
|
}
|
|
|
|
});
|
2024-02-27 10:23:34 -05:00
|
|
|
});
|