Browse Source
Add irc.js for peering with IRC Change default to reflect that Add basic peering. Implemented: register/unregister a user messages join/part Not implemented/TODO: Kill, kick, change nick It is likely that the web chat will not have full feature parity with clients connecting directly to IRC.merge-requests/1/head
5 changed files with 241 additions and 4 deletions
@ -0,0 +1,2 @@ |
|||
knotteye <knotteye@airmail.cc> |
|||
crushv <nik@telekem.net> |
@ -0,0 +1,213 @@ |
|||
// written by crushv <nik@telekem.net>
|
|||
// thanks nikki
|
|||
|
|||
const net = require('net') |
|||
const EventEmitter = require('events') |
|||
|
|||
const socket = new net.Socket() |
|||
const emitter = new EventEmitter() |
|||
|
|||
socket.setEncoding('utf8') |
|||
|
|||
socket.on('error', console.error) |
|||
|
|||
function m (text) { |
|||
console.log('> ' + text) |
|||
socket.write(text + '\r\n') |
|||
} |
|||
|
|||
var config |
|||
|
|||
socket.once('connect', async () => { |
|||
console.log('Connected') |
|||
m(`PASS ${config.pass} TS 6 :${config.sid}`) |
|||
m('CAPAB QS ENCAP EX IE SAVE EUID') |
|||
m(`SERVER ${config.server} 1 satyr`) |
|||
}) |
|||
|
|||
function parseLine (l) { |
|||
const colIndex = l.lastIndexOf(':') |
|||
if (colIndex > -1) { |
|||
return { |
|||
params: l.substring(0, colIndex - 1).split(' '), |
|||
query: l.substring(colIndex + 1) |
|||
} |
|||
} else return { params: l.split(' ') } |
|||
} |
|||
|
|||
const servers = [] |
|||
const users = {} |
|||
const channels = {} |
|||
|
|||
const globalCommands = { |
|||
// PING :42X
|
|||
// params: SID
|
|||
PING: l => { |
|||
const { query } = parseLine(l) |
|||
m(`PONG :${query}`) |
|||
emitter.emit('ping') |
|||
}, |
|||
// PASS hunter2 TS 6 :42X
|
|||
// params: password, 'TS', TS version, SID
|
|||
PASS: l => { |
|||
const { query } = parseLine(l) |
|||
// adds a server
|
|||
servers.push(query) |
|||
} |
|||
} |
|||
|
|||
const serverCommands = { |
|||
// EUID nik 1 1569146316 +i ~nik localhost6.attlocal.net 0::1 42XAAAAAB * * :nik
|
|||
// params: nickname, hopcount, nickTS, umodes, username, visible hostname, IP address, UID, real hostname, account name, gecos
|
|||
EUID: l => { |
|||
const { params } = parseLine(l) |
|||
const user = { |
|||
nick: params[0], |
|||
nickTS: params[2], |
|||
modes: params[3], |
|||
username: params[4], |
|||
vhost: params[5], |
|||
ip: params[6], |
|||
uid: params[7] |
|||
} |
|||
users[user.uid] = user |
|||
}, |
|||
// SJOIN 1569142987 #test +nt :42XAAAAAB
|
|||
// params: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
|||
SJOIN: l => { |
|||
const { params, query } = parseLine(l) |
|||
const channel = { |
|||
timestamp: params[0], |
|||
name: params[1], |
|||
modes: params.slice(2).join(' '), |
|||
nicklist: query.split(' ').map(uid => { |
|||
if (/[^0-9a-zA-Z]/.test(uid[0])) return { uid: uid.slice(1), mode: uid[0] } |
|||
else return { uid: uid, mode: '' } |
|||
}) |
|||
} |
|||
channels[channel.name] = channel |
|||
} |
|||
} |
|||
|
|||
const userCommands = { |
|||
// :42XAAAAAC PRIVMSG #test :asd
|
|||
// params: target, msg
|
|||
PRIVMSG: (l, source) => { |
|||
const { params, query } = parseLine(l) |
|||
emitter.emit('message', users[source].nick, params[0], query) |
|||
}, |
|||
// :42XAAAAAC JOIN 1569149395 #test +
|
|||
JOIN: (l, source) => { |
|||
const { params } = parseLine(l) |
|||
channels[params[1]].nicklist.push({ |
|||
uid: source |
|||
}) |
|||
}, |
|||
// :42XAAAAAC PART #test :WeeChat 2.6
|
|||
PART: (l, source) => { |
|||
const { params } = parseLine(l) |
|||
for (let i = 0; i < channels[params[0]].nicklist.length; i++) { |
|||
if (channels[params[0]].nicklist[i].uid === source) { |
|||
channels[params[0]].nicklist.splice(i, 1) |
|||
return |
|||
} |
|||
} |
|||
}, |
|||
QUIT: (_l, source) => { |
|||
delete users[source] |
|||
} |
|||
} |
|||
|
|||
function parser (l) { |
|||
const split = l.split(' ') |
|||
const cmd = split[0] |
|||
const args = split.slice(1).join(' ') |
|||
if (globalCommands[cmd]) return globalCommands[cmd](args) |
|||
if (cmd[0] === ':') { |
|||
const source = cmd.slice(1) |
|||
const subcmd = split[1] |
|||
const subargs = split.slice(2).join(' ') |
|||
if (servers.indexOf(source) > -1 && serverCommands[subcmd]) serverCommands[subcmd](subargs) |
|||
if (users[source] && userCommands[subcmd]) userCommands[subcmd](subargs, source) |
|||
} |
|||
} |
|||
|
|||
socket.on('data', data => { |
|||
data.split('\r\n') |
|||
.filter(l => l !== '') |
|||
.forEach(l => { |
|||
console.log('< ' + l) |
|||
parser(l) |
|||
}) |
|||
}) |
|||
|
|||
process.on('SIGINT', () => { |
|||
socket.write('QUIT\r\n') |
|||
process.exit() |
|||
}) |
|||
|
|||
module.exports.connect = conf => new Promise((resolve, reject) => { |
|||
emitter.once('ping', resolve) |
|||
config = conf |
|||
socket.connect(config.port) |
|||
}) |
|||
module.exports.events = emitter |
|||
|
|||
const genTS = () => Math.trunc((new Date()).getTime() / 1000) |
|||
const genUID = () => { |
|||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
|||
var uid = '' |
|||
for (let i = 0; i < 6; i++) uid += chars.charAt(Math.floor(Math.random() * chars.length)) |
|||
if (users[uid]) return genUID() |
|||
return config.sid + uid |
|||
} |
|||
const getUID = nick => { |
|||
for (const key in users) if (users[key].nick === nick) return key |
|||
} |
|||
|
|||
module.exports.registerUser = nick => { |
|||
const user = { |
|||
nick: nick, |
|||
nickTS: genTS(), |
|||
modes: '+i', |
|||
username: '~' + nick, |
|||
vhost: config.vhost, |
|||
ip: '0::1', |
|||
uid: genUID() |
|||
} |
|||
users[user.uid] = user |
|||
m(`EUID ${user.nick} 1 ${user.nickTS} ${user.modes} ~${user.nick} ${user.vhost} 0::1 ${user.uid} * * :${user.nick}`) |
|||
} |
|||
module.exports.unregisterUser = nick => { |
|||
const uid = getUID(nick) |
|||
m(`:${uid} QUIT :Quit: satyr`) |
|||
delete users[uid] |
|||
} |
|||
module.exports.join = (nick, channelName) => { |
|||
const uid = getUID(nick) |
|||
if (!channels[channelName]) { |
|||
const channel = { |
|||
timestamp: genTS(), |
|||
name: channelName, |
|||
modes: '+nt', |
|||
nicklist: [{ uid: uid, mode: '' }] |
|||
} |
|||
channels[channel.name] = channel |
|||
} |
|||
m(`:${uid} JOIN ${channels[channelName].timestamp} ${channelName} +`) |
|||
} |
|||
module.exports.part = (nick, channelName) => { |
|||
const uid = getUID(nick) |
|||
m(`:${uid} PART ${channelName} :satyr`) |
|||
for (let i = 0; i < channels[channelName].nicklist.length; i++) { |
|||
if (channels[channelName].nicklist[i].uid === uid) { |
|||
channels[channelName].nicklist.splice(i, 1) |
|||
return |
|||
} |
|||
} |
|||
} |
|||
module.exports.send = (nick, channelName, message) => { |
|||
const uid = getUID(nick) |
|||
m(`:${uid} PRIVMSG ${channelName} :${message}`) |
|||
emitter.emit('message', nick, channelName, message) |
|||
} |
Loading…
Reference in new issue