<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>blog</title>
    <link>https://shidetake.com/</link>
    <description>Recent content on blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 31 Oct 2021 17:06:03 +0900</lastBuildDate>
    
        <atom:link href="https://shidetake.com/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Macの新規設定項目</title>
      <link>https://shidetake.com/init_mac/</link>
      <pubDate>Sun, 31 Oct 2021 17:06:03 +0900</pubDate>
      
      <guid>https://shidetake.com/init_mac/</guid>
      <description>&lt;p&gt;新しいMacを買ったとき、前のMacから環境移行せずに新しく設定したい派である。とはいえ、いつもやることはある程度決まっているのでそれを記録しておく。&lt;/p&gt;

&lt;h1 id=&#34;電源投入&#34;&gt;電源投入&lt;/h1&gt;

&lt;p&gt;電源投入してからの初期セットアップはなるべく後回しにせずやってしまってOK。
フルネームとアカウント名が分かれているが、フルネームにも本名は入れずアカウント名と同じでよい。&lt;/p&gt;

&lt;h1 id=&#34;capslockをctrlに置き換え&#34;&gt;CapsLockをCtrlに置き換え&lt;/h1&gt;

&lt;p&gt;キーボード &amp;gt; 修飾キー&lt;br /&gt;
Caps LockキーにControlキーを割り当てる&lt;/p&gt;

&lt;h1 id=&#34;トラックパッド&#34;&gt;トラックパッド&lt;/h1&gt;

&lt;h2 id=&#34;軌跡の速さ&#34;&gt;軌跡の速さ&lt;/h2&gt;

&lt;p&gt;トラックパッド &amp;gt; ポイントとクリック &amp;gt; 軌跡の速さ&lt;br /&gt;
右から5番目にセット&lt;/p&gt;

&lt;h2 id=&#34;３本指のドラッグ&#34;&gt;３本指のドラッグ&lt;/h2&gt;

&lt;p&gt;アクセシビリティ &amp;gt; ポインタコントロール &amp;gt; トラックパッドオプション&lt;br /&gt;
ドラッグを有効にする &amp;gt; ３本指のドラッグ&lt;/p&gt;

&lt;h1 id=&#34;pc名-ユーザ名変更&#34;&gt;PC名・ユーザ名変更&lt;/h1&gt;

&lt;p&gt;システム環境設定 &amp;gt; 共有 &amp;gt; コンピュータ名&lt;/p&gt;

&lt;h1 id=&#34;秘密鍵&#34;&gt;秘密鍵&lt;/h1&gt;

&lt;p&gt;古いPCから鍵ペアをコピーする。
AirDropを使うのが良い。&lt;/p&gt;

&lt;p&gt;※AirDropならP2Pの通信なのでリスクは低い。鍵を使い回すリスクはここでは考えない&lt;/p&gt;

&lt;h1 id=&#34;git&#34;&gt;Git&lt;/h1&gt;

&lt;p&gt;ターミナルを開いて、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ git -v
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;とかすると出てくるコマンドライン・デベロッパ・ツールをインストールする。この中にGitも入っている模様。
自前でHomebrewとか使ってそれぞれ必要なツールをインストールしたくなるが、一応入れておく。この手順はそのうち見直すかも。&lt;/p&gt;

&lt;h1 id=&#34;homebrew&#34;&gt;Homebrew&lt;/h1&gt;

&lt;p&gt;&lt;a href=&#34;https://brew.sh/index_ja&#34; target=&#34;_blank&#34;&gt;Web&lt;/a&gt;のトップにインストール用のコマンドが記載されているので、
その通りにインストールする。&lt;/p&gt;

&lt;h1 id=&#34;tools&#34;&gt;tools&lt;/h1&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ brew install --cask bettertouchtool
$ brew install --cask google-chrome
$ brew install --cask google-drive
$ brew install --cask bartender
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ちなみにBetterTouchToolはWindowのスナップ機能が使いたいだけなのでほぼデフォルト設定でOK。
環境設定から自動起動の設定をして、ライセンスキーを入れるだけ。&lt;/p&gt;

&lt;h1 id=&#34;ターミナル設定&#34;&gt;ターミナル設定&lt;/h1&gt;

&lt;h2 id=&#34;カラースキーム&#34;&gt;カラースキーム&lt;/h2&gt;

&lt;p&gt;環境設定 &amp;gt; プロファイルでProを選択してデフォルトボタンを押してデフォルトにする。&lt;br /&gt;
環境設定 &amp;gt; 一般 &amp;gt; 起動時に開くの方もProにする。&lt;/p&gt;

&lt;h2 id=&#34;起動時ウィンドウサイズ&#34;&gt;起動時ウィンドウサイズ&lt;/h2&gt;

&lt;p&gt;開きたいサイズに手動で調整して、ウィンドウタイトルからサイズを確認する。&lt;br /&gt;
環境設定 &amp;gt; プロファイル &amp;gt; ウィンドウ &amp;gt; ウィンドウサイズに入力。&lt;/p&gt;

&lt;h2 id=&#34;シェル終了時にウィンドウを閉じる&#34;&gt;シェル終了時にウィンドウを閉じる&lt;/h2&gt;

&lt;p&gt;環境設定 &amp;gt; プロファイル &amp;gt; シェル &amp;gt; シェルの終了時で正常終了した場合は閉じる。&lt;/p&gt;

&lt;h1 id=&#34;dotfiles&#34;&gt;dotfiles&lt;/h1&gt;

&lt;p&gt;.vimrcや.bashrcなどの設定ファイルはGitHubで管理しているのでそれを使う。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ cd ~/git
$ git clone git@github.com:shidetake/dotfiles.git
$ cd dotfiles
$ make install
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&#34;asdf&#34;&gt;asdf&lt;/h1&gt;

&lt;p&gt;rbenvのような言語のバージョン管理ツールの現時点での最適解がたぶんこれ。
rbenvとかpyenvをまとめて管理できるようにしたのがanyenvで、ちょっと前までこれが主流だったっぽいが、
より使いやすく統合したのがasdfらしい。env系の管理ではなくて、直接いろんな言語の管理をするようなので、アプローチは大きく違うっぽいけど。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ brew install asdf
$ echo . /opt/homebrew/opt/asdf/libexec/asdf.sh &amp;gt;&amp;gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ターミナルを再起動する。&lt;/p&gt;

&lt;h2 id=&#34;python&#34;&gt;Python&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ asdf plugin add python
$ asdf list all python
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;表示されるバージョンから選んで&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ asdf install python 3.10.0
$ asdf global python 3.10.0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;pipのバージョンも最新にしておく。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ pip list
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;してWARNINGが出てきたらそれに従ってバージョンアップする。&lt;/p&gt;

&lt;h2 id=&#34;ruby&#34;&gt;Ruby&lt;/h2&gt;

&lt;p&gt;上のpyhonをrubyに置き換える&lt;/p&gt;

&lt;h1 id=&#34;vim&#34;&gt;vim&lt;/h1&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ brew install neovim
$ pip install pynvim
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&#34;finder設定&#34;&gt;Finder設定&lt;/h1&gt;

&lt;h2 id=&#34;finderのパスバー表示&#34;&gt;Finderのパスバー表示&lt;/h2&gt;

&lt;p&gt;Finder &amp;gt; 表示 &amp;gt; パスバーを表示&lt;/p&gt;

&lt;h2 id=&#34;finderを常にカラム表示&#34;&gt;Finderを常にカラム表示&lt;/h2&gt;

&lt;p&gt;適当な階層でカラム表示に変更したあと、表示 &amp;gt; 表示オプション &amp;gt; 常にカラム表示で開く&lt;/p&gt;

&lt;h2 id=&#34;よく使う項目を整理&#34;&gt;よく使う項目を整理&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ホームディレクトリを追加&lt;/li&gt;
&lt;li&gt;最近の項目を削除&lt;/li&gt;
&lt;li&gt;アプリケーションを削除&lt;/li&gt;
&lt;li&gt;デスクトップを削除&lt;/li&gt;
&lt;li&gt;書類を削除&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;新規finderウィンドウでホームディレクトリを表示&#34;&gt;新規Finderウィンドウでホームディレクトリを表示&lt;/h2&gt;

&lt;p&gt;環境設定 &amp;gt; 一般 &amp;gt; 新規Finderウィンドウで次を表示&lt;/p&gt;

&lt;h2 id=&#34;拡張子を表示&#34;&gt;拡張子を表示&lt;/h2&gt;

&lt;p&gt;環境設定 &amp;gt; 詳細 &amp;gt; すべてのファイル名拡張子を表示&lt;/p&gt;

&lt;h2 id=&#34;タグを非表示にする&#34;&gt;タグを非表示にする&lt;/h2&gt;

&lt;p&gt;環境設定 &amp;gt; サイドバー &amp;gt; 最近使ったタグのチェックを外す&lt;/p&gt;

&lt;h1 id=&#34;その他&#34;&gt;その他&lt;/h1&gt;

&lt;h2 id=&#34;ドックを隠す&#34;&gt;ドックを隠す&lt;/h2&gt;

&lt;p&gt;ドックを右クリックして&amp;rdquo;自動的に非表示&amp;rdquo;をオンにする&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>macOS起動時にrailsを走らせる</title>
      <link>https://shidetake.com/launchd_rails/</link>
      <pubDate>Mon, 03 May 2021 22:26:56 +0900</pubDate>
      
      <guid>https://shidetake.com/launchd_rails/</guid>
      <description>&lt;p&gt;メインマシンとして使っているMac miniを自作サービスのサーバーとしても使っていて、
毎回立ち上げるのが面倒だったので自動で起動するようにした。&lt;/p&gt;

&lt;p&gt;PATH周りでトラブったので備忘録を残しておく。&lt;/p&gt;

&lt;h1 id=&#34;launchd&#34;&gt;launchd&lt;/h1&gt;

&lt;p&gt;通常のアプリの起動ならGUIで設定することができるっぽいが、自作のRailsアプリなので
&lt;code&gt;launchd&lt;/code&gt;を使うことにした。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;init.d&lt;/code&gt;や&lt;code&gt;systemctl&lt;/code&gt;のmacOS版らしい。&lt;/p&gt;

&lt;h2 id=&#34;plistファイル&#34;&gt;plistファイル&lt;/h2&gt;

&lt;p&gt;plistファイルというのに実行するコマンドや条件を書いておいて、loadすると起動時に自動で実行されるようになる。&lt;/p&gt;

&lt;p&gt;plistファイルは&lt;code&gt;~/Library/LaunchAgents/&lt;/code&gt;に置いた。サーバーアプリケーションだから&lt;code&gt;/Library/LaunchDaemons/&lt;/code&gt;に置くのが正解な気はするけど、
rootが実行するとなると&lt;code&gt;rbenv&lt;/code&gt;や&lt;code&gt;bundler&lt;/code&gt;あたりがややこしくなりそうだったので避けた。試してないので案外すんなり通るかもしれないが。&lt;/p&gt;

&lt;p&gt;以下がサンプル。ファイル名は&lt;code&gt;local.mac.sample.plist&lt;/code&gt;とでもしておく。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml:local.mac.sample.plist&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &amp;quot;-//Apple//DTD PLIST 1.0//EN&amp;quot; &amp;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;quot;&amp;gt;
&amp;lt;plist version=&amp;quot;1.0&amp;quot;&amp;gt;
&amp;lt;dict&amp;gt;
  &amp;lt;key&amp;gt;KeepAlive&amp;lt;/key&amp;gt;
  &amp;lt;true/&amp;gt;
  &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;local.mac.sample&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
  &amp;lt;array&amp;gt;
    &amp;lt;string&amp;gt;echo&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;hoge&amp;lt;/string&amp;gt;
  &amp;lt;/array&amp;gt;
  &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
  &amp;lt;true/&amp;gt;
  &amp;lt;key&amp;gt;WorkingDirectory&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;/Users/shidetake&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;StandardOutPath&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;/Users/shidetake/sample.plist.log&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;StandardErrorPath&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;/Users/shidetake/sample.plist.log&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;WorkingDirectory&lt;/code&gt;で実行する場所を選べる。
あと大事なのが&lt;code&gt;StandardOutPath&lt;/code&gt;と&lt;code&gt;StandardErrorPath&lt;/code&gt;。これをちゃんと設定しておくとデバッグしやすい。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;echo hoge&lt;/code&gt;の部分を&lt;code&gt;rails s&lt;/code&gt;にしてやるだけで動くように見えるが実は動かない。ここの対処がキモだが、一旦後回しにしてplistをloadする方法を先に説明する。&lt;/p&gt;

&lt;h2 id=&#34;plistのload&#34;&gt;plistのload&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ launchctl load local.mac.sample.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じ。
unloadする場合はunloadに変えればOK。&lt;/p&gt;

&lt;h1 id=&#34;railsを実行する際の注意&#34;&gt;railsを実行する際の注意&lt;/h1&gt;

&lt;p&gt;ここがこの記事のメイントピック。
上述したように、単純に&lt;code&gt;rails s&lt;/code&gt;を&lt;code&gt;launchd&lt;/code&gt;から実行しようとしてもうまくいかない。&lt;/p&gt;

&lt;h2 id=&#34;pathの問題&#34;&gt;PATHの問題&lt;/h2&gt;

&lt;p&gt;どうやら&lt;code&gt;launchd&lt;/code&gt;で実行する際には通常のPATHが通ってないらしい。
なので、単に&lt;code&gt;rails&lt;/code&gt;と入力しても、そんなコマンドはないと怒られてしまう。&lt;/p&gt;

&lt;p&gt;ならばフルパスにしたら終わりかと思ったらそうでもない。&lt;/p&gt;

&lt;h2 id=&#34;なぜか発生するjavascriptのランタイムエラー&#34;&gt;なぜか発生するJavascriptのランタイムエラー&lt;/h2&gt;

&lt;p&gt;エラー名でググっても、nodejsを入れたらいいとか、そんなのしか出てこない。
普通に実行するときはできているので、そういう問題ではない。&lt;/p&gt;

&lt;p&gt;おそらくこれもPATHの問題と思われる。結局&lt;code&gt;rails&lt;/code&gt;だけでなく、依存関係のあるプログラムもPATHが通ってないと動かない。まぁ当然だ。&lt;/p&gt;

&lt;h2 id=&#34;launchdに環境変数を設定する方法&#34;&gt;launchdに環境変数を設定する方法&lt;/h2&gt;

&lt;p&gt;plistに&lt;code&gt;EnvironmentVariables&lt;/code&gt;というパラメータを設定してやると、環境変数を設定できる。
ただしこれを使うには&lt;code&gt;/bin/zsh -c rails s&lt;/code&gt;というように一度&lt;code&gt;zsh&lt;/code&gt;を起動してやる必要がある。理由は不明。&lt;/p&gt;

&lt;h2 id=&#34;完成形のplist&#34;&gt;完成形のplist&lt;/h2&gt;

&lt;p&gt;ということで、PATHを正しく設定して、&lt;code&gt;zsh&lt;/code&gt;を起動してその中で実行する形にしたのがこれ。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml:local.mac.rails.plist&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &amp;quot;-//Apple//DTD PLIST 1.0//EN&amp;quot; &amp;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;quot;&amp;gt;
&amp;lt;plist version=&amp;quot;1.0&amp;quot;&amp;gt;
&amp;lt;dict&amp;gt;
  &amp;lt;key&amp;gt;KeepAlive&amp;lt;/key&amp;gt;
  &amp;lt;true/&amp;gt;
  &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;local.mac.rails&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;EnvironmentVariables&amp;lt;/key&amp;gt;
  &amp;lt;dict&amp;gt;
    &amp;lt;key&amp;gt;PATH&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;/Users/shidetake/.rbenv/shims:/Users/shidetake/.rbenv/bin:/usr/local/bin:/usr/bin:/bin&amp;lt;/string&amp;gt;
  &amp;lt;/dict&amp;gt;
  &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
  &amp;lt;array&amp;gt;
    &amp;lt;string&amp;gt;/bin/zsh&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;-c&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;rails s&amp;lt;/string&amp;gt;
  &amp;lt;/array&amp;gt;
  &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
  &amp;lt;true/&amp;gt;
  &amp;lt;key&amp;gt;WorkingDirectory&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;/Users/shidetake/git/rails&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;StandardOutPath&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;/Users/shidetake/git/rails/rails.plist.log&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;StandardErrorPath&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;/Users/shidetake/git/rails/rails.plist.log&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&#34;参考&#34;&gt;参考&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://qiita.com/rsahara/items/7d37a4cb6c73329d4683&#34; target=&#34;_blank&#34;&gt;launchdで定期的にスクリプトを実行&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://serverfault.com/questions/111391/use-an-environment-variable-in-a-launchd-script&#34; target=&#34;_blank&#34;&gt;Use an environment variable in a launchd script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>任意のGoogle APIをRubyで使うときの手順</title>
      <link>https://shidetake.com/google_auth/</link>
      <pubDate>Sun, 22 Dec 2019 17:53:28 +0900</pubDate>
      
      <guid>https://shidetake.com/google_auth/</guid>
      <description>&lt;p&gt;GmailのAPIについては&lt;a href=&#34;../gmail_api_1.md&#34;&gt;以前記事にした&lt;/a&gt;が、
別のAPI使うときにどうするのか、毎回悩むので汎用的な部分はここにまとめることにした。&lt;/p&gt;

&lt;p&gt;今回使いたくなったのはGoogle Photos APIsなのでこれを例に進めるが、
汎用的に使えるように書く。&lt;/p&gt;

&lt;h1 id=&#34;apiの有効化&#34;&gt;APIの有効化&lt;/h1&gt;

&lt;p&gt;Gmailのときと同じく、&lt;a href=&#34;https://console.developers.google.com/&#34; target=&#34;_blank&#34;&gt;Google API Console&lt;/a&gt;で使いたいAPIを有効化する。
ライブラリからAPIの検索ができる。今回は&amp;rsquo;Photo&amp;rsquo;で検索すると、Photos Library API&lt;sup class=&#34;footnote-ref&#34; id=&#34;fnref:a&#34;&gt;&lt;a href=&#34;#fn:a&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;が出てくるので、
選択して、&amp;rsquo;有効にする&amp;rsquo;をクリック。&lt;/p&gt;

&lt;p&gt;このページにドキュメントへのリンクがあるので、開いておく（あとで使う）。&lt;/p&gt;

&lt;p&gt;続いて、Google API Consoleのトップに戻ってから、認証情報に進む。&lt;/p&gt;

&lt;p&gt;認証情報を作成 -&amp;gt; OAuthクライアントID -&amp;gt; その他&lt;/p&gt;

&lt;p&gt;で認証情報を作成して、JSONファイルをダウンロードする。&lt;/p&gt;

&lt;h1 id=&#34;scopeの取得&#34;&gt;Scopeの取得&lt;/h1&gt;

&lt;p&gt;APIのドキュメントにScopeが書かれているので、それを探す。
Photos Library APIの場合は、Overviewの中のAuthorizationにScope記載ページへのリンクがあった。
ここから、必要な権限を取れるScopeを選ぶ。&lt;/p&gt;

&lt;p&gt;ちなみにGmailとかDriveだとgoogle-api-clientというgemにScopeのパスを定義した定数があるので、それを使うことが多い。
&lt;a href=&#34;https://github.com/googleapis/google-api-ruby-client/tree/master/generated/google/apis&#34; target=&#34;_blank&#34;&gt;ここ&lt;/a&gt;に使いたいAPIがあればそれを使うと良い。&lt;/p&gt;

&lt;h1 id=&#34;access-tokenの取得&#34;&gt;Access Tokenの取得&lt;/h1&gt;

&lt;p&gt;googleauthというgemを使う。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ gem install googleauth
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;からの&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &amp;quot;googleauth&amp;quot;
require &amp;quot;googleauth/stores/file_token_store&amp;quot;
require &amp;quot;fileutils&amp;quot;
require &amp;quot;json&amp;quot;

