;; mixi.el --- API libraries for accessing to mixi -*- coding: euc-jp -*-
;; Copyright (C) 2005, 2006, 2007, 2008 OHASHI Akira
;; Author: OHASHI Akira
閲覧に同意された方のみ、先へお進みください。")
(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))
;; 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_profile.pl?id=" (mixi-friend-id ,friend)))
(defconst mixi-friend-nick-regexp
"\\(.*\\)さん([0-9]+)
")
(defconst mixi-friend-name-regexp
"
\\(.*\\)
\\(.+\\)
") (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 "※申し訳ありませんが、このニュースは掲載期間が終了したか、URLが間違っているためご覧いただけません。詳しくはこちらをご覧ください。
") (defconst mixi-news-title-regexp "(\\(.+\\) - \\([0-9]+\\)月\\([0-9]+\\)日 \\([0-9]+\\):\\([0-9]+\\))
") (defconst mixi-news-content-regexp "・ \\(.+\\)
\\(.+\\) |