let PLUGIN_INFO = {NAME} update Twitter status to current video/search page information and comment. 今見ている動画 / 検索結果の情報を Twitter に投稿する。 janus_wel New BSD License 0.74 2.3pre 2.3pre https://github.com/vimpr/vimperator-plugins/raw/master/matanico.js ; (function () { // class definitions // change XPath query when html changed. function NicoScraper() {} NicoScraper.prototype = { _constants: { VERSION: '0.73', PAGECHECK: [ ['live', '^http://live\\.nicovideo\\.jp/watch/'], ['watch', '^http://[^.]+\\.nicovideo\\.jp/watch/'], ['tag', '^http://[^.]+\\.nicovideo\\.jp/tag/'], ['relatedTag', '^http://[^.]+\\.nicovideo\\.jp/related_tag/'], ], }, version: function () { return this.constants.VERSION; }, pagecheck: function () { const pageCheckData = this._constants.PAGECHECK; const currentURL = this.getURL(); for each (let [name, url] in pageCheckData) { if (currentURL.match(url)) return name; } throw new Error('current tab is not nicovideo.jp'); }, _flvplayer: function () { if (this.pagecheck() === 'watch') { let flvplayer = window.content.document.getElementById('flvplayer'); if (!flvplayer) throw new Error('video player is not found'); return flvplayer.wrappedJSObject || flvplayer; } throw new Error('current tab is not watch page on nicovideo.jp'); }, getURL: function () { return liberator.modules.buffer.URL; }, getSubject: function () { let subjectNode; switch (this.pagecheck()) { case 'watch': subjectNode = $f('id("des_2")/table/tbody/tr/td/h1'); break; case 'live': subjectNode = $f('id("stream_description")'); break; default: break; } if (subjectNode) return subjectNode.textContent; throw new Error('current tab is not watch page on nicovideo.jp'); }, getPlaytime: function () { let p = this._flvplayer(); if (p && p.ext_getTotalTime) { let playtime = Math.round(p.ext_getTotalTime()); let min = Math.floor(playtime / 60); let sec = playtime % 60; if (sec < 10) sec = '0' + sec; return [min, sec].join(':'); } throw new Error('current tab is not watch page on nicovideo.jp'); }, getTagName: function () { if (this.pagecheck() === 'tag') { let wordNodes = liberator.modules.util.evaluateXPath('id("search_words")/span[contains(concat(" ", @class, " "), " search_word ")]'); let words = []; for (let wordNode in wordNodes) words.push(wordNode.textContent); return words.join(' '); } throw new Error('current tab is not tag search page on nicovideo.jp'); }, getNumofVideos: function () { if (this.pagecheck() === 'tag') { let numofVideos = $f('//strong[contains(concat(" ", @class, " "), " result_total ")]'); return numofVideos.textContent; } throw new Error('current tab is not tag search page on nicovideo.jp'); }, getKeyword: function () { if (this.pagecheck() === 'relatedTag') { let keyword = $f('//strong[contains(concat(" ", @class, " "), " search_word ")]'); return keyword.textContent; } throw new Error('current tab is not related tag search page on nicovideo.jp'); }, getNumofTags: function () { if (this.pagecheck() === 'relatedTag') { let numofTags = $f('//strong[contains(concat(" ", @class, " "), " result_total ")]'); return numofTags.textContent; } throw new Error('current tab is not related tag search page on nicovideo.jp'); }, }; function TwitterUpdaterFactory() { let pUsername, pPassword; let pEndPoint = 'https://twitter.com/statuses/update.json'; function TwitterUpdater() {} TwitterUpdater.prototype.update = function (data) { let parameter = 'status=' + encodeURIComponent(data.newStatus); let req = new XMLHttpRequest(); if (req) { req.open('POST', pEndPoint, true, pUsername, pPassword); req.onreadystatechange = function () { if (req.readyState === 4 && req.status === 200) { data.onSuccess(); return; } throw new Error('failure to update status in Twitter. HTTP status code : ' + req.status); } req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); req.send(parameter); } }; TwitterUpdater.prototype.__defineSetter__( 'username', function (username) { pUsername = username; return this; } ); TwitterUpdater.prototype.__defineSetter__( 'password', function (password) { pPassword = password; return this; } ); return new TwitterUpdater(); } // main --- let scraper = new NicoScraper(); liberator.modules.commands.addUserCommand( ['matanico'], 'update Twitter status to current video/search page information and comment', function (args) { let arg = args.string; try { // build post string ----- let postString; // domain check switch(scraper.pagecheck()) { case 'watch': postString = onWatch(scraper, arg); break; case 'live': postString = onLive(scraper, arg); break; case 'tag': postString = onTagSearch(scraper, arg); break; case 'relatedTag': postString = onRelatedTagSearch(scraper, arg); break; default: throw new Error('current tab is not nicovideo.jp'); break; } // ':matanico!' display the evaluated format. if (args.bang) { liberator.modules.util.copyToClipboard(postString, true); return; } // get username/password and set TwitterUpdater let t = TwitterUpdaterFactory(); [t.username, t.password] = getUserAccount({ hostname: 'http://twitter.com/', formSubmitURL: 'https://twitter.com/statuses/update.json', httpRealm: null, description: 'Enter e-mail address and password. This information is cached and use from next time.', }); t.update({ newStatus: postString, onSuccess: function () liberator.echo('Posted ' + postString), }); } catch (e) { liberator.echoerr(e); liberator.log(e); } }, { bang: true, }, true ); // sub function onWatch(scraper, comment) { let format = liberator.globalVariables.matanico_watch_format || '$SERVICENAME : $SUBJECT($PLAYTIME) - $URL $COMMENT'; let serviceName = liberator.globalVariables.matanico_watch_servicename || fromUTF8Octets('またニコニコ動画見てる'); return format.replace(/\$SERVICENAME/g, serviceName) .replace(/\$SUBJECT/g, scraper.getSubject()) .replace(/\$PLAYTIME/g, scraper.getPlaytime()) .replace(/\$URL/g, scraper.getURL()) .replace(/\$COMMENT/g, comment); } function onLive(scraper, comment) { let format = liberator.globalVariables.matanico_live_format || '$SERVICENAME : $SUBJECT - $URL $COMMENT'; let serviceName = liberator.globalVariables.matanico_live_servicename || fromUTF8Octets('またニコニコ生放送見てる'); return format.replace(/\$SERVICENAME/g, serviceName) .replace(/\$SUBJECT/g, scraper.getSubject()) .replace(/\$URL/g, scraper.getURL()) .replace(/\$COMMENT/g, comment); } function onTagSearch(scraper, comment) { let format = liberator.globalVariables.matanico_tag_format || fromUTF8Octets('$SERVICENAME : $TAG($NUMOFVIDEOS件) - $URL $COMMENT'); let serviceName = liberator.globalVariables.matanico_tag_servicename || fromUTF8Octets('またニコニコタグ検索してる'); return format.replace(/\$SERVICENAME/g, serviceName) .replace(/\$TAG/g, scraper.getTagName()) .replace(/\$NUMOFVIDEOS/g, scraper.getNumofVideos()) .replace(/\$URL/g, scraper.getURL()) .replace(/\$COMMENT/g, comment); } function onRelatedTagSearch(scraper, comment) { let format = liberator.globalVariables.matanico_related_tag_format || fromUTF8Octets('$SERVICENAME : $KEYWORD($NUMOFTAGS件) - $URL $COMMENT'); let serviceName = liberator.globalVariables.matanico_related_tag_servicename || fromUTF8Octets('またキーワードでニコニコタグ検索してる'); return format.replace(/\$SERVICENAME/g, serviceName) .replace(/\$KEYWORD/g, scraper.getKeyword()) .replace(/\$NUMOFTAGS/g, scraper.getNumofTags()) .replace(/\$URL/g, scraper.getURL()) .replace(/\$COMMENT/g, comment); } // stuff functions function $f(query, node) { node = node || window.content.document; let result = (node.ownerDocument || node).evaluate( query, node, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); return result.singleNodeValue || null; } function fromUTF8Octets(octets) decodeURIComponent(octets.replace(/[%\x80-\xFF]/g, function (c) '%' + c.charCodeAt(0).toString(16))); function getUserAccount(accountInfo) { // refer: // https://developer.mozilla.org/ja/Using_nsILoginManager let loginManager = Cc['@mozilla.org/login-manager;1'].getService(Ci.nsILoginManager); let logins = loginManager.findLogins( {}, accountInfo.hostname, accountInfo.formSubmitURL, accountInfo.httpRealm ); if (logins.length > 0) { // found return [logins[0].username, logins[0].password]; } else { // not found, so register // refer: https://developer.mozilla.org/Ja/Code_snippets/Dialogs_and_Prompts let promptSvc = Cc['@mozilla.org/embedcomp/prompt-service;1'].getService(Ci.nsIPromptService); let promptUsername = {value: ''}; let promptPassword = {value: ''}; let isOK = promptSvc.promptUsernameAndPassword( window, accountInfo.hostname, accountInfo.description, promptUsername, promptPassword, null, {} ); if (isOK) { let nsLoginInfo = new Components.Constructor( '@mozilla.org/login-manager/loginInfo;1', Ci.nsILoginInfo, 'init' ); // refer: https://developer.mozilla.org/ja/NsILoginInfo loginManager.addLogin(new nsLoginInfo( accountInfo.hostname, accountInfo.formSubmitURL, accountInfo.httpRealm, promptUsername.value, promptPassword.value, '', '' )); return [promptUsername.value, promptPassword.value]; } } throw new Error('account is not found: ' + accountInfo.hostname); } })(); // vim:sw=4 ts=4 et: