type type_event = { timestamp: int; kind: string; data: any; }; type type_user = { name: string; role: string; }; type type_connection = { events: Array; users: Array; client: any; }; function get_timestamp(): int { return Math.floor(Date.now()/1000); } function log(message: string): void { process.stderr.write(`-- ${message}\n`); } function generate_id(): string { return (Math.random() * (1 << 24)).toFixed(0).padStart(8, '0'); } var connections: Record = {}; function get_connection(data_in: any): type_connection { if (! connections.hasOwnProperty(data_in["id"])) { throw (new Error("no connection for ID '" + data_in["id"] + "'")); } else { return connections[data_in["id"]]; } } async function main(): Promise { var nm_irc: any = require("irc"); const server: lib_server.class_server = new lib_server.class_server( 7979, (input: string) => { const request: lib_http.type_request = lib_http.decode_request(input); // process.stderr.write(JSON.stringify(request, undefined, "\t") + "\n"); const data_in: any = JSON.parse(request.body); // log(data_in["action"] + " | " + (data_in["id"] ?? "-") + " | " + JSON.stringify(data_in["data"])); let data_out: any; let error: (null | Error); try { switch (data_in["action"]) { case "connect": { if (data_in.hasOwnProperty("id") && connections.hasOwnProperty(data_in["id"])) { throw (new Error("already connected")); } else { const id: string = generate_id(); const client = new nm_irc.Client( data_in["data"]["server"], data_in["data"]["nickname"], { "channels": data_in["data"]["channels"], "autoConnect": false, } ); const connection: type_connection = { "client": client, "events": [], "users": [], }; client.addListener( "message", (from, to, message) => { connection.events.push({ "timestamp": get_timestamp(), "kind": "channel_message", "data": { "from": from, "to": to, "message": message, } }); } ); client.addListener( "pm", (from, message) => { connection.events.push({ "timestamp": get_timestamp(), "kind": "private_message", "data": { "from": from, "message": message, } }); } ); client.addListener( "names", (channel, users) => { connection.users = Object.entries(users).map(([key, value]) => ({"name": key, "role": value.toString()})); } ); client.addListener( "error", (error) => { log("error: " + error.message); } ); client.connect( 3, () => { connections[id] = connection; } ); data_out = id; } break; } case "check": { try { get_connection(data_in); data_out = true; } catch (error) { data_out = false; } break; } case "disconnect": { const connection: type_connection = get_connection(data_in); delete connections[data_in["id"]]; connection.client.disconnect("", () => {}); data_out = null; break; } case "say": { const connection: type_connection = get_connection(data_in); connection.client.say(data_in["data"]["channel"], data_in["data"]["message"]); data_out = null; break; } case "fetch": { const connection: type_connection = get_connection(data_in); data_out = { "users": connection.users, "events": connection.events, }; connection.events = []; break; } } error = null; } catch (error_) { process.stderr.write(error_.toString() + "\n"); error = error_; } const response: lib_http.type_response = ( (error !== null) ? { "statuscode": 500, "headers": { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*", }, "body": error.toString(), } : { "statuscode": 200, "headers": { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", }, "body": JSON.stringify(data_out), } ); const output: string = lib_http.encode_response(response); return Promise.resolve(output); } ); server.start() } main();