mixi メッセージにはたらく Greasemonkey かきなおした
2013/03/26 追記。 mixi メッセージの仕様変更により、現在は動作しない。書き直したいが、受信はともかく送信メッセージの単体表示が見当たらないので悩んでいる。
先日のエントリの続き。 Datula 1.52.01.01 でも動作を確認したので貼っておく。
なお Base64 へのエンコードと utf-16 を utf-8 へ変換する関数は高度な JavaScript 技集よりいただきました。助かりました。
相変わらず汚いソースだなあ。
// ==UserScript== // @name mixi message export helper // @namespace http://d.hatena.ne.jp/naglfar/ // @description v0.04 // @include http://mixi.jp/view_message.pl* // ==/UserScript== (function() { /* * 変換関数はこちらのサイトよりいただきました。感謝します。 * 高度な JavaScript 技集 * <http://www.onicos.com/staff/iz/amuse/javascript/expert/> */ { /* * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp> Version: 1.0 * LastModified: Dec 25 1999 This library is free. You can redistribute * it and/or modify it. */ /* * Interfaces: b64 = base64encode(data); */ var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; function base64encode(str) { var out, i, len; var c1, c2, c3; len = str.length; i = 0; out = ""; while (i < len) { c1 = str.charCodeAt(i++) & 0xff; if (i == len) { out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt((c1 & 0x3) << 4); out += "=="; break; } c2 = str.charCodeAt(i++); if (i == len) { out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt((c2 & 0xF) << 2); out += "="; break; } c3 = str.charCodeAt(i++); out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); out += base64EncodeChars.charAt(c3 & 0x3F); } return out; } /* * utf.js - UTF-8 <=> UTF-16 convertion * * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp> Version: 1.0 * LastModified: Dec 25 1999 This library is free. You can redistribute * it and/or modify it. */ /* * Interfaces: utf8 = utf16to8(utf16); */ function utf16to8(str) { var out, i, len, c; out = ""; len = str.length; for (i = 0; i < len; i++) { c = str.charCodeAt(i); if ((c >= 0x0001) && (c <= 0x007F)) { out += str.charAt(i); } else if (c > 0x07FF) { out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F)); out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F)); out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); } else { out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F)); out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); } } return out; } } const myname = "John Doe"; const myid = "identification"; const htmlHeader = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>'; const htmlFooter = '</body></html>'; const crlf = "\r\n"; var container = document.getElementById("messageDetail"); var headers = document.getElementsByTagName("dd"); var address = headers[1].getElementsByTagName("a")[0].getAttribute("href"); var senddate = parseDate(headers[0].firstChild.nodeValue); main(); function main() { var parent = document.createElement("form"); var filename = document.createElement("input"); var body = document.createElement("textarea"); var newline = document.createElement("br"); var separator = document.createElement("hr"); filename.setAttribute("type", "text"); filename.setAttribute("size", "70"); filename.setAttribute("onfocus", "select();"); filename.setAttribute("value", getFileName()); body.setAttribute("rows", "20"); body.setAttribute("cols", "70"); body.setAttribute("onfocus", "select();"); body.innerHTML = getTextAsMailFormat(); parent.appendChild(filename); parent.appendChild(newline); parent.appendChild(body); container.appendChild(separator); container.appendChild(parent); } function getFileName() { return (+address.substring(address.indexOf("id=") + 3) + '_' + senddate.getFullYear() + padZero(senddate.getMonth() + 1) + padZero(senddate.getDate()) + padZero(senddate.getHours()) + padZero(senddate.getMinutes()) + '.eml'); } /** * メッセージ本文がすでにエンティティ参照になっているので * TextNode として追加するとエンティティ参照の & が & へ変換される * かといってエンティティ参照を文字へ戻せばタグと混ざる * よって textarea の innerHTML へ設定するようにテキストを作る */ function getTextAsMailFormat() { var escapeNegligence = function(s) { return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' '); }; var encodeToHeader = function(s) { return '=?UTF-8?B?' + base64encode(utf16to8(s)) + '?='; }; var vt = document.getElementsByTagName("h3")[0].firstChild.nodeValue; var vhb = document.getElementById("message_body").innerHTML; var vf = encodeToHeader(escapeNegligence(headers[1] .getElementsByTagName("a")[0].firstChild.nodeValue)) + " <http://mixi.jp/" + address + ">" var vm = encodeToHeader(myname) + " <http://mixi.jp/show_friend.pl?id=" + myid + ">"; var isInbox = (document.location.search.indexOf("box=inbox") >= 0); var vi = document.location.search.substring(document.location.search .indexOf("id=") + 3, document.location.search.indexOf("box") - 1); var boundary = "----=_NextPart_MIXI_MESSAGE_" + vi; var vo = ""; vo += "Message-ID: <" + vi + "@mixi.jp>" + crlf; vo += "Date: "; vo += formatDate(senddate) + crlf; vo += "From: " + (isInbox ? vf : vm) + crlf; vo += "To: " + (isInbox ? vm : vf) + crlf; vo += "Subject: "; vo += encodeToHeader(vt) + crlf; vo += 'Content-Type: multipart/alternative; boundary="' + boundary + '"' + crlf; vo += crlf; vo += "--" + boundary + crlf; vo += 'Content-Type: text/plain; charset="utf-8"' + crlf; vo += crlf; vo += vhb.replace(/[\r\n]/g, '').replace(/<br[ /]*>/g, '\r\n').replace( /<img src="http:\/\/img\.mixi\.net\/img\/emoji\//gi, '[m:') .replace(/\.gif"[^>]*>/g, ']').replace(/<[^>]+>/g, '') + crlf; vo += crlf; vo += "--" + boundary + crlf; vo += 'Content-Type: text/html; charset="utf-8"' + crlf; vo += crlf; vo += escapeNegligence(htmlHeader) + crlf; vo += escapeNegligence(vhb) + crlf; vo += escapeNegligence(htmlFooter) + crlf; vo += crlf; vo += "--" + boundary + "--" + crlf; return vo; } function padZero(s) { return (s < 10 ? '0' + s : s); } function parseDate(s) { var result = new Date("1900/01/01 00:00:00"); result.setFullYear(s.substring(0, 4)); result.setMonth(s.substring(5, 7) - 1); result.setDate(s.substring(8, 10)); result.setHours(s.substring(12, 14)); result.setMinutes(s.substring(15, 17)); return result; } function formatDate(d) { var weekdays = new Array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); var monthes = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); var result = ""; result += weekdays[d.getDay()]; result += ", " result += padZero(d.getDate()); result += " "; result += monthes[d.getMonth()]; result += " "; result += d.getFullYear(); result += " "; result += padZero(d.getHours()); result += ":"; result += padZero(d.getMinutes()); result += ":"; result += padZero(d.getSeconds()); result += " +0900"; return result; } })();
バージョンアップしてしまった。といっても、 plain text 部のタグを削除するようにしただけだけれど。
絵文字のサーバが mixi.jp から mixi.net になっていたのでリテラルの内容を変更した。……どっちでも動作するようにしておくべきかな。とりあえずの対応。