1;;; twittering-mode.el --- Major mode for Twitter
3;; Copyright (C) 2007 Yuto Hayamizu.
6;; Author: Y. Hayamizu <y.hayamizu@gmail.com>
7;; Tsuyoshi CHO <Tsuyoshi.CHO+develop@Gmail.com>
10;; Keywords: twitter web
11;; URL: http://lambdarepos.svnrepository.com/share/trac.cgi/browser/lang/elisp/twittering-mode
13;; This file is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
18;; This file is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs; see the file COPYING. If not, write to
25;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26;; Boston, MA 02111-1307, USA.
30;; twittering-mode.el is a major mode for Twitter.
31;; You can check friends timeline, and update your status on Emacs.
35;; URL : http://twitter.com/d00dle/statuses/577876082
36;; URL : http://twitter.com/d00dle/statuses/577879732
37;; * Status Input from Popup buffer and C-cC-c to POST.
39;; URL : http://code.nanigac.com/source/view/419
40;; * update status for region
48(defconst twittering-mode-version "0.3")
50(defvar twittering-mode-map (make-sparse-keymap))
52(defvar twittering-timer nil "Timer object for timeline refreshing will be stored here. DO NOT SET VALUE MANUALLY.")
54(defvar twittering-idle-time 20)
56(defvar twittering-timer-interval 90)
58(defvar twittering-username nil)
60(defvar twittering-password nil)
62(defvar twittering-scroll-mode nil)
64(defvar twittering-jojo-mode nil)
66(defvar twittering-status-format nil)
67(setq twittering-status-format "%i %s, %@:\n %t // from %f%L")
77; %c - created_at (raw UTC string)
78; %C{time-format-str} - created_at (formatted with time-format-str)
85(defvar twittering-buffer "*twittering*")
86(defun twittering-buffer ()
87 (twittering-get-or-generate-buffer twittering-buffer))
89(defvar twittering-http-buffer "*twittering-http-buffer*")
90(defun twittering-http-buffer ()
91 (twittering-get-or-generate-buffer twittering-http-buffer))
93(defvar twittering-friends-timeline-data nil)
95(defvar twittering-username-face 'twittering-username-face)
96(defvar twittering-uri-face 'twittering-uri-face)
98(defun twittering-get-or-generate-buffer (buffer)
100 (if (buffer-live-p buffer)
102 (generate-new-buffer (buffer-name buffer)))
104 (or (get-buffer buffer)
105 (generate-new-buffer buffer)))))
107(defun assocref (item alist)
108 (cdr (assoc item alist)))
109(defmacro list-push (value listvar)
110 `(setq ,listvar (cons ,value ,listvar)))
113(defvar twittering-proxy-use nil)
114(defvar twittering-proxy-server nil)
115(defvar twittering-proxy-port 8080)
116(defvar twittering-proxy-user nil)
117(defvar twittering-proxy-password nil)
119(defun twittering-toggle-proxy () ""
121 (setq twittering-proxy-use
122 (not twittering-proxy-use))
125 (if twittering-proxy-use
128(defun twittering-user-agent-default-function ()
129 "Twittering mode default User-Agent function."
131 (int-to-string emacs-major-version) "." (int-to-string
135 twittering-mode-version))
137(defvar twittering-user-agent-function 'twittering-user-agent-default-function)
139(defun twittering-user-agent ()
140 "Return User-Agent header string."
141 (funcall twittering-user-agent-function))
143;;; to show image files
145(defvar twittering-wget-buffer "*twittering-wget-buffer*")
146(defun twittering-wget-buffer ()
147 (twittering-get-or-generate-buffer twittering-wget-buffer))
149(defvar twittering-tmp-dir
150 (expand-file-name (concat "twmode-images-" (user-login-name))
151 temporary-file-directory))
153(defvar twittering-icon-mode nil "You MUST NOT CHANGE this variable directory. You should change through function'twittering-icon-mode'")
154(defun twittering-icon-mode (&optional arg)
156 (setq twittering-icon-mode
157 (if twittering-icon-mode
160 (> (prefix-numeric-value arg) 0))
162 (and arg (> (prefix-numeric-value arg) 0)))
163 (when (file-writable-p twittering-tmp-dir)
165 (if (not (file-directory-p twittering-tmp-dir))
166 (make-directory twittering-tmp-dir))
168 (twittering-render-friends-timeline))
170(defun twittering-scroll-mode (&optional arg)
172 (setq twittering-scroll-mode
174 (not twittering-scroll-mode)
175 (> (prefix-numeric-value arg) 0))))
177(defun twittering-jojo-mode (&optional arg)
179 (setq twittering-jojo-mode
181 (not twittering-jojo-mode)
182 (> (prefix-numeric-value arg) 0))))
184(defvar twittering-image-stack nil)
186(defun twittering-image-type (file-name)
188 ((string-match "\\.jpe?g" file-name) 'jpeg)
189 ((string-match "\\.png" file-name) 'png)
190 ((string-match "\\.gif" file-name) 'gif)
193(defun twittering-local-strftime (fmt string)
194 (format-time-string fmt ; like "%Y-%m-%d %H:%M:%S", shown in localtime
195 (apply 'encode-time (parse-time-string string))))
197(defvar twittering-debug-mode nil)
198(defvar twittering-debug-buffer "*debug*")
199(defun twittering-debug-buffer ()
200 (twittering-get-or-generate-buffer twittering-debug-buffer))
201(defmacro debug-print (obj)
202 (let ((obsym (gensym)))
203 `(let ((,obsym ,obj))
204 (if twittering-debug-mode
205 (with-current-buffer (twittering-debug-buffer)
206 (insert (prin1-to-string ,obsym))
211(defun twittering-debug-mode ()
213 (setq twittering-debug-mode
214 (not twittering-debug-mode))
215 (message (if twittering-debug-mode "debug mode:on" "debug mode:off")))
217(if twittering-mode-map
218 (let ((km twittering-mode-map))
219 (define-key km "\C-c\C-f" 'twittering-friends-timeline)
220 (define-key km "\C-c\C-s" 'twittering-update-status-interactive)
221 (define-key km "\C-c\C-e" 'twittering-erase-old-statuses)
222 (define-key km "\C-m" 'twittering-enter)
223 (define-key km "\C-c\C-l" 'twittering-update-lambda)
224 (define-key km [mouse-1] 'twittering-click)
225 (define-key km "\C-c\C-v" 'twittering-view-user-page)
226 (define-key km "j" 'next-line)
227 (define-key km "k" 'previous-line)
228 (define-key km "l" 'forward-char)
229 (define-key km "h" 'backward-char)
230 (define-key km "0" 'beginning-of-line)
231 (define-key km "^" 'beginning-of-line-text)
232 (define-key km "$" 'end-of-line)
233 (define-key km [backspace] 'backward-char)
234 (define-key km "G" 'end-of-buffer)
235 (define-key km "H" 'beginning-of-buffer)
236 (define-key km "\C-c\C-p" 'twittering-toggle-proxy)
239(defvar twittering-mode-syntax-table nil "")
241(if twittering-mode-syntax-table
243 (setq twittering-mode-syntax-table (make-syntax-table))
244 ; (modify-syntax-entry ? "" twittering-mode-syntax-table)
245 (modify-syntax-entry ?\" "w" twittering-mode-syntax-table)
248(defun twittering-mode-init-variables ()
249 ;(make-variable-buffer-local 'variable)
252 (defface twittering-username-face
253 `((t nil)) "" :group 'faces)
254 (copy-face 'font-lock-string-face 'twittering-username-face)
255 (set-face-attribute 'twittering-username-face nil :underline t)
256 (defface twittering-uri-face
257 `((t nil)) "" :group 'faces)
258 (set-face-attribute 'twittering-uri-face nil :underline t)
259 (add-to-list 'minor-mode-alist '(twittering-icon-mode " tw-icon"))
260 (add-to-list 'minor-mode-alist '(twittering-scroll-mode " tw-scroll"))
261 (add-to-list 'minor-mode-alist '(twittering-jojo-mode " tw-jojo"))
264(defmacro case-string (str &rest clauses)
268 (let ((keylist (car clause))
270 `(,(if (listp keylist)
271 `(or ,@(mapcar (lambda (key) `(string-equal ,str ,key)) keylist))
276;; If you use Emacs21, decode-char 'ucs will fail unless Mule-UCS is loaded.
277;; TODO: Show error messages if Emacs 21 without Mule-UCS
278(defmacro twittering-ucs-to-char (num)
279 (if (functionp 'ucs-to-char)
281 `(decode-char 'ucs ,num)))
283(defvar twittering-mode-string "Twittering mode")
285(defun twittering-mode ()
286 "Major mode for Twitter"
288 (switch-to-buffer (twittering-buffer))
289 (kill-all-local-variables)
290 (twittering-mode-init-variables)
291 (use-local-map twittering-mode-map)
292 (setq major-mode 'twittering-mode)
293 (setq mode-name twittering-mode-string)
294 (set-syntax-table twittering-mode-syntax-table)
295 (run-hooks 'twittering-mode-hook)
301;;; Basic HTTP functions
304(defun twittering-http-get (method-class method &optional sentinel)
305 (if (null sentinel) (setq sentinel 'twittering-http-get-default-sentinel))
309 (set-buffer (twittering-http-buffer))
312 (let (proc server port
313 (proxy-user twittering-proxy-user)
314 (proxy-password twittering-proxy-password))
317 (if (and twittering-proxy-use twittering-proxy-server)
318 (setq server twittering-proxy-server
319 port (if (integerp twittering-proxy-port)
320 (int-to-string twittering-proxy-port)
321 twittering-proxy-port))
322 (setq server "twitter.com"
326 "network-connection-process" (twittering-http-buffer)
327 server (string-to-number port)))
328 (set-process-sentinel proc sentinel)
334 (concat "GET http://twitter.com/" method-class "/" method ".xml HTTP/1.1" nl
335 "Host: twitter.com" nl
336 "User-Agent: " (twittering-user-agent) nl
337 "Authorization: Basic "
338 (base64-encode-string
339 (concat twittering-username ":" (twittering-get-password)))
343 ",application/xhtml+xml"
344 ",application/html;q=0.9"
346 ",image/png,*/*;q=0.5" nl
347 "Accept-Charset: utf-8;q=0.7,*;q=0.7" nl
348 (when twittering-proxy-use
349 "Proxy-Connection: Keep-Alive" nl
350 (when (and proxy-user proxy-password)
352 "Proxy-Authorization: Basic "
353 (base64-encode-string
354 (concat proxy-user ":"
358 (debug-print (concat "GET Request\n" request))
361 (message "Failure: HTTP GET") nil))))
363(defun twittering-http-get-default-sentinel (proc stat &optional suc-msg)
364 (let ((header (twittering-get-response-header))
365 (body (twittering-get-response-body))
368 (if (string-match "HTTP/1\.[01] \\([a-z0-9 ]+\\)\r?\n" header)
370 (setq status (match-string-no-properties 1 header))
375 #'twittering-cache-status-datum
376 (reverse (twittering-xmltree-to-status
378 (twittering-render-friends-timeline)
379 (message (if suc-msg suc-msg "Success: Get.")))
380 (t (message status))))
381 (message "Failure: Bad http response.")))
384(defun twittering-render-friends-timeline ()
385 (with-current-buffer (twittering-buffer)
386 (let ((point (point))
388 (setq buffer-read-only nil)
391 (mapconcat (lambda (status)
392 (twittering-format-status status twittering-status-format))
393 twittering-friends-timeline-data
395 (if twittering-image-stack
397 (setq buffer-read-only t)
398 (debug-print (current-buffer))
399 (goto-char (+ point (if twittering-scroll-mode (- (point-max) end) 0))))
402(defun twittering-format-status (status format-str)
404 (assocref key status))
407 (let ((profile-image-url (attr 'user-profile-image-url))
409 (if (string-match "/\\([^/?]+\\)\\(?:\\?\\|$\\)" profile-image-url)
410 (let ((filename (match-string-no-properties 1 profile-image-url)))
411 ;; download icons if does not exist
412 (if (file-exists-p (concat twittering-tmp-dir
415 (add-to-list 'twittering-image-stack profile-image-url))
417 (when (and icon-string twittering-icon-mode)
420 (image :type ,(twittering-image-type filename)
421 :file ,(concat twittering-tmp-dir
433 (while (setq found-at (string-match "%\\(C{\\([^}]+\\)}\\|[A-Za-z#@']\\)" format-str cursor))
434 (setq c (string-to-char (match-string-no-properties 1 format-str)))
435 (if (> found-at cursor)
436 (list-push (substring format-str cursor found-at) result)
438 (setq cursor (match-end 1))
441 ((?s) ; %s - screen_name
442 (list-push (attr 'user-screen-name) result))
444 (list-push (attr 'user-name) result))
445 ((?i) ; %i - profile_image
446 (list-push (profile-image) result))
447 ((?d) ; %d - description
448 (list-push (attr 'user-description) result))
449 ((?l) ; %l - location
450 (list-push (attr 'user-location) result))
451 ((?L) ; %L - " [location]"
452 (let ((location (attr 'user-location)))
453 (unless (or (null location) (string= "" location))
454 (list-push (concat " [" location "]") result)) ))
456 (list-push (attr 'user-url) result))
458 (list-push (attr 'user-id) result))
459 ((?p) ; %p - protected?
460 (let ((protected (attr 'user-protected)))
461 (when (string= "true" protected)
462 (list-push "[x]" result))))
463 ((?c) ; %c - created_at (raw UTC string)
464 (list-push (attr 'created-at) result))
465 ((?C) ; %C{time-format-str} - created_at (formatted with time-format-str)
466 (list-push (twittering-local-strftime
467 (or (match-string-no-properties 2 format-str) "%H:%M:%S")
470 ((?@) ; %@ - X seconds ago
474 (parse-time-string (attr 'created-at))))
475 (now (current-time)))
476 (let ((secs (+ (* (- (car now) (car created-at)) 65536)
477 (- (cadr now) (cadr created-at)))))
478 (list-push (cond ((< secs 5) "less than 5 seconds ago")
479 ((< secs 10) "less than 10 seconds ago")
480 ((< secs 20) "less than 20 seconds ago")
481 ((< secs 30) "half a minute ago")
482 ((< secs 60) "less than a minute ago")
483 ((< secs 150) "1 minute ago")
484 ((< secs 2400) (format "%d minutes ago"
486 ((< secs 5400) "about 1 hour ago")
487 ((< secs 84600) (format "about %d hours ago"
488 (/ (+ secs 1800) 3600)))
489 (t (format-time-string "%I:%M %p %B %d, %Y" created-at)))
492 (list-push ;(clickable-text)
495 ((?') ; %' - truncated
496 (let ((truncated (attr 'truncated)))
497 (when (string= "true" truncated)
498 (list-push "..." result))))
500 (list-push (attr 'source) result))
502 (list-push (attr 'id) result))
504 (list-push (char-to-string c) result)))
506 (list-push (substring format-str cursor) result)
507 (apply 'concat (nreverse result))
510(defun twittering-http-post
511 (method-class method &optional parameters contents sentinel)
512 "Send HTTP POST request to twitter.com
514METHOD-CLASS must be one of Twitter API method classes(statuses, users or direct_messages).
515METHOD must be one of Twitter API method which belongs to METHOD-CLASS.
516PARAMETERS is alist of URI parameters. ex) ((\"mode\" . \"view\") (\"page\" . \"6\")) => <URI>?mode=view&page=6"
517 (if (null sentinel) (setq sentinel 'twittering-http-post-default-sentinel))
521 (set-buffer (twittering-http-buffer))
524 (let (proc server port
525 (proxy-user twittering-proxy-user)
526 (proxy-password twittering-proxy-password))
528 (if (and twittering-proxy-use twittering-proxy-server)
529 (setq server twittering-proxy-server
530 port (if (integerp twittering-proxy-port)
531 (int-to-string twittering-proxy-port)
532 twittering-proxy-port))
533 (setq server "twitter.com"
537 "network-connection-process" (twittering-http-buffer)
538 server (string-to-number port)))
539 (set-process-sentinel proc sentinel)
545 (concat "POST http://twitter.com/" method-class "/" method ".xml?"
550 (twittering-percent-encode (car param-pair))
551 (twittering-percent-encode (cdr param-pair))))
555 "Host: twitter.com" nl
556 "User-Agent: " (twittering-user-agent) nl
557 "Authorization: Basic "
558 (base64-encode-string
559 (concat twittering-username ":" (twittering-get-password)))
561 "Content-Type: text/plain" nl
562 "Content-Length: 0" nl
563 (when twittering-proxy-use
564 "Proxy-Connection: Keep-Alive" nl
565 (when (and proxy-user proxy-password)
567 "Proxy-Authorization: Basic "
568 (base64-encode-string
569 (concat proxy-user ":"
573 (debug-print (concat "POST Request\n" request))
576(defun twittering-http-post-default-sentinel (proc stat &optional suc-msg)
578 (condition-case err-signal
579 (let ((header (twittering-get-response-header))
580 ; (body (twittering-get-response-body)) not used now.
582 (string-match "HTTP/1\.1 \\([a-z0-9 ]+\\)\r?\n" header)
583 (setq status (match-string-no-properties 1 header))
586 (message (if suc-msg suc-msg "Success: Post")))
587 (t (message status)))
589 (error (message (prin1-to-string err-signal))))
592(defun twittering-get-response-header (&optional buffer)
593 "Exract HTTP response header from HTTP response.
594`buffer' may be a buffer or the name of an existing buffer.
595 If `buffer' is omitted, the value of `twittering-http-buffer' is used as `buffer'."
596 (if (stringp buffer) (setq buffer (get-buffer buffer)))
597 (if (null buffer) (setq buffer (twittering-http-buffer)))
600 (let ((content (buffer-string)))
601 (substring content 0 (string-match "\r?\n\r?\n" content)))))
603(defun twittering-get-response-body (&optional buffer)
604 "Exract HTTP response body from HTTP response, parse it as XML, and return a XML tree as list.
605`buffer' may be a buffer or the name of an existing buffer.
606 If `buffer' is omitted, the value of `twittering-http-buffer' is used as `buffer'."
607 (if (stringp buffer) (setq buffer (get-buffer buffer)))
608 (if (null buffer) (setq buffer (twittering-http-buffer)))
611 (let ((content (buffer-string)))
612 (let ((content (buffer-string)))
613 (xml-parse-region (+ (string-match "\r?\n\r?\n" content)
614 (length (match-string 0 content)))
618(defun twittering-cache-status-datum (status-datum &optional data-var)
619 "Cache status datum into data-var(default twittering-friends-timeline-data)
620If STATUS-DATUM is already in DATA-VAR, return nil. If not, return t."
622 (setf data-var 'twittering-friends-timeline-data))
623 (let ((id (cdr (assq 'id status-datum))))
624 (if (or (null (symbol-value data-var))
627 (eql id (cdr (assq 'id item))))
628 (symbol-value data-var))))
630 (if twittering-jojo-mode
631 (twittering-update-jojo (cdr (assq 'user-screen-name status-datum))
632 (cdr (assq 'text status-datum))))
633 (set data-var (cons status-datum (symbol-value data-var)))
637(defun twittering-status-to-status-datum (status)
638 (flet ((assq-get (item seq)
639 (car (cddr (assq item seq)))))
640 (let* ((status-data (cddr status))
641 id text source created-at truncated
642 (user-data (cddr (assq 'user status-data)))
647 user-profile-image-url
652 (setq id (string-to-number (assq-get 'id status-data)))
653 (setq text (twittering-decode-html-entities
654 (assq-get 'text status-data)))
655 (setq source (twittering-decode-html-entities
656 (assq-get 'source status-data)))
657 (setq created-at (assq-get 'created_at status-data))
658 (setq truncated (assq-get 'truncated status-data))
659 (setq user-id (string-to-number (assq-get 'id user-data)))
660 (setq user-name (twittering-decode-html-entities
661 (assq-get 'name user-data)))
662 (setq user-screen-name (twittering-decode-html-entities
663 (assq-get 'screen_name user-data)))
664 (setq user-location (twittering-decode-html-entities
665 (assq-get 'location user-data)))
666 (setq user-description (twittering-decode-html-entities
667 (assq-get 'description user-data)))
668 (setq user-profile-image-url (assq-get 'profile_image_url user-data))
669 (setq user-url (assq-get 'url user-data))
670 (setq user-protected (assq-get 'protected user-data))
672 ;; make username clickable
673 (add-text-properties 0 (length user-screen-name)
674 `(mouse-face highlight
675 uri ,(concat "http://twitter.com/" user-screen-name)
676 username ,user-screen-name
677 face twittering-username-face)
680 ;; make URI clickable
684 (string-match "@\\([_a-zA-Z0-9]+\\)\\|\\(https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+\\)"
688 (let* ((matched-string (match-string-no-properties 0 text))
689 (screen-name (match-string-no-properties 1 text))
690 (uri (match-string-no-properties 2 text)))
693 (+ 1 (match-beginning 0))
699 face twittering-uri-face
700 username ,screen-name
701 uri ,(concat "http://twitter.com/" screen-name))
702 `(mouse-face highlight
703 face twittering-uri-face
706 (setq regex-index (match-end 0)) ))
708 ;; make screen-name clickable
710 0 (length user-screen-name)
711 `(mouse-face highlight
712 face twittering-username-face
713 uri ,(concat "http://twitter.com/" user-screen-name)
714 username ,user-screen-name)
717 ;; make source pretty and clickable
718 (if (string-match "<a href=\"\\(.*\\)\">\\(.*\\)</a>" source)
719 (let ((uri (match-string-no-properties 1 source))
720 (caption (match-string-no-properties 2 source)))
721 (setq source caption)
724 `(mouse-face highlight
726 face twittering-uri-face
733 `(,sym . ,(symbol-value sym)))
734 '(id text source created-at truncated
735 user-id user-name user-screen-name user-location
737 user-profile-image-url
741(defun twittering-xmltree-to-status (xmltree)
742 (mapcar #'twittering-status-to-status-datum
743 ;; quirk to treat difference between xml.el in Emacs21 and Emacs22
744 ;; On Emacs22, there may be blank strings
745 (let ((ret nil) (statuses (reverse (cddr (car xmltree)))))
747 (if (consp (car statuses))
748 (setq ret (cons (car statuses) ret)))
749 (setq statuses (cdr statuses)))
752(defun twittering-percent-encode (str &optional coding-system)
753 (if (or (null coding-system)
754 (not (coding-system-p coding-system)))
755 (setq coding-system 'utf-8))
759 ((twittering-url-reserved-p c)
762 (t (format "%%%x" c))))
763 (encode-coding-string str coding-system)
766(defun twittering-url-reserved-p (ch)
767 (or (and (<= ?A ch) (<= ch ?z))
768 (and (<= ?0 ch) (<= ch ?9))
774(defun twittering-decode-html-entities (encoded-str)
779 (while (setq found-at
780 (string-match "&\\(#\\([0-9]+\\)\\|\\([A-Za-z]+\\)\\);"
782 (when (> found-at cursor)
783 (list-push (substring encoded-str cursor found-at) result))
784 (let ((number-entity (match-string-no-properties 2 encoded-str))
785 (letter-entity (match-string-no-properties 3 encoded-str)))
789 (twittering-ucs-to-char
790 (string-to-number number-entity))) result))
792 (cond ((string= "gt" letter-entity) (list-push ">" result))
793 ((string= "lt" letter-entity) (list-push "<" result))
794 (t (list-push "?" result))))
795 (t (list-push "?" result)))
796 (setq cursor (match-end 0))))
797 (list-push (substring encoded-str cursor) result)
798 (apply 'concat (nreverse result)))
801(defun twittering-timer-action (func)
802 (let ((buf (get-buffer twittering-buffer)))
808(defun twittering-update-status-if-not-blank (status)
809 (if (string-match "^\\s-*\\(?:@[-_a-z0-9]+\\)?\\s-*$" status)
811 (twittering-http-post "statuses" "update"
812 `(("status" . ,status)
813 ("source" . "twmode")))
816(defun twittering-update-status-from-minibuffer (&optional init-str)
817 (if (null init-str) (setq init-str ""))
818 (let ((status init-str) (not-posted-p t))
820 (setq status (read-from-minibuffer "status: " status nil nil nil nil t))
822 (not (twittering-update-status-if-not-blank status))))))
824(defun twittering-update-lambda ()
826 (twittering-http-post
828 `(("status" . "\xd34b\xd22b\xd26f\xd224\xd224\xd268\xd34b")
829 ("source" . "twmode"))))
831(defun twittering-update-jojo (usr msg)
832 (if (string-match "\xde21\xd24b\\(\xd22a\xe0b0\\|\xdaae\xe6cd\\)\xd24f\xd0d6\\([^\xd0d7]+\\)\xd0d7\xd248\xdc40\xd226"
834 (twittering-http-post
836 `(("status" . ,(concat
838 (match-string-no-properties 2 msg)
839 "\xd0a1\xd24f\xd243!?"))
840 ("source" . "twmode")))))
846(defun twittering-start (&optional action)
849 (setq action #'twittering-friends-timeline))
852 (setq twittering-timer
854 twittering-timer-interval
855 #'twittering-timer-action action))))
857(defun twittering-stop ()
859 (cancel-timer twittering-timer)
860 (setq twittering-timer nil))
862(defun twittering-friends-timeline ()
864 (let ((buf (get-buffer twittering-buffer)))
867 (twittering-http-get "statuses" "friends_timeline")
870 (if twittering-icon-mode
871 (if twittering-image-stack
876 (twittering-wget-buffer)
878 (format "--directory-prefix=%s" twittering-tmp-dir)
881 twittering-image-stack)))
882 (set-process-sentinel
887 (set-buffer (twittering-wget-buffer))
890(defun twittering-update-status-interactive ()
892 (twittering-update-status-from-minibuffer))
894(defun twittering-erase-old-statuses ()
896 (setq twittering-friends-timeline-data nil)
897 (twittering-http-get "statuses" "friends_timeline"))
899(defun twittering-click ()
901 (let ((uri (get-text-property (point) 'uri)))
905(defun twittering-enter ()
907 (let ((username (get-text-property (point) 'username))
908 (uri (get-text-property (point) 'uri)))
910 (twittering-update-status-from-minibuffer (concat "@" username " "))
914(defun twittering-view-user-page ()
916 (let ((uri (get-text-property (point) 'uri)))
920(defun twittering-reply-to-user ()
922 (let ((username (get-text-property (point) 'username)))
924 (twittering-update-status-from-minibuffer (concat "@" username " ")))))
926(defun twittering-get-password ()
927 (or twittering-password
928 (setq twittering-password (read-passwd "twittering-mode: "))))
930(provide 'twittering-mode)
931;;; twittering.el ends here