今朝
HootSuiteからFacebookとTwitterの両方につぶやこうと思ったらFacebookにポストできなくて、調べたらFacebookのポリシーが変わったみたいで、その影響で今月からHootSuiteからもIFTTTからも連携がなくなったみたい。
ちぇっ。両方に書くのめんどくさいなー。
ってことで
Chrome Extensionを作って遊んでた。
https://github.com/bufferings/fbtw
ポップアップにつぶやきたいことを入れてボタンを押したら、単純に、TwitterとFacebookの画面を開いてつぶやきを貼ってボタンを押して閉じる、というのを自動でやってくれる感じ。エラー処理も何も入れてないから、ログインしてなかったらだめだし、タイムアウトとか文字数オーバーとかもあれだけど。まぁ、満足した。たまに気が向いたら使ってみよっと。
使ってみたいなーと思う人は、ソースを読んで「あほなことやってるなーこいつー」ってのが分かってから使ってください。
そもそも
今朝つぶやきたかったのは、これ。つぶやけて満足した。
お盆休みはCakePHPの勉強をするー(๑•̀ㅂ•́)و✧
— Mitsuyuki Shiiba (@bufferings) 2018年8月11日
でも、もう今日は眠いから勉強は明日からだな。おやすみー。
起きてから追記
Chrome拡張のこと折角だからメモっておくかと思って追記。たまに遊びで作るぐらいなので全然分かんなくて、公式ドキュメント読みながらちょっとずつやりました。
やりたいのは
ってだけ。
マニフェストファイルは
こんな感じ。
{ "manifest_version": 2, "name": "fbtw", "version": "0.0.1", "icons" : { "128": "icon.png" },
"browser_action": { "default_icon": "icon.png", "default_title": "fbtw", "default_popup": "popup.html" },
- 特定のページにどうこうしたいわけじゃないから
page_action
じゃなくてbrowser_action
で popup.html
ってファイルにポップアップのことを書いてる
"background": { "scripts": ["background.js"], "persistent": false },
- 最初、ポップアップの中で全部やろうとしてたら「ツイートしようとしてるけど、もう閉じてるよ!」みたいに怒られて「おー、ポップアップって閉じたらそこで終わるってことか。そうか。」ってなって
background.js
に処理を書くことにして - なんか、
persistent
をfalse
にすると常駐せずに、イベントページと呼ばれるものになるらしく。そっちのがメモリとかに優しいから推奨されてるみたいなので、そっちにしといた。
"permissions": [ "<all_urls>", "tabs", "activeTab", "storage" ] }
- パーミッションはとりあえずタブ周りかなと
- URLは、TwitterとFacebookにだけスクリプトを挿入するのでそこだけでいいんだけど、個人用だしまいっかと思って全部許可しといた
- それから、書きかけのままポップアップを閉じてしまったら消えるの悲しいので
storage
も使うことにした
ポップアップ
見た目それっぽくなるようにふわっと書いた。この記事の上の方に貼ってる画像みたいなの。
<!DOCTYPE html> <html> <head> <style> body { height: 145px; } button { margin-top: 5px; height: 30px; width: 90px; outline: none; float: right; } textarea { height: 100px; width: 400px; } </style> </head> <body> <textarea id="tweet_area"></textarea> <button id="tweet_button" disabled>Tweet</button> <script src="popup.js"></script> </body> </html>
んで popup.js
を呼んでる。JavaScriptの流儀が良くわからないのでドキドキしながら。
let tweetButton = document.getElementById("tweet_button"); let tweetArea = document.getElementById("tweet_area");
let
をよく見かけたので使っといた。 var
ってもう使わないのかな?それか使い分けがあるのかな?よく分かってない。
tweetButton.onTweetChanged = function(){ tweetButton.disabled = (tweetArea.value.length === 0); }
ボタンに関数くっつけといたらいっかなと思って。onTweetChanged
をくっつけといた。こういうことが流儀的に許されるのかどうかよく分かってない。
入力されてたらボタンを押せるようにしてるだけ。
tweetButton.onclick = function(){ chrome.runtime.sendMessage({type:"tweet", tweet:tweetArea.value}); tweetArea.clearTweet(); window.close(); };
クリックされたら sendMessage
ってのでイベントページにメッセージを送って、成否に関係なくテキストエリアをクリアして閉じてる。へー。やりとりはメッセージでやるのかー。
tweetArea.onkeyup = function(){ chrome.storage.local.set({tweet:tweetArea.value}); tweetButton.onTweetChanged(); }
入力内容が変わるたびにローカルのストレージに保存してる。書いてる途中でポップアップを閉じてしまっても次に開いたら続きが書けるように。
tweetArea.clearTweet = function(){ chrome.storage.local.remove("tweet"); tweetArea.value = ""; tweetButton.onTweetChanged(); }
ボタンを押したときにテキストエリアをクリアする処理をテキストエリア自身にくっつけといた。ローカルストレージの内容もクリア。
chrome.storage.local.get({tweet:""}, function(result){ tweetArea.value = result.tweet; tweetButton.onTweetChanged(); });
ポップアップを開いたときに、ローカルストレージからツイートを引っ張ってきて初期値にしてる。ツイートが保存されてなかったときに undefined
って表示されてしまってたから {tweet:""}
で初期値を空文字にした。
イベントページ
ってことで、ポップアップから sendMessage
で送られてきたツイートをTwitterとFacebookに送るのをイベントページ(background.js)でやればOK。
これがメッセージハンドラー:
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse){ if(request.type === "tweet") { postToTwitter(request.tweet); postToFacebook(request.tweet); } if(request.type === "close_tab") { chrome.tabs.remove(request.tabid); } } );
メッセージを受け取って、タイプが tweet
の場合にはそれをTwitterとFacebookにポストする関数を呼び出してる。 close_tab
はタブを閉じるんだけど、呼び出してる場所は後で。
これがTwitter用
let postToTwitter = function(tweet){ chrome.tabs.create({ active: false, url: "https://twitter.com/intent/tweet?text=" + encodeURI(tweet) }, function(tab){ setTimeout(function(){ chrome.tabs.executeScript(tab.id, { code: "document.getElementById('update-form').submit();", runAt: "document_end" }, function(){ setTimeout(function(){ chrome.tabs.remove(tab.id); }, 5000); }); }, 1000); }); };
URLで内容を指定できるページがあったから、新しいタブを開いてそのページを開くことにした。ページを開いたら、1秒待ってからスクリプトを差し込む。ボタンを押すスクリプト。んで、差し込んでから5秒待ってタブを閉じてる。
最初は、ボタンを押した後に少し待ってから「ボタン押したよ!タブクローズしていいよ!」ってメッセージをイベントページに送るスクリプトも合わせて差し込もうとしたんだけど、メッセージが届かないなーって思ってみてたら、ボタンを押したら別のページに遷移するから差し込んだスクリプトがなくなってしまってたんだった。
ので、ちょっと残念だけど、ツイート成功の画面に遷移したことを検知してから閉じるでもなく、ボタンを押してから5秒でもなく、スクリプトを差し込んでから5秒になった。
これがFacebook用
let postToFacebook = function(tweet){ chrome.tabs.create({ // Need to activate the page to activate the button. active: true, url: "https://facebook.com" }, function(tab){ setTimeout(function(){ chrome.tabs.executeScript(tab.id, { file: "forFacebook.js", runAt: "document_end" }, function(){ chrome.tabs.sendMessage(tab.id, {tabid:tab.id, tweet:tweet}); }); }, 1000); }); };
Facebookはもうちょっと面倒で。どうも、画面が実際に見られてないと動かないみたいなので、chrome.tabs.create
のオプションで active: true
を指定して、開いたタブがアクティブになるようにした。
Twitter用のと違って、ちょっとだけ複雑な処理をするから forFacebook.js
ってファイルに書いてそれを差し込むことにした。なので、ツイートの内容は別途そのスクリプトに伝えてあげないといけなくて、sendMessage
で送ることにした。
forFacebook.js
はこんな感じ:
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse){ let box = document.getElementsByName("xhpc_message")[0]; box.value = request.tweet; box.focus(); setTimeout(function(){ let button = document.querySelector("button[data-testid='react-composer-post-button']"); button.click(); setTimeout(function(){ chrome.runtime.sendMessage({type:"close_tab", tabid:request.tabid}); }, 3000); }, 5000); } );
テキストボックスに値を入れて、フォーカスを当ててしばらくしたらボタンが押せるようになるので、5秒待ってからボタンをクリックして、その後3秒したら「タブを閉じてー!」ってメッセージをイベントページに送って、イベントページのメッセージハンドラーでタブを閉じてる。
という流れ
エラーハンドリングとか考え出すとお盆休みが終わってしまいそうなので、あきらめた。
途中で、Promise使ったらもうちょっとキレイに書けるかなー?と思って調べてたら横道にそれて =>
みたいなの出てきて「これでも関数書けるのかー!」ってやってみたりしつつ、んー、読みやすいのか読みにくいのか分からんなーってなったり、結局Promise使うほどじゃないかってなってやめたりして。
ひとつツイートするのに一日かかったんだけど、まぁ、色々楽しかった。