MAJOR OVERHAUL: Working WebSocket client! No translation server, yet.

This commit is contained in:
Evert Prants 2020-02-03 21:45:12 +02:00
parent e7b9836df6
commit 394eb42fbc
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
14 changed files with 2626 additions and 3229 deletions

View File

@ -1,3 +1,4 @@
{ {
"presets": ["@babel/preset-env"] "presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime"]
} }

View File

@ -1,5 +1,5 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016 Evert Prants Copyright (c) 2016-2020 Evert Prants
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

19
globals.js Normal file
View File

@ -0,0 +1,19 @@
const fs = require('fs')
const path = require('path')
const toml = require('toml')
const filename = path.join(__dirname, 'client.config.toml')
const pkg = require(path.join(__dirname, 'package.json'))
let config
try {
config = toml.parse(fs.readFileSync(filename))
} catch (e) {
console.error(e)
process.exit(1)
}
config.client.version = pkg.version
config.client.description = pkg.description
module.exports = config

4744
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{ {
"name": "teemantirc", "name": "teemantirc",
"version": "2.0.0", "version": "2.0.1",
"description": "Web-based IRC client", "description": "A Web-based IRC client",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
@ -22,30 +22,32 @@
"author": "Evert", "author": "Evert",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"express": "^4.16.4", "express": "^4.17.1",
"seedrandom": "^2.4.4", "toml": "^2.3.6",
"socket.io": "^2.2.0", "ws": "^7.2.1"
"toml": "^2.3.5"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://gitlab.icynet.eu/IcyNetwork/teemant.git" "url": "git://gitlab.icynet.eu/IcyNetwork/teemant.git"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.2.3", "@babel/cli": "^7.8.4",
"@babel/core": "^7.2.2", "@babel/core": "^7.8.4",
"@babel/preset-env": "^7.2.3", "@babel/plugin-transform-runtime": "^7.8.3",
"babel-loader": "^8.0.5", "@babel/preset-env": "^7.8.4",
"concurrently": "^4.1.0", "@babel/runtime": "^7.8.4",
"copy-webpack-plugin": "^4.6.0", "babel-loader": "^8.0.6",
"css-loader": "^2.1.0", "concurrently": "^4.1.2",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^2.1.1",
"file-loader": "^3.0.1", "file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.5.0", "mini-css-extract-plugin": "^0.5.0",
"standard": "^12.0.1", "standard": "^12.0.1",
"stylus": "^0.54.5", "stylus": "^0.54.7",
"stylus-loader": "^3.0.2", "stylus-loader": "^3.0.2",
"webpack": "^4.28.1", "webpack": "^4.41.5",
"webpack-command": "^0.4.2" "webpack-cli": "^3.3.10",
"seedrandom": "^2.4.4"
} }
} }

View File

@ -3,14 +3,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>TeemantIRC</title> <title>TeemantIRC</title>
<script src="//twemoji.maxcdn.com/2/twemoji.min.js?11.2"></script> <script src="//twemoji.maxcdn.com/2/twemoji.min.js?11.2"></script>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript" src="main.js"></script>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css"> <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="style/layout.css"> <link rel="stylesheet" type="text/css" href="style/layout.css">
<link rel="stylesheet" type="text/css" href="style/theme_default.css" id="theme_stylesheet"> <link rel="stylesheet" type="text/css" href="style/theme_default.css" id="theme_stylesheet">

View File

