※この記事は7年以上前の記事です。
現在は状況が異なる可能性がありますのでご注意ください。
どうも、左折ができないみやびです。
難しい、車の運転・・・。
エントリーとまったく関係ない話をすると、
(早く本題見せろって人はこちら)
ここ2年くらい、ちょいちょい実力試しに、CodeIQってサイトの問題に挑戦したりしてるのだが、
その中で、プログラム言語を擬人化してるって、漫画があるんだけど、それの作者さんがかわいいのなんのって。
ちょまどさん。
イラストレーター・漫画家兼、プログラマーという、
僕と似た方向なので親近感が湧いたという。
(↑おまえは格下だけどな)
ちょまど帳というブログでいろいろやってるので、皆さんも応援&拡散を!
(というかすでにかなり有名ですが)
いつも以上に、前置きひどいね。
(すでに前置きじゃなくなってるという)
さて、本題。
表記の通り、前回のエントリーでは、twitterへ自動ログインまでやったけど、
ログインまでいきゃ、当然投稿までできちゃいます。
今回はそれをやっていこうと。
まずはスクリプト。
前回のスクリプトに様々加えている。
// Google Chromeを定義 var app = Application("Google Chrome"); app.includeStandardAdditions = true; var windowChrome = null, tabFirst = null; if (app.windows.length === 0) { app.Window().make(); } // twitterのURLを定義 var twitterUrlAccess = "https://twitter.com/?lang=ja"; windowChrome = app.windows[0]; tabFirst = windowChrome.tabs[0]; tabFirst.url = twitterUrlAccess; // Chromeをアクティブにする app.activate(); // 次に移るために、フォーム送信が成功したかを格納する変数 var formSubmitFlg = null; // ブラウザ上で実行させる関数を定義 var runFunction = function(){ var loginBtn = document.getElementsByClassName('StreamsHero-buttonContainer').item(0).lastElementChild; loginBtn.click(); var formDom = document.getElementsByClassName('LoginDialog-form').item(0).firstElementChild; var inputDivs = document.getElementsByClassName('LoginForm-input'); var inputDivsUser = document.getElementsByClassName('LoginForm-username'); var inputDivsPass = document.getElementsByClassName('LoginForm-password'); for (var iU = 0; iU < inputDivsUser.length; iU++) { inputDivsUser.item(iU).firstElementChild.value = 'ログインアドレス'; } for (var iP = 0; iP < inputDivsPass.length; iP++) { inputDivsPass.item(iP).firstElementChild.value = 'パスワード'; } formDom.submit(); }; // 投稿するメソッド var runFunctionSubmit = function(){ var newTweetBtn = document.getElementById('global-new-tweet-button'); newTweetBtn.click(); var tweetBlock = document.getElementById('tweet-box-global'); var submitBlock = document.createDocumentFragment(); var text001 = document.createElement('div'), text002 = document.createElement('div'); text001.textContent = 'JXAによるtwitter自動投稿テスト001'; text002.textContent = 'ああああああ'; submitBlock.appendChild(text001); submitBlock.appendChild(text002); tweetBlock.textContent = null; tweetBlock.appendChild(submitBlock); var formDom = tweetBlock.parentNode.parentNode.parentNode.parentNode.parentNode; var formChildBtn = formDom.children.item(2).children.item(1).lastElementChild; var timerF = setTimeout(function(){ document.activeElement.blur(); formChildBtn.click(); }, 500); }; // 関数を一行テキストに var runFunctionStr = runFunction.toString(); runFunctionStr = runFunctionStr.replace(/t/g, ""); runFunctionStr = runFunctionStr.replace(/n/g, ""); var runFunctionSubmitStr = runFunctionSubmit.toString(); runFunctionSubmitStr = runFunctionSubmitStr.replace(/t/g, ""); runFunctionSubmitStr = runFunctionSubmitStr.replace(/n/g, ""); // twitterに投稿するメソッド function twitterSubmit(){ // ログインが完了したかを、タイトルから検知させる if (tabFirst.title() == 'Twitter' && !tabFirst.loading()) { formSubmitFlg = app.execute(tabFirst, {javascript: "(" + runFunctionSubmitStr + ")(); 'true'"}); } else { while (tabFirst.title() != 'Twitter' || tabFirst.loading()) { delay(0.5); if (tabFirst.title() == 'Twitter' && !tabFirst.loading()) { // 投稿メソッドの実行 formSubmitFlg = app.execute(tabFirst, {javascript: "(" + runFunctionSubmitStr + ")(); 'true'"}); break; } } } } // ページが完全に読み込まれるまで、delayを繰り返す while (tabFirst.loading()) { delay(0.5); if (!tabFirst.loading()) { if (tabFirst.title() != 'Twitter') { formSubmitFlg = app.execute(tabFirst, {javascript: "(" + runFunctionStr + ")(); 'true'"}); } twitterSubmit(); break; } }
では、前回と違う部分だけ解説していこうか。
まずは、投稿をさせるスクリプト
// 投稿するメソッド var runFunctionSubmit = function(){ var newTweetBtn = document.getElementById('global-new-tweet-button'); newTweetBtn.click(); var tweetBlock = document.getElementById('tweet-box-global'); var submitBlock = document.createDocumentFragment(); var text001 = document.createElement('div'), text002 = document.createElement('div'); text001.textContent = 'JXAによるtwitter自動投稿テスト001'; text002.textContent = 'ああああああ'; submitBlock.appendChild(text001); submitBlock.appendChild(text002); tweetBlock.textContent = null; tweetBlock.appendChild(submitBlock); var formDom = tweetBlock.parentNode.parentNode.parentNode.parentNode.parentNode; var formChildBtn = formDom.children.item(2).children.item(1).lastElementChild; var timerF = setTimeout(function(){ document.activeElement.blur(); formChildBtn.click(); }, 500); }; // 関数を一行テキストに ~ 中略 ~ var runFunctionSubmitStr = runFunctionSubmit.toString(); runFunctionSubmitStr = runFunctionSubmitStr.replace(/t/g, ""); runFunctionSubmitStr = runFunctionSubmitStr.replace(/n/g, "");
前回同様、ページで実行する関数を作っただけなので、なんのことないのだが、
ポイントは、3点ある。
1点目は、
6行目で、Fragmentのメソッドを使ってるところ。
これは、レンダリングする前に、あらかじめ、仮DOMを生成するというもので、
こいつをエレメントにAppendChildすると、Fragmentの中身が入るってわけで。
これを使う利点としては、レンダリングが一回で済むのでメモリを節約、
どんな要素に対しても、中身のみを挿入できる点だ。
ツイッターの投稿フォームが、textareaではなく、divの入れ子構造だったので、
これを使った。
2点目は、
23行目からで、setTimeoutを使ってる点。
これ、setTimeout使わないと、
どうしても、フォームに書かれたあとってことを検知できない・・・。
そのタイムラグの穴埋めだ。
だから、もし、上の行のappendeChildeのコールバックとかが可能ならそっちを使った方がいい。
実際、回線状況とかによっては、処理タイミングがおかしくなりそうだ。
3点目は、
24行目で、document内でフォーカスされている全てのエレメントからフォーカスを解除している。
これをやることによって、ツイッターの投稿内容を反映させるJS(ツイッターの仕様)が起動する。
さて、投稿関数はこれでオーケーなので、
あとは、どう実行するか、だ。
// twitterに投稿するメソッドを実行するメソッド function twitterSubmit(){ // ログインが完了したかを、タイトルから検知させる if (tabFirst.title() == 'Twitter' && !tabFirst.loading()) { formSubmitFlg = app.execute(tabFirst, {javascript: "(" + runFunctionSubmitStr + ")(); 'true'"}); } else { while (tabFirst.title() != 'Twitter' || tabFirst.loading()) { delay(0.5); if (tabFirst.title() == 'Twitter' && !tabFirst.loading()) { // 投稿メソッドの実行 formSubmitFlg = app.execute(tabFirst, {javascript: "(" + runFunctionSubmitStr + ")(); 'true'"}); break; } } } }
さてこっからが大事。
JXAから、投稿を実行させるのだが、
まず、ログインが完了しているかを、JXA上で検知させる必要がある。
(トップと、ログイン後じゃ、DOM構成全然違うからね)
ひとまず、ツイッターは、ログイン前と後では、titleが違うので、そいつを使って検知させる。
例の、条件合うまで、whileでdelayを実行して、処理を遅らせ続けるあれだ。
4行目のifで、タイトルがTwitterであるかつ、ローディングが完了してたら、
そのまま投稿メソッドを実行させている。
違ったら、while開始だ。
ここで、なぜ、また.loadingを使って、読み完了してるかをとっているいるというと、
ログインを常にするって状態の場合、
タイミング的に、ローディングが完了する前に、こっちの関数にきてしまうので、
こっちでもローディング完了チェックが必要なのだ。
では、最後に、前回のwhile文を少し変更する
// ページが完全に読み込まれるまで、delayを繰り返す while (tabFirst.loading()) { delay(0.5); if (!tabFirst.loading()) { if (tabFirst.title() != 'Twitter') { formSubmitFlg = app.execute(tabFirst, {javascript: "(" + runFunctionStr + ")(); 'true'"}); } twitterSubmit(); break; } }
前回と違う点は、 ローディングが完了したら、すぐさまログインメソッドを実行してたが、 今回は、自動ログインされた時のことを考え、 (前回のやつでも考えるべきだったという) titleが、Twitterでない時にログイン、 そのまま、投稿メソッドを実行している。
前回は、ログインだけだったので、
使い道微妙でしたが、
投稿までいけば、たとえば、Macの起動時に、スクリプトを実行するようにしておけば、
起動時に、Mac起動!今日も頑張るぞ!とか、自動でツイートできるわけですよ。
(ただし、ツイッターは基本同じ文言のツイートはできないので、毎日内容変わるスクリプトを入れる必要がある)
PCからもしょっちゅうツイートする人とか、ぜひやってみてはいかがでしょうか。
ただ実際、ツイッターの仕様とか、DOMの構成とか変わったら、書きなおさなきゃいけないんだけどね、これ(汗
とにかく勉強になったばい。
コメントする