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 only the first page of new diaries like a mail format.
44 ;; (let ((mixi-new-diary-max-pages 1)
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))
60 ;; (set-buffer-modified-p nil)
61 ;; (setq buffer-read-only t)
62 ;; (goto-char (point-min)))
64 ;; Display only the first page of new diaries including all comments like a
65 ;; mail format. Comments are displayed like a reply mail.
67 ;; (let ((mixi-new-diary-max-pages 1)
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))))
94 ;; (mixi-get-new-diaries))
95 ;; (set-buffer-modified-p nil)
96 ;; (setq buffer-read-only t)
97 ;; (goto-char (point-min)))
101 (require 'url "url" t)
102 (require 'w3m "w3m" t)
103 (eval-when-compile (require 'cl))
106 "API library for accessing to mixi."
109 (defcustom mixi-url "http://mixi.jp"
114 (defcustom mixi-coding-system 'euc-jp
115 "*Coding system for mixi."
119 (defcustom mixi-retrieve-function
120 (if (and (require 'url "url" t)
121 (fboundp 'url-retrieve-synchronously))
122 'mixi-w3-retrieve 'mixi-w3m-retrieve)
123 "*The function for retrieving."
124 :type '(choice (const :tag "Using w3" mixi-w3-retrieve)
125 (const :tag "Using w3m" mixi-w3m-retrieve)
126 (function :format "Other function: %v\n" :size 0))
129 (defcustom mixi-curl-program "curl"
130 "*The program name of `curl'."
134 (defcustom mixi-curl-cookie-file (expand-file-name "~/.mixi-cookies.txt")
135 "*The location of cookie file created by `curl'."
139 (defcustom mixi-default-email nil
140 "*Default E-mail address that is used to login automatically."
141 :type '(choice (string :tag "E-mail address")
142 (const :tag "Asked when it is necessary" nil))
145 (defcustom mixi-default-password nil
146 "*Default password that is used to login automatically."
147 :type '(choice (string :tag "Password")
148 (const :tag "Asked when it is necessary" nil))
151 (defcustom mixi-accept-adult-contents t
152 "*If non-nil, accept to access to the adult contents."
156 (defcustom mixi-continuously-access-interval 4.0
157 "*Time interval between each mixi access.
158 Increase this value when unexpected error frequently occurs."
162 (defcustom mixi-cache-expires 3600
163 "*Seconds for expiration of a cached object."
164 :type '(choice (integer :tag "Expired seconds")
165 (const :tag "Don't expire" nil))
168 ;; FIXME: Not implemented.
169 (defcustom mixi-cache-use-file t
170 "*If non-nil, caches are saved to files."
174 (defcustom mixi-cache-directory (expand-file-name "~/.mixi")
175 "*Where to look for cache files."
182 (defmacro mixi-message (string)
183 `(concat "[mixi] " ,string))
185 (defconst mixi-message-adult-contents
186 "¤³¤Î¥Ú¡¼¥¸¤«¤éÀè¤Ï¥¢¥À¥ë¥È¡ÊÀ®¿Í¸þ¤±¡Ë¥³¥ó¥Æ¥ó¥Ä¤¬´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£<br>
187 ±ÜÍ÷¤ËƱ°Õ¤µ¤ì¤¿Êý¤Î¤ß¡¢Àè¤Ø¤ª¿Ê¤ß¤¯¤À¤µ¤¤¡£")
188 (defconst mixi-message-continuously-accessing
189 "°ÂÄꤷ¤Æ¥µ¥¤¥È¤Î±¿±Ä¤ò¤ª¤³¤Ê¤¦°Ù¡¢´Ö³Ö¤ò¶õ¤±¤Ê¤¤Ï¢Â³Åª¤Ê¥Ú¡¼¥¸¤ÎÁ«°Ü¡¦¹¹<br>
190 ¿·¤ÏÀ©¸Â¤µ¤»¤Æ¤¤¤¿¤À¤¤¤Æ¤ª¤ê¤Þ¤¹¡£¤´ÌÂÏǤò¤ª¤«¤±¤¤¤¿¤·¤Þ¤¹¤¬¡¢¤·¤Ð¤é¤¯¤ª<br>
191 ÂÔ¤Á¤¤¤¿¤À¤¤¤Æ¤«¤éÁàºî¤ò¤ª¤³¤Ê¤Ã¤Æ¤¯¤À¤µ¤¤¡£")
192 (defconst mixi-warning-continuously-accessing
193 "´Ö³Ö¤ò¶õ¤±¤Ê¤¤Ï¢Â³Åª¤Ê¥Ú¡¼¥¸¤ÎÁ«°Ü¡¦¹¹¿·¤òÉÑÈˤˤª¤³¤Ê¤ï¤ì¤Æ¤¤¤ë¤³¤È¤¬¸«<br>
194 ¼õ¤±¤é¤ì¤Þ¤·¤¿¤Î¤Ç¡¢°ì»þŪ¤ËÁàºî¤òÄä»ß¤µ¤»¤Æ¤¤¤¿¤À¤¤Þ¤¹¡£¿½¤·Ìõ¤´¤¶¤¤¤Þ<br>
195 ¤»¤ó¤¬¡¢¤·¤Ð¤é¤¯¤Î´Ö¤ªÂÔ¤Á¤¯¤À¤µ¤¤¡£")
197 (defun mixi-retrieve-1 (buffer url &optional post-data)
198 (when (string-match mixi-message-adult-contents buffer)
199 (if mixi-accept-adult-contents
200 (setq buffer (funcall mixi-retrieve-function url "submit=agree"))
201 (setq buffer (funcall mixi-retrieve-function (concat url "?")))))
202 (when (string-match mixi-warning-continuously-accessing buffer)
203 (error (mixi-message "Continuously accessing")))
204 (if (not (string-match mixi-message-continuously-accessing buffer))
206 (message (mixi-message "Waiting for continuously accessing..."))
207 (sit-for mixi-continuously-access-interval)
208 (funcall mixi-retrieve-function url post-data)))
210 (defun mixi-w3-retrieve (url &optional post-data)
211 "Retrieve the URL and return getted strings."
214 (setq url-request-method "POST")
215 (setq url-request-data post-data))
216 (setq url-request-method "GET")
217 (setq url-request-data nil))
218 (let* ((url (url-expand-file-name url mixi-url))
219 (buffer (url-retrieve-synchronously url))
221 (unless (bufferp buffer)
222 (error (mixi-message "Cannot retrieve")))
223 (with-current-buffer buffer
224 (goto-char (point-min))
225 (if (re-search-forward "HTTP/[0-9.]+ 302 Moved" nil t)
226 (if (re-search-forward
227 (concat "Location: " mixi-url "\\(.+\\)") nil t)
228 (setq ret (mixi-w3-retrieve (match-string 1) post-data))
229 (setq ret (mixi-w3-retrieve "/home.pl" post-data)))
230 (unless (re-search-forward "HTTP/[0-9.]+ 200 OK" nil t)
231 (error (mixi-message "Cannot retrieve")))
232 (search-forward "\n\n")
233 (setq ret (mm-decode-coding-string
234 (buffer-substring-no-properties (point) (point-max))
237 (setq ret (mixi-retrieve-1 ret url post-data))))
240 (defun mixi-w3m-retrieve (url &optional post-data)
241 "Retrieve the URL and return getted strings."
242 (let ((url (w3m-expand-url url mixi-url)))
244 (if (not (string= (w3m-retrieve url nil nil post-data) "text/html"))
245 (error (mixi-message "Cannot retrieve"))
246 (w3m-decode-buffer url)
247 (let ((ret (buffer-substring-no-properties (point-min) (point-max))))
248 (mixi-retrieve-1 ret url post-data))))))
250 (defun mixi-curl-retrieve (url &optional post-data)
251 "Retrieve the URL and return getted strings."
253 (let ((orig-mode (default-file-modes))
257 (set-default-file-modes 448)
259 (apply #'start-process "curl" (current-buffer)
261 (append (if post-data '("-d" "@-"))
263 "-b" mixi-curl-cookie-file
264 "-c" mixi-curl-cookie-file
265 (concat mixi-url url)))))
266 (set-process-sentinel process #'ignore))
267 (set-default-file-modes orig-mode))
269 (process-send-string process (concat post-data "\n"))
270 (process-send-eof process))
271 (while (eq (process-status process) 'run)
272 (accept-process-output process 1))
273 (goto-char (point-min))
274 (while (looking-at "HTTP/[0-9]+\\.[0-9]+ [13][0-9][0-9]")
275 (delete-region (point) (re-search-forward "\r?\n\r?\n")))
276 (unless (looking-at "HTTP/[0-9]+\\.[0-9]+ 200")
277 (error (mixi-message "Cannot retrieve")))
278 (delete-region (point) (re-search-forward "\r?\n\r?\n"))
279 (setq ret (decode-coding-string (buffer-substring (point) (point-max))
281 (mixi-retrieve-1 ret url post-data))))
283 (defconst mixi-my-id-regexp
284 "<a href=\"add_diary\\.pl\\?id=\\([0-9]+\\)")
286 (defun mixi-login (&optional email password)
288 (when (and (eq mixi-retrieve-function 'mixi-w3m-retrieve)
289 (not w3m-use-cookies))
292 "Require to accept cookies. Please set `w3m-use-cookies' to t.")))
293 (let ((email (or email mixi-default-email
294 (read-from-minibuffer (mixi-message "Login Email: "))))
295 (password (or password mixi-default-password
296 (read-passwd (mixi-message "Login Password: ")))))
297 (let ((buffer (funcall mixi-retrieve-function "/login.pl"
298 (concat "email=" email
299 "&password=" password
302 (unless (string-match "url=/check\\.pl\\?n=" buffer)
303 (error (mixi-message "Cannot login")))
304 (setq buffer (funcall mixi-retrieve-function "/check.pl?n=home.pl"))
305 (if (string-match mixi-my-id-regexp buffer)
306 (setq mixi-me (mixi-make-friend (match-string 1 buffer)))
307 (error (mixi-message "Cannot login"))))))
309 (defun mixi-logout ()
310 (funcall mixi-retrieve-function "/logout.pl"))
312 (defmacro with-mixi-retrieve (url &rest body)
315 (setq buffer (funcall mixi-retrieve-function ,url))
316 (when (string-match "login.pl" buffer)
318 (setq buffer (funcall mixi-retrieve-function ,url))))
320 (put 'with-mixi-retrieve 'lisp-indent-function 'defun)
322 (defun mixi-get-matched-items (url max-pages regexp)
323 "Get matched items to REGEXP in URL."
327 (while (or (null max-pages) (<= page max-pages))
328 (with-mixi-retrieve (format url page)
330 (while (string-match regexp buffer pos)
333 (while (match-string num buffer)
334 (setq list (cons (match-string num buffer) list))
336 (setq ids (cons (reverse list) ids))
337 (setq pos (match-end (1- num)))))
341 ;; FIXME: Sort? Now order by newest.
344 ;; stolen (and modified) from shimbun.el
345 (defun mixi-remove-markup (string)
346 "Remove markups from STRING."
350 (goto-char (point-min))
351 (while (search-forward "<!--" nil t)
352 (delete-region (match-beginning 0)
353 (or (search-forward "-->" nil t)
355 (goto-char (point-min))
356 (while (re-search-forward "<[^>]+>" nil t)
357 (replace-match "" t t))
358 (goto-char (point-min))
359 (while (re-search-forward "
\r" nil t)
360 (replace-match "\n" t t)))
365 (defun mixi-cache-expired-p (object)
366 "Whether a cache of OBJECT is expired."
367 ;; FIXME: Use method instead of `(aref (cdr object) 0)'.
368 (let ((timestamp (aref (cdr object) 0)))
369 (unless (or (null mixi-cache-expires)
371 (time-less-p (time-add timestamp
372 (seconds-to-time mixi-cache-expires))
375 (defun mixi-make-cache (key value table)
376 "Make a cache object and return it."
377 (let ((cache (gethash key table)))
378 (if (and cache (not (mixi-cache-expired-p cache)))
380 (puthash key value table))))
383 (defconst mixi-object-prefix "mixi-")
385 (defmacro mixi-object-class (object)
388 (defmacro mixi-object-p (object)
389 `(eq (string-match (concat "^" mixi-object-prefix)
390 (symbol-name (mixi-object-class ,object))) 0))
392 (defun mixi-object-name (object)
393 "Return the name of OBJECT."
394 (unless (mixi-object-p object)
395 (signal 'wrong-type-argument (list 'mixi-object-p object)))
396 (let ((class (mixi-object-class object)))
397 (substring (symbol-name class) (length mixi-object-prefix))))
399 (defun mixi-object-id (object)
400 "Return the id of OBJECT."
401 (unless (mixi-object-p object)
402 (signal 'wrong-type-argument (list 'mixi-object-p object)))
403 (let ((func (intern (concat mixi-object-prefix
404 (mixi-object-name object) "-id"))))
405 (funcall func object)))
408 (defvar mixi-friend-cache (make-hash-table :test 'equal))
409 (defun mixi-make-friend (id &optional nick)
410 "Return a friend object."
411 (mixi-make-cache id (cons 'mixi-friend (vector nil id nick nil nil nil nil
412 nil nil nil nil nil nil nil))
415 (defun mixi-make-me ()
417 (with-mixi-retrieve "/home.pl"
418 (if (string-match mixi-my-id-regexp buffer)
419 (setq mixi-me (mixi-make-friend (match-string 1 buffer)))
420 (signal 'error (list 'who-am-i)))))
423 (defmacro mixi-friend-p (friend)
424 `(eq (mixi-object-class ,friend) 'mixi-friend))
426 (defmacro mixi-friend-page (friend)
427 `(concat "/show_friend.pl?id=" (mixi-friend-id ,friend)))
429 (defconst mixi-friend-nick-regexp
430 "<img alt=\"\\*\" src=\"http://img\\.mixi\\.jp/img/dot0\\.gif\" width=\"1\" height=\"5\"><br>\n\\(.*\\)¤µ¤ó([0-9]+)")
431 (defconst mixi-friend-name-sex-regexp
432 "<td BGCOLOR=#F2DDB7 WIDTH=80 NOWRAP><font COLOR=#996600>̾\\( \\| \\)Á°</font></td>\n+<td WIDTH=345>\\([^<]+\\) (\\([Ã˽÷]\\)À)</td>")
433 (defconst mixi-friend-address-regexp
434 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¸½½»½ê</font></td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
435 (defconst mixi-friend-age-regexp
436 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>ǯ\\( \\| \\)Îð</font></td>\n<td>\\([0-9]+\\)ºÐ\\(\n.+\n\\)?</td></tr>")
437 (defconst mixi-friend-birthday-regexp
438 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>ÃÂÀ¸Æü</font></td>\n<td>\\([0-9]+\\)·î\\([0-9]+\\)Æü\\(\n.+\n\\)?</td></tr>")
439 (defconst mixi-friend-blood-type-regexp
440 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>·ì±Õ·¿</font></td>\n<td>\\([ABO]B?\\)·¿\\(\n\n\\)?</td></tr>")
441 (defconst mixi-friend-birthplace-regexp
442 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>½Ð¿ÈÃÏ</font>\n?</td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
443 (defconst mixi-friend-hobby-regexp
444 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¼ñ\\( \\| \\)Ì£</font></td>\n<td>\\(.+\\)</td></tr>")
445 (defconst mixi-friend-job-regexp
446 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¿¦\\( \\| \\)¶È</font></td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
447 (defconst mixi-friend-organization-regexp
448 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>½ê\\( \\| \\)°</font></td>\n<td[^>]*>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
449 (defconst mixi-friend-profile-regexp
450 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¼«¸Ê¾Ò²ð</font></td>\n<td CLASS=h120>\\(.+\\)</td></tr>")
452 (defun mixi-friend-realize (friend)
454 ;; FIXME: Check a expiration of cache?
455 (unless (mixi-friend-realize-p friend)
457 (with-mixi-retrieve (mixi-friend-page friend)
459 (if (string-match mixi-friend-nick-regexp buf)
460 (mixi-friend-set-nick friend (match-string 1 buf))
461 (signal 'error (list 'cannot-find-nick friend)))
462 ;; For getting my profile.
463 (unless (string-match mixi-friend-name-sex-regexp buf)
464 (with-mixi-retrieve "/show_profile.pl"
466 (if (string-match mixi-friend-name-sex-regexp buf)
468 (mixi-friend-set-name friend (match-string 2 buf))
469 (mixi-friend-set-sex friend
470 (if (string= (match-string 3 buf) "ÃË")
472 (signal 'error (list 'cannot-find-name-or-sex friend)))
473 (when (string-match mixi-friend-address-regexp buf)
474 (mixi-friend-set-address friend (match-string 1 buf)))
475 (when (string-match mixi-friend-age-regexp buf)
477 friend (string-to-number (match-string 2 buf))))
478 (when (string-match mixi-friend-birthday-regexp buf)
479 (mixi-friend-set-birthday
480 friend (list (string-to-number (match-string 1 buf))
481 (string-to-number (match-string 2 buf)))))
482 (when (string-match mixi-friend-blood-type-regexp buf)
483 (mixi-friend-set-blood-type friend (intern (match-string 1 buf))))
484 (when (string-match mixi-friend-birthplace-regexp buf)
485 (mixi-friend-set-birthplace friend (match-string 1 buf)))
486 (when (string-match mixi-friend-hobby-regexp buf)
487 (mixi-friend-set-hobby
488 friend (split-string (match-string 2 buf) ", ")))
489 (when (string-match mixi-friend-job-regexp buf)
490 (mixi-friend-set-job friend (match-string 2 buf)))
491 (when (string-match mixi-friend-organization-regexp buf)
492 (mixi-friend-set-organization friend (match-string 2 buf)))
493 (when (string-match mixi-friend-profile-regexp buf)
494 (mixi-friend-set-profile
495 friend (mixi-remove-markup (match-string 1 buf)))))
496 (mixi-friend-touch friend)))
498 (defun mixi-friend-realize-p (friend)
499 "Return the timestamp of FRIEND."
500 (unless (mixi-friend-p friend)
501 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
502 (aref (cdr friend) 0))
504 (defun mixi-friend-id (friend)
505 "Return the id of FRIEND."
506 (unless (mixi-friend-p friend)
507 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
508 (aref (cdr friend) 1))
510 (defun mixi-friend-nick (friend)
511 "Return the nick of FRIEND."
512 (unless (mixi-friend-p friend)
513 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
514 (unless (aref (cdr friend) 2)
515 (mixi-friend-realize friend))
516 (aref (cdr friend) 2))
518 (defun mixi-friend-name (friend)
519 "Return the name of FRIEND."
520 (unless (mixi-friend-p friend)
521 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
522 (mixi-friend-realize friend)
523 (aref (cdr friend) 3))
525 (defun mixi-friend-sex (friend)
526 "Return the sex of FRIEND."
527 (unless (mixi-friend-p friend)
528 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
529 (mixi-friend-realize friend)
530 (aref (cdr friend) 4))
532 (defun mixi-friend-address (friend)
533 "Return the address of FRIEND."
534 (unless (mixi-friend-p friend)
535 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
536 (mixi-friend-realize friend)
537 (aref (cdr friend) 5))
539 (defun mixi-friend-age (friend)
540 "Return the age of FRIEND."
541 (unless (mixi-friend-p friend)
542 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
543 (mixi-friend-realize friend)
544 (aref (cdr friend) 6))
546 (defun mixi-friend-birthday (friend)
547 "Return the birthday of FRIEND."
548 (unless (mixi-friend-p friend)
549 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
550 (mixi-friend-realize friend)
551 (aref (cdr friend) 7))
553 (defun mixi-friend-blood-type (friend)
554 "Return the blood type of FRIEND."
555 (unless (mixi-friend-p friend)
556 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
557 (mixi-friend-realize friend)
558 (aref (cdr friend) 8))
560 (defun mixi-friend-birthplace (friend)
561 "Return the birthplace of FRIEND."
562 (unless (mixi-friend-p friend)
563 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
564 (mixi-friend-realize friend)
565 (aref (cdr friend) 9))
567 (defun mixi-friend-hobby (friend)
568 "Return the hobby of FRIEND."
569 (unless (mixi-friend-p friend)
570 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
571 (mixi-friend-realize friend)
572 (aref (cdr friend) 10))
574 (defun mixi-friend-job (friend)
575 "Return the job of FRIEND."
576 (unless (mixi-friend-p friend)
577 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
578 (mixi-friend-realize friend)
579 (aref (cdr friend) 11))
581 (defun mixi-friend-organization (friend)
582 "Return the organization of FRIEND."
583 (unless (mixi-friend-p friend)
584 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
585 (mixi-friend-realize friend)
586 (aref (cdr friend) 12))
588 (defun mixi-friend-profile (friend)
589 "Return the pforile of FRIEND."
590 (unless (mixi-friend-p friend)
591 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
592 (mixi-friend-realize friend)
593 (aref (cdr friend) 13))
595 (defun mixi-friend-touch (friend)
596 "Set the timestamp of FRIEND."
597 (unless (mixi-friend-p friend)
598 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
599 (aset (cdr friend) 0 (current-time)))
601 (defun mixi-friend-set-nick (friend nick)
602 "Set the nick of FRIEND."
603 (unless (mixi-friend-p friend)
604 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
605 (aset (cdr friend) 2 nick))
607 (defun mixi-friend-set-name (friend name)
608 "Set the name of FRIEND."
609 (unless (mixi-friend-p friend)
610 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
611 (aset (cdr friend) 3 name))
613 (defun mixi-friend-set-sex (friend sex)
614 "Set the sex of FRIEND."
615 (unless (mixi-friend-p friend)
616 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
617 (aset (cdr friend) 4 sex))
619 (defun mixi-friend-set-address (friend address)
620 "Set the address of FRIEND."
621 (unless (mixi-friend-p friend)
622 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
623 (aset (cdr friend) 5 address))
625 (defun mixi-friend-set-age (friend age)
626 "Set the age of FRIEND."
627 (unless (mixi-friend-p friend)
628 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
629 (aset (cdr friend) 6 age))
631 (defun mixi-friend-set-birthday (friend birthday)
632 "Set the birthday of FRIEND."
633 (unless (mixi-friend-p friend)
634 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
635 (aset (cdr friend) 7 birthday))
637 (defun mixi-friend-set-blood-type (friend blood-type)
638 "Set the blood type of FRIEND."
639 (unless (mixi-friend-p friend)
640 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
641 (aset (cdr friend) 8 blood-type))
643 (defun mixi-friend-set-birthplace (friend birthplace)
644 "Set the birthplace of FRIEND."
645 (unless (mixi-friend-p friend)
646 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
647 (aset (cdr friend) 9 birthplace))
649 (defun mixi-friend-set-hobby (friend hobby)
650 "Set the hobby of FRIEND."
651 (unless (mixi-friend-p friend)
652 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
653 (aset (cdr friend) 10 hobby))
655 (defun mixi-friend-set-job (friend job)
656 "Set the job of FRIEND."
657 (unless (mixi-friend-p friend)
658 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
659 (aset (cdr friend) 11 job))
661 (defun mixi-friend-set-organization (friend organization)
662 "Set the organization of FRIEND."
663 (unless (mixi-friend-p friend)
664 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
665 (aset (cdr friend) 12 organization))
667 (defun mixi-friend-set-profile (friend profile)
668 "Set the profile of FRIEND."
669 (unless (mixi-friend-p friend)
670 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
671 (aset (cdr friend) 13 profile))
673 (defmacro mixi-friend-list-page (&optional friend)
674 `(concat "/list_friend.pl?page=%d"
675 (when ,friend (concat "&id=" (mixi-friend-id ,friend)))))
677 (defconst mixi-friend-list-id-regexp
678 "<a href=show_friend\\.pl\\?id=\\([0-9]+\\)")
679 (defconst mixi-friend-list-nick-regexp
680 "<td valign=middle>\\(.+\\)¤µ¤ó([0-9]+)<br />")
682 (defvar mixi-friend-max-pages 10)
683 (defun mixi-get-friends (&optional friend)
684 "Get friends of FRIEND."
685 (unless (or (null friend) (mixi-friend-p friend))
686 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
687 (let ((ids (mixi-get-matched-items (mixi-friend-list-page friend)
688 mixi-friend-max-pages
689 mixi-friend-list-id-regexp))
690 (nicks (mixi-get-matched-items (mixi-friend-list-page friend)
691 mixi-friend-max-pages
692 mixi-friend-list-nick-regexp)))
695 (while (< index (length ids))
696 (setq ret (cons (mixi-make-friend (nth 0 (nth index ids))
697 (nth 0 (nth index nicks))) ret))
702 (defmacro mixi-favorite-list-page ()
703 `(concat "/list_bookmark.pl?page=%d"))
705 (defconst mixi-favorite-list-id-regexp
706 "<td ALIGN=center BGCOLOR=#FDF9F2 width=330><a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">")
707 (defconst mixi-favorite-list-nick-regexp
708 "<td BGCOLOR=#FDF9F2><font COLOR=#996600>̾ Á°</font></td>
709 <td COLSPAN=2 BGCOLOR=#FFFFFF>\\(.+\\)</td></tr>")
711 (defvar mixi-favorite-max-pages nil)
712 (defun mixi-get-favorites ()
714 (let ((ids (mixi-get-matched-items (mixi-favorite-list-page)
715 mixi-favorite-max-pages
716 mixi-favorite-list-id-regexp))
717 (nicks (mixi-get-matched-items (mixi-favorite-list-page)
718 mixi-favorite-max-pages
719 mixi-favorite-list-nick-regexp)))
722 (while (< index (length ids))
723 (setq ret (cons (mixi-make-friend (nth 0 (nth index ids))
724 (nth 0 (nth index nicks))) ret))
729 (defun mixi-make-log (friend time)
730 "Return a log object."
731 (cons 'mixi-log (vector friend time)))
733 (defmacro mixi-log-p (log)
734 `(eq (mixi-object-class ,log) 'mixi-log))
736 (defun mixi-log-friend (log)
737 "Return the friend of LOG."
738 (unless (mixi-log-p log)
739 (signal 'wrong-type-argument (list 'mixi-log-p log)))
742 (defun mixi-log-time (log)
743 "Return the time of LOG."
744 (unless (mixi-log-p log)
745 (signal 'wrong-type-argument (list 'mixi-log-p log)))
748 (defmacro mixi-log-list-page ()
749 `(concat "/show_log.pl"))
751 (defconst mixi-log-list-regexp
752 "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\) <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a><br>")
754 (defvar mixi-log-max-pages 1)
755 (defun mixi-get-logs ()
757 (let ((items (mixi-get-matched-items (mixi-log-list-page)
759 mixi-log-list-regexp)))
760 (mapcar (lambda (item)
761 (mixi-make-log (mixi-make-friend (nth 5 item) (nth 6 item))
763 (string-to-number (nth 4 item))
764 (string-to-number (nth 3 item))
765 (string-to-number (nth 2 item))
766 (string-to-number (nth 1 item))
767 (string-to-number (nth 0 item)))))
771 (defvar mixi-diary-cache (make-hash-table :test 'equal))
772 (defun mixi-make-diary (owner id)
773 "Return a diary object."
774 (let ((owner (or owner (mixi-make-me))))
775 (mixi-make-cache (list (mixi-friend-id owner) id)
776 (cons 'mixi-diary (vector nil owner id nil nil nil))
779 (defmacro mixi-diary-p (diary)
780 `(eq (mixi-object-class ,diary) 'mixi-diary))
782 (defmacro mixi-diary-page (diary)
783 `(concat "/view_diary.pl?id=" (mixi-diary-id ,diary)
784 "&owner_id=" (mixi-friend-id (mixi-diary-owner ,diary))))
786 ;; FIXME: Remove `¤µ¤ó'.
787 (defconst mixi-diary-owner-nick-regexp
788 "<td WIDTH=490 background=http://img\\.mixi\\.jp/img/bg_w\\.gif><b><font COLOR=#605048>\\(.+\\)\\(¤µ¤ó\\)?¤ÎÆüµ</font></b></td>")
789 (defconst mixi-diary-time-regexp
790 "<td ALIGN=center ROWSPAN=2 NOWRAP WIDTH=95 bgcolor=#FFD8B0>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)</td>")
791 (defconst mixi-diary-title-regexp
792 "<td BGCOLOR=#FFF4E0 WIDTH=430> \\([^<]+\\)</td></tr>")
793 (defconst mixi-diary-content-regexp
794 "<td CLASS=h12>\\(.+\\)</td></tr>")
796 (defun mixi-diary-realize (diary)
798 ;; FIXME: Check a expiration of cache?
799 (unless (mixi-diary-realize-p diary)
800 (with-mixi-retrieve (mixi-diary-page diary)
801 (if (string-match mixi-diary-owner-nick-regexp buffer)
802 (mixi-friend-set-nick (mixi-diary-owner diary)
803 (match-string 1 buffer))
804 (signal 'error (list 'cannot-find-owner-nick diary)))
805 (if (string-match mixi-diary-time-regexp buffer)
807 diary (encode-time 0 (string-to-number (match-string 5 buffer))
808 (string-to-number (match-string 4 buffer))
809 (string-to-number (match-string 3 buffer))
810 (string-to-number (match-string 2 buffer))
811 (string-to-number (match-string 1 buffer))))
812 (signal 'error (list 'cannot-find-time diary)))
813 (if (string-match mixi-diary-title-regexp buffer)
814 (mixi-diary-set-title diary (match-string 1 buffer))
815 (signal 'error (list 'cannot-find-title diary)))
816 (if (string-match mixi-diary-content-regexp buffer)
817 (mixi-diary-set-content diary (mixi-remove-markup
818 (match-string 1 buffer)))
819 (signal 'error (list 'cannot-find-content diary))))
820 (mixi-diary-touch diary)))
822 (defun mixi-diary-realize-p (diary)
823 "Return the timestamp of DIARY."
824 (unless (mixi-diary-p diary)
825 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
826 (aref (cdr diary) 0))
828 (defun mixi-diary-owner (diary)
829 "Return the owner of DIARY."
830 (unless (mixi-diary-p diary)
831 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
832 (aref (cdr diary) 1))
834 (defun mixi-diary-id (diary)
835 "Return the id of DIARY."
836 (unless (mixi-diary-p diary)
837 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
838 (aref (cdr diary) 2))
840 (defun mixi-diary-time (diary)
841 "Return the time of DIARY."
842 (unless (mixi-diary-p diary)
843 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
844 (mixi-diary-realize diary)
845 (aref (cdr diary) 3))
847 (defun mixi-diary-title (diary)
848 "Return the title of DIARY."
849 (unless (mixi-diary-p diary)
850 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
851 (mixi-diary-realize diary)
852 (aref (cdr diary) 4))
854 (defun mixi-diary-content (diary)
855 "Return the content of DIARY."
856 (unless (mixi-diary-p diary)
857 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
858 (mixi-diary-realize diary)
859 (aref (cdr diary) 5))
861 (defun mixi-diary-touch (diary)
862 "Set the timestamp of DIARY."
863 (unless (mixi-diary-p diary)
864 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
865 (aset (cdr diary) 0 (current-time)))
867 (defun mixi-diary-set-time (diary time)
868 "Set the time of DIARY."
869 (unless (mixi-diary-p diary)
870 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
871 (aset (cdr diary) 3 time))
873 (defun mixi-diary-set-title (diary title)
874 "Set the title of DIARY."
875 (unless (mixi-diary-p diary)
876 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
877 (aset (cdr diary) 4 title))
879 (defun mixi-diary-set-content (diary content)
880 "Set the content of DIARY."
881 (unless (mixi-diary-p diary)
882 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
883 (aset (cdr diary) 5 content))
885 (defmacro mixi-diary-list-page (&optional friend)
886 `(concat "/list_diary.pl?page=%d"
887 (when ,friend (concat "&id=" (mixi-friend-id ,friend)))))
889 (defconst mixi-diary-list-regexp
890 "<a href=\"view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=[0-9]+\">")
892 (defvar mixi-diary-max-pages nil)
893 (defun mixi-get-diaries (&optional friend)
894 "Get diaries of FRIEND."
895 (unless (or (null friend) (mixi-friend-p friend))
896 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
897 (let ((items (mixi-get-matched-items (mixi-diary-list-page friend)
899 mixi-diary-list-regexp)))
900 (mapcar (lambda (item)
901 (mixi-make-diary friend (nth 0 item)))
904 (defmacro mixi-new-diary-list-page ()
905 `(concat "/new_friend_diary.pl?page=%d"))
907 (defconst mixi-new-diary-list-regexp
908 "<a class=\"new_link\" href=view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=\\([0-9]+\\)>")
910 (defvar mixi-new-diary-max-pages nil)
911 (defun mixi-get-new-diaries ()
913 (let ((items (mixi-get-matched-items (mixi-new-diary-list-page)
914 mixi-new-diary-max-pages
915 mixi-new-diary-list-regexp)))
916 (mapcar (lambda (item)
917 (mixi-make-diary (mixi-make-friend (nth 1 item)) (nth 0 item)))
921 (defvar mixi-community-cache (make-hash-table :test 'equal))
922 (defun mixi-make-community (id &optional name)
923 "Return a community object."
924 (mixi-make-cache id (cons 'mixi-community (vector nil id name nil nil nil
926 mixi-community-cache))
928 (defmacro mixi-community-p (community)
929 `(eq (mixi-object-class ,community) 'mixi-community))
931 (defmacro mixi-community-page (community)
932 `(concat "/view_community.pl?id=" (mixi-community-id ,community)))
934 (defconst mixi-community-nodata-regexp
935 "^¥Ç¡¼¥¿¤¬¤¢¤ê¤Þ¤»¤ó")
936 (defconst mixi-community-name-regexp
937 "<td WIDTH=345>\\(.*\\)</td></tr>")
938 (defconst mixi-community-birthday-regexp
939 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>³«ÀßÆü</font></td>\n<td>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü</td>")
940 ;; FIXME: Care when the owner has seceded.
941 (defconst mixi-community-owner-regexp
942 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>´ÉÍý¿Í</font></td>\n<td>\n\n<a href=\"\\(home\\.pl\\|show_friend\\.pl\\?id=\\([0-9]+\\)\\)\">\n\\(.+\\)</a>")
943 (defconst mixi-community-category-regexp
944 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥«¥Æ¥´¥ê</font></td>\n<td>\\([^<]+\\)</td>")
945 (defconst mixi-community-members-regexp
946 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥á¥ó¥Ð¡¼¿ô</font></td>\n<td>\\([0-9]+\\)¿Í</td></tr>")
947 (defconst mixi-community-open-level-regexp
948 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>»²²Ã¾ò·ï¤È<br>¸ø³«¥ì¥Ù¥ë</font></td>
949 <td>\\(.+\\)</td></tr>")
950 (defconst mixi-community-authority-regexp
951 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥È¥Ô¥Ã¥¯ºîÀ®¤Î¸¢¸Â</font></td>\n<td>\\(.+\\)</td></tr>")
952 (defconst mixi-community-description-regexp
953 "<td CLASS=h120>\\(.+\\)</td>")
955 (defun mixi-community-realize (community)
956 "Realize a COMMUNITY."
957 ;; FIXME: Check a expiration of cache?
958 (unless (mixi-community-realize-p community)
959 (with-mixi-retrieve (mixi-community-page community)
960 (if (string-match mixi-community-nodata-regexp buffer)
961 ;; FIXME: Set all members?
962 (mixi-community-set-name community "¥Ç¡¼¥¿¤¬¤¢¤ê¤Þ¤»¤ó")
963 (if (string-match mixi-community-name-regexp buffer)
964 (mixi-community-set-name community (match-string 1 buffer))
965 (signal 'error (list 'cannot-find-name community)))
966 (if (string-match mixi-community-birthday-regexp buffer)
967 (mixi-community-set-birthday
969 (encode-time 0 0 0 (string-to-number (match-string 3 buffer))
970 (string-to-number (match-string 2 buffer))
971 (string-to-number (match-string 1 buffer))))
972 (signal 'error (list 'cannot-find-birthday community)))
973 (if (string-match mixi-community-owner-regexp buffer)
974 (if (string= (match-string 1 buffer) "home.pl")
975 (mixi-community-set-owner community (mixi-make-me))
976 (mixi-community-set-owner
977 community (mixi-make-friend (match-string 2 buffer)
978 (match-string 3 buffer))))
979 (signal 'error (list 'cannot-find-owner community)))
980 (if (string-match mixi-community-category-regexp buffer)
981 (mixi-community-set-category community (match-string 1 buffer))
982 (signal 'error (list 'cannot-find-category community)))
983 (if (string-match mixi-community-members-regexp buffer)
984 (mixi-community-set-members
985 community (string-to-number (match-string 1 buffer)))
986 (signal 'error (list 'cannot-find-members community)))
987 (if (string-match mixi-community-open-level-regexp buffer)
988 (mixi-community-set-open-level community (match-string 1 buffer))
989 (signal 'error (list 'cannot-find-open-level community)))
990 (if (string-match mixi-community-authority-regexp buffer)
991 (mixi-community-set-authority community (match-string 1 buffer))
992 (signal 'error (list 'cannot-find-authority community)))
993 (if (string-match mixi-community-description-regexp buffer)
994 (mixi-community-set-description
995 community (mixi-remove-markup (match-string 1 buffer)))
996 (signal 'error (list 'cannot-find-description community)))))
997 (mixi-community-touch community)))
999 (defun mixi-community-realize-p (community)
1000 "Return the timestamp of COMMUNITY."
1001 (unless (mixi-community-p community)
1002 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1003 (aref (cdr community) 0))
1005 (defun mixi-community-id (community)
1006 "Return the id of COMMUNITY."
1007 (unless (mixi-community-p community)
1008 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1009 (aref (cdr community) 1))
1011 (defun mixi-community-name (community)
1012 "Return the name of COMMUNITY."
1013 (unless (mixi-community-p community)
1014 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1015 (unless (aref (cdr community) 2)
1016 (mixi-community-realize community))
1017 (aref (cdr community) 2))
1019 (defun mixi-community-birthday (community)
1020 "Return the birthday of COMMUNITY."
1021 (unless (mixi-community-p community)
1022 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1023 (mixi-community-realize community)
1024 (aref (cdr community) 3))
1026 (defun mixi-community-owner (community)
1027 "Return the owner of COMMUNITY."
1028 (unless (mixi-community-p community)
1029 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1030 (mixi-community-realize community)
1031 (aref (cdr community) 4))
1033 (defun mixi-community-category (community)
1034 "Return the category of COMMUNITY."
1035 (unless (mixi-community-p community)
1036 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1037 (mixi-community-realize community)
1038 (aref (cdr community) 5))
1040 (defun mixi-community-members (community)
1041 "Return the members of COMMUNITY."
1042 (unless (mixi-community-p community)
1043 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1044 (mixi-community-realize community)
1045 (aref (cdr community) 6))
1047 (defun mixi-community-open-level (community)
1048 "Return the open-level of COMMUNITY."
1049 (unless (mixi-community-p community)
1050 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1051 (mixi-community-realize community)
1052 (aref (cdr community) 7))
1054 (defun mixi-community-authority (community)
1055 "Return the authority of COMMUNITY."
1056 (unless (mixi-community-p community)
1057 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1058 (mixi-community-realize community)
1059 (aref (cdr community) 8))
1061 (defun mixi-community-description (community)
1062 "Return the description of COMMUNITY."
1063 (unless (mixi-community-p community)
1064 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1065 (mixi-community-realize community)
1066 (aref (cdr community) 9))
1068 (defun mixi-community-touch (community)
1069 "Set the timestamp of COMMUNITY."
1070 (unless (mixi-community-p community)
1071 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1072 (aset (cdr community) 0 (current-time)))
1074 (defun mixi-community-set-name (community name)
1075 "Set the name of COMMUNITY."
1076 (unless (mixi-community-p community)
1077 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1078 (aset (cdr community) 2 name))
1080 (defun mixi-community-set-birthday (community birthday)
1081 "Set the birthday of COMMUNITY."
1082 (unless (mixi-community-p community)
1083 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1084 (aset (cdr community) 3 birthday))
1086 (defun mixi-community-set-owner (community owner)
1087 "Set the owner of COMMUNITY."
1088 (unless (mixi-community-p community)
1089 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1090 (unless (mixi-friend-p owner)
1091 (signal 'wrong-type-argument (list 'mixi-friend-p owner)))
1092 (aset (cdr community) 4 owner))
1094 (defun mixi-community-set-category (community category)
1095 "Set the category of COMMUNITY."
1096 (unless (mixi-community-p community)
1097 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1098 (aset (cdr community) 5 category))
1100 (defun mixi-community-set-members (community members)
1101 "Set the name of COMMUNITY."
1102 (unless (mixi-community-p community)
1103 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1104 (aset (cdr community) 6 members))
1106 (defun mixi-community-set-open-level (community open-level)
1107 "Set the name of COMMUNITY."
1108 (unless (mixi-community-p community)
1109 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1110 (aset (cdr community) 7 open-level))
1112 (defun mixi-community-set-authority (community authority)
1113 "Set the name of COMMUNITY."
1114 (unless (mixi-community-p community)
1115 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1116 (aset (cdr community) 8 authority))
1118 (defun mixi-community-set-description (community description)
1119 "Set the name of COMMUNITY."
1120 (unless (mixi-community-p community)
1121 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1122 (aset (cdr community) 9 description))
1124 (defmacro mixi-community-list-page (&optional friend)
1125 `(concat "/list_community.pl?page=%d"
1126 (when ,friend (concat "&id=" (mixi-friend-id ,friend)))))
1128 (defconst mixi-community-list-id-regexp
1129 "<a href=view_community\\.pl\\?id=\\([0-9]+\\)")
1130 (defconst mixi-community-list-name-regexp
1131 "<td valign=middle>\\(.+\\)([0-9]+)</td>")
1133 (defvar mixi-community-max-pages nil)
1134 (defun mixi-get-communities (&optional friend)
1135 "Get communities of FRIEND."
1136 (unless (or (null friend) (mixi-friend-p friend))
1137 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
1138 (let ((ids (mixi-get-matched-items (mixi-community-list-page friend)
1139 mixi-community-max-pages
1140 mixi-community-list-id-regexp))
1141 (names (mixi-get-matched-items (mixi-community-list-page friend)
1142 mixi-community-max-pages
1143 mixi-community-list-name-regexp)))
1146 (while (< index (length ids))
1147 (setq ret (cons (mixi-make-community (nth 0 (nth index ids))
1148 (nth 0 (nth index names))) ret))
1153 (defvar mixi-topic-cache (make-hash-table :test 'equal))
1154 (defun mixi-make-topic (community id)
1155 "Return a topic object."
1156 (mixi-make-cache (list (mixi-community-id community) id)
1157 (cons 'mixi-topic (vector nil community id nil nil nil nil))
1160 (defmacro mixi-topic-p (topic)
1161 `(eq (mixi-object-class ,topic) 'mixi-topic))
1163 (defmacro mixi-topic-page (topic)
1164 `(concat "/view_bbs.pl?id=" (mixi-topic-id ,topic)
1165 "&comm_id=" (mixi-community-id (mixi-topic-community ,topic))))
1167 (defconst mixi-topic-time-regexp
1168 "<td rowspan=\"3\" width=\"110\" bgcolor=\"#ffd8b0\" align=\"center\" valign=\"top\" nowrap>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)</td>")
1169 (defconst mixi-topic-title-regexp
1170 "<td bgcolor=\"#fff4e0\"> \\([^<]+\\)</td>")
1171 ;; FIXME: Remove `¤µ¤ó'.
1172 (defconst mixi-topic-owner-regexp
1173 "<td bgcolor=\"#fdf9f2\"> <font color=\"#dfb479\"></font> <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)\\(¤µ¤ó\\)?</a>")
1174 (defconst mixi-topic-content-regexp
1175 "<td class=\"h120\"><table><tr>\\(.+\\)?</tr></table>\\(.+\\)</td>")
1177 (defun mixi-topic-realize (topic)
1179 ;; FIXME: Check a expiration of cache?
1180 (unless (mixi-topic-realize-p topic)
1181 (with-mixi-retrieve (mixi-topic-page topic)
1182 (if (string-match mixi-topic-time-regexp buffer)
1183 (mixi-topic-set-time
1184 topic (encode-time 0 (string-to-number (match-string 5 buffer))
1185 (string-to-number (match-string 4 buffer))
1186 (string-to-number (match-string 3 buffer))
1187 (string-to-number (match-string 2 buffer))
1188 (string-to-number (match-string 1 buffer))))
1189 (signal 'error (list 'cannot-find-time topic)))
1190 (if (string-match mixi-topic-title-regexp buffer)
1191 (mixi-topic-set-title topic (match-string 1 buffer))
1192 (signal 'error (list 'cannot-find-title topic)))
1193 (if (string-match mixi-topic-owner-regexp buffer)
1194 (mixi-topic-set-owner topic
1195 (mixi-make-friend (match-string 1 buffer)
1196 (match-string 2 buffer)))
1197 (signal 'error (list 'cannot-find-owner topic)))
1198 (if (string-match mixi-topic-content-regexp buffer)
1199 (mixi-topic-set-content topic (mixi-remove-markup
1200 (match-string 2 buffer)))
1201 (signal 'error (list 'cannot-find-content topic))))
1202 (mixi-topic-touch topic)))
1204 (defun mixi-topic-realize-p (topic)
1205 "Return the timestamp of TOPIC."
1206 (unless (mixi-topic-p topic)
1207 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1208 (aref (cdr topic) 0))
1210 (defun mixi-topic-community (topic)
1211 "Return the community of TOPIC."
1212 (unless (mixi-topic-p topic)
1213 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1214 (aref (cdr topic) 1))
1216 (defun mixi-topic-id (topic)
1217 "Return the id of TOPIC."
1218 (unless (mixi-topic-p topic)
1219 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1220 (aref (cdr topic) 2))
1222 (defun mixi-topic-time (topic)
1223 "Return the time of TOPIC."
1224 (unless (mixi-topic-p topic)
1225 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1226 (mixi-topic-realize topic)
1227 (aref (cdr topic) 3))
1229 (defun mixi-topic-title (topic)
1230 "Return the title of TOPIC."
1231 (unless (mixi-topic-p topic)
1232 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1233 (mixi-topic-realize topic)
1234 (aref (cdr topic) 4))
1236 (defun mixi-topic-owner (topic)
1237 "Return the owner of TOPIC."
1238 (unless (mixi-topic-p topic)
1239 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1240 (mixi-topic-realize topic)
1241 (aref (cdr topic) 5))
1243 (defun mixi-topic-content (topic)
1244 "Return the content of TOPIC."
1245 (unless (mixi-topic-p topic)
1246 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1247 (mixi-topic-realize topic)
1248 (aref (cdr topic) 6))
1250 (defun mixi-topic-touch (topic)
1251 "Set the timestamp of TOPIC."
1252 (unless (mixi-topic-p topic)
1253 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1254 (aset (cdr topic) 0 (current-time)))
1256 (defun mixi-topic-set-time (topic time)
1257 "Set the time of TOPIC."
1258 (unless (mixi-topic-p topic)
1259 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1260 (aset (cdr topic) 3 time))
1262 (defun mixi-topic-set-title (topic title)
1263 "Set the title of TOPIC."
1264 (unless (mixi-topic-p topic)
1265 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1266 (aset (cdr topic) 4 title))
1268 (defun mixi-topic-set-owner (topic owner)
1269 "Set the owner of TOPIC."
1270 (unless (mixi-topic-p topic)
1271 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1272 (unless (mixi-friend-p owner)
1273 (signal 'wrong-type-argument (list 'mixi-friend-p owner)))
1274 (aset (cdr topic) 5 owner))
1276 (defun mixi-topic-set-content (topic content)
1277 "Set the content of TOPIC."
1278 (unless (mixi-topic-p topic)
1279 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1280 (aset (cdr topic) 6 content))
1282 (defmacro mixi-topic-list-page (community)
1283 `(concat "/list_bbs.pl?page=%d"
1284 "&id=" (mixi-community-id ,community)))
1286 (defconst mixi-topic-list-regexp
1287 "<a href=view_bbs\\.pl\\?id=\\([0-9]+\\)")
1289 (defvar mixi-topic-max-pages nil)
1290 (defun mixi-get-topics (community)
1291 "Get topics of COMMUNITY."
1292 (unless (mixi-community-p community)
1293 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1294 (let ((items (mixi-get-matched-items (mixi-topic-list-page community)
1295 mixi-topic-max-pages
1296 mixi-topic-list-regexp)))
1297 (mapcar (lambda (item)
1298 (mixi-make-topic community (nth 0 item)))
1301 (defmacro mixi-new-topic-list-page ()
1302 `(concat "/new_bbs.pl?page=%d"))
1304 (defconst mixi-new-topic-list-regexp
1305 "<a href=\"view_bbs\\.pl\\?id=\\([0-9]+\\)&comment_count=[0-9]+&comm_id=\\([0-9]+\\)\" class=\"new_link\">")
1307 (defvar mixi-new-topic-max-pages nil)
1308 (defun mixi-get-new-topics ()
1310 (let ((items (mixi-get-matched-items (mixi-new-topic-list-page)
1311 mixi-new-topic-max-pages
1312 mixi-new-topic-list-regexp)))
1313 (mapcar (lambda (item)
1314 (mixi-make-topic (mixi-make-community (nth 1 item))
1319 (defun mixi-make-comment (parent owner time content)
1320 "Return a comment object."
1321 (cons 'mixi-comment (vector parent owner time content)))
1323 (defmacro mixi-comment-p (comment)
1324 `(eq (mixi-object-class ,comment) 'mixi-comment))
1326 (defun mixi-comment-parent (comment)
1327 "Return the parent of COMMENT."
1328 (unless (mixi-comment-p comment)
1329 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1330 (aref (cdr comment) 0))
1332 (defun mixi-comment-owner (comment)
1333 "Return the owner of COMMENT."
1334 (unless (mixi-comment-p comment)
1335 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1336 (aref (cdr comment) 1))
1338 (defun mixi-comment-time (comment)
1339 "Return the time of COMMENT."
1340 (unless (mixi-comment-p comment)
1341 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1342 (aref (cdr comment) 2))
1344 (defun mixi-comment-content (comment)
1345 "Return the content of COMMENT."
1346 (unless (mixi-comment-p comment)
1347 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1348 (aref (cdr comment) 3))
1350 (defun mixi-diary-comment-list-page (diary)
1351 (concat "/view_diary.pl?page=all"
1352 "&id=" (mixi-diary-id diary)
1353 "&owner_id=" (mixi-friend-id (mixi-diary-owner diary))))
1355 ;; FIXME: Split regexp to time, owner(id and nick) and contents.
1356 (defconst mixi-diary-comment-list-regexp
1357 "<td rowspan=\"2\" align=\"center\" width=\"95\" bgcolor=\"#f2ddb7\" nowrap>
1358 \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)\\(<br>
1359 <input type=checkbox name=comment_id value=\".+\">
1362 <td ALIGN=center BGCOLOR=#FDF9F2 WIDTH=430>
1363 <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"410\">
1366 <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a>
1373 <!-- ËÜʸ : start -->
1375 <td bgcolor=\"#ffffff\">
1376 <table BORDER=0 CELLSPACING=0 CELLPADDING=[35] WIDTH=410>
1380 </td></tr></table>")
1382 (defun mixi-topic-comment-list-page (topic)
1383 (concat "/view_bbs.pl?page=all"
1384 "&id=" (mixi-topic-id topic)
1385 "&comm_id=" (mixi-community-id (mixi-topic-community topic))))
1387 ;; FIXME: Split regexp to time, owner(id and nick) and contents.
1388 (defconst mixi-topic-comment-list-regexp
1389 "<tr valign=\"top\">
1390 <td rowspan=\"2\" width=\"110\" bgcolor=\"#f2ddb7\" align=\"center\" nowrap>
1391 \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>
1392 \\([0-9]+\\):\\([0-9]+\\)<br>
1394 <td bgcolor=\"#fdf9f2\"> <font color=\"#f8a448\">
1395 <b> [0-9]+</b>:</font>
1397 <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a>
1404 <td bgcolor=\"#ffffff\" align=\"center\">
1405 <table border=\"0\" cellspacing=\"0\" cellpadding=\"5\" width=\"500\">
1416 (defun mixi-get-comments (parent)
1417 "Get comments of PARENT."
1418 (unless (mixi-object-p parent)
1419 (signal 'wrong-type-argument (list 'mixi-object-p parent)))
1420 (let* ((name (mixi-object-name parent))
1421 (list-page (intern (concat mixi-object-prefix name
1422 "-comment-list-page")))
1423 (regexp (eval (intern (concat mixi-object-prefix name
1424 "-comment-list-regexp")))))
1425 (let ((items (mixi-get-matched-items (funcall list-page parent) 1 regexp)))
1426 (mapcar (lambda (item)
1427 (mixi-make-comment parent (mixi-make-friend
1428 (nth 6 item) (nth 7 item))
1431 (string-to-number (nth 4 item))
1432 (string-to-number (nth 3 item))
1433 (string-to-number (nth 2 item))
1434 (string-to-number (nth 1 item))
1435 (string-to-number (nth 0 item)))
1436 (mixi-remove-markup (nth 8 item))))
1439 (defmacro mixi-new-comment-list-page ()
1440 `(concat "/new_comment.pl?page=%d"))
1442 (defconst mixi-new-comment-list-regexp
1443 "<a href=\"view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=\\([0-9]+\\)&comment_count=[0-9]+\" class=\"new_link\">")
1445 (defvar mixi-new-comment-max-pages nil)
1446 (defun mixi-get-new-comments ()
1448 (let ((items (mixi-get-matched-items (mixi-new-comment-list-page)
1449 mixi-new-comment-max-pages
1450 mixi-new-comment-list-regexp)))
1451 (mapcar (lambda (item)
1452 (let ((diary (mixi-make-diary
1453 (mixi-make-friend (nth 1 item))
1455 (mixi-get-comments diary)))
1460 ;;; mixi.el ends here