CONN_ALERT = auto()
CONNECTED = auto()
CONN_WINDOW = auto()
+ DISCONNECTED = auto()
EXCEPTION = auto()
INIT_CONNECT = auto()
INIT_RECONNECT = auto()
self._login = login
self._socket: Optional[socket] = None
self._assumed_open = False
- self._recv_loop: Optional[SocketRecvLoop] = None
+ self._loop: Optional[ConnectionLoop] = None
self._broadcast(EventType.CONN_WINDOW, self._idx)
self._start_connecting()
return
self._socket.settimeout(TIMEOUT_LOOP)
self._assumed_open = True
- self._broadcast(EventType.CONNECTED)
- self._recv_loop = SocketRecvLoop(self._idx, self._q_to_main,
- self._read_lines())
+ self._loop = ConnectionLoop(self._idx, self._q_to_main,
+ self._read_lines())
+ self._broadcast(EventType.CONNECTED, self._login)
Thread(target=connect, daemon=True, args=(self,)).start()
def close(self):
- 'Close both SocketRecvLoop and socket.'
+ 'Close both ConnectionLoop and socket.'
self._assumed_open = False
- if self._recv_loop:
- self._recv_loop.stop()
- self._recv_loop = None
+ if self._loop:
+ self._loop.stop()
+ self._loop = None
if self._socket:
self._socket.close()
self._socket = None
def handle(self, event: Event) -> None:
'Process connection-directed Event into further steps.'
- if event.type_ == EventType.CONNECTED:
- self._broadcast(EventType.SEND,
- IrcMessage('USER', [self._login[0], '0', '*',
- self._login[2]]))
- self._broadcast(EventType.SEND,
- IrcMessage('NICK', [self._login[1]]))
- return
if event.type_ == EventType.INIT_RECONNECT:
if self._assumed_open:
self._broadcast(EventType.CONN_ALERT,
'so nothing to do.')
else:
self._start_connecting()
- return
- msg: IrcMessage = event.payload[1]
- if event.type_ == EventType.SEND:
+ elif event.type_ == EventType.CONNECTED:
+ assert self._loop is not None
+ self._loop.put(event)
+ elif event.type_ == EventType.DISCONNECTED:
+ self.close()
+ elif event.type_ == EventType.SEND:
+ msg: IrcMessage = event.payload[1]
self._write_line(msg.raw)
- elif event.type_ == EventType.RECV:
- if msg.verb == 'PING':
- self._broadcast(EventType.SEND,
- IrcMessage('PONG', [msg.parameters[0]]))
- elif msg.verb == 'ERROR'\
- and msg.parameters[0].startswith('Closing link:'):
- self._assumed_open = False
class IrcMessage:
def __init__(self,
verb: str,
- parameters: Optional[list[str]] = None,
+ parameters: Optional[tuple[str, ...]] = None,
source: str = '',
tags: Optional[dict[str, str]] = None
) -> None:
self.verb: str = verb
- self.parameters: list[str] = parameters or []
+ self.parameters: tuple[str, ...] = parameters or tuple()
self.source: str = source
self.tags: dict[str, str] = tags or {}
tags[key] = val
return tags
- def _split_params(str_params: str) -> list[str]:
+ def _split_params(str_params: str) -> tuple[str, ...]:
params = []
params_stage = 0 # 0: gap, 1: non-trailing, 2: trailing
for char in str_params:
params_stage += 1
continue
params[-1] += char
- return params
+ return tuple(p for p in params)
stages = [_Stage('tags', '@', _parse_tags),
_Stage('source', ':'),
def cmd__disconnect(self, quit_msg: str = 'ircplom says bye') -> None:
'Send QUIT command to server.'
self._broadcast(EventType.SEND,
- (self._conn_idx, IrcMessage('QUIT', [quit_msg])))
+ (self._conn_idx, IrcMessage('QUIT', (quit_msg, ))))
def cmd__reconnect(self) -> None:
'Attempt reconnection.'
return None
-class SocketRecvLoop(Loop):
+class ConnectionLoop(Loop):
'Loop receiving and translating socket messages towards main loop.'
def __init__(self, connection_idx: int, *args, **kwargs) -> None:
self._conn_idx = connection_idx
super().__init__(*args, **kwargs)
+ def _send(self, verb: str, parameters: tuple[str, ...]) -> None:
+ self.broadcast(EventType.SEND, (self._conn_idx,
+ IrcMessage(verb, parameters)))
+
+ def process_main(self, event: Event) -> bool:
+ if not super().process_main(event):
+ return False
+ if event.type_ == EventType.CONNECTED:
+ login = event.payload[1]
+ self._send('USER', (login[0], '0', '*', login[2]))
+ self._send('NICK', (login[1],))
+ return True
+
def process_bonus(self, yielded: str) -> None:
- self.broadcast(EventType.RECV, (self._conn_idx,
- IrcMessage.from_raw(yielded)))
+ msg = IrcMessage.from_raw(yielded)
+ self.broadcast(EventType.RECV, (self._conn_idx, msg))
+ if msg.verb == 'PING':
+ self._send('PONG', (msg.parameters[0],))
+ if msg.verb == 'ERROR'\
+ and msg.parameters[0].startswith('Closing link:'):
+ self.broadcast(EventType.DISCONNECTED, (self._conn_idx,))
class KeyboardLoop(Loop):
connections += [IrcConnection(q_to_main, len(connections),
*event.payload)]
elif event.type_ in {
- EventType.CONNECTED, EventType.INIT_RECONNECT,
- EventType.RECV, EventType.SEND}:
+ EventType.CONNECTED,
+ EventType.DISCONNECTED,
+ EventType.INIT_RECONNECT,
+ EventType.RECV,
+ EventType.SEND,
+ }:
connections[event.payload[0]].handle(event)
finally:
for conn in connections: