| @WWW

Untitled

Docker で rep2 を動かす - portal shit!

なぜ 2016 年にもなって rep2 を使うのか。実は2ちゃんねるはほとんど見ていなくて、まちBBSを見ている。特に大人になってから他県から移り住んできたような人だと住んでる町に同級生や昔からの知り合いというのがいないので町のちょっとした情報というのがきわめて入って来にくい。近所の人たちは自分の親かそれよりも上の世代の人ばかりなのでなかなか気軽に情報交換するということもできない。となるとネットで情報収集したいと思うけど、そういうのができる場所はまちBBSしかないような気がする。

ブログは今どきやる気がある人しかやってないので情報が少ない。情報あったとしても福岡だと天神だとか博多だとか薬院だとか人が集まる場所の情報がほとんど。自分が住んでる町の情報にはなかなかたどり着けない。 Twitter は検索しても出てくる情報にばらつきがある。たとえば住んでる町の名前で検索しても、同じ地名が横浜や姫路にあったりして効率が悪い。そもそも Twitter は町の情報よりもその人の思ったこと、感じたこと、やってることがメインなので効率的に情報を収集できない。 Facebook は基本的に情報が閉じられているし、知らない人から情報を集めるというより知ってる人の近況を読む場所という感じ。 mixi はどうだろうかと思って、 10 年ぶりくらいにアカウントを作って自分が住んでいる町のコミュニティに参加してみたけど誰も人がいなくて閑古鳥が鳴いてた。

今年の梅雨頃、マチマチというウェブサービスできて地元の人とお店や病院の情報などをやりとりできるという話だったので、これこそ求めていたものだと思って飛びついたけど、お前がこの町の最初のユーザーだから一ヶ月以内に 5 人ユーザー集めろや、できなきゃマチマチ上のコミュニティは閉鎖な、という厳しいルールだった。嫁さんはこういうのは一切興味ないし近所の人はじいさんばあさんばかりなので当然 5 人もユーザーを集めることはできず終了した。そもそも近所に知り合いがいないからネットで情報を集めたいと思ってるのに知り合いはお前が人力で集めろというのは難易度が高いと思う。

まちBBSは30代以降みたいな人たちが皆好き勝手に自分が書きたいことを書いているけど、最近できたあの店はどうだとか、あそこの病院はよくないだとか、どこそこの店が閉店して別の店になる、移転する、などのような情報を仕入れることができる。当然匿名掲示板なので嘘やネイティブ広告(工作員による宣伝投稿)も時々はあるけど、まぁ読み手もそれは織り込み済みなので特に問題はない。少なくとも SNS よりかは格段に情報に触れやすい。掲示板はトピックが決まっていて、多少脱線することはあってもそのトピックについて話すので、福岡の今宿の話かと思っていたのに横浜の今宿だったみたいなことはないし、失恋してつらいさみしい、今年の夏に別れた彼氏と行った今宿の花火大会の写真です、みたいなおセンチツイートを目にすることもない。 2016 年になったいまでも掲示板が一番情報を集めやすいのかもしれないなぁと思った。

| @技術/プログラミング

糸島の海

自作の Lokka プラグインに Amazon の Product Advertising API に問い合わせてアフィリエイトリンク付きの商品画像を返すやつがある。 Amazon 上の商品管理番号を <!-- ASIN=XXXXXXXX --> みたいな感じで本文中に書いとくと良い感じに小銭を稼げるかたちのリンクにして画像を表示してくれるという便利なやつ。

Amazon の規約上、 Amazon Product Advertising API に対しては一秒間に一リクエストしか送ってはいけないことになってるので、データを取得する度に 1 秒 sleep するようにしていたけど、一ページ内で複数のリンクがあるときにキャッシュがエクスパイアして Amazon の API を叩くとめっちゃレスポンス遅くなってださかったので非同期で取るようにした。ページコンテンツ本体はサクッと返して、商品情報などの取得はページがレンダリングされたあとに JavaScript で行う。 Amazon から JSON を取ってくる処理自体は Ruby にやらせる。

+----------+            +----------------+             +--------------------------------+
|          | +--------> |                | +---------> |                                |
|  client  |            |  Ruby (Lokka)  |             | Amazon Product Advertising API |
|          | <--------+ |                | <---------+ |                                |
+----------+            +----------------+             +--------------------------------+

最初は雑に body の innerHTML を正規表現で replace したりしてたんだけど、そうすると Twitter のウィジェットなど本文内に埋め込んである script タグが動かなくなる問題に気がついた。 HTML 5 の仕様で、 innerHTML = で挿入されたコンテンツの script タグは無視されるらしい。なるほど〜。

ということでもうちょい調べたら DOM には Node.nodeType というプロパティがあって、COMMENT_NODE など type を持っているらしい。

<!-- ASIN=XXXXXXXX -->COMMENT_NODE として扱われるので、こいつの後ろに document.createElementして JavaScript で動的に生成された要素を突っ込むようにした。 Promise を使ってナウでヤングな感じに書いた。

let promise = new Promise(function(resolve, reject) {
  request.open('GET', url);
  request.onreadystatechange = function() {
    if (request.readyState != 4) {
      // リクエスト中
    } else if (request.status != 200) {
      // 失敗
      reject(request.response);
    } else {
      // 取得成功
      let formatter = new Formatter(request.response);
      let result = formatter.formatItem();
      resolve(result);
    }
  };
  request.send();
});
promise.then(function(result) {
  let previous = node.previousSibling;
  let parent = node.parentNode;
  let d = document.createElement('div');
  d.className = 'amazon';
  d.innerHTML = result;
  parent.insertBefore(d, previous.nextSibling);
}).catch(function(error) {
  console.log(error);
});
return promise;

意地でも jQuery 使うまいと思ってやってみたけど意外と大丈夫だった。楽をせずに素の JavaScript を書いていると精神が浄化されるような感覚があってよい。写経に通じるものがある。写経やったことないけど。

| @技術/プログラミング

むかしやってた映画のブログ( WordPress )のデータをこのブログに取り込もうとしたら、 mysqldump しといたデータが文字化けして読めなかった。 映画ブログのデータベースの文字コードは utf-8 にしてたけど、 Dreamhost の MySQL サーバーの defualt-character-set がおそらく latin1 になっていたと考えられ、 mysqldump するときに utf8 のデータが latin1 として export されてしまい文字化けを起こしていたっぽい。

vim で開いて e ++enc=utf8 してみたり、 nkr で変換しようと試みたり、ググって出てきた Perl のスクリプトで変換しようと試してみたけど直らなかった。この変換は MySQL 自身にやらせないとダメなのではと思い、以下を試したがダメ。

  • [x] latin1 で DB 作成し latin1 で import 、 utf8 で export
  • [x] latin1 で DB 作成し utf8 で import 、 utf8 で export

結局、 Stack Overflow にあった以下の方法で解決できた。

一旦文字化けしたまま utf-8 の文字列として import し、以下の SQL を流す。

UPDATE [table_name] SET [column_name] = CONVERT(BINARY CONVERT([column_name] USING latin1) USING utf8);

やっぱり MySQL で化けたデータは MySQL で直すしかないみたいだった。

| @技術/プログラミング

jikoku with BitBar

BitBar で私家版通勤タイマー (2) - portal shit! の続き。

時刻表を JSON 化した奴を食わせて 60 分以内(まえの記事では 30 分以内にしてるけど 60 分以内に変えた。パラメーターで調整できるようにすると良さそう)の次の電車一覧と発車時刻までの分数を表示できるようになった。あとはこれを BitBar で扱えるようにする。以下のようなシェルスクリプトを用意した。 BitBar をインストールして pupjq をインストールし、 go get github.com/morygonzalez/jikoku した上で以下のようなシェルスクリプトを ~/bitbar/jikoku.1m.sh という名前で保存すればよい。

#!/bin/sh
export PATH="$HOME/bin:/usr/local/bin:/usr/bin:/bin:$PATH"

echo ":train:"
echo "---"

go_filter=""
return_filter="筑 西 唐"
go_base="http://transit.yahoo.co.jp/station/time/28074/?gid=2480"
return_base="http://transit.yahoo.co.jp/station/time/28236/?gid=6400"

day=`date "+%a"`
hour=`date "+%H"`

if [ $hour -lt 13 ]; then
    go=true
else
    go=false
fi

case ${day} in
    "Sun")
        kind=4
    ;;
    "Sat")
        kind=2
    ;;
    *)
        kind=1
    ;;
esac

if [ $go = true ]; then
    url="${go_base}&kind=${kind}"
    path="/tmp/jikoku_go_${kind}"
    filter=$go_filter
    echo "Go | href=$url"
else
    url="${return_base}&kind=${kind}"
    path="/tmp/jikoku_return_${kind}"
    filter=$return_filter
    echo "Return | href=$url"
fi

if [ ! -f ${path} ] && [ ! -s ${path} ]; then
    curl -s ${url} -o ${path}
else
    current=`date +%s`
    last_modified=`stat -f "%m" ${path}`
    if [ $(($current - $last_modified)) -gt 3600 ]; then
        curl -s ${url} -o ${path}
    fi
fi

echo "---"

cat ${path} |\
    pup 'table.tblDiaDetail [id*="hh_"] json{}' |\
    jq '[.[] | { hour: .children[0].text, minutes: [.children[1].children[].children[].children[].children[].children | map(.text) | join(" ") ] }]' |\
    jikoku -f "${filter}"

echo "---"
echo "Refresh | refresh=true"

go_base には往路の、 return_base には復路の Yahoo! 乗換案内時刻表の URL が入る。 go_filterback_filter はユーザーごとに行き先を絞り込みたいだろうから(自分の場合は福岡市地下鉄の JR 筑肥線直通電車だけを絞り込みたかったので行き先表示に「筑」「西」「唐」が含まれるものだけがフィルタリングされるようにした。BitBar はファイル名を 1h.sh1m.sh とすることで 1 時間おきの実行や 1 分おきの実行など実行間隔を指定できる。

ちなみに当初は 5m.sh にしていたが、次の列車までの時間を表示するソフトが 5 分おきに実行とかだったらまずいことに気がついた。メニューバーで確認してまだ余裕だと思ってたら間に合わなかったみたいな事態になる。毎分処理が走らなければ意味がない。毎分実行しても問題ないよう( Yahoo! 乗換案内に迷惑をかけないよう)、時刻表を一時間キャッシュするようにした。もっと長期間キャッシュしても良いのだろうけど、 13 時を境に往路と復路を切り替えるようにしているので、まぁ 60 分もキャッシュすれば十分サーバーリソースにやさしいかなと思い、このような作りにしている。

最初は curlpupjq を組み合わせて簡単に作れないかな、と思ってやり始めて意外と簡単にできそうだと思っていたけど結局は結構複雑になってしまった。 Go で書いた jikoku コマンドの方も複雑かつ終電後の翌朝始発を考慮できていなくていまいち感がある。時刻表は平日と土曜、日曜祝日で異なるので安易に当日のデータを翌日のデータとして使い回せない(曜日判定しないといけない)。たとえば金曜日の終電後に翌朝の始発を金曜日の時刻表を使って表示してしまうとまずい。ちゃんと土曜日の時刻表を使わないといけない。しかし時刻表自体は curl で HTML を取得して pupjq で JSON に整形しているので、翌日の時刻表が必要になっても Go コマンドの方からはどうしようもない。

  • 時刻表の HTML を取得 (いまは curl でやってる)
  • HTML から必要な要素を取り出す (いまは pup でやってる)
  • 取り出した情報を使いやすい形式に直す (いまは pupjq でやってる)
  • 次の電車を表示する (いまは自作の jikoku コマンドでやってる)

という四つのステップを全部 Go でやった方がよいのかもしれない。そもそも jq 職人だったら jikoku コマンドみたいなものは不要で、次の電車を表示するところまで jq でできてしまうのかもしれない。

ちょっともやっとした感じは残ったけどなかなか便利ですのでよかったらご利用ください。

なお pupjq に依存してますんでインストールが必要です。

brew install pup
brew install jq

| @技術/プログラミング

BitBar で私家版通勤タイマー (1) - portal shit! の続き。

JSON から情報を読み取って出力する奴は Ruby で書こうかなと思っていたんだけど、先日新しい上司と 1 on 1 をしていてどんな言語を使えるかの話になったときに Ruby と PHP とコールドフュ〜ジョン(と片手間 JavaScript )くらいしか使える言語がないということが判明してダメだと思ったので無理矢理 Go 言語で書いてみた。クソコードなので恥ずかしい。

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "strconv"
    "strings"
    "time"
)

type Hour struct {
    Hour    string
    Minutes []string
}

func printLefts(m int, minutes []string, hour string) {
    for i := 0; i < len(minutes); i++ {
        mindes := strings.Split(minutes[i], " ")
        min := mindes[0]
        var Dest string
        if len(mindes) > 1 {
            Dest = mindes[1]
        }
        _m, _ := strconv.Atoi(min)
        left := _m - m
        if left > 0 && left < 30 {
            leftS := strconv.Itoa(left)
            takeM := fmt.Sprintf("%02d", _m)
            Str := leftS + " minutes left to " + hour + ":" + takeM
            if Dest != "" {
                Str = Str + " " + Dest
            }
            fmt.Println(Str)
        }
    }
}

func main() {
    decoder := json.NewDecoder(os.Stdin)
    timetable := make([]Hour, 0)
    decoder.Decode(&timetable)
    t := time.Now()
    h := t.Hour()
    m := t.Minute()
    var CurrentHour, NextHour Hour
    for i := 0; i < len(timetable); i++ {
        hour := timetable[i]
        _h, _ := strconv.Atoi(hour.Hour)
        if _h == h {
            CurrentHour = hour
            NextHour = timetable[i+1]
            break
        }
    }
    minutes := CurrentHour.Minutes
    printLefts(m, minutes, CurrentHour.Hour)
    if m > 44 {
        nextMinutes := NextHour.Minutes
        printLefts(m-60, nextMinutes, NextHour.Hour)
    }
}

こんな感じに表示される。

私家版通勤タイマー

あとはこれを BitBar で良い感じに読めるように出力内容を調整していけば良い。

Go 言語、 2 年くらい前に gyowitter っての作って Yo が来たら Twitter に晒すってのやってた以来に触った。変数のスコープとかがわかりづらかったし、 PHP っぽい雰囲気もある。やっぱり Ruby の方が書きやすいし好きだけど歯を食いしばって使ってみる。

| @技術/プログラミング

以前は電車の本数多いところに住んでたので帰りの電車の時間とか気にしたことなかったけど、いまは郊外に住んでるので電車の本数少なくて、一本乗り遅れると駅で 20 分待たされたりする。特に遅くまで仕事してたりすると電車の本数がみるみる少なくなる。なので乗るべき電車に乗り遅れないように Yahoo! 乗換案内の通勤タイマーをよく使ってる。家まで帰る電車の発車時刻まであと何分かがカウントダウン表示されて便利。

ただ iPhone のウィジェットで見るとちょっともっさりしててだるい。 Pebble とかでサクッと確認できないかと思っているけど Pebble 入手して 10 ヶ月以上経つにもかかわらず Pebble 用のソフトを自分で作るという機運が高まらず今日に至る。

