In theory everything is ported now, just need some tests
This commit is contained in:
parent
69260ab280
commit
71ab3cad19
216
src/server.ts
216
src/server.ts
|
@ -46,6 +46,16 @@ export class Server extends EventEmitter {
|
||||||
this.on('TAGMSG', this.handleMessage)
|
this.on('TAGMSG', this.handleMessage)
|
||||||
this.on(Numeric.RPL_VISIBLEHOST, this.handleVisiblehost)
|
this.on(Numeric.RPL_VISIBLEHOST, this.handleVisiblehost)
|
||||||
this.on(Numeric.RPL_WHOREPLY, this.handleWho)
|
this.on(Numeric.RPL_WHOREPLY, this.handleWho)
|
||||||
|
this.on(Numeric.RPL_WHOISUSER, this.handleWhoisUser)
|
||||||
|
this.on('CHGHOST', this.handleChghost)
|
||||||
|
this.on('SETNAME', this.handleSetname)
|
||||||
|
this.on('RENAME', this.handleRename)
|
||||||
|
this.on(Numeric.RPL_AWAY, this.handleAwayNum)
|
||||||
|
this.on('AWAY', this.handleAway)
|
||||||
|
this.on('ACCOUNT', this.handleAccount)
|
||||||
|
this.on('CAP', this.handleCap)
|
||||||
|
this.on(Numeric.RPL_LOGGEDIN, this.handleLoggedin)
|
||||||
|
this.on(Numeric.RPL_LOGGEDOUT, this.handleLoggedout)
|
||||||
}
|
}
|
||||||
|
|
||||||
nickname = ''
|
nickname = ''
|
||||||
|
@ -70,9 +80,9 @@ export class Server extends EventEmitter {
|
||||||
isupport = new ISupport()
|
isupport = new ISupport()
|
||||||
|
|
||||||
hasCap = false
|
hasCap = false
|
||||||
#tempCaps: Record<string, string> = {}
|
#tempCaps: Map<string, string> = new Map()
|
||||||
availableCaps: Record<string, string> = {}
|
availableCaps: Map<string, string> = new Map()
|
||||||
agreedCaps: string[] = []
|
agreedCaps: Set<string> = new Set()
|
||||||
|
|
||||||
recv (data: Uint8Array) {
|
recv (data: Uint8Array) {
|
||||||
const lines = this.#decoder.push(data)
|
const lines = this.#decoder.push(data)
|
||||||
|
@ -133,7 +143,7 @@ export class Server extends EventEmitter {
|
||||||
return new Line({ command: 'WHO', params: [target, `n%afhinrstu,${WHO_TYPE}`] })
|
return new Line({ command: 'WHO', params: [target, `n%afhinrstu,${WHO_TYPE}`] })
|
||||||
}
|
}
|
||||||
|
|
||||||
private selfHostmask (hostmask: Hostmask) {
|
private thisHostmask (hostmask: Hostmask) {
|
||||||
this.nickname = hostmask.nickname
|
this.nickname = hostmask.nickname
|
||||||
if (hostmask.username) this.username = hostmask.username
|
if (hostmask.username) this.username = hostmask.username
|
||||||
if (hostmask.hostname) this.hostname = hostmask.hostname
|
if (hostmask.hostname) this.hostname = hostmask.hostname
|
||||||
|
@ -211,7 +221,7 @@ export class Server extends EventEmitter {
|
||||||
this.channels.set(channelLower, channel)
|
this.channels.set(channelLower, channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selfHostmask(line.hostmask)
|
this.thisHostmask(line.hostmask)
|
||||||
if (extended) {
|
if (extended) {
|
||||||
this.account = account
|
this.account = account
|
||||||
this.realname = realname
|
this.realname = realname
|
||||||
|
@ -318,7 +328,7 @@ export class Server extends EventEmitter {
|
||||||
if (hm.username) user.username = hm.username
|
if (hm.username) user.username = hm.username
|
||||||
if (hm.hostname) user.hostname = hm.hostname
|
if (hm.hostname) user.hostname = hm.hostname
|
||||||
|
|
||||||
if (this.isMe(nicknameLower)) this.selfHostmask(hm)
|
if (this.isMe(nicknameLower)) this.thisHostmask(hm)
|
||||||
|
|
||||||
for (const mode of modes) {
|
for (const mode of modes) {
|
||||||
if (!channelUser.modes.has(mode)) channelUser.modes.add(mode)
|
if (!channelUser.modes.has(mode)) channelUser.modes.add(mode)
|
||||||
|
@ -505,7 +515,7 @@ export class Server extends EventEmitter {
|
||||||
private handleMessage (line: Line) {
|
private handleMessage (line: Line) {
|
||||||
if (!line.source) return undefined
|
if (!line.source) return undefined
|
||||||
// const message = line.params[1]
|
// const message = line.params[1]
|
||||||
if (this.isMe(line.hostmask.nickname)) this.selfHostmask(line.hostmask)
|
if (this.isMe(line.hostmask.nickname)) this.thisHostmask(line.hostmask)
|
||||||
|
|
||||||
let user = this.getUser(line.hostmask.nickname)
|
let user = this.getUser(line.hostmask.nickname)
|
||||||
if (!user) user = new User(new Name(line.hostmask.nickname, this.casefold(line.hostmask.nickname)))
|
if (!user) user = new User(new Name(line.hostmask.nickname, this.casefold(line.hostmask.nickname)))
|
||||||
|
@ -564,4 +574,196 @@ export class Server extends EventEmitter {
|
||||||
user.away = away
|
user.away = away
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleWhox ({ params }: Line) {
|
||||||
|
if (params[1] !== WHO_TYPE || params.length !== 10) return
|
||||||
|
|
||||||
|
const nickname = params[6]
|
||||||
|
const username = params[2]
|
||||||
|
const hostname = params[4]
|
||||||
|
const status = params[7]
|
||||||
|
const away = status.includes('G') ? '' : undefined
|
||||||
|
const realname = params[9]
|
||||||
|
const account = params[8] === '0' ? '' : params[8]
|
||||||
|
const server = params[5] === '*' ? undefined : params[5]
|
||||||
|
|
||||||
|
let ip: string | undefined
|
||||||
|
if (params[3] !== '255.255.255.255') {
|
||||||
|
// TODO: handle IP
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = this.getUser(nickname)
|
||||||
|
if (user) {
|
||||||
|
user.username = username
|
||||||
|
user.hostname = hostname
|
||||||
|
user.realname = realname
|
||||||
|
user.account = account
|
||||||
|
user.server = server
|
||||||
|
user.away = away
|
||||||
|
user.ip = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isMe(nickname)) {
|
||||||
|
this.username = username
|
||||||
|
this.hostname = hostname
|
||||||
|
this.realname = realname
|
||||||
|
this.account = account
|
||||||
|
this.server = server
|
||||||
|
this.away = away
|
||||||
|
this.ip = ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WHOIS "user" line, one of "WHOIS nickname" response lines
|
||||||
|
private handleWhoisUser ({ params }: Line) {
|
||||||
|
const nickname = params[1]
|
||||||
|
const username = params[2]
|
||||||
|
const hostname = params[3]
|
||||||
|
const realname = params[5]
|
||||||
|
|
||||||
|
const user = this.getUser(username)
|
||||||
|
if (user) {
|
||||||
|
user.username = username
|
||||||
|
user.hostname = hostname
|
||||||
|
user.realname = realname
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isMe(nickname)) {
|
||||||
|
this.username = username
|
||||||
|
this.hostname = hostname
|
||||||
|
this.realname = realname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleChghost ({ params, hostmask }: Line) {
|
||||||
|
const username = params[0]
|
||||||
|
const hostname = params[1]
|
||||||
|
|
||||||
|
const user = this.getUser(hostmask.nickname)
|
||||||
|
if (user) {
|
||||||
|
user.username = username
|
||||||
|
user.hostname = hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isMe(hostmask.nickname)) {
|
||||||
|
this.username = username
|
||||||
|
this.hostname = hostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleSetname ({ params, hostmask }: Line) {
|
||||||
|
const realname = params[0]
|
||||||
|
|
||||||
|
const user = this.getUser(hostmask.nickname)
|
||||||
|
if (user) user.realname = realname
|
||||||
|
|
||||||
|
if (this.isMe(hostmask.nickname)) this.realname = realname
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRename ({ params }: Line) {
|
||||||
|
const sourceFold = this.casefold(params[0])
|
||||||
|
const rename = params[1]
|
||||||
|
const renameFold = this.casefold(rename)
|
||||||
|
|
||||||
|
const channel = this.getChannel(sourceFold)
|
||||||
|
if (channel) {
|
||||||
|
channel.changeName(rename, renameFold)
|
||||||
|
for (const [nickname] of channel.users) {
|
||||||
|
const user = this.getUser(nickname)
|
||||||
|
if (!user) continue
|
||||||
|
user.channels.delete(sourceFold)
|
||||||
|
user.channels.add(sourceFold)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channels.delete(sourceFold)
|
||||||
|
this.channels.set(renameFold, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleAwayNum ({ params }: Line) {
|
||||||
|
const nickname = params[1]
|
||||||
|
const reason = params[2]
|
||||||
|
|
||||||
|
const user = this.getUser(nickname)
|
||||||
|
if (user) user.away = reason
|
||||||
|
if (this.isMe(nickname)) this.away = reason
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleAway ({ params, hostmask }: Line) {
|
||||||
|
const away = params[0]
|
||||||
|
|
||||||
|
const user = this.getUser(hostmask.nickname)
|
||||||
|
if (user) user.away = away
|
||||||
|
if (this.isMe(hostmask.nickname)) this.away = away
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleAccount ({ params, hostmask }: Line) {
|
||||||
|
const account = params[0].replace(/(^\*+|\*+$)/g, '')
|
||||||
|
|
||||||
|
const user = this.getUser(hostmask.nickname)
|
||||||
|
if (user) user.account = account
|
||||||
|
if (this.isMe(hostmask.nickname)) this.account = account
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCap ({ params }: Line) {
|
||||||
|
this.hasCap = true
|
||||||
|
const subcommand = params[1].toLocaleUpperCase()
|
||||||
|
const multiline = params[2] === '*'
|
||||||
|
const caps = params[2 + (multiline ? 1 : 0)]
|
||||||
|
|
||||||
|
const tokens: Map<string, string> = new Map()
|
||||||
|
const tokensStr: string[] = [] // TODO: used?
|
||||||
|
|
||||||
|
for (const cap of caps.split(' ').filter(c => !!c)) {
|
||||||
|
tokensStr.push(cap)
|
||||||
|
const [key, val] = cap.split(/=(.*)/)
|
||||||
|
tokens.set(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (subcommand) {
|
||||||
|
case 'LS': {
|
||||||
|
this.#tempCaps = new Map([...this.#tempCaps, ...tokens])
|
||||||
|
if (!multiline) {
|
||||||
|
this.availableCaps = new Map(this.#tempCaps)
|
||||||
|
this.#tempCaps.clear()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'NEW':
|
||||||
|
this.availableCaps = new Map([...this.availableCaps, ...tokens])
|
||||||
|
break
|
||||||
|
case 'DEL': {
|
||||||
|
for (const [key] of tokens) {
|
||||||
|
this.availableCaps.delete(key)
|
||||||
|
this.agreedCaps.delete(key)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'ACK': {
|
||||||
|
for (let [key] of tokens) {
|
||||||
|
if (key.startsWith('-')) {
|
||||||
|
key = key.substring(1)
|
||||||
|
this.agreedCaps.delete(key)
|
||||||
|
} else if (this.availableCaps.has(key)) {
|
||||||
|
this.agreedCaps.add(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleLoggedin ({ params }: Line) {
|
||||||
|
const hm = hostmask(params[1])
|
||||||
|
const account = params[2]
|
||||||
|
|
||||||
|
this.account = account
|
||||||
|
this.thisHostmask(hm)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleLoggedout ({ params }: Line) {
|
||||||
|
const hm = hostmask(params[1])
|
||||||
|
|
||||||
|
this.account = undefined
|
||||||
|
this.thisHostmask(hm)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue