MAJOR OVERHAUL: Working WebSocket client! No translation server, yet.
This commit is contained in:
parent
e7b9836df6
commit
394eb42fbc
3
.babelrc
3
.babelrc
@ -1,3 +1,4 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"]
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
|
4
LICENSE
4
LICENSE
@ -1,8 +1,8 @@
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
19
globals.js
Normal file
19
globals.js
Normal 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
|
4754
package-lock.json
generated
4754
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "teemantirc",
|
||||
"version": "2.0.0",
|
||||
"description": "Web-based IRC client",
|
||||
"version": "2.0.1",
|
||||
"description": "A Web-based IRC client",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
@ -22,30 +22,32 @@
|
||||
"author": "Evert",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.16.4",
|
||||
"seedrandom": "^2.4.4",
|
||||
"socket.io": "^2.2.0",
|
||||
"toml": "^2.3.5"
|
||||
"express": "^4.17.1",
|
||||
"toml": "^2.3.6",
|
||||
"ws": "^7.2.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://gitlab.icynet.eu/IcyNetwork/teemant.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/preset-env": "^7.2.3",
|
||||
"babel-loader": "^8.0.5",
|
||||
"concurrently": "^4.1.0",
|
||||
"copy-webpack-plugin": "^4.6.0",
|
||||
"css-loader": "^2.1.0",
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.8.4",
|
||||
"@babel/plugin-transform-runtime": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.4",
|
||||
"@babel/runtime": "^7.8.4",
|
||||
"babel-loader": "^8.0.6",
|
||||
"concurrently": "^4.1.2",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^2.1.1",
|
||||
"file-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"standard": "^12.0.1",
|
||||
"stylus": "^0.54.5",
|
||||
"stylus": "^0.54.7",
|
||||
"stylus-loader": "^3.0.2",
|
||||
"webpack": "^4.28.1",
|
||||
"webpack-command": "^0.4.2"
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"seedrandom": "^2.4.4"
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>TeemantIRC</title>
|
||||
|
||||
<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 rel="stylesheet" type="text/css" href="style/layout.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/theme_default.css" id="theme_stylesheet">
|
||||
|
@ -1,8 +1,134 @@
|
||||
/* global WebSocket */
|
||||
import { EventEmitter } from 'events'
|
||||
import net from 'net'
|
||||
import tls from 'tls'
|
||||
import util from 'util'
|
||||
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 {
|
||||
constructor (connection) {
|
||||
@ -122,11 +248,11 @@ class IRCConnectionHandler {
|
||||
let resp = '\x01' + line[0] + ' %s\x01'
|
||||
|
||||
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') {
|
||||
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) {
|
||||
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 {
|
||||
resp = null
|
||||
}
|
||||
@ -376,6 +502,7 @@ class IRCConnectionHandler {
|
||||
case '323':
|
||||
case '351':
|
||||
case '381':
|
||||
case '489':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'server_message', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName
|
||||
})
|
||||
@ -471,7 +598,7 @@ class IRCConnectionHandler {
|
||||
// start whois queue
|
||||
list = {
|
||||
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 || ''
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
@ -620,39 +747,19 @@ class IRCConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
connect () {
|
||||
this.socket = (this.config.secure ? tls : net).connect({
|
||||
port: this.config.port,
|
||||
host: this.config.server,
|
||||
rejectUnauthorized: this.config.rejectUnauthorized
|
||||
}, () => {
|
||||
this.connected = true
|
||||
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
|
||||
async function wrapped (argument) {
|
||||
try {
|
||||
this.socket = await _trySocket(this.config.server, this.config.port, this.config.secure)
|
||||
} catch (e) {
|
||||
this.emit('connerror', { type: 'sock_error', message: 'A socket error occured.' })
|
||||
throw e
|
||||
}
|
||||
})
|
||||
|
||||
let buffer = ''
|
||||
this.socket.on('data', (chunk) => {
|
||||
buffer += chunk
|
||||
let data = buffer.split('\r\n')
|
||||
buffer = data.pop()
|
||||
data.forEach((line) => {
|
||||
this.socket.addEventListener('message', (e) => {
|
||||
let line = e.data
|
||||
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
|
||||
}
|
||||
|
||||
@ -668,21 +775,26 @@ class IRCConnection extends EventEmitter {
|
||||
console.error(e.stack)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.socket.on('close', (data) => {
|
||||
if (!this.queue['close']) {
|
||||
this.emit('closed', { type: 'sock_closed', raw: data, message: 'Connection closed.' })
|
||||
}
|
||||
this.socket.addEventListener('close', (data) => {
|
||||
if (!this.queue['close']) {
|
||||
this.emit('closed', { type: 'sock_closed', raw: data, message: 'Connection closed.' })
|
||||
}
|
||||
|
||||
this.connected = false
|
||||
this.authenticated = false
|
||||
})
|
||||
this.connected = false
|
||||
this.authenticated = false
|
||||
})
|
||||
|
||||
this.connected = true
|
||||
this.authenticate()
|
||||
}
|
||||
|
||||
return wrapped.call(this)
|
||||
}
|
||||
|
||||
authenticate () {
|
||||
if (this.config.password) {
|
||||
this.write('PASS %s\r\n', this.config.password)
|
||||
this.write('PASS %s', this.config.password)
|
||||
}
|
||||
|
||||
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('NICK %s\r\n', this.config.nickname)
|
||||
this.write('USER %s 8 * :%s', this.config.username, this.config.realname)
|
||||
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) {
|
||||
@ -703,17 +830,17 @@ class IRCConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
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 () {
|
||||
let message = util.format.apply(null, arguments)
|
||||
let message = format.apply(null, arguments)
|
||||
if (!this.connected) {
|
||||
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 }
|
430
src/js/main.js
430
src/js/main.js
@ -1,13 +1,16 @@
|
||||
/* global irc, twemoji, io */
|
||||
/* global irc, twemoji, globalConfig */
|
||||
/* 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'
|
||||
|
||||
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 = {
|
||||
socketUp: false,
|
||||
primaryFrame: null,
|
||||
config: {
|
||||
colors: true,
|
||||
@ -25,7 +28,17 @@ window.irc = {
|
||||
serverData: {},
|
||||
serverChatQueue: {},
|
||||
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: {} }
|
||||
@ -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
|
||||
window.irc.joinChannel = (channel) => {
|
||||
let buf = irc.chat.getActiveChatBuffer()
|
||||
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
|
||||
}
|
||||
|
||||
@ -333,12 +352,12 @@ let commands = {
|
||||
execute: function (buffer, handler, command, message, listargs) {
|
||||
if (!listargs[1]) {
|
||||
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 {
|
||||
handler.commandError(buffer, listargs[0].toUpperCase() + ': Missing parameters!')
|
||||
}
|
||||
} 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'
|
||||
@ -347,7 +366,7 @@ let commands = {
|
||||
part: {
|
||||
execute: function (buffer, handler, command, message, listargs) {
|
||||
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') {
|
||||
handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.')
|
||||
} else if (listargs[1]) {
|
||||
@ -356,10 +375,10 @@ let commands = {
|
||||
if (listargs[2]) {
|
||||
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 {
|
||||
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 {
|
||||
handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.')
|
||||
}
|
||||
@ -373,7 +392,7 @@ let commands = {
|
||||
topic: {
|
||||
execute: function (buffer, handler, command, message, listargs) {
|
||||
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') {
|
||||
handler.commandError(buffer, listargs[0].toUpperCase() + ': Buffer is not a channel.')
|
||||
} else if (listargs[1]) {
|
||||
@ -382,10 +401,10 @@ let commands = {
|
||||
if (listargs[2]) {
|
||||
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 {
|
||||
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 {
|
||||
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>!')
|
||||
}
|
||||
|
||||
irc.socket.emit('userinput', {
|
||||
sendToServer(buffer.server, {
|
||||
command: 'kick',
|
||||
server: buffer.server,
|
||||
message: listargs.slice(2).join(' '),
|
||||
arguments: [buffer.name, listargs[1]]
|
||||
})
|
||||
@ -418,7 +436,7 @@ let commands = {
|
||||
|
||||
quit: {
|
||||
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.',
|
||||
aliases: ['exit']
|
||||
@ -426,7 +444,7 @@ let commands = {
|
||||
|
||||
mode: {
|
||||
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.'
|
||||
},
|
||||
@ -441,7 +459,7 @@ let commands = {
|
||||
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.',
|
||||
aliases: ['privmsg', 'q', 'query', 'say']
|
||||
@ -459,7 +477,7 @@ let commands = {
|
||||
|
||||
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.'
|
||||
},
|
||||
@ -474,9 +492,8 @@ let commands = {
|
||||
listargs[1] = buffer.name
|
||||
}
|
||||
|
||||
irc.socket.emit('userinput', {
|
||||
sendToServer(buffer.server, {
|
||||
command: 'notice',
|
||||
server: buffer.server,
|
||||
message: listargs.slice(2).join(' '),
|
||||
arguments: [listargs[1]]
|
||||
})
|
||||
@ -486,9 +503,8 @@ let commands = {
|
||||
|
||||
action: {
|
||||
execute: function (buffer, handler, command, message, listargs) {
|
||||
irc.socket.emit('userinput', {
|
||||
sendToServer(buffer.server, {
|
||||
command: 'privmsg',
|
||||
server: buffer.server,
|
||||
message: '\x01ACTION ' + message.substring(command.length + 2) + '\x01',
|
||||
arguments: [buffer.name]
|
||||
})
|
||||
@ -499,7 +515,7 @@ let commands = {
|
||||
|
||||
list: {
|
||||
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.'
|
||||
},
|
||||
@ -513,7 +529,7 @@ let commands = {
|
||||
}
|
||||
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.',
|
||||
aliases: ['nickname']
|
||||
@ -533,7 +549,7 @@ let commands = {
|
||||
} else {
|
||||
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.',
|
||||
aliases: ['nicknames']
|
||||
@ -541,9 +557,8 @@ let commands = {
|
||||
|
||||
quote: {
|
||||
execute: function (buffer, handler, command, message, listargs) {
|
||||
irc.socket.emit('userinput', {
|
||||
sendToServer(buffer.server, {
|
||||
command: listargs[1],
|
||||
server: buffer.server,
|
||||
message: listargs.slice(2).join(' '),
|
||||
arguments: listargs.splice(2)
|
||||
})
|
||||
@ -558,7 +573,7 @@ let commands = {
|
||||
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.'
|
||||
},
|
||||
@ -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']) + ' ' +
|
||||
span(data.users, ['usercount']) + ' ' +
|
||||
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 {
|
||||
constructor () {
|
||||
this.formLocked = false
|
||||
@ -1318,7 +1474,7 @@ class IRCConnector {
|
||||
case 'nick':
|
||||
case 'nickname':
|
||||
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
|
||||
case 'secure':
|
||||
@ -1438,7 +1594,7 @@ class IRCConnector {
|
||||
|
||||
return {
|
||||
nickname: nickname,
|
||||
autojoin: channel,
|
||||
channels: channel,
|
||||
server: server,
|
||||
port: port,
|
||||
password: password,
|
||||
@ -1452,7 +1608,8 @@ class IRCConnector {
|
||||
let data = this.getDataFromForm()
|
||||
if (!data) return
|
||||
|
||||
irc.socket.emit('irc_create', data)
|
||||
irc.handler.createConnection(data)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -1633,7 +1790,7 @@ class InputHandler {
|
||||
}
|
||||
}
|
||||
} 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)
|
||||
@ -1757,29 +1914,29 @@ class IRCChatWindow {
|
||||
|
||||
let prefixes = ''
|
||||
|
||||
for (let v in serverinfo.supportedModes) {
|
||||
prefixes += serverinfo.supportedModes[v]
|
||||
for (let v in serverinfo.data.supportedModes) {
|
||||
prefixes += serverinfo.data.supportedModes[v]
|
||||
}
|
||||
|
||||
irc.serverData[serverinfo.address] = {
|
||||
modeTranslation: serverinfo.supportedModes,
|
||||
irc.serverData[serverinfo.data.actualServer] = {
|
||||
modeTranslation: serverinfo.data.supportedModes,
|
||||
supportedPrefixes: prefixes,
|
||||
network: serverinfo.network,
|
||||
my_nick: serverinfo.nickname,
|
||||
max_channel_length: serverinfo.max_channel_length
|
||||
network: serverinfo.data.network,
|
||||
my_nick: serverinfo.config.nickname,
|
||||
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.render(newServer)
|
||||
this.firstServer = false
|
||||
|
||||
if (irc.serverChatQueue[serverinfo.address]) {
|
||||
for (let a in irc.serverChatQueue[serverinfo.address]) {
|
||||
let mesg = irc.serverChatQueue[serverinfo.address][a]
|
||||
if (irc.serverChatQueue[serverinfo.data.actualServer]) {
|
||||
for (let a in irc.serverChatQueue[serverinfo.data.actualServer]) {
|
||||
let mesg = irc.serverChatQueue[serverinfo.data.actualServer][a]
|
||||
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) {
|
||||
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) {
|
||||
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)
|
||||
@ -1971,12 +2128,12 @@ class IRCChatWindow {
|
||||
}
|
||||
}
|
||||
|
||||
handleMode (data) {
|
||||
handleMode (server, data) {
|
||||
let buf = null
|
||||
if (data.target === irc.serverData[data.server].my_nick) {
|
||||
buf = this.getServerChatBuffer(data.server)
|
||||
if (data.target === irc.serverData[server].my_nick) {
|
||||
buf = this.getServerChatBuffer(server)
|
||||
} else {
|
||||
buf = this.getChatBufferByServerName(data.server, data.target)
|
||||
buf = this.getChatBufferByServerName(server, data.target)
|
||||
}
|
||||
|
||||
if (!buf) return
|
||||
@ -1996,29 +2153,31 @@ class IRCChatWindow {
|
||||
}
|
||||
|
||||
joinChannels (server, channel) {
|
||||
if (channel.indexOf(',') !== -1) {
|
||||
channel = channel.trim().split(',')
|
||||
if (typeof channel !== 'object') {
|
||||
if (channel.indexOf(',') !== -1) {
|
||||
channel = channel.trim().split(',')
|
||||
|
||||
for (let t in channel) {
|
||||
let chan = channel[t]
|
||||
for (let t in channel) {
|
||||
let chan = channel[t]
|
||||
|
||||
channel[t] = chan.trim()
|
||||
channel[t] = chan.trim()
|
||||
|
||||
if (chan.indexOf('#') !== 0) {
|
||||
channel[t] = '#' + chan
|
||||
if (chan.indexOf('#') !== 0) {
|
||||
channel[t] = '#' + chan
|
||||
}
|
||||
}
|
||||
} else if (channel !== '') {
|
||||
channel = channel.trim()
|
||||
if (channel.indexOf('#') !== 0) {
|
||||
channel = '#' + channel
|
||||
}
|
||||
channel = [channel]
|
||||
} else {
|
||||
channel = []
|
||||
}
|
||||
} else if (channel !== '') {
|
||||
channel = channel.trim()
|
||||
if (channel.indexOf('#') !== 0) {
|
||||
channel = '#' + channel
|
||||
}
|
||||
channel = [channel]
|
||||
} else {
|
||||
channel = []
|
||||
}
|
||||
|
||||
irc.socket.emit('userinput', { command: 'join', server: server, message: '', arguments: channel })
|
||||
sendToServer(server, { command: 'join', message: '', arguments: channel })
|
||||
}
|
||||
|
||||
changeTitle (title) {
|
||||
@ -2104,140 +2263,13 @@ window.onload = function () {
|
||||
clientdom['smsctrig'] = clientdom.chat.querySelector('.smsc-nicklistbtn')
|
||||
clientdom.settings['open'] = irc.primaryFrame.querySelector('.open_settings')
|
||||
|
||||
irc.socket = io.connect()
|
||||
|
||||
irc.settings = new Settings()
|
||||
irc.handler = new ConnectionHandler()
|
||||
irc.auther = new IRCConnector()
|
||||
irc.chat = new IRCChatWindow()
|
||||
|
||||
irc.settings.setInitialValues()
|
||||
|
||||
irc.socket.on('defaults', function (data) {
|
||||
irc.auther.defaultTo(data)
|
||||
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']) + ' ' +
|
||||
span(data.users, ['usercount']) + ' ' +
|
||||
span(data.topic, ['topic']),
|
||||
type: 'listentry',
|
||||
from: data.from
|
||||
})
|
||||
break
|
||||
}
|
||||
})
|
||||
parseURL()
|
||||
window.onpopstate = parseURL
|
||||
}
|
||||
|
73
src/js/previews.js
Normal file
73
src/js/previews.js
Normal 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
|
||||
}
|
@ -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
|
@ -1,14 +1,8 @@
|
||||
'use strict'
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
import sockio from 'socket.io'
|
||||
import dns from 'dns'
|
||||
|
||||
import pkginfo from '../package.json'
|
||||
import irclib from './irc'
|
||||
import webirc from './webirc'
|
||||
|
||||
import config from './config'
|
||||
import config from '../globals'
|
||||
import logger from './logger'
|
||||
|
||||
const app = express()
|
||||
@ -17,21 +11,6 @@ const pubdir = path.join(__dirname, 'public')
|
||||
const port = config.server.port || 8080
|
||||
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()
|
||||
|
||||
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('/', router)
|
||||
|
||||
const io = sockio.listen(app.listen(port, function () {
|
||||
app.listen(port, function () {
|
||||
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)
|
||||
})
|
||||
|
@ -1,7 +0,0 @@
|
||||
import connector from './irc.js'
|
||||
import parser from './parser.js'
|
||||
|
||||
module.exports = {
|
||||
IRCConnection: connector,
|
||||
Parser: parser
|
||||
}
|
@ -3,42 +3,48 @@
|
||||
const webpack = require('webpack')
|
||||
const path = require('path')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const glb = require(path.join(__dirname, 'globals'))
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
main: './src/js/main'
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'app', 'public'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [ '@babel/preset-env' ]
|
||||
module.exports = (env, options) => {
|
||||
return {
|
||||
entry: {
|
||||
main: './src/js/main'
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'app', 'public'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [ '@babel/preset-env' ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
// Detect and inject
|
||||
_: 'underscore'
|
||||
}),
|
||||
new CopyWebpackPlugin([{
|
||||
from: 'src/document/index.html',
|
||||
to: '.'
|
||||
]
|
||||
},
|
||||
{
|
||||
from: 'static',
|
||||
to: 'static'
|
||||
}])
|
||||
],
|
||||
devtool: 'inline-source-map'
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
// Detect and inject
|
||||
_: 'underscore'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'src/document/index.html'
|
||||
}),
|
||||
new CopyWebpackPlugin([{
|
||||
from: 'static',
|
||||
to: 'static'
|
||||
}]),
|
||||
new webpack.DefinePlugin({
|
||||
globalConfig: JSON.stringify(glb.client)
|
||||
})
|
||||
],
|
||||
devtool: (options.mode === 'development' ? 'inline-source-map' : undefined)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user