ある晩ふと、これって BitBar でできるんじゃねと思った。 BitBar は Mac のメニューバーに CLI で出力される情報を表示されることができるソフトで、 Netatmo Weather Station の計測値を表示するのに使っている。 Mac のメニューバーから二酸化炭素濃度や湿度確認できて便利。こんな感じ。これの通勤タイマー版が欲しい。

取り急ぎ時刻表の情報必要だけどちょっと探した感じでは簡単に時刻表取れるような API なかった。ので Yahoo! 乗換案内の時刻表をダウンロードして pup というコマンドラインの HTML パーサーで JSON 化し、それを jq に食わせて整形して良い感じの JSON にするという風にした。

# curl して HTML 取得
curl -s http://transit.yahoo.co.jp/station/time/28236/?gid=6400 | \
# HTML から必要な情報取り出して JSON 化
pup 'table.tblDiaDetail [id*="hh_"] json{}' | \
# JSON を良い感じに整形
jq '[.[] | { hour: .children[0].text, minutes: [.children[1].children[].children[].children[].children[].children | map(.text) | join(" ") ] }]'

得られる JSON はこんなのになる。(福岡市地下鉄空港線天神駅の姪浜方面の時刻一覧)

[
  {
    "hour": "5",
    "minutes": [
      "36",
      "42",
      "56 筑"
    ]
  },
  {
    "hour": "6",
    "minutes": [
      "8",
      "12 筑",
      "17",
      "26 筑",
      "34",
      "39",
      "43 筑",
      "56 筑"
    ]
  },
  ...,
  {
    "hour": "24",
    "minutes": [
      "6",
      "12 筑"
    ]
  }
]

これをコマンドラインで良い感じに表示するスクリプトを Ruby なり Shell Script なりで書いて BitBar に登録すればメニューバーから次の電車を確認できるようになり便利。

| @散財

先日買った Weber コンパクトケトル の感想。

ワンタッチケトルと勘違いしてコンパクトグリルの方を購入してしまっていた。

簡単なバーベキューをする分には問題ないが、グリルの深さが浅いため空気の量が不足し、温度はあまり上がらないみたい。ステーキ肉を焼いてみた限りだと火力に不足はなかったが、コストコで売ってるような巨大な塊肉をローストするのには向かないっぽい。

見た目に関しても、リンゴのような丸みのワンタッチケトルの方がエレガントに見える。近所のバーベキュー屋や金持ちの家にあるのはそれで、買ったけど何かださいなぁ、思ってたのと違うなぁと感じてたけど、似た別の製品を買っていたということだった。

加えてなによりも大きいのが灰を掻き出すシステムの違いだ。こちらは排出フィンの取っ手が本体下部に付いているため、開け閉め時に耐熱手袋をつけなければならない(初めて使ったとき勝手がわからず手を火傷してしまった)。ワンタッチケトルの方は長さのあるレバーが付いていて素手でもフィンの切り替えができる。便利極まりなさそう。

さらにフィンの形状がコンパクトケトルに比べてより積極的に灰を掻き出すような構造になっており、ただ穴が開いたり閉じたりするだけのコンパクトケトルとは構造が異なる。穴が空いているだけだと底に溜まった灰を掻き出すことが難しい。掻き出し機構とセットになっているフィンは相当便利だと思う。

コンパクトグリルは名称にコンパクトと付いているが結構大きく保存場所はとるし、組み立ててしまった後では解体は難しくキャンプに持ち出せるようなメリットもない。値段もほとんど差がないのでどうせ買うならワンタッチケトルの方がよいと思う。自分はヨドバシでワンタッチケトルより少し安く売られているためコンパクトケトルを買ってしまったけど、 Amazon だったらワンタッチケトルでも同じくらいの値段で買える。