859 lines
24 KiB
JavaScript
859 lines
24 KiB
JavaScript
/* global WebSocket */
|
|
/* eslint-disable no-control-regex */
|
|
import { EventEmitter } from 'events'
|
|
import parse from './parser'
|
|
import { format } from 'util'
|
|
|
|
let translatorSocket
|
|
|
|
class FakeSocket {
|
|
constructor (server, socket) {
|
|
this.open = true
|
|
this.server = server
|
|
this.socket = socket
|
|
this.handlers = []
|
|
this.socket.addEventListener('message', (msg) => {
|
|
let tm = msg.split(' ')
|
|
if (tm[0] === 'TRANSLATOR') {
|
|
if ((tm[1] === 'CLOSE' || tm[1] === 'ERROR') && tm[2] === this.server) {
|
|
return this._socketClosed()
|
|
}
|
|
}
|
|
|
|
if (tm[0] === server) {
|
|
this._handle('message', tm.slice(1).join(' '))
|
|
}
|
|
})
|
|
this.socket.addEventListener('close', () => {
|
|
this._socketClosed()
|
|
})
|
|
}
|
|
|
|
send () {
|
|
if (!this.open) return
|
|
this.socket.send(this.server + ' ' + format.apply(Array.prototype.slice.call(arguments)))
|
|
}
|
|
|
|
addEventListener (type, fn) {
|
|
this.handlers.push({ target: type, fn })
|
|
}
|
|
|
|
_socketClosed () {
|
|
this.open = false
|
|
for (let a in this.handlers) {
|
|
let at = this.handlers[a]
|
|
if (at.target === 'close') {
|
|
at.fn.apply(this, [])
|
|
}
|
|
}
|
|
}
|
|
|
|
_handle (ev) {
|
|
if (!this.open) return
|
|
for (let a in this.handlers) {
|
|
let ap = this.handlers[a]
|
|
if (ap.target === ev) {
|
|
ap.fn.apply(this.socket, Array.prototype.slice.call(arguments, 1))
|
|
}
|
|
}
|
|
}
|
|
|
|
close () {
|
|
this.open = false
|
|
this.socket.send('TRANSLATOR DISCONNECT ' + this.server)
|
|
}
|
|
|
|
static addListenerPassthrough (fs, type) {
|
|
fs.socket.addEventListener(type, () => {
|
|
this._handle.apply(this, [type].concat(arguments))
|
|
})
|
|
}
|
|
}
|
|
|
|
async function waitForSocketOpen (s) {
|
|
let o
|
|
let c
|
|
|
|
try {
|
|
await new Promise((resolve, reject) => {
|
|
o = function () {
|
|
s.removeEventListener('close', c)
|
|
s.removeEventListener('open', o)
|
|
resolve()
|
|
}
|
|
s.addEventListener('open', o)
|
|
c = function (err) {
|
|
reject(err)
|
|
}
|
|
s.addEventListener('close', c)
|
|
})
|
|
} catch (e) {
|
|
throw e
|
|
}
|
|
return true
|
|
}
|
|
|
|
async function createSocket (address, port, ssl, useTranslator) {
|
|
// Attempt a direct ws connection
|
|
let proto = 'ws'
|
|
if (ssl) proto = 'wss'
|
|
|
|
if (!useTranslator) {
|
|
let conn = address
|
|
if (port && (port < 6000 || port > 7000)) {
|
|
conn = address + ':' + port
|
|
}
|
|
let tSock = new WebSocket(proto + '://' + conn)
|
|
try {
|
|
await waitForSocketOpen(tSock)
|
|
} catch (e) {
|
|
console.error(e)
|
|
return createSocket(address, port, ssl, true)
|
|
}
|
|
|
|
tSock.open = true
|
|
tSock.addEventListener('close', () => { tSock.open = false })
|
|
|
|
return tSock
|
|
}
|
|
|
|
if (translatorSocket) {
|
|
translatorSocket.send('TRANSLATOR SERVER ' + address + ' ' + port + (ssl ? ' SSL' : ''))
|
|
return new FakeSocket(address, translatorSocket)
|
|
}
|
|
|
|
translatorSocket = new WebSocket(`ws${window.location.protocol === 'https:' ? 's' : ''}://${window.location.host}`)
|
|
console.log('Opening translator socket')
|
|
await waitForSocketOpen(translatorSocket)
|
|
return new FakeSocket(address, translatorSocket)
|
|
}
|
|
|
|
class IRCConnectionHandler {
|
|
constructor (connection) {
|
|
this.conn = connection
|
|
}
|
|
|
|
handleUserLine (data) {
|
|
switch (data.command) {
|
|
case 'topic':
|
|
this.conn.write('%s %s', data.command.toUpperCase(), data.arguments[0], (data.message !== '' ? ' :' + data.message : ''))
|
|
break
|
|
case 'kick':
|
|
this.conn.write('%s %s :%s', data.command.toUpperCase(), data.arguments.join(' '), data.message)
|
|
break
|
|
case 'part':
|
|
this.conn.write('%s %s :%s', data.command.toUpperCase(), data.arguments[0], data.message)
|
|
break
|
|
case 'nick':
|
|
case 'whois':
|
|
case 'who':
|
|
case 'names':
|
|
case 'join':
|
|
this.conn.write('%s %s', data.command.toUpperCase(), data.arguments[0])
|
|
break
|
|
case 'quit':
|
|
this.conn.write('%s :%s', data.command.toUpperCase(), (data.message === ''
|
|
? this.conn.defaultParams.default_quit_msg : data.message))
|
|
break
|
|
case 'privmsg':
|
|
this.conn.write('PRIVMSG %s :%s', data.arguments[0], data.message)
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
messageType: 'privmsg',
|
|
to: data.arguments[0],
|
|
user: {
|
|
nickname: this.conn.config.nickname
|
|
},
|
|
message: data.message,
|
|
server: data.server
|
|
})
|
|
break
|
|
case 'notice':
|
|
this.conn.write('NOTICE %s :%s', data.arguments[0], data.message)
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
messageType: 'notice',
|
|
to: data.arguments[0],
|
|
user: {
|
|
nickname: this.conn.config.nickname
|
|
},
|
|
message: data.message,
|
|
server: data.server
|
|
})
|
|
break
|
|
case 'list':
|
|
this.conn.write(data.command.toUpperCase())
|
|
break
|
|
case 'ctcp':
|
|
let ctcpmsg = ''
|
|
|
|
if (data.arguments[1].toLowerCase() === 'ping') {
|
|
ctcpmsg = 'PING ' + Math.floor(Date.now() / 1000)
|
|
} else {
|
|
ctcpmsg = data.message
|
|
}
|
|
|
|
this.conn.write('PRIVMSG %s :\x01%s\x01', data.arguments[0], ctcpmsg)
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
messageType: 'ctcpRequest',
|
|
to: this.conn.config.nickname,
|
|
user: {
|
|
nickname: data.arguments[0]
|
|
},
|
|
message: ctcpmsg,
|
|
server: data.server
|
|
})
|
|
break
|
|
default:
|
|
this.conn.write(data.command.toUpperCase(), data.message)
|
|
}
|
|
if (data.targetType === 'channel' || data.targetType === 'message') {
|
|
this.conn.write('PRIVMSG %s :%s', data.target, data.message)
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
messageType: 'privmsg',
|
|
to: data.target,
|
|
user: {
|
|
nickname: this.conn.config.nickname
|
|
},
|
|
message: data.message,
|
|
server: data.server
|
|
})
|
|
}
|
|
}
|
|
|
|
whoisManage (whom, list) {
|
|
if (!this.conn.queue.whois) {
|
|
this.conn.queue.whois = {}
|
|
}
|
|
|
|
if (!this.conn.queue.whois[whom]) {
|
|
this.conn.queue.whois[whom] = list
|
|
} else {
|
|
for (let a in list) {
|
|
this.conn.queue.whois[whom][a] = list[a]
|
|
}
|
|
}
|
|
}
|
|
|
|
ctcpManage (data) {
|
|
let line = data.trailing.replace(/\x01/g, '').trim().split(' ') /* ignore no-control-regex */
|
|
|
|
if (!line[0]) return
|
|
line[0] = line[0].toUpperCase()
|
|
|
|
let resp = '\x01' + line[0] + ' %s\x01'
|
|
|
|
if (line[0] === 'PING' && line[1] != null && line[1] !== '') {
|
|
resp = format(resp, line.slice(1).join(' '))
|
|
} else if (line[0] === 'CLIENTINFO') {
|
|
resp = format(resp, 'CLIENTINFO PING ' + Object.keys(this.conn.extras.ctcps).join(' '))
|
|
} else if (this.conn.extras.ctcps && this.conn.extras.ctcps[line[0]] != null) {
|
|
resp = format(resp, this.conn.extras.ctcps[line[0]](data, this.conn))
|
|
} else {
|
|
resp = null
|
|
}
|
|
|
|
if (resp != null) {
|
|
this.conn.write('NOTICE %s :%s', data.user.nickname, resp)
|
|
}
|
|
|
|
return resp != null
|
|
}
|
|
|
|
handleServerLine (line) {
|
|
if (this.conn.queue['supportsmsg'] && line.command !== '005') {
|
|
delete this.conn.queue['supportsmsg']
|
|
|
|
if (this.conn.config.autojoin.length > 0) {
|
|
for (let t in this.conn.config.autojoin) {
|
|
this.conn.write('JOIN', this.conn.config.autojoin[t])
|
|
}
|
|
}
|
|
|
|
this.conn.emit('authenticated', {})
|
|
}
|
|
|
|
let serverName = this.conn.config.server
|
|
let realServerName = this.conn.data.actualServer
|
|
if (line.user.nickname === '') {
|
|
realServerName = line.user.hostname
|
|
}
|
|
|
|
let list = null
|
|
switch (line.command) {
|
|
case 'error':
|
|
this.conn.emit('connectionError', new Error('IRCError' + line.raw))
|
|
break
|
|
case 'PONG':
|
|
this.conn.ping = Date.now() - this.conn.pingSent
|
|
this.conn.pingSent = 0
|
|
this.conn.emit('ping', this.conn.ping)
|
|
break
|
|
case '001':
|
|
this.conn.data.actualServer = line.user.hostname
|
|
break
|
|
case '005':
|
|
if (!this.conn.queue['supportsmsg']) {
|
|
this.conn.queue['supportsmsg'] = true
|
|
}
|
|
|
|
this.conn.authenticated = true
|
|
|
|
let argv = line.arguments.slice(1)
|
|
for (let a in argv) {
|
|
let t = argv[a]
|
|
if (t.indexOf('=') !== -1) {
|
|
t = t.split('=')
|
|
if (t[0] === 'PREFIX') {
|
|
let d = t[1].match(/\((\w+)\)(.*)/)
|
|
let r = d[1].split('')
|
|
let aa = d[2].split('')
|
|
for (let b in r) {
|
|
this.conn.data.supportedModes[r[b]] = aa[b]
|
|
}
|
|
} else if (t[0] === 'NETWORK') {
|
|
this.conn.data.network = t[1]
|
|
} else if (t[0] === 'CHANNELLEN') {
|
|
this.conn.data.maxChannelLength = parseInt(t[1])
|
|
}
|
|
|
|
this.conn.data.serverSupports[t[0]] = t[1]
|
|
} else {
|
|
this.conn.data.serverSupports[t] = true
|
|
}
|
|
}
|
|
break
|
|
case 'JOIN':
|
|
if (line.trailing) {
|
|
this.conn.emit('fromServer', {
|
|
type: 'joinChannel',
|
|
user: line.user,
|
|
channel: line.trailing,
|
|
server: serverName
|
|
})
|
|
} else {
|
|
for (let i in line.arguments) {
|
|
this.conn.emit('fromServer', {
|
|
type: 'joinChannel',
|
|
user: line.user,
|
|
channel: line.arguments[i],
|
|
server: serverName
|
|
})
|
|
}
|
|
}
|
|
break
|
|
case 'PART':
|
|
this.conn.emit('fromServer', {
|
|
type: 'partChannel',
|
|
user: line.user,
|
|
channel: line.arguments[0],
|
|
reason: line.trailing,
|
|
server: serverName
|
|
})
|
|
break
|
|
case 'QUIT':
|
|
this.conn.emit('fromServer', {
|
|
type: 'quit',
|
|
user: line.user,
|
|
reason: line.trailing,
|
|
server: serverName
|
|
})
|
|
break
|
|
case '353':
|
|
if (!this.conn.queue['names']) {
|
|
this.conn.queue['names'] = {}
|
|
}
|
|
|
|
let splittrail = line.trailing.split(' ')
|
|
for (let a in splittrail) {
|
|
let nick = splittrail[a]
|
|
if (nick.trim() === '') continue
|
|
if (this.conn.queue['names'][line.arguments[2]]) {
|
|
this.conn.queue['names'][line.arguments[2]].push(nick)
|
|
} else {
|
|
this.conn.queue['names'][line.arguments[2]] = [nick]
|
|
}
|
|
}
|
|
|
|
break
|
|
case '366':
|
|
if (!this.conn.queue['names']) break
|
|
if (this.conn.queue['names'][line.arguments[1]]) {
|
|
this.conn.emit('fromServer', {
|
|
type: 'nicks',
|
|
channel: line.arguments[1],
|
|
nicks: this.conn.queue['names'][line.arguments[1]],
|
|
server: serverName
|
|
})
|
|
delete this.conn.queue['names'][line.arguments[1]]
|
|
}
|
|
|
|
if (Object.keys(this.conn.queue['names']).length === 0) {
|
|
delete this.conn.queue['names']
|
|
}
|
|
|
|
break
|
|
case 'PRIVMSG':
|
|
if (line.trailing.indexOf('\x01') === 0 && line.trailing.indexOf('\x01ACTION') !== 0) {
|
|
return this.ctcpManage(line)
|
|
}
|
|
|
|
if (line.user.nickname !== '') {
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
messageType: 'privmsg',
|
|
to: line.arguments[0],
|
|
user: line.user,
|
|
message: line.trailing,
|
|
server: serverName
|
|
})
|
|
} else {
|
|
this.conn.emit('fromServer', {
|
|
type: 'serverMessage',
|
|
messageType: 'privmsg',
|
|
message: line.trailing,
|
|
server: serverName,
|
|
from: realServerName
|
|
})
|
|
}
|
|
break
|
|
case 'NOTICE':
|
|
if (line.trailing.indexOf('\x01') === 0 && line.trailing.indexOf('\x01ACTION') !== 0) {
|
|
let composethis = line.trailing.replace(/\x01/g, '').trim().split(' ')
|
|
composethis[0] = composethis[0].toUpperCase()
|
|
let message = composethis.join(' ')
|
|
|
|
if (composethis[0] === 'PING') {
|
|
message = Math.floor(Date.now() / 1000) - composethis[1] + 's'
|
|
}
|
|
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
messageType: 'ctcpResponse',
|
|
to: line.arguments[0],
|
|
user: line.user,
|
|
message: message,
|
|
server: serverName
|
|
})
|
|
return
|
|
}
|
|
|
|
if (line.user.nickname !== '') {
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
messageType: 'notice',
|
|
to: line.arguments[0],
|
|
user: line.user,
|
|
message: line.trailing,
|
|
server: serverName
|
|
})
|
|
} else {
|
|
this.conn.emit('fromServer', {
|
|
type: 'serverMessage',
|
|
messageType: 'notice',
|
|
message: line.trailing,
|
|
server: serverName,
|
|
from: realServerName
|
|
})
|
|
}
|
|
break
|
|
case 'NICK':
|
|
if (line.user.nickname === this.conn.config.nickname) {
|
|
this.conn.config.nickname = line.arguments[0]
|
|
}
|
|
|
|
this.conn.emit('fromServer', {
|
|
type: 'nick', nick: line.user.nickname, newNick: line.arguments[0], server: serverName
|
|
})
|
|
break
|
|
case 'KICK':
|
|
this.conn.emit('fromServer', {
|
|
type: 'kickedFromChannel', user: line.user, channel: line.arguments[0], reason: line.trailing, kickee: line.arguments[1], server: serverName
|
|
})
|
|
break
|
|
case 'TOPIC':
|
|
this.conn.emit('fromServer', {
|
|
type: 'topic', channel: line.arguments[0], setBy: line.user.nickname, topic: line.trailing, server: serverName
|
|
})
|
|
break
|
|
case '332':
|
|
this.conn.emit('fromServer', {
|
|
type: 'topic', channel: line.arguments[1], topic: line.trailing, server: serverName
|
|
})
|
|
break
|
|
case '333':
|
|
this.conn.emit('fromServer', {
|
|
type: 'topic', channel: line.arguments[1], setBy: line.arguments[2], time: (line.arguments[3] || line.trailing), server: serverName
|
|
})
|
|
break
|
|
case '375':
|
|
case '372':
|
|
case '376':
|
|
this.conn.emit('fromServer', {
|
|
type: 'serverMessage', messageType: 'motd', message: line.trailing, server: serverName, from: realServerName
|
|
})
|
|
break
|
|
case '006':
|
|
case '007':
|
|
case '251':
|
|
case '255':
|
|
case '270':
|
|
case '290':
|
|
case '292':
|
|
case '323':
|
|
case '351':
|
|
case '381':
|
|
case '489':
|
|
this.conn.emit('fromServer', {
|
|
type: 'serverMessage', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName
|
|
})
|
|
break
|
|
case '252':
|
|
case '254':
|
|
case '396':
|
|
case '042':
|
|
this.conn.emit('fromServer', {
|
|
type: 'serverMessage', messageType: 'regular', message: line.arguments[1] + ' ' + line.trailing, server: serverName, from: realServerName
|
|
})
|
|
break
|
|
case '501':
|
|
case '401':
|
|
case '402':
|
|
case '421':
|
|
case '482':
|
|
case '331':
|
|
case '432':
|
|
this.conn.emit('fromServer', {
|
|
type: 'message',
|
|
to: null,
|
|
message: line.arguments[1] + ': ' + line.trailing,
|
|
server: serverName,
|
|
user: {
|
|
nickname: realServerName
|
|
},
|
|
messageType: 'error'
|
|
})
|
|
break
|
|
case 'MODE':
|
|
let isChannelMode = false
|
|
let method = '+'
|
|
if (line.arguments[0].indexOf('#') !== -1) {
|
|
isChannelMode = true
|
|
}
|
|
|
|
let modes = line.arguments[1]
|
|
|
|
if (!modes && line.trailing !== '') {
|
|
modes = line.trailing
|
|
}
|
|
|
|
let sender = line.user.nickname
|
|
if (sender === '') {
|
|
sender = line.user.hostname
|
|
}
|
|
|
|
method = modes.substring(0, 1)
|
|
modes = modes.substring(1).split('')
|
|
let pass = []
|
|
|
|
if (isChannelMode) {
|
|
for (let i in modes) {
|
|
let mode = modes[i]
|
|
if (this.conn.data.supportedModes[mode]) {
|
|
this.conn.emit('fromServer', {
|
|
type: 'mode' + (method === '+' ? 'Add' : 'Del'),
|
|
target: line.arguments[0],
|
|
mode: mode,
|
|
modeTarget: line.arguments[2 + parseInt(i)],
|
|
server: serverName,
|
|
user: {
|
|
nickname: sender
|
|
}
|
|
})
|
|
} else {
|
|
pass.push(mode)
|
|
}
|
|
}
|
|
} else {
|
|
pass = modes
|
|
}
|
|
|
|
if (pass.length > 0) {
|
|
this.conn.emit('fromServer', {
|
|
type: 'mode',
|
|
target: line.arguments[0],
|
|
message: method + pass.join(''),
|
|
server: serverName,
|
|
user: {
|
|
nickname: sender
|
|
}
|
|
})
|
|
}
|
|
break
|
|
case '433':
|
|
let newNick = this.conn.config.nickname + '_'
|
|
this.conn.write('NICK ' + newNick)
|
|
this.conn.config.nickname = newNick
|
|
break
|
|
case '311':
|
|
// start whois queue
|
|
list = {
|
|
nickname: line.arguments[1],
|
|
hostmask: format('%s!%s@%s', line.arguments[1], line.arguments[2], line.arguments[3]),
|
|
realname: line.trailing || ''
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '319':
|
|
// whois: channels
|
|
list = {
|
|
channels: line.trailing.split(' ')
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '378':
|
|
list = {
|
|
connectingFrom: line.trailing
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '379':
|
|
list = {
|
|
usingModes: line.trailing
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '312':
|
|
list = {
|
|
server: line.arguments[2],
|
|
serverName: line.trailing || ''
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '313':
|
|
list = {
|
|
title: line.trailing
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '330':
|
|
list = {
|
|
loggedIn: line.trailing + ' ' + line.arguments[2]
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '335':
|
|
list = {
|
|
bot: true
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '307':
|
|
list = {
|
|
registered: line.trailing
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '671':
|
|
list = {
|
|
secure: true
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '317':
|
|
list = {
|
|
signonTime: line.arguments[3],
|
|
idleSeconds: line.arguments[2]
|
|
}
|
|
this.whoisManage(line.arguments[1], list)
|
|
break
|
|
case '318':
|
|
if (!this.conn.queue.whois || !this.conn.queue.whois[line.arguments[1]]) return
|
|
|
|
this.conn.emit('fromServer', {
|
|
type: 'whoisResponse',
|
|
whois: this.conn.queue.whois[line.arguments[1]],
|
|
server: serverName,
|
|
from: realServerName
|
|
})
|
|
|
|
delete this.conn.queue.whois[line.arguments[1]]
|
|
break
|
|
case '321':
|
|
this.conn.emit('fromServer', {
|
|
type: 'listedChannel',
|
|
channel: 'Channel',
|
|
users: 'Users',
|
|
topic: 'Topic',
|
|
server: serverName,
|
|
from: realServerName
|
|
})
|
|
break
|
|
case '322':
|
|
this.conn.emit('fromServer', {
|
|
type: 'listedChannel',
|
|
channel: line.arguments[1],
|
|
users: line.arguments[2],
|
|
topic: line.trailing,
|
|
server: serverName,
|
|
from: realServerName
|
|
})
|
|
break
|
|
case 'CAP':
|
|
// might come in the future, who knows
|
|
this.conn.write('CAP END')
|
|
break
|
|
default:
|
|
let argc = line.arguments
|
|
if (argc.indexOf(this.conn.config.nickname) === 0) argc = argc.slice(1)
|
|
this.conn.emit('fromServer', {
|
|
type: 'serverMessage',
|
|
messageType: 'unknown',
|
|
message: (argc.length ? argc.join(' ') + ' :' : '') + line.trailing,
|
|
server: serverName,
|
|
from: realServerName
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
class IRCConnection extends EventEmitter {
|
|
constructor (providedInfo, defaultParams, extras) {
|
|
super()
|
|
|
|
this.defaultParams = defaultParams
|
|
this.extras = extras || { authenticationSteps: [], ctcps: {} }
|
|
this.config = {
|
|
nickname: 'teemant',
|
|
username: defaultParams.username,
|
|
realname: defaultParams.realname,
|
|
server: 'localhost',
|
|
port: 6667,
|
|
autojoin: [],
|
|
secure: defaultParams.secure_by_default,
|
|
password: '',
|
|
address: providedInfo.server,
|
|
rejectUnauthorized: defaultParams.rejectUnauthorizedCertificates
|
|
}
|
|
|
|
for (let a in providedInfo) {
|
|
this.config[a] = providedInfo[a]
|
|
}
|
|
|
|
this.socket = null
|
|
this.connected = false
|
|
this.authenticated = false
|
|
|
|
this.handler = new IRCConnectionHandler(this)
|
|
|
|
this.data = {
|
|
serverSupports: {},
|
|
network: this.config.server,
|
|
actualServer: this.config.server,
|
|
maxChannelLength: 64,
|
|
supportedModes: {}
|
|
}
|
|
this.queue = {}
|
|
this.pingSent = 0
|
|
this.ping = 0
|
|
}
|
|
|
|
connect () {
|
|
async function wrapped (argument) {
|
|
this.socket = await createSocket(this.config.server, this.config.port, this.config.secure)
|
|
|
|
this.socket.addEventListener('message', (e) => {
|
|
let line = e.data
|
|
|
|
// Handle from-server pings
|
|
if (line.indexOf('PING') === 0) {
|
|
this.write('PONG %s', line.substring(4))
|
|
return
|
|
}
|
|
|
|
this.emit('raw', line)
|
|
|
|
let parsed = parse(line)
|
|
this.emit('line', parsed)
|
|
|
|
try {
|
|
this.handler.handleServerLine(parsed)
|
|
} catch (e) {
|
|
console.error('An error occured while handling parsed server line', parsed)
|
|
console.error(e.stack)
|
|
}
|
|
})
|
|
|
|
this.socket.addEventListener('close', (data) => {
|
|
if (!this.queue['close']) {
|
|
this.emit('connectionClosed', new Error('Socket has been closed.'))
|
|
}
|
|
|
|
this.connected = false
|
|
this.authenticated = false
|
|
})
|
|
|
|
this.connected = true
|
|
this.authenticate()
|
|
}
|
|
|
|
return wrapped.call(this)
|
|
}
|
|
|
|
authenticate () {
|
|
if (this.config.password) {
|
|
this.write('PASS %s', this.config.password)
|
|
}
|
|
|
|
if (this.extras.authenticationSteps) {
|
|
for (let i in this.extras.authenticationSteps) {
|
|
let step = this.extras.authenticationSteps[i]
|
|
step.authenticate(this)
|
|
}
|
|
}
|
|
|
|
this.write('USER %s 8 * :%s', this.config.username, this.config.realname)
|
|
this.write('NICK %s', this.config.nickname)
|
|
|
|
this.bindFinal()
|
|
}
|
|
|
|
bindFinal () {
|
|
this.on('userInput', (data) => {
|
|
return this.handler.handleUserLine(data)
|
|
})
|
|
|
|
this.once('authenticated', () => this.sendPing())
|
|
}
|
|
|
|
sendPing () {
|
|
if (!this.connected) return
|
|
this.write('PING :' + this.data.actualServer)
|
|
this.pingSent = Date.now()
|
|
setTimeout(() => this.sendPing(), 5000)
|
|
}
|
|
|
|
disconnect (message) {
|
|
if (!this.connected) {
|
|
this.emit('connectionError', new Error('SocketError: Socket is already closed.'))
|
|
return
|
|
}
|
|
|
|
this.queue['close'] = true
|
|
this.write('QUIT :%s', (message != null ? message : this.defaultParams.default_quit_msg))
|
|
}
|
|
|
|
write () {
|
|
let message = format.apply(null, arguments)
|
|
if (!this.connected) {
|
|
return this.emit('connectionError', new Error('SocketError: Socket is closed.'))
|
|
}
|
|
|
|
this.socket.send(message + '\r\n')
|
|
}
|
|
}
|
|
|
|
export { IRCConnection }
|