@ -1,8 +1,134 @@
/* global WebSocket */
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import net from 'net'
import tls from 'tls'
import util from 'util'
import parse from './parser' 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 _waitOpen (s) {
let o
let c
try {
await new Promise((resolve, reject) => {
o = function () {
console.log('removing events', o, c)
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 _trySocket (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 {
console.log('Waiting to open')
await _waitOpen(tSock)
} catch (e) {
console.error(e)
console.error('Waiting to open failed')
return _trySocket(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 _waitOpen(translatorSocket)
return new FakeSocket(address, translatorSocket)
}
class IRCConnectionHandler { class IRCConnectionHandler {
constructor (connection) { constructor (connection) {
@ -122,11 +248,11 @@ class IRCConnectionHandler {
let resp = '\x01' + line[0] + ' %s\x01' let resp = '\x01' + line[0] + ' %s\x01'
if (line[0] === 'PING' && line[1] != null && line[1] !== '') { if (line[0] === 'PING' && line[1] != null && line[1] !== '') {
resp = util.format(resp, line.slice(1).join(' ')) resp = format(resp, line.slice(1).join(' '))
} else if (line[0] === 'CLIENTINFO') { } else if (line[0] === 'CLIENTINFO') {
resp = util.format(resp, 'CLIENTINFO PING ' + Object.keys(this.conn.extras.ctcps).join(' ')) 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) { } else if (this.conn.extras.ctcps && this.conn.extras.ctcps[line[0]] != null) {
resp = util.format(resp, this.conn.extras.ctcps[line[0]](data, this.conn)) resp = format(resp, this.conn.extras.ctcps[line[0]](data, this.conn))
} else { } else {
resp = null resp = null
} }
@ -376,6 +502,7 @@ class IRCConnectionHandler {
case '323': case '323':
case '351': case '351':
case '381': case '381':
case '489':
this.conn.emit('pass_to_client', { this.conn.emit('pass_to_client', {
type: 'server_message', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName type: 'server_message', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName
}) })
@ -471,7 +598,7 @@ class IRCConnectionHandler {
// start whois queue // start whois queue
list = { list = {
nickname: line.arguments[1], nickname: line.arguments[1],
hostmask: util.format('%s!%s@%s', line.arguments[1], line.arguments[2], line.arguments[3]), hostmask: format('%s!%s@%s', line.arguments[1], line.arguments[2], line.arguments[3]),
realname: line.trailing || '' realname: line.trailing || ''
} }
this.whoisManage(line.arguments[1], list) this.whoisManage(line.arguments[1], list)
@ -620,39 +747,19 @@ class IRCConnection extends EventEmitter {
} }
connect () { connect () {
this.socket = (this.config.secure ? tls : net).connect({ async function wrapped (argument) {
port: this.config.port, try {
host: this.config.server, this.socket = await _trySocket(this.config.server, this.config.port, this.config.secure)
rejectUnauthorized: this.config.rejectUnauthorized } catch (e) {
}, () => { this.emit('connerror', { type: 'sock_error', message: 'A socket error occured.' })
this.connected = true throw e
this.authenticate()
})
this.socket.setEncoding(this.globalConfig.encoding)
this.socket.setTimeout(this.globalConfig.timeout)
this.socket.on('error', (data) => {
this.emit('connerror', { type: 'sock_error', message: 'A socket error occured.', raw: data })
})
this.socket.on('lookup', (err, address, family, host) => {
if (err) {
this.emit('connerror', { type: 'resolve_error', message: 'Failed to resolve host.' })
} else {
this.emit('lookup', { address: address, family: address, host: host })
this.config.address = address
} }
})
let buffer = '' this.socket.addEventListener('message', (e) => {
this.socket.on('data', (chunk) => { let line = e.data
buffer += chunk
let data = buffer.split('\r\n')
buffer = data.pop()
data.forEach((line) => {
if (line.indexOf('PING') === 0) { if (line.indexOf('PING') === 0) {
this.write('PONG %s\r\n', line.substring(4)) console.log('%s server pinged us (%s)', new Date(), line)
this.write('PONG %s', line.substring(4))
return return
} }
@ -668,9 +775,8 @@ class IRCConnection extends EventEmitter {
console.error(e.stack) console.error(e.stack)
} }
}) })
})
this.socket.on('close', (data) => { this.socket.addEventListener('close', (data) => {
if (!this.queue['close']) { if (!this.queue['close']) {
this.emit('closed', { type: 'sock_closed', raw: data, message: 'Connection closed.' }) this.emit('closed', { type: 'sock_closed', raw: data, message: 'Connection closed.' })
} }
@ -678,11 +784,17 @@ class IRCConnection extends EventEmitter {
this.connected = false this.connected = false
this.authenticated = false this.authenticated = false
}) })
this.connected = true
this.authenticate()
}
return wrapped.call(this)
} }
authenticate () { authenticate () {
if (this.config.password) { if (this.config.password) {
this.write('PASS %s\r\n', this.config.password) this.write('PASS %s', this.config.password)
} }
if (this.extras.authenticationSteps) { if (this.extras.authenticationSteps) {
@ -692,8 +804,23 @@ class IRCConnection extends EventEmitter {
} }
} }
this.write('USER %s 8 * :%s\r\n', this.config.username, this.config.realname) this.write('USER %s 8 * :%s', this.config.username, this.config.realname)
this.write('NICK %s\r\n', this.config.nickname) this.write('NICK %s', this.config.nickname)
this.bindFinal()
this.sendPing()
}
bindFinal () {
this.on('userinput', (data) => {
return this.handler.handleUserLine(data)
})
}
sendPing () {
if (!this.connected) return
this.write('PING :' + this.data.actualServer)
setTimeout(() => this.sendPing(), 5000)
} }
disconnect (message) { disconnect (message) {
@ -703,17 +830,17 @@ class IRCConnection extends EventEmitter {
} }
this.queue['close'] = true this.queue['close'] = true
this.write('QUIT :%s\r\n', (message != null ? message : this.globalConfig.default_quit_msg)) this.write('QUIT :%s', (message != null ? message : this.globalConfig.default_quit_msg))
} }
write () { write () {
let message = util.format.apply(null, arguments) let message = format.apply(null, arguments)
if (!this.connected) { if (!this.connected) {
return this.emit('connerror', { type: 'sock_closed', message: 'Connection is closed.' }) return this.emit('connerror', { type: 'sock_closed', message: 'Connection is closed.' })
} }
this.socket.write(message + '\r\n') this.socket.send(message + '\r\n')
} }
} }
module.exports = IRCConnection export { IRCConnection }

View File

@ -1,13 +1,16 @@
/* global irc, twemoji, io */ /* global irc, twemoji, globalConfig */
/* eslint-disable no-useless-escape, no-control-regex */ /* eslint-disable no-useless-escape, no-control-regex */
import { el, span, sf, formatDate, removeStr, match, serialize, objectGetKey, rand, addClass, removeClass, toggleClass } from './utility'
import stylize from './colorparser'
import seedrandom from 'seedrandom' import seedrandom from 'seedrandom'
const themes = require('./theme.js') import { el, span, sf, formatDate, removeStr, match, serialize, objectGetKey, rand, addClass, removeClass, toggleClass } from './utility'
import stylize from './colorparser'
import themes from './theme'
import { IRCConnection } from './irc'
const gcfg = Object.assign({}, globalConfig)
window.irc = { window.irc = {
socketUp: false,
primaryFrame: null, primaryFrame: null,
config: { config: {
colors: true, colors: true,
@ -25,7 +28,17 @@ window.irc = {
serverData: {}, serverData: {},
serverChatQueue: {}, serverChatQueue: {},
chatType: 'simple', chatType: 'simple',
documentTitle: 'TeemantIRC' documentTitle: 'TeemantIRC',
handler: null
}
const customCTCPs = {
VERSION: function (data, connection) {
return `TeemantIRC - ${gcfg.description} ver. ${gcfg.version} - https://teemant.icynet.eu/`
},
SOURCE: function (data, connection) {
return 'https://gitlab.icynet.eu/IcyNetwork/teemant'
}
} }
const clientdom = { connector: {}, settings: {} } const clientdom = { connector: {}, settings: {} }
@ -302,12 +315,18 @@ let composer = {
} }
} }
function sendToServer (server, obj) {
let i = irc.handler.getClientByActualServer(server)
if (!i) throw new Error('Invalid connection ' + server)
i.emit('userinput', obj)
}
// onclick food // onclick food
window.irc.joinChannel = (channel) => { window.irc.joinChannel = (channel) => {
let buf = irc.chat.getActiveChatBuffer() let buf = irc.chat.getActiveChatBuffer()
if (!buf || !buf.server) return if (!buf || !buf.server) return
irc.socket.emit('userinput', { command: 'join', server: buf.server, message: '', arguments: [channel] }) sendToServer(buf.server, { command: 'join', message: '', arguments: [channel] })
return false return false
} }
@ -333,12 +352,12 @@ let commands = {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
if (!listargs[1]) { if (!listargs[1]) {
if (!buffer.alive) { if (!buffer.alive) {
irc.socket.emit('userinput', { command: 'join', server: buffer.server, message: '', arguments: [buffer.name] }) sendToServer(buffer.server, { command: 'join', message: '', arguments: [buffer.name] })
} else { } else {
handler.commandError(buffer, listargs[0].toUpperCase() + ': Missing parameters!') handler.commandError(buffer, listargs[0].toUpperCase() + ': Missing parameters!')
} }
} else { } else {
irc.socket.emit('userinput', { command: 'join', server: buffer.server, message: '', arguments: [listargs[1]] }) sendToServer(buffer.server, { command: 'join', message: '', arguments: [listargs[1]] })
} }
}, },
description: '<channel> - Join a channel' description: '<channel> - Join a channel'
@ -347,7 +366,7 @@ let commands = {
part: { part: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
if (!listargs[1] && buffer.type === 'channel') { if (!listargs[1] && buffer.type === 'channel') {
irc.socket.emit('userinput', { command: 'part', server: buffer.server, message: '', arguments: [buffer.name] }) sendToServer(buffer.server, { command: 'part', message: '', arguments: [buffer.name] })
} else if (buffer.type !== 'channel') { } else if (buffer.type !== 'channel') {
handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.') handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.')
} else if (listargs[1]) { } else if (listargs[1]) {
@ -356,10 +375,10 @@ let commands = {
if (listargs[2]) { if (listargs[2]) {
msg = listargs.slice(2).join(' ') msg = listargs.slice(2).join(' ')
} }
irc.socket.emit('userinput', { command: 'part', server: buffer.server, message: msg, arguments: [listargs[1]] }) sendToServer(buffer.server, { command: 'part', message: msg, arguments: [listargs[1]] })
} else { } else {
if (buffer.type === 'channel') { if (buffer.type === 'channel') {
irc.socket.emit('userinput', { command: 'part', server: buffer.server, message: listargs.slice(1).join(' '), arguments: [buffer.name] }) sendToServer(buffer.server, { command: 'part', message: listargs.slice(1).join(' '), arguments: [buffer.name] })
} else { } else {
handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.') handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.')
} }
@ -373,7 +392,7 @@ let commands = {
topic: { topic: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
if (!listargs[1] && buffer.type === 'channel') { if (!listargs[1] && buffer.type === 'channel') {
irc.socket.emit('userinput', { command: 'topic', server: buffer.server, message: '', arguments: [buffer.name] }) sendToServer(buffer.server, { command: 'topic', message: '', arguments: [buffer.name] })
} else if (buffer.type !== 'channel') { } else if (buffer.type !== 'channel') {
handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.') handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.')
} else if (listargs[1]) { } else if (listargs[1]) {
@ -382,10 +401,10 @@ let commands = {
if (listargs[2]) { if (listargs[2]) {
msg = listargs.slice(2).join(' ') msg = listargs.slice(2).join(' ')
} }
irc.socket.emit('userinput', { command: 'topic', server: buffer.server, message: msg, arguments: [listargs[1]] }) sendToServer(buffer.server, { command: 'topic', message: msg, arguments: [listargs[1]] })
} else { } else {
if (buffer.type === 'channel') { if (buffer.type === 'channel') {
irc.socket.emit('userinput', { command: 'topic', server: buffer.server, message: listargs.slice(1).join(' '), arguments: [buffer.name] }) sendToServer(buffer.server, { command: 'topic', message: listargs.slice(1).join(' '), arguments: [buffer.name] })
} else { } else {
handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.') handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.')
} }
@ -406,9 +425,8 @@ let commands = {
return handler.commandError(buffer, listargs[0].toUpperCase() + ': Missing parameter <user>!') return handler.commandError(buffer, listargs[0].toUpperCase() + ': Missing parameter <user>!')
} }
irc.socket.emit('userinput', { sendToServer(buffer.server, {
command: 'kick', command: 'kick',
server: buffer.server,
message: listargs.slice(2).join(' '), message: listargs.slice(2).join(' '),
arguments: [buffer.name, listargs[1]] arguments: [buffer.name, listargs[1]]
}) })
@ -418,7 +436,7 @@ let commands = {
quit: { quit: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
irc.socket.emit('userinput', { command: 'quit', server: buffer.server, message: listargs.slice(1).join(' '), arguments: [] }) sendToServer(buffer.server, { command: 'quit', message: listargs.slice(1).join(' '), arguments: [] })
}, },
description: '[<message>] - Quit the current server with message.', description: '[<message>] - Quit the current server with message.',
aliases: ['exit'] aliases: ['exit']
@ -426,7 +444,7 @@ let commands = {
mode: { mode: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
irc.socket.emit('userinput', { command: 'mode', server: buffer.server, message: listargs.slice(1).join(' '), arguments: [] }) sendToServer(buffer.server, { command: 'mode', message: listargs.slice(1).join(' '), arguments: [] })
}, },
description: '[target] [mode] - Set/remove mode of target.' description: '[target] [mode] - Set/remove mode of target.'
}, },
@ -441,7 +459,7 @@ let commands = {
listargs[1] = buffer.name listargs[1] = buffer.name
} }
irc.socket.emit('userinput', { command: 'privmsg', server: buffer.server, message: listargs.slice(2).join(' '), arguments: [listargs[1]] }) sendToServer(buffer.server, { command: 'privmsg', message: listargs.slice(2).join(' '), arguments: [listargs[1]] })
}, },
description: '<target> <message> - Sends a message to target.', description: '<target> <message> - Sends a message to target.',
aliases: ['privmsg', 'q', 'query', 'say'] aliases: ['privmsg', 'q', 'query', 'say']
@ -459,7 +477,7 @@ let commands = {
listargs[2] = listargs[2].toUpperCase() listargs[2] = listargs[2].toUpperCase()
irc.socket.emit('userinput', { command: 'ctcp', server: buffer.server, message: listargs.slice(2).join(' '), arguments: listargs.slice(1) }) sendToServer(buffer.server, { command: 'ctcp', message: listargs.slice(2).join(' '), arguments: listargs.slice(1) })
}, },
description: '<target> <type> [arguments] - Sends a CTCP request to target.' description: '<target> <type> [arguments] - Sends a CTCP request to target.'
}, },
@ -474,9 +492,8 @@ let commands = {
listargs[1] = buffer.name listargs[1] = buffer.name
} }
irc.socket.emit('userinput', { sendToServer(buffer.server, {
command: 'notice', command: 'notice',
server: buffer.server,
message: listargs.slice(2).join(' '), message: listargs.slice(2).join(' '),
arguments: [listargs[1]] arguments: [listargs[1]]
}) })
@ -486,9 +503,8 @@ let commands = {
action: { action: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
irc.socket.emit('userinput', { sendToServer(buffer.server, {
command: 'privmsg', command: 'privmsg',
server: buffer.server,
message: '\x01ACTION ' + message.substring(command.length + 2) + '\x01', message: '\x01ACTION ' + message.substring(command.length + 2) + '\x01',
arguments: [buffer.name] arguments: [buffer.name]
}) })
@ -499,7 +515,7 @@ let commands = {
list: { list: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
irc.socket.emit('userinput', { command: 'list', server: buffer.server, message: '', arguments: listargs.splice(1) }) sendToServer(buffer.server, { command: 'list', message: '', arguments: listargs.splice(1) })
}, },
description: '- List all channels on the current server.' description: '- List all channels on the current server.'
}, },
@ -513,7 +529,7 @@ let commands = {
} }
return return
} }
irc.socket.emit('userinput', { command: 'nick', server: buffer.server, message: '', arguments: listargs.splice(1) }) sendToServer(buffer.server, { command: 'nick', message: '', arguments: listargs.splice(1) })
}, },
description: '- List all channels on the current server.', description: '- List all channels on the current server.',
aliases: ['nickname'] aliases: ['nickname']
@ -533,7 +549,7 @@ let commands = {
} else { } else {
return handler.commandError(buffer, '/' + command.toUpperCase() + ': Invalid channel name!') return handler.commandError(buffer, '/' + command.toUpperCase() + ': Invalid channel name!')
} }
irc.socket.emit('userinput', { command: 'names', server: buffer.server, message: '', arguments: [channel] }) sendToServer(buffer.server, { command: 'names', message: '', arguments: [channel] })
}, },
description: '- List all users on the current channel.', description: '- List all users on the current channel.',
aliases: ['nicknames'] aliases: ['nicknames']
@ -541,9 +557,8 @@ let commands = {
quote: { quote: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
irc.socket.emit('userinput', { sendToServer(buffer.server, {
command: listargs[1], command: listargs[1],
server: buffer.server,
message: listargs.slice(2).join(' '), message: listargs.slice(2).join(' '),
arguments: listargs.splice(2) arguments: listargs.splice(2)
}) })
@ -558,7 +573,7 @@ let commands = {
return handler.commandError(buffer, listargs[0].toUpperCase() + ': Missing parameters!') return handler.commandError(buffer, listargs[0].toUpperCase() + ': Missing parameters!')
} }
irc.socket.emit('userinput', { command: 'whois', server: buffer.server, message: '', arguments: [listargs[1]] }) sendToServer(buffer.server, { command: 'whois', message: '', arguments: [listargs[1]] })
}, },
description: '<nickname> - Display information about a user.' description: '<nickname> - Display information about a user.'
}, },
@ -1269,6 +1284,147 @@ class Settings extends AppletChatBuffer {
} }
} }
class ConnectionHandler {
constructor () {
this.clients = {}
}
getClientByActualServer (actual) {
for (let i in this.clients) {
let a = this.clients[i]
if (a.data.actualServer === actual) return a
}
return null
}
createConnection (data) {
let client = new IRCConnection(data, gcfg, { ctcps: customCTCPs })
irc.auther.authMessage('Connecting to server...', false)
client.on('authenticated', () => {
irc.auther.authComplete()
this.clients[data.server] = client
this.bindAll(data.server, client)
irc.chat.newServerChatBuffer(client)
irc.chat.joinChannels(client.data.actualServer, data.channels)
})
client.connect().catch(e => {
console.error(e)
irc.auther.authMessage('Socket connection failed!', true)
})
}
bindAll (srv, client) {
let server = client.data.actualServer
client.on('closed', () => {
this.clients[srv] = null
})
client.on('pass_to_client', (data) => {
switch (data.type) {
case 'event_join_channel':
if (data.user.nickname === irc.serverData[server].my_nick) {
irc.chat.createChatBuffer(server, data.channel, 'channel', true)
}
irc.chat.handleJoin(server, data.user, data.channel)
break
case 'event_kick_channel':
irc.chat.handleLeave(server, data.kickee, data.channel, data.reason, data.user)
break
case 'event_part_channel':
irc.chat.handleLeave(server, data.user, data.channel, data.reason)
break
case 'event_quit':
irc.chat.handleQuit(server, data.user, data.reason)
break
case 'message':
if (data.to === irc.serverData[server].my_nick) {
irc.chat.messageChatBuffer(data.user.nickname, server, { message: data.message, type: data.messageType, from: data.user.nickname })
} else if (data.to == null) {
let atest = irc.chat.getActiveChatBuffer()
if (atest.server !== server) {
atest = irc.chat.getServerChatBuffer(server)
}
atest.addMessage(data.message, data.user.nickname, data.messageType)
} else {
irc.chat.messageChatBuffer(data.to, server, { message: data.message, type: data.messageType, from: data.user.nickname })
}
break
case 'channel_nicks':
irc.chat.buildNicklist(data.channel, server, data.nicks)
break
case 'channel_topic':
if (data['topic'] && data['set_by']) {
irc.chat.topicChange(data.channel, server, data.topic, data['set_by'])
} else if (data['topic']) {
irc.chat.topicChange(data.channel, server, data.topic, null)
} else if (data['set_by']) {
irc.chat.messageChatBuffer(data.channel, server, {
message: 'Topic set by ' + data.set_by + ' on ' + (new Date(data.time * 1000)),
type: 'topic',
from: null
})
}
break
case 'nick_change':
irc.chat.nickChange(server, data.nick, data.newNick)
break
case 'mode_add':
case 'mode_del':
case 'mode':
irc.chat.handleMode(server, data)
break
case 'server_message':
if (data['error']) data.messageType = 'error'
if (irc.chat.getServerChatBuffer(server) == null) {
if (!irc.serverChatQueue[server]) {
irc.serverChatQueue[server] = []
} else {
irc.serverChatQueue[server].push({ type: data.messageType, message: data.message, from: data.from || null, time: new Date() })
}
} else {
irc.chat.messageChatBuffer(server, server, { message: data.message, type: data.messageType, from: data.from || null })
}
break
case 'connect_message':
irc.auther.authMessage(data.message, data.error)
break
case 'whoisResponse':
whoisMessage(data.whois, irc.chat.getActiveChatBuffer())
break
case 'listedchan':
irc.chat.messageChatBuffer(server, server,
{
message: span(data.channel, ['channel']) + '&nbsp;' +
span(data.users, ['usercount']) + '&nbsp;' +
span(data.topic, ['topic']),
type: 'listentry',
from: data.from
})
break
}
})
client.on('closed', () => {
let serverz = irc.chat.getChatBuffersByServer(server)
for (let a in serverz) {
let serv = serverz[a]
serv.addMessage('You are no longer talking on this server.', null, 'error')
serv.setAliveStatus(false)
}
if (irc.serverData[server]) {
delete irc.serverData[server]
}
stopWarnings()
})
}
}
class IRCConnector { class IRCConnector {
constructor () { constructor () {
this.formLocked = false this.formLocked = false
@ -1318,7 +1474,7 @@ class IRCConnector {
case 'nick': case 'nick':
case 'nickname': case 'nickname':
if (validators.nickname(value)) { if (validators.nickname(value)) {
clientdom.connector.nickname.value = value.replace(/\?/g, rand(10000, 99999)) clientdom.connector.nickname.value = value.replace(/\?/g, rand(seedrandom(1), 10000, 99999))
} }
break break
case 'secure': case 'secure':
@ -1438,7 +1594,7 @@ class IRCConnector {
return { return {
nickname: nickname, nickname: nickname,
autojoin: channel, channels: channel,
server: server, server: server,
port: port, port: port,
password: password, password: password,
@ -1452,7 +1608,8 @@ class IRCConnector {
let data = this.getDataFromForm() let data = this.getDataFromForm()
if (!data) return if (!data) return
irc.socket.emit('irc_create', data) irc.handler.createConnection(data)
return true return true
} }
@ -1633,7 +1790,7 @@ class InputHandler {
} }
} }
} else { } else {
irc.socket.emit('userinput', { command: 'privmsg', server: buffer.server, message: message, arguments: [buffer.name] }) sendToServer(buffer.server, { command: 'privmsg', message: message, arguments: [buffer.name] })
} }
this.history.push(message) this.history.push(message)
@ -1757,29 +1914,29 @@ class IRCChatWindow {
let prefixes = '' let prefixes = ''
for (let v in serverinfo.supportedModes) { for (let v in serverinfo.data.supportedModes) {
prefixes += serverinfo.supportedModes[v] prefixes += serverinfo.data.supportedModes[v]
} }
irc.serverData[serverinfo.address] = { irc.serverData[serverinfo.data.actualServer] = {
modeTranslation: serverinfo.supportedModes, modeTranslation: serverinfo.data.supportedModes,
supportedPrefixes: prefixes, supportedPrefixes: prefixes,
network: serverinfo.network, network: serverinfo.data.network,
my_nick: serverinfo.nickname, my_nick: serverinfo.config.nickname,
max_channel_length: serverinfo.max_channel_length max_channel_length: serverinfo.data.max_channel_length
} }
let newServer = new ChatBuffer(serverinfo.address, serverinfo.address, serverinfo.network, 'server') let newServer = new ChatBuffer(serverinfo.data.actualServer, serverinfo.data.actualServer, serverinfo.data.network, 'server')
this.buffers.push(newServer) this.buffers.push(newServer)
this.render(newServer) this.render(newServer)
this.firstServer = false this.firstServer = false
if (irc.serverChatQueue[serverinfo.address]) { if (irc.serverChatQueue[serverinfo.data.actualServer]) {
for (let a in irc.serverChatQueue[serverinfo.address]) { for (let a in irc.serverChatQueue[serverinfo.data.actualServer]) {
let mesg = irc.serverChatQueue[serverinfo.address][a] let mesg = irc.serverChatQueue[serverinfo.data.actualServer][a]
newServer.addMessage(mesg.message, mesg.from, mesg.type, mesg.time) newServer.addMessage(mesg.message, mesg.from, mesg.type, mesg.time)
} }
delete irc.serverChatQueue[serverinfo.address] delete irc.serverChatQueue[serverinfo.data.actualServer]
} }
} }
@ -1804,9 +1961,9 @@ class IRCChatWindow {
closeChatBuffer (buffer) { closeChatBuffer (buffer) {
if (buffer.type === 'server') { if (buffer.type === 'server') {
irc.socket.emit('userinput', { command: 'quit', server: buffer.server, message: 'Server tab closed', arguments: [] }) sendToServer(buffer.server, { command: 'quit', message: 'Server tab closed', arguments: [] })
} else if (buffer.type === 'channel' && buffer.alive) { } else if (buffer.type === 'channel' && buffer.alive) {
irc.socket.emit('userinput', { command: 'part', server: buffer.server, message: 'Tab closed', arguments: [buffer.name] }) sendToServer(buffer.server, { command: 'part', message: 'Tab closed', arguments: [buffer.name] })
} }
let bufIndex = this.buffers.indexOf(buffer) let bufIndex = this.buffers.indexOf(buffer)
@ -1971,12 +2128,12 @@ class IRCChatWindow {
} }
} }
handleMode (data) { handleMode (server, data) {
let buf = null let buf = null
if (data.target === irc.serverData[data.server].my_nick) { if (data.target === irc.serverData[server].my_nick) {
buf = this.getServerChatBuffer(data.server) buf = this.getServerChatBuffer(server)
} else { } else {
buf = this.getChatBufferByServerName(data.server, data.target) buf = this.getChatBufferByServerName(server, data.target)
} }
if (!buf) return if (!buf) return
@ -1996,6 +2153,7 @@ class IRCChatWindow {
} }
joinChannels (server, channel) { joinChannels (server, channel) {
if (typeof channel !== 'object') {
if (channel.indexOf(',') !== -1) { if (channel.indexOf(',') !== -1) {
channel = channel.trim().split(',') channel = channel.trim().split(',')
@ -2017,8 +2175,9 @@ class IRCChatWindow {
} else { } else {
channel = [] channel = []
} }
}
irc.socket.emit('userinput', { command: 'join', server: server, message: '', arguments: channel }) sendToServer(server, { command: 'join', message: '', arguments: channel })
} }
changeTitle (title) { changeTitle (title) {
@ -2104,140 +2263,13 @@ window.onload = function () {
clientdom['smsctrig'] = clientdom.chat.querySelector('.smsc-nicklistbtn') clientdom['smsctrig'] = clientdom.chat.querySelector('.smsc-nicklistbtn')
clientdom.settings['open'] = irc.primaryFrame.querySelector('.open_settings') clientdom.settings['open'] = irc.primaryFrame.querySelector('.open_settings')
irc.socket = io.connect()
irc.settings = new Settings() irc.settings = new Settings()
irc.handler = new ConnectionHandler()
irc.auther = new IRCConnector() irc.auther = new IRCConnector()
irc.chat = new IRCChatWindow() irc.chat = new IRCChatWindow()
irc.settings.setInitialValues() irc.settings.setInitialValues()
irc.socket.on('defaults', function (data) {
irc.auther.defaultTo(data)
parseURL() parseURL()
window.onpopstate = parseURL window.onpopstate = parseURL
})
irc.socket.on('connect', function (data) {
irc.socketUp = true
})
irc.socket.on('disconnect', function (data) {
irc.socketUp = false
irc.chat.destroyAllChatBuffers()
clientdom.connector.frame.style.display = 'block'
})
// Does everything
irc.socket.on('act_client', function (data) {
if (data['message']) {
data.message = validators.escapeHTML(data.message)
}
if (data['reason']) {
data.reason = validators.escapeHTML(data.reason)
}
switch (data.type) {
case 'event_connect':
irc.auther.authComplete()
irc.chat.newServerChatBuffer(data)
break
case 'event_join_channel':
if (data.user.nickname === irc.serverData[data.server].my_nick) {
irc.chat.createChatBuffer(data.server, data.channel, 'channel', true)
}
irc.chat.handleJoin(data.server, data.user, data.channel)
break
case 'event_kick_channel':
irc.chat.handleLeave(data.server, data.kickee, data.channel, data.reason, data.user)
break
case 'event_part_channel':
irc.chat.handleLeave(data.server, data.user, data.channel, data.reason)
break
case 'event_quit':
irc.chat.handleQuit(data.server, data.user, data.reason)
break
case 'event_server_quit':
let serverz = irc.chat.getChatBuffersByServer(data.server)
for (let a in serverz) {
let serv = serverz[a]
serv.addMessage('You are no longer talking on this server.', null, 'error')
serv.setAliveStatus(false)
}
if (irc.serverData[data.server]) {
delete irc.serverData[data.server]
}
stopWarnings()
break
case 'message':
if (data.to === irc.serverData[data.server].my_nick) {
irc.chat.messageChatBuffer(data.user.nickname, data.server, { message: data.message, type: data.messageType, from: data.user.nickname })
} else if (data.to == null) {
let atest = irc.chat.getActiveChatBuffer()
if (atest.server !== data.server) {
atest = irc.chat.getServerChatBuffer(data.server)
}
atest.addMessage(data.message, data.user.nickname, data.messageType)
} else {
irc.chat.messageChatBuffer(data.to, data.server, { message: data.message, type: data.messageType, from: data.user.nickname })
}
break
case 'channel_nicks':
irc.chat.buildNicklist(data.channel, data.server, data.nicks)
break
case 'channel_topic':
if (data['topic'] && data['set_by']) {
irc.chat.topicChange(data.channel, data.server, data.topic, data['set_by'])
} else if (data['topic']) {
irc.chat.topicChange(data.channel, data.server, data.topic, null)
} else if (data['set_by']) {
irc.chat.messageChatBuffer(data.channel, data.server, {
message: 'Topic set by ' + data.set_by + ' on ' + (new Date(data.time * 1000)),
type: 'topic',
from: null
})
}
break
case 'nick_change':
irc.chat.nickChange(data.server, data.nick, data.newNick)
break
case 'mode_add':
case 'mode_del':
case 'mode':
irc.chat.handleMode(data)
break
case 'server_message':
if (data['error']) data.messageType = 'error'
if (irc.chat.getServerChatBuffer(data.server) == null) {
if (!irc.serverChatQueue[data.server]) {
irc.serverChatQueue[data.server] = []
} else {
irc.serverChatQueue[data.server].push({ type: data.messageType, message: data.message, from: data.from || null, time: new Date() })
}
} else {
irc.chat.messageChatBuffer(data.server, data.server, { message: data.message, type: data.messageType, from: data.from || null })
}
break
case 'connect_message':
irc.auther.authMessage(data.message, data.error)
break
case 'whoisResponse':
whoisMessage(data.whois, irc.chat.getActiveChatBuffer())
break
case 'listedchan':
irc.chat.messageChatBuffer(data.server, data.server,
{
message: span(data.channel, ['channel']) + '&nbsp;' +
span(data.users, ['usercount']) + '&nbsp;' +
span(data.topic, ['topic']),
type: 'listentry',
from: data.from
})
break
}
})
} }

73
src/js/previews.js Normal file
View File

@ -0,0 +1,73 @@
const imgs = ['.png', '.jpg', '.jpeg', '.svg']
const embeds = [
{
match: /youtu.?be\//is,
exec: function (pgurl) {
let dat = pgurl.match(/(?:be|com)\/([^?&#]+)/i)
if (!dat) {
dat = pgurl.match('[\\?&]v=([^&#]*)')
}
if (!dat) return null
return 'https://www.youtube.com/embed/' + dat[1] + '?autoplay=1'
}
}
]
export function handleUrlElement (elem) {
let cover = null
let ext = elem.href.split('.')
ext = ext[ext.length - 1]
if (ext && imgs.indexOf(ext) !== -1) {
cover = document.createElement('img')
cover.src = elem.href
}
if (!cover) {
for (let a in embeds) {
if (cover) break
let fn = embeds[a]
if (elem.href.match(fn.match)) {
let r = fn.exec(elem.href)
if (r) {
cover = document.createElement('iframe')
cover.src = r
}
}
}
}
if (cover) {
cover.className = 'preview'
let contelem = document.createElement('span')
contelem.className = 'preview-box'
let contbutton = document.createElement('button')
contbutton.className = 'preview-btn'
contbutton.innerHTML = 'Show Preview'
elem.parentNode.appendChild(contelem)
contelem.appendChild(elem)
contelem.appendChild(contbutton)
contbutton.addEventListener('click', function (e) {
e.preventDefault()
if (cover.parentNode) {
cover.remove()
contbutton.innerHTML = 'Show Preview'
} else {
contelem.appendChild(cover)
contbutton.innerHTML = 'Hide Preview'
}
}, false)
return contelem
}
return elem
}

View File

@ -1,15 +0,0 @@
import fs from 'fs'
import path from 'path'
import toml from 'toml'
const filename = path.join(__dirname, '..', 'client.config.toml')
let config
try {
config = toml.parse(fs.readFileSync(filename))
} catch (e) {
throw new Error('config.toml parse error: ', e.message)
}
module.exports = config

View File

@ -1,14 +1,8 @@
'use strict' 'use strict'
import express from 'express' import express from 'express'
import path from 'path' import path from 'path'
import sockio from 'socket.io'
import dns from 'dns'
import pkginfo from '../package.json' import config from '../globals'
import irclib from './irc'
import webirc from './webirc'
import config from './config'
import logger from './logger' import logger from './logger'
const app = express() const app = express()
@ -17,21 +11,6 @@ const pubdir = path.join(__dirname, 'public')
const port = config.server.port || 8080 const port = config.server.port || 8080
const cacheAge = 365 * 24 * 60 * 60 * 1000 const cacheAge = 365 * 24 * 60 * 60 * 1000
let runtimeStats = {
connectionsMade: 0
}
let connections = {}
let customCTCPs = {
VERSION: function (data, connection) {
return `TeemantIRC ver. ${pkginfo.version} - ${pkginfo.description} - https://teemant.icynet.eu/`
},
SOURCE: function (data, connection) {
return 'https://gitlab.icynet.eu/IcyNetwork/teemant'
}
}
process.stdin.resume() process.stdin.resume()
router.get('/', function (req, res) { router.get('/', function (req, res) {
@ -51,186 +30,6 @@ app.use('/:server', express.static(path.join(pubdir, 'icons'), { maxAge: cacheAg
app.use('/:server', express.static(path.join(pubdir, 'static'), { maxAge: cacheAge })) app.use('/:server', express.static(path.join(pubdir, 'static'), { maxAge: cacheAge }))
app.use('/', router) app.use('/', router)
app.listen(port, function () {
const io = sockio.listen(app.listen(port, function () {
logger.log(`*** Listening on http://localhost:${port}/`) logger.log(`*** Listening on http://localhost:${port}/`)
setInterval(() => {
logger.printRuntimeStats(runtimeStats, connections)
}, 3600000)
}))
function resolveHostname (ipaddr) {
return new Promise(function (resolve, reject) {
dns.reverse(ipaddr, function (err, hostnames) {
if (err != null) return reject(err)
resolve(hostnames)
})
})
}
io.sockets.on('connection', function (socket) {
let userip = socket.handshake.headers['x-real-ip'] || socket.handshake.headers['x-forwarded-for'] ||
socket.request.connection._peername.address || '127.0.0.1'
if (userip.indexOf('::ffff:') === 0) {
userip = userip.substring(7)
}
logger.debugLog(`clientID: ${socket.id} from: ${userip}`)
socket.emit('defaults', {
server: config.client.default_server,
port: config.client.default_port,
ssl: config.client.secure_by_default
})
// New object for connections
connections[socket.id] = {
host: {
ipaddr: userip,
hostname: userip
}
}
// Get the hostname of the connecting user
let hostQuery = resolveHostname(userip)
hostQuery.then((arr) => {
if (arr.length > 0) {
connections[socket.id].host.hostname = arr[0]
}
logger.debugLog(`Hostname of ${socket.id} was determined to be ${connections[socket.id].host.hostname}`)
}).catch((err) => {
logger.debugLog(`Host resolve for ${socket.id} failed:`, err.message)
})
socket.on('disconnect', function () {
for (let d in connections[socket.id]) {
if (connections[socket.id][d].ipaddr) continue
if (connections[socket.id][d].connected === true) {
connections[socket.id][d].disconnect()
}
}
delete connections[socket.id]
logger.debugLog(`clientID: ${socket.id} disconnect`)
})
socket.on('error', (e) => {
logger.errorLog(e, 'Socket error')
})
socket.on('userinput', (data) => {
let serv = connections[socket.id][data.server]
if (!serv) return
if (serv.authenticated === false) return
logger.debugLog(`[${socket.id}] ->`, data)
try {
serv.handler.handleUserLine(data)
} catch (e) {
console.error('An error occured while handling message from client', data)
console.error(e.stack)
}
})
socket.on('irc_create', function (connectiondata) {
logger.debugLog(`${socket.id} created irc connection: `, connectiondata)
socket.emit('act_client', { type: 'connect_message', message: 'Connecting to server..', error: false })
if (connectiondata.port === null || connectiondata.server === null) {
return socket.emit('act_client', {
type: 'connect_message',
server: connectiondata.server,
message: 'Failed to connect to the server!',
error: true
})
}
let newConnection = new irclib.IRCConnection(connectiondata, config.client,
{
authenticationSteps: [
new webirc.Authenticator(connections[socket.id].host)
],
ctcps: customCTCPs
})
newConnection.connect()
connections[socket.id][connectiondata.server] = newConnection
newConnection.on('authenticated', () => {
socket.emit('act_client', {
type: 'event_connect',
address: connectiondata.server,
network: newConnection.data.network,
supportedModes: newConnection.data.supportedModes,
nickname: newConnection.config.nickname,
max_channel_length: newConnection.data.max_channel_length
})
runtimeStats.connectionsMade += 1
})
if (config.server.debug) {
newConnection.on('line', function (line) {
logger.debugLog(`[${socket.id}] <-`, line)
})
newConnection.on('debug_log', function (data) {
logger.debugLog(`[${socket.id}] <-`, data)
})
}
newConnection.on('connerror', (data) => {
logger.debugLog(data)
if (newConnection.authenticated === false) {
socket.emit('act_client', {
type: 'connect_message',
server: connectiondata.server,
message: 'Failed to connect to the server!',
error: true
})
}
})
newConnection.on('pass_to_client', (data) => {
socket.emit('act_client', data)
})
newConnection.on('closed', (data) => {
logger.debugLog(data)
if (newConnection.authenticated === false) {
socket.emit('act_client', {
type: 'connect_message',
server: connectiondata.server,
message: 'Failed to connect to the server!',
error: true
})
} else {
socket.emit('act_client', {
type: 'event_server_quit',
server: connectiondata.server
})
}
})
})
})
process.on('SIGINT', () => {
logger.log('!!! Received SIGINT; Terminating all IRC connections and exiting.. !!!')
logger.printRuntimeStats(runtimeStats, connections)
for (let c in connections) {
for (let ircconn in connections[c]) {
if (connections[c][ircconn].ipaddr) continue
connections[c][ircconn].disconnect()
}
}
process.exit(0)
}) })

View File

@ -1,7 +0,0 @@
import connector from './irc.js'
import parser from './parser.js'
module.exports = {
IRCConnection: connector,
Parser: parser
}

View File

@ -3,8 +3,11 @@
const webpack = require('webpack') const webpack = require('webpack')
const path = require('path') const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const glb = require(path.join(__dirname, 'globals'))
module.exports = { module.exports = (env, options) => {
return {
entry: { entry: {
main: './src/js/main' main: './src/js/main'
}, },
@ -31,14 +34,17 @@ module.exports = {
// Detect and inject // Detect and inject
_: 'underscore' _: 'underscore'
}), }),
new HtmlWebpackPlugin({
template: 'src/document/index.html'
}),
new CopyWebpackPlugin([{ new CopyWebpackPlugin([{
from: 'src/document/index.html',
to: '.'
},
{
from: 'static', from: 'static',
to: 'static' to: 'static'
}]) }]),
new webpack.DefinePlugin({
globalConfig: JSON.stringify(glb.client)
})
], ],
devtool: 'inline-source-map' devtool: (options.mode === 'development' ? 'inline-source-map' : undefined)
}
} }