Twitterの新しいツイートを読ませるGreasemonkeyスクリプト

Twitterで新しいツイートがあるとそれを自動で読み込ませ、
それをボイス・ソムリエで読ませるスクリプト


ボイス・ソムリエのページのiframeを追加してpostMessageを使って操作する。


音声の長さは1文字0.25秒と推定して音声を流す。
場合によっては短すぎる場合もあるかもしれない。


2,3日前の朝のニュースでTwitterの新しいツイートを読む商品の紹介を見かけて
GreaseMonkeyで作れないかと思って作ってみた。


フォローしている人が多い人は、新しいTweetの読み込みに音声の再生が
追いつかないと思います。VOICE_SPEEDとかを調整して下さい。


// ==UserScript==
// @name twitter voice reader
// @namespace http://d.hatena.ne.jp/kiyo_hoge/
// @include http://twitter.com/*
// @include http://voice-demo.hitachi-skb.co.jp/voice_neo/vsdemo.aspx
// ==/UserScript==

(function() {
location.href = 'javascript:(' +
((document.URL.indexOf('http://voice-demo.hitachi-skb.co.jp/voice_neo/vsdemo.aspx') == 0) ?
(function() {
if (parent == self || !parent.postMessage) {
return;
}

var voiceElm = document.evaluate(
'//embed[@src and not(@src="")]',
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null).singleNodeValue;

if (voiceElm) {
setTimeout(function() {
parent.postMessage(voiceElm.src, 'http://twitter.com');
}, (document.getElementById('WriteText').value.length * 0.25 * 1000) /
document.getElementById('SpeedText').value);
}
else {
parent.postMessage('loaded', 'http://twitter.com');
}

window.addEventListener('message', function(evt) {
if (evt.origin != 'http://twitter.com') {
return;
}

var opt = JSON.parse(evt.data);
if (opt.type) {
var voiceRadio = document.getElementById(opt.type);
if (voiceRadio &&
voiceRadio.nodeName.toLowerCase() == 'input' &&
voiceRadio.type == 'radio') {
voiceRadio.checked = 'checked';
}
}

if (opt.speed) {
document.getElementById('SpeedText').value = opt.speed;
}

document.getElementById('WriteText').value = opt.text;
document.getElementById('PlayButton').click();
}, false);
}).toString() :
(function() {
const VOICE_TYPE = 'WomanVoice1'; // 'WomanVoice1', 'ManVoice1'
const VOICE_SPEED = 1.0; // 0.5 - 3.0

var voiceWindow;
var newTweetsList = [];
var audioElm = [];
var lastTweetIdOnRefresh;
var posted = false;

var newResultsNotification = document.getElementById('new_results_notification');
var timeline = document.getElementById('timeline');

if (!newResultsNotification || !timeline) {
return;
}

var sendTexts = function() {
if (!voiceWindow || newTweetsList.length == 0) {
return;
}

voiceWindow.postMessage(JSON.stringify({
type: VOICE_TYPE,
text: newTweetsList.shift().replace(/[?!]/g, ''),
speed: VOICE_SPEED
}), 'http://voice-demo.hitachi-skb.co.jp');

voiceWindow = void 0;
};

newResultsNotification.addEventListener('DOMNodeInserted', function() {
setTimeout(function() {
var e = document.createEvent('MouseEvents');
e.initEvent('click', true, true);
document.getElementById('results_update').dispatchEvent(e);
}, 0);
}, false);

timeline.addEventListener('DOMNodeInserted', function() {
setTimeout(function() {
var lastElmOnRefresh = document.getElementsByClassName('last-on-refresh');
if (lastElmOnRefresh.length == 0) {
return;
}

var newLastTweetIdOnRefresh = lastElmOnRefresh[0].id.split('_')[1];
if (lastTweetIdOnRefresh && (lastTweetIdOnRefresh >= newLastTweetIdOnRefresh)) {
return;
}

lastTweetIdOnRefresh = newLastTweetIdOnRefresh;
var newTweets = document.evaluate(
'id("timeline")/li[not(contains(concat(" ", @class, " "), " buffered ")) and substring-after(@id, "_") >= ' +
lastTweetIdOnRefresh + ' and .//span[@class="entry-content"] and .//a[contains(concat(" ", @class, " "), " screen-name ")]]',
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null);

for (var i = newTweets.snapshotLength - 1; i >= 0; i--) {
var tweet = newTweets.snapshotItem(i);
newTweetsList.push(tweet.getElementsByClassName("screen-name")[0].textContent +
' ' + tweet.getElementsByClassName("entry-content")[0].textContent);
}
sendTexts();
}, 0);
}, false);

window.addEventListener('message', function(evt) {
if (evt.origin != 'http://voice-demo.hitachi-skb.co.jp') {
return;
}

voiceWindow = evt.source;
sendTexts();
}, false);

newResultsNotification.style.display = 'none';
var iframe = document.createElement('iframe');
iframe.width = '1px';
iframe.height = '1px';
iframe.src = 'http://voice-demo.hitachi-skb.co.jp/voice_neo/vsdemo.aspx';
iframe.style.visibility = 'hidden';
iframe.style.position = 'absolute';
document.body.appendChild(iframe);
}).toString()) +
')();';
})();

とりあえずFirefox 3.6.9とGoogle Chrome 5.0で動作を確認。


はてなダイアリーだと「*1」で囲むと脚注になるのか。どうしたらならないんだろ。

*1:」と 「