|
|
@@ -205,6 +205,9 @@ class IRCClientProtocol(asyncio.Protocol): |
|
|
|
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 |
|
|
|
self.whoxQueue = collections.deque() # Names of channels that were joined successfully but for which no WHO (WHOX) query was sent yet |
|
|
|
self.whoxChannel = None # Name of channel for which a WHO query is currently running |
|
|
|
self.whoxReply = [] # List of (nickname, account) tuples from the currently running WHO query |
|
|
|
|
|
|
|
@staticmethod |
|
|
|
def nick_command(nick: str): |
|
|
@@ -320,10 +323,12 @@ class IRCClientProtocol(asyncio.Protocol): |
|
|
|
self.logger.debug(f'Message received at {time_}: {message!r}') |
|
|
|
|
|
|
|
# Queue message for storage |
|
|
|
# Note: WHOX is queued further down |
|
|
|
self.messageQueue.put_nowait((time_, b'< ' + message, None, None)) |
|
|
|
for command, channel, logMessage in self.render_message(line): |
|
|
|
self.messageQueue.put_nowait((time_, logMessage, command, channel)) |
|
|
|
|
|
|
|
maybeTriggerWhox = False |
|
|
|
# PING/PONG |
|
|
|
if line.command == 'PING': |
|
|
|
self.send(irctokens.build('PONG', line.params).format().encode('utf-8')) |
|
|
@@ -396,11 +401,33 @@ class IRCClientProtocol(asyncio.Protocol): |
|
|
|
self.channels.remove(channel) |
|
|
|
break |
|
|
|
|
|
|
|
# WHOX on successful JOIN if supported to fetch account information |
|
|
|
elif line.command == 'JOIN' and self.server.isupport.whox and line.source and self.server.casefold(line.hostmask.nickname) == self.server.casefold(self.server.nickname): |
|
|
|
self.whoxQueue.extend(line.params[0].split(',')) |
|
|
|
maybeTriggerWhox = True |
|
|
|
|
|
|
|
# WHOX response |
|
|
|
elif line.command == ircstates.numerics.RPL_WHOSPCRPL and line.params[1] == '042': |
|
|
|
self.whoxReply.append((line.params[2], line.params[3] if line.params[3] != '0' else None)) |
|
|
|
|
|
|
|
# End of WHOX response |
|
|
|
elif line.command == ircstates.numerics.RPL_ENDOFWHO: |
|
|
|
self.messageQueue.put_nowait((time_, self.render_whox(), 'WHOX', self.whoxChannel)) |
|
|
|
self.whoxChannel = None |
|
|
|
self.whoxReply = [] |
|
|
|
maybeTriggerWhox = True |
|
|
|
|
|
|
|
# General fatal ERROR |
|
|
|
elif line.command == 'ERROR': |
|
|
|
self.logger.error(f'Server sent ERROR: {message!r}') |
|
|
|
self.transport.close() |
|
|
|
|
|
|
|
# Send next WHOX if appropriate |
|
|
|
if maybeTriggerWhox and self.whoxChannel is None and self.whoxQueue: |
|
|
|
self.whoxChannel = self.whoxQueue.popleft() |
|
|
|
self.whoxReply = [] |
|
|
|
self.send(b'WHO ' + self.whoxChannel.encode('utf-8') + b' c%nat,042') |
|
|
|
|
|
|
|
def get_mode_char(self, channelUser): |
|
|
|
if channelUser is None: |
|
|
|
return '' |
|
|
@@ -471,6 +498,13 @@ class IRCClientProtocol(asyncio.Protocol): |
|
|
|
users = self.server.channels[self.server.casefold(channel)].users |
|
|
|
yield 'NAMES', channel, f'Currently in {channel}: {", ".join(self.render_nick_with_mode(u, u.nickname) for u in users.values())}' |
|
|
|
|
|
|
|
def render_whox(self): |
|
|
|
users = [] |
|
|
|
for nickname, account in self.whoxReply: |
|
|
|
accountStr = f' ({account})' if account is not None else '' |
|
|
|
users.append(f'{self.render_nick_with_mode(self.server.channels[self.server.casefold(self.whoxChannel)].users.get(self.server.casefold(nickname)), nickname)}{accountStr}') |
|
|
|
return f'Currently in {self.whoxChannel}: {", ".join(users)}' |
|
|
|
|
|
|
|
async def quit(self): |
|
|
|
# The server acknowledges a QUIT by sending an ERROR and closing the connection. The latter triggers connection_lost, so just wait for the closure event. |
|
|
|
self.logger.info('Quitting') |
|
|
|