def affect(self, target: 'Client') -> None:
target.log(msg='# connected to server', chat=CHAT_GLOB)
target.send(IrcMessage(verb='CAP', params=('LS', '302')))
- target.send(IrcMessage(verb='CAP', params=('LIST',)))
- target.send(IrcMessage(verb='CAP', params=('END',)))
target.send(IrcMessage(verb='USER', params=(getuser(), '0', '*',
target.realname)))
target.send(IrcMessage(verb='NICK', params=(target.nickname,)))
self._hostname = hostname
self._socket: Optional[socket] = None
self._recv_loop: Optional[Loop] = None
- self._cap_ls_done = False
- self._caps: dict[str, Optional[bool]] = {}
+ self.cap_neg_state: set[str] = set()
+ self.caps: dict[str, tuple[bool, str]] = {}
self.id_ = uuid4()
self.assumed_open = False
self.realname = realname
Thread(target=connect, daemon=True, args=(self,)).start()
def collect_caps(self, params: tuple[str, ...]) -> None:
- 'Record and list available or enabled server capabilities.'
- collect_enabled = params[0] != 'LS'
+ 'Record available and enabled server capabilities.'
+ verb = params[0]
+ items = params[-1].strip().split()
is_final_line = params[1] != '*'
- listed_names = params[-1].strip().split()
- if collect_enabled:
- for k in [k for k, v in self._caps.items() if v is None]:
- self._caps[k] = False
- for name in [name for name in self._caps
- if name.split('=')[0] in listed_names]:
- self._caps[name] = True
- else:
- if self._cap_ls_done:
- self._caps.clear()
- for cap_name in listed_names:
- self._caps[cap_name] = None
- self._cap_ls_done = is_final_line
- if is_final_line and collect_enabled:
- self.log('# server capabilities available (* if enabled): '
- + ', '.join([(f'(*){k}' if v else k)
- for k, v in self._caps.items()]))
+ doneness = f'{verb} done'
+ waiting = f'{verb} wait'
+ if doneness in self.cap_neg_state:
+ if verb == 'LS':
+ self.caps.clear()
+ else:
+ for name in self.caps:
+ self.caps[name] = (False, self.caps[name][1])
+ self.cap_neg_state.remove(doneness)
+ if waiting not in self.cap_neg_state:
+ self.cap_neg_state.add(waiting)
+ for item in items:
+ if verb == 'LS':
+ splitted = item.split('=', maxsplit=1)
+ self.caps[splitted[0]] = (False, ''.join(splitted[1:]))
+ else:
+ self.caps[item] = (True, self.caps[item][1])
+ if is_final_line:
+ self.cap_neg_state.remove(waiting)
+ self.cap_neg_state.add(doneness)
@abstractmethod
def log(self, msg: str, chat: str = '') -> None:
elif msg.verb == 'CAP':
if msg.params[1] in {'LS', 'LIST'}:
target.collect_caps(msg.params[1:])
+ if ('LIST done' in target.cap_neg_state
+ and 'listed' not in target.cap_neg_state):
+ target.send(IrcMessage(verb='CAP', params=('END',)))
+ target.log('# available server capabilities (enabled: "+"):')
+ for cap_name, config in target.caps.items():
+ target.log(f'# {"+" if config[0] else "-"} {cap_name}'
+ + (f' ({config[1]})' if config[1] else ''))
+ target.cap_neg_state.add('listed')
+ elif ('LS done' in target.cap_neg_state
+ and 'LIST wait' not in target.cap_neg_state):
+ target.send(IrcMessage(verb='CAP', params=('LIST',)))