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 (eval-when-compile (require 'cl))
102 (autoload 'w3m-expand-url "w3m" nil t)
105 "API library for accessing to mixi."
108 (defcustom mixi-url "http://mixi.jp"
113 (defcustom mixi-coding-system 'euc-jp
114 "*Coding system for mixi."
118 (defcustom mixi-retrieve-function
119 (if (and (require 'url "url" t)
120 (fboundp 'url-retrieve-synchronously))
121 'mixi-w3-retrieve 'mixi-w3m-retrieve)
122 "*The function for retrieving."
123 :type '(choice (const :tag "Using w3" mixi-w3-retrieve)
124 (const :tag "Using w3m" mixi-w3m-retrieve)
125 (function :format "Other function: %v\n" :size 0))
128 (defcustom mixi-default-email nil
129 "*Default E-mail address that is used to login automatically."
130 :type '(choice (string :tag "E-mail address")
131 (const :tag "Asked when it is necessary" nil))
134 (defcustom mixi-default-password nil
135 "*Default password that is used to login automatically."
136 :type '(choice (string :tag "Password")
137 (const :tag "Asked when it is necessary" nil))
140 (defcustom mixi-accept-adult-contents t
141 "*If non-nil, accept to access to the adult contents."
145 (defcustom mixi-continuously-access-interval 4.0
146 "*Time interval between each mixi access.
147 Increase this value when unexpected error frequently occurs."
151 (defcustom mixi-cache-expires 3600
152 "*Seconds for expiration of a cached object."
153 :type '(choice (integer :tag "Expired seconds")
154 (const :tag "Don't expire" nil))
157 ;; FIXME: Not implemented.
158 (defcustom mixi-cache-use-file t
159 "*If non-nil, caches are saved to files."
163 (defcustom mixi-cache-directory (expand-file-name "~/.mixi")
164 "*Where to look for cache files."
171 (defmacro mixi-message (string)
172 `(concat "[mixi] " ,string))
174 (defconst mixi-message-adult-contents
175 "¤³¤Î¥Ú¡¼¥¸¤«¤éÀè¤Ï¥¢¥À¥ë¥È¡ÊÀ®¿Í¸þ¤±¡Ë¥³¥ó¥Æ¥ó¥Ä¤¬´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£<br>
176 ±ÜÍ÷¤ËƱ°Õ¤µ¤ì¤¿Êý¤Î¤ß¡¢Àè¤Ø¤ª¿Ê¤ß¤¯¤À¤µ¤¤¡£")
177 (defconst mixi-message-continuously-accessing
178 "°ÂÄꤷ¤Æ¥µ¥¤¥È¤Î±¿±Ä¤ò¤ª¤³¤Ê¤¦°Ù¡¢´Ö³Ö¤ò¶õ¤±¤Ê¤¤Ï¢Â³Åª¤Ê¥Ú¡¼¥¸¤ÎÁ«°Ü¡¦¹¹<br>
179 ¿·¤ÏÀ©¸Â¤µ¤»¤Æ¤¤¤¿¤À¤¤¤Æ¤ª¤ê¤Þ¤¹¡£¤´ÌÂÏǤò¤ª¤«¤±¤¤¤¿¤·¤Þ¤¹¤¬¡¢¤·¤Ð¤é¤¯¤ª<br>
180 ÂÔ¤Á¤¤¤¿¤À¤¤¤Æ¤«¤éÁàºî¤ò¤ª¤³¤Ê¤Ã¤Æ¤¯¤À¤µ¤¤¡£")
181 (defconst mixi-warning-continuously-accessing
182 "´Ö³Ö¤ò¶õ¤±¤Ê¤¤Ï¢Â³Åª¤Ê¥Ú¡¼¥¸¤ÎÁ«°Ü¡¦¹¹¿·¤òÉÑÈˤˤª¤³¤Ê¤ï¤ì¤Æ¤¤¤ë¤³¤È¤¬¸«<br>
183 ¼õ¤±¤é¤ì¤Þ¤·¤¿¤Î¤Ç¡¢°ì»þŪ¤ËÁàºî¤òÄä»ß¤µ¤»¤Æ¤¤¤¿¤À¤¤Þ¤¹¡£¿½¤·Ìõ¤´¤¶¤¤¤Þ<br>
184 ¤»¤ó¤¬¡¢¤·¤Ð¤é¤¯¤Î´Ö¤ªÂÔ¤Á¤¯¤À¤µ¤¤¡£")
186 (defun mixi-retrieve-1 (buffer url &optional post-data)
187 (when (string-match mixi-message-adult-contents buffer)
188 (if mixi-accept-adult-contents
189 (setq buffer (funcall mixi-retrieve-function url "submit=agree"))
190 (setq buffer (funcall mixi-retrieve-function (concat url "?")))))
191 (when (string-match mixi-warning-continuously-accessing buffer)
192 (error (mixi-message "Continuously accessing")))
193 (if (not (string-match mixi-message-continuously-accessing buffer))
195 (message (mixi-message "Waiting for continuously accessing..."))
196 (sit-for mixi-continuously-access-interval)
197 (funcall mixi-retrieve-function url post-data)))
199 (defun mixi-w3-retrieve (url &optional post-data)
200 "Retrieve the URL and return getted strings."
203 (setq url-request-method "POST")
204 (setq url-request-data post-data))
205 (setq url-request-method "GET")
206 (setq url-request-data nil))
207 (let* ((url (url-expand-file-name url mixi-url))
208 (buffer (url-retrieve-synchronously url))
210 (unless (bufferp buffer)
211 (error (mixi-message "Cannot retrieve")))
212 (with-current-buffer buffer
213 (goto-char (point-min))
214 (if (re-search-forward "HTTP/[0-9.]+ 302 Moved" nil t)
215 (if (re-search-forward
216 (concat "Location: " mixi-url "\\(.+\\)") nil t)
217 (setq ret (mixi-w3-retrieve (match-string 1) post-data))
218 (setq ret (mixi-w3-retrieve "/home.pl" post-data)))
219 (unless (re-search-forward "HTTP/[0-9.]+ 200 OK" nil t)
220 (error (mixi-message "Cannot retrieve")))
221 (search-forward "\n\n")
222 (setq ret (mm-decode-coding-string
223 (buffer-substring-no-properties (point) (point-max))
226 (setq ret (mixi-retrieve-1 ret url post-data))))
229 (defun mixi-w3m-retrieve (url &optional post-data)
230 "Retrieve the URL and return getted strings."
231 (let ((url (w3m-expand-url url mixi-url)))
233 (if (not (string= (w3m-retrieve url nil nil post-data) "text/html"))
234 (error (mixi-message "Cannot retrieve"))
235 (w3m-decode-buffer url)
236 (let ((ret (buffer-substring-no-properties (point-min) (point-max))))
237 (mixi-retrieve-1 ret url post-data))))))
239 (defconst mixi-my-id-regexp
240 "<a href=\"add_diary\\.pl\\?id=\\([0-9]+\\)")
242 (defun mixi-login (&optional email password)
244 (when (and (eq mixi-retrieve-function 'mixi-w3m-retrieve)
245 (not w3m-use-cookies))
248 "Require to accept cookies. Please set `w3m-use-cookies' to t.")))
249 (let ((email (or email mixi-default-email
250 (read-from-minibuffer (mixi-message "Login Email: "))))
251 (password (or password mixi-default-password
252 (read-passwd (mixi-message "Login Password: ")))))
253 (let ((buffer (funcall mixi-retrieve-function "/login.pl"
254 (concat "email=" email
255 "&password=" password
258 (unless (string-match "url=/check\\.pl\\?n=" buffer)
259 (error (mixi-message "Cannot login")))
260 (setq buffer (funcall mixi-retrieve-function "/check.pl?n=home.pl"))
261 (if (string-match mixi-my-id-regexp buffer)
262 (setq mixi-me (mixi-make-friend
263 (string-to-number (match-string 1 buffer))))
264 (error (mixi-message "Cannot login"))))))
266 (defun mixi-logout ()
267 (funcall mixi-retrieve-function "/logout.pl"))
269 (defmacro with-mixi-retrieve (url &rest body)
272 (setq buffer (funcall mixi-retrieve-function ,url))
273 (when (string-match "login.pl" buffer)
275 (setq buffer (funcall mixi-retrieve-function ,url))))
277 (put 'with-mixi-retrieve 'lisp-indent-function 'defun)
279 (defun mixi-get-matched-items (url max-pages regexp)
280 "Get matched items to REGEXP in URL."
284 (while (or (null max-pages) (<= page max-pages))
285 (with-mixi-retrieve (format url page)
287 (while (string-match regexp buffer pos)
290 (while (match-string num buffer)
291 (let ((string (match-string num buffer)))
293 (when (string-match "^[0-9]+$" string)
294 (setq string (string-to-number string))))
295 (setq list (cons string list)))
297 (setq ids (cons (reverse list) ids))
298 (setq pos (match-end (1- num)))))
302 ;; FIXME: Sort? Now order by newest.
305 ;; stolen (and modified) from shimbun.el
306 (defun mixi-remove-markup (string)
307 "Remove markups from STRING."
311 (goto-char (point-min))
312 (while (search-forward "<!--" nil t)
313 (delete-region (match-beginning 0)
314 (or (search-forward "-->" nil t)
316 (goto-char (point-min))
317 (while (re-search-forward "<[^>]+>" nil t)
318 (replace-match "" t t))
319 (goto-char (point-min))
320 (while (re-search-forward "
\r" nil t)
321 (replace-match "\n" t t)))
326 (defun mixi-cache-expired-p (object)
327 "Whether a cache of OBJECT is expired."
328 ;; FIXME: Use method instead of `(aref (cdr object) 0)'.
329 (let ((timestamp (aref (cdr object) 0)))
330 (unless (or (null mixi-cache-expires)
332 (time-less-p (time-add timestamp
333 (seconds-to-time mixi-cache-expires))
336 (defun mixi-make-cache (key value table)
337 "Make a cache object and return it."
338 (let ((cache (gethash key table)))
339 (if (and cache (not (mixi-cache-expired-p cache)))
341 (puthash key value table))))
344 (defconst mixi-object-prefix "mixi-")
346 (defmacro mixi-object-class (object)
349 (defmacro mixi-object-p (object)
350 `(eq (string-match (concat "^" mixi-object-prefix)
351 (symbol-name (mixi-object-class ,object))) 0))
353 (defun mixi-object-name (object)
354 "Return the name of OBJECT."
355 (unless (mixi-object-p object)
356 (signal 'wrong-type-argument (list 'mixi-object-p object)))
357 (let ((class (mixi-object-class object)))
358 (substring (symbol-name class) (length mixi-object-prefix))))
360 (defun mixi-object-id (object)
361 "Return the id of OBJECT."
362 (unless (mixi-object-p object)
363 (signal 'wrong-type-argument (list 'mixi-object-p object)))
364 (let ((func (intern (concat mixi-object-prefix
365 (mixi-object-name object) "-id"))))
366 (funcall func object)))
369 (defvar mixi-friend-cache (make-hash-table :test 'equal))
370 (defun mixi-make-friend (id &optional nick)
371 "Return a friend object."
372 (mixi-make-cache id (cons 'mixi-friend (vector nil id nick nil nil nil nil
373 nil nil nil nil nil nil nil))
376 (defun mixi-make-me ()
378 (with-mixi-retrieve "/home.pl"
379 (if (string-match mixi-my-id-regexp buffer)
381 (mixi-make-friend (string-to-number (match-string 1 buffer))))
382 (signal 'error (list 'who-am-i)))))
385 (defmacro mixi-friend-p (friend)
386 `(eq (mixi-object-class ,friend) 'mixi-friend))
388 (defmacro mixi-friend-page (friend)
389 `(concat "/show_friend.pl?id=" (number-to-string (mixi-friend-id ,friend))))
391 (defconst mixi-friend-nick-regexp
392 "<img alt=\"\\*\" src=\"http://img\\.mixi\\.jp/img/dot0\\.gif\" width=\"1\" height=\"5\"><br>\n\\(.*\\)¤µ¤ó([0-9]+)")
393 (defconst mixi-friend-name-sex-regexp
394 "<td BGCOLOR=#F2DDB7 WIDTH=80 NOWRAP><font COLOR=#996600>̾\\( \\| \\)Á°</font></td>\n+<td WIDTH=345>\\([^<]+\\) (\\([Ã˽÷]\\)À)</td>")
395 (defconst mixi-friend-address-regexp
396 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¸½½»½ê</font></td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
397 (defconst mixi-friend-age-regexp
398 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>ǯ\\( \\| \\)Îð</font></td>\n<td>\\([0-9]+\\)ºÐ\\(\n.+\n\\)?</td></tr>")
399 (defconst mixi-friend-birthday-regexp
400 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>ÃÂÀ¸Æü</font></td>\n<td>\\([0-9]+\\)·î\\([0-9]+\\)Æü\\(\n.+\n\\)?</td></tr>")
401 (defconst mixi-friend-blood-type-regexp
402 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>·ì±Õ·¿</font></td>\n<td>\\([ABO]B?\\)·¿\\(\n\n\\)?</td></tr>")
403 (defconst mixi-friend-birthplace-regexp
404 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>½Ð¿ÈÃÏ</font>\n?</td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
405 (defconst mixi-friend-hobby-regexp
406 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¼ñ\\( \\| \\)Ì£</font></td>\n<td>\\(.+\\)</td></tr>")
407 (defconst mixi-friend-job-regexp
408 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¿¦\\( \\| \\)¶È</font></td>\n<td>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
409 (defconst mixi-friend-organization-regexp
410 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>½ê\\( \\| \\)°</font></td>\n<td[^>]*>\\(.+\\)\\(\n.+\n\\)?</td></tr>")
411 (defconst mixi-friend-profile-regexp
412 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¼«¸Ê¾Ò²ð</font></td>\n<td CLASS=h120>\\(.+\\)</td></tr>")
414 (defun mixi-friend-realize (friend)
416 ;; FIXME: Check a expiration of cache?
417 (unless (mixi-friend-realize-p friend)
419 (with-mixi-retrieve (mixi-friend-page friend)
421 (if (string-match mixi-friend-nick-regexp buf)
422 (mixi-friend-set-nick friend (match-string 1 buf))
423 (signal 'error (list 'cannot-find-nick friend)))
424 ;; For getting my profile.
425 (unless (string-match mixi-friend-name-sex-regexp buf)
426 (with-mixi-retrieve "/show_profile.pl"
428 (if (string-match mixi-friend-name-sex-regexp buf)
430 (mixi-friend-set-name friend (match-string 2 buf))
431 (mixi-friend-set-sex friend
432 (if (string= (match-string 3 buf) "ÃË")
434 (signal 'error (list 'cannot-find-name-or-sex friend)))
435 (when (string-match mixi-friend-address-regexp buf)
436 (mixi-friend-set-address friend (match-string 1 buf)))
437 (when (string-match mixi-friend-age-regexp buf)
439 friend (string-to-number (match-string 2 buf))))
440 (when (string-match mixi-friend-birthday-regexp buf)
441 (mixi-friend-set-birthday
442 friend (list (string-to-number (match-string 1 buf))
443 (string-to-number (match-string 2 buf)))))
444 (when (string-match mixi-friend-blood-type-regexp buf)
445 (mixi-friend-set-blood-type friend (intern (match-string 1 buf))))
446 (when (string-match mixi-friend-birthplace-regexp buf)
447 (mixi-friend-set-birthplace friend (match-string 1 buf)))
448 (when (string-match mixi-friend-hobby-regexp buf)
449 (mixi-friend-set-hobby
450 friend (split-string (match-string 2 buf) ", ")))
451 (when (string-match mixi-friend-job-regexp buf)
452 (mixi-friend-set-job friend (match-string 2 buf)))
453 (when (string-match mixi-friend-organization-regexp buf)
454 (mixi-friend-set-organization friend (match-string 2 buf)))
455 (when (string-match mixi-friend-profile-regexp buf)
456 (mixi-friend-set-profile
457 friend (mixi-remove-markup (match-string 1 buf)))))
458 (mixi-friend-touch friend)))
460 (defun mixi-friend-realize-p (friend)
461 "Return the timestamp of FRIEND."
462 (unless (mixi-friend-p friend)
463 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
464 (aref (cdr friend) 0))
466 (defun mixi-friend-id (friend)
467 "Return the id of FRIEND."
468 (unless (mixi-friend-p friend)
469 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
470 (aref (cdr friend) 1))
472 (defun mixi-friend-nick (friend)
473 "Return the nick of FRIEND."
474 (unless (mixi-friend-p friend)
475 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
476 (unless (aref (cdr friend) 2)
477 (mixi-friend-realize friend))
478 (aref (cdr friend) 2))
480 (defun mixi-friend-name (friend)
481 "Return the name of FRIEND."
482 (unless (mixi-friend-p friend)
483 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
484 (mixi-friend-realize friend)
485 (aref (cdr friend) 3))
487 (defun mixi-friend-sex (friend)
488 "Return the sex of FRIEND."
489 (unless (mixi-friend-p friend)
490 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
491 (mixi-friend-realize friend)
492 (aref (cdr friend) 4))
494 (defun mixi-friend-address (friend)
495 "Return the address of FRIEND."
496 (unless (mixi-friend-p friend)
497 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
498 (mixi-friend-realize friend)
499 (aref (cdr friend) 5))
501 (defun mixi-friend-age (friend)
502 "Return the age of FRIEND."
503 (unless (mixi-friend-p friend)
504 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
505 (mixi-friend-realize friend)
506 (aref (cdr friend) 6))
508 (defun mixi-friend-birthday (friend)
509 "Return the birthday of FRIEND."
510 (unless (mixi-friend-p friend)
511 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
512 (mixi-friend-realize friend)
513 (aref (cdr friend) 7))
515 (defun mixi-friend-blood-type (friend)
516 "Return the blood type of FRIEND."
517 (unless (mixi-friend-p friend)
518 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
519 (mixi-friend-realize friend)
520 (aref (cdr friend) 8))
522 (defun mixi-friend-birthplace (friend)
523 "Return the birthplace of FRIEND."
524 (unless (mixi-friend-p friend)
525 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
526 (mixi-friend-realize friend)
527 (aref (cdr friend) 9))
529 (defun mixi-friend-hobby (friend)
530 "Return the hobby of FRIEND."
531 (unless (mixi-friend-p friend)
532 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
533 (mixi-friend-realize friend)
534 (aref (cdr friend) 10))
536 (defun mixi-friend-job (friend)
537 "Return the job of FRIEND."
538 (unless (mixi-friend-p friend)
539 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
540 (mixi-friend-realize friend)
541 (aref (cdr friend) 11))
543 (defun mixi-friend-organization (friend)
544 "Return the organization of FRIEND."
545 (unless (mixi-friend-p friend)
546 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
547 (mixi-friend-realize friend)
548 (aref (cdr friend) 12))
550 (defun mixi-friend-profile (friend)
551 "Return the pforile of FRIEND."
552 (unless (mixi-friend-p friend)
553 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
554 (mixi-friend-realize friend)
555 (aref (cdr friend) 13))
557 (defun mixi-friend-touch (friend)
558 "Set the timestamp of FRIEND."
559 (unless (mixi-friend-p friend)
560 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
561 (aset (cdr friend) 0 (current-time)))
563 (defun mixi-friend-set-nick (friend nick)
564 "Set the nick of FRIEND."
565 (unless (mixi-friend-p friend)
566 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
567 (aset (cdr friend) 2 nick))
569 (defun mixi-friend-set-name (friend name)
570 "Set the name of FRIEND."
571 (unless (mixi-friend-p friend)
572 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
573 (aset (cdr friend) 3 name))
575 (defun mixi-friend-set-sex (friend sex)
576 "Set the sex of FRIEND."
577 (unless (mixi-friend-p friend)
578 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
579 (aset (cdr friend) 4 sex))
581 (defun mixi-friend-set-address (friend address)
582 "Set the address of FRIEND."
583 (unless (mixi-friend-p friend)
584 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
585 (aset (cdr friend) 5 address))
587 (defun mixi-friend-set-age (friend age)
588 "Set the age of FRIEND."
589 (unless (mixi-friend-p friend)
590 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
591 (aset (cdr friend) 6 age))
593 (defun mixi-friend-set-birthday (friend birthday)
594 "Set the birthday of FRIEND."
595 (unless (mixi-friend-p friend)
596 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
597 (aset (cdr friend) 7 birthday))
599 (defun mixi-friend-set-blood-type (friend blood-type)
600 "Set the blood type of FRIEND."
601 (unless (mixi-friend-p friend)
602 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
603 (aset (cdr friend) 8 blood-type))
605 (defun mixi-friend-set-birthplace (friend birthplace)
606 "Set the birthplace of FRIEND."
607 (unless (mixi-friend-p friend)
608 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
609 (aset (cdr friend) 9 birthplace))
611 (defun mixi-friend-set-hobby (friend hobby)
612 "Set the hobby of FRIEND."
613 (unless (mixi-friend-p friend)
614 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
615 (aset (cdr friend) 10 hobby))
617 (defun mixi-friend-set-job (friend job)
618 "Set the job of FRIEND."
619 (unless (mixi-friend-p friend)
620 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
621 (aset (cdr friend) 11 job))
623 (defun mixi-friend-set-organization (friend organization)
624 "Set the organization of FRIEND."
625 (unless (mixi-friend-p friend)
626 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
627 (aset (cdr friend) 12 organization))
629 (defun mixi-friend-set-profile (friend profile)
630 "Set the profile of FRIEND."
631 (unless (mixi-friend-p friend)
632 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
633 (aset (cdr friend) 13 profile))
635 (defmacro mixi-friend-list-page (&optional friend)
636 `(concat "/list_friend.pl?page=%d"
637 (when ,friend (concat "&id=" (number-to-string
638 (mixi-friend-id ,friend))))))
640 (defconst mixi-friend-list-id-regexp
641 "<a href=show_friend\\.pl\\?id=\\([0-9]+\\)")
642 (defconst mixi-friend-list-nick-regexp
643 "<td valign=middle>\\(.+\\)¤µ¤ó([0-9]+)<br />")
645 (defvar mixi-friend-max-pages 10)
646 (defun mixi-get-friends (&optional friend)
647 "Get friends of FRIEND."
648 (unless (or (null friend) (mixi-friend-p friend))
649 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
650 (let ((ids (mixi-get-matched-items (mixi-friend-list-page friend)
651 mixi-friend-max-pages
652 mixi-friend-list-id-regexp))
653 (nicks (mixi-get-matched-items (mixi-friend-list-page friend)
654 mixi-friend-max-pages
655 mixi-friend-list-nick-regexp)))
658 (while (< index (length ids))
659 (setq ret (cons (mixi-make-friend (nth 0 (nth index ids))
660 (nth 0 (nth index nicks))) ret))
665 (defmacro mixi-favorite-list-page ()
666 `(concat "/list_bookmark.pl?page=%d"))
668 (defconst mixi-favorite-list-id-regexp
669 "<td ALIGN=center BGCOLOR=#FDF9F2 width=330><a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">")
670 (defconst mixi-favorite-list-nick-regexp
671 "<td BGCOLOR=#FDF9F2><font COLOR=#996600>̾ Á°</font></td>
672 <td COLSPAN=2 BGCOLOR=#FFFFFF>\\(.+\\)</td></tr>")
674 (defvar mixi-favorite-max-pages nil)
675 (defun mixi-get-favorites ()
677 (let ((ids (mixi-get-matched-items (mixi-favorite-list-page)
678 mixi-favorite-max-pages
679 mixi-favorite-list-id-regexp))
680 (nicks (mixi-get-matched-items (mixi-favorite-list-page)
681 mixi-favorite-max-pages
682 mixi-favorite-list-nick-regexp)))
685 (while (< index (length ids))
686 (setq ret (cons (mixi-make-friend (nth 0 (nth index ids))
687 (nth 0 (nth index nicks))) ret))
692 (defun mixi-make-log (friend time)
693 "Return a log object."
694 (cons 'mixi-log (vector friend time)))
696 (defmacro mixi-log-p (log)
697 `(eq (mixi-object-class ,log) 'mixi-log))
699 (defun mixi-log-friend (log)
700 "Return the friend of LOG."
701 (unless (mixi-log-p log)
702 (signal 'wrong-type-argument (list 'mixi-log-p log)))
705 (defun mixi-log-time (log)
706 "Return the time of LOG."
707 (unless (mixi-log-p log)
708 (signal 'wrong-type-argument (list 'mixi-log-p log)))
711 (defmacro mixi-log-list-page ()
712 `(concat "/show_log.pl"))
714 (defconst mixi-log-list-regexp
715 "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\) <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a><br>")
717 (defvar mixi-log-max-pages 1)
718 (defun mixi-get-logs ()
720 (let ((items (mixi-get-matched-items (mixi-log-list-page)
722 mixi-log-list-regexp)))
723 (mapcar (lambda (item)
724 (mixi-make-log (mixi-make-friend (nth 5 item) (nth 6 item))
725 (encode-time 0 (nth 4 item) (nth 3 item)
726 (nth 2 item) (nth 1 item)
731 (defvar mixi-diary-cache (make-hash-table :test 'equal))
732 (defun mixi-make-diary (owner id)
733 "Return a diary object."
734 (let ((owner (or owner (mixi-make-me))))
735 (mixi-make-cache (list (mixi-friend-id owner) id)
736 (cons 'mixi-diary (vector nil owner id nil nil nil))
739 (defmacro mixi-diary-p (diary)
740 `(eq (mixi-object-class ,diary) 'mixi-diary))
742 (defmacro mixi-diary-page (diary)
743 `(concat "/view_diary.pl?id=" (number-to-string (mixi-diary-id ,diary))
744 "&owner_id=" (number-to-string (mixi-friend-id
745 (mixi-diary-owner ,diary)))))
747 ;; FIXME: Remove `¤µ¤ó'.
748 (defconst mixi-diary-owner-nick-regexp
749 "<td WIDTH=490 background=http://img\\.mixi\\.jp/img/bg_w\\.gif><b><font COLOR=#605048>\\(.+\\)\\(¤µ¤ó\\)?¤ÎÆüµ</font></b></td>")
750 (defconst mixi-diary-time-regexp
751 "<td ALIGN=center ROWSPAN=2 NOWRAP WIDTH=95 bgcolor=#FFD8B0>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)</td>")
752 (defconst mixi-diary-title-regexp
753 "<td BGCOLOR=#FFF4E0 WIDTH=430> \\([^<]+\\)</td></tr>")
754 (defconst mixi-diary-content-regexp
755 "<td CLASS=h12>\\(.+\\)</td></tr>")
757 (defun mixi-diary-realize (diary)
759 ;; FIXME: Check a expiration of cache?
760 (unless (mixi-diary-realize-p diary)
761 (with-mixi-retrieve (mixi-diary-page diary)
762 (if (string-match mixi-diary-owner-nick-regexp buffer)
763 (mixi-friend-set-nick (mixi-diary-owner diary)
764 (match-string 1 buffer))
765 (signal 'error (list 'cannot-find-owner-nick diary)))
766 (if (string-match mixi-diary-time-regexp buffer)
768 diary (encode-time 0 (string-to-number (match-string 5 buffer))
769 (string-to-number (match-string 4 buffer))
770 (string-to-number (match-string 3 buffer))
771 (string-to-number (match-string 2 buffer))
772 (string-to-number (match-string 1 buffer))))
773 (signal 'error (list 'cannot-find-time diary)))
774 (if (string-match mixi-diary-title-regexp buffer)
775 (mixi-diary-set-title diary (match-string 1 buffer))
776 (signal 'error (list 'cannot-find-title diary)))
777 (if (string-match mixi-diary-content-regexp buffer)
778 (mixi-diary-set-content diary (mixi-remove-markup
779 (match-string 1 buffer)))
780 (signal 'error (list 'cannot-find-content diary))))
781 (mixi-diary-touch diary)))
783 (defun mixi-diary-realize-p (diary)
784 "Return the timestamp of DIARY."
785 (unless (mixi-diary-p diary)
786 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
787 (aref (cdr diary) 0))
789 (defun mixi-diary-owner (diary)
790 "Return the owner of DIARY."
791 (unless (mixi-diary-p diary)
792 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
793 (aref (cdr diary) 1))
795 (defun mixi-diary-id (diary)
796 "Return the id of DIARY."
797 (unless (mixi-diary-p diary)
798 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
799 (aref (cdr diary) 2))
801 (defun mixi-diary-time (diary)
802 "Return the time of DIARY."
803 (unless (mixi-diary-p diary)
804 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
805 (mixi-diary-realize diary)
806 (aref (cdr diary) 3))
808 (defun mixi-diary-title (diary)
809 "Return the title of DIARY."
810 (unless (mixi-diary-p diary)
811 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
812 (mixi-diary-realize diary)
813 (aref (cdr diary) 4))
815 (defun mixi-diary-content (diary)
816 "Return the content of DIARY."
817 (unless (mixi-diary-p diary)
818 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
819 (mixi-diary-realize diary)
820 (aref (cdr diary) 5))
822 (defun mixi-diary-touch (diary)
823 "Set the timestamp of DIARY."
824 (unless (mixi-diary-p diary)
825 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
826 (aset (cdr diary) 0 (current-time)))
828 (defun mixi-diary-set-time (diary time)
829 "Set the time of DIARY."
830 (unless (mixi-diary-p diary)
831 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
832 (aset (cdr diary) 3 time))
834 (defun mixi-diary-set-title (diary title)
835 "Set the title of DIARY."
836 (unless (mixi-diary-p diary)
837 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
838 (aset (cdr diary) 4 title))
840 (defun mixi-diary-set-content (diary content)
841 "Set the content of DIARY."
842 (unless (mixi-diary-p diary)
843 (signal 'wrong-type-argument (list 'mixi-diary-p diary)))
844 (aset (cdr diary) 5 content))
846 (defmacro mixi-diary-list-page (&optional friend)
847 `(concat "/list_diary.pl?page=%d"
848 (when ,friend (concat "&id=" (number-to-string
849 (mixi-friend-id ,friend))))))
851 (defconst mixi-diary-list-regexp
852 "<a href=\"view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=[0-9]+\">")
854 (defvar mixi-diary-max-pages nil)
855 (defun mixi-get-diaries (&optional friend)
856 "Get diaries of FRIEND."
857 (unless (or (null friend) (mixi-friend-p friend))
858 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
859 (let ((items (mixi-get-matched-items (mixi-diary-list-page friend)
861 mixi-diary-list-regexp)))
862 (mapcar (lambda (item)
863 (mixi-make-diary friend (nth 0 item)))
866 (defmacro mixi-new-diary-list-page ()
867 `(concat "/new_friend_diary.pl?page=%d"))
869 (defconst mixi-new-diary-list-regexp
870 "<a class=\"new_link\" href=view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=\\([0-9]+\\)>")
872 (defvar mixi-new-diary-max-pages nil)
873 (defun mixi-get-new-diaries ()
875 (let ((items (mixi-get-matched-items (mixi-new-diary-list-page)
876 mixi-new-diary-max-pages
877 mixi-new-diary-list-regexp)))
878 (mapcar (lambda (item)
879 (mixi-make-diary (mixi-make-friend (nth 1 item)) (nth 0 item)))
883 (defvar mixi-community-cache (make-hash-table :test 'equal))
884 (defun mixi-make-community (id &optional name)
885 "Return a community object."
886 (mixi-make-cache id (cons 'mixi-community (vector nil id name nil nil nil
888 mixi-community-cache))
890 (defmacro mixi-community-p (community)
891 `(eq (mixi-object-class ,community) 'mixi-community))
893 (defmacro mixi-community-page (community)
894 `(concat "/view_community.pl?id=" (number-to-string
895 (mixi-community-id ,community))))
897 (defconst mixi-community-nodata-regexp
898 "^¥Ç¡¼¥¿¤¬¤¢¤ê¤Þ¤»¤ó")
899 (defconst mixi-community-name-regexp
900 "<td WIDTH=345>\\(.*\\)</td></tr>")
901 (defconst mixi-community-birthday-regexp
902 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>³«ÀßÆü</font></td>\n<td>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü</td>")
903 ;; FIXME: Care when the owner has seceded.
904 (defconst mixi-community-owner-regexp
905 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>´ÉÍý¿Í</font></td>\n<td>\n\n<a href=\"\\(home\\.pl\\|show_friend\\.pl\\?id=\\([0-9]+\\)\\)\">\n\\(.+\\)</a>")
906 (defconst mixi-community-category-regexp
907 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥«¥Æ¥´¥ê</font></td>\n<td>\\([^<]+\\)</td>")
908 (defconst mixi-community-members-regexp
909 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥á¥ó¥Ð¡¼¿ô</font></td>\n<td>\\([0-9]+\\)¿Í</td></tr>")
910 (defconst mixi-community-open-level-regexp
911 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>»²²Ã¾ò·ï¤È<br>¸ø³«¥ì¥Ù¥ë</font></td>
912 <td>\\(.+\\)</td></tr>")
913 (defconst mixi-community-authority-regexp
914 "<td BGCOLOR=#F2DDB7><font COLOR=#996600>¥È¥Ô¥Ã¥¯ºîÀ®¤Î¸¢¸Â</font></td>\n<td>\\(.+\\)</td></tr>")
915 (defconst mixi-community-description-regexp
916 "<td CLASS=h120>\\(.+\\)</td>")
918 (defun mixi-community-realize (community)
919 "Realize a COMMUNITY."
920 ;; FIXME: Check a expiration of cache?
921 (unless (mixi-community-realize-p community)
922 (with-mixi-retrieve (mixi-community-page community)
923 (if (string-match mixi-community-nodata-regexp buffer)
924 ;; FIXME: Set all members?
925 (mixi-community-set-name community "¥Ç¡¼¥¿¤¬¤¢¤ê¤Þ¤»¤ó")
926 (if (string-match mixi-community-name-regexp buffer)
927 (mixi-community-set-name community (match-string 1 buffer))
928 (signal 'error (list 'cannot-find-name community)))
929 (if (string-match mixi-community-birthday-regexp buffer)
930 (mixi-community-set-birthday
932 (encode-time 0 0 0 (string-to-number (match-string 3 buffer))
933 (string-to-number (match-string 2 buffer))
934 (string-to-number (match-string 1 buffer))))
935 (signal 'error (list 'cannot-find-birthday community)))
936 (if (string-match mixi-community-owner-regexp buffer)
937 (if (string= (match-string 1 buffer) "home.pl")
938 (mixi-community-set-owner community (mixi-make-me))
939 (mixi-community-set-owner
940 community (mixi-make-friend
941 (string-to-number (match-string 2 buffer))
942 (match-string 3 buffer))))
943 (signal 'error (list 'cannot-find-owner community)))
944 (if (string-match mixi-community-category-regexp buffer)
945 (mixi-community-set-category community (match-string 1 buffer))
946 (signal 'error (list 'cannot-find-category community)))
947 (if (string-match mixi-community-members-regexp buffer)
948 (mixi-community-set-members
949 community (string-to-number (match-string 1 buffer)))
950 (signal 'error (list 'cannot-find-members community)))
951 (if (string-match mixi-community-open-level-regexp buffer)
952 (mixi-community-set-open-level community (match-string 1 buffer))
953 (signal 'error (list 'cannot-find-open-level community)))
954 (if (string-match mixi-community-authority-regexp buffer)
955 (mixi-community-set-authority community (match-string 1 buffer))
956 (signal 'error (list 'cannot-find-authority community)))
957 (if (string-match mixi-community-description-regexp buffer)
958 (mixi-community-set-description
959 community (mixi-remove-markup (match-string 1 buffer)))
960 (signal 'error (list 'cannot-find-description community)))))
961 (mixi-community-touch community)))
963 (defun mixi-community-realize-p (community)
964 "Return the timestamp of COMMUNITY."
965 (unless (mixi-community-p community)
966 (signal 'wrong-type-argument (list 'mixi-community-p community)))
967 (aref (cdr community) 0))
969 (defun mixi-community-id (community)
970 "Return the id of COMMUNITY."
971 (unless (mixi-community-p community)
972 (signal 'wrong-type-argument (list 'mixi-community-p community)))
973 (aref (cdr community) 1))
975 (defun mixi-community-name (community)
976 "Return the name of COMMUNITY."
977 (unless (mixi-community-p community)
978 (signal 'wrong-type-argument (list 'mixi-community-p community)))
979 (unless (aref (cdr community) 2)
980 (mixi-community-realize community))
981 (aref (cdr community) 2))
983 (defun mixi-community-birthday (community)
984 "Return the birthday of COMMUNITY."
985 (unless (mixi-community-p community)
986 (signal 'wrong-type-argument (list 'mixi-community-p community)))
987 (mixi-community-realize community)
988 (aref (cdr community) 3))
990 (defun mixi-community-owner (community)
991 "Return the owner of COMMUNITY."
992 (unless (mixi-community-p community)
993 (signal 'wrong-type-argument (list 'mixi-community-p community)))
994 (mixi-community-realize community)
995 (aref (cdr community) 4))
997 (defun mixi-community-category (community)
998 "Return the category of COMMUNITY."
999 (unless (mixi-community-p community)
1000 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1001 (mixi-community-realize community)
1002 (aref (cdr community) 5))
1004 (defun mixi-community-members (community)
1005 "Return the members of COMMUNITY."
1006 (unless (mixi-community-p community)
1007 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1008 (mixi-community-realize community)
1009 (aref (cdr community) 6))
1011 (defun mixi-community-open-level (community)
1012 "Return the open-level of COMMUNITY."
1013 (unless (mixi-community-p community)
1014 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1015 (mixi-community-realize community)
1016 (aref (cdr community) 7))
1018 (defun mixi-community-authority (community)
1019 "Return the authority of COMMUNITY."
1020 (unless (mixi-community-p community)
1021 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1022 (mixi-community-realize community)
1023 (aref (cdr community) 8))
1025 (defun mixi-community-description (community)
1026 "Return the description of COMMUNITY."
1027 (unless (mixi-community-p community)
1028 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1029 (mixi-community-realize community)
1030 (aref (cdr community) 9))
1032 (defun mixi-community-touch (community)
1033 "Set the timestamp of COMMUNITY."
1034 (unless (mixi-community-p community)
1035 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1036 (aset (cdr community) 0 (current-time)))
1038 (defun mixi-community-set-name (community name)
1039 "Set the name of COMMUNITY."
1040 (unless (mixi-community-p community)
1041 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1042 (aset (cdr community) 2 name))
1044 (defun mixi-community-set-birthday (community birthday)
1045 "Set the birthday of COMMUNITY."
1046 (unless (mixi-community-p community)
1047 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1048 (aset (cdr community) 3 birthday))
1050 (defun mixi-community-set-owner (community owner)
1051 "Set the owner of COMMUNITY."
1052 (unless (mixi-community-p community)
1053 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1054 (unless (mixi-friend-p owner)
1055 (signal 'wrong-type-argument (list 'mixi-friend-p owner)))
1056 (aset (cdr community) 4 owner))
1058 (defun mixi-community-set-category (community category)
1059 "Set the category of COMMUNITY."
1060 (unless (mixi-community-p community)
1061 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1062 (aset (cdr community) 5 category))
1064 (defun mixi-community-set-members (community members)
1065 "Set the name of COMMUNITY."
1066 (unless (mixi-community-p community)
1067 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1068 (aset (cdr community) 6 members))
1070 (defun mixi-community-set-open-level (community open-level)
1071 "Set the name of COMMUNITY."
1072 (unless (mixi-community-p community)
1073 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1074 (aset (cdr community) 7 open-level))
1076 (defun mixi-community-set-authority (community authority)
1077 "Set the name of COMMUNITY."
1078 (unless (mixi-community-p community)
1079 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1080 (aset (cdr community) 8 authority))
1082 (defun mixi-community-set-description (community description)
1083 "Set the name of COMMUNITY."
1084 (unless (mixi-community-p community)
1085 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1086 (aset (cdr community) 9 description))
1088 (defmacro mixi-community-list-page (&optional friend)
1089 `(concat "/list_community.pl?page=%d"
1090 (when ,friend (concat "&id=" (number-to-string
1091 (mixi-friend-id ,friend))))))
1093 (defconst mixi-community-list-id-regexp
1094 "<a href=view_community\\.pl\\?id=\\([0-9]+\\)")
1095 (defconst mixi-community-list-name-regexp
1096 "<td valign=middle>\\(.+\\)([0-9]+)</td>")
1098 (defvar mixi-community-max-pages nil)
1099 (defun mixi-get-communities (&optional friend)
1100 "Get communities of FRIEND."
1101 (unless (or (null friend) (mixi-friend-p friend))
1102 (signal 'wrong-type-argument (list 'mixi-friend-p friend)))
1103 (let ((ids (mixi-get-matched-items (mixi-community-list-page friend)
1104 mixi-community-max-pages
1105 mixi-community-list-id-regexp))
1106 (names (mixi-get-matched-items (mixi-community-list-page friend)
1107 mixi-community-max-pages
1108 mixi-community-list-name-regexp)))
1111 (while (< index (length ids))
1112 (setq ret (cons (mixi-make-community (nth 0 (nth index ids))
1113 (nth 0 (nth index names))) ret))
1118 (defvar mixi-topic-cache (make-hash-table :test 'equal))
1119 (defun mixi-make-topic (community id)
1120 "Return a topic object."
1121 (mixi-make-cache (list (mixi-community-id community) id)
1122 (cons 'mixi-topic (vector nil community id nil nil nil nil))
1125 (defmacro mixi-topic-p (topic)
1126 `(eq (mixi-object-class ,topic) 'mixi-topic))
1128 (defmacro mixi-topic-page (topic)
1129 `(concat "/view_bbs.pl?id=" (number-to-string (mixi-topic-id ,topic))
1130 "&comm_id=" (number-to-string
1131 (mixi-community-id (mixi-topic-community ,topic)))))
1133 (defconst mixi-topic-time-regexp
1134 "<td rowspan=\"3\" width=\"110\" bgcolor=\"#ffd8b0\" align=\"center\" valign=\"top\" nowrap>\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)</td>")
1135 (defconst mixi-topic-title-regexp
1136 "<td bgcolor=\"#fff4e0\"> \\([^<]+\\)</td>")
1137 ;; FIXME: Remove `¤µ¤ó'.
1138 (defconst mixi-topic-owner-regexp
1139 "<td bgcolor=\"#fdf9f2\"> <font color=\"#dfb479\"></font> <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)\\(¤µ¤ó\\)?</a>")
1140 (defconst mixi-topic-content-regexp
1141 "<td class=\"h120\"><table><tr>\\(.+\\)?</tr></table>\\(.+\\)</td>")
1143 (defun mixi-topic-realize (topic)
1145 ;; FIXME: Check a expiration of cache?
1146 (unless (mixi-topic-realize-p topic)
1147 (with-mixi-retrieve (mixi-topic-page topic)
1148 (if (string-match mixi-topic-time-regexp buffer)
1149 (mixi-topic-set-time
1150 topic (encode-time 0 (string-to-number (match-string 5 buffer))
1151 (string-to-number (match-string 4 buffer))
1152 (string-to-number (match-string 3 buffer))
1153 (string-to-number (match-string 2 buffer))
1154 (string-to-number (match-string 1 buffer))))
1155 (signal 'error (list 'cannot-find-time topic)))
1156 (if (string-match mixi-topic-title-regexp buffer)
1157 (mixi-topic-set-title topic (match-string 1 buffer))
1158 (signal 'error (list 'cannot-find-title topic)))
1159 (if (string-match mixi-topic-owner-regexp buffer)
1160 (mixi-topic-set-owner topic
1162 (string-to-number (match-string 1 buffer))
1163 (match-string 2 buffer)))
1164 (signal 'error (list 'cannot-find-owner topic)))
1165 (if (string-match mixi-topic-content-regexp buffer)
1166 (mixi-topic-set-content topic (mixi-remove-markup
1167 (match-string 2 buffer)))
1168 (signal 'error (list 'cannot-find-content topic))))
1169 (mixi-topic-touch topic)))
1171 (defun mixi-topic-realize-p (topic)
1172 "Return the timestamp of TOPIC."
1173 (unless (mixi-topic-p topic)
1174 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1175 (aref (cdr topic) 0))
1177 (defun mixi-topic-community (topic)
1178 "Return the community of TOPIC."
1179 (unless (mixi-topic-p topic)
1180 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1181 (aref (cdr topic) 1))
1183 (defun mixi-topic-id (topic)
1184 "Return the id of TOPIC."
1185 (unless (mixi-topic-p topic)
1186 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1187 (aref (cdr topic) 2))
1189 (defun mixi-topic-time (topic)
1190 "Return the time of TOPIC."
1191 (unless (mixi-topic-p topic)
1192 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1193 (mixi-topic-realize topic)
1194 (aref (cdr topic) 3))
1196 (defun mixi-topic-title (topic)
1197 "Return the title of TOPIC."
1198 (unless (mixi-topic-p topic)
1199 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1200 (mixi-topic-realize topic)
1201 (aref (cdr topic) 4))
1203 (defun mixi-topic-owner (topic)
1204 "Return the owner of TOPIC."
1205 (unless (mixi-topic-p topic)
1206 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1207 (mixi-topic-realize topic)
1208 (aref (cdr topic) 5))
1210 (defun mixi-topic-content (topic)
1211 "Return the content of TOPIC."
1212 (unless (mixi-topic-p topic)
1213 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1214 (mixi-topic-realize topic)
1215 (aref (cdr topic) 6))
1217 (defun mixi-topic-touch (topic)
1218 "Set the timestamp of TOPIC."
1219 (unless (mixi-topic-p topic)
1220 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1221 (aset (cdr topic) 0 (current-time)))
1223 (defun mixi-topic-set-time (topic time)
1224 "Set the time of TOPIC."
1225 (unless (mixi-topic-p topic)
1226 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1227 (aset (cdr topic) 3 time))
1229 (defun mixi-topic-set-title (topic title)
1230 "Set the title of TOPIC."
1231 (unless (mixi-topic-p topic)
1232 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1233 (aset (cdr topic) 4 title))
1235 (defun mixi-topic-set-owner (topic owner)
1236 "Set the owner of TOPIC."
1237 (unless (mixi-topic-p topic)
1238 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1239 (unless (mixi-friend-p owner)
1240 (signal 'wrong-type-argument (list 'mixi-friend-p owner)))
1241 (aset (cdr topic) 5 owner))
1243 (defun mixi-topic-set-content (topic content)
1244 "Set the content of TOPIC."
1245 (unless (mixi-topic-p topic)
1246 (signal 'wrong-type-argument (list 'mixi-topic-p topic)))
1247 (aset (cdr topic) 6 content))
1249 (defmacro mixi-topic-list-page (community)
1250 `(concat "/list_bbs.pl?page=%d"
1251 "&id=" (number-to-string (mixi-community-id ,community))))
1253 (defconst mixi-topic-list-regexp
1254 "<a href=view_bbs\\.pl\\?id=\\([0-9]+\\)")
1256 (defvar mixi-topic-max-pages nil)
1257 (defun mixi-get-topics (community)
1258 "Get topics of COMMUNITY."
1259 (unless (mixi-community-p community)
1260 (signal 'wrong-type-argument (list 'mixi-community-p community)))
1261 (let ((items (mixi-get-matched-items (mixi-topic-list-page community)
1262 mixi-topic-max-pages
1263 mixi-topic-list-regexp)))
1264 (mapcar (lambda (item)
1265 (mixi-make-topic community (nth 0 item)))
1268 (defmacro mixi-new-topic-list-page ()
1269 `(concat "/new_bbs.pl?page=%d"))
1271 (defconst mixi-new-topic-list-regexp
1272 "<a href=\"view_bbs\\.pl\\?id=\\([0-9]+\\)&comment_count=[0-9]+&comm_id=\\([0-9]+\\)\" class=\"new_link\">")
1274 (defvar mixi-new-topic-max-pages nil)
1275 (defun mixi-get-new-topics ()
1277 (let ((items (mixi-get-matched-items (mixi-new-topic-list-page)
1278 mixi-new-topic-max-pages
1279 mixi-new-topic-list-regexp)))
1280 (mapcar (lambda (item)
1281 (mixi-make-topic (mixi-make-community (nth 1 item))
1286 (defun mixi-make-comment (parent owner time content)
1287 "Return a comment object."
1288 (cons 'mixi-comment (vector parent owner time content)))
1290 (defmacro mixi-comment-p (comment)
1291 `(eq (mixi-object-class ,comment) 'mixi-comment))
1293 (defun mixi-comment-parent (comment)
1294 "Return the parent of COMMENT."
1295 (unless (mixi-comment-p comment)
1296 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1297 (aref (cdr comment) 0))
1299 (defun mixi-comment-owner (comment)
1300 "Return the owner of COMMENT."
1301 (unless (mixi-comment-p comment)
1302 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1303 (aref (cdr comment) 1))
1305 (defun mixi-comment-time (comment)
1306 "Return the time of COMMENT."
1307 (unless (mixi-comment-p comment)
1308 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1309 (aref (cdr comment) 2))
1311 (defun mixi-comment-content (comment)
1312 "Return the content of COMMENT."
1313 (unless (mixi-comment-p comment)
1314 (signal 'wrong-type-argument (list 'mixi-comment-p comment)))
1315 (aref (cdr comment) 3))
1317 (defun mixi-diary-comment-list-page (diary)
1318 (concat "/view_diary.pl?page=all"
1319 "&id=" (number-to-string (mixi-diary-id diary))
1320 "&owner_id=" (number-to-string
1321 (mixi-friend-id (mixi-diary-owner diary)))))
1323 ;; FIXME: Split regexp to time, owner(id and nick) and contents.
1324 (defconst mixi-diary-comment-list-regexp
1325 "<td rowspan=\"2\" align=\"center\" width=\"95\" bgcolor=\"#f2ddb7\" nowrap>
1326 \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>\\([0-9]+\\):\\([0-9]+\\)\\(<br>
1327 <input type=checkbox name=comment_id value=\".+\">
1330 <td ALIGN=center BGCOLOR=#FDF9F2 WIDTH=430>
1331 <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"410\">
1334 <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a>
1341 <!-- ËÜʸ : start -->
1343 <td bgcolor=\"#ffffff\">
1344 <table BORDER=0 CELLSPACING=0 CELLPADDING=[35] WIDTH=410>
1348 </td></tr></table>")
1350 (defun mixi-topic-comment-list-page (topic)
1351 (concat "/view_bbs.pl?page=all"
1352 "&id=" (number-to-string (mixi-topic-id topic))
1353 "&comm_id=" (number-to-string
1354 (mixi-community-id (mixi-topic-community topic)))))
1356 ;; FIXME: Split regexp to time, owner(id and nick) and contents.
1357 (defconst mixi-topic-comment-list-regexp
1358 "<tr valign=\"top\">
1359 <td rowspan=\"2\" width=\"110\" bgcolor=\"#f2ddb7\" align=\"center\" nowrap>
1360 \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü<br>
1361 \\([0-9]+\\):\\([0-9]+\\)<br>
1363 <td bgcolor=\"#fdf9f2\"> <font color=\"#f8a448\">
1364 <b> [0-9]+</b>:</font>
1366 <a href=\"show_friend\\.pl\\?id=\\([0-9]+\\)\">\\(.+\\)</a>
1373 <td bgcolor=\"#ffffff\" align=\"center\">
1374 <table border=\"0\" cellspacing=\"0\" cellpadding=\"5\" width=\"500\">
1385 (defun mixi-get-comments (parent)
1386 "Get comments of PARENT."
1387 (unless (mixi-object-p parent)
1388 (signal 'wrong-type-argument (list 'mixi-object-p object)))
1389 (let* ((name (mixi-object-name parent))
1390 (list-page (intern (concat mixi-object-prefix name
1391 "-comment-list-page")))
1392 (regexp (eval (intern (concat mixi-object-prefix name
1393 "-comment-list-regexp")))))
1394 (let ((items (mixi-get-matched-items (funcall list-page parent) 1 regexp)))
1395 (mapcar (lambda (item)
1396 (mixi-make-comment parent (mixi-make-friend
1397 (nth 6 item) (nth 7 item))
1398 (encode-time 0 (nth 4 item)
1403 (mixi-remove-markup (nth 8 item))))
1406 (defmacro mixi-new-comment-list-page ()
1407 `(concat "/new_comment.pl?page=%d"))
1409 (defconst mixi-new-comment-list-regexp
1410 "<a href=\"view_diary\\.pl\\?id=\\([0-9]+\\)&owner_id=\\([0-9]+\\)&comment_count=[0-9]+\" class=\"new_link\">")
1412 (defvar mixi-new-comment-max-pages nil)
1413 (defun mixi-get-new-comments ()
1415 (let ((items (mixi-get-matched-items (mixi-new-comment-list-page)
1416 mixi-new-comment-max-pages
1417 mixi-new-comment-list-regexp)))
1418 (mapcar (lambda (item)
1419 (let ((diary (mixi-make-diary
1420 (mixi-make-friend (nth 1 item))
1422 (mixi-get-comments diary)))
1427 ;;; mixi.el ends here