thelounge/src/client.js

425 lines
9 KiB
JavaScript
Raw Normal View History

2014-09-13 23:29:45 +02:00
var _ = require("lodash");
2016-05-06 19:51:38 +02:00
var package = require("../package.json");
2014-09-16 22:06:13 +02:00
var Chan = require("./models/chan");
2014-09-15 23:13:03 +02:00
var crypto = require("crypto");
2016-04-16 12:35:04 +02:00
var userLog = require("./userLog");
2014-09-13 23:29:45 +02:00
var Msg = require("./models/msg");
var Network = require("./models/network");
var ircFramework = require("irc-framework");
var Helper = require("./helper");
2014-09-13 23:29:45 +02:00
module.exports = Client;
var id = 0;
var events = [
"connection",
2014-09-13 23:29:45 +02:00
"ctcp",
"error",
2016-02-12 12:24:13 +01:00
"invite",
2014-09-13 23:29:45 +02:00
"join",
"kick",
"mode",
"motd",
"message",
2014-09-27 21:17:05 +02:00
"link",
2014-09-13 23:29:45 +02:00
"names",
"nick",
"part",
"quit",
"topic",
"welcome",
"whois"
];
var inputs = [
"ctcp",
2016-03-06 10:24:56 +01:00
"msg",
"part",
2014-09-13 23:29:45 +02:00
"action",
"connect",
2016-04-14 10:56:02 +02:00
"disconnect",
2014-09-13 23:29:45 +02:00
"invite",
"kick",
"mode",
"notice",
"query",
2014-09-13 23:29:45 +02:00
"quit",
"raw",
"topic",
].reduce(function(plugins, name) {
var path = "./plugins/inputs/" + name;
var plugin = require(path);
plugin.commands.forEach(function(command) {
plugins[command] = plugin;
});
return plugins;
}, {});
2014-09-13 23:29:45 +02:00
function Client(manager, name, config) {
2014-09-13 23:29:45 +02:00
_.merge(this, {
activeChannel: -1,
2014-09-13 23:29:45 +02:00
config: config,
id: id++,
2014-09-16 21:47:01 +02:00
name: name,
2014-09-13 23:29:45 +02:00
networks: [],
sockets: manager.sockets,
manager: manager
2014-09-13 23:29:45 +02:00
});
2014-09-15 23:13:03 +02:00
var client = this;
crypto.randomBytes(48, function(err, buf) {
client.token = buf.toString("hex");
});
2014-09-13 23:29:45 +02:00
if (config) {
var delay = 0;
(config.networks || []).forEach(function(n) {
setTimeout(function() {
client.connect(n);
}, delay);
delay += 1000;
});
}
2016-04-16 13:32:38 +02:00
log.info("User '" + name + "' loaded");
2014-09-13 23:29:45 +02:00
}
Client.prototype.emit = function(event, data) {
if (this.sockets !== null) {
this.sockets.in(this.id).emit(event, data);
}
2014-10-14 21:25:04 +02:00
var config = this.config || {};
if (config.log === true) {
2015-10-01 00:39:57 +02:00
if (event === "msg") {
2014-09-16 21:47:01 +02:00
var target = this.find(data.chan);
if (target) {
2014-09-16 22:06:13 +02:00
var chan = target.chan.name;
2015-10-01 00:39:57 +02:00
if (target.chan.type === Chan.Type.LOBBY) {
2014-09-16 22:06:13 +02:00
chan = target.network.host;
}
2016-04-16 12:35:04 +02:00
userLog.write(
2014-09-16 22:06:13 +02:00
this.name,
target.network.host,
chan,
data.msg
);
2014-09-16 21:47:01 +02:00
}
}
}
2014-09-13 19:18:42 +02:00
};
2014-09-13 23:29:45 +02:00
Client.prototype.find = function(id) {
var network = null;
var chan = null;
for (var i in this.networks) {
var n = this.networks[i];
chan = _.find(n.channels, {id: id});
if (chan) {
network = n;
break;
}
}
if (network && chan) {
return {
network: network,
chan: chan
};
} else {
return false;
}
};
Client.prototype.connect = function(args) {
2014-10-11 08:17:41 +02:00
var config = Helper.getConfig();
2014-09-13 23:29:45 +02:00
var client = this;
2016-02-21 13:02:35 +01:00
var nick = args.nick || "lounge-user";
2016-04-03 07:12:49 +02:00
var webirc = null;
var network = new Network({
name: args.name || "",
host: args.host || "",
port: parseInt(args.port, 10) || (args.tls ? 6697 : 6667),
tls: !!args.tls,
password: args.password,
username: args.username || nick.replace(/[^a-zA-Z0-9]/g, ""),
realname: args.realname || "The Lounge User",
2016-04-03 07:12:49 +02:00
commands: args.commands,
ip: args.ip,
hostname: args.hostname,
});
2016-05-12 13:15:38 +02:00
network.setNick(nick);
client.networks.push(network);
client.emit("network", {
network: network
});
2016-02-21 13:02:35 +01:00
if (config.lockNetwork) {
// This check is needed to prevent invalid user configurations
if (args.host && args.host.length > 0 && args.host !== config.defaults.host) {
network.channels[0].pushMessage(client, new Msg({
type: Msg.Type.ERROR,
text: "Hostname you specified is not allowed."
}));
2016-02-21 13:02:35 +01:00
return;
}
network.host = config.defaults.host;
network.port = config.defaults.port;
network.tls = config.defaults.tls;
2016-02-21 13:02:35 +01:00
}
if (network.host.length === 0) {
network.channels[0].pushMessage(client, new Msg({
type: Msg.Type.ERROR,
text: "You must specify a hostname to connect."
}));
2016-02-21 13:02:35 +01:00
return;
}
if (config.webirc && network.host in config.webirc) {
2016-04-03 07:12:49 +02:00
args.ip = args.ip || (client.config && client.config.ip) || client.ip;
args.hostname = args.hostname || (client.config && client.config.hostname) || client.hostname || args.ip;
if (args.ip) {
if (config.webirc[network.host] instanceof Function) {
webirc = config.webirc[network.host](client, args);
} else {
webirc = {
password: config.webirc[network.host],
2016-05-06 19:51:38 +02:00
username: package.name,
2016-04-03 07:12:49 +02:00
address: args.ip,
hostname: args.hostname
};
}
} else {
log.warn("Cannot find a valid WEBIRC configuration for " + nick
+ "!" + network.username + "@" + network.host);
}
}
network.irc = new ircFramework.Client();
2016-04-22 18:38:13 +02:00
network.irc.requestCap([
"echo-message",
"znc.in/self-message",
]);
network.irc.connect({
2016-05-06 19:51:38 +02:00
version: package.name + " " + package.version + " -- " + package.homepage,
host: network.host,
port: network.port,
nick: nick,
username: network.username,
gecos: network.realname,
password: network.password,
tls: network.tls,
localAddress: config.bind,
rejectUnauthorized: false,
2016-04-13 09:10:44 +02:00
auto_reconnect: true,
2016-04-03 07:12:49 +02:00
webirc: webirc,
2014-09-13 23:29:45 +02:00
});
network.irc.on("registered", function() {
2016-04-24 10:14:06 +02:00
if (network.irc.network.cap.enabled.length > 0) {
network.channels[0].pushMessage(client, new Msg({
text: "Enabled capabilities: " + network.irc.network.cap.enabled.join(", ")
}));
}
2014-09-13 23:29:45 +02:00
var delay = 1000;
var commands = args.commands;
if (Array.isArray(commands)) {
commands.forEach(function(cmd) {
setTimeout(function() {
client.input({
target: network.channels[0].id,
text: cmd
});
}, delay);
delay += 1000;
});
}
var join = (args.join || "");
if (join) {
setTimeout(function() {
join = join.split(/\s+/);
network.irc.join(join[0], join[1]);
}, delay);
2014-09-13 23:29:45 +02:00
}
});
events.forEach(function(plugin) {
var path = "./plugins/irc-events/" + plugin;
require(path).apply(client, [
network.irc,
network
]);
});
2014-09-13 23:29:45 +02:00
};
Client.prototype.setPassword = function(hash) {
var client = this;
2016-06-05 20:48:34 +02:00
client.manager.updateUser(client.name, {password: hash});
// re-read the hash off disk to ensure we use whatever is saved. this will
// prevent situations where the password failed to save properly and so
// a restart of the server would forget the change and use the old
// password again.
var user = client.manager.readUserConfig(client.name);
if (user.password === hash) {
client.config.password = hash;
return true;
}
return false;
};
2014-09-13 23:29:45 +02:00
Client.prototype.input = function(data) {
var client = this;
var text = data.text;
2014-09-13 23:29:45 +02:00
var target = client.find(data.target);
2016-03-06 10:24:56 +01:00
// This is either a normal message or a command escaped with a leading '/'
if (text.charAt(0) !== "/" || text.charAt(1) === "/") {
text = "say " + text.replace(/^\//, "");
} else {
text = text.substr(1);
2014-09-13 23:29:45 +02:00
}
2016-03-06 10:24:56 +01:00
2014-09-13 23:29:45 +02:00
var args = text.split(" ");
2016-03-06 10:24:56 +01:00
var cmd = args.shift().toLowerCase();
var irc = target.network.irc;
var connected = irc && irc.connection && irc.connection.connected;
if (cmd in inputs) {
var plugin = inputs[cmd];
if (connected || plugin.allowDisconnected) {
connected = true;
plugin.input.apply(client, [target.network, target.chan, cmd, args]);
}
} else if (connected) {
irc.raw(text);
}
if (!connected) {
target.chan.pushMessage(this, new Msg({
type: Msg.Type.ERROR,
text: "You are not connected to the IRC network, unable to send your command."
}));
2016-03-06 10:24:56 +01:00
}
2014-09-13 23:29:45 +02:00
};
Client.prototype.more = function(data) {
var client = this;
var target = client.find(data.target);
if (!target) {
return;
}
var chan = target.chan;
var count = chan.messages.length - (data.count || 0);
var messages = chan.messages.slice(Math.max(0, count - 100), count);
client.emit("more", {
chan: chan.id,
messages: messages
});
};
Client.prototype.open = function(data) {
var target = this.find(data);
if (target) {
target.chan.unread = 0;
target.chan.highlight = false;
this.activeChannel = target.chan.id;
}
};
2014-09-24 21:42:36 +02:00
Client.prototype.sort = function(data) {
var self = this;
var type = data.type;
var order = data.order || [];
2015-10-01 00:39:57 +02:00
var sorted = [];
2014-09-24 21:42:36 +02:00
switch (type) {
case "networks":
_.each(order, function(i) {
var find = _.find(self.networks, {id: i});
if (find) {
sorted.push(find);
}
});
self.networks = sorted;
break;
case "channels":
var target = data.target;
var network = _.find(self.networks, {id: target});
if (!network) {
return;
}
_.each(order, function(i) {
var find = _.find(network.channels, {id: i});
if (find) {
sorted.push(find);
}
});
network.channels = sorted;
break;
}
};
Client.prototype.names = function(data) {
var client = this;
var target = client.find(data.target);
if (!target) {
return;
}
client.emit("names", {
id: target.chan.id,
users: target.chan.users
});
};
2014-09-13 23:29:45 +02:00
Client.prototype.quit = function() {
2014-09-29 17:49:38 +02:00
var sockets = this.sockets.sockets;
var room = sockets.adapter.rooms[this.id] || [];
for (var user in room) {
var socket = sockets.adapter.nsp.connected[user];
if (socket) {
socket.disconnect();
}
}
2014-09-13 23:29:45 +02:00
this.networks.forEach(function(network) {
if (network.irc) {
network.irc.quit("Page closed");
}
2014-09-13 23:29:45 +02:00
});
2014-09-13 19:18:42 +02:00
};
2014-10-11 22:44:56 +02:00
2014-10-12 02:20:30 +02:00
var timer;
Client.prototype.save = function(force) {
var client = this;
2014-10-12 07:15:03 +02:00
var config = Helper.getConfig();
2015-10-01 00:39:57 +02:00
if (config.public) {
2014-10-12 07:15:03 +02:00
return;
}
2014-11-09 17:01:22 +01:00
2014-10-12 02:20:30 +02:00
if (!force) {
clearTimeout(timer);
timer = setTimeout(function() {
client.save(true);
}, 1000);
return;
}
2014-11-09 17:01:22 +01:00
2014-10-12 00:47:24 +02:00
var networks = _.map(
this.networks,
function(n) {
return n.export();
}
);
2014-11-09 17:01:22 +01:00
2014-10-12 00:47:24 +02:00
var json = {};
json.networks = networks;
client.manager.updateUser(client.name, json);
2014-10-11 22:44:56 +02:00
};