var net = require("net"); var tls = require("tls"); var EventEmitter = require("events").EventEmitter; var util = require("util"); class irc { constructor(options) { EventEmitter.call(this); this.options = options || {}; this.options.channels = this.options.channels || []; this.options.host = this.options.host || "127.0.0.1"; this.options.port = this.options.port || 6667; this.options.ssl = this.options.ssl || false; this.options.selfSigned = this.options.selfSigned || false; this.options.sasl = this.options.sasl || false; this.network = this.options.network || "test"; this.nickname = this.options.nickname || "test"; this.username = this.options.username || "test"; this.realname = this.options.realname || "test"; this.channels = this.options.channels || []; this._recachetime = 60 * 5; // 10 minutes this.server = { motd: "", me: {}, channel: [], user: new Map() }; this.socket = (this.options.ssl ? tls : net).connect({ host: this.options.host, port: this.options.port, rejectUnauthorized: !this.options.selfSigned }, () => { this.send(`NICK ${this.nickname}`); this.send(`USER ${this.username} 0 * : ${this.realname}`); }); this.socket.setEncoding("utf-8"); this.socket.on("data", msg => { msg = this.parse(msg); let tmpuser = {}; let tmpchan = {}; let chans = []; switch (msg.command) { // auslagern! // WHOIS BEGIN case "307": // Rizon Account tmpuser = {}; if(this.server.user.has( msg.params[1].toLowerCase() )) tmpuser = this.server.user.get( msg.params[1].toLowerCase() ); tmpuser.account = msg.params[1].toLowerCase(); tmpuser.registered = true; this.server.user.set( msg.params[1].toLowerCase(), tmpuser ); break; case "311": // first tmpuser = {}; if (this.server.user.has( msg.params[1].toLowerCase() )) tmpuser = this.server.user.get( msg.params[1].toLowerCase() ); tmpuser.nickname = msg.params[1].toLowerCase(); tmpuser.username = msg.params[2]; tmpuser.hostname = msg.params[3]; tmpuser.realname = msg.params[5]; this.server.user.set( msg.params[1].toLowerCase(), tmpuser ); break; case "313": // Oper tmpuser = {}; if (this.server.user.has( msg.params[1].toLowerCase() )) tmpuser = this.server.user.get( msg.params[1].toLowerCase() ); tmpuser.oper = true; this.server.user.set( msg.params[1].toLowerCase(), tmpuser ); break; case "318": // last (check Data) tmpuser = {}; if (this.server.user.has( msg.params[1].toLowerCase() )) tmpuser = this.server.user.get( msg.params[1].toLowerCase() ); tmpuser = { nickname: tmpuser.nickname || false, username: tmpuser.username || false, hostname: tmpuser.hostname || false, realname: tmpuser.realname || false, account: tmpuser.account || false, registered: tmpuser.registered || false, oper: tmpuser.oper || false, channels: tmpuser.channels || [], cached: ~~(Date.now() / 1000) }; this.server.user.set( msg.params[1].toLowerCase(), tmpuser ); break; case "319": // Chanlist Todo tmpchan = new Map(); tmpuser = {}; if (this.server.user.has( msg.params[1].toLowerCase() )) { tmpuser = this.server.user.get( msg.params[1].toLowerCase() ); if(tmpuser.channels) tmpchan = tmpuser.channels; } chans = msg.params[2].trim().split(" "); for(let chan in chans) { chan = chans[chan].split("#"); tmpchan.set(`#${chan[1]}`, chan[0]); } tmpuser.channels = tmpchan; this.server.user.set( msg.params[1].toLowerCase(), tmpuser ); break; case "330": // Quarknet tmpuser = {}; if (this.server.user.has( msg.params[1].toLowerCase() )) tmpuser = this.server.user.get( msg.params[1].toLowerCase() ); tmpuser.account = msg.params[2]; tmpuser.registered = true; this.server.user.set( msg.params[1].toLowerCase(), tmpuser ); break; // WHOIS END case "001": // welcome this.server.me = msg.params[0]; this.join(this.options.channels); this.emit("data", ["connected", msg.params[1]]); break; case "352": // who_entry if(!this.server.channel[msg.params[1]]) this.server.channel[msg.params[1]] = new Map(); this.server.channel[msg.params[1]].set(msg.params[5], { // chan nick: msg.params[5], username: msg.params[2], hostname: msg.params[3] }); this.whois(msg.params[5]); break; case "315": // who_end console.log(this.server.channel); break; case "372": // motd this.server.motd += `${msg.params[1]}\n`; break; case "375": // motd_start this.server.motd = `${msg.params[1]}\n`; break; case "376": // motd_end this.server.motd += `${msg.params[1]}\n`; this.emit("data", ["motd", this.server.motd]); break; case "464": // Password required if (this.options.password.length > 0) this.send(`PASS ${this.options.password}`); // pwd break; case "PING": this.send(`PONG ${msg.params.join``}`); break; case "JOIN": console.log("join", msg); this.send(`WHO ${msg.params[0]}`); break; case "PART": console.log("part", msg); delete this.server.user[msg.params[0]]; //this.whois(msg.params[0], true); // force whois break; case "PRIVMSG": if (msg.params[1] === "\u0001VERSION\u0001") this.emit("data", ["ctcp:version", this.reply(msg)]); else this.emit("data", ["message", this.reply(msg)]); break; case "NOTICE": this.emit("data", ["notice", msg.params[1]]); break; default: console.log(msg); break; } }); } send() { for (let i = 0; i < arguments.length; i++) this.socket.write(arguments[i]); this.socket.write("\n"); } parse(data) { let raw = data; let i = 0; let prefix = ""; if (data.charAt(0) === ":") { i = data.indexOf(" "); prefix = data.slice(1, i); data = data.slice(i + 1); } i = data.indexOf(" "); if (i === -1) i = data.length; var command = data.slice(0, i); data = data.slice(i + 1); var params = []; while (data && data.charAt(0) !== ":") { i = data.indexOf(" "); if (i === -1) i = data.length; params.push(data.slice(0, i)); data = data.slice(i + 1); } if (data) { data = data.replace(/\r?\n|\r/g, ""); params.push(data.slice(1)); } return { raw: raw, prefix: prefix, command: command, params: params }; } reply(tmp) { return { type: "irc", network: this.network, channel: tmp.params[0], user: parsePrefix(tmp.prefix), message: tmp.params[1], time: ~~(Date.now() / 1000), raw: tmp, reply: msg => { this.send(`PRIVMSG ${tmp.params[0]} :${msg}`); }, replyAction: msg => { this.send(`PRIVMSG ${tmp.params[0]} :\u0001ACTION ${msg}\u0001`); }, replyNotice: msg => { this.send(`NOTICE ${tmp.params[0]} :${msg}`); }, // act chan _chan: this.server.channel[tmp.params[0]], _user: this.server.user, // commands join: chan => this.join(chan), part: (chan, msg) => this.part(chan, msg), whois: user => this.whois(user), write: msg => this.send(msg) }; } join(channel) { this.send(`JOIN ${(typeof channel === "object") ? channel.join(",") : channel}`); } part(channel, msg=false) { this.send(`PART ${(typeof channel === "object") ? channel.join(",") : channel}${msg ? " " + msg : " part"}`); } whois(user, force = false) { user = user.toLowerCase(); if(this.server.user.has(user) && !force) { if(this.server.user.get(user).cached >= ~~(Date.now() / 1000) - this._recachetime) { console.log(this.server.user.get(user).cached, ~~(Date.now() / 1000) - this._recachetime); console.log(user, "already cached"); return; } } console.log("get whois for", user); this.send(`WHOIS ${user}`); } } module.exports = irc; util.inherits(irc, EventEmitter); function parsePrefix(prefix) { let usertmp = prefix.split("!"); let hosttmp = usertmp[1].split("@"); return { nick: usertmp[0], username: hosttmp[0], hostname: hosttmp[1] }; }