1 ;; mixi.el --- API library for accessing to mixi
3 ;; Copyright (C) 2005,2006 OHASHI Akira
5 ;; Author: OHASHI Akira <bg66@koka-in.org>
6 ;; Keywords: hypermedia
8 ;; This file is *NOT* a part of Emacs.
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program; if not, you can either send email to this
22 ;; program's maintainer or write to: The Free Software Foundation,
23 ;; Inc.; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 ;; API for getting contents:
30 ;; * mixi-get-favorites
33 ;; * mixi-get-new-diaries
34 ;; * mixi-get-communities
36 ;; * mixi-get-new-topics
37 ;; * mixi-get-comments
38 ;; * mixi-get-new-comments
42 ;; Display newest 3 diaries like a mail format.
44 ;; (let ((max-numbers 3)
45 ;; (buffer (generate-new-buffer "*temp*"))
46 ;; (format "%Y/%m/%d %H:%M"))
47 ;; (pop-to-buffer buffer)
48 ;; (mapc (lambda (diary)
49 ;; (let ((subject (mixi-diary-title diary))
50 ;; ;; Don't get owner's nick at first for omitting a useless
52 ;; (from (mixi-friend-nick (mixi-diary-owner diary)))
53 ;; (date (format-time-string format (mixi-diary-time diary)))
54 ;; (body (mixi-diary-content diary)))
55 ;; (insert "From: " from "\n"
56 ;; "Subject: " subject "\n"
57 ;; "Date: " date "\n\n"
59 ;; (mixi-get-new-diaries max-numbers))
60 ;; (set-buffer-modified-p nil)
61 ;; (setq buffer-read-only t)
62 ;; (goto-char (point-min)))
64 ;; Display newest 3 diaries including newest 3 comments like a mail format.
65 ;; Comments are displayed like a reply mail.
67 ;; (let ((max-numbers 3)
68 ;; (buffer (generate-new-buffer "*temp*"))
69 ;; (format "%Y/%m/%d %H:%M"))
70 ;; (pop-to-buffer buffer)
71 ;; (mapc (lambda (diary)
72 ;; (let ((subject (mixi-diary-title diary))
73 ;; ;; Don't get owner's nick at first for omitting a useless
75 ;; (from (mixi-friend-nick (mixi-diary-owner diary)))
76 ;; (date (format-time-string format (mixi-diary-time diary)))
77 ;; (body (mixi-diary-content diary)))
78 ;; (insert "From: " from "\n"
79 ;; "Subject: " subject "\n"
80 ;; "Date: " date "\n\n"
82 ;; (mapc (lambda (comment)
83 ;; (let ((from (mixi-friend-nick
84 ;; (mixi-comment-owner comment)))
85 ;; (subject (concat "Re: " subject))
86 ;; (date (format-time-string
87 ;; format (mixi-comment-time comment)))
88 ;; (body (mixi-comment-content comment)))
89 ;; (insert "From: " from "\n"
90 ;; "Subject: " subject "\n"
91 ;; "Date: " date "\n\n"
93 ;; (mixi-get-comments diary max-numbers))))
94 ;; (mixi-get-new-diaries max-numbers))
95 ;; (set-buffer-modified-p nil)
96 ;; (setq buffer-read-only t)
97 ;; (goto-char (point-min)))
109 (eval-when-compile (require 'cl))
112 "API library for accessing to mixi."
115 (defcustom mixi-url "http://mixi.jp"
120 (defcustom mixi-coding-system 'euc-jp
121 "*Coding system for mixi."
125 (defcustom mixi-retrieve-function
126 (if (fboundp 'url-retrieve-synchronously)
127 'mixi-w3-retrieve 'mixi-w3m-retrieve)
128 "*The function for retrieving."
129 :type '(choice (const :tag "Using w3" mixi-w3-retrieve)
130 (const :tag "Using w3m" mixi-w3m-retrieve)
131 (const :tag "Using curl" mixi-curl-retrieve)
132 (function :format "Other function: %v\n" :size 0))
135 (defcustom mixi-curl-program "curl"
136 "*The program name of `curl'."
140 (defcustom mixi-curl-cookie-file (expand-file-name "~/.mixi-cookies.txt")
141 "*The location of cookie file created by `curl'."
145 (defcustom mixi-default-email nil
146 "*Default E-mail address that is used to login automatically."
147 :type '(choice (string :tag "E-mail address")
148 (const :tag "Asked when it is necessary" nil))
151 (defcustom mixi-default-password nil
152 "*Default password that is used to login automatically."
153 :type '(choice (string :tag "Password")
154 (const :tag "Asked when it is necessary" nil))
157 (defcustom mixi-accept-adult-contents t
158 "*If non-nil, accept to access to the adult contents."
162 (defcustom mixi-continuously-access-interval 4.0
163 "*Time interval between each mixi access.
164 Increase this value when unexpected error frequently occurs."
168 (defcustom mixi-cache-expires 3600
169 "*Seconds for expiration of a cached object."
170 :type '(choice (integer :tag "Expired seconds")
171 (const :tag "Don't expire" nil))
174 ;; FIXME: Not implemented.
175 (defcustom mixi-cache-use-file t
176 "*If non-nil, caches are saved to files."
180 (defcustom mixi-cache-directory (expand-file-name "~/.mixi")
181 "*Where to look for cache files."
188 (defmacro mixi-message (string)
189 `(concat "[mixi] " ,string))
191 (defconst mixi-message-adult-contents
192 "¤³¤Î¥Ú¡¼¥¸¤«¤éÀè¤Ï¥¢¥À¥ë¥È¡ÊÀ®¿Í¸þ¤±¡Ë¥³¥ó¥Æ¥ó¥Ä¤¬´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£<br>
193 ±ÜÍ÷¤ËƱ°Õ¤µ¤ì¤¿Êý¤Î¤ß¡¢Àè¤Ø¤ª¿Ê¤ß¤¯¤À¤µ¤¤¡£")
194 (defconst mixi-message-continuously-accessing
195 "°ÂÄꤷ¤Æ¥µ¥¤¥È¤Î±¿±Ä¤ò¤ª¤³¤Ê¤¦°Ù¡¢´Ö³Ö¤ò¶õ¤±¤Ê¤¤Ï¢Â³Åª¤Ê¥Ú¡¼¥¸¤ÎÁ«°Ü¡¦¹¹<br>
196 ¿·¤ÏÀ©¸Â¤µ¤»¤Æ¤¤¤¿¤À¤¤¤Æ¤ª¤ê¤Þ¤¹¡£¤´ÌÂÏǤò¤ª¤«¤±¤¤¤¿¤·¤Þ¤¹¤¬¡¢¤·¤Ð¤é¤¯¤ª<br>
197 ÂÔ¤Á¤¤¤¿¤À¤¤¤Æ¤«¤éÁàºî¤ò¤ª¤³¤Ê¤Ã¤Æ¤¯¤À¤µ¤¤¡£")
198 (defconst mixi-warning-continuously-accessing
199 "´Ö³Ö¤ò¶õ¤±¤Ê¤¤Ï¢Â³Åª¤Ê¥Ú¡¼¥¸¤ÎÁ«°Ü¡¦¹¹¿·¤òÉÑÈˤˤª¤³¤Ê¤ï¤ì¤Æ¤¤¤ë¤³¤È¤¬¸«<br>
200 ¼õ¤±¤é¤ì¤Þ¤·¤¿¤Î¤Ç¡¢°ì»þŪ¤ËÁàºî¤òÄä»ß¤µ¤»¤Æ¤¤¤¿¤À¤¤Þ¤¹¡£¿½¤·Ìõ¤´¤¶¤¤¤Þ<br>
201 ¤»¤ó¤¬¡¢¤·¤Ð¤é¤¯¤Î´Ö¤ªÂÔ¤Á¤¯¤À¤µ¤¤¡£")
203 (defun mixi-retrieve-1 (buffer url &optional post-data)
204 (when (string-match mixi-message-adult-contents buffer)
205 (if mixi-accept-adult-contents
206 (setq buffer (funcall mixi-retrieve-function url "submit=agree"))
207 (setq buffer (funcall mixi-retrieve-function (concat url "?")))))
208 (when (string-match mixi-warning-continuously-accessing buffer)
209 (error (mixi-message "Continuously accessing")))
210 (if (not (string-match mixi-message-continuously-accessing buffer))
212 (message (mixi-message "Waiting for continuously accessing..."))
213 (sit-for mixi-continuously-access-interval)
214 (funcall mixi-retrieve-function url post-data)))
216 (defun mixi-w3-retrieve (url &optional post-data)
217 "Retrieve the URL and return gotten strings."
220 (setq url-request-method "POST")
221 (setq url-request-data post-data))
222 (setq url-request-method "GET")
223 (setq url-request-data nil))
224 (let* ((url (url-expand-file-name url mixi-url))
225 (buffer (url-retrieve-synchronously url))
227 (unless (bufferp buffer)
228 (error (mixi-message "Cannot retrieve")))
229 (with-current-buffer buffer
230 (goto-char (point-min))
231 (if (re-search-forward "HTTP/[0-9.]+ 302 Moved" nil t)
232 (if (re-search-forward
233 (concat "Location: " mixi-url "\\(.+\\)") nil t)
234 (setq ret (mixi-w3-retrieve (match-string 1) post-data))
235 (setq ret (mixi-w3-retrieve "/home.pl" post-data)))
236 (unless (re-search-forward "HTTP/[0-9.]+ 200 OK" nil t)
237 (error (mixi-message "Cannot retrieve")))
238 (search-forward "\n\n")
239 (setq ret (mm-decode-coding-string
240 (buffer-substring-no-properties (point) (point-max))
243 (setq ret (mixi-retrieve-1 ret url post-data))))
246 (defun mixi-w3m-retrieve (url &optional post-data)
247 "Retrieve the URL and return gotten strings."
248 (let ((url (w3m-expand-url url mixi-url)))
250 (if (not (string= (w3m-retrieve url nil nil post-data) "text/html"))
251 (error (mixi-message "Cannot retrieve"))
252 (w3m-decode-buffer url)
253 (let ((ret (buffer-substring-no-properties (point-min) (point-max))))
254 (mixi-retrieve-1 ret url post-data))))))
256 (defun mixi-curl-retrieve (url &optional post-data)
257 "Retrieve the URL and return gotten strings."
259 (if (fboundp 'set-buffer-multibyte)
260 (set-buffer-multibyte nil))
261 (let ((orig-mode (default-file-modes))
262 (coding-system-for-read 'binary)
263 (coding-system-for-write 'binary)
267 (set-default-file-modes 448)
269 (apply #'start-process "curl" (current-buffer)
271 (append (if post-data '("-d" "@-"))
273 "-b" mixi-curl-cookie-file
274 "-c" mixi-curl-cookie-file
275 (concat mixi-url url)))))
276 (set-process-sentinel process #'ignore))
277 (set-default-file-modes orig-mode))
279 (process-send-string process (concat post-data "\n"))
280 (process-send-eof process))
281 (while (eq (process-status process) 'run)
282 (accept-process-output process 1))
283 (goto-char (point-min))
284 (while (looking-at "HTTP/[0-9]+\\.[0-9]+ [13][0-9][0-9]")
285 (delete-region (point) (re-search-forward "\r?\n\r?\n")))
286 (unless (looking-at "HTTP/[0-9]+\\.[0-9]+ 200")
287 (error (mixi-message "Cannot retrieve")))
288 (delete-region (point) (re-search-forward "\r?\n\r?\n"))
289 (setq ret (decode-coding-string (buffer-string) mixi-coding-system))
290 (mixi-retrieve-1 ret url post-data))))
292 (defconst mixi-my-id-regexp
293 "<a href=\"add_diary\\.pl\\?id=\\([0-9]+\\)")
295 (defun mixi-login (&optional email password)
297 (when (and (eq mixi-retrieve-function 'mixi-w3m-retrieve)
298 (not w3m-use-cookies))
301 "Require to accept cookies. Please set `w3m-use-cookies' to t.")))
302 (let ((email (or email mixi-default-email
303 (read-from-minibuffer (mixi-message "Login Email: "))))
304 (password (or password mixi-default-password
305 (read-passwd (mixi-message "Login Password: ")))))
306 (let ((buffer (funcall mixi-retrieve-function "/login.pl"
307 (concat "email=" email
308 "&password=" password
311 (unless (string-match "url=/check\\.pl\\?n=" buffer)
312 (error (mixi-message "Cannot login")))
313 (setq buffer (funcall mixi-retrieve-function "/check.pl?n=home.pl"))
314 (if (string-match mixi-my-id-regexp buffer)
315 (setq mixi-me (mixi-make-friend (match-string 1 buffer)))
316 (error (mixi-message "Cannot login"))))))
318 (defun mixi-logout ()
319 (funcall mixi-retrieve-function "/logout.pl"))
321 (defmacro with-mixi-retrieve (url &rest body)
324 (setq buffer (funcall mixi-retrieve-function ,url))
325 (when (string-match "login.pl" buffer)
327 (setq buffer (funcall mixi-retrieve-function ,url))))
329 (put 'with-mixi-retrieve 'lisp-indent-function 'defun)
330 (put 'with-mixi-retrieve 'edebug-form-spec '(form body))
332 (defun mixi-get-matched-items (url max-numbers regexp)
333 "Get matched items to REGEXP in URL."
337 (while (or (null max-numbers) (< (length ids) max-numbers))
338 (with-mixi-retrieve (format url page)
340 (while (and (string-match regexp buffer pos)
341 (or (null max-numbers) (< (length ids) max-numbers)))
344 (while (match-string num buffer)
345 (setq list (cons (match-string num buffer) list))
347 (when (member (reverse list) ids)
349 (setq ids (cons (reverse list) ids))
350 (setq pos (match-end (1- num)))))
354 ;; FIXME: Sort? Now order by newest.
357 ;; stolen (and modified) from shimbun.el
358 (defun mixi-remove-markup (string)
359 "Remove markups from STRING."
363 (goto-char (point-min))
364 (while (search-forward "<!--" nil t)
365 (delete-region (match-beginning 0)
366 (or (search-forward "-->" nil t)
368 (goto-char (point-min))
369 (while (re-search-forward "<[^>]+>" nil t)
370 (replace-match "" t t))
371 (goto-char (point-min))
372 (while (re-search-forward "
\r" nil t)
373 (replace-match "\n" t t)))
378 ;; stolen from time-date.el
379 (defmacro with-mixi-decoded-time-value (varlist &rest body)
380 "Decode a time value and bind it according to VARLIST, then eval BODY.
382 The value of the last form in BODY is returned.
384 Each element of the list VARLIST is a list of the form
385 \(HIGH-SYMBOL LOW-SYMBOL MICRO-SYMBOL [TYPE-SYMBOL] TIME-VALUE).
386 The time value TIME-VALUE is decoded and the result it bound to
387 the symbols HIGH-SYMBOL, LOW-SYMBOL and MICRO-SYMBOL.
389 The optional TYPE-SYMBOL is bound to the type of the time value.
390 Type 0 is the cons cell (HIGH . LOW), type 1 is the list (HIGH
391 LOW), and type 3 is the list (HIGH LOW MICRO)."
393 (debug ((&rest (symbolp symbolp symbolp &or [symbolp form] form))
396 (let* ((elt (pop varlist))
400 (type (unless (eq (length elt) 1)
402 (time-value (car elt))
403 (gensym (make-symbol "time")))
404 `(let* ,(append `((,gensym ,time-value)
405 (,high (pop ,gensym))
407 (when type `(,type)))
410 (setq ,low (pop ,gensym))
412 ,(append `(setq ,micro (car ,gensym))
413 (when type `(,type 2)))
414 ,(append `(setq ,micro 0)
415 (when type `(,type 1)))))
416 ,(append `(setq ,low ,gensym ,micro 0)
417 (when type `(,type 0))))
418 (with-mixi-decoded-time-value ,varlist ,@body)))
420 (put 'with-mixi-decoded-time-value 'lisp-indent-function 'defun)
421 (put 'with-mixi-decoded-time-value 'edebug-form-spec '(form body))
423 ;; stolen from time-date.el
424 (defun mixi-encode-time-value (high low micro type)
425 "Encode HIGH, LOW, and MICRO into a time value of type TYPE.
426 Type 0 is the cons cell (HIGH . LOW), type 1 is the list (HIGH LOW),
427 and type 3 is the list (HIGH LOW MICRO)."
429 ((eq type 0) (cons high low))
430 ((eq type 1) (list high low))
431 ((eq type 2) (list high low micro))))
433 ;; stolen from time-date.el
434 (defun mixi-time-less-p (t1 t2)
435 "Say whether time value T1 is less than time value T2."
436 (with-mixi-decoded-time-value ((high1 low1 micro1 t1)
437 (high2 low2 micro2 t2))
442 (< micro1 micro2)))))))
444 ;; stolen from time-date.el
445 (defun mixi-time-add (t1 t2)
446 "Add two time values. One should represent a time difference."
447 (with-mixi-decoded-time-value ((high low micro type t1)
448 (high2 low2 micro2 type2 t2))
449 (setq high (+ high high2)
451 micro (+ micro micro2)
452 type (max type type2))
453 (when (>= micro 1000000)
455 micro (- micro 1000000)))
459 (mixi-encode-time-value high low micro type)))
461 ;; stolen from time-date.el
462 (defun mixi-seconds-to-time (seconds)
463 "Convert SECONDS (a floating point number) to a time value."
464 (list (floor seconds 65536)
465 (floor (mod seconds 65536))
466 (floor (* (- seconds (ffloor seconds)) 1000000))))
468 (defun mixi-cache-expired-p (object)
469 "Whether a cache of OBJECT is expired."
470 ;; FIXME: Use method instead of `(aref (cdr object) 0)'.
471 (let ((timestamp (aref (cdr object) 0)))
472 (unless (or (null mixi-cache-expires)
475 (mixi-time-add timestamp (mixi-seconds-to-time mixi-cache-expires))
478 (defun mixi-make-cache (key value table)
479 "Make a cache object and return it."
480 (let ((cache (gethash key table)))
481 (if (and cache (not (mixi-cache-expired-p cache)))
483 (puthash key value table))))
486 (defconst mixi-object-prefix "mixi-")
488 (defmacro mixi-object-class (object)
491 (defmacro mixi-object-p (object)
492 `(eq (string-match (concat "^" mixi-object-prefix)
493 (symbol-name (mixi-object-class ,object))) 0))
495 (defun mixi-object-name (object)
496 "Return the name of OBJECT."
497 (unless (mixi-object-p object)
498 (signal 'wrong-type-argument (list 'mixi-object-p object)))
499 (let ((class (mixi-object-class object)))
500 (substring (symbol-name class) (length mixi-object-prefix))))
502 (defun mixi-object-id (object)
503 "Return the id of OBJECT."
504 (unless (mixi-object-p object)
505 (signal 'wrong-type-argument (list 'mixi-object-p object)))
506 (let ((func (intern (concat mixi-object-prefix
507 (mixi-object-name object) "-id"))))
508 (funcall func object)))
511 (defvar mixi-friend-cache (make-hash-table :test 'equal))
512 (defun mixi-make-friend (id &optional nick)
513 "Return a friend object."
514 (mixi-make-cache id (cons 'mixi-friend (vector nil id nick nil nil nil nil
515 nil nil nil nil nil nil nil))
518 (defun mixi-make-me ()
520 (with-mixi-retrieve "/home.pl"
521 (if (string-match mixi-my-id-regexp buffer)
522 (setq mixi-me (mixi-make-friend (match-string 1 buffer)))
523 (signal 'error (list 'who-am-i)))))
526 (defmacro mixi-friend-p (friend)
527 `(eq (mixi-object-class ,friend) 'mixi-friend))
529 (defmacro mixi-friend-page (friend)
530 `(concat "/show_friend.pl?id=" (mixi-friend-id ,friend)))
532 (defconst mixi-friend-nick-regexp
533 "<img alt=\"\\*\" src=\"http://img\\.mixi\\.jp/img/dot0\\.gif\" width=\"1\" height=\"5\"><br>\n\\(.*\\)¤µ¤ó([0-9]+)")
534 (defconst mixi-friend-name-sex-regexp
535 "<td BGCOLOR=#F2DDB7 WIDTH=80 NOWRAP><font COLOR=#996600>̾\\( \\| \\)Á°</font></td>\n+<td WIDTH=345>\\([^<]+\\) (\\([Ã˽÷]\\)À)</td>")
536 (defconst mixi-friend-address-regexp
537 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¸½½»½ê</font></td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
538 (defconst mixi-friend-age-regexp
539 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>ǯ\\( \\| \\)Îð</font></td>\n<td>\\([0-9]+\\)ºÐ\\(\n.+\n\\)?</td></tr>")
540 (defconst mixi-friend-birthday-regexp
541 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>ÃÂÀ¸Æü</font></td>\n<td>\\([0-9]+\\)·î\\([0-9]+\\)Æü\\(\n.+\n\\)?</td></tr>")
542 (defconst mixi-friend-blood-type-regexp
543 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>·ì±Õ·¿</font></td>\n<td>\\([ABO]B?\\)·¿\\(\n\n\\)?</td></tr>")
544 (defconst mixi-friend-birthplace-regexp
545 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>½Ð¿ÈÃÏ</font>\n?</td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
546 (defconst mixi-friend-hobby-regexp
547 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¼ñ\\( \\| \\)Ì£</font></td>\n<td>\\(.+\\)</td></tr>")
548 (defconst mixi-friend-job-regexp
549 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¿¦\\( \\| \\)¶È</font></td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
550 (defconst mixi-friend-organization-regexp
551 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>½ê\\( \\| \\)°</font></td>\n<td[^>]*>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
552 (defconst mixi-friend-profile-regexp
553 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¼«¸Ê¾Ò²ð</font></td>\n<td CLASS=h120>\\(.+\\)</td></tr>")
555 (defun mixi-friend-realize (friend)
557 ;; FIXME: Check a expiration of cache?
558 (unless (mixi-friend-realize-p friend)
560 (with-mixi-retrieve (mixi-friend-page friend)
562 (if (string-match mixi-friend-nick-regexp buf)
563 (mixi-friend-set-nick friend (match-string 1 buf))
564 (signal 'error (list 'cannot-find-nick friend)))
565 ;; For getting my profile.
566 (unless (string-match mixi-friend-name-sex-regexp buf)
567 (with-mixi-retrieve "/show_profile.pl"
569 (if (string-match mixi-friend-name-sex-regexp buf)
571 (mixi-friend-set-name friend (match-string 2 buf))
572 (mixi-friend-set-sex friend
573 (if (string= (match-string 3 buf) "ÃË")
575 (signal 'error (list 'cannot-find-name-or-sex friend)))
576 (when (string-match mixi-friend-address-regexp buf)
577 (mixi-friend-set-address friend (match-string 1 buf)))
578 (when (string-match mixi-friend-age-regexp buf)
580 friend (string-to-number (match-string 2 buf))))
581 (when (string-match mixi-friend-birthday-regexp buf)
582 (mixi-friend-set-birthday
583 friend (list (string-to-number (match-string 1 buf))
584 (string-to-number (match-string 2 buf)))))
585 (when (string-match mixi-friend-blood-type-regexp buf)
586 (mixi-friend-set-blood-type friend (intern (match-string 1 buf))))
587 (when (string-match mixi-friend-birthplace-regexp buf)
588 (mixi-friend-set-birthplace friend (match-string 1 buf)))
589 (when (string-match mixi-friend-hobby-regexp buf)
590 (mixi-friend-set-hobby
591 friend (split-string (match-string 2 buf) ", ")))
592 (when (string-match mixi-friend-job-regexp buf)
593 (mixi-friend-set-job friend (match-string 2 buf)))
594 (when (string-match mixi-friend-organization-regexp buf)
595 (mixi-friend-set-organization friend (match-string 2 buf)))
596 (when (string-match mixi-friend-profile-regexp buf)
597 (mixi-friend-set-profile
598 friend (mixi-remove-markup (match-string 1 buf)))))
599 (mixi-friend-touch friend)))
601 (defun mixi-friend-realize-p (friend)
602 "Return the timestamp of FRIEND."
603 (unless (mixi-friend-p friend)
604 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
605 (aref (cdr friend) 0))
607 (defun mixi-friend-id (friend)
608 "Return the id of FRIEND."
609 (unless (mixi-friend-p friend)
610 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
611 (aref (cdr friend) 1))
613 (defun mixi-friend-nick (friend)
614 "Return the nick of FRIEND."
615 (unless (mixi-friend-p friend)
616 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
617 (unless (aref (cdr friend) 2)
618 (mixi-friend-realize friend))
619 (aref (cdr friend) 2))
621 (defun mixi-friend-name (friend)
622 "Return the name of FRIEND."
623 (unless (mixi-friend-p friend)
624 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
625 (mixi-friend-realize friend)
626 (aref (cdr friend) 3))
628 (defun mixi-friend-sex (friend)
629 "Return the sex of FRIEND."
630 (unless (mixi-friend-p friend)
631 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
632 (mixi-friend-realize friend)
633 (aref (cdr friend) 4))
635 (defun mixi-friend-address (friend)
636 "Return the address of FRIEND."
637 (unless (mixi-friend-p friend)
638 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
639 (mixi-friend-realize friend)
640 (aref (cdr friend) 5))
642 (defun mixi-friend-age (friend)
643 "Return the age of FRIEND."
644 (unless (mixi-friend-p friend)
645 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
646 (mixi-friend-realize friend)
647 (aref (cdr friend) 6))
649 (defun mixi-friend-birthday (friend)
650 "Return the birthday of FRIEND."
651 (unless (mixi-friend-p friend)
652 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
653 (mixi-friend-realize friend)
654 (aref (cdr friend) 7))
656 (defun mixi-friend-blood-type (friend)
657 "Return the blood type of FRIEND."
658 (unless (mixi-friend-p friend)
659 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
660 (mixi-friend-realize friend)
661 (aref (cdr friend) 8))
663 (defun mixi-friend-birthplace (friend)
664 "Return the birthplace of FRIEND."
665 (unless (mixi-friend-p friend)
666 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
667 (mixi-friend-realize friend)
668 (aref (cdr friend) 9))
670 (defun mixi-friend-hobby (friend)
671 "Return the hobby of FRIEND."
672 (unless (mixi-friend-p friend)
673 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
674 (mixi-friend-realize friend)
675 (aref (cdr friend) 10))
677 (defun mixi-friend-job (friend)
678 "Return the job of FRIEND."
679 (unless (mixi-friend-p friend)
680 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
681 (mixi-friend-realize friend)
682 (aref (cdr friend) 11))
684 (defun mixi-friend-organization (friend)
685 "Return the organization of FRIEND."
686 (unless (mixi-friend-p friend)
687 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
688 (mixi-friend-realize friend)
689 (aref (cdr friend) 12))
691 (defun mixi-friend-profile (friend)
692 "Return the pforile of FRIEND."
693 (unless (mixi-friend-p friend)
694 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
695 (mixi-friend-realize friend)
696 (aref (cdr friend) 13))
698 (defun mixi-friend-touch (friend)
699 "Set the timestamp of FRIEND."
700 (unless (mixi-friend-p friend)
701 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
702 (aset (cdr friend) 0 (current-time)))
704 (defun mixi-friend-set-nick (friend nick)
705 "Set the nick of FRIEND."
706 (unless (mixi-friend-p friend)
707 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
708 (aset (cdr friend) 2 nick))
710 (defun mixi-friend-set-name (friend name)
711 "Set the name of FRIEND."
712 (unless (mixi-friend-p friend)
713 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
714 (aset (cdr friend) 3 name))
716 (defun mixi-friend-set-sex (friend sex)
717 "Set the sex of FRIEND."
718 (unless (mixi-friend-p friend)
719 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
720 (aset (cdr friend) 4 sex))
722 (defun mixi-friend-set-address (friend address)
723 "Set the address of FRIEND."
724 (unless (mixi-friend-p friend)
725 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
726 (aset (cdr friend) 5 address))
728 (defun mixi-friend-set-age (friend age)
729 "Set the age of FRIEND."
730 (unless (mixi-friend-p friend)
731 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
732 (aset (cdr friend) 6 age))
734 (defun mixi-friend-set-birthday (friend birthday)
735 "Set the birthday of FRIEND."
736 (unless (mixi-friend-p friend)
737 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
738 (aset (cdr friend) 7 birthday))
740 (defun mixi-friend-set-blood-type (friend blood-type)
741 "Set the blood type of FRIEND."
742 (unless (mixi-friend-p friend)
743 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
744 (aset (cdr friend) 8 blood-type))
746 (defun mixi-friend-set-birthplace (friend birthplace)
747 "Set the birthplace of FRIEND."
748 (unless (mixi-friend-p friend)
749 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
750 (aset (cdr friend) 9 birthplace))
752 (defun mixi-friend-set-hobby (friend hobby)
753 "Set the hobby of FRIEND."
754 (unless (mixi-friend-p friend)
755 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
756 (aset (cdr friend) 10 hobby))
758 (defun mixi-friend-set-job (friend job)
759 "Set the job of FRIEND."
760 (unless (mixi-friend-p friend)
761 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
762 (aset (cdr friend) 11 job))
764 (defun mixi-friend-set-organization (friend organization)
765 "Set the organization of FRIEND."
766 (unless (mixi-friend-p friend)
767 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
768 (aset (cdr friend) 12 organization))
770 (defun mixi-friend-set-profile (friend profile)
771 "Set the profile of FRIEND."
772 (unless (mixi-friend-p friend)
773 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
774 (aset (cdr friend) 13 profile))
776 (defmacro mixi-friend-list-page (&optional friend)
777 `(concat "/list_friend.pl?page=%d"
778 (when ,friend (concat "&id=" (mixi-friend-id ,friend)))))
780 (defconst mixi-friend-list-id-regexp
781 "<a href=show_friend\\.pl\\?id=\\([0-9]+\\)")
782 (defconst mixi-friend-list-nick-regexp
783 "<td valign=middle>\\(.+\\)¤µ¤ó([0-9]+)<br />")
785 (defun mixi-get-friends (&rest args)
786 "Get friends of FRIEND."
787 (when (> (length args) 2)
788 (signal 'wrong-number-of-arguments (list 'mixi-get-friends (length args))))
789 (let ((friend (nth 0 args))
790 (max-numbers (nth 1 args)))
791 (when (or (not (mixi-friend-p friend)) (mixi-friend-p max-numbers))
792 (setq friend (nth 1 args))
793 (setq max-numbers (nth 0 args)))
794 (unless (or (null friend) (mixi-friend-p friend))
795 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
796 (let ((ids (mixi-get-matched-items (mixi-friend-list-page friend)
798 mixi-friend-list-id-regexp))
799 (nicks (mixi-get-matched-items (mixi-friend-list-page friend)
801 mixi-friend-list-nick-regexp)))
804 (while (< index (length ids))
805 (setq ret (cons (mixi-make-friend (nth 0 (nth index ids))
806 (nth 0 (nth index nicks))) ret))
811 (defmacro mixi-favorite-list-page ()
812 `(concat "/list_bookmark.pl?page=%d"))
814 (defconst mixi-favorite-list-id-regexp
815 "<td ALIGN=center BGCOLOR=#FDF9F2 width=330><a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">")
816 (defconst mixi-favorite-list-nick-regexp
817 "<td BGCOLOR=#FDF9F2><font COLOR=#996600>̾ Á°</font></td>
818 <td COLSPAN=2 BGCOLOR=#FFFFFF>\\(.+\\)</td></tr>")
820 (defun mixi-get-favorites (&optional max-numbers)
822 (let ((ids (mixi-get-matched-items (mixi-favorite-list-page)
824 mixi-favorite-list-id-regexp))
825 (nicks (mixi-get-matched-items (mixi-favorite-list-page)
827 mixi-favorite-list-nick-regexp)))
830 (while (< index (length ids))
831 (setq ret (cons (mixi-make-friend (nth 0 (nth index ids))
832 (nth 0 (nth index nicks))) ret))
837 (defun mixi-make-log (friend time)
838 "Return a log object."
839 (cons 'mixi-log (vector friend time)))
841 (defmacro mixi-log-p (log)
842 `(eq (mixi-object-class ,log) 'mixi-log))
844 (defun mixi-log-friend (log)
845 "Return the friend of LOG."
846 (unless (mixi-log-p log)
847 (signal 'wrong-type-argument (list 'mixi-log-p log)))
850 (defun mixi-log-time (log)
851 "Return the time of LOG."
852 (unless (mixi-log-p log)
853 (signal 'wrong-type-argument (list 'mixi-log-p log)))
856 (defmacro mixi-log-list-page ()
857 `(concat "/show_log.pl"))
859 (defconst mixi-log-list-regexp
860 "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\) <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a><br>")
862 (defun mixi-get-logs (&optional max-numbers)
864 (let ((items (mixi-get-matched-items (mixi-log-list-page)
866 mixi-log-list-regexp)))
867 (mapcar (lambda (item)
868 (mixi-make-log (mixi-make-friend (nth 5 item) (nth 6 item))
870 (string-to-number (nth 4 item))
871 (string-to-number (nth 3 item))
872 (string-to-number (nth 2 item))
873 (string-to-number (nth 1 item))
874 (string-to-number (nth 0 item)))))
878 (defvar mixi-diary-cache (make-hash-table :test 'equal))
879 (defun mixi-make-diary (owner id)
880 "Return a diary object."
881 (let ((owner (or owner (mixi-make-me))))
882 (mixi-make-cache (list (mixi-friend-id owner) id)
883 (cons 'mixi-diary (vector nil owner id nil nil nil))
886 (defmacro mixi-diary-p (diary)
887 `(eq (mixi-object-class ,diary) 'mixi-diary))
889 (defmacro mixi-diary-page (diary)
890 `(concat "/view_diary.pl?id=" (mixi-diary-id ,diary)
891 "&owner_id=" (mixi-friend-id (mixi-diary-owner ,diary))))
893 ;; FIXME: Remove `¤µ¤ó'.
894 (defconst mixi-diary-owner-nick-regexp
895 "<td WIDTH=490 background=http://img\\.mixi\\.jp/img/bg_w\\.gif><b><font COLOR=#605048>\\(.+\\)\\(¤µ¤ó\\)?¤ÎÆüµ</font></b></td>")
896 (defconst mixi-diary-time-regexp
897 "<td ALIGN=center ROWSPAN=2 NOWRAP WIDTH=95 bgcolor=#FFD8B0>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)</td>")
898 (defconst mixi-diary-title-regexp
899 "<td BGCOLOR=#FFF4E0 WIDTH=430> \\([^<]+\\)</td></tr>")
900 (defconst mixi-diary-content-regexp
901 "<td CLASS=h12>\\(.+\\)</td></tr>")
903 (defun mixi-diary-realize (diary)
905 ;; FIXME: Check a expiration of cache?
906 (unless (mixi-diary-realize-p diary)
907 (with-mixi-retrieve (mixi-diary-page diary)
908 (if (string-match mixi-diary-owner-nick-regexp buffer)
909 (mixi-friend-set-nick (mixi-diary-owner diary)
910 (match-string 1 buffer))
911 (signal 'error (list 'cannot-find-owner-nick diary)))
912 (if (string-match mixi-diary-time-regexp buffer)
914 diary (encode-time 0 (string-to-number (match-string 5 buffer))
915 (string-to-number (match-string 4 buffer))
916 (string-to-number (match-string 3 buffer))
917 (string-to-number (match-string 2 buffer))
918 (string-to-number (match-string 1 buffer))))
919 (signal 'error (list 'cannot-find-time diary)))
920 (if (string-match mixi-diary-title-regexp buffer)
921 (mixi-diary-set-title diary (match-string 1 buffer))
922 (signal 'error (list 'cannot-find-title diary)))
923 (if (string-match mixi-diary-content-regexp buffer)
924 (mixi-diary-set-content diary (mixi-remove-markup
925 (match-string 1 buffer)))
926 (signal 'error (list 'cannot-find-content diary))))
927 (mixi-diary-touch diary)))
929 (defun mixi-diary-realize-p (diary)
930 "Return the timestamp of DIARY."
931 (unless (mixi-diary-p diary)
932 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
933 (aref (cdr diary) 0))
935 (defun mixi-diary-owner (diary)
936 "Return the owner of DIARY."
937 (unless (mixi-diary-p diary)
938 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
939 (aref (cdr diary) 1))
941 (defun mixi-diary-id (diary)
942 "Return the id of DIARY."
943 (unless (mixi-diary-p diary)
944 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
945 (aref (cdr diary) 2))
947 (defun mixi-diary-time (diary)
948 "Return the time of DIARY."
949 (unless (mixi-diary-p diary)
950 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
951 (mixi-diary-realize diary)
952 (aref (cdr diary) 3))
954 (defun mixi-diary-title (diary)
955 "Return the title of DIARY."
956 (unless (mixi-diary-p diary)
957 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
958 (mixi-diary-realize diary)
959 (aref (cdr diary) 4))
961 (defun mixi-diary-content (diary)
962 "Return the content of DIARY."
963 (unless (mixi-diary-p diary)
964 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
965 (mixi-diary-realize diary)
966 (aref (cdr diary) 5))
968 (defun mixi-diary-touch (diary)
969 "Set the timestamp of DIARY."
970 (unless (mixi-diary-p diary)
971 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
972 (aset (cdr diary) 0 (current-time)))
974 (defun mixi-diary-set-time (diary time)
975 "Set the time of DIARY."
976 (unless (mixi-diary-p diary)
977 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
978 (aset (cdr diary) 3 time))
980 (defun mixi-diary-set-title (diary title)
981 "Set the title of DIARY."
982 (unless (mixi-diary-p diary)
983 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
984 (aset (cdr diary) 4 title))
986 (defun mixi-diary-set-content (diary content)
987 "Set the content of DIARY."
988 (unless (mixi-diary-p diary)
989 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
990 (aset (cdr diary) 5 content))
992 (defmacro mixi-diary-list-page (&optional friend)
993 `(concat "/list_diary.pl?page=%d"
994 (when ,friend (concat "&id=" (mixi-friend-id ,friend)))))
996 (defconst mixi-diary-list-regexp
997 "<a href=\"view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=[0-9]+\">")
999 (defun mixi-get-diaries (&rest args)
1000 "Get diaries of FRIEND."
1001 (when (> (length args) 2)
1002 (signal 'wrong-number-of-arguments (list 'mixi-get-friends (length args))))
1003 (let ((friend (nth 0 args))
1004 (max-numbers (nth 1 args)))
1005 (when (or (not (mixi-friend-p friend)) (mixi-friend-p max-numbers))
1006 (setq friend (nth 1 args))
1007 (setq max-numbers (nth 0 args)))
1008 (unless (or (null friend) (mixi-friend-p friend))
1009 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
1010 (let ((items (mixi-get-matched-items (mixi-diary-list-page friend)
1012 mixi-diary-list-regexp)))
1013 (mapcar (lambda (item)
1014 (mixi-make-diary friend (nth 0 item)))
1017 (defmacro mixi-new-diary-list-page ()
1018 `(concat "/new_friend_diary.pl?page=%d"))
1020 (defconst mixi-new-diary-list-regexp
1021 "<a class=\"new_link\" href=view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=\\([0-9]+\\)>")
1023 (defun mixi-get-new-diaries (&optional max-numbers)
1025 (let ((items (mixi-get-matched-items (mixi-new-diary-list-page)
1027 mixi-new-diary-list-regexp)))
1028 (mapcar (lambda (item)
1029 (mixi-make-diary (mixi-make-friend (nth 1 item)) (nth 0 item)))
1032 ;; Community object.
1033 (defvar mixi-community-cache (make-hash-table :test 'equal))
1034 (defun mixi-make-community (id &optional name)
1035 "Return a community object."
1036 (mixi-make-cache id (cons 'mixi-community (vector nil id name nil nil nil
1038 mixi-community-cache))
1040 (defmacro mixi-community-p (community)
1041 `(eq (mixi-object-class ,community) 'mixi-community))
1043 (defmacro mixi-community-page (community)
1044 `(concat "/view_community.pl?id=" (mixi-community-id ,community)))
1046 (defconst mixi-community-nodata-regexp
1047 "^¥Ç¡¼¥¿¤¬¤¢¤ê¤Þ¤»¤ó")
1048 (defconst mixi-community-name-regexp
1049 "<td WIDTH=345>\\(.*\\)</td></tr>")
1050 (defconst mixi-community-birthday-regexp
1051 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>³«ÀßÆü</font></td>\n<td>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü</td>")
1052 ;; FIXME: Care when the owner has seceded.
1053 (defconst mixi-community-owner-regexp
1054 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>´ÉÍý¿Í</font></td>\n<td>\n\n<a href=\"\\(home\\.pl\\|show_friend\\.pl\\?id=\\([0-9]+\\)\\)\">\n\\(.+\\)</a>")
1055 (defconst mixi-community-category-regexp
1056 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥«¥Æ¥´¥ê</font></td>\n<td>\\([^<]+\\)</td>")
1057 (defconst mixi-community-members-regexp
1058 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥á¥ó¥Ð¡¼¿ô</font></td>\n<td>\\([0-9]+\\)¿Í</td></tr>")
1059 (defconst mixi-community-open-level-regexp
1060 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>»²²Ã¾ò·ï¤È<br>¸ø³«¥ì¥Ù¥ë</font></td>
1061 <td>\\(.+\\)</td></tr>")
1062 (defconst mixi-community-authority-regexp
1063 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥È¥Ô¥Ã¥¯ºîÀ®¤Î¸¢¸Â</font></td>\n<td>\\(.+\\)</td></tr>")
1064 (defconst mixi-community-description-regexp
1065 "<td CLASS=h120>\\(.+\\)</td>")
1067 (defun mixi-community-realize (community)
1068 "Realize a COMMUNITY."
1069 ;; FIXME: Check a expiration of cache?
1070 (unless (mixi-community-realize-p community)
1071 (with-mixi-retrieve (mixi-community-page community)
1072 (if (string-match mixi-community-nodata-regexp buffer)
1073 ;; FIXME: Set all members?
1074 (mixi-community-set-name community "¥Ç¡¼¥¿¤¬¤¢¤ê¤Þ¤»¤ó")
1075 (if (string-match mixi-community-name-regexp buffer)
1076 (mixi-community-set-name community (match-string 1 buffer))
1077 (signal 'error (list 'cannot-find-name community)))
1078 (if (string-match mixi-community-birthday-regexp buffer)
1079 (mixi-community-set-birthday
1081 (encode-time 0 0 0 (string-to-number (match-string 3 buffer))
1082 (string-to-number (match-string 2 buffer))
1083 (string-to-number (match-string 1 buffer))))
1084 (signal 'error (list 'cannot-find-birthday community)))
1085 (if (string-match mixi-community-owner-regexp buffer)
1086 (if (string= (match-string 1 buffer) "home.pl")
1087 (mixi-community-set-owner community (mixi-make-me))
1088 (mixi-community-set-owner
1089 community (mixi-make-friend (match-string 2 buffer)
1090 (match-string 3 buffer))))
1091 (signal 'error (list 'cannot-find-owner community)))
1092 (if (string-match mixi-community-category-regexp buffer)
1093 (mixi-community-set-category community (match-string 1 buffer))
1094 (signal 'error (list 'cannot-find-category community)))
1095 (if (string-match mixi-community-members-regexp buffer)
1096 (mixi-community-set-members
1097 community (string-to-number (match-string 1 buffer)))
1098 (signal 'error (list 'cannot-find-members community)))
1099 (if (string-match mixi-community-open-level-regexp buffer)
1100 (mixi-community-set-open-level community (match-string 1 buffer))
1101 (signal 'error (list 'cannot-find-open-level community)))
1102 (if (string-match mixi-community-authority-regexp buffer)
1103 (mixi-community-set-authority community (match-string 1 buffer))
1104 (signal 'error (list 'cannot-find-authority community)))
1105 (if (string-match mixi-community-description-regexp buffer)
1106 (mixi-community-set-description
1107 community (mixi-remove-markup (match-string 1 buffer)))
1108 (signal 'error (list 'cannot-find-description community)))))
1109 (mixi-community-touch community)))
1111 (defun mixi-community-realize-p (community)
1112 "Return the timestamp of COMMUNITY."
1113 (unless (mixi-community-p community)
1114 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1115 (aref (cdr community) 0))
1117 (defun mixi-community-id (community)
1118 "Return the id of COMMUNITY."
1119 (unless (mixi-community-p community)
1120 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1121 (aref (cdr community) 1))
1123 (defun mixi-community-name (community)
1124 "Return the name of COMMUNITY."
1125 (unless (mixi-community-p community)
1126 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1127 (unless (aref (cdr community) 2)
1128 (mixi-community-realize community))
1129 (aref (cdr community) 2))
1131 (defun mixi-community-birthday (community)
1132 "Return the birthday of COMMUNITY."
1133 (unless (mixi-community-p community)
1134 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1135 (mixi-community-realize community)
1136 (aref (cdr community) 3))
1138 (defun mixi-community-owner (community)
1139 "Return the owner of COMMUNITY."
1140 (unless (mixi-community-p community)
1141 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1142 (mixi-community-realize community)
1143 (aref (cdr community) 4))
1145 (defun mixi-community-category (community)
1146 "Return the category of COMMUNITY."
1147 (unless (mixi-community-p community)
1148 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1149 (mixi-community-realize community)
1150 (aref (cdr community) 5))
1152 (defun mixi-community-members (community)
1153 "Return the members of COMMUNITY."
1154 (unless (mixi-community-p community)
1155 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1156 (mixi-community-realize community)
1157 (aref (cdr community) 6))
1159 (defun mixi-community-open-level (community)
1160 "Return the open-level of COMMUNITY."
1161 (unless (mixi-community-p community)
1162 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1163 (mixi-community-realize community)
1164 (aref (cdr community) 7))
1166 (defun mixi-community-authority (community)
1167 "Return the authority of COMMUNITY."
1168 (unless (mixi-community-p community)
1169 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1170 (mixi-community-realize community)
1171 (aref (cdr community) 8))
1173 (defun mixi-community-description (community)
1174 "Return the description of COMMUNITY."
1175 (unless (mixi-community-p community)
1176 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1177 (mixi-community-realize community)
1178 (aref (cdr community) 9))
1180 (defun mixi-community-touch (community)
1181 "Set the timestamp of COMMUNITY."
1182 (unless (mixi-community-p community)
1183 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1184 (aset (cdr community) 0 (current-time)))
1186 (defun mixi-community-set-name (community name)
1187 "Set the name of COMMUNITY."
1188 (unless (mixi-community-p community)
1189 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1190 (aset (cdr community) 2 name))
1192 (defun mixi-community-set-birthday (community birthday)
1193 "Set the birthday of COMMUNITY."
1194 (unless (mixi-community-p community)
1195 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1196 (aset (cdr community) 3 birthday))
1198 (defun mixi-community-set-owner (community owner)
1199 "Set the owner of COMMUNITY."
1200 (unless (mixi-community-p community)
1201 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1202 (unless (mixi-friend-p owner)
1203 (signal 'wrong-type-argument (list 'mixi-friend-p owner)))
1204 (aset (cdr community) 4 owner))
1206 (defun mixi-community-set-category (community category)
1207 "Set the category of COMMUNITY."
1208 (unless (mixi-community-p community)
1209 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1210 (aset (cdr community) 5 category))
1212 (defun mixi-community-set-members (community members)
1213 "Set the name of COMMUNITY."
1214 (unless (mixi-community-p community)
1215 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1216 (aset (cdr community) 6 members))
1218 (defun mixi-community-set-open-level (community open-level)
1219 "Set the name of COMMUNITY."
1220 (unless (mixi-community-p community)
1221 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1222 (aset (cdr community) 7 open-level))
1224 (defun mixi-community-set-authority (community authority)
1225 "Set the name of COMMUNITY."
1226 (unless (mixi-community-p community)
1227 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1228 (aset (cdr community) 8 authority))
1230 (defun mixi-community-set-description (community description)
1231 "Set the name of COMMUNITY."
1232 (unless (mixi-community-p community)
1233 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1234 (aset (cdr community) 9 description))
1236 (defmacro mixi-community-list-page (&optional friend)
1237 `(concat "/list_community.pl?page=%d"
1238 (when ,friend (concat "&id=" (mixi-friend-id ,friend)))))
1240 (defconst mixi-community-list-id-regexp
1241 "<a href=view_community\\.pl\\?id=\\([0-9]+\\)")
1242 (defconst mixi-community-list-name-regexp
1243 "<td valign=middle>\\(.+\\)([0-9]+)</td>")
1245 (defun mixi-get-communities (&rest args)
1246 "Get communities of FRIEND."
1247 (when (> (length args) 2)
1248 (signal 'wrong-number-of-arguments (list 'mixi-get-friends (length args))))
1249 (let ((friend (nth 0 args))
1250 (max-numbers (nth 1 args)))
1251 (when (or (not (mixi-friend-p friend)) (mixi-friend-p max-numbers))
1252 (setq friend (nth 1 args))
1253 (setq max-numbers (nth 0 args)))
1254 (unless (or (null friend) (mixi-friend-p friend))
1255 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
1256 (let ((ids (mixi-get-matched-items (mixi-community-list-page friend)
1258 mixi-community-list-id-regexp))
1259 (names (mixi-get-matched-items (mixi-community-list-page friend)
1261 mixi-community-list-name-regexp)))
1264 (while (< index (length ids))
1265 (setq ret (cons (mixi-make-community (nth 0 (nth index ids))
1266 (nth 0 (nth index names))) ret))
1271 (defvar mixi-topic-cache (make-hash-table :test 'equal))
1272 (defun mixi-make-topic (community id)
1273 "Return a topic object."
1274 (mixi-make-cache (list (mixi-community-id community) id)
1275 (cons 'mixi-topic (vector nil community id nil nil nil nil))
1278 (defmacro mixi-topic-p (topic)
1279 `(eq (mixi-object-class ,topic) 'mixi-topic))
1281 (defmacro mixi-topic-page (topic)
1282 `(concat "/view_bbs.pl?id=" (mixi-topic-id ,topic)
1283 "&comm_id=" (mixi-community-id (mixi-topic-community ,topic))))
1285 (defconst mixi-topic-time-regexp
1286 "<td rowspan=\"3\" width=\"110\" bgcolor=\"#ffd8b0\" align=\"center\" valign=\"top\" nowrap>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)</td>")
1287 (defconst mixi-topic-title-regexp
1288 "<td bgcolor=\"#fff4e0\"> \\([^<]+\\)</td>")
1289 ;; FIXME: Remove `¤µ¤ó'.
1290 (defconst mixi-topic-owner-regexp
1291 "<td bgcolor=\"#fdf9f2\"> <font color=\"#dfb479\"></font> <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)\\(¤µ¤ó\\)?</a>")
1292 (defconst mixi-topic-content-regexp
1293 "<td class=\"h120\"><table><tr>\\(.+\\)?</tr></table>\\(.+\\)</td>")
1295 (defun mixi-topic-realize (topic)
1297 ;; FIXME: Check a expiration of cache?
1298 (unless (mixi-topic-realize-p topic)
1299 (with-mixi-retrieve (mixi-topic-page topic)
1300 (if (string-match mixi-topic-time-regexp buffer)
1301 (mixi-topic-set-time
1302 topic (encode-time 0 (string-to-number (match-string 5 buffer))
1303 (string-to-number (match-string 4 buffer))
1304 (string-to-number (match-string 3 buffer))
1305 (string-to-number (match-string 2 buffer))
1306 (string-to-number (match-string 1 buffer))))
1307 (signal 'error (list 'cannot-find-time topic)))
1308 (if (string-match mixi-topic-title-regexp buffer)
1309 (mixi-topic-set-title topic (match-string 1 buffer))
1310 (signal 'error (list 'cannot-find-title topic)))
1311 (if (string-match mixi-topic-owner-regexp buffer)
1312 (mixi-topic-set-owner topic
1313 (mixi-make-friend (match-string 1 buffer)
1314 (match-string 2 buffer)))
1315 (signal 'error (list 'cannot-find-owner topic)))
1316 (if (string-match mixi-topic-content-regexp buffer)
1317 (mixi-topic-set-content topic (mixi-remove-markup
1318 (match-string 2 buffer)))
1319 (signal 'error (list 'cannot-find-content topic))))
1320 (mixi-topic-touch topic)))
1322 (defun mixi-topic-realize-p (topic)
1323 "Return the timestamp of TOPIC."
1324 (unless (mixi-topic-p topic)
1325 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1326 (aref (cdr topic) 0))
1328 (defun mixi-topic-community (topic)
1329 "Return the community of TOPIC."
1330 (unless (mixi-topic-p topic)
1331 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1332 (aref (cdr topic) 1))
1334 (defun mixi-topic-id (topic)
1335 "Return the id of TOPIC."
1336 (unless (mixi-topic-p topic)
1337 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1338 (aref (cdr topic) 2))
1340 (defun mixi-topic-time (topic)
1341 "Return the time of TOPIC."
1342 (unless (mixi-topic-p topic)
1343 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1344 (mixi-topic-realize topic)
1345 (aref (cdr topic) 3))
1347 (defun mixi-topic-title (topic)
1348 "Return the title of TOPIC."
1349 (unless (mixi-topic-p topic)
1350 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1351 (mixi-topic-realize topic)
1352 (aref (cdr topic) 4))
1354 (defun mixi-topic-owner (topic)
1355 "Return the owner of TOPIC."
1356 (unless (mixi-topic-p topic)
1357 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1358 (mixi-topic-realize topic)
1359 (aref (cdr topic) 5))
1361 (defun mixi-topic-content (topic)
1362 "Return the content of TOPIC."
1363 (unless (mixi-topic-p topic)
1364 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1365 (mixi-topic-realize topic)
1366 (aref (cdr topic) 6))
1368 (defun mixi-topic-touch (topic)
1369 "Set the timestamp of TOPIC."
1370 (unless (mixi-topic-p topic)
1371 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1372 (aset (cdr topic) 0 (current-time)))
1374 (defun mixi-topic-set-time (topic time)
1375 "Set the time of TOPIC."
1376 (unless (mixi-topic-p topic)
1377 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1378 (aset (cdr topic) 3 time))
1380 (defun mixi-topic-set-title (topic title)
1381 "Set the title of TOPIC."
1382 (unless (mixi-topic-p topic)
1383 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1384 (aset (cdr topic) 4 title))
1386 (defun mixi-topic-set-owner (topic owner)
1387 "Set the owner of TOPIC."
1388 (unless (mixi-topic-p topic)
1389 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1390 (unless (mixi-friend-p owner)
1391 (signal 'wrong-type-argument (list 'mixi-friend-p owner)))
1392 (aset (cdr topic) 5 owner))
1394 (defun mixi-topic-set-content (topic content)
1395 "Set the content of TOPIC."
1396 (unless (mixi-topic-p topic)
1397 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1398 (aset (cdr topic) 6 content))
1400 (defmacro mixi-topic-list-page (community)
1401 `(concat "/list_bbs.pl?page=%d"
1402 "&id=" (mixi-community-id ,community)))
1404 (defconst mixi-topic-list-regexp
1405 "<a href=view_bbs\\.pl\\?id=\\([0-9]+\\)")
1407 (defun mixi-get-topics (community &optional max-numbers)
1408 "Get topics of COMMUNITY."
1409 (unless (mixi-community-p community)
1410 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1411 (let ((items (mixi-get-matched-items (mixi-topic-list-page community)
1413 mixi-topic-list-regexp)))
1414 (mapcar (lambda (item)
1415 (mixi-make-topic community (nth 0 item)))
1418 (defmacro mixi-new-topic-list-page ()
1419 `(concat "/new_bbs.pl?page=%d"))
1421 (defconst mixi-new-topic-list-regexp
1422 "<a href=\"view_bbs\\.pl\\?id=\\([0-9]+\\)&comment_count=[0-9]+&comm_id=\\([0-9]+\\)\" class=\"new_link\">")
1424 (defun mixi-get-new-topics (&optional max-numbers)
1426 (let ((items (mixi-get-matched-items (mixi-new-topic-list-page)
1428 mixi-new-topic-list-regexp)))
1429 (mapcar (lambda (item)
1430 (mixi-make-topic (mixi-make-community (nth 1 item))
1435 (defun mixi-make-comment (parent owner time content)
1436 "Return a comment object."
1437 (cons 'mixi-comment (vector parent owner time content)))
1439 (defmacro mixi-comment-p (comment)
1440 `(eq (mixi-object-class ,comment) 'mixi-comment))
1442 (defun mixi-comment-parent (comment)
1443 "Return the parent of COMMENT."
1444 (unless (mixi-comment-p comment)
1445 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1446 (aref (cdr comment) 0))
1448 (defun mixi-comment-owner (comment)
1449 "Return the owner of COMMENT."
1450 (unless (mixi-comment-p comment)
1451 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1452 (aref (cdr comment) 1))
1454 (defun mixi-comment-time (comment)
1455 "Return the time of COMMENT."
1456 (unless (mixi-comment-p comment)
1457 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1458 (aref (cdr comment) 2))
1460 (defun mixi-comment-content (comment)
1461 "Return the content of COMMENT."
1462 (unless (mixi-comment-p comment)
1463 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1464 (aref (cdr comment) 3))
1466 (defun mixi-diary-comment-list-page (diary)
1467 (concat "/view_diary.pl?page=%d"
1468 "&id=" (mixi-diary-id diary)
1469 "&owner_id=" (mixi-friend-id (mixi-diary-owner diary))))
1471 ;; FIXME: Split regexp to time, owner(id and nick) and contents.
1472 (defconst mixi-diary-comment-list-regexp
1473 "<td rowspan=\"2\" align=\"center\" width=\"95\" bgcolor=\"#f2ddb7\" nowrap>
1474 \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)\\(<br>
1475 <input type=checkbox name=comment_id value=\".+\">
1478 <td ALIGN=center BGCOLOR=#FDF9F2 WIDTH=430>
1479 <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"410\">
1482 <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a>
1489 <!-- ËÜʸ : start -->
1491 <td bgcolor=\"#ffffff\">
1492 <table BORDER=0 CELLSPACING=0 CELLPADDING=[35] WIDTH=410>
1496 </td></tr></table>")
1498 (defun mixi-topic-comment-list-page (topic)
1499 (concat "/view_bbs.pl?page=%d"
1500 "&id=" (mixi-topic-id topic)
1501 "&comm_id=" (mixi-community-id (mixi-topic-community topic))))
1503 ;; FIXME: Split regexp to time, owner(id and nick) and contents.
1504 (defconst mixi-topic-comment-list-regexp
1505 "<tr valign=\"top\">
1506 <td rowspan=\"2\" width=\"110\" bgcolor=\"#f2ddb7\" align=\"center\" nowrap>
1507 \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>
1508 \\([0-9]+\\):\\([0-9]+\\)<br>
1510 <td bgcolor=\"#fdf9f2\"> <font color=\"#f8a448\">
1511 <b> [0-9]+</b>:</font>
1513 <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a>
1520 <td bgcolor=\"#ffffff\" align=\"center\">
1521 <table border=\"0\" cellspacing=\"0\" cellpadding=\"5\" width=\"500\">
1532 (defun mixi-get-comments (parent &optional max-numbers)
1533 "Get comments of PARENT."
1534 (unless (mixi-object-p parent)
1535 (signal 'wrong-type-argument (list 'mixi-object-p parent)))
1536 (let* ((name (mixi-object-name parent))
1537 (list-page (intern (concat mixi-object-prefix name
1538 "-comment-list-page")))
1539 (regexp (eval (intern (concat mixi-object-prefix name
1540 "-comment-list-regexp")))))
1541 (let ((items (mixi-get-matched-items
1542 (funcall list-page parent) max-numbers regexp)))
1543 (mapcar (lambda (item)
1544 (mixi-make-comment parent (mixi-make-friend
1545 (nth 6 item) (nth 7 item))
1548 (string-to-number (nth 4 item))
1549 (string-to-number (nth 3 item))
1550 (string-to-number (nth 2 item))
1551 (string-to-number (nth 1 item))
1552 (string-to-number (nth 0 item)))
1553 (mixi-remove-markup (nth 8 item))))
1556 (defmacro mixi-new-comment-list-page ()
1557 `(concat "/new_comment.pl?page=%d"))
1559 (defconst mixi-new-comment-list-regexp
1560 "<a href=\"view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=\\([0-9]+\\)&comment_count=[0-9]+\" class=\"new_link\">")
1562 (defun mixi-get-new-comments (&optional max-numbers)
1564 (let ((items (mixi-get-matched-items (mixi-new-comment-list-page)
1566 mixi-new-comment-list-regexp)))
1567 (mapcar (lambda (item)
1568 (let ((diary (mixi-make-diary
1569 (mixi-make-friend (nth 1 item))
1571 (mixi-get-comments diary)))
1576 ;;; mixi.el ends here