自力でネットワークに復活してほしい

前回の記事で導入した Raspberry Pi でしたが、うちの無線LANが悪いのかなんなのか、数時間経つとネットワークからいなくなってしまう症状がありました。
ときどき気付いて電源を挿し直すのとかだるいので、自力で復活してくれるように設定しました。

本記事の目次

方針としては以下の通りです。

ping は送った対象が生きているか確認するものですが、今回はルータに送り、返ってこなければ自分が死んでいると判断します。
ちなみに送る対象はルータとしていますが、ネットワークに常駐している機器なら他のものでも問題ありません。

やること

  • ルータの IP アドレス確認
  • bashを書く
  • cron で毎分実行

ルータの IP アドレス確認

Raspberry Pisshして入れたら、netstat -rnしてルータの IP アドレスを調べます。

netstat -rn

そうしたら

Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.249.*.*      0.0.0.0         UG        0 0          0 wlan0
10.249.*.0      0.0.0.0         255.255.255.0   U         0 0          0 wlan0

こういう感じになると思います。
Gatewayの項目で 0.0.0.0 じゃない数字が書いてあったらそれがルータの IP アドレスになります。

bash を書く

私はRaspberry Piのユーザディレクトリの直下に"reconnect"というディレクトリを作成しましたが、置く場所はどこでもいいと思います。
内容としては単純で、ping した結果が正常でなければ日付を log に残しネットワークインターフェイスを再起動するだけです。

#!/bin/sh
ping -c 1 (ルータのIPアドレス)
if [ $? -ne 0 ]; then
	echo `date` >> /home/(ユーザ名)/reconnect/log
	/sbin/ifconfig wlan0 down
	/sbin/ifconfig wlan0 up
fi

以上を "reconnect.sh" など適当な名前で保存します。
また、実行権限を付与する必要もあります。

sudo chmod 755 reconnect.sh

これで実行するソースはできました。

cronで毎分実行

cronとは日付や時間を指定すると実行してくれるマンです。

sudo crontab -e

と入力すると、root権限で実行されるcronを追加できます。
ここに以下を追記します。

* * * * * /home/(ユーザ名)/reconnect/reconnect.sh >>/dev/null 2>>/home/(ユーザ名)/reconnect/errlog

実はこの方法はあまりよくなくて、誤ってcrontab -r としてしまうと全cronが消失してしまうという危険と隣合わせとなっています。
本記事では横着しましたが、うっかりさんや厳密な方はテキストファイルを読み込ませるなど、他の安全な方法を取るべきだと思います。

さて、以上で自力で復活できるよう設定できたと思います。
それでも死んでしまう、という方は以下の原因・対処法が考えられます。

  • ルータへの ping が通っていない

→ ラズパイから手動で ping してみる、ルータのIPアドレスを確認する

  • bashファイルが実行されていない(cronがうまくいっていない)

→ cronの記法を確認する、もっと結果が見やすいスクリプト(echoなど)でcronが正しく動くか検証してみる

感想

bashを書いたりcronについて調べたり、ifconfigについて調べたりといろいろ要素が多かったです。
特にcronに関しては結構苦労させられたので思い出深いです。

ワークスペースのメンバーをチャンネルに全員招待するコマンド(/inviteall)

教員がメンバーの多いワークスペースを運営しているらしくて、そこで「新しく作ったチャンネルに全員参加させたい」という要望が出たらしい。
確かにinviteは現状ちまちまひとりずつするしかないみたいで、100人越えると大変そうです。

本記事の目次

今回はAWS Lambdaを利用してSlackのSlash Commandを実装してみました。
echo commandっていうテンプレートが存在するみたいですが、初心者すぎてコード読めないので1から作ってしまいました。

いるもの

  • slack api token (scopeはcommands, channels:write, users:read)
  • Verification Token(Basic informationで取得, requestが正しく来たものか確認するためのもの)
  • AWSアカウント

 ユーザー情報をjson形式で取得
→IDを抽出
→GET requestを送信してひとりずつinvite
という流れです。

slack側の初期設定は例によって割愛します。
slash command appを利用する手もありますが、既存のappでslash command機能を追加して作りました。
このへんのこと調べてみたけど結局なにが違うのかよくわからん。

