twitterをなんかするやつ

検索すると色々出てくるけど、シンプルにシェルスクリプトというのがよさげ。

基本的なアイデアは生きてるけど、今のtwitterの仕様では動画がダウンロードできないらしい。

GIF動画についてはこちらのページのやり方でいける。

mp4の動画はどうしようかと思って色々と悩んだ結果、youtube-dlに任せることにした。名前からしYoutube専用なのかと思ったら色々なサイトから動画がダウンロードできて便利ね。

youtube-dl

あとは不具合ではないんだけど、-oオプション(:orig)を付けた際にファイル名が "*.jpg%3Aorig" みたいになって見にくいのを直した。

全体的にはこんな感じか。時間があったら後で備忘録として実装の説明を書いておきたい。

youtube-dlにcookieを渡してないので、ログインしないとダメなヤツは保存できない。ま、必要になったら追加しよう・・・

$ diff -u berryjack berryjack.org
--- berryjack   2017-10-18 02:12:01.985022700 +0900
+++ berryjack.org       2017-10-17 23:34:22.090356500 +0900
@@ -4,9 +4,6 @@
 SCRIPT_DIR=$(cd $(dirname $0);pwd)
 source "$SCRIPT_DIR/common.sh"

-YOUTUBE_DL=$SCRIPT_DIR/youtube-dl
-FFMPEG=$SCRIPT_DIR/ffmpeg
-
 function get_profile_image()
 {
   src="$1"
@@ -130,31 +127,6 @@
       #   skip=1
       # fi
     done
-
-    # video(2) - GIF
-    # cf. http://www.unknownengineer.net/entry/2017/06/03/193913
-    for video in $(grep -Eo 'https://pbs\.twimg\.com/tweet_video_thumb/[a-zA-Z0-9_\-]+\.(jpg|png)' $tmp |sort |uniq |sed -e "s/pbs.twimg.com\/tweet_video_thumb/video.twimg.com\/tweet_video/" | sed -e "s/jpg/mp4/")
-    do
-      exist "$dir" "$video"
-    done
-
-    # video(3) - MP4(HLS)
-    # save sumbnails
-    for image in $(grep -Eo 'https://pbs\.twimg\.com/ext_tw_video_thumb/[0-9]+/[a-z]+/[a-z]+/[a-zA-Z0-9_\-]+\.(jpg|png)' $tmp)
-    do
-      exist "$dir" "$image"
-    done
-    # save videos
-    for video in $(grep -Eo 'data-conversation-id="[0-9]+"|ext_tw_video_thumb' $tmp | grep -B 1 ext_tw_video_thumb | grep data-conversation-id | grep -Eo '[0-9]+' | sed -e "s/^/https\:\/\/twitter.com\/$id\/status\//")
-    do
-      m3u8url=$($YOUTUBE_DL -g $video)
-      video2=$(echo $(basename $m3u8url) | sed -e 's/\.m3u8/.mp4/')
-      exist "$dir" "$video2" > /dev/null
-      if [ $? -eq 0 ]; then
-        echo $m3u8url
-      fi
-    done
-
     # image
     for image in $( grep -Eo 'https://pbs\.twimg\.com/media/[a-zA-Z0-9_\-]+\.(jpg|png)' $tmp | sort | uniq | \
       (if [ $orig -eq 0 ]; then
@@ -164,18 +136,9 @@
       fi
       ) )
     do
-      if [ $orig -eq 0 ]; then
-        exist "$dir" "$image"
-        if [ $? -ne 0 ]; then
-          skip=1
-        fi
-      else
-        exist "$dir" "$(echo $(basename $image) | sed -e 's/:orig$//')" > /dev/null
-        if [ $? -ne 0 ]; then
-          skip=1
-        else
-          echo $image
-        fi
+      exist "$dir" "$image"
+      if [ $? -ne 0 ]; then
+        skip=1
       fi
     done
     if [ $force -ne 1 -a $skip -eq 1 ]; then
@@ -276,17 +239,7 @@
     fi
     for media_url in $(get_media_url $id $get_orig "$cookie_file" $userdir $force)
     do
-      echo $media_url | grep -E '\.m3u8' > /dev/null 2>&1
-      if [ $? -eq 0 ]; then
-        $YOUTUBE_DL -o "${userdir:+${userdir}/}%(id)s.%(ext)s" --ffmpeg-location $FFMPEG $media_url
-      else
-        echo $media_url | grep -E ':orig$' > /dev/null 2>&1
-        if [ $? -eq 0 ]; then
-          wget $cookie_opt -nc -nv -P "$userdir" -O ${userdir:+${userdir}/}$(echo $(basename $media_url) | sed -e 's/:orig$//') "$media_url"
-        else
-          wget $cookie_opt -nc -nv -P "$userdir" "$media_url"
-        fi
-      fi
+      wget $cookie_opt -nc -nv -P "$userdir" "$media_url"
     done
   else
     get_media_url $id $get_orig "$cookie_file" "" $force

return; と return undef; の違い

use strict;
use warnings;
use Smart::Comments;

my %n = (val=>return_nothing());
my %u = (val=>return_undef());
### %n
### %u

sub return_nothing{
  return;
}
sub return_undef{
  return undef;
}
$ perl a.pl
Odd number of elements in hash assignment at /home/tmp/a.pl line 5.

### %n: {
###       val => undef
###     }
### %u: {
###       val => undef
###     }

ただの return; だと、ハッシュの初期化に使った場合 "Odd number of elements in hash assignment" などという警告が出るみたい。

結果はどっちでも同じなんだけど、明示的にreturn undef;としておいたほうが良い(場合もある)みたい。

追記

調べてみると結構おなじようなこと考えてる人がいた。

Use a bare return to return failure
返り値で失敗を報告する時は、裸の return をもってせよ

404 Blog Not Found:perl - (undef) is true

ふーむ。

print "return_nothing is " . ((return_nothing()) ? "TRUE" : "FALSE") . "\n";
print "return_undef is " . ((return_undef()) ? "TRUE" : "FALSE") . "\n";
return_nothing is FALSE
return_undef is FALSE
$ perl -v
This is perl 5, version 22, subversion 2 (v5.22.2) built for cygwin-thread-multi

ふーむ。。

return undefしてもfalseになるっぽいけど、バージョン依存なんだろうか。

まあ何にせよ、成功・失敗を返り値で判断するような場合はただのreturn、値を返すときはreturn undefと使い分ける感じが良いということかな。

dropbox-api-commandのファイル更新判定を変更する

Perlで実装されたDropbox操作コマンドです。

ディレクトリのsyncのために定期的にcronで実行して便利に使わせていただいていたのですが、ファイルの更新を「サイズが違う」あるいは「タイムスタンプが違う」のどちらかで判定しているので、古いファイルに同期してしまう問題があります。

普段は古いファイルをダウンロード(sync)される前に手動でpushして対応していたのですが、先ほど油断した隙に1時間の作業を全部ロールバックされてしまったので、さすがに何とかすることにしました。

   1114 sub has_change ($$) {
   1115     my ($local_path, $content) = @_;
   1116
   1117     my $remote_epoch = $strp->parse_datetime($content->{client_modified})->epoch;
   1118     my $local_epoch = $local_path->stat->mtime;
   1119     my $remote_size = $content->{size};
   1120     my $local_size = $local_path->stat->size;
   1121
   1122     if ($debug) {
   1123         printf "remote: %10s %10s %s\n", $remote_epoch, $remote_size, $content->{path_display};
   1124         printf "local:  %10s %10s %s\n", $local_epoch, $local_size, decode('locale_fs', $local_path);
   1125     }
   1126
   1127     if (($remote_size != $local_size) || ($remote_epoch != $local_epoch)) {
   1128         return 1;
   1129     }
   1130
   1131     return;
   1132 }

問題の部分はここ。

   1114 sub has_change ($$$) {
...
   1127 #    if (($remote_size != $local_size) || ($remote_epoch != $local_epoch)) {
   1128     if ($remote_size != $local_size){
   1129         if($is_upload){
   1130             return 1 if $remote_epoch < $local_epoch;
   1131         }
   1132         else{ # download
   1133             return 1 if $remote_epoch > $local_epoch;
   1134         }
   1135     }
   1136     return;
   1137 }

has_changedはダウンロードとアップロードの2箇所呼ばれるので、「ファイルが新しい場合に更新」を実現させようとすると不等号の向きが逆になります。のでフラグを追加。

$ grep -nE "has_change|^sub sync_upload|sub sync_download" /usr/local/bin/dropbox-api
720:sub sync_download {
764:            if ((!-f $local_path) || has_change($local_path, $content, 0)) {
872:sub sync_download_file {
884:    if ((!-f $local_path) || has_change($local_path, $content, 0)) {
927:sub sync_upload {
976:                if (has_change($local_path, $content, 1)) {
1050:sub sync_upload_file {
1091:        if (has_change($local_path, $content, 1)) {

Perlで配列の値をハッシュにしたいような時

表現が難しいんだけど

my @value = (1, 2, 3, 4, 5);

みたいなデータがあって、それをハッシュとして初期化したいときはどうするか。

my %hash = (
  key1 => $value[0],
  key2 => $value[1],
..
..
);

とかするのだろうけど、配列の個数が何個もあると面倒くさいなぁってなる。

例では5個だけど、10個も20個も項目があるとちょっと厳しい。

実はもっと簡単に

my %hash;
@hash{qw/key1 key2 key3 key4 key5/} = @value;

みたいな感じで書ける。ただし代入はできるけど、初期化でやろうとするとコンパイルエラーになる。

数年前に見かけて何じゃそりゃって思ったけど便利な書き方なのでメモしておく。でもこれ、数年後にコード読み返したら分からなくなりそうだな。

bio監視する的な

推しのプロフィールの更新をチェックするツール、bioischanged.comみたいなサービスがあるみたいだけど、どこまで変更を細かくチェックするのか分からなかったので、自分でもちょっと作ってみた。

cronに登録して放っておくとDMが来る。ネトストぽい。。

別にJSONでなくても良かった気がする。まあいっか。

追記

100年ぶりにgistにアクセスしたらアカウントがロボット判定されたらしく強制プライベートモード?みたいな感じになってた。

フォームから連絡したら1時間も経たずにゴメンゴメン直しておいた的な返事がきてスゴイなって思った。

追記2

"This request looks like it might be automated. To protect our users from spam and other malicious activity, we can't complete this action right now. Please try again later." などというエラーが出てDMを送れなくなってしまった。どうすりゃいいのこれ。。

とりあえずDMじゃなくてメールを送る方式に変更した。

追記3


エモい(;ω;)(;ω;)(;ω;)

追記4


お疲れ様でした。

作成から1週間でcronから外される運命のスクリプトでしたが、プログラムが多少なりとも書けて良かったと思った。

twitterを掘る

通常twitterでは3200くらいしか過去のツイートを遡れません。とはいえ全ツイートを保存したい。

そんなときは、"from:XXXX since:YYYY until:ZZZZ" でツイッターを検索すると好きな期間のツイートを取得できます。

例えばこんな感じ。

https://twitter.com/search?f=tweets&q=from%3AMilkShake_yuuna%20since%3A2013-12-01%20until%3A2013-12-31

これでツイッター開始から1ヶ月ずつ(ツイ廃の子なら1週間くらい?)検索していけば、全ツイートを保存することができます。

ツイッター開始日はAPIで取得できますので、検索するといくらでもその手のサービスが見つかるでしょう。

https://api.twitter.com/1.1/users/show.json?screen_name=MilkShake_yuuna

{
  "id": 2238776738,
  "id_str": "2238776738",
  "name": "にょろ@ミルクセーキ",
  "screen_name": "MilkShake_yuuna",
  "location": "10/22(土) 長崎",
  "profile_location": null,
  "description": "ミルキーナンバー2番、にょろこと河合ゆうなです。メロンソーダがすきです。10月は1と2と9と10と15と22(卒業)出演しますよろしくお願いします。わは。#にょろさん @MilkShake_NGS",
  "url": "https://t.co/SrXI42d5mY",
  "entities":  {},
  "protected": false,
  "followers_count": 3684,
  "friends_count": 210,
  "listed_count": 149,
  "created_at": "Tue Dec 10 07:45:32 +0000 2013",
  "favourites_count": 208284,
  "utc_offset": null,
  "time_zone": null,
  "geo_enabled": false,
  "verified": false,
  "statuses_count": 10450,
  "lang": "ja",
  "status":  {},
  "contributors_enabled": false,
  "is_translator": false,
  "is_translation_enabled": false,
  "profile_background_color": "C0DEED",
  "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
  "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
  "profile_background_tile": false,
  "profile_image_url": "http://pbs.twimg.com/profile_images/755076601574862849/1X_rswi__normal.jpg",
  "profile_image_url_https": "https://pbs.twimg.com/profile_images/755076601574862849/1X_rswi__normal.jpg",
  "profile_banner_url": "https://pbs.twimg.com/profile_banners/2238776738/1474279384",
  "profile_link_color": "0084B4",
  "profile_sidebar_border_color": "C0DEED",
  "profile_sidebar_fill_color": "DDEEF6",
  "profile_text_color": "333333",
  "profile_use_background_image": true,
  "has_extended_profile": true,
  "default_profile": true,
  "default_profile_image": false,
  "following": true,
  "follow_request_sent": false,
  "notifications": true,
  "translator_type": "none"
}

追記

それにしても、昔は「ニョロ姫」とか呼ばれてたりツイッターのリプは釣るタイプだったり、なるほど昔は結構キャラが違ったんだな・・・と思った。アイドルも色々だな。

Eventernoteのお気に入り声優/アーティストにイベント参加回数を表示させるブックマークレット

誰かが作ってるだろうけど。なんとなく仕事をする気がなかったので作ってみた。

javascript:(function(){var v=document.getElementsByClassName('gb_actors_list')[0].getElementsByTagName('li'); for(var i=0;i<v.length;i++){n=v[i].className.replace(/.*c(\d+).*/,'$1'); v[i].getElementsByTagName('a')[0].textContent+='('+n+')'}}())