9c267f96ac
editor. Most features are implemented via module extensions. For example, DCC (Direct Client-to-Client) can be enabled at the user's option. inputs and ok deanna@
468 lines
16 KiB
EmacsLisp
468 lines
16 KiB
EmacsLisp
;;; zenirc-chanbuf.el --- zenirc one channel per buffer support
|
||
|
||
;; Copyright (c) 2002, 2003 Michele Bini
|
||
|
||
;; Author: Michele Bini <mibin@libero.it>
|
||
;; Created: Dec 2000
|
||
;; Version: 0.3
|
||
;; Keywords: comm
|
||
|
||
;; This 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 of the License, 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 this program; if not, write to the Free
|
||
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||
;; MA 02111-1307 USA
|
||
|
||
;;; Commentary:
|
||
;;
|
||
|
||
;;; History:
|
||
;; 0.3: Hold nicknames in hash tables; this should improve performance
|
||
;; on very crowded channels, various bugfixes (topic lines handling
|
||
;; and more).
|
||
|
||
;;; Code:
|
||
|
||
(require 'zenirc)
|
||
|
||
(defvar zenirc-chanbuf-mode-hook nil)
|
||
(defvar zenirc-chanbuf-mode-map '()
|
||
"Keymap for `zenirc-chanbuf-mode'.")
|
||
|
||
(unless zenirc-chanbuf-mode-map
|
||
(setq zenirc-chanbuf-mode-map (make-sparse-keymap))
|
||
(define-key zenirc-chanbuf-mode-map "\n" 'zenirc-chanbuf-send-line)
|
||
(define-key zenirc-chanbuf-mode-map "\C-m" 'zenirc-chanbuf-send-line)
|
||
(define-key zenirc-chanbuf-mode-map "\r" 'zenirc-chanbuf-send-line))
|
||
|
||
(defvar zenirc-chanbuf-server-buffer '())
|
||
(make-variable-buffer-local 'zenirc-chanbuf-server-buffer)
|
||
(defvar zenirc-chanbuf-victim '())
|
||
(make-variable-buffer-local 'zenirc-chanbuf-victim)
|
||
(defvar zenirc-chanbuf-nicks nil
|
||
"Nicknames in the current channel.")
|
||
(defvar zenirc-chanbuf-topic nil
|
||
"Current channel topic line.")
|
||
(make-variable-buffer-local 'zenirc-chanbuf-nicks)
|
||
(defun zenirc-chanbuf-has-nick-p (nick)
|
||
(and zenirc-chanbuf-nicks
|
||
(gethash (zenirc-downcase-name nick) zenirc-chanbuf-nicks)))
|
||
(defun zenirc-chanbuf-delete-nick (nick)
|
||
(when zenirc-chanbuf-nicks
|
||
(setq nick (zenirc-downcase-name nick))
|
||
(puthash nick nil zenirc-chanbuf-nicks)))
|
||
(defun zenirc-chanbuf-add-nick (nick)
|
||
(unless zenirc-chanbuf-nicks
|
||
(setq zenirc-chanbuf-nicks
|
||
(make-hash-table :test 'equal :size 3)))
|
||
(setq nick (zenirc-downcase-name nick))
|
||
(or (zenirc-chanbuf-has-nick-p nick)
|
||
(puthash nick t zenirc-chanbuf-nicks)))
|
||
|
||
(defcustom zenirc-chanbuf-use-header-line t
|
||
"Whether to use the header line to show the channel topic."
|
||
:type 'boolean :group 'zenirc-chanbuf)
|
||
|
||
(defun zenirc-chanbuf-update-header-line ()
|
||
(when (boundp 'header-line-format)
|
||
(if (and zenirc-chanbuf-use-header-line zenirc-chanbuf-topic)
|
||
(progn
|
||
(make-local-variable 'header-line-format)
|
||
(setq header-line-format 'zenirc-chanbuf-topic))
|
||
(kill-local-variable 'header-line-format))))
|
||
|
||
(defun zenirc-chanbuf-mode ()
|
||
"Additional time wasting mode ... IRC-channel oriented."
|
||
(interactive)
|
||
(kill-all-local-variables)
|
||
;;(setq mode-line-process '("%s"))
|
||
(setq mode-name "ZenIRC-chn")
|
||
(setq major-mode 'zenirc-chanbuf-mode)
|
||
(use-local-map zenirc-chanbuf-mode-map)
|
||
(make-local-variable 'zenirc-chanbuf-topic)
|
||
(setq zenirc-chanbuf-nicks nil)
|
||
(run-hooks 'zenirc-chanbuf-mode-hook)
|
||
(zenirc-chanbuf-update-header-line))
|
||
|
||
|
||
(defun zenirc-chanbuf-send-line ()
|
||
"Send current line to the IRC server using the appropriate buffer."
|
||
(interactive)
|
||
(when (zenirc-in-input-p)
|
||
(end-of-line)
|
||
(let ((victim zenirc-chanbuf-victim)
|
||
(what (buffer-substring zenirc-process-mark (point))))
|
||
(with-current-buffer zenirc-chanbuf-server-buffer
|
||
(goto-char (point-max))
|
||
(when (zenirc-in-input-p)
|
||
(kill-region (marker-position zenirc-process-mark) (point-max))
|
||
(unless (equal zenirc-current-victim victim)
|
||
(setq zenirc-current-victim nil)
|
||
(insert "/query " victim)
|
||
(setq zenirc-current-victim victim)
|
||
(zenirc-send-line))
|
||
(insert what)
|
||
(zenirc-send-line))))
|
||
(insert "\n(sent)\n")
|
||
(set-marker zenirc-process-mark (point))))
|
||
|
||
(defun zenirc-chanbuf-message (string &rest args)
|
||
(when (symbolp string)
|
||
(setq string (zenirc-lang-retrieve-catalog-entry string)))
|
||
(when args
|
||
(if string
|
||
(setq string (apply 'format string args))
|
||
(setq string (format "[raw] %s" args))))
|
||
(setq string (concat string "\n"))
|
||
(save-excursion
|
||
(goto-char zenirc-process-mark)
|
||
(insert-before-markers string)
|
||
(set-marker zenirc-process-mark (point))))
|
||
|
||
|
||
(defun zenirc-chanbuf-buffer-name (server nick victim)
|
||
(setq victim (and victim
|
||
(not (equal victim ""))
|
||
(zenirc-downcase-name victim)))
|
||
(concat
|
||
(zenirc-downcase-name nick)
|
||
" " (or victim "") (if victim " " "") "@" server))
|
||
|
||
;;;; ... and the related zenirc minor mode
|
||
|
||
(defvar zenirc-chanbuf-minor-mode nil)
|
||
(make-variable-buffer-local 'zenirc-chanbuf-minor-mode)
|
||
(defvar zenirc-chanbuf-minor-mode-hook nil)
|
||
;; (defvar zenirc-chanbuf-victims '()
|
||
;; "list of current-victims handled in separate buffers")
|
||
;; (make-variable-buffer-local 'zenirc-chanbuf-victims)
|
||
|
||
(defun zenirc-chanbuf-minor-buffer-name (victim)
|
||
(zenirc-chanbuf-buffer-name zenirc-server zenirc-nick victim))
|
||
|
||
(defun zenirc-chanbuf-map-children (func)
|
||
(let ((servbuf (current-buffer)))
|
||
(mapcar
|
||
(lambda (buf)
|
||
(when
|
||
(and (buffer-live-p buf)
|
||
(not (eq buf servbuf))
|
||
(with-current-buffer buf
|
||
(and
|
||
(equal servbuf zenirc-chanbuf-server-buffer)
|
||
(funcall func buf))))))
|
||
(buffer-list))))
|
||
(defun zenirc-chanbuf-kill-children ()
|
||
(interactive)
|
||
(zenirc-chanbuf-map-children #'kill-buffer))
|
||
(defun zenirc-chanbuf-with-nick (nick func &rest args)
|
||
(let ((servbuf (current-buffer)))
|
||
(mapcar
|
||
(lambda (buf)
|
||
(with-current-buffer buf
|
||
(when (and (equal servbuf zenirc-chanbuf-server-buffer)
|
||
(zenirc-chanbuf-has-nick-p nick))
|
||
(apply func args))))
|
||
(buffer-list))))
|
||
|
||
(defun zenirc-chanbuf-nick-message (nick string &rest args)
|
||
(when (symbolp string)
|
||
(setq string (zenirc-lang-retrieve-catalog-entry string)))
|
||
(when args
|
||
(if string
|
||
(setq string (apply 'format string args))
|
||
(setq string (format "[raw] %s" args))))
|
||
(setq string (concat string "\n"))
|
||
(zenirc-chanbuf-with-nick
|
||
nick
|
||
(lambda ()
|
||
(save-excursion
|
||
(goto-char zenirc-process-mark)
|
||
(insert-before-markers string)
|
||
(set-marker zenirc-process-mark (point))))))
|
||
|
||
(defun zenirc-chanbuf (victim)
|
||
"*Create a new buffer for chatting with the specified VICTIM.
|
||
Return that buffer."
|
||
(interactive "sVictim: ")
|
||
(let ((server-buffer (current-buffer))
|
||
(new-buffer
|
||
(get-buffer-create
|
||
(zenirc-chanbuf-minor-buffer-name
|
||
victim))))
|
||
(with-current-buffer new-buffer
|
||
(unless (eq major-mode 'zenirc-chanbuf-mode)
|
||
(zenirc-chanbuf-mode)
|
||
(setq zenirc-chanbuf-victim victim)
|
||
(setq zenirc-chanbuf-server-buffer server-buffer)
|
||
(setq zenirc-process-mark (set-marker (make-marker) (point-max)))))
|
||
(when (interactive-p)
|
||
(switch-to-buffer new-buffer))
|
||
new-buffer))
|
||
(defun zenirc-chanbuf-JOIN (proc parsedmsg)
|
||
(let ((channel (aref parsedmsg 2))
|
||
(who (aref parsedmsg 1))
|
||
(-nick zenirc-nick)
|
||
(mode nil))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(setq who (zenirc-extract-nick who))
|
||
(zenirc-chanbuf-add-nick who)
|
||
(if (zenirc-names-equal-p who -nick)
|
||
(zenirc-chanbuf-message 'join_you channel)
|
||
(when (string-match "" channel)
|
||
(setq channel (substring channel 0 (- (match-end 0) 1)))
|
||
(setq mode (substring channel (match-end 0) (length channel))))
|
||
(if mode
|
||
(zenirc-chanbuf-message 'join_mode who channel mode)
|
||
(zenirc-chanbuf-message 'join who channel))))))
|
||
|
||
;; /names reply
|
||
(defun zenirc-chanbuf-353 (proc parsedmsg)
|
||
(let ((channel (aref parsedmsg 4))
|
||
(nicks (aref parsedmsg 5)))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(zenirc-chanbuf-message 's353 channel nicks)
|
||
(save-match-data
|
||
(let ((start 0))
|
||
(while (string-match "\\([^ \t@\+]+\\)" nicks start)
|
||
(zenirc-chanbuf-add-nick (match-string 1 nicks))
|
||
(setq start (match-end 1))))))))
|
||
|
||
;; /whois reply -- userinfo
|
||
(defun zenirc-chanbuf-311 (proc parsedmsg)
|
||
(let ((who (aref parsedmsg 3)))
|
||
(zenirc-chanbuf-nick-message
|
||
who 's311 who (aref parsedmsg 4)
|
||
(aref parsedmsg 5) (aref parsedmsg 7))))
|
||
;; /whois reply -- server
|
||
(defun zenirc-chanbuf-312 (proc parsedmsg)
|
||
(let ((who (aref parsedmsg 3)))
|
||
(zenirc-chanbuf-nick-message
|
||
who 's312 who (aref parsedmsg 4)
|
||
(aref parsedmsg 5))))
|
||
;; /whois reply -- is an irc oper
|
||
(defun zenirc-chanbuf-313 (proc parsedmsg)
|
||
(let ((who (aref parsedmsg 3)))
|
||
(zenirc-chanbuf-nick-message
|
||
who 's313 who)))
|
||
;; /whois reply -- whowas
|
||
(defun zenirc-chanbuf-314 (proc parsedmsg)
|
||
(let ((who (aref parsedmsg 3)))
|
||
(zenirc-chanbuf-nick-message
|
||
who 's314 who (aref parsedmsg 4)
|
||
(aref parsedmsg 5) (aref parsedmsg 7))))
|
||
;; /whois reply -- idle
|
||
(defun zenirc-chanbuf-317 (proc parsedmsg)
|
||
(let* ((who (aref parsedmsg 3))
|
||
(s (string-to-int (aref parsedmsg 4)))
|
||
(h (/ s 3600))
|
||
(s (- s (* h 3600)))
|
||
(m (/ s 60))
|
||
(s (- s (* m 60))))
|
||
(zenirc-chanbuf-nick-message
|
||
who 's317 who (format "%d:%02d:%02d" h m s))))
|
||
;; /whois reply -- channels
|
||
(defun zenirc-chanbuf-319 (proc parsedmsg)
|
||
(let ((who (aref parsedmsg 3)))
|
||
(zenirc-chanbuf-nick-message
|
||
who 's319 who (aref parsedmsg 4))))
|
||
|
||
;; 331 - RPL_NOTOPIC "<channel> :No topic is set"
|
||
(defun zenirc-chanbuf-331 (proc parsedmsg)
|
||
(let ((channel (aref parsedmsg 3)))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(zenirc-chanbuf-message 's331 channel)
|
||
(setq zenirc-chanbuf-topic nil)
|
||
(zenirc-chanbuf-update-header-line))))
|
||
|
||
;; 332 - channel topic on join, etc.
|
||
(defun zenirc-chanbuf-332 (proc parsedmsg)
|
||
(let ((channel (aref parsedmsg 3))
|
||
(topic (aref parsedmsg 4)))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(zenirc-chanbuf-message 's332 channel topic)
|
||
(setq zenirc-chanbuf-topic topic)
|
||
(zenirc-chanbuf-update-header-line))))
|
||
|
||
;; 333 - user who set topic and when it was set
|
||
;; :server 333 to channel who-set-topic time-when-set
|
||
(defun zenirc-chanbuf-333 (proc parsedmsg)
|
||
(let ((channel (aref parsedmsg 3)))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(zenirc-chanbuf-message
|
||
's333
|
||
channel (aref parsedmsg 4)
|
||
(current-time-string
|
||
(zenirc-epoch-seconds-to-time
|
||
(aref parsedmsg 5)))))))
|
||
|
||
(defun zenirc-chanbuf-KICK (proc parsedmsg)
|
||
(let ((-nick zenirc-nick)
|
||
(kicker (zenirc-extract-nick (aref parsedmsg 1)))
|
||
(channel (aref parsedmsg 2))
|
||
(kicked (zenirc-extract-nick (aref parsedmsg 3)))
|
||
(reason (aref parsedmsg 4)))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(if (zenirc-names-equal-p kicked -nick)
|
||
(zenirc-chanbuf-message
|
||
'kick_you channel
|
||
(format "%s - %s" kicker reason))
|
||
(zenirc-chanbuf-message
|
||
'kick kicked channel
|
||
(format "%s - %s" kicker reason))))))
|
||
(defun zenirc-chanbuf-MODE (proc parsedmsg)
|
||
(let ((who (zenirc-extract-nick (aref parsedmsg 1)))
|
||
(channel (aref parsedmsg 2)))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(zenirc-chanbuf-message
|
||
'mode who channel
|
||
(format "%s %s %s %s %s %s"
|
||
(or (aref parsedmsg 3) "")
|
||
(or (aref parsedmsg 4) "")
|
||
(or (aref parsedmsg 5) "")
|
||
(or (aref parsedmsg 6) "")
|
||
(or (aref parsedmsg 7) "")
|
||
(or (aref parsedmsg 8) ""))))))
|
||
(defun zenirc-chanbuf-PART (proc parsedmsg)
|
||
(let ((channel (aref parsedmsg 2))
|
||
(who (zenirc-extract-nick (aref parsedmsg 1)))
|
||
(why (or (aref parsedmsg 3) ""))
|
||
(-nick zenirc-nick))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(zenirc-chanbuf-delete-nick who)
|
||
(if (zenirc-names-equal-p who -nick)
|
||
(zenirc-chanbuf-message 'part_you channel why)
|
||
(zenirc-chanbuf-message 'part who channel why)))))
|
||
(defun zenirc-chanbuf-QUIT (proc parsedmsg)
|
||
(let ((who (zenirc-extract-nick (aref parsedmsg 1)))
|
||
(reason (aref parsedmsg 2)))
|
||
(zenirc-chanbuf-with-nick
|
||
who
|
||
(lambda ()
|
||
(zenirc-chanbuf-message 'quit who reason)
|
||
(zenirc-chanbuf-delete-nick who)))))
|
||
(defun zenirc-chanbuf-privmsg-or-notice (proc parsedmsg)
|
||
(let ((which (aref parsedmsg 0))
|
||
(from (or (zenirc-extract-nick (aref parsedmsg 1)) ""))
|
||
(to (aref parsedmsg 2))
|
||
(msg (aref parsedmsg 3))
|
||
(-nick zenirc-nick))
|
||
(let ((channel (zenirc-channel-p to)))
|
||
(with-current-buffer (zenirc-chanbuf (if channel to from))
|
||
(let ((time (when zenirc-timestamp
|
||
(concat zenirc-timestamp-prefix
|
||
(zenirc-timestamp-string)
|
||
zenirc-timestamp-suffix))))
|
||
(unless (string-match "\C-a" msg)
|
||
(if channel
|
||
(zenirc-chanbuf-message
|
||
(if (equal which "PRIVMSG") 'privmsg_nochannel
|
||
'notice_nochannel)
|
||
(concat from time) msg)
|
||
(zenirc-chanbuf-add-nick from)
|
||
(zenirc-chanbuf-add-nick to)
|
||
(zenirc-chanbuf-message
|
||
(if (equal which "PRIVMSG") 'privmsg_you 'notice_you)
|
||
(concat from time) msg))))))))
|
||
(defun zenirc-chanbuf-TOPIC (proc parsedmsg)
|
||
(let ((channel (aref parsedmsg 2))
|
||
(topic (aref parsedmsg 3)))
|
||
(with-current-buffer (zenirc-chanbuf channel)
|
||
(zenirc-chanbuf-message
|
||
'topic (zenirc-extract-nick (aref parsedmsg 1))
|
||
channel topic)
|
||
(setq zenirc-chanbuf-topic topic)
|
||
(zenirc-chanbuf-update-header-line))))
|
||
(defun zenirc-chanbuf-NICK (proc parsedmsg)
|
||
(let* ((from (zenirc-extract-nick (aref parsedmsg 1)))
|
||
(to (aref parsedmsg 2))
|
||
(servbuf (current-buffer))
|
||
(-nick zenirc-nick)
|
||
(-zenirc-server zenirc-server))
|
||
(zenirc-chanbuf-nick-message
|
||
from 'nick from to)
|
||
(unless (zenirc-names-equal-p from to)
|
||
(zenirc-chanbuf-map-children
|
||
(lambda (buf)
|
||
(if (zenirc-names-equal-p from zenirc-chanbuf-victim)
|
||
(setq zenirc-chanbuf-victim to))
|
||
(when (zenirc-chanbuf-has-nick-p from)
|
||
(zenirc-chanbuf-delete-nick from)
|
||
(zenirc-chanbuf-add-nick to))
|
||
(rename-buffer
|
||
(zenirc-chanbuf-buffer-name
|
||
-zenirc-server -nick zenirc-chanbuf-victim)))))))
|
||
(defun zenirc-chanbuf-ctcp-query-ACTION (proc parsedctcp from to)
|
||
(with-current-buffer
|
||
(zenirc-chanbuf (if (zenirc-channel-p to) to (zenirc-extract-nick from)))
|
||
(zenirc-chanbuf-message
|
||
'ctcp_action_nochannel
|
||
(zenirc-extract-nick from) (cdr parsedctcp))))
|
||
(defconst zenirc-chanbuf-mode-hooks
|
||
'((zenirc-server-JOIN-hook . zenirc-chanbuf-JOIN)
|
||
(zenirc-server-KICK-hook . zenirc-chanbuf-KICK)
|
||
(zenirc-server-MODE-hook . zenirc-chanbuf-MODE)
|
||
(zenirc-server-PART-hook . zenirc-chanbuf-PART)
|
||
(zenirc-server-QUIT-hook . zenirc-chanbuf-QUIT)
|
||
(zenirc-server-353-hook . zenirc-chanbuf-353)
|
||
(zenirc-server-311-hook . zenirc-chanbuf-311)
|
||
(zenirc-server-312-hook . zenirc-chanbuf-312)
|
||
(zenirc-server-313-hook . zenirc-chanbuf-313)
|
||
(zenirc-server-314-hook . zenirc-chanbuf-314)
|
||
(zenirc-server-317-hook . zenirc-chanbuf-317)
|
||
(zenirc-server-319-hook . zenirc-chanbuf-319)
|
||
(zenirc-server-331-hook . zenirc-chanbuf-331) ; no topic message
|
||
(zenirc-server-332-hook . zenirc-chanbuf-332) ; topic message
|
||
(zenirc-server-333-hook . zenirc-chanbuf-333) ; topic set time
|
||
(zenirc-server-PRIVMSG-hook .
|
||
zenirc-chanbuf-privmsg-or-notice)
|
||
(zenirc-server-NOTICE-hook
|
||
. zenirc-chanbuf-privmsg-or-notice)
|
||
(zenirc-server-TOPIC-hook . zenirc-chanbuf-TOPIC)
|
||
(zenirc-server-NICK-hook . zenirc-chanbuf-NICK)
|
||
(zenirc-ctcp-query-ACTION-hook .
|
||
zenirc-chanbuf-ctcp-query-ACTION)))
|
||
(defun zenirc-chanbuf-minor-mode-enable ()
|
||
(mapcar
|
||
(lambda (a)
|
||
(zenirc-add-hook (car a) (cdr a) t))
|
||
zenirc-chanbuf-mode-hooks)
|
||
(setq zenirc-chanbuf-minor-mode t)
|
||
(run-hooks 'zenirc-chanbuf-minor-mode-hook)
|
||
(add-hook 'kill-buffer-hook 'zenirc-chanbuf-kill-children))
|
||
(defun zenirc-chanbuf-minor-mode-disable ()
|
||
(mapcar
|
||
(lambda (a)
|
||
(zenirc-remove-hook (car a) (cdr a)))
|
||
zenirc-chanbuf-mode-hooks)
|
||
(remove-hook 'kill-buffer-hook 'zenirc-chanbuf-kill-children)
|
||
(setq zenirc-chanbuf-minor-mode nil))
|
||
|
||
(defun zenirc-chanbuf-minor-mode (&optional arg)
|
||
"Toggle zenirc chanbuf minor mode.
|
||
This mode allows different victims to be handled in separate buffers.
|
||
|
||
With prefix ARG, turn the mode on iff ARG is positive."
|
||
(interactive)
|
||
(cond
|
||
((null arg)
|
||
(if zenirc-chanbuf-minor-mode
|
||
(zenirc-chanbuf-minor-mode-disable)
|
||
(zenirc-chanbuf-minor-mode-enable)))
|
||
((> (prefix-numeric-value arg) 0)
|
||
(zenirc-chanbuf-minor-mode-enable))
|
||
(t (zenirc-chanbuf-minor-mode-disable))))
|
||
|
||
(provide 'zenirc-chanbuf)
|
||
|
||
;;; zenirc-chanbuf.el ends here
|