Browse Source

Add userhost-in-names, away-notify, account-notify, and extended-join capability support

master
JustAnotherArchivist 3 years ago
parent
commit
b604607d0f
1 changed files with 31 additions and 11 deletions
  1. +31
    -11
      irclog.py

+ 31
- 11
irclog.py View File

@@ -203,6 +203,8 @@ class IRCClientProtocol(asyncio.Protocol):
self.sasl = bool(self.config['irc']['certfile'] and self.config['irc']['certkeyfile'])
self.authenticated = False
self.server = ircstates.Server(self.config['irc']['host'])
self.capReqsPending = set() # Capabilities requested from the server but not yet ACKd or NAKd
self.caps = set() # Capabilities acknowledged by the server

@staticmethod
def nick_command(nick: str):
@@ -236,8 +238,12 @@ class IRCClientProtocol(asyncio.Protocol):
self.logger.info('IRC connected')
self.transport = transport
self.connected = True
caps = [b'userhost-in-names', b'away-notify', b'account-notify', b'extended-join']
if self.sasl:
self.send(b'CAP REQ :sasl')
caps.append(b'sasl')
for cap in caps:
self.capReqsPending.add(cap.decode('ascii'))
self.send(b'CAP REQ :' + cap)
self.send(self.nick_command(self.config['irc']['nick']))
self.send(self.user_command(self.config['irc']['nick'], self.config['irc']['real']))

@@ -322,18 +328,29 @@ class IRCClientProtocol(asyncio.Protocol):
if line.command == 'PING':
self.send(irctokens.build('PONG', line.params).format().encode('utf-8'))

# SASL
elif line.command == 'CAP' and self.sasl:
if line.params[-2] == 'ACK' and 'sasl' in line.params[-1].split(' '):
self.send(b'AUTHENTICATE EXTERNAL')
else:
self.logger.error(f'Received unexpected CAP reply {message!r}, terminating connection')
self.transport.close()
# IRCv3 and SASL
elif line.command == 'CAP':
if line.params[1] == 'ACK':
for cap in line.params[2].split(' '):
self.logger.debug('CAP ACK: {cap}')
self.caps.add(cap)
if cap == 'sasl' and self.sasl:
self.send(b'AUTHENTICATE EXTERNAL')
else:
self.capReqsPending.remove(cap)
elif line.params[1] == 'NAK':
self.logger.warning(f'Failed to activate CAP(s): {line.params[2]}')
for cap in line.params[2].split(' '):
self.capReqsPending.remove(cap)
if len(self.capReqsPending) == 0:
self.send(b'CAP END')
elif line.command == 'AUTHENTICATE' and line.params == ['+']:
self.send(b'AUTHENTICATE +')
elif line.command == '903': # SASL auth successful
self.authenticated = True
self.send(b'CAP END')
self.capReqsPending.remove('sasl')
if len(self.capReqsPending) == 0:
self.send(b'CAP END')
elif line.command in ('902', '904', '905', '906', '908'):
self.logger.error('SASL error, terminating connection')
self.transport.close()
@@ -402,9 +419,10 @@ class IRCClientProtocol(asyncio.Protocol):
if line.command == 'JOIN':
# Although servers SHOULD NOT send multiple channels in one message per the modern IRC docs <https://modern.ircdocs.horse/#join-message>, let's do the safe thing...
channels = [line.params[0]] if ',' not in line.params[0] else line.params[0].split(',')
account = f' ({line.params[-2]})' if 'extended-join' in self.caps and line.params[-2] != '*' else ''
for channel in channels:
# There can't be a mode set yet on the JOIN, so no need to use get_mode_nick (which would complicate the self-join).
yield 'JOIN', channel, f'{line.hostmask.nickname} joins {channel}'
yield 'JOIN', channel, f'{line.hostmask.nickname}{account} joins {channel}'
elif line.command in ('PRIVMSG', 'NOTICE'):
channel = line.params[0]
if channel not in self.server.channels:
@@ -415,7 +433,7 @@ class IRCClientProtocol(asyncio.Protocol):
reason = f' [{line.params[1]}]' if len(line.params) == 2 else ''
for channel in channels:
yield 'PART', channel, f'{get_mode_nick(channel)} leaves {channel}'
elif line.command in ('QUIT', 'NICK'):
elif line.command in ('QUIT', 'NICK', 'ACCOUNT'):
if line.hostmask.nickname == self.server.nickname:
channels = self.channels
elif sourceUser is not None:
@@ -428,6 +446,8 @@ class IRCClientProtocol(asyncio.Protocol):
elif line.command == 'NICK':
newMode = self.get_mode_char(self.server.channels[self.server.casefold(channel)].users[self.server.casefold(line.hostmask.nickname)])
message = f'{get_mode_nick(channel)} is now known as {newMode}{line.params[0]}'
elif line.command == 'ACCOUNT':
message = f'{get_mode_nick(channel)} is now authenticated as {line.params[0]}'
yield line.command, channel, message
elif line.command == 'MODE' and line.params[0][0] in ('#', '&'):
yield 'MODE', line.params[0], f'{get_mode_nick(line.params[0])} sets mode: {" ".join(line.params[1:])}'


Loading…
Cancel
Save