どうも、左折ができないみやびです。
難しい、車の運転・・・。
エントリーとまったく関係ない話をすると、
(早く本題見せろって人はこちら)
ここ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の構成とか変わったら、書きなおさなきゃいけないんだけどね、これ(汗
とにかく勉強になったばい。
コメントする