Request URLの入力

コマンドが呼び出されたときにリクエストが送信される先のURLを決めます。
AWSで新しいLambdaをplainで作成し、トリガーをAPI Gatewayに設定したあとのところからいきます。
ググるといろんな方がもっと詳しくやってくれているので、飛ばし気味で。

LambdaMicroserviceの画面を開いて作成したリソースのANYメソッドを削除して、POSTメソッドを追加します。
統合リクエストから本文マッピングテンプレートの項目を開いて、Content-Typeに「application/x-www-form-urlencoded」と入力し、こちらのコードをコピペしました
これでイベントがjson形式でLambdaへ渡されるようになりました。

アクション→APIのデプロイを行うと、呼び出しURLが表示されます。
これにメソッド名を追加した「https://~.amazonaws.com/<デプロイしたステージ>/<メソッド名>」というのがRequest URLになります
メソッド名を追加しないといけないのが分からなくてしばらく悩んでしまいました。
このURLをslackのcommand作成画面で入力します。
あとは要求された通りに各項目を入力すればslack側の設定は終わりです。

コマンド呼び出しから関数を動かすまで

コマンドが実行されるとPOST requestでいろいろ届きます。
ここからVerified Tokenと実行されたチャンネルの情報を抜き出します。

var https = require("https");
var event_token = event["token"];
var channel = event["channel_id"];
//verified tokenが一致しなければはじく
if(event_token != <Verified Token>) return;

以上が準備で、ここから先に実際に処理を書いていきます。

全員のユーザーIDの取得

GETでユーザー情報のjsonを取得します。

