Uwev2/src/clients/irc.mjs

190 lines
5.9 KiB
JavaScript
Raw Normal View History

2017-12-04 13:12:02 +01:00
import { logger } from "../inc/log";
import { getLevel } from "../inc/admin";
2017-12-04 12:22:53 +01:00
import modules from "./irc/main";
import net from "net";
import tls from "tls";
import EventEmitter from "events";
2017-11-25 13:31:26 +01:00
2017-11-26 00:26:34 +01:00
const colors = {
2017-11-26 13:51:08 +01:00
red: "\x0304$1\x0304",
blue: "\x0312$1\x0312",
yellow: "\x0308$1\x0308"
2017-11-26 00:26:34 +01:00
};
2018-02-14 00:33:00 +01:00
const msgmodes = {
normal: "PRIVMSG {recipient} :{msg}",
action: "PRIVMSG {recipient} :\u0001ACTION {msg}\u0001",
notice: "NOTICE {recipient} :{msg}"
};
2017-11-26 00:26:34 +01:00
const replaceColor = (match, color, text) => {
if (colors.hasOwnProperty(color))
return colors[color].replace("\$1", text);
return text;
};
2017-12-04 13:03:21 +01:00
export class irc extends EventEmitter {
2017-11-08 21:59:54 +01:00
constructor(options) {
2017-12-04 13:03:21 +01:00
super();
2017-11-08 21:59:54 +01:00
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;
2017-11-09 15:34:21 +01:00
this.options.sasl = this.options.sasl || false;
2017-11-08 21:59:54 +01:00
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 || [];
2018-02-28 04:34:52 +01:00
this.set = this.options.set || "all";
2017-11-24 22:35:35 +01:00
this._recachetime = 60 * 30; // 30 minutes
2017-11-20 14:19:52 +01:00
this._cmd = new Map();
2017-12-04 14:59:42 +01:00
modules.forEach(mod => mod(this));
2017-11-20 14:19:52 +01:00
2017-11-09 12:21:40 +01:00
this.server = {
2018-02-28 04:34:52 +01:00
set: this.set,
2017-11-09 12:21:40 +01:00
motd: "",
2017-11-09 17:04:30 +01:00
me: {},
channel: [],
2017-11-11 23:13:47 +01:00
user: new Map()
2017-11-09 12:21:40 +01:00
};
2017-11-08 21:59:54 +01:00
this.socket = (this.options.ssl ? tls : net).connect({
host: this.options.host,
port: this.options.port,
rejectUnauthorized: !this.options.selfSigned
}, () => {
2017-11-09 17:04:30 +01:00
this.send(`NICK ${this.nickname}`);
this.send(`USER ${this.username} 0 * : ${this.realname}`);
2017-11-19 22:11:23 +01:00
if(this.options.sasl)
2017-11-19 10:19:17 +01:00
this.send("CAP LS");
2017-11-19 20:16:25 +01:00
});
2017-11-18 11:53:58 +01:00
this.socket.setEncoding("utf-8");
2017-11-19 20:16:25 +01:00
this.socket.on("data", msg => {
2017-11-24 15:52:57 +01:00
msg.split(/\r?\n|\r/).filter(tmp => tmp.length > 0).forEach(tmp => {
const cmd = this.parse(tmp);
if (this._cmd.has(cmd.command))
this._cmd.get(cmd.command)(cmd);
})
2017-11-08 21:59:54 +01:00
});
}
2017-11-19 23:03:13 +01:00
send(data) {
this.socket.write(`${data}\n`);
2017-11-20 15:49:04 +01:00
logger.debug(`(${this.network}) out: ${data}`);
2017-11-08 21:59:54 +01:00
}
2018-02-14 00:33:00 +01:00
sendmsg(mode, recipient, msg) {
msg.split(/\r?\n/).forEach(e => {
this.send( msgmodes[mode].replace("{recipient}", recipient).replace("{msg}", e) );
});
}
2017-11-19 22:11:23 +01:00
parse(data, [a, ...b] = data.split(/ +:/)) {
let tmp = a.split(" ").concat(b);
2017-11-20 15:49:04 +01:00
logger.debug(`(${this.network}) in: ${[...tmp]}`);
2017-11-19 22:11:23 +01:00
return data.charAt(0) === ":" ? {
prefix: tmp.shift(),
command: tmp.shift(),
params: tmp
} : {
prefix: null,
command: tmp.shift(),
params: tmp
2017-11-08 21:59:54 +01:00
};
2017-11-08 19:43:08 +01:00
}
2017-11-08 21:59:54 +01:00
reply(tmp) {
return {
type: "irc",
network: this.network,
channel: tmp.params[0],
2018-03-03 07:12:40 +01:00
channelid: tmp.params[0],
2017-11-25 13:31:26 +01:00
user: Object.assign(this.parsePrefix(tmp.prefix), {
account: this.server.user.geti(this.parsePrefix(tmp.prefix).nick).account,
prefix: tmp.prefix.charAt(0) === ":" ? tmp.prefix.substring(1) : tmp.prefix,
level: getLevel(this.network, Object.assign(this.parsePrefix(tmp.prefix), {
account: this.server.user.geti(this.parsePrefix(tmp.prefix).nick).account,
prefix: tmp.prefix.charAt(0) === ":" ? tmp.prefix.substring(1) : tmp.prefix
}))
}),
2017-11-09 12:21:40 +01:00
message: tmp.params[1],
2017-11-08 22:29:43 +01:00
time: ~~(Date.now() / 1000),
2017-11-09 12:21:40 +01:00
raw: tmp,
2018-02-14 00:33:00 +01:00
reply: msg => this.sendmsg("normal", tmp.params[0], this.format(""+msg)),
replyAction: msg => this.sendmsg("action", tmp.params[0], this.format(""+msg)),
replyNotice: msg => this.sendmsg("notice", tmp.params[0], this.format(""+msg)),
2017-11-26 00:26:34 +01:00
self: this.server,
2017-11-09 17:04:30 +01:00
_chan: this.server.channel[tmp.params[0]],
2017-11-11 23:13:47 +01:00
_user: this.server.user,
2017-11-20 14:19:52 +01:00
_cmd: this._cmd,
2017-11-09 12:21:40 +01:00
join: chan => this.join(chan),
part: (chan, msg) => this.part(chan, msg),
2017-11-17 18:38:00 +01:00
whois: user => this.whois(user),
2017-11-09 12:21:40 +01:00
write: msg => this.send(msg)
2017-11-08 21:59:54 +01:00
};
2017-11-08 19:43:08 +01:00
}
2017-11-09 12:21:40 +01:00
join(channel) {
2017-11-18 11:53:58 +01:00
this.send(`JOIN ${(typeof channel === "object") ? channel.join(",") : channel}`);
2017-11-09 12:21:40 +01:00
}
part(channel, msg=false) {
2017-11-18 11:53:58 +01:00
this.send(`PART ${(typeof channel === "object") ? channel.join(",") : channel}${msg ? " " + msg : " part"}`);
2017-11-09 12:21:40 +01:00
}
2017-11-17 18:38:00 +01:00
whois(user, force = false) {
user = user.toLowerCase();
2017-11-24 22:35:35 +01:00
let tmpuser = {};
if(this.server.user.hasi(user) && !force) {
tmpuser = this.server.user.geti(user);
2017-12-03 17:55:36 +01:00
if(tmpuser.cached >= ~~(Date.now() / 1000) - this._recachetime)
2017-11-17 18:38:00 +01:00
return;
2017-11-24 22:35:35 +01:00
}
tmpuser = {
nickname: tmpuser.nickname || false,
username: tmpuser.username || false,
hostname: tmpuser.hostname || false,
realname: tmpuser.realname || false,
account: tmpuser.account || false,
2017-11-25 13:31:26 +01:00
prefix: tmpuser.prefix || false,
2017-11-24 22:35:35 +01:00
registered: tmpuser.registered || false,
oper: tmpuser.oper || false,
channels: tmpuser.channels || [],
cached: ~~(Date.now() / 1000)
};
this.server.user.set(user, tmpuser);
2017-11-17 18:38:00 +01:00
this.send(`WHOIS ${user}`);
}
2017-11-20 14:19:52 +01:00
parsePrefix(prefix) {
prefix = /:?(.*)\!(.*)@(.*)/.exec(prefix);
return {
nick: prefix[1],
username: prefix[2],
hostname: prefix[3]
};
}
2017-11-20 23:16:49 +01:00
format(msg) {
2017-11-22 23:15:49 +01:00
return msg
2017-11-26 13:51:08 +01:00
.replace(/\[b\](.*?)\[\/b\]/g, "\x02$1\x02") // bold
.replace(/\[i\](.*?)\[\/i\]/g, "\x1D$1\x1D") // italic
2017-11-26 00:26:34 +01:00
.replace(/\[color=(.*?)](.*?)\[\/color\]/g, replaceColor) // colors
2017-11-22 23:15:49 +01:00
;
2017-11-20 23:16:49 +01:00
}
2017-11-08 19:43:08 +01:00
}
2017-11-09 12:21:40 +01:00
2017-11-18 23:41:21 +01:00
Map.prototype.hasi = function(val) {
for (let [key] of this)
if(key.toLowerCase() === val.toLowerCase())
return true;
return false;
};
Map.prototype.geti = function(val) {
for (let [key, value] of this)
if(key.toLowerCase() === val.toLowerCase())
return value;
return false;
};
Map.prototype.deli = function(val) {
for (let [key] of this)
if(key.toLowerCase() === val.toLowerCase())
this.delete(key);
};