;;; liece-handle.el --- implementation of IRC message handlers ;; Copyright (C) 1998-2000 Daiki Ueno ;; Author: Daiki Ueno ;; Created: 1998-09-28 ;; Revised: 1998-11-25 ;; Keywords: IRC, liece ;; This file is part of Liece. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; ;;; Code: (eval-when-compile (require 'liece-inlines) (require 'liece-misc) (require 'liece-intl)) (require 'liece-message) (require 'liece-filter) (require 'liece-handler) (defmacro liece-handle-prepare-comment (rest &optional quote) `(if (zerop (length ,rest)) "" (if ,quote (regexp-quote (format " (%s)" ,rest)) (format " (%s)" ,rest)))) (defmacro liece-handle-message-check-empty (msg) `(string= ,msg "")) (defmacro liece-handle-message-check-ignored (prefix rest) `(and ,prefix (liece-ignore-this-p ,prefix liece-user-at-host) (liece-message-from-ignored ,prefix ,rest))) (defmacro liece-handle-check-changes-ignored () 'liece-ignore-changes) (defconst liece-handle-ctcp-message-regexp "\001\\(.*\\)\001") (defmacro liece-handle-ctcp-message-p (msg) `(string-match liece-handle-ctcp-message-regexp ,msg)) (autoload 'liece-ctcp-message "liece-ctcp") (autoload 'liece-ctcp-notice "liece-ctcp") (liece-handler-define-backend "generic") (mapcar (lambda (message) (liece-handler-define-function message '(prefix rest "generic") (intern (format "liece-handle-%s-message" message))) (defvar ,(intern (format "liece-%s-hook" message)) nil) (defvar ,(intern (format "liece-after-%s-hook" message)) nil)) '("nick" "notice" "privmsg" "ping" "wall" "wallops" "quit" "topic" "mode" "kick" "invite" "kill" "join" "part" "silence")) (defun* liece-handle-nick-message (prefix rest) (let ((chnls (liece-nick-get-joined-channels prefix))) (liece-nick-change prefix rest) (cond ((liece-nick-equal prefix liece-real-nickname) (setq liece-nickname-last liece-real-nickname liece-real-nickname rest)) ((liece-nick-member prefix liece-current-chat-partners) (setq liece-current-chat-partners (string-list-modify-ignore-case (list (cons prefix rest)) liece-current-chat-partners)) (setcar (string-assoc-ignore-case prefix liece-nick-buffer-alist) rest) (setcar (string-assoc-ignore-case prefix liece-channel-buffer-alist) rest) (if (liece-nick-equal prefix liece-current-chat-partner) (setq liece-current-chat-partner rest)) (add-to-list 'chnls rest) (liece-channel-change))) (if (liece-handle-check-changes-ignored) (return-from liece-handle-nick-message)) (liece-insert-change (append (liece-pick-buffer chnls) liece-D-buffer liece-O-buffer) (format (_ "%s is now known as %s\n") prefix rest)))) (defun* liece-handle-notice-message (prefix rest) (if (liece-handle-message-check-ignored prefix rest) (return-from liece-handle-notice-message)) (or liece-ignore-extra-notices prefix (string-match "as being away" rest) (return-from liece-handle-notice-message)) ;; No prefix. This is a server notice. (when (and (null prefix) (string-match "^[^ ]* +:?" rest)) (liece-insert-notice (append liece-D-buffer liece-O-buffer) (concat (substring rest (match-end 0)) "\n")) (return-from liece-handle-notice-message)) (multiple-value-bind (chnl temp) (liece-split-line rest) ;; This is a ctcp reply but contains additional messages ;; at the left or/and right side. (if (liece-handle-ctcp-message-p temp) (setq temp (liece-ctcp-notice prefix temp))) (if (liece-handle-message-check-empty temp) (return-from liece-handle-notice-message)) ;; Normal message via notice. (setq chnl (liece-channel-virtual chnl)) (let ((liece-message-target chnl) (liece-message-speaker prefix) (liece-message-type 'notice)) (liece-display-message temp)))) (defun* liece-handle-privmsg-message (prefix rest) (if (liece-handle-message-check-ignored prefix rest) (return-from liece-handle-privmsg-message)) (multiple-value-bind (chnl temp) (liece-split-line rest) (setq temp (or temp "")) ;; This is a ctcp request but contains additional messages ;; at the left or/and right side. (if (liece-handle-ctcp-message-p temp) (setq temp (liece-ctcp-message prefix chnl temp))) (if (liece-handle-message-check-empty temp) (return-from liece-handle-privmsg-message)) (setq chnl (liece-channel-virtual chnl)) (when liece-beep-on-bells (if (string-match "\007" rest) (liece-beep)) (if (liece-nick-equal chnl liece-real-nickname) (and liece-beep-when-privmsg (liece-beep)) (with-current-buffer (if liece-channel-buffer-mode (liece-pick-buffer-1 chnl) liece-dialogue-buffer) (if liece-beep (liece-beep)))) (dolist (word liece-beep-words-list) (if (string-match word rest) (liece-beep)))) ;; Append timestamp if we are being away. (if (and (string-equal "A" liece-away-indicator) (liece-nick-equal chnl liece-real-nickname)) (setq temp (concat temp " (" (funcall liece-format-time-function (current-time)) ")"))) ;; Normal message. (let ((liece-message-target chnl) (liece-message-speaker prefix) (liece-message-type 'privmsg)) (liece-display-message temp)) ;; Append to the unread list. (let ((item (if (eq liece-command-buffer-mode 'chat) liece-current-chat-partner liece-current-channel))) (unless (liece-channel-equal chnl item) (if (liece-channel-unread-p chnl) (setq liece-channel-unread-list (delete chnl liece-channel-unread-list))) (setq liece-channel-unread-list (cons chnl liece-channel-unread-list)) (run-hook-with-args 'liece-channel-unread-functions chnl))) (if (and (liece-nick-equal chnl liece-real-nickname) (not (liece-nick-equal prefix liece-current-chat-partner))) (liece-message (_ "A private message has arrived from %s") prefix)))) (defun liece-handle-ping-message (prefix rest) (liece-send "PONG :%s" rest) (liece-command-timestamp-if-interval-expired t) (liece-maybe-poll)) (defun liece-handle-wall-message (prefix rest) (liece-insert-broadcast (append liece-D-buffer liece-O-buffer) (concat (if prefix (concat "from " prefix) "") " " rest "\n"))) (defun liece-handle-wallops-message (prefix rest) (if liece-show-wallops (liece-insert-wallops (append liece-D-buffer liece-O-buffer) (concat (if prefix prefix "UNKNOWN") ": " rest "\n"))) (liece-insert-wallops liece-W-buffer (concat (if prefix (concat "from " prefix) "") " " rest "\n"))) (defun* liece-handle-quit-message (prefix rest) (let ((chnls (liece-nick-get-joined-channels prefix)) text match default) ;; Mark temporary apart, if quitting user is one of our chat partners. (when (liece-nick-member prefix liece-current-chat-partners) (add-to-list 'chnls prefix) (liece-nick-mark-as-part t prefix)) (if (liece-handle-check-changes-ignored) (return-from liece-handle-quit-message)) (cond (liece-compress-changes (setq text (format (_ " \\(has\\|have\\) left IRC%s") (liece-handle-prepare-comment rest t)) match (format "^%s%s.*%s$" (if liece-display-time liece-time-prefix-regexp "") (regexp-quote liece-change-prefix) (regexp-quote text)) default (format (_ "%s%s has left IRC%s\n") liece-change-prefix prefix (liece-handle-prepare-comment rest))) (liece-replace (append (liece-pick-buffer chnls) liece-D-buffer liece-O-buffer) match default text (format (_ ", %s have left IRC%s") prefix (liece-handle-prepare-comment rest)))) (t (liece-insert-change (append (liece-pick-buffer chnls) liece-D-buffer liece-O-buffer) (format (_ "%s has left IRC%s\n") (liece-handle-prepare-comment rest))))) (liece-nick-change prefix nil))) (defun* liece-handle-topic-message (prefix rest) (multiple-value-bind (chnl topic) (liece-split-line rest) (setq chnl (liece-channel-virtual chnl) topic (or topic "")) (liece-channel-set-topic topic chnl) (if (liece-handle-check-changes-ignored) (return-from liece-handle-topic-message)) (liece-insert-change (liece-pick-buffer chnl) (format (_ "New topic on channel %s set by %s: %s\n") chnl prefix topic)) (liece-insert-change (if (liece-nick-equal chnl liece-current-channel) liece-D-buffer (append liece-D-buffer liece-O-buffer)) (format (_ "New topic on channel %s set by %s: %s\n") chnl prefix topic)) (liece-set-channel-indicator))) (defun* liece-handle-mode-message (prefix rest) (if (liece-handle-check-changes-ignored) (return-from liece-handle-mode-message)) (let (mflgs margs val chnl mode chnlp) (if (string-match "\\([^ ]*\\) *:?" rest) (progn (setq chnl (match-string 1 rest) mode (substring rest (match-end 0))) (if (liece-channel-p chnl) (setq chnl (liece-channel-virtual chnl) chnlp t)) (if (string-match " *$" mode) (setq mode (substring mode 0 (match-beginning 0))))) (return-from liece-handle-mode-message)) ;; parse modes (when (string-match "\\([^ ]*\\) *" mode) (setq mflgs (liece-string-to-list (match-string 1 mode)) margs (delete "" (split-string (substring mode (match-end 0)) "[ ]+"))) (while mflgs (cond ((eq ?- (car mflgs)) (setq val nil)) ((eq ?+ (car mflgs)) (setq val t)) ((and chnlp (eq ?o (car mflgs))) (liece-channel-set-operator chnl (car margs) val) (setq margs (cdr margs))) ((and chnlp (eq ?v (car mflgs))) (liece-channel-set-voice chnl (car margs) val) (setq margs (cdr margs))) ((and chnlp (eq ?b (car mflgs))) (liece-channel-set-ban chnl (car margs) val) (setq margs (cdr margs))) ((and chnlp (eq ?e (car mflgs))) (liece-channel-set-exception chnl (car margs) val) (setq margs (cdr margs))) ((and chnlp (eq ?I (car mflgs))) (liece-channel-set-invite chnl (car margs) val) (setq margs (cdr margs))) (chnlp (liece-channel-set-mode chnl (car mflgs) val)) (t (liece-nick-set-mode chnl (car mflgs) val))) (setq mflgs (cdr mflgs)))) (liece-set-channel-indicator) (cond (liece-compress-changes (let* ((text (concat (regexp-quote rest) "\n")) (match (format (_ "^%s%sNew mode for %s set by %s: ") (if liece-display-time liece-time-prefix-regexp "") (regexp-quote liece-change-prefix) (regexp-quote chnl) (regexp-quote prefix))) (default (format (_ "%sNew mode for %s set by %s: %s\n") liece-change-prefix chnl prefix mode))) (liece-replace (liece-pick-buffer chnl) match default text (concat ", " mode "\n")) (liece-replace (if (and liece-current-channel (liece-channel-equal chnl liece-current-channel)) liece-D-buffer (append liece-D-buffer liece-O-buffer)) match default text (concat ", " mode "\n")))) (t (liece-insert-change (liece-pick-buffer chnl) (format (_ "New mode for %s set by %s: %s\n") chnl prefix mode)) (liece-insert-change (if (and liece-current-channel (liece-channel-equal chnl liece-current-channel)) liece-D-buffer (append liece-D-buffer liece-O-buffer)) (format (_ "New mode for %s set by %s: %s\n") chnl prefix mode)))))) (defun* liece-handle-kick-message (prefix rest) (if (/= 3 (length (setq rest (liece-split-line rest)))) (return-from liece-handle-kick-message)) (multiple-value-bind (chnl nick message) rest (setq chnl (liece-channel-virtual chnl)) (if (liece-nick-equal nick liece-real-nickname) (progn (liece-insert-change (liece-pick-buffer chnl) (format (_ "You were kicked off channel %s by %s (%s).\n") chnl prefix message)) (liece-channel-part chnl)) (liece-nick-part nick chnl)) (if (liece-handle-check-changes-ignored) (return-from liece-handle-kick-message)) (liece-insert-change (append (liece-pick-buffer chnl) (if (liece-channel-equal chnl liece-current-channel) liece-D-buffer (append liece-D-buffer liece-O-buffer))) (format "%s has kicked %s out%s%s\n" prefix nick (if (string= (or liece-current-channel "") chnl) "" (format " from channel %s" chnl)) (if (not message) "" (format " (%s)" message)))))) (defun* liece-handle-invite-message (prefix rest) (or (string-match " +:" rest) (return-from liece-handle-invite-message)) (and liece-beep-when-invited liece-beep-on-bells (liece-beep)) (let ((chnl (liece-channel-virtual (substring rest (match-end 0))))) (liece-insert-info (append liece-D-buffer liece-O-buffer) (format "%s invites you to channel %s\n" prefix chnl)) (setq liece-default-channel-candidate chnl))) (defun* liece-handle-kill-message (prefix rest) (or (string-match " +:" rest) (return-from liece-handle-kill-message)) (let ((path (substring rest (match-end 0)))) (liece-insert-info (append liece-D-buffer liece-O-buffer) (format "You were killed by %s. (Path: %s. RIP)\n" prefix path))) (liece-close-server)) (defun* liece-handle-join-message (prefix rest) (let (flag (xnick prefix) (nick prefix) (chnl rest)) (cond ((string-match "\007[ov]" chnl) (setq flag (aref (match-string 0 chnl) 1) chnl (substring rest 0 (match-beginning 0)))) ((string-match " +$" chnl) (setq chnl (substring chnl 0 (match-beginning 0))))) (setq chnl (liece-channel-virtual chnl)) (liece-nick-set-user-at-host nick liece-user-at-host) (if (liece-nick-equal nick liece-real-nickname) (progn (and liece-gather-channel-modes (not (liece-channel-modeless-p (liece-channel-real chnl))) (liece-send "MODE %s " (liece-channel-real chnl))) (liece-channel-join chnl)) (liece-nick-join nick chnl)) (cond ((eq flag ?o) (liece-channel-set-operator chnl xnick t) (setq xnick (concat "@" xnick))) ((eq flag ?v) (liece-channel-set-voice chnl xnick t) (setq xnick (concat "+" xnick)))) (if (liece-handle-check-changes-ignored) (return-from liece-handle-join-message)) ;; Restore the private conversation to its original state. (when (and (liece-nick-member nick liece-current-chat-partners) (get (intern nick liece-obarray) 'part)) (liece-insert-change (liece-pick-buffer nick) (format (_ "%s has come back as (%s)\n") nick liece-user-at-host)) (liece-nick-mark-as-part nil nick)) (cond (liece-compress-changes (let* ((text (format (_ " \\(has\\|have\\) joined channel %s") (regexp-quote chnl))) (match (format "^%s%s.*%s$" (if liece-display-time liece-time-prefix-regexp "") (regexp-quote liece-change-prefix) (regexp-quote text))) (default (format (_ "%s%s (%s) has joined channel %s\n") liece-change-prefix nick liece-user-at-host chnl))) (liece-replace (liece-pick-buffer chnl) match default text (format (_ ", %s (%s) have joined channel %s") nick liece-user-at-host chnl)) (liece-replace (if (and liece-current-channel (liece-channel-equal chnl liece-current-channel)) liece-D-buffer (append liece-D-buffer liece-O-buffer)) match default text (format (_ ", %s (%s) have joined channel %s") nick liece-user-at-host chnl)))) (t (liece-insert-change (liece-pick-buffer chnl) (format (_ "%s (%s) has joined channel %s\n") nick liece-user-at-host chnl)) (liece-insert-change (if (liece-channel-equal chnl liece-current-channel) liece-D-buffer (append liece-D-buffer liece-O-buffer)) (format (_ "%s (%s) has joined channel %s\n") nick liece-user-at-host chnl)))))) (defun* liece-handle-part-message (prefix rest) (multiple-value-bind (chnl comment text match default buf) (liece-split-line rest) (setq chnl (liece-channel-virtual chnl) comment (liece-handle-prepare-comment comment)) (if (liece-nick-equal prefix liece-real-nickname) (liece-channel-part chnl) (liece-nick-part prefix chnl)) (if (liece-handle-check-changes-ignored) (return-from liece-handle-part-message)) (setq buf (append liece-D-buffer (liece-pick-buffer chnl))) (unless (and liece-current-channel (liece-channel-equal chnl liece-current-channel)) (setq buf (append buf liece-O-buffer))) (cond (liece-compress-changes (setq text (format (_ " \\(has\\|have\\) left channel %s%s") (regexp-quote chnl) (regexp-quote comment)) match (format "^%s%s.*%s$" (if liece-display-time liece-time-prefix-regexp "") (regexp-quote liece-change-prefix) (regexp-quote text)) default (format (_ "%s%s has left channel %s%s\n") liece-change-prefix prefix chnl comment)) (liece-replace buf match default text (format (_ ", %s have left channel %s%s") prefix chnl comment))) (t (liece-insert-change buf (format (_ "%s has left channel %s%s\n") prefix chnl comment)))))) (defun* liece-handle-silence-message (prefix rest) (let* ((flag (aref rest 0)) (rest (substring rest 1))) (liece-insert-info (append liece-D-buffer liece-O-buffer) (concat "User " rest (if (eq flag ?-) "unsilenced" "silenced"))))) (provide 'liece-handle) ;;; liece-handle.el ends here