(2017/02/27段階)
Elixir-Slackをベースに改造を加えたbotを社内slackに常駐させています。
最近は新機能追加ネタに困っていたのですが、ふと毎朝botに挨拶をさせ、ついでにwikipediaの本日の出来事を投稿させようかと思い立ちました。
荒々しいので想定したwikipediaの記述ルールなどが崩れたら破綻します・・。
- Elixir-Slack : https://github.com/BlakeWilliams/Elixir-Slack
wikipediaの本日の出来事ページ
wikipediaには月日で検索するとその日に起きた出来事一覧のページを表示することができます。
URLとしては「 https://ja.wikipedia.org/wiki/2月27日 」(2月27日の場合)となります。
このページには、この月日に起きた歴史的な出来事と有名人の誕生日などが載っています。
誕生日を取得しても面白そうだったのですが、今回は出来事に絞っています。
(botの出力で、「〜の日です」と楽に出したかったのですが、誕生日は人物名で文が終わっていて合わなかったため)
HTMLを眺める
何らかの法則でパースできないかベガ立ちならぬベガ座りで腕組みしながらHTML全体を眺めます。
実サービスで利用するわけではないので、できるだけ低コストで楽に作り上げたいという気持ちがとても強い状態です。
パースの仕方の決定
この辺が荒々しい部分。
雑に以下の法則に気がつきます。
- 出来事や誕生日は
<li>
タグで表現されている(他の項目も含む) - 出来事や誕生日は行の始まりから
<li>
タグ - 出来事や誕生日には「 - 」という3文字を含む
- 出来事は「。
</li>
」で終わる
こんな感じでしょうか。
String.match?(x, ~r/^<li>.*/) && String.match?(x, ~r/。<\/li>/) && String.match?(x, ~r/ - /)
HTMLの処理の仕方
ここも荒々しい・・。
- HTMLを改行ごとにsplitしてarrayを作成
- arrayに対して出来事行だけを取得するようにfilter
- filter結果から1つをrandomで取得
- 取得した行をbotが話す形に整形
response.body
|> String.split("\n")
|> Enum.filter(fn(x) ->
String.match?(x, ~r/^<li>.*/) && String.match?(x, ~r/。<\/li>/) && String.match?(x, ~r/ - /)
end)
|> Enum.random()
と、取れましたね・・(汗
出来事行の処理
botには以下の形で最終的に話させたいと思います。
「出来事(XXX年) の日」
しかし実際に取れるのはこんな形
<li><a href="/wiki/xxxxxxxxxx" title="YYYY年">YYYY年</a> - <a href="/wiki/xxxxxxxxxxxxxxxxxxxxxxxxx" title="hoge">hoge</a>: 出来事</li>
こんな感じで処理をします。
# 年と本文を切り分けてそれぞれ整形する(タグを除去)
splitLi = String.split(randomLi, " - ")
year = Enum.at(splitLi, 0)
year = String.replace(year, ~r/<.*?>/, "")
dekigoto = Enum.at(splitLi, 1)
dekigoto = String.replace(dekigoto, ~r/<.*?>/, "")
# 連結して結果を作成(長すぎるときは頭だけ返すようにしたい) TODO
result = dekigoto <> "(" <> year <> ")"
結果
荒くやった割には動いていますね・・。
ただもうちょっと平和的な内容がいいな・・。
おまけ
いつも同じような投稿を繰り返すbotは語尾とか一部を変えると、少しだけですが面白くなると思っています。
例えばこの機能の場合は「おはようございます部」「今日も頑張りましょう部」が変化するようになっています。
@ohayo ["おはようござます。", "おはようござます!", "おはようござます(^o^)", "おはようござます(-_-)", "おはようござます(^_^)"]
@gobi ["今日も一日頑張りましょう!p(^_^)q", "さて仕事仕事..", "今日も一日頑張りましょう!♪( ´▽`)", "今日も一日頑張りましょう!( ̄▽ ̄)"]
def hogehoge()
Enum.random(@ohayo) <> "\n"
...
<> Enum.random(@gobi)
end