thelounge/src/client.js
Suyeol Jeon 1839162421 Add username field to user info.
Most of the networks don't allow unicode username. This patch allows users to specify username optionally. When specified, username is used for connecting IRC networks instead of nickname.
2014-09-24 20:08:14 +09:00

259 lines
4.7 KiB
JavaScript

var _ = require("lodash");
var config = require("../config");
var Chan = require("./models/chan");
var crypto = require("crypto");
var log = require("./log");
var net = require("net");
var Msg = require("./models/msg");
var Network = require("./models/network");
var slate = require("slate-irc");
var tls = require("tls");
module.exports = Client;
var id = 0;
var events = [
"ctcp",
"error",
"image",
"join",
"kick",
"mode",
"motd",
"message",
"names",
"nick",
"notice",
"part",
"quit",
"topic",
"welcome",
"whois"
];
var inputs = [
"action",
"connect",
"invite",
"join",
"kick",
"mode",
"msg",
"nick",
"notice",
"part",
"quit",
"raw",
"topic",
"whois"
];
function Client(sockets, name, config) {
_.merge(this, {
activeChannel: -1,
config: config,
id: id++,
name: name,
networks: [],
sockets: sockets
});
var client = this;
crypto.randomBytes(48, function(err, buf) {
client.token = buf.toString("hex");
});
if (config) {
var delay = 0;
(config.networks || []).forEach(function(n) {
setTimeout(function() {
client.connect(n);
}, delay);
delay += 1000;
});
}
}
Client.prototype.emit = function(event, data) {
if (this.sockets !== null) {
this.sockets.in(this.id).emit(event, data);
}
if ((this.config || {}).log === true) {
if (event == "msg") {
var target = this.find(data.chan);
if (target) {
var chan = target.chan.name;
if (target.chan.type == Chan.Type.LOBBY) {
chan = target.network.host;
}
log.write(
this.name,
target.network.host,
chan,
data.msg
);
}
}
}
};
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) {
var client = this;
var server = {
host: args.host || "irc.freenode.org",
port: args.port || (args.tls ? 6697 : 6667),
name: args.name || "",
rejectUnauthorized: false
};
var stream = args.tls ? tls.connect(server) : net.connect(server);
stream.on("error", function(e) {
console.log("Client#connect():\n" + e);
stream.end();
var msg = new Msg({
type: Msg.Type.ERROR,
text: "Connection error."
});
client.emit("msg", {
msg: msg
});
});
var nick = args.nick || "shout-user";
var username = args.username || nick;
var realname = args.realname || "Shout User";
var irc = slate(stream);
if (args.password) {
irc.pass(args.password);
}
irc.me = nick;
irc.nick(nick);
irc.user(username, realname);
var network = new Network({
host: server.host,
name: server.name,
irc: irc
});
client.networks.push(network);
client.emit("network", {
network: network
});
events.forEach(function(plugin) {
var path = "./plugins/irc-events/" + plugin;
require(path).apply(client, [
irc,
network
]);
});
irc.once("welcome", function() {
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;
});
}
setTimeout(function() {
irc.write("PING " + network.host);
}, delay);
});
irc.once("pong", function() {
var join = (args.join || "");
if (join) {
join = join.replace(/\,/g, " ").split(/\s+/g);
irc.join(join);
}
});
};
Client.prototype.input = function(data) {
var client = this;
var text = data.text;
var target = client.find(data.target);
if (text.charAt(0) !== "/") {
text = "/say " + text;
}
var args = text.split(" ");
var cmd = args.shift().replace("/", "").toLowerCase();
_.each(inputs, function(plugin) {
try {
var path = "./plugins/inputs/" + plugin;
var fn = require(path);
fn.apply(client, [
target.network,
target.chan,
cmd,
args
]);
} catch (e) {
console.log(path + ": " + e);
}
});
};
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;
this.activeChannel = target.chan.id;
}
};
Client.prototype.quit = function() {
this.networks.forEach(function(network) {
var irc = network.irc;
if (network.connected) {
irc.quit();
} else {
irc.stream.end();
}
});
};