IRC Federation: RFC2813 implementation (ngIRCd) (#10113)
* Initial progress * Direct messaging complete * Handle net-splits * Cleaned up logging * more cleanup, better log messages * Added support for IRC users to create rooms and invite RC users * Keep rooms in sync * IRC user can kick RC user * Working on transcription of coffescript to ecmascript code and fitting on the codebase. * Adds settings section for config the IRC Server bridge. * Working handles for direct messages * Working handles for direct messages * Working handles for direct messages * Working handles for direct messages * Working handles for direct messages * Working on RC server connection to a local IRC Network * first version, using a RFC2813 implementation * Fixing lint errors * Fixed partial username * Fixed problems with scope * removed parser name * Added a button to reset the IRC connection * Adjusted messages * Fixed IRC federation for new users * Ignore eslint about control character on regex * Adjusted settings stringspull/11187/head
parent
3ebb78d2f9
commit
17a63ec2ee
@ -0,0 +1 @@ |
||||
node_modules |
||||
@ -0,0 +1,7 @@ |
||||
This directory and the files immediately inside it are automatically generated |
||||
when you change this package's NPM dependencies. Commit the files in this |
||||
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control |
||||
so that others run the same versions of sub-dependencies. |
||||
|
||||
You should NOT check in the node_modules directory that Meteor automatically |
||||
creates; if you are using git, the .gitignore file tells git to ignore it. |
||||
@ -0,0 +1,20 @@ |
||||
{ |
||||
"lockfileVersion": 1, |
||||
"dependencies": { |
||||
"dbly-linked-list": { |
||||
"version": "0.2.0", |
||||
"resolved": "https://registry.npmjs.org/dbly-linked-list/-/dbly-linked-list-0.2.0.tgz", |
||||
"integrity": "sha512-Ool7y15f6JRDs0YKx7Dh9uiTb1jS1SZLNdT3Y2q16DlaEghXbMsmODS/XittjR2xztt1gJUpz7jVxpqAPF8VGg==" |
||||
}, |
||||
"lodash.isequal": { |
||||
"version": "4.5.0", |
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", |
||||
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" |
||||
}, |
||||
"queue-fifo": { |
||||
"version": "0.2.4", |
||||
"resolved": "https://registry.npmjs.org/queue-fifo/-/queue-fifo-0.2.4.tgz", |
||||
"integrity": "sha512-o2xWptfzdw4QLIozUUcRPnppoTNK+X1DxWGd8csnJ1gQUsATfQaDryaGB1MhAu1L48vqPMtH69PZ1kZD82zlVw==" |
||||
} |
||||
} |
||||
} |
||||
@ -1,20 +1,22 @@ |
||||
Package.describe({ |
||||
name: 'rocketchat:irc', |
||||
version: '0.0.2', |
||||
summary: 'RocketChat libraries', |
||||
version: '0.0.1', |
||||
summary: 'RocketChat support for federating with IRC servers as a leaf node', |
||||
git: '' |
||||
}); |
||||
|
||||
Package.onUse(function(api) { |
||||
api.use([ |
||||
'ecmascript', |
||||
'underscore', |
||||
'rocketchat:lib' |
||||
]); |
||||
|
||||
api.addFiles([ |
||||
'server/settings.js', |
||||
'server/server.js' |
||||
], 'server'); |
||||
api.addFiles('server/irc.js', 'server'); |
||||
api.addFiles('server/methods/resetIrcConnection.js', 'server'); |
||||
api.addFiles('server/irc-settings.js', 'server'); |
||||
}); |
||||
|
||||
api.export(['Irc'], ['server']); |
||||
Npm.depends({ |
||||
'queue-fifo': '0.2.4' |
||||
}); |
||||
|
||||
@ -0,0 +1,138 @@ |
||||
import Queue from 'queue-fifo'; |
||||
import * as servers from '../servers'; |
||||
import * as peerCommandHandlers from './peerHandlers'; |
||||
import * as localCommandHandlers from './localHandlers'; |
||||
|
||||
class Bridge { |
||||
constructor(config) { |
||||
// General
|
||||
this.config = config; |
||||
|
||||
// Workaround for Rocket.Chat callbacks being called multiple times
|
||||
this.loggedInUsers = []; |
||||
|
||||
// Server
|
||||
const Server = servers[this.config.server.protocol]; |
||||
|
||||
this.server = new Server(this.config); |
||||
|
||||
this.setupPeerHandlers(); |
||||
this.setupLocalHandlers(); |
||||
|
||||
// Command queue
|
||||
this.queue = new Queue(); |
||||
this.queueTimeout = 5; |
||||
} |
||||
|
||||
init() { |
||||
this.loggedInUsers = []; |
||||
this.server.register(); |
||||
|
||||
this.server.on('registered', () => { |
||||
this.logQueue('Starting...'); |
||||
|
||||
this.runQueue(); |
||||
}); |
||||
} |
||||
|
||||
stop() { |
||||
this.server.disconnect(); |
||||
} |
||||
|
||||
/** |
||||
* Log helper |
||||
*/ |
||||
log(message) { |
||||
console.log(`[irc][bridge] ${ message }`); |
||||
} |
||||
|
||||
logQueue(message) { |
||||
console.log(`[irc][bridge][queue] ${ message }`); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* |
||||
* Queue |
||||
* |
||||
* |
||||
*/ |
||||
onMessageReceived(from, command, ...parameters) { |
||||
this.queue.enqueue({ from, command, parameters }); |
||||
} |
||||
|
||||
async runQueue() { |
||||
// If it is empty, skip and keep the queue going
|
||||
if (this.queue.isEmpty()) { |
||||
return setTimeout(this.runQueue.bind(this), this.queueTimeout); |
||||
} |
||||
|
||||
// Get the command
|
||||
const item = this.queue.dequeue(); |
||||
|
||||
this.logQueue(`Processing "${ item.command }" command from "${ item.from }"`); |
||||
|
||||
// Handle the command accordingly
|
||||
switch (item.from) { |
||||
case 'local': |
||||
if (!localCommandHandlers[item.command]) { |
||||
throw new Error(`Could not find handler for local:${ item.command }`); |
||||
} |
||||
|
||||
await localCommandHandlers[item.command].apply(this, item.parameters); |
||||
break; |
||||
case 'peer': |
||||
if (!peerCommandHandlers[item.command]) { |
||||
throw new Error(`Could not find handler for peer:${ item.command }`); |
||||
} |
||||
|
||||
await peerCommandHandlers[item.command].apply(this, item.parameters); |
||||
break; |
||||
} |
||||
|
||||
// Keep the queue going
|
||||
setTimeout(this.runQueue.bind(this), this.queueTimeout); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* |
||||
* Peer |
||||
* |
||||
* |
||||
*/ |
||||
setupPeerHandlers() { |
||||
this.server.on('peerCommand', (cmd) => { |
||||
this.onMessageReceived('peer', cmd.identifier, cmd.args); |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* |
||||
* Local |
||||
* |
||||
* |
||||
*/ |
||||
setupLocalHandlers() { |
||||
// Auth
|
||||
RocketChat.callbacks.add('afterValidateLogin', this.onMessageReceived.bind(this, 'local', 'onLogin'), RocketChat.callbacks.priority.LOW, 'irc-on-login'); |
||||
RocketChat.callbacks.add('afterCreateUser', this.onMessageReceived.bind(this, 'local', 'onCreateUser'), RocketChat.callbacks.priority.LOW, 'irc-on-create-user'); |
||||
// Joining rooms or channels
|
||||
RocketChat.callbacks.add('afterCreateChannel', this.onMessageReceived.bind(this, 'local', 'onCreateRoom'), RocketChat.callbacks.priority.LOW, 'irc-on-create-channel'); |
||||
RocketChat.callbacks.add('afterCreateRoom', this.onMessageReceived.bind(this, 'local', 'onCreateRoom'), RocketChat.callbacks.priority.LOW, 'irc-on-create-room'); |
||||
RocketChat.callbacks.add('afterJoinRoom', this.onMessageReceived.bind(this, 'local', 'onJoinRoom'), RocketChat.callbacks.priority.LOW, 'irc-on-join-room'); |
||||
// Leaving rooms or channels
|
||||
RocketChat.callbacks.add('afterLeaveRoom', this.onMessageReceived.bind(this, 'local', 'onLeaveRoom'), RocketChat.callbacks.priority.LOW, 'irc-on-leave-room'); |
||||
// Chatting
|
||||
RocketChat.callbacks.add('afterSaveMessage', this.onMessageReceived.bind(this, 'local', 'onSaveMessage'), RocketChat.callbacks.priority.LOW, 'irc-on-save-message'); |
||||
// Leaving
|
||||
RocketChat.callbacks.add('afterLogoutCleanUp', this.onMessageReceived.bind(this, 'local', 'onLogout'), RocketChat.callbacks.priority.LOW, 'irc-on-logout'); |
||||
} |
||||
|
||||
sendCommand(command, parameters) { |
||||
this.server.emit('onReceiveFromLocal', command, parameters); |
||||
} |
||||
} |
||||
|
||||
export default Bridge; |
||||
@ -0,0 +1,9 @@ |
||||
import onCreateRoom from './onCreateRoom'; |
||||
import onJoinRoom from './onJoinRoom'; |
||||
import onLeaveRoom from './onLeaveRoom'; |
||||
import onLogin from './onLogin'; |
||||
import onLogout from './onLogout'; |
||||
import onSaveMessage from './onSaveMessage'; |
||||
import onCreateUser from './onCreateUser'; |
||||
|
||||
export { onCreateRoom, onJoinRoom, onLeaveRoom, onLogin, onLogout, onSaveMessage, onCreateUser }; |
||||
@ -0,0 +1,15 @@ |
||||
export default function handleOnCreateRoom(user, room) { |
||||
if (!room.usernames) { |
||||
return this.log(`Room ${ room.name } does not have a valid list of usernames`); |
||||
} |
||||
|
||||
for (const username of room.usernames) { |
||||
const user = RocketChat.models.Users.findOne({ username }); |
||||
|
||||
if (user.profile.irc.fromIRC) { |
||||
this.sendCommand('joinChannel', { room, user }); |
||||
} else { |
||||
this.sendCommand('joinedChannel', { room, user }); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,32 @@ |
||||
export default function handleOnCreateUser(newUser) { |
||||
if (!newUser) { |
||||
return this.log('Invalid handleOnCreateUser call'); |
||||
} |
||||
if (!newUser.username) { |
||||
return this.log('Invalid handleOnCreateUser call (Missing username)'); |
||||
} |
||||
if (this.loggedInUsers.indexOf(newUser._id) !== -1) { |
||||
return this.log('Duplicate handleOnCreateUser call'); |
||||
} |
||||
|
||||
this.loggedInUsers.push(newUser._id); |
||||
|
||||
Meteor.users.update({ _id: newUser._id }, { |
||||
$set: { |
||||
'profile.irc.fromIRC': false, |
||||
'profile.irc.username': `${ newUser.username }-rkt`, |
||||
'profile.irc.nick': `${ newUser.username }-rkt`, |
||||
'profile.irc.hostname': 'rocket.chat' |
||||
} |
||||
}); |
||||
|
||||
const user = RocketChat.models.Users.findOne({ |
||||
_id: newUser._id |
||||
}); |
||||
|
||||
this.sendCommand('registerUser', user); |
||||
|
||||
const rooms = RocketChat.models.Rooms.findWithUsername(user.username).fetch(); |
||||
|
||||
rooms.forEach(room => this.sendCommand('joinedChannel', { room, user })); |
||||
} |
||||
@ -0,0 +1,3 @@ |
||||
export default function handleOnJoinRoom(user, room) { |
||||
this.sendCommand('joinedChannel', { room, user }); |
||||
} |
||||
@ -0,0 +1,3 @@ |
||||
export default function handleOnLeaveRoom(user, room) { |
||||
this.sendCommand('leftChannel', { room, user }); |
||||
} |
||||
@ -0,0 +1,32 @@ |
||||
export default function handleOnLogin(login) { |
||||
if (login.user === null) { |
||||
return this.log('Invalid handleOnLogin call'); |
||||
} |
||||
if (!login.user.username) { |
||||
return this.log('Invalid handleOnLogin call (Missing username)'); |
||||
} |
||||
if (this.loggedInUsers.indexOf(login.user._id) !== -1) { |
||||
return this.log('Duplicate handleOnLogin call'); |
||||
} |
||||
|
||||
this.loggedInUsers.push(login.user._id); |
||||
|
||||
Meteor.users.update({ _id: login.user._id }, { |
||||
$set: { |
||||
'profile.irc.fromIRC': false, |
||||
'profile.irc.username': `${ login.user.username }-rkt`, |
||||
'profile.irc.nick': `${ login.user.username }-rkt`, |
||||
'profile.irc.hostname': 'rocket.chat' |
||||
} |
||||
}); |
||||
|
||||
const user = RocketChat.models.Users.findOne({ |
||||
_id: login.user._id |
||||
}); |
||||
|
||||
this.sendCommand('registerUser', user); |
||||
|
||||
const rooms = RocketChat.models.Rooms.findWithUsername(user.username).fetch(); |
||||
|
||||
rooms.forEach(room => this.sendCommand('joinedChannel', { room, user })); |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
import _ from 'underscore'; |
||||
|
||||
export default function handleOnLogout(user) { |
||||
this.loggedInUsers = _.without(this.loggedInUsers, user._id); |
||||
|
||||
this.sendCommand('disconnected', { user }); |
||||
} |
||||
@ -0,0 +1,32 @@ |
||||
export default function handleOnSaveMessage(message, to) { |
||||
let toIdentification = ''; |
||||
// Direct message
|
||||
if (to.t === 'd') { |
||||
const subscriptions = RocketChat.models.Subscriptions.findByRoomId(to._id); |
||||
subscriptions.forEach((subscription) => { |
||||
if (subscription.u.username !== to.username) { |
||||
const userData = RocketChat.models.Users.findOne({ username: subscription.u.username }); |
||||
if (userData) { |
||||
if (userData.profile && userData.profile.irc && userData.profile.irc.nick) { |
||||
toIdentification = userData.profile.irc.nick; |
||||
} else { |
||||
toIdentification = userData.username; |
||||
} |
||||
} else { |
||||
toIdentification = subscription.u.username; |
||||
} |
||||
} |
||||
}); |
||||
|
||||
if (!toIdentification) { |
||||
console.error('[irc][server] Target user not found'); |
||||
return; |
||||
} |
||||
} else { |
||||
toIdentification = `#${ to.name }`; |
||||
} |
||||
|
||||
const user = RocketChat.models.Users.findOne({ _id: message.u._id }); |
||||
|
||||
this.sendCommand('sentMessage', { to: toIdentification, user, message: message.msg }); |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
export default function handleQUIT(args) { |
||||
const user = RocketChat.models.Users.findOne({ |
||||
'profile.irc.nick': args.nick |
||||
}); |
||||
|
||||
Meteor.users.update({ _id: user._id }, { |
||||
$set: { |
||||
status: 'offline' |
||||
} |
||||
}); |
||||
|
||||
RocketChat.models.Rooms.removeUsernameFromAll(user.username); |
||||
} |
||||
@ -0,0 +1,8 @@ |
||||
import disconnected from './disconnected'; |
||||
import joinedChannel from './joinedChannel'; |
||||
import leftChannel from './leftChannel'; |
||||
import nickChanged from './nickChanged'; |
||||
import sentMessage from './sentMessage'; |
||||
import userRegistered from './userRegistered'; |
||||
|
||||
export { disconnected, joinedChannel, leftChannel, nickChanged, sentMessage, userRegistered }; |
||||
@ -0,0 +1,22 @@ |
||||
export default function handleJoinedChannel(args) { |
||||
const user = RocketChat.models.Users.findOne({ |
||||
'profile.irc.nick': args.nick |
||||
}); |
||||
|
||||
if (!user) { |
||||
throw new Error(`Could not find a user with nick ${ args.nick }`); |
||||
} |
||||
|
||||
let room = RocketChat.models.Rooms.findOneByName(args.roomName); |
||||
|
||||
if (!room) { |
||||
const createdRoom = RocketChat.createRoom('c', args.roomName, user.username, [ /* usernames of the participants here */]); |
||||
room = RocketChat.models.Rooms.findOne({ _id: createdRoom.rid }); |
||||
|
||||
this.log(`${ user.username } created room ${ args.roomName }`); |
||||
} else { |
||||
RocketChat.addUserToRoom(room._id, user); |
||||
|
||||
this.log(`${ user.username } joined room ${ room.name }`); |
||||
} |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
export default function handleLeftChannel(args) { |
||||
const user = RocketChat.models.Users.findOne({ |
||||
'profile.irc.nick': args.nick |
||||
}); |
||||
|
||||
if (!user) { |
||||
throw new Error(`Could not find a user with nick ${ args.nick }`); |
||||
} |
||||
|
||||
const room = RocketChat.models.Rooms.findOneByName(args.roomName); |
||||
|
||||
if (!room) { |
||||
throw new Error(`Could not find a room with name ${ args.roomName }`); |
||||
} |
||||
|
||||
this.log(`${ user.username } left room ${ room.name }`); |
||||
RocketChat.removeUserFromRoom(room._id, user); |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
export default function handleNickChanged(args) { |
||||
const user = RocketChat.models.Users.findOne({ |
||||
'profile.irc.nick': args.nick |
||||
}); |
||||
|
||||
if (!user) { |
||||
throw new Error(`Could not find an user with nick ${ args.nick }`); |
||||
} |
||||
|
||||
this.log(`${ user.username } changed nick: ${ args.nick } -> ${ args.newNick }`); |
||||
|
||||
// Update on the database
|
||||
RocketChat.models.Users.update({ _id: user._id }, { |
||||
$set: { |
||||
name: args.newNick, |
||||
'profile.irc.nick': args.newNick |
||||
} |
||||
}); |
||||
} |
||||
@ -0,0 +1,82 @@ |
||||
/* |
||||
* |
||||
* Get direct chat room helper |
||||
* |
||||
* |
||||
*/ |
||||
const getDirectRoom = (source, target) => { |
||||
const rid = [ source._id, target._id ].sort().join(''); |
||||
|
||||
RocketChat.models.Rooms.upsert({ _id: rid }, { |
||||
$set: { |
||||
usernames: [source.username, target.username] |
||||
}, |
||||
$setOnInsert: { |
||||
t: 'd', |
||||
msgs: 0, |
||||
ts: new Date() |
||||
} |
||||
}); |
||||
|
||||
RocketChat.models.Subscriptions.upsert({rid, 'u._id': target._id}, { |
||||
$setOnInsert: { |
||||
name: source.username, |
||||
t: 'd', |
||||
open: false, |
||||
alert: false, |
||||
unread: 0, |
||||
u: { |
||||
_id: target._id, |
||||
username: target.username |
||||
} |
||||
} |
||||
}); |
||||
|
||||
RocketChat.models.Subscriptions.upsert({rid, 'u._id': source._id}, { |
||||
$setOnInsert: { |
||||
name: target.username, |
||||
t: 'd', |
||||
open: false, |
||||
alert: false, |
||||
unread: 0, |
||||
u: { |
||||
_id: source._id, |
||||
username: source.username |
||||
} |
||||
} |
||||
}); |
||||
|
||||
return { |
||||
_id: rid, |
||||
t: 'd' |
||||
}; |
||||
}; |
||||
|
||||
export default function handleSentMessage(args) { |
||||
const user = RocketChat.models.Users.findOne({ |
||||
'profile.irc.nick': args.nick |
||||
}); |
||||
|
||||
if (!user) { |
||||
throw new Error(`Could not find a user with nick ${ args.nick }`); |
||||
} |
||||
|
||||
let room; |
||||
|
||||
if (args.roomName) { |
||||
room = RocketChat.models.Rooms.findOneByName(args.roomName); |
||||
} else { |
||||
const recipientUser = RocketChat.models.Users.findOne({ |
||||
'profile.irc.nick': args.recipientNick |
||||
}); |
||||
|
||||
room = getDirectRoom(user, recipientUser); |
||||
} |
||||
|
||||
const message = { |
||||
msg: args.message, |
||||
ts: new Date() |
||||
}; |
||||
|
||||
RocketChat.sendMessage(user, message, room); |
||||
} |
||||
@ -0,0 +1,42 @@ |
||||
export default async function handleUserRegistered(args) { |
||||
// Check if there is an user with the given username
|
||||
let user = RocketChat.models.Users.findOne({ |
||||
'profile.irc.username': args.username |
||||
}); |
||||
|
||||
// If there is no user, create one...
|
||||
if (!user) { |
||||
this.log(`Registering ${ args.username } with nick: ${ args.nick }`); |
||||
|
||||
const userToInsert = { |
||||
name: args.nick, |
||||
username: `${ args.username }-irc`, |
||||
status: 'online', |
||||
utcOffset: 0, |
||||
active: true, |
||||
type: 'user', |
||||
profile: { |
||||
irc: { |
||||
fromIRC: true, |
||||
nick: args.nick, |
||||
username: args.username, |
||||
hostname: args.hostname |
||||
} |
||||
} |
||||
}; |
||||
|
||||
user = RocketChat.models.Users.create(userToInsert); |
||||
} else { |
||||
// ...otherwise, log the user in and update the information
|
||||
this.log(`Logging in ${ args.username } with nick: ${ args.nick }`); |
||||
|
||||
Meteor.users.update({ _id: user._id }, { |
||||
$set: { |
||||
status: 'online', |
||||
'profile.irc.nick': args.nick, |
||||
'profile.irc.username': args.username, |
||||
'profile.irc.hostname': args.hostname |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
@ -0,0 +1,64 @@ |
||||
Meteor.startup(function() { |
||||
RocketChat.settings.addGroup('IRC_Federation', function() { |
||||
this.add('IRC_Enabled', false, { |
||||
type: 'boolean', |
||||
i18nLabel: 'Enabled', |
||||
i18nDescription: 'IRC_Enabled', |
||||
alert: 'IRC_Enabled_Alert' |
||||
}); |
||||
|
||||
this.add('IRC_Protocol', 'RFC2813', { |
||||
type: 'select', |
||||
i18nLabel: 'Protocol', |
||||
i18nDescription: 'IRC_Protocol', |
||||
values: [ |
||||
{ |
||||
key: 'RFC2813', |
||||
i18nLabel: 'RFC2813' |
||||
} |
||||
] |
||||
}); |
||||
|
||||
this.add('IRC_Host', 'localhost', { |
||||
type: 'string', |
||||
i18nLabel: 'Host', |
||||
i18nDescription: 'IRC_Host' |
||||
}); |
||||
|
||||
this.add('IRC_Port', 6667, { |
||||
type: 'int', |
||||
i18nLabel: 'Port', |
||||
i18nDescription: 'IRC_Port' |
||||
}); |
||||
|
||||
this.add('IRC_Name', 'irc.rocket.chat', { |
||||
type: 'string', |
||||
i18nLabel: 'Name', |
||||
i18nDescription: 'IRC_Name' |
||||
}); |
||||
|
||||
this.add('IRC_Description', 'Rocket.Chat IRC Bridge', { |
||||
type: 'string', |
||||
i18nLabel: 'Description', |
||||
i18nDescription: 'IRC_Description' |
||||
}); |
||||
|
||||
this.add('IRC_Local_Password', 'password', { |
||||
type: 'string', |
||||
i18nLabel: 'Local_Password', |
||||
i18nDescription: 'IRC_Local_Password' |
||||
}); |
||||
|
||||
this.add('IRC_Peer_Password', 'password', { |
||||
type: 'string', |
||||
i18nLabel: 'Peer_Password', |
||||
i18nDescription: 'IRC_Peer_Password' |
||||
}); |
||||
|
||||
this.add('IRC_Reset_Connection', 'resetIrcConnection', { |
||||
type: 'action', |
||||
actionText: 'Reset_Connection', |
||||
i18nLabel: 'Reset_Connection' |
||||
}); |
||||
}); |
||||
}); |
||||
@ -0,0 +1,24 @@ |
||||
import Bridge from './irc-bridge'; |
||||
|
||||
if (!!RocketChat.settings.get('IRC_Enabled') === true) { |
||||
// Normalize the config values
|
||||
const config = { |
||||
server: { |
||||
protocol: RocketChat.settings.get('IRC_Protocol'), |
||||
host: RocketChat.settings.get('IRC_Host'), |
||||
port: RocketChat.settings.get('IRC_Port'), |
||||
name: RocketChat.settings.get('IRC_Name'), |
||||
description: RocketChat.settings.get('IRC_Description') |
||||
}, |
||||
passwords: { |
||||
local: RocketChat.settings.get('IRC_Local_Password'), |
||||
peer: RocketChat.settings.get('IRC_Peer_Password') |
||||
} |
||||
}; |
||||
|
||||
Meteor.ircBridge = new Bridge(config); |
||||
|
||||
Meteor.startup(() => { |
||||
Meteor.ircBridge.init(); |
||||
}); |
||||
} |
||||
@ -0,0 +1,52 @@ |
||||
import Bridge from '../irc-bridge'; |
||||
|
||||
Meteor.methods({ |
||||
resetIrcConnection() { |
||||
const ircEnabled = (!!RocketChat.settings.get('IRC_Enabled')) === true; |
||||
|
||||
if (Meteor.ircBridge) { |
||||
Meteor.ircBridge.stop(); |
||||
if (!ircEnabled) { |
||||
return { |
||||
message: 'Connection_Closed', |
||||
params: [] |
||||
}; |
||||
} |
||||
} |
||||
|
||||
if (ircEnabled) { |
||||
if (Meteor.ircBridge) { |
||||
Meteor.ircBridge.init(); |
||||
return { |
||||
message: 'Connection_Reset', |
||||
params: [] |
||||
}; |
||||
} |
||||
|
||||
// Normalize the config values
|
||||
const config = { |
||||
server: { |
||||
protocol: RocketChat.settings.get('IRC_Protocol'), |
||||
host: RocketChat.settings.get('IRC_Host'), |
||||
port: RocketChat.settings.get('IRC_Port'), |
||||
name: RocketChat.settings.get('IRC_Name'), |
||||
description: RocketChat.settings.get('IRC_Description') |
||||
}, |
||||
passwords: { |
||||
local: RocketChat.settings.get('IRC_Local_Password'), |
||||
peer: RocketChat.settings.get('IRC_Peer_Password') |
||||
} |
||||
}; |
||||
|
||||
Meteor.ircBridge = new Bridge(config); |
||||
Meteor.ircBridge.init(); |
||||
|
||||
return { |
||||
message: 'Connection_Reset', |
||||
params: [] |
||||
}; |
||||
} |
||||
|
||||
throw new Meteor.Error(t('IRC_Federation_Disabled')); |
||||
} |
||||
}); |
||||
@ -1,439 +0,0 @@ |
||||
import _ from 'underscore'; |
||||
import net from 'net'; |
||||
import Lru from 'lru-cache'; |
||||
|
||||
///////
|
||||
// Assign values
|
||||
|
||||
//Package availability
|
||||
const IRC_AVAILABILITY = RocketChat.settings.get('IRC_Enabled'); |
||||
|
||||
// Cache prep
|
||||
const MESSAGE_CACHE_SIZE = RocketChat.settings.get('IRC_Message_Cache_Size'); |
||||
const ircReceiveMessageCache = Lru(MESSAGE_CACHE_SIZE);//eslint-disable-line
|
||||
const ircSendMessageCache = Lru(MESSAGE_CACHE_SIZE);//eslint-disable-line
|
||||
|
||||
// IRC server
|
||||
const IRC_PORT = RocketChat.settings.get('IRC_Port'); |
||||
const IRC_HOST = RocketChat.settings.get('IRC_Host'); |
||||
|
||||
const ircClientMap = {}; |
||||
|
||||
//////
|
||||
// Core functionality
|
||||
|
||||
const bind = function(f) { |
||||
const g = Meteor.bindEnvironment((self, ...args) => f.apply(self, args)); |
||||
return function(...args) { g(this, ...args); }; |
||||
}; |
||||
|
||||
const async = (f, ...args) => Meteor.wrapAsync(f)(...args); |
||||
|
||||
class IrcClient { |
||||
constructor(loginReq) { |
||||
this.loginReq = loginReq; |
||||
|
||||
this.user = this.loginReq.user; |
||||
ircClientMap[this.user._id] = this; |
||||
this.ircPort = IRC_PORT; |
||||
this.ircHost = IRC_HOST; |
||||
this.msgBuf = []; |
||||
|
||||
this.isConnected = false; |
||||
this.isDistroyed = false; |
||||
this.socket = new net.Socket; |
||||
this.socket.setNoDelay; |
||||
this.socket.setEncoding('utf-8'); |
||||
this.socket.setKeepAlive(true); |
||||
this.connect = this.connect.bind(this); |
||||
this.onConnect = this.onConnect.bind(this); |
||||
this.onConnect = bind(this.onConnect); |
||||
this.onClose = bind(this.onClose); |
||||
this.onTimeout = bind(this.onTimeout); |
||||
this.onError = bind(this.onError); |
||||
this.onReceiveRawMessage = this.onReceiveRawMessage.bind(this); |
||||
this.onReceiveRawMessage = bind(this.onReceiveRawMessage); |
||||
this.socket.on('data', this.onReceiveRawMessage); |
||||
this.socket.on('close', this.onClose); |
||||
this.socket.on('timeout', this.onTimeout); |
||||
this.socket.on('error', this.onError); |
||||
|
||||
this.isJoiningRoom = false; |
||||
this.receiveMemberListBuf = {}; |
||||
this.pendingJoinRoomBuf = []; |
||||
|
||||
this.successLoginMessageRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_successLogin')); |
||||
this.failedLoginMessageRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_failedLogin')); |
||||
this.receiveMessageRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_receiveMessage')); |
||||
this.receiveMemberListRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_receiveMemberList')); |
||||
this.endMemberListRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_endMemberList')); |
||||
this.addMemberToRoomRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_addMemberToRoom')); |
||||
this.removeMemberFromRoomRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_removeMemberFromRoom')); |
||||
this.quitMemberRegex = new RegExp(RocketChat.settings.get('IRC_RegEx_quitMember')); |
||||
} |
||||
|
||||
connect(loginCb) { |
||||
this.loginCb = loginCb; |
||||
this.socket.connect(this.ircPort, this.ircHost, this.onConnect); |
||||
this.initRoomList(); |
||||
} |
||||
|
||||
disconnect() { |
||||
this.isDistroyed = true; |
||||
this.socket.destroy(); |
||||
} |
||||
|
||||
onConnect() { |
||||
console.log('[irc] onConnect -> '.yellow, this.user.username, 'connect success.'); |
||||
this.socket.write(`NICK ${ this.user.username }\r\n`); |
||||
this.socket.write(`USER ${ this.user.username } 0 * :${ this.user.name }\r\n`); |
||||
// message order could not make sure here
|
||||
this.isConnected = true; |
||||
const messageBuf = this.msgBuf; |
||||
messageBuf.forEach(msg => this.socket.write(msg)); |
||||
} |
||||
|
||||
onClose() { |
||||
console.log('[irc] onClose -> '.yellow, this.user.username, 'connection close.'); |
||||
this.isConnected = false; |
||||
if (this.isDistroyed) { |
||||
delete ircClientMap[this.user._id]; |
||||
} else { |
||||
this.connect(); |
||||
} |
||||
} |
||||
|
||||
onTimeout() { |
||||
console.log('[irc] onTimeout -> '.yellow, this.user.username, 'connection timeout.', arguments); |
||||
} |
||||
|
||||
onError() { |
||||
console.log('[irc] onError -> '.yellow, this.user.username, 'connection error.', arguments); |
||||
} |
||||
|
||||
onReceiveRawMessage(data) { |
||||
data = data.toString().split('\n'); |
||||
|
||||
data.forEach(line => { |
||||
line = line.trim(); |
||||
console.log(`[${ this.ircHost }:${ this.ircPort }]:`, line); |
||||
|
||||
// Send heartbeat package to irc server
|
||||
if (line.indexOf('PING') === 0) { |
||||
this.socket.write(line.replace('PING :', 'PONG ')); |
||||
return; |
||||
} |
||||
let matchResult = this.receiveMessageRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onReceiveMessage(matchResult[1], matchResult[2], matchResult[3]); |
||||
return; |
||||
} |
||||
matchResult = this.receiveMemberListRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onReceiveMemberList(matchResult[1], matchResult[2].split(' ')); |
||||
return; |
||||
} |
||||
matchResult = this.endMemberListRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onEndMemberList(matchResult[1]); |
||||
return; |
||||
} |
||||
matchResult = this.addMemberToRoomRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onAddMemberToRoom(matchResult[1], matchResult[2]); |
||||
return; |
||||
} |
||||
matchResult = this.removeMemberFromRoomRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onRemoveMemberFromRoom(matchResult[1], matchResult[2]); |
||||
return; |
||||
} |
||||
matchResult = this.quitMemberRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onQuitMember(matchResult[1]); |
||||
return; |
||||
} |
||||
matchResult = this.successLoginMessageRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onSuccessLoginMessage(); |
||||
return; |
||||
} |
||||
matchResult = this.failedLoginMessageRegex.exec(line); |
||||
if (matchResult) { |
||||
this.onFailedLoginMessage(); |
||||
return; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
onSuccessLoginMessage() { |
||||
console.log('[irc] onSuccessLoginMessage -> '.yellow); |
||||
if (this.loginCb) { |
||||
this.loginCb(null, this.loginReq); |
||||
} |
||||
} |
||||
|
||||
onFailedLoginMessage() { |
||||
console.log('[irc] onFailedLoginMessage -> '.yellow); |
||||
this.loginReq.allowed = false; |
||||
this.disconnect(); |
||||
if (this.loginCb) { |
||||
this.loginCb(null, this.loginReq); |
||||
} |
||||
} |
||||
|
||||
onReceiveMessage(source, target, content) { |
||||
const now = new Date; |
||||
const timestamp = now.getTime(); |
||||
let cacheKey = [source, target, content].join(','); |
||||
console.log('[irc] ircSendMessageCache.get -> '.yellow, 'key:', cacheKey, 'value:', ircSendMessageCache.get(cacheKey), 'ts:', timestamp - 1000); |
||||
if (ircSendMessageCache.get(cacheKey) > (timestamp - 1000)) { |
||||
return; |
||||
} else { |
||||
ircSendMessageCache.set(cacheKey, timestamp); |
||||
} |
||||
console.log('[irc] onReceiveMessage -> '.yellow, 'source:', source, 'target:', target, 'content:', content); |
||||
source = this.createUserWhenNotExist(source); |
||||
let room; |
||||
if (target[0] === '#') { |
||||
room = RocketChat.models.Rooms.findOneByName(target.substring(1)); |
||||
} else { |
||||
room = this.createDirectRoomWhenNotExist(source, this.user); |
||||
} |
||||
const message = { msg: content, ts: now }; |
||||
cacheKey = `${ source.username }${ timestamp }`; |
||||
ircReceiveMessageCache.set(cacheKey, true); |
||||
console.log('[irc] ircReceiveMessageCache.set -> '.yellow, 'key:', cacheKey); |
||||
RocketChat.sendMessage(source, message, room); |
||||
} |
||||
|
||||
onReceiveMemberList(roomName, members) { |
||||
this.receiveMemberListBuf[roomName] = this.receiveMemberListBuf[roomName].concat(members); |
||||
} |
||||
|
||||
onEndMemberList(roomName) { |
||||
const newMembers = this.receiveMemberListBuf[roomName]; |
||||
console.log('[irc] onEndMemberList -> '.yellow, 'room:', roomName, 'members:', newMembers.join(',')); |
||||
const room = RocketChat.models.Rooms.findOneByNameAndType(roomName, 'c'); |
||||
if (!room) { |
||||
return; |
||||
} |
||||
const oldMembers = room.usernames; |
||||
const appendMembers = _.difference(newMembers, oldMembers); |
||||
const removeMembers = _.difference(oldMembers, newMembers); |
||||
appendMembers.forEach(member => this.createUserWhenNotExist(member)); |
||||
RocketChat.models.Rooms.removeUsernamesById(room._id, removeMembers); |
||||
RocketChat.models.Rooms.addUsernamesById(room._id, appendMembers); |
||||
|
||||
this.isJoiningRoom = false; |
||||
roomName = this.pendingJoinRoomBuf.shift(); |
||||
if (roomName) { |
||||
this.joinRoom({ |
||||
t: 'c', |
||||
name: roomName |
||||
}); |
||||
} |
||||
} |
||||
|
||||
sendRawMessage(msg) { |
||||
console.log('[irc] sendRawMessage -> '.yellow, msg.slice(0, -2)); |
||||
if (this.isConnected) { |
||||
this.socket.write(msg); |
||||
} else { |
||||
this.msgBuf.push(msg); |
||||
} |
||||
} |
||||
|
||||
sendMessage(room, message) { |
||||
console.log('[irc] sendMessage -> '.yellow, 'userName:', message.u.username); |
||||
let target = ''; |
||||
if (room.t === 'c') { |
||||
target = `#${ room.name }`; |
||||
} else if (room.t === 'd') { |
||||
const usernames = room.usernames; |
||||
usernames.forEach(name => { |
||||
if (message.u.username !== name) { |
||||
target = name; |
||||
return; |
||||
} |
||||
}); |
||||
} |
||||
const cacheKey = [this.user.username, target, message.msg].join(','); |
||||
console.log('[irc] ircSendMessageCache.set -> '.yellow, 'key:', cacheKey, 'ts:', message.ts.getTime()); |
||||
ircSendMessageCache.set(cacheKey, message.ts.getTime()); |
||||
const msg = `PRIVMSG ${ target } :${ message.msg }\r\n`; |
||||
this.sendRawMessage(msg); |
||||
} |
||||
|
||||
initRoomList() { |
||||
const roomsCursor = RocketChat.models.Rooms.findByTypeContainingUsername('c', this.user.username, { fields: { name: 1, t: 1 }}); |
||||
const rooms = roomsCursor.fetch(); |
||||
rooms.forEach(room => this.joinRoom(room)); |
||||
} |
||||
|
||||
joinRoom(room) { |
||||
if (room.t !== 'c' || room.name === 'general') { |
||||
return; |
||||
} |
||||
if (this.isJoiningRoom) { |
||||
return this.pendingJoinRoomBuf.push(room.name); |
||||
} |
||||
console.log('[irc] joinRoom -> '.yellow, 'roomName:', room.name, 'pendingJoinRoomBuf:', this.pendingJoinRoomBuf.join(',')); |
||||
const msg = `JOIN #${ room.name }\r\n`; |
||||
this.receiveMemberListBuf[room.name] = []; |
||||
this.sendRawMessage(msg); |
||||
this.isJoiningRoom = true; |
||||
} |
||||
|
||||
leaveRoom(room) { |
||||
if (room.t !== 'c') { |
||||
return; |
||||
} |
||||
const msg = `PART #${ room.name }\r\n`; |
||||
this.sendRawMessage(msg); |
||||
} |
||||
|
||||
getMemberList(room) { |
||||
if (room.t !== 'c') { |
||||
return; |
||||
} |
||||
const msg = `NAMES #${ room.name }\r\n`; |
||||
this.receiveMemberListBuf[room.name] = []; |
||||
this.sendRawMessage(msg); |
||||
} |
||||
|
||||
onAddMemberToRoom(member, roomName) { |
||||
if (this.user.username === member) { |
||||
return; |
||||
} |
||||
console.log('[irc] onAddMemberToRoom -> '.yellow, 'roomName:', roomName, 'member:', member); |
||||
this.createUserWhenNotExist(member); |
||||
RocketChat.models.Rooms.addUsernameByName(roomName, member); |
||||
} |
||||
|
||||
onRemoveMemberFromRoom(member, roomName) { |
||||
console.log('[irc] onRemoveMemberFromRoom -> '.yellow, 'roomName:', roomName, 'member:', member); |
||||
RocketChat.models.Rooms.removeUsernameByName(roomName, member); |
||||
} |
||||
|
||||
onQuitMember(member) { |
||||
console.log('[irc] onQuitMember ->'.yellow, 'username:', member); |
||||
RocketChat.models.Rooms.removeUsernameFromAll(member); |
||||
Meteor.users.update({ name: member }, { $set: { status: 'offline' }}); |
||||
} |
||||
|
||||
createUserWhenNotExist(name) { |
||||
const user = Meteor.users.findOne({ name }); |
||||
if (user) { |
||||
return user; |
||||
} |
||||
console.log('[irc] createNotExistUser ->'.yellow, 'userName:', name); |
||||
Meteor.call('registerUser', { |
||||
email: `${ name }@rocketchat.org`, |
||||
pass: 'rocketchat', |
||||
name |
||||
}); |
||||
Meteor.users.update({ name }, { |
||||
$set: { |
||||
status: 'online', |
||||
username: name |
||||
} |
||||
}); |
||||
return Meteor.users.findOne({ name }); |
||||
} |
||||
|
||||
createDirectRoomWhenNotExist(source, target) { |
||||
console.log('[irc] createDirectRoomWhenNotExist -> '.yellow, 'source:', source, 'target:', target); |
||||
const rid = [source._id, target._id].sort().join(''); |
||||
const now = new Date(); |
||||
RocketChat.models.Rooms.upsert({ _id: rid}, { |
||||
$set: { |
||||
usernames: [source.username, target.username] |
||||
}, |
||||
$setOnInsert: { |
||||
t: 'd', |
||||
msgs: 0, |
||||
ts: now |
||||
} |
||||
}); |
||||
RocketChat.models.Subscriptions.upsert({ rid, $and: [{ 'u._id': target._id}]}, { |
||||
$setOnInsert: { |
||||
name: source.username, |
||||
t: 'd', |
||||
open: false, |
||||
alert: false, |
||||
unread: 0, |
||||
userMentions: 0, |
||||
groupMentions: 0, |
||||
u: { _id: target._id, username: target.username }} |
||||
}); |
||||
return { t: 'd', _id: rid }; |
||||
} |
||||
} |
||||
|
||||
IrcClient.getByUid = function(uid) { |
||||
return ircClientMap[uid]; |
||||
}; |
||||
|
||||
IrcClient.create = function(login) { |
||||
if (login.user == null) { |
||||
return login; |
||||
} |
||||
if (!(login.user._id in ircClientMap)) { |
||||
const ircClient = new IrcClient(login); |
||||
return async(ircClient.connect); |
||||
} |
||||
return login; |
||||
}; |
||||
|
||||
function IrcLoginer(login) { |
||||
console.log('[irc] validateLogin -> '.yellow, login); |
||||
return IrcClient.create(login); |
||||
} |
||||
|
||||
|
||||
function IrcSender(message) { |
||||
const name = message.u.username; |
||||
const timestamp = message.ts.getTime(); |
||||
const cacheKey = `${ name }${ timestamp }`; |
||||
if (ircReceiveMessageCache.get(cacheKey)) { |
||||
return message; |
||||
} |
||||
const room = RocketChat.models.Rooms.findOneById(message.rid, { fields: { name: 1, usernames: 1, t: 1 }}); |
||||
const ircClient = IrcClient.getByUid(message.u._id); |
||||
ircClient.sendMessage(room, message); |
||||
return message; |
||||
} |
||||
|
||||
|
||||
function IrcRoomJoiner(user, room) { |
||||
const ircClient = IrcClient.getByUid(user._id); |
||||
ircClient.joinRoom(room); |
||||
return room; |
||||
} |
||||
|
||||
|
||||
function IrcRoomLeaver(user, room) { |
||||
const ircClient = IrcClient.getByUid(user._id); |
||||
ircClient.leaveRoom(room); |
||||
return room; |
||||
} |
||||
|
||||
function IrcLogoutCleanUper(user) { |
||||
const ircClient = IrcClient.getByUid(user._id); |
||||
ircClient.disconnect(); |
||||
return user; |
||||
} |
||||
|
||||
//////
|
||||
// Make magic happen
|
||||
|
||||
// Only proceed if the package has been enabled
|
||||
if (IRC_AVAILABILITY === true) { |
||||
RocketChat.callbacks.add('beforeValidateLogin', IrcLoginer, RocketChat.callbacks.priority.LOW, 'irc-loginer'); |
||||
RocketChat.callbacks.add('beforeSaveMessage', IrcSender, RocketChat.callbacks.priority.LOW, 'irc-sender'); |
||||
RocketChat.callbacks.add('beforeJoinRoom', IrcRoomJoiner, RocketChat.callbacks.priority.LOW, 'irc-room-joiner'); |
||||
RocketChat.callbacks.add('beforeCreateChannel', IrcRoomJoiner, RocketChat.callbacks.priority.LOW, 'irc-room-joiner-create-channel'); |
||||
RocketChat.callbacks.add('beforeLeaveRoom', IrcRoomLeaver, RocketChat.callbacks.priority.LOW, 'irc-room-leaver'); |
||||
RocketChat.callbacks.add('afterLogoutCleanUp', IrcLogoutCleanUper, RocketChat.callbacks.priority.LOW, 'irc-clean-up'); |
||||
} |
||||
@ -0,0 +1,519 @@ |
||||
/** |
||||
* This file is part of https://github.com/martynsmith/node-irc
|
||||
* by https://github.com/martynsmith
|
||||
*/ |
||||
|
||||
module.exports = { |
||||
'001': { |
||||
name: 'rpl_welcome', |
||||
type: 'reply' |
||||
}, |
||||
'002': { |
||||
name: 'rpl_yourhost', |
||||
type: 'reply' |
||||
}, |
||||
'003': { |
||||
name: 'rpl_created', |
||||
type: 'reply' |
||||
}, |
||||
'004': { |
||||
name: 'rpl_myinfo', |
||||
type: 'reply' |
||||
}, |
||||
'005': { |
||||
name: 'rpl_isupport', |
||||
type: 'reply' |
||||
}, |
||||
200: { |
||||
name: 'rpl_tracelink', |
||||
type: 'reply' |
||||
}, |
||||
201: { |
||||
name: 'rpl_traceconnecting', |
||||
type: 'reply' |
||||
}, |
||||
202: { |
||||
name: 'rpl_tracehandshake', |
||||
type: 'reply' |
||||
}, |
||||
203: { |
||||
name: 'rpl_traceunknown', |
||||
type: 'reply' |
||||
}, |
||||
204: { |
||||
name: 'rpl_traceoperator', |
||||
type: 'reply' |
||||
}, |
||||
205: { |
||||
name: 'rpl_traceuser', |
||||
type: 'reply' |
||||
}, |
||||
206: { |
||||
name: 'rpl_traceserver', |
||||
type: 'reply' |
||||
}, |
||||
208: { |
||||
name: 'rpl_tracenewtype', |
||||
type: 'reply' |
||||
}, |
||||
211: { |
||||
name: 'rpl_statslinkinfo', |
||||
type: 'reply' |
||||
}, |
||||
212: { |
||||
name: 'rpl_statscommands', |
||||
type: 'reply' |
||||
}, |
||||
213: { |
||||
name: 'rpl_statscline', |
||||
type: 'reply' |
||||
}, |
||||
214: { |
||||
name: 'rpl_statsnline', |
||||
type: 'reply' |
||||
}, |
||||
215: { |
||||
name: 'rpl_statsiline', |
||||
type: 'reply' |
||||
}, |
||||
216: { |
||||
name: 'rpl_statskline', |
||||
type: 'reply' |
||||
}, |
||||
218: { |
||||
name: 'rpl_statsyline', |
||||
type: 'reply' |
||||
}, |
||||
219: { |
||||
name: 'rpl_endofstats', |
||||
type: 'reply' |
||||
}, |
||||
221: { |
||||
name: 'rpl_umodeis', |
||||
type: 'reply' |
||||
}, |
||||
241: { |
||||
name: 'rpl_statslline', |
||||
type: 'reply' |
||||
}, |
||||
242: { |
||||
name: 'rpl_statsuptime', |
||||
type: 'reply' |
||||
}, |
||||
243: { |
||||
name: 'rpl_statsoline', |
||||
type: 'reply' |
||||
}, |
||||
244: { |
||||
name: 'rpl_statshline', |
||||
type: 'reply' |
||||
}, |
||||
250: { |
||||
name: 'rpl_statsconn', |
||||
type: 'reply' |
||||
}, |
||||
251: { |
||||
name: 'rpl_luserclient', |
||||
type: 'reply' |
||||
}, |
||||
252: { |
||||
name: 'rpl_luserop', |
||||
type: 'reply' |
||||
}, |
||||
253: { |
||||
name: 'rpl_luserunknown', |
||||
type: 'reply' |
||||
}, |
||||
254: { |
||||
name: 'rpl_luserchannels', |
||||
type: 'reply' |
||||
}, |
||||
255: { |
||||
name: 'rpl_luserme', |
||||
type: 'reply' |
||||
}, |
||||
256: { |
||||
name: 'rpl_adminme', |
||||
type: 'reply' |
||||
}, |
||||
257: { |
||||
name: 'rpl_adminloc1', |
||||
type: 'reply' |
||||
}, |
||||
258: { |
||||
name: 'rpl_adminloc2', |
||||
type: 'reply' |
||||
}, |
||||
259: { |
||||
name: 'rpl_adminemail', |
||||
type: 'reply' |
||||
}, |
||||
261: { |
||||
name: 'rpl_tracelog', |
||||
type: 'reply' |
||||
}, |
||||
265: { |
||||
name: 'rpl_localusers', |
||||
type: 'reply' |
||||
}, |
||||
266: { |
||||
name: 'rpl_globalusers', |
||||
type: 'reply' |
||||
}, |
||||
300: { |
||||
name: 'rpl_none', |
||||
type: 'reply' |
||||
}, |
||||
301: { |
||||
name: 'rpl_away', |
||||
type: 'reply' |
||||
}, |
||||
302: { |
||||
name: 'rpl_userhost', |
||||
type: 'reply' |
||||
}, |
||||
303: { |
||||
name: 'rpl_ison', |
||||
type: 'reply' |
||||
}, |
||||
305: { |
||||
name: 'rpl_unaway', |
||||
type: 'reply' |
||||
}, |
||||
306: { |
||||
name: 'rpl_nowaway', |
||||
type: 'reply' |
||||
}, |
||||
311: { |
||||
name: 'rpl_whoisuser', |
||||
type: 'reply' |
||||
}, |
||||
312: { |
||||
name: 'rpl_whoisserver', |
||||
type: 'reply' |
||||
}, |
||||
313: { |
||||
name: 'rpl_whoisoperator', |
||||
type: 'reply' |
||||
}, |
||||
314: { |
||||
name: 'rpl_whowasuser', |
||||
type: 'reply' |
||||
}, |
||||
315: { |
||||
name: 'rpl_endofwho', |
||||
type: 'reply' |
||||
}, |
||||
317: { |
||||
name: 'rpl_whoisidle', |
||||
type: 'reply' |
||||
}, |
||||
318: { |
||||
name: 'rpl_endofwhois', |
||||
type: 'reply' |
||||
}, |
||||
319: { |
||||
name: 'rpl_whoischannels', |
||||
type: 'reply' |
||||
}, |
||||
321: { |
||||
name: 'rpl_liststart', |
||||
type: 'reply' |
||||
}, |
||||
322: { |
||||
name: 'rpl_list', |
||||
type: 'reply' |
||||
}, |
||||
323: { |
||||
name: 'rpl_listend', |
||||
type: 'reply' |
||||
}, |
||||
324: { |
||||
name: 'rpl_channelmodeis', |
||||
type: 'reply' |
||||
}, |
||||
329: { |
||||
name: 'rpl_creationtime', |
||||
type: 'reply' |
||||
}, |
||||
331: { |
||||
name: 'rpl_notopic', |
||||
type: 'reply' |
||||
}, |
||||
332: { |
||||
name: 'rpl_topic', |
||||
type: 'reply' |
||||
}, |
||||
333: { |
||||
name: 'rpl_topicwhotime', |
||||
type: 'reply' |
||||
}, |
||||
341: { |
||||
name: 'rpl_inviting', |
||||
type: 'reply' |
||||
}, |
||||
342: { |
||||
name: 'rpl_summoning', |
||||
type: 'reply' |
||||
}, |
||||
351: { |
||||
name: 'rpl_version', |
||||
type: 'reply' |
||||
}, |
||||
352: { |
||||
name: 'rpl_whoreply', |
||||
type: 'reply' |
||||
}, |
||||
353: { |
||||
name: 'rpl_namreply', |
||||
type: 'reply' |
||||
}, |
||||
364: { |
||||
name: 'rpl_links', |
||||
type: 'reply' |
||||
}, |
||||
365: { |
||||
name: 'rpl_endoflinks', |
||||
type: 'reply' |
||||
}, |
||||
366: { |
||||
name: 'rpl_endofnames', |
||||
type: 'reply' |
||||
}, |
||||
367: { |
||||
name: 'rpl_banlist', |
||||
type: 'reply' |
||||
}, |
||||
368: { |
||||
name: 'rpl_endofbanlist', |
||||
type: 'reply' |
||||
}, |
||||
369: { |
||||
name: 'rpl_endofwhowas', |
||||
type: 'reply' |
||||
}, |
||||
371: { |
||||
name: 'rpl_info', |
||||
type: 'reply' |
||||
}, |
||||
372: { |
||||
name: 'rpl_motd', |
||||
type: 'reply' |
||||
}, |
||||
374: { |
||||
name: 'rpl_endofinfo', |
||||
type: 'reply' |
||||
}, |
||||
375: { |
||||
name: 'rpl_motdstart', |
||||
type: 'reply' |
||||
}, |
||||
376: { |
||||
name: 'rpl_endofmotd', |
||||
type: 'reply' |
||||
}, |
||||
381: { |
||||
name: 'rpl_youreoper', |
||||
type: 'reply' |
||||
}, |
||||
382: { |
||||
name: 'rpl_rehashing', |
||||
type: 'reply' |
||||
}, |
||||
391: { |
||||
name: 'rpl_time', |
||||
type: 'reply' |
||||
}, |
||||
392: { |
||||
name: 'rpl_usersstart', |
||||
type: 'reply' |
||||
}, |
||||
393: { |
||||
name: 'rpl_users', |
||||
type: 'reply' |
||||
}, |
||||
394: { |
||||
name: 'rpl_endofusers', |
||||
type: 'reply' |
||||
}, |
||||
395: { |
||||
name: 'rpl_nousers', |
||||
type: 'reply' |
||||
}, |
||||
401: { |
||||
name: 'err_nosuchnick', |
||||
type: 'error' |
||||
}, |
||||
402: { |
||||
name: 'err_nosuchserver', |
||||
type: 'error' |
||||
}, |
||||
403: { |
||||
name: 'err_nosuchchannel', |
||||
type: 'error' |
||||
}, |
||||
404: { |
||||
name: 'err_cannotsendtochan', |
||||
type: 'error' |
||||
}, |
||||
405: { |
||||
name: 'err_toomanychannels', |
||||
type: 'error' |
||||
}, |
||||
406: { |
||||
name: 'err_wasnosuchnick', |
||||
type: 'error' |
||||
}, |
||||
407: { |
||||
name: 'err_toomanytargets', |
||||
type: 'error' |
||||
}, |
||||
409: { |
||||
name: 'err_noorigin', |
||||
type: 'error' |
||||
}, |
||||
411: { |
||||
name: 'err_norecipient', |
||||
type: 'error' |
||||
}, |
||||
412: { |
||||
name: 'err_notexttosend', |
||||
type: 'error' |
||||
}, |
||||
413: { |
||||
name: 'err_notoplevel', |
||||
type: 'error' |
||||
}, |
||||
414: { |
||||
name: 'err_wildtoplevel', |
||||
type: 'error' |
||||
}, |
||||
421: { |
||||
name: 'err_unknowncommand', |
||||
type: 'error' |
||||
}, |
||||
422: { |
||||
name: 'err_nomotd', |
||||
type: 'error' |
||||
}, |
||||
423: { |
||||
name: 'err_noadmininfo', |
||||
type: 'error' |
||||
}, |
||||
424: { |
||||
name: 'err_fileerror', |
||||
type: 'error' |
||||
}, |
||||
431: { |
||||
name: 'err_nonicknamegiven', |
||||
type: 'error' |
||||
}, |
||||
432: { |
||||
name: 'err_erroneusnickname', |
||||
type: 'error' |
||||
}, |
||||
433: { |
||||
name: 'err_nicknameinuse', |
||||
type: 'error' |
||||
}, |
||||
436: { |
||||
name: 'err_nickcollision', |
||||
type: 'error' |
||||
}, |
||||
441: { |
||||
name: 'err_usernotinchannel', |
||||
type: 'error' |
||||
}, |
||||
442: { |
||||
name: 'err_notonchannel', |
||||
type: 'error' |
||||
}, |
||||
443: { |
||||
name: 'err_useronchannel', |
||||
type: 'error' |
||||
}, |
||||
444: { |
||||
name: 'err_nologin', |
||||
type: 'error' |
||||
}, |
||||
445: { |
||||
name: 'err_summondisabled', |
||||
type: 'error' |
||||
}, |
||||
446: { |
||||
name: 'err_usersdisabled', |
||||
type: 'error' |
||||
}, |
||||
451: { |
||||
name: 'err_notregistered', |
||||
type: 'error' |
||||
}, |
||||
461: { |
||||
name: 'err_needmoreparams', |
||||
type: 'error' |
||||
}, |
||||
462: { |
||||
name: 'err_alreadyregistred', |
||||
type: 'error' |
||||
}, |
||||
463: { |
||||
name: 'err_nopermforhost', |
||||
type: 'error' |
||||
}, |
||||
464: { |
||||
name: 'err_passwdmismatch', |
||||
type: 'error' |
||||
}, |
||||
465: { |
||||
name: 'err_yourebannedcreep', |
||||
type: 'error' |
||||
}, |
||||
467: { |
||||
name: 'err_keyset', |
||||
type: 'error' |
||||
}, |
||||
471: { |
||||
name: 'err_channelisfull', |
||||
type: 'error' |
||||
}, |
||||
472: { |
||||
name: 'err_unknownmode', |
||||
type: 'error' |
||||
}, |
||||
473: { |
||||
name: 'err_inviteonlychan', |
||||
type: 'error' |
||||
}, |
||||
474: { |
||||
name: 'err_bannedfromchan', |
||||
type: 'error' |
||||
}, |
||||
475: { |
||||
name: 'err_badchannelkey', |
||||
type: 'error' |
||||
}, |
||||
481: { |
||||
name: 'err_noprivileges', |
||||
type: 'error' |
||||
}, |
||||
482: { |
||||
name: 'err_chanoprivsneeded', |
||||
type: 'error' |
||||
}, |
||||
483: { |
||||
name: 'err_cantkillserver', |
||||
type: 'error' |
||||
}, |
||||
491: { |
||||
name: 'err_nooperhost', |
||||
type: 'error' |
||||
}, |
||||
501: { |
||||
name: 'err_umodeunknownflag', |
||||
type: 'error' |
||||
}, |
||||
502: { |
||||
name: 'err_usersdontmatch', |
||||
type: 'error' |
||||
} |
||||
}; |
||||
@ -0,0 +1,183 @@ |
||||
import net from 'net'; |
||||
import util from 'util'; |
||||
import { EventEmitter } from 'events'; |
||||
|
||||
import parseMessage from './parseMessage'; |
||||
|
||||
import peerCommandHandlers from './peerCommandHandlers'; |
||||
import localCommandHandlers from './localCommandHandlers'; |
||||
|
||||
class RFC2813 { |
||||
constructor(config) { |
||||
this.config = config; |
||||
|
||||
// Hold registered state
|
||||
this.registerSteps = []; |
||||
this.isRegistered = false; |
||||
|
||||
// Hold peer server information
|
||||
this.serverPrefix = null; |
||||
|
||||
// Hold the buffer while receiving
|
||||
this.receiveBuffer = new Buffer(''); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Setup socket |
||||
*/ |
||||
setupSocket() { |
||||
// Setup socket
|
||||
this.socket = new net.Socket(); |
||||
this.socket.setNoDelay(); |
||||
this.socket.setEncoding('utf-8'); |
||||
this.socket.setKeepAlive(true); |
||||
this.socket.setTimeout(90000); |
||||
|
||||
this.socket.on('data', this.onReceiveFromPeer.bind(this)); |
||||
|
||||
this.socket.on('connect', this.onConnect.bind(this)); |
||||
this.socket.on('error', (err) => console.log('[irc][server][err]', err)); |
||||
this.socket.on('timeout', () => this.log('Timeout')); |
||||
this.socket.on('close', () => this.log('Connection Closed')); |
||||
// Setup local
|
||||
this.on('onReceiveFromLocal', this.onReceiveFromLocal.bind(this)); |
||||
} |
||||
|
||||
/** |
||||
* Log helper |
||||
*/ |
||||
log(message) { |
||||
console.log(`[irc][server] ${ message }`); |
||||
} |
||||
|
||||
/** |
||||
* Connect |
||||
*/ |
||||
register() { |
||||
this.log(`Connecting to @${ this.config.server.host }:${ this.config.server.port }`); |
||||
|
||||
if (!this.socket) { |
||||
this.setupSocket(); |
||||
} |
||||
|
||||
this.socket.connect(this.config.server.port, this.config.server.host); |
||||
} |
||||
|
||||
/** |
||||
* Disconnect |
||||
*/ |
||||
disconnect() { |
||||
this.log('Disconnecting from server.'); |
||||
|
||||
if (this.socket) { |
||||
this.socket.destroy(); |
||||
this.socket = undefined; |
||||
} |
||||
this.isRegistered = false; |
||||
this.registerSteps = []; |
||||
} |
||||
|
||||
/** |
||||
* Setup the server connection |
||||
*/ |
||||
onConnect() { |
||||
this.log('Connected! Registering as server...'); |
||||
|
||||
this.write({ |
||||
command: 'PASS', |
||||
parameters: [ this.config.passwords.local, '0210', 'ngircd' ] |
||||
}); |
||||
|
||||
this.write({ |
||||
command: 'SERVER', parameters: [ this.config.server.name ], |
||||
trailer: this.config.server.description |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Sends a command message through the socket |
||||
*/ |
||||
write(command) { |
||||
let buffer = command.prefix ? `:${ command.prefix } ` : ''; |
||||
buffer += command.command; |
||||
|
||||
if (command.parameters && command.parameters.length > 0) { |
||||
buffer += ` ${ command.parameters.join(' ') }`; |
||||
} |
||||
|
||||
if (command.trailer) { |
||||
buffer += ` :${ command.trailer }`; |
||||
} |
||||
|
||||
this.log(`Sending Command: ${ buffer }`); |
||||
|
||||
return this.socket.write(`${ buffer }\r\n`); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* |
||||
* Peer message handling |
||||
* |
||||
* |
||||
*/ |
||||
onReceiveFromPeer(chunk) { |
||||
if (typeof (chunk) === 'string') { |
||||
this.receiveBuffer += chunk; |
||||
} else { |
||||
this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk]); |
||||
} |
||||
|
||||
const lines = this.receiveBuffer.toString().split(/\r\n|\r|\n|\u0007/); // eslint-disable-line no-control-regex
|
||||
|
||||
// If the buffer does not end with \r\n, more chunks are coming
|
||||
if (lines.pop()) { |
||||
return; |
||||
} |
||||
|
||||
// Reset the buffer
|
||||
this.receiveBuffer = new Buffer(''); |
||||
|
||||
lines.forEach((line) => { |
||||
if (line.length && !line.startsWith('\a')) { |
||||
const parsedMessage = parseMessage(line); |
||||
|
||||
if (peerCommandHandlers[parsedMessage.command]) { |
||||
this.log(`Handling peer message: ${ line }`); |
||||
|
||||
const command = peerCommandHandlers[parsedMessage.command].call(this, parsedMessage); |
||||
|
||||
if (command) { |
||||
this.log(`Emitting peer command to local: ${ JSON.stringify(command) }`); |
||||
this.emit('peerCommand', command); |
||||
} |
||||
} else { |
||||
this.log(`Unhandled peer message: ${ JSON.stringify(parsedMessage) }`); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* |
||||
* Local message handling |
||||
* |
||||
* |
||||
*/ |
||||
onReceiveFromLocal(command, parameters) { |
||||
if (localCommandHandlers[command]) { |
||||
this.log(`Handling local command: ${ command }`); |
||||
|
||||
localCommandHandlers[command].call(this, parameters); |
||||
|
||||
} else { |
||||
this.log(`Unhandled local command: ${ JSON.stringify(command) }`); |
||||
} |
||||
} |
||||
} |
||||
|
||||
util.inherits(RFC2813, EventEmitter); |
||||
|
||||
export default RFC2813; |
||||
@ -0,0 +1,73 @@ |
||||
function registerUser(parameters) { |
||||
const { name, profile: { irc: { nick, username } } } = parameters; |
||||
|
||||
this.write({ |
||||
prefix: this.config.server.name, |
||||
command: 'NICK', parameters: [ nick, 1, username, 'irc.rocket.chat', 1, '+i' ], |
||||
trailer: name |
||||
}); |
||||
} |
||||
|
||||
function joinChannel(parameters) { |
||||
const { |
||||
room: { name: roomName }, |
||||
user: { profile: { irc: { nick } } } |
||||
} = parameters; |
||||
|
||||
this.write({ |
||||
prefix: this.config.server.name, |
||||
command: 'NJOIN', parameters: [ `#${ roomName }` ], |
||||
trailer: nick |
||||
}); |
||||
} |
||||
|
||||
function joinedChannel(parameters) { |
||||
const { |
||||
room: { name: roomName }, |
||||
user: { profile: { irc: { nick } } } |
||||
} = parameters; |
||||
|
||||
this.write({ |
||||
prefix: nick, |
||||
command: 'JOIN', parameters: [ `#${ roomName }` ] |
||||
}); |
||||
} |
||||
|
||||
function leftChannel(parameters) { |
||||
const { |
||||
room: { name: roomName }, |
||||
user: { profile: { irc: { nick } } } |
||||
} = parameters; |
||||
|
||||
this.write({ |
||||
prefix: nick, |
||||
command: 'PART', parameters: [ `#${ roomName }` ] |
||||
}); |
||||
} |
||||
|
||||
function sentMessage(parameters) { |
||||
const { |
||||
user: { profile: { irc: { nick } } }, |
||||
to, |
||||
message |
||||
} = parameters; |
||||
|
||||
this.write({ |
||||
prefix: nick, |
||||
command: 'PRIVMSG', parameters: [ to ], |
||||
trailer: message |
||||
}); |
||||
} |
||||
|
||||
function disconnected(parameters) { |
||||
const { |
||||
user: { profile: { irc: { nick } } } |
||||
} = parameters; |
||||
|
||||
this.write({ |
||||
prefix: nick, |
||||
command: 'QUIT' |
||||
}); |
||||
} |
||||
|
||||
export default { registerUser, joinChannel, joinedChannel, leftChannel, sentMessage, disconnected }; |
||||
@ -0,0 +1,69 @@ |
||||
/** |
||||
* This file is part of https://github.com/martynsmith/node-irc
|
||||
* by https://github.com/martynsmith
|
||||
*/ |
||||
|
||||
const replyFor = require('./codes'); |
||||
|
||||
/** |
||||
* parseMessage(line, stripColors) |
||||
* |
||||
* takes a raw "line" from the IRC server and turns it into an object with |
||||
* useful keys |
||||
* @param {String} line Raw message from IRC server. |
||||
* @return {Object} A parsed message object. |
||||
*/ |
||||
module.exports = function parseMessage(line) { |
||||
const message = {}; |
||||
let match; |
||||
|
||||
// Parse prefix
|
||||
match = line.match(/^:([^ ]+) +/); |
||||
if (match) { |
||||
message.prefix = match[1]; |
||||
line = line.replace(/^:[^ ]+ +/, ''); |
||||
match = message.prefix.match(/^([_a-zA-Z0-9\~\[\]\\`^{}|-]*)(!([^@]+)@(.*))?$/); |
||||
if (match) { |
||||
message.nick = match[1]; |
||||
message.user = match[3]; |
||||
message.host = match[4]; |
||||
} else { |
||||
message.server = message.prefix; |
||||
} |
||||
} |
||||
|
||||
// Parse command
|
||||
match = line.match(/^([^ ]+) */); |
||||
message.command = match[1]; |
||||
message.rawCommand = match[1]; |
||||
message.commandType = 'normal'; |
||||
line = line.replace(/^[^ ]+ +/, ''); |
||||
|
||||
if (replyFor[message.rawCommand]) { |
||||
message.command = replyFor[message.rawCommand].name; |
||||
message.commandType = replyFor[message.rawCommand].type; |
||||
} |
||||
|
||||
message.args = []; |
||||
let middle; |
||||
let trailing; |
||||
|
||||
// Parse parameters
|
||||
if (line.search(/^:|\s+:/) !== -1) { |
||||
match = line.match(/(.*?)(?:^:|\s+:)(.*)/); |
||||
middle = match[1].trimRight(); |
||||
trailing = match[2]; |
||||
} else { |
||||
middle = line; |
||||
} |
||||
|
||||
if (middle.length) { |
||||
message.args = middle.split(/ +/); |
||||
} |
||||
|
||||
if (typeof (trailing) !== 'undefined' && trailing.length) { |
||||
message.args.push(trailing); |
||||
} |
||||
|
||||
return message; |
||||
}; |
||||
@ -0,0 +1,112 @@ |
||||
function PASS() { |
||||
this.log('Received PASS command, continue registering...'); |
||||
|
||||
this.registerSteps.push('PASS'); |
||||
} |
||||
|
||||
function SERVER(parsedMessage) { |
||||
this.log('Received SERVER command, waiting for first PING...'); |
||||
|
||||
this.serverPrefix = parsedMessage.prefix; |
||||
|
||||
this.registerSteps.push('SERVER'); |
||||
} |
||||
|
||||
function PING() { |
||||
if (!this.isRegistered && this.registerSteps.length === 2) { |
||||
this.log('Received first PING command, server is registered!'); |
||||
|
||||
this.isRegistered = true; |
||||
|
||||
this.emit('registered'); |
||||
} |
||||
|
||||
this.write({ |
||||
prefix: this.config.server.name, |
||||
command: 'PONG', |
||||
parameters: [ this.config.server.name ] |
||||
}); |
||||
} |
||||
|
||||
function NICK(parsedMessage) { |
||||
let command; |
||||
|
||||
// Check if the message comes from the server,
|
||||
// which means it is a new user
|
||||
if (parsedMessage.prefix === this.serverPrefix) { |
||||
command = { |
||||
identifier: 'userRegistered', |
||||
args: { |
||||
nick: parsedMessage.args[0], |
||||
username: parsedMessage.args[2], |
||||
host: parsedMessage.args[3], |
||||
name: parsedMessage.args[6] |
||||
} |
||||
}; |
||||
} else { // Otherwise, it is a nick change
|
||||
command = { |
||||
identifier: 'nickChanged', |
||||
args: { |
||||
nick: parsedMessage.nick, |
||||
newNick: parsedMessage.args[0] |
||||
} |
||||
}; |
||||
} |
||||
|
||||
return command; |
||||
} |
||||
|
||||
function JOIN(parsedMessage) { |
||||
const command = { |
||||
identifier: 'joinedChannel', |
||||
args: { |
||||
roomName: parsedMessage.args[0].substring(1), |
||||
nick: parsedMessage.prefix |
||||
} |
||||
}; |
||||
|
||||
return command; |
||||
} |
||||
|
||||
function PART(parsedMessage) { |
||||
const command = { |
||||
identifier: 'leftChannel', |
||||
args: { |
||||
roomName: parsedMessage.args[0].substring(1), |
||||
nick: parsedMessage.prefix |
||||
} |
||||
}; |
||||
|
||||
return command; |
||||
} |
||||
|
||||
function PRIVMSG(parsedMessage) { |
||||
const command = { |
||||
identifier: 'sentMessage', |
||||
args: { |
||||
nick: parsedMessage.prefix, |
||||
message: parsedMessage.args[1] |
||||
} |
||||
}; |
||||
|
||||
if (parsedMessage.args[0][0] === '#') { |
||||
command.args.roomName = parsedMessage.args[0].substring(1); |
||||
} else { |
||||
command.args.recipientNick = parsedMessage.args[0]; |
||||
} |
||||
|
||||
return command; |
||||
} |
||||
|
||||
function QUIT(parsedMessage) { |
||||
const command = { |
||||
identifier: 'disconnected', |
||||
args: { |
||||
nick: parsedMessage.prefix |
||||
} |
||||
}; |
||||
|
||||
return command; |
||||
} |
||||
|
||||
export default { PASS, SERVER, PING, NICK, JOIN, PART, PRIVMSG, QUIT }; |
||||
@ -0,0 +1,3 @@ |
||||
import RFC2813 from './RFC2813'; |
||||
|
||||
export { RFC2813 }; |
||||
@ -1,78 +0,0 @@ |
||||
Meteor.startup(function() { |
||||
RocketChat.settings.addGroup('IRC', function() { |
||||
|
||||
// Is this thing on?
|
||||
this.add('IRC_Enabled', false, { |
||||
type: 'boolean', |
||||
i18nLabel: 'Enabled', |
||||
i18nDescription: 'IRC_Enabled', |
||||
alert: 'IRC Support is a work in progress. Use on a production system is not recommended at this time.' |
||||
}); |
||||
|
||||
// The IRC host server to talk to
|
||||
this.add('IRC_Host', 'irc.freenode.net', { |
||||
type: 'string', |
||||
i18nLabel: 'Host', |
||||
i18nDescription: 'IRC_Hostname' |
||||
}); |
||||
|
||||
// The port to connect on the remote server
|
||||
this.add('IRC_Port', 6667, { |
||||
type: 'int', |
||||
i18nLabel: 'Port', |
||||
i18nDescription: 'IRC_Port' |
||||
}); |
||||
|
||||
// Cache size of the messages we send the host IRC server
|
||||
this.add('IRC_Message_Cache_Size', 200, { |
||||
type: 'int', |
||||
i18nLabel: 'Message Cache Size', |
||||
i18nDescription: 'IRC_Message_Cache_Size' |
||||
}); |
||||
|
||||
// Expandable box for modifying regular expressions for IRC interaction
|
||||
this.section('Regular_Expressions', function() { |
||||
this.add('IRC_RegEx_successLogin', 'Welcome to the freenode Internet Relay Chat Network', { |
||||
type: 'string', |
||||
i18nLabel: 'Login Successful', |
||||
i18nDescription: 'IRC_Login_Success' |
||||
}); |
||||
this.add('IRC_RegEx_failedLogin', 'You have not registered', { |
||||
type: 'string', |
||||
i18nLabel: 'Login Failed', |
||||
i18nDescription: 'IRC_Login_Fail' |
||||
}); |
||||
this.add('IRC_RegEx_receiveMessage', '^:(\S+)!~\S+ PRIVMSG (\S+) :(.+)$', { |
||||
type: 'string', |
||||
i18nLabel: 'Private Message', |
||||
i18nDescription: 'IRC_Private_Message' |
||||
}); |
||||
this.add('IRC_RegEx_receiveMemberList', '^:\S+ \d+ \S+ = #(\S+) :(.*)$', { |
||||
type: 'string', |
||||
i18nLabel: 'Channel User List Start', |
||||
i18nDescription: 'IRC_Channel_Users' |
||||
}); |
||||
this.add('IRC_RegEx_endMemberList', '^.+#(\S+) :End of \/NAMES list.$', { |
||||
type: 'string', |
||||
i18nLabel: 'Channel User List End', |
||||
i18nDescription: 'IRC_Channel_Users_End' |
||||
}); |
||||
this.add('IRC_RegEx_addMemberToRoom', '^:(\S+)!~\S+ JOIN #(\S+)$', { |
||||
type: 'string', |
||||
i18nLabel: 'Join Channel', |
||||
i18nDescription: 'IRC_Channel_Join' |
||||
}); |
||||
this.add('IRC_RegEx_removeMemberFromRoom', '^:(\S+)!~\S+ PART #(\S+)$', { |
||||
type: 'string', |
||||
i18nLabel: 'Leave Channel', |
||||
i18nDescription: 'IRC_Channel_Leave' |
||||
}); |
||||
this.add('IRC_RegEx_quitMember', '^:(\S+)!~\S+ QUIT .*$', { |
||||
type: 'string', |
||||
i18nLabel: 'Quit IRC Session', |
||||
i18nDescription: 'IRC_Quit' |
||||
}); |
||||
}); |
||||
|
||||
}); |
||||
}); |
||||
@ -0,0 +1,874 @@ |
||||
{ |
||||
"name": "rocketchat-livechat", |
||||
"version": "1.0.0", |
||||
"lockfileVersion": 1, |
||||
"requires": true, |
||||
"dependencies": { |
||||
"abbrev": { |
||||
"version": "1.1.1", |
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", |
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" |
||||
}, |
||||
"ajv": { |
||||
"version": "5.5.2", |
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", |
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", |
||||
"requires": { |
||||
"co": "4.6.0", |
||||
"fast-deep-equal": "1.0.0", |
||||
"fast-json-stable-stringify": "2.0.0", |
||||
"json-schema-traverse": "0.3.1" |
||||
} |
||||
}, |
||||
"ansi-regex": { |
||||
"version": "2.1.1", |
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", |
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" |
||||
}, |
||||
"aproba": { |
||||
"version": "1.2.0", |
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", |
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" |
||||
}, |
||||
"are-we-there-yet": { |
||||
"version": "1.1.4", |
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", |
||||
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", |
||||
"requires": { |
||||
"delegates": "1.0.0", |
||||
"readable-stream": "2.3.4" |
||||
} |
||||
}, |
||||
"asn1": { |
||||
"version": "0.2.3", |
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", |
||||
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" |
||||
}, |
||||
"assert-plus": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", |
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" |
||||
}, |
||||
"asynckit": { |
||||
"version": "0.4.0", |
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", |
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" |
||||
}, |
||||
"autolinker": { |
||||
"version": "1.6.2", |
||||
"resolved": "https://registry.npmjs.org/autolinker/-/autolinker-1.6.2.tgz", |
||||
"integrity": "sha512-IKLGtYFb3jzGTtgCpb4bm//1sXmmmgmr0msKshhYoc7EsWmLCFvuyxLcEIfcZ5gbCgZGXrnXkOkcBblOFEnlog==" |
||||
}, |
||||
"aws-sign2": { |
||||
"version": "0.7.0", |
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", |
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" |
||||
}, |
||||
"aws4": { |
||||
"version": "1.6.0", |
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", |
||||
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" |
||||
}, |
||||
"babel-runtime": { |
||||
"version": "6.26.0", |
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", |
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", |
||||
"requires": { |
||||
"core-js": "2.5.3", |
||||
"regenerator-runtime": "0.11.1" |
||||
} |
||||
}, |
||||
"balanced-match": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", |
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" |
||||
}, |
||||
"bcrypt": { |
||||
"version": "1.0.3", |
||||
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-1.0.3.tgz", |
||||
"integrity": "sha512-pRyDdo73C8Nim3jwFJ7DWe3TZCgwDfWZ6nHS5LSdU77kWbj1frruvdndP02AOavtD4y8v6Fp2dolbHgp4SDrfg==", |
||||
"requires": { |
||||
"nan": "2.6.2", |
||||
"node-pre-gyp": "0.6.36" |
||||
} |
||||
}, |
||||
"bcrypt-pbkdf": { |
||||
"version": "1.0.1", |
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", |
||||
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", |
||||
"optional": true, |
||||
"requires": { |
||||
"tweetnacl": "0.14.5" |
||||
} |
||||
}, |
||||
"block-stream": { |
||||
"version": "0.0.9", |
||||
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", |
||||
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", |
||||
"requires": { |
||||
"inherits": "2.0.3" |
||||
} |
||||
}, |
||||
"boom": { |
||||
"version": "4.3.1", |
||||
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", |
||||
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", |
||||
"requires": { |
||||
"hoek": "4.2.1" |
||||
} |
||||
}, |
||||
"brace-expansion": { |
||||
"version": "1.1.11", |
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |
||||
"requires": { |
||||
"balanced-match": "1.0.0", |
||||
"concat-map": "0.0.1" |
||||
} |
||||
}, |
||||
"caseless": { |
||||
"version": "0.12.0", |
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", |
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" |
||||
}, |
||||
"co": { |
||||
"version": "4.6.0", |
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", |
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" |
||||
}, |
||||
"code-point-at": { |
||||
"version": "1.1.0", |
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", |
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" |
||||
}, |
||||
"combined-stream": { |
||||
"version": "1.0.6", |
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", |
||||
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", |
||||
"requires": { |
||||
"delayed-stream": "1.0.0" |
||||
} |
||||
}, |
||||
"concat-map": { |
||||
"version": "0.0.1", |
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", |
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" |
||||
}, |
||||
"console-control-strings": { |
||||
"version": "1.1.0", |
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", |
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" |
||||
}, |
||||
"core-js": { |
||||
"version": "2.5.3", |
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", |
||||
"integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=" |
||||
}, |
||||
"core-util-is": { |
||||
"version": "1.0.2", |
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", |
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" |
||||
}, |
||||
"cryptiles": { |
||||
"version": "3.1.2", |
||||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", |
||||
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", |
||||
"requires": { |
||||
"boom": "5.2.0" |
||||
}, |
||||
"dependencies": { |
||||
"boom": { |
||||
"version": "5.2.0", |
||||
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", |
||||
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", |
||||
"requires": { |
||||
"hoek": "4.2.1" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"dashdash": { |
||||
"version": "1.14.1", |
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", |
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", |
||||
"requires": { |
||||
"assert-plus": "1.0.0" |
||||
} |
||||
}, |
||||
"debug": { |
||||
"version": "2.6.9", |
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", |
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", |
||||
"requires": { |
||||
"ms": "2.0.0" |
||||
} |
||||
}, |
||||
"deep-extend": { |
||||
"version": "0.4.2", |
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", |
||||
"integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" |
||||
}, |
||||
"delayed-stream": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", |
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" |
||||
}, |
||||
"delegates": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", |
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" |
||||
}, |
||||
"ecc-jsbn": { |
||||
"version": "0.1.1", |
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", |
||||
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", |
||||
"optional": true, |
||||
"requires": { |
||||
"jsbn": "0.1.1" |
||||
} |
||||
}, |
||||
"extend": { |
||||
"version": "3.0.1", |
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", |
||||
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" |
||||
}, |
||||
"extsprintf": { |
||||
"version": "1.3.0", |
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", |
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" |
||||
}, |
||||
"fast-deep-equal": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", |
||||
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" |
||||
}, |
||||
"fast-json-stable-stringify": { |
||||
"version": "2.0.0", |
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", |
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" |
||||
}, |
||||
"forever-agent": { |
||||
"version": "0.6.1", |
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", |
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" |
||||
}, |
||||
"form-data": { |
||||
"version": "2.3.2", |
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", |
||||
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", |
||||
"requires": { |
||||
"asynckit": "0.4.0", |
||||
"combined-stream": "1.0.6", |
||||
"mime-types": "2.1.18" |
||||
} |
||||
}, |
||||
"fs.realpath": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", |
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" |
||||
}, |
||||
"fstream": { |
||||
"version": "1.0.11", |
||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", |
||||
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", |
||||
"requires": { |
||||
"graceful-fs": "4.1.11", |
||||
"inherits": "2.0.3", |
||||
"mkdirp": "0.5.1", |
||||
"rimraf": "2.6.2" |
||||
} |
||||
}, |
||||
"fstream-ignore": { |
||||
"version": "1.0.5", |
||||
"resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", |
||||
"integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", |
||||
"requires": { |
||||
"fstream": "1.0.11", |
||||
"inherits": "2.0.3", |
||||
"minimatch": "3.0.4" |
||||
} |
||||
}, |
||||
"gauge": { |
||||
"version": "2.7.4", |
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", |
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", |
||||
"requires": { |
||||
"aproba": "1.2.0", |
||||
"console-control-strings": "1.1.0", |
||||
"has-unicode": "2.0.1", |
||||
"object-assign": "4.1.1", |
||||
"signal-exit": "3.0.2", |
||||
"string-width": "1.0.2", |
||||
"strip-ansi": "3.0.1", |
||||
"wide-align": "1.1.2" |
||||
} |
||||
}, |
||||
"getpass": { |
||||
"version": "0.1.7", |
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", |
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", |
||||
"requires": { |
||||
"assert-plus": "1.0.0" |
||||
} |
||||
}, |
||||
"glob": { |
||||
"version": "7.1.2", |
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", |
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", |
||||
"requires": { |
||||
"fs.realpath": "1.0.0", |
||||
"inflight": "1.0.6", |
||||
"inherits": "2.0.3", |
||||
"minimatch": "3.0.4", |
||||
"once": "1.4.0", |
||||
"path-is-absolute": "1.0.1" |
||||
} |
||||
}, |
||||
"graceful-fs": { |
||||
"version": "4.1.11", |
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", |
||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" |
||||
}, |
||||
"har-schema": { |
||||
"version": "2.0.0", |
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", |
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" |
||||
}, |
||||
"har-validator": { |
||||
"version": "5.0.3", |
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", |
||||
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", |
||||
"requires": { |
||||
"ajv": "5.5.2", |
||||
"har-schema": "2.0.0" |
||||
} |
||||
}, |
||||
"has-unicode": { |
||||
"version": "2.0.1", |
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", |
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" |
||||
}, |
||||
"hawk": { |
||||
"version": "6.0.2", |
||||
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", |
||||
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", |
||||
"requires": { |
||||
"boom": "4.3.1", |
||||
"cryptiles": "3.1.2", |
||||
"hoek": "4.2.1", |
||||
"sntp": "2.1.0" |
||||
} |
||||
}, |
||||
"hoek": { |
||||
"version": "4.2.1", |
||||
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", |
||||
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" |
||||
}, |
||||
"http-signature": { |
||||
"version": "1.2.0", |
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", |
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", |
||||
"requires": { |
||||
"assert-plus": "1.0.0", |
||||
"jsprim": "1.4.1", |
||||
"sshpk": "1.13.1" |
||||
} |
||||
}, |
||||
"inflight": { |
||||
"version": "1.0.6", |
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", |
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", |
||||
"requires": { |
||||
"once": "1.4.0", |
||||
"wrappy": "1.0.2" |
||||
} |
||||
}, |
||||
"inherits": { |
||||
"version": "2.0.3", |
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", |
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" |
||||
}, |
||||
"ini": { |
||||
"version": "1.3.5", |
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", |
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" |
||||
}, |
||||
"is-fullwidth-code-point": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", |
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", |
||||
"requires": { |
||||
"number-is-nan": "1.0.1" |
||||
} |
||||
}, |
||||
"is-typedarray": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", |
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" |
||||
}, |
||||
"isarray": { |
||||
"version": "1.0.0", |
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", |
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" |
||||
}, |
||||
"isstream": { |
||||
"version": "0.1.2", |
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", |
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" |
||||
}, |
||||
"jquery": { |
||||
"version": "3.3.1", |
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", |
||||
"integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" |
||||
}, |
||||
"jsbn": { |
||||
"version": "0.1.1", |
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", |
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", |
||||
"optional": true |
||||
}, |
||||
"json-schema": { |
||||
"version": "0.2.3", |
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", |
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" |
||||
}, |
||||
"json-schema-traverse": { |
||||
"version": "0.3.1", |
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", |
||||
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" |
||||
}, |
||||
"json-stringify-safe": { |
||||
"version": "5.0.1", |
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", |
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" |
||||
}, |
||||
"jsprim": { |
||||
"version": "1.4.1", |
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", |
||||
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", |
||||
"requires": { |
||||
"assert-plus": "1.0.0", |
||||
"extsprintf": "1.3.0", |
||||
"json-schema": "0.2.3", |
||||
"verror": "1.10.0" |
||||
} |
||||
}, |
||||
"mime-db": { |
||||
"version": "1.33.0", |
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", |
||||
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" |
||||
}, |
||||
"mime-types": { |
||||
"version": "2.1.18", |
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", |
||||
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", |
||||
"requires": { |
||||
"mime-db": "1.33.0" |
||||
} |
||||
}, |
||||
"minimatch": { |
||||
"version": "3.0.4", |
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", |
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", |
||||
"requires": { |
||||
"brace-expansion": "1.1.11" |
||||
} |
||||
}, |
||||
"minimist": { |
||||
"version": "0.0.8", |
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", |
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" |
||||
}, |
||||
"mkdirp": { |
||||
"version": "0.5.1", |
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", |
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", |
||||
"requires": { |
||||
"minimist": "0.0.8" |
||||
} |
||||
}, |
||||
"moment": { |
||||
"version": "2.20.1", |
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", |
||||
"integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" |
||||
}, |
||||
"ms": { |
||||
"version": "2.0.0", |
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", |
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" |
||||
}, |
||||
"nan": { |
||||
"version": "2.6.2", |
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", |
||||
"integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" |
||||
}, |
||||
"node-pre-gyp": { |
||||
"version": "0.6.36", |
||||
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", |
||||
"integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=", |
||||
"requires": { |
||||
"mkdirp": "0.5.1", |
||||
"nopt": "4.0.1", |
||||
"npmlog": "4.1.2", |
||||
"rc": "1.2.5", |
||||
"request": "2.83.0", |
||||
"rimraf": "2.6.2", |
||||
"semver": "5.5.0", |
||||
"tar": "2.2.1", |
||||
"tar-pack": "3.4.1" |
||||
} |
||||
}, |
||||
"nopt": { |
||||
"version": "4.0.1", |
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", |
||||
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", |
||||
"requires": { |
||||
"abbrev": "1.1.1", |
||||
"osenv": "0.1.5" |
||||
} |
||||
}, |
||||
"npmlog": { |
||||
"version": "4.1.2", |
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", |
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", |
||||
"requires": { |
||||
"are-we-there-yet": "1.1.4", |
||||
"console-control-strings": "1.1.0", |
||||
"gauge": "2.7.4", |
||||
"set-blocking": "2.0.0" |
||||
} |
||||
}, |
||||
"number-is-nan": { |
||||
"version": "1.0.1", |
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", |
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" |
||||
}, |
||||
"oauth-sign": { |
||||
"version": "0.8.2", |
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", |
||||
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" |
||||
}, |
||||
"object-assign": { |
||||
"version": "4.1.1", |
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", |
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" |
||||
}, |
||||
"once": { |
||||
"version": "1.4.0", |
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", |
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", |
||||
"requires": { |
||||
"wrappy": "1.0.2" |
||||
} |
||||
}, |
||||
"os-homedir": { |
||||
"version": "1.0.2", |
||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", |
||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" |
||||
}, |
||||
"os-tmpdir": { |
||||
"version": "1.0.2", |
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", |
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" |
||||
}, |
||||
"osenv": { |
||||
"version": "0.1.5", |
||||
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", |
||||
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", |
||||
"requires": { |
||||
"os-homedir": "1.0.2", |
||||
"os-tmpdir": "1.0.2" |
||||
} |
||||
}, |
||||
"path-is-absolute": { |
||||
"version": "1.0.1", |
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", |
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" |
||||
}, |
||||
"performance-now": { |
||||
"version": "2.1.0", |
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", |
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" |
||||
}, |
||||
"process-nextick-args": { |
||||
"version": "2.0.0", |
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", |
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" |
||||
}, |
||||
"punycode": { |
||||
"version": "1.4.1", |
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", |
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" |
||||
}, |
||||
"qs": { |
||||
"version": "6.5.1", |
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", |
||||
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" |
||||
}, |
||||
"rc": { |
||||
"version": "1.2.5", |
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.5.tgz", |
||||
"integrity": "sha1-J1zWh/bjs2zHVrqibf7oCnkDAf0=", |
||||
"requires": { |
||||
"deep-extend": "0.4.2", |
||||
"ini": "1.3.5", |
||||
"minimist": "1.2.0", |
||||
"strip-json-comments": "2.0.1" |
||||
}, |
||||
"dependencies": { |
||||
"minimist": { |
||||
"version": "1.2.0", |
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", |
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" |
||||
} |
||||
} |
||||
}, |
||||
"readable-stream": { |
||||
"version": "2.3.4", |
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", |
||||
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", |
||||
"requires": { |
||||
"core-util-is": "1.0.2", |
||||
"inherits": "2.0.3", |
||||
"isarray": "1.0.0", |
||||
"process-nextick-args": "2.0.0", |
||||
"safe-buffer": "5.1.1", |
||||
"string_decoder": "1.0.3", |
||||
"util-deprecate": "1.0.2" |
||||
} |
||||
}, |
||||
"regenerator-runtime": { |
||||
"version": "0.11.1", |
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", |
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" |
||||
}, |
||||
"request": { |
||||
"version": "2.83.0", |
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", |
||||
"integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", |
||||
"requires": { |
||||
"aws-sign2": "0.7.0", |
||||
"aws4": "1.6.0", |
||||
"caseless": "0.12.0", |
||||
"combined-stream": "1.0.6", |
||||
"extend": "3.0.1", |
||||
"forever-agent": "0.6.1", |
||||
"form-data": "2.3.2", |
||||
"har-validator": "5.0.3", |
||||
"hawk": "6.0.2", |
||||
"http-signature": "1.2.0", |
||||
"is-typedarray": "1.0.0", |
||||
"isstream": "0.1.2", |
||||
"json-stringify-safe": "5.0.1", |
||||
"mime-types": "2.1.18", |
||||
"oauth-sign": "0.8.2", |
||||
"performance-now": "2.1.0", |
||||
"qs": "6.5.1", |
||||
"safe-buffer": "5.1.1", |
||||
"stringstream": "0.0.5", |
||||
"tough-cookie": "2.3.3", |
||||
"tunnel-agent": "0.6.0", |
||||
"uuid": "3.2.1" |
||||
} |
||||
}, |
||||
"rimraf": { |
||||
"version": "2.6.2", |
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", |
||||
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", |
||||
"requires": { |
||||
"glob": "7.1.2" |
||||
} |
||||
}, |
||||
"safe-buffer": { |
||||
"version": "5.1.1", |
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", |
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" |
||||
}, |
||||
"semver": { |
||||
"version": "5.5.0", |
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", |
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" |
||||
}, |
||||
"set-blocking": { |
||||
"version": "2.0.0", |
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", |
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" |
||||
}, |
||||
"signal-exit": { |
||||
"version": "3.0.2", |
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", |
||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" |
||||
}, |
||||
"sntp": { |
||||
"version": "2.1.0", |
||||
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", |
||||
"integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", |
||||
"requires": { |
||||
"hoek": "4.2.1" |
||||
} |
||||
}, |
||||
"sprintf-js": { |
||||
"version": "1.1.1", |
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", |
||||
"integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" |
||||
}, |
||||
"sshpk": { |
||||
"version": "1.13.1", |
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", |
||||
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", |
||||
"requires": { |
||||
"asn1": "0.2.3", |
||||
"assert-plus": "1.0.0", |
||||
"bcrypt-pbkdf": "1.0.1", |
||||
"dashdash": "1.14.1", |
||||
"ecc-jsbn": "0.1.1", |
||||
"getpass": "0.1.7", |
||||
"jsbn": "0.1.1", |
||||
"tweetnacl": "0.14.5" |
||||
} |
||||
}, |
||||
"string-width": { |
||||
"version": "1.0.2", |
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", |
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", |
||||
"requires": { |
||||
"code-point-at": "1.1.0", |
||||
"is-fullwidth-code-point": "1.0.0", |
||||
"strip-ansi": "3.0.1" |
||||
} |
||||
}, |
||||
"string_decoder": { |
||||
"version": "1.0.3", |
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", |
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", |
||||
"requires": { |
||||
"safe-buffer": "5.1.1" |
||||
} |
||||
}, |
||||
"stringstream": { |
||||
"version": "0.0.5", |
||||
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", |
||||
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" |
||||
}, |
||||
"strip-ansi": { |
||||
"version": "3.0.1", |
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", |
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", |
||||
"requires": { |
||||
"ansi-regex": "2.1.1" |
||||
} |
||||
}, |
||||
"strip-json-comments": { |
||||
"version": "2.0.1", |
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", |
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" |
||||
}, |
||||
"tar": { |
||||
"version": "2.2.1", |
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", |
||||
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", |
||||
"requires": { |
||||
"block-stream": "0.0.9", |
||||
"fstream": "1.0.11", |
||||
"inherits": "2.0.3" |
||||
} |
||||
}, |
||||
"tar-pack": { |
||||
"version": "3.4.1", |
||||
"resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", |
||||
"integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==", |
||||
"requires": { |
||||
"debug": "2.6.9", |
||||
"fstream": "1.0.11", |
||||
"fstream-ignore": "1.0.5", |
||||
"once": "1.4.0", |
||||
"readable-stream": "2.3.4", |
||||
"rimraf": "2.6.2", |
||||
"tar": "2.2.1", |
||||
"uid-number": "0.0.6" |
||||
} |
||||
}, |
||||
"toastr": { |
||||
"version": "2.1.4", |
||||
"resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz", |
||||
"integrity": "sha1-i0O+ZPudDEFIcURvLbjoyk6V8YE=", |
||||
"requires": { |
||||
"jquery": "3.3.1" |
||||
} |
||||
}, |
||||
"tough-cookie": { |
||||
"version": "2.3.3", |
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", |
||||
"integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", |
||||
"requires": { |
||||
"punycode": "1.4.1" |
||||
} |
||||
}, |
||||
"tunnel-agent": { |
||||
"version": "0.6.0", |
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", |
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", |
||||
"requires": { |
||||
"safe-buffer": "5.1.1" |
||||
} |
||||
}, |
||||
"tweetnacl": { |
||||
"version": "0.14.5", |
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", |
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", |
||||
"optional": true |
||||
}, |
||||
"uid-number": { |
||||
"version": "0.0.6", |
||||
"resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", |
||||
"integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" |
||||
}, |
||||
"underscore": { |
||||
"version": "1.8.3", |
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", |
||||
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" |
||||
}, |
||||
"underscore.string": { |
||||
"version": "3.3.4", |
||||
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", |
||||
"integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=", |
||||
"requires": { |
||||
"sprintf-js": "1.1.1", |
||||
"util-deprecate": "1.0.2" |
||||
} |
||||
}, |
||||
"util-deprecate": { |
||||
"version": "1.0.2", |
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", |
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" |
||||
}, |
||||
"uuid": { |
||||
"version": "3.2.1", |
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", |
||||
"integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" |
||||
}, |
||||
"verror": { |
||||
"version": "1.10.0", |
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", |
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", |
||||
"requires": { |
||||
"assert-plus": "1.0.0", |
||||
"core-util-is": "1.0.2", |
||||
"extsprintf": "1.3.0" |
||||
} |
||||
}, |
||||
"wide-align": { |
||||
"version": "1.1.2", |
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", |
||||
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", |
||||
"requires": { |
||||
"string-width": "1.0.2" |
||||
} |
||||
}, |
||||
"wrappy": { |
||||
"version": "1.0.2", |
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", |
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue