OmniAICreator/Qiita-1.07M huggingface dataset:
RedmineのJavaScriptからREST APIを使用する

JavaScriptからでもRedmineのREST APIを実行することはできる。
しかしREST APIの実行にはBASIC認証かログインユーザーのapikeyが必要になる。

この記事では少々トリッキーながらJavaScriptからログインユーザーのapikeyを取得し、実際にREST APIを実行してみる。

動作確認は **Redmine 2.6系** で行った。

REST APIを使わず、チケット一覧のコンテキストメニューが使用する`bulk_update`を利用する記事はこちら。
[RedmineのJavaScriptからbulk_updateを使用する](https://qiita.com/forenoonM/items/517d10e6a6b08c89f8f9)

# ログインユーザーのapikeyの取得
apikeyを取得するようなAPIは存在しない。
よってログインユーザーの「個人設定」をスクレイピングして取得する。
（下記コードは、当然ながらRedmineのバージョンアップなどで動かなくなる可能性はある）

```js
// Redmine 3.1以前版。
// 3.1以降のいずれかのバージョンからはajaxでapikeyが埋め込まれるためこの方法だと失敗する。
// REST APIがONになっていなければapikeyには空文字が入る。
let apikey = "";
$.get('/my/account').done(function(data){
     apikey = $("#api-access-key", $(data)).first().text();
});

// Redmine 3.1以降版。専用ページなので読み込み負荷は若干低いはず。
// REST APIのON/OFFに関わらずapikeyにはAPIキーの文字列が入る。
let apikey = "";
$.get('/my/api_key').done(function(data){
    apikey = $('#content > div.box > pre', $(data)).first().text();
});
```


# REST APIの実行
RedmineのAPIリファレンスを参考にしながら、ajax等でREST APIを叩く。

[Redmine API](http://www.redmine.org/projects/redmine/wiki/Rest_api)

```js
$.ajax({
    type: 'get',
    url: '/issues/7.json',
    data: {include:'relations'},
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'text',
    contentType: 'application/json',
})
.done((strJson) => console.log(JSON.parse(strJson)))
```

簡単にapikeyの取得とREST APIを続けて書くなら以下のようになるだろうか。

```js
$.get('/my/account')
.then(
    // マイページをスクレイピングしてapikeyを取得
    (html) => {
        return $("#api-access-key", $(html)).first().text();
    }
)
.then(
    // REST APIでチケット詳細を取得
    (apikey) => {
        return $.ajax({
            type: 'get',
            url: '/issues/15780.json',
            data: {include:'relations'},
            headers: {'X-Redmine-API-Key': apikey},
            dataType: 'text',
            contentType: 'application/json',
        });
    }
)
.then(
    // 取得したチケット情報を表示
    (json) =>  console.log(json)
)
```

# 雑感
最初はapikeyを入力させるダイアログを作成し、Web Storage経由でapikeyを各画面で使い回す方法も考えた。
しかしRedmineの内部フックを使っているわけではないため、ログアウトしても前のユーザーのapikeyが残ってしまうのはセキュリティ的にかなりよくない。

*（REST APIでアクセスできるデータを調べると分かるが、ユーザーのアクセス権限内ならばほぼ全てのデータを大量に読み書きできてしまう）*

そこで多少"黒魔術"的ではあるしアクセス負荷が余計にかかるが、個人設定ページをスクレイピングしてapikeyを取得し使い捨てる方法を考えた。
これならクッキーにもWeb Storageにも残らず、個人ページをブラウザで開いてコピペするのと同じ処理になる。
acmart使用時にヘッダ・フッタ・ページ番号・ACMRefの表示を操作する

カンファレンスの場合はヘッダ・フッタやページ番号などを削除するように求められることがある。

ページ番号もヘッダもフッタも削除したい場合は、

```
\pagestyle{empty}
```

プリアンブルにこう書けばOK。ヘッダ・フッタは残して、ページ番号だけ消したい場合は、

```
\settopmatter{printfolios=false}
```

あんまりないと思うけど、ヘッダ・フッタを消してページ番号だけ残したい場合は

```
\pagestyle{plain}
```

とする。

また、Abstractの下に入っているACM bibliographic entryを消したい場合は

```
\settopmatter{printacmref=false}
```

とする。
AWS RDS イベントサブスクリプションの通知を Slack へ流す（Lambda Python3.6）

## IAM ロール作成
ロール名：rdsevents-to-slack
信頼関係：Lambda
ポリシー：AWSLambdaBasicExecutionRole（AWS 管理ポリシー）

## Lambda 作成
リージョン：東京 (ap-northeast-1)
関数の作成:一から作成（設計図 cloudwatch-alarm-to-slack を参考）
名前：rdsevents-to-slack
ランタイム：Python 3.6
ロール：rdsevents-to-slack
コードエントリタイプ：コードをインラインで編集
ハンドラ情報：lambda_function.lambda_handler
関数コード：

```python
import os
import json
import logging
from datetime import datetime, timedelta
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
 
Slack_Webhook_URL = os.environ['SLACK_WEBHOOK_URL']
 
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
def lambda_handler(event, context):
    logger.info("Event: " + str(event))
    message = json.loads(event['Records'][0]['Sns']['Message'])
    logger.info("Message: " + str(message))
     
    slack_text1 = (datetime.strptime(message["Event Time"][:-4], '%Y-%m-%d %H:%M:%S') + timedelta(hours=9)).strftime("%Y/%m/%d %H:%M:%S")
    slack_text2= "%s: %s" % (message["Source ID"], message["Event Message"])
    slack_text = slack_text1+ "\n" + slack_text2
 
    slack_message = {
        'username': "AWS Norifier",
        'text': "*[ALARM] AWS RDS Events*",
        'icon_emoji': ":bangbang:",
        'attachments': [
            {
                "text": slack_text
            }
        ]
    }
     
    req = Request(Slack_Webhook_URL, json.dumps(slack_message).encode('utf-8'))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted to %s", Slack_Webhook_URL)
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)
```

環境変数
　SLACK_WEBHOOK_URL：Slack の Webhook URL

基本設定
　タイムアウト：5分

## AWS SNS 設定
トピック：rdsevents-to-slack

サブスクリプション
　プロトコル：AWS Lambda
　エンドポイント：arn:aws:lambda:ap-northeast-1:************:function:rdsevents-to-slack　

## RDS イベントサブスクリプション 設定
通知の送信先：SNS rdsevents-to-slack
NEMを使った認証


##はじめに
TipAssistというChrome拡張を作っているヤマコーと申します。
PCでtipnem使ってる人は是非インストールしてみてください（露骨な宣伝）。
（tipbot廃止に伴い非公開になりました)

#NEMの活用法
私は直接NEMに関する開発をしたことがないので（TipAssistはあくまで、twitter上でテキストをいじったりするだけ）、
想像と思いつき、あとはTLの人から出てきた知見を元に少し語る事にします。


まずNEMのユーザー側にとって特徴的な機能はおおよそ以下の通り。
・モザイク(トークン)の作成
・アポスティーユ（証明書発行）
・ハーベスティング
・Voting(投票)

この中でも、モザイクの作成は異質で、かつ応用性が高いです。
という事でモザイクの内容を取り上げます。


##モザイクの活用


>モザイク認証

指定のモザイクを持つ人のみ、アクセスできるページ。

![001.PNG](https://qiita-image-store.s3.amazonaws.com/0/132034/cfeb2bef-4edd-362e-3812-9e9e5dbe0b93.png)

>作ってみた。

####ファイル構成
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132034/c0265ca6-c8ea-01b0-699c-1196fc5f962c.png)

####
```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>認証が必要なページ</title>
    <script type="text/javascript" src="js/jquery-3.2.1.min.js"></script>
    <script type="text/javascript" src="js/main.js"></script>
  </head>
  <body>
    <p id="status"></p>
    <p id="mosaic"></p>
  </body>
</html>

```
```js:main.js
$(function(){
    var nem = prompt("NEMアドレスを入力してください").replace(/-/g,"");
    status("104.238.161.61:7890", nem,"nem_games.ammo:9mm");
});

function status(supernode,nemAddress,mosaic){
  $.getJSON(
    "http://"+ supernode +"/status", //リクエストURL
    null,       //送信データ
    function(data, status) {
      if(data.code == 6){
          $('#status').text("SN "+ supernode+"は正常に動作している");
          // getBalance(supernode,nemAddress);
          getMosaic(supernode,nemAddress,mosaic);
      }else{
          $('#status').text("SN "+ supernode +" に何らかの障害あり");
          console.log(data);
      }
    }
  );
}


//モザイク一覧
function getMosaic(supernode,nemAddress,mosaic){
  $.getJSON(
    "http://"+ supernode+"/account/mosaic/owned?address=" + nemAddress, //リクエストURL
    null,       //送信データ
    function(data, status) {
      mosaics = mosaic.split(":");
      hasflg = false;
      data.data.forEach(function(v, i, a){
        if(v.mosaicId.namespaceId == mosaics[0] && v.mosaicId.name == mosaics[1]){
          if(v.quantity > 1){//数量指定
            $('#mosaic').text(mosaic +"を" + v.quantity + "個持っています。ありがとうございます");
          }else{
            $('#mosaic').text(mosaic +"を" + v.quantity + "個持っていますがたりません");
          }
          hasflg = true;
        }
      });
      if(hasflg == false){
        alert(mosaic+ "をお持ちでない人にはお見せ出来ません！");
        window.location.href = "https://google.com";
      }

    }
  );
}


```

###実行結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132034/e2f5f8b1-b80e-312d-46f6-57d5932df288.png)


しかし、NEMアドレスだけだと、他のユーザーも知る事が出来る情報のため、簡単になりすましが出来てしまう。
~~というかこの例だとそれ以前の問題なのは見逃して下さい~~

![00.PNG](https://qiita-image-store.s3.amazonaws.com/0/132034/4953a063-5e78-0779-ad4c-9d2916123c76.png)

認証までの手順は以下の通り。

1.認証用アドレスに数XEMを送信する
2.認証用アドレスから暗号化されたメッセージでパスワードを送られてくる(ユーザー登録の完了)
3.認証が必要なページにアクセスして、NEMアドレスとパスワードを入力
4.そのNEMアドレスで指定のモザイクを所持していたら認証(yay!)



この認証方法をいろいろ使いまわせたら、汎用性が高そうですね！

ただ、この認証方式を特定のサーバーに依存したシステムにするんではなく、あくまでNEMだけでなんかできないかなーとか考えてます。
面白い方法を思いついた人は是非ヤマコーに教えてください！~~できるかわからんけど！~~
Google Homeと電子レンジの干渉を避ける

#Google HomeのWiFi接続がいきなり切れる事件の犯人
我が家にGoogle Home miniが来てから1週間。なぜか朝食の支度をするときに限って、Google Homeのラジオストリーミングが切れるという怪現象に悩まされていました。

- Google Homeはリビング、カウンターキッチンの脇に設置
- Google HomeはWiFiを拾って動作している
- 家庭内WiFiルーター側に異常なし
- Homeアプリの設定にも問題なし

そのうえ、なぜか一定時間経つとWiFi接続の不具合は解消されるという時限謎…
朝ごはん…
だいたい夕飯の残りを温めて食べる…
温める…そうか、電子レンジ！犯人はおまえだ！

#WiFiの2.4GHz帯と電子レンジは混線する
電子レンジの電磁波周波数は2.45GHzとなっています。

> 電磁波の周波数は、2450MHzでISMバンドのひとつであり、周波数を共用している無線LANやWi-Fi、直下の2400MHz帯アマチュア無線などは、電子レンジを動作させると、通信不能になる大影響を受けるが、総務省告示周波数割当計画脚注に「混信を容認しなければならない」と規定している。
https://ja.wikipedia.org/wiki/%E9%9B%BB%E5%AD%90%E3%83%AC%E3%83%B3%E3%82%B8

まさに、WiFiの2.4GHz帯と電子レンジが混線していることが原因のようです。

#Google HomeのWiFiを5Gにつなぎ直す
電子レンジをアルミホイルで密閉したりするのは現実的でないので、Google Homeを5GHz帯につなぎ直してあげましょう。

1.ルーターの説明書に従って、5GHz帯の設定をする
2.Homeアプリを入れているAndroidデバイスを、5GのWiFiに接続させる
3.Homeアプリ→デバイス→デバイス名（リビングなど）→設定　より、WiFiをつなぎ直す

#カウンターキッチンのリビングでGoogle Home
しばらく技術的な問題かと思って右往左往していましたが、まさか物理的な問題とは思いませんでした。もし今後1世帯に1台程度普及したら、多くの家庭で詰まりそうなポイントですね。

田舎のオカンに「あんたちょっとGoogle Homeがブチブチ切れるんだけど」と言われたら、本記事をそっとシェアしましょう。
windowsのPhpStormを使ってる人でterminalでうっかりlsを打っちゃう人へ

# はじめに
MacとWindowどちらもインストールして使ってるPhpStormerも多いかと思いますが、うっかりterminalでls打って、

```
'ls' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
```
っておこられることありませんか？（自分だけかなぁ）

そんなD●S嫌いな方におすすめな設定がこちら！

# terminalをPowerShellに変更する
下記のようにShellパスをPowerShellに変更すると！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/68254/99ee2635-02a2-008d-26a4-5de6ac6186ab.png)

ls使っても大丈夫！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/68254/518cf357-0422-f6e8-e389-c1de6aa9e809.png)


# terminalをGit Bashに変更する
どうせなら、ちゃんとbash使いたいですよね？ってことで。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/68254/2324f2b3-94f6-4f3d-4ed1-5ba8b28ca658.png)

もちろんls使っても大丈夫！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/68254/8fda162c-10f8-d530-fd09-07a74225da6b.png)

# おわりに
最近投稿してないし1ヵ月に1投稿はしたいよねってことで、小ネタでした。
PHPでの日本語対応メール＋添付ファイル

#はじめに
　ファイルを添付した日本語のメールを送る必要があったので、送り方のまとめです。
　PHPは7.1、ソースファイルはUTF-8Nで動作確認済みです。
#メールを送る
　メールタイトルと本文のみ日本語を使うなら、単純に下のようにすれば送れます。

```php
$to = "to@xxxx.co.jp";
$from = "from@xxxx.co.jp";
$subject = "日本語タイトル";
$body = "日本語の入った本文\n簡単ですね。";

mb_language("ja");
mb_internal_encoding('utf-8');
$head  = "From: {$from} \n";
if (!mb_send_mail($to, $subject, $body, $head)) {
	error_log('mb_send_mail fail');
}
```

#Fromの日本語対応
差出人を from@xxxx.co.jp と表示させるのではなく、差出人\<from@xxxx.co.jp\> と表示させるには以下の様にします。

```php
$to = "to@xxxx.co.jp";
$from = "from@xxxx.co.jp";
$fromtitle = "差出人";
$subject = "日本語タイトル";
$body = "日本語の入った本文\n簡単ですね。";

mb_language("ja");
mb_internal_encoding('utf-8');
$head  = "From: " . mb_encode_mimeheader(mb_convert_encoding($from_name,"ISO-2022-JP")) . "<{$from}> \n";
if (!mb_send_mail($to, $subject, $body, $head)) {
	error_log('mb_send_mail fail');
}
```

#toの日本語対応
ついでに宛先も to@xxxx.co.jp と表示させるのではなく、差出人\<to@xxxx.co.jp\> と表示させるには以下の様にします。

```php
$to = "to@xxxx.co.jp";
$totitle = "宛先";
$from = "from@xxxx.co.jp";
$fromtitle = "差出人";
$subject = "日本語タイトル";
$body = "日本語の入った本文\n簡単ですね。";

mb_language("ja");
mb_internal_encoding('utf-8');
$to = mb_encode_mimeheader(mb_convert_encoding($totitle,"ISO-2022-JP")) . "<{$to}>";
$head  = "From: " . mb_encode_mimeheader(mb_convert_encoding($from_name,"ISO-2022-JP")) . "<{$from}> \n";
if (!mb_send_mail($to, $subject, $body, $head)) {
	error_log('mb_send_mail fail');
}
```

複数宛先に送るには

```php
$to = mb_encode_mimeheader(mb_convert_encoding("宛先１","ISO-2022-JP")) . "<{$to1}>";
$to .= "," . mb_encode_mimeheader(mb_convert_encoding("宛先２","ISO-2022-JP")) . "<{$to2}>";
```

の様にします。

#添付ファイルを付けるには
　添付ファイルのファイル名が \$filename、ファイル本体が \$filebody に入っているとすると。


```php
$to = "to@xxxx.co.jp";
$totitle = "宛先";
$from = "from@xxxx.co.jp";
$fromtitle = "差出人";
$subject = "日本語タイトル";
$message = "日本語の入った本文\n簡単ですね。";
$filename = "日本語ファイル.pdf";
$filebody = file_get_contents("tmp.pdf");

mb_language("ja");
mb_internal_encoding('utf-8');
$mime_type = "application/octet-stream";
$boundary = '----=_Boundary_' . uniqid(rand(1000,9999) . '_') . '_';
$to = mb_encode_mimeheader(mb_convert_encoding($totitle,"ISO-2022-JP")) . "<" . $to . ">";
$head  = "From: " . mb_encode_mimeheader(mb_convert_encoding($from_name,"ISO-2022-JP")) . "<" . $from . "> \n";
$head .= "MIME-Version: 1.0\n";
$head .= "Content-Type: multipart/mixed; boundary=\"{$boundary}\"\n";
$head .= "Content-Transfer-Encoding: 7bit";

// 本文
$body .= "--{$boundary}\n";
$body .= "Content-Type: text/plain; charset=ISO-2022-JP;" .
	"Content-Transfer-Encoding: 7bit\n";
$body .= "\n";
$body .= "{$message}\n";
$body .= "\n";
// 添付ファイル
$filename = mb_convert_encoding($filename, 'ISO-2022-JP');
$filename = "=?ISO-2022-JP?B?" . base64_encode($filename) . "?=";
$body .= "--{$boundary}\n";
$body .= "Content-Type: {$mime_type}; name=\"{$filename}\"\n" .
	"Content-Transfer-Encoding: base64\n" .
	"Content-Disposition: attachment; filename=\"{$filename}\"\n\n";
$f_encoded = chunk_split(base64_encode($filebody));
$body .= $f_encoded . "\n\n";

if (!mb_send_mail($to, $subject, $body, $head)) {
	error_log('mb_send_mail fail');
}
```

簡単ですね。
GCE + CloudSQL に Railsアプリをデプロイする方法

# GCP GCE CentOS6

## CentOS6の初期設定
```bash

$ yum update -y
$ yum install -y epel-release
$ yum install -y gcc gcc-c++ make
$ yum install -y git tig zlib-devel libxml2-devel libxslt-devel readline-devel openssl-devel ImageMagick ImageMagick-devel
$ yum install -y curl-devel bind-utils sqlite sqlite-devel bzip2 which
$ yum install -y httpd httpd-devel curl-devel bind-utils
$ yum install -y mysql-devel
$ yum install -y nodejs --enablerepo=epel

# selinux無効化
$ setenforce 0

```

