thelounge/lib/server.js

754 lines
14 KiB
JavaScript
Raw Normal View History

2014-04-24 20:34:40 +00:00
var _ = require("lodash");
var config = require("../config") || {};
var fs = require("fs");
var http = require("connect");
var io = require("socket.io");
var irc = require("slate-irc");
var net = require("net");
2014-03-06 15:11:25 +00:00
// Models
var Chan = require("./models/chan");
var Msg = require("./models/msg");
var Network = require("./models/network");
var User = require("./models/user");
var sockets = null;
2014-05-02 17:46:18 +00:00
var networks = [];
2014-03-06 15:11:25 +00:00
2014-03-24 15:47:29 +00:00
var events = [
2014-06-13 13:57:11 +00:00
"errors",
2014-03-24 15:47:29 +00:00
"join",
"kick",
2014-04-07 22:03:21 +00:00
"mode",
2014-04-03 20:55:51 +00:00
"motd",
2014-03-30 03:12:29 +00:00
"message",
2014-03-24 15:47:29 +00:00
"names",
"nick",
"notice",
"part",
"quit",
"topic",
"welcome",
2014-04-05 22:01:19 +00:00
"whois",
2014-03-24 15:47:29 +00:00
];
module.exports = listen;
function listen() {
2014-04-26 17:44:16 +00:00
var port = config.port || 9000;
2014-03-24 15:47:29 +00:00
var app = http()
2014-04-24 20:34:40 +00:00
.use(index)
2014-03-24 15:47:29 +00:00
.use(http.static("client"))
2014-04-26 17:44:16 +00:00
.listen(port);
2014-03-24 15:47:29 +00:00
var self = this;
2014-03-24 15:47:29 +00:00
sockets = io.listen(app, {log: 0}).sockets.on("connection", function(s) {
2014-04-24 23:57:51 +00:00
s.emit("networks", {networks: networks});
2014-06-02 21:08:03 +00:00
s.on("input", input);
s.on("fetch", function(data) {
fetch(s, data);
});
2014-03-13 17:50:02 +00:00
});
2014-03-24 13:44:41 +00:00
2014-04-24 20:34:40 +00:00
(config.networks || []).forEach(function(n) {
connect(n);
2014-04-05 22:01:19 +00:00
});
}
2014-03-13 17:50:02 +00:00
2014-04-24 20:34:40 +00:00
function index(req, res, next) {
2014-04-24 23:57:51 +00:00
if (req.url != "/") return next();
fs.readFile("client/index.html", function(err, file) {
var data = _.merge(
2014-04-24 20:34:40 +00:00
require("../package.json"),
2014-06-14 20:28:17 +00:00
config
2014-04-24 20:34:40 +00:00
);
2014-04-24 23:57:51 +00:00
res.end(_.template(
file,
data
));
});
2014-04-24 20:34:40 +00:00
}
2014-04-05 22:01:19 +00:00
function connect(params) {
2014-04-24 23:57:51 +00:00
_.defaults(
params,
config.defaults
2014-04-05 22:01:19 +00:00
);
var host = params.host;
2014-04-24 23:57:51 +00:00
var port = params.port || 6667;
2014-04-05 22:01:19 +00:00
2014-03-24 15:47:29 +00:00
var stream = net.connect({
2014-04-05 22:01:19 +00:00
port: port,
2014-03-24 15:47:29 +00:00
host: host,
});
stream.on("error", function(e) {
console.log(e);
});
var client = irc(stream);
var network = new Network({
2014-03-29 23:59:28 +00:00
host: host,
client: client,
});
2014-03-24 15:47:29 +00:00
networks.push(network);
2014-04-24 23:57:51 +00:00
sockets.emit("networks", {networks: networks});
2014-04-05 22:01:19 +00:00
client.nick(params.nick);
client.user(params.nick, params.realname);
2014-06-13 14:45:32 +00:00
2014-04-05 22:01:19 +00:00
client.once("welcome", function() {
(params.channels || []).forEach(function(c) {
2014-06-13 14:45:32 +00:00
client.join.apply(
client,
c.split(' ')
);
2014-04-05 22:01:19 +00:00
});
});
2014-03-24 15:47:29 +00:00
events.forEach(function(e) {
2014-04-02 15:24:33 +00:00
client.on(e, function() {
event.apply(network, [e, arguments]);
2014-03-24 15:47:29 +00:00
});
});
2014-03-24 13:44:41 +00:00
}
function input(data) {
var target = find(data.id);
2014-03-09 21:22:37 +00:00
if (!target) {
return;
}
2014-03-24 13:44:41 +00:00
var network = target.network;
var chan = target.chan;
2014-03-30 03:12:29 +00:00
var client = network.client;
var id = data.id;
var text = data.text;
2014-05-13 22:47:35 +00:00
if (!text) {
return;
}
2014-03-24 13:44:41 +00:00
var args = text.replace(/^\//, '').split(" ");
var cmd = text.charAt(0) == "/" ? args[0].toLowerCase() : "";
2014-03-24 13:44:41 +00:00
2014-03-09 21:22:37 +00:00
switch (cmd) {
2014-05-15 21:56:23 +00:00
case "say":
// Remove '/say' and treat this command as a message.
args.shift();
2014-03-24 13:44:41 +00:00
case "":
args.unshift(
"msg",
chan.name
2014-03-13 15:25:01 +00:00
);
2014-03-29 15:36:12 +00:00
case "msg":
2014-03-30 03:12:29 +00:00
var user;
2014-05-03 19:22:14 +00:00
var text = args.slice(2).join(" ");
2014-03-30 03:12:29 +00:00
if (client) {
user = client.me;
client.send(args[1], text);
}
2014-05-04 00:33:05 +00:00
var chan = _.findWhere(network.channels, {name: args[1]});
if (typeof chan !== "undefined") {
var msg = new Msg({
from: user,
text: text,
});
chan.messages.push(msg)
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
}
2014-03-24 13:44:41 +00:00
break;
2014-05-03 17:21:11 +00:00
case "notice":
if (client && args[2]) {
client.notice(args[1], args.slice(2).join(" "));
}
break;
2014-05-28 14:12:16 +00:00
case "slap":
var slap = "slaps " + args[1] + " around a bit with a large trout";
2014-05-03 19:22:14 +00:00
case "me":
if (!args[1]) {
break;
}
var user;
2014-05-28 14:12:16 +00:00
var text = slap || args.slice(1).join(" ");
2014-05-03 19:22:14 +00:00
if (client) {
user = client.me;
client.action(chan.name, text);
}
var msg = new Msg({
type: "action",
from: user,
text: text,
});
chan.messages.push(msg)
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
break;
2014-05-18 16:18:19 +00:00
case "ame":
var type = "action";
case "amsg":
var user = client.me;
var text = args.slice(1).join(" ");
var channels = [];
network.channels.forEach(function(chan) {
if (chan.type == "channel") {
channels.push(chan.name);
var msg = new Msg({
2014-05-25 20:46:54 +00:00
type: type || "normal",
2014-05-18 16:18:19 +00:00
from: user,
text: text,
});
chan.messages.push(msg)
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
}
});
client[type || "send"](
channels,
text
);
break;
2014-03-24 13:44:41 +00:00
case "server":
case "connect":
2014-03-24 15:47:29 +00:00
if (args[1]) {
2014-04-05 22:01:19 +00:00
connect({host: args[1]});
2014-03-14 15:12:01 +00:00
}
2014-03-12 18:06:08 +00:00
break;
2014-03-24 13:44:41 +00:00
2014-03-29 23:59:28 +00:00
case "join":
2014-05-18 21:23:31 +00:00
if (client && args[1]) {
2014-06-12 22:35:39 +00:00
if (!args[2] || args[2].charAt(0) == "#") {
client.join(args.slice(1));
} else {
console.log(args[1], args[2]);
client.join(
args[1],
args[2] // Password
);
}
2014-05-18 21:23:31 +00:00
}
break;
2014-03-30 03:12:29 +00:00
case "nick":
if (client && args[1]) {
2014-05-18 21:23:31 +00:00
client.nick(args[1]);
2014-03-29 23:59:28 +00:00
}
break;
2014-05-16 13:12:57 +00:00
case "part":
if (chan.type != "channel") {
return;
}
2014-05-15 21:58:50 +00:00
case "close":
2014-04-06 20:53:07 +00:00
case "leave":
2014-05-16 13:12:57 +00:00
if (chan.type == "lobby") {
return;
}
var id = chan.id;
if (chan.type == "query" || !chan.users.length) {
remove(id);
sockets.emit("part", {
id: id,
});
2014-04-06 20:53:07 +00:00
} else if (client) {
client.part(chan.name);
2014-04-06 20:53:07 +00:00
}
break;
2014-05-16 13:12:57 +00:00
case "partall":
var part = [];
network.channels.forEach(function(c) {
if (c.type == "channel") part.push(c.name);
});
2014-05-18 21:23:31 +00:00
client.part(part);
2014-05-16 13:12:57 +00:00
break;
2014-05-17 22:17:50 +00:00
case "invite":
if (client && args[2]) {
client.invite(args[1], args[2]);
}
break;
2014-03-30 03:12:29 +00:00
case "topic":
2014-04-06 18:04:11 +00:00
if (client) {
var msg = "TOPIC";
msg += " " + chan.name;
msg += args[1] ? " :" + args.slice(1).join(" ") : "";
client.write(msg);
2014-03-29 23:59:28 +00:00
}
break;
2014-06-12 12:11:12 +00:00
case "whoami":
var user = client.me;
2014-04-02 15:24:33 +00:00
case "query":
case "whois":
2014-06-12 12:11:12 +00:00
var user = user || args[1];
if (client && user) {
client.whois(user);
2014-04-02 15:24:33 +00:00
}
break;
case "kick":
2014-04-06 15:46:42 +00:00
if (client && args[1]) {
client.kick(chan.name, args[1]);
2014-04-06 15:46:42 +00:00
}
break;
2014-04-07 22:03:21 +00:00
case "op":
case "deop":
case "voice":
case "devoice":
2014-04-06 15:46:42 +00:00
case "mode":
2014-04-07 22:03:21 +00:00
if (!client || !args[1]) {
break;
}
var mode;
var user;
if (cmd != "mode") {
user = args[1];
mode = {
"op": "+o",
"deop": "-o",
"voice": "+v",
"devoice": "-v",
}[cmd];
} else if (!args[2]) {
break;
} else {
mode = args[1];
user = args[2];
}
client.mode(
chan.name,
2014-04-07 22:03:21 +00:00
mode,
user
);
2014-04-02 15:24:33 +00:00
break;
2014-03-30 03:12:29 +00:00
case "quit":
case "disconnect":
if (client) {
networks = _.without(networks, network);
2014-04-24 23:57:51 +00:00
sockets.emit("networks", {networks: networks});
client.quit();
}
2014-03-30 03:12:29 +00:00
break;
2014-05-07 19:30:14 +00:00
2014-05-15 21:58:50 +00:00
// Send raw IRC messages.
case "raw":
2014-05-07 19:30:14 +00:00
case "send":
if (client) {
client.write(args.slice(1).join(" "));
}
break;
2014-03-12 15:09:37 +00:00
}
2014-03-24 15:47:29 +00:00
}
2014-06-02 21:08:03 +00:00
function fetch(socket, data) {
var target = find(data.id);
if (!target) {
return;
}
var chan = target.chan;
var messages = chan
.messages
.slice(0, chan.messages.length - (data.count || 0));
socket.emit("messages", {
id: data.id,
msg: messages,
});
}
function event(e, data) {
var data = _.last(data);
var channels = this.channels;
2014-03-24 15:47:29 +00:00
switch (e) {
2014-06-13 13:57:11 +00:00
case "errors":
sockets.emit("msg", {
msg: new Msg({
type: "error",
from: "-!-",
text: data.message,
}),
});
2014-06-14 15:57:26 +00:00
if (!this.connected) {
if (data.cmd == "ERR_NICKNAMEINUSE") {
var random = config.defaults.nick + Math.floor(10 + (Math.random() * 89));
this.client.nick(random);
}
}
2014-06-13 13:57:11 +00:00
break;
2014-03-24 15:47:29 +00:00
case "join":
var chan = _.findWhere(channels, {name: data.channel});
if (typeof chan === "undefined") {
chan = new Chan({
name: data.channel,
});
channels.push(chan);
sockets.emit("join", {
id: this.id,
chan: chan,
});
2014-04-19 21:40:36 +00:00
}
var users = chan.users;
users.push(new User({name: data.nick}));
2014-04-29 13:01:30 +00:00
chan.sortUsers();
sockets.emit("users", {
id: chan.id,
users: users,
});
var msg = new Msg({
from: data.nick,
2014-03-29 23:59:28 +00:00
type: "join",
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-04-06 15:46:42 +00:00
break;
2014-03-24 15:47:29 +00:00
case "kick":
var chan = _.findWhere(channels, {name: data.channel});
2014-04-24 23:57:51 +00:00
if (typeof chan === "undefined") {
break;
}
if (data.client == this.client.me) {
chan.users = [];
2014-04-06 15:46:42 +00:00
} else {
chan.users = _.without(chan.users, _.findWhere(chan.users, {name: data.client}));
2014-04-06 15:46:42 +00:00
}
sockets.emit("users", {
id: chan.id,
users: chan.users,
});
var msg = new Msg({
2014-04-06 15:46:42 +00:00
type: "kick",
from: data.nick,
text: data.client,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
2014-04-06 15:46:42 +00:00
});
2014-04-03 20:55:51 +00:00
break;
2014-04-07 22:03:21 +00:00
case "mode":
var chan = _.findWhere(channels, {name: data.target});
2014-04-07 22:03:21 +00:00
if (typeof chan !== "undefined") {
2014-05-30 01:01:20 +00:00
clearTimeout(this.timer);
this.timer = setTimeout((function() {
this.client.write("NAMES " + data.target);
}).bind(this), 200);
var nick = data.nick;
if (nick.indexOf(".") !== -1) {
nick = data.target;
}
var msg = new Msg({
2014-04-07 22:03:21 +00:00
type: "mode",
2014-05-30 01:01:20 +00:00
from: nick,
text: data.mode + " " + data.client,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
2014-04-07 22:03:21 +00:00
});
}
break;
2014-04-03 20:55:51 +00:00
case "motd":
var chan = channels[0];
data.motd.forEach(function(m) {
var msg = new Msg({
type: "motd",
from: "-!-",
text: m,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-04-03 20:55:51 +00:00
});
2014-03-24 15:47:29 +00:00
break;
2014-03-30 03:12:29 +00:00
case "message":
2014-04-26 11:52:11 +00:00
var target = data.to;
var chan = _.findWhere(channels, {name: target.charAt(0) == "#" ? target : data.from});
if (typeof chan === "undefined") {
chan = new Chan({
name: data.from,
type: "query",
});
channels.push(chan);
sockets.emit("join", {
id: this.id,
chan: chan,
});
}
2014-04-21 15:08:57 +00:00
var type = "";
var text = data.message;
2014-05-03 19:22:14 +00:00
if (text.split(" ")[0] === "\u0001ACTION") {
type = "action";
text = text.replace(/\u0001|ACTION/g, "");
}
2014-04-21 15:08:57 +00:00
var network = this;
2014-04-30 15:14:22 +00:00
text.split(' ').forEach(function(w) {
2014-05-03 19:22:14 +00:00
if (w.indexOf(network.client.me) == 0) type += " highlight";
2014-04-21 15:08:57 +00:00
});
var msg = new Msg({
2014-05-02 21:54:31 +00:00
type: type || "normal",
from: data.from,
2014-04-21 15:08:57 +00:00
text: text,
2014-03-30 03:12:29 +00:00
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-03-30 03:12:29 +00:00
break;
2014-03-24 15:47:29 +00:00
case "names":
var chan = _.findWhere(channels, {name: data.channel});
2014-04-24 23:57:51 +00:00
if (typeof chan === "undefined") {
break;
}
chan.users = [];
2014-06-06 20:05:47 +00:00
_.each(data.names, function(n) {
chan.users.push(new User(n));
});
2014-04-29 13:01:30 +00:00
chan.sortUsers();
sockets.emit("users", {
id: chan.id,
users: chan.users,
});
2014-03-24 15:47:29 +00:00
break;
case "nick":
if (data["new"] == this.client.me) {
var chan = channels[0];
var msg = new Msg({
from: "-!-",
text: "You're now known as " + data["new"],
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
2014-04-02 15:24:33 +00:00
});
}
channels.forEach(function(chan) {
var user = _.findWhere(chan.users, {name: data.nick});
2014-03-30 03:12:29 +00:00
if (!user) {
return;
}
user.name = data["new"];
2014-04-29 13:01:30 +00:00
chan.sortUsers();
sockets.emit("users", {
id: chan.id,
users: chan.users,
});
var msg = new Msg({
2014-03-30 03:12:29 +00:00
type: "nick",
from: data.nick,
text: data["new"],
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
2014-03-30 03:12:29 +00:00
});
});
2014-03-24 15:47:29 +00:00
break;
case "notice":
var chan = channels[0];
2014-05-22 23:14:01 +00:00
var from = data.from || "-!-";
if (data.to == "*" || data.from.indexOf(".") !== -1) {
from = "-!-";
}
var msg = new Msg({
2014-04-20 22:12:07 +00:00
type: "notice",
2014-05-22 23:14:01 +00:00
from: from,
text: data.message,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
2014-04-20 22:12:07 +00:00
});
2014-03-24 15:47:29 +00:00
break;
2014-04-07 21:19:20 +00:00
case "part":
var chan = _.findWhere(channels, {name: data.channels[0]});
2014-04-24 23:57:51 +00:00
if (typeof chan === "undefined") {
break;
}
if (data.nick == this.client.me) {
remove(chan.id);
sockets.emit("part", {
id: chan.id,
});
} else {
chan.users = _.without(chan.users, _.findWhere(chan.users, {name: data.nick}));
sockets.emit("users", {
id: chan.id,
users: chan.users,
});
var msg = new Msg({
type: "part",
from: data.nick,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-04-07 21:19:20 +00:00
}
break;
2014-03-24 15:47:29 +00:00
case "quit":
channels.forEach(function(chan) {
var user = _.findWhere(chan.users, {name: data.nick});
if (!user) {
return;
2014-03-30 03:12:29 +00:00
}
chan.users = _.without(chan.users, user);
sockets.emit("users", {
id: chan.id,
users: chan.users,
});
var msg = new Msg({
type: "quit",
from: data.nick,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-03-30 03:12:29 +00:00
});
2014-03-24 15:47:29 +00:00
break;
case "topic":
var chan = _.findWhere(channels, {name: data.channel});
2014-04-24 23:57:51 +00:00
if (typeof chan === "undefined") {
break;
}
var from = data.nick || chan.name;
var msg = new Msg({
2014-03-30 03:12:29 +00:00
type: "topic",
from: from,
text: data.topic,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
2014-03-30 03:12:29 +00:00
});
2014-03-24 15:47:29 +00:00
break;
case "welcome":
2014-06-14 15:57:26 +00:00
this.connected = true;
var chan = channels[0];
var msg = new Msg({
from: "-!-",
text: "You're now known as " + data,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-03-24 15:47:29 +00:00
break;
case "whois":
if (!data) {
2014-06-13 13:57:11 +00:00
break;
}
var chan = _.findWhere(channels, {name: data.nickname});
if (typeof chan === "undefined") {
chan = new Chan({
type: "query",
name: data.nickname,
2014-04-02 15:24:33 +00:00
});
2014-06-13 13:57:11 +00:00
channels.push(chan);
sockets.emit("join", {
id: this.id,
chan: chan,
});
}
var prefix = {
hostname: "from",
realname: "is",
channels: "on",
server: "using",
};
var i = 0;
for (var k in data) {
var key = prefix[k];
if (!key || data[k].toString() == "") {
continue;
}
2014-05-02 21:54:31 +00:00
var msg = new Msg({
type: "whois",
from: data.nickname,
2014-06-13 13:57:11 +00:00
text: key + " " + data[k],
2014-05-02 21:54:31 +00:00
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-04-02 15:24:33 +00:00
}
2014-06-13 13:57:11 +00:00
var msg = new Msg({
type: "whois",
from: data.nickname,
text: "End of /WHOIS list.",
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
2014-03-24 15:47:29 +00:00
break;
}
2014-04-02 15:24:33 +00:00
}
function find(id) {
2014-04-24 23:57:51 +00:00
for (var i = 0; i < networks.length; i++) {
var result = {
network: networks[i],
chan: _.findWhere(networks[i].channels, {id: id}),
};
2014-04-24 23:57:51 +00:00
if (result.chan) {
return result;
}
2014-04-24 23:57:51 +00:00
}
}
function remove(id) {
networks.forEach(function(n) {
n.channels = _.without(n.channels, _.findWhere(n.channels, {id: id}));
});
}