;; mixi.el --- API libraries for accessing to mixi -*- coding: euc-jp -*-
;; Copyright (C) 2005, 2006, 2007, 2008, 2009 OHASHI Akira
;; Author: OHASHI Akira \\(\\|\\)\\(.*\\)さん([0-9]+)\\(\\|\n\\)")
(defconst mixi-friend-name-regexp
"
閲覧に同意された方のみ、先へお進みください。")
(defconst mixi-message-continuously-accessing
"安定してサイトの運営をおこなう為、間隔を空けない連続的なページの遷移・更
新は制限させていただいております。ご迷惑をおかけいたしますが、しばらくお
待ちいただいてから操作をおこなってください。")
(defconst mixi-warning-continuously-accessing
"間隔を空けない連続的なページの遷移・更新を頻繁におこなわれていることが見受けられましたので、一時的に操作を停止させていただきます。申し訳ございませんが、しばらくの間お待ちください。")
(defmacro mixi-retrieve (url &optional post-data)
`(funcall (intern (concat "mixi-" (symbol-name mixi-backend) "-retrieve"))
,url ,post-data))
(defmacro mixi-post-form (url fields)
`(funcall (intern (concat "mixi-" (symbol-name mixi-backend) "-post-form"))
,url ,fields))
(defun mixi-parse-buffer (url buffer &optional post-data)
(when (string-match mixi-message-adult-contents buffer)
(if mixi-accept-adult-contents
(setq buffer (mixi-retrieve url "submit=agree"))
(setq buffer (mixi-retrieve (concat url "?")))))
(when (string-match mixi-warning-continuously-accessing buffer)
(error (mixi-message "Access denied. Please wait a while and increase "
"the value of `mixi-continuously-access-interval'.")))
(if (not (string-match mixi-message-continuously-accessing buffer))
buffer
(message (mixi-message "Waiting for continuously accessing..."))
(sleep-for mixi-continuously-access-interval)
(mixi-retrieve url post-data)))
(defmacro mixi-expand-url (url)
`(if (string-match "^http" ,url)
,url
(concat mixi-url ,url)))
;; FIXME: Support files.
(defun mixi-make-form-data (fields)
"Make form data and return (CONTENT-TYPE . FORM-DATA)."
(let* ((boundary (apply 'format "--_%d_%d_%d" (current-time)))
(content-type (concat "multipart/form-data; boundary=" boundary))
(form-data
(mapconcat
(lambda (field)
(concat "--" boundary "\r\n"
"Content-Disposition: form-data; name=\""
(car field) "\"\r\n"
"\r\n"
(encode-coding-string (cdr field) mixi-coding-system)))
fields "\r\n")))
(cons content-type (concat form-data "\r\n--" boundary "--"))))
(defun mixi-url-retrieve (url &optional post-data extra-headers)
"Retrieve the URL and return gotten strings."
(let* ((url-request-method (if post-data "POST" "GET"))
(url-request-data post-data)
(url-request-extra-headers extra-headers)
(url (mixi-expand-url url))
url-show-status
(buffer (url-retrieve-synchronously url))
ret)
(unless (bufferp buffer)
(error (mixi-message "Cannot retrieve: " url)))
(with-current-buffer buffer
(goto-char (point-min))
(while (looking-at "HTTP/[0-9]+\\.[0-9]+ [13][0-9][0-9]")
(delete-region (point) (re-search-forward "\r?\n\r?\n")))
(unless (looking-at "HTTP/[0-9]+\\.[0-9]+ 200")
(error (mixi-message "Cannot retrieve: " url)))
(delete-region (point) (re-search-forward "\r?\n\r?\n"))
(setq ret (decode-coding-string (buffer-string) mixi-coding-system))
(kill-buffer buffer)
(mixi-parse-buffer url ret post-data))))
(defun mixi-url-post-form (url fields)
(let* ((form-data (mixi-make-form-data fields))
(extra-headers `(("Content-Type" . ,(car form-data)))))
(mixi-url-retrieve url (cdr form-data) extra-headers)))
(defun mixi-w3m-retrieve (url &optional post-data)
"Retrieve the URL and return gotten strings."
(let ((url (mixi-expand-url url)))
(with-temp-buffer
(if (not (string= (w3m-retrieve url nil nil post-data) "text/html"))
(error (mixi-message "Cannot retrieve: " url))
(w3m-decode-buffer url)
(let ((ret (buffer-substring-no-properties (point-min) (point-max))))
(mixi-parse-buffer url ret post-data))))))
(defun mixi-w3m-post-form (url fields)
(let ((form-data (mixi-make-form-data fields)))
(mixi-w3m-retrieve url form-data)))
(defun mixi-curl-retrieve (url &optional post-data form-data)
"Retrieve the URL and return gotten strings."
(with-temp-buffer
(if (fboundp 'set-buffer-multibyte)
(set-buffer-multibyte nil))
(let ((orig-mode (default-file-modes))
(coding-system-for-read 'binary)
(coding-system-for-write 'binary)
process ret)
(unwind-protect
(progn
(set-default-file-modes 448)
(setq process
(apply #'start-process "curl" (current-buffer)
mixi-curl-program
(append (if post-data '("-d" "@-"))
form-data
(list "-i" "-L" "-s"
"-b" mixi-curl-cookie-file
"-c" mixi-curl-cookie-file
(mixi-expand-url url)))))
(set-process-sentinel process #'ignore))
(set-default-file-modes orig-mode))
(when post-data
(process-send-string process (concat post-data "\n"))
(process-send-eof process))
(while (eq (process-status process) 'run)
(accept-process-output process 1))
(goto-char (point-min))
(while (looking-at "HTTP/[0-9]+\\.[0-9]+ [13][0-9][0-9]")
(delete-region (point) (re-search-forward "\r?\n\r?\n")))
(unless (looking-at "HTTP/[0-9]+\\.[0-9]+ 200")
(error (mixi-message "Cannot retrieve: " url)))
(delete-region (point) (re-search-forward "\r?\n\r?\n"))
(setq ret (decode-coding-string (buffer-string) mixi-coding-system))
(mixi-parse-buffer url ret post-data))))
(defun mixi-curl-post-form (url fields)
(let (form-data)
(mapcar (lambda (field)
(push "-F" form-data)
(push (concat (car field) "="
(encode-coding-string (cdr field)
mixi-coding-system))
form-data))
fields)
(mixi-curl-retrieve url nil (reverse form-data))))
(defconst mixi-my-id-regexp
"")
(defmacro with-mixi-retrieve (url &rest body)
`(with-current-buffer (get-buffer-create mixi-temp-buffer-name)
(when ,url
(erase-buffer)
(insert (mixi-retrieve ,url))
(goto-char (point-min))
(when (search-forward mixi-login-form nil t)
(mixi-login)
(erase-buffer)
(insert (mixi-retrieve ,url))))
(goto-char (point-min))
,@body))
(put 'with-mixi-retrieve 'lisp-indent-function 'defun)
(put 'with-mixi-retrieve 'edebug-form-spec '(body))
(defmacro with-mixi-post-form (url fields &rest body)
`(with-current-buffer (get-buffer-create mixi-temp-buffer-name)
(when ,url
(erase-buffer)
(insert (mixi-post-form ,url ,fields))
(goto-char (point-min))
(when (search-forward mixi-login-form nil t)
(mixi-login)
(erase-buffer)
(insert (mixi-post-form ,url ,fields))))
(goto-char (point-min))
,@body))
(put 'with-mixi-post-form 'lisp-indent-function 'defun)
(put 'with-mixi-post-form 'edebug-form-spec '(body))
(defun mixi-get-matched-items (url regexp &optional range reverse)
"Get matched items to REGEXP in URL."
(let ((page 1)
ids)
(catch 'end
(while (and (or (null range) (< (length ids) range))
(or (= page 1) (and (stringp url) (string-match "%d" url))))
(with-mixi-retrieve (when url (format url page))
(let ((func (if reverse (progn
(goto-char (point-max))
're-search-backward)
're-search-forward))
found)
(while (and (funcall func regexp nil t)
(or (null range) (< (length ids) range)))
(let ((num 1)
list)
(while (match-string num)
(setq list (cons (match-string num) list))
(incf num))
(when (not (member (reverse list) ids))
(setq found t)
(setq ids (cons (reverse list) ids)))))
(when (not found)
(throw 'end ids))))
(incf page)))
(reverse ids)))
;; stolen (and modified) from shimbun.el
(defun mixi-remove-markup (string)
"Remove markups from STRING."
(with-temp-buffer
(insert (or string ""))
(save-excursion
(goto-char (point-min))
(while (search-forward "" nil t)
(point-max))))
(goto-char (point-min))
(while (re-search-forward "<[^>]+>" nil t)
(replace-match "" t t))
(goto-char (point-min))
(while (re-search-forward "
" nil t)
(replace-match "\n" t t)))
;; FIXME: Decode entities.
(buffer-string)))
;; stolen (and modified) from w3m.el
(defconst mixi-entity-alist '(("gt" . ">")
("lt" . "<")
("amp" . "&"))
"Alist of html character entities and values.")
;; stolen (and modified) from w3m.el
(defun mixi-encode-specials-string (str)
"Encode special characters in the string STR."
(let ((pos 0)
(buf))
(while (string-match "[<>&]" str pos)
(setq buf
(cons ";"
(cons (car (rassoc (match-string 0 str) mixi-entity-alist))
(cons "&"
(cons (substring str pos (match-beginning 0))
buf))))
pos (match-end 0)))
(if buf
(apply 'concat (nreverse (cons (substring str pos) buf)))
str)))
;; stolen (and modified) from w3m.el
(defun mixi-url-encode-string (string)
(apply (function concat)
(mapcar
(lambda (char)
(cond
((eq char ?\n) ; newline
"%0D%0A")
((string-match "[-a-zA-Z0-9_:/.]" (char-to-string char)) ; xxx?
(char-to-string char)) ; printable
((char-equal char ?\x20) ; space
"+")
(t
(format "%%%02x" char)))) ; escape
;; Coerce a string into a list of chars.
(append (encode-coding-string (or string "") mixi-coding-system)
nil))))
(defun mixi-url-encode-and-quote-percent-string (string)
(let ((string (mixi-url-encode-string string))
(pos 0))
(while (string-match "%" string pos)
(setq string (replace-match "%%" nil nil string))
(setq pos (+ (match-end 0) 1)))
string))
(defun mixi-replace-tab-and-space-to-nbsp (string)
(when mixi-replace-tab-and-space-to-nbsp
(let ((pos 0))
(while (or (string-match "\t" string pos)
(string-match " " string pos))
(if (string= (match-string 0 string) "\t")
(setq string (replace-match (make-string tab-width ?\ )
nil nil string))
(setq string (replace-match " " nil nil string))
(setq pos (+ (match-end 0) 1))))
string)))
;; Object.
(defconst mixi-object-prefix "mixi-")
(defmacro mixi-object-class (object)
`(car-safe ,object))
(defmacro mixi-object-p (object)
`(let ((class (mixi-object-class ,object)))
(when (symbolp class)
(eq (string-match (concat "^" mixi-object-prefix)
(symbol-name class)) 0))))
(defun mixi-object-name (object)
"Return the name of OBJECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(let ((class (mixi-object-class object)))
(substring (symbol-name class) (length mixi-object-prefix))))
(defun mixi-read-object (exp)
"Read one Lisp expression as mixi object."
(if (mixi-object-p exp)
(let ((func (intern (concat mixi-object-prefix "make-"
(mixi-object-name exp))))
(args (mapcar (lambda (arg)
(mixi-read-object arg))
(cdr exp))))
(let ((object (apply func (cdr args))))
(when (car args)
(mixi-object-set-timestamp object (car args)))
object))
exp))
(defun mixi-object-timestamp (object)
"Return the timestamp of OJBECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(aref (cdr object) 0))
(defalias 'mixi-object-realized-p 'mixi-object-timestamp)
(defun mixi-object-owner (object)
"Return the owner of OBJECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(let ((func (intern (concat mixi-object-prefix
(mixi-object-name object) "-owner"))))
(funcall func object)))
(defun mixi-object-id (object)
"Return the id of OBJECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(let ((func (intern (concat mixi-object-prefix
(mixi-object-name object) "-id"))))
(funcall func object)))
(defun mixi-object-time (object)
"Return the time of OBJECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(let ((func (intern (concat mixi-object-prefix
(mixi-object-name object) "-time"))))
(funcall func object)))
(defun mixi-object-title (object)
"Return the title of OBJECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(let ((func (intern (concat mixi-object-prefix
(mixi-object-name object) "-title"))))
(funcall func object)))
(defun mixi-object-content (object)
"Return the content of OBJECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(let ((func (intern (concat mixi-object-prefix
(mixi-object-name object) "-content"))))
(funcall func object)))
(defun mixi-object-set-timestamp (object timestamp)
"Set the timestamp of OBJECT."
(unless (mixi-object-p object)
(signal 'wrong-type-argument (list 'mixi-object-p object)))
(aset (cdr object) 0 timestamp))
(defmacro mixi-object-touch (object)
`(mixi-object-set-timestamp ,object (current-time)))
(defconst mixi-object-url-regexp
"/\\(show\\|view\\)_\\([a-z]+\\)\\.pl")
(defun mixi-make-object-from-url (url)
"Return a mixi object from URL."
(if (string-match mixi-object-url-regexp url)
(let ((name (match-string 2 url)))
(cond ((string= name "bbs")
(setq name "topic"))
((string= name "profile")
(setq name "friend")))
(let ((func (intern (concat mixi-object-prefix "make-" name
"-from-url"))))
(funcall func url)))
(when (string-match "/home\\.pl" url)
(mixi-make-friend-from-url url))))
;; Cache.
;; stolen from time-date.el
(defun mixi-time-less-p (t1 t2)
"Say whether time value T1 is less than time value T2."
(unless (numberp (cdr t1))
(setq t1 (cons (car t1) (car (cdr t1)))))
(unless (numberp (cdr t2))
(setq t2 (cons (car t2) (car (cdr t2)))))
(or (< (car t1) (car t2))
(and (= (car t1) (car t2))
(< (cdr t1) (cdr t2)))))
(defun mixi-time-add (t1 t2)
"Add two time values. One should represent a time difference."
(unless (numberp (cdr t1))
(setq t1 (cons (car t1) (car (cdr t1)))))
(unless (numberp (cdr t2))
(setq t2 (cons (car t2) (car (cdr t2)))))
(let ((low (+ (cdr t1) (cdr t2))))
(cons (+ (car t1) (car t2) (lsh low -16)) low)))
;; stolen from time-date.el
(defun mixi-seconds-to-time (seconds)
"Convert SECONDS (a floating point number) to a time value."
(cons (floor seconds 65536)
(floor (mod seconds 65536))))
(defun mixi-cache-expired-p (object)
"Whether a cache of OBJECT is expired."
(let ((timestamp (mixi-object-timestamp object)))
(unless (or (null mixi-cache-expires)
(null timestamp))
(if (numberp mixi-cache-expires)
(mixi-time-less-p
(mixi-time-add timestamp (mixi-seconds-to-time mixi-cache-expires))
(current-time))
t))))
(defun mixi-make-cache (key value table)
"Make a cache object and return it."
(let ((cache (gethash key table)))
(if (and cache (not (mixi-cache-expired-p cache)))
cache
(puthash key value table))))
(defconst mixi-cache-file-regexp "[a-z]+-cache$")
(defconst mixi-cache-regexp (concat mixi-object-prefix
mixi-cache-file-regexp))
(defun mixi-save-cache ()
(let ((cache-directory (expand-file-name "cache" mixi-directory)))
(unless (file-directory-p cache-directory)
(make-directory cache-directory t))
(let ((caches (apropos-internal mixi-cache-regexp 'boundp)))
(while caches
(with-temp-file (expand-file-name
(substring (symbol-name (car caches))
(length mixi-object-prefix))
cache-directory)
(let ((coding-system-for-write mixi-coding-system)
(cache (symbol-value (car caches))))
(insert "#s(hash-table size "
(number-to-string (hash-table-count cache))
" test equal data (\n")
(maphash
(lambda (key value)
(let (print-level print-length)
(insert (prin1-to-string key) " "
(prin1-to-string value) "\n")))
cache))
(insert "))"))
(setq caches (cdr caches))))))
;; stolen (and modified) from lsdb.el
(defun mixi-read-cache (&optional marker)
"Read one Lisp expression as text from MARKER, return as Lisp object."
(save-excursion
(goto-char marker)
(if (looking-at "^#s(")
(let ((end-marker
(progn
(forward-char 2);skip "#s"
(forward-sexp);move to the left paren
(point-marker))))
(with-temp-buffer
(buffer-disable-undo)
(insert-buffer-substring (marker-buffer marker)
marker end-marker)
(goto-char (point-min))
(delete-char 2)
(let ((object (read (current-buffer)))
data)
(if (eq 'hash-table (car object))
(progn
(setq data (plist-get (cdr object) 'data))
(while data
(pop data);throw it away
(mixi-read-object (pop data))))
object))))
(read marker))))
(defun mixi-load-cache ()
(let ((cache-directory (expand-file-name "cache" mixi-directory)))
(when (file-directory-p cache-directory)
;; FIXME: Load friend and community first.
(let ((files (directory-files cache-directory t
mixi-cache-file-regexp)))
(while files
(let ((buffer (find-file-noselect (car files))))
(unwind-protect
(save-excursion
(set-buffer buffer)
(goto-char (point-min))
(re-search-forward "^#s(")
(goto-char (match-beginning 0))
(mixi-read-cache (point-marker)))
(kill-buffer buffer)))
(setq files (cdr files)))))))
;; Friend object.
(defvar mixi-friend-cache (make-hash-table :test 'equal))
(defun mixi-make-friend (id &optional nick name sex address age birthday
blood-type birthplace hobby job organization
profile)
"Return a friend object."
(mixi-make-cache id (cons 'mixi-friend (vector nil id nick name sex address
age birthday blood-type
birthplace hobby job
organization profile))
mixi-friend-cache))
(defun mixi-make-me ()
"Return a my object."
(unless mixi-me
(with-mixi-retrieve "/home.pl"
(if (re-search-forward mixi-my-id-regexp nil t)
(setq mixi-me (mixi-make-friend (match-string 1)))
(signal 'error (list 'who-am-i)))))
mixi-me)
(defconst mixi-friend-url-regexp
"/show_\\(friend\\|profile\\)\\.pl\\?id=\\([0-9]+\\)")
(defun mixi-make-friend-from-url (url)
"Return a friend object from URL."
(if (string-match mixi-friend-url-regexp url)
(let ((id (match-string 2 url)))
(mixi-make-friend id))
(when (string-match "/home\\.pl" url)
(mixi-make-me))))
(defmacro mixi-friend-p (friend)
`(eq (mixi-object-class ,friend) 'mixi-friend))
(defmacro mixi-friend-page (friend)
`(concat "/show_friend.pl?id=" (mixi-friend-id ,friend)))
(defconst mixi-friend-nick-regexp
"名前 \n+?\\(.+?\\)\\(\\)")
(defconst mixi-friend-sex-regexp
" 性別 \n+?\\([男女]\\)性\\(\\)")
(defconst mixi-friend-address-regexp
" 現住所 \n+?\\(.+?\\)\\(\\)")
(defconst mixi-friend-age-regexp
" 年齢 \n+?\\([0-9]+\\)歳\\( \\)")
(defconst mixi-friend-birthday-regexp
" 誕生日 \n+?\\([0-9]+\\)月\\([0-9]+\\)日\\(\\)")
(defconst mixi-friend-blood-type-regexp
" 血液型 \n+?\\([ABO]B?\\)型\\(\\)")
(defconst mixi-friend-birthplace-regexp
" 出身地 \n+?\\(.+?\\)\\(\\)")
(defconst mixi-friend-hobby-regexp
" 趣味 \n+?\\(.+?\\)\\(\\)")
(defconst mixi-friend-job-regexp
" 職業 \n+?\\(.+?\\)\\(\\)")
(defconst mixi-friend-organization-regexp
" 所属 \n+?\\(.+?\\)\\(\\)")
(defconst mixi-friend-profile-regexp
" 自己紹介 \n+?\\(.+?\\) ")
(defun mixi-realize-friend (friend)
"Realize a FRIEND."
;; FIXME: Check an expiration of cache?
(unless (mixi-object-realized-p friend)
(with-mixi-retrieve (if (equal friend mixi-me)
"/home.pl"
(mixi-friend-page friend))
(let ((case-fold-search t))
(if (re-search-forward mixi-friend-nick-regexp nil t)
(mixi-friend-set-nick friend (match-string 2))
(mixi-realization-error 'cannot-find-nick friend))
(when (re-search-forward mixi-friend-name-regexp nil t)
(mixi-friend-set-name friend (match-string 1)))
(when (re-search-forward mixi-friend-sex-regexp nil t)
(mixi-friend-set-sex friend (if (string= (match-string 1) "男")
'male 'female)))
(when (re-search-forward mixi-friend-address-regexp nil t)
(mixi-friend-set-address friend (match-string 1)))
(when (re-search-forward mixi-friend-age-regexp nil t)
(mixi-friend-set-age friend (string-to-number (match-string 1))))
(when (re-search-forward mixi-friend-birthday-regexp nil t)
(mixi-friend-set-birthday
friend (list (string-to-number (match-string 1))
(string-to-number (match-string 2)))))
(when (re-search-forward mixi-friend-blood-type-regexp nil t)
(mixi-friend-set-blood-type friend (intern (match-string 1))))
(when (re-search-forward mixi-friend-birthplace-regexp nil t)
(mixi-friend-set-birthplace friend (match-string 1)))
(when (re-search-forward mixi-friend-hobby-regexp nil t)
(mixi-friend-set-hobby friend (split-string (match-string 1) ", ")))
(when (re-search-forward mixi-friend-job-regexp nil t)
(mixi-friend-set-job friend (match-string 1)))
(when (re-search-forward mixi-friend-organization-regexp nil t)
(mixi-friend-set-organization friend (match-string 1)))
(when (re-search-forward mixi-friend-profile-regexp nil t)
(mixi-friend-set-profile friend (match-string 1)))))
(mixi-object-touch friend)))
(defun mixi-friend-id (friend)
"Return the id of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aref (cdr friend) 1))
(defun mixi-friend-nick (friend)
"Return the nick of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(unless (aref (cdr friend) 2)
(mixi-realize-friend friend))
(aref (cdr friend) 2))
(defun mixi-friend-name (friend)
"Return the name of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 3))
(defun mixi-friend-sex (friend)
"Return the sex of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 4))
(defun mixi-friend-address (friend)
"Return the address of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 5))
(defun mixi-friend-age (friend)
"Return the age of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 6))
(defun mixi-friend-birthday (friend)
"Return the birthday of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 7))
(defun mixi-friend-blood-type (friend)
"Return the blood type of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 8))
(defun mixi-friend-birthplace (friend)
"Return the birthplace of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 9))
(defun mixi-friend-hobby (friend)
"Return the hobby of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 10))
(defun mixi-friend-job (friend)
"Return the job of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 11))
(defun mixi-friend-organization (friend)
"Return the organization of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 12))
(defun mixi-friend-profile (friend)
"Return the profile of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(mixi-realize-friend friend)
(aref (cdr friend) 13))
(defun mixi-friend-set-nick (friend nick)
"Set the nick of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 2 nick))
(defun mixi-friend-set-name (friend name)
"Set the name of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 3 name))
(defun mixi-friend-set-sex (friend sex)
"Set the sex of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 4 sex))
(defun mixi-friend-set-address (friend address)
"Set the address of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 5 address))
(defun mixi-friend-set-age (friend age)
"Set the age of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 6 age))
(defun mixi-friend-set-birthday (friend birthday)
"Set the birthday of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 7 birthday))
(defun mixi-friend-set-blood-type (friend blood-type)
"Set the blood type of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 8 blood-type))
(defun mixi-friend-set-birthplace (friend birthplace)
"Set the birthplace of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 9 birthplace))
(defun mixi-friend-set-hobby (friend hobby)
"Set the hobby of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 10 hobby))
(defun mixi-friend-set-job (friend job)
"Set the job of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 11 job))
(defun mixi-friend-set-organization (friend organization)
"Set the organization of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 12 organization))
(defun mixi-friend-set-profile (friend profile)
"Set the profile of FRIEND."
(unless (mixi-friend-p friend)
(signal 'wrong-type-argument (list 'mixi-friend-p friend)))
(aset (cdr friend) 13 profile))
(defun mixi-my-friend-list-page (&optional dummy)
(concat "/list_friend_simple.pl?page=%d"))
(defun mixi-friend-list-page (friend)
(concat "/list_friend.pl?page=%d&id=" (mixi-friend-id friend)))
(defconst mixi-friend-list-id-regexp
"\\(.+\\)([0-9]+)
\\(.*\\)
\\(.+\\)
") (defun mixi-realize-community (community) "Realize a COMMUNITY." ;; FIXME: Check an expiration of cache? (unless (mixi-object-realized-p community) (with-mixi-retrieve (mixi-community-page community) (let ((case-fold-search t)) (if (re-search-forward mixi-community-nodata-regexp nil t) ;; FIXME: Set all members? (mixi-community-set-name community "データがありません") (if (re-search-forward mixi-community-name-regexp nil t) (mixi-community-set-name community (match-string 1)) (mixi-realization-error 'cannot-find-name community)) (if (re-search-forward mixi-community-birthday-regexp nil t) (mixi-community-set-birthday community (encode-time 0 0 0 (string-to-number (match-string 3)) (string-to-number (match-string 2)) (string-to-number (match-string 1)))) (mixi-realization-error 'cannot-find-birthday community)) (if (re-search-forward mixi-community-owner-regexp nil t) (if (string= (match-string 1) "home.pl") (mixi-community-set-owner community (mixi-make-me)) (mixi-community-set-owner community (mixi-make-friend (match-string 2) (match-string 3)))) (mixi-realization-error 'cannot-find-owner community)) (if (re-search-forward mixi-community-category-regexp nil t) (mixi-community-set-category community (match-string 1)) (mixi-realization-error 'cannot-find-category community)) (if (re-search-forward mixi-community-members-regexp nil t) (mixi-community-set-members community (string-to-number (match-string 1))) (mixi-realization-error 'cannot-find-members community)) (if (re-search-forward mixi-community-open-level-regexp nil t) (mixi-community-set-open-level community (match-string 1)) (mixi-realization-error 'cannot-find-open-level community)) (if (re-search-forward mixi-community-authority-regexp nil t) (mixi-community-set-authority community (match-string 1)) (mixi-realization-error 'cannot-find-authority community)) (if (re-search-forward mixi-community-description-regexp nil t) (mixi-community-set-description community (match-string 1)) (mixi-realization-error 'cannot-find-description community))))) (mixi-object-touch community))) (defun mixi-community-id (community) "Return the id of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aref (cdr community) 1)) (defun mixi-community-name (community) "Return the name of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (unless (aref (cdr community) 2) (mixi-realize-community community)) (aref (cdr community) 2)) (defun mixi-community-birthday (community) "Return the birthday of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (mixi-realize-community community) (aref (cdr community) 3)) (defun mixi-community-owner (community) "Return the owner of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (mixi-realize-community community) (aref (cdr community) 4)) (defun mixi-community-category (community) "Return the category of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (mixi-realize-community community) (aref (cdr community) 5)) (defun mixi-community-members (community) "Return the members of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (mixi-realize-community community) (aref (cdr community) 6)) (defun mixi-community-open-level (community) "Return the open-level of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (mixi-realize-community community) (aref (cdr community) 7)) (defun mixi-community-authority (community) "Return the authority of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (mixi-realize-community community) (aref (cdr community) 8)) (defun mixi-community-description (community) "Return the description of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (mixi-realize-community community) (aref (cdr community) 9)) (defun mixi-community-set-name (community name) "Set the name of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aset (cdr community) 2 name)) (defun mixi-community-set-birthday (community birthday) "Set the birthday of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aset (cdr community) 3 birthday)) (defun mixi-community-set-owner (community owner) "Set the owner of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (unless (mixi-friend-p owner) (signal 'wrong-type-argument (list 'mixi-friend-p owner))) (aset (cdr community) 4 owner)) (defun mixi-community-set-category (community category) "Set the category of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aset (cdr community) 5 category)) (defun mixi-community-set-members (community members) "Set the name of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aset (cdr community) 6 members)) (defun mixi-community-set-open-level (community open-level) "Set the name of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aset (cdr community) 7 open-level)) (defun mixi-community-set-authority (community authority) "Set the name of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aset (cdr community) 8 authority)) (defun mixi-community-set-description (community description) "Set the name of COMMUNITY." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (aset (cdr community) 9 description)) (defmacro mixi-community-list-page (&optional friend) `(concat "/list_community.pl?page=%d" (when ,friend (concat "&id=" (mixi-friend-id ,friend))))) (defconst mixi-community-list-id-regexp "\\(.+\\)([0-9]+)") ;;;###autoload (defun mixi-get-communities (&rest friend-or-range) "Get communities of FRIEND." (when (> (length friend-or-range) 2) (signal 'wrong-number-of-arguments (list 'mixi-get-communities (length friend-or-range)))) (let ((friend (nth 0 friend-or-range)) (range (nth 1 friend-or-range))) (when (or (not (mixi-friend-p friend)) (mixi-friend-p range)) (setq friend (nth 1 friend-or-range)) (setq range (nth 0 friend-or-range))) (unless (or (null friend) (mixi-friend-p friend)) (signal 'wrong-type-argument (list 'mixi-friend-p friend))) (let ((ids (mixi-get-matched-items (mixi-community-list-page friend) mixi-community-list-id-regexp range)) (names (mixi-get-matched-items (mixi-community-list-page friend) mixi-community-list-name-regexp range))) (let ((index 0) ret) (while (< index (length ids)) (setq ret (cons (mixi-make-community (nth 0 (nth index ids)) (nth 0 (nth index names))) ret)) (incf index)) (reverse ret))))) (defmacro mixi-search-community-list-page (keyword) `(concat "/search_community.pl?page=%d&&sort=date&type=com&submit=main" "&keyword=" (mixi-url-encode-and-quote-percent-string ,keyword) "&category_id=0")) (defconst mixi-search-community-list-regexp "