var url = "https://slack.com/api/users.list?token=<slack api token>";
https.get(url, (response) => {
    let rawData = "";
    response.on('data', (chunk) => {rawData += chunk;}); //データ部を全て生データとしておく
    response.on('end', () => {
        var usr_ls = JSON.parse(rawData); //生データをJSON形式に変換

この後のコードはresponse.onのスコープの中に書いていきます。

IDを抽出する

ここで取得しているjsonこちらを参照してください。

var IDs = [];
var usr_nu = Object.keys(usr_ls["members"]).length;
for(var i = 0; i < usr_nu; i++){
    IDs.push(usr_ls["members"][i]["id"]);
}

inviteを実行する

あとは取得したIDの配列でinviteしていくだけです。
本当はPOSTリクエストしたかったのですが、うまくいかなくてGETリクエストになっています。

url = "https://slack.com/api/channels.invite";
url += "?token=" + <slack api token> + "&channel=" + channel + "&user=";
for(var j = 0; j < usr_nu; j++){
    https.get(url + IDs[j], (res) => {
        res.on("data", () => {});
    });
}

コールバックは適当に"Command Accepted!"とかにしときました。

感想

いじくるところがいろいろあって苦労度は高かったです。
なんでHTTP POSTうまくいかないんだ...
とりあえず動くものができたのでよしとしました。

6/21 追記
200人超えでタイムアウトエラーが出てしまうという報告がありました。
Lambdaの基本設定のメモリ(CPU割り当て)をMAXにする力技で解決しました。
当方は関数呼出が頻繁でないので無料枠の範囲内でした。

毎日定刻に予定を知らせてくれるやつが欲しい

ラボでSlackを導入したところタイトルのような要望があったのでやってみました。
もともと#generalにGoogle Calender Appが予定前日に投稿してくれていたのですが、「当日朝にもDMがほしいけど設定ができない」との声があったという経緯があります。

本記事の目次

ラボの日程管理にGoogle Calenderを使っていたので、Google Apps Scriptを利用しました。

いるもの

  • Googleアカウント
  • 参照したいGoogle Calender
  • slack api token (scopeはusers:read, chat:write:bot あたりかな)
  • 投稿先のchannelまたはuserのID

slack appのインテグレーションとtoken取得は調べるといろいろ出てくるので割愛します。

流れとしては
 Google Apps Scriptのtriggerでスクリプト起動
→投稿する文章を形成
json形式でPOST request
→発言
といった感じになります。

user IDの取得

channelに対して投稿する場合はchannel名でいいけれど、個人にDMという形で投稿するならuser IDを取得する必要があります。
今回はひとりのIDを取得するだけだったので手軽にブラウザで

https://slack.com/api/users.list?token=<取得したtoken>

して探しました。

カレンダーの取得とイベントの取得

//カレンダー取得
var myCals = CalendarApp.getCalendarById(<カレンダーID>);
//todayを今日に設定
var today = new Date();
//今日の予定を取得
var myEvents = myCals.getEventsForDay(today);

<カレンダーID>には"*@group.calendar.google.com"を入力します。
これで今日の予定がmyEventsに格納されました。

POST bodyの整形

2/24 There are n events today
イベント名 12:00 at 2階ラウンジ 担当:maple
イベント名 17:00 at 302講義室 担当:おもち
~

こんな感じにしてほしいということだったのでこの通り整形していきます。

日時表記の整形

todayには

Sat Feb 24 12:19:40 GMT+09:00 2018

こんな感じで日時が入っているので、これを目的の形式にしていきます。
イベントの開始時刻もこれなので、併せて処理を書いてみました。

//日付表記を 'M/d' 形式に
function MMdd(date){
  return Utilities.formatDate(date, 'JST', 'M/d');
}

//時刻表記を 'HH:mm' 形式に
function HHmm(date){
  return Utilities.formatDate(date, 'JST', 'HH:mm');
}

1行目を決定

日付とイベントの数を記入

2/24 There are n events today

英語はisとかareとかあって面倒ですね。

var strBody = "*" + MMdd(today) + "* ";
if(myEvents.length == 0){
  strBody += "There are no events today";
}
else if(myEvents.length == 1){
  strBody += "There is *1* event today";
}
else{
  strBody += "There are *" + myEvents.length + "* events today";
}

2行目以降を決定

ゴリ押しです。

for(var i = 0; i < myEvents.length; i++){
  strBody += "\n*" + myEvents[i].getTitle();
  strBody += "* " + HHmm(myEvents[i].getStartTime());
  strBody += " at " + myEvents[i].getLocation();
  strBody += " 担当:" + myEvents[i].getDescription();
}

POST requestする

Google Apps ScriptではUrlFetchAppクラスでHTTPリクエストが定義されています。
Argumentsのkeyはslack側の指定に従います(slack API methods|chat.postMessage 参照)

//json形式に
var Arguments = {
  token : <取得したtoken>,
  channel : <channel名またはユーザーID>,
  text : strBody
};

//POST指定とArgumentsをpayloadに指定  
var options = {
  'method' : 'post',
  'payload' : Arguments
};
  
//以上の設定でPOSTリクエストを送信
var url = 'https://slack.com/api/chat.postMessage';
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText());

ここまででとりあえず予定を取得して投稿ができるようになりました。
次は毎日定刻に動くようにトリガーを設定します。

定刻に動くようにする

GASのトリガーって実はよくわからなくて、"日タイマー"で毎日9時って設定すると9時~10時の間のいつかに開始する、っていう感じになってしまうんです
しかし、"特定の日時"で時刻もきっちり指定できるという謎仕様。
この"特定の日時"指定は1回で使い捨てなので、少し工夫して使ってみました。

整理すると、
 毎日、目的の時間より前にトリガーセットスクリプトを起動
→トリガーセットスクリプトがその日の9時に"特定の日時"指定でトリガーをセット
→そのトリガーに従ってリマインダースクリプトが起動
→slackに投稿される
という流れになります。
トリガーは2回発動するというわけです。

function setTrigger() {
  var triggerDay = new Date();
  triggerDay.setHours(9);
  triggerDay.setMinutes(0);
  ScriptApp.newTrigger("Reminder").timeBased().at(triggerDay).create();
}

この関数について"日タイマー"で7時とかでトリガーを設定しておけばOKです。
これで作製したトリガーは日々たまってしまうので、リマインダースクリプト自体にトリガーを削除する文を入れておくと吉です。

function deleteTrigger() {
  var triggers = ScriptApp.getProjectTriggers();
  for(var i=0; i < triggers.length; i++) {
    if (triggers[i].getHandlerFunction() == "Reminder") {
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }
}

以上で一応動くものができました。

感想

ドキュメントを読んだり試行錯誤したりで勉強になったし、毎日誰かの役に立っているということでよかったなと思っています。
ただ、ワークスペースの管理者ならGoogle Calender AppをいじってユーザーにDM通知ができるらしいです。
後から発覚して悲しい気持ちになりました。
しかしながらこちらはこちらで、好きにテキストを整形できるところや特定の曜日に投稿しないなどの調節が簡単なのは利点かなと思います。
Goole Apps Script なんでもできそうなのでまたいろいろやってみたいです。

ノーゲーム・ノーライフ ゼロ

8/3にノーゲーム・ノーライフ ゼロを観てきた。

ノゲノラ自体は2014年のテレビシリーズで知って、それ以来原作小説を追い続けていて、その6巻であるところの過去編がこのたび映像化ということで、原作の感想も混ぜつつ思ったところを。

 

映画全体としては、テレビシリーズとは真逆とも言える禍々しい描写が印象的。これは監督のいしづかあつこさんもかなり意識して本編との差別化を図っていたみたい。キービジュのボロボロになったシュヴィちゃんが空を見上げている絵が、絶望しているけど希望を捨てていないという彼女の最期を、荒廃した世界と瞳の輝きとの対比で絶妙に表現していると思う。

 

そういえばこの子は変わった後の世界を知らないんだな...そう思うとまた泣きそうだ...

 

機凱種(エクスマキナ)っていうのがヒロイン・シュヴィちゃんの種族なんだけど、これがまた演出がいちいちかっこいいんだ。戦うときにガシャンガシャンって兵器を展開するシーンとか、指揮体への通信シーンとか。終盤の、この子の種族の仲間が総員で展開するシーンとか。かっこいい。

 

でもやっぱりこの映画というか、この最も旧き神話で私の心を掴んで離さないのが、リクとシュヴィという夫婦の関係なんです。主人公で人間であるリクはヒロインのシュヴィに故郷を破壊されているのに、それも折り込んでシュヴィを愛していて、で、ほんとにめちゃくちゃ愛してるっていうのがすごく繊細かつ強烈に表現されてた。

人類種と機凱種では子供を作れないばかりか、機凱種には穴も棒もないので生殖行為の真似事もできなくて、リクは最期に一番の後悔として「おまえを抱きたかったよシュヴィ」って言うシーンがあるんだけど(劇場版ではカットされています)、これが正直めちゃくちゃ効いてるんだよな…

この作品中で下ネタは当然のように幅を効かせてて、「童貞」ってワードも冗談というかボケの一貫としてよく出現するんですよね。そんな作品中で、共に戦い、死んでしまった妻を、彼女の遺した布石を以てして目的を達成し今にも死なんとする男が、最後の最後で「抱きたかった」と言って死ぬ。下ネタでなく愛情表現としての性行為であって、シュヴィをいかにめちゃくちゃ愛しているかというのが痛いくらい伝わってくるところです。あれ、映画の感想は...

ああでも、リクが眠りにつく時にシュヴィに声をかけるシーンでの松岡の演技はよかったです。

 

めっちゃ演技がすごかったのはかやのんで、ジブリールにやられる!ってときに連結解除された連結体に自分のデータの同期を依頼するところの切羽詰まり感というか、泣きながら駄々こねてる感じが、声優の本気を見せられたというか。島村さんの泣きに通じるものがありますね。あそこだけのためでも見にいく価値があるんじゃないかなってくらい。かやのんも演じながら泣いたらしいし。

シュヴィもやっぱりめちゃくちゃリクを愛してて、避けられる攻撃を、リクに貰った指輪を守るために急所に受けたり、最後の体が消し飛ぶほどの攻撃を受ける直前に最後の力で局所的なバリアを張って指輪だけ守り抜いたりするんですよ。もう無理。

かやのんといえば冒頭の「おにいちゃん、もうがまんできない、わたしをおんなにして(棒)」もSiriっぽくて最高でした。

 

とりあえずパッと書けるのはこれぐらいかな。まだまだ多分いろいろ思うところはあるんだけど、いちばん言いたいのはいろんな人に見てほしい。そして原作を読んでもう一度観てほしい。映像はもちろんとてもよくできていて、単体でも十分楽しめる作品だと思うけど、原作でなされている心理描写だったりを踏まえた上で観るとさらに楽しめるし、涙が止まらなくなる。私ははじめから分かっていたのでタオル持っていきました。来世ではふたり幸せに生きてほしい......そのためにもはやく空白は結ばれるべき。

 

円盤買います。

入院レポ

人生で初めて水疱瘡にかかって、人生で初めて40℃超えの熱が出て、人生で初めて入院しました。

大学院より先に病院に入院することになるとは思わなかった。

初めての病院入院は絶対出産の時だと思っていたのに。

水疱瘡感染症なのではじめての入院は個室でした。

 

初めての採血もしました。

実は入院する前日に京都の医院でされそうになったんだけど、だめすぎて目の前が真っ白になってベッドに倒れこんでしまいました。

でもまぁ入院するとなったら逃げられなかった...

明日もあるらしい...ぶるぶる...

人間、40℃の熱が出て体中ぶつぶつであんなに血を抜かれても死なないものなんですね。

その刺した針を利用して今は点滴が繋がっています。

上の口からも腕の口からも水分摂り続けててトイレが近くて困ります。

でも物理的にもトイレは近くて、一歩で行ける距離にあるので安心ですね。個室でよかった。

 

初車椅子もしました。

あれは押す人への信頼が必要ですね。

点滴の吊るしてあるあの棒?をジョイントできる以外には車椅子はそんなに感動的ではなかったです。

 

パラマウントベッドもしました。

ボタン一つでウィーンって座る感じになるタイプのベッドです。

これでもしPS3持って来てたりテザリングでないネット環境があったならば、ダメ人間になれそう。

これはもう入院した時だけでいいかな...

 

そして一番大事な食事ですが、粥とかたくて冷たい肉や魚が出てきます。

f:id:wood_wind108:20150107181356j:plain

昨日の晩御飯でした。

そりゃあそうですよ。

学校の給食みたいなもんだ。

でも個人の体調には合わせてくれるのでありがたや。

f:id:wood_wind108:20150107181355p:plain

今日のお昼。にゅうめんはおいしかったです。

 

今日の晩御飯はバナナ以外全部食べられたし明日あたり退院っぽい。

バナナは苦手だ...運転のお礼にお父さんにプレゼントしようと思います。

退院してもしばらくは実家にいないといけないので、アニメ見たり漫画読んだり勉強したり(!!!!!)しようと思います...

 

早く帰ってエスカ&ロジーのアトリエがやりたいよーうわーん

入院記念パピコ

少し遅くなりましたが、明けましておめでとうございます。

わたしは今岡山の某病院でこの記事を書いているのですがこれがまたなぜかというと水疱瘡に罹患しました。

新年早々最悪だ。

2週間ほど実家から出られないと聞いたので単位がピンチかもしれない...

教授もこういうとこで優しさを見せていかないといけないと思う。

 

熱は下がったけどまだ食事や水分補給が十分にできないということで病人の服を着て病人の食事をしているわけですが、暇で暇で13日坊主ですっぽかしたブログとやらを掘り出すに至りました。

次は何日坊主か分からないけどね。

 

水疱瘡なんだけど、実は小さいころにワクチン打ってまして。

で、軽く済むでしょと高をくくっていたら大惨事に、実家に強制送還&即入院でした。

聞けば20年くらいで切れるとかいうそうじゃないですか...

それなら弟がなった時に私もかかっておきたかった...

顔がね、とくにやばくてね、ゴブリンですよゴブリン。

京大病院なんかに入院してたら何人かにこの顔見られてたんだろうなー。

そんなのもうお嫁にいけないわよ。

 

暇すぎて猫の映像が流れるDVDをずっと流している。

女優さんがなんかナレーションつけてくれてるんだけど妙に感動を誘おうとしてるッぽくて腹立ってきたから消音した。

人間なんて猫からしたらただの温かい棒じゃボケ。

猫は無音でもかわいい。

 

そろそろごはんだ。