:type '(string))
(defcustom murk-networks
- '(("debug" "localhost" 6667 :notls)
+ '(("debug" "localhost" 6697)
("libera" "irc.libera.chat" 6697)
("tilde" "tilde.chat" 6697)
- ("freenode" "chat.freenode.net" 6697))
+ ("sdf" "irc.sdf.org" 6697)
+ ("freenode" "chat.freenode.net" 6697)
+ ("mbr" "mbrserver.com" 6667 :notls))
"IRC networks."
:type '(alist :key-type string))
+(defcustom murk-show-joins nil
+ "Set to non-nil to be notified of joins, parts and quits.")
+
(defcustom murk-display-header t
"If non-nil, use buffer header to display current host and channel."
:type '(boolean))
(equal (seq-take c1 2)
(seq-take c2 2)))))
-(defun murk-context-server (ctx) (elt ctx 0))
-(defun murk-context-channel (ctx) (elt ctx 1))
-(defun murk-context-users (ctx) (elt ctx 2))
+(defun murk-context-server (ctx)
+ (elt ctx 0))
+
+(defun murk-context-channel (ctx)
+ (elt ctx 1))
+
+(defun murk-context-users (ctx)
+ (elt ctx 2))
+
(defun murk-set-context-users (ctx users)
(setcar (cddr ctx) users))
-(defun murk-server-context-p (ctx) (not (cdr ctx)))
+
+(defun murk-server-context-p (ctx)
+ (not (cdr ctx)))
(defun murk-add-context (ctx)
(add-to-list 'murk-contexts ctx))
(concat (murk-context-channel ctx) "@"
(murk-context-server ctx))))
-(defun murk-get-context (server &optional name)
- (if name
- (let ((test-ctx (list server name)))
+(defun murk-string->context (string)
+ (if (not (string-prefix-p "#" string))
+ (murk-get-context string)
+ (let* ((parts (string-split string "@"))
+ (channel (elt parts 0))
+ (server (elt parts 1)))
+ (murk-get-context server channel))))
+
+(defun murk-get-context (server &optional channel)
+ (if channel
+ (let ((test-ctx (list server channel)))
(seq-find (lambda (ctx)
(equal (seq-take ctx 2) test-ctx))
murk-contexts))
- (assoc server murk-contexts)))
+ (car (member (list server) murk-contexts))))
(defun murk-cycle-contexts (&optional reverse)
(setq murk-contexts
(seq-take murk-contexts nminus1)))
(append (cdr murk-contexts) (list (car murk-contexts))))))
+(defun murk-switch-to-context (ctx)
+ (setq murk-contexts
+ (let* ((new-head (memq ctx murk-contexts))
+ (new-tail (take (- (length murk-contexts)
+ (length new-head)))))
+ (append new-head new-tail))))
+
(defun murk-add-context-users (ctx users)
(murk-set-context-users
ctx
(cl-union users (murk-context-users ctx))))
+(defun murk-del-context-user (ctx user)
+ (murk-set-context-users
+ ctx
+ (delete user (murk-context-users ctx))))
+
+(defun murk-del-server-user (server user)
+ (dolist (ctx murk-contexts)
+ (if (equal (murk-context-server ctx) server)
+ (murk-del-context-user ctx user))))
+
+(defun murk-rename-server-user (server old-nick new-nick)
+ (dolist (ctx murk-contexts)
+ (when (and (equal (murk-context-server ctx) server)
+ (member old-nick (murk-context-users ctx)))
+ (murk-del-context-user ctx old-nick)
+ (murk-add-context-users ctx (list new-nick)))))
+
;;; Buffer
;;
(concat (murk-context->string context) " <" from ">"))
text)))
+(defun murk-display-action (server from to action-text)
+ (let ((context (if (string-prefix-p "#" to)
+ (murk-get-context server to)
+ (murk-get-context server))))
+ (murk-display-string
+ context
+ (concat (murk-context->string context) " *")
+ from " " action-text)))
+
(defun murk-display-notice (context &rest notices)
(murk-display-string
context
("PONG")
- ("001"
+ ("001" ; RPL_WELCOME
(let* ((params (murk-msg-params msg))
(nick (elt params 0))
(text (string-join (seq-drop params 1) " ")))
" users in " channel)
(murk-display-notice nil "End of " channel " names list."))))
- ("331"
+ ("331" ; RPL_NOTOPIC
(let* ((params (murk-msg-params msg))
(channel (elt params 1))
(ctx (murk-get-context server channel)))
(murk-display-notice ctx "No topic set.")))
- ("332"
+ ("332" ; RPL_TOPIC
(let* ((params (murk-msg-params msg))
(channel (elt params 1))
(topic (elt params 2))
"Joining channel " channel " on " server)
(murk-render-prompt)))
+ ("JOIN"
+ (let* ((channel (car (murk-msg-params msg)))
+ (nick (murk-msg-src msg))
+ (ctx (murk-get-context server channel)))
+ (murk-add-context-users ctx (list nick))
+ (if murk-show-joins
+ (murk-display-notice ctx nick " joined channel " channel
+ " on " server))))
+
((and "PART"
(guard (equal (murk-connection-nick server)
(murk-msg-src msg))))
(murk-remove-context (list server channel))
(murk-render-prompt)))
+ ("PART"
+ (let* ((channel (car (murk-msg-params msg)))
+ (nick (murk-msg-src msg))
+ (ctx (murk-get-context server channel)))
+ (murk-del-context-user ctx nick)
+ (if murk-show-joins
+ (murk-display-notice ctx nick " left channel " channel
+ " on " server))))
+
+ ((and "NICK"
+ (guard (equal (murk-connection-nick server)
+ (murk-msg-src msg))))
+ (let ((new-nick (car (murk-msg-params msg)))
+ (old-nick (murk-connection-nick server)))
+ (murk-set-connection-nick server new-nick)
+ (murk-rename-server-user server old-nick new-nick)
+ (murk-display-notice nil "Nick set to " new-nick " on " server)))
+
+ ("NICK"
+ (let ((old-nick (murk-msg-src msg))
+ (new-nick (car (murk-msg-params msg))))
+ (murk-display-notice nil old-nick " is now known as " new-nick
+ " on " server)
+ (murk-rename-server-user server old-nick new-nick)))
+
+ ("TOPIC"
+ (let ((channel (car (murk-msg-params msg)))
+ (nick (murk-msg-src msg))
+ (topic (cadr (murk-msg-params msg))))
+ (murk-display-notice (murk-get-context server channel)
+ nick " set the topic: " topic)))
+
("QUIT"
(let ((nick (murk-msg-src msg))
(reason (mapconcat 'identity (murk-msg-params msg) " ")))
- (murk-del-user nick)
+ (murk-del-server-user server nick)
(if murk-show-joins
(murk-display-notice nil nick " quit: " reason))))
+ ("NOTICE"
+ (let ((nick (murk-msg-src msg))
+ (channel (car (murk-msg-params msg)))
+ (text (cadr (murk-msg-params msg))))
+ (pcase text
+ ((rx (: "\01VERSION "
+ (let version (* (not "\01")))
+ "\01"))
+ (murk-display-notice (murk-get-context server)
+ "CTCP version reply from " nick ": " version))
+ (_
+ (murk-display-notice nil text)))))
+
("PRIVMSG"
(let* ((from (murk-msg-src msg))
(params (murk-msg-params msg))
" on " server " (no response sent)"))
((rx (: "\01ACTION " (let action-text (* (not "\01"))) "\01"))
- (murk-display-action from to action-text))
+ (murk-display-action server from to action-text))
(_
(murk-display-message server from to text)))))
(defvar murk-command-table
'(("DEBUG" "Toggle debug mode on/off." murk-command-debug murk-boolean-completions)
("HEADER" "Toggle display of header." murk-command-header murk-boolean-completions)
+ ("SHOWJOINS" "Toggles display of joins/parts." murk-command-showjoins murk-boolean-completions)
("NETWORKS" "List known IRC networks." murk-command-networks)
("CONNECT" "Connect to an IRC network." murk-command-connect murk-network-completions)
("QUIT" "Disconnect from current network." murk-command-quit)
("JOIN" "Join one or more channels." murk-command-join)
("PART" "Leave channel." murk-command-part murk-context-completions)
("NICK" "Change nick." murk-command-nick)
+ ("LIST" "Display details of one or more channels." murk-command-list)
+ ("TOPIC" "Set/query topic for current channel." murk-command-topic)
+ ("USERS" "List nicks of users in current context." murk-command-users)
("MSG" "Send private message to user." murk-command-msg murk-nick-completions)
+ ("ME" "Display action." murk-command-me)
+ ("VERSION" "Request version of another user's client via CTCP." murk-command-version murk-nick-completions)
("CLEAR" "Clear buffer text." murk-command-clear murk-context-completions)
("HELP" "Display help on client commands." murk-command-help murk-help-completions))
"Table of commands explicitly supported by murk.")
(setq-local header-line-format nil)
(murk-display-notice nil "Header disabled.")))
-(defun murk-command-clear (params)
- (if (not params)
- (murk-clear-buffer)
- (dolist (context params)
- (murk-clear-context context))))
+(defun murk-command-showjoins (params)
+ (setq murk-show-joins
+ (if params
+ (if (equal (upcase (car params)) "ON")
+ t
+ nil)
+ (not murk-show-joins)))
+ (murk-display-notice nil "Joins/parts will now be "
+ (if murk-show-joins "shown" "hidden") "."))
(defun murk-command-connect (params)
(if params
(murk-send-msg server (murk-msg nil nil "PART" channel))
(murk-display-error "No current channel to leave"))))
+(defun murk-command-nick (params)
+ (if params
+ (let ((new-nick (string-join params " "))
+ (ctx (murk-current-context)))
+ (if ctx
+ (murk-send-msg (murk-context-server ctx)
+ (murk-msg nil nil "NICK" new-nick))
+ (murk-display-error "No current connection")))
+ (murk-display-notice nil "Usage: /nick <new-nick>")))
+
+(defun murk-command-list (params)
+ (let ((ctx (murk-current-context)))
+ (if ctx
+ (if (not params)
+ (murk-display-notice nil "This command can generate lots of output. Use `/LIST -yes' if you really want this, or `/LIST <channel_regexp>' to reduce the output.")
+ (let ((server (murk-context-server ctx)))
+ (if (equal (upcase (car params)) "-YES")
+ (murk-send-msg server (murk-msg nil nil "LIST"))
+ (murk-send-msg server (murk-msg nil nil "LIST"
+ (car params))))))
+ (murk-display-error "No current connection"))))
+
+(defun murk-command-topic (params)
+ (let ((ctx (murk-current-context)))
+ (if (and ctx (not (murk-server-context-p ctx)))
+ (let ((server (murk-context-server ctx))
+ (channel (murk-context-channel ctx)))
+ (if params
+ (murk-send-msg server
+ (murk-msg nil nil "TOPIC" channel
+ (string-join params " ")))
+ (murk-send-msg server
+ (murk-msg nil nil "TOPIC" channel))))
+ (murk-display-notice nil "No current channel."))))
+
(defun murk-command-msg (params)
(let ((server (murk-context-server (murk-current-context))))
(if (and params (>= (length params) 2))
to text))
(murk-display-notice nil "Usage: /msg <nick> <message>"))))
+(defun murk-command-me (params)
+ (let* ((ctx (murk-current-context))
+ (server (murk-context-server ctx)))
+ (if (and ctx (not (murk-server-context-p ctx)))
+ (if params
+ (let* ((channel (murk-context-channel ctx))
+ (my-nick (murk-connection-nick server))
+ (action (string-join params " "))
+ (ctcp-text (concat "\01ACTION " action "\01")))
+ (murk-send-msg server
+ (murk-msg nil nil "PRIVMSG"
+ (list channel ctcp-text)))
+ (murk-display-action server my-nick channel action))
+ (murk-display-notice nil "Usage: /me <action>"))
+ (murk-display-notice nil "No current channel."))))
+
+(defun murk-command-version (params)
+ (let ((ctx (murk-current-context)))
+ (if ctx
+ (if params
+ (let ((server (murk-context-server ctx))
+ (nick (car params)))
+ (murk-send-msg server
+ (lurk-msg nil nil "PRIVMSG"
+ (list nick "\01VERSION\01")))
+ (murk-display-notice ctx "CTCP version request sent to "
+ nick " on " server))
+ (murk-display-notice ctx "Usage: /version <nick>"))
+ (murk-display-notice nil "No current channel."))))
+
+(defun murk-command-users (_params)
+ (let ((ctx (murk-current-context)))
+ (if (and ctx (not (murk-server-context-p ctx)))
+ (let ((channel (murk-context-channel ctx))
+ (server (murk-context-server ctx))
+ (users (murk-context-users ctx)))
+ (murk-display-notice ctx "Users in " channel " on " server ":")
+ (murk-display-notice ctx (string-join users " ")))
+ (murk-display-notice nil "No current channel."))))
+
+
;;; Command entering
;;