## Rubyインストール
```bash

$ echo 'gem: --no-ri --no-rdoc' >> ~/.gemrc
$ mkdir -v /usr/local/rbenv
$ groupadd rbenv
$ chgrp -R rbenv /usr/local/rbenv
$ chmod -R g+rwxXs /usr/local/rbenv
$ git clone https://github.com/sstephenson/rbenv.git /usr/local/rbenv
$ git clone https://github.com/sstephenson/ruby-build.git /usr/local/rbenv/plugins/ruby-build

$ vi .bash_profile
export RBENV_ROOT=/usr/local/rbenv
export PATH=${RBENV_ROOT}/shims:${RBENV_ROOT}/bin:${PATH}

$ source .bash_profile
$ rbenv install 2.4.2
$ rbenv global 2.4.2

```

## Webサーバ準備（apache+passenger）
```bash

$ gem install rails
$ gem install passenger
$ passenger-install-apache2-module -a
$ touch /etc/httpd/conf.d/passenger.conf
$ passenger-install-apache2-module --snippet >> /etc/httpd/conf.d/passenger.conf

```



## アプリケーション設置
```bash

$ cd /var/www/
$ git clone [リポジトリー] sample-app
$ cd sample-app/
$ bundle

```

## confファイルの設置
```bash

$ vi /etc/httpd/conf.d/test-batch-app.conf

```

```bash:sample-app.conf
<VirtualHost *:80>
   ServerName localhost

   RailsEnv production
   SetEnv SECRET_KEY_BASE d96fbcea88c6d235e458218c0d123ce60008b4a1e556c

   DocumentRoot /var/www/test-batch-app/public
   <Directory /var/www/test-batch-app/public>
     AllowOverride all
     Options -MultiViews
     Allow from all
     # Uncomment this if you're on Apache > 2.4:
     # Require all granted
   </Directory>
</VirtualHost>
```


## Cloud SQL Proxy 設定

```bash

$ cd
$ wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
$ chmod +x cloud_sql_proxy
$ mkdir /cloudsql
$ chmod 777 /cloudsql/

# プロキシースタート
$ ./cloud_sql_proxy -instances="[YOUR_INSTANCE_CONNECTION_NAME]" -dir=/cloudsql

```

## 参考
https://www.totalsolution.biz/cloudsql-proxy-connect/
Connect(); Japan 2017にみるマイクロソフトのクラウド戦略

日本マイクロソフトが11月17日に開催した技術イベント「Connect(); Japan 2017」。直前に米国でMicrosoftが開催した「Connect(); 2017」を解説するイベントで、デジタルトランスフォーメーションやオープンソースという近年のMicrosoftの取り組みが語られました。オープンソースとクラウドによって、Microsoft自身の開発スタイルも変えていっていることが感じられるイベントだったようです。オンラインメディアの記事からピックアップして紹介します。

