* liece-crypt.el: Remove.
[elisp/liece.git] / lisp / liece-commands.el
1 ;;; liece-commands.el --- Interactive commands in command buffer.
2 ;; Copyright (C) 1998-2000 Daiki Ueno
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Created: 1998-09-28
6 ;; Revised: 1999-12-24
7 ;; Keywords: IRC, liece
8
9 ;; This file is part of Liece.
10
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26
27 ;;; Commentary:
28 ;; 
29
30 ;;; Code:
31
32 (eval-when-compile
33    (require 'liece-misc))
34
35 (require 'liece-channel)
36 (require 'liece-nick)
37 (require 'liece-coding)
38 (require 'liece-intl)
39 (require 'liece-minibuf)
40
41 (autoload 'liece-dcc-chat-send "liece-dcc")
42 (autoload 'liece-window-configuration-pop "liece-window")
43
44 (autoload 'liece-command-ctcp-version "liece-ctcp" nil t)
45 (autoload 'liece-command-ctcp-userinfo "liece-ctcp" nil t)
46 (autoload 'liece-command-ctcp-clientinfo "liece-ctcp" nil t)
47 (autoload 'liece-command-ctcp-ping "liece-ctcp" nil t)
48 (autoload 'liece-command-ctcp-time "liece-ctcp" nil t)
49 (autoload 'liece-command-ctcp-x-face "liece-ctcp" nil t)
50 (autoload 'liece-command-ctcp-comment "liece-ctcp" nil t)
51 (autoload 'liece-command-ctcp-help "liece-ctcp" nil t)
52
53 (defun liece-command-poll-names ()
54   "Handler for polling NAMES."
55   (when (liece-server-opened)
56     (setq liece-polling
57           (+ liece-polling
58              (length liece-channel-alist)))
59     (dolist (chnl liece-channel-alist)
60       (liece-send "NAMES %s" (car chnl)))))
61
62 (defun liece-command-poll-friends ()
63   "Handler for polling ISON."
64   (and liece-friends
65        (liece-server-opened)
66        (liece-send "ISON %s" (mapconcat 'identity liece-friends " "))))
67
68 (defun liece-command-find-timestamp ()
69   "Find recent timestamp in dialogue buffer."
70   (interactive)
71   (save-excursion
72     (let ((range "")
73           (regexp (concat "^\\(" liece-time-prefix-regexp "\\)?"
74                           (regexp-quote liece-timestamp-prefix))))
75       (unless (eq 'liece-dialogue-mode (derived-mode-class major-mode))
76         (set-buffer liece-dialogue-buffer)
77         (goto-char (point-max)))
78       (if (re-search-backward regexp (point-min) t)
79           (setq range (concat (buffer-substring (match-end 0)
80                                                 (line-end-position))
81                               "   ---   ")))
82       (if (re-search-forward regexp (point-max) t)
83           (setq range (concat range (buffer-substring (match-end 0)
84                                                       (line-end-position)))))
85       (liece-message range))))
86
87 (defun liece-command-keepalive ()
88   "Handler for polling server connection."
89   (if (not (liece-server-opened))
90       (liece)
91     (liece-ping-if-idle)))
92
93 (defvar liece-last-timestamp-time nil "Last time timestamp was inserted.")
94 (defvar liece-last-timestamp-no-cons-p nil "Last timestamp was no-cons.")
95
96 (defun liece-command-timestamp-if-interval-expired (&optional no-cons)
97   "If interval timer has expired, insert timestamp into dialogue buffer.
98 And save variables into `liece-variable-file' if there are variables to save.
99 Optional argument NO-CONS specifies timestamp format is cons cell."
100   (interactive)
101   (when (and (not (and no-cons
102                        liece-last-timestamp-no-cons-p))
103              (numberp liece-timestamp-interval)
104              (> liece-timestamp-interval 0)
105              (or (null liece-last-timestamp-time)
106                  (> (liece-time-difference liece-last-timestamp-time
107                                             (current-time))
108                     liece-timestamp-interval)))
109     (if liece-save-variables-are-dirty
110         (liece-command-save-vars))
111     (liece-command-timestamp)
112     (setq liece-last-timestamp-no-cons-p no-cons)))
113
114 (defun liece-command-timestamp ()
115   "Insert timestamp into dialogue buffer."
116   (interactive)
117   (let ((stamp (format liece-timestamp-format
118                        (funcall liece-format-time-function (current-time))))
119         (liece-timestamp-interval 0))
120     (liece-insert liece-D-buffer (concat stamp "\n"))
121     (setq liece-last-timestamp-time (current-time))))
122
123 (defun liece-command-point-back-to-command-buffer ()
124   "Set point back to command buffer."
125   (interactive)
126   (let ((win (liece-get-buffer-window liece-command-buffer)))
127     (if win (select-window win))))
128
129 (defun liece-command-send-message (message)
130   "Send MESSAGE to current chat partner of current channel."
131   (if (string-equal message "")
132       (progn (liece-message (_ "No text to send")) nil)
133     (let ((addr (if (eq liece-command-buffer-mode 'chat)
134                     liece-current-chat-partner
135                   liece-current-channel))
136           repr method name target)
137       (cond
138        ((eq liece-command-buffer-mode 'chat)
139         (or liece-current-chat-partner
140             (error
141              (substitute-command-keys
142               "Type \\[liece-command-join] to start private conversation")))
143         (setq repr (liece-channel-parse-representation
144                     liece-current-chat-partner)
145               method (aref repr 0)
146               name (aref repr 1)
147               target (aref repr 2))
148         (cond ((eq method 'dcc)
149                (liece-dcc-chat-send target message))
150               ((eq method 'irc)
151                (liece-send "PRIVMSG %s :%s"
152                            liece-current-chat-partner message)))
153         (liece-own-private-message message))
154        (t
155         (or liece-current-channel
156             (error
157              (substitute-command-keys
158               "Type \\[liece-command-join] to join a channel")))
159         (liece-send
160          "PRIVMSG %s :%s"
161          (liece-channel-real liece-current-channel) message)
162         (liece-own-channel-message message))))))
163
164 (defun liece-command-enter-message ()
165   "Enter the current line as an entry in the IRC dialogue."
166   (interactive)
167   (beginning-of-line)
168   (liece-command-send-message
169    (buffer-substring (point)(progn (end-of-line) (point))))
170   (liece-next-line 1))
171
172 (defun liece-dialogue-enter-message ()
173   "Ask for a line as an entry in the IRC dialogue on the current channel."
174   (interactive)
175   (let (message)
176     (while (not (string-equal (setq message (read-string "> ")) ""))
177       (liece-command-send-message message))))
178
179 (defun liece-command-inline ()
180   "Send current line as a message to the IRC server."
181   (interactive)
182   (beginning-of-line)
183   (liece-send (buffer-substring (point)(progn (end-of-line) (point))))
184   (liece-next-line 1))
185
186 (defun liece-command-join-channel (join-channel-var key)
187   "Join a JOIN-CHANNEL-VAR with KEY."
188   (let ((nicks liece-nick-alist) nick)
189     (while (and nicks
190                 (not (and
191                       (car nick)
192                       (liece-channel-equal join-channel-var (car nick)))))
193       (setq nick (pop nicks)))
194     (when nicks
195       (setq join-channel-var
196             (or (car (get (intern (car nick) liece-obarray) 'chnl))
197                 join-channel-var)))
198     (if (liece-channel-member join-channel-var liece-current-channels)
199         (progn
200           (setq liece-current-channel join-channel-var)
201           (liece-switch-to-channel liece-current-channel)
202           (liece-channel-change))
203       (liece-send "JOIN %s %s" (liece-channel-real join-channel-var) key))))
204
205 (defun liece-command-join-partner (join-channel-var)
206   "Join a JOIN-CHANNEL-VAR."
207   (if (liece-channel-member join-channel-var liece-current-chat-partners)
208       (progn
209         (setq liece-current-chat-partner join-channel-var)
210         (liece-switch-to-channel liece-current-chat-partner))
211     (setq liece-current-chat-partner join-channel-var)
212     (liece-channel-join liece-current-chat-partner))
213   (liece-channel-change))
214
215 (defun liece-command-join (join-channel-var &optional key)
216   "Join a JOIN-CHANNEL-VAR with KEY.
217 If user nickname is given join the same set of channels as the specified user.
218 If command-buffer is in chat-mode, start private conversation
219 with specified user."
220   (interactive
221    (let (join-channel-var key (completion-ignore-case t))
222      (setq join-channel-var
223            (if (numberp current-prefix-arg)
224                current-prefix-arg
225              (liece-channel-virtual
226               (if (eq liece-command-buffer-mode 'chat)
227                   (liece-minibuffer-completing-default-read
228                    (_ "Start private conversation with: ")
229                    liece-nick-alist
230                    nil nil liece-privmsg-partner)
231                 (liece-minibuffer-completing-default-read
232                  (_ "Join channel: ")
233                  (append liece-channel-alist liece-nick-alist)
234                  nil nil liece-default-channel-candidate)))))
235      (if (and current-prefix-arg
236               (not (numberp current-prefix-arg)))
237          (setq key
238                (if (eq current-prefix-arg '-)
239                    (read-string
240                     (format (_ "Key for channel %s: ") join-channel-var))
241                  (let ((passwd-echo ?*))
242                    (read-passwd
243                     (format (_ "Key for channel %s: ") join-channel-var))))))
244      (list join-channel-var key)))
245   (let ((real-chnl (liece-channel-real join-channel-var)))
246     (if (numberp join-channel-var)
247         (liece-switch-to-channel-no join-channel-var)
248       (setq liece-default-channel-candidate nil)
249       (if (liece-channel-p real-chnl)
250           (liece-toggle-command-buffer-mode 'channel)
251         (liece-toggle-command-buffer-mode 'chat))
252       (if (eq liece-command-buffer-mode 'chat)
253           (liece-command-join-partner join-channel-var)
254         (if (null key)
255             (setq key (get (intern join-channel-var liece-obarray) 'key)))
256         (put (intern join-channel-var liece-obarray) 'key key)
257         (if (null key)
258             (setq key ""))
259         (liece-command-join-channel join-channel-var key))
260       (force-mode-line-update))))
261
262 (defun liece-command-part (part-channel-var &optional part-msg)
263   "Part a PART-CHANNEL-VAR with PART-MSG."
264   (interactive
265    (let (part-channel-var
266          (completion-ignore-case t)
267          (part-msg "bye..."))
268      (setq part-channel-var
269            (liece-channel-virtual
270             (if (eq liece-command-buffer-mode 'chat)
271                 (liece-minibuffer-completing-default-read
272                  (_ "End private conversation with: ")
273                  (list-to-alist liece-current-chat-partners)
274                  nil nil liece-current-chat-partner)
275               (liece-minibuffer-completing-default-read
276                (_ "Part channel: ")
277                (list-to-alist liece-current-channels)
278                nil nil liece-current-channel))))
279      (when current-prefix-arg
280        (setq part-msg (read-string (_ "Part Message: "))))
281      (list part-channel-var part-msg)))
282   (let ((real-chnl (liece-channel-real part-channel-var)))
283     (if (liece-channel-p real-chnl)
284         (progn
285           (if (liece-channel-member part-channel-var liece-current-channels)
286               (setq liece-current-channel part-channel-var))
287           (liece-send "PART %s :%s" real-chnl part-msg)
288           (setq liece-default-channel-candidate part-channel-var))
289       (setq liece-current-chat-partners
290             (liece-channel-remove part-channel-var
291                                   liece-current-chat-partners)
292             liece-current-chat-partner
293             (car liece-current-chat-partners))
294       (liece-set-channel-indicator)
295       (liece-channel-part part-channel-var))))
296
297 (defun liece-command-kill (kill-nickname-var &optional timeout silent)
298   "Ignore messages from KILL-NICKNAME-VAR.
299 Username can be given as case insensitive regular expression of form
300 \".*@.*\.sub.domain\".
301 If already ignoring him/her, toggle.
302 If `liece-variables-file' is defined and the file is writable,
303 settings are updated automatically for future sessions.
304 Optional argument TIMEOUT says expiration.
305 If SILENT is non-nil, don't notify current status."
306   (interactive
307    (let (kill-nickname-var timeout (completion-ignore-case t))
308      (setq kill-nickname-var (completing-read
309                               (_ "Ignore nickname or regexp: ")
310                               (append liece-nick-alist
311                                       liece-kill-nickname)))
312      (or (string-equal "" kill-nickname-var)
313          (string-assoc-ignore-case kill-nickname-var liece-kill-nickname)
314          (setq timeout (string-to-int (read-from-minibuffer
315                                        (_ "Timeout [RET for none]: ")))))
316      (list kill-nickname-var timeout)))
317   ;; empty, just list them
318   (if (string-equal "" kill-nickname-var)
319       (with-current-buffer liece-dialogue-buffer
320         (let ((ignores liece-kill-nickname) (time (current-time))
321               buffer-read-only expire expiretime)
322           (goto-char (point-max))
323           (liece-insert-info liece-D-buffer (_ "Currently ignoring:"))
324           (dolist (ignore ignores)
325             (setq expiretime (if (cdr ignore)
326                                  (/ (liece-time-difference time (cdr ignore))
327                                     60))
328                   expire (cond ((not expiretime) "")
329                                ((>= expiretime 0)
330                                 (format (_ " (%d min)") expiretime))
331                                ((< expiretime 0)
332                                 (_ " expired"))))
333             (liece-insert liece-D-buffer
334                            (concat " " (car ignore) expire "\n")))))
335     ;; else not empty, check if exists
336     (let ((ignore
337            (string-assoc-ignore-case
338             kill-nickname-var liece-kill-nickname)))
339       (if ignore
340           (when (setq ignore (string-assoc-ignore-case
341                               (car ignore) liece-kill-nickname))
342             (setq liece-kill-nickname
343                   (delq ignore liece-kill-nickname))
344             (liece-insert-info liece-D-buffer
345                                (format (_ "No longer ignoring: %s.\n")
346                                        (car ignore))))
347         ;; did not find, add to ignored ones
348         (let ((expire-time (if (> timeout 0)
349                                (liece-time-add (current-time)
350                                                 (* timeout 60)))))
351           (and silent (> timeout 0)
352                (setcar (cdr (cdr expire-time)) -1))
353           (setq liece-kill-nickname
354                 (cons (cons kill-nickname-var expire-time)
355                       liece-kill-nickname))
356           (unless silent
357             (liece-insert-info liece-D-buffer
358                                 (format (_ "Ignoring %s") kill-nickname-var))
359             (liece-insert-info liece-D-buffer
360                                 (if (> timeout 0)
361                                     (format " for %d minutes.\n" timeout)
362                                   (format ".\n")))))))
363     (setq liece-save-variables-are-dirty t)))
364
365 (defun liece-command-kick (nick &optional msg)
366   "Kick this NICK out with MSG."
367   (interactive
368    (let ((completion-ignore-case t)
369          (nicks (liece-channel-get-nicks)) nick msg)
370      (setq nick (completing-read
371                  (_ "Kick out nickname: ")
372                  (list-to-alist nicks)))
373      (if current-prefix-arg
374          (setq msg (concat " :" (read-string (_ "Kick Message: ")))))
375      (list nick msg)))
376   (liece-send "KICK %s %s%s"
377                (liece-channel-real liece-current-channel)
378                nick (or msg "")))
379
380 (defun liece-command-ban (ban)
381   "BAN this user out."
382   (interactive
383    (let* ((completion-ignore-case t)
384           (nicks (liece-channel-get-nicks))
385           (uahs (mapcar
386                  (function
387                   (lambda (nick)
388                     (list
389                      (concat nick "!" (liece-nick-get-user-at-host nick)))))
390                  nicks))
391           ban nick msg)
392      (setq ban (liece-minibuffer-completing-default-read
393                 (_ "Ban pattern: ") uahs nil nil
394                 (concat nick "!" (liece-nick-get-user-at-host nick))))
395      (list ban)))
396   (liece-send "MODE %s :+b %s"
397                (liece-channel-real liece-current-channel) ban))
398    
399 (defun liece-command-ban-kick (ban nick &optional msg)
400   "BAN kick this NICK out with MSG."
401   (interactive
402    (let* ((completion-ignore-case t)
403           (nicks (liece-channel-get-nicks))
404           (uahs (mapcar
405                  (function
406                   (lambda (nick)
407                     (list
408                      (concat nick "!" (liece-nick-get-user-at-host nick)))))
409                  nicks))
410           ban nick msg)
411      (setq nick (completing-read (_ "Kick out nickname: ")
412                                  (list-to-alist nicks))
413            ban (liece-minibuffer-completing-default-read
414                 (_ "Ban pattern: ") uahs nil nil
415                 (concat nick "!" (liece-nick-get-user-at-host nick))))
416      (if current-prefix-arg
417          (setq msg (concat " :" (read-string (_ "Kick Message: "))))
418        (setq msg ""))
419      (list ban nick msg)))
420   (liece-send "MODE %s :+b %s"
421                (liece-channel-real liece-current-channel) ban)
422   (liece-send "KICK %s %s%s"
423                (liece-channel-real liece-current-channel)
424                nick (or msg "")))
425
426 (defun liece-command-list (&optional channel)
427   "List the given CHANNEL and its topics.
428 If you enter only Control-U as argument, list the current channel.
429 With - as argument, list all channels."
430   (interactive
431    (if (or current-prefix-arg (null liece-current-channel))
432        (if (eq current-prefix-arg '-)
433            (list current-prefix-arg))
434      (let ((completion-ignore-case t) channel)
435        (setq channel (liece-minibuffer-completing-default-read
436                       (_ "LIST channel: ")
437                       liece-channel-alist nil nil liece-current-channel))
438        (unless (string-equal "" channel)
439          (list channel)))))
440   
441   (cond ((not channel)
442          (if liece-current-channel
443              (liece-send "LIST %s"
444                           (liece-channel-real liece-current-channel))))
445         ((and (eq channel '-)
446               (y-or-n-p (_ "Do you really query LIST without argument?")))
447          (liece-send "LIST"))
448         ((not (string-equal channel ""))
449          (liece-send "LIST %s" (liece-channel-real channel))
450          )))
451
452 (defun liece-command-lusers ()
453   "List the number of users and servers."
454   (interactive)
455   (liece-send "LUSERS"))
456
457 (defun liece-command-modec (chnl change)
458   "Send a MODE command to this CHNL.
459 Argument CHANGE ."
460   (interactive
461    (let ((completion-ignore-case t)
462          (chnl liece-current-channel)
463          liece-minibuffer-complete-function prompt)
464      (if current-prefix-arg
465          (setq chnl
466                (liece-minibuffer-completing-default-read
467                 (_ "Channel/User: ")
468                 (append liece-channel-alist liece-nick-alist)
469                 nil nil liece-current-channel)))
470      (cond
471       ((liece-channel-p (liece-channel-real chnl))
472        (setq prompt (format
473                      (_ "Mode for channel %s [%s]: ")
474                      chnl (or (liece-channel-get-modes chnl) ""))
475              liece-minibuffer-complete-function
476              (function liece-minibuffer-complete-channel-modes)))
477       (t
478        (setq prompt (format
479                      (_ "Mode for user %s [%s]: ")
480                      chnl (or (liece-nick-get-modes chnl) ""))
481              liece-minibuffer-complete-function
482              (function liece-minibuffer-complete-user-modes))))
483      (list chnl (read-from-minibuffer prompt nil liece-minibuffer-map))))
484   (liece-send "MODE %s %s" (liece-channel-real chnl) change))
485
486 (defun liece-command-mode+o (opers)
487   "Send a MODE +o OPERS command."
488   (interactive
489    (let ((opers (liece-channel-get-operators)) oper
490          (nicks (liece-channel-get-nicks))
491          (completion-ignore-case t))
492      (setq nicks (filter-elements
493                   nick nicks
494                   (not (liece-nick-member nick opers)))
495            opers (liece-minibuffer-completing-sequential-read
496                   (_ "Set Operator for") 0
497                   (list-to-alist nicks)))
498      (list opers)))
499   (let (ops)
500     (dolist (oper opers)
501       (push oper ops)
502       (when (= (length ops) liece-compress-mode-length)
503         (liece-send "MODE %s +%s %s"
504                      (liece-channel-real liece-current-channel)
505                      (string-times "o" liece-compress-mode-length)
506                      (string-join ops " "))
507         (setq ops nil)))
508     (if ops
509         (liece-send "MODE %s +%s %s"
510                      (liece-channel-real liece-current-channel)
511                      (string-times "o" (length ops))
512                      (string-join ops " ")))))
513
514 (defun liece-command-mode-o (opers)
515   "Send a MODE -o OPERS command."
516   (interactive
517    (let ((completion-ignore-case t)
518          (opers (liece-channel-get-operators)) oper nicks)
519      (setq nicks (liece-minibuffer-completing-sequential-read
520                   (_ "Unset Operator for") 0
521                   (list-to-alist opers)))
522      (list nicks)))
523   (let (ops)
524     (dolist (oper opers)
525       (push oper ops)
526       (when (= (length ops) liece-compress-mode-length)
527         (liece-send "MODE %s -%s %s"
528                      (liece-channel-real liece-current-channel)
529                      (string-times "o" liece-compress-mode-length)
530                      (string-join ops " "))
531         (setq ops nil)))
532     (if ops
533         (liece-send "MODE %s -%s %s"
534                      (liece-channel-real liece-current-channel)
535                      (string-times "o" (length ops))
536                      (string-join ops " ")))))
537
538 (defun liece-command-mode+v (voices)
539   "Send a MODE +v VOICES command."
540   (interactive
541    (let ((voices (append (liece-channel-get-voices)
542                          (liece-channel-get-operators)))
543          voice
544          (nicks (liece-channel-get-nicks))
545          (completion-ignore-case t)
546          (count 0))
547      (setq nicks (filter-elements nick nicks
548                    (not (string-assoc-ignore-case nick voices)))
549            voices (liece-minibuffer-completing-sequential-read
550                    (_ "Set Voice for") 0 (list-to-alist nicks)))
551      (list voices)))
552   (let (vcs)
553     (dolist (voice voices)
554       (push voice vcs)
555       (when (= (length vcs) liece-compress-mode-length)
556         (liece-send "MODE %s +%s %s"
557                      (liece-channel-real liece-current-channel)
558                      (string-times "v" liece-compress-mode-length)
559                      (string-join vcs " "))
560         (setq vcs nil)))
561     (if vcs
562         (liece-send "MODE %s +%s %s"
563                      (liece-channel-real liece-current-channel)
564                      (string-times "v" (length vcs))
565                      (string-join vcs " ")))))
566
567 (defun liece-command-mode-v (voices)
568   "Send a MODE -v VOICES command."
569   (interactive
570    (let ((completion-ignore-case t)
571          (voices (liece-channel-get-voices)) voice nicks)
572      (setq nicks (liece-minibuffer-completing-sequential-read
573                   (_ "Unset Voice for") 0 (list-to-alist voices)))
574      (list nicks)))
575   (let (vcs)
576     (dolist (voice voices)
577       (push voice vcs)
578       (when (= (length vcs) liece-compress-mode-length)
579         (liece-send "MODE %s -%s %s"
580                      (liece-channel-real liece-current-channel)
581                      (string-times "v" liece-compress-mode-length)
582                      (string-join vcs " "))
583         (setq vcs nil)))
584     (if vcs
585         (liece-send "MODE %s -%s %s"
586                      (liece-channel-real liece-current-channel)
587                      (string-times "v" (length vcs))
588                      (string-join vcs " ")))))
589
590 (defun liece-command-message (address message)
591   "Send ADDRESS a private MESSAGE."
592   (interactive
593    (let ((completion-ignore-case t) address)
594      (setq address
595            (liece-channel-virtual
596             (liece-minibuffer-completing-default-read
597              (_ "Private message to: ")
598              (append liece-nick-alist liece-channel-alist)
599              nil nil liece-privmsg-partner)))
600      (list address
601            (read-string
602             (format
603              (_ "Private message to %s: ")
604              address)))))
605   (if (funcall liece-message-empty-predicate message)
606       (progn (liece-message (_ "No text to send")) nil)
607     (let ((chnl (liece-channel-real address)))
608       (liece-send "PRIVMSG %s :%s" chnl message)
609       (if (liece-channel-p chnl)
610           (liece-own-channel-message message
611                                      (liece-channel-virtual address))
612         (liece-own-private-message message address)))))
613
614 (defun liece-command-mta-private (partner)
615   "Send a private message (current line) to PARTNER."
616   (interactive
617    (let ((completion-ignore-case t))
618      (setq liece-privmsg-partner
619            (liece-channel-virtual
620             (liece-minibuffer-completing-default-read
621              (_ "To whom: ")
622              (append liece-nick-alist liece-channel-alist)
623              nil nil liece-privmsg-partner)))
624      (list liece-privmsg-partner)))
625   (let ((message (buffer-substring (progn (beginning-of-line) (point))
626                                    (progn (end-of-line) (point)))))
627     (if (> (length message) 0)
628         (progn
629           (liece-command-message liece-privmsg-partner message)
630           (liece-next-line 1))
631       (liece-message (_ "No text to send")))))
632
633 (defun liece-command-names (&optional expr)
634   "List the nicknames of the current IRC users on given EXPR.
635 With an Control-U as argument, only the current channel is listed.
636 With - as argument, list all channels."
637   (interactive
638    (if (or current-prefix-arg (null liece-current-channel))
639        (if (eq current-prefix-arg '-)
640            (list current-prefix-arg))
641      (let ((completion-ignore-case t) expr)
642        (setq expr (liece-minibuffer-completing-default-read
643                       (_ "Names on channel: ")
644                       liece-channel-alist nil nil liece-current-channel))
645        (unless (string-equal "" expr)
646          (list expr)))))
647   (when (or (and (eq expr '-)
648                  (y-or-n-p
649                   (_ "Do you really query NAMES without argument?")))
650             (not (or expr
651                      (if liece-current-channel
652                          (setq expr (liece-channel-real
653                                      liece-current-channel))))))
654     (setq expr ""))
655   (when expr
656     (liece-send "NAMES %s" expr)))
657
658 (defun liece-command-nickname (nick)
659   "Set your nickname to NICK."
660   (interactive "sEnter your nickname: ")
661   (let ((nickname (truncate-string nick liece-nick-max-length)))
662     (if (zerop (length nickname))
663         (liece-message (_ "illegal nickname \"%s\"; not changed") nickname)
664       (liece-send "NICK %s" nick))))
665       
666 (defun liece-command-who (&optional expr)
667   "Lists tue users that match the given expression EXPR.
668 If you enter only Control-U as argument, list the current channel.
669 With - as argument, list all users."
670   (interactive
671    (if (or current-prefix-arg (null liece-current-channel))
672        (if (eq current-prefix-arg '-)
673            (list current-prefix-arg))
674      (let ((completion-ignore-case t) expr)
675        (setq expr (liece-minibuffer-completing-default-read
676                    (_ "WHO expression: ")
677                    (append liece-channel-alist liece-nick-alist)))
678        (unless (string-equal "" expr)
679          (list expr)))))
680   (when (or (and (eq expr '-)
681                  (y-or-n-p
682                   (_ "Do you really query WHO without argument?")))
683             (not (or expr
684                      (if liece-current-channel
685                          (setq expr (liece-channel-real
686                                      liece-current-channel))))))
687     (setq expr ""))
688   (when expr
689     (liece-send "WHO %s" expr)
690     (setq liece-who-expression expr)))
691
692 (defun liece-command-finger (finger-nick-var &optional server)
693   "Get information about a specific user FINGER-NICK-VAR.
694 If called with optional argument SERVER or any prefix argument,
695 query information to the foreign server."
696   (interactive
697    (let (finger-nick-var (completion-ignore-case t))
698      (setq finger-nick-var
699            (completing-read (_ "Finger whom: ") liece-nick-alist))
700      (list finger-nick-var (and current-prefix-arg finger-nick-var))))
701   (if server
702       (liece-send "WHOIS %s %s" server finger-nick-var)
703     (liece-send "WHOIS %s" finger-nick-var)))
704
705 (defun liece-command-topic (topic)
706   "Change TOPIC of the current channel."
707   (interactive
708    (list (read-from-minibuffer
709           "Topic: " (cons (or (liece-channel-get-topic) "") 0))))
710   (liece-send "TOPIC %s :%s"
711               (liece-channel-real liece-current-channel) topic))
712
713 (defun liece-command-invite (&optional invite-nick-var invite-channel-var)
714   "Invite INVITE-NICK-VAR to INVITE-CHANNEL-VAR."
715   (interactive
716    (let ((completion-ignore-case t) invite-channel-var invite-nick-var)
717      (if current-prefix-arg
718          (setq invite-channel-var
719                (liece-channel-virtual
720                 (completing-read
721                  (_ "Invite channel: ")
722                  (list-to-alist liece-current-channels)))))
723      (setq invite-nick-var
724            (completing-read
725             (_ "Invite whom: ")
726             liece-nick-alist))
727      (list invite-nick-var invite-channel-var)))
728   (or invite-channel-var
729       (setq invite-channel-var liece-current-channel))
730   (liece-send "INVITE %s %s"
731                invite-nick-var (liece-channel-real invite-channel-var)))
732
733 (defun liece-command-away (awaymsg)
734   "Mark/unmark yourself as being away.
735 Leave message AWAYMSG."
736   (interactive "sAway message: ")
737   (liece-send "AWAY :%s" awaymsg)
738   (setq liece-away-message awaymsg))
739
740 (defun liece-command-scroll-down (lines)
741   "Scroll LINES down dialogue buffer from command buffer."
742   (interactive "P")
743   (let ((other-window-scroll-buffer
744          (if liece-channel-buffer-mode
745              liece-channel-buffer
746            liece-dialogue-buffer)))
747     (when (liece-get-buffer-window other-window-scroll-buffer)
748       (condition-case nil
749           (scroll-other-window-down lines)
750         (beginning-of-buffer
751          (message "Beginning of buffer"))))))
752
753 (defun liece-command-scroll-up (lines)
754   "Scroll LINES up dialogue buffer from command buffer."
755   (interactive "P")
756   (let* ((other-window-scroll-buffer
757           (if liece-channel-buffer-mode
758               liece-channel-buffer
759             liece-dialogue-buffer)))
760     (when (liece-get-buffer-window other-window-scroll-buffer)
761       (condition-case nil
762           (scroll-other-window lines)
763         (end-of-buffer
764          (message "End of buffer"))))))
765
766 (defun liece-command-nick-scroll-down (lines)
767   "Scroll LINES down nick buffer from command buffer."
768   (interactive "P")
769   (let ((other-window-scroll-buffer liece-nick-buffer))
770     (when (liece-get-buffer-window other-window-scroll-buffer)
771       (condition-case nil
772           (scroll-other-window-down lines)
773         (beginning-of-buffer
774          (message "Beginning of buffer"))))))
775
776 (defun liece-command-nick-scroll-up (lines)
777   "Scroll LINES up nick buffer from command buffer."
778   (interactive "P")
779   (let* ((other-window-scroll-buffer liece-nick-buffer))
780     (when (liece-get-buffer-window other-window-scroll-buffer)
781       (condition-case nil
782           (scroll-other-window lines)
783         (end-of-buffer
784          (message "End of buffer"))))))
785
786 (defun liece-command-freeze (&optional arg)
787   "Prevent automatic scrolling of the dialogue window.
788 If prefix argument ARG is non-nil, toggle frozen status."
789   (interactive "P")
790   (liece-freeze (if liece-channel-buffer-mode
791                     liece-channel-buffer
792                   liece-dialogue-buffer)
793                 (if arg (prefix-numeric-value arg))))
794
795 (defun liece-command-own-freeze (&optional arg)
796   "Prevent automatic scrolling of the dialogue window.
797 The difference from `liece-command-freeze' is that your messages are hidden.
798 If prefix argument ARG is non-nil, toggle frozen status."
799   (interactive "P")
800   (liece-own-freeze (if liece-channel-buffer-mode
801                         liece-channel-buffer
802                       liece-dialogue-buffer)
803                     (if arg (prefix-numeric-value arg))))
804
805 (defun liece-command-beep (&optional arg)
806   "Toggle the automatic beep notice when the channel message is received."
807   (interactive "P")
808   (liece-set-beep (if liece-channel-buffer-mode
809                       liece-channel-buffer
810                     liece-dialogue-buffer)
811                   (if arg (prefix-numeric-value arg))))
812
813 (defun liece-command-quit (&optional arg)
814   "Quit IRC.
815 If prefix argument ARG is non-nil, leave signoff message."
816   (interactive "P")
817   (when (and (liece-server-opened)
818              (y-or-n-p (_ "Quit IRC? ")))
819     (message "")
820     (let ((quit-string
821            (if arg (read-string (_ "Signoff message: "))
822              (or liece-signoff-message
823                  (product-name (product-find 'liece-version))))))
824       (liece-send "QUIT :%s" quit-string))
825     (liece-clear-system)
826     (liece-close-server)
827     (if liece-save-variables-are-dirty
828         (liece-command-save-vars))
829     (if (interactive-p)
830         (liece-window-configuration-pop))
831     (run-hooks 'liece-exit-hook)))
832
833 (defun liece-command-generic (message)
834   "Enter a generic IRC MESSAGE, which is sent to the server.
835 A ? lists the useful generic messages."
836   (interactive "sIRC command (? to help): ")
837   (if (string-equal message "?")
838       (with-output-to-temp-buffer "*IRC Help*"
839         (princ "The following generic IRC messages may be of interest to you:
840 TOPIC <new topic>               set the topic of your channel
841 INVITE <nickname>               invite another user to join your channel
842 LINKS                           lists the currently reachable IRC servers
843 SUMMON <user@host>              invites an user not currently in IRC
844 USERS <host>                    lists the users on a host
845 AWAY <reason>                   marks you as not really actively using IRC
846                                 (an empty reason clears it)
847 WALL <message>                  send to everyone on IRC
848 NAMES <channel>                 lists users per channel
849 "))
850     (liece-send "%s" message)))
851
852 (defun liece-command-irc-compatible ()
853   "If entered at column 0, allow you to enter a generic IRC message."
854   (interactive)
855   (if (zerop (current-column))
856       (call-interactively (function liece-command-generic))
857     (self-insert-command 1)))
858
859 (defun liece-command-exec (command)
860   "Execute COMMAND, stdout to dialogue."
861   (interactive "sShell command: ")
862   (shell-command command t)
863   (let ((opoint (point)))
864     (while (< (point) (mark))
865       (liece-command-enter-message)
866       (set-buffer liece-command-buffer))
867     (push-mark opoint t)))
868
869 (defun liece-command-yank-send (&optional arg)
870   "Send message from yank buffer.
871 Prefix argument ARG is regarded as distance from yank pointer."
872   (interactive)
873   (when (y-or-n-p (_ "Really SEND from Yank Buffer?"))
874     (save-restriction
875       (narrow-to-region (point) (point))
876       (insert (car kill-ring-yank-pointer))
877       (goto-char (point-min))
878       (while (eobp)
879         (liece-command-enter-message)
880         (set-buffer liece-command-buffer)))))
881
882 (defun liece-command-complete ()
883   "Complete word before point from userlist."
884   (interactive)
885   (let ((completion-ignore-case t)
886         (alist (if liece-current-channel
887                    (list-to-alist (liece-channel-get-nicks))
888                  liece-nick-alist))
889         candidate completion all)
890     (setq candidate (current-word)
891           completion (try-completion candidate alist)
892           all (all-completions candidate alist))
893     (liece-minibuffer-finalize-completion completion candidate all)))
894
895 (defun liece-command-load-vars ()
896   "Load configuration from liece-variables-file."
897   (interactive)
898   (let ((nick liece-real-nickname))
899     (unwind-protect
900         (liece-read-variables-files)
901       (setq liece-real-nickname nick)
902       (liece-command-reconfigure-windows))))
903
904 (defun liece-command-save-vars ()
905   "Save current settings to `liece-variables-file'."
906   (interactive)
907   (let* ((output-buffer
908           (find-file-noselect
909            (expand-file-name liece-variables-file)))
910          output-marker p)
911     (save-excursion
912       (set-buffer output-buffer)
913       (goto-char (point-min))
914       (cond ((re-search-forward "^;; Saved Settings *\n" nil 'move)
915              (setq p (match-beginning 0))
916              (goto-char p)
917              (or (re-search-forward
918                   "^;; End of Saved Settings *\\(\n\\|\\'\\)" nil t)
919                  (error
920                   (concat "can't find END of saved state in "
921                           liece-variables-file)))
922              (delete-region p (match-end 0)))
923             (t
924              (goto-char (point-max))
925              (insert "\n")))
926       (setq output-marker (point-marker))
927       (let ((print-readably t)
928             (print-escape-newlines t)
929             (standard-output output-marker))
930         (princ ";; Saved Settings\n")
931         (dolist (var liece-saved-forms)
932           (if (symbolp var)
933               (prin1 (list 'setq var
934                            (let ((val (symbol-value var)))
935                              (if (memq val '(t nil))
936                                  val
937                                (list 'quote val)))))
938             (setq var (eval var))
939             (cond ((eq (car-safe var) 'progn)
940                    (while (setq var (cdr var))
941                      (prin1 (car var))
942                      (princ "\n")
943                      (if (cdr var) (princ "  "))))
944                   (var
945                    (prin1 "xx")(prin1 var))))
946           (if var (princ "\n")))
947         (princ "\n")
948         (princ ";; End of Saved Settings\n")))
949     (set-marker output-marker nil)
950     (save-excursion
951       (set-buffer output-buffer)
952       (save-buffer)))
953   (setq liece-save-variables-are-dirty nil))
954
955 (defun liece-command-reconfigure-windows ()
956   "Rearrange window splitting."
957   (interactive)
958   (let ((command-window (liece-get-buffer-window liece-command-buffer))
959         (dialogue-window (liece-get-buffer-window liece-dialogue-buffer))
960         (obuffer (current-buffer)))
961     (if (and command-window dialogue-window)
962         (let ((ch (window-height command-window))
963               (dh (window-height dialogue-window)))
964           (delete-window command-window)
965           (pop-to-buffer liece-dialogue-buffer)
966           (enlarge-window (+ ch dh (- dh))))
967       (pop-to-buffer liece-dialogue-buffer))
968     (liece-configure-windows)
969     (if liece-one-buffer-mode
970         (pop-to-buffer liece-dialogue-buffer)
971       (pop-to-buffer obuffer))))
972
973 (defun liece-command-end-of-buffer ()
974   "Get end of the dialogue buffer."
975   (interactive)
976   (let (buffer window)
977     (setq buffer (if liece-channel-buffer-mode
978                      liece-channel-buffer
979                    liece-dialogue-buffer))
980     (or (setq window (liece-get-buffer-window buffer))
981         (setq window (liece-get-buffer-window liece-dialogue-buffer)
982               buffer liece-dialogue-buffer))
983     (when window
984       (save-selected-window
985         (select-window window)
986         (goto-char (point-max))))))
987
988 (defun liece-command-private-conversation (arg)
989   "Toggle between private conversation mode and channel mode.
990 User can then join and part to a private conversation as he would
991 join or part to a channel.
992
993 If there are no private conversations or argument is given user is
994 prompted the partner/channel (return as partner/channel means toggle
995 mode, the current channel and current chat partner are not altered)
996 Argument ARG is prefix argument of toggle status."
997   (interactive
998    (let ((completion-ignore-case t))
999      (list
1000       (if current-prefix-arg
1001           ;; prefixed, ask where to continue
1002           (if (eq liece-command-buffer-mode 'chat)
1003               (liece-minibuffer-completing-default-read
1004                (_ "Return to channel: ")
1005                (append liece-channel-alist liece-nick-alist)
1006                nil nil liece-current-channel)
1007             (completing-read
1008              (_ "Start private conversation with: ")
1009              liece-nick-alist nil nil))
1010         ;; no prefix, see if going to chat
1011         (if (eq liece-command-buffer-mode 'channel)
1012             ;; and if we have chat partner, select that
1013             (if liece-current-chat-partner
1014                 liece-current-chat-partner
1015               (completing-read
1016                (_ "Start private conversation with: ")
1017                liece-nick-alist )))))))
1018   
1019   (liece-toggle-command-buffer-mode)
1020   (if (and arg (not (string-equal arg "")))
1021       (liece-command-join arg))
1022   (liece-set-channel-indicator)
1023   ;; refresh mode line
1024   (force-mode-line-update))
1025
1026 (defun liece-command-next-channel ()
1027   "Select next channel or chat partner, and *DONT* rotate list."
1028   (interactive)
1029   (let ((rest (copy-sequence
1030                (if (eq liece-command-buffer-mode 'chat)
1031                    liece-current-chat-partners
1032                  liece-current-channels)))
1033         (chnl (if (eq liece-command-buffer-mode 'chat)
1034                   liece-current-chat-partner
1035                 liece-current-channel)))
1036     (liece-switch-to-channel
1037      (or (cadr (liece-channel-member chnl (delq nil rest)))
1038          (car (delq nil rest))
1039          chnl))))
1040
1041 (defun liece-command-previous-channel ()
1042   "Select previous channel or chat partner, and *DONT* rotate list."
1043   (interactive)
1044   (let ((rest
1045          (reverse
1046           (if (eq liece-command-buffer-mode 'chat)
1047               liece-current-chat-partners
1048             liece-current-channels)))
1049         (chnl
1050          (if (eq liece-command-buffer-mode 'chat)
1051              liece-current-chat-partner
1052            liece-current-channel)))
1053     (liece-switch-to-channel
1054      (or (cadr (liece-channel-member chnl (delq nil rest)))
1055          (car (delq nil rest))
1056          chnl))))
1057       
1058 (defun liece-command-unread-channel ()
1059   "Select unread channel or chat partner."
1060   (interactive)
1061   (let ((chnl (car liece-channel-unread-list)))
1062     (if chnl
1063         (liece-switch-to-channel chnl)
1064       (liece-message (_ "No unread channel or chat partner.")))))
1065
1066 (defun liece-command-push ()
1067   "Select next channel or chat partner, and rotate list."
1068   (interactive)
1069   (let* ((rest
1070           (if (eq liece-command-buffer-mode 'chat)
1071               liece-current-chat-partners
1072             liece-current-channels))
1073          (temp (car (last rest)))
1074          (len (length rest)))
1075     (unwind-protect
1076         (while (< 1 len)
1077           (setcar (nthcdr (1- len) rest) (nth (- len 2) rest))
1078           (decf len))
1079       (when rest
1080         (setcar rest temp)))
1081     (liece-channel-change)))
1082
1083 (defun liece-command-pop ()
1084   "Select previous channel or chat partner, and rotate list."
1085   (interactive)
1086   (let* ((rest
1087           (if (eq liece-command-buffer-mode 'chat)
1088               liece-current-chat-partners
1089             liece-current-channels))
1090          (temp (car rest))
1091          (len (length rest)))
1092     (unwind-protect
1093         (dotimes (i len)
1094           (setcar (nthcdr i rest) (nth (1+ i) rest)))
1095       (when rest
1096         (setcar (last rest) temp)))
1097     (liece-channel-change)))
1098
1099 (defvar liece-redisplay-buffer-functions
1100   '(liece-channel-redisplay-buffer
1101     liece-nick-redisplay-buffer
1102     liece-channel-list-redisplay-buffer))
1103
1104 (defun liece-switch-to-channel (chnl)
1105   "Switch the current channel to CHNL."
1106   (if (liece-channel-p (liece-channel-real chnl))
1107       (progn
1108         (liece-toggle-command-buffer-mode 'channel)
1109         (setq liece-current-channel chnl)
1110         (liece-set-channel-indicator))
1111     (liece-toggle-command-buffer-mode 'chat)
1112     (setq liece-current-chat-partner chnl)
1113     (liece-set-channel-indicator))
1114   (save-excursion
1115     (run-hook-with-args 'liece-redisplay-buffer-functions chnl)))
1116
1117 (defun liece-switch-to-channel-no (num)
1118   "Switch the current channel to NUM."
1119   (let* ((mode liece-command-buffer-mode)
1120          (chnls (if (eq mode 'chat)
1121                     liece-current-chat-partners
1122                   liece-current-channels)))
1123     (if (and (integerp num)
1124              (stringp (nth num chnls)))
1125         (let ((chnl (nth num chnls)))
1126           (if (eq mode 'chat)
1127               (progn
1128                 (liece-toggle-command-buffer-mode 'chat)
1129                 (setq liece-current-chat-partner chnl)
1130                 (liece-set-channel-indicator))
1131             (liece-toggle-command-buffer-mode 'channel)
1132             (setq liece-current-channel chnl)
1133             (liece-set-channel-indicator))
1134           (save-excursion
1135             (run-hook-with-args 'liece-redisplay-buffer-functions chnl)))
1136       (message "Invalid channel!"))))
1137
1138 (defun liece-command-ping ()
1139   "Send PING to server."
1140   (interactive)
1141   (if (stringp liece-server-name)
1142       (liece-send "PING %s" liece-server-name)))
1143
1144 (defun liece-command-ison (nicks)
1145   "IsON users NICKS."
1146   (interactive
1147    (let (nicks (completion-ignore-case t))
1148      (setq nicks (liece-minibuffer-completing-sequential-read
1149                   "IsON" 0 liece-nick-alist))
1150      (list nicks)))
1151   (liece-send "ISON :%s" (mapconcat #'identity nicks " ")))
1152
1153 (defun liece-command-activate-friends (nicks)
1154   "Register NICKS to the frinends list."
1155   (interactive
1156    (let (nicks (completion-ignore-case t))
1157      (setq nicks
1158            (liece-minibuffer-completing-sequential-read
1159             (_ "Friend") 0
1160             (filter-elements nick liece-nick-alist
1161               (not (string-list-member-ignore-case
1162                     (car nick) liece-friends)))))
1163      (list nicks)))
1164   (setq liece-friends (append nicks liece-friends)))
1165
1166 (defun liece-command-deactivate-friends ()
1167   "Clear current friends list."
1168   (interactive)
1169   (setq liece-friends nil))
1170
1171 (defun liece-command-display-friends ()
1172   "Display status of the friends."
1173   (interactive)
1174   (with-output-to-temp-buffer " *IRC Friends*"
1175     (set-buffer standard-output)
1176     (insert "Friends status: \n\n")
1177     (dolist (friend liece-friends)
1178       (if (string-list-member-ignore-case friend liece-friends-last)
1179           (insert "+ " friend "\n")
1180         (insert "- " friend "\n")))))
1181
1182 (defun liece-command-userhost (nicks)
1183   "Ask for the hostnames of NICKS."
1184   (interactive
1185    (let (nicks (completion-ignore-case t))
1186      (setq nicks (liece-minibuffer-completing-sequential-read
1187                   (_ "Userhost nick") 0
1188                   (list-to-alist liece-nick-alist)))
1189      (list nicks)))
1190   (liece-send "USERHOST :%s" (mapconcat 'identity nicks ",")))
1191
1192 (defun liece-command-show-last-kill ()
1193   "Dig last kill from KILL and show it."
1194   (interactive)
1195   (liece-insert-info
1196    (append liece-D-buffer liece-O-buffer)
1197    (save-excursion
1198      (set-buffer liece-KILLS-buffer)
1199      (goto-char (point-max))
1200      (forward-line -1)
1201      (concat (buffer-substring (point) (point-max)) "\n"))))
1202
1203 (defun liece-command-toggle-private ()
1204   "Toggle private mode / channel mode."
1205   (interactive)
1206   (case (prog1 liece-command-buffer-mode
1207           (liece-toggle-command-buffer-mode))
1208     (chat
1209      (if liece-current-channel
1210          (liece-switch-to-channel liece-current-channel))
1211      (setq liece-command-buffer-mode-indicator "Channels"))
1212     (channel
1213      (if liece-current-chat-partner
1214          (liece-switch-to-channel liece-current-chat-partner))
1215      (setq liece-command-buffer-mode-indicator "Partners")))
1216   (liece-channel-change))
1217
1218 (defun liece-command-tag-region (start end)
1219   "Move current region between START and END to `kill-ring'."
1220   (interactive
1221    (if (region-active-p)
1222        (list (region-beginning)(region-end))
1223      (list (line-beginning-position)(line-end-position))))
1224   (static-if (fboundp 'extent-property)
1225       (kill-ring-save start end)
1226     (let ((start (set-marker (make-marker) start))
1227           (end (set-marker (make-marker) end))
1228           (inhibit-read-only t)
1229           buffer-read-only
1230           buffer-undo-list)
1231       (liece-remove-properties-region start end)
1232       (kill-ring-save start end)
1233       (push nil buffer-undo-list)
1234       (undo))))
1235
1236 (provide 'liece-commands)
1237
1238 ;;; liece-commands.el ends here