OmniAICreator/Qiita-1.07M huggingface dataset:
PowerAutomateって結局何ができるの？ 

## はじめに

Power AutomateはMicrosoftが提供しているクラウドのRPAツールです。
人が行うPC操作をノンプログラミングで自動化できます。
本記事ではそのPower Automateで何ができるんだ？？という超初心者的疑問を解消していきます。


## 注意事項

本記事では、ある事情により、Desktop版ではなく、クラウド版を扱います。
また、Power Automateの実際のフローの作成には触れず、できることだけにフォーカスしています。
ご了承ください。

## 概要

Power Automateクラウド版では、Microsoft 365サービスだけでなく、様々なクラウドサービスのコネクタが提供されており、簡単に連携が可能になっています。
[Power Automateの公式ページ](https://flow.microsoft.com/ja-jp/connectors/?filter=&category=all)でコネクタ一覧を確認できます。
2021/11/11時点では573件のサービスのコネクタが利用可能なようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429691/954551c0-58a5-377f-7e5c-9e5274933329.png)

中には仕事でよく使う以下のサービスも含まれていました。

- Slack
- GitHub
- Outlook
- Microsoft Teams

また、そのほかにYoutubeやTwitterなどもコネクタが提供されています。仕事以外の場面でも使えそうですね。

## それぞれのサービスでできること

これだけのサービスが使えると、少し不安にもなってきます。
「実はそれぞれのサービスでできることってショボいのでは...？」と。
なので、先ほど挙げた仕事でよく使う4つのサービスについて、少し深掘りしてみます。

### Slackで可能な操作

Slackでは以下の6つの操作が可能なようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429691/a0c19ed1-0835-921c-f0a1-92cdabb982b7.png)

あまり多くありませんが、メッセージ投稿は可能なので、「ほかのサービスで受け取ったメッセージをSlackに流す」といった操作はできそうです。
しかし、「Slackのメッセージを他のサービスに送信する」みたいなことはできなさそうですね。まぁSlackbotでもメッセージ読み取りはできないので、そこは許容範囲でしょうか。

### GitHubで可能な操作

GitHubで可能な操作はSlackよりかなり多かったです。
そのため、よく使いそうな項目を何個か取り上げます。

- プルリクを作成、更新、取得する
- issueを作成する
- 自分にissueが割り当てられたときにアクションを実行する
- プルリクが作成されたときにアクションを実行する

こんなところでしょうか。
取得だけでなく、プルリクやissueの作成までできるので、たいていの操作は自動化できそうです。

### Outlookで可能な操作

載せきれないくらい大量の操作が可能でした。ここはMicrosoft 365サービスの一種である強みでしょうか。
よく使いそうな項目を取り上げます。

- メールの新規作成、返信、削除、取得
- イベントの新規作成、更新、削除、取得
- 連絡先の取得
- 新しくメールが届いたときにアクションを実行する
- イベントが追加、更新、削除されたときにアクションを実行する

Outlookでよく行う操作はすべて対応されていました。Outlookを介した自動化は何でもできるという認識で大丈夫でしょう。

### Microsoft Teamsで可能な操作

Outlookと同じく、Microsoft 365サービスの一種なので、Outlookと同様、ほぼすべての操作が可能です。
よく使いそうな項目を取り上げます。

- 会議、チーム、チャネル、チャットの作成
- チャットまたはチャネルへのメッセージ投稿
- チャネル内のメッセージへの応答
- チャネルに新しいメッセージが追加されたらアクションを実行する
- 新しいチームメンバーが追加されたらアクションを実行する

こちらもよく行う操作はすべて対応済みでした。よって、Teamsを介した自動化も何でもできるという認識で大丈夫でしょう。

## その他の機能

Power Automateでは、そのほかにも以下のような操作が可能です。

- 変数の作成、更新
- アクションの繰り返し
- 条件分岐
- フローのスケジュール実行(有償版のみ)

etc...

「毎日決まった動作を自動化する」という観点では、スケジュール実行が有償版でしかできないのは少し不便ですね...
しかし、変数や繰り返し、条件分岐が扱えるので、プログラムを書く人もプログラミング感覚でフロー作成が可能なのではないでしょうか。

## さいごに

本記事ではPower Automateでできることにフォーカスし、よく使うツールにおいて、どんな自動化が可能かを取り上げました。
しかし、私もPower Automate初心者なので、まだまだ紹介すべき内容があると自覚しています。
Power Automateには他にも大量の機能があり、そのすべてを紹介しようとするとキリがありません。
要するにキリがないくらい機能があるので、並大抵のPC操作は自動化できるはずです。

よって、どんなことが自動化できるかというアイデア次第なのです。
常日頃から行っている一定の作業を不便だと思う心が重要なのかもしれませんね。

この記事は先ほども述べた通り、まだまだ不足している点が大量にあります。
よって、私がPower Automateを使うにあたって気づいて点はどんどん更新していこうと思っています。
「Power Automateのこんな機能は取り上げておいたほうがいい！」というアドバイスがあれば、教えていただけますと助かります。
【CodePen】Bootstrap導入方法&日本語リファレンス

今後使用していくであろうBootstrapについて導入方法や日本語リファレンス等を記載するもの。


#Bootstrapとは

>Webデザイン界隈では言わずと知れた超有名CSSフレームワークです。
>開発元はみなさんも使っているTwitter社です。

https://getbootstrap.com/

#CodePenでのBootstrap導入方法

①CSSタブで「Bootstrap」を登録する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/e328be73-fd2a-6e7b-1768-429842e21569.jpeg" width=70%>

②JSタブで「Bootstrap」「JQuery」を登録する。
 「Vue.js」が登録されていることを確認する。
 「axios」等、その他使用したい機能を登録する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/cc7182fb-3265-5e0b-7ebf-dfbf511eb4af.jpeg" width=70%>

③保存する。
ここで保存しないと登録したい内容が保存されない。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122199/c286a334-4dbb-6a5c-7a2f-74f757e488fa.jpeg">

