AnyOSInstallBotJS/client.js
2024-06-23 18:45:14 -04:00

267 lines
5.9 KiB
JavaScript

// Small single-file CollabVM client library, in semi-modern Javascript
import * as ws from "ws";
const guacutils = {
parse: (string) => {
let pos = -1;
let sections = [];
for (;;) {
let len = string.indexOf(".", pos + 1);
if (len === -1) break;
pos = parseInt(string.slice(pos + 1, len)) + len + 1;
sections.push(
string
.slice(len + 1, pos)
.replace(/'/g, "'")
.replace(/"/g, '"')
.replace(///g, "/")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&amp;/g, "&"),
);
if (string.slice(pos, pos + 1) === ";") break;
}
return sections;
},
encode: (cypher) => {
let command = "";
for (var i = 0; i < cypher.length; i++) {
let current = cypher[i];
command += current.length + "." + current;
command += i < cypher.length - 1 ? "," : ";";
}
return command;
},
};
const ConnectionState = Object.freeze({
CLOSED: 0,
CONNECTING: 1,
CONNECTED: 2,
});
// System chat messages have a nil username.
function IsSystemChatInstruction(inst) {
return inst[1] == "";
}
class UserData {
#_name;
#_rank;
constructor(name, rank) {
this._name = name;
this._rank = rank;
}
GetName() {
return this._name;
}
GetRank() {
return this._rank;
}
UpdateRank(new_rank) {
this._rank = new_rank;
}
}
export default class CollabVMClient {
constructor() {
this._state = ConnectionState.CLOSED;
this._users = [];
}
GetState() {
return this._state;
}
Connect(uri) {
this._ws = new ws.WebSocket(uri, "guacamole", {
origin: "https://computernewb.com",
});
this._ws.onopen = this.OnWebSocketOpen.bind(this);
this._ws.onclose = this.OnWebSocketClose.bind(this);
this._ws.onerror = this.OnWebSocketError.bind(this);
this._ws.onmessage = this.OnWebSocketMessage.bind(this);
this._state = ConnectionState.CONNECTING;
}
OnWebSocketOpen() {
this._state = ConnectionState.CONNECTED;
this.OnOpen();
}
OnWebSocketClose() {
this._state = ConnectionState.CLOSED;
this.OnClose(arguments);
}
OnWebSocketError() {
// fire the close handler
this._state = ConnectionState.CLOSED;
this.OnClose(arguments);
}
Close() {
this._state = ConnectionState.CLOSED;
this._ws.close();
}
OnWebSocketMessage(ev) {
// cvm server should never send binary data
if (typeof ev.data !== "string") return;
let message = guacutils.parse(ev.data);
if (message.length === 0) return;
// Hardcoded, we need to keep this to be alive
if (message[0] === "nop") {
this.SendGuacamoleMessage("nop");
return;
}
//if(message[0] === "chat") {
// console.log(`FUCK (${message.length}) ${message[1]} ${message[2]}`)
//}
if (
message[0] === "chat" &&
message.length === 3 &&
!IsSystemChatInstruction(message)
) {
if (message[1] != this._username) {
//console.log(`FUCK 2 (${message.length}) ${message[1]} ${message[2]}`)
this.OnChat(message[1], message[2]);
return;
}
}
if (message[0] === "adduser") {
this.OnAddUser(message.slice(2), parseInt(message[1]));
return;
}
if (message[0] === "remuser")
this.OnRemUser(message.slice(2), parseInt(message[1]));
// Handle renames
if (message[0] === "rename") {
if (message.length === 5) {
if (message[1] == "1") {
this._username = message[3];
}
}
}
this.OnGuacamoleMessage(message);
}
OnAddUser(users, count) {
//console.log(users);
for (var i = 0; i < count * 2; i += 2) {
let name = users[i];
let rank = users[i + 1];
//console.log(`[${this.GetVM()}] user ${name} rank ${rank}`)
let existingUser = this._users.find((elem) => elem.GetName() == name);
if (existingUser === undefined) {
//console.log(`[${this.GetVM()}] New user ${name} rank ${rank}`)
this._users.push(new UserData(name, rank));
} else {
// Handle admin/mod rank update
if (existingUser.GetRank() != rank) {
//console.log(`[${this.GetVM()}] updating ${name} to rank ${rank}`)
existingUser.UpdateRank(rank);
}
}
}
this.OnAddUser_Bot(this.GetUserCount());
}
OnRemUser(users, count) {
for (var i = 0; i < count; i++) {
let saveUserTemp = this.GetUser(users[i]);
this._users = this._users.filter((user) => user.GetName() != users[i]);
}
this.OnRemUser_Bot(this.GetUserCount());
}
// This subtracts bots, including ourselves
GetUserCount() {
const KnownBots = [
this.GetUsername(),
//"General Darian"
// logger bots
"Specialized Egg",
"Emperor Kevin",
];
var len = this._users.length;
// subtract known bots
for (var i = len - 1; i != 0; --i) {
// ?
if (this._users[i] === undefined) return;
var name = this._users[i].GetName();
if (KnownBots.find((elem) => name == elem) !== undefined) {
//console.log("found blacklisted username", name)
len--;
}
}
return len;
}
GetUserCountFull() {
return this._users.length;
}
GetUsers() {
this._users;
}
GetUser(username) {
let existingUser = this._users.find((elem) => elem.GetName() == username);
// Apparently this can fail somehow..?
if (existingUser === undefined) return null;
return existingUser;
}
GetUsername() {
return this._username;
}
Rename(name) {
this._username = name;
this.SendGuacamoleMessage("rename", name);
}
GetVM() {
return this._vm;
}
ConnectToVM(vm) {
this._vm = vm;
this.SendGuacamoleMessage("connect", vm);
}
SendGuacamoleMessage() {
if (this._state !== ConnectionState.CONNECTED) return;
this._ws.send(guacutils.encode(Array.prototype.slice.call(arguments)));
}
}