X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=mixi.el;h=789ba27b8d75be993f3ed4c35109b72f4b36ced5;hb=6eb8092c58724e9bc319322b3fab924ad7aa10ed;hp=a0a4e43c26fb294d995489fa519dfa340488e778;hpb=a1c4e96bb540a10e4c5849105f91b8ea9d46aa29;p=elisp%2Fmixi.git diff --git a/mixi.el b/mixi.el index a0a4e43..789ba27 100644 --- a/mixi.el +++ b/mixi.el @@ -31,13 +31,24 @@ ;; * mixi-get-logs ;; * mixi-get-diaries ;; * mixi-get-new-diaries +;; * mixi-search-diaries ;; * mixi-get-communities -;; * mixi-get-topics -;; * mixi-get-new-topics +;; * mixi-search-communities +;; * mixi-get-bbses +;; * mixi-get-new-bbses +;; * mixi-search-bbses ;; * mixi-get-comments ;; * mixi-get-new-comments ;; * mixi-get-messages ;; * mixi-get-introductions +;; * mixi-get-news +;; +;; API for posting: +;; +;; * mixi-post-diary +;; * mixi-post-topic +;; * mixi-post-comment +;; * mixi-post-message ;; ;; Utilities: ;; @@ -47,14 +58,12 @@ ;; ;; Display newest 3 diaries like a mail format. ;; -;; (let ((max-numbers 3) -;; (buffer (generate-new-buffer "*temp*")) +;; (let ((range 3) +;; (buffer (get-buffer-create "*temp*")) ;; (format "%Y/%m/%d %H:%M")) ;; (pop-to-buffer buffer) ;; (mapc (lambda (diary) ;; (let ((subject (mixi-diary-title diary)) -;; ;; Don't get owner's nick at first for omitting a useless -;; ;; retrieval. ;; (from (mixi-friend-nick (mixi-diary-owner diary))) ;; (date (format-time-string format (mixi-diary-time diary))) ;; (body (mixi-remove-markup (mixi-diary-content diary)))) @@ -62,7 +71,7 @@ ;; "Subject: " subject "\n" ;; "Date: " date "\n\n" ;; body "\n\n"))) -;; (mixi-get-new-diaries max-numbers)) +;; (mixi-get-new-diaries range)) ;; (set-buffer-modified-p nil) ;; (setq buffer-read-only t) ;; (goto-char (point-min))) @@ -70,14 +79,12 @@ ;; Display newest 3 diaries including newest 3 comments like a mail format. ;; Comments are displayed like a reply mail. ;; -;; (let ((max-numbers 3) -;; (buffer (generate-new-buffer "*temp*")) +;; (let ((range 3) +;; (buffer (get-buffer-create "*temp*")) ;; (format "%Y/%m/%d %H:%M")) ;; (pop-to-buffer buffer) ;; (mapc (lambda (diary) ;; (let ((subject (mixi-diary-title diary)) -;; ;; Don't get owner's nick at first for omitting a useless -;; ;; retrieval. ;; (from (mixi-friend-nick (mixi-diary-owner diary))) ;; (date (format-time-string format (mixi-diary-time diary))) ;; (body (mixi-remove-markup (mixi-diary-content diary)))) @@ -97,12 +104,17 @@ ;; "Subject: " subject "\n" ;; "Date: " date "\n\n" ;; body "\n\n"))) -;; (mixi-get-comments diary max-numbers)))) -;; (mixi-get-new-diaries max-numbers)) +;; (mixi-get-comments diary range)))) +;; (mixi-get-new-diaries range)) ;; (set-buffer-modified-p nil) ;; (setq buffer-read-only t) ;; (goto-char (point-min))) +;; Bug reports: +;; +;; If you have bug reports and/or suggestions for improvement, please +;; send them via . + ;;; Code: (eval-when-compile (require 'cl)) @@ -116,6 +128,11 @@ :type 'string :group 'mixi) +(defcustom mixi-directory (expand-file-name "~/.mixi") + "*Where to look for mixi files." + :type 'directory + :group 'mixi) + (defcustom mixi-coding-system 'euc-jp "*Coding system for mixi." :type 'coding-system @@ -126,32 +143,33 @@ :type 'file :group 'mixi) -(defcustom mixi-curl-cookie-file (expand-file-name "~/.mixi-cookies.txt") +(defcustom mixi-curl-cookie-file (expand-file-name "cookies.txt" + mixi-directory) "*The location of cookie file created by `curl'." :type 'file :group 'mixi) -(defcustom mixi-retrieve-function +(defcustom mixi-backend (or (condition-case nil (progn - (require 'url) - (if (fboundp 'url-retrieve-synchronously) - 'mixi-w3-retrieve)) + (require 'w3m) + 'w3m) (error)) (condition-case nil (progn - (require 'w3m) - 'mixi-w3m-retrieve) + (require 'url) + (if (fboundp 'url-retrieve-synchronously) + 'url)) (error)) (if (and (fboundp 'executable-find) (executable-find mixi-curl-program)) - 'mixi-curl-retrieve) - (error "Can't set `mixi-retrieve-function'")) - "*The function for retrieving." - :type '(radio (const :tag "Using w3" mixi-w3-retrieve) - (const :tag "Using w3m" mixi-w3m-retrieve) - (const :tag "Using curl" mixi-curl-retrieve) - (function :format "Other function: %v\n" :size 0)) + 'curl) + (error "Cannot set `mixi-backend'.")) + "*The backend for accessing to mixi." + :type '(radio (const :tag "Use emacs-w3m" w3m) + (const :tag "Use url.el" url) + (const :tag "Use curl" curl) + (symbol :tag "The other backend")) :group 'mixi) (defcustom mixi-default-email nil @@ -177,7 +195,7 @@ Increase this value when unexpected error frequently occurs." :type 'number :group 'mixi) -(defcustom mixi-cache-expires 3600 +(defcustom mixi-cache-expires nil "*Seconds for expiration of a cached object." :type '(radio (integer :tag "Expired seconds") (const :tag "Don't expire" nil) @@ -190,16 +208,35 @@ Increase this value when unexpected error frequently occurs." :type 'boolean :group 'mixi) -(defcustom mixi-cache-directory (expand-file-name "~/.mixi") - "*Where to look for cache files." - :type 'directory - :group 'mixi) - (defvar mixi-me nil) ;; Utilities. -(defmacro mixi-message (string) - `(concat "[mixi] " ,string)) +(defmacro mixi-message (&rest strings) + `(concat "[mixi] " ,@strings)) + +(put 'mixi-realization-error + 'error-message (mixi-message "Cannot realize object")) +(put 'mixi-realization-error + 'error-conditions '(mixi-realization-error error)) + +(put 'mixi-post-error + 'error-message (mixi-message "Cannot post")) +(put 'mixi-post-error + 'error-conditions '(mixi-post-error error)) + +(defmacro mixi-realization-error (type object) + `(let ((data (if (and (boundp 'buffer) debug-on-error) + (list ,type ,object buffer) + (list ,type ,object)))) + (signal 'mixi-realization-error data))) + +(defmacro mixi-post-error (type &optional object) + `(let ((data (when (and (boundp 'buffer) debug-on-error) + (list buffer)))) + (if ,object + (setq data (cons ,type (cons ,object data))) + (setq data (cons ,type data))) + (signal 'mixi-post-error data))) (defconst mixi-message-adult-contents "¤³¤Î¥Ú¡¼¥¸¤«¤éÀè¤Ï¥¢¥À¥ë¥È¡ÊÀ®¿Í¸þ¤±¡Ë¥³¥ó¥Æ¥ó¥Ä¤¬´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£
@@ -213,53 +250,74 @@ Increase this value when unexpected error frequently occurs." ¼õ¤±¤é¤ì¤Þ¤·¤¿¤Î¤Ç¡¢°ì»þŪ¤ËÁàºî¤òÄä»ß¤µ¤»¤Æ¤¤¤¿¤À¤­¤Þ¤¹¡£¿½¤·Ìõ¤´¤¶¤¤¤Þ
¤»¤ó¤¬¡¢¤·¤Ð¤é¤¯¤Î´Ö¤ªÂÔ¤Á¤¯¤À¤µ¤¤¡£") -(defun mixi-retrieve-1 (buffer url &optional post-data) +(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 (funcall mixi-retrieve-function url "submit=agree")) - (setq buffer (funcall mixi-retrieve-function (concat url "?"))))) + (setq buffer (mixi-retrieve url "submit=agree")) + (setq buffer (mixi-retrieve (concat url "?"))))) (when (string-match mixi-warning-continuously-accessing buffer) - (error (mixi-message "Continuously accessing"))) + (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...")) - (sit-for mixi-continuously-access-interval) - (funcall mixi-retrieve-function url post-data))) + (sleep-for mixi-continuously-access-interval) + (mixi-retrieve url post-data))) (defmacro mixi-expand-url (url) - `(if (string-match (concat "^" mixi-url) ,url) + `(if (string-match "^http" ,url) ,url (concat mixi-url ,url))) -(defun mixi-w3-retrieve (url &optional post-data) +;; FIXME: Support file, checkbox and so on. +(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." - (if post-data - (progn - (setq url-request-method "POST") - (setq url-request-data post-data)) - (setq url-request-method "GET") - (setq url-request-data nil)) - (let* ((url (mixi-expand-url url)) + (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)) (buffer (url-retrieve-synchronously url)) ret) (unless (bufferp buffer) (error (mixi-message "Cannot retrieve"))) (with-current-buffer buffer (goto-char (point-min)) - (if (re-search-forward "HTTP/[0-9.]+ 302 Moved" nil t) - (if (re-search-forward - (concat "Location: " mixi-url "\\(.+\\)") nil t) - (setq ret (mixi-w3-retrieve (match-string 1) post-data)) - (setq ret (mixi-w3-retrieve "/home.pl" post-data))) - (unless (re-search-forward "HTTP/[0-9.]+ 200 OK" nil t) - (error (mixi-message "Cannot retrieve"))) - (search-forward "\n\n") - (setq ret (mm-decode-coding-string - (buffer-substring-no-properties (point) (point-max)) - mixi-coding-system)) - (kill-buffer buffer) - (setq ret (mixi-retrieve-1 ret url post-data)))) - ret)) + (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"))) + (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." @@ -269,7 +327,11 @@ Increase this value when unexpected error frequently occurs." (error (mixi-message "Cannot retrieve")) (w3m-decode-buffer url) (let ((ret (buffer-substring-no-properties (point-min) (point-max)))) - (mixi-retrieve-1 ret url post-data)))))) + (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) "Retrieve the URL and return gotten strings." @@ -305,71 +367,82 @@ Increase this value when unexpected error frequently occurs." (error (mixi-message "Cannot retrieve"))) (delete-region (point) (re-search-forward "\r?\n\r?\n")) (setq ret (decode-coding-string (buffer-string) mixi-coding-system)) - (mixi-retrieve-1 ret url post-data)))) + (mixi-parse-buffer url ret post-data)))) (defconst mixi-my-id-regexp "" + buffer) (mixi-login) - (setq buffer (funcall mixi-retrieve-function ,url)))) + (setq buffer (mixi-retrieve ,url)))) ,@body)) (put 'with-mixi-retrieve 'lisp-indent-function 'defun) (put 'with-mixi-retrieve 'edebug-form-spec '(form body)) -(defun mixi-get-matched-items (url max-numbers regexp) +(defmacro with-mixi-post-form (url fields &rest body) + `(let (buffer) + (when ,url + (setq buffer (mixi-post-form ,url ,fields)) + (when (string-match "
" + buffer) + (mixi-login) + (setq buffer (mixi-post-form ,url ,fields)))) + ,@body)) +(put 'with-mixi-post-form 'lisp-indent-function 'defun) +(put 'with-mixi-post-form 'edebug-form-spec '(form body)) + +(defun mixi-get-matched-items (url regexp &optional range) "Get matched items to REGEXP in URL." (let ((page 1) ids) (catch 'end - (while (or (null max-numbers) (< (length ids) max-numbers)) + (while (or (null range) (< (length ids) range)) (with-mixi-retrieve (format url page) - (let ((pos 0)) + (let ((pos 0) + found) (while (and (string-match regexp buffer pos) - (or (null max-numbers) (< (length ids) max-numbers))) + (or (null range) (< (length ids) range))) (let ((num 1) list) (while (match-string num buffer) (setq list (cons (match-string num buffer) list)) (incf num)) - (when (member (reverse list) ids) - (throw 'end ids)) - (setq ids (cons (reverse list) ids)) + (when (not (member (reverse list) ids)) + (setq found t) + (setq ids (cons (reverse list) ids))) (setq pos (match-end (1- num))))) - (when (eq pos 0) + (when (not found) (throw 'end ids)))) (incf page))) - ;; FIXME: Sort? Now order by newest. (reverse ids))) ;; stolen (and modified) from shimbun.el @@ -392,50 +465,24 @@ Increase this value when unexpected error frequently occurs." ;; FIXME: Decode entities. (buffer-string))) -;; 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)))) +;; stolen (and modified) from w3m.el +;; FIXME: Hmm. +(defun mixi-url-encode-and-quote-percent-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)))) ;; Object. (defconst mixi-object-prefix "mixi-") @@ -444,8 +491,10 @@ Increase this value when unexpected error frequently occurs." `(car-safe ,object)) (defmacro mixi-object-p (object) - `(eq (string-match (concat "^" mixi-object-prefix) - (symbol-name (mixi-object-class ,object))) 0)) + `(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." @@ -454,12 +503,26 @@ Increase this value when unexpected error frequently occurs." (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-realize-p 'mixi-object-timestamp) +(defalias 'mixi-object-realized-p 'mixi-object-timestamp) (defun mixi-object-owner (object) "Return the owner of OBJECT." @@ -525,12 +588,135 @@ Increase this value when unexpected error frequently occurs." (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))) + (mapc (lambda (symbol) + (with-temp-file (expand-file-name + (substring (symbol-name symbol) + (length mixi-object-prefix)) + cache-directory) + (let ((coding-system-for-write mixi-coding-system) + (cache (symbol-value symbol))) + (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 "))"))) + 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))) + (mapc (lambda (file) + (let ((buffer (find-file-noselect file))) + (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)))) + files))))) + ;; Friend object. (defvar mixi-friend-cache (make-hash-table :test 'equal)) -(defun mixi-make-friend (id &optional nick) +(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 nil nil nil nil - nil nil nil nil nil nil nil)) + (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 () @@ -560,9 +746,16 @@ Increase this value when unexpected error frequently occurs." `(concat "/show_friend.pl?id=" (mixi-friend-id ,friend))) (defconst mixi-friend-nick-regexp - "\"\\*\"
\n\\(.*\\)¤µ¤ó([0-9]+)") -(defconst mixi-friend-name-sex-regexp - "̾\\( \\| \\)Á°\n+\\([^<]+\\) (\\([Ã˽÷]\\)À­)") + "\"\\*\"
? +\\(.*\\)¤µ¤ó([0-9]+)") +(defconst mixi-friend-name-regexp + "̾\\( \\| \\)Á° + +?\\(.+?\\)\\(\\|À­\\( \\| \\)ÊÌ + +?\\([Ã˽÷]\\)À­\\(\\|¸½½»½ê\n\\(.+\\)\\(\n.+\n\\)?") (defconst mixi-friend-age-regexp @@ -574,7 +767,7 @@ Increase this value when unexpected error frequently occurs." (defconst mixi-friend-birthplace-regexp "½Ð¿ÈÃÏ\n?\n\\(.+\\)\\(\n.+\n\\)?") (defconst mixi-friend-hobby-regexp - "¼ñ\\( \\| \\)Ì£\n\\(.+\\)") + "¼ñ\\( \\| \\)Ì£\n\\(.+?\\)\\(\\|¿¦\\( \\| \\)¶È\n\\(.+\\)\\(\n.+\n\\)?") (defconst mixi-friend-organization-regexp @@ -582,27 +775,29 @@ Increase this value when unexpected error frequently occurs." (defconst mixi-friend-profile-regexp "¼«¸Ê¾Ò²ð\n\\(.+\\)") -(defun mixi-friend-realize (friend) +(defun mixi-realize-friend (friend) "Realize a FRIEND." ;; FIXME: Check a expiration of cache? - (unless (mixi-object-realize-p friend) + (unless (mixi-object-realized-p friend) (let (buf) (with-mixi-retrieve (mixi-friend-page friend) (setq buf buffer)) (if (string-match mixi-friend-nick-regexp buf) (mixi-friend-set-nick friend (match-string 1 buf)) - (signal 'error (list 'cannot-find-nick friend))) + (mixi-realization-error 'cannot-find-nick friend)) ;; For getting my profile. - (unless (string-match mixi-friend-name-sex-regexp buf) - (with-mixi-retrieve "/show_profile.pl" + (unless (string-match mixi-friend-name-regexp buf) + (with-mixi-retrieve (concat "/show_profile.pl?id=" + (mixi-friend-id friend)) (setq buf buffer))) - (if (string-match mixi-friend-name-sex-regexp buf) - (progn + (if (string-match mixi-friend-name-regexp buf) (mixi-friend-set-name friend (match-string 2 buf)) - (mixi-friend-set-sex friend - (if (string= (match-string 3 buf) "ÃË") - 'male 'female))) - (signal 'error (list 'cannot-find-name-or-sex friend))) + (mixi-realization-error 'cannot-find-name friend)) + (if (string-match mixi-friend-sex-regexp buf) + (mixi-friend-set-sex friend + (if (string= (match-string 3 buf) "ÃË") + 'male 'female)) + (mixi-realization-error 'cannot-find-sex friend)) (when (string-match mixi-friend-address-regexp buf) (mixi-friend-set-address friend (match-string 1 buf))) (when (string-match mixi-friend-age-regexp buf) @@ -638,84 +833,84 @@ Increase this value when unexpected error frequently occurs." (unless (mixi-friend-p friend) (signal 'wrong-type-argument (list 'mixi-friend-p friend))) (unless (aref (cdr friend) 2) - (mixi-friend-realize friend)) + (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-friend-realize 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-friend-realize 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-friend-realize 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-friend-realize 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-friend-realize 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-friend-realize 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-friend-realize 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-friend-realize 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-friend-realize 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-friend-realize friend) + (mixi-realize-friend friend) (aref (cdr friend) 12)) (defun mixi-friend-profile (friend) "Return the pforile of FRIEND." (unless (mixi-friend-p friend) (signal 'wrong-type-argument (list 'mixi-friend-p friend))) - (mixi-friend-realize friend) + (mixi-realize-friend friend) (aref (cdr friend) 13)) (defun mixi-friend-set-nick (friend nick) @@ -799,23 +994,24 @@ Increase this value when unexpected error frequently occurs." (defconst mixi-friend-list-nick-regexp "\\(.+\\)¤µ¤ó([0-9]+)
") -(defun mixi-get-friends (&rest args) +(defun mixi-get-friends (&rest friend-or-range) "Get friends of FRIEND." - (when (> (length args) 2) - (signal 'wrong-number-of-arguments (list 'mixi-get-friends (length args)))) - (let ((friend (nth 0 args)) - (max-numbers (nth 1 args))) - (when (or (not (mixi-friend-p friend)) (mixi-friend-p max-numbers)) - (setq friend (nth 1 args)) - (setq max-numbers (nth 0 args))) + (when (> (length friend-or-range) 2) + (signal 'wrong-number-of-arguments (list 'mixi-get-friends + (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-friend-list-page friend) - max-numbers - mixi-friend-list-id-regexp)) + mixi-friend-list-id-regexp + range)) (nicks (mixi-get-matched-items (mixi-friend-list-page friend) - max-numbers - mixi-friend-list-nick-regexp))) + mixi-friend-list-nick-regexp + range))) (let ((index 0) ret) (while (< index (length ids)) @@ -834,14 +1030,14 @@ Increase this value when unexpected error frequently occurs." "̾  Á° \\(.+\\)") -(defun mixi-get-favorites (&optional max-numbers) +(defun mixi-get-favorites (&optional range) "Get favorites." (let ((ids (mixi-get-matched-items (mixi-favorite-list-page) - max-numbers - mixi-favorite-list-id-regexp)) + mixi-favorite-list-id-regexp + range)) (nicks (mixi-get-matched-items (mixi-favorite-list-page) - max-numbers - mixi-favorite-list-nick-regexp))) + mixi-favorite-list-nick-regexp + range))) (let ((index 0) ret) (while (< index (length ids)) @@ -874,13 +1070,13 @@ Increase this value when unexpected error frequently occurs." `(concat "/show_log.pl")) (defconst mixi-log-list-regexp - "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\)
\\(.*\\)
") + "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\) \\(.*\\)") -(defun mixi-get-logs (&optional max-numbers) +(defun mixi-get-logs (&optional range) "Get logs." (let ((items (mixi-get-matched-items (mixi-log-list-page) - max-numbers - mixi-log-list-regexp))) + mixi-log-list-regexp + range))) (mapcar (lambda (item) (mixi-make-log (mixi-make-friend (nth 5 item) (nth 6 item)) (encode-time 0 @@ -893,11 +1089,12 @@ Increase this value when unexpected error frequently occurs." ;; Diary object. (defvar mixi-diary-cache (make-hash-table :test 'equal)) -(defun mixi-make-diary (owner id) +(defun mixi-make-diary (owner id &optional time title content) "Return a diary object." (let ((owner (or owner (mixi-make-me)))) (mixi-make-cache (list (mixi-friend-id owner) id) - (cons 'mixi-diary (vector nil owner id nil nil nil)) + (cons 'mixi-diary (vector nil owner id time title + content)) mixi-diary-cache))) (defconst mixi-diary-url-regexp @@ -917,39 +1114,41 @@ Increase this value when unexpected error frequently occurs." `(concat "/view_diary.pl?id=" (mixi-diary-id ,diary) "&owner_id=" (mixi-friend-id (mixi-diary-owner ,diary)))) -;; FIXME: Remove `¤µ¤ó'. +(defconst mixi-diary-closed-regexp + "ͧ¿Í\\(¤Îͧ¿Í\\)?¤Þ¤Ç¸ø³«¤Î¤¿¤áÆɤळ¤È¤¬½ÐÍè¤Þ¤»¤ó¡£") (defconst mixi-diary-owner-nick-regexp - "\\(.+\\)\\(¤µ¤ó\\)?¤ÎÆüµ­") + "\\(.+?\\)\\(¤µ¤ó\\)?¤ÎÆüµ­") (defconst mixi-diary-time-regexp - "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü
\\([0-9]+\\):\\([0-9]+\\)") + "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü\\(
\\|
\\)\\([0-9]+\\):\\([0-9]+\\)") (defconst mixi-diary-title-regexp - " \\([^<]+\\)") + " \\([^<]+\\)") (defconst mixi-diary-content-regexp - "\\(.+\\)") + "\\(.*\\)") -(defun mixi-diary-realize (diary) +(defun mixi-realize-diary (diary) "Realize a DIARY." ;; FIXME: Check a expiration of cache? - (unless (mixi-object-realize-p diary) + (unless (mixi-object-realized-p diary) (with-mixi-retrieve (mixi-diary-page diary) - (if (string-match mixi-diary-owner-nick-regexp buffer) - (mixi-friend-set-nick (mixi-diary-owner diary) - (match-string 1 buffer)) - (signal 'error (list 'cannot-find-owner-nick diary))) - (if (string-match mixi-diary-time-regexp buffer) - (mixi-diary-set-time - diary (encode-time 0 (string-to-number (match-string 5 buffer)) - (string-to-number (match-string 4 buffer)) - (string-to-number (match-string 3 buffer)) - (string-to-number (match-string 2 buffer)) - (string-to-number (match-string 1 buffer)))) - (signal 'error (list 'cannot-find-time diary))) - (if (string-match mixi-diary-title-regexp buffer) - (mixi-diary-set-title diary (match-string 1 buffer)) - (signal 'error (list 'cannot-find-title diary))) - (if (string-match mixi-diary-content-regexp buffer) - (mixi-diary-set-content diary (match-string 1 buffer)) - (signal 'error (list 'cannot-find-content diary)))) + (unless (string-match mixi-diary-closed-regexp buffer) + (if (string-match mixi-diary-owner-nick-regexp buffer) + (mixi-friend-set-nick (mixi-diary-owner diary) + (match-string 1 buffer)) + (mixi-realization-error 'cannot-find-owner-nick diary)) + (if (string-match mixi-diary-time-regexp buffer) + (mixi-diary-set-time + diary (encode-time 0 (string-to-number (match-string 10 buffer)) + (string-to-number (match-string 9 buffer)) + (string-to-number (match-string 7 buffer)) + (string-to-number (match-string 6 buffer)) + (string-to-number (match-string 5 buffer)))) + (mixi-realization-error 'cannot-find-time diary)) + (if (string-match mixi-diary-title-regexp buffer) + (mixi-diary-set-title diary (match-string 2 buffer)) + (mixi-realization-error 'cannot-find-title diary)) + (if (string-match mixi-diary-content-regexp buffer) + (mixi-diary-set-content diary (match-string 2 buffer)) + (mixi-realization-error 'cannot-find-content diary)))) (mixi-object-touch diary))) (defun mixi-diary-owner (diary) @@ -968,21 +1167,23 @@ Increase this value when unexpected error frequently occurs." "Return the time of DIARY." (unless (mixi-diary-p diary) (signal 'wrong-type-argument (list 'mixi-diary-p diary))) - (mixi-diary-realize diary) + (unless (aref (cdr diary) 3) + (mixi-realize-diary diary)) (aref (cdr diary) 3)) (defun mixi-diary-title (diary) "Return the title of DIARY." (unless (mixi-diary-p diary) (signal 'wrong-type-argument (list 'mixi-diary-p diary))) - (mixi-diary-realize diary) + (unless (aref (cdr diary) 4) + (mixi-realize-diary diary)) (aref (cdr diary) 4)) (defun mixi-diary-content (diary) "Return the content of DIARY." (unless (mixi-diary-p diary) (signal 'wrong-type-argument (list 'mixi-diary-p diary))) - (mixi-diary-realize diary) + (mixi-realize-diary diary) (aref (cdr diary) 5)) (defun mixi-diary-set-time (diary time) @@ -1008,48 +1209,155 @@ Increase this value when unexpected error frequently occurs." (when ,friend (concat "&id=" (mixi-friend-id ,friend))))) (defconst mixi-diary-list-regexp - "") + " +\\([0-9]+\\)·î\\([0-9]+\\)Æü
\\([0-9]+\\):\\([0-9]+\\)
\\(
\\|\\) + 
\\(.*\\)") -(defun mixi-get-diaries (&rest args) +(defun mixi-get-diaries (&rest friend-or-range) "Get diaries of FRIEND." - (when (> (length args) 2) + (when (> (length friend-or-range) 2) (signal 'wrong-number-of-arguments - (list 'mixi-get-diaries (length args)))) - (let ((friend (nth 0 args)) - (max-numbers (nth 1 args))) - (when (or (not (mixi-friend-p friend)) (mixi-friend-p max-numbers)) - (setq friend (nth 1 args)) - (setq max-numbers (nth 0 args))) + (list 'mixi-get-diaries (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 ((items (mixi-get-matched-items (mixi-diary-list-page friend) - max-numbers - mixi-diary-list-regexp))) + mixi-diary-list-regexp + range)) + (year (nth 5 (decode-time (current-time)))) + (month (nth 4 (decode-time (current-time))))) (mapcar (lambda (item) - (mixi-make-diary friend (nth 0 item))) + (let ((month-of-item (string-to-number (nth 0 item)))) + (when (> month-of-item month) + (decf year)) + (setq month month-of-item) + (mixi-make-diary friend (nth 5 item) + (encode-time + 0 (string-to-number (nth 3 item)) + (string-to-number (nth 2 item)) + (string-to-number (nth 1 item)) + month year) + (nth 6 item)))) items)))) (defmacro mixi-new-diary-list-page () `(concat "/new_friend_diary.pl?page=%d")) (defconst mixi-new-diary-list-regexp - "") + "\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\) +\\(.+\\) (\\(.*\\)) ") -(defun mixi-get-new-diaries (&optional max-numbers) +(defun mixi-get-new-diaries (&optional range) "Get new diaries." (let ((items (mixi-get-matched-items (mixi-new-diary-list-page) - max-numbers - mixi-new-diary-list-regexp))) + mixi-new-diary-list-regexp + range))) (mapcar (lambda (item) - (mixi-make-diary (mixi-make-friend (nth 1 item)) (nth 0 item))) + (mixi-make-diary (mixi-make-friend (nth 6 item) (nth 8 item)) + (nth 5 item) + (encode-time + 0 (string-to-number (nth 4 item)) + (string-to-number (nth 3 item)) + (string-to-number (nth 2 item)) + (string-to-number (nth 1 item)) + (string-to-number (nth 0 item))) + (nth 7 item))) items))) +(defmacro mixi-search-diary-list-page (keyword) + `(concat "/search_diary.pl?page=%d&submit=search&keyword=" + (mixi-url-encode-and-quote-percent-string ,keyword))) + +(defconst mixi-search-diary-list-regexp + "̾  Á° +\\(.*\\) + + + + +¥¿¥¤¥È¥ë +\\(.+\\) + + +ËÜ  Ê¸ +\\(.*\\) + + + +ºîÀ®Æü»þ +\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\) +¾ÜºÙ¤ò¸«¤ë + +") + +(defun mixi-search-diaries (keyword &optional range) + (let ((items (mixi-get-matched-items (mixi-search-diary-list-page keyword) + mixi-search-diary-list-regexp + range)) + (year (nth 5 (decode-time (current-time)))) + (month (nth 4 (decode-time (current-time))))) + (mapcar (lambda (item) + (let ((month-of-item (string-to-number (nth 3 item)))) + (when (> month-of-item month) + (decf year)) + (setq month month-of-item) + (mixi-make-diary (mixi-make-friend (nth 8 item) (nth 0 item)) + (nth 7 item) + (encode-time + 0 (string-to-number (nth 6 item)) + (string-to-number (nth 5 item)) + (string-to-number (nth 4 item)) + month year) + (nth 1 item) + (nth 2 item)))) + items))) + +(defmacro mixi-post-diary-page () + `(concat "/add_diary.pl")) + +(defconst mixi-post-key-regexp + "") +(defconst mixi-post-succeed-regexp + "\\(ºîÀ®\\|½ñ¤­¹þ¤ß\\)¤¬´°Î»¤·¤Þ¤·¤¿¡£È¿±Ç¤Ë»þ´Ö¤¬¤«¤«¤ë¤³¤È¤¬¤¢¤ê¤Þ¤¹¤Î¤Ç¡¢É½¼¨¤µ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ï¾¯¡¹¤ªÂÔ¤Á¤¯¤À¤µ¤¤¡£") + +;; FIXME: Support photos. +(defun mixi-post-diary (title content) + "Post a diary." + (unless (stringp title) + (signal 'wrong-type-argument (list 'stringp title))) + (unless (stringp content) + (signal 'wrong-type-argument (list 'stringp content))) + (let ((fields `(("id" . ,(mixi-friend-id (mixi-make-me))) + ("diary_title" . ,title) + ("diary_body" . ,content) + ("submit" . "main"))) + post-key) + (with-mixi-post-form (mixi-post-diary-page) fields + (if (string-match mixi-post-key-regexp buffer) + (setq post-key (match-string 1 buffer)) + (mixi-post-error 'cannot-find-key))) + (setq fields `(("post_key" . ,post-key) + ("id" . ,(mixi-friend-id (mixi-make-me))) + ("diary_title" . ,title) + ("diary_body" . ,content) + ("submit" . "confirm"))) + (with-mixi-post-form (mixi-post-diary-page) fields + (unless (string-match mixi-post-succeed-regexp buffer) + (mixi-post-error 'cannot-find-succeed))))) + ;; Community object. (defvar mixi-community-cache (make-hash-table :test 'equal)) -(defun mixi-make-community (id &optional name) +(defun mixi-make-community (id &optional name birthday owner category members + open-level authority description) "Return a community object." - (mixi-make-cache id (cons 'mixi-community (vector nil id name nil nil nil - nil nil nil nil)) + (mixi-make-cache id (cons 'mixi-community (vector nil id name birthday owner + category members + open-level authority + description)) mixi-community-cache)) (defconst mixi-community-url-regexp @@ -1073,63 +1381,70 @@ Increase this value when unexpected error frequently occurs." (defconst mixi-community-name-regexp "\\(.*\\)") (defconst mixi-community-birthday-regexp - "³«ÀßÆü\n\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü") + "³«ÀßÆü +\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü") ;; FIXME: Care when the owner has seceded. (defconst mixi-community-owner-regexp - "´ÉÍý¿Í\n\n\n\n\\(.*\\)") + "´ÉÍý¿Í + +\n") + " +") (defconst mixi-community-members-regexp - "\n") + " +") (defconst mixi-community-open-level-regexp - " -") + " +") (defconst mixi-community-authority-regexp - "\n") + " +") (defconst mixi-community-description-regexp - "") + "") -(defun mixi-community-realize (community) +(defun mixi-realize-community (community) "Realize a COMMUNITY." ;; FIXME: Check a expiration of cache? - (unless (mixi-object-realize-p community) + (unless (mixi-object-realized-p community) (with-mixi-retrieve (mixi-community-page community) (if (string-match mixi-community-nodata-regexp buffer) ;; FIXME: Set all members? (mixi-community-set-name community "¥Ç¡¼¥¿¤¬¤¢¤ê¤Þ¤»¤ó") (if (string-match mixi-community-name-regexp buffer) (mixi-community-set-name community (match-string 1 buffer)) - (signal 'error (list 'cannot-find-name community))) + (mixi-realization-error 'cannot-find-name community)) (if (string-match mixi-community-birthday-regexp buffer) (mixi-community-set-birthday community (encode-time 0 0 0 (string-to-number (match-string 3 buffer)) (string-to-number (match-string 2 buffer)) (string-to-number (match-string 1 buffer)))) - (signal 'error (list 'cannot-find-birthday community))) + (mixi-realization-error 'cannot-find-birthday community)) (if (string-match mixi-community-owner-regexp buffer) (if (string= (match-string 1 buffer) "home.pl") (mixi-community-set-owner community (mixi-make-me)) (mixi-community-set-owner community (mixi-make-friend (match-string 2 buffer) (match-string 3 buffer)))) - (signal 'error (list 'cannot-find-owner community))) + (mixi-realization-error 'cannot-find-owner community)) (if (string-match mixi-community-category-regexp buffer) (mixi-community-set-category community (match-string 1 buffer)) - (signal 'error (list 'cannot-find-category community))) + (mixi-realization-error 'cannot-find-category community)) (if (string-match mixi-community-members-regexp buffer) (mixi-community-set-members community (string-to-number (match-string 1 buffer))) - (signal 'error (list 'cannot-find-members community))) + (mixi-realization-error 'cannot-find-members community)) (if (string-match mixi-community-open-level-regexp buffer) (mixi-community-set-open-level community (match-string 1 buffer)) - (signal 'error (list 'cannot-find-open-level community))) + (mixi-realization-error 'cannot-find-open-level community)) (if (string-match mixi-community-authority-regexp buffer) (mixi-community-set-authority community (match-string 1 buffer)) - (signal 'error (list 'cannot-find-authority community))) + (mixi-realization-error 'cannot-find-authority community)) (if (string-match mixi-community-description-regexp buffer) (mixi-community-set-description community (match-string 1 buffer)) - (signal 'error (list 'cannot-find-description community))))) + (mixi-realization-error 'cannot-find-description community)))) (mixi-object-touch community))) (defun mixi-community-id (community) @@ -1143,56 +1458,56 @@ Increase this value when unexpected error frequently occurs." (unless (mixi-community-p community) (signal 'wrong-type-argument (list 'mixi-community-p community))) (unless (aref (cdr community) 2) - (mixi-community-realize community)) + (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-community-realize 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-community-realize 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-community-realize 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-community-realize 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-community-realize 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-community-realize 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-community-realize community) + (mixi-realize-community community) (aref (cdr community) 9)) (defun mixi-community-set-name (community name) @@ -1254,24 +1569,24 @@ Increase this value when unexpected error frequently occurs." (defconst mixi-community-list-name-regexp "") -(defun mixi-get-communities (&rest args) +(defun mixi-get-communities (&rest friend-or-range) "Get communities of FRIEND." - (when (> (length args) 2) + (when (> (length friend-or-range) 2) (signal 'wrong-number-of-arguments - (list 'mixi-get-communities (length args)))) - (let ((friend (nth 0 args)) - (max-numbers (nth 1 args))) - (when (or (not (mixi-friend-p friend)) (mixi-friend-p max-numbers)) - (setq friend (nth 1 args)) - (setq max-numbers (nth 0 args))) + (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) - max-numbers - mixi-community-list-id-regexp)) + mixi-community-list-id-regexp + range)) (names (mixi-get-matched-items (mixi-community-list-page friend) - max-numbers - mixi-community-list-name-regexp))) + mixi-community-list-name-regexp + range))) (let ((index 0) ret) (while (< index (length ids)) @@ -1280,12 +1595,33 @@ Increase this value when unexpected error frequently occurs." (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 + " + +") + +;; FIXME: Support category. +(defun mixi-search-communities (keyword &optional range) + (let ((items (mixi-get-matched-items (mixi-search-community-list-page + keyword) + mixi-search-community-list-regexp + range))) + (mapcar (lambda (item) + (mixi-make-community (nth 0 item) (nth 1 item))) + items))) + ;; Topic object. (defvar mixi-topic-cache (make-hash-table :test 'equal)) -(defun mixi-make-topic (community id) +(defun mixi-make-topic (community id &optional time title owner content) "Return a topic object." (mixi-make-cache (list (mixi-community-id community) id) - (cons 'mixi-topic (vector nil community id nil nil nil nil)) + (cons 'mixi-topic (vector nil community id time title owner + content)) mixi-topic-cache)) (defconst mixi-topic-url-regexp @@ -1305,21 +1641,26 @@ Increase this value when unexpected error frequently occurs." `(concat "/view_bbs.pl?id=" (mixi-topic-id ,topic) "&comm_id=" (mixi-community-id (mixi-topic-community ,topic)))) +(defconst mixi-topic-community-regexp + "") (defconst mixi-topic-time-regexp "") (defconst mixi-topic-title-regexp "") -;; FIXME: Remove `¤µ¤ó'. (defconst mixi-topic-owner-regexp - "") + "
+\\(.*\\)") (defconst mixi-community-category-regexp - "¥«¥Æ¥´¥ê\\([^<]+\\)¥«¥Æ¥´¥ê\\([^<]+\\)¥á¥ó¥Ð¡¼¿ô\\([0-9]+\\)¿Í
¥á¥ó¥Ð¡¼¿ô\\([0-9]+\\)¿Í
»²²Ã¾ò·ï¤È
¸ø³«¥ì¥Ù¥ë
\\(.+\\)
»²²Ã¾ò·ï¤È
¸ø³«¥ì¥Ù¥ë
\\(.+\\)
¥È¥Ô¥Ã¥¯ºîÀ®¤Î¸¢¸Â\\(.+\\)
¥È¥Ô¥Ã¥¯ºîÀ®¤Î¸¢¸Â\\(.+\\)
\\(.+\\)\\(.+\\)\\(.+\\)([0-9]+)¥³¥ß¥å¥Ë¥Æ¥£Ì¾\\([^<]+\\)
\\[\\(.+\\)\\] ¥È¥Ô¥Ã¥¯\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü
\\([0-9]+\\):\\([0-9]+\\)
 \\([^<]+\\)  \\(.*\\)\\(¤µ¤ó\\)?") + "  \\(.*?\\)\\(¤µ¤ó\\)?") (defconst mixi-topic-content-regexp - "\\(.+\\)?\n?\\(.+\\)?\n?\\(.+\\)?\n?
\\(.+\\)
\\(\n\\)*
\\(.+\\)
") -(defun mixi-topic-realize (topic) +(defun mixi-realize-topic (topic) "Realize a TOPIC." ;; FIXME: Check a expiration of cache? - (unless (mixi-object-realize-p topic) + (unless (mixi-object-realized-p topic) (with-mixi-retrieve (mixi-topic-page topic) + (if (string-match mixi-topic-community-regexp buffer) + (mixi-community-set-name (mixi-topic-community topic) + (match-string 1 buffer)) + (mixi-realization-error 'cannot-find-community topic)) (if (string-match mixi-topic-time-regexp buffer) (mixi-topic-set-time topic (encode-time 0 (string-to-number (match-string 5 buffer)) @@ -1327,18 +1668,18 @@ Increase this value when unexpected error frequently occurs." (string-to-number (match-string 3 buffer)) (string-to-number (match-string 2 buffer)) (string-to-number (match-string 1 buffer)))) - (signal 'error (list 'cannot-find-time topic))) + (mixi-realization-error 'cannot-find-time topic)) (if (string-match mixi-topic-title-regexp buffer) (mixi-topic-set-title topic (match-string 1 buffer)) - (signal 'error (list 'cannot-find-title topic))) + (mixi-realization-error 'cannot-find-title topic)) (if (string-match mixi-topic-owner-regexp buffer) (mixi-topic-set-owner topic (mixi-make-friend (match-string 1 buffer) (match-string 2 buffer))) - (signal 'error (list 'cannot-find-owner topic))) + (mixi-realization-error 'cannot-find-owner topic)) (if (string-match mixi-topic-content-regexp buffer) - (mixi-topic-set-content topic (match-string 4 buffer)) - (signal 'error (list 'cannot-find-content topic)))) + (mixi-topic-set-content topic (match-string 2 buffer)) + (mixi-realization-error 'cannot-find-content topic))) (mixi-object-touch topic))) (defun mixi-topic-community (topic) @@ -1357,28 +1698,28 @@ Increase this value when unexpected error frequently occurs." "Return the time of TOPIC." (unless (mixi-topic-p topic) (signal 'wrong-type-argument (list 'mixi-topic-p topic))) - (mixi-topic-realize topic) + (mixi-realize-topic topic) (aref (cdr topic) 3)) (defun mixi-topic-title (topic) "Return the title of TOPIC." (unless (mixi-topic-p topic) (signal 'wrong-type-argument (list 'mixi-topic-p topic))) - (mixi-topic-realize topic) + (mixi-realize-topic topic) (aref (cdr topic) 4)) (defun mixi-topic-owner (topic) "Return the owner of TOPIC." (unless (mixi-topic-p topic) (signal 'wrong-type-argument (list 'mixi-topic-p topic))) - (mixi-topic-realize topic) + (mixi-realize-topic topic) (aref (cdr topic) 5)) (defun mixi-topic-content (topic) "Return the content of TOPIC." (unless (mixi-topic-p topic) (signal 'wrong-type-argument (list 'mixi-topic-p topic))) - (mixi-topic-realize topic) + (mixi-realize-topic topic) (aref (cdr topic) 6)) (defun mixi-topic-set-time (topic time) @@ -1407,38 +1748,347 @@ Increase this value when unexpected error frequently occurs." (signal 'wrong-type-argument (list 'mixi-topic-p topic))) (aset (cdr topic) 6 content)) -(defmacro mixi-topic-list-page (community) +(defmacro mixi-post-topic-page (community) + `(concat "/add_bbs.pl?id=" (mixi-community-id community))) + +;; FIXME: Support photos. +(defun mixi-post-topic (community title content) + "Post a topic to COMMUNITY." + (unless (mixi-community-p community) + (signal 'wrong-type-argument (list 'mixi-community-p community))) + (unless (stringp title) + (signal 'wrong-type-argument (list 'stringp title))) + (unless (stringp content) + (signal 'wrong-type-argument (list 'stringp content))) + (let ((fields `(("bbs_title" . ,title) + ("bbs_body" . ,content) + ("submit" . "main"))) + post-key) + (with-mixi-post-form (mixi-post-topic-page community) fields + (if (string-match mixi-post-key-regexp buffer) + (setq post-key (match-string 1 buffer)) + (mixi-post-error 'cannot-find-key community))) + (setq fields `(("post_key" . ,post-key) + ("bbs_title" . ,title) + ("bbs_body" . ,content) + ("submit" . "confirm"))) + (with-mixi-post-form (mixi-post-topic-page community) fields + (unless (string-match mixi-post-succeed-regexp buffer) + (mixi-post-error 'cannot-find-succeed community))))) + +;; Event object. +(defvar mixi-event-cache (make-hash-table :test 'equal)) +(defun mixi-make-event (community id &optional time title owner date place + detail limit members) + "Return a event object." + (mixi-make-cache (list (mixi-community-id community) id) + (cons 'mixi-event (vector nil community id time title owner + date place detail limit members)) + mixi-event-cache)) + +(defconst mixi-event-url-regexp + "/view_event\\.pl\\?id=\\([0-9]+\\)\\(&comment_count=[0-9]+\\)?&comm_id=\\([0-9]+\\)") + +(defun mixi-make-event-from-url (url) + "Return a event object from URL." + (when (string-match mixi-event-url-regexp url) + (let ((id (match-string 1 url)) + (community-id (match-string 3 url))) + (mixi-make-event (mixi-make-community community-id) id)))) + +(defmacro mixi-event-p (event) + `(eq (mixi-object-class ,event) 'mixi-event)) + +(defmacro mixi-event-page (event) + `(concat "/view_event.pl?id=" (mixi-event-id ,event) + "&comm_id=" (mixi-community-id (mixi-event-community ,event)))) + +(defconst mixi-event-community-regexp + "\\[\\(.+\\)\\] ¥¤¥Ù¥ó¥È") +(defconst mixi-event-time-regexp + " +?\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü
+?\\([0-9]+\\):\\([0-9]+\\)") +(defconst mixi-event-title-regexp + " \\([^<]+\\)") +(defconst mixi-event-owner-regexp + " \\(.*\\)") +(defconst mixi-event-owner-seceded-regexp + " \\((mixi Âà²ñºÑ)\\)") +(defconst mixi-event-date-regexp + "³«ºÅÆü»þ + + \\(.+\\) +") +(defconst mixi-event-place-regexp + "³«ºÅ¾ì½ê + + \\(.+\\) +") +(defconst mixi-event-detail-regexp + "¾ÜºÙ +
\\(.+\\)
") +(defconst mixi-event-limit-regexp + "Ê罸´ü¸Â +? \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü") +(defconst mixi-event-members-regexp + "»²²Ã¼Ô + + +? +? + + +?") + +(defun mixi-realize-event (event) + "Realize a EVENT." + ;; FIXME: Check a expiration of cache? + (unless (mixi-object-realized-p event) + (with-mixi-retrieve (mixi-event-page event) + (if (string-match mixi-event-community-regexp buffer) + (mixi-community-set-name (mixi-event-community event) + (match-string 1 buffer)) + (mixi-realization-error 'cannot-find-community event)) + (if (string-match mixi-event-time-regexp buffer) + (mixi-event-set-time + event (encode-time 0 (string-to-number (match-string 8 buffer)) + (string-to-number (match-string 7 buffer)) + (string-to-number (match-string 6 buffer)) + (string-to-number (match-string 5 buffer)) + (string-to-number (match-string 4 buffer)))) + (mixi-realization-error 'cannot-find-time event)) + (if (string-match mixi-event-title-regexp buffer) + (mixi-event-set-title event (match-string 2 buffer)) + (mixi-realization-error 'cannot-find-title event)) + (if (string-match mixi-event-owner-regexp buffer) + (mixi-event-set-owner event + (mixi-make-friend (match-string 2 buffer) + (match-string 3 buffer))) + (if (string-match mixi-event-owner-seceded-regexp buffer) + (mixi-event-set-owner event + (mixi-make-friend nil + (match-string 2 buffer))) + (mixi-realization-error 'cannot-find-owner event))) + (if (string-match mixi-event-date-regexp buffer) + (mixi-event-set-date event (match-string 6 buffer)) + (mixi-realization-error 'cannot-find-date event)) + (if (string-match mixi-event-place-regexp buffer) + (mixi-event-set-place event (match-string 6 buffer)) + (mixi-realization-error 'cannot-find-place event)) + (if (string-match mixi-event-detail-regexp buffer) + (mixi-event-set-detail event (match-string 6 buffer)) + (mixi-realization-error 'cannot-find-detail event)) + (when (string-match mixi-event-limit-regexp buffer) + (mixi-event-set-limit + event (encode-time 0 0 0 (string-to-number (match-string 8 buffer)) + (string-to-number (match-string 7 buffer)) + (string-to-number (match-string 6 buffer))))) + (if (string-match mixi-event-members-regexp buffer) + (mixi-event-set-members event (match-string 6 buffer)) + (mixi-realization-error 'cannot-find-members event))) + (mixi-object-touch event))) + +(defun mixi-event-community (event) + "Return the community of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aref (cdr event) 1)) + +(defun mixi-event-id (event) + "Return the id of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aref (cdr event) 2)) + +(defun mixi-event-time (event) + "Return the time of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 3)) + +(defun mixi-event-title (event) + "Return the title of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 4)) + +(defun mixi-event-owner (event) + "Return the owner of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 5)) + +(defun mixi-event-date (event) + "Return the date of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 6)) + +(defun mixi-event-place (event) + "Return the place of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 7)) + +(defun mixi-event-detail (event) + "Return the detail of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 8)) + +(defun mixi-event-limit (event) + "Return the limit of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 9)) + +(defun mixi-event-members (event) + "Return the members of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (mixi-realize-event event) + (aref (cdr event) 10)) + +(defun mixi-event-set-time (event time) + "Set the time of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aset (cdr event) 3 time)) + +(defun mixi-event-set-title (event title) + "Set the title of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aset (cdr event) 4 title)) + +(defun mixi-event-set-owner (event owner) + "Set the owner of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (unless (mixi-friend-p owner) + (signal 'wrong-type-argument (list 'mixi-friend-p owner))) + (aset (cdr event) 5 owner)) + +(defun mixi-event-set-date (event date) + "Set the date of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aset (cdr event) 6 date)) + +(defun mixi-event-set-place (event place) + "Set the place of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aset (cdr event) 7 place)) + +(defun mixi-event-set-detail (event detail) + "Set the detail of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aset (cdr event) 8 detail)) + +(defun mixi-event-set-limit (event limit) + "Set the limit of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aset (cdr event) 9 limit)) + +(defun mixi-event-set-members (event members) + "Set the members of EVENT." + (unless (mixi-event-p event) + (signal 'wrong-type-argument (list 'mixi-event-p event))) + (aset (cdr event) 10 members)) + +;; Bbs object. +(defconst mixi-bbs-list '(mixi-topic mixi-event)) + +(defmacro mixi-bbs-p (object) + `(memq (mixi-object-class ,object) mixi-bbs-list)) + +(defun mixi-bbs-community (object) + "Return the community of OBJECT." + (unless (mixi-bbs-p object) + (signal 'wrong-type-argument (list 'mixi-bbs-p object))) + (let ((func (intern (concat mixi-object-prefix + (mixi-object-name object) "-community")))) + (funcall func object))) + +(defalias 'mixi-bbs-id 'mixi-object-id) +(defalias 'mixi-bbs-time 'mixi-object-time) +(defalias 'mixi-bbs-title 'mixi-object-title) +(defalias 'mixi-bbs-owner 'mixi-object-owner) +(defalias 'mixi-bbs-content 'mixi-object-content) + +(defmacro mixi-bbs-list-page (community) `(concat "/list_bbs.pl?page=%d" "&id=" (mixi-community-id ,community))) -(defconst mixi-topic-list-regexp - "") +(defconst mixi-new-bbs-list-regexp + "") -(defun mixi-get-new-topics (&optional max-numbers) +(defun mixi-get-new-bbses (&optional range) "Get new topics." - (let ((items (mixi-get-matched-items (mixi-new-topic-list-page) - max-numbers - mixi-new-topic-list-regexp))) + (let ((items (mixi-get-matched-items (mixi-new-bbs-list-page) + mixi-new-bbs-list-regexp + range))) + (mapcar (lambda (item) + (let ((name (nth 0 item))) + (when (string= name "bbs") + (setq name "topic")) + (let ((func (intern (concat "mixi-make-" name)))) + (funcall func (mixi-make-community (nth 2 item)) + (nth 1 item))))) + items))) + +(defmacro mixi-search-bbs-list-page (keyword) + `(concat "/search_topic.pl?page=%d&type=top&submit=search" + "&keyword=" (mixi-url-encode-and-quote-percent-string ,keyword) + "&community_id=0&category_id=0")) + +(defconst mixi-search-bbs-list-regexp + "¾ÜºÙ¤ò¸«¤ë") + +;; FIXME: Support community and category. +(defun mixi-search-bbses (keyword &optional range) + (let ((items (mixi-get-matched-items (mixi-search-bbs-list-page keyword) + mixi-search-bbs-list-regexp + range))) (mapcar (lambda (item) - (mixi-make-topic (mixi-make-community (nth 1 item)) - (nth 0 item))) + (let ((name (nth 0 item))) + (when (string= name "bbs") + (setq name "topic")) + (let ((func (intern (concat "mixi-make-" name)))) + (funcall func (mixi-make-community (nth 2 item)) + (nth 1 item))))) items))) ;; Comment object. @@ -1474,7 +2124,7 @@ Increase this value when unexpected error frequently occurs." (aref (cdr comment) 3)) (defun mixi-diary-comment-list-page (diary) - (concat "/view_diary.pl?page=%d" + (concat "/view_diary.pl?full=1" "&id=" (mixi-diary-id diary) "&owner_id=" (mixi-friend-id (mixi-diary-owner diary)))) @@ -1498,7 +2148,7 @@ Increase this value when unexpected error frequently occurs."
 \\(.+\\)
- + @@ -1527,6 +2177,7 @@ Increase this value when unexpected error frequently occurs." ?\\( +\\|ºï½ü \\|\\) @@ -1542,7 +2193,36 @@ Increase this value when unexpected error frequently occurs." ") -(defun mixi-get-comments (parent &optional max-numbers) +(defun mixi-event-comment-list-page (event) + (concat "/view_event.pl?page=all" + "&id=" (mixi-event-id event) + "&comm_id=" (mixi-community-id (mixi-event-community event)))) + +;; FIXME: Split regexp to time, owner(id and nick) and contents. +(defconst mixi-event-comment-list-regexp + " +\\) +\\( + + + +") + +(defun mixi-get-comments (parent &optional range) "Get comments of PARENT." (unless (mixi-object-p parent) (signal 'wrong-type-argument (list 'mixi-object-p parent))) @@ -1552,7 +2232,16 @@ Increase this value when unexpected error frequently occurs." (regexp (eval (intern (concat mixi-object-prefix name "-comment-list-regexp"))))) (let ((items (mixi-get-matched-items - (funcall list-page parent) max-numbers regexp))) + (funcall list-page parent) regexp))) + (let (list) + (catch 'stop + (mapc (lambda (item) + (when (and (numberp range) + (>= (length list) range)) + (throw 'stop nil)) + (setq list (cons item list))) + (reverse items))) + (setq items (reverse list))) (mapcar (lambda (item) (mixi-make-comment parent (mixi-make-friend (nth 7 item) (nth 8 item)) @@ -1572,22 +2261,64 @@ Increase this value when unexpected error frequently occurs." (defconst mixi-new-comment-list-regexp "") -(defun mixi-get-new-comments (&optional max-numbers) +(defun mixi-get-new-comments (&optional range) "Get new comments." (let ((items (mixi-get-matched-items (mixi-new-comment-list-page) - max-numbers - mixi-new-comment-list-regexp))) + mixi-new-comment-list-regexp + range))) (mapcar (lambda (item) - ;; FIXME: Return comments? (mixi-make-diary (mixi-make-friend (nth 1 item)) (nth 0 item))) items))) +(defun mixi-post-diary-comment-page (diary) + (concat "/add_comment.pl?&diary_id=" (mixi-diary-id diary))) + +(defun mixi-post-topic-comment-page (topic) + (concat "/add_bbs_comment.pl?id=" (mixi-topic-id topic) + "&comm_id=" (mixi-community-id (mixi-topic-community topic)))) + +(defun mixi-post-event-comment-page (event) + (concat "/add_event_comment.pl?id=" (mixi-event-id event) + "&comm_id=" (mixi-community-id (mixi-event-community event)))) + +;; FIXME: Support photos. +(defun mixi-post-comment (parent content) + "Post a comment to PARENT." + (unless (mixi-object-p parent) + (signal 'wrong-type-argument (list 'mixi-object-p parent))) + (unless (stringp content) + (signal 'wrong-type-argument (list 'stringp content))) + (let* ((name (mixi-object-name parent)) + (page (intern (concat mixi-object-prefix "post-" name + "-comment-page"))) + fields post-key) + (if (mixi-diary-p parent) + (setq fields + `(("owner_id" . ,(mixi-friend-id (mixi-diary-owner parent))) + ("comment_body" . ,content))) + (setq fields `(("comment" . ,content)))) + (with-mixi-post-form (funcall page parent) fields + (if (string-match mixi-post-key-regexp buffer) + (setq post-key (match-string 1 buffer)) + (mixi-post-error 'cannot-find-key parent))) + (if (mixi-diary-p parent) + (setq fields + `(("post_key" . ,post-key) + ("owner_id" . ,(mixi-friend-id (mixi-diary-owner parent))) + ("comment_body" . ,content) + ("submit" . "confirm"))) + (setq fields `(("post_key" . ,post-key) + ("comment" . ,content) + ("submit" . "confirm")))) + (with-mixi-post-form (funcall page parent) fields + (unless (string-match mixi-post-succeed-regexp buffer) + (mixi-post-error 'cannot-find-succeed parent))))) + ;; Message object. (defconst mixi-message-box-list '(inbox outbox savebox thrash)) ; thrash? (defmacro mixi-message-box-p (box) - `(when (memq ,box mixi-message-box-list) - t)) + `(memq ,box mixi-message-box-list)) (defun mixi-message-box-name (box) "Return the name of BOX." @@ -1596,10 +2327,11 @@ Increase this value when unexpected error frequently occurs." (symbol-name box)) (defvar mixi-message-cache (make-hash-table :test 'equal)) -(defun mixi-make-message (id box) +(defun mixi-make-message (id box &optional owner title time content) "Return a message object." (mixi-make-cache (list id box) - (cons 'mixi-message (vector nil id box nil nil nil nil)) + (cons 'mixi-message (vector nil id box owner title time + content)) mixi-message-cache)) (defconst mixi-message-url-regexp @@ -1620,38 +2352,37 @@ Increase this value when unexpected error frequently occurs." "&box=" (mixi-message-box ,message))) (defconst mixi-message-owner-regexp - "º¹½Ð¿Í : \\(.*\\)") + "\\(º¹½Ð¿Í\\|°¸ Àè\\) : \\(.*\\)\\(\\|\\)") (defconst mixi-message-title-regexp - "·ï¡¡Ì¾ : \\(.+\\) -") +"·ï\\(¡¡\\| \\)̾ : \\(.+\\)\n?") (defconst mixi-message-time-regexp - "Æü¡¡ÉÕ : \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\)»þ\\([0-9]+\\)ʬ  ") +"Æü\\(¡¡\\| \\)ÉÕ : \\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\)»þ\\([0-9]+\\)ʬ  ") (defconst mixi-message-content-regexp "") -(defun mixi-message-realize (message) +(defun mixi-realize-message (message) "Realize a MESSAGE." - (unless (mixi-object-realize-p message) + (unless (mixi-object-realized-p message) (with-mixi-retrieve (mixi-message-page message) (if (string-match mixi-message-owner-regexp buffer) (mixi-message-set-owner message - (mixi-make-friend (match-string 1 buffer) - (match-string 2 buffer))) - (signal 'error (list 'cannot-find-owner message))) + (mixi-make-friend (match-string 2 buffer) + (match-string 3 buffer))) + (mixi-realization-error 'cannot-find-owner message)) (if (string-match mixi-message-title-regexp buffer) - (mixi-message-set-title message (match-string 1 buffer)) - (signal 'error (list 'cannot-find-title message))) + (mixi-message-set-title message (match-string 2 buffer)) + (mixi-realization-error 'cannot-find-title message)) (if (string-match mixi-message-time-regexp buffer) (mixi-message-set-time - message (encode-time 0 (string-to-number (match-string 5 buffer)) + message (encode-time 0 (string-to-number (match-string 6 buffer)) + (string-to-number (match-string 5 buffer)) (string-to-number (match-string 4 buffer)) (string-to-number (match-string 3 buffer)) - (string-to-number (match-string 2 buffer)) - (string-to-number (match-string 1 buffer)))) - (signal 'error (list 'cannot-find-time message))) + (string-to-number (match-string 2 buffer)))) + (mixi-realization-error 'cannot-find-time message)) (if (string-match mixi-message-content-regexp buffer) (mixi-message-set-content message (match-string 1 buffer)) - (signal 'error (list 'cannot-find-content message)))) + (mixi-realization-error 'cannot-find-content message))) (mixi-object-touch message))) (defun mixi-message-id (message) @@ -1670,28 +2401,28 @@ Increase this value when unexpected error frequently occurs." "Return the owner of MESSAGE." (unless (mixi-message-p message) (signal 'wrong-type-argument (list 'mixi-message-p message))) - (mixi-message-realize message) + (mixi-realize-message message) (aref (cdr message) 3)) (defun mixi-message-title (message) "Return the title of MESSAGE." (unless (mixi-message-p message) (signal 'wrong-type-argument (list 'mixi-message-p message))) - (mixi-message-realize message) + (mixi-realize-message message) (aref (cdr message) 4)) (defun mixi-message-time (message) "Return the date of MESSAGE." (unless (mixi-message-p message) (signal 'wrong-type-argument (list 'mixi-message-p message))) - (mixi-message-realize message) + (mixi-realize-message message) (aref (cdr message) 5)) (defun mixi-message-content (message) "Return the content of MESSAGE." (unless (mixi-message-p message) (signal 'wrong-type-argument (list 'mixi-message-p message))) - (mixi-message-realize message) + (mixi-realize-message message) (aref (cdr message) 6)) (defun mixi-message-set-owner (message owner) @@ -1725,26 +2456,60 @@ Increase this value when unexpected error frequently occurs." (defconst mixi-message-list-regexp "") -(defun mixi-get-introductions (&rest args) - "Get introductions." - (when (> (length args) 2) +(defun mixi-get-introductions (&rest friend-or-range) + "Get introductions of FRIEND." + (when (> (length friend-or-range) 2) (signal 'wrong-number-of-arguments - (list 'mixi-get-introduction (length args)))) - (let ((friend (nth 0 args)) - (max-numbers (nth 1 args))) - (when (or (not (mixi-friend-p friend)) (mixi-friend-p max-numbers)) - (setq friend (nth 1 args)) - (setq max-numbers (nth 0 args))) + (list 'mixi-get-introduction (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* ((regexp (if friend mixi-introduction-list-regexp mixi-my-introduction-list-regexp)) (items (mixi-get-matched-items (mixi-introduction-list-page friend) - max-numbers - regexp))) + regexp + range))) (mapcar (lambda (item) (mixi-make-introduction (or friend (mixi-make-me)) (mixi-make-friend (nth 0 item) @@ -1840,6 +2605,207 @@ Increase this value when unexpected error frequently occurs." (nth 2 item))) items)))) +;; News object. +(defvar mixi-news-cache (make-hash-table :test 'equal)) +(defun mixi-make-news (media-id id &optional media time title content) + "Return a news object." + (mixi-make-cache (list media-id id) + (cons 'mixi-news (vector nil media-id id media time title + content)) + mixi-news-cache)) + +(defconst mixi-news-url-regexp + "/view_news\\.pl\\?id=\\([0-9]+\\)&media_id=\\([0-9]+\\)") + +(defun mixi-make-news-from-url (url) + "Return a news object from URL." + (when (string-match mixi-news-url-regexp url) + (let ((id (match-string 1 url)) + (media-id (match-string 2 url))) + (mixi-make-news media-id id)))) + +(defmacro mixi-news-p (news) + `(eq (mixi-object-class ,news) 'mixi-news)) + +(defmacro mixi-news-page (news) + `(concat "http://news.mixi.jp/view_news.pl?id=" (mixi-news-id ,news) + "&media_id=" (mixi-news-media-id ,news))) + +(defconst mixi-news-title-regexp + "") +(defconst mixi-news-media-time-regexp + "") +(defconst mixi-news-content-regexp + "\\|
\\)") + +(defun mixi-realize-news (news) + "Realize a NEWS." + ;; FIXME: Check a expiration of cache? + (unless (mixi-object-realized-p news) + (with-mixi-retrieve (mixi-news-page news) + (if (string-match mixi-news-title-regexp buffer) + (mixi-news-set-title news (match-string 1 buffer)) + (mixi-realization-error 'cannot-find-title news)) + (if (string-match mixi-news-media-time-regexp buffer) + (progn + (mixi-news-set-media news (match-string 1 buffer)) + (let ((year (nth 5 (decode-time (current-time)))) + (month (nth 4 (decode-time (current-time)))) + (month-of-item (string-to-number (match-string 2 buffer)))) + (when (> month-of-item month) + (decf year)) + (mixi-news-set-time + news (encode-time 0 (string-to-number (match-string 5 buffer)) + (string-to-number (match-string 4 buffer)) + (string-to-number (match-string 3 buffer)) + month year)))) + (mixi-realization-error 'cannot-find-media-time news)) + (if (string-match mixi-news-content-regexp buffer) + (mixi-news-set-content news (match-string 1 buffer)) + (mixi-realization-error 'cannot-find-content news))) + (mixi-object-touch news))) + +(defun mixi-news-media-id (news) + "Return the media-id of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (aref (cdr news) 1)) + +(defun mixi-news-id (news) + "Return the id of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (aref (cdr news) 2)) + +(defun mixi-news-media (news) + "Return the media of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (unless (aref (cdr news) 3) + (mixi-realize-news news)) + (aref (cdr news) 3)) + +(defun mixi-news-time (news) + "Return the time of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (unless (aref (cdr news) 4) + (mixi-realize-news news)) + (aref (cdr news) 4)) + +(defun mixi-news-title (news) + "Return the title of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (unless (aref (cdr news) 5) + (mixi-realize-news news)) + (aref (cdr news) 5)) + +(defun mixi-news-content (news) + "Return the content of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (mixi-realize-news news) + (aref (cdr news) 6)) + +(defun mixi-news-set-media (news media) + "Set the media of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (aset (cdr news) 3 media)) + +(defun mixi-news-set-time (news time) + "Set the time of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (aset (cdr news) 4 time)) + +(defun mixi-news-set-title (news title) + "Set the title of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (aset (cdr news) 5 title)) + +(defun mixi-news-set-content (news content) + "Set the content of NEWS." + (unless (mixi-news-p news) + (signal 'wrong-type-argument (list 'mixi-news-p news))) + (aset (cdr news) 6 content)) + +(defconst mixi-news-category-list '(domestic politics economy area abroad + sports entertainment IT)) + +(defmacro mixi-news-category-p (category) + `(memq ,category mixi-news-category-list)) + +(defun mixi-news-category-id (category) + "Return the id of CATEGORY." + (unless (mixi-news-category-p category) + (signal 'wrong-type-argument (list 'mixi-news-category-p category))) + (number-to-string + (1+ (- (length mixi-news-category-list) + (length (memq category mixi-news-category-list)))))) + +(defconst mixi-news-sort-list '(newest pickup)) + +(defmacro mixi-news-sort-p (sort) + `(memq ,sort mixi-news-sort-list)) + +(defun mixi-news-sort-id (sort) + "Return the id of SORT." + (unless (mixi-news-sort-p sort) + (signal 'wrong-type-argument (list 'mixi-news-sort-p sort))) + (number-to-string + (- (length mixi-news-sort-list) + (length (memq sort mixi-news-sort-list))))) + +(defmacro mixi-news-list-page (category sort) + `(concat "http://news.mixi.jp/list_news_category.pl?page=%d" + "&sort=" (mixi-news-sort-id ,sort) + "&id=" (mixi-news-category-id ,category) + "&type=bn")) + +(defconst mixi-news-list-regexp + " + + + +") + +(defun mixi-get-news (category sort &optional range) + "Get news of CATEGORY and SORT." + (unless (mixi-news-category-p category) + (signal 'wrong-type-argument (list 'mixi-news-category-p category))) + (unless (mixi-news-sort-p sort) + (signal 'wrong-type-argument (list 'mixi-news-sort-p sort))) + (let ((items (mixi-get-matched-items (mixi-news-list-page category sort) + mixi-news-list-regexp + range)) + (year (nth 5 (decode-time (current-time)))) + (month (nth 4 (decode-time (current-time))))) + (mapcar (lambda (item) + (let ((month-of-item (string-to-number (nth 6 item)))) + (when (> month-of-item month) + (decf year)) + (setq month month-of-item) + (mixi-make-news (nth 2 item) (nth 1 item) (nth 5 item) + (encode-time + 0 (string-to-number (nth 9 item)) + (string-to-number (nth 8 item)) + (string-to-number (nth 7 item)) + month year) + (nth 3 item)))) + items))) + (provide 'mixi) ;;; mixi.el ends here
+\\([0-9]+\\)ǯ\\([0-9]+\\)·î\\([0-9]+\\)Æü
+\\([0-9]+\\):\\([0-9]+\\)
+\\(
\\) +[^<]+ : +\\(.*\\) + +\\(| +ºï½ü + +\\|\\)
+ + +
\\(.+\\)
+
\\(.+\\)
") -(defun mixi-get-messages (&rest args) - "Get messages." - (when (> (length args) 2) +(defun mixi-get-messages (&rest box-or-range) + "Get messages of BOX." + (when (> (length box-or-range) 2) (signal 'wrong-number-of-arguments - (list 'mixi-get-messages (length args)))) - (let ((box (nth 0 args)) - (max-numbers (nth 1 args))) + (list 'mixi-get-messages (length box-or-range)))) + (let ((box (nth 0 box-or-range)) + (range (nth 1 box-or-range))) (when (or (not (mixi-message-box-p box)) - (mixi-message-box-p max-numbers)) - (setq box (nth 1 args)) - (setq max-numbers (nth 0 args))) + (mixi-message-box-p range)) + (setq box (nth 1 box-or-range)) + (setq range (nth 0 box-or-range))) (let ((items (mixi-get-matched-items (mixi-message-list-page (when box (mixi-message-box-name box))) - max-numbers - mixi-message-list-regexp))) + mixi-message-list-regexp + range))) (mapcar (lambda (item) (mixi-make-message (nth 0 item) (nth 1 item))) items)))) +(defmacro mixi-post-message-page (friend) + `(concat "/send_message.pl?id=" (mixi-friend-id friend))) + +(defconst mixi-post-message-key-regexp + "") + +(defconst mixi-post-message-succeed-regexp + "Á÷¿®´°Î»¤·¤Þ¤·¤¿¡£") + +(defun mixi-post-message (friend title content) + "Post a message to FRIEND." + (unless (mixi-friend-p friend) + (signal 'wrong-type-argument (list 'mixi-friend-p friend))) + (unless (stringp title) + (signal 'wrong-type-argument (list 'stringp title))) + (unless (stringp content) + (signal 'wrong-type-argument (list 'stringp content))) + (let ((fields `(("subject" . ,title) + ("body" . ,content) + ("submit" . "main"))) + post-key) + (with-mixi-post-form (mixi-post-message-page friend) fields + (if (string-match mixi-post-message-key-regexp buffer) + (setq post-key (match-string 1 buffer)) + (mixi-post-error 'cannot-find-key friend))) + (setq fields `(("post_key" . ,post-key) + ("subject" . ,title) + ("body" . ,content) + ("yes" . "¡¡Á÷¡¡¿®¡¡") + ("submit" . "confirm"))) + (with-mixi-post-form (mixi-post-message-page friend) fields + (unless (string-match mixi-post-message-succeed-regexp buffer) + (mixi-post-error 'cannot-find-succeed friend))))) + ;; Introduction object. (defun mixi-make-introduction (parent owner content) "Return a introduction object." @@ -1816,23 +2581,23 @@ Increase this value when unexpected error frequently occurs."
\\(.+\\)(\\(.+\\) - \\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\))
+ +\\(.+\\) + +? + +\\(
¡¦\\(.+\\) +\\(\\|\\) + +\\(.+\\)\\([0-9]+\\)·î\\([0-9]+\\)Æü \\([0-9]+\\):\\([0-9]+\\)