class Auth
  OOB_URI = &amp;quot;urn:ietf:wg:oauth:2.0:oob&amp;quot;.freeze
  CREDENTIALS_PATH = &amp;quot;credentials.json&amp;quot;.freeze
  # The file token.yaml stores the user&#39;s access and refresh tokens, and is
  # created automatically when the authorization flow completes for the first
  # time.
  TOKEN_PATH = &amp;quot;token.yaml&amp;quot;.freeze
  SCOPE = [&#39;https://www.googleapis.com/auth/photoslibrary.readonly&#39;]

  def initialize
    authorize
  end

  private

  ##
  # Ensure valid credentials, either by restoring from the saved credentials
  # files or intitiating an OAuth2 authorization. If authorization is required,
  # the user&#39;s default browser will be launched to approve the request.
  #
  # @return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
  def authorize
    client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
    token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
    authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
    user_id = &amp;quot;default&amp;quot;
    credentials = authorizer.get_credentials user_id
    if credentials.nil?
      url = authorizer.get_authorization_url base_url: OOB_URI
      puts &amp;quot;Open the following URL in the browser and enter the &amp;quot; \
           &amp;quot;resulting code after authorization:\n&amp;quot; + url
      code = gets
      credentials = authorizer.get_and_store_credentials_from_code(
        user_id: user_id, code: code, base_url: OOB_URI
      )
    end
    credentials
  end
end

Auth.new
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ここで、CREDENTIALS_PATHはダウロードしたJSONファイル。これはrbファイルと同じ場所に置いている前提。
また、SCOPEには取得したScopeを書く。&lt;/p&gt;

&lt;p&gt;このスクリプトを実行すると、初回はAccess Tokenを取得するためのページへのURLが表示される。
ブラウザでここを開くと、アクセス権限を渡してよいか聞かれるのでOKを押すと、コードが表示されるので、それをターミナルに貼り付けてエンターキーを押すと、
Access Tokenが取得できる。&lt;/p&gt;
&lt;div class=&#34;footnotes&#34;&gt;

&lt;hr /&gt;

&lt;ol&gt;
&lt;li id=&#34;fn:a&#34;&gt;ここでの名前はPhotos Library APIだけど、ドキュメントにはGoogle Photos APIsと出る
 &lt;a class=&#34;footnote-return&#34; href=&#34;#fnref:a&#34;&gt;&lt;sup&gt;[return]&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>素のRubyで日時を扱う</title>
      <link>https://shidetake.com/active_support_time/</link>
      <pubDate>Mon, 16 Dec 2019 23:07:30 +0900</pubDate>
      
      <guid>https://shidetake.com/active_support_time/</guid>
      <description>&lt;p&gt;Railsではなく素のRubyで日時を扱うときのベストプラクティスについて。
毎回悩むのでここに書いておく。&lt;/p&gt;

&lt;p&gt;Timeを使うか、Dateを使うか、DateTimeを使うか。
それについては
&lt;a href=&#34;https://qiita.com/jnchito/items/cae89ee43c30f5d6fa2c&#34; target=&#34;_blank&#34;&gt;RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い&lt;/a&gt;
に従ってTimeを使う。&lt;/p&gt;

&lt;p&gt;これで間に合う場合は終わり。&lt;/p&gt;

&lt;p&gt;ただ、ちょっと計算したいとなったときには、やはりActiveSupportを使うのがよい。
ActiveSupportを使うかどうか迷うポイントとして、一部の機能しか使わないのに、
あんなに巨大なライブラリを使ってしまっていいのだろうか…という罪悪感があると思う（あるよね？）。&lt;/p&gt;

&lt;p&gt;その罪悪感を小さくするために、ActiveSupportには一部の機能だけをrequireできるようになっている。
本当のミニマリストは使う機能だけをrequireするのかもしれないが、ここは少し妥協して、日時系の機能全般をrequireすることにする。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;active_support/time&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これで大抵の機能が使えるようになる。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/rails/rails/blob/master/activesupport/lib/active_support/time.rb&#34; target=&#34;_blank&#34;&gt;https://github.com/rails/rails/blob/master/activesupport/lib/active_support/time.rb&lt;/a&gt;&lt;br /&gt;
を見るとわかるが、このファイルは正にそのためのものだ。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>MakeMKVとHandBrakeによるBlu-lay Discリッピング</title>
      <link>https://shidetake.com/blu_ripping/</link>
      <pubDate>Sun, 10 Nov 2019 17:00:51 +0900</pubDate>
      
      <guid>https://shidetake.com/blu_ripping/</guid>
      <description>&lt;p&gt;DVDのリッピングについて&lt;a href=&#34;../ripping_1/&#34;&gt;以前&lt;/a&gt;書いたが、ブルーレイに未対応だったので対応した。&lt;/p&gt;

&lt;h1 id=&#34;概要&#34;&gt;概要&lt;/h1&gt;

&lt;p&gt;HandBrakeではBlu-rayをそのままリッピングできないので、MakeMKVというソフトを使う。
mkv形式にしてくれるので、これだけでリッピング完成としてもいいが、
ほとんど圧縮かけてないようなので、HandBrakeを使って圧縮する。ついでに必要に応じてチャプター毎に分割する。&lt;/p&gt;

&lt;h1 id=&#34;makemkvのインストール&#34;&gt;MakeMKVのインストール&lt;/h1&gt;

&lt;p&gt;Homebrewには無いので&lt;a href=&#34;https://makemkv.com/download&#34; target=&#34;_blank&#34;&gt;公式サイト&lt;/a&gt;からインストーラーを使う。&lt;/p&gt;

&lt;h1 id=&#34;makemkvの使い方&#34;&gt;MakeMKVの使い方&lt;/h1&gt;

&lt;p&gt;GUIもあるが、ここでは&lt;code&gt;makemkvcon&lt;/code&gt;というCUIを使う。
おそらく&lt;code&gt;/Applications/MakeMKV.app/Contents/MacOS/makemkvcon&lt;/code&gt;にあるので、ここにパスを通す。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;makemkvcon mkv dev:/dev/disk2 all ~/dist
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じ。&lt;code&gt;/dev/disk2&lt;/code&gt;にはBlu-lay Discを入れたドライブを環境に応じて設定する。
&lt;code&gt;~/dist&lt;/code&gt;にファイルが出力される。&lt;/p&gt;

&lt;h1 id=&#34;handbrakeによる加工&#34;&gt;HandBrakeによる加工&lt;/h1&gt;

&lt;p&gt;このままだとかなり重たいファイルなのと、場合によってはチャプターごとにファイルを分けたいので、
HandBrakeを使って加工する。
これについては&lt;a href=&#34;../ripping_1/&#34;&gt;DVDリッピングのとき&lt;/a&gt;と同じ（入力ファイルをisoでもDVDドライブでもなくMakeMKVで出力したmkvにするだけ）
なので割愛する。&lt;/p&gt;

&lt;h1 id=&#34;お約束&#34;&gt;お約束&lt;/h1&gt;

&lt;p&gt;違法なリッピングを推奨しているわけではないので注意。
コピーガードされたBlu-rayをリッピングするのはダメ。ぜったい。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Heroku上のRailsアプリのDBをリセットする</title>
      <link>https://shidetake.com/heroku_reset/</link>
      <pubDate>Sun, 28 Oct 2018 11:15:57 +0900</pubDate>
      
      <guid>https://shidetake.com/heroku_reset/</guid>
      <description>&lt;p&gt;HerokuでホスティングしているRailsアプリのDB構造を変えて、
&lt;code&gt;rails db:migrate:reset&lt;/code&gt;したくなったんだけど、そのままのコマンドではだめだったので調査した。&lt;/p&gt;

&lt;p&gt;失敗したときのログがこちら&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;rails aborted!
ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your &#39;production&#39; database.
If you are sure you want to continue, run the same command with the environment variable:
DISABLE_DATABASE_ENVIRONMENT_CHECK=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;どうやら、productionのDBを破壊するようなコマンドはそのままでは通らないらしい。
ということで、ここに書かれているように、&lt;code&gt;DISABLE_DATABASE_ENVIRONMENT_CHECK=1&lt;/code&gt;にして実行する。以下にようにコマンドに続けて環境変数をセットするだけ。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ heroku run rails db:migrate:reset DISABLE_DATABASE_ENVIRONMENT_CHECK=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ちなみに、このようにコマンドに続けて環境変数をセットしただけだと、一時的に環境変数を変えるだけなので安心。
常に&lt;code&gt;DISABLE_DATABASE_ENVIRONMENT_CHECK=1&lt;/code&gt;にしておきたい場合は、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ heroku config:set DISABLE_DATABASE_ENVIRONMENT_CHECK=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;とすればよいが、危険なので推奨しない。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Dropboxで自分専用のPodcast配信</title>
      <link>https://shidetake.com/dropcast/</link>
      <pubDate>Wed, 29 Aug 2018 23:13:09 +0900</pubDate>
      
      <guid>https://shidetake.com/dropcast/</guid>
      <description>&lt;p&gt;最近Podcastにハマっている。
Rebuild.fmから始まり、backspace.fm、TCFM、バイリンガルニュースなど。&lt;/p&gt;

&lt;p&gt;その流れで、過去に録ったラジオを聞きたくなり、古いHDDからmp3を引っ張り出してきた。
できればPodcastと同じように聞きたいなと思い、自分専用に配信すればよいのでは、と考えて作ってみた。&lt;/p&gt;

&lt;h1 id=&#34;スクリーンショット&#34;&gt;スクリーンショット&lt;/h1&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/dropcast.png&#34; alt=&#34;image&#34; /&gt;
当然、著作物は私的利用に留めること。
音源もPodcast配信用のRSSファイルも自分しかアクセスできない場所に置いているので私的利用の範囲内。&lt;/p&gt;

&lt;h1 id=&#34;動機&#34;&gt;動機&lt;/h1&gt;

&lt;p&gt;上でも書いたが、Podcastにハマっているので、それと同じ体験で聞けるととても楽。
音楽再生用のツールで聞くと、レジューム再生できないだろうし、再生速度も変えられないし、
普通に音楽聴きたいときにランダム再生でラジオ流れてくるの嫌だし。&lt;/p&gt;

&lt;h1 id=&#34;サーバー選定&#34;&gt;サーバー選定&lt;/h1&gt;

&lt;p&gt;タイトルにも書いたが、Dropboxを使うことにした。
VPSでも自宅サーバでもなんでもいい気はするが、なんとなく。&lt;/p&gt;

&lt;h1 id=&#34;dropboxについて&#34;&gt;Dropboxについて&lt;/h1&gt;

&lt;p&gt;Dropbox APIというものを使って、ファイルのアップロードと、直リンクの取得を行う。
Dropbox APIの使い方についてはQiitaあたりで探してほしい。&lt;/p&gt;

&lt;p&gt;APIを使って取得できるのは共有リンクというもので、直リンクではないのが注意点。
共有リンクの&lt;code&gt;www.dropbox.com&lt;/code&gt;という部分を&lt;code&gt;dl.dropboxusercontent.com&lt;/code&gt;に置き換えて、
末尾にある&lt;code&gt;?dl=0&lt;/code&gt;という文字列を削除すると直リンクになる。&lt;/p&gt;

&lt;h1 id=&#34;配信方法&#34;&gt;配信方法&lt;/h1&gt;

&lt;p&gt;Podcastというのは、RSSフィードに書かれているリンク先のmp3を引っ張ってくるだけの仕組みなので、
mp3をDropboxにアップロードして、その直リンクをRSSに書いて、更にそのRSSファイルをDropboxにアップロードすればよい。
その直リンクを手持ちのPodcastクライアントに読ませれば完成。&lt;/p&gt;

&lt;h1 id=&#34;スクリプト&#34;&gt;スクリプト&lt;/h1&gt;

&lt;p&gt;mp3が少ない場合は手動でやってもいいが、今回は100本以上の番組だったので、スクリプト化した。
GitHubに&lt;a href=&#34;https://github.com/shidetake/dropcast&#34; target=&#34;_blank&#34;&gt;dropcast&lt;/a&gt;というリポジトリに、今回作成したスクリプトをおいた。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;mp3フォルダに置いたmp3をDropbox APIを使って指定したディレクトリにアップロード&lt;/li&gt;
&lt;li&gt;アップロードしたmp3の直リンクを取得し、それをもとにPodcast用のRSSファイルを生成&lt;/li&gt;
&lt;li&gt;生成したRSSをDropboxにアップロードして、直リンクを標準出力&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;という処理を一気に実行してくれるシェルスクリプト。
3行目にDropbox API用のTOKENを入力して、mp3ファイルを用意すればあとは実行するだけ。
最後に出力されるリンクを、手持ちのPodcastクライアントに入力すればOK。&lt;/p&gt;

&lt;p&gt;オプションとしてタイトルとDropbox上のパスを変更できるようにしている。&lt;/p&gt;

&lt;h1 id=&#34;おまけ&#34;&gt;おまけ&lt;/h1&gt;

&lt;p&gt;結構いい感じで使えるようになったけど、気になるのがサムネイル画像。
最初はどうでもいいと思っていたが、ここまで来たら画像まで完璧にしたくなってしまった。
さすがに番組紹介的な文章はいらないけれど、サムネは欲しい。&lt;/p&gt;

&lt;p&gt;ということで、スクリプトには含めてないけれど、サムネイル画像を手動で設定することにした。
画像もDropboxにアップロードしたものを使う。いつものように直リンクを取得して、RSSファイルの&lt;code&gt;title&lt;/code&gt;の下あたりに、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;    &amp;lt;itunes:image href=&amp;quot;https://dl.dropboxusercontent.com/s/*/thumbnail.jpg&amp;quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じで書けばOK。ただしクライアントアプリの作り次第なのかDropboxの問題なのかわからないが、
反映には結構時間がかかる。RSSファイルの名前を変えて直リンクを生成し直せばすぐに反映される。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>地域ごとのマンション単価をGoogleマップで見る</title>
      <link>https://shidetake.com/reins_map/</link>
      <pubDate>Sat, 25 Aug 2018 17:40:38 +0900</pubDate>
      
      <guid>https://shidetake.com/reins_map/</guid>
      <description>&lt;p&gt;最近、新築マンションの購入を検討し始めた。
そこで気になるのが、周辺の相場だ。
ということで、Googleマップのマイマップを使って地域ごとの相場を視覚的に確認できるようにした。&lt;/p&gt;

&lt;p&gt;新築マンションは似たような地域で同時に建つことは稀なので、
中古マンション価格から推測する。&lt;/p&gt;

&lt;p&gt;中古マンションは売出し価格と成約価格に乖離があることが珍しくないので、
できれば実態に近い成約価格を使いたい。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://www.contract.reins.or.jp&#34; target=&#34;_blank&#34;&gt;REINS Market Information&lt;/a&gt;というサイトでは、
地域ごとのマンション・戸建の成約価格を調べることができる。&lt;/p&gt;

&lt;p&gt;これを使って、Googleマップにインポート可能な形式のデータを作成する。&lt;/p&gt;

&lt;h1 id=&#34;スクリーンショット&#34;&gt;スクリーンショット&lt;/h1&gt;

&lt;p&gt;完成系のイメージはこんな感じ（というか、完成したスクリプトで作ったマップ）。
&lt;img src=&#34;https://shidetake.com/img/reins_map.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;東京都心の単価は高すぎてサチってしまっている。
本来買いたいレンジが単価100万以下なのでしょうがない。&lt;/p&gt;

&lt;h1 id=&#34;成果物&#34;&gt;成果物&lt;/h1&gt;

&lt;p&gt;GitHubに&lt;a href=&#34;https://github.com/shidetake/reins_map&#34; target=&#34;_blank&#34;&gt;reins_map&lt;/a&gt;というリポジトリを作成して、成果物をおいた。
使い方等はREADMEを読んでほしい。Rubyを使った。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Yahoo!ジオコーダAPIで住所から経度緯度を取得</title>
      <link>https://shidetake.com/geoapi/</link>
      <pubDate>Thu, 16 Aug 2018 15:56:34 +0900</pubDate>
      
      <guid>https://shidetake.com/geoapi/</guid>
      <description>&lt;p&gt;Rubyを使う。&lt;/p&gt;

&lt;h1 id=&#34;アプリケーションid登録&#34;&gt;アプリケーションID登録&lt;/h1&gt;

&lt;p&gt;まずはAPIを使うための登録。
&lt;a href=&#34;https://e.developer.yahoo.co.jp/register&#34; target=&#34;_blank&#34;&gt;アプリケーション登録ページ&lt;/a&gt;から登録できる。
デフォルトの設定のまま登録でOK。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.yahoo-help.jp/app/answers/detail/p/537/a_id/43398&#34; target=&#34;_blank&#34;&gt;アプリケーションIDを登録する&lt;/a&gt;に記入例があるので、
ちゃんとした登録をする場合は参考になる。&lt;/p&gt;

&lt;h1 id=&#34;お試しアクセス&#34;&gt;お試しアクセス&lt;/h1&gt;

&lt;p&gt;登録時に表示されるClient IDを使って、以下のURLにブラウザからアクセスする。
&lt;code&gt;CLIENT_ID&lt;/code&gt;という部分を自分のClient IDに置き換えること。
東京都の各区の情報が表示されれば成功。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;https://map.yahooapis.jp/geocode/V1/geoCoder?appid=CLIENT_ID&amp;amp;query=東京都
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&#34;rubyでアクセス&#34;&gt;Rubyでアクセス&lt;/h1&gt;

&lt;p&gt;上と同じく、&lt;code&gt;CLIENT_ID&lt;/code&gt;という部分を自分のClient IDに置き換えること。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;json&#39;
require &#39;open-uri&#39;

CLIENT_ID = &#39;CLIENT_ID&#39;

class Geocoder
  def exec

    base_url = &#39;https://map.yahooapis.jp/geocode/V1/geoCoder&#39;
    params = {
      &#39;appid&#39; =&amp;gt; CLIENT_ID,
      &#39;query&#39; =&amp;gt; &#39;東京都&#39;,
      &#39;results&#39; =&amp;gt; &#39;1&#39;,
      &#39;output&#39; =&amp;gt; &#39;json&#39;,
    }
    url = base_url + &#39;?&#39; + URI.encode_www_form(params)

    res = JSON.parse(open(url).read)
    lon, lat = res[&#39;Feature&#39;][0][&#39;Geometry&#39;][&#39;Coordinates&#39;].split(&#39;,&#39;)
    puts &amp;quot;経度: #{lon}&amp;quot;
    puts &amp;quot;緯度: #{lat}&amp;quot;
  end
end

geocoder = Geocoder.new
geocoder.exec
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>AWSのRDSインスタンスをCLIで操作</title>
      <link>https://shidetake.com/aws_cli/</link>
      <pubDate>Sun, 13 May 2018 17:45:37 +0900</pubDate>
      
      <guid>https://shidetake.com/aws_cli/</guid>
      <description>&lt;p&gt;AWSのRDSは使っていないときは停止しないともったいない。
とは言え、毎回ブラウザでログインして操作するのはかったるいので、コマンドラインで操作できるようにした。
&lt;code&gt;aws&lt;/code&gt;コマンドは既に導入しているという前提。&lt;/p&gt;

&lt;h1 id=&#34;インスタンス名の取得&#34;&gt;インスタンス名の取得&lt;/h1&gt;

&lt;p&gt;操作するインスタンスの名前を取得する。
自分で名前を付けているので、わざわざ取得しなくても知っていると思うが。
飛ばして次に進んでも良い。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ aws rds describe-db-instances
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;とすると、ずらずらとインスタンスの情報が出てくる。
&lt;code&gt;DBInstanceIdentifier&lt;/code&gt;という項目に名前がある。&lt;/p&gt;

&lt;h1 id=&#34;停止&#34;&gt;停止&lt;/h1&gt;

&lt;p&gt;以下のコマンドで停止できる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ aws rds stop-db-instance --db-instance-identifier $instance
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&#34;開始&#34;&gt;開始&lt;/h1&gt;

&lt;p&gt;以下のコマンドで開始できる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ aws rds start-db-instance --db-instance-identifier $instance
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;GitHubに&lt;a href=&#34;https://github.com/shidetake/aws/blob/master/aws_rds.sh&#34; target=&#34;_blank&#34;&gt;簡単に操作するためのシェルスクリプト&lt;/a&gt;を置いたので、参考にして欲しい。
インスタンスの名前と状態のリスト表示、全インスタンスの開始、全インスタンスの停止ができる（はず）。
手元の環境だと1インスタンスしかないので、複数インスタンスがある場合のテストはしていない。&lt;/p&gt;

&lt;h1 id=&#34;参考&#34;&gt;参考&lt;/h1&gt;

&lt;p&gt;&lt;a href=&#34;https://blog.manabusakai.com/2017/06/stopping-rds/&#34; target=&#34;_blank&#34;&gt;RDS の停止機能を使ってコストを半分まで削減してみた&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>画像を任意のサイズ（容量）に変換する</title>
      <link>https://shidetake.com/big_image/</link>
      <pubDate>Sun, 06 May 2018 21:37:25 +0900</pubDate>
      
      <guid>https://shidetake.com/big_image/</guid>
      <description>&lt;p&gt;テストのために容量の大きい画像が必要になったが、
案外見つからなくて、結局ImageMagickで適当な画像を変換して作ることにしたので、そのやり方を記す。&lt;/p&gt;

&lt;p&gt;iPhoneとかで取った写真だと2〜3MBくらい。
今回欲しかったのは5MB以上だったのでダメ。&lt;/p&gt;

&lt;p&gt;前に一眼レフで撮ってもらった写真があったなと思い、Google Photoで探したら、数百KBくらいだった。
Google Photoが頑張って圧縮しちゃっていたようだ。&lt;/p&gt;

&lt;p&gt;インターネットで適当に探しても、5MB以上となるとなかなか出てこない。
1GBオーバーの天体画像なら出てきたが、さすがにでかすぎた。&lt;/p&gt;

&lt;p&gt;ということで、ImageMagickに頼ることにした。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;convert input.jpg -resize 2048x2048 output.bmp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じ。ビットマップなので、2048x2048の画像でだいたい12MB。&lt;/p&gt;

&lt;p&gt;完全に任意の容量にするにはちょっと計算が必要だけど、ざっくりでいいならこれで作れる。
ビットマップにしているのは、無圧縮のほうが計算しやすいため。
RGBそれぞれ1バイトずつなので、1画素あたり3MBになる。
1024x1024サイズだと、画素数がちょうど1Mなので、3MB。5MBには少し足りないので、
縦横倍にして4倍の12MBの画像になるというわけ。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Herokuの公開URLをコマンドで確認する</title>
      <link>https://shidetake.com/heroku_domains/</link>
      <pubDate>Sat, 10 Mar 2018 15:37:24 +0900</pubDate>
      
      <guid>https://shidetake.com/heroku_domains/</guid>
      <description>&lt;p&gt;最近、&lt;a href=&#34;https://railstutorial.jp&#34; target=&#34;_blank&#34;&gt;Ruby on Railsチュートリアル&lt;/a&gt;を進めているんだけど、
その中でHerokuを使うことになる。で、途中まで進めて、別の日に続きをやろうとすると、作っているWebサービスのURLがわからなくなり、
いちいちHerokuにログインして確認するという手間が発生する。&lt;/p&gt;

&lt;p&gt;たぶん&lt;code&gt;heroku&lt;/code&gt;コマンドにあるだろうと思って探したらあったので、備忘録的に残しておく。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ heroku domains
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これだけ。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title></title>
      <link>https://shidetake.com/index.js</link>
      <pubDate>Sun, 18 Feb 2018 22:50:07 +0900</pubDate>
      
      <guid>https://shidetake.com/index.js</guid>
      <description></description>
    </item>
    
    <item>
      <title>AlexaとNode-REDを利用したスマートホームスキル作成時の注意</title>
      <link>https://shidetake.com/alexa_node_red1/</link>
      <pubDate>Sun, 28 Jan 2018 07:56:05 +0900</pubDate>
      
      <guid>https://shidetake.com/alexa_node_red1/</guid>
      <description>&lt;p&gt;最近、AlexaとNode-REDを連携してスマートホームスキルを作っている。
&lt;a href=&#34;../pyatv/&#34;&gt;前回のApple TVを操作するスクリプト&lt;/a&gt;もその一環だったのだが、うまく動かなかったのでその対処法を書く。&lt;/p&gt;

&lt;h2 id=&#34;alexaとnode-redの連携&#34;&gt;AlexaとNode-REDの連携&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://qiita.com/kikuzo/items/753b5065dde9633bda18&#34; target=&#34;_blank&#34;&gt;Amazon Echoとラズパイで、音声で照明をon/offする&lt;/a&gt;という記事を参考にした。&lt;/p&gt;

&lt;p&gt;丁寧に書いてあって、補足する必要が全く無いので、連携についてはこちらの記事を読むといい。&lt;/p&gt;

&lt;h2 id=&#34;execにpyatvを設定&#34;&gt;execにpyatvを設定&lt;/h2&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/alexa_node_red1.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;こんな感じで、「Alexa, テレビつけて」でmenuボタンを押すようにしたんだけど、うまく動かない。
よく見ると、&lt;code&gt;atvremote&lt;/code&gt;コマンドが見つからないというエラーが出ていた。&lt;/p&gt;

&lt;p&gt;systemdで起動しているサービスは、ユーザーの環境変数を読み込まないらしい。
systemdについては&lt;a href=&#34;../dasher_systemd/&#34;&gt;前にも書いた&lt;/a&gt;が、絶対パスで書かないといけないとか、いろいろ作法があって面倒くさい。&lt;/p&gt;

&lt;h2 id=&#34;systemdの設定変更&#34;&gt;systemdの設定変更&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt;を使って導入したソフトをsystemdで使う場合の注意点は&lt;a href=&#34;https://github.com/pyenv/pyenv/wiki/Deploying-with-pyenv&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;pyenv&lt;/code&gt;の公式に記載があった&lt;/a&gt;のでこれを参考にした。&lt;/p&gt;

&lt;p&gt;まず以下のコマンドでNode-REDのサービスファイルの場所を調べる。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo systemctl status nodered
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;たぶん &lt;code&gt;/lib/systemd/system/nodered.service&lt;/code&gt;にある。
これを書き換える。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;Environment=&amp;quot;PI_NODE_OPTIONS=--max_old_space_size=256&amp;quot;
Environment=&amp;quot;PATH=/home/user_name/.pyenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;PI_NODE_OPTIONS&lt;/code&gt;という環境変数を設定しているところの下に、&lt;code&gt;PATH&lt;/code&gt;の設定を追加する。
デフォルトでは、&lt;code&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&lt;/code&gt;が&lt;code&gt;PATH&lt;/code&gt;に設定されているようなので、
そこに追加する形。&lt;/p&gt;

&lt;p&gt;ちなみに環境変数の展開もできないので、こんな書き方はNG。ホントに面倒くさい。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;Environment=&amp;quot;PATH=/home/user_name/.pyenv/shims:$PATH&amp;quot;
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>Apple TVをRaspberry Piで操作</title>
      <link>https://shidetake.com/pyatv/</link>
      <pubDate>Fri, 26 Jan 2018 22:28:20 +0900</pubDate>
      
      <guid>https://shidetake.com/pyatv/</guid>
      <description>&lt;p&gt;スマートホーム化のために、いろんな家電をRaspberry Piで動かせるようにしている。
テレビはほとんどApple TVのために存在しているので、これを動かしてみた。&lt;/p&gt;

&lt;p&gt;使ったのは、&lt;code&gt;pyatv&lt;/code&gt;というツール。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/postlund/pyatv&#34; target=&#34;_blank&#34;&gt;postlund/pyatv&lt;/a&gt;にインストール方法から使い方まで書いてあるが、
一部わかりにくいところがあったので、そこだけ説明する。&lt;/p&gt;

&lt;h2 id=&#34;初期設定&#34;&gt;初期設定&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pip install pyatv&lt;/code&gt;したあと、ペアリングが必要。
iTunesのホームシェアリングを有効にしている場合はいらないようだけど、
無効の場合は、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;atvremote pair
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;して、Apple TV側で、設定 &amp;gt; リモコンとデバイス &amp;gt; Remote Appとデバイス と進み、pyatvを選択して、pinに1234と入力すればよい。&lt;/p&gt;

&lt;h2 id=&#34;動作確認&#34;&gt;動作確認&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;atvremote -a menu
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これでMENUボタンを押したときと同じように動けばOK。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Gmailに重要なメールが届いたらLINEに通知する</title>
      <link>https://shidetake.com/gmail2line/</link>
      <pubDate>Thu, 21 Dec 2017 23:37:27 +0900</pubDate>
      
      <guid>https://shidetake.com/gmail2line/</guid>
      <description>&lt;p&gt;最近はめっきりメールを使わなくなった。iOSもメール機能なんかどうでもいいと思っているのか、
いまいち使い勝手が悪い。Gmailのプッシュ通知に非対応ってどうなの。&lt;/p&gt;

&lt;p&gt;それでもAmazonの注文メールやら、航空券の予約完了メールやら、色々な場面で送られてくるので、嫌々ながら使っている。
中には緊急性のあるメールもあって、その筆頭が銀行引き落とし失敗メール。残高が足りずに引き落とされなかったときに送られてくる。
早めに気づいて入金できれば、再度引き落としのタイミングがあって、なんとかカードを止められずにすむ。&lt;/p&gt;

&lt;p&gt;今回は、引き落としに失敗した時にLINE通知してくれるシステムを構築する。&lt;/p&gt;

&lt;h2 id=&#34;参考サイト&#34;&gt;参考サイト&lt;/h2&gt;

&lt;p&gt;先に白状しておくと、この記事は
&lt;a href=&#34;https://asatte.biz/gmail-line/&#34; target=&#34;_blank&#34;&gt;Google Apps ScriptでGmailの特定のメールを受信したらLINEと連携して通知する&lt;/a&gt;という記事のほとんどパクリです。&lt;/p&gt;

&lt;h2 id=&#34;構成&#34;&gt;構成&lt;/h2&gt;

&lt;p&gt;Google Apps ScriptとLINE Messaging APIを組み合わせて使う。
IFTTTあたりを使うともっと簡単に作れるが、ポーリング間隔が数時間と長すぎるので今回はNG。
Google Apps Scriptだと1分間隔でポーリング可能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/gmail2line.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;事前準備&#34;&gt;事前準備&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;LINE Messaging APIを使えるようにしておく(&lt;a href=&#34;../line_push/&#34;&gt;別記事参照&lt;/a&gt;)。&lt;/li&gt;
&lt;li&gt;Gmailにtreatedラベルを作る

&lt;ul&gt;
&lt;li&gt;1度処理したメールを再度処理しないようにするためのラベル&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;google-apps-script&#34;&gt;Google Apps Script&lt;/h2&gt;

&lt;p&gt;以下のようなスクリプトを作る。&lt;/p&gt;

&lt;p&gt;Gmailから&lt;code&gt;QUERY&lt;/code&gt;にマッチするメールを取得して、メールの数だけLINEにメッセージを送る。
処理したメールはtreatedラベルを付けて、再度処理しないようにしている。（そのために&lt;code&gt;QUERY&lt;/code&gt;で&lt;code&gt;-label:treated&lt;/code&gt;している）&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;var CHANNEL_ACCESS_TOKEN = &#39;YOUR_CHANNEL_ACCESS_TOKEN&#39;;
var LINE_USER_ID = &#39;YOUR_LINE_USER_ID&#39;;
var QUERY = &#39;-label:treated subject:未済&#39;;

function main() {
  var threads = GmailApp.search(QUERY, 0, 10); 
  var messages = GmailApp.getMessagesForThreads(threads);
  
  for (var i in messages) {
    for (var j in messages[i]) {
      line_push(&#39;MUFJからの引き落とし失敗してるよ&#39;);
    }
  }
  
  // add label:treated
  var label_treated = GmailApp.getUserLabelByName(&#39;treated&#39;);
  for (var i in threads) {
    threads[i].addLabel(label_treated);    
  }
}

// LINEにプッシュ通知する
function line_push(message) {
  var postData = {
    &amp;quot;to&amp;quot;: LINE_USER_ID,
    &amp;quot;messages&amp;quot; : [
      {
        &amp;quot;type&amp;quot; : &amp;quot;text&amp;quot;,
        &amp;quot;text&amp;quot; : message
      }
    ]
  };
  
  var options = {
    &amp;quot;method&amp;quot; : &amp;quot;post&amp;quot;,
    &amp;quot;headers&amp;quot; : {
      &amp;quot;Content-Type&amp;quot; : &amp;quot;application/json&amp;quot;,
      &amp;quot;Authorization&amp;quot; : &amp;quot;Bearer &amp;quot; + CHANNEL_ACCESS_TOKEN
    },
    &amp;quot;payload&amp;quot; : JSON.stringify(postData)
  };
  
  UrlFetchApp.fetch(&amp;quot;https://api.line.me/v2/bot/message/push&amp;quot;, options);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;トリガの設定&#34;&gt;トリガの設定&lt;/h2&gt;

&lt;p&gt;Google Apps Scriptのスクリプト編集画面で、編集 &amp;gt; 現在のプロジェクトのトリガーを選択する。
ここで、main関数を任意の時間間隔で実行するように設定できる。
最短は1分なのでとりあえず1分にしておいた。&lt;/p&gt;

&lt;h2 id=&#34;おまけ&#34;&gt;おまけ&lt;/h2&gt;

&lt;p&gt;読めばわかると思うが、今回の例は三菱東京UFJ銀行の口座からの引き落としに失敗したときのケース。
このメールが届くようにするには、三菱東京UFJダイレクトで設定する必要がある（設定したのはだいぶ前なので詳細は忘れた）。&lt;/p&gt;

&lt;p&gt;で、みずほ銀行も使っているのでこっちも同じようにしようと思ったら、みずほは通知メールサービスが無いらしい。
結構古い情報だけど、&lt;a href=&#34;http://moneylab.ldblog.jp/archives/51639875.html&#34; target=&#34;_blank&#34;&gt;このブログ記事&lt;/a&gt;によると、銀行によって通知サービスが無かったり、有料だったりする模様。&lt;/p&gt;

&lt;p&gt;サブバンクをみずほから新生銀行あたりに乗り換えようかなと思った。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>定期的なリマインドをLINE BOTにやってもらう</title>
      <link>https://shidetake.com/line_feeding/</link>
      <pubDate>Thu, 21 Dec 2017 22:46:24 +0900</pubDate>
      
      <guid>https://shidetake.com/line_feeding/</guid>
      <description>&lt;p&gt;自分だけで完結するような定期処理は、適当なアラームアプリあたりに設定すれば良いが、
他人にお願いしていることは、わざわざアラーム設定しておいてと頼むのも角が立つので、こちらからリマインドしたい。&lt;/p&gt;

&lt;p&gt;でも面倒なので、LINE BOTに頼むことにした。&lt;/p&gt;

&lt;h2 id=&#34;構成&#34;&gt;構成&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;../line_push/&#34;&gt;LINE BOTでPUSH通知する&lt;/a&gt;と&lt;a href=&#34;../make_cron/&#34;&gt;Makefileでcronを登録する&lt;/a&gt;を組み合わせるだけ。&lt;/p&gt;

&lt;p&gt;詳細はそれぞれの記事を読んでもらうとして、ソースを記載する。&lt;/p&gt;

&lt;h2 id=&#34;スクリプト&#34;&gt;スクリプト&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/sh
curl -X POST \
-H &#39;Content-Type:application/json&#39; \
-H &#39;Authorization: Bearer {CHANNEL_ACCESS_TOKEN}&#39; \
-d &#39;{
    &amp;quot;to&amp;quot;: &amp;quot;GROUPID&amp;quot;,
    &amp;quot;messages&amp;quot;:[
        {
            &amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;,
            &amp;quot;text&amp;quot;: &amp;quot;エサあげた？&amp;quot;
        }
    ]
}&#39; https://api.line.me/v2/bot/message/push
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ここで、&lt;code&gt;CHANNEL_ACCESS_TOKEN&lt;/code&gt;はLINE depelopersで取得するトークン。
&lt;code&gt;GROUPID&lt;/code&gt;は、LINEのグループを識別するためのID。
これの取得方法は前の記事には書いてなかったので説明する。&lt;/p&gt;

&lt;h2 id=&#34;group-idの取得&#34;&gt;GROUP IDの取得&lt;/h2&gt;

&lt;p&gt;以下のスクリプトをGoogle Apps Scriptでウェブアプリケーションとして導入し、
LINE BOTのWebhook URLに紐付ける。&lt;/p&gt;

&lt;p&gt;その状態で、LINEグループにBOT招待すれば、example@gmailにGROUP IDが通知される。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;function doPost(e) {
  Logger.log(&#39;doPost&#39;)
  var events = JSON.parse(e.postData.contents).events;
  events.forEach (function(event) {
    if (event.type == &amp;quot;join&amp;quot;) { mailGroupId(event); }
  });
}

function mailGroupId(e) {
  MailApp.sendEmail(&#39;example@gmail.com&#39;, &#39;groupId&#39;, e.source.groupId);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;cron登録&#34;&gt;cron登録&lt;/h2&gt;

&lt;p&gt;以下のような&lt;code&gt;Makefile&lt;/code&gt;を作って、&lt;code&gt;make install&lt;/code&gt;すればよい。
ここでは、毎日9時と18時に通知する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;install:
	crontab -l | grep line_feeding.sh || \
		(crontab -l; echo  &amp;quot;0 9,18 * * * /usr/local/bin/line_feeding.sh&amp;quot;) | crontab
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;ユースケース&#34;&gt;ユースケース&lt;/h2&gt;

&lt;p&gt;今回の例は、犬を預かってもらう人にエサをちゃんとあげてくれるようにリマインドするケース。
自分で聞くのは忘れそうだし、BOTに機械的に聞いてもらうことで角が立ちにくい（気がする）。&lt;/p&gt;

&lt;p&gt;あとは、今回の例では個人に通知するのではなく、グループに通知する形にしている。
これにより、相手が実行してくれたかどうか聞けるという利点もある（相手がBOTに返信してくれる人なら）。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Makefileでcronを登録する</title>
      <link>https://shidetake.com/make_cron/</link>
      <pubDate>Sun, 17 Dec 2017 11:18:31 +0900</pubDate>
      
      <guid>https://shidetake.com/make_cron/</guid>
      <description>&lt;p&gt;主に重複登録をしないようにするためのtipsを紹介する。&lt;/p&gt;

&lt;h2 id=&#34;cron登録コマンド&#34;&gt;cron登録コマンド&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;echo &amp;quot;0 9,18 * * * /usr/local/bin/example.sh&amp;quot; | crontab
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じ。とても簡単。
ただ、これは設定を上書きするという非常に危険なコマンド。
通常は以下のように使う。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;(crontab -l; echo  &amp;quot;0 9,18 * * * /usr/local/bin/example.sh&amp;quot;) | crontab
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;makefile化&#34;&gt;Makefile化&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;install:
    (crontab -l; echo  &amp;quot;0 9,18 * * * /usr/local/bin/example.sh&amp;quot;) | crontab
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;そのまんま。これで、&lt;code&gt;make install&lt;/code&gt;すれば&lt;code&gt;cron&lt;/code&gt; に登録される。
これの問題点は、重複登録してしまうこと。&lt;/p&gt;

&lt;p&gt;以下のように書くことで、重複登録が防げる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;install:
	crontab -l | grep example.sh || \
		(crontab -l; echo  &amp;quot;0 9,18 * * * /usr/local/bin/example.sh&amp;quot;) | crontab
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;||&lt;/code&gt;でつなぐことで、最初のコマンドが失敗したときだけ、次のコマンドを実行するようになる。
ここでは、既に登録されている&lt;code&gt;cron&lt;/code&gt;を&lt;code&gt;grep&lt;/code&gt;して、登録しようとしているスクリプトがあれば何もせず終了、
なければ登録する。&lt;/p&gt;

&lt;h2 id=&#34;uninstall&#34;&gt;uninstall&lt;/h2&gt;

&lt;p&gt;おまけ。uninstallは以下のように書けば良い。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;uninstall:
	crontab -l | grep -v example.sh | crontab
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>Alexa SkillsでHello worldする</title>
      <link>https://shidetake.com/alexa_skill_1/</link>
      <pubDate>Mon, 04 Dec 2017 22:44:29 +0900</pubDate>
      
      <guid>https://shidetake.com/alexa_skill_1/</guid>
      <description>&lt;p&gt;Amazon Echoが届いたので、早速スキルを作ってみることにした。
やはり最初はHello world。&lt;/p&gt;

&lt;h2 id=&#34;構成&#34;&gt;構成&lt;/h2&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/alexa_skill_1.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Amazon Echoにスキルをインストールして、声でスキルを呼び出す。
呼び出されたスキルは、登録されたAWS Lambdaの関数を呼び出して、処理を実行するという流れ。&lt;/p&gt;

&lt;h2 id=&#34;手順&#34;&gt;手順&lt;/h2&gt;

&lt;p&gt;ざっくり以下のような手順が必要&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS LambdaでAlexaに実行させる関数を作成&lt;/li&gt;
&lt;li&gt;Amazon DevelopperにてAlexaスキルを作成してAWS Lambda関数に紐付ける&lt;/li&gt;
&lt;li&gt;Amazon echoにインストール&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;aws-lamda&#34;&gt;AWS Lamda&lt;/h2&gt;

&lt;p&gt;AWS登録して、ログインしたら、AWS Lamdaの設定画面に進む。
右上にオハイオとか地名が書かれているので、東京にしておく。&lt;/p&gt;

&lt;p&gt;関数の作成ボタンを押して、設計図からalexa-skill-kit-sdk-factskillを選ぶ。
基本的な情報は以下のように設定した。&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設定項目&lt;/th&gt;
&lt;th&gt;設定&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;名前&lt;/td&gt;
&lt;td&gt;alexaHello&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;ロール&lt;/td&gt;
&lt;td&gt;テンプレートから新しいロールを作成&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;ロール名&lt;/td&gt;
&lt;td&gt;alexaHello&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Lambda関数のコードは以下のようにした。
ちなみにこれは、&lt;a href=&#34;https://github.com/alexa/skill-sample-nodejs-hello-world&#34; target=&#34;_blank&#34;&gt;alexa/skill-sample-nodejs-hello-world&lt;/a&gt;に公開されているindex.jsから必要な部分だけを残した。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;&#39;use strict&#39;;
const Alexa = require(&amp;quot;alexa-sdk&amp;quot;);

exports.handler = function(event, context, callback) {
    const alexa = Alexa.handler(event, context);
    alexa.registerHandlers(handlers);
    alexa.execute();
};

const handlers = {
    &#39;LaunchRequest&#39;: function () {
        this.emit(&#39;SayHello&#39;);
    },
    &#39;HelloWorldIntent&#39;: function () {
        this.emit(&#39;SayHello&#39;);
    },
    &#39;SayHello&#39;: function () {
        this.response.speak(&#39;Hello World!&#39;);
        this.emit(&#39;:responseReady&#39;);
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;関数の作成ボタンを押して、次の画面に進む。&lt;/p&gt;

&lt;p&gt;トリガーの設定画面が出るので、Alexa Skills Kitを選択して、保存すればAWS Lambda側の設定は完了。
右上にARNが表示されている。スキルが関数を呼び出す際に使うので控えておく。&lt;/p&gt;

&lt;h2 id=&#34;amazon-developper&#34;&gt;Amazon Developper&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;http://developer.amazon.com/&#34; target=&#34;_blank&#34;&gt;Amazon Developper Services and Technologies&lt;/a&gt;にアクセスして、右上のサインインからアカウントを作成する。&lt;/p&gt;

&lt;h2 id=&#34;スキル作成&#34;&gt;スキル作成&lt;/h2&gt;

&lt;p&gt;アカウントを作ってログインしたら、amazon alexaのページに進み、左上のメニューから、
&amp;ldquo;Alexa Skills Kit (ASK) &amp;gt; 始めてみよう&amp;rdquo;を選ぶ。
この辺のドキュメントは後で読むとして、&amp;rdquo;スキル開発を始める&amp;rdquo;。&lt;/p&gt;

&lt;h3 id=&#34;スキル情報&#34;&gt;スキル情報&lt;/h3&gt;

&lt;p&gt;スキル情報は以下の通り&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設定項目&lt;/th&gt;
&lt;th&gt;設定&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;スキルの種類&lt;/td&gt;
&lt;td&gt;カスタム対話モデル&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;言語&lt;/td&gt;
&lt;td&gt;Japanese&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;スキル名&lt;/td&gt;
&lt;td&gt;hello world&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;呼び出し名&lt;/td&gt;
&lt;td&gt;hello world&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;この設定で保存して次へ。&lt;/p&gt;

&lt;h3 id=&#34;対話モデル&#34;&gt;対話モデル&lt;/h3&gt;

&lt;p&gt;インテントスキーマ&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &amp;quot;intents&amp;quot;: [
    {
      &amp;quot;intent&amp;quot;: &amp;quot;HelloWorldIntent&amp;quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;このインテントはAWS Lambdaで作った関数のfunction名と紐付ける。&lt;/p&gt;

&lt;p&gt;サンプル発話&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;HelloWorldIntent hello
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;設定&#34;&gt;設定&lt;/h3&gt;

&lt;p&gt;サービスエンドポイントのタイプにAWS LamdaのARNを指定して、
デフォルト欄に、AWS Lambdaで作った関数のARNを入力する。
&lt;code&gt;arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:alexaHello&lt;/code&gt;みたいな形式。&lt;/p&gt;

&lt;h3 id=&#34;テスト&#34;&gt;テスト&lt;/h3&gt;

&lt;p&gt;次の画面でテストできる。画面中ほどにあるサービスシミュレーターで、先ほど入力したARNが選択されていることを確認して、
テキスト入力欄にhelloと書いてhello worldを呼び出す。&lt;/p&gt;

&lt;p&gt;サービスレスポンス側にある聴くというボタンを押すと、レスポンスが聴ける。ここでエラーが出ていなければ、スキルからAWS Lambdaの呼び出しは成功。&lt;/p&gt;

&lt;h2 id=&#34;公開情報&#34;&gt;公開情報&lt;/h2&gt;

&lt;p&gt;あとはインストールするための準備。Skills Beta Testingというテスト用のインストール機能があるのでこれを使う。
これを使うためには、画面左上にある各種設定を完成させてオールグリーンにする必要がある。
ここまで正しくできていれば、残りは&amp;rdquo;公開情報&amp;rdquo;と&amp;rdquo;プライバシーとコンプライアンス&amp;rdquo;だけのはず。&lt;/p&gt;

&lt;p&gt;基本的には全て適当に埋めれば良い。
公開情報は以下のように書いた。&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設定項目&lt;/th&gt;
&lt;th&gt;設定&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;カテゴリー&lt;/td&gt;
&lt;td&gt;Assistants&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;テストの手順&lt;/td&gt;
&lt;td&gt;hello&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;国と地域&lt;/td&gt;
&lt;td&gt;Amazonがスキルを配布するすべての国と地域&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;スキルの簡単な説明&lt;/td&gt;
&lt;td&gt;hello&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;スキルの詳細な説明&lt;/td&gt;
&lt;td&gt;hello&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;サンプルフレーズ&lt;/td&gt;
&lt;td&gt;hello&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;アイコン用の画像登録も必要。サイズ指定があるのでちゃんと正しいサイズの画像を用意すること。
&lt;code&gt;imagemagick&lt;/code&gt;があれば以下のコマンドで作れる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;convert -resize 108x108 original_icon.jpg icon_108.jpg
convert -resize 512x512 original_icon.jpg icon_512.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;プライバシーとコンプライアンス&#34;&gt;プライバシーとコンプライアンス&lt;/h2&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設定項目&lt;/th&gt;
&lt;th&gt;設定&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;このスキルを使って何かを購入したり、実際にお金を支払うことができますか？&lt;/td&gt;
&lt;td&gt;いいえ&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;このスキルはユーザーの個人情報を収集しますか？&lt;/td&gt;
&lt;td&gt;いいえ&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;このスキルは13歳未満の子供を対象にしていますか？&lt;/td&gt;
&lt;td&gt;いいえ&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;輸出コンプライアンス&lt;/td&gt;
&lt;td&gt;チェック&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;このスキルは広告を含みますか？&lt;/td&gt;
&lt;td&gt;いいえ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;これで保存を押すと、左側のSkills Beta Testingのステータスがアクティブになるので、テストの管理ボタンを押して設定画面に遷移する。&lt;/p&gt;

&lt;h2 id=&#34;skills-beta-testing&#34;&gt;Skills Beta Testing&lt;/h2&gt;

&lt;p&gt;テスターを追加ボタンから自分のメールアドレスを入力すると、テスター招待メールが届く。
2つリンクがあるが、2つ目のリンクが日本人向けのもの。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;JP customers: To get started, follow this link:  
Enable Alexa skill &amp;quot;hello world&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これをクリックして、スキルを有効化すれば使えるようになる。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>git showの差分をvimdiffで見る</title>
      <link>https://shidetake.com/git_showtool/</link>
      <pubDate>Sat, 25 Nov 2017 20:45:30 +0900</pubDate>
      
      <guid>https://shidetake.com/git_showtool/</guid>
      <description>&lt;p&gt;以前、&lt;a href=&#34;../git_difftool/&#34;&gt;git diffをvimdiffで見る方法&lt;/a&gt;を紹介した。
今回は&lt;code&gt;git show&lt;/code&gt;を&lt;code&gt;vimdiff&lt;/code&gt;で見る方法を紹介する。&lt;/p&gt;

&lt;h2 id=&#34;git-show&#34;&gt;git show&lt;/h2&gt;

&lt;p&gt;あるコミットの詳細を表示するコマンド。&lt;code&gt;diff&lt;/code&gt;も表示してくれる。
&lt;code&gt;git diff&lt;/code&gt;程使う機会は少ないかもしれないが、個人的にはかなりよく使うコマンド。&lt;/p&gt;

&lt;p&gt;コードレビューや不具合解析なんかで、過去のコミットでの修正内容を確認するのに便利なんだけど、
クラシックな&lt;code&gt;diff&lt;/code&gt;で表示されるのが気に入らなかったので&lt;code&gt;vimdiff&lt;/code&gt;で表示する方法を探していた。&lt;/p&gt;

&lt;h2 id=&#34;エイリアス&#34;&gt;エイリアス&lt;/h2&gt;

&lt;p&gt;結論から言うと、&lt;code&gt;git show&lt;/code&gt;で&lt;code&gt;vimdiff&lt;/code&gt;を使う方法はわからなかった。
環境変数&lt;code&gt;GIT_EXTERNAL_DIFF&lt;/code&gt;にうまいこと設定できればできそうな気もするが、情報が少ない。&lt;/p&gt;

&lt;p&gt;今回はエイリアスを使った方法にした。
&lt;code&gt;git show&lt;/code&gt;と言いつつ、裏では&lt;code&gt;git diff&lt;/code&gt;を使っている。実際、&lt;code&gt;git show&lt;/code&gt;したときの&lt;code&gt;diff&lt;/code&gt;は&lt;code&gt;git diff&lt;/code&gt;を使っているのだから問題ない。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[alias]
    showtool = &amp;quot;!sh -c &#39;git difftool &amp;quot;${0}&amp;quot;~ &amp;quot;${0}&amp;quot;&#39;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;チルダを使って1世代前のコミットからの差分を見るような形にしている。
&lt;code&gt;sh -c&lt;/code&gt;を使って&lt;code&gt;git&lt;/code&gt;を呼び出すような冗長な書き方は、ハッシュやブランチ名を引数として渡すため。&lt;/p&gt;

&lt;p&gt;このエイリアスだと引数がないと動かない。
オリジナルの&lt;code&gt;git show&lt;/code&gt;の場合、引数を指定しないときはHEADを指定したときの動作をするので、これと揃えるために以下のように変更した。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[alias]
    showtool = &amp;quot;!sh -c &#39;if [ &amp;quot;sh&amp;quot; == &amp;quot;${0}&amp;quot; ]; then REVISION=&amp;quot;HEAD&amp;quot;; else REVISION=&amp;quot;${0}&amp;quot;; fi; git difftool $REVISION~ $REVISION&#39;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;おまけ&#34;&gt;おまけ&lt;/h2&gt;

&lt;p&gt;差分ファイルが大量にあると、全ファイルの差分を表示するので非常にめんどくさいことになる。
そこで、&lt;code&gt;peco&lt;/code&gt;を使って選択的に差分ファイルを見れるようにしている。これが完成形。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[alias]
    showtool = &amp;quot;!sh -c &#39;if [ &amp;quot;sh&amp;quot; == &amp;quot;${0}&amp;quot; ]; then REVISION=&amp;quot;HEAD&amp;quot;; else REVISION=&amp;quot;${0}&amp;quot;; fi;\
        git log -1 --stat-width=800 $REVISION | grep \&amp;quot;|\&amp;quot; | awk \&amp;quot; {print \\$1}\&amp;quot; | peco | xargs -o git difftool $REVISION~ $REVISION&#39;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;なかなかトリッキーなコマンドなので、解説はいつか余裕があるときに書くことにする。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>マンション付属の光回線が早くなった話</title>
      <link>https://shidetake.com/ebroad/</link>
      <pubDate>Wed, 15 Nov 2017 23:11:02 +0900</pubDate>
      
      <guid>https://shidetake.com/ebroad/</guid>
      <description>&lt;p&gt;自宅の光回線が遅くて、&lt;a href=&#34;../speedtest/&#34;&gt;スピードテストを自動実行するスクリプトで現状把握してみるといった内容の記事&lt;/a&gt;を書いた。
その後の話。&lt;/p&gt;

&lt;h2 id=&#34;光回線についての調査&#34;&gt;光回線についての調査&lt;/h2&gt;

&lt;p&gt;マンション付属の光回線で、壁についているLANポートに繋ぐと何の手続きもせずに接続できるタイプのもの。
当時の資料には書いてあったかもしれないが、この時点ではどんな会社の回線なのか不明だった。&lt;/p&gt;

&lt;p&gt;マンションの管理会社に電話すれば一発だが、平日昼間に電話するのが難しいので、ひとまず調べられる範囲で調べてみた。
まずはとりあえず&lt;code&gt;traceroute&lt;/code&gt;してみた。
するといくつかローカルっぽいIPアドレスを経て（恐らくマンションのルーター）、ap.yournet.ne.jpなんちゃらみたいな所につながっていた。
このアドレスで検索してみると、どうやらfreebit系と呼ばれる悪名高いバックボーンであることがわかった。&lt;/p&gt;

&lt;p&gt;ざっと調べたところ、freebit系プロバイダというのは、フリービット株式会社のYourNet ISP Networkサービスというものを利用しているISPのことを言うらしい。
プロバイダ機能を自前で持たないISPに対してその機能を提供するというサービスのようだ。&lt;/p&gt;

&lt;p&gt;このあたりを調べている時に知ったのだが、
&lt;a href=&#34;https://env.b4iine.net&#34; target=&#34;_blank&#34;&gt;確認くん+&lt;/a&gt;というウェブサイトにアクセスすると、&lt;code&gt;traceroute&lt;/code&gt;などしなくても簡単にわかる。
ここにアクセスして、お使いのプロバイダーがap.yournet.ne.jpならばfreebit系。&lt;/p&gt;

&lt;h2 id=&#34;プロバイダに確認&#34;&gt;プロバイダに確認&lt;/h2&gt;

&lt;p&gt;マンションの管理会社に聞くと、イーブロードというプロバイダが管理しているとのことだったので、
電話して状況を確認してみることにした。&lt;/p&gt;

&lt;p&gt;週末に電話すると、サポートにはつながるけど、少し専門的な話になると対応できないようで、
平日に専門部署から折り返すとの返事。
平日の昼間は電話に出れない可能性が高いので、バックボーンをfreebitから変更することはできないか知りたいので結果を留守電に入れて欲しいと伝えた。&lt;/p&gt;

&lt;h2 id=&#34;あっけない結末&#34;&gt;あっけない結末&lt;/h2&gt;

&lt;p&gt;その後、すぐに留守電が入ったが、折り返し電話して欲しいという内容だけだった。
留守電に内容を入れてくれよと脱力して、しばらく放置していると、いつの間にか回線速度が速くなった。&lt;/p&gt;

&lt;p&gt;確認くん+で見てみると、プロバイダがOCNになっていた。どうやらバックボーンを変えたらしい。
数年前も同じくらいの速度が出ていたので、理由は不明だが、ときどきバックボーンが変わるのだと思う。
電話が効いたのかどうか知らないが、速くなったのでこれ以上の追求はやめた。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>WindowsでもLinuxでも動くMakefileを書く</title>
      <link>https://shidetake.com/make_win/</link>
      <pubDate>Sun, 05 Nov 2017 08:38:17 +0900</pubDate>
      
      <guid>https://shidetake.com/make_win/</guid>
      <description>&lt;p&gt;シェルのコマンドが異なるので、両方に対応するのは意外とめんどくさい。
今回はシンボリックリンクを作成するコマンドを例にして説明する。&lt;/p&gt;

&lt;p&gt;Windowsの&lt;code&gt;make&lt;/code&gt;はMinGWのものを想定している。&lt;/p&gt;

&lt;h2 id=&#34;linux専用&#34;&gt;Linux専用&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;install:
    ln -s ~/dotfiles/.vimrc ~/.vimrc;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;シンボリックリンクを作るコマンドをそのまま書いただけ。&lt;/p&gt;

&lt;h2 id=&#34;windows専用&#34;&gt;Windows専用&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;install:
    cmd.exe /C mklink $(HOME)\.vimrc $(HOME)\dotfiles\.vimrc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Windowsのコマンドを使う場合は、&lt;code&gt;cmd.exe /C&lt;/code&gt;を接頭語のように付ける。
これで、&lt;code&gt;mklink&lt;/code&gt;コマンドが使えるようになる。&lt;/p&gt;

&lt;p&gt;Linux用と比べて以下の点が異なっている&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sourceとtargetの順番が逆&lt;/li&gt;
&lt;li&gt;パスのディレクトリ区切りがバックスラッシュ&lt;/li&gt;
&lt;li&gt;ホームディレクトリの指定がチルダではなく&lt;code&gt;$(HOME)&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;環境変数&lt;code&gt;HOME&lt;/code&gt;をあらかじめ指定する必要があるかも&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;両対応版&#34;&gt;両対応版&lt;/h2&gt;

&lt;p&gt;愚直に書くと以下のようになる。
&lt;code&gt;ifeq ($(OS),Windows_NT)&lt;/code&gt;でWindowsかどうかを分岐させている。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;install:
ifeq ($(OS),Windows_NT)
    ln -s ~/dotfiles/.vimrc ~/.vimrc;
else
    cmd.exe /C mklink $(HOME)\.vimrc $(HOME)\dotfiles\.vimrc
endif
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;この書き方だと、シンボリックリンクを張るファイルが増えると、Linux用とWindows用のそれぞれに追記する必要がある。
以下のように書くとスマート。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;install:
	make link SOURCE:=$(HOME)/dotfiles/.vimrc TARGET:=$(HOME)/.vimrc

link:
ifeq ($(OS),Windows_NT)
    cmd.exe /C mklink $(subst /,\,$(TARGET)) $(subst /,\,$(SOURCE))
else
    ln -s $(SOURCE) $(TARGET)
endif
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;link&lt;/code&gt;というターゲットを用意して、関数のように使っている。
&lt;code&gt;link&lt;/code&gt;の中でOSを分岐させることで、それを呼び出す側を統一できるというわけ。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;subst&lt;/code&gt;というのは&lt;code&gt;make&lt;/code&gt;の文字列置換関数。スラッシュをバックスラッシュに置き換えるために使っている。&lt;/p&gt;

&lt;h2 id=&#34;おまけ&#34;&gt;おまけ&lt;/h2&gt;

&lt;p&gt;ここまででほぼ完成。&lt;/p&gt;

&lt;p&gt;最後に鬱陶しい出力を抑えるために、&lt;code&gt;--no-print-directory&lt;/code&gt;したり、シンボリックリンクが既に存在している場合にはコマンドをスキップする仕組みを入れたものを載せる
（解説は割愛）。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;MAKEFLAGS += --no-print-directory

install:
	make link SOURCE:=$(HOME)/dotfiles/.vimrc TARGET:=$(HOME)/.vimrc

link:
ifeq ($(OS),Windows_NT)
	@cmd.exe /C if not exist $(subst /,\,$(TARGET)) \
		cmd.exe /C mklink $(subst /,\,$(TARGET)) $(subst /,\,$(SOURCE))
else
	@if [ ! -e $(TARGET) ]; then\
		ln -s $(SOURCE) $(TARGET);\
	fi
endif
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>gitのdiffをvimdiffで見る</title>
      <link>https://shidetake.com/git_difftool/</link>
      <pubDate>Thu, 26 Oct 2017 23:34:29 +0900</pubDate>
      
      <guid>https://shidetake.com/git_difftool/</guid>
      <description>&lt;p&gt;少し前まで、&lt;code&gt;git&lt;/code&gt;のdiffに&lt;code&gt;vimdiff&lt;/code&gt;を使う場合、&lt;code&gt;git_diff_wrapper&lt;/code&gt;なるファイルを用意して、
&lt;code&gt;.gitconfig&lt;/code&gt;の&lt;code&gt;[diff]&lt;/code&gt;セクションに&lt;code&gt;external = git_diff_wrapper&lt;/code&gt;とするのが主流だった（と思っている）。&lt;/p&gt;

&lt;p&gt;今、各種dotfileを見直している中で、このダサいやり方を改善できないか調べたところ、
今の主流は&lt;code&gt;difftool&lt;/code&gt;を使うやり方のようなので、乗り換えた。&lt;/p&gt;

&lt;h2 id=&#34;gitconfig&#34;&gt;.gitconfig&lt;/h2&gt;

&lt;p&gt;まず早速&lt;code&gt;.gitconfig&lt;/code&gt;を示す。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[diff]
    tool = vimdiff
[difftool]
    prompt = false
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これでOK。ただし、&lt;code&gt;difftool&lt;/code&gt;を使うのでコマンドが変わる。
&lt;code&gt;diff&lt;/code&gt;のexternalを変える場合は、あくまで&lt;code&gt;git diff&lt;/code&gt;が使うツールを変更するという意味になるのだが、
&lt;code&gt;diff&lt;/code&gt;のtoolオプションを変える場合は、&lt;code&gt;git difftool&lt;/code&gt;が使うツールを指定するという意味になる。&lt;/p&gt;

&lt;h2 id=&#34;alias化&#34;&gt;alias化&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git_diff_wrapper&lt;/code&gt;手法では&lt;code&gt;git diff&lt;/code&gt;でよかったのに、今回の方法では&lt;code&gt;git difftool&lt;/code&gt;と打たないといけないはイマイチなので、
エイリアスを用意する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[diff]
    tool = vimdiff
[difftool]
    prompt = false
[alias]
    dt = difftool
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これでOK。本当は&lt;code&gt;diff = difftool&lt;/code&gt;としたかったが、もともと用意されているコマンドを上書きするようなエイリアスは作れないらしい。&lt;/p&gt;

&lt;h2 id=&#34;リードオンリー解除&#34;&gt;リードオンリー解除&lt;/h2&gt;

&lt;p&gt;さて、これで今までとだいたい同じになったが、カレントのファイルがリードオンリーで開かれるのだけが不満。
diff見ながら手直しするというのはよくあるので、毎回&lt;code&gt;:set noro&lt;/code&gt;なんて打ってられない。&lt;/p&gt;

&lt;p&gt;なぜか以下のようにしたら解決した。これが完成形。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[diff]
    tool = vimdiff
[difftool]
    prompt = false
[difftool &amp;quot;vimdiff&amp;quot;]
    cmd = vimdiff $LOCAL $REMOTE
[alias]
    dt = difftool
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;[difftool &amp;quot;vimdiff&amp;quot;]&lt;/code&gt;セクションで&lt;code&gt;cmd&lt;/code&gt;オプションを指定することで、&lt;code&gt;difftool&lt;/code&gt;で開くコマンドを指定できる。
ここに編集可能な状態で開くオプションを指定すればいいかなと思っていたが、特になんの指定もなくできた。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cmd&lt;/code&gt;オプションのデフォルトがどうなってるか知らないけど、リードオンリーな状態で開くようなオプションが指定されているのかな。&lt;/p&gt;

&lt;h2 id=&#34;関連ポスト&#34;&gt;関連ポスト&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;../git_showtool/&#34;&gt;git showの差分をvimdiffで見る&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>スピードテストを定期的に実行して回線速度の推移を見る</title>
      <link>https://shidetake.com/speedtest/</link>
      <pubDate>Mon, 16 Oct 2017 22:43:05 +0900</pubDate>
      
      <guid>https://shidetake.com/speedtest/</guid>
      <description>&lt;p&gt;自宅の回線速度が遅いので、なんとか改善できないか考えている。
まずは、現状把握ということで、スピードテストを実施した。
常に遅いのか、時間によって遅いのかなどがわかれば改善の手がかりになるかもしれないと考え、
cron定期的に実行してくれるようにした。&lt;/p&gt;

&lt;p&gt;Raspberry Piで動作する。&lt;/p&gt;

&lt;h2 id=&#34;実行結果&#34;&gt;実行結果&lt;/h2&gt;

&lt;p&gt;まずは作成したスクリプトで取得したデータを載せる。
csv形式で左からping値 [ms]、Download [Mbit/s], Upload [Mbit/s], 実行日時。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-csv&#34;&gt;167.908,2.23,5.37,2017/10/16 00:00:39
129.306,7.82,5.82,2017/10/16 01:00:41
88.187,18.31,19.27,2017/10/16 02:00:36
87.879,24.88,21.74,2017/10/16 03:00:36
156.129,26.47,16.25,2017/10/16 04:00:41
75.103,25.61,21.42,2017/10/16 05:00:36
94.37,26.99,21.17,2017/10/16 06:00:40
76.879,24.55,21.35,2017/10/16 07:00:38
71.107,24.72,21.67,2017/10/16 08:01:39
254.614,19.98,16.24,2017/10/16 09:00:37
81.341,23.46,18.63,2017/10/16 10:00:37
89.473,23.30,17.17,2017/10/16 11:00:38
275.777,23.40,10.58,2017/10/16 12:00:38
110.481,22.26,17.16,2017/10/16 13:00:36
149.522,25.06,18.38,2017/10/16 14:00:36
82.292,17.59,8.26,2017/10/16 15:00:38
202.99,17.83,4.05,2017/10/16 16:00:38
102.178,17.50,4.52,2017/10/16 17:00:37
114.628,14.78,16.30,2017/10/16 18:00:38
102.745,12.89,13.11,2017/10/16 19:00:39
1795.938,11.67,1.76,2017/10/16 20:00:51
699.312,6.57,3.20,2017/10/16 21:00:52
352.963,3.47,0.98,2017/10/16 22:00:47
480.408,3.98,2.48,2017/10/16 23:00:47
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ついでにグラフ化。
&lt;img src=&#34;https://shidetake.com/img/speedtest.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;昼間は25Mbpsくらい出ているらしい。15時以降から下がりだして、21時から25時までは1桁というありさま。&lt;/p&gt;

&lt;h2 id=&#34;speedtest-cli&#34;&gt;speedtest-cli&lt;/h2&gt;

&lt;h3 id=&#34;インストール&#34;&gt;インストール&lt;/h3&gt;

&lt;p&gt;スピードテストをCUIで行う場合は、恐らくspeedtest-cli一択。
&lt;code&gt;pip&lt;/code&gt;で入れるので、Pythonのインストールから。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo apt install python
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;続いて&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install speedtest-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;使い方&#34;&gt;使い方&lt;/h3&gt;

&lt;p&gt;一番シンプルなのは&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;speedtest
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これだけ。&lt;code&gt;speedtest-cli&lt;/code&gt;でも同じ結果が得られる。&lt;/p&gt;

&lt;p&gt;データ収集には不要な出力が多いのと、サーバーをping値で自動選択してしまうので、
以下のように使うことにする。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;speedtest --server 6476 --simple
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;シェルスクリプト&#34;&gt;シェルスクリプト&lt;/h2&gt;

&lt;h3 id=&#34;1ライン化&#34;&gt;1ライン化&lt;/h3&gt;

&lt;p&gt;上のコマンドをベースに、&lt;code&gt;cron&lt;/code&gt;で実行するシェルスクリプトを作成する。
1行にping, download, uploadをまとめたいので&lt;code&gt;tr&lt;/code&gt;でカンマと置換する。
また、速度以外は不要なので&lt;code&gt;awk&lt;/code&gt;を使って、以下のように書く。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;/usr/local/bin/speedtest --server 6476 --simple | awk &#39;{print $2}&#39; | tr &#39;\n&#39; &#39;,&#39; &amp;gt;&amp;gt; ~/tmp/speedtest.log
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;cron&lt;/code&gt;ではコマンドを絶対パスで指定する必要があるので、絶対パスで書いている。&lt;/p&gt;

&lt;h3 id=&#34;実行日時を追加&#34;&gt;実行日時を追加&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;date &amp;quot;+%Y/%m/%d %H:%M:%S&amp;quot; &amp;gt;&amp;gt; ~/tmp/speedtest.log
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;リトライ機能を追加&#34;&gt;リトライ機能を追加&lt;/h3&gt;

&lt;p&gt;ここまででほぼ完成だけど、結構な頻度で失敗してたのでリトライ機能を追加した。
&lt;code&gt;cron&lt;/code&gt;でリトライしてくれればいいのに。
以下が完成形。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

command=&amp;quot;/usr/local/bin/speedtest --server 6476 --simple&amp;quot;

NEXT_WAIT_TIME=0
until RET=`$command` || [ $NEXT_WAIT_TIME -eq 4 ]; do
  sleep $(( (NEXT_WAIT_TIME++) * 60 ))
done
echo &amp;quot;$RET&amp;quot; | awk &#39;{print $2}&#39; | tr &#39;\n&#39; &#39;,&#39; &amp;gt;&amp;gt; ~/tmp/speedtest.log
date &amp;quot;+%Y/%m/%d %H:%M:%S&amp;quot; &amp;gt;&amp;gt; ~/tmp/speedtest.log
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4回までリトライする。リトライ間隔は、1分、2分、3分、4分と伸ばしていくようにした。&lt;/p&gt;

&lt;h2 id=&#34;cron&#34;&gt;cron&lt;/h2&gt;

&lt;p&gt;上のシェルスクリプトを&lt;code&gt;cron&lt;/code&gt;に登録する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;crontab -e
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;すると、エディタが立ち上がる（もしくはエディタ選択画面になるので選ぶ）ので、
以下のように1時間毎に実行するように設定する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;0 * * * * /usr/local/bin/speedtest_cron.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これで完成。&lt;/p&gt;

&lt;h2 id=&#34;注意点&#34;&gt;注意点&lt;/h2&gt;

&lt;p&gt;実行環境はRaspberry Piを想定しているが、場合によってはRaspberry Pi自体がボトルネックとなって、
正しい速度が取れないので注意。うちの場合はMac Book Airを有線でつないで測定してもだいたい同じような値が取れることを確認している。&lt;/p&gt;

&lt;h2 id=&#34;関連ポスト&#34;&gt;関連ポスト&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;../ebroad/&#34;&gt;マンション付属の光回線が早くなった話&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Ubuntu 16.04にmysqlをインストールする</title>
      <link>https://shidetake.com/mysql_install/</link>
      <pubDate>Sun, 01 Oct 2017 22:27:25 +0900</pubDate>
      
      <guid>https://shidetake.com/mysql_install/</guid>
      <description>&lt;p&gt;aptで適当にインストールすれば使えると思っていたが、意外と手間取ったので、手順を書いておく。&lt;/p&gt;

&lt;h2 id=&#34;install&#34;&gt;install&lt;/h2&gt;

&lt;p&gt;ここは簡単。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo apt install mysql-server mysql-client
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんだけ。ただ、既に別のバージョンのものが入っていたりするので、キレイにしたい場合は、
先に以下のコマンドを送る。別バージョンのmysqlを削除することで、色々動かなくなる可能性もあるので注意。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo dpkg -l | grep mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;でインストール済みのmysqlを確認して、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo apt remove --purge mysql*
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じで、消していく。&lt;code&gt;--purge&lt;/code&gt;オプションは、関連する設定ファイルも一緒に削除するのもので、
この後の再インストール後に悪さしないためにも指定しておいたほうがよい。&lt;/p&gt;

&lt;h2 id=&#34;rootパスワード設定&#34;&gt;rootパスワード設定&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mysql -uroot
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;で入ろうとしたら、&lt;code&gt;ERROR 1698 (28000): Access denied for user &#39;root&#39;@&#39;localhost&#39;&lt;/code&gt;と怒られた。
初期パスワードは無かったと思ってたが、仕様が変わったらしい。&lt;/p&gt;

&lt;p&gt;で色々やってみたんだけど、実はやっぱり初期パスワードは設定されていなくて、&lt;code&gt;sudo&lt;/code&gt;つければいいだけだった。
ということで、改めて&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo mysql -uroot
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これでOK。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Raspberry PiでSystemdを使ってdasherをサービス化</title>
      <link>https://shidetake.com/dasher_systemd/</link>
      <pubDate>Tue, 26 Sep 2017 23:42:10 +0900</pubDate>
      
      <guid>https://shidetake.com/dasher_systemd/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../dash_button&#34;&gt;Amazon Dash Buttonを使ってLINEに通知する方法&lt;/a&gt;を以前書いた。
記事には記載していないが、このときは、init.dを使って自動起動するように設定していた。&lt;/p&gt;

&lt;p&gt;最近はinit.dは古くて、Systemdが推奨されているようなので、これに書き換えることにした。&lt;/p&gt;

&lt;h2 id=&#34;サンプルソース&#34;&gt;サンプルソース&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/maddox/dasher/wiki/Running-Dasher-on-a-Raspberry-Pi-at-startup&#34; target=&#34;_blank&#34;&gt;GitHubのWiki&lt;/a&gt;
にサンプルソースがあったのでそのまま記載する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[Unit]
Description=Dasher
After=network.target

[Service]
Type=simple
#user with access to dasher files
User=root
WorkingDirectory=/home/pi/dasher
#use command &amp;quot;which npm&amp;quot; to determine correct location of NPM
ExecStart=/usr/local/bin/npm run start
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;unitセクション&#34;&gt;Unitセクション&lt;/h3&gt;

&lt;p&gt;SystemdではUnitという単位で処理を管理する。
init.dで言うサービスに当たる概念のようだ。&lt;/p&gt;

&lt;p&gt;ここではdasherが1つのUnitということになる。&lt;/p&gt;

&lt;h4 id=&#34;description&#34;&gt;Description&lt;/h4&gt;

&lt;p&gt;Unitの説明を書く。&lt;/p&gt;

&lt;h4 id=&#34;after&#34;&gt;After&lt;/h4&gt;

&lt;p&gt;このUnitが起動するタイミングを記述する。
ここではnetwork.targetの後に実行することになる。&lt;/p&gt;

&lt;p&gt;.targetというのは今回のように順序関係や依存関係を定義する際に、複数のUnitをグループ化するために使われる。
network.targetの実体は、&lt;code&gt;/lib/systemd/system/network.target&lt;/code&gt;にあるが、内容を確かめずとも、ネットワーク関連のUnit群であることがわかる(のでこれ以上詳しくは見ない)。&lt;/p&gt;

&lt;p&gt;ちなみにSystemdの起動時にはdefault.targetというUnitが有効化されることになっており、これに依存するUnitがまとめて有効化されていく。&lt;/p&gt;

&lt;h3 id=&#34;serviceセクション&#34;&gt;Serviceセクション&lt;/h3&gt;

&lt;p&gt;このセクションはUnitの種類によって異なる。
デーモンを起動する場合には.serviceなのでServiceセクションとなる。他にも上に出てきた.targetや.deviceなどいくつかの種類がある。&lt;/p&gt;

&lt;h4 id=&#34;type&#34;&gt;Type&lt;/h4&gt;

&lt;p&gt;サービスプロセスの起動完了の判定方法。
&lt;code&gt;simple&lt;/code&gt;を指定すると、&lt;code&gt;ExecStart&lt;/code&gt;で指定したコマンドを実行したタイミングで起動完了とみなす。&lt;/p&gt;

&lt;p&gt;起動完了をどう使うかは未調査。上述のAfterなど順序関係のためだろうか。&lt;/p&gt;

&lt;h4 id=&#34;user&#34;&gt;User&lt;/h4&gt;

&lt;p&gt;実行ユーザを指定する。rootで実行することになる。&lt;/p&gt;

&lt;h4 id=&#34;workingdirectory&#34;&gt;WorkingDirectory&lt;/h4&gt;

&lt;p&gt;実行時のカレントディレクトリを指定する。
ここではpiユーザーのhomeにdasherを取得したことになっている模様。
通常はセキュリティの観点からもpiユーザー以外にしているはずなので、ここは変更必須。&lt;/p&gt;

&lt;h4 id=&#34;execstart&#34;&gt;ExecStart&lt;/h4&gt;

&lt;p&gt;サービスを起動するコマンド。フルパスじゃないとダメらしい。
このタイミングではまだパスが通ってないのだろうか？&lt;/p&gt;

&lt;h4 id=&#34;restart&#34;&gt;Restart&lt;/h4&gt;

&lt;p&gt;再起動条件を指定する。&lt;code&gt;on-failure&lt;/code&gt;は終了コード0以外で停止した場合に再起動する設定。
ようするに異常終了した時は再起動してねってこと。&lt;/p&gt;

&lt;h4 id=&#34;restartsec&#34;&gt;RestartSec&lt;/h4&gt;

&lt;p&gt;再起動する前の待ち時間。Restartの設定と合わせて読むと、異常終了を検知したら10秒後に再起動してねってこと。&lt;/p&gt;

&lt;h4 id=&#34;killmode&#34;&gt;KillMode&lt;/h4&gt;

&lt;p&gt;サービス停止時にプロセスが残っていた場合の処理を指定する。
&lt;code&gt;process&lt;/code&gt;はメインプロセスが残っていたらkillする設定。&lt;/p&gt;

&lt;h3 id=&#34;installセクション&#34;&gt;Installセクション&lt;/h3&gt;

&lt;p&gt;systemctl enable/disableに関する設定群&lt;/p&gt;

&lt;h4 id=&#34;wantedby&#34;&gt;WantedBy&lt;/h4&gt;

&lt;p&gt;init.dで言うrunlevelの指定に相当する。
&lt;code&gt;multi-user.target&lt;/code&gt;はrunlevel 3に相当するので、CUIでログインすると起動することになる。&lt;/p&gt;

&lt;h2 id=&#34;セットアップ&#34;&gt;セットアップ&lt;/h2&gt;

&lt;p&gt;上の設定を
&lt;code&gt;/etc/systemd/system/dasher.service&lt;/code&gt;に置いて、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl list-unit-files --type=service | grep dasher
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;すると、dasher.serviceがサービスとして認識されていることがわかる。
この時点ではdisable状態。&lt;/p&gt;

&lt;p&gt;続いて自動起動を有効化する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo systemctl enable dasher
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;してから、先程のコマンドを再送すると、enableに変わっているはず。
この状態で再起動する。そして、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo systemctl status dasher
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;でactive状態になっていればOK&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>DDNSでLANの外から自宅のRaspberry PiにSSH接続する</title>
      <link>https://shidetake.com/ddns/</link>
      <pubDate>Mon, 18 Sep 2017 21:51:18 +0900</pubDate>
      
      <guid>https://shidetake.com/ddns/</guid>
      <description>&lt;p&gt;普通のインターネット回線では、グローバルIPが定期的に変わってしまうため、
自宅に置いたRaspberry Piなんかをサーバー代わりに使うには不便。&lt;/p&gt;

&lt;p&gt;かといって、固定グローバルIPアドレスを貰うには月1000円くらい払う必要がある。&lt;/p&gt;

&lt;p&gt;そこで、Dynamic DNS (DDNS) というサービスを利用することにする。&lt;/p&gt;

&lt;h2 id=&#34;ddnsとは&#34;&gt;DDNSとは&lt;/h2&gt;

&lt;p&gt;Domain Name System (DNS) は、ドメイン名とIPアドレスを紐付けるシステムのこと。
サーバーへのアクセスは通常、IPアドレスによって対象サーバーを判別するが、
IPアドレスは数値の羅列で人間には覚えにくいので、ドメイン名という覚えやすい名称に変換する。&lt;/p&gt;

&lt;p&gt;Dynamic DNSというのは、その紐付けをDynamicに、つまり動的に行う。
グローバルIPが変化しても、ドメイン名との紐付けを再度行うことで、ドメイン名の固定ができるというわけ。&lt;/p&gt;

&lt;p&gt;世の中にはこんな便利なサービスを無料で提供してくれる人たちがいる。&lt;/p&gt;

&lt;h2 id=&#34;登録&#34;&gt;登録&lt;/h2&gt;

&lt;p&gt;いくつか選択肢はあるが、今回は&lt;a href=&#34;https://www.noip.com&#34; target=&#34;_blank&#34;&gt;no-ip&lt;/a&gt;というサービスを使うことにした。
なぜなら、&lt;a href=&#34;http://portaltan.hatenablog.com/entry/2017/02/07/092205&#34; target=&#34;_blank&#34;&gt;raspberryPi にnoipでDDNSの設定をする方法&lt;/a&gt;というサイトを参考にしたため。&lt;/p&gt;

&lt;p&gt;登録・設定から自動実行まで、ほぼほぼこの手順でいける。&lt;/p&gt;

&lt;p&gt;ただ、Dynamic Domain Update Client (DUC) の自動起動の設定だけは、少し好みではなかったのでアレンジした。&lt;/p&gt;

&lt;h2 id=&#34;ducの自動起動設定&#34;&gt;DUCの自動起動設定&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/etc/init.d/noip2&lt;/code&gt;を以下のように書き換えた。
自動起動の設定を先頭に追加しただけ。これにより、わざわざrc.localに起動のためのコマンドを書かなくとも、
自動で起動してくれる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#! /bin/sh
# /etc/init.d/noip2.sh
### BEGIN INIT INFO
# Provides:          noip2
# Required-Start:    $all
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:
### END INIT INFO

# Supplied by no-ip.com
# Modified for Debian GNU/Linux by Eivind L. Rygge &amp;lt;eivind@rygge.org&amp;gt;
# corrected 1-17-2004 by Alex Docauer &amp;lt;alex@docauer.net&amp;gt;

# . /etc/rc.d/init.d/functions  # uncomment/modify for your killproc

DAEMON=/usr/local/bin/noip2
NAME=noip2

test -x $DAEMON || exit 0

case &amp;quot;$1&amp;quot; in
    start)
    echo -n &amp;quot;Starting dynamic address update: &amp;quot;
    start-stop-daemon --start --exec $DAEMON
    echo &amp;quot;noip2.&amp;quot;
    ;;
    stop)
    echo -n &amp;quot;Shutting down dynamic address update:&amp;quot;
    start-stop-daemon --stop --oknodo --retry 30 --exec $DAEMON
    echo &amp;quot;noip2.&amp;quot;
    ;;

    restart)
    echo -n &amp;quot;Restarting dynamic address update: &amp;quot;
    start-stop-daemon --stop --oknodo --retry 30 --exec $DAEMON
    start-stop-daemon --start --exec $DAEMON
    echo &amp;quot;noip2.&amp;quot;
    ;;

    *)
    echo &amp;quot;Usage: $0 {start|stop|restart}&amp;quot;
    exit 1
esac
exit 0
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;動作確認&#34;&gt;動作確認&lt;/h2&gt;

&lt;p&gt;自動起動するかどうか、実際に再起動して確かめる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;からの、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo /usr/local/bin/noip2 -S

Process 445, started as noip2, (version 2.1.9)
Using configuration from /usr/local/etc/no-ip2.conf
Last IP Address set 0.0.0.0
Account hogehoge@gmail.com
configured for:
	host  hogehoge.ddns.net
Updating every 30 minutes via /dev/wlan0 with NAT enabled.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;とすると、なんとIPアドレスが0になってしまっている。
失敗か？と思ったが、特に問題なくつながる。
30分後に再度確認したら正しいIPアドレスがセットされていた。どうやら起動後1発目のIPアドレスセットの内容は反映されないらしい。
Last IP Addressとあるので、初回より前のアドレスを出そうとしているのだろうか。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>株価チャートからトレンドラインを引く 1</title>
      <link>https://shidetake.com/stock_trendline_1/</link>
      <pubDate>Sun, 17 Sep 2017 16:36:18 +0900</pubDate>
      
      <guid>https://shidetake.com/stock_trendline_1/</guid>
      <description>&lt;p&gt;以前、&lt;a href=&#34;../google_finance_api/&#34;&gt;Google Finance APIにより株価を取得する方法&lt;/a&gt;を書いた。
このデータからトレンドラインを引くスクリプトを作ろうと思う。&lt;/p&gt;

&lt;p&gt;まずは、トレンドラインを引くためのポイントを選択する。&lt;/p&gt;

&lt;h2 id=&#34;移動平均&#34;&gt;移動平均&lt;/h2&gt;

&lt;p&gt;取得した生データだと、エッジが立ちすぎていてトレンドラインのためのポイント選択には不向きなので、
とりあえず移動平均してみた。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;net/http&#39;
require &#39;numo/gnuplot&#39;

class GoogleFinance

  BASE_URL = &#39;https://finance.google.com/finance/getprices?&#39;

  def initialize(issue)
    @issue = issue
  end

  # 終値を取得する
  # param [String] 期間
  # param [String] 間隔 [秒]
  def fetch_close(period = &#39;1Y&#39;, interval_sec = 86400)
    raw_data = Net::HTTP.get(URI.parse(BASE_URL + &amp;quot;p=#{period}&amp;amp;i=#{interval_sec}&amp;amp;x=TYO&amp;amp;q=#{@issue}&amp;quot;))
    @close = []
    raw_data.lines do |line|
      @close &amp;lt;&amp;lt; line[/^[0-9]*,([0-9]*)/, 1]
    end
    @close.compact!.map!(&amp;amp;:to_i)
  end

  # 移動平均を求める
  # @param [Fixnum] 期間
  # @note 終値が取得できている前提
  def calc_move_average(term)
    # 移動平均のために端のデータを引き伸ばす
    tmp = @close[0..0] * (term / 2) + @close + @close[-1..-1] * (term / 2)

    @move_average =
      tmp.each_cons(term).map do |window|
        window.inject(:+) / term.to_f
      end
  end
end

ISSUE_TOYOTA = 7203 # TOYOTAの証券コード
MOVE_AVERAGE_TERM = 10 # 移動平均の期間

google_finance = GoogleFinance.new(ISSUE_TOYOTA)
close = google_finance.fetch_close
move_average_close = google_finance.calc_move_average(MOVE_AVERAGE_TERM)

Numo.gnuplot do
  set title: &#39;TOYOTA&#39;
  plot [close, w: &#39;lines&#39;, t: &#39;close&#39;],
       [move_average_close, w: &#39;lines&#39;, t: &#39;move\_average&#39;]
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じ。前回の記事からメソッド名など少し変えているので注意。&lt;/p&gt;

&lt;p&gt;移動平均を求める方法はググったらいくらでも出てくるのであまり書かないが、
データの端の扱いだけは注意が必要。今回は端のデータを引き伸ばすことにした。
0で埋めるとか色々あるが、簡単にできて誤差が少ないのが特徴。&lt;/p&gt;

&lt;p&gt;移動平均の期間をどう決めるかが難しいところだが、いったん10日とした。あとで調整する。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;img/2.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;極値&#34;&gt;極値&lt;/h2&gt;

&lt;p&gt;移動平均から極値を求める。
今回は、となりの値との差分を計算して、その差分の正負が入れ替わる瞬間を極値とする。
ただ、激しく値動きしていると、極値が多くなりすぎるので、少し計算期間を長くすることでフィルタする。
例えば計算期間を5とすると、その差分が正 =&amp;gt; 正 =&amp;gt; 負 =&amp;gt; 負ならば極大値、負 =&amp;gt; 負 =&amp;gt; 正 =&amp;gt; 正ならば極小値。&lt;/p&gt;

&lt;p&gt;この期間も移動平均の期間同様、どうやって決めるかが難しい。今回は7とした。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;net/http&#39;
require &#39;numo/gnuplot&#39;

class GoogleFinance
  # 省略

  # 極値を求める
  # @param [Fixnum] 期間 大きいほど微小な変動を無視する
  # @note move_averageが求まっている前提
  # @note 定めた期間の中央まで単調増加し、残りが単調減少していれば極小値とする
  # @note 定めた期間の中央まで単調減少し、残りが単調増加していれば極大値とする
  def calc_extremum(term)
    local_min = { x: [], y: [] }
    local_max = { x: [], y: [] }
    @move_average.each_cons(term).with_index do |window, i|
      mid_index = i + term / 2 # windowの中央
      diff = window.each_cons(2).map { |arr| arr[1] - arr[0] }
      if diff[0..(term / 2 - 1)].all? { |elm| elm &amp;lt; 0 } and
         diff[(term / 2)..-1].all? { |elm| 0 &amp;lt; elm }
        # 負 =&amp;gt; 正
        local_min[:x] &amp;lt;&amp;lt; mid_index
        local_min[:y] &amp;lt;&amp;lt; @close[mid_index]
      elsif diff[0..(term / 2 - 1)].all? { |elm| 0 &amp;lt; elm } and
         diff[(term / 2)..-1].all? { |elm| elm &amp;lt; 0 }
        # 正 =&amp;gt; 負
        local_max[:x] &amp;lt;&amp;lt; mid_index
        local_max[:y] &amp;lt;&amp;lt; @close[mid_index]
      end
    end
    return local_min, local_max
  end
end

ISSUE_TOYOTA = 7203 # TOYOTAの証券コード
MOVE_AVERAGE_TERM = 10 # 移動平均の期間
LOCAL_SIZE = 7 # 極値計算の範囲

google_finance = GoogleFinance.new(ISSUE_TOYOTA)
close = google_finance.fetch_close
move_average_close = google_finance.calc_move_average(MOVE_AVERAGE_TERM)
local_min, local_max = google_finance.calc_extremum(LOCAL_SIZE)

Numo.gnuplot do
  set title: &#39;TOYOTA&#39;
  plot [close, w: &#39;lines&#39;, t: &#39;close&#39;],
       [move_average_close, w: &#39;lines&#39;, t: &#39;move\_average&#39;],
       [local_min[:x], local_min[:y], w: &#39;points&#39;, t: &#39;local\_min&#39;],
       [local_max[:x], local_max[:y], w: &#39;points&#39;, t: &#39;local\_max&#39;]
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;GoogleFinanceクラスはcalc_extremumメソッド以外は変わっていないので省略した。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/stock_trendline_1.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;少し見にくいが、緑点が極小値、紫点が極大値。それなりにいい具合の場所を選んでいるように思える。
実際に線を引くのは次回。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Google Finance APIを使って株価チャートを作成</title>
      <link>https://shidetake.com/google_finance_api/</link>
      <pubDate>Mon, 04 Sep 2017 23:14:36 +0900</pubDate>
      
      <guid>https://shidetake.com/google_finance_api/</guid>
      <description>&lt;p&gt;株を買うかどうか判断するために株価チャートに補助線を書いたりする手法があるらしい。
手軽に補助線（トレンドラインと呼ぶらしい）を引いてくれるスクリプトでも作ろうかと思い、
まずは普通の株価チャートを描いてみた。&lt;/p&gt;

&lt;p&gt;そのへんに落ちてるアプリでもできそうだけど、最終的に買いかどうか判断させたり、
色々自由にカスタマイズするには自分で作ったほうがいいかなと。&lt;/p&gt;

&lt;p&gt;2017.9.16追記&lt;br /&gt;
これを公開した直後にAPIのURLが変わった模様。&lt;/p&gt;

&lt;p&gt;元は&lt;code&gt;https://www.google.com/finance/getprices?&lt;/code&gt;だったが、今は&lt;code&gt;https://finance.google.com/finance/getprices?&lt;/code&gt;になっている。
この記事のコードも修正したので、そのまま使えるはず。&lt;/p&gt;

&lt;h2 id=&#34;google-finance-api&#34;&gt;Google Finance API&lt;/h2&gt;

&lt;p&gt;国内で株価を取得するためのAPIは（正規には）ないらしい。
ただ、Google Finance APIという非公式のAPIがあり、使ってる人は使っているとか。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://ymtttk.hatenablog.jp/entry/2017/02/18/192130&#34; target=&#34;_blank&#34;&gt;google financeのAPIのメモ&lt;/a&gt;という記事が参考になる。
今回やりたいことのほとんどはここに書いてある。
違いは、&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rubyを使う (Pythonのが需要ありそうだけど)&lt;/li&gt;
&lt;li&gt;チャートを描く&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;くらい。&lt;/p&gt;

&lt;p&gt;まずは適当な銘柄の株価を以下のように取得する。
ここではトヨタにした（丸パクリ）。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;net/http&#39;

class GoogleFinance

  BASE_URL = &#39;https://finance.google.com/finance/getprices?&#39;

  def initialize(issue)
    @issue = issue
  end

  def stock(period = &#39;1Y&#39;, interval_sec = 86400)
    raw_data = Net::HTTP.get(URI.parse(BASE_URL + &amp;quot;p=#{period}&amp;amp;i=#{interval_sec}&amp;amp;x=TYO&amp;amp;q=#{@issue}&amp;quot;))
    data = []
    raw_data.lines do |line|
      data &amp;lt;&amp;lt; line[/^[0-9]*,([0-9]*)/, 1]
    end
    data.compact
  end
end

google_finance = GoogleFinance.new(7203)
stock_data = google_finance.stock
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;取得したデータを1行ずつ取ってきて、&lt;code&gt;/^[0-9]*,([0-9]*)/, 1&lt;/code&gt;という正規表現で終値を抜き出している。
先頭に数値があり、カンマ挟んで次の数値を取得するという意味。&lt;/p&gt;

&lt;p&gt;これを配列に突っ込んで、先頭の方にあるゴミ（抽出条件などが書かれている）をcompactで取り除くことで、
この1年間のトヨタ株の終値の配列が完成。&lt;/p&gt;

&lt;h2 id=&#34;チャート描画&#34;&gt;チャート描画&lt;/h2&gt;

&lt;p&gt;これはgnuplotを使う。
&lt;a href=&#34;https://github.com/ruby-numo/gnuplot&#34; target=&#34;_blank&#34;&gt;Numo::Gnuplot&lt;/a&gt;というgemが便利そうだったので、これを使った。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gem install numo-gnuplot
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;してから、
（当然、gnuplotも入れる）&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;net/http&#39;
require &#39;numo/gnuplot&#39;

class GoogleFinance
  # 省略
end

google_finance = GoogleFinance.new(7203)
stock_data = google_finance.stock

Numo.gnuplot do
  set title:&#39;TOYOTA&#39;
  plot stock_data, w:&#39;lines&#39;
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これだけ。GoogleFinanceクラスは変わらないので省略した。
得られたチャートを貼って終わり。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/google_finance_api.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>IIJmioの追加クーポンをLINE botに買ってもらう</title>
      <link>https://shidetake.com/iij_coupon/</link>
      <pubDate>Sun, 03 Sep 2017 10:27:20 +0900</pubDate>
      
      <guid>https://shidetake.com/iij_coupon/</guid>
      <description>&lt;p&gt;IIJmioにはIIJmioクーポンスイッチというiPhoneアプリが用意されているが、
残りの通信量（IIJではクーポンと呼ぶ）を見ることはできても、追加購入することができない。
購入はブラウザから。&lt;/p&gt;

&lt;p&gt;不便なのでLINE botに買ってもらうことにした。&lt;/p&gt;

&lt;h2 id=&#34;実行画面&#34;&gt;実行画面&lt;/h2&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/iij_coupon.png&#34; alt=&#34;image&#34; /&gt; &lt;img src=&#34;img/2.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;構成&#34;&gt;構成&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;LINEメッセージをMessaging APIが受けて、Google Apps Scriptを発動&lt;/li&gt;
&lt;li&gt;GASはソケット通信でVPS上のrubyスクリプトを起こす&lt;/li&gt;
&lt;li&gt;rubyスクリプト上でCapybaraさんがIIJmioのウェブページにクーポン購入を依頼する
&lt;img src=&#34;img/3.png&#34; alt=&#34;image&#34; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;クローラー作成&#34;&gt;クローラー作成&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;../line_highlights_1/&#34;&gt;Kindleのハイライト通知&lt;/a&gt;のときと同じような作り。
動作環境も同じ。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;# coding: utf-8
require &#39;capybara&#39;
require &#39;capybara/dsl&#39;
require &#39;capybara/poltergeist&#39;
require &#39;selenium-webdriver&#39;

class BuyIIJmioCoupon
  include Capybara::DSL

  SELENIUM = 0
  POLTERGEIST = 1

  USER_NAME = &#39;your_user_name&#39;
  PASSWORD  = &#39;your_password&#39;

  def initialize(driver)
    Capybara.app_host = &#39;https://www.iijmio.jp/service/setup/hdd/charge/&#39;
    Capybara.default_max_wait_time = 5
    case driver
    when SELENIUM
      Capybara.current_driver = :selenium
      Capybara.javascript_driver = :selenium
      Capybara.register_driver :selenium do |app|
        Capybara::Selenium::Driver.new(app, :browser =&amp;gt; :chrome)
      end

    when POLTERGEIST
      Capybara.current_driver = :poltergeist
      Capybara.javascript_driver = :poltergeist
      Capybara.register_driver :poltergeist do |app|
        Capybara::Poltergeist::Driver.new(app, {:timeout =&amp;gt; 120, js_errors: false})
      end
      page.driver.headers = {&#39;User-Agent&#39; =&amp;gt; &#39;Mac Safari&#39;}
    end
  end

  def login
    visit(&#39;&#39;)
    fill_in &#39;j_username&#39;,
      :with =&amp;gt; USER_NAME
    fill_in &#39;j_password&#39;,
      :with =&amp;gt; PASSWORD
    click_button &#39;ログイン&#39;
  end

  def buy
    select &#39;1枚（100MB）&#39;, from: &#39;selectList&#39;
    click_button &#39;次へ&#39;

    check &#39;confirm&#39;
    click_button &#39;お申し込み&#39;
  end
end

crawler = BuyIIJmioCoupon.new(BuyIIJmioCoupon::POLTERGEIST)
crawler.login
crawler.buy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;追加クーポン購入ページに直接アクセスして、ログインしたあとで、
購入枚数を選択して購入ボタンをポチポチ押していくだけ。購入枚数は1枚で固定した。&lt;/p&gt;

&lt;p&gt;デバッグ用にSELENIUMで動作させることもできるようにしている。&lt;/p&gt;

&lt;h2 id=&#34;vpsで待ち受ける&#34;&gt;VPSで待ち受ける&lt;/h2&gt;

&lt;p&gt;これも&lt;a href=&#34;../line_highlights_3/&#34;&gt;Kindleのハイライト通知&lt;/a&gt;と同じ。
上のソースを少し変えて、以下のようにする。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;# coding: utf-8
require &#39;capybara&#39;
require &#39;capybara/dsl&#39;
require &#39;capybara/poltergeist&#39;
require &#39;selenium-webdriver&#39;
require &#39;socket&#39;

class BuyIIJmioCoupon
  # 上と同じなので省略
end

gs = TCPServer.open(23456)
addr = gs.addr
addr.shift
printf(&amp;quot;server is on %s\n&amp;quot;, addr.join(&amp;quot;:&amp;quot;))

crawler = BuyIIJmioCoupon.new(BuyIIJmioCoupon::POLTERGEIST)
loop do
  s = gs.accept
  print(s, &amp;quot; is accepted\n&amp;quot;)

  begin
    crawler.login
    crawler.buy
  rescue
    raise
  end

  print(s, &amp;quot; is gone\n&amp;quot;)
  s.close
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;google-apps-script&#34;&gt;Google Apps Script&lt;/h2&gt;

&lt;p&gt;LINE Messaging APIから以下のスクリプトを実行するように紐付ければ (&lt;a href=&#34;../line_push/&#34;&gt;参考&lt;/a&gt;)、
ソケット通信でVPS上のスクリプトを起こせる。&lt;/p&gt;

&lt;p&gt;こちらから送った文章にiijという文字列が含まれている場合にトリガがかかるようにした。
なお、冒頭の実行画面ではLINE botからACKが返っているが、以下のソースでは省略している。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;var CHANNEL_ACCESS_TOKEN = &#39;your_line_channel_access_token&#39;;
var SERVER_ADDRESS_IIJ = &#39;http://your_server_address.com:23456&#39;

function doPost(e) {
  Logger.log(&#39;doPost&#39;)
  var events = JSON.parse(e.postData.contents).events;
  events.forEach (function(event) {
    if (event.type == &#39;message&#39;) { parseMessage(event); }
  });
}

function parseMessage(e) {
  if (e.message.text.match(/iij/)) {
    buyIIJmioCoupon()
  }
}

function buyIIJmioCoupon() {
  UrlFetchApp.fetch(SERVER_ADDRESS_IIJ);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>IFTTTとGoogle Photoを連携するとブログ用の画像準備がはかどる</title>
      <link>https://shidetake.com/blog_img/</link>
      <pubDate>Mon, 21 Aug 2017 22:44:54 +0000</pubDate>
      
      <guid>https://shidetake.com/blog_img/</guid>
      <description>&lt;p&gt;ブログに貼る画像置き場として、Google Photoを選んだ。
github-pageでホスティングしているのだが、画像をアップするのはイマイチかなと。
容量制限はないものの、1GBまでを推奨しているらしいし。&lt;/p&gt;

&lt;p&gt;というわけで、Google Photoの画像を&lt;strong&gt;簡単に&lt;/strong&gt;直接ブログに貼り付ける方法を模索していたが、
IFTTTを使って直リンクアドレスをメールしてもらうのが一番簡単だった。&lt;/p&gt;

&lt;h2 id=&#34;設定手順&#34;&gt;設定手順&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;IFTTTでNew Appletボタンをクリック&lt;/li&gt;
&lt;li&gt;サービスはGoogle Photoを選択&lt;/li&gt;
&lt;li&gt;トリガはNew photo uploaded

&lt;ul&gt;
&lt;li&gt;現時点ではこれしか選べない&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;アルバムは適当なものを選ぶ

&lt;ul&gt;
&lt;li&gt;共有アルバムは選べないので注意

&lt;ul&gt;
&lt;li&gt;恐らくPicasa APIの仕様&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;アクションサービスはメールを選択

&lt;ul&gt;
&lt;li&gt;メールが嫌いならLINEでもよい&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;アクションはSend me an email&lt;/li&gt;
&lt;li&gt;Subjectは適当に入れる

&lt;ul&gt;
&lt;li&gt;例: 画像URL {{CreatedAt}}

&lt;ul&gt;
&lt;li&gt;{{CreatedAt}}はトリガがかかった日時&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Bodyは{{PhotoUrl}}&lt;/li&gt;
&lt;li&gt;Create actionボタンを押して完了&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;実行手順&#34;&gt;実行手順&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;トリガ用のアルバムに写真を放り込む&lt;/li&gt;
&lt;li&gt;1分程するとメールが届く&lt;/li&gt;
&lt;li&gt;開いてURLをコピーしてブログに貼り付ける&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;特徴&#34;&gt;特徴&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;大きい画像をいい具合に縮小してくれる

&lt;ul&gt;
&lt;li&gt;細かい条件は不明だが、いい具合に縮小してくれる&lt;/li&gt;
&lt;li&gt;PicasaのAPIで取得できるURLだとこうなってしまう模様

&lt;ul&gt;
&lt;li&gt;Picasa APIの使い方は&lt;a href=&#34;../google_photo_api_1/&#34;&gt;こちら&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;短縮URLにして送ってくれる

&lt;ul&gt;
&lt;li&gt;IFTTTの機能（設定が必要かも）&lt;/li&gt;
&lt;li&gt;オリジナルアドレスだと長過ぎてブログに貼り付けたくないので助かる&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>iPadをウェブカメラ化してiCamSourceと連携</title>
      <link>https://shidetake.com/icam/</link>
      <pubDate>Thu, 17 Aug 2017 17:14:33 +0900</pubDate>
      
      <guid>https://shidetake.com/icam/</guid>
      <description>&lt;p&gt;iCamを使って外出先から部屋にいる犬の様子を見たいが、MBAのカメラ1つじゃ視野が狭い。
ということで、部屋に置いているiPadのカメラの映像も同時に見れるようにした。&lt;/p&gt;

&lt;p&gt;使わなくなった古いiPhoneを使ってもよい。&lt;/p&gt;

&lt;h2 id=&#34;必要なもの&#34;&gt;必要なもの&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PC (今回はMBA)

&lt;ul&gt;
&lt;li&gt;EpocCam Viewer Pro&lt;/li&gt;
&lt;li&gt;CamTwist&lt;/li&gt;
&lt;li&gt;iCamSource&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;iPad

&lt;ul&gt;
&lt;li&gt;EpocCam&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;iPhone

&lt;ul&gt;
&lt;li&gt;iCam&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;構成&#34;&gt;構成&lt;/h2&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/icam.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;ipadのウェブカメラ化&#34;&gt;iPadのウェブカメラ化&lt;/h2&gt;

&lt;p&gt;EpocCamというアプリを使って、iPadのカメラ画像をMBAで受信する。
これは特に設定もいらない簡単アプリ。同一LANである必要があるので、それだけ注意。&lt;/p&gt;

&lt;p&gt;更に、それをCamTwistというアプリで受けることで、ウェブカメラの入力画像のように使えるようになる。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;img/2.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;EpocCamが繋がっている状態 (上図左下) で、CamTwistの設定をSyphonにして、Syphon ServerとしてEpocCam Viewer Proを選択する (上図上) 。
CamTwistのPreviewにEpocCamと同じ映像が表示されれば成功 (上図右下) 。&lt;/p&gt;

&lt;h2 id=&#34;icamの設定&#34;&gt;iCamの設定&lt;/h2&gt;

&lt;p&gt;MBAにiCamSourceというアプリを入れて、VideoにCamTwist (2VUY)を選択する。
idとパスワードは適当に入力して、右下のStartボタンを押して、EpocCamと同じ映像が表示されれば成功。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;img/3.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;iPhoneに入れたiCamアプリにidとパスワードを入力するとiPadのカメラの映像を見ることができる。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>テレビのアンテナ線を無線化する</title>
      <link>https://shidetake.com/wireless_tv/</link>
      <pubDate>Thu, 10 Aug 2017 22:26:23 +0900</pubDate>
      
      <guid>https://shidetake.com/wireless_tv/</guid>
      <description>&lt;p&gt;テレビ端子がイマイチな場所にあって、テレビを置く位置が限られてしまうので無線化を考えた。
マンションなのでアンテナ設置など大掛かりな方法は除いて、
ざっと調べたところ、以下の方法がある。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;室内アンテナを設置する&lt;/li&gt;
&lt;li&gt;PS4とnasneを使う方法&lt;/li&gt;
&lt;li&gt;Apple TVとピクセラのワイヤレステレビチューナーを使う方法&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3つ目の案を採用することにした。&lt;/p&gt;

&lt;p&gt;理由はApple TVを持っているから。
もし何も持っていなければ、PS4案にしたと思う。PS4の方が圧倒的にUIが洗練されているし、
何よりピクセラのアプリの評判の悪さがすごい（実際、使ってみてイマイチと感じる）。&lt;/p&gt;

&lt;p&gt;それでも有線の頃に比べればかなり良くなった。&lt;/p&gt;

&lt;h2 id=&#34;必要なもの&#34;&gt;必要なもの&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Apple TV (第4世代)&lt;/li&gt;
&lt;li&gt;ピクセラのワイヤレステレビチューナー PIX-BR310L&lt;/li&gt;
&lt;li&gt;無線LAN中継器 BUFFALO WEX-733D&lt;/li&gt;
&lt;li&gt;無線LANルーター 親機&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;構成&#34;&gt;構成&lt;/h2&gt;

&lt;p&gt;&lt;img src=&#34;https://shidetake.com/img/wireless_tv.png&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;環境構築手順&#34;&gt;環境構築手順&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;無線LAN環境を構築

&lt;ul&gt;
&lt;li&gt;11acで組んだほうが良い&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;テレビ端子近くに無線LAN中継器を設置し、有線に変換&lt;/li&gt;
&lt;li&gt;ピクセラチューナーをテレビ端子と有線LANに接続&lt;/li&gt;
&lt;li&gt;Apple TVとテレビをHDMIケーブルで接続

&lt;ul&gt;
&lt;li&gt;Apple TVは当然無線LANに接続&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Apple TVにピクセラのStation TVアプリをインストール&lt;/li&gt;
&lt;li&gt;アプリでテレビの映像が受信できれば成功&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;メリット-デメリット&#34;&gt;メリット／デメリット&lt;/h2&gt;

&lt;p&gt;これで終わりだと味気ないのでこの構成のメリット／デメリットを少しだけ。&lt;/p&gt;

&lt;h3 id=&#34;アンテナ線が不要なので配線がスッキリする&#34;&gt;アンテナ線が不要なので配線がスッキリする&lt;/h3&gt;

&lt;p&gt;これが一番のメリット。ただしPS4案や室内アンテナ案でも実現可能。&lt;/p&gt;

&lt;h3 id=&#34;全てがapple-tvで完結するので-入力切替が不要になる&#34;&gt;全てがApple TVで完結するので、入力切替が不要になる&lt;/h3&gt;

&lt;p&gt;我が家ではApple TVでHuluを観たり、NASに入れた動画を観たりとApple TVをヘビーに使っている。
有線のときは、テレビとApple TVの入力切替が必要で、結構煩わしかった。&lt;/p&gt;

&lt;p&gt;Apple TVのスリープ時にテレビの電源を落とす設定と、Apple TVリモコンによる音量変更設定 (外部スピーカーの設定)
をすると、テレビのリモコンも不要になる。&lt;/p&gt;

&lt;p&gt;これが、PS4案よりも優れている唯一のポイントだと思う。&lt;/p&gt;

&lt;h3 id=&#34;がんばれば外出先でスマホでテレビが見れる&#34;&gt;がんばれば外出先でスマホでテレビが見れる&lt;/h3&gt;

&lt;p&gt;iPhoneアプリもあるので、家のLANに接続できれば外でも使える。
VPNを張る必要があるのでハードルは高め。いつか記事にするかも。&lt;/p&gt;

&lt;h3 id=&#34;録画は諦めたほうがいい&#34;&gt;録画は諦めたほうがいい&lt;/h3&gt;

&lt;p&gt;ピクセラチューナーに外付けHDDを接続すると録画もできるが、UIがクソなので使ってない。
録画予約もできない。Windowsのアプリなんかではできるのかもしれないが、よく知らない。
Mac用のアプリを2500円も出して買ったが起動すらしないクソアプリだったのでMacでは無理。&lt;/p&gt;

&lt;p&gt;アプリの開発もたぶん止まっているので、今後の改善も期待できない。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Picasa APIを使ってGoogle Photoからデジカメ写真だけを取得 2</title>
      <link>https://shidetake.com/google_photo_api_2/</link>
      <pubDate>Tue, 01 Aug 2017 22:41:29 +0000</pubDate>
      
      <guid>https://shidetake.com/google_photo_api_2/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../google_photo_api_1/&#34;&gt;前回&lt;/a&gt;の続き。
今回は任意のアルバムから写真の情報を取得する。&lt;/p&gt;

&lt;h1 id=&#34;写真情報の取得&#34;&gt;写真情報の取得&lt;/h1&gt;

&lt;p&gt;your_album_nameアルバムから取得する場合は以下のような感じ。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;picasa&#39;

client = Picasa::Client.new(
  user_id: &#39;your@email&#39;,
  access_token: &#39;your_access_token&#39;
)

albums = client.album.list.entries
album = albums.find { |a| a.title == &#39;your_album_name&#39; }

photos = client.album.show(album.id).entries

photos.each do |photo|
  puts &amp;quot;#{photo.id}, #{photo.exif.time}, #{photo.title}, #{photo.exif.model}&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Exif情報から撮影機器のモデル名を取得することもできた。
これで最初にやりたかったことはほぼできたが、
このスクリプトだと1000枚しか処理できないという欠点があるのでもう少し続ける。&lt;/p&gt;

&lt;h1 id=&#34;大量の写真情報の取得&#34;&gt;大量の写真情報の取得&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;start-index&lt;/code&gt;オプションを使って以下のように書くことで、1000枚以上の写真を処理することができる。
ただし、&lt;code&gt;start-index&lt;/code&gt;の最大値は10001らしいので、この方法でも11000枚までしか処理できない。
11000枚で十分事足りたので、この対処法までは調べていない。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;PAGE_NUM_MAX = 11

page_num = [album.numphotos / 1000, PAGE_NUM_MAX].min

page_num.times do |page|
  opt = {}
  opt[&#39;start-index&#39;] = page * 1000 + 1
  photos = client.album.show(album.id, opt).entries
  photos.each do |photo|
    puts &amp;quot;#{photo.id}, #{photo.exif.time}, #{photo.title}, #{photo.exif.model}&amp;quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;albumの指定まではこれまでと変わらないので省略した。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Picasa APIを使ってGoogle Photoからデジカメ写真だけを取得 1</title>
      <link>https://shidetake.com/google_photo_api_1/</link>
      <pubDate>Sun, 30 Jul 2017 22:56:40 +0000</pubDate>
      
      <guid>https://shidetake.com/google_photo_api_1/</guid>
      <description>&lt;p&gt;Google Photoの検索では、Exif情報の撮影機器のモデル名をキーにすることができないらしい。
あるデジカメで撮った写真だけを抽出するために試行錯誤した結果、
PicasaのAPIを使うことで実現できたので、そのやり方を記す。&lt;/p&gt;

&lt;p&gt;ちなみにGoogle PhotoのAPIはまだ用意されておらず、Piacasa APIがその代替となっている。&lt;/p&gt;

&lt;h1 id=&#34;picasa-apiを使う&#34;&gt;Picasa APIを使う&lt;/h1&gt;

&lt;h2 id=&#34;access-tokenの取得&#34;&gt;Access tokenの取得&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;../gmail_api_1/&#34;&gt;Gmailのとき&lt;/a&gt;とは違い、PicasaのAPIを使うのは少しコツがいる。
既にPicasaのサービスが終了しているためか、API MANAGERで検索してもPicasa APIが出てこない。&lt;/p&gt;

&lt;p&gt;そこで、Access tokenを取得するために&lt;a href=&#34;https://developers.google.com/oauthplayground/&#34; target=&#34;_blank&#34;&gt;OAuth 2.0 Playground&lt;/a&gt;を使う。&lt;/p&gt;

&lt;p&gt;Select the scopeの一覧から、Picasa Web v2を選択して表示されるURLをクリックする。
Authorize APIsボタンが有効になるので、それを押してAPIを有効にする許可を与える。&lt;/p&gt;

&lt;p&gt;Authorization codeが取得できるので、続けてExchange authorization code for tokensボタンを押下。
これでAccess tokenが得られる。&lt;/p&gt;

&lt;h2 id=&#34;access-tokenの確認&#34;&gt;Access tokenの確認&lt;/h2&gt;

&lt;p&gt;得られたAccess tokenの確認も兼ねて、簡単なAPIを使ってみる。&lt;/p&gt;

&lt;p&gt;まずはRubyでPicasa APIを簡単に使えるようにするgemをインストール。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gem install picasa
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;あとは以下のようなスクリプトでアルバム名の一覧を取得する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;picasa&#39;

client = Picasa::Client.new(
  user_id: &#39;your@email&#39;,
  access_token: &#39;your_access_token&#39;
)

albums = client.album.list.entries
albums.each { |a| p a.title }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;your@emailとyour_access_tokenにはそれぞれ自分の（Access token取得時に許可を与えた）アカウントと、上で取得したAccess tokenを入れる。&lt;/p&gt;

&lt;p&gt;Google Photoのアルバム名の一覧が表示されれば成功。Access tokenがちゃんと使えることがわかった。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;../google_photo_api_2/&#34;&gt;次回&lt;/a&gt;以降で写真の取得、Exif情報の取得あたりのやり方を書いていく。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>HandBrakeCLIによるDVDリッピング 3</title>
      <link>https://shidetake.com/ripping_3/</link>
      <pubDate>Wed, 19 Jul 2017 22:36:23 +0900</pubDate>
      
      <guid>https://shidetake.com/ripping_3/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../ripping_2/&#34;&gt;前回&lt;/a&gt;からの続き。
これまではisoファイルからリッピングする形だったが、DVDドライブから直接リッピングする。&lt;/p&gt;

&lt;h2 id=&#34;dvdドライブのパスを取得&#34;&gt;DVDドライブのパスを取得&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;diskutil&lt;/code&gt;コマンドを使う（たぶんmacOSでしか使えない）。
DVDをドライブに入れた状態で、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;diskutil list
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;すると、HDD含め、OSが認識しているディスクのパーティション一覧が出てくる。
NAMEの部分に入れたDVDの名前が表示されているのがあるはず。
例えば以下のような感じ。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                            VIBY_521               *2.0 GB     disk2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;この場合、&lt;code&gt;/dev/disk2&lt;/code&gt;を入力として指定することでDVDから直接リッピングが可能になる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -c 1 -i /dev/disk2 -o hoge_1.mkv
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;前回の最後のスクリプトと組み合わせて、&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;chapter_num=`lsdvd /dev/disk2 | grep Chapters | awk &#39;{gsub(/,/,&amp;quot;&amp;quot;); print $6}&#39;`
for ((i = 1; i &amp;lt;= $chapter_num; i++)); do
  HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -c $i -i /dev/disk2 -o hoge_$(printf %02d $i).mkv
done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ここまでで、かなり形になったので今回でラストとする。
ただ、実はこれだけだと使えないタイプのDVDがある。
そのあたりは、そのうち載せようと思う。スクリプトを少し整理してGitHubで公開する予定。&lt;/p&gt;

&lt;p&gt;Blu-rayからのリッピングについては&lt;a href=&#34;../blu_ripping/&#34;&gt;こちら&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;お約束&#34;&gt;お約束&lt;/h2&gt;

&lt;p&gt;違法なリッピングを推奨しているわけではないので注意。
コピーガードされたDVDをリッピングするのはダメ。ぜったい。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>HandBrakeCLIによるDVDリッピング 2</title>
      <link>https://shidetake.com/ripping_2/</link>
      <pubDate>Mon, 10 Jul 2017 22:41:58 +0900</pubDate>
      
      <guid>https://shidetake.com/ripping_2/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../ripping_1/&#34;&gt;前回&lt;/a&gt;からの続き。&lt;/p&gt;

&lt;h2 id=&#34;チャプター分割&#34;&gt;チャプター分割&lt;/h2&gt;

&lt;p&gt;チャプター毎にファイルを分割してリッピングしたい場合は&lt;code&gt;-c&lt;/code&gt;オプションを使って&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -c 1 -i hoge.iso -o hoge_1.mkv
HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -c 2 -i hoge.iso -o hoge_2.mkv
HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -c 3 -i hoge.iso -o hoge_3.mkv
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;いちいちチャプター数だけ繰り返すのはバカらしいので、
for文を使って以下のように書く。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;for ((i = 1; i &amp;lt;= 3; i++)); do
  HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -c $i -i hoge.iso -o hoge_$(printf %02d $i).mkv
done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3の部分を変数にして、引数として渡してやれば、任意のチャプター数に対応できる。&lt;/p&gt;

&lt;h2 id=&#34;チャプター数の取得&#34;&gt;チャプター数の取得&lt;/h2&gt;

&lt;p&gt;いちいち、チャプター数を指定するのは面倒なので、
&lt;code&gt;lsdvd&lt;/code&gt;を使ってチャプター数を取得する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;brew install lsdvd
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;からの&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;lsdvd hoge.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これでチャプター数を含む標準出力が得られる。
あとは適当に文字列を抜き出して使えば良い。
以下に&lt;code&gt;awk&lt;/code&gt;を使った例を示す。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;lsdvd hoge.iso | grep Chapters | awk &#39;{gsub(/,/,&amp;quot;&amp;quot;); print $6}&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;先ほどのfor文と組み合わせて&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;chapter_num=`lsdvd hoge.iso | grep Chapters | awk &#39;{gsub(/,/,&amp;quot;&amp;quot;); print $6}&#39;`
for ((i = 1; i &amp;lt;= $chapter_num; i++)); do
  HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -c $i -i hoge.iso -o hoge_$(printf %02d $i).mkv
done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;長くなってきたので、いったんここまで。
&lt;a href=&#34;../ripping_3/&#34;&gt;次回&lt;/a&gt;はisoファイルではなく、DVDドライブから直接mkvに変換する方法。&lt;/p&gt;

&lt;h2 id=&#34;お約束&#34;&gt;お約束&lt;/h2&gt;

&lt;p&gt;違法なリッピングを推奨しているわけではないので注意。
コピーガードされたDVDをリッピングするのはダメ。ぜったい。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>HandBrakeCLIによるDVDリッピング 1</title>
      <link>https://shidetake.com/ripping_1/</link>
      <pubDate>Mon, 03 Jul 2017 22:08:55 +0900</pubDate>
      
      <guid>https://shidetake.com/ripping_1/</guid>
      <description>&lt;p&gt;DVDのリッピングというのは、設定が異常に多い。&lt;/p&gt;

&lt;p&gt;まず大きいところで、コーデック。汎用性の高いH.264にするのか、先を見越してH.265にするのか。
コンテナはどうする？mp4なんかはよく見るし汎用性ありそう。でも字幕や音声を切り替えるならmkvを選んだ方がいい。
フレームレートは？ノイズ除去フィルタの種類や強さも決めなきゃ。&lt;/p&gt;

&lt;p&gt;と言った具合に無数に決めることがあり、それをいちいち設定してたら何十枚もあるDVDをリッピングするのにどれだけ時間がかかるかわからない。
しかも、今回設定した内容を次に覚えてないとまたノイズフィルタのかかり具合を見極める作業を繰り返すことになる。&lt;/p&gt;

&lt;p&gt;そういった面倒なことを避けるために、DVDをドライブに挿入したら、あとはコマンドを一発送るだけでいつもの設定でリッピングできるようにした。&lt;/p&gt;

&lt;h2 id=&#34;handbrakecli&#34;&gt;HandBrakeCLI&lt;/h2&gt;

&lt;p&gt;今回使ったのは、有名なリッピングソフトであるHandBrakeのCUIバージョンであるHandBrakeCLI。
Homebrewにはなかったので公式サイトからダウンロードしてインストールする。&lt;/p&gt;

&lt;h2 id=&#34;isoイメージからの変換&#34;&gt;ISOイメージからの変換&lt;/h2&gt;

&lt;p&gt;まずはISOイメージから適当な設定で変換する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;HandBrakeCLI -i hoge.iso -o hoge.mkv
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じ。デフォルトのオプションがどうなってるかはhelpに書いてあるとは思うが長すぎて読んでられない。&lt;/p&gt;

&lt;h2 id=&#34;もう少しちゃんとした変換&#34;&gt;もう少しちゃんとした変換&lt;/h2&gt;

&lt;p&gt;完全にマニュアルでオプションを設定するより、プリセットオプションと組み合わせたほうがよい。
以下のコマンドで、プリセットオプションの一覧が出てくる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;HandBrakeCLI -z
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;画質重視＋音声や字幕の切り替えがしたいのでmkv形式のH.265 MKV 1080p30にしてみる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; -i hoge.iso -o hoge.mkv
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;せっかく音声と字幕の切り替えができるmkv形式にしたのに、このままでは1種類しか取り込んでくれないので、
&lt;code&gt;--all-audio&lt;/code&gt;オプションと&lt;code&gt;-s&lt;/code&gt;オプションを使って&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;HandBrakeCLI -Z &#39;H.265 MKV 1080p30&#39; --all-audio -s &#39;1,2,3,4,5,6&#39; -i hoge.iso -o hoge.mkv
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じ。
DVDからの取り込みやチャプター分割など、もう少し突っ込んだ内容は&lt;a href=&#34;../ripping_2&#34;&gt;次回&lt;/a&gt;以降。&lt;/p&gt;

&lt;h2 id=&#34;お約束&#34;&gt;お約束&lt;/h2&gt;

&lt;p&gt;違法なリッピングを推奨しているわけではないので注意。
コピーガードされたDVDをリッピングするのはダメ。ぜったい。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>RubyでGmail本文を取得する 2</title>
      <link>https://shidetake.com/gmail_api_2/</link>
      <pubDate>Mon, 26 Jun 2017 22:44:57 +0900</pubDate>
      
      <guid>https://shidetake.com/gmail_api_2/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../gmail_api_1/&#34;&gt;前回&lt;/a&gt;はアクセストークンを取得して、メール本文を取得することに成功した。
ただしアクセストークンには期限があり、取得しなおさなくてはならない。
今回はリフレッシュトークンを使うことで、これを解決する。&lt;/p&gt;

&lt;h2 id=&#34;リフレッシュトークンの取得&#34;&gt;リフレッシュトークンの取得&lt;/h2&gt;

&lt;p&gt;前回のアクセストークン取得の項に記載の通り。&lt;/p&gt;

&lt;h2 id=&#34;リフレッシュトークンを使ったメールの取得&#34;&gt;リフレッシュトークンを使ったメールの取得&lt;/h2&gt;

&lt;p&gt;前回からの差分&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;require &#39;google/api_client&#39;
require &#39;json&#39;

-ACCESS_TOKEN     = &#39;YourAccessToken&#39;
+CLIENT_ID        = &#39;YourClientID&#39;
+CLIENT_SECRET    = &#39;YourClientSecret&#39;
+REFRESH_TOKEN    = &#39;YourRefreshToken&#39;
APPLICATION_NAME = &#39;YourApplicationName&#39;

# APIクライアントの準備
client = Google::APIClient.new(application_name: APPLICATION_NAME)
-client.authorization.access_token = ACCESS_TOKEN
+client.authorization = Signet::OAuth2::Client.new(
+  token_credential_uri: &#39;https://accounts.google.com/o/oauth2/token&#39;,
+  audience:             &#39;https://accounts.google.com/o/oauth2/token&#39;,
+  scope:                [&#39;https://www.googleapis.com/auth/drive.file&#39;],
+  client_id:     CLIENT_ID,
+  client_secret: CLIENT_SECRET,
+  refresh_token: REFRESH_TOKEN,
+)
+
+client.authorization.refresh!
gmail = client.discovered_api(&#39;gmail&#39;)

# query
res = client.execute(
  api_method: gmail.users.messages.list,
  parameters: {&#39;userId&#39; =&amp;gt; &#39;me&#39;, &#39;q&#39;=&amp;gt;&#39;from:auto-confirm@amazon.co.jp&#39;},
)

# parse
if res.status == 200
  json = JSON.parse(res.body)
  json[&#39;messages&#39;].each do |mail_ids|
    mail = client.execute(
      api_method: gmail.users.messages.get,
      parameters: {&#39;userId&#39; =&amp;gt; &#39;me&#39;, &#39;id&#39;=&amp;gt;mail_ids[&#39;id&#39;]},
    )
    p mail.body
  end
else
  puts &#39;error&#39;
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これでこのシリーズは終わり。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>RubyでGmail本文を取得する 1</title>
      <link>https://shidetake.com/gmail_api_1/</link>
      <pubDate>Tue, 20 Jun 2017 22:55:21 +0900</pubDate>
      
      <guid>https://shidetake.com/gmail_api_1/</guid>
      <description>&lt;p&gt;GmailのAPIをRubyで使う方法を何回かに分けて書く。&lt;/p&gt;

&lt;h2 id=&#34;api準備&#34;&gt;API準備&lt;/h2&gt;

&lt;p&gt;まずはAPIを使うための準備。&lt;a href=&#34;https://console.developers.google.com/&#34; target=&#34;_blank&#34;&gt;https://console.developers.google.com/&lt;/a&gt; にアクセスして、プロジェクトを作成する。
それから、Gmail APIを有効にし、認証情報を追加する。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;使用するAPI =&amp;gt; Gmail API&lt;/li&gt;
&lt;li&gt;APIを呼び出す場所 =&amp;gt; その他のUI&lt;/li&gt;
&lt;li&gt;アクセスするデータの種類 =&amp;gt; ユーザーデータ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;あとは適当に選んで進めると、クライントIDとクライアントシークレットが取得できる。&lt;/p&gt;

&lt;h2 id=&#34;access-tokenの取得&#34;&gt;Access Tokenの取得&lt;/h2&gt;

&lt;p&gt;取得したクライアントIDとクライアントシークレットから、
以下のスクリプトでアクセストークンとリフレッシュトークンを取得できる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;net/http&#39;
require &#39;uri&#39;
require &#39;oauth2&#39;
require &#39;launchy&#39;

CLIENT_ID     = &#39;YourClientID&#39;
CLIENT_SECRET = &#39;YourClientSecret&#39;

client = OAuth2::Client.new(
  CLIENT_ID, CLIENT_SECRET,
  :site =&amp;gt; &amp;quot;https://accounts.google.com&amp;quot;,
  :token_url =&amp;gt; &amp;quot;/o/oauth2/token&amp;quot;,
  :authorize_url =&amp;gt; &amp;quot;/o/oauth2/auth&amp;quot;)
auth_url = client.auth_code.authorize_url(
  :redirect_uri =&amp;gt; &#39;urn:ietf:wg:oauth:2.0:oob&#39;,
  :scope =&amp;gt; &#39;https://www.googleapis.com/auth/gmail.readonly&#39;)
 
# 表示されるURLをブラウザで開く
Launchy.open auth_url
 
print &amp;quot;authorization code:&amp;quot;
authorization_code = gets.chomp

res = Net::HTTP.post_form(URI.parse(&#39;https://accounts.google.com/o/oauth2/token&#39;),
                          {&#39;client_id&#39;     =&amp;gt; CLIENT_ID,
                           &#39;client_secret&#39; =&amp;gt; CLIENT_SECRET,
                           &#39;redirect_uri&#39;  =&amp;gt; &#39;urn:ietf:wg:oauth:2.0:oob&#39;,
                           &#39;grant_type&#39;    =&amp;gt; &#39;authorization_code&#39;,
                           &#39;code&#39;          =&amp;gt; authorization_code})
puts res.body
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;メールの取得&#34;&gt;メールの取得&lt;/h2&gt;

&lt;p&gt;取得したアクセストークンを以下のように使って、Gmail APIが使える。
以下は、auto-confirm@amazon.co.jpからの受信メールを取得するスクリプト。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;require &#39;google/api_client&#39;
require &#39;json&#39;

ACCESS_TOKEN     = &#39;YourAccessToken&#39;
APPLICATION_NAME = &#39;YourApplicationName&#39;

# APIクライアントの準備
client = Google::APIClient.new(application_name: APPLICATION_NAME)
client.authorization.access_token = ACCESS_TOKEN
gmail = client.discovered_api(&#39;gmail&#39;)

# query
res = client.execute(
  api_method: gmail.users.messages.list,
  parameters: {&#39;userId&#39; =&amp;gt; &#39;me&#39;, &#39;q&#39;=&amp;gt;&#39;from:auto-confirm@amazon.co.jp&#39;},
)

# parse
if res.status == 200
  json = JSON.parse(res.body)
  json[&#39;messages&#39;].each do |mail_ids|
    mail = client.execute(
      api_method: gmail.users.messages.get,
      parameters: {&#39;userId&#39; =&amp;gt; &#39;me&#39;, &#39;id&#39;=&amp;gt;mail_ids[&#39;id&#39;]},
    )
    p mail.body
  end
else
  puts &#39;error&#39;
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;今後&#34;&gt;今後&lt;/h2&gt;

&lt;p&gt;アクセストークンには期限があり、一定時間経過すると使えなくなってしまう。
これを解決するのがリフレッシュトークンを使った方法。&lt;a href=&#34;../gmail_api_2/&#34;&gt;次回&lt;/a&gt;はこれを使っていつでもAPIを利用できるようにする。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>KindleのハイライトをLINEに通知する 4</title>
      <link>https://shidetake.com/line_highlights_4/</link>
      <pubDate>Sun, 18 Jun 2017 14:33:41 +0900</pubDate>
      
      <guid>https://shidetake.com/line_highlights_4/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../line_highlights_3/&#34;&gt;前回&lt;/a&gt;はLINEのBOTにメッセージを送ることで、
ハイライトを取得して返信してくれるようにした。
現状では、設定したページ数だけスクレイピングして、全てのハイライトを返信することになっているので、
何度も同じハイライトが送られてきてしまう。
今回は最新のハイライトだけを返信するようにする。&lt;/p&gt;

&lt;p&gt;今回でこのシリーズは最後。&lt;/p&gt;

&lt;h2 id=&#34;方針&#34;&gt;方針&lt;/h2&gt;

&lt;p&gt;今まで取得したハイライトを記録しておいて、
差分だけを返信するという単純な仕様にした。&lt;/p&gt;

&lt;p&gt;記録する方法は、これまた単純に外部ファイルに保存するだけ。
容量が増えてきたら、ソートしておく、データベースに記録する、などの工夫が必要になるかもしれないが、
とりあえず問題になるまでは単純な実装にする。&lt;/p&gt;

&lt;h2 id=&#34;json形式で保存&#34;&gt;JSON形式で保存&lt;/h2&gt;

&lt;p&gt;以下のメソッドで&lt;code&gt;@highlights&lt;/code&gt;という配列をJSON形式にして保存する。
&lt;code&gt;&#39;json&#39;&lt;/code&gt;を忘れずに&lt;code&gt;require&lt;/code&gt;すること。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;  # ハイライトをJSON形式にして外部ファイルに保存する
  def store_highlights
    File.open(JSON_FILE_NAME, &#39;w&#39;) do |file|
      JSON.dump(@highlights, file)
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;json形式のファイルを読み出し&#34;&gt;JSON形式のファイルを読み出し&lt;/h2&gt;

&lt;p&gt;以下のメソッドでJSON_FILE_NAMEというJSON形式ファイルから&lt;code&gt;@highlights&lt;/code&gt;配列にデータを読み出す。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;  # 外部ファイルから既に取得しているハイライトを読み出す
  def restore_highlights
    return unless File.exist?(JSON_FILE_NAME)
    File.open(JSON_FILE_NAME, &#39;r&#39;) do |file|
      @highlights = JSON.load(file)
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;あとは、&lt;code&gt;@highlights&lt;/code&gt;に存在しないハイライトだけをLINEで送信し、外部ファイルに保存すればよい。&lt;/p&gt;

&lt;h2 id=&#34;全ソース&#34;&gt;全ソース&lt;/h2&gt;

&lt;p&gt;githubに全ソースをあげた。
&lt;a href=&#34;https://github.com/shidetake/line_kindle_highlights&#34; target=&#34;_blank&#34;&gt;https://github.com/shidetake/line_kindle_highlights&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>KindleのハイライトをLINEに通知する 3</title>
      <link>https://shidetake.com/line_highlights_3/</link>
      <pubDate>Wed, 07 Jun 2017 22:27:01 +0900</pubDate>
      
      <guid>https://shidetake.com/line_highlights_3/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../line_highlights_2/&#34;&gt;前回&lt;/a&gt;はサーバで起動してLINE通知するところまで。
このままだとサーバ側で手動で起動しないといけない。これを解決する。&lt;/p&gt;

&lt;h2 id=&#34;起動タイミング&#34;&gt;起動タイミング&lt;/h2&gt;

&lt;h3 id=&#34;仕様&#34;&gt;仕様&lt;/h3&gt;

&lt;p&gt;cronで周期的に実行する仕様や、手動で合図してから一定時間だけ連続起動する仕様など、いくつか考えたが、
結局は手動で合図したタイミングで1度だけ起動する仕様にした。&lt;/p&gt;

&lt;p&gt;LINEでタイミングを通知する方法であれば、手動でも大した手間ではない。&lt;/p&gt;

&lt;p&gt;妥協案とも言える仕様だが、この仕様にした一番の理由は、
頻繁にアクセスすると、ロボットと疑われてCAPTCHAでブロックされてしまうため。
これを解除することもできるとは思うが、一気に難易度が上がるので今回は見送ることにした。&lt;/p&gt;

&lt;h3 id=&#34;流れ&#34;&gt;流れ&lt;/h3&gt;

&lt;p&gt;以下のような流れで起動要求を伝達する。&lt;/p&gt;

&lt;p&gt;LINEで起動要求&lt;br /&gt;
↓&lt;br /&gt;
LINEからGoogle Apps Scriptのスクリプト実行&lt;br /&gt;
↓&lt;br /&gt;
ソケット通信でスクレイピング開始要求&lt;/p&gt;

&lt;h3 id=&#34;実装&#34;&gt;実装&lt;/h3&gt;

&lt;p&gt;まずは前回まで作ったクローラーを、ソケット通信をトリガにして動作するようにする。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;+require &#39;socket&#39;

+gs = TCPServer.open(12345)
+addr = gs.addr
+addr.shift
+printf(&amp;quot;server is on %s\n&amp;quot;, addr.join(&amp;quot;:&amp;quot;))

 crawler = LineKindleHighlights.new

+loop do
+  s = gs.accept
+  print(s, &amp;quot; is accepted\n&amp;quot;)
+
   crawler.scrape
+
+  print(s, &amp;quot; is gone\n&amp;quot;)
+  s.close
+end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これでポート12345番でアクセスされると1度だけ動作するようになる。&lt;/p&gt;

&lt;p&gt;つづいて、LINEをトリガにして動作するスクリプトをGoogle Apps Scriptとして作成する。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;function doPost(e) {
  Logger.log(&#39;doPost&#39;)
  var events = JSON.parse(e.postData.contents).events;
  events.forEach (function(event) {
    if (event.type == &amp;quot;message&amp;quot;) { UrlFetchApp.fetch(&amp;quot;http://your_server_address.com:12345&amp;quot;); }
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じでウェブアプリケーションとして導入して、LINE側のWebhookアドレスに紐付けると、
BOTに話しかけたタイミングでソケット通信が飛ぶようになる。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;../line_highlights_4/&#34;&gt;続きはこちら&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>KindleのハイライトをLINEに通知する 2</title>
      <link>https://shidetake.com/line_highlights_2/</link>
      <pubDate>Sun, 04 Jun 2017 22:44:34 +0900</pubDate>
      
      <guid>https://shidetake.com/line_highlights_2/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;../line_highlights_1/&#34;&gt;前回&lt;/a&gt;はクローラーを作成してハイライトを取得するところまでだった。
今回はサーバで起動してLINE通知するところまでを作る。&lt;/p&gt;

&lt;h2 id=&#34;サーバで起動&#34;&gt;サーバで起動&lt;/h2&gt;

&lt;p&gt;前回はSeleniumを使ったが、サーバで実行する場合はGUIアプリケーションにはできないので、
POLTERGEISTを使う。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;  def initialize
-   Capybara.current_driver = :selenium
+   Capybara.current_driver = :poltergeist
-   Capybara.javascript_driver = :selenium
+   Capybara.javascript_driver = :poltergeist
    Capybara.app_host = &#39;https://kindle.amazon.co.jp&#39;
    Capybara.default_max_wait_time = 5
-   Capybara.register_driver :selenium do |app|
+   Capybara.register_driver :poltergeist do |app|
-     # 最新のSeleniumではFirefoxが動作しない問題があるのでchromeを使う
-     Capybara::Selenium::Driver.new(app, :browser =&amp;gt; :chrome)
+     Capybara::Poltergeist::Driver.new(app, {:timeout =&amp;gt; 120, js_errors: false})
    end
+   page.driver.headers = {&#39;User-Agent&#39; =&amp;gt; &#39;Mac Safari&#39;}
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Amazonのウェブサイト固有の問題で、ユーザーエージェントの変更が必要なので、Mac Safariとしている。&lt;/p&gt;

&lt;p&gt;これでサーバ（今回はUbuntuを使った）で動作するようになった。&lt;/p&gt;

&lt;h2 id=&#34;line通知&#34;&gt;LINE通知&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/line/line-bot-sdk-ruby&#34; target=&#34;_blank&#34;&gt;line-bot-api&lt;/a&gt;を使った。だいたい以下のような感じ。Channel Secret, Channel Access Token, userIdはLINE developersから取得して入力すること。
push_highlightメソッドを使って、前回標準出力していた文字列をLINEに送れる。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;  def initialize(driver)
    Capybara.current_driver = :poltergeist
    Capybara.javascript_driver = :poltergeist
    Capybara.app_host = &#39;https://kindle.amazon.co.jp&#39;
    Capybara.default_max_wait_time = 5
    Capybara.register_driver :poltergeist do |app|
      Capybara::Poltergeist::Driver.new(app, {:timeout =&amp;gt; 120, js_errors: false})
    end
    page.driver.headers = {&#39;User-Agent&#39; =&amp;gt; &#39;Mac Safari&#39;} if driver == POLTERGEIST

+   @line = Line::Bot::Client.new do |config|
+     config.channel_secret = &#39;&#39;
+     config.channel_token  = &#39;&#39;
+   end

+   @user_id = &#39;&#39;
  end

+ def push_highlight(highlight)
+   message = {
+     type: &#39;text&#39;,
+     text: highlight
+   }

+   @line.push_message(@user_id, message)
+ end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&#34;../line_highlights_3/&#34;&gt;続きはこちら&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>KindleのハイライトをLINEに通知する 1</title>
      <link>https://shidetake.com/line_highlights_1/</link>
      <pubDate>Sat, 27 May 2017 13:08:32 +0900</pubDate>
      
      <guid>https://shidetake.com/line_highlights_1/</guid>
      <description>&lt;p&gt;読書メモのためにハイライトした文章をコピペできる形にしたい。
KindleにはハイライトをメールやTwitterでシェアする機能があるが、これはアクションが増えて読書を妨げるので使いたくない。&lt;/p&gt;

&lt;p&gt;ハイライトした内容は &lt;a href=&#34;https://kindle.amazon.co.jp/your_highlights&#34; target=&#34;_blank&#34;&gt;https://kindle.amazon.co.jp/your_highlights&lt;/a&gt; で見えるので、これを取得して通知するクローラーを作ればよい。&lt;/p&gt;

&lt;h2 id=&#34;クローラー作成&#34;&gt;クローラー作成&lt;/h2&gt;

&lt;p&gt;RubyのCapybaraを使った。&lt;/p&gt;

&lt;h3 id=&#34;動作環境&#34;&gt;動作環境&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]
selenium-webdriver (2.53.0, 2.52.0)
capybara (2.14.0)
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;ソースコード&#34;&gt;ソースコード&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;# coding: utf-8
require &#39;capybara&#39;
require &#39;capybara/dsl&#39;
require &#39;capybara/poltergeist&#39;
require &#39;selenium-webdriver&#39;

class LineKindleHighlights
  include Capybara::DSL

  KINDLE_EMAIL    = &#39;your@email&#39;
  KINDLE_PASSWORD = &#39;your_password&#39;
  CRAWL_PAGE_NUM = 2

  def initialize
    Capybara.current_driver = :selenium
    Capybara.javascript_driver = :selenium
    Capybara.app_host = &#39;https://kindle.amazon.co.jp&#39;
    Capybara.default_max_wait_time = 5
    Capybara.register_driver :selenium do |app|
      # 最新のSeleniumではFirefoxが動作しない問題があるのでchromeを使う
      Capybara::Selenium::Driver.new(app, :browser =&amp;gt; :chrome)
    end
  end

  def scrape
    login

    # ページ読み込み待ち
    sleep 5

    go_to_highlights

    CRAWL_PAGE_NUM.times do
      all(&#39;.title&#39;).each do |element|
        p element.text
      end

      all(&#39;.highlight&#39;).each do |element|
        p element.text
      end

      next_page
    end
  end

  private

  # Kindleのマイページにアクセスしログインする
  def login
    visit(&#39;&#39;)
    click_link &#39;sign in&#39;
    fill_in &#39;ap_email&#39;,
      :with =&amp;gt; KINDLE_EMAIL
    fill_in &#39;password&#39;,
      :with =&amp;gt; KINDLE_PASSWORD
    click_on &#39;signInSubmit&#39;
  end

  # Your Hightlightsのページに遷移する
  # @note ログイン直後のページで使う
  def go_to_highlights
    click_link &#39;Your Highlights&#39;
  end

  # 次のページに遷移
  # @note Your Hightlightsのページで使うことで次の本のハイライトページに遷移する
  def next_page
    visit(find_by_id(&#39;nextBookLink&#39;, visible: false)[:href])
  end
end

crawler = LineKindleHighlights.new
crawler.scrape
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;KINDLE_EMAIL&lt;/code&gt;と&lt;code&gt;KINDLE_PASSWORD&lt;/code&gt;だけ設定すれば動く&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CRAWL_PAGE_NUM&lt;/code&gt;は実質的には取得するハイライトの本の数になる (1ページに1冊分の情報が出る）&lt;/li&gt;
&lt;li&gt;次のページへの遷移で少しハマった。hiddenなクラスのリンクを取得するには&lt;code&gt;visible: false&lt;/code&gt;する必要があるらしい&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;とりあえず今回はここまで。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;../line_highlights_2/&#34;&gt;続きはこちら&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;todo&#34;&gt;TODO&lt;/h2&gt;

&lt;ul class=&#34;task-list&#34;&gt;
&lt;li&gt;&lt;label&gt;&lt;input type=&#34;checkbox&#34; checked disabled class=&#34;task-list-item&#34;&gt; クローラー作成&lt;/label&gt;&lt;/li&gt;
&lt;li&gt;&lt;label&gt;&lt;input type=&#34;checkbox&#34; disabled class=&#34;task-list-item&#34;&gt; LINE通知機能&lt;/label&gt;&lt;/li&gt;
&lt;li&gt;&lt;label&gt;&lt;input type=&#34;checkbox&#34; disabled class=&#34;task-list-item&#34;&gt; 通知済みハイライトを通知しない&lt;/label&gt;&lt;/li&gt;
&lt;li&gt;&lt;label&gt;&lt;input type=&#34;checkbox&#34; disabled class=&#34;task-list-item&#34;&gt; サーバ上で動作&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Time Machineの容量制限</title>
      <link>https://shidetake.com/time_capsule/</link>
      <pubDate>Sun, 14 May 2017 21:01:17 +0900</pubDate>
      
      <guid>https://shidetake.com/time_capsule/</guid>
      <description>&lt;p&gt;Time Capsuleをバックアップ用途だけでなく、NASとしても使いたい。
Time Machineを使うと、容量がいっぱいになるまでバックアップを取り続けてしまうので、
何らかの方法で容量を制限する必要がある。&lt;/p&gt;

&lt;p&gt;パーティションを切って、バックアップ領域とデータ領域に分けるという方法が主流のようだ。
ただ、裏ワザ的な使い方のためあまり推奨しないという意見も多い。&lt;/p&gt;

&lt;p&gt;そこで、別の方法を採用した。&lt;/p&gt;

&lt;h2 id=&#34;ダミーファイルを置く&#34;&gt;ダミーファイルを置く&lt;/h2&gt;

&lt;p&gt;ダミーファイルを置くことで、パーティションを切ることなく、Time Machineの侵食を逃れる。
新しくデータを置きたい場合は、ダミーファイルの一部をデータと置き換えればよい。&lt;/p&gt;

&lt;p&gt;1GBのダミーファイルを100個と、10GBのダミーファイルを10個用意して、
計200GB分のデータ領域を確保することにした。&lt;/p&gt;

&lt;p&gt;ダミーファイルの作成は以下のコマンドを使った。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;dd if=/dev/zero of=./dummy_10GB bs=1000000 count=10000
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;実際にはfor文で以下のように使う。
ネットワーク越しなのでかなり時間がかかることに注意。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cd /Volumes/TimeCapsule
for ((i=0; i &amp;lt; 100; i++)); do dd if=/dev/zero of=./dummy_1GB_$i bs=1000000 count=1000; done
for ((i=0; i &amp;lt; 10; i++)); do dd if=/dev/zero of=./dummy_10GB_$i bs=1000000 count=10000; done
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>Amazon Dash ButtonからLINE通知</title>
      <link>https://shidetake.com/dash_button/</link>
      <pubDate>Thu, 11 May 2017 22:31:20 +0900</pubDate>
      
      <guid>https://shidetake.com/dash_button/</guid>
      <description>&lt;h2 id=&#34;dasher導入&#34;&gt;dasher導入&lt;/h2&gt;

&lt;p&gt;他の人が詳しく書いているので割愛。&lt;br /&gt;
個人的には&lt;a href=&#34;http://kakts-tec.hatenablog.com/entry/2016/12/10/231205&#34; target=&#34;_blank&#34;&gt;Amazon Dash Button と slackを連携させる&lt;/a&gt;という記事がとても参考になった。&lt;/p&gt;

&lt;h2 id=&#34;lineへのpush通知設定&#34;&gt;LINEへのPUSH通知設定&lt;/h2&gt;

&lt;p&gt;LINE側の設定は&lt;a href=&#34;../line_push/&#34;&gt;LINE BOTでPUSH通知する&lt;/a&gt;を参照。&lt;/p&gt;

&lt;p&gt;CHANNEL_ACCESS_TOKENとUSERIDは上記事で取得したもの。
MAC_ADDRESSはdasher導入時に取得したものを書くこと。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{&amp;quot;buttons&amp;quot;: [
  {
    &amp;quot;name&amp;quot;: &amp;quot;joy&amp;quot;,
    &amp;quot;address&amp;quot;: &amp;quot;MAC_ADDRESS&amp;quot;,
    &amp;quot;url&amp;quot;: &amp;quot;https://api.line.me/v2/bot/message/push&amp;quot;,
    &amp;quot;method&amp;quot;: &amp;quot;POST&amp;quot;,
    &amp;quot;headers&amp;quot;: {
      &amp;quot;Content-Type&amp;quot;: &amp;quot;application/json&amp;quot;,
      &amp;quot;Authorization&amp;quot;: &amp;quot;Bearer {CHANNEL_ACCESS_TOKEN}&amp;quot;},
    &amp;quot;json&amp;quot;: true,
    &amp;quot;body&amp;quot;: {
      &amp;quot;to&amp;quot;: &amp;quot;USERID&amp;quot;,
      &amp;quot;messages&amp;quot;: [
        {
          &amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;,
          &amp;quot;text&amp;quot;: &amp;quot;JOY!&amp;quot;
        }
      ]
    }
  }
]}
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>LINE BOTでPUSH通知する</title>
      <link>https://shidetake.com/line_push/</link>
      <pubDate>Mon, 17 Apr 2017 23:11:57 +0900</pubDate>
      
      <guid>https://shidetake.com/line_push/</guid>
      <description>&lt;h2 id=&#34;登録&#34;&gt;登録&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PUSH通知する場合はDeveloper Trialで登録&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;google-apps-scriptでユーザid取得&#34;&gt;Google Apps ScriptでユーザID取得&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;友達に追加したユーザのIDを登録したメールアドレスにメールする&lt;/li&gt;
&lt;li&gt;以下のコードでウェブアプリケーションとして導入する

&lt;ul&gt;
&lt;li&gt;CHANNEL_ACCESS_TOKENはLINE depelopersで取得できる&lt;/li&gt;
&lt;li&gt;example@gmail.comは任意のgmailアドレス&lt;/li&gt;
&lt;li&gt;アプリケーションにアクセスできるユーザーは全員（匿名ユーザーを含む）&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;var CHANNEL_ACCESS_TOKEN = &#39;CHANNEL_ACCESS_TOKEN&#39;;

function doPost(e) {
  Logger.log(&#39;doPost&#39;)
  var events = JSON.parse(e.postData.contents).events;
  events.forEach (function(event) {
    if (event.type == &amp;quot;follow&amp;quot;) { mailUserId(event); }
  });
}

function mailUserId(e) {
  MailApp.sendEmail(&#39;example@gmail.com&#39;, &#39;mailId&#39;, e.source.userId);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;curlでmessaging-apiのpush通知を使う&#34;&gt;curlでMessaging APIのPUSH通知を使う&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CHANNEL_ACCESS_TOKENは上で使ったものと同じ&lt;/li&gt;
&lt;li&gt;USERIDはプッシュ先

&lt;ul&gt;
&lt;li&gt;今回は上で取得した自分のユーザIDを使う&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;curl -X POST \
-H &#39;Content-Type:application/json&#39; \
-H &#39;Authorization: Bearer {CHANNEL_ACCESS_TOKEN}&#39; \
-d &#39;{
    &amp;quot;to&amp;quot;: &amp;quot;USERID&amp;quot;,
    &amp;quot;messages&amp;quot;:[
        {
            &amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;,
            &amp;quot;text&amp;quot;: &amp;quot;Hello, world!&amp;quot;
        }
    ]
}&#39; https://api.line.me/v2/bot/message/push
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;関連ポスト&#34;&gt;関連ポスト&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;../dash_button/&#34;&gt;Amazon Dash ButtonからLINE通知&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>cheat_sheet</title>
      <link>https://shidetake.com/playground/cheat_sheet/</link>
      <pubDate>Thu, 30 Mar 2017 23:30:11 +0900</pubDate>
      
      <guid>https://shidetake.com/playground/cheat_sheet/</guid>
      <description>&lt;p&gt;要約&lt;br /&gt;
チートシート&lt;/p&gt;

&lt;p&gt;かなり癖がある。
恐らく、moreより上に見出し、もしくは小見出しがある場合、
そこまでが要約となり、moreより上にあっても通常の文章は表示されない。
見出しがない場合は、moreより上が全て要約となる。&lt;br /&gt;
バグな気もするが、詳細は不明。&lt;/p&gt;

&lt;h1 id=&#34;見出し&#34;&gt;見出し&lt;/h1&gt;

&lt;h2 id=&#34;小見出し&#34;&gt;小見出し&lt;/h2&gt;

&lt;h1 id=&#34;改行&#34;&gt;改行&lt;/h1&gt;

&lt;p&gt;文末に半角スペース2つ&lt;br /&gt;
で改行。&lt;/p&gt;

&lt;h1 id=&#34;脚注&#34;&gt;脚注&lt;/h1&gt;

&lt;p&gt;こんな感じ&lt;sup class=&#34;footnote-ref&#34; id=&#34;fnref:a&#34;&gt;&lt;a href=&#34;#fn:a&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h1 id=&#34;画像&#34;&gt;画像&lt;/h1&gt;

&lt;h2 id=&#34;from-google-photo&#34;&gt;from google photo&lt;/h2&gt;

&lt;p&gt;&lt;img src=&#34;https://lh3.googleusercontent.com/IrDV2v9sWU8odajHY3Eq6aHccBAlaRC_UKYmddPmc_cg_mxY-2EB5Ud3jvL9djBI74haJHvI-wKsHzfUjDIRNbfbO2a5p6OKRrxA5i8wzJLQJ7OR-uelob5foqV1LWU2FaL76nAco16z_w0lihhyjeSsoQMcvRSmNI4REfVNmasj0MIV5TO5TEA7evE9P-lJMCHRWQIViuQc7SeoZK79NOXlkPSmudl2yCPOGDz8OsPRd4i0oOxAhUZ8F4WPMvKGSbdDk-wl9UjsmwN9UmY_d4E7XRFDXMRId4kEe_BvGi_8j0hHTn9x_f1w0lYKK8g3dwLGw28QDiL0Djp7w_56FshJ5JsIy3fY2iEEVpDdPMzIBdssE3-fk_BvvZ3rO4mRqlnQSGmm1XeaYSTQl-jsUBJG_NsWPjPHO3oNU2wgdP0uQAZ4YW9cik2hmlkTbwuRHoNUvzeWeL9PTpSlI3f7Zu1frVoYkI4co8FmDpKzaaQH2akc40HhL4Pyi06UjXfimRo1DYEIabxEeNiHfY97bGRDVsyf6eX_7WS7Pb0vTtSzaauLCsqVqBd5NRJSoxGtfvbKd9Je56N8ofiSMIc9BqpOCX7PLTc_30t2wTQt_3ZtcugvUEeSpw=w510-h679-no&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://goo.gl/sWqbQI&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;

&lt;h1 id=&#34;リンク&#34;&gt;リンク&lt;/h1&gt;

&lt;h2 id=&#34;絶対パス&#34;&gt;絶対パス&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;http://google.com&#34; target=&#34;_blank&#34;&gt;Google&lt;/a&gt;&lt;br /&gt;
&lt;a href=&#34;http://google.com&#34; target=&#34;_blank&#34;&gt;http://google.com&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;相対パス&#34;&gt;相対パス&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;../../misc/line_push/&#34;&gt;LINE BOTでPUSH通知する&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&#34;シンタックスハイライト&#34;&gt;シンタックスハイライト&lt;/h1&gt;

&lt;h2 id=&#34;ブロック&#34;&gt;ブロック&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&#34;language-ruby&#34;&gt;p hello
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;インライン&#34;&gt;インライン&lt;/h2&gt;

&lt;p&gt;文章の途中で&lt;code&gt;p hello&lt;/code&gt;こんな感じ&lt;/p&gt;

&lt;h2 id=&#34;強調&#34;&gt;強調&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;強め&lt;/em&gt;
&lt;em&gt;強め&lt;/em&gt;
&lt;strong&gt;強い&lt;/strong&gt;
&lt;strong&gt;強い&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;箇条書き&#34;&gt;箇条書き&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1段目1

&lt;ul&gt;
&lt;li&gt;2段目

&lt;ul&gt;
&lt;li&gt;3段目は3タブ&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;1段目2&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;表&#34;&gt;表&lt;/h2&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&#34;left&#34;&gt;Left align&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;Right align&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;Center align&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&#34;left&#34;&gt;This&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;This&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;This&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=&#34;left&#34;&gt;column&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;column&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;column&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=&#34;left&#34;&gt;will&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;will&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;will&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=&#34;left&#34;&gt;be&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;be&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;be&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=&#34;left&#34;&gt;left&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;right&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;center&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=&#34;left&#34;&gt;aligned&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;aligned&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;aligned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h1 id=&#34;category&#34;&gt;Category&lt;/h1&gt;

&lt;p&gt;タグとの使い分けが難しい。イメージ的には、完全に分離できるものがカテゴリーで、重複を許すのがタグ。
例えば、テック系か日常の日記のような記事かはカテゴリーで分ける。
Rubyの話かPythonの話かはタグで分ける。
ソフトかFPGAかは難しいところ。&lt;/p&gt;

&lt;p&gt;結局のところ、このブログではテック系の話しかしないので、カテゴリーわけはせずに、タグだけ付ける形がいい気がする。&lt;/p&gt;

&lt;h1 id=&#34;tag&#34;&gt;Tag&lt;/h1&gt;

&lt;p&gt;タグは重複を許すので、いくつでもつければ良い。あまり注意はしなくていいが、表記ゆれだけは気をつけたい。
rubyとRubyとか。現時点ではざっくり以下のタグわけ
- Ruby
- LINE BOT
- Raspberry Pi
- Scraping
- Fintech
- Blog
- Smart Home
- API
- Ripping
- Kindle
- Amazon Dash Button&lt;/p&gt;

&lt;h1 id=&#34;amazonアソシエイト&#34;&gt;Amazonアソシエイト&lt;/h1&gt;

&lt;h2 id=&#34;テキスト&#34;&gt;テキスト&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;http://amzn.to/2CuC9Qc&#34; target=&#34;_blank&#34;&gt;任意のテキスト&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;画像-1&#34;&gt;画像&lt;/h2&gt;

&lt;p&gt;小
&lt;a href=&#34;https://www.amazon.co.jp/Kindle-Paperwhite-%E9%9B%BB%E5%AD%90%E6%9B%B8%E7%B1%8D%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC-Wi-Fi-%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF/dp/B00QJDOM6U/ref=as_li_ss_il?ie=UTF8&amp;qid=1518946008&amp;sr=8-2&amp;keywords=kindle&amp;linkCode=li1&amp;tag=shidetake0e-22&amp;linkId=06cf4657a1480646e915ff34bda84373&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00QJDOM6U&amp;Format=_SL110_&amp;ID=AsinImage&amp;MarketPlace=JP&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=shidetake0e-22&#34; &gt;&lt;/a&gt;&lt;img src=&#34;https://ir-jp.amazon-adsystem.com/e/ir?t=shidetake0e-22&amp;l=li1&amp;o=9&amp;a=B00QJDOM6U&#34; width=&#34;1&#34; height=&#34;1&#34; border=&#34;0&#34; alt=&#34;&#34; style=&#34;border:none !important; margin:0px !important;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;中
&lt;a href=&#34;https://www.amazon.co.jp/Kindle-Paperwhite-%E9%9B%BB%E5%AD%90%E6%9B%B8%E7%B1%8D%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC-Wi-Fi-%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF/dp/B00QJDOM6U/ref=as_li_ss_il?ie=UTF8&amp;qid=1518946008&amp;sr=8-2&amp;keywords=kindle&amp;linkCode=li2&amp;tag=shidetake0e-22&amp;linkId=0eca241f7fe8e004ca2cb53345f74ef4&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00QJDOM6U&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=JP&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=shidetake0e-22&#34; &gt;&lt;/a&gt;&lt;img src=&#34;https://ir-jp.amazon-adsystem.com/e/ir?t=shidetake0e-22&amp;l=li2&amp;o=9&amp;a=B00QJDOM6U&#34; width=&#34;1&#34; height=&#34;1&#34; border=&#34;0&#34; alt=&#34;&#34; style=&#34;border:none !important; margin:0px !important;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;大
&lt;a href=&#34;https://www.amazon.co.jp/Kindle-Paperwhite-%E9%9B%BB%E5%AD%90%E6%9B%B8%E7%B1%8D%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC-Wi-Fi-%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF/dp/B00QJDOM6U/ref=as_li_ss_il?ie=UTF8&amp;qid=1518946008&amp;sr=8-2&amp;keywords=kindle&amp;linkCode=li3&amp;tag=shidetake0e-22&amp;linkId=d4b4280270aa00c819d5a99ba50aae18&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00QJDOM6U&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=JP&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=shidetake0e-22&#34; &gt;&lt;/a&gt;&lt;img src=&#34;https://ir-jp.amazon-adsystem.com/e/ir?t=shidetake0e-22&amp;l=li3&amp;o=9&amp;a=B00QJDOM6U&#34; width=&#34;1&#34; height=&#34;1&#34; border=&#34;0&#34; alt=&#34;&#34; style=&#34;border:none !important; margin:0px !important;&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;テキストと画像&#34;&gt;テキストと画像&lt;/h2&gt;

&lt;iframe style=&#34;width:120px;height:240px;&#34; marginwidth=&#34;0&#34; marginheight=&#34;0&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; src=&#34;//rcm-fe.amazon-adsystem.com/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=shidetake0e-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B00QJDOM6U&amp;linkId=05fa0e9ef8fc667966c6780e891940c1&#34;&gt;&lt;/iframe&gt;
&lt;div class=&#34;footnotes&#34;&gt;

&lt;hr /&gt;

&lt;ol&gt;
&lt;li id=&#34;fn:a&#34;&gt;hoge
 &lt;a class=&#34;footnote-return&#34; href=&#34;#fnref:a&#34;&gt;&lt;sup&gt;[return]&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Search</title>
      <link>https://shidetake.com/search.html</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://shidetake.com/search.html</guid>
      <description></description>
    </item>
    
  </channel>
</rss>
