self.update_login(nickname=msg.params[0], nick_confirmed=True)
             case 'PRIVMSG':
                 self._log(msg.params[-1], scope=LogScope.CHAT, out=False,
-                          sender=msg.source.split('!')[0],
-                          channel=msg.params[0])
+                          sender=msg.nick_from_source, channel=msg.params[0])
             case 'CAP':
                 if (result := self._caps.process_msg(msg.params[1:])):
                     if isinstance(result, str):
                 self._log(f'SASL auth {"failed" if alert else "succeeded"}',
                           alert=alert)
                 self._caps.challenge('END')
+            case 'JOIN' | 'PART':
+                user, channel = msg.nick_from_source, msg.params[-1]
+                self._log(f'{user} {msg.verb.lower()}s {channel}',
+                          scope=LogScope.CHAT, channel=channel)
 
 
 @dataclass
 
 CMD_SHORTCUTS['disconnect'] = 'window.disconnect'
 CMD_SHORTCUTS['join'] = 'window.join'
 CMD_SHORTCUTS['nick'] = 'window.nick'
+CMD_SHORTCUTS['part'] = 'window.part'
 CMD_SHORTCUTS['privmsg'] = 'window.privmsg'
 CMD_SHORTCUTS['reconnect'] = 'window.reconnect'
 
         'Attempt joining a channel.'
         self._send_msg('JOIN', (channel,))
 
+    def cmd__part(self, channel: str) -> None:
+        'Attempt joining a channel.'
+        self._send_msg('PART', (channel,))
+
     def cmd__privmsg(self, target: str, msg: str) -> None:
         'Send chat message msg to target.'
         self._send_msg('PRIVMSG', (target, msg), log_target=target, to_log=msg)
         prefix = ''
         if 'out' in kwargs:
             prefix += _LOG_PREFIX_OUT if kwargs['out'] else _LOG_PREFIX_IN
+            if scope == LogScope.CHAT:
+                nickname = (self.nickname if kwargs['out']
+                            else kwargs['sender'])
+                prefix += f' [{nickname}]'
         else:
             prefix += _LOG_PREFIX_SERVER
-        if scope == LogScope.CHAT:
-            nickname = self.nickname if kwargs['out'] else kwargs['sender']
-            prefix += f' [{nickname}]'
         self._tui_log(msg, scope=scope, prefix=prefix, **kwargs)
 
     def update(self, **kwargs) -> bool:
             if target == LogScope.SERVER:
                 return [m.window(LogScope.SERVER), m.window(LogScope.RAW)]
             if target == LogScope.CHAT:
-                chatname = (kwargs['target'] if kwargs['out']
-                            else kwargs.get('channel', kwargs['sender']))
+                chatname = (kwargs['target'] if kwargs.get('out', False)
+                            else kwargs.get('sender', kwargs['channel']))
                 return [m.window(LogScope.CHAT, chatname=chatname)]
             return [m.window(target)]
         return super()._log_target_wins(**kwargs)