it doesnt build yet
This commit is contained in:
parent
73610f6c18
commit
6e39b056c3
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@ -0,0 +1,14 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*.js]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
/node_modules/
|
||||
/build/
|
||||
/dist/
|
||||
/lib/
|
||||
/client.config.toml
|
||||
/webirc.data.json
|
||||
|
@ -1,15 +1,15 @@
|
||||
# TeemantIRC
|
||||
LunaSquee's third attempt at creating a working IRC client..
|
||||
|
||||
###Running the development version
|
||||
### Running the development version
|
||||
This application requires [node.js](https://nodejs.org/) to be installed.
|
||||
|
||||
1. Install the dependencies `npm install`
|
||||
2. Copy the configuration `cp client.config.example.toml client.config.toml`
|
||||
3. Build the project using `gulp`
|
||||
3. Build the project using `npm run build`
|
||||
4. Run the server `./teemant.js`
|
||||
|
||||
The client will be accessible at http://localhost:8080/
|
||||
The client will be accessible at http://localhost:9000/
|
||||
|
||||
### WebIRC
|
||||
|
||||
|
279
gulpfile.js
279
gulpfile.js
@ -1,279 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const gulp = require('gulp');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const gutil = require('gulp-util');
|
||||
const gdata = require('gulp-data');
|
||||
const del = require('del');
|
||||
const gulpif = require('gulp-if');
|
||||
const plumber = require('gulp-plumber');
|
||||
const mergeStream = require('merge-stream');
|
||||
const lodash = require('lodash');
|
||||
const Sequence = require('run-sequence');
|
||||
const watch = require('gulp-watch');
|
||||
const realFavicon = require('gulp-real-favicon');
|
||||
const debug = require('gulp-debug');
|
||||
const filter = require('gulp-filter');
|
||||
const fs = require('fs');
|
||||
|
||||
const eslint = require('gulp-eslint');
|
||||
const webpack = require('webpack');
|
||||
const webpackConfig = require(path.join(__dirname, '/webpack.config'));
|
||||
|
||||
const stylus = require('gulp-stylus');
|
||||
const nib = require('nib');
|
||||
const csso = require('gulp-csso');
|
||||
|
||||
const htmlmin = require('gulp-htmlmin');
|
||||
|
||||
const sequence = Sequence.use(gulp);
|
||||
|
||||
let sources = {
|
||||
// script: ['main.js', 'admin.coffee', 'popout.js', 'livestream.js'],
|
||||
style: ['layout.styl', 'theme_default.styl', 'theme_night.styl'],
|
||||
document: ['index.html']
|
||||
};
|
||||
let lintES = ['src/script/**/*.js', 'server/**/*.js', 'gulpfile.js', 'teemant.js', 'webpack.config.js'];
|
||||
|
||||
let inProduction = process.env.NODE_ENV === 'production' || process.argv.indexOf('-p') !== -1;
|
||||
|
||||
let eslintOpts = {
|
||||
rules: {
|
||||
quotes: [1, 'single'],
|
||||
semi: [1, 'always']
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
impliedStrict: true,
|
||||
globalReturn: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let stylusOpts = {
|
||||
use: nib(),
|
||||
compress: false
|
||||
};
|
||||
|
||||
let cssoOpts = {
|
||||
restructure: true
|
||||
};
|
||||
|
||||
let htmlminOpts = {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
removeAttributeQuotes: true,
|
||||
collapseBooleanAttributes: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true
|
||||
};
|
||||
|
||||
let watchOpts = {
|
||||
readDelay: 500,
|
||||
verbose: true
|
||||
};
|
||||
|
||||
// File where the favicon markups are stored
|
||||
let faviconDataFile = 'build/icons/favicon-data.json';
|
||||
|
||||
if (inProduction) {
|
||||
webpackConfig.plugins.push(new webpack.optimize.DedupePlugin());
|
||||
webpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin(false));
|
||||
webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false,
|
||||
screw_ie8: true
|
||||
},
|
||||
comments: false,
|
||||
mangle: {
|
||||
screw_ie8: true
|
||||
},
|
||||
screw_ie8: true,
|
||||
sourceMap: false
|
||||
}));
|
||||
}
|
||||
|
||||
let wpCompiler = webpack(lodash.assign({}, webpackConfig, {
|
||||
cache: {},
|
||||
devtool: inProduction ? null : 'inline-source-map',
|
||||
debug: !inProduction
|
||||
}));
|
||||
|
||||
function webpackTask (callback) {
|
||||
// run webpack
|
||||
wpCompiler.run(function (err, stats) {
|
||||
if (err) throw new gutil.PluginError('webpack', err);
|
||||
gutil.log('[script]', stats.toString({
|
||||
colors: true,
|
||||
hash: false,
|
||||
version: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}));
|
||||
if (typeof callback === 'function') callback();
|
||||
});
|
||||
}
|
||||
|
||||
function styleTask () {
|
||||
return gulp.src(sources.style.map(function (f) { return 'src/style/' + f; }))
|
||||
.pipe(plumber())
|
||||
.pipe(gulpif(!inProduction, sourcemaps.init()))
|
||||
.pipe(stylus(stylusOpts))
|
||||
.pipe(gulpif(inProduction, csso(cssoOpts)))
|
||||
.pipe(gulpif(!inProduction, sourcemaps.write()))
|
||||
.pipe(debug({title: '[style]'}))
|
||||
.pipe(gulp.dest('build/style/'));
|
||||
}
|
||||
|
||||
function documentTask (p) {
|
||||
let data = {
|
||||
config: require('./server/config'),
|
||||
env: process.env.NODE_ENV || 'development',
|
||||
};
|
||||
return p
|
||||
.pipe(plumber())
|
||||
.pipe(gdata(function () { return data; }))
|
||||
.pipe(realFavicon.injectFaviconMarkups(JSON.parse(fs.readFileSync(faviconDataFile)).favicon.html_code))
|
||||
.pipe(gulpif(inProduction, htmlmin(htmlminOpts)))
|
||||
.pipe(gulp.dest('build/document/'))
|
||||
.pipe(debug({title: '[document]'}));
|
||||
}
|
||||
|
||||
// Cleanup tasks
|
||||
gulp.task('clean', () => del('build'));
|
||||
gulp.task('clean:quick', ['clean:script', 'clean:style'], (done) => {
|
||||
done();
|
||||
});
|
||||
gulp.task('clean:script', () => {
|
||||
return del('build/script');
|
||||
});
|
||||
gulp.task('clean:style', () => {
|
||||
return del('build/style');
|
||||
});
|
||||
gulp.task('clean:icons', () => {
|
||||
return del('build/icons');
|
||||
});
|
||||
gulp.task('clean:document', () => {
|
||||
return del('build/document');
|
||||
});
|
||||
|
||||
// Main tasks
|
||||
gulp.task('script', ['clean:script'], webpackTask);
|
||||
gulp.task('watch:script', () => {
|
||||
return watch(['src/script/**/*.js'], watchOpts, webpackTask);
|
||||
});
|
||||
|
||||
gulp.task('style', ['clean:style'], styleTask);
|
||||
gulp.task('watch:style', () => {
|
||||
return watch('src/style/**/*.styl', watchOpts, styleTask);
|
||||
});
|
||||
|
||||
// Generate the icons. This task takes a few seconds to complete.
|
||||
// You should run it at least once to create the icons. Then,
|
||||
// you should run it whenever RealFaviconGenerator updates its
|
||||
// package (see the update-favicon task below).
|
||||
gulp.task('generate-favicon', ['clean:icons'], (done) => {
|
||||
realFavicon.generateFavicon({
|
||||
masterPicture: 'static/image/diamond.svg',
|
||||
dest: 'build/icons/',
|
||||
iconsPath: '/',
|
||||
design: {
|
||||
ios: {
|
||||
masterPicture: 'static/image/diamond.svg',
|
||||
pictureAspect: 'backgroundAndMargin',
|
||||
backgroundColor: '#00c7e0',
|
||||
margin: '0%',
|
||||
appName: 'Teemant'
|
||||
},
|
||||
desktopBrowser: {},
|
||||
windows: {
|
||||
pictureAspect: 'noChange',
|
||||
backgroundColor: '#00c7e0',
|
||||
onConflict: 'override',
|
||||
appName: 'Teemant'
|
||||
},
|
||||
androidChrome: {
|
||||
masterPicture: 'static/image/diamond.svg',
|
||||
pictureAspect: 'noChange',
|
||||
themeColor: '#00c7e0',
|
||||
manifest: {
|
||||
name: 'Teemant',
|
||||
display: 'standalone',
|
||||
orientation: 'notSet',
|
||||
onConflict: 'override',
|
||||
declared: true
|
||||
}
|
||||
},
|
||||
safariPinnedTab: {
|
||||
pictureAspect: 'silhouette',
|
||||
themeColor: '#00c7e0'
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
scalingAlgorithm: 'Lanczos',
|
||||
errorOnImageTooSmall: false
|
||||
},
|
||||
versioning: true,
|
||||
markupFile: faviconDataFile
|
||||
}, done);
|
||||
});
|
||||
|
||||
gulp.task('update-favicon', (done) => {
|
||||
let currentVersion;
|
||||
try {
|
||||
currentVersion = JSON.parse(fs.readFileSync(faviconDataFile)).version;
|
||||
} catch (e) {}
|
||||
|
||||
if (currentVersion) {
|
||||
realFavicon.checkForUpdates(currentVersion, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
sequence('generate-favicon', done);
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('document', ['clean:document', 'update-favicon'], () => {
|
||||
return documentTask(gulp.src(sources.document.map(function (f) { return 'src/document/' + f; })));
|
||||
});
|
||||
|
||||
gulp.task('watch:document', () => {
|
||||
return documentTask(
|
||||
watch(['src/document/**/*.html'], watchOpts)
|
||||
.pipe(filter(sources.document.map(function (f) { return 'src/document/' + f; })))
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('lint', () => {
|
||||
return mergeStream(
|
||||
gulp.src(lintES).pipe(eslint(eslintOpts))
|
||||
.pipe(eslint.format())
|
||||
);
|
||||
});
|
||||
gulp.task('watch:lint', () => {
|
||||
return mergeStream(
|
||||
watch(lintES, watchOpts, function (file) {
|
||||
gulp.src(file.path).pipe(eslint(eslintOpts))
|
||||
.pipe(eslint.format());
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Default task
|
||||
gulp.task('default', (done) => {
|
||||
sequence('script', 'style', 'lint', 'document', done);
|
||||
});
|
||||
|
||||
// Watch task
|
||||
gulp.task('watch', (done) => {
|
||||
sequence('default', ['watch:lint', 'watch:script', 'watch:style', 'watch:document'], done);
|
||||
});
|
8290
package-lock.json
generated
Normal file
8290
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
50
package.json
50
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "teemantirc",
|
||||
"version": "1.1.0",
|
||||
"version": "2.0.0",
|
||||
"description": "Web-based IRC client",
|
||||
"main": "teemant.js",
|
||||
"scripts": {
|
||||
@ -12,38 +12,26 @@
|
||||
"author": "Evert",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.14.0",
|
||||
"socketio": "^1.0.0",
|
||||
"toml": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.1",
|
||||
"lodash": "^4.17.2",
|
||||
"webpack": "^1.13.3",
|
||||
"del": "^2.2.2",
|
||||
"gulp-csso": "^2.0.0",
|
||||
"gulp-data": "^1.2.1",
|
||||
"gulp-debug": "^3.0.0",
|
||||
"gulp-eslint": "^3.0.1",
|
||||
"gulp-filter": "^4.0.0",
|
||||
"gulp-htmlmin": "^3.0.0",
|
||||
"gulp-if": "^2.0.2",
|
||||
"gulp-plumber": "^1.1.0",
|
||||
"gulp-pug": "^3.2.0",
|
||||
"gulp-real-favicon": "^0.2.1",
|
||||
"gulp-sourcemaps": "^1.9.1",
|
||||
"gulp-standard": "^8.0.2",
|
||||
"gulp-stylus": "^2.6.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-watch": "^4.3.11",
|
||||
"gulp-watch-pug": "^1.0.0",
|
||||
"lazypipe": "^1.0.1",
|
||||
"merge-stream": "^1.0.1",
|
||||
"nib": "^1.1.2",
|
||||
"run-sequence": "^1.2.2"
|
||||
"express": "^4.16.4",
|
||||
"socket.io": "^2.2.0",
|
||||
"toml": "^2.3.5"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/DiamondtechDev/TeemantIRC.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",
|
||||
"css-loader": "^2.1.0",
|
||||
"file-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"standard": "^12.0.1",
|
||||
"stylus-loader": "^3.0.2",
|
||||
"webpack": "^4.28.1",
|
||||
"webpack-command": "^0.4.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
let fs = require('fs');
|
||||
let toml = require('toml');
|
||||
let filename = __dirname+'/../client.config.toml';
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import toml from 'toml'
|
||||
|
||||
let config;
|
||||
const filename = path.join(__dirname, '..', 'client.config.toml')
|
||||
|
||||
let config
|
||||
|
||||
try {
|
||||
config = toml.parse(fs.readFileSync(filename));
|
||||
config = toml.parse(fs.readFileSync(filename))
|
||||
} catch (e) {
|
||||
throw 'config.toml parse error: ' + e;
|
||||
console.error(e.stack);
|
||||
throw new Error('config.toml parse error: ', e.message)
|
||||
}
|
||||
|
||||
module.exports = config;
|
||||
module.exports = config
|
||||
|
7
server/irc/index.js
Normal file
7
server/irc/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import connector from './irc.js'
|
||||
import parser from './parser.js'
|
||||
|
||||
module.exports = {
|
||||
IRCConnection: connector,
|
||||
Parser: parser
|
||||
}
|
713
server/irc/irc.js
Normal file
713
server/irc/irc.js
Normal file
@ -0,0 +1,713 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import net from 'net'
|
||||
import tls from 'tls'
|
||||
import util from 'util'
|
||||
import parse from './parser'
|
||||
|
||||
class IRCConnectionHandler {
|
||||
constructor (connection) {
|
||||
this.conn = connection
|
||||
}
|
||||
|
||||
handleUserLine (data) {
|
||||
switch (data.command) {
|
||||
case 'topic':
|
||||
this.conn.write('%s %s', data.command.toUpperCase(), data.arguments[0], (data.message !== '' ? ' :' + data.message : ''))
|
||||
break
|
||||
case 'kick':
|
||||
this.conn.write('%s %s :%s', data.command.toUpperCase(), data.arguments.join(' '), data.message)
|
||||
break
|
||||
case 'part':
|
||||
this.conn.write('%s %s :%s', data.command.toUpperCase(), data.arguments[0], data.message)
|
||||
break
|
||||
case 'nick':
|
||||
case 'whois':
|
||||
case 'who':
|
||||
case 'names':
|
||||
case 'join':
|
||||
this.conn.write('%s %s', data.command.toUpperCase(), data.arguments[0])
|
||||
break
|
||||
case 'quit':
|
||||
this.conn.write('%s :%s', data.command.toUpperCase(), (data.message === ''
|
||||
? this.conn.globalConfig.default_quit_msg : data.message))
|
||||
break
|
||||
case 'privmsg':
|
||||
this.conn.write('PRIVMSG %s :%s', data.arguments[0], data.message)
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
messageType: 'privmsg',
|
||||
to: data.arguments[0],
|
||||
user: {
|
||||
nickname: this.conn.config.nickname
|
||||
},
|
||||
message: data.message,
|
||||
server: data.server
|
||||
})
|
||||
break
|
||||
case 'notice':
|
||||
this.conn.write('NOTICE %s :%s', data.arguments[0], data.message)
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
messageType: 'notice',
|
||||
to: data.arguments[0],
|
||||
user: {
|
||||
nickname: this.conn.config.nickname
|
||||
},
|
||||
message: data.message,
|
||||
server: data.server
|
||||
})
|
||||
break
|
||||
case 'list':
|
||||
this.conn.write(data.command.toUpperCase())
|
||||
break
|
||||
case 'ctcp':
|
||||
let ctcpmsg = ''
|
||||
|
||||
if (data.arguments[1].toLowerCase() === 'ping') {
|
||||
ctcpmsg = 'PING ' + Math.floor(Date.now() / 1000)
|
||||
} else {
|
||||
ctcpmsg = data.message
|
||||
}
|
||||
|
||||
this.conn.write('PRIVMSG %s :\x01%s\x01', data.arguments[0], ctcpmsg)
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
messageType: 'ctcp_request',
|
||||
to: this.conn.config.nickname,
|
||||
user: {
|
||||
nickname: data.arguments[0]
|
||||
},
|
||||
message: ctcpmsg,
|
||||
server: data.server
|
||||
})
|
||||
break
|
||||
default:
|
||||
this.conn.write(data.command.toUpperCase(), data.message)
|
||||
}
|
||||
if (data.targetType === 'channel' || data.targetType === 'message') {
|
||||
this.conn.write('PRIVMSG %s :%s', data.target, data.message)
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
messageType: 'privmsg',
|
||||
to: data.target,
|
||||
user: {
|
||||
nickname: this.conn.config.nickname
|
||||
},
|
||||
message: data.message,
|
||||
server: data.server
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
whoisManage (whom, list) {
|
||||
if (!this.conn.queue.whois) {
|
||||
this.conn.queue.whois = {}
|
||||
}
|
||||
|
||||
if (!this.conn.queue.whois[whom]) {
|
||||
this.conn.queue.whois[whom] = list
|
||||
} else {
|
||||
for (let a in list) {
|
||||
this.conn.queue.whois[whom][a] = list[a]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctcpManage (data) {
|
||||
let line = data.trailing.replace(/\x01/g, '').trim().split(' ') /* ignore no-control-regex */
|
||||
|
||||
if (!line[0]) return
|
||||
line[0] = line[0].toUpperCase()
|
||||
|
||||
let resp = '\x01' + line[0] + ' %s\x01'
|
||||
|
||||
if (line[0] === 'PING' && line[1] != null && line[1] !== '') {
|
||||
resp = util.format(resp, line.slice(1).join(' '))
|
||||
} else if (line[0] === 'CLIENTINFO') {
|
||||
resp = util.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))
|
||||
} else {
|
||||
resp = null
|
||||
}
|
||||
|
||||
if (resp != null) {
|
||||
this.conn.write('NOTICE %s :%s', data.user.nickname, resp)
|
||||
}
|
||||
|
||||
return resp != null
|
||||
}
|
||||
|
||||
handleServerLine (line) {
|
||||
if (this.conn.queue['supportsmsg'] && line.command !== '005') {
|
||||
delete this.conn.queue['supportsmsg']
|
||||
|
||||
if (this.conn.config.autojoin.length > 0) {
|
||||
for (let t in this.conn.config.autojoin) {
|
||||
this.conn.write('JOIN', this.conn.config.autojoin[t])
|
||||
}
|
||||
}
|
||||
|
||||
this.conn.emit('authenticated', {})
|
||||
}
|
||||
|
||||
let serverName = this.conn.config.server
|
||||
let realServerName = this.conn.data.actualServer
|
||||
if (line.user.nickname === '') {
|
||||
realServerName = line.user.hostname
|
||||
}
|
||||
|
||||
let list = null
|
||||
switch (line.command) {
|
||||
case 'error':
|
||||
this.conn.emit('connerror', { type: 'irc_error', raw: line.raw })
|
||||
break
|
||||
case '001':
|
||||
this.conn.data.actualServer = line.user.hostname
|
||||
break
|
||||
case '005':
|
||||
if (!this.conn.queue['supportsmsg']) {
|
||||
this.conn.queue['supportsmsg'] = true
|
||||
}
|
||||
|
||||
this.conn.authenticated = true
|
||||
|
||||
let argv = line.arguments.slice(1)
|
||||
for (let a in argv) {
|
||||
let t = argv[a]
|
||||
if (t.indexOf('=') !== -1) {
|
||||
t = t.split('=')
|
||||
if (t[0] === 'PREFIX') {
|
||||
let d = t[1].match(/\((\w+)\)(.*)/)
|
||||
let r = d[1].split('')
|
||||
let aa = d[2].split('')
|
||||
for (let b in r) {
|
||||
this.conn.data.supportedModes[r[b]] = aa[b]
|
||||
}
|
||||
} else if (t[0] === 'NETWORK') {
|
||||
this.conn.data.network = t[1]
|
||||
} else if (t[0] === 'CHANNELLEN') {
|
||||
this.conn.data.maxChannelLength = parseInt(t[1])
|
||||
}
|
||||
|
||||
this.conn.data.serverSupports[t[0]] = t[1]
|
||||
} else {
|
||||
this.conn.data.serverSupports[t] = true
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'JOIN':
|
||||
if (line.trailing) {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'event_join_channel',
|
||||
user: line.user,
|
||||
channel: line.trailing,
|
||||
server: serverName
|
||||
})
|
||||
} else {
|
||||
for (let i in line.arguments) {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'event_join_channel',
|
||||
user: line.user,
|
||||
channel: line.arguments[i],
|
||||
server: serverName
|
||||
})
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'PART':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'event_part_channel',
|
||||
user: line.user,
|
||||
channel: line.arguments[0],
|
||||
reason: line.trailing,
|
||||
server: serverName
|
||||
})
|
||||
break
|
||||
case 'QUIT':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'event_quit',
|
||||
user: line.user,
|
||||
reason: line.trailing,
|
||||
server: serverName
|
||||
})
|
||||
break
|
||||
case '353':
|
||||
if (!this.conn.queue['names']) {
|
||||
this.conn.queue['names'] = {}
|
||||
}
|
||||
|
||||
let splittrail = line.trailing.split(' ')
|
||||
for (let a in splittrail) {
|
||||
let nick = splittrail[a]
|
||||
if (nick.trim() === '') continue
|
||||
if (this.conn.queue['names'][line.arguments[2]]) {
|
||||
this.conn.queue['names'][line.arguments[2]].push(nick)
|
||||
} else {
|
||||
this.conn.queue['names'][line.arguments[2]] = [nick]
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
case '366':
|
||||
if (!this.conn.queue['names']) break
|
||||
if (this.conn.queue['names'][line.arguments[1]]) {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'channel_nicks',
|
||||
channel: line.arguments[1],
|
||||
nicks: this.conn.queue['names'][line.arguments[1]],
|
||||
server: serverName
|
||||
})
|
||||
delete this.conn.queue['names'][line.arguments[1]]
|
||||
}
|
||||
|
||||
if (Object.keys(this.conn.queue['names']).length === 0) {
|
||||
delete this.conn.queue['names']
|
||||
}
|
||||
|
||||
break
|
||||
case 'PRIVMSG':
|
||||
if (line.trailing.indexOf('\x01') === 0 && line.trailing.indexOf('\x01ACTION') !== 0) {
|
||||
return this.ctcpManage(line)
|
||||
}
|
||||
|
||||
if (line.user.nickname !== '') {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
messageType: 'privmsg',
|
||||
to: line.arguments[0],
|
||||
user: line.user,
|
||||
message: line.trailing,
|
||||
server: serverName
|
||||
})
|
||||
} else {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'server_message',
|
||||
messageType: 'privmsg',
|
||||
message: line.trailing,
|
||||
server: serverName,
|
||||
from: realServerName
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'NOTICE':
|
||||
if (line.trailing.indexOf('\x01') === 0 && line.trailing.indexOf('\x01ACTION') !== 0) {
|
||||
let composethis = line.trailing.replace(/\x01/g, '').trim().split(' ')
|
||||
composethis[0] = composethis[0].toUpperCase()
|
||||
let message = composethis.join(' ')
|
||||
|
||||
if (composethis[0] === 'PING') {
|
||||
message = Math.floor(Date.now() / 1000) - composethis[1] + 's'
|
||||
}
|
||||
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
messageType: 'ctcp_response',
|
||||
to: line.arguments[0],
|
||||
user: line.user,
|
||||
message: message,
|
||||
server: serverName
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (line.user.nickname !== '') {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
messageType: 'notice',
|
||||
to: line.arguments[0],
|
||||
user: line.user,
|
||||
message: line.trailing,
|
||||
server: serverName
|
||||
})
|
||||
} else {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'server_message',
|
||||
messageType: 'notice',
|
||||
message: line.trailing,
|
||||
server: serverName,
|
||||
from: realServerName
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'NICK':
|
||||
if (line.user.nickname === this.conn.config.nickname) {
|
||||
this.conn.config.nickname = line.arguments[0]
|
||||
}
|
||||
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'nick_change', nick: line.user.nickname, newNick: line.arguments[0], server: serverName
|
||||
})
|
||||
break
|
||||
case 'KICK':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'event_kick_channel', user: line.user, channel: line.arguments[0], reason: line.trailing, kickee: line.arguments[1], server: serverName
|
||||
})
|
||||
break
|
||||
case 'TOPIC':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'channel_topic', channel: line.arguments[0], set_by: line.user.nickname, topic: line.trailing, server: serverName
|
||||
})
|
||||
break
|
||||
case '332':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'channel_topic', channel: line.arguments[1], topic: line.trailing, server: serverName
|
||||
})
|
||||
break
|
||||
case '333':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'channel_topic', channel: line.arguments[1], set_by: line.arguments[2], time: line.arguments[3], server: serverName
|
||||
})
|
||||
break
|
||||
case '375':
|
||||
case '372':
|
||||
case '376':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'server_message', messageType: 'motd', message: line.trailing, server: serverName, from: realServerName
|
||||
})
|
||||
break
|
||||
case '006':
|
||||
case '007':
|
||||
case '251':
|
||||
case '255':
|
||||
case '270':
|
||||
case '290':
|
||||
case '292':
|
||||
case '323':
|
||||
case '351':
|
||||
case '381':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'server_message', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName
|
||||
})
|
||||
break
|
||||
case '252':
|
||||
case '254':
|
||||
case '396':
|
||||
case '042':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'server_message', messageType: 'regular', message: line.arguments[1] + ' ' + line.trailing, server: serverName, from: realServerName
|
||||
})
|
||||
break
|
||||
case '501':
|
||||
case '401':
|
||||
case '402':
|
||||
case '421':
|
||||
case '482':
|
||||
case '331':
|
||||
case '432':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'message',
|
||||
to: null,
|
||||
message: line.arguments[1] + ': ' + line.trailing,
|
||||
server: serverName,
|
||||
user: {
|
||||
nickname: realServerName
|
||||
},
|
||||
messageType: 'error'
|
||||
})
|
||||
break
|
||||
case 'MODE':
|
||||
let isChannelMode = false
|
||||
let method = '+'
|
||||
if (line.arguments[0].indexOf('#') !== -1) {
|
||||
isChannelMode = true
|
||||
}
|
||||
|
||||
let modes = line.arguments[1]
|
||||
|
||||
if (!modes && line.trailing !== '') {
|
||||
modes = line.trailing
|
||||
}
|
||||
|
||||
let sender = line.user.nickname
|
||||
if (sender === '') {
|
||||
sender = line.user.hostname
|
||||
}
|
||||
|
||||
method = modes.substring(0, 1)
|
||||
modes = modes.substring(1).split('')
|
||||
let pass = []
|
||||
|
||||
if (isChannelMode) {
|
||||
for (let i in modes) {
|
||||
let mode = modes[i]
|
||||
if (this.conn.data.supportedModes[mode]) {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'mode_' + (method === '+' ? 'add' : 'del'),
|
||||
target: line.arguments[0],
|
||||
mode: mode,
|
||||
modeTarget: line.arguments[2 + parseInt(i)],
|
||||
server: serverName,
|
||||
user: {
|
||||
nickname: sender
|
||||
}
|
||||
})
|
||||
} else {
|
||||
pass.push(mode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pass = modes
|
||||
}
|
||||
|
||||
if (pass.length > 0) {
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'mode',
|
||||
target: line.arguments[0],
|
||||
message: method + pass.join(''),
|
||||
server: serverName,
|
||||
user: {
|
||||
nickname: sender
|
||||
}
|
||||
})
|
||||
}
|
||||
break
|
||||
case '433':
|
||||
let newNick = this.conn.config.nickname + '_'
|
||||
this.conn.write('NICK ' + newNick)
|
||||
this.conn.config.nickname = newNick
|
||||
break
|
||||
case '311':
|
||||
// start whois queue
|
||||
list = {
|
||||
nickname: line.arguments[1],
|
||||
hostmask: '%s!%s@%s'.format(line.arguments[1], line.arguments[2], line.arguments[3]),
|
||||
realname: line.trailing || ''
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '319':
|
||||
// whois: channels
|
||||
list = {
|
||||
channels: line.trailing.split(' ')
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '378':
|
||||
list = {
|
||||
connectingFrom: line.trailing
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '379':
|
||||
list = {
|
||||
usingModes: line.trailing
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '312':
|
||||
list = {
|
||||
server: line.arguments[2],
|
||||
server_name: line.trailing || ''
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '313':
|
||||
list = {
|
||||
title: line.trailing
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '330':
|
||||
list = {
|
||||
loggedIn: line.trailing + ' ' + line.arguments[2]
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '335':
|
||||
list = {
|
||||
bot: true
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '307':
|
||||
list = {
|
||||
registered: line.trailing
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '671':
|
||||
list = {
|
||||
secure: true
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '317':
|
||||
list = {
|
||||
signonTime: line.arguments[3],
|
||||
idleSeconds: line.arguments[2]
|
||||
}
|
||||
this.whoisManage(line.arguments[1], list)
|
||||
break
|
||||
case '318':
|
||||
if (!this.conn.queue.whois || !this.conn.queue.whois[line.arguments[1]]) return
|
||||
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'whoisResponse',
|
||||
whois: this.conn.queue.whois[line.arguments[1]],
|
||||
server: serverName,
|
||||
from: realServerName
|
||||
})
|
||||
|
||||
delete this.conn.queue.whois[line.arguments[1]]
|
||||
break
|
||||
case '321':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'listedchan',
|
||||
channel: 'Channel',
|
||||
users: 'Users',
|
||||
topic: 'Topic',
|
||||
server: serverName,
|
||||
from: realServerName
|
||||
})
|
||||
break
|
||||
case '322':
|
||||
this.conn.emit('pass_to_client', {
|
||||
type: 'listedchan',
|
||||
channel: line.arguments[1],
|
||||
users: line.arguments[2],
|
||||
topic: line.trailing,
|
||||
server: serverName,
|
||||
from: realServerName
|
||||
})
|
||||
break
|
||||
case 'CAP':
|
||||
// might come in the future, who knows
|
||||
this.conn.write('CAP END')
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IRCConnection extends EventEmitter {
|
||||
constructor (providedInfo, globalConfig, extras) {
|
||||
super()
|
||||
|
||||
this.globalConfig = globalConfig
|
||||
this.extras = extras || { authenticationSteps: [], ctcps: {} }
|
||||
this.config = {
|
||||
nickname: 'teemant',
|
||||
username: globalConfig.username,
|
||||
realname: globalConfig.realname,
|
||||
server: 'localhost',
|
||||
port: 6667,
|
||||
autojoin: [],
|
||||
secure: globalConfig.secure_by_default,
|
||||
password: '',
|
||||
address: providedInfo.server,
|
||||
rejectUnauthorized: globalConfig.rejectUnauthorizedCertificates
|
||||
}
|
||||
|
||||
for (let a in providedInfo) {
|
||||
this.config[a] = providedInfo[a]
|
||||
}
|
||||
|
||||
this.socket = null
|
||||
this.connected = false
|
||||
this.authenticated = false
|
||||
|
||||
this.handler = new IRCConnectionHandler(this)
|
||||
|
||||
this.data = {
|
||||
serverSupports: {},
|
||||
network: this.config.server,
|
||||
actualServer: this.config.server,
|
||||
maxChannelLength: 64,
|
||||
supportedModes: {}
|
||||
}
|
||||
this.authorizationError = ''
|
||||
this.queue = {}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
})
|
||||
|
||||
let buffer = ''
|
||||
this.socket.on('data', (chunk) => {
|
||||
buffer += chunk
|
||||
let data = buffer.split('\r\n')
|
||||
buffer = data.pop()
|
||||
data.forEach((line) => {
|
||||
if (line.indexOf('PING') === 0) {
|
||||
this.socket.write('PONG %s\r\n', line.substring(4))
|
||||
return
|
||||
}
|
||||
|
||||
this.emit('raw', line)
|
||||
|
||||
let parsed = parse(line)
|
||||
this.emit('line', parsed)
|
||||
this.handler.handleServerLine(parsed)
|
||||
})
|
||||
})
|
||||
|
||||
this.socket.on('close', (data) => {
|
||||
if (!this.queue['close']) {
|
||||
this.emit('closed', { type: 'sock_closed', raw: data, message: 'Connection closed.' })
|
||||
}
|
||||
|
||||
this.connected = false
|
||||
this.authenticated = false
|
||||
})
|
||||
}
|
||||
|
||||
authenticate () {
|
||||
if (this.config.password) {
|
||||
this.socket.write('PASS %s\r\n', this.config.password)
|
||||
}
|
||||
|
||||
if (this.extras.authenticationSteps) {
|
||||
for (let i in this.extras.authenticationSteps) {
|
||||
let step = this.extras.authenticationSteps[i]
|
||||
step.authenticate(this)
|
||||
}
|
||||
}
|
||||
|
||||
this.socket.write('USER %s 8 * :%s\r\n', this.config.username, this.config.realname)
|
||||
this.socket.write('NICK %s\r\n', this.config.nickname)
|
||||
}
|
||||
|
||||
disconnect (message) {
|
||||
if (!this.connected) {
|
||||
this.emit('connerror', { type: 'sock_closed', message: 'Connection already closed.' })
|
||||
return
|
||||
}
|
||||
|
||||
this.queue['close'] = true
|
||||
this.socket.write('QUIT :%s\r\n', (message != null ? message : this.globalConfig.default_quit_msg))
|
||||
}
|
||||
|
||||
write () {
|
||||
let message = util.format.apply(null, arguments)
|
||||
if (!this.connected) {
|
||||
return this.emit('connerror', { type: 'sock_closed', message: 'Connection is closed.' })
|
||||
}
|
||||
|
||||
this.socket.write(message + '\r\n')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IRCConnection
|
62
server/irc/parser.js
Normal file
62
server/irc/parser.js
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
// :nickname!username@hostname command arg ume nts :trailing
|
||||
// or
|
||||
// :hostname command arg ume nts :trailing
|
||||
|
||||
function parseERROR (line) {
|
||||
let final = {
|
||||
user: { nickname: '', username: '', hostname: '' },
|
||||
command: 'ERROR',
|
||||
message: '',
|
||||
raw: line.join(' ')
|
||||
}
|
||||
|
||||
let pass1 = line.slice(1).join(' ')
|
||||
if (pass1.indexOf(':') === 0) {
|
||||
pass1 = pass1.substring(1)
|
||||
}
|
||||
|
||||
final.message = pass1
|
||||
|
||||
return final
|
||||
}
|
||||
|
||||
module.exports = function (rawline) {
|
||||
let final = {
|
||||
user: {
|
||||
nickname: '',
|
||||
username: '',
|
||||
hostname: ''
|
||||
},
|
||||
command: '',
|
||||
arguments: [],
|
||||
trailing: '',
|
||||
raw: rawline
|
||||
}
|
||||
|
||||
let pass1 = (rawline.indexOf(':') === 0 ? rawline.substring(1).split(' ') : rawline.split(' '))
|
||||
if (pass1[0] === 'ERROR') return parseERROR(pass1)
|
||||
|
||||
if (pass1[0].indexOf('!') !== -1) {
|
||||
let nickuser = pass1[0].split('!')
|
||||
final.user.nickname = nickuser[0]
|
||||
|
||||
let userhost = nickuser[1].split('@')
|
||||
final.user.username = userhost[0]
|
||||
final.user.hostname = userhost[1]
|
||||
} else {
|
||||
final.user.hostname = pass1[0]
|
||||
}
|
||||
|
||||
final.command = pass1[1]
|
||||
|
||||
let pass2 = pass1.slice(2).join(' ')
|
||||
if (pass2.indexOf(':') !== -1) {
|
||||
final.arguments = pass2.substring(0, pass2.indexOf(' :')).split(' ')
|
||||
final.trailing = pass2.substring(pass2.indexOf(':') + 1)
|
||||
} else {
|
||||
final.arguments = pass2.split(' ')
|
||||
}
|
||||
|
||||
return final
|
||||
}
|
@ -1,50 +1,52 @@
|
||||
const util = require('util');
|
||||
const config = require(__dirname+'/config');
|
||||
import util from 'util'
|
||||
import config from './config'
|
||||
|
||||
module.exports.log = function() {
|
||||
console.log.apply(null, arguments);
|
||||
};
|
||||
module.exports.log = function () {
|
||||
console.log.apply(null, arguments)
|
||||
}
|
||||
|
||||
module.exports.debugLog = function() {
|
||||
if(!config.server.debug) return;
|
||||
module.exports.debugLog = function () {
|
||||
if (!config.server.debug) return
|
||||
|
||||
console.log.apply(null, arguments);
|
||||
};
|
||||
console.log.apply(null, arguments)
|
||||
}
|
||||
|
||||
module.exports.errorLog = function(errObj, specify) {
|
||||
if(specify)
|
||||
console.error(specify);
|
||||
module.exports.errorLog = function (errObj, specify) {
|
||||
if (specify) console.error(specify)
|
||||
console.error(errObj)
|
||||
|
||||
console.error(errObj);
|
||||
if (errObj.stack) {
|
||||
console.error(errObj.stack)
|
||||
}
|
||||
}
|
||||
|
||||
if(errObj.stack)
|
||||
console.error(errObj.stack);
|
||||
};
|
||||
module.exports.printRuntimeStats = function (runtimeStats, connections) {
|
||||
if (!config.server.printStats) return
|
||||
|
||||
module.exports.printRuntimeStats = function(runtime_stats, connections) {
|
||||
if(!config.server.printStats) return;
|
||||
let date = new Date()
|
||||
let users = 0
|
||||
let servers = 0
|
||||
let serversPerUser = 0
|
||||
|
||||
let date = new Date();
|
||||
let users = 0;
|
||||
let servers = 0;
|
||||
let serversPerUser = 0;
|
||||
|
||||
for(let uid in connections) {
|
||||
let ca = connections[uid];
|
||||
users += 1;
|
||||
for(let snam in ca) {
|
||||
if(!snam) continue;
|
||||
if(snam == 'host') continue;
|
||||
servers += 1;
|
||||
for (let uid in connections) {
|
||||
let ca = connections[uid]
|
||||
users += 1
|
||||
for (let snam in ca) {
|
||||
if (!snam) continue
|
||||
if (snam === 'host') continue
|
||||
servers += 1
|
||||
}
|
||||
}
|
||||
|
||||
if(users != 0) // Don't divide by zero lmao
|
||||
serversPerUser = servers/users;
|
||||
if (users !== 0) { // Don't divide by zero lmao
|
||||
serversPerUser = servers / users
|
||||
}
|
||||
|
||||
console.log(date+(': Currently connected users: {0}. ' +
|
||||
'IRC server connections: {1}. ' +
|
||||
'Average servers per user: {2}. ' +
|
||||
'Total connections made: {3}. ' +
|
||||
'Uptime: {4}s;').format(users, servers, serversPerUser, runtime_stats.connectionsMade, process.uptime()));
|
||||
};
|
||||
console.log(date,
|
||||
util.format('- Currently connected users: %d. ' +
|
||||
'IRC server connections: %d. ' +
|
||||
'Average servers per user: %f. ' +
|
||||
'Total connections made: %d. ' +
|
||||
'Uptime: %d s;', users, servers, serversPerUser, runtimeStats.connectionsMade, process.uptime())
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
let connector = require(__dirname+'/irc.js');
|
||||
let parser = require(__dirname+'/parser.js');
|
||||
|
||||
module.exports = {
|
||||
IRCConnection: connector,
|
||||
Parser: parser
|
||||
};
|
@ -1,549 +0,0 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const parse = require(__dirname+'/parser');
|
||||
|
||||
class IRCConnectionHandler {
|
||||
constructor(connection) {
|
||||
this.conn = connection;
|
||||
}
|
||||
|
||||
handleUserLine(data) {
|
||||
switch(data.command) {
|
||||
case 'topic':
|
||||
this.conn.write(('{0} {1}'+(data.message != '' ? ' :'+data.message : '')).format(data.command.toUpperCase(), data.arguments[0]));
|
||||
break;
|
||||
case 'kick':
|
||||
this.conn.write('{0} {1} :{2}'.format(data.command.toUpperCase(), data.arguments.join(' '), data.message));
|
||||
break;
|
||||
case 'part':
|
||||
this.conn.write('{0} {1} :{2}'.format(data.command.toUpperCase(), data.arguments[0], data.message));
|
||||
break;
|
||||
case 'nick':
|
||||
case 'whois':
|
||||
case 'who':
|
||||
case 'names':
|
||||
case 'join':
|
||||
this.conn.write('{0} {1}'.format(data.command.toUpperCase(), data.arguments[0]));
|
||||
break;
|
||||
case 'quit':
|
||||
this.conn.write('{0} :{1}'.format(data.command.toUpperCase(), (data.message == '' ?
|
||||
this.conn.globalConfig.default_quit_msg : data.message)));
|
||||
break;
|
||||
case 'privmsg':
|
||||
this.conn.write('PRIVMSG {0} :{1}'.format(data.arguments[0], data.message));
|
||||
this.conn.emit('pass_to_client', {type: 'message', messageType: 'privmsg', to: data.arguments[0],
|
||||
user: {nickname: this.conn.config.nickname}, message: data.message, server: data.server});
|
||||
break;
|
||||
case 'notice':
|
||||
this.conn.write('NOTICE {0} :{1}'.format(data.arguments[0], data.message));
|
||||
this.conn.emit('pass_to_client', {type: 'message', messageType: 'notice', to: data.arguments[0],
|
||||
user: {nickname: this.conn.config.nickname}, message: data.message, server: data.server});
|
||||
break;
|
||||
case 'list':
|
||||
this.conn.write(data.command.toUpperCase());
|
||||
break;
|
||||
case 'ctcp':
|
||||
let ctcpmsg = '';
|
||||
|
||||
if(data.arguments[1].toLowerCase() == 'ping')
|
||||
ctcpmsg = 'PING '+Math.floor(Date.now()/1000);
|
||||
else
|
||||
ctcpmsg = data.message;
|
||||
|
||||
this.conn.write('PRIVMSG {0} :\x01{1}\x01'.format(data.arguments[0], ctcpmsg));
|
||||
this.conn.emit('pass_to_client', {type: 'message', messageType: 'ctcp_request', to: this.conn.config.nickname,
|
||||
user: {nickname: data.arguments[0]}, message: ctcpmsg, server: data.server});
|
||||
break;
|
||||
default:
|
||||
this.conn.write(data.command.toUpperCase()+' '+data.message);
|
||||
}
|
||||
if(data.targetType == 'channel' || data.targetType == 'message') {
|
||||
this.conn.write('PRIVMSG {0} :{1}'.format(data.target, data.message));
|
||||
this.conn.emit('pass_to_client', {type: 'message', messageType: 'privmsg', to: data.target,
|
||||
user: {nickname: this.conn.config.nickname}, message: data.message, server: data.server});
|
||||
}
|
||||
}
|
||||
|
||||
whoisManage(whom, list) {
|
||||
if(!this.conn.queue.whois)
|
||||
this.conn.queue.whois = {};
|
||||
|
||||
if(!this.conn.queue.whois[whom])
|
||||
this.conn.queue.whois[whom] = list;
|
||||
else
|
||||
for(let a in list)
|
||||
this.conn.queue.whois[whom][a] = list[a];
|
||||
}
|
||||
|
||||
ctcpManage(data) {
|
||||
let line = data.trailing.replace(/\x01/g, '').trim().split(' ');
|
||||
|
||||
if(!line[0]) return;
|
||||
line[0] = line[0].toUpperCase();
|
||||
|
||||
let resp = '\x01'+line[0]+' {0}\x01';
|
||||
|
||||
if(line[0] == 'PING' && line[1] != null && line[1] != '') {
|
||||
resp = resp.format(line.slice(1).join(' '));
|
||||
} else if(line[0] == 'CLIENTINFO') {
|
||||
resp = resp.format('CLIENTINFO PING '+Object.keys(this.conn.extras.ctcps).join(' '));
|
||||
} else if(this.conn.extras.ctcps && this.conn.extras.ctcps[line[0]] != null) {
|
||||
resp = resp.format(this.conn.extras.ctcps[line[0]](data, this.conn));
|
||||
} else {
|
||||
resp = null;
|
||||
}
|
||||
|
||||
if (resp != null)
|
||||
this.conn.write('NOTICE {0} :{1}'.format(data.user.nickname, resp));
|
||||
|
||||
return resp != null;
|
||||
}
|
||||
|
||||
handleServerLine(line) {
|
||||
if(this.conn.queue['supportsmsg'] && line.command != '005') {
|
||||
|
||||
delete this.conn.queue['supportsmsg'];
|
||||
|
||||
if(this.conn.config.autojoin.length > 0)
|
||||
for(let t in this.conn.config.autojoin)
|
||||
this.conn.write('JOIN '+this.conn.config.autojoin[t]);
|
||||
|
||||
this.conn.emit('authenticated', {});
|
||||
}
|
||||
|
||||
let serverName = this.conn.config.server;
|
||||
let realServerName = this.conn.data.actualServer;
|
||||
if(line.user.nickname == '')
|
||||
realServerName = line.user.hostname;
|
||||
|
||||
let list = null;
|
||||
switch(line.command) {
|
||||
case 'error':
|
||||
this.conn.emit('connerror', {type: 'irc_error', raw: line.raw});
|
||||
break;
|
||||
case '001':
|
||||
this.conn.data.actualServer = line.user.hostname;
|
||||
break;
|
||||
case '005':
|
||||
if(!this.conn.queue['supportsmsg'])
|
||||
this.conn.queue['supportsmsg'] = true;
|
||||
|
||||
this.conn.authenticated = true;
|
||||
|
||||
let argv = line.arguments.slice(1);
|
||||
for(let a in argv) {
|
||||
let t = argv[a];
|
||||
if(t.indexOf('=') != -1) {
|
||||
t = t.split('=');
|
||||
if(t[0] === 'PREFIX') {
|
||||
let d = t[1].match(/\((\w+)\)(.*)/);
|
||||
let r = d[1].split('');
|
||||
let aa = d[2].split('');
|
||||
for(let b in r)
|
||||
this.conn.data.supportedModes[r[b]] = aa[b];
|
||||
} else if(t[0] === 'NETWORK') {
|
||||
this.conn.data.network = t[1];
|
||||
} else if(t[0] === 'CHANNELLEN') {
|
||||
this.conn.data.max_channel_length = parseInt(t[1]);
|
||||
}
|
||||
|
||||
this.conn.data.serverSupports[t[0]] = t[1];
|
||||
} else {
|
||||
this.conn.data.serverSupports[t] = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'JOIN':
|
||||
if(line.trailing) {
|
||||
this.conn.emit('pass_to_client', {type: 'event_join_channel', user: line.user, channel: line.trailing, server: serverName });
|
||||
} else {
|
||||
for(let i in line.arguments) {
|
||||
this.conn.emit('pass_to_client', {type: 'event_join_channel', user: line.user, channel: line.arguments[i], server: serverName });
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'PART':
|
||||
this.conn.emit('pass_to_client', {type: 'event_part_channel', user: line.user, channel: line.arguments[0], reason: line.trailing, server: serverName });
|
||||
break;
|
||||
case 'QUIT':
|
||||
this.conn.emit('pass_to_client', {type: 'event_quit', user: line.user, reason: line.trailing, server: serverName });
|
||||
break;
|
||||
case '353':
|
||||
if(!this.conn.queue['names'])
|
||||
this.conn.queue['names'] = {};
|
||||
|
||||
let splittrail = line.trailing.split(' ');
|
||||
for(let a in splittrail) {
|
||||
let nick = splittrail[a];
|
||||
if(nick.trim() == '') continue;
|
||||
if(this.conn.queue['names'][line.arguments[2]])
|
||||
this.conn.queue['names'][line.arguments[2]].push(nick);
|
||||
else
|
||||
this.conn.queue['names'][line.arguments[2]] = [nick];
|
||||
}
|
||||
|
||||
break;
|
||||
case '366':
|
||||
if(!this.conn.queue['names']) break;
|
||||
if(this.conn.queue['names'][line.arguments[1]]) {
|
||||
this.conn.emit('pass_to_client', {type: 'channel_nicks', channel: line.arguments[1], nicks: this.conn.queue['names'][line.arguments[1]], server: serverName});
|
||||
delete this.conn.queue['names'][line.arguments[1]];
|
||||
}
|
||||
|
||||
if(Object.keys(this.conn.queue['names']).length == 0)
|
||||
delete this.conn.queue['names'];
|
||||
|
||||
break;
|
||||
case 'PRIVMSG':
|
||||
if(line.trailing.indexOf('\x01') == 0 && line.trailing.indexOf('\x01ACTION') != 0)
|
||||
return this.ctcpManage(line);
|
||||
|
||||
if(line.user.nickname != '')
|
||||
this.conn.emit('pass_to_client', {type: 'message', messageType: 'privmsg', to: line.arguments[0],
|
||||
user: line.user, message: line.trailing, server: serverName});
|
||||
else
|
||||
this.conn.emit('pass_to_client', {type: 'server_message', messageType: 'privmsg', message: line.trailing, server: serverName, from: realServerName});
|
||||
break;
|
||||
case 'NOTICE':
|
||||
if(line.trailing.indexOf('\x01') == 0 && line.trailing.indexOf('\x01ACTION') != 0) {
|
||||
let composethis = line.trailing.replace(/\x01/g,'').trim().split(' ');
|
||||
composethis[0] = composethis[0].toUpperCase();
|
||||
let message = composethis.join(' ');
|
||||
|
||||
if(composethis[0] == 'PING')
|
||||
message = Math.floor(Date.now()/1000) - composethis[1]+'s';
|
||||
|
||||
this.conn.emit('pass_to_client', {type: 'message', messageType: 'ctcp_response', to: line.arguments[0],
|
||||
user: line.user, message: message, server: serverName});
|
||||
return;
|
||||
}
|
||||
|
||||
if(line.user.nickname != '')
|
||||
this.conn.emit('pass_to_client', {type: 'message', messageType: 'notice', to: line.arguments[0],
|
||||
user: line.user, message: line.trailing, server: serverName});
|
||||
else
|
||||
this.conn.emit('pass_to_client', {type: 'server_message', messageType: 'notice', message: line.trailing, server: serverName, from: realServerName});
|
||||
break;
|
||||
case 'NICK':
|
||||
if(line.user.nickname == this.conn.config.nickname)
|
||||
this.conn.config.nickname = line.arguments[0];
|
||||
|
||||
this.conn.emit('pass_to_client', {type: 'nick_change', nick: line.user.nickname, newNick: line.arguments[0], server: serverName});
|
||||
break;
|
||||
case 'KICK':
|
||||
this.conn.emit('pass_to_client', {type: 'event_kick_channel', user: line.user, channel: line.arguments[0], reason: line.trailing, kickee: line.arguments[1], server: serverName});
|
||||
break;
|
||||
case 'TOPIC':
|
||||
this.conn.emit('pass_to_client', {type: 'channel_topic', channel: line.arguments[0], set_by: line.user.nickname, topic: line.trailing, server: serverName});
|
||||
break;
|
||||
case '332':
|
||||
this.conn.emit('pass_to_client', {type: 'channel_topic', channel: line.arguments[1], topic: line.trailing, server: serverName});
|
||||
break;
|
||||
case '333':
|
||||
this.conn.emit('pass_to_client', {type: 'channel_topic', channel: line.arguments[1], set_by: line.arguments[2], time: line.arguments[3], server: serverName});
|
||||
break;
|
||||
case '375':
|
||||
case '372':
|
||||
case '376':
|
||||
this.conn.emit('pass_to_client', {type: 'server_message', messageType: 'motd', message: line.trailing, server: serverName, from: realServerName});
|
||||
break;
|
||||
case '006':
|
||||
case '007':
|
||||
case '251':
|
||||
case '255':
|
||||
case '270':
|
||||
case '290':
|
||||
case '292':
|
||||
case '323':
|
||||
case '351':
|
||||
case '381':
|
||||
this.conn.emit('pass_to_client', {type: 'server_message', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName});
|
||||
break;
|
||||
case '252':
|
||||
case '254':
|
||||
case '396':
|
||||
case '042':
|
||||
this.conn.emit('pass_to_client', {type: 'server_message', messageType: 'regular', message: line.arguments[1] +' '+ line.trailing, server: serverName, from: realServerName});
|
||||
break;
|
||||
case '501':
|
||||
case '401':
|
||||
case '402':
|
||||
case '421':
|
||||
case '482':
|
||||
case '331':
|
||||
case '432':
|
||||
this.conn.emit('pass_to_client', {type: 'message', to: null, message: line.arguments[1]+': '+line.trailing,
|
||||
server: serverName, user: {nickname: realServerName}, messageType: 'error'});
|
||||
break;
|
||||
case 'MODE':
|
||||
let isChannelMode = false;
|
||||
let method = '+';
|
||||
if(line.arguments[0].indexOf('#') != -1)
|
||||
isChannelMode = true;
|
||||
|
||||
let modes = line.arguments[1];
|
||||
|
||||
if(!modes && line.trailing != '')
|
||||
modes = line.trailing;
|
||||
|
||||
let sender = line.user.nickname;
|
||||
if(sender == '')
|
||||
sender = line.user.hostname;
|
||||
|
||||
method = modes.substring(0, 1);
|
||||
modes = modes.substring(1).split('');
|
||||
let pass = [];
|
||||
|
||||
if(isChannelMode) {
|
||||
for(let i in modes) {
|
||||
let mode = modes[i];
|
||||
if(this.conn.data.supportedModes[mode])
|
||||
this.conn.emit('pass_to_client', {type: 'mode_'+(method=='+'?'add':'del'), target: line.arguments[0], mode: mode,
|
||||
modeTarget: line.arguments[2+parseInt(i)], server: serverName, user: {nickname: sender}});
|
||||
else
|
||||
pass.push(mode);
|
||||
}
|
||||
} else {
|
||||
pass = modes;
|
||||
}
|
||||
|
||||
if(pass.length > 0)
|
||||
this.conn.emit('pass_to_client', {type: 'mode', target: line.arguments[0], message: method+pass.join(''),
|
||||
server: serverName, user: {nickname: sender}});
|
||||
break;
|
||||
case '433':
|
||||
let newNick = this.conn.config.nickname + '_';
|
||||
this.conn.write('NICK '+newNick);
|
||||
this.conn.config.nickname = newNick;
|
||||
break;
|
||||
case '311':
|
||||
// start whois queue
|
||||
list = {
|
||||
nickname: line.arguments[1],
|
||||
hostmask: '{0}!{1}@{2}'.format(line.arguments[1], line.arguments[2], line.arguments[3]),
|
||||
realname: line.trailing || ''
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '319':
|
||||
// whois: channels
|
||||
list = {
|
||||
channels: line.trailing.split(' '),
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '378':
|
||||
list = {
|
||||
connectingFrom: line.trailing,
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '379':
|
||||
list = {
|
||||
usingModes: line.trailing,
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '312':
|
||||
list = {
|
||||
server: line.arguments[2],
|
||||
server_name: line.trailing || ''
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '313':
|
||||
list = {
|
||||
title: line.trailing
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '330':
|
||||
list = {
|
||||
loggedIn: line.trailing+' '+line.arguments[2]
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '335':
|
||||
list = {
|
||||
bot: true
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '307':
|
||||
list = {
|
||||
registered: line.trailing
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '671':
|
||||
list = {
|
||||
secure: true
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '317':
|
||||
list = {
|
||||
signonTime: line.arguments[3],
|
||||
idleSeconds: line.arguments[2]
|
||||
};
|
||||
this.whoisManage(line.arguments[1], list);
|
||||
break;
|
||||
case '318':
|
||||
if(!this.conn.queue.whois || !this.conn.queue.whois[line.arguments[1]])
|
||||
return;
|
||||
|
||||
this.conn.emit('pass_to_client', {type: 'whoisResponse', whois: this.conn.queue.whois[line.arguments[1]],
|
||||
server: serverName, from: realServerName});
|
||||
|
||||
delete this.conn.queue.whois[line.arguments[1]];
|
||||
break;
|
||||
case '321':
|
||||
this.conn.emit('pass_to_client', {type: 'listedchan', channel: 'Channel', users: 'Users', topic: 'Topic',
|
||||
server: serverName, from: realServerName});
|
||||
break;
|
||||
case '322':
|
||||
this.conn.emit('pass_to_client', {type: 'listedchan', channel: line.arguments[1], users: line.arguments[2], topic: line.trailing,
|
||||
server: serverName, from: realServerName});
|
||||
break;
|
||||
case 'CAP':
|
||||
// might come in the future, who knows
|
||||
this.conn.write('CAP END');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IRCConnection extends EventEmitter {
|
||||
constructor(providedInfo, globalConfig, extras) {
|
||||
super();
|
||||
|
||||
this.globalConfig = globalConfig;
|
||||
this.extras = extras || { authenticationSteps: [], ctcps: {} };
|
||||
this.config = {
|
||||
nickname: 'teemant',
|
||||
username: globalConfig.username,
|
||||
realname: globalConfig.realname,
|
||||
server: 'localhost',
|
||||
port: 6667,
|
||||
autojoin: [],
|
||||
secure: globalConfig.secure_by_default,
|
||||
password: '',
|
||||
address: providedInfo.server,
|
||||
rejectUnauthorized: globalConfig.rejectUnauthorizedCertificates
|
||||
};
|
||||
|
||||
for(let a in providedInfo) {
|
||||
this.config[a] = providedInfo[a];
|
||||
}
|
||||
|
||||
this.socket = null;
|
||||
this.connected = false;
|
||||
this.authenticated = false;
|
||||
|
||||
this.handler = new IRCConnectionHandler(this);
|
||||
|
||||
this.data = {
|
||||
serverSupports: {},
|
||||
network: this.config.server,
|
||||
actualServer: this.config.server,
|
||||
max_channel_length: 64,
|
||||
supportedModes: {}
|
||||
};
|
||||
this.authorizationError = '';
|
||||
this.queue = {};
|
||||
}
|
||||
|
||||
on(...args) {
|
||||
return super.on(...args);
|
||||
}
|
||||
|
||||
emit(...args) {
|
||||
return super.emit(...args);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
let buffer = '';
|
||||
this.socket.on('data', (chunk) => {
|
||||
buffer += chunk;
|
||||
let data = buffer.split('\r\n');
|
||||
buffer = data.pop();
|
||||
data.forEach((line) => {
|
||||
if(line.indexOf('PING') === 0) {
|
||||
this.socket.write('PONG'+line.substring(4)+'\r\n');
|
||||
return;
|
||||
}
|
||||
this.emit('raw', line);
|
||||
let parsed = parse(line);
|
||||
this.emit('line', parsed);
|
||||
this.handler.handleServerLine(parsed);
|
||||
});
|
||||
});
|
||||
|
||||
this.socket.on('close', (data) => {
|
||||
if(!this.queue['close'])
|
||||
this.emit('closed', {type: 'sock_closed', raw: data, message: 'Connection closed.'});
|
||||
|
||||
this.connected = false;
|
||||
this.authenticated = false;
|
||||
});
|
||||
}
|
||||
|
||||
authenticate() {
|
||||
if (this.config.password)
|
||||
this.socket.write('PASS {0}\r\n'.format(this.config.password));
|
||||
|
||||
if(this.extras.authenticationSteps) {
|
||||
for(let i in this.extras.authenticationSteps) {
|
||||
let step = this.extras.authenticationSteps[i];
|
||||
step.authenticate(this);
|
||||
}
|
||||
}
|
||||
|
||||
this.socket.write('USER {0} 8 * :{1}\r\n'.format(this.config.username, this.config.realname));
|
||||
this.socket.write('NICK {0}\r\n'.format(this.config.nickname));
|
||||
}
|
||||
|
||||
disconnect(message) {
|
||||
if(!this.connected) {
|
||||
this.emit('connerror', {type: 'sock_closed', message: 'Connection already closed.'});
|
||||
return;
|
||||
}
|
||||
|
||||
this.queue['close'] = true;
|
||||
this.socket.write('QUIT :{0}\r\n'.format(message != null ? message : this.globalConfig.default_quit_msg));
|
||||
}
|
||||
|
||||
write(message) {
|
||||
if(!this.connected) {
|
||||
this.emit('connerror', {type: 'sock_closed', message: 'Connection is closed.'});
|
||||
return;
|
||||
}
|
||||
this.socket.write(message+'\r\n');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = IRCConnection;
|
@ -1,61 +0,0 @@
|
||||
|
||||
// :nickname!username@hostname command arg ume nts :trailing
|
||||
// or
|
||||
// :hostname command arg ume nts :trailing
|
||||
|
||||
function parseERROR(line) {
|
||||
let final = {
|
||||
user: { nickname: '', username: '', hostname: '' },
|
||||
command: 'ERROR',
|
||||
message: '',
|
||||
raw: line.join(' ')
|
||||
};
|
||||
|
||||
let pass1 = line.slice(1).join(' ');
|
||||
if(pass1.indexOf(':') == 0)
|
||||
pass1 = pass1.substring(1);
|
||||
|
||||
final.message = pass1;
|
||||
|
||||
return final;
|
||||
}
|
||||
|
||||
module.exports = function(rawline) {
|
||||
let final = {
|
||||
user: {
|
||||
nickname: '',
|
||||
username: '',
|
||||
hostname: ''
|
||||
},
|
||||
command: '',
|
||||
arguments: [],
|
||||
trailing: '',
|
||||
raw: rawline
|
||||
};
|
||||
|
||||
let pass1 = (rawline.indexOf(':') == 0 ? rawline.substring(1).split(' ') : rawline.split(' '));
|
||||
if (pass1[0] === 'ERROR')
|
||||
return parseERROR(pass1);
|
||||
|
||||
if(pass1[0].indexOf('!') != -1) {
|
||||
let nickuser = pass1[0].split('!');
|
||||
final.user.nickname = nickuser[0];
|
||||
let userhost = nickuser[1].split('@');
|
||||
final.user.username = userhost[0];
|
||||
final.user.hostname = userhost[1];
|
||||
} else {
|
||||
final.user.hostname = pass1[0];
|
||||
}
|
||||
|
||||
final.command = pass1[1];
|
||||
|
||||
let pass2 = pass1.slice(2).join(' ');
|
||||
if(pass2.indexOf(':') != -1) {
|
||||
final.arguments = pass2.substring(0, pass2.indexOf(' :')).split(' ');
|
||||
final.trailing = pass2.substring(pass2.indexOf(':')+1);
|
||||
} else {
|
||||
final.arguments = pass2.split(' ');
|
||||
}
|
||||
|
||||
return final;
|
||||
};
|
140
server/webirc.js
140
server/webirc.js
@ -1,122 +1,120 @@
|
||||
const dns = require('dns');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const config = require(__dirname+'/config');
|
||||
const logger = require(__dirname+'/logger');
|
||||
const webirc_data_path = path.resolve(__dirname+'/../webirc.data.json');
|
||||
import dns from 'dns'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
let webirc_data = {};
|
||||
let timeouts = {};
|
||||
import config from './server/config'
|
||||
import logger from './server/logger'
|
||||
|
||||
function writeToFile() {
|
||||
fs.writeFile(webirc_data_path, JSON.stringify(webirc_data, null, '\t'), function (err) {if (err) throw err;});
|
||||
const webircDataPath = path.join(__dirname, '..', 'webirc.data.json')
|
||||
|
||||
let webircData = {}
|
||||
let timeouts = {}
|
||||
|
||||
function writeToFile () {
|
||||
fs.writeFile(webircDataPath, JSON.stringify(webircData, null, '\t'), (err) => { if (err) throw err })
|
||||
}
|
||||
|
||||
function timeoutRefresh(address, seconds) {
|
||||
if(timeouts[address])
|
||||
clearTimeout(timeouts[address]);
|
||||
function timeoutRefresh (address, seconds) {
|
||||
if (timeouts[address]) clearTimeout(timeouts[address])
|
||||
|
||||
timeouts[address] = setTimeout(()=>{resolveAddress(address);}, seconds*1000);
|
||||
timeouts[address] = setTimeout(() => resolveAddress(address), seconds * 1000)
|
||||
}
|
||||
|
||||
function resolveAddress(address, force) {
|
||||
logger.debugLog('** WEBIRC ** Attempting to update IP for '+address);
|
||||
let obj = webirc_data[address];
|
||||
function resolveAddress (address, force) {
|
||||
logger.debugLog('** WEBIRC ** Attempting to update IP for', address)
|
||||
let obj = webircData[address]
|
||||
|
||||
if(!obj) return;
|
||||
if (!obj) return
|
||||
|
||||
if((Date.now() - obj.last_update)/1000 < config.webirc.resolveInterval && !force) {
|
||||
let nextTime = (config.webirc.resolveInterval - (Date.now() - obj.last_update)/1000);
|
||||
if ((Date.now() - obj.last_update) / 1000 < config.webirc.resolveInterval && !force) {
|
||||
let nextTime = (config.webirc.resolveInterval - (Date.now() - obj.last_update) / 1000)
|
||||
|
||||
logger.debugLog('** WEBIRC ** {0} IP is {1}, refresh in {2} seconds'.format(address, obj.cached_ip, Math.floor(nextTime)));
|
||||
logger.debugLog(`** WEBIRC ** ${address} IP is ${obj.cachedIP}, refresh in ${Math.floor(nextTime)} seconds`)
|
||||
|
||||
return timeoutRefresh(address, nextTime);
|
||||
return timeoutRefresh(address, nextTime)
|
||||
}
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
if(address === '127.0.0.1' || address === '0.0.0.0' || address === 'localhost') {
|
||||
logger.debugLog('** WEBIRC ** Ignoring localhost entry..');
|
||||
return resolve('127.0.0.1');
|
||||
if (address === '127.0.0.1' || address === '0.0.0.0' || address === 'localhost') {
|
||||
logger.debugLog('** WEBIRC ** Ignoring localhost entry..')
|
||||
return resolve('127.0.0.1')
|
||||
}
|
||||
|
||||
dns.resolve(address, (err, data) => {
|
||||
if(err!=null) return reject(err);
|
||||
let ip = data.length > 0 ? data[0] : null;
|
||||
if (err != null) return reject(err)
|
||||
let ip = data.length > 0 ? data[0] : null
|
||||
|
||||
if(ip) {
|
||||
resolve(ip);
|
||||
if (ip) {
|
||||
resolve(ip)
|
||||
} else {
|
||||
reject(new Error('no ips'));
|
||||
reject(new Error('no ips'))
|
||||
}
|
||||
});
|
||||
})
|
||||
}).then((data) => {
|
||||
logger.debugLog('** WEBIRC ** Updated DNS for {0}; IP is now {1}'.format(address, data));
|
||||
logger.debugLog('** WEBIRC ** Updated DNS for {0}; IP is now {1}'.format(address, data))
|
||||
|
||||
webirc_data[address].last_update = Date.now();
|
||||
webirc_data[address].cached_ip = data;
|
||||
webircData[address].last_update = Date.now()
|
||||
webircData[address].cachedIP = data
|
||||
|
||||
writeToFile();
|
||||
timeoutRefresh(address, config.webirc.resolveInterval);
|
||||
writeToFile()
|
||||
timeoutRefresh(address, config.webirc.resolveInterval)
|
||||
}, (err) => {
|
||||
logger.debugLog('** WEBIRC ** Failed to updated DNS for {0}; IP is still {1}'.format(address, webirc_data[address].cached_ip));
|
||||
logger.debugLog(`** WEBIRC ** Failed to updated DNS for ${address}; IP is still ${webircData[address].cachedIP}:`, err.message)
|
||||
|
||||
timeoutRefresh(address, (config.webirc.resolveInterval+60));
|
||||
});
|
||||
timeoutRefresh(address, config.webirc.resolveInterval + 60)
|
||||
})
|
||||
}
|
||||
|
||||
function reload(force) {
|
||||
if(!config.webirc.enabled) return;
|
||||
function reload (force) {
|
||||
if (!config.webirc.enabled) return
|
||||
|
||||
try {
|
||||
fs.accessSync(webirc_data_path, fs.F_OK);
|
||||
fs.accessSync(webircDataPath, fs.F_OK)
|
||||
|
||||
webirc_data = require(webirc_data_path);
|
||||
webircData = require(webircDataPath)
|
||||
|
||||
if (require.cache && require.cache[webirc_data_path]) {
|
||||
delete require.cache[webirc_data_path];
|
||||
if (require.cache && require.cache[webircDataPath]) {
|
||||
delete require.cache[webircDataPath]
|
||||
}
|
||||
} catch(e) {
|
||||
writeToFile();
|
||||
} catch (e) {
|
||||
writeToFile()
|
||||
}
|
||||
|
||||
for(let adr in webirc_data) {
|
||||
resolveAddress(adr, force);
|
||||
for (let adr in webircData) {
|
||||
resolveAddress(adr, force)
|
||||
}
|
||||
}
|
||||
|
||||
function get_password(server_ip) {
|
||||
let ip = null;
|
||||
for(let a in webirc_data) {
|
||||
if(webirc_data[a].cached_ip == server_ip)
|
||||
ip = webirc_data[a];
|
||||
function getPassword (serverIP) {
|
||||
let ip = null
|
||||
for (let a in webircData) {
|
||||
if (webircData[a].cachedIP !== serverIP) continue
|
||||
ip = webircData[a]
|
||||
}
|
||||
|
||||
return ip;
|
||||
return ip
|
||||
}
|
||||
|
||||
class WebIRCAuthenticator {
|
||||
constructor(userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
constructor (userInfo) {
|
||||
this.userInfo = userInfo
|
||||
}
|
||||
|
||||
authenticate(connection) {
|
||||
let serverpass = get_password(connection.config.address);
|
||||
if(serverpass)
|
||||
connection.socket.write('WEBIRC {0} {1} {2} {3}\r\n'.format(serverpass.password, connection.config.username,
|
||||
this.userInfo.hostname, this.userInfo.ipaddr));
|
||||
authenticate (connection) {
|
||||
let serverpass = getPassword(connection.config.address)
|
||||
if (serverpass) {
|
||||
connection.socket.write(`WEBIRC ${serverpass.password} ${connection.config.username} ${this.userInfo.hostname} ${this.userInfo.ipaddr}\r\n`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
reload: reload,
|
||||
authenticator: WebIRCAuthenticator,
|
||||
get_password: get_password,
|
||||
writeToFile: writeToFile
|
||||
};
|
||||
Authenticator: WebIRCAuthenticator, reload, getPassword, writeToFile
|
||||
}
|
||||
|
||||
process.on('SIGUSR1', () => {
|
||||
logger.log('\n!!! Received SIGUSR1; Reloading webirc data.. !!!\n');
|
||||
reload(true);
|
||||
});
|
||||
logger.log('\n!!! Received SIGUSR1; Reloading webirc data.. !!!\n')
|
||||
reload(true)
|
||||
})
|
||||
|
||||
reload(false);
|
||||
reload(false)
|
||||
|
291
teemant.js
291
teemant.js
@ -1,99 +1,90 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const sockio = require('socket.io');
|
||||
const dns = require('dns');
|
||||
const app = express();
|
||||
const router = express.Router();
|
||||
'use strict'
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
import sockio from 'socket.io'
|
||||
import dns from 'dns'
|
||||
|
||||
const pubdir = path.join(__dirname, 'build');
|
||||
const pkg = require(__dirname+'/package.json');
|
||||
import pkginfo from './package.json'
|
||||
import irclib from './server/irc'
|
||||
import webirc from './server/webirc'
|
||||
|
||||
const config = require(__dirname+'/server/config');
|
||||
const logger = require(__dirname+'/server/logger');
|
||||
import config from './server/config'
|
||||
import logger from './server/logger'
|
||||
|
||||
const port = config.server.port || 8080;
|
||||
const app = express()
|
||||
const router = express.Router()
|
||||
const pubdir = path.join(__dirname, 'build')
|
||||
const port = config.server.port || 8080
|
||||
const cacheAge = 365 * 24 * 60 * 60 * 1000
|
||||
|
||||
if (!String.prototype.format) {
|
||||
String.prototype.format = function() {
|
||||
var args = arguments;
|
||||
return this.replace(/{(\d+)}/g, function(match, number) {
|
||||
return typeof args[number] != undefined ? args[number] : match;
|
||||
});
|
||||
};
|
||||
let runtimeStats = {
|
||||
connectionsMade: 0
|
||||
}
|
||||
|
||||
let irclib = require(__dirname+'/server/teemant_irc');
|
||||
let webirc = require(__dirname+'/server/webirc');
|
||||
|
||||
let runtime_stats = {
|
||||
connectionsMade: 0
|
||||
};
|
||||
|
||||
let connections = {};
|
||||
let connections = {}
|
||||
|
||||
let customCTCPs = {
|
||||
VERSION: function(data, connection) {
|
||||
return 'TeemantIRC ver. {0} - {1} - https://teemant.icynet.ml/'.format(pkg.version, pkg.description);
|
||||
VERSION: function (data, connection) {
|
||||
return `TeemantIRC ver. ${pkginfo.version} - ${pkginfo.description} - https://teemant.icynet.eu/`
|
||||
},
|
||||
SOURCE: function(data, connection) {
|
||||
return 'https://github.com/DiamondtechDev/TeemantIRC';
|
||||
SOURCE: function (data, connection) {
|
||||
return 'https://gitlab.icynet.eu/IcyNetwork/teemant'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
process.stdin.resume();
|
||||
process.stdin.resume()
|
||||
|
||||
router.get('/', function(req, res){
|
||||
res.sendFile(pubdir+'/document/index.html');
|
||||
});
|
||||
router.get('/', function (req, res) {
|
||||
res.sendFile(path.join(pubdir, '/document/index.html'))
|
||||
})
|
||||
|
||||
router.get('/:server?*', function(req, res){
|
||||
res.sendFile(pubdir+'/document/index.html');
|
||||
});
|
||||
router.get('/:server?*', function (req, res) {
|
||||
res.sendFile(path.join(pubdir, '/document/index.html'))
|
||||
})
|
||||
|
||||
app.use('/', express.static(pubdir, { maxAge: 365*24*60*60*1000 }));
|
||||
app.use('/', express.static(pubdir+'/icons', { maxAge: 365*24*60*60*1000 }));
|
||||
app.use('/', express.static(__dirname+'/static', { maxAge: 365*24*60*60*1000 }));
|
||||
app.use('/', express.static(pubdir, { maxAge: cacheAge }))
|
||||
app.use('/', express.static(path.join(pubdir, 'icons'), { maxAge: cacheAge }))
|
||||
app.use('/', express.static(path.join(__dirname, 'static'), { maxAge: cacheAge }))
|
||||
|
||||
app.use('/:server', express.static(pubdir, { maxAge: 365*24*60*60*1000 }));
|
||||
app.use('/:server', express.static(pubdir+'/icons', { maxAge: 365*24*60*60*1000 }));
|
||||
app.use('/:server', express.static(pubdir+'/static', { maxAge: 365*24*60*60*1000 }));
|
||||
app.use('/:server', express.static(pubdir, { maxAge: cacheAge }))
|
||||
app.use('/:server', express.static(path.join(pubdir, 'icons'), { maxAge: cacheAge }))
|
||||
app.use('/:server', express.static(path.join(pubdir, 'static'), { maxAge: cacheAge }))
|
||||
|
||||
app.use('/', router);
|
||||
app.use('/', router)
|
||||
|
||||
const io = sockio.listen(app.listen(port, function() {
|
||||
logger.log('*** Listening on http://localhost:{0}/'.format(port));
|
||||
const io = sockio.listen(app.listen(port, function () {
|
||||
logger.log(`*** Listening on http://localhost:${port}/`)
|
||||
|
||||
setInterval(() => {
|
||||
logger.printRuntimeStats(runtime_stats, connections);
|
||||
}, 3600000);
|
||||
}));
|
||||
logger.printRuntimeStats(runtimeStats, connections)
|
||||
}, 3600000)
|
||||
}))
|
||||
|
||||
function resolveHostname(ipaddr) {
|
||||
let promise = new Promise(function(resolve, reject) {
|
||||
dns.reverse(ipaddr, function(err, hostnames) {
|
||||
if(err != null) return reject(err);
|
||||
resolve(hostnames);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
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';
|
||||
socket.request.connection._peername.address || '127.0.0.1'
|
||||
|
||||
if(userip.indexOf('::ffff:') == 0)
|
||||
userip = userip.substring(7);
|
||||
if (userip.indexOf('::ffff:') === 0) {
|
||||
userip = userip.substring(7)
|
||||
}
|
||||
|
||||
logger.debugLog('clientID: {0} from: {1}'.format(socket.id, userip));
|
||||
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] = {
|
||||
@ -101,117 +92,141 @@ io.sockets.on('connection', function (socket) {
|
||||
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 {0} was determined to be {1}'.format(socket.id, connections[socket.id].host.hostname));
|
||||
}).catch((err) => {
|
||||
logger.debugLog('Host resolve for {0} failed: {1}'.format(socket.id, err));
|
||||
});
|
||||
|
||||
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];
|
||||
// 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('clientID: {0} disconnect'.format(socket.id));
|
||||
});
|
||||
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: {0} disconnect'.format(socket.id))
|
||||
})
|
||||
|
||||
socket.on('error', (e) => {
|
||||
logger.errorLog(e, 'Socket error');
|
||||
});
|
||||
logger.errorLog(e, 'Socket error')
|
||||
})
|
||||
|
||||
socket.on('userinput', (data) => {
|
||||
let serv = connections[socket.id][data.server];
|
||||
if(!serv) return;
|
||||
if(serv.authenticated == false) return;
|
||||
let serv = connections[socket.id][data.server]
|
||||
if (!serv) return
|
||||
if (serv.authenticated === false) return
|
||||
|
||||
logger.debugLog('['+socket.id+'] ->', data);
|
||||
logger.debugLog(`[${socket.id}] ->`, data)
|
||||
|
||||
serv.handler.handleUserLine(data);
|
||||
});
|
||||
serv.handler.handleUserLine(data)
|
||||
})
|
||||
|
||||
socket.on('irc_create', function(connectiondata) {
|
||||
logger.debugLog(socket.id+' created irc connection: ', connectiondata);
|
||||
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});
|
||||
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});
|
||||
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)
|
||||
new webirc.Authenticator(connections[socket.id].host)
|
||||
],
|
||||
ctcps: customCTCPs
|
||||
});
|
||||
})
|
||||
|
||||
newConnection.connect();
|
||||
newConnection.connect()
|
||||
|
||||
connections[socket.id][connectiondata.server] = newConnection;
|
||||
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});
|
||||
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
|
||||
})
|
||||
|
||||
runtime_stats.connectionsMade += 1;
|
||||
});
|
||||
runtimeStats.connectionsMade += 1
|
||||
})
|
||||
|
||||
if(config.server.debug) {
|
||||
newConnection.on('line', function(line) {
|
||||
logger.debugLog('['+socket.id+'] <-', line);
|
||||
});
|
||||
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('debug_log', function (data) {
|
||||
logger.debugLog(`[${socket.id}] <-`, data)
|
||||
})
|
||||
}
|
||||
|
||||
newConnection.on('connerror', (data) => {
|
||||
logger.debugLog(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});
|
||||
});
|
||||
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);
|
||||
});
|
||||
socket.emit('act_client', data)
|
||||
})
|
||||
|
||||
newConnection.on('closed', (data) => {
|
||||
logger.debugLog(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});
|
||||
});
|
||||
});
|
||||
});
|
||||
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(runtime_stats, connections);
|
||||
for(let c in connections) {
|
||||
for(let ircconn in connections[c]) {
|
||||
if(connections[c][ircconn].ipaddr) continue;
|
||||
connections[c][ircconn].disconnect();
|
||||
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();
|
||||
});
|
||||
process.exit(0)
|
||||
})
|
||||
|
@ -1,49 +1,39 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
let inProduction = process.env.NODE_ENV === 'production' || process.argv.indexOf('-p') !== -1;
|
||||
const webpack = require('webpack')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
main: ['./src/script/main']
|
||||
main: './src/js/main'
|
||||
},
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: './build/script/[name].js',
|
||||
chunkFilename: './build/script/[id].js'
|
||||
path: path.join(__dirname, 'dist', 'js'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
preLoaders: [
|
||||
// { test: /\.js$/, loader: 'eslint-loader', exclude: /node_modules/ },
|
||||
// { test: /\.coffee$/, loader: 'coffeelint-loader', exclude: /node_modules/ },
|
||||
],
|
||||
loaders: [
|
||||
// { test: /\.mustache$/, loader: 'mustache', exclude: /node_modules/ }
|
||||
],
|
||||
noParse: [
|
||||
/node_modules/
|
||||
]
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.json'],
|
||||
root: [path.join(__dirname, '/src/script')],
|
||||
|
||||
alias: {
|
||||
'underscore': 'lodash'
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [ '@babel/preset-env' ]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
test: /src\/style\/.+\.styl$/,
|
||||
loader: 'file-loader?name=./dist/style/[name].css!css-loader!stylus-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
|
||||
new webpack.ProvidePlugin({
|
||||
// Detect and inject
|
||||
_: 'underscore',
|
||||
_: 'underscore'
|
||||
})
|
||||
],
|
||||
|
||||
devtool: 'inline-source-map',
|
||||
debug: true
|
||||
};
|
||||
devtool: 'inline-source-map'
|
||||
}
|
||||
|
Reference in New Issue
Block a user