#Bootstrap日本語リファレンス
・[フォーム](https://getbootstrap.jp/docs/5.0/forms/overview/)
・[ボタン](https://getbootstrap.jp/docs/5.0/components/buttons/)
・[アラート](https://getbootstrap.jp/docs/5.0/components/alerts/)
・[カラー](https://getbootstrap.jp/docs/5.0/utilities/colors/)
・[Navs and tabs(ナブとタブ)](https://getbootstrap.jp/docs/5.0/components/navs-tabs/)
・[Images (イメージ)](https://getbootstrap.jp/docs/5.0/content/images/)
PHPにおけるstrictモードを使った厳密な型宣言

[zenn](https://zenn.dev/mo_ri_regen/articles/php-strict-mode)でも同じ記事を投稿しています📮
[zenn](https://zenn.dev/mo_ri_regen)ではフロントエンドのことも含め、いろいろ投稿しているのでぜひご覧ください👋

以前、zennでPHP の型について[記事](https://zenn.dev/mo_ri_regen/articles/php-about-types)を書きました。今回は型を強制できるというおはなしです。

## strict モードについて

strict モードを有効にすることで厳密な型宣言ができます。
strict モードが有効でないとき、エラーが出ずに実行できてしまうことがあります。[^1]
有効にするには declare 文 に strict_types=1 をセットします。

[^1]: '1'+2 といった「数値に変換できる文字列+数値」に対して、計算結果(この例では 3)が出力されます。

### 例

```php
<?php
// strictモードの有効化
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

// int(3) という結果が得られる
var_dump(sum(1, 2));

// 引数がintでないためエラーがでる
var_dump(sum(1.5, 2.5));
?>
```

### 注意

declare 文では下記のように変数や定数をセットできません

```php

<?php
const STRICT_VALUE = 1;
// これは無効
declare(strict_types=STRICT_VALUE);
?>
```

## strict モードを有効にする必要性

strict モードを有効にしないと、意図しない挙動を起こすことがあります。
たとえば、整数同士の足し算のみ行いたい関数に対して引数として小数を与えたときにエラーが出ず実行されてしまいます。

```php
<?php
function sum(int $a, int $b) {
    return $a + $b;
}

// int(3) という結果が得られる
var_dump(sum(1, 2));

// エラーが出ずにint(3) という結果が得られる
var_dump(sum(1.5, 2.5));
?>
```

## 参考

[declare](https://www.php.net/manual/ja/control-structures.declare.php)
[厳密な型付け](https://www.php.net/manual/ja/language.types.declarations.php#language.types.declarations.strict)
[Rails6] 5秒ごとにブラウザリロードをさせる + α

# 手順

1. 自動更新用のJavascriptを用意する
1. 自動更新したいviewで読み込む

これだけの記事です。

## やりかた: 自動更新用のJavascriptを用意する

以下のJavascriptを用意しておきます。

```js:app/javascript/packs/autoreload.js
const interval = setInterval('location.reload()', 5000)
$(document).on('turbolinks:before-cache turbolinks:before-render', () => clearTimeout(interval))
```

## やりかた: 自動更新したいviewで読み込む

```erb:app/views/**/*.erb.html
<%= javascript_pack_tag 'autoreload' %>
```

---

以上です。
とはいえ、「流石にreloadとか無いやろ...」という方には、以下の応用編も御覧ください。


# 応用編手順

1. controllerでhtml以外のformatに対応させる（ajaxリクエストに対応）
1. `*.html.erb`　の一部をパーシャルに切り出す
1. `*.js.erb` を用意
1. `autoreload.js` を書き換える


## やりかた: controllerでhtml以外のformatに対応させる（ajaxリクエストに対応）

```ruby:app/controllers/**/*_controller.rb
...
def index
  ...
  @records = Record.all
  respond_to do |format|
    format.js
    format.html
  end
end
...
```

## やりかた: `*.html.erb`　の一部をパーシャルに切り出す

```erb:app/views/**/index.html.erb
<div id="records">
  <%= render 'record' %>
</div>
```

```erb:app/views/**/_record.html.erb
<%= record.name %>
```

## やりかた: `*.js.erb` を用意

```erb:app/views/**/index.js.erb
$("#records").empty();
$("#records").append("<%= escape_javascript(render @records) %>");
```

## やりかた: `autoreload.js` を書き換える

```js:app/javascript/packs/autoreload.js
const milliseconds = 5000
const sync = () => {
  $.ajax({
    type: 'GET',
    url: $(location).attr('href'),
    dataType: 'script'
  })
}
const interval = setInterval(sync, milliseconds)
$(document).on('turbolinks:before-cache turbolinks:before-render', () => clearTimeout(interval))
```
[Unity][iOS] UnityCloudBuild を使って TestFlight にアップロードするお話。

#自分の環境
Unity 2019.4.31
Windows10
（Macでもあまり関係なく使えるはずだけど）

#前提条件
・iPhone/Androidアプリを開発している。
・UnityCloudBuildを使っている。
**・Build後ipaファイルを落として手動でAppstoreにあげている。**

#解決しなくてはいけない事
・出来上がったipaファイルの場所は？
・それをUnityloudbuildからiOS Devにアップロードする方法あるの？

→　ありました。

#実装

## 1.Bashファイルを以下の場所に用意

```Assets\Editor\post-build-push-ipa-appstore.bash
#!/bin/bash
echo "Uploading IPA to Appstore Connect..."
#Path is "/BUILD_PATH/<ORG_ID>.<PROJECT_ID>.<BUILD_TARGET_ID>/.build/last/<BUILD_TARGET_ID>/build.ipa"
path="$WORKSPACE/.build/last/$TARGET_NAME/build.ipa"
if xcrun altool --upload-app -f $path -u $ITUNES_USERNAME -p $ITUNES_PASSWORD -t ios; then
    echo "Upload IPA to Appstore Connect finished with success"
else
    echo "Upload IPA to Appstore Connect failed"
fi
```

## 2.UnityCloudbuild内で変数を設定
自分の場合　DevOps Configで開いている。
![2021-11-11_10h59_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182971/048f6c31-b071-14e3-0e3e-a6e7e9c75ed0.png)
EnvironmentVariavlesを開いて
![2021-11-11_10h50_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182971/988d9244-382e-9ae2-582c-6980dadb7643.png)
追加していく（必要なのは３つ）
![2021-11-11_11h26_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182971/41c81d88-b3d6-0646-eef0-c5c23d7dffb1.png)


※2 ここを見ながら作ったが
https://support.apple.com/en-gb/HT204397

https://appleid.apple.com/account/manage
ここに入って
![2021-11-11_11h42_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182971/e147c4c3-008e-019c-8613-be0d6180c059.png)


## 3.Advanced Optionsを設定
Post-Build Script Path　に　`Assets/Editor/post-build-push-ipa-appstore.bash` を設定
![2021-11-11_11h52_35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182971/446e1c62-af9f-7098-c614-be077e093838.png)

これでうまくいけばTestFlightにアップロードされます。

#4「輸出コンプライアンスの確認」チェックの回避]

ここを見ながら入れておくとチェックしに行かなくて便利です。
https://scrapbox.io/bexide-tech-blog/UnityCloudBuild%E3%81%A7TestFlight%E3%81%B8%E3%81%AE%E9%85%8D%E4%BF%A1%E3%82%92%E5%AE%8C%E5%85%A8%E8%87%AA%E5%8B%95%E5%8C%96%E3%81%99%E3%82%8B



#つぶやき
<Androidも似たようなことをしたいけど>
gradleと仲良くないとダメそう。なので簡単にできる方法知っている人いないかな・・



#参考文献
sshであるユーザだけが接続出来ない場合の一例 /etc/shadowに該当ユーザ行が存在しない

# 現象 あるユーザだけssh接続出来ない

サーバはLXDなubuntu

他のユーザだと接続可能なのにあるユーザだけ

```
Connection closed by ホスト
```

とか多段sshの場合

```
Connection closed by UNKNOWN port 65535
```

とかで接続出来ないとき。いろいろ原因はあるのですがとりあえず
`ssh -vvv リモートホスト`
してみましょう。
でも何か情報があるログが見つからなかったり。

# いろいろ確認のち

* 接続元の`~/.ssh/config`
* 接続先の`~/.ssh/配下`

とか確認、特に接続先の~/.sshのアクセス権限は基本`drwx------`、`-rw-------`です。

# 今回の原因 ユーザを中途半端に作成していた

引っ越し作業途中だったので`vipw`で`/etc/passwd`に該当ユーザ行を`手で追加`してました。
そして`/etc/shadow`に該当ユーザ行がありませんでした。

`vipw -s`して該当ユーザ行を追加して解消しました。

うっかりごとなのでメモしておきます。
RDPを使わずにリモートデスクトップを作ってみました

# 開発のきっかけ

とある理由で１、２ヶ月入院しないといけなくなりまして、ただ仕事は止められないし、病室からモバイルルータ＋VPNで会社のネットワークを入るの辛いなと考えてまして・・・[Guacamole](https://guacamole.apache.org/)とかあるなーと思いつつもサーバでトラフィック中継するの辛い。[Chrome Remote Desktop](https://remotedesktop.google.com/)なんていいんじゃないの？と思いつつも認証機能ついてないのもまずかろう、という事で開発してみました。

# システム構成
FreeRDPを使って頑張るという選択もありましたが、iPadやChromeBookでも使いたいので、Chrome Remote Desktopと同じWebRTCを使ってP2Pで実装することにしました。構成は以下のものです。

![system.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2269532/e439c7a0-33ef-5397-c9e3-265e449e857f.png)

1. リモートクライアント側のエージェントにはElectronを採用しました。リモートデスクトップの取得にはMediaStream、キーボードやマウスの入力にはDataStreamを使っています。
1. 管理サーバ系はEC2の上にdocker-swarmにて構築しました。ユーザ管理、デバイス管理情報はRDS内に格納してあります。
1. IDPにはkeycloakを使い管理画面へのアクセスにはoidc認証が必要となります。
1. STUN&TURNにはさくらインターネットさんのVPSを利用しました。

ざっくり構成はこんなところで、WebRTC利用時にSSOで認証できる形にしてあります。この構成であれば

1. 管理画面アクセス時にkeycloakにて2要素認証
1. シグナリングサーバ接続時にデバイス認証

が可能になるので当初の目的であった認証付きリモートデスクトップ(RDPを使わずに）が可能となります。

# 性能
コーディックはVP8でパフォーマンスは40fpsくらいで動作するものが完成しました。もう少しパフォーマンスチューニングすれば、更なる最適化が図れるかなと考えています。

# 商品化について
本システムにstripeで課金処理をつけて商品化しました。

https://www.g1tec.jp/vnode/

コロナ禍に限らず、私のように急にオフィスに出勤できなくなった人達にご活用いただければと考えております。
ブラウザだけでサクッと動きますので、簡単にお試しいただけます。
擬似クラス　only-of-typeを使ってみた

下記のような場合、

```HTML
<div>
  <p>子要素</p>
</div>

<div>
  <h1>タイトル</h1>
  <p>子要素</p>
</div>
```

１つ目のdiv(親要素)に入っているpにのみCSSを当てたい場合に
クラスをつけるのではなく、擬似クラスで指定したい。


その場合は

```CSS
p:only-of-type {
  font-weight: bold;
}
```

こうすると、1つ目のdiv(親要素)に入っているpだけ、CSSを当てられました。
初めて使ってみたので更新！
referencesを使用した外部キーに、あとからUnique属性を設定する

Postテーブルを作成し、userテーブルを外部参照にした。
しかし、あとからuser_idをユニークにしたくなった。

```xxxxxxxxxxxxxx_create_posts.rb
class CreatePosts < ActiveRecord::Migration[6.1]
  def change
    enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
    create_table :posts, id: :uuid do |t|
      t.references :user, type: :uuid, null: false, foreign_key: true
      t.timestamps
    end
  end
end
```

※ポスグレで、idをuuidにしています。

# あとからユニーク制約をつけたい


## 成功した例
indexを一旦削除してから、unique属性をつけたindexを追加する。

```xxxxxxxxxxxxxx_add_index_user_id_to_posts.rb
class AddIndexUserIdToPosts < ActiveRecord::Migration[6.1]
  def change
    remove_index :posts, :user_id
    add_index :posts, :user_id, unique: true
  end
end
```

## 失敗した例

```xxxxxxxxxxxxxx_add_index_user_id_to_posts.rb
class AddIndexUserIdToPosts < ActiveRecord::Migration[6.1]
  def change
    add_index :posts, :user_id, unique: true
  end
end
```

これだと、以下のように「もうindexがあるからダメだよ」と言われてしまう。

```
PG::DuplicateTable: ERROR:  relation "index_posts_on_user_id" already exists
```
Scala List : 一階メソッド

## リスト : 一階メソッド
関数をパラメータとして受け取らないメソッド

## Sample
```Scala
    val abcde = List("a", "b", "c", "d", "e") // List(a, b, c, d, e)
    val oneToFive = List(1, 2, 3, 4, 5) // List(1, 2, 3, 4, 5)
    val zeroArray = new Array[Int](10) // Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

    // :::
    val con = List(1, 2) ::: List(3, 4, 5)
    println(con) // List(1, 2, 3, 4, 5)

    // length
    val len = List(1, 2, 3).length
    println(len) // 3

    // last
    println(abcde.last) // e

    // init
    println(abcde.init) // List(a, b, c, d)

    // reverse
    println(abcde.reverse) // List(e, d, c, b, a)

    // drop
    println(abcde drop 2) // List(c, d, e)

    // take
    println(abcde take 2) // List(a, b)

    // splitAt
    println(abcde splitAt 2) // (List(a, b),List(c, d, e))

    // indices
    println(abcde.indices) // Range 0 until 5

    // flatten
    val flat = List(List(1, 2), List(3), List(), List(4, 5)).flatten
    println(flat) // List(1, 2, 3, 4, 5)

    // zip
    val ziped = oneToFive zip abcde
    println(ziped) // List((1,a), (2,b), (3,c), (4,d), (5,e))

    // unzip
    val unziped = ziped.unzip
    println(unziped) // (List(1, 2, 3, 4, 5),List(a, b, c, d, e))

    // toString
    println(abcde.toString) // List(a, b, c, d, e)

    // mkString
    println(abcde.mkString) // abcde

    // toArray
    val arr = zeroArray.toArray
    arr.foreach(print) // 0000000000
    println()

    // copyToArray
    oneToFive.copyToArray(zeroArray)
    zeroArray.foreach(print _) // 1234500000
    println()

    // iterator
    val ite = abcde.iterator
    println(ite.next()) // a
    println(ite.next()) // b
    println(ite.next()) // c
    println(ite.next()) // d
    println(ite.next()) // e
```

## GitHub
https://github.com/GL-Kageyama/Scala_List_FirstOrderMethods
PlantUML(プラントユーエムエル)を使ってみました。

クラウド上でダイアグラムを作成できます。
https://plantuml.com/ja/
ダイアグラムは、コードを書くと作成してくれます。
Excelで作成するより、かなり便利ですね。きれいに書けますし。

####コード
```
@startuml
left to right direction
actor XXXシステム
actor 管理者
actor データベース
rectangle API {
 XXXシステム--> (XXXを登録する)
}
rectangle 管理画面 {
 管理者 --> (ログインする)
 管理者 --> (XXXを参照する)
}
(XXXを登録する) -- データベース
(XXXを参照する) -- データベース
@enduml
```

※ユースケース図記述例
https://plantuml.com/ja/use-case-diagram


####作成されたもの
![SoWkIImgAStDuSf9JIjHACbNACfCpoXHICaiIaqkoSpFuqfCBialKeY8Y7ZSjFrnqyx7pMsFcnT0HP-lM_XyGjkBXbOe_t5p--FcFO-RzZzkdWbcxEG26PEObvwJgk0OuAbGpOLkagwkdO86M6pIyvcxN_PiUjmuyt5JjoPNBK8hr9xdKtQ_dBi8Q0R2NWM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350277/3046a162-d53c-7cc7-6b73-36656ea5e3a8.png)

####追記　教えていただいたのですが、Qiitaのコードブロックでもplantumlを書けました。
※変更内容が、プレビューに即時反映されます。
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350277/8238f713-1a9c-9128-060c-8fcf54cd032d.jpeg)
【AWSコスト削減の道 for 開発環境】EC2編 ※ スポットインスタンス変更編

# 【AWSコスト削減の道 for 開発環境】EC2編

## 前提

* 前回ページにて紹介しているTerraformを用いて環境を構築している
   * <a href="https://qiita.com/H_broth/items/3cbcfb7fa55b242acf36" target="_blank">Terraformを用いた開発環境構築について</a>

## 概要
* 開発環境用に使用しているEC2インスタンスをスポットインスタンスに変更をおこなう
* 使用する時間になったら自動的にスポットインスタンスを起動する
* 使用していない時間に関しては自動的にスポットインスタンスを停止する

## 背景
* 開発環境用に利用していたEC2インスタンスがオンデマンドインスタンスだったので、スポットインスタンスに比べ料金が高くなっていた

## 本記事内の利用技術について
* <a href="https://aws.amazon.com/jp/ec2/spot/?cards.sort-by=item.additionalFields.startDateTime&cards.sort-order=asc" target="_blank">EC2スポットインスタンス</a>
    * AWSの料金を安くするための必須リソース!!
    * 私たちの開発環境ではソースコードやDBはすべて外出しをしているので、  
    ソースコードのデプロイとインスタンス内に実行環境があれば問題ない状態となっている
* <a href="https://docs.aws.amazon.com/ja_jp/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html" target="_blank">EC2 Auto Scaling</a>
    * EC2インスタンスに対しての起動及び停止ハンドラ
    * EC2 Auto Scalingを用いて、EC2インスタンスを起動するときに**EC2スポットインスタンス**として起動させるために使用
    * EC2スポットインスタンスを起動する際に予め定義しているVPCや  
    ロードバランサーターゲットグループに自動機に紐づけてくれるので超便利!!
* <a href="https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-launch-templates.html" target="_blank">起動テンプレート</a>
    * EC2 Auto Scalingから呼び出されるEC2インスタンス構築の際の手順をテンプレート化している
    * 「ユーザーデータ」にはすごくお世話になっております・・・
* <a href="https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AMIs.html" target="_blank">AMI</a>
    * 特定時点のEC2インスタンスの状態を保存している
    * サーバ内のミドルウェアなど基本的に変更がない部分を設定後、  
    その状態をAMI化して起動テンプレートから呼び出すようにしている

## 利用料金の比較 ※ 1時間当たり

### 前提
* 開発環境のインスタンスタイプは**t3a.medium**を利用している
* 通信料金は変更がないため記載をしていない

### インスタンス料金
![ondemand_and_spot_price_comparison.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2209596/de9dc3ce-fc9f-5639-83bd-40930ba0715d.png)
**70%安い！！！！！ ※ リザーブドインスタンスに匹敵！！！**

### EC2 Auto Scaling

#### 対応前
使っていない

#### 対応後
EC2 Auto Scalingに対して料金は発生しない

### 起動テンプレート

#### 対応前
使っていない

#### 対応後
記事は発見できなかったが、利用料金を見る限り起動テンプレートに対しても料金は発生していない

### AMI ※ EBSを含める

#### 対応前
50G(AMI作成時点のストレージサイズ) * 1(1世代) * 0.05(1Gあたりの料金 ※USD) = $2.5

#### 対応後
50G(AMI作成時点のストレージサイズ) * 1(1世代) * 0.05(1Gあたりの料金 ※USD) = $2.5

# 結論
### 🤣🤣コスト削減対応をしてもEC2の料金分しか変動対象がないため、単純に利用料金が**70%**安くなった！！！🤣🤣

## おまけ

### 本記事の内容を実装した際のEC2スポットインスタンスのライフサイクルについて

#### 前提
* 画像にはEvent BridgeやLambdaを記載しているが、そちらのリソースについては別の記事で紹介します

![spot_instance_life_cycle.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2209596/2216672c-5959-4ba7-7a5a-a231fc35d028.png)
【Flutter】コールバックを自分なりにまとめました。

# はじめに

iOSメインのスマホアプリエンジニアです。
iOSエンジニア視点からFlutterのコールバックの利用タイミングなどを自分なりにまとめました。

Dartの文化に反していたり、好みや等があるかもしれませんがご了承ください。
その他ご意見ございましたらご指摘ください。

# VoidCallback

最もシンプルで`Flutter callback`と検索すると真っ先に引っかかるコールバック方法です。
その名の通り、「空っぽのコールバック」

https://api.flutter.dev/flutter/dart-ui/VoidCallback.html

### 宣言
VoidCallbackを受ける側の実装。

```
voidCallback = (){ 
   print("voidCallback");
};
```

### 呼び出し
VoidCallbackを呼び出す

```
voidCallback.call();

// 以下でも可能。
voidCallback();
```

### 利用用途
明示的に「このコールバックには引数がないですよー」と表したいときに利用する。
ただ、後述の`Function`で事足りるため、チーム内で統一するために使わないようにするのも良いと思いました。


#Function
コールバックを呼び出すタイミングで何かしらパラメータを渡したいことが多いと思います。
そのときに登場するのがこちらの`Function`になります。

https://api.flutter.dev/flutter/dart-core/Function-class.html

##引数なし

### 宣言
受ける側の実装。

``` 
// 引数なし。VoidCallbackと同じ。
Function function1;

// 受ける側の実装。
function1 = (){
  print("voidCallBack");
};
```

### 呼び出し
`Function`を呼び出す

```
function1();
```

##引数あり

### 宣言
受ける側の実装。

``` 
// 引数ありのコールバック
Function(String) function1;

// 受ける側の実装。
function1 = (args){
  print("function1 args:$args");
};
```

### 呼び出し
呼び出し側の実装。

```
function1("このパラメータが渡されます。");
```


##引数あり応用編

### 宣言
``` 
// 引数が複数のコールバック。
Function(int, String) function2;

function2 = (args1 , args2){
  print("function2 args1:$args1 args2:$args2");
};


// 引数にクラスを渡す。
class Hoge {
  final String name;
  final int age;
  Hoge({this.name, this.age});
}

Function(Hoge) function3;

function3 = (hoge){
  print("function3 name:${hoge.name} age:${hoge.age}");
};

```

### 呼び出し

```
// 引数が複数のコールバックを呼び出す。
function2(111 , "function2の引数が渡されます。");

// 引数がクラスのコールバックを呼び出す
function3(Hoge(name: "name" ,age: 20));
```

### 利用用途
iOSでいう`Block`や`Closures`に似ており、単一のコールバックを返すに適していると思います。
複数のコールバックを返したい場合に、メンバー変数等に宣言すると冗長になります。
Delegateみたいなこと出来ないかな。と探しましたところありました！

#mixinを使ったDelegateパターン

`mixin`を使った`Delegate`パターンを使って実装してみましたところ、iOSエンジニアから見ると非常に馴染み深い書き方になります。
今回は`StatefulWidget`に記載する例を記載しました。

### 宣言
```
// iOSで言うところのprotocolを宣言。
mixin HogeDelegate {
  void hoge();
  void hogeHoge1(String);
  void hogeHoge2(String args1, String args2);
  String hogehoge4();
}

// クラスにDelegateを作成。
class Hoge {
  HogeDelegate delegate;

  void run(){
    delegate.hoge();
  }
}

// Stateクラスに受け取れるように実装します。
// with HogeDelegate追加。
class _MyHomePageState extends State<MyHomePage> with HogeDelegate {

  // Hogeクラスを宣言。
  Hoge hogehoge;

  void hoge() {
    print("delegate hoge");
  }

  void hogeHoge1(String) {
    print("delegate hogeHoge1");
  }

  void hogeHoge2(String args1, String args2) {
    print("delegate hogeHoge2 args1$args1 $args2");
  }

  String hogehoge4(){
    return "hogehoge4を返却する。";
  }

  @override
  void initState() {
    super.initState();

　　 // initでクラスとdelegateを宣言しておく。
    hogehoge = Hoge();
    hogehoge.delegate = this;
  }
}
```

### 呼び出し
```
hogehoge.run();
```

#まとめ

個人的には以下のように使い分けてみようかと思います。

|概要  |iOSでは？|使いみち|
|---|---|---|
|VoidCallback  |iOSではない|明示的にパラメータ等を返さないと宣言したい時に利用する。|
|Function  |Block/Closures|単一のコールバックを返すに適している。複数のコールバックをFunctionで実装すると冗長になる可能性がある。|
|mixin  |Delegate  |TableViewDelegateのように、複数のコールバックを返却したいときに適している。|

その他のコールバックを見つけたら追記していきたいと思います。
One messageソーシャルアカウントの利用ロジック

# Twitter
## アクセストークンの取得方法
1. ユーザーがFireabaseの認証コンポーネントで、Twitterの認証を完了させる
2. 認証後にTwitterのアクセストークンとユーザー名を取得し、その情報をSpringBootのAPIに送信する
3. 送られてきたアクセストークンとユーザー名をDBに保存する
![One Message-twitterアクセストークン取得ロジック.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500157/f96729bc-f554-718e-72b3-4e0c544b3934.png)
Anaconda3をきれいにアンインストールする

### はじめに
Anaconda3で開発を行っていたのですが、下記のようなエラーが出て、importされているはずのBeautifulSoupやRequestsが使用できない事象が発生していました。
``` ModuleNotFoundError: No module named 'hoge' ```
そのため、今回はAnaconda3をきれいにアンインストールしようと思います。

### アンインストールする前に...
下記を確認します。
①Anacondaの場所を確認
``` $ env | grep CONDA ```

②Anaconda3のパスを確認
``` $ cat ~/.bash_profile ```

### アンインストールするパッケージをインストールする
``` $ conda install anaconda-clean ```

「anaconda-clean」とは
→AnacondaやPython関連ライブラリが作成したデータファイルを削除するパッケージ


### Anaconda3をアンインストールする
``` $ anaconda-clean ```
上記を実行します。
対象フォルダを削除して良いか聞かれるのでyを入力して削除します。

また、.anacondaを削除すると、バックアップが作成されます。
バックアップが不要な場合は、下記のコマンドで削除できます。
``` $ rm -fr ~/.anaconda_backup ```

### Anaconda3のディレクトリを削除する
上項①で確認したディレクトリを削除します。
``` $ rm -fr /anaconda3 ```

### パスを削除する
上項②で確認したパスを削除します。
.bash_profileを開いて対象の記述を削除します。

### 最後に
また、Anacondaのショートカットなどが残っている場合はそちらも削除します。
これできれいにAnaconda3をアンインストールできていると思いますのでpythonなどをインストールし直すことができます。

### リファレンス
https://docs.anaconda.com/anaconda/install/uninstall/
For loop の処理が全て終わってから次の処理をする


```swift
let dispatchGroup = DispatchGroup()

for work in works {
    dispatchGroup.enter()
    // Do your work
    dispatchGroup.leave()
}

dispatchGroup.notify(queue: .main){
    // ループ処理が全て完了したあと呼ばれる。
}
```

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
VCLの処理フロー順 (Lumen Edge)

###本項はLumen Edge Application DeliveryでのVarnishについての記述です
https://lumen-28.hubspotpagebuilder.com/lumen-technologies-japan/lumen-edge-cloud-app-delivery

![request.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264348/404f07b1-a13e-87d4-2ff0-6998da546299.png)
SVGの不要な空白をトリミングしたい

### はじめに
今回は下記のようなロゴの周りに空白が残っているSVGファイルをトリミングしていきます。
ツールはFigmaを使用します。https://www.figma.com
![スクリーンショット 2021-11-11 10.17.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219871/95d4589d-c824-f35d-e726-a06c26a5b648.png)


### さっそくトリミングする
①使用したいロゴの部分だけ範囲選択する
②右クリックから「Frame selection」を押下する
これでトリミングは終わりです。
![スクリーンショット 2021-11-11 10.18.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219871/c87004c1-ea8b-3548-32cc-58adde2afe43.png)



### トリミングした画像をSVGで出力する
①対象の画像(ロゴの部分)を選択して右メニューのExportでSVGを選択する
②「Export Frame名」ボタンを押下する
これでSVGファイルがダウンロードされます。
![スクリーンショット 2021-11-11 10.19.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219871/62148fe1-26d6-4ffa-30d9-34bfeac03da5.png)


### 確認すると...
ダウンロードしたファイルを確認すると、画像のように不要な空白がなくなっていることがわかります。
![スクリーンショット 2021-11-11 10.30.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219871/408f217b-4664-260a-3716-98cb7f2b8c52.png)
Varnishのリクエストアグリゲーション(Request CoalescingまたはRequest Collapsing) Lumen Edge

###本項はLumen Edge Application DeliveryでのVarnishについての記述です
https://lumen-28.hubspotpagebuilder.com/lumen-technologies-japan/lumen-edge-cloud-app-delivery


#リクエストのアグリゲーション(リクエストコラプシング/Request Collapsing)
リクエストコラプシング/Request Collapsingとは、同じオブジェクトに対する複数のリクエストを保留し1つのオリジンリクエストにまとめ、レスポンス結果を使用して保留中のすべてのリクエストに対して返答を行う機能です。

これにより、キャッシュ内の人気コンテンツオブジェクトの有効期限が切れた場合に、オリジンサーバーへの複数リクエストが殺到し、オリジン側のサーバリソースを消費するのを防ぎます。トラフィックが多いサービスの場合、リクエストコラプシング(Request Collapsing）を正しく使用すると、オリジンサーバーへのトラフィックが減少して負荷の軽減になります。

キャッシュミス(初回リクエスト及びキャッシュ期限切れ)の場合は、Varnishにてデフォルトでリクエストコラプシング(Request Collapsing）される可能性があります。

Varnish VCLでは、キャッシュ応答のために、vcl_fetchでreturn（pass）が見つかった場合、hit-for-passオブジェクトが作成され、キャッシュに挿入されます。この場合、その後のユーザからの複数リクエストは保留リストに一旦待ち状態とされて個別に処理されます。

https://info.varnish-software.com/blog/request-coalescing-and-other-reasons-to-use-varnish-as-an-origin-shield

これらの動きは以下になります：

|ステータス|Collapses機能の発動|詳細|VCL処理|
|:-:|:-:|:-:|:-:|
|Default miss| Y  |通常のキャッシュミスの場合でかつキャッシュ可能なコンテンツの場合、リクエストはコラプスされ、一番最初のユーザリクエストを待って、オリジンからのレスポンスを持って保留リクエストすべてに回答されます。(Cookie等を利用した特定ユーザセッションに対するプライベートコンテンツなど注意が必要です)| vcl_recv: lookup ➔ vcl_miss: fetch ➔ vcl_fetch: deliver ➔ *  |
|Uncacheable response   | Y  |とあるリクエストがキャッシュミスの場合で、オリジンからキャッシュしないコンテンツである回答があった場合、一旦コラプスされたリクエストでの一番最初のリクエストにのみ当該レスポンスを返却し、他の保留リクエストに於いても順に個別のリクエストとしてオリジン側に接続を行います。| vcl_recv: lookup ➔ vcl_miss: fetch ➔ vcl_fetch: deliver ➔ *  |
|Pass on response VCL  | Y  |VCL設定によりreturn(pass)透過バイパス設定がvcl_fetch内にキャッシュ可能なコンテンツに指定されている場合、一旦コラプスされたリクエストでの一番最初のリクエストにのみ当該レスポンスを返却し、hit-for-passオブジェクトと認識されたのちは、他のリクエストに於いて以降新たな保留キューは作成せず順に個別のリクエストとしてオリジン側に接続を行います→次項のHit for Passに続く| vcl_recv: lookup ➔ vcl_miss: fetch ➔ vcl_fetch: pass ➔ *  |
|Hit for pass| N  | とあるコンテンツに対するリクエストにて、当該コンテンツが既にhit-for-pass指定されていた場合他のリクエストに於いて以降新たな保留キューは作成せず順に個別のリクエストとしてオリジン側に接続を行います。| vcl_recv: lookup ➔ vcl_pass: fetch ➔ vcl_fetch: *  |
|Pass on request   |  N | vcl_recv内でreturn(pass)透過バイパス設定がされている場合は、当該コンテンツはキャッシュせずコラプシングも発動しません| vcl_recv: pass ➔ *  |
|req.hash_ignore_busy   | N  | vcl_recv内でreq.hash_ignore_busyをTrue設定にした場合コラプシングは動作しません。 | All  |

```ruby:VCL
  # Disable request collapsing
  set req.hash_ignore_busy = true;
```

参考:VCL処理フロー
https://qiita.com/yoshitaka_nakahara/items/316dd9ac221d6a859f66


###キャッシュしないコンテンツとは
下記の通りCache-Controlに**”Private”**と記述されているものだけが対象となる点要注意です。max-age:0やno-cacheはVarnishではキャッシュNGとみなしません。この場合でキャッシュさせたくない、若しくはコラプシングを発動させたくない場合は明示的にPASS設定をする等の対処が必要です。

```ruby:HTTPHeader
Cache-Control: private
```
.Net6 + Npgsql.EntityFrameworkCore.PostgreSQL(Ver6.0)にしたらハマったこと

# 問題
２０２１年１１月８日（日本時間では９日）に.Net6がローンチされたので、早速アプデ
EntityFrameworkCore(EFCore)を使っているので、Npgsql.EntityFrameworkCore.PostgreSQLもVer6.0に上げたら
Date型でエラーが発生でハマった(小一時間）

# 現象
PostgresのDate型のカラムをEFCoreで取得すると

```
System.InvalidCastException: Cannot write DateTime with Kind=Local to PostgreSQL type 'timestamp with time zone', only UTC is supported. Note that it's not possible to mix DateTimes with different Kinds in an array/range. See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.
```
なるエラーが突如発生

DotnetでのDateTime型とPostgresのDate型がアンマッチとのこと

# 解決
解決方法は二つありました
### LegacyTimestampBehaviorをTrueにする

```
    public class TestDbContext : DbContext
    {
        public TestDbContext(DbContextOptions<TestDbContext> options)
            : base(options)
        {
            AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);//<---この行を追加
        }
    }
```
この方法は.Net6よりも前と同じ動作にするようです。

### DateTime型を変える
.Net6(C# 10?)からサポートされたDayOnly型に変更しましょう。
これは日付のみの型でPostgresのDate型とピッタリですね。
日付検索するときも便利です。

[Npgsql](https://www.npgsql.org/doc/types/datetime.html)の元ネタです
override_settingsはクラス変数には効かない

# override_settings
djangoでテストをする際に[override_settings](https://docs.djangoproject.com/en/3.2/topics/testing/tools/#django.test.override_settings)を使うとsettings.pyの内容を一時的に変更することができます。
しかしコード内でクラス変数としてsettings.pyの値を使っていると効かない場合があります。

ユーザー登録をする際にメールで有効期限付きのアクティベーションリンクを送るようなシステムを例に、うまく行かなかった例を説明します。

# testコード
テストコードは以下のようにして、`ACTIVATION_TIMEOUT_SECONDS`という値がsettings.pyで運用時に使う値が設定されています。テストでは、１秒にして期限切れのリンクを踏んでちゃんとアクティベートされないか確認しています。

```python:tests.py
class MailActivationTests(TestCase):
    @override_settings(ACTIVATION_TIMEOUT_SECONDS=1)
    def test_create_expired_user(self):
        User = get_user_model()
        url = reverse('users:user_create')
        post_data = {
            'email':'expired@mail.com',
            'password1':'abcd1234',
            'password2':'abcd1234'}
        self.client.post(url, post_data)

        user = User.objects.get(email='expired@mail.com')
        self.assertFalse(user.is_active)

        url = get_activation_url()
        
        sleep(2)
        res = self.client.get(url, follow=True)
        user = User.objects.get(email='expired@mail.com')
        self.assertFalse(user.is_active)
```

# うまく行かなかった例

```python:views.py
from django.conf import settings
from django.core.signing import loads

class UserCreateComplete(generic.TemplateView):
    template_name = 'users/user_create_complete.html'
    timeout_sec = settings.ACTIVATION_TIMEOUT_SECONDS

    def get(self, request, **kwargs):
        token = kwargs.get('token')
        try:
            user_pk = loads(token, max_age=self.timeout_sec)
        ...
```

# うまく行った例
```python:views.py
from django.conf import settings
from django.core.signing import loads

class UserCreateComplete(generic.TemplateView):
    template_name = 'users/user_create_complete.html'

    def get(self, request, **kwargs):
        timeout_sec = settings.ACTIVATION_TIMEOUT_SECONDS
        token = kwargs.get('token')
        try:
            user_pk = loads(token, max_age=timeout_sec)
        ...
```
# クラス変数にするとoverrideされない
上記の例のようにクラス変数でsettings.pyの値を使っているとoverrideされないようです。メソッド内で値を取得するようにすると、上記のテストコードがパスされました！
簡易版 .NETのランタイムデバッグをする方法

先日.NET6が正式リリースされました。

ということで、良い機会なのでランタイムのコードを読んでみているのですが、やっぱり動かしながらどんな値が通っているのかを見ながらの方が捗ります。

以下の公式ドキュメントを参考にしています。

https://github.com/dotnet/runtime/blob/main/docs/workflow/debugging/coreclr/debugging.md

# ランタイムのソースコードをClone

```bat
git clone git@github.com:dotnet/runtime.git
cd runtime
git checkout v6.0.0
```

https://github.com/dotnet/runtime

# ランタイムのビルド

```bat
.\build.cmd -vs coreclr.sln -a x64 -c Debug
```

コマンド完了すると、VisualStudioが立ち上がります。
また、`.dotnet`というディレクトリが出来上がっており、中にランタイムのバイナリが入っています。

# .NET実行可能アプリケーションの作成

C#のConsoleAppプロジェクトのことです。

```bat
cd artifacts
mkdir MyApp
cd MyApp
..\..\.dotnet\dotnet.exe new console
```

`error IDE0073`回避のために、`Program.cs`にヘッダを追加します。
また、使用されているランタイムを確認するために、標準ライブラリのアセンブリのパスをダンプしておきます。

```cs:Program.cs
// <auto-generated />
Console.WriteLine(typeof(Console).Assembly.Location);
Console.WriteLine("Hello, World!");
```

ビルドします。

```bat
..\..\.dotnet\dotnet.exe build -p:Configuration=Debug
```

`artifacts\MyApp\bin\Debug\net6.0\MyApp.exe`が生成されます。

# VisualStudioのデバッグの設定

`build.cmd`によって`artifacts\obj\coreclr\windows.x64.Debug\ide\CoreCLR.sln`がVisaualStudioで開かれている状態のはずです。（閉じてしまったらslnを再度開いてください）

適当なデバッグ用のプロジェクトを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/57079479-c149-7081-d1fc-8bd9e339ac51.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/ffe59547-eae3-5951-169a-5315554b4281.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/8d88ec5d-7d83-0063-9c7f-c1996167b07c.png)

先ほど作成した.NETアプリでデバッグ設定をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/59186114-fcd9-a962-8b5b-e94cd4b5d4ed.png)

スタートアップに設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280975/ea2f6549-ff99-8f10-7688-b00afd7856f4.png)

実行

```console
Hello, World!
C:\Users\huser\Desktop\runtime\.dotnet\shared\Microsoft.NETCore.App\6.0.0-rc.1.21430.1\System.Console.dll

C:\Users\huser\Desktop\runtime\artifacts\MyApp\bin\Debug\net6.0\MyApp.exe (プロセス 24616) は、コード 0 で終了しました。
デバッグが停止したときに自動的にコンソールを閉じるには、[ツール] -> [オプション] -> [デバッグ] -> [デバッグの停止時に自 動的にコンソールを閉じる] を有効にします。
このウィンドウを閉じるには、任意のキーを押してください...
```

VisualStuido内での`MyApp`のビルドを避けているのは、システムインストールのSDKではなく、リポジトリ内のSDKを使ってC#プロジェクトのビルドを行いたいためです。

# エントリポイント 

`corehost.h`の`wmain`がエントリポイントになりますので、ソースコード上の好きなところにブレークポイントを置いてデバッグをすることができます。

```cpp
#if defined(_WIN32)
int __cdecl wmain(const int argc, const pal::char_t* argv[])
#else
int main(const int argc, const pal::char_t* argv[])
#endif
{
    ...
    int exit_code = exe_start(argc, argv);
    ...
    return exit_code;
}
```

https://github.com/dotnet/runtime/blob/v6.0.0/src/native/corehost/corehost.cpp#L278-L312
Pythonで処理したデータをLineで通知する

### 前提
こちらは前回の続きになります。
「スクレイピングしたニュースタイトルを形態素解析してwordCloudを作成する」
https://qiita.com/yu_ku/items/d3b1ebda57ab9543eef6

### 今回やること
前回までで下記のことを行いました。今回は①と②をLineNotifyを使用してLineで通知させたいと思います。
①スクレイピングしたニュースからURLとタイトルを取得
②①を形態素解析してwordcloudを作成

### アクセストークンを取得する
まずは下記からLINE Notify APIを登録して使えるようにします。
https://notify-bot.line.me/ja/

以下のページにわかりやすいやり方が書いてあります。
https://qiita.com/iitenkida7/items/576a8226ba6584864d95

### notify.pyを作成して、Line通知の処理を記述する
```
import requests
import settings

LINE_API_KEY = settings.LINE_NOTIFY
MESSAGE_SIZE_LIMIT = 1000

# スクレイピングしたタイトルとURLを通知する（テキスト）
def line_notify_message(message):
  line_notify_token = LINE_API_KEY # 発行したアクセストークン
  line_notify_api = 'https://notify-api.line.me/api/notify'
  payload = {'message': message}
  headers = {'Authorization': 'Bearer ' + line_notify_token}
  message_length = len(message)
  i = 0
  while message_length > 0:
      if i == 0:
          buf = message[:MESSAGE_SIZE_LIMIT]
      else:
          buf = message[MESSAGE_SIZE_LIMIT * i:MESSAGE_SIZE_LIMIT * (i+1)]
      payload = {"message" :  buf}
      i += 1
      message_length -= MESSAGE_SIZE_LIMIT

      r = requests.post(line_notify_api, headers = headers, params = payload)

      if r.status_code != 200 :
            pass # エラー処理

# 作成したwordCloudを通知する（画像）
def line_notify_file(file):
  line_notify_token = LINE_API_KEY　# 発行したアクセストークン
  line_notify_api = 'https://notify-api.line.me/api/notify'
  headers = {'Authorization': 'Bearer ' + line_notify_token}
  data = {'message': 'Morphological analysis'}
  files = {'imageFile': open(file,'rb')}
  result = requests.post(line_notify_api, headers = headers, data = data, files = files)
```

### main.pyでnotify.pyを呼び出す

```
import notify
# Line通知
notify.line_notify_message(cr_hatena[0])
notify.line_notify_message(cr_hacker_news[0])
notify.line_notify_file((wordcloud_path + str(time) + ".png"))
```

上記を実行すると指定した部屋にLineの通知が来ていると思います。
【JavaScript】反復処理④　MapとSet


#はじめに

Udemyの[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)の講座の振り返りです。

前回の記事

https://qiita.com/nao0725/items/c65ba57599239948cec0

#目的

* 反復処理についての理解を深める

#本題
###1.MapとSet

MapとSetとは

* データを入れておく入れ物のこと
* ES6から導入された
* **コレクション**と呼ばれることもある

####ObjectとMapの違い

|  |Object | Map |
|:-:|:-:|:-:|
|  キー | 文字列  |  制約なし（数値や関数もキーとして取れる） |
| for...in  | ◎  |  ✗ |
| for...of  |  ✗ | ◎  |


#####例1

使い方

オブジェクトを渡した場合

````js
// mapというオブジェクトを作成
const map = new Map();
// 値をセット 空のオブジェクトを渡す
const key1 = {};

// setメソッドを使って第一引数にキー情報,第二引数に値を渡す
map.set(key1,"value1")
// getメソッドを使用して値を渡すとキーを取得できる
console.log(map.get(key1));
// value1と出力される
`````

関数を渡した場合

```js
const map = new Map();
const key2 = function(){}
map.set(key2, "value2");
console.log(map.get(key2));
// value2と出力されるので,関数もキーとして取れることがわかる
`````

プリミティブ型で渡した場合

```js
const map = new Map();
let key3 = 0;
// 下記の場所でkey3 = 0;でもOK
map.set(key3, "value3")
console.log(map.get(key3));
// value3と出力される
````

注意：キーの値の参照を保持している

```js
const map = new Map();
const key1 = {};
map.set(key1,"value1")
//undefinedと出力される
console.log(map.get({}));
````

#####例2

キー情報を削除したい場合

```js
const map = new Map();
const key1 = {};
map.set(key1,"value1")
// 下記のdeleteメソッドを使うと情報を削除できる
map.delete(key1);
// undefinedと出力される
console.log(map.get(key1));
`````

#####例3

for...ofで値を取得した場合

````js
const map = new Map();
const key1 = {};
map.set(key1,"value1")

for(const m of map){
  // 配列として値が渡ってくる
  console.log(m);
} 
`````

```実行結果.
(2) [{…}, 'value1']
0: {}
1: "value1"
length: 2
````

分割代入を使用して値を渡すと

```js
const map = new Map();
const key1 = {};
map.set(key1,"value1")
// keyとvalueの値をそれぞれ渡す
for(const [k,v] of map){
  // {}[[Prototype]]: Object 'value1'と出力される
  console.log(k,v);
} 

for(const a in map){
  // for...inでは出力されない
  console.log(a);
}
````

####ArrayとSetの違い

|  |Array | 　Set　|
|:-:|:-:|:-:|
|  重複値 | ◎  |  ✗ |
| for...in  | ◎  |  ✗ |
| for...of  |  ✗ | ◎  |

#####例1

setが重複で表示されないか確認

````js
// setをインスタンス化
const set = new Set();
const key1 = {};
// addメソッドで値を取得
set.add(key1)
set.add(key1)

for(const k of set){
  // このようにしても{}が１つしか出力されない
  console.log(k);
}
`````

#####例2

その他のsetの動作確認

````js
// setをインスタンス化
const set = new Set();
const key1 = {};
set.add(key1)
// キーの値が削除され、何も出力されない
set.delete(key1)
// 値が含まれているか確認する(hasメソッド)
console.log(set.has(key1));
// deleteしているのでfalseが返ってくる
// deleteをコメントアウトするとtrueと返ってくる

for(const k of set){
  console.log(k);
}
`````



#####例3

配列の取得

配列の添字とは

https://wa3.i-3-i.info/word11927.html


````js
// setをインスタンス化
const set = new Set();
const key1 = {};
set.add(key1)

// setは配列の添字を指定して値を取得できない
// よって以下の通りに取得する
// Array.fromというメソッドを使用する
const array = Array.from(set);
// 下記のスプレット構文でも可
// const array = [...set];
console.log(array);
// 添字を指定して出力される（0:{}）
````


今日はここまで！

#参考にさせて頂いた記事

* [【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)
* [Let'sプログラミング JavaScript入門](https://www.javadrive.jp/javascript/)
Arduino IDEでRaspberry Pi Picoを動かす　I2Cバス

　前回、Arduino IDEでRaspberry Pi Picoが使えるようになりました。とりあえず、I2Cバスを使います。
###I2Cscanner
　C:\Users\ユーザ名\AppData\Local\Arduino15\packages\arduino\hardware\mbed_rp2040\2.5.2\variants\RASPBERRY_PI_PICO
の中に、pins_arduino.hがあります。I2Cは、

```C
// Wire
#define PIN_WIRE_SDA        (6u)
#define PIN_WIRE_SCL        (7u)
```

と書かれているので、GPIO6にSDA、GPIO7にSCLをつなぎます。手元にあったAdafruit STEMMA QTのLPS25HBボードをつなぎ、I2Cscannerを動かしました。

![名称未設定-15909baa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/aa54cf27-1b1d-add3-a634-46a33fd16601.png)

　正しく0x5dを見つけました。

![2021-11-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/2e553795-266b-4c4e-faec-b3a6edf85d12.png)

###気圧センサLPS25HB

　おもなスペックです。

　　[データシート](https://www.st.com/resource/en/datasheet/lps25hb.pdf)

- 動作電圧　3.3V（デバイスは1.7～3.6 V）
- 測定範囲　260～1260hPa
- 確度　±0.1hPa（25℃）、±1hPa（0～80℃）
- インターフェース　SPI、I2C。I2C用プルアップ抵抗は実装
- スレーブ・アドレス　0x5d。SDOピンの設定で0x5cも設定できる

###スケッチを書く
　気圧のデータは、3バイト24ビットです。PRESS_OUT_H (2Ah)、 PRESS_OUT_L (29h)、PRESS_OUT_XL (28h)を読み出し、4096で割るとhPaの値になります。

　1Hzの繰り返しで気圧データを読み出します。電源が入ったのち、パワーダウン・モードになっているので、測定できるモードに変更します。この二つの設定は、「CTRL_REG1」（0x20）に0x90を書き込むことで実現します。

　気圧データの低いアドレス28hから3バイトを読み出しますが、0x28 | 0x80の形で読まないと、正しいデータが読めません。

```C
#include <Wire.h>

#define LPS25HB_address 0x5d
 
uint8_t readbuffer[3];
float press;
 
void setup() {
  Wire.begin();
  Serial.begin(9600);
  Serial.println("start");
  Wire.beginTransmission(LPS25HB_address);
    Wire.write(0x20);Wire.write(0x90);
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(LPS25HB_address);
    Wire.write(0x28 | 0x80);
  Wire.endTransmission();
  Wire.requestFrom(LPS25HB_address, 3);
  readbuffer[0] = Wire.read();
  readbuffer[1] = Wire.read();
  readbuffer[2] = Wire.read();
  press = ((uint32_t)readbuffer[2] << 16 | (uint16_t)readbuffer[1] << 8 | readbuffer[0]) / 4096.0;
  Serial.print("\nPress ");  Serial.print(press);  Serial.println("hPa ");
  delay(2000);   
}
```
スクレイピングしたニュースタイトルを形態素解析してwordCloudを作成する

### 前提
こちらは前回の続きになります。
「RequestsとBeautifulSoupを使用してニュースをスクレイピングし、CSV出力する」
https://qiita.com/yu_ku/items/c9480c1b6c2e804cb104


### 形態素解析とは...
形態素解析(Morphological Analysis)
自然言語処理分野で主に事前処理として用いられる手法で、対象となる言語の文法や単語の情報をもとに、文章を形態素(単語が意味を持つ最小の単位)に分解する解析方法。


### 下記をインストールする
pip install mecab-python3
pip install wordcloud
pip install DateTime

### main.pyでスクレイピングしたデータを形態素解析後にカウントし、CSVファイルに出力する
```
import MeCab
from wordcloud import WordCloud
import crawler
import datetime as dt

now = dt.datetime.now()
time = now.strftime('%Y%m%d')
wordFreq_dic = {}
wordcount_output = []

wordcount_path = "wordcount/wordcount_dic_"
wordcloud_path = "wordcloud/wordcloud_"

#　解析テキストの準備
cr_data = cr_hatena[1] +  cr_hacker_news[1]
text = ''.join(cr_data)

#　単語頻出度カウントする
def WordFrequencyCount(word):
    if word in wordFreq_dic:
        wordFreq_dic[word] +=1

    else:
        wordFreq_dic.setdefault(word, 1)
    return wordFreq_dic

#　特定の品詞の単語を抽出(形態素解析)
mecab = MeCab.Tagger()
mecab.parse('')
node = mecab.parseToNode(text)

while node:
    if node.feature.split(",")[0] == "名詞":
        word = node.surface
        WordFrequencyCount(word)
    elif node.feature.split(",")[0] =="動詞":
        word = node.surface
        WordFrequencyCount(word)
    elif node.feature.split(",")[0] == "形容詞":
        word = node.surface
        WordFrequencyCount(word)
    elif node.feature.split(",")[0] == "形容動詞":
        word = node.surface
        WordFrequencyCount(word)
    else:pass
    node = node.next

#　辞書リストを取り出し、降順に並び替える
for item in wordFreq_dic.items():
    wordcount_output.append(item)
wordcount_output = sorted(wordcount_output, key = lambda x:x[1], reverse=True)

# 単語のカウント数をCSV出力する
with open(wordcount_path + str(time) + ".csv", "w", encoding="utf-8") as f:
    writer = csv.writer(f, lineterminator="\n")
    writer.writerow(['Word', 'Count'])
    writer.writerows(wordcount_output)
```

### 単語頻出度をまとめたCSVからwordCloudを作成する
```
# CSVファイルを読み込み加工
text = open(wordcount_path + str(time) + ".csv", encoding="utf8").read()
splitted_data = " ".join([x.split("\t")[0] for x in mecab.parse(text).splitlines()[:-1] if x.split("\t")[1].split(",")[0] not in ["助詞", "助動詞"]])

# WordCloudを作成してpngファイルで保存する
wordcloud = WordCloud(background_color="white", width=1000, height=600, font_path="NotoSansJP-Regular.otf")
wordcloud.generate(splitted_data)
wordcloud.to_file(wordcloud_path + str(time) + ".png")
```


上記のように実装すると下記のように頻出度が高い順に大きい単語が表示されます。
![wordcloud_20211003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219871/f29761f8-4388-d569-9cb2-c1f6531b623e.png)
古いDockerイメージのコンテナでSSL通信を復活させた話

「[古いMastodonを今更アップデートした話](https://qiita.com/AkiraTouno/items/ea2cd8e437db83301654)」の子記事です。


ここで解決したもののうち**「古いDockerイメージのコンテナからlet's encryptのSSL証明書を持つページにアクセスできない問題」**に焦点を絞って記載しています。
備忘録です。


#問題
古いDockerイメージを使ってコンテナから**let's encryptのSSL証明書を持つページ**にアクセスすると、何故か「証明書が信頼できない」旨のエラーが出てしまう。
証明書チェーンを作り直してもだめ。

```
ssl_client: ****: certificate verification failed: certificate has expired
```

他の機関から発行された証明書のページには正常にSSL通信が可能でした。

#環境
Docker環境のMastodonでの話です。

* Docker
    * ruby:2.5.0-alpine3.7

#原因
**「let's encryptルート証明書期限切れ」**と**「Openssl1.0.2の仕様」**の2つが合わさって起こった問題です。
これにより、問題のあったコンテナではいくら正しい適切な証明書であろうともlet's encryptの証明書だけ信頼しないという状態となっていました。


* **let's encrypt ルート証明書期限切れ**

2021/9/30をもって、それまでlet's encryptで使用されていたルート証明書"DST Root CA X3"が期限切れとなりました。それ以降はISRGというものが使用されています。
この「let's encryptが失効したルート証明書を持つ」ことが原因のひとつです。

* **Openssl1.0.2の仕様**

今回問題の起きた環境ではOpenssl1.0.2を使い、アクセスしたサイトの持つ証明書が信頼できるかどうか判断しています。

Openssl1.0.2の仕様ではサイトの持つ証明書が"信頼できるルート証明書"と"信頼できないルート証明書"2つに紐づけられている場合、"信頼できないルート証明書"を優先します。
つまり"信頼できないルート証明書"を持つものは絶対に信頼しないということです。
これがふたつめの原因です。

Openssl2.0以降にはこの仕様は無く、信頼できるルート証明書があれば信頼してくれます。

#対応策

Openssl1.0.2のままで内部のCA証明書を入れ替えることで対策しました。

コンテナ作成時にもともと持っているCA証明書は期限切れの"信頼できないルート証明書"に紐づいてしまっているため、これを削除して最新のCA証明書に入れ替えました。
またもともと持っている"DST Root CA X3"に関するファイルも削除して、念のためISRGの証明書を同じ場所に保存しておきました。

具体的には下記内容をDockerfileに記載することで上手くいきました。
mastodonで動かす際にはCOPY処理が書かれている部分の前後に書いてました。
なんか一部不要な記述がありそうですが、とりあえずこれで動きました。

```
RUN apk -U upgrade
RUN rm -f /etc/ssl/certs/2e5ac55d.0
RUN rm -f /etc/ssl/certs/790a7190.0
RUN rm -f /etc/ssl/certs/ca-cert-DST_ACES_CA_X6.pem
RUN rm -f /etc/ssl/certs/ca-cert-DST_Root_CA_X3.pem
RUN rm -f /usr/share/ca-certificates/mozilla/DST_ACES_CA_X6.crt
RUN rm -f /usr/share/ca-certificates/mozilla/DST_Root_CA_X3.crt
COPY isrgrootx1.pem /usr/local/share/ca-certificates/isrgrootx1.pem
COPY cacert-2021-09-30.pem /etc/ssl/cert.pem
RUN update-ca-certificates
```

* ISRG証明書・CA証明書ファイルのコピー
mastodonのフォルダ(Dockerfileなどと同じフォルダ)に「isrgrootx1.pem」「cacert-2021-09-30.pem」を用意します。
 * isrgrootx1.pem : [このファイル](https://letsencrypt.org/certs/isrgrootx1.pem.txt)を保存して名前と拡張子を書き換えました。
 * cacert-2021-09-30.pem : [ここ](https://curl.se/docs/caextract.html)から適宜最新のものを選んできました。Mozilaさんのところから使わせてもらいました。


#補足
コンテナ内で"DST Root CA X3"関連のファイルを削除する際、はじめはそのパスが分からず苦労してました
(1)Opensslのトラストストアのパスを確認して、(2)その中で"DST Root CA X3"関連を探すという手順が必要だったんですが、結局Dockerfileに

```
RUN cat /usr/lib/ssl/openssl.cnf

RUN ls /etc/ssl/certs/
```

みたいなの書いて、失敗前提でdocker buildして確認したりしてました。
泥臭いというか頭悪い。
もっとスマートなやり方があると思うんですが、手軽さに負けました。

#まとめ
let's encryptの証明書を持つサイトだけSSL通信できなかったのは、「ルート証明書期限切れ」と「Openssl1.0.2の"信頼しないルート証明書を優先する"という仕様」が原因。
Opensslトラストストアから信頼されないルート証明書関連の情報をすべて消してやることで解決。


#おわりに
CA証明書とかトラストストアとか言葉が正しい意味通りに使えているのか自信がない・・・
今回Mozilaさんから持ってきたcacertって、ほんとにここでいう「CA証明書」で合ってるのか？
中間CA証明書ってopensslトラストストア内でいうとどのファイルなんだ・・・？
理解が足りてないから知らべてまとめます。
vagrant

前提としてvirtualboxがインストールされているものとする。
vagrantのインストールはここから。https://www.vagrantup.com/downloads

####vagrantにbox(仮想マシンのテンプレート)を追加
https://app.vagrantup.com/boxes/search
から検索

```
vagrant box add ubuntu/hirsute64
```

####vagrantfileの作成
-m(--minimal)なしだとコメントが表示される。

```
vagrant init -m
```
以降のコマンドは`vagrant global-status`を除いてvagrantfileがあるディレクトリからコマンドをうつ。

Vagrantfileを見てみると以下のようになっているので`config.vm.box`を先程addした`ubuntu/hirsute64`に指定する。

```
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "base"
end

```
`config.vm.box`を変更

```
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/hirsute64"
end

```



####VMの起動

```
vagrant up
```
Oracle VM VirtualBox Managerを確認するとrunningになっているのが確認できる。
  
####VMのステータス確認

```
vagrant status
```
####すべてのVMのステータス表示
このコマンドはvagrantfileがあるdirectoryからでなくてもよい。　　

```
vagrant global-status
```

####VMのshut-down

```
vagrant halt
```
####VMのshut-down,削除(VM内での変更は保存されない）

```
vagrant destroy
```


##実践

boxのみinitコマンドから指定することができる。

```
vagrant init -m ubuntu/hirsute64
```
Vagrantfileができるので見てみる。

```
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/hirsute64"
end
```
ネットワークの設定を追加する。
**注意点としてvirtualboxのfile=>host-network-managerで指定されているネットワークの範囲内にipを指定しなければエラーになった。**
私の環境では`vboxnet0 192.168.56.1/24`に設定されていたのでipを`"192.168.56.10"`に設定。

```
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/hirsute64"
  config.vm.network "private_network", ip: "192.168.56.10"
end
```
ssh接続

```
vagrant ssh
```
VM内ではユーザーはvagrant,パスワードもvagrantにデフォルトでなっている。
`sudo su`でルートユーザーになれる。
【狂気再び】SNSを通して大好きなあの人を温かく見守るアプリ　 #Facebook #Twitter #Bootstrap ＃CSS

##完成品##
<font color="Red">**大好きなあの人を温かく見守るアプリ「お気に入りトレース」がバージョンアップ！**</font>

https://okiniiritracekyoukinosata2.netlify.app/

<font color="Red">**Twitterに加えてFacebookのTLも見守れるようになりました。笑**</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122173/5d2e6753-a43f-a711-ee5d-cc2fc3797a88.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122173/4f9902cf-a725-fe35-f40c-a9fa737ae4c9.png)




<font color="Red">**Twitterで息してなくてもFacebookで息してるかもよ！**</font>


##企画（と弁明）※ほぼ再掲##
はじめに、<font color="Red">**ネットに落ちている「お気に入り」の最新情報を集約して（できれば１ページで）表示できるアプリを作りたい**</font>と考えました。

みんな好きな芸能人、ゲーム、漫画、アニメ、色々あると思うのですが、そういった<font color="Red">**「お気に入り」の情報を、あちこちに取りに行かなくても、勝手に手元に流れ込んでくるアプリ**</font>が最終的な目標です。

とはいえ、いきなりそんな素敵なものは作れないので、今回は比較的難易度の低いTwitterの情報取得から着手しました。

どうせなら今役立つものになればと思って仕様を改造しているうちに、知人をストーキングするアプリになってた。<font color="blue">**翌日調子に乗ってFacebookも追加してた。**</font>そういう話です。:poop:

##ベース##

前回の自分の記事をベースにしてます。
最初から追いたい方はリンク先をご確認ください。

https://qiita.com/TAKA_xedge/items/c1dc451b27b2e340ea28

##修正手順##
①バックグラウンド色とフォントを変更。(CSSに「/*------bg collor------*/」以下を追加)
②３タブ目をFacebook用の枠に変更
③特定IDのFacebookTLを表示するよう変更
　こちらを使用➡https://developers.facebook.com/docs/plugins/page-plugin?locale=ja_JP
④縦の長さを少し調整(変え方は↑のリンク先に詳しく書いてくれています。超丁寧！)
⑤雪を降らせてみました。(CSSに「/*------snow effect------*/」以下を追加。①があんま意味なくなった。)


##ソース##

プレビューで見る場合は大きいので0.25倍設定でお願いします。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="QWMBwbO" data-user="taka_xedge" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/taka_xedge/pen/QWMBwbO">
  お気に入りトレース2.0</a> by TAKA (<a href="https://codepen.io/taka_xedge">@taka_xedge</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

以下の●●●●●●の部分を任意のIDに変更すれば「大好きなあの人を温かく見守るアプリ」の出来上がりです。。

```html
<div class="col-sm-5 mx-auto">  
<div id="fb-root"></div>
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v12.0" nonce="bpHQ0CoU"></script>
<div class="fb-page" data-href="https://www.facebook.com/●●●●●●" data-tabs="timeline" data-width="250" data-height="8000" data-small-header="true" data-adapt-container-width="true" data-hide-cover="false" data-show-facepile="false"><blockquote cite="https://www.facebook.com/●●●●●●" class="fb-xfbml-parse-ignore"><a href="https://www.facebook.com/●●●●●●">●●●●●●</a></blockquote></div> 
</div></div>
```

##反省点##
特になし。
ですが、<font color="Red">**ルール的なところはちょっと調べました。Facebookは個人はダメかな</font>**って感じですね。
なのでインスタも少し難しそう？
今後もそこらへんは気にしながらやろうと思います。