ircgrampp
Version:
IRCGram++ is a complexly simple Telegram <-> IRC Gateway
508 lines (402 loc) • 16.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.IRCChannel = exports.defaultConfig = void 0;
var _debug = _interopRequireDefault(require("debug"));
var _events = require("events");
var _lodash = require("lodash");
var _irc = require("irc");
var _config = _interopRequireDefault(require("./config"));
var _hooks = require("./hooks");
var _dec, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _class, _dec10, _dec11, _dec12, _dec13, _dec14, _dec15, _class2;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; }
var Promise = require("bluebird");
let instances = [];
const debug = {
irc: (0, _debug.default)("irc")
};
const defaultConfig = (0, _lodash.assignIn)({
server: null,
nick: null,
port: 6697,
ssl: true,
autoConnect: true,
channels: null
}, _config.default.get("ircDefaults") || {});
/**
* An IRCChannel
*/
exports.defaultConfig = defaultConfig;
let IRCChannel = (_dec = (0, _hooks.syncHookedMethod)('ircchannel:create', 'channel', 'connection'), _dec2 = (0, _hooks.asyncHookedMethod)('ircchannel:handle.message', 'nick', 'message'), _dec3 = (0, _hooks.asyncHookedMethod)('ircchannel:handle.action', 'nick', 'message'), _dec4 = (0, _hooks.asyncHookedMethod)('ircchannel:handle.join'), _dec5 = (0, _hooks.asyncHookedMethod)('ircchannel:handle.left'), _dec6 = (0, _hooks.asyncHookedMethod)('ircchannel:handle.topic', 'channel', 'topic', 'nick', 'message'), _dec7 = (0, _hooks.asyncHookedMethod)('ircchannel:nick.change', 'old', 'new'), _dec8 = (0, _hooks.asyncHookedMethod)('ircchannel:message.send'), _dec9 = (0, _hooks.syncHookedMethod)('ircchannel:destroy'), (_class = class IRCChannel extends _events.EventEmitter {
/**
* Create new channel handler
* @param {string} channel The channel name
* @param {IRCConnection} connection The connection father
*/
constructor(channel, connection) {
super();
this._constructor(channel, connection);
}
_constructor(channel, connection) {
debug.irc(`Creating IRCChannel for ${channel} for ${connection.ident}`);
this._channel = channel;
this._connection = connection;
this._nicks = [connection.nick];
this._handlers = {
message: this._handleMessage.bind(this),
join: this._handleJoin.bind(this),
left: this._handleLeft.bind(this),
topic: this._handleTopic.bind(this),
action: this._handleAction.bind(this),
changeNick: this._handleChangeNick.bind(this)
};
this.bind();
return this;
}
_handleMessage(nick, message) {
if (!message) {
return;
}
this.emit("message", nick, message);
}
_handleAction(nick, message) {
if (!message) {
return;
}
this.emit("action", nick, message);
}
_handleJoin(nick) {
this.emit("join", nick);
}
_handleLeft(nick) {
this.emit("left", nick);
}
_handleTopic(channel, topic, nick, message) {
this.emit("topic", channel, topic, nick, message);
}
_handleChangeNick(oldNick, newNick) {
debug.irc(`Change nick from ${oldNick} to ${newNick}`);
this._nicks = this._nicks.filter(n => n !== oldNick);
this._nicks.push(newNick);
}
bind() {
let channel = this._channel;
this._connection.on(`${channel}:join`, this._handlers.join);
this._connection.on(`${channel}:part`, this._handlers.left);
this._connection.on(`${channel}:topic`, this._handlers.topic);
this._connection.on(`${channel}:message`, this._handlers.message);
this._connection.on(`${channel}:action`, this._handlers.action);
this._connection.on("nickchange", this._handlers.changeNick);
}
unbind() {
let channel = this._channel;
this._connection.removeListener(`${channel}:join`, this._handlers.join);
this._connection.removeListener(`${channel}:part`, this._handlers.left);
this._connection.removeListener(`${channel}:topic`, this._handlers.topic);
this._connection.removeListener(`${channel}:message`, this._handlers.message);
this._connection.removeListener(`${channel}:action`, this._handlers.action);
this._connection.removeListener("nickchange", this._handlers.changeNick);
}
sendMessage(msg) {
return this._connection.sendMessage(this._channel, msg);
}
/**
* Return true if the channel has an own nickname
* @param {string} nickName The nickname to find
* @return {bool}
*/
hasNick(nickName) {
return this._nicks.indexOf(nickName) !== -1;
}
destroy() {
debug.irc(`Destroy channel ${this.name}`);
this.unbind();
this._connection.removeChannel(this.name);
return this;
}
/**
* The name of the channel
* @property name
*/
get name() {
return `${this._channel}`;
}
}, (_applyDecoratedDescriptor(_class.prototype, "_constructor", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "_constructor"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_handleMessage", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "_handleMessage"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_handleAction", [_dec3], Object.getOwnPropertyDescriptor(_class.prototype, "_handleAction"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_handleJoin", [_dec4], Object.getOwnPropertyDescriptor(_class.prototype, "_handleJoin"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_handleLeft", [_dec5], Object.getOwnPropertyDescriptor(_class.prototype, "_handleLeft"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_handleTopic", [_dec6], Object.getOwnPropertyDescriptor(_class.prototype, "_handleTopic"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_handleChangeNick", [_dec7], Object.getOwnPropertyDescriptor(_class.prototype, "_handleChangeNick"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "sendMessage", [_dec8], Object.getOwnPropertyDescriptor(_class.prototype, "sendMessage"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "destroy", [_dec9], Object.getOwnPropertyDescriptor(_class.prototype, "destroy"), _class.prototype)), _class));
/**
* IRC connection
*/
exports.IRCChannel = IRCChannel;
let IRCConnection = (_dec10 = (0, _hooks.syncHookedMethod)('ircconnection:create'), _dec11 = (0, _hooks.asyncHookedMethod)('ircconnection:handle.registered'), _dec12 = (0, _hooks.asyncHookedMethod)('ircconnection:handle.message', 'from', 'to', 'message'), _dec13 = (0, _hooks.asyncHookedMethod)('ircconnection:wait.for.registered'), _dec14 = (0, _hooks.syncHookedMethod)('ircconnection:add.channel'), _dec15 = (0, _hooks.asyncHookedMethod)('ircconnection:remove.channel'), (_class2 = class IRCConnection extends _events.EventEmitter {
/**
* Create new connection
* @param {object} options The options for connection
* @param {string} options.server Server ip or name
* @param {string} options.nick The nick in the server
* @param {number} [options.port] Port number, by default 6697
* @param {boolean} [options.ssl] Use SSL, by default true
* @param {boolean} [options.autoConnect] Auto-connect to server, by
* default true
* @param {Array:<string>} [options.channels] Channels to subscribe
*/
constructor(options) {
super();
this._constructor(options);
}
_constructor(options) {
this._options = (0, _lodash.assignIn)({}, defaultConfig, options);
this._client = null;
this._channels = [];
this._registered = false;
this._originalNick = this._options.nick;
debug.irc(`Start irc connection ${this._options.server}`);
if (!this._options.server || !this._options.port || !this._options.nick) {
throw new Error("You need to specify some server and nick");
}
if (options.channels) {
options.channels.forEach(chan => {
this.addChannel(chan);
});
}
delete this._options.channels;
if (this._options.autoConnect) {
(0, _lodash.noop)(this.client);
}
instances.push(this);
return this;
}
_handleRegistered(data) {
let nick = data.args[0];
debug.irc(`${nick} registered`);
this._registered = true;
this.emit("irc:registered", nick, data);
let oldNick = this._options.nick;
this._options.nick = nick;
this.emit("nickchange", oldNick, nick);
}
/**
* Handle join events from the server
* @param {string} channel
* @param {string} nick
*/
_handleJoin(channel, nick) {
let ownChannel = this.getChannel(channel);
if (ownChannel) {
this.emit("join", channel, nick);
this.emit(`${channel}:join`, nick);
} else {
debug.irc(`${nick} join to unhandled channel ${channel}`);
}
}
/**
* Handle incomming message from the server
* @param {string} from
* @param {string} to
* @param {string} message
*/
_handleIncommingMessage(from, to, message) {
let ownChannel = this.getChannel(to);
if (ownChannel && !ownChannel.hasNick(from)) {
this.emit(`${ownChannel.name}:message`, from, message);
}
}
/**
* Handle incomming action from the server
* @param {string} from
* @param {string} to
* @param {string} message
*/
_handleIncommingAction(from, to, message) {
let ownChannel = this.getChannel(to);
debug.irc(`Incomming action ${from} -> ${to} : ${message}`);
if (ownChannel && !ownChannel.hasNick(from)) {
this.emit(`${ownChannel.name}:action`, from, message);
}
}
/**
* Handle part from the server
* @param {string} channel
* @param {string} nick
* @param {string} message
*/
_handlePart(channel, nick, message) {
let ownChannel = this.getChannel(channel);
if (ownChannel && !ownChannel.hasNick(nick)) {
this.emit(`${ownChannel.name}:part`, nick, message);
}
}
/**
* Handle topic from the server
* @param {string} channel
* @param {string} topic
* @param {string} nick
* @param {string} message
*/
_handleTopic(channel, topic, nick, message) {
let ownChannel = this.getChannel(channel);
if (ownChannel && !ownChannel.hasNick(nick)) {
this.emit(`${ownChannel.name}:topic`, channel, topic, nick, message);
}
}
waitForRegistered() {
if (this._registered) {
return Promise.resolve(false);
}
return new Promise(resolve => {
debug.irc('Waiting for registered');
this.once("irc:registered", () => {
debug.irc('registered!');
resolve(true);
});
});
}
/**
* Get a channel handler from name
* @param {string} channelName Channel name
* @return {null|IRCChannel}
*/
getChannel(channelName) {
return this._channels.find(c => c.name === channelName);
}
/**
* Add channel handle
* @param {string} name
* @return {Promise<IRCChannel>}
*/
addChannel(channelName) {
debug.irc(`Add channel to IRCConnection ${channelName}`);
if (this._channels.find(c => c.name === channelName)) {
throw new Error("Channel already exists");
}
let channel = new IRCChannel(channelName, this);
this._channels.push(channel);
this.waitForRegistered().then(() => {
debug.irc("Send irc join");
this.client.join(channelName);
});
return channel;
}
removeChannel(channelName) {
let channel = this._channels.find(c => c.name === channelName);
if (!channel) {
throw new Error("Channel does not exists");
}
this.waitForRegistered().then(() => {
this.client.part(channelName);
});
this._channels = this._channels.filter(x => x.name !== channel.name);
}
/**
* Client object
* @property
* @see https://github.com/martynsmith/node-irc
*/
get client() {
if (this._client) {
return this._client;
}
let {
server,
port,
ssl,
nick,
autoConnect,
channels
} = this._options;
this._client = new _irc.Client(server, nick, {
port,
autoConnect,
channels,
secure: ssl
});
this._client.addListener("message", (from, to, message) => {
debug.irc(`${from} => ${to}: ${message}`);
this.emit("irc:message", from, to, message);
return this._handleIncommingMessage(from, to, message);
});
this._client.addListener("pm", (from, message) => {
debug.irc(`${from} => me (pm): ${message}`);
this.emit("irc:pm", from, message);
});
this._client.addListener("action", (from, to, message) => {
debug.irc(`${from} => ${to}: ${message}`);
this.emit("irc:action", from, to, message);
return this._handleIncommingAction(from, to, message);
});
this._client.addListener("join", (channel, nick, message) => {
debug.irc(`${nick} join in ${channel}`);
this.emit("irc:join", channel, nick, message);
return this._handleJoin(channel, nick, message);
});
this._client.addListener("part", (channel, nick, message) => {
debug.irc(`${nick} part of ${channel}`);
this.emit("irc:part", channel, nick, message);
return this._handlePart(channel, nick, message);
});
this._client.addListener("topic", (channel, topic, nick, message) => {
debug.irc(`${nick} changed the topic of ${channel} to ${topic}`);
this.emit("irc:topic", channel, topic, nick, message);
return this._handleTopic(channel, topic, nick, message);
});
this._client.addListener("registered", data => {
return this._handleRegistered(data);
});
this._client.addListener("error", message => {
debug.irc("error: ", message);
this.emit("error", message);
});
this._ownUsernames = [this._options.nick];
return this._client;
}
sendMessage(channel, msg) {
// hack ¿?
(0, _lodash.noop)(this.client);
this.waitForRegistered().then(() => {
debug.irc('message out to ' + channel);
return this.client.say(channel, msg);
});
}
/**
* Nick
* @property
*/
get nick() {
return this._options.nick;
}
/**
* identifier
* @property
*/
get identifier() {
let {
port,
server,
ssl
} = this._options;
let originalNick = this._originalNick;
return `${originalNick}@${server}:${port}${ssl ? "+" : ""}`;
}
/**
* Get connection by server options, if does not exists, create one
* @param {object} options @see IRCConnection.contrusctor
* @return {IRCConnection}
*/
static getByServerOptions(uoptions) {
let options = (0, _lodash.assignIn)({}, defaultConfig, uoptions);
let {
nick,
port,
server,
ssl
} = options;
let identifier = `${nick}@${server}:${port}${ssl ? "+" : ""}`;
debug.irc(`Search server by identifier ${identifier}`);
let instance = instances.find(i => i.identifier === identifier);
if (!instance) {
instance = new IRCConnection(options);
}
return instance;
}
}, (_applyDecoratedDescriptor(_class2.prototype, "_constructor", [_dec10], Object.getOwnPropertyDescriptor(_class2.prototype, "_constructor"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "_handleRegistered", [_dec11], Object.getOwnPropertyDescriptor(_class2.prototype, "_handleRegistered"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "_handleIncommingMessage", [_dec12], Object.getOwnPropertyDescriptor(_class2.prototype, "_handleIncommingMessage"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "waitForRegistered", [_dec13], Object.getOwnPropertyDescriptor(_class2.prototype, "waitForRegistered"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "addChannel", [_dec14], Object.getOwnPropertyDescriptor(_class2.prototype, "addChannel"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "removeChannel", [_dec15], Object.getOwnPropertyDescriptor(_class2.prototype, "removeChannel"), _class2.prototype)), _class2));
exports.default = IRCConnection;