※下記サイトからの転載。ビッグデータ・AIなどに関するトピックを毎週取り上げています。
[TechCrowd: https://www.techcrowd.jp/related/](https://www.techcrowd.jp/related/)


#[Microsoftはオープンソースで企業のデジタルトランスフォーメーションを支える] (https://thinkit.co.jp/article/13019)

ThinkITのイベント・セミナーコーナーでの、Connect(); Japan 2017の講演内容および
プレスラウンドテーブルの紹介記事です。

プレスラウンドテーブルでは日本マイクロソフト株式会社の浅野智氏（クラウド＆エンタープライズ本部 業務執行役員 本部長）がMicrosoftがオープンソースに積極的に取り組むのは「デジタルトランスフォーメーションを進めるため」と説明。また、すでに、Microsoft Azure上で動いている仮想マシンのOS比率では、Linuxが40%を超えており、さらに年々増えているという。「この40%を、12か月で60%に増やしていきたい。半分以上がオープンソースという、メモラブルな年になるのではないかと考えている」とのこと。

また、2018年の第1四半期（カレンダーイヤー）での正式リリースを想定しているPaaS型のリレーショナルデータベースサービス「Azure DB for MySQL／PostgreSQL」をすでにプレビュー版として提供しているが、MicrosoftのNolan Wu氏がそのプレビュー版の事例を紹介。浅野氏も「昔のMicrosoftではソフトウェアのベータ版を、テスト用で本番には使わないように断って出していた。この提供の考え方も、オープンソースとクラウドで変わった。プレビュー版でありながらお客様に実運用してもらい、フィードバックしてもらって、そこから製品をよりよくしていく。
そこが大きな変化」とマイクロソフトがオープンソースとクラウドによって開発スタイルなども変えていることを紹介しています。


#[Connect(); Japan 2017レポート--MSはビッグデータ時代のビジネスと開発現場を支援] (https://japan.zdnet.com/article/35110680/)

ZDnetのConnect(); Japan 2017の紹介記事の中の、基調講演及び特別講演の内容の紹介記事です。
最初に登壇した日本マイクロソフト アーキテクト 廣瀬一海氏は、OSやソフトウェア、サービスなどの構築・運用管理を自動化するオーケストレーションサービス「Kubernetes」を、Microsoft Azureの標準機能として提供する「Azure Containers Service（AKS）」のプレビュー版を先頃公開したことを紹介。ドイツのシーメンスは、ヘルスケア分野でAKSを用いたアプリケーションを開発し、データをグローバル分散型マルチモデルデータベースであるAzure Cosmos DBに格納。世界中で同じデータを管理し、同一のユーザー体験を提供しているとのこと。

また、MariaDBにも「Azure Database for MariaDB」としてMicrosoft Azureが対応。これでMySQL、PostgreSQL、MariaDBとOSS系DBの大半が利用可能になったことなどさまざまな発表がされました。


#[Connect(); Japan 2017レポート--ペアプロを実現する統合開発環境の強化] (https://japan.zdnet.com/article/35110683/)

ZDnetのConnect(); Japan 2017の紹介記事の中の、日本マイクロソフト 井上章氏のセッション「Visual Studioとクロスデバイスアプリケーション開発の今」の紹介記事です。

マイクロソフトは今回のConnect(); 2017で「Any Developer, Any App, Any Platform」というテーマを掲げ、開発者が好きなプラットフォームで好みの開発環境を利用できることを明示。そのプラットフォームとなる開発統合環境のVisual Studioに、新機能「Visual Studio Live Share」を加えたことを講演で紹介しています。

また今回新たに「.NET Embedding in iOS+Adroid」を発表。C#やF#といった既存の.NETコードをAndroidならJavaへJIT(Just-In-Time)コンパイルし、iOSならSwiftやObjective-Cネイティブコードに静的コンパイルする仕組みを実現。.NET Frameworkというアプリケーション開発・実行環境をiOSやAndroidを対象にしたXamarinと、Windows/macOS/Linuxを対象にした.NET Coreを合わせた「.NET Application Models」を一歩進めた発表も行っています。


#[Connect(); Japan 2017レポート--生産性を大幅に改善するMicrosoft AI
] (https://japan.zdnet.com/article/35110679/)

ZDnetのConnect(); Japan 2017の紹介記事の中の、「Microsoft AIによるインテリジェントアプリケーションの構築」講演の紹介記事です。

Microsoftは「AI（人工知能）の民主化」を標榜し、データの意味を解釈する「Understanding（理解）」、結論を導き出す「Reasoning(推測・予測)」、そしてフロントエンドにチャットボットなどを配置して、人から見ると自然なやりとりを実現する「Natural UI(自然なUI)」を注力ポイントに定めているとのこと。

講演では、Microsoft Cognitive Services、Azure Machine Learningなどを活用した事例を紹介しています。
CMake で GitHub から Boost を取得して静的リンクする

CMake については鋭意研究中なので best な方法ではないと思います。

# 環境
macOS 10.13.1
CMake 3.9.5
Boost 1.65.1
CLion 2017.3

# プロジェクト構成
ざっくりですが以下のような構成で `cli` に Boost ライブラリを静的リンクします。

```
.
├── CMakeLists.txt
├── cli
│   ├── CMakeLists.txt
│   └── cli.cpp
└── vendor
    └── boost
        └── boost.cmake
```

# CMakeLists.txt の書き方

プロジェクトルートの CMakeLists.txt の記載内容は以下のとおりです（一部省略しています）。重要なのは `include(ExternalProject)` で、これを記載することで外部から Boost を取り込めるようになります。

```
cmake_minimum_required(VERSION 3.9.5)

...（省略）...

add_subdirectory(cli)

...（省略）...

include(ExternalProject)

include(${PROJECT_SOURCE_DIR}/vendor/boost/boost.cmake)
```

また、`./vendor/boost/boost.cmake` の内容は以下の通りです。

```
set(Boost_Bootstrap_Command ./bootstrap.sh)
set(Boost_b2_Command ./b2 toolset=clang install
        --prefix=${CMAKE_BINARY_DIR}/INSTALL
        --threading=single,multi
        --link=static
        --variant=debug
        --address-model=64
        --stagedir=stage/x64
        -j2
)
ExternalProject_Add(boost
        GIT_REPOSITORY    https://github.com/boostorg/boost.git
        GIT_TAG           boost-1.65.1
        BUILD_IN_SOURCE   1
        CONFIGURE_COMMAND ${Boost_Bootstrap_Command}
        BUILD_COMMAND     ${Boost_b2_Command}
        INSTALL_COMMAND   ""
        PATCH_COMMAND     ""
        UPDATE_COMMAND    ""
        INSTALL_DIR       ${CMAKE_BINARY_DIR}/INSTALL
)
```

`INSTALL_DIR` を指定してプロジェクト内に Boost ライブラリをインストールするようにしています。また、`b2` のオプションについては [boostjp](https://boostjp.github.io/howtobuild.html) や [boost.org](http://www.boost.org/doc/libs/1_65_1/more/getting_started/unix-variants.html) を参考にしました。GitHub で公開されている CMake プロジェクトからも多くのことを学べると思います。最後に `./cli/CMakeLists.txt` の内容です。

```
set(SOURCE_FILES cli.cpp)
add_executable(cli ${SOURCE_FILES})

add_dependencies(cli boost)
set(BOOST_LIBRARIES
    ${CMAKE_BINARY_DIR}/INSTALL/lib/libboost_atomic.a
    ${CMAKE_BINARY_DIR}/INSTALL/lib/libboost_date_time.a
    ${CMAKE_BINARY_DIR}/INSTALL/lib/libboost_regex.a
    ${CMAKE_BINARY_DIR}/INSTALL/lib/libboost_serialization.a
    ${CMAKE_BINARY_DIR}/INSTALL/lib/libboost_system.a
    ${CMAKE_BINARY_DIR}/INSTALL/lib/libboost_thread.a
    ${CMAKE_BINARY_DIR}/INSTALL/lib/libboost_wserialization.a
)

include_directories(${CMAKE_BINARY_DIR}/INSTALL/include)

target_link_libraries(cli PRIVATE ${BOOST_LIBRARIES})
```

`target_link_libraries` で `cli` に Boost ライブラリをリンクします。

# まとめ
Boost.Asio などを使いたい場合は上記の手続きのみでは対応できません。ヘッダファイルをビルドディレクトリ（私の環境だと `./cmake-build-debug/boost-prefix/src/boost` になります）からインストールディレクトリにコピーしてあげる必要があります（なぜインストールされないかは謎・・・）。
見よう見まねで書いているので、おかしな部分も多いと思いますが、私と同じように CMake よーわからん・・・となっている方の一助となれば幸いです :thumbsup:
Kaggle_Titanic問題_機械学習(RamdomForest)

# はじめに
Titanic問題をRandomForestを用いてSubmitしました。
Scikit-learnのRandomForestモデルを用いて分類してみました。

# コード
行ったこと
1．ライブラリimport
2. pandasによるデータの読み込み
3. 分類しにくかったColumnの削除
  ↑ここの工夫をもっと頑張ればよい結果が出たんだろうなと思いました。
4. Nanの処理（中央値で補完）
5. 学習・予測
6. 結果のOutput

```py3:Code
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
import matplotlib
import seaborn as sns
%matplotlib inline

#DataRead
TrainDF = pd.read_csv(r'Data\train.csv',index_col=0)
#testRead
TestDF = pd.read_csv(r'Data\test.csv',index_col=0)

TrainDF_NoName = TrainDF.drop(['Name','Ticket','Cabin'],axis=1)
TrainDF_NoName.dropna(inplace=True)
TestDF_NoName = TestDF.drop(['Name','Ticket','Cabin'],axis=1)

#TrainDF,TestDFそれぞれのデータフレーム生成
TrainDF = pd.get_dummies(TrainDF_NoName)
Trainx = TrainDF.drop(['Survived'],axis=1)
Trainy = TrainDF['Survived']
TrainDF.head()
TestDF = pd.get_dummies(TestDF_NoName)
TestDF['Age'] = TestDF['Age'].replace(np.nan,TestDF['Age'].median())
TestDF['Fare'] = TestDF['Fare'].replace(np.nan,TestDF['Fare'].median())

# RandomForestのfitting
rfc = RandomForestClassifier().fit(Trainx,Trainy)
results = rfc.predict(TestDF)

#Submit用データフレーム生成
ResultDF = pd.DataFrame({'Survived':results},
                       index = TestDF.index)

# 結果のOutput
OutputFilePath = 'Output\Output.csv'
ResultDF.to_csv(OutputFilePath)

```


# 結果(Submit)
結果は下記のようになりました。スコア0.713で、8847/9524位でした。
![SnapCrab_NoName_2017-12-3_21-31-22_No-00.jpg](https://qiita-image-store.s3.amazonaws.com/0/148213/25cd767b-afef-8cb5-9d22-a01f17b68680.jpeg)

# 考察
Name,Ticket,Cabinの説明変数を利用したかった。
- Name要素を使って、家族毎に分けてみたい。
- fareでランク分けされてたりするのかな？

Score1.00出してるteamはどうやってるんだろう。
Ubuntu Artful (17.10) にBacula 9をインストールする方法


2017年7月にBacula9がリリースされました。

[CentOSにはパッケージがある](https://www.bacula.jp/install-server)のですが、Ubuntuの場合、Artfulで用意されているバージョンは7止まりとなっています。

しかし、2018年4月リリース予定の Bionic (18.04 LTS) にはパッケージがあります。これをソースパッケージからビルドすれば、Artfulでも9がインストールできます。

## 環境と前提

* Ubuntu Artful (17.10)
* データベースはMySQLを前提にします。（MariaDBなどを使う場合は、お好みで各所読み替えて下さい）


## MySQLサーバのインストール

MySQLサーバを先にインストールしておきます。

Ubuntu用のBaculaパッケージはインストール時にDBの作成をしてくれますが、その時にMySQLサーバが動いていることが必要なためです。

```bash
apt install mysql-server
```


## リポジトリの追加

旧式な方法ですが…

```bash
nano /etc/apt/sources.list.d/bionic.list
```

で、次の行を追加します：

```txt
deb-src http://jp.archive.ubuntu.com/ubuntu/ bionic universe
```

## ソースパッケージのダウンロードとビルド

```bash
apt update
apt --build source bacula
```

うまくいけば以下のdebが出来上がります。

```txt
bacula-bscan_9.0.6-1_amd64.deb
bacula-client_9.0.6-1_all.deb
bacula-common-mysql_9.0.6-1_amd64.deb
bacula-common-pgsql_9.0.6-1_amd64.deb
bacula-common-sqlite3_9.0.6-1_amd64.deb
bacula-common_9.0.6-1_amd64.deb
bacula-console-qt_9.0.6-1_amd64.deb
bacula-console_9.0.6-1_amd64.deb
bacula-director-common_9.0.6-1_amd64.deb
bacula-director-mysql_9.0.6-1_amd64.deb
bacula-director-pgsql_9.0.6-1_amd64.deb
bacula-director-sqlite3_9.0.6-1_amd64.deb
bacula-director_9.0.6-1_amd64.deb
bacula-fd_9.0.6-1_amd64.deb
bacula-sd_9.0.6-1_amd64.deb
bacula-server_9.0.6-1_all.deb
bacula_9.0.6-1_all.deb
```

## debのインストール

MySQL用のパッケージだけを選択してインストールします。
※GUIツールを外したい場合は bacula-console-qt_*deb を除いて下さい。

```bash
dpkg -i bacula-common_*deb bacula-common-mysql_*deb \
	bacula-director_*deb bacula-director-common_*deb \
	bacula-director-mysql_*deb bacula-fd_*deb bacula-sd_*deb \
	bacula-console_*deb bacula-console-qt_*deb bacula_bscan*deb
```

上のコマンドは依存関係が満たされず、完了しません。

[第152回　パッケージをビルドしてみる：Ubuntu Weekly Recipe](http://gihyo.jp/admin/serial/01/ubuntu-recipe/0152) の通り、 -f (--fix-broken) 付きでapt installを実行して解決します。

```bash
apt -f install
```

以上です。
webpackでmoment.jsを日本語設定したものをimportする

## 追記 プラグイン使ってね

コメントいただきました。ありがとうございます。現在は不要な言語カットはプラグインで簡単にできます。

> moment-locales-webpack-pluginを使うともうちょっと楽に使うlocaleを絞れそうですー。
> http://momentjs.com/docs/#/use-it/webpack/

## 追記

かつてのバージョンは以下のように記述すれば、localeファイルを除いてmoment.jsをインポートできたんですが、最新のは普通に全てインポートされます。

```js
import moment from 'moment/moment'
```

おとなしく webpack.IgnorePlugin とか使ってください。

```js:webpack.config.js
plugins: [
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
]
```

---

moment.jsを普通にimportすると全ての言語ファイルも含まれてしまいファイルサイズが大きくなってしまいます。それを回避するため、webpack.config.jsでmoment.jsを言語ファイルを除いてimport方法が紹介されてたりします。

[webpack で moment.js の無駄なロケールファイルを除去する
](https://qiita.com/jimbo/items/95da1c223ad25a33ed16)

```js:webpack.config.js
plugins: [
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
]
```
この方法のもと、moment.jsを日本語に設定して使うとき以下のように書く必要があります。

```js
import moment from 'moment'
import 'moment/locale/ja'

moment.locale('ja')

console.log(moment().format('M月D日（dd）')) // => 12月３日（日）
```
## 日本語設定にしたものをimport

日本語設定が前提のときは、毎回これを書くのもだるいので、日本語設定されたmoment.jsをexportするファイルを作ってaliasを設定してあげるとスマートかと。日本語以外の言語ファイルも読み込まれません。

↓

```js:src/moment-ja.js
import moment from 'moment/moment'
import 'moment/locale/ja'

moment.locale('ja')

export default moment
```
```js:webpack.config.js
resolve: {
  alias: {
    'moment-ja': path.join(__dirname, 'src/moment-ja.js')
  }
}
```
```js
import moment from 'moment-ja'

console.log(moment().format('M月D日（dd）')) // => 12月３日（日）
```
AWS FargateとECSの違いは？

## AWS Fargateとは？
ラスベガスで開催された2017年 AWS re:Invent会議で、新しいサービスAWS Fargateを発表した。このサービスを使うことで、実行インフラストラクチャについて考えることなくコンテナを運用できるようになる。

## AWS ECSとの違いは？
コンテナベースのサービスと言えばAmazon ECSがあげられる。
ECSとの違いはなんだろうか？一言で言えばEC2インスタンスが不要なコンテナベースのサービスだ。

ECSではコンテナベースのクラスタリングをEC2上で行なっていた。コンテナの設定や必要なリソースの設定はコンソール上で行うためEC2の存在を意識することはなかったが、EC2コンソールを見てみると確かにインスタンスが存在する。

「EC2インスタンスが不要なコンテナベースのサービス」とは要はインフラレイヤーの管理を行わなくてもよくなると言うこと。おそらく裏ではEC2が動いているのだろうが、マネージドでよろしくやってくれるので、我々はアプリケーションレイヤーのみに集中することができますよと言うこと。

コンテナベースのサービスはECSの他にもAWS Batchなどもあるが、これも裏ではECSが動いている。
もちろんさらにその裏ではEC2が動いていた。Fargateはインフラレイヤーが完全にマネージドに移ったと言う意味では他のコンテナサービスと一線を画すものだと思う。

## Fargateのメリット/デメリット整理

### メリット

- インフラレイヤーの管理が不要
  - → コンテナはインフラレイヤーを気にしないとか言いつつ、ECSインスタンスの死活監視をCloudwatchでやってたり・・ということがなくなる
- リソースを効率的に使用できる
  - → CPU3コア(そんなインスタンスは無いが)のインスタンスにCPU２コアを割り当てたコンテナを乗っけた場合、1コア分のCPUが無駄になる。Fargateではそもそもインスタンスのコア数を考慮する必要が無いため、効率的にリソースを使用することができる。
- スケールが容易
　→　従来であればコンテナのスケールに合わせてEC2インスタンスのスケールも設計しなければ行けなかったが、Fargateでは不要になる。

### デメリット

- EC2インスタンスに比べ少々お高い
  - → ただしメリットでも述べたように効率的にリソースを使うことができるのでコスパはよくなるかも？

## 終わりに
2017年12月1日時点でus-east-1リージョンでしか使用できません。
近いうちに東京リージョンでも公開される予定らしいですが、基本的にはFargateを使用すれば間違いないかなと思っています。

おしまい。


(2018/06/06追記)
2018年7月中にTokyoリージョンでも使えるようになるみたいです。
https://aws.amazon.com/jp/blogs/news/aws-fargate-tokyo-launch/
ImageMagickを使う（Windows編）

画像処理ライブラリ「ImageMagick」を使ってみる

参考：
[ImageMagick (1) インストール編](https://higuma.github.io/2016/08/31/imagemagick-1/)

# 目的
- 大量にある画像を縦長、横長に分類したい（ImageMagick）

# 前提
- ImagiMagick
    - すぐ消すかもしれないのでポータブル版を使う
    - すぐ消すかもしれないのでパスは通さない
    - Git Bashが導入済みなので、これでUnixコマンドを使う

# 導入
1. [ダウンロードページ](http://www.imagemagick.org/script/download.php)から「ImageMagick-7.0.7-8-portable-Q16-x64.zip」をダウンロード
2. `C:\library\ImageMagick`に展開
3. `C:\Temp`でGit Bashを立ち上げる（右クリック→「Git Bash Here」）
4. 下記コマンドを実行し、画像の幅と高さを取得して表示してみる


![affine.png](https://qiita-image-store.s3.amazonaws.com/0/146590/7df32e0e-73db-06d2-2542-680357ed17ca.png)
↑ affine.png：304pixel x 85pixelの画像（ImageMagick付属の画像集より）

```
$ /c/library/ImageMagick/identify.exe -format "%w %h" /c/library/ImageMagick/images/affine.png
304 85
```
#### 画像のサイズが取得できたので、導入成功

# 動作を確認しながら目的のシェルスクリプトを作成
```
$ cat definition
ROOT_PATH="/c/Temp/"
IDENTIFY="/c/library/ImageMagick/identify.exe"
TEMP_FOLDER="_temp"
HEIGHT="_hight-is-longer"
WIDTH="_width_is_longer"

$ cat testShell01.sh
#!/bin/bash

function isPicture(){
    # 拡張子で画像かどうか判定
    case ${FILE_EXTENSION} in
        "jpg" ) TOF="true";;
        "png" ) TOF="true";;
        * ) TOF="false";;
    esac
}

. ./definition

# _____picuturesの中のファイル一覧
ARRAY=$(ls ${ROOT_PATH}${TEMP_FOLDER}/)

if [ -n "${ARRAY}" ]; then
    # 一枚ずつサイズを取得して縦横の長さを比較
    for item in ${ARRAY[@]};do
        FILE_EXTENSION=$(echo ${item} | sed 's/^.*\.\([^\.]*\)$/\1/')
        isPicture
        # 対象のファイルが画像かどうかの判定
        if [ ${TOF} = "true" ] ; then
            echo ${item}
            tate=$(${IDENTIFY} -format "%h" ${ROOT_PATH}${TEMP_FOLDER}/"${item}")
            yoko=$(${IDENTIFY} -format "%w" ${ROOT_PATH}${TEMP_FOLDER}/"${item}")
            echo -ne "high:${tate} width:${yoko}\n"

            # 縦長なら_hight-is-longerディレクトリへ
            # 横長なら_width_is_longerディレクトリへファイルを移動させる
            if [ ${tate} -ge ${yoko} ]; then
                echo "Vertical picture"
                mv -vvv ${ROOT_PATH}${TEMP_FOLDER}/"${item}" ${ROOT_PATH}${HEIGHT}/
            else
                echo "Horizontal picture"
                mv -vvv ${ROOT_PATH}${TEMP_FOLDER}/"${item}" ${ROOT_PATH}${WIDTH}/
            fi
            echo -e "---\n"
        else
            echo "${item} is not picture"
        fi
    done
else
    echo "No pictures"
fi
```

# 課題
- 自分が用意した作業対象の画像が空白を含んでいるせいで上手く動かなかった。
　→ シェルスクリプト内でリネームしようとするも上手くいかず、やむなくそこだけバッチで処理

- 上の理由から、この作業もバッチ化すれば良い感じなのだけれど、それも思ったようにいかず
PeerCast プログラミング配信 Advent Calendar 2017 3日目

# やったこと

冬コミのフロントエンド本の原稿を書きました
アウトラインを作って本文を少し書けたので進捗しました！やったぜ！

# こまったこと

Re:VIEWに不慣れなのと、環境構築が不十分だったので書いてから成果物を確認するまでのサイクルが遅くなりがちでつらかった
参考にしたプロジェクトにhtmlのビルドが設定されてなかったので、これを解決してフィードバックサイクルをもっと早くしないと締め切りに縊り殺されると思いましたまる


以上になります
電車の中で一生モノの英語語彙力を身につけるアプリ


[プログラマのための英語・外国語 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/lingo)
にお邪魔する形で書かせて頂きます。

個人的にものすごく重宝させて頂いている語彙力強化アプリです。

語彙力。

大事ですよね。
英語力の本質だと思います。

iKnow!は忘却曲線理論に基づいた語彙力学習アプリです。
長期記憶に定着するまで、適度に復習を促してくれるので
効率よく「一生モノの語彙力」を身につけられます。

Android版、iPhone版、そしてPCのWEB版があります。

エンジニアに特化した語彙力が身につけられるわけではないので
英語の基礎力をつけたい方向けです。

ちなみに有料です。月１48０円〜。（その分の価値は間違いなくあると思います）

#忘却曲線

要は、適度に復習させるだけです。

以下の図をみればなんとなくご理解頂けると思います。

![forgettingcurve1.png](https://qiita-image-store.s3.amazonaws.com/0/158747/dff5613d-2016-f0dd-96a9-ccdc88e7f1c9.png)
iKnowの忘却曲線。（画像は　http://eikaiwa.dmm.com/blog/17029/ から拝借しました）

復習のタイミングは、覚えはじめはなるべく早く
定着するにつれ、すこしずつスパンが伸びていきます。

![studyfrequency1.png](https://qiita-image-store.s3.amazonaws.com/0/158747/9a223e52-b8c3-302f-4213-90c6d63bf246.png)
iKnowの学習頻度イメージ。（画像は　http://eikaiwa.dmm.com/blog/17029/ から拝借しました）

やってることは定期的に復習させる、ってだけなのですが
ちょうど忘れかけた時期（だけど完全に忘れてないタイミング）で
復習がやってくることが多いです。

効果を実感しています。


#語彙だけでなく、発音、３つの例文、リスニング補強もできる。

１つの語彙につき３つほど例文と音声が入っています。
音声もアメリカ英語、イギリス訛り、など混じっていて
適度にリスニング力が鍛えられます。

スペルを問われる出題もあるので、
一緒に覚えられます。



#iknowの弱点

語彙の細かいニュアンスの違いを重視してないのが気になります。
例えば「主要な」を意味する単語は比較的簡単な単語でも

major
principal
chief

と、ごろごろ出てきますが、
iKnowではこれらの使い分けは学習できません。
（センスがいい人は例文から勘どころをつかめるかもしれませんが、私には無理です）

あとは、名詞形　形容詞形　動詞形　副詞形　変化が一緒に覚えられないのは不満です。

competition　（名）競争　大会　コンペ　

が出題されたなら、それらの変化形である、

- competitive　（形）競争的な　競争に強い
- competitively　（副）競争的に
- competitor　（名）競争者　競争相手

などもまとめて出題してくれた方が覚えやすいと思うのですが・・・。

iknowでは全く別の語彙として、
全く別の学習コースに入っています。学習タイミングもバラバラになります。
各自で辞書ひくなどして補強をしたほうがいいでしょう。


#まとめ

・復習サイクルが良く出来てる語彙学習アプリ
・ついでにリスニング
・有料。無料お試し期間もあるので興味あれば是非
Dart、AngularDartに関するこれまでのまとめと、個人的な所感

[Dart Advent Calendar 2017](https://qiita.com/advent-calendar/2017/dart) 三日目の記事です。

AngularDartを仕事でも使っているの人間なので、今回はDartのこれまでのおさらいと、今までの経験から感じた事などをアウトプットしたいと思います。（今回は文字中心です）
ちなみに先日[Dart Meetup Tokyo #2](https://dartisans-jp.connpass.com/event/68392/)を主催させてもらいました。
Adevent Calendarではあと2回記事を書く予定なので技術的な部分はそちらで記事にしようかなと。

#Dart

　Dartの当初の思想の1つに他の言語から来たエンジニアがすんなりDartに慣れる事が出きるように設計されている点があります。主にターゲットとして考えられていたのがJavaScript、Java、C等のエンジニアでした。なのでそのあたりを経験しているエンジニアであれば構文などにおいて面食らうことはあまり無いと思います。実際に自分自身も構文に慣れるのに時間はかかりませんでしたし、AngularDartを使って開発しているチームメンバーも同じような感想です。
　ただし、後発の言語なので今の時代に合わせてAPIが整備されていて、非常に扱いやすい言語になっていると思います。そのおかげで学習コスト等は高くないと感じます。まぁ、学習コストというのは各々のバックグラウンドに大きく影響されると思うので一概に言えることではないですが。
　
　紆余曲折ありDartVMをChromeに搭載する事を断念しましたが、それによりDartの進化が止まることは無く、むしろ方向性を明確にした（WebではJSのトランスパイルに専念した）おかげなのかよりDart自身の開発が活発になった感があります。
　実際細かいながらもDart自身の言語としての使い方はstrong-modeを始め確実に進化しています。また、当初からDart開発チームは「どれだけ素晴らしい言語であっても環境が整備されていなければ普及は難しい」との考えを基にSDKだけでなく周辺ライブラリーや、ツール類などの整備に力を入れてました。その努力もあり現在ではかなりDartエコシステムも整備されてきています。このあたりはDart Meetup Tokyoで少し話をしたので[参考](https://speakerdeck.com/takayukihayashi/dartekosisutemufalseshao-jie)にしていただければと。

#AngularDart
　当初はAngular2を開発する過程でTypeScriptからDartとJavaScriptのソースを生成していましたが、この場合Angular2を開発するためには3つの言語に精通する必要があり、これによりAngular2開発者の負担増加と開発スピードの低下が問題となっていました。そこでBeta版の途中でTypeScript版とDart版は各々別の道を歩むことになります。これにより双方で開発スピードが上がる且つTypeScrip及びDartそれぞれの言語特性を活かした開発が可能になりました。現在では得にイベント処理の部分がTS版とDart版ではかなり違ってきてるはずです（とはいえ自分はTS版をWatchしてないので正直違いはわかりません・・・）
　
#Flutter
　今後Dartが普及する上で重要なポジションになると思います。海外ではすでに大きく注目され始めているモバイルアプリ開発に特化したSDKです。AOTコンパイルによりAndroidとiOS両方のアプリをDart（Flutter）のコードから生成します。fluxアーキテクチャーにインスパイヤされているのでreactを触ったことがある人は違和感なくflutterアプリを構築出来るのではないでしょうか。個人的にも来年はFlutterの勉強に力を入れていきたいと思っています。

#どういう人にDart、AngularDartを勧めるか？
　個人的な見解としてはすでにTSやJSでバリバリフロントエンドを開発している人がDartやAngularDartの世界に来てもあんまり意味はないというかキャッチアップにコストが掛かるだけかなと思います。得にTSを使っているなら静的な型もありますしね。
　ではどういう人に合いそうかというと今までServer Sideに生息してた人だったりWebもJS以外でのSSRが中心だった人がSPA作るって時にはマッチしそうかなと。というのも上記に挙げたとおりDartはDart開発チームが環境を整備していることもあり、ビルド周りや各種ツールで迷う事が無いためです。公式のドキュメントは非常に充実しているので英語に拒否反応が出ない人であればAngularDartは1つの選択肢になるのではないでしょうか。
　まぁ、結局は選定者の好みでしょうがｗ

#2018年のDart
　まず2018年の1月にLAにて2日間かけてDartの大きな[カンファレンスが開かれます。](https://news.dartlang.org/2017/11/you-are-invited-to-dartconf-los-angeles.html)
ここではDart2.0、AngularDart、Flutterを中心にした話が主になるようです。おそらくそこを前後してDart2.0のリリースがあるのではないでしょうか。Dart2.0に関してはnullsafeやオーバーロードのサポート等大きな言語仕様の拡張は見送られています。中の人も「nullsafeは素晴らしい機能だけどまだDartには早いかな」とgiiterにて答えてました。オーバーロードに関しても同様です。Dart2.0はとにかくstrong-modeをデフォルトにするという部分がメインの仕様変更になっていて、あとは便利なAPIの拡張などが主になってます。このあたりは本家のニュースレターの[過去ログなど](https://github.com/dart-lang/sdk/tree/master/docs)を見るといいです。個人的にはもっといろいろ期待してたのですが段階的にアップデートしていこうという事なのでしょう。2.0以降にも期待しています。
　ちなみに現在2.0は絶賛開発中ではありますが仕事でも使っていて得に支障は無い感じです（あんまり難しいことしてないからでしょうが）
　
　Dartの2018年は今年以上にflutterを中心に動きそうです（正直もっとWebの方にももっと力入れて欲しいなと思ってますが・・・）。flutterにかけるリソースからgoogleの本気度が伺えますし、1月のカンファレンスもflutterの話題がメインになりそうです。そしてそこを中心にさらにDartの周辺ツールだったり、WebやServer Sideがさらに充実していく事を期待して Advent Calendar3日目を締めたいと思います。来週の担当日にはより技術的な事を書こうと思います。
DNS切り替えを伴う環境移行のハマりどころと気をつけポイント

## これは何
大きくサーバ群をリプレイスする時に考えるべきこと、詰まりどころをまとめておきたい。


## 事前リソース見積もりについて
###データ取得と方針

* 継続的にデータを取得しておく
  - ピーク時平均値とスパイクするポイントが重要
* 負荷について
  - 新環境は負荷をかけても耐えられるか。まずは旧環境と同様の負荷
  - 今後の伸びを考慮した負荷

### 特に気にすべき負荷
#####  ストレージ

* ディスクサイズ
  - 負荷とは少し違うかも
  - 今後の伸びを考慮した割当を行う。オンライン後から追加できるならある程度攻めても良い。補足：気軽に増やしたり出来ない物理サーバで5年でリプレイスするなら、5年後に70％になる程度にするとよいかもしれない
* IOPS
ランダムアクセスに対する指標である。ブロックサイズが大きいものについてはIOPSが思ったより出ないこともある。その場合はスループットが重要

##### ネットワーク

* 新旧環境をまたぐトラフィックが発生する場合には特に注意が必要
* レイテンシ
  - サービスでどの程度まで許容できるのか
  - DBなのかcacheなのかでも違う
* トラフィック量
  - LB、ルータ、スイッチ、ケーブルのキャパ
  - 特に環境間のルータがボトルネックになるし、影響範囲も大きい

### 疎通問題について

* 同じIPセグメントなら問題は起こりにくい
* 違うセグメントに新サーバを立てるなら既存サーバから/に疎通ができるか
  - L7レイヤまで確認するのが望ましい
* 必要に応じてルーティング設定をいれる
  - 細かく /24に対してのroutingなどを入れていっても良いが、セキュリティ的な問題がない or リスクが飲めるなら /8 でいれるのもあり

### LB・FWの設定について

* Global IPは割り当てられているか
* LB設定はすべてのドメインとパスに対して入っているか
* SSL証明書は設定されているか
  - 鍵と証明書は行方不明になりがち  
  - 鍵の置き場（秘匿情報置き場）をあらかじめ決めておくと良い
* IP直うちでアクセスした時にどういう動きをさせるのか決めておく
  - 503を返していいのか、何からのデフォルトサーバに到達させて200にするか、など
* 社内外からアクセスできるか
  - 特に社内だけ空いていて、社外からアクセスできないということがないか？
* 必要に応じてACL設定を行う、もしくは抜いておく
  - レイヤは複数あるので注意。FWアプライアンス、LBのACLなど、iptables, nginxなどのミドルウェア

###DNS設定切り替えについて
* ここまでは準備だが、DNS切り替えをした時が本当の切り替えタイミング。重要なので慎重に行う
* 他の関係者に切り替え時間と目的が分かるように周知する
  - 社内 
  - 社外関係サービス（あれば）
* 設定担当者と動作確認担当者の連絡手段を決めておく
  - いつでもいいのか、連絡が必要なのか
  - メンションなどが必要なのかどうか
  - 同期をとって行うか
* TTL設定
  - あらかじめ小さくしておく
  - 長いとなかなか反映しない
  - 問題が起こった時に切り戻しに時間がかかる
* 問題発生時
  - すぐに切り戻す
  - 根本原因究明はその後
ansible のダウンロード処理のリトライ（と既存コードの書き換えスクリプト）

# これは何
playbook の自動テストで各種パッケージダウンロードの処理がたまに落ちていた。コードの問題ではないところで落ちてjenkinsが赤くなると嫌なので出来るだけ通るようにしたい。
リトライ処理をいれて問題の緩和を図りたい。
微妙に詰まったのでメモを残しておく。

# 参考文献

* get_url のreturn valuesについて；http://docs.ansible.com/ansible/latest/get_url_module.html#return-values 
* untilのところ： http://docs.ansible.com/ansible/latest/playbooks_loops.html#do-until-loops 
* result|succeededのところ： http://docs.ansible.com/ansible/latest/playbooks_conditionals.html#the-when-statement


# 書いたもの
### コード

```
- name: download nginx source
  become: no
  get_url:
    url: "http://nginx.org/download/nginx-{{ NGINX_VERSION }}.tar.gz"
    dest: "/usr/local/src"
  register: download_result
  until: download_result|succeeded
  retries: 3
  delay: 5
```

### 解説的な

until, reties はセットで書く必要がある。untilの記述がなければretiesは1になる。
untilでの判定をするためにregisterで変数にタスク実行結果を入れておく。

# おまけ
### 既存コードの書き換えのためのスクリプト
こっちがメインかも。
沢山の場所を書き換えないといけないのでperlスクリプトを使った。
```
grep -r "get_url:" | awk -F':' '{print $1}' | sort | uniq | xargs perl -i -0pe 's/(get_url:(.*\n)+?)^\s*$/\1  register: download_result\n  until: download_result|succeeded\n  retries: 3\n  delay: 5\n/m'
```

### 以下スクリプト解説のようなメモ書きのような。  
基本的にyamlのブロックごとに改行を挟んでいるので、それを用いる。
オプション修飾子mは対象文字列が複数行である場合に使う。
以下のような構造のyamlになっていた場合に、`^\s*$`で空行までのマッチになる。
ここで`(.*\n)+?`に?をつけているのは最短一致のためで、これをつけないと最長一致になってしまって空行1までではなくて、いけるところまでいったところ（この場合は空行2）までマッチするので注意が必要。

```
- name: download nginx source
  become: no
  get_url:
    url: "http://nginx.org/download/nginx-{{ NGINX_VERSION }}.tar.gz"
    dest: "/usr/local/src" 
(空行1)
- name: hoge
  piyo: fuga
(空行2)
- name: hoge
  piyo: fuga
```

### 便利ツール紹介 
ところで、正規表現を最初からコマンドラインで書くのもありなのだけど、あまりにも辛いのでGUIツールを使うのがよい。以下のサイトが最高なのでみんな使うべき。
https://regex101.com/
社内Ansible galaxy（共用role）を作ろう

# これは何
自分でansible galaxy を作る。特に今回は公開のものではなくて、社内に限る共通roleを作る話。
実際に作ったroleは社内事情や秘匿情報を含むのでここには書きません。

# 背景
社内クラウドのリソースを管理するplaybookをある人が作った。その後それをコピーして他のサービスで同じコードを使った（以下繰り返し）。
何かミドルウェア仕様が変更になったり、設定を変えたい場合にすべてのサービスでメンテナンスコストが発生する。
この状態は最低だと思ったので、社内galaxyを作り共通roleにしようと考えた。
その時に考えたこと、ハマったことをここに記録しておきたい。

# 参考
- http://docs.ansible.com/ansible/latest/galaxy.html
- その他いろいろgalaxyのレポジトリ

# 作り方

* meta/main.yml を配置する
  - 内容は `ansible-galaxy init hoge` で適当なものを作ってコピーしてくるのが楽
* 使う
  - 上のmetaファイルだけ置いてしまえばもうansible-galaxy installで読み込める
  - `ansible-galaxy install git+http://github.fuga.co.jp/dw-ansible/piyo.git --roles-path ./ex_roles/`  
* 整理する
  - あくまでgalaxyはroleを共有するという感じなので、普通ansibleレポジトリにはinventoryやplaybookファイルが含まれていると思うが、それらを削除する
  - defaults/* ファイルへデフォルト設定を配置する
  
# defaults/ について

* どのようなサービスでも共通の設定はmain.ymlにおいておくと良い
* 必ず変えて使って欲しいという変数はあえてデフォルトを配置せず実行時にエラーで落ちるようにしたほうが安全かなと思った
* main.ymlは読み込まれるが他は特に指定しなければば読み込まれない。変えて欲しい値だけどテンプレートは欲しいよねというような値はtemplate-*.ymlみたいなのをおいて読み込み先のplaybookでコピーして書き換えてもらうのがよさそう

# その他

* 社内共通galaxyは、作るのはそこまで難しくないことがわかった
* むしろ広報や運用が難しい。使ってもらわなければroleは育っていかないし、破壊的変更をすると困るユーザがいるはずなので合意を取って前にすすめていくのは大変。後方互換を保ったまま育てていくのもカオスになりがちだし、これからそこの悩みが出てきそう
* とはいえ、作ればみんな喜ぶので積極的に共通パーツを作って展開していきたい
netscaler LBにおいてサーバを使わずにリダイレクトする

 
# これは何
netscaler LBにおいて特定FQDNへのアクセスをパスそのままで他のFQDNにリダイレクトする方法についてまとめる。
この資料は NetScaler version 11.0 で実験した動きを元に書いています。


# 参考資料
https://support.citrix.com/article/CTX120664
大体これを読めばできるが以下の問題があった（この資料を作るモティベーション）  

* LoadBalancing Virtual Server の設定のところがServiceと書いてあり誤植と思われる
* 設定箇所の階層が書いておらず多少読みづらい
* LoadBalancing Virtual ServerにGlobal IPを設定するパターンしか書かれていない

# 手順
### System設定でresponderの有効化
NetScaler -> System -> Settings -> Configure Acvanced Features  
responder にチェックを入れる。

### Responder Actionの設定をする

* Addする（既存のActionを選択してAddするとそれをテンプレートとして使うことになる。最初から作るなら選択を外してAddする）
* TypeはRedirectを選択
* Expression  
例1) http->httpsにリダイレクトするなら、スキーマのところだけhttpsと書いて後は同じ  

```
"https://" + HTTP.REQ.HOSTNAME.HTTP_URL_SAFE + HTTP.REQ.URL.PATH_AND_QUERY.HTTP_URL_SAFE
```
例2) 特定の別のドメインにリダイレクトする  

```
"http://piyo.jp" + HTTP.REQ.URL.PATH_AND_QUERY.HTTP_URL_SAFE
```
補足： L7で http://hoge.jp/fuga などで入ってきたLoadBalancing Virtual Server経由でこのactionで http://piyo.jp/fuga に飛ばすイメージ  
* Response Status Code を設定する  デフォルト 302

### Responder Policy の設定をする

* さっきのActionを設定する　
* Undefined-Result ActionをRESETにする
  * tcp handshake の rst に対応すると思われる。dropとするとクライアント側はそのまま待ち状態になってしまう。
* Expressionには HTTP.REQ.IS_VALID を設定（frequently Userd Expressionから選択すると良い）
  * 要するにこのpolicyでは特に何もせずに（validかチェックだけして）actionへ流すという意味か

### monitor 設定をする
localhost に pingをうつmonitor設定を作る  

* name: localhost_ping
* Type: PING 
* destination ip: 127.0.0.1  
   * アプライアンスそのものが生きている間はpingが通るので、実質的に常にヘルスチェックOKになる宛先  

### service を作る
上で作成したmonitorを使って常にserviceがUP状態になるようにする。あくまでダミーで、LoadBalancing Virtual Serverが常にUP状態になるようにするためのものの模様

* 上記serverを紐付ける
* ProtocolとPortはHTTPと80とでも適当に設定しておく
* 作成後、Monitoring設定で先程作成したmonitorを設定し、UP状態になることを確認する

### LoadBalancing Virtual Serverを作る

* IP Address： WebサイトのIPアドレスもしくはPrivate IP
   * Global IPで直接受ける場合にはここに設定
   * Content Switching Virtual Server通すならここはPrivate IPを指定する
* 作成後Responder Policyを設定

### Content Switching Virtual Server 設定
上記でLoadBalancing Virtual ServerにPrivate IPを設定した場合にはContent Switching Virtual Serverのpolicyに紐付ける
この手順はredirector特有というよりもContent Switching Virtual ServerからLoadBalancing Virtual Serverに流す手順と同じなので省略する
キャッシュサーバが遠くなるとレイテンシは上がるのか。pingとtcpdumpで計算してみた

# これは何
私の持っているシステムではフロント系サーバからmemcached サーバへ接続回数が結構ある。データセンタ内で今まではフロント系サーバもキャッシュサーバも同一セグメント、物理的にも近くにいた。とある事情でサーバをリプレイスしなくてはならず一時的にそれが違うセグメントで、物理的にも遠く、間にいくつかスイッチとルータが挟まるようになる。これにより新しいフロント系サーバから古いmemcachedサーバに接続するとレイテンシが問題になるかもしれない。ネットワーク的に遠くなると実際にアプリケーションの応答性能の視点で問題になるのか、いまと比べて実際は大した事ないのか判断するために考えたことのメモ。  
  

# 測定
### 測定方法
ping のtimeの100回の平均を取る。
pingのtimeは何か -> https://linuxjm.osdn.jp/html/netkit/man8/ping.8.html  

> 往復時間 (round-trip time) と消失パケットの統計が計算される。 

なので、単なる往復時間。  
  
TCPにおける遅延は？ TCPの場合はパケットごとにACKするので、送りたいもののデータサイズが大きく、パケットの個数が増えると全体としてはそれだけ１パケットあたりの遅延が蓄積されていくことになる。
なので、pingのRTTの増加はそのままレスポンスタイムの遅れに繋がると考えられる。

### 測定実施
2台のサーバが同一セグメントにある 旧frontサーバ(hoge-old-web) => 旧cacheサーバ(hoge-cache) 
 
```
[myuser@hoge-old-web ~]$  ping -c 100 xx.xx.xx.xx > ./tmp_to_hoge-cache_ping
[myuser@hoge-old-web ~]$ grep "time="  ./tmp_to_hoge-cache_ping > ./tmp_to_hoge-cache_latency
[myuser@hoge-old-web ~]$ awk '{print $(NF-1)}' tmp_to_hoge-cache_latency | awk -F'=' '{sum+=$2} END{print sum/NR}'
0.14284
```
0.14 ms くらい  

新frontサーバ(hoge-new-web) => 旧cacheサーバ(hoge-cache)  

```
[root@hoge-new-web ~]# ping -c 100 xx.xx.xx.xx > ./tmp_to_hoge-cache_ping
[root@hoge-new-web ~]# grep "time="  ./tmp_to_hoge-cache_ping > ./tmp_to_hoge-cache_latency
[root@hoge-new-web check_latency]# awk '{print $(NF-1)}' tmp_to_hoge-cache_latency | awk -F'=' '{sum+=$2} END{print sum/NR}'
0.16793
```
0.16 ms くらい

#### 差分
0.16793 - 0.14284 = 0.02509 ms
割合ていうと 
0.02509 / 0.14284  * 100 = 17％。
 
#### frontへアクセスした時にmemcachedはどれくらい叩かれている？
コードで呼ばれているcacheメソッドの回数も重要だが、遅延全体を考えるならパケットがどれくらい投げられているかが重要。なのでtcpdump でパケット数がどれくらい投げられているのか確認したい。  

``` 
myuser@hoge-dev-web [~ ] $ sudo tcpdump -i eth0 > ./tmp_tcpdump_to_memcache
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C7136 packets captured
7136 packets received by filter   // 全部でこれだけのパケットが1リクエストで通った
0 packets dropped by kernel
```
その中で memcachedにつなぎにいっているのは？    

```
myuser@hoge-dev-web [~ ] $ grep "xx.xx.xx.xx.11211 >" tmp_tcpdump_to_memcache -c
1579
```
memcachedから来ているパケットは1リクエスト1579回。（行きも同数。pingのtimeはRTTなので片方の道を通っている回数がわかれば良い。）
#### 最大で増えるレイテンシはどれくらい？
毎回のパケット 0.02509 ms * 1579 = 39.6 ms。ほとんど誤差。気にしなくてもよさそう。
 
# 結論
今回の件ではレイテンシはあまり気にしなくても良い。
Laravelで管理サイトを構築する方法

# 開発環境
- Laravel5.5
- Vagrant(Homestead)
- Windows
- bootstrap3

# 目的
- Laravelで管理ページを構築する方法の備忘録です

# 作業フロー
1. デザインのテンプレートをダウンロードします
    - 今回はbootstrap3で作られたテンプレートの「Startmin」を使用します
    - https://github.com/secondtruth/startmin

1. ダウンロードしてきたZipファイルを展開し、publicフォルダ内にコピペします
    - css、js、fontを移動します

1. Resources/viewsフォルダ内にAdminというディレクトリを作成し、bladeファイルを新規で作成します
    - 新規作成したブレードファイル内に1でDLしてきた「TEMPLATE.html」のHTMLを貼り付けます
    - jsやcssのパス指定を「{{ asset('css/font-awesome.min.css') }}」のようにassetヘルパー関数を使う形に修正します

1. 管理ページ専用のルートを作成するために、routesフォルダ内にadmin.phpファイルを新規で作成します

1. 管理ページ専用のControllerを作成するために、Controllersフォルダ内にAdminというフォルダを新規で作成します

1. 管理ページ専用のミドルウェアグループを作成するために、Kernel.php内の$middlewareGroups配列にadminを追加します

1. 管理ページ専用のURLが機能するために、RouteServiceProvider.php内にmapAdminRoutesメソッドを追加します

1. 管理ページ用のController、Bladeファイルを作成し、適時ルートも追加します

# 参考ソースコード
- routes/admin.php

```php

Route::get('users/','UserManagerController@index');
```

- Providers/RouteServiceProvider.php

```php
/**
 * アプリケーションの"admin"ルート定義
 *
 * 通常、これらのルートはステートレス
 *
 * @return void
 */
protected function mapAdminRoutes()
{
    Route::prefix('admin')
         ->middleware('admin')
         ->namespace($this->namespace . '\Admin')
         ->group(base_path('routes/admin.php'));
}
```

- Controllers/Admin/UserManagerController

```php
/**
 * Class UserManagerController
 *
 * @package App\Http\Controllers\Admin
 */
class UserManagerController extends Controller
{
    /**
     * ユーザー情報ページ（トップページ）
     *
     * @param Request $request
     * @return View
     */
    public function index(Request $request)
    {
        return view('Admin.Users.index');
    }
}
```

# 参考
- Laravel5.3でAPIを簡単に作ってみる
    - https://www.wantedly.com/companies/matchingood/post_articles/38978
- Laravelのアセットに関するTips
    - https://qiita.com/sakuraya/items/411dbc2e1e633928340e
- (5) BLADEでレイアウトを作る
    - [ララ帳](https://laravel10.wordpress.com/2015/02/19/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AElaravel-5-5-blade%E3%81%A7%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%82%92%E4%BD%9C%E3%82%8B/)
- Startmin
    - https://github.com/secondtruth/startmin
- 管理画面で使えそうな無料の Bootstrap テンプレート
    - https://qiita.com/shotets/items/50afa5a215c54abadb00
はじめてnodejsを使った。はじめてチャットリレーbotを作った話。

# これは何
nodejsを使ってslack botを作った話。
インフラエンジニアだけどコードもかけるようになりたい、といって少しだけ時間をもらってbotを作る仕事をやった。
その時のメモ的なもの。

# 何を使う
botkitというツールキットを使う。
hubotと何が違うの？　そういうのを調べたりはしてません。お手軽にモダンな感じで作れるよ、とおすすめされたので使った見た感じ。

# コード
コードは長くないのでここに貼ってしまう。  
`npm install --save botkit` とかして、package.json作らせた（と思う）。  
channel id はチャンネルがprivateの場合はhttps://api.slack.com/methods/groups.list/test で確認する。publicの場合は /channels.list/test で確認する。slackの場合。facebook messanger とかだとまた別の取得方法だと思う。  
token はbot のものを使用する。https://app.slack.com/apps/new/bot から作成したもの。custom botについてはこちらを参照のこと：https://api.slack.com/bot-users。  

```js
const Botkit = require('botkit');
const os = require('os');

// 色々変数チェック。他dest,originについてはここの説明では略
if (!process.env.token) {
  console.warn('Error: Specify token in environment');
  process.exit(1);
}

const controller = Botkit.slackbot({
  debug: true,
});

controller.spawn({
  token: process.env.token,
}).startRTM();

const destChannel = process.env.destChannel;
const originChannel = process.env.originChannel;

controller.hears('.*https?://www.example.com[sS]*', 'ambient', (bot, message) => {  // [sS]* は改行含む全一致。.*だと改行入らない。ambient はbotが全発言を拾うということ(direct_messageとかもある)。 
  // 指定のチャンネルのときのみ発言を拾う
  if (message.channel === originChannel) {
    const matchMessage = message.text;
    const userId = message.user;
    bot.api.users.info({ user: userId }, (userApiError, userApiRes) => {
      const userName = userApiRes.user.profile.real_name;
      bot.api.channels.info({ channel: message.channel }, (channelApiError, channelApiRes) => {
        // private channel はchannels apiからは情報取得できない。groups apiなら取れるが、privateチャンネル名は知られなくても良い
        const channelName = (channelApiError !== 'channel_not_found') ? channelApiRes.channel.name : 'どこかprivate channel';
        bot.say(
          {
            text: `${userName}さんが作品リンクを投稿しました(in #${channelName}) \n -------------------------- \n ${matchMessage} \n --------------------------`,
            channel: destChannel,
          });
      });
    });
  }
});
```
起動は

```js
originChannel='<channel_id1>' destChannel='<channel_id2>' token=<bot_token> node urlcopy_bot.js
```

# その他
eslint 使ってみた。
明らかな表記ブレは適当に直してくれるし、コードはきれいになるし、紛らわしい表記とか教えてくれるし、割と良かった。
他のところにもlint入れてみようかなと思った。

# 追記
slack接続が切れてしまって(close RTMになる) botが死んだように見える現象があった。
最初は自分で close_rtmイベントをフックしてconnectするように書いていたが、botkitはデフォルトで予期せぬ接続断があったらreconnectするようになっているため、両方で接続しに行ってしまい、多重接続（このbotの場合は２回投稿してしまう動き）。参考：https://github.com/howdyai/botkit/blob/bee9c5c70dcf3c024e71f2a10bdb2e425c421988/lib/Slackbot_worker.js#L230-L233  
また、その他のエラーでcloseRTMしたときにはretryEnabledがtrueならreconnectしてくれるっぽい。こちらの動作に任せることにした。参考： https://github.com/howdyai/botkit/blob/bee9c5c70dcf3c024e71f2a10bdb2e425c421988/lib/Slackbot_worker.js#L70-L86  
そこで、ドキュメントの通り、

```js
const controller = Botkit.slackbot({ retry: Infinity });
```
みたいにしたらちゃんと接続が切れてもreconnectしてくれるようになった。