OmniAICreator/Qiita-1.07M huggingface dataset:
ラテラルフィッシング とは

## 勉強前イメージ

フィッシングの一種？

## 調査

### ラテラルフィッシング 

Lateral Phishing と書き、社会的信頼の高い組織で利用されているメールアドレスなどを乗っ取って悪用していく攻撃手法になります。
ラテラルとは横方向を意味しており、サイバー攻撃で不正侵入後感染を拡大するラテラルムーブメントとも近い意味になります。
ラテラルフィッシングは乗っ取り後、正規のアカウントからフィッシングメールを送るので一般的に検知が困難になります。

以前の被害例で言えばMicrosoft365のアカウントの情報を狙ったものがありました。
企業アカウントを乗っ取ることで共有ファイルなど企業の機密情報を取得することが出来ます。

### ラテラルフィッシングの対策

- 従来の考えは捨てる

ラテラルフィッシングでは怪しいドメインからメールが来たり、怪しいURLが来たときにクリックしないというだけでは防げません。

- セキュリティソフトを使用する

複数の種類のセキュリティソフトを導入する必要があります。

- 多要素認証を導入

多くのクラウドサービスでは多要素認証が利用できます。
ログインパスワード以外の認証が必要にしておきましょう

## 勉強後イメージ

ざわざわ大企業とかを狙ってる攻撃手法もあるってことか

## 参考

- [テレワークだからこそ知っておきたいラテラルフィッシング 狙われるMicrosoft 365](https://www.daj.jp/security_reports/200514_1/)
- [テレワークで急増するラテラルフィッシングとは？](https://www.ines-solutions.com/industry/a281)
さくらVPSのubuntuにDocker入れてWordPress使うまで

# 用意した環境
- sakura VPS 1G
- ubuntu 20系

# Docker入れる
- こちらの記事を参照

https://qiita.com/shimano_equipped/items/80d83ec485d83e18d142

# DockerにWordPress入れる
ふつーに入れるとコンテナ落としたときぜーんぶ消えちゃうから永続化とやら。
## 参考
https://keruuweb.com/docker-wordpress%E3%82%92%E6%B0%B8%E7%B6%9A%E5%8C%96%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/
## install log
### 必要なフォルダ作っておく
```
mkdir /var/www
mkdir /var/www/omu
chmod -R 777 /var/www
cd /var/www/omu
```
### docker-compose.ymlを準備
```yml:docker-compose.yml
version: '3'

services:
  db:
    image: mariadb:latest
    restart: always
    volumes:
      - /var/www/omu/mysql:/var/lib/mysql
    ports:
      - 3306:3306
    env_file: .env

  wordpress-omu:
    depends_on:
      - db
    image: wordpress:latest
    volumes:
      - /var/www/omu/html:/var/www/html
    ports: 
      - 80:80
    restart: always
    env_file: .env
```


```text:.env
MYSQL_ROOT_PASSWORD=xxxxxx
MYSQL_DATABASE=xxx
MYSQL_USER=xxx
MYSQL_PASSWORD=xxxxxx

WORDPRESS_DB_HOST=db:3306
WORDPRESS_DB_NAME=xxx
WORDPRESS_DB_USER=xxx
WORDPRESS_DB_PASSWORD=xxxxxx
```
## いざ実行
```
docker-compose up -d
```
成功。やったあ。
Ubuntu 22.04 に Python 2.7 をインストール（OS標準？）

# はじめに

Ubuntu 22.04にPython 2.7をインストール

## サポート

保守期間不明。
OS EOLまで保守されるのだろうか？

## LOG

### インストール

```shell-session
# cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

# apt update; apt install -y python
... 略
Package python is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
However the following packages replace it:
  python2-minimal python2 dh-python 2to3 python-is-python3

E: Package 'python' has no installation candidate

# apt update; apt install -y python2
```

### 各種確認

```shell-session
# which python{,2,3}
/usr/bin/python2

# ll /usr/bin/python*
lrwxrwxrwx 1 root root       9 Jul 28  2021 /usr/bin/python2 -> python2.7*
-rwxr-xr-x 1 root root 3588440 Mar 12 06:24 /usr/bin/python2.7*

# python2 -V
Python 2.7.18

# apt show python2
Package: python2
Version: 2.7.18-3
Priority: optional
Section: universe/python
Source: python-defaults
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Matthias Klose <doko@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 70.7 kB
Provides: python-ctypes, python-email, python-importlib, python-profiler, python-wsgiref
Pre-Depends: python2-minimal (= 2.7.18-3)
Depends: python2.7 (>= 2.7.18~), libpython2-stdlib (= 2.7.18-3)
Suggests: python2-doc (= 2.7.18-3), python-tk (>= 2.7.18~)
Conflicts: python-central (<< 0.5.5)
Breaks: python (<< 2.7.15-2), python2-doc (<< 2.7.18-2), update-manager-core (<< 0.200.5-2)
Replaces: python (<< 2.7.15-2), python-dev (<< 2.6.5-2), python2-doc (<< 2.7.18-2)
Homepage: https://www.python.org/
Download-Size: 9098 B
APT-Manual-Installed: yes
APT-Sources: http://archive.ubuntu.com/ubuntu jammy/universe amd64 Packages
Description: interactive high-level object-oriented language (Python2 version)
```
PowerShellで.NET FrameworkのConvert.ToDoubleを呼び出す

# PowershellでC#のConvert.ToDoubleなどを呼び出す

以下の記法、ただし、ToDoubleに空白文字を入れると例外を出す。
$nullの場合には0を返す。

```
C:\> [System.Convert]::ToDouble("100")
100
C:\> [System.Convert]::ToDouble($null)
0
C:\> [System.Convert]::ToDouble("")
"1" 個の引数を指定して "ToDouble" を呼び出し中に例外が発生しました: "入力文字列の形式が正しくありません。"
発生場所 行:1 文字:1
+ [System.Convert]::ToDouble("")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : FormatException
```
TypeScriptを基本からまとめてみた【5】【型の利用】




## 動的型付け言語

事前に変数などの型を決定せず、実行時のデータ（値）によって種類を区別する言語。
これを『データ型』と呼ぶ。

##### number (例：1, 5.3, -10etc)
整数や浮動少数点を含む全ての数値

##### string (例：'Hi', "Hi", `Hi`etc)
全ての文字列

##### boolean (true, false)
trueまたはfalse
##### ※ truthy / falsy とは？
Booleanの文脈で使われた時にtrue/falseと評価される値
##### truthyの例 ： true,1,{},[],"0"etc
##### falsyの例 ： false,0,null,undefined,"",'',Nan etc

##### object (例：{age：30}etc)
⇨ object型は　{} (中括弧)にkey value形式の値を入れて表する
JavaScriptの全てのobject。object型をより明確に定義する事が可能。

## 『型注釈』と『型推論』

##### 『型注釈』
TypeScriptでは変数宣言するときに、その変数にどんな値が代入可能かを指定できる。その指定のことを型注釈(type annotation; 型アノテーション)と言う。
変数宣言の型注釈は、下記のように変数名の右に型を書く。

```sample.ts
const num: number = 123;
```
##### 『型推論』
型推論（かたすいろん、英: type inference）とはプログラミング言語の機能の1つで、静的な型付けを持つ言語において、変数や関数シグネチャの型を明示的に宣言しなくても、初期化のための代入式の右辺値や関数呼び出し時の実引数などといった、周辺情報および文脈などから自動的に（暗黙的に）各々の型を決定する機構のこと。

```sample.ts
const num = 123; //ホバーしたら、numberと表示される
```

## オブジェクトに型をつける方法

```sample.ts
const person = {
   name = string;
   age = number;
} = {
   name = "Jack",
   age = 21
}
```


## 参考サイト
[【世界で7万人が受講】Understanding TypeScript 日本語版](https://www.udemy.com/course/understanding-typescript-jp/learn/lecture/17812920#overview)
[超TypeScript入門 完全パック](https://www.udemy.com/course/typescript-complete/learn/lecture/17877674#overview)
【対処/対策】Windows のコンソールで Docker やってて Ctrl+z 押してしまったら

# Windows のコンソールで Docker やってて Ctrl+z 押してしまったら

...しまったら、Dockerコンテナからexitしてしまいます。

私がした対処と対策をメモしておきます。
同じことをして対処に困っている方で、かつexit前にtmuxを使っていなかった場合は、この先を読んでも意味ないかもしれません。
(ただ、tmuxはお勧めです。今回のような切断だけでなく、sshが切断されたときにも同じやり方で復帰できます。)

* 対処に必要なもの : tmux
* 対策に必要なもの : Cmder

## 対処

dockerコンテナ上でtmuxを使っていました。
なので、tmuxのセッションが残っていたため復帰できました。

下記、復帰の方法です(tmuxのプレフィクスが`ctrl+b`であるという前提です)。

1. コンテナにログインする
1. tmuxを起動する
1. コマンド入力 `ctrl+b -> s`
1. 表示されるセッションリストから上下キーでexitしてしまったセッションが残っている確認する(e.g. vim中にexitしてしまったらvimの画面が開いているセッションが残っているはずです)
1. exitしてしまったセッションが見つかったら、それをEnterで選択する

以上で復帰できるはずです。

## 対策

対策には、Cmderのホットキー機能へのマクロ割り当て機能を使います。

ctrl+zに対して、なにかマクロを割り当てるとCmderがctrl+z入力を受け取って、dockerへの入力を遮断してくれます。

下記に設定例をサクッと書いておきます([公式サイトに詳しい説明](https://conemu.github.io/en/SettingsHotkeys.html)があります)。

1. Cmderの設定を開く
1. Key & Macro を開く
1. デフォルト値が`All hotkeys`になっているプルダウンから、Macrosを選択する
1. Choose hotkey欄をクリックしてから、キーボードでctrl+zを押さえる
1. GUI Macro欄に次の文字列を入力する `MsgBox("dockerコンテナからのexitを防止しました","ctrl+zを遮断")`
1. 設定を保存する

以上で対策となります。
設定が終わったら正しく設定できているかどうか、ctrl+zを押して確かめてみるといいかもしれません。

## 参考
https://conemu.github.io/en/SettingsHotkeys.html
https://conemu.github.io/en/GuiMacro.html#MsgBox

## あとがき

この対策方法を望ましいものとは感じていません。
ctrl+zをCmderで使えなくなります。
本来このような挙動(ctrl+zでexit)をすることをdocker作っている側も想定していないようです。
これについてのgithubのイシューがありますが、docker on linuxならこうした挙動はないようです。
このイシューは放置されているようですが、困っている人はそこそこ存在するようです。修正が楽しみです。
https://github.com/docker/for-win/issues/7811
共有ライブラリについて

ec2にsqliteの新バージョンをインストールした後、pathを通す際に出てきた共有ライブラリというワードにて調べてみました。


# そもそもライブラリとは
　・よく使う機能をまとめたもの
　・プログラム(lsコマンド等の実行ファイル)から利用できるようにしたもの

　※実行ファイル
　　┗/usr/bin/ls等

# ライブラリの種類
　・静的ライブラリ
　　┗実行ファイル内に埋め込まれているライブラリ
　　┗実行ファイルごとにライブラリが重複してしまうデメリットがある
　　┗拡張子「lib○○.a」
　　
　・共有ライブラリ
　　┗実行ファイル内に埋め込まないライブラリ
　　┗左記などに配置されている： /usr/lib64
　　┗拡張子「lib○○.so.○○」

# 実行ファイルが必要とする共有ライブラリを確認する方法
　・lddコマンドを使用する

```
root:~/environment (master) $ ldd  /usr/bin/ls
        linux-vdso.so.1 (0x00007ffe847f0000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f400b368000)
        libcap.so.2 => /lib64/libcap.so.2 (0x00007f400b15f000)
        libacl.so.1 => /lib64/libacl.so.1 (0x00007f400af56000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f400abab000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f400a947000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f400a743000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f400b58f000)
        libattr.so.1 => /lib64/libattr.so.1 (0x00007f400a53e000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f400a320000)
```

　※実行ファイルの場所を確認するコマンド
　　┗which ls


■その他の共有ライブラリの検索の仕方
　・下記に共有ライブラリのpathが載っている
　　┗/etc/ld.so.conf
　　　┗中身→include ld.so.conf.d/*.conf
```
root:~/environment (master) $ ls -l  /etc/ld.so.conf.d
total 24
-rw-r--r-- 1 root root  26 May 25  2021 bind-export-x86_64.conf
-rw-r--r-- 1 root root  19 Feb 13  2020 dyninst-x86_64.conf
-r--r--r-- 1 root root 324 Mar  2 18:41 kernel-4.14.268-205.500.amzn2.x86_64.conf
-r--r--r-- 1 root root 324 Mar 30 01:15 kernel-4.14.273-207.502.amzn2.x86_64.conf
-r--r--r-- 1 root root 324 Apr 27 21:11 kernel-4.14.276-211.499.amzn2.x86_64.conf
-rw-r--r-- 1 root root  17 Dec 17 17:17 mysql-x86_64.conf
root:~/environment (master) $ 

root:~/environment (master) $ cat  /etc/ld.so.conf.d/mysql-x86_64.conf 
/usr/lib64/mysql
```

# 毎回共有ライブラリを参照するのは非効率
　・上記の理由により、実際には下記キャッシュを参照する
　　┗ /etc/ld.so.cache 

　・共有ライブラリを追加した場合、/etc/ld.so.confの編集が必要
　・また「ld.so.conf」に基づき、「ld.so.cache」 を変更する必要がある
　　┗ldconifgを実行


# その他の共有ライブラリの追加方法
　・環境変数である、LD_LIBRARY_PATHを使用
```
$export LD_LIBRARY_PATH="/hogehoge/lib"
```

　・ログイン時に実行されるようにbash_profile内に組み込む
```
$export LD_LIBRARY_PATH="/hogehoge/lib">> ~/.bash_profile
```
　・最後に読み込み忘れずに
```
$source ~/.bash_profile
```
Ubuntu 22.04 に Python 3.10 をインストール（OS標準）

# はじめに

Ubuntu 22.04にPython 3.10をインストール

## LOG

### インストール

```shell-session
# cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

# apt update; apt install -y python3
... 略
```

### 各種確認

```shell-session
# which python{,2,3}
/usr/bin/python3

# ll /usr/bin/python*
lrwxrwxrwx 1 root root      10 Mar 25 12:41 /usr/bin/python3 -> python3.10*
-rwxr-xr-x 1 root root 5901416 Apr  2 09:04 /usr/bin/python3.10*

# python3 -V
Python 3.10.4

# apt show python3
Package: python3
Version: 3.10.4-0ubuntu2
Priority: important
Section: python
Source: python3-defaults
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Matthias Klose <doko@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 92.2 kB
Provides: python3-profiler
Pre-Depends: python3-minimal (= 3.10.4-0ubuntu2)
Depends: python3.10 (>= 3.10.4-1~), libpython3-stdlib (= 3.10.4-0ubuntu2)
Suggests: python3-doc (>= 3.10.4-0ubuntu2), python3-tk (>= 3.9.10-2~), python3-venv (>= 3.10.4-0ubuntu2)
Replaces: python3-minimal (<< 3.1.2-2)
Homepage: https://www.python.org/
Task: minimal, server-minimal
Download-Size: 22.8 kB
APT-Manual-Installed: yes
APT-Sources: http://archive.ubuntu.com/ubuntu jammy/main amd64 Packages
Description: interactive high-level object-oriented language (default python3 version)
```
WSLで`code .'などでVScodeが起動できない場合の設定

## 前提
- OS: Windows 11 Pro 21H2 22000.675
- WSL: カーネルバージョン: 5.10.102.1
- ディストリビューション: CentOS Stream release 8
- VSCode: 1.67.2

---
## 現象
Windows 11でVSCodeをインストールしてある状態で、WSL上のCentOSにおいて、`code .`コマンドでカレントディレクトリを起点としたVSCodeの新しい窓を起動したいが、`code`のコマンドを入力しても"command not found"になり、VSCodeが起動しない。
```
$ code .
-bash: code: command not found
```
---
## 解決法
以下を"~/.bashrc"に追記
```~/.bashrc
# for vscode
export PATH=/mnt/c/Users/<貴方のユーザーのフォルダ名>/AppData/Local/Programs/Microsoft\ VS\ Code/bin:$PATH
```
---
## やりがちなミス
適用をお忘れなく
```
source ~/.bashrc
```
【備忘録】Golangでenum風に利用する

### Golangでenum風に利用する。
Golangではenum型が提供されていないため、実装を工夫する事でenum風に利用する事ができます。

```golang:main.go
package main

import "fmt"

type Animal int

const (
	Unknown Animal = iota
	Dog
	Cat
	Horse
	Lion
	Tiger
	Rabbit
)

func (e Animal) Names() []string {
	return []string{
		"unknown",
		"犬",
		"猫",
		"馬",
		"ライオン",
		"トラ",
		"兎",
	}
}

func (e Animal) String() string {
	return e.Names()[e]
}

func main() {
	fmt.Printf("動物の名前は？ %+v\n", Dog)
	fmt.Printf("動物の名前は？ %+v\n", Cat)
	fmt.Printf("動物の名前は？ %+v\n", Tiger)
}
```

```shell:実行結果
~/go/src/enum_go $ go run main.go
動物の名前は？ 犬
動物の名前は？ 猫
動物の名前は？ トラ
```

```
package main

import "fmt"

type Animal string

const (
	Unknown Animal = "unknown"
	Dog = "犬"
	Cat = "猫"
	Horse = "馬"
	Lion = "ライオン"
	Tiger = "トラ"
	Rabbit = "兎"
)

func main() {
	var animal Animal
	animal = "犬"
	fmt.Println(animal)
}
```

```shell:実行結果
~/go/src/enum_go $ go run main.go
犬
```
Module not found: Error: Can't resolve './src' in　｜　webpackエラー

webpackをセッティングしてJSファイルを２つ用意してnpm run devを実行した時に以下のエラーが出ました。

```error.sh
ERROR in main
Module not found: Error: Can't resolve './src' in
```

これはsrcディレクトリの中にモジュールが見つかりませんでしたという内容です。

そこでルートに置いてしまっていた２つのJSファイルを./src/*.jsのような形でsrcディレクトリの中に入れてnpm run devを実行したところdistディレクトリが作成されmain.jsが生成されていました。

これはおそらくwebpackの仕様で、デフォルトでsrcディレクトリ内のファイルを吸い上げ、distディレクトリにまとめて吐き出すという形なのだと思います。

同様のエラーに遭遇した方はお試しください。
The 'mode' option has not been set｜webpackでの警告

webpackをインストールしてnpm webpackを打ったら警告が出た時の対応記録です。

# 警告

```error.sh
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
```

直訳するとmodeオプションが設定されていません。modeを設定してください。というものでwebpack4からこの警告文が出るようになったとのこと。

# 設定
package.jsonに以下のように設定しました。

```package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --mode development",
    "prd": "webpack --mode production"
  },
```

testというものは最初から設定されていたものでdevの一行を追加した形です。
これは何をしているかというとwebpackを走らせる時「npx webpack」などとコマンドを打ちますがそれのショートカットを作成している作業になります。
つまり「webpack --mode development」というコマンドが「npm run dev」という短いコマンドで打つことができるようになります。

これで自動でオプションがつけられたコマンドが設定できました。

```rundev.sh
$ npm run dev
```

これでdistというディレクトリが作成されその中にmain.jsが生成されていれば成功です。

また以下のようにprdをつけてコマンドを実行すると

```rundev.sh
$ npm run prd
```

同様にmain.jsが生成されますが中身を見てみると圧縮されているのがわかると思います。devは開発中人が読みやすい形で書き出し、prdは本番環境でなるべく軽い状態で書き出すことができるようになりました。


# 環境
webpack 5.72.1
node v17.6.0
npm 8.5.2

# 参考
参考にさせていただきました。感謝です。

https://www.valentinog.com/blog/webpack/

https://qiita.com/MasanoriIwakura/items/32f9be227adab21d7f87
VBA_01_キーワード(検索用)_Dictionary


Search Keyword
---

Dictionaryにdictionaryを追加
Dictionary 複数アイテム
Dictionaryに配列を格納する
Dictionaryオブジェクト リストの重複排除
Dictionary 配列 取り出し
Dictionary Item 上書き
Dictionary クラスモジュール 
Dictionary 二重
Dictionary 入れ子

ディクショナリ
連想配列 多次元
多次元配列 ループ
二次元配列 動的 ループ
1次元配列を2次元配列に格納
連想配列に連想配列
連想配列 参照
連想配列 集計
連想配列 重複

多次元配列 ループ
2次元配列(1行 格納)

dictionary method & property
---

Dictionaryに値を登録
Add

Dictionaryの値を出力
For Each myKey In myDic
Debug.Print myKey
Next

検索してitemを出力
myDic("Key1")
myDic.item("Key1")

すべてのkeyとitemを配列に入力
myVarinat1 = myDic.keys
myVariant2 = myDic.items

Dictionary初期化
myDic.RemoveAll

Key1のkey削除
myDic.Remove "Key1"

4番目のkey削除(indexは0から)
myKeys = myDic.keys
myDic.Remove myKeys(3)

keyを変更
myDic.Key("BeforeKey4") = "AfterKey4"

番号を指定してkeyを変更(indexは0から)
myKeys = myDic.keys
myDic.Key(myKeys(3)) = "AfterKey4"

itemを変更
myDic("Key4") = "afterItem4"

番号を指定してitemを変更
myKeys = myDic.keys
myDic(myKeys(3)) = "afterItem4"
Pythonで歌詞を集めて軽く分析してみた話#2

前編
https://qiita.com/kuroge/items/420e5c91606b6b31664c

前回から変わったこと
---
・保存場所を8GBUSBメモリから1.5TBHDDに変更
・5年間隔でデータをまとめる（約20000件くらいの間隔）

今回は…
---
1990年〜1995年の5年間のデータを収集しました。
約23000件を調べ上げました。

結果はこちらです↓
順位 言葉: 回数
5 なんて: 5285
4 きっと: 6311
3 いつも: 6721
2 れない: 8449
1 あなた: 19297
不自然な所もありますが、AIによるものなので、温かい目で見てください。
やっぱり「あなた」がダントツでした。
「あなた」が1位という事はどの時代でも共通なんでしょうか？
今度投稿するときは多分2015年〜2020年あたりの音楽を収集し終わったら発表したいと思います。

ご視聴ありがとうございました
Leetcode 13. Roman to Integer Easyの解説


問題：
https://leetcode.com/problems/roman-to-integer/
正解者さん：
https://leetcode.com/problems/roman-to-integer/discuss/965594/Python
```Python:正解の引用コード
class Solution(object):
    def romanToInt(self, s):
        rd = {
            "I" : 1,
            "V" : 5,
            "X" : 10,
            "L" : 50,
            "C" : 100,
            "D" : 500,
            "M" : 1000
        }
        
        n = len(s)
        rt = 0
        for i in range(n):
            if  i==n-1 or rd[s[i]] >= rd[s[i+1]] :
                rt += rd[s[i]]
            else :
                rt -= rd[s[i]]
                
        return rt
```
　　
まず、
Input: s = "III"
Output: 3
とする。
　　
　　
次に、
rd = {
            "I" : 1,
            "V" : 5,
            "X" : 10,
            "L" : 50,
            "C" : 100,
            "D" : 500,
            "M" : 1000
        }
と書いてアルファベットと数字を関連付けた辞書を作る。
　　
　　
次に、
n = len(s)
で、sの中の要素数を数える
(n=3)
　　
　　
次に、
for i in range(n):
で、0,1,2がiに入っていく間に下のコードを実行する。
　　
　　
次に、
if  i==n-1 or rd[s[i]] >= rd[s[i+1]] :
    rt += rd[s[i]]
で、もしi(0)がn-1(2)の数字と同じ、
または、"III"の0番目であるrdの辞書内の要素("I"(1))が、"III"の0+1番目("I"(1))よりも大きいか同じだったら、
rt = 0に、{rt(0) + "III"の0番目であるrdの辞書内の要素("I"(1))}の値を入れる。
(rt = 1)
　　
　　
次に、
else :
    rt -= rd[s[i]]
で、それ以外だったら{rt(0) - "III"の0番目であるrdの辞書内の要素("I"(1))}の値をrt = 0に入れる。
(rt = -1)
　　
　　
次に、
if i==n-1 or rd[s[i]] >= rd[s[i+1]] :
rt += rd[s[i]]
を繰り返す。
もしも　1が2と同じか、"III"の1番目であるrdの辞書内の要素("I"(1))が、"III"の1+1番目("I"(1))よりも大きいか同じだったら、
rt = 1に、{rt(1) + "III"の1番目であるrdの辞書内の要素("I"(1))}の値を入れる。
(rt = 2)
　　
　　
また、
if i==n-1 or rd[s[i]] >= rd[s[i+1]] :
rt += rd[s[i]]
を繰り返す。
もしも　2が2と同じか、"III"の2番目であるrdの辞書内の要素("I"(1))が、"III"の2+1番目("I"(1))よりも大きいか同じだったら、
rt = 2に、{rt(2) + "III"の1番目であるrdの辞書内の要素("I"(1))}の値を入れる。
(rt = 3)

次に、
for i in range(n):
を確認してみると、この時点でn(0,1,2)が全てiに入ってしまっているため、forループ文は終了する。
　　
　　
最後に、
return rt(3)
となり、正解にたどり着く。
　　
　　
　　
　　
別の値を入れてみよう。　
Input: s = "MCMXCIV"
Output: 1994
とする。

次に、
n = len(s)
で、sの中の要素数を数える
(n=7)
　　
　　
for i in range(n):
で、0,1,2,3,4,5,6がiに入っていく間に下のコードを実行する。
　　
　　
次に、
if i==n-1 or rd[s[i]] >= rd[s[i+1]] :
rt += rd[s[i]]
で、もしi(0)がn-1(6)の数字と同じ、
または、"MCMXCIV"の0番目であるrdの辞書内の要素("M"(1000))が、"MCMXCIV"の0+1番目("C"(100))よりも大きいか同じだったら、
rt = 0に、{rt(0) + "MCMXCIV"の0番目であるrdの辞書内の要素("M"(1000)}の値を入れる。
(rt = 1000)
　　
　　
次に、
if i==n-1 or rd[s[i]] >= rd[s[i+1]] :
rt += rd[s[i]]
を繰り返す。
今回は、1が6と同じか、"MCMXCIV"の1番目であるrdの辞書内の要素("C"(100))が、"MCMXCIV"の1+1番目("M"(1000))よりも大きいか同じではないので、
else :
rt -= rd[s[i]]
を使う。{rt(1000) - "MCMXCIV"の1番目であるrdの辞書内の要素("C"(100))}の値をrtに入れる。
(rt = 900)
　　
　　
次に、
if i==n-1 or rd[s[i]] >= rd[s[i+1]] :
rt += rd[s[i]]
で、もし2が6と同じ、
または、"MCMXCIV"の2番目であるrdの辞書内の要素("M"(1000))が、"MCMXCIV"の2+1番目("X"(10))よりも大きいか同じだったら、
rt = 900に、{rt(900) + "MCMXCIV"の2番目であるrdの辞書内の要素("M"(1000)}の値を入れる。
(rt = 1900)
　　
　　
と繰り返していく。(長いので省略)
　　
　　
今回の肝は、
・I can be placed before V (5) and X (10) to make 4 and 9. 
・X can be placed before L (50) and C (100) to make 40 and 90. 
・C can be placed before D (500) and M (1000) to make 400 and 900.
という縛りをいかに解決するかである。
この縛りが無ければ、
rt += rd[s[i]]
を何回も繰り返して足し算すればいい。

しかし、これだと、IVのときは、VからIが引かれて4になるという引き算ができなくなる。
そのため、もしもIがVよりも小さかった場合、Iの値を合計から引く、つまり、後で足されるVの値から先にIを引いておく必要がある。
ECRのコンテナイメージを再ビルドし、別アカウントのECRにプッシュするシェルを作成してみた


## はじめに

- 検証用環境で確認済のECRイメージを他環境に展開する際、**設定ファイルを上書きした状態で再プッシュが必要**な場面がありました。
- その為、以下を実施するシェルを作成しました。
  - 元となるECRのイメージをプル
  - イメージを基にコンテナを起動し、設定ファイルを上書きして保存
  - 新規作成したコンテナイメージを別環境のECRにプッシュ

### 準備

- アカウントA（元となるECRイメージ）、アカウントB（ECRイメージのプッシュ先）
- EC2を用意（アカウントB側）
- EC2にDocker、aws-cliのインストール
- EC2のIAMロールにECRからプルできる権限を付与
- アカウントA側のECRのパーミッションの設定（アカウントBからのプルを許可）

## 使い方

以下のシェルを叩く想定です。
引数としてECRのイメージURIを渡します。

```bash
/bin/sh container-rebuild.sh ECRのイメージURI
```

## シェル詳細

```sh:container-rebuild.sh
##########################
# 変数定義

# 再ビルド対象イメージのアカウントID
ACCOUNT_ID_PRE_IMAGE="アカウントID"
# 再ビルド対象イメージのリージョン
REGION_PRE_IMAGE="リージョン名"
# ビルド後イメージのアカウントID
ACCOUNT_ID_BUILD_IMAGE="アカウントID"
# ビルド後イメージのリージョン
REGION_BUILD_IMAGE="リージョン名"
# ビルド後イメージのECRリポジトリ名
REPO_NAME_BUILD_IMAGE="ECRリポジトリ名"
#ビルド後イメージURI
BUILD_IMAGE_URI=${ACCOUNT_ID_BUILD_IMAGE}.dkr.ecr.${REGION_BUILD_IMAGE}.amazonaws.com/${REPO_NAME_BUILD_IMAGE}:任意のイメージタグ名

##########################
# コンテナイメージ再構築処理

# コンテナ名を定義
CONTAINER_NAME="tmpContainer"
# ECRの認証トークン取得
aws ecr get-login-password --region ${REGION_PRE_IMAGE} | docker login --username AWS --password-stdin ${ACCOUNT_ID_PRE_IMAGE}.dkr.ecr.${REGION_PRE_IMAGE}.amazonaws.com
# シェル実行時の引数(イメージURI)を元に、イメージをプル
docker pull $1
# コンテナ起動
docker run -d --name ${CONTAINER_NAME} $1
# 設定ファイルの上書き等
任意の設定
# 新規イメージを保存
docker commit ${CONTAINER_NAME} ${BUILD_IMAGE_URI}

##########################
# コンテナイメージを別アカウントのECRへプッシュ

# ECRの認証トークン取得
aws ecr get-login-password --region ${REGION_BUILD_IMAGE} | docker login --username AWS --password-stdin ${ACCOUNT_ID_BUILD_IMAGE}.dkr.ecr.${REGION_BUILD_IMAGE}.amazonaws.com
# イメージをプッシュ
docker push ${BUILD_IMAGE_URI}

##########################
# 後片付け処理

# コンテナ停止
docker stop ${CONTAINER_NAME}
echo "再ビルド用のコンテナを停止しました"
# コンテナ破棄
docker rm ${CONTAINER_NAME}
echo "再ビルド用のコンテナを破棄しました"
# コンテナイメージ削除
docker rmi $1
docker rmi ${BUILD_IMAGE_URI}
```

## おわりに

- 今回はEC2上で動かす想定なので、最後にコンテナ停止、破棄、イメージの削除を行う事で「何回もシェルを利用している内にマシンの容量を圧迫しない」ようにしています。
  - 項目「コンテナイメージ再構築処理」内の「設定ファイルの上書き」の処理内容を変更して利用頂けると良いかと思います。

## 参考文献

<https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/getting-started-cli.html>
M5Stamp C3でOpen Drone IDを動かす準備

# はじめに
本記事は、
　　[M5Stamp C3でリモートIDを作りたい](https://qiita.com/hsgucci/items/8a79745c6af41c3e0a0d)
の一部です。

全体が長くなってきたので、部分的に別ページにしました。

2022年6月20日以降に機体登録する100g以上のドローンに搭載が義務付けられている「リモートID」を、
M5Stamp C3を使って、作ってみたいな～という記事の一部です。

# Open Drone ID

Open Drone IDのプログラムはgithubの　[opendroneid-core-c](https://github.com/opendroneid/opendroneid-core-c)　に置かれています。

**Open Drone ID Core C Library**と書かれているように、これ単体で動くのではなく、
Wi-FiやBLEで送受信するパケットのエンコード・デコードをする関数ライブラリのみです。
パソコンやマイコンそれぞれの機種ごとのプログラムに組み込んで使うことになります。

今回はM5Stamp C3で使いたいので、ESP32のExampleが必要がです。
[Transmitter examples](https://github.com/opendroneid/opendroneid-core-c#transmitter-examples)　のところに書かれていました。

ESP32用は　[https://github.com/sxjack/uav_electronic_ids](https://github.com/sxjack/uav_electronic_ids)　で開発されているそうです。
ただし、BLE4のLegacyのAdvertiseのみのサポートであり、
Long Rangeと拡張Adevertiseが使えるESP32-C3 や ESP32-S3にはまだ対応していないと書かれています(T_T)

BLE5対応は自前でやらないとダメなんですね。。。

# M5Stamp C3でOpen Drone IDを動かす準備

BLE5が動かないのは残念ですが、とりあえずBLE4で試してみましょう。

## uav_electronic_idsライブラリのインストール

まずは　[https://github.com/sxjack/uav_electronic_ids](https://github.com/sxjack/uav_electronic_ids)　からファイルをダウンロードします。
[**Download ZIP**]をクリックして、ファイルを保存します。(gitツールでダウンロードしても良いです)
![odi_esp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/97917cba-2599-94fd-b012-39a28a694356.png)
ファイルを解答し、`id_france`と`id_open`、`utm`の３つのフォルダをArduinoのスケッチ保存場所にある`libraries`の中にコピーします。
![odi_esp_lib.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/d922978a-3e1c-450a-2959-3711246a4fda.png)


これでESP32のプログラムはコピーしましたが、Open Drone IDの関数がまだ入っていません。

## opendroneid-core-cライブラリのインストール

次は　[https://github.com/opendroneid/opendroneid-core-c](https://github.com/opendroneid/opendroneid-core-c)　からファイルをダウンロードします。
![odi_libc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/4229540e-2aa4-6e37-b458-3f2cad2d4fd4.png)
このファイルも解答し、`libmav2odid`と`libopendroneid`、`mavlink_c_library_v2`の３つのフォルダをArduinoのスケッチ保存場所にある`libraries`の中にコピーします。
![odi_clib_copy2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/760baa37-4936-d90b-49d1-d3cc99a28446.png)

ただし`mavlink_c_library_v2`フォルダの中身は空なので、中身を別途コピーする必要があります。

## mavlinkのc_library_v2ライブラリのインストール

[https://github.com/mavlink/c_library_v2](https://github.com/mavlink/c_library_v2)からファイルをダウンロードします。
![odi_mavc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/74dcb480-e548-2b6e-4d74-a346b5598f41.png)
このファイルも解答し、`c_library_v2-master`の中の全てのフォルダとファイルを
`libraries`の中の`mavlink_c_library_v2`の中にコピーします。
![odi_mav_copy2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/19fd9786-b9ff-29bc-d693-07ad3c492a93.png)

これでライブラリのインストールが完了です。
ライブラリを有効にするために、Arduino IDEを一旦終了して再起動しておきましょう。
iOSアプリにSafari Web Extensionを同梱する方法



SwiftUIでSafari Web Extensionを含めたiOSアプリを作成したいときにハマったのでまとめたいと思います。

## XcodeでSwiftUIアプリのプロジェクト作成
Xcodeで新規プロジェクトを作成します。
今回はiOSとmacOSの両方から使用できるアプリを作成したいので [Multi Platform]タグの[App]を選択します。

:::note info
iOSアプリのみを作成したい場合は[iOS]タグの[App]を選択して、interfaceにSwiftUIを選択して作成してください。
:::

iOS用とmacOS用にSwiftUIのContentViewなどが共有リソースとなっているプロジェクトフォルダが作成されます。
![58B76B24-8A1D-4CD3-9934-1CC123532BA5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/2dcbdd64-a2ad-3a4b-4a7f-d79b07b1f38d.png)

## Safari Web Extensionの追加
ここからプロジェクト設定画面を開き、ビルドターゲットを追加します。
![67A43B60-BF79-4EC4-AFAF-E32CEC5949F5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/bc9c8c47-c4ce-bdc3-28b4-a57d01036a01.png)
[multiplatform]タブにはSafariWebExtensionがない（SafariAppExtensionはある）ので、iOSとmacOSそれぞれでSafariWebExtensionを追加します。
![DDA8BEAC-790F-482C-91FB-F2F6C1DA574C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/49b06012-826c-8385-b21b-0d7194330ae7.png)

EmbendedApplicationに同梱したいiOSアプリのターゲットが設定されていることを確認します。
![EBB0D13E-C448-4089-BE86-912991F7CD3C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/e85b4259-b19e-ef8b-be1e-b024630bb97b.png)

![8FF955A3-EA1D-4BD1-8D34-697E5C5B2947.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/58e94c76-1185-4158-2161-837469cf4df0.png)

## シミュレータでの確認
追加されたら、iOSアプリのシミュレータを起動してみます。
<img width="150" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/2b6d407b-a887-ed31-f60f-0a7aac190ba7.png">

シミュレータ状の設定アプリを開いて、同梱した拡張機能が表示されていれば成功です。
<img width="150" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/f6418239-54ad-ba9f-a44d-b17830916dc7.png">
<img width="150" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213979/f8b7bbeb-8ebf-a6f8-36f3-426809dc6d23.png">
macOS 12.1 Monterey でATOKの入力モードを設定する

# 困ったこと
* macOSのバージョンをMonterey（12.1）に上げたら、ATOKの設定が消え、戻してみたら、自分が使いたい「ひらがな」以外のモードがついてきた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64391/08094171-6ce9-05f2-0a0e-734abcc8a536.png)

* 従来なら、**システム環境設定** ＞ **キーボード** ＞ **入力ソース** でモードごとに表示の有無を設定できたのが空っぽ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64391/dadc231f-7957-24dd-e258-853d6b5b5e21.png)

# 解決方法
* **ATOK環境設定** ウィンドウの左下隅に『使用する入力モードの選択…』ボタンがあるので、これをクリック（ATOKのバージョンが31以下だとボタンはないので、まずATOKをアップデートする）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64391/ddd6ece6-e1f7-3971-0d00-fa89a72ec655.png)

* クリックした際に「**システム環境設定** ＞ **セキュリティとプライバシー** ＞ **プライバシー** ＞ **フルディスクアクセス** で、**ATOK入力モード設定ツール.app** にチェックを入れろ」と指示されたら、その通りにする。
    * **フルディスクアクセス** のリストに **ATOK入力モード設定ツール.app** がないようなら、『＋』をクリックして、自分で加えてチェックボックスをチェックする。一般的なインストールだと、 **ATOK入力モード設定ツール.app** は、`/Applications/JustSystems/ATOK32/ATOK入力モード設定ツール.app` にある。

* **ATOK入力モード設定ツール.app** を起動したら、希望するとおりに設定する。保存ボタンなどはなく、チェックした途端、入力メニューに反映される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64391/c8c2a31b-9d8b-0686-724a-8145295447e1.png)
    * チェックを外してあったものをオンにした場合、**システム環境設定** ＞ **キーボード** ＞ **入力ソース** が開き、設定を反映してよいか許可を求められる場合があるので、対応する。
 
### おしまい
Github プルリクエストのテンプレートを追加する方法

# 概要

- Githubのプルリクのテンプレートmdを設定する方法をまとめる。

# 方法

1. 任意のリモートリポジトリをGithubを作成
1. ローカルにclone
1. 既定のブランチ（デフォルトだとmainかmaster）`リポジトリ/.github/pull_request_template.md`にファイルを作成しMarkdownでプルリクのテンプレートを記載しコミットを作成しリモートリポジトリにPush
1. プルリクの作成を行いpull_request_template.mdに記載した内容がプルリク作成画面にテンプレート仁記載した文字列がデフォルトで出力される

    ![Comparing_main___test_·_miriwo0104_pull_request_md.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/64330a1c-21ab-81ed-08ee-bcb667edf9cb.png)

# 参考文献

https://docs.github.com/ja/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository

https://docs.github.com/ja/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates
PHP の && と and って同じなの？

最近 PHP の資格を取ろうとしているんですが、どうせなら国際資格にしようと思って Zend 社の [Zend Certified PHP Engineer](https://www.zend.com/training/php-certification-exam) を勉強しています。

その中で、論理演算について目からウロコ（ただし使うかどうかは不明）な情報があったので紹介したいと思います。

## TL;DR

- PHP の論理演算子は英語でも書ける
- **記号で書く方が英語で書く方より処理が先に行われる**
- うまいこと使えば括弧地獄を軽減できる（が、おすすめはしない）
- **「かつ」「または」の意味で使うなら記号で書いた方が安全**

## 論理演算子とは

論理演算子は真偽値同士の演算をするための演算子です。

- `&&`: 演算子の両側が `true` の場合に限り、結果が `true` となる。「論理積」ともいう。
- `||`: 演算子の片側が `true` の場合に結果が `true` となる。両側が `true` でもよい。「論理和」ともいう。
- `!`: 真偽値の左側について、真偽値を反転させる。例えば `!true` は `false` になる。

これらの記号には英語表記も存在します。

- `and` は上記の `&&` の説明と同じ動作をする。
- `or` は上記の `||` の説明と同じ動作をする。
- `xor`: 演算子のどちらか片側が `true` の場合に限り、結果が `true` となる。両側が `true` の場合は `false` となる。

## 論理和が先に計算されてしまう罠

優先順位は、 `&&` と `||` では `&&` が先、 `and` と `or` では `and` が先です。

では、以下の場合はどうでしょうか？

- ~~`&&` と `or`~~ 指摘がありましたので削除しました
- `and` と `||`

論理和と論理積の計算順序によって結果が変わるような例で確認してみましょう。

```php
<?php

echo "'and' と '||' では ";
if (true || false and false) {
    echo "'and' が優先される", PHP_EOL;
} else {
    echo "'||' が優先される", PHP_EOL;
}
```

```text
'and' と '||' では '||' が優先される
```

なんと、記号の方が先に計算され、特に 2 つ目の例では論理積と論理和の順番に関する「神話」が崩れてしまったではありませんか。

急いでマニュアルの [演算子](https://www.php.net/manual/en/language.operators.precedence.php) のところを確認しましょう！

## マニュアルによると

[マニュアル](https://www.php.net/manual/en/language.operators.precedence.php) の Operator Precedence の表によると、論理演算子の優先順位は以下のようになります。

|順位|演算子|
|:-:|:-:|
|1|`!`|
|2|`&&`|
|3|`\|\|`|
|4|`and`|
|5|`xor`|
|6|`or`|

記号の方が順位が上に来ていることがわかります。

PHP において `&&` と `and` 、 `||` と `or` はそれぞれ全く同じ意味というわけではないため、動きを理解したうえで使う必要があります。

## (個人的非推奨) 括弧地獄の軽減に役立てる例

PHP で開発中に条件分岐が複雑になり、 if の中身がこんな感じになってしまった人もいるのではないでしょうか？

```php
if (($person->入場券を持っている() && ($person->ちゃんと列に並んでる() || $person->isおりこうさん())) || ($person->特別な券を持っている() && $person->is55歳以上である())
) {
    // 処理
}
```

論理演算子の優先順位を正しく理解すれば、括弧を使わずに書くこともできます。

```php
if (
    $person->入場券を持っている()
    and $person->ちゃんと列に並んでる() || $person->isおりこうさん()
    or $person->特別な券を持っている() && $person->is55歳以上である()
) {
    // 処理
}
```

でも個人的には流行らないのかなあと思っています。
なぜかというと使う記号が増えて直感的に読めなくなり、 PHP の論理演算子に関する深い理解を強いられるからです。
というか、ここまで複雑な条件を書くことになる前にコードそのものを見直してください。

会社で提供したいサービスに合わせて言語を選定している企業さんや、新人さんをたくさん採用されている企業さんでは、引き続き括弧を補助的に用いて `&&` と `||` だけで論理を記述することをおすすめします。

PHP の資格を取りたい人は私と一緒に頑張って覚えましょう。
資格取得のためには、言語仕様を細かく理解する必要があります。
Centos Stream 9:firewall + ipsetでメールサーバーへのアクセス制御

##### 初めに
/var/log/maillogから「connect from unknown」の行の中のIPを取り出し、そのIPを firewall-cmd コマンドでドロップする..という方法です。日に一回、以下のシェルスクリプトを実行させることで段々と減少しているのが分かります。合わせて postfix におけるアクセス制御(smtpd_recipient_restrictions 等)と組み合わせて作動させれば効果は増すものと思われます。
##### ipset とは？
IPアドレスやネットワークアドレスをまとめてインポートすることができる、IPテーブルの補助的役割を持っています。
ipsetはfirewalldに統合されており firewall-cmd で操作可能です。よって、ipset に登録されているアドレスを、firewall-cmd で一括してブロックする事ができます。
ここのサイトを参考にしました。[ipsetで大量のBlock指定を行う](https://www.bit-hive.com/articles/20190706)
##### 手順
1: maillog より「connect from unknown」のある行のIPを抽出し/var/log/blacklist-file に書き込み( /var/log/blacklist-file は任意の場所.任意名)
```
#「connect from unknown」のある行のIPを抽出
  cat /var/log/maillog | grep 'connect from unknown' | egrep -o -e '\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\]' | sed -e 's/^\[\(.*\)\]$/\1/g' | sort | uniq -c | awk '$1 > 1 {print $2}' >> /var/log/blacklist-file
# 特定の IP を除く(ネットワーク内のクライアントのIP等)
  cat /var/log/blacklist-file | grep -v "192.168.＊.＊" >> /var/log/black_ma-file
```
2: blacklist-file(IP アドレス) を昇順にソート, 重複行を削除し、blacklist-fileを更新
```
  sort -V /var/log/black_ma-file > /var/log/blacklist-sort_file
  uniq /var/log/blacklist-sort_file /var/log/blacklist-sort_file2
  rm -f /var/log/blacklist-file
  cp -p /var/log/blacklist-sort_file2 /var/log/blacklist-file
# 不要ファイルを削除
  rm -f /var/log/black_ma-file
  rm -f /var/log/blacklist-sort_file
  rm -f /var/log/blacklist-sort_file2
``` 
3: firewalldを停止
```
  systemctl stop firewalld.service
```  
4: 旧 ipset のデータを削除します
```
  ipset destroy
  rm -f /etc/firewalld/ipsets/BLACKLIST.xml
  rm -f /etc/firewalld/ipsets/BLACKLIST.xml.old
  rm -f /etc/firewalld/zones/drop.xml  
```
5: firewalldを再開
```
  systemctl start firewalld.service
```  
6: ipsetを新たに作成(セット名をBLACKLISTとする）
```
  firewall-cmd --permanent --new-ipset=BLACKLIST --type=hash:net
# これにより /etc/firewalld/ipsets に blacklist.xmlファイルが作成される
```  
7: 作成したipsetがruntime状態にて利用可能にする
```
   firewall-cmd --reload
   systemctl reload firewalld.service
```
8: 作成した blacklist-file を /etc/firewalld にコピー 
```  
  rm -f /etc/firewalld/blacklist-file
  cp -p /var/log/blacklist-file /etc/firewalld/blacklist-file 
```
9: blacklist-fileに記述されているipアドレスを以下の要領で BLACKLISTに登録する
```
 #旧 BLACKLIST 削除
   firewall-cmd --permanent --ipset=BLACKLIST --remove-entries-from-file=/etc/firewalld/blacklist-file
 #新 BLACKLIST 登録
   firewall-cmd --permanent --ipset=BLACKLIST --add-entries-from-file=/etc/firewalld/blacklist-file
```
10: ipset blacklistに登録されているアドレスを一括してブロック
```
    firewall-cmd --permanent --zone=drop --add-source=ipset:BLACKLIST
```
11: 以上の手続きを有効にするため、以下のコマンドでxmlの内容をreload
```
    firewall-cmd --reload
    systemctl restart firewalld.service 
```
   12:整理して postfix dovecot を再起動
```
 cd /var/log
  : > maillog
  : > secure
  : > messages  
 cd

 postmap /etc/postfix/reject_sender
 postfix reload
 systemctl restart postfix dovecot   
```
PHPの三項演算子とは

# 三項演算子
三項演算子は演算子のひとつで3つの項目を使用する唯一の演算子です。書式は次の通りです。


`条件式 ? 式1 : 式2`



```php5
$a = 100;
$b = 100;
if ($a === $b) {
    echo '$aと$bは同じ値';
} else {
    echo '$aと$bは異なる値';
}

```

↑矢印これが、、




```php
$a = 100;
$b = 100;
echo $a === $b ? '$aと$bは同じ値' : '$aと$bは異なる値';

```

たったのこれだけで済んでしまうのです。

コード量が減るメリットがある一方で、多用しすぎると可読性が下がるというデミリットも含むため、三項演算子の中で三項演算子を呼び出すなどはやめましょう。

# 参照
[PHPの便利な演算子でコードを短くしよう！](https://tatsuno-system.co.jp/2021/06/15/blog_php-code/)
SwiftUI の画面遷移アニメーションを無効にする

SwiftUI で画面遷移を行うための方法に `.sheet` や `.fullScreenCover`、 `NavigationLink` などがありますが、いずれもデフォルトでアニメーションがついています。このアニメーションはほとんどの場合にはありがたいですが、ときには邪魔になることがあります。

`disablesAnimation` を `true` にした Transaction の中で画面遷移を発生させることでアニメーションなしで遷移させることができます。

```swift
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
    // ここで画面遷移を発生させる
}
```

具体例として、 `.fullScreenCover` のアニメーションを無効にしたものを示します。 `.sheet` や `NavigationLink` にも同様の方法が使えます。

```swift
struct ContentView: View {
    @State private var isPresented: Bool = false
    
    var body: some View {
        VStack {
            Button(action: {
                // NOTE: ここで表示アニメーションを無効にしている
                var transaction = Transaction()
                transaction.disablesAnimations = true
                withTransaction(transaction) {
                    isPresented = true
                }
            }) {
                Text("FullScreenCover")
            }
        }
        .fullScreenCover(isPresented: $isPresented) {
            FullScreenView()
        }
    }
}

struct FullScreenView: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        ZStack {
            Color.black.ignoresSafeArea()
            
            Button(action: {
                // NOTE: ここで非表示アニメーションを無効にしている
                var transaction = Transaction()
                transaction.disablesAnimations = true
                withTransaction(transaction) {
                    dismiss()
                }
            }) {
                Text("Close")
                    .foregroundColor(.white)
            }
        }
    }
}
```

| 通常の `.fullScreenCover` | ^ のコードでアニメーションを無効にしたもの |
| --- | --- |
| ![before.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159260/090d4818-3014-6478-0548-d9cf32c4e622.gif) | ![after.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159260/df6f38a7-2e6c-6d56-5f07-29c3620647a9.gif) |

以上です。

この記事は Transaction の機能のうちの一部しか使っていません。Transaction の基礎知識や一般的な使い方については以下の記事がわかりやすいと思います。

https://swiftwithmajid.com/2020/10/07/transactions-in-swiftui/
プロセスとスレッドの違い

## プロセスとスレッド

### プロセス

* 実行中のプログラム一つ一つのこと。
* メモリ空間が与えられる
cf. https://wa3.i-3-i.info/word11032.html
※ あるプロセスが、別のプロセスを作った場合は、それぞれ親プロセス、子プロセスと呼ぶ
cf. https://wa3.i-3-i.info/word11034.html

### スレッド

* プロセス内で命令を逐次実行する部分であり、CPU コアを利用する単位
* 1プロセスは複数スレッドを持てる
* 昔はCPU数＝同時に実行できる命令の数だったが、今は一つのCPUが複数のコア（実際に処理を行う部品）があるので、コア数に依存することになる。また、最近の CPU は、SMT (Intel ではハイパースレッディングと呼ぶ) 機能を搭載しているモデルが多くなってきており、この機能を使うと、1 つのコアに対して複数のスレッド (多くは 2 つのスレッド) を割り当てることができます。
* 一つのプロセスは同じメモリを共有するので、同じプロセス内のスレッドはメモリ空間を共用できる。
cf. https://milestone-of-se.nesuke.com/sv-basic/architecture/cpu/#:~:text=%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%A8%E3%81%AF%E3%80%81%E5%AE%9F%E8%A1%8C%E4%B8%AD,%E3%81%99%E3%82%8B%E5%8D%98%E4%BD%8D%E3%81%AE%E3%81%93%E3%81%A8%E3%81%A7%E3%81%99%E3%80%82

### ApacheのMPM（prefork/worker/event）

ApacheのMPM（prefork/worker/event）はプロセス、スレッドの違いでもある。
https://qiita.com/esparrago_b/items/4f368599aba1a059dbd1
TypeScript を使ってローカル環境で Firebase をアドミン操作する

# やりたいこと
アドミン操作をローカルで行えると、ちょっと集計したいときとかデータを書き変えたいときに便利です。アドミンならセキュリティルールを無視できますしね。
プラスして、型の恩恵が欲しいので TS を使いたい。


# 実装

## TS プロジェクトを作成し設定

必要なパッケージを導入

```bash
npm init
yarn add -D typescript ts-node firebase-admin tsc
npx tsc --init
```

`tsconfig.json` にて設定

```bash
{
  "compilerOptions": {
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "preserveConstEnums": true,
    "strictNullChecks": true,
    "sourceMap": true,
    "allowJs": true,
    "target": "es5",
    "outDir": ".build",
    "moduleResolution": "node",
    "lib": [
      "es2015"
    ],
    "rootDir": "./"
  }
}
```

## アドミン用秘密鍵発行

Firebase コンソールから秘密鍵の `json` をダウンロード

![s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516428/32209d76-fc2d-43b2-19e4-d03ccd615075.png)


## 秘密鍵を読み込んで初期化

さっきの秘密鍵の json ファイルを見ながら対応するパラメータに書き込んで初期化。
JSON だとスネークケースだけどこっちはキャメルケースなのに注意。
`credential.ts` とかにしてこの部分だけ分離しておくと取り回ししやすい。

```tsx
import * as admin from "firebase-admin";

const params = {
  type: "service_account",
  projectId: "",
  privateKeyId: "",
  privateKey: "",
  clientEmail: "",
  clientId: "",
  authUri: "",
  tokenUri: "",
  authProviderX509CertUrl: "",
  clientC509CertUrl: "",
};

export const adminApp = admin.initializeApp({
  credential: admin.credential.cert(params),
});

```

## 実際に使ってみる

初期化した `adminApp` が出来たので、あとはお好きにどうぞ。

```tsx
import { adminApp } from "./credential";
import { firestore } from "firebase-admin";

const db = adminApp.firestore(); // app を使って firestore インスタンスの生成

async function userAdd() {
  const userRef = db.collection("users").doc();
  await userRef.create({
    name: "hoge",
    age: 21,
    createdAt: firestore.FieldValue.serverTimestamp(),
  });
}

async function main() {
  await userAdd();
}

main();
```
【マーケットバスケット分析】おむつとビール

# はじめに
データ分析の手法の１つのマーケットバスケット分析（Market Basket Analysis）について主要な**信頼度**、**支持度**、**リフト** の概念についてまとめます。

# マーケットバスケット分析（Market Basket Analysis）とは
バスケット分析とはデータマイニンの手法の１つで、同じ買い物かご（バスケット）に入って購入された商品の共起性を導き出し、顧客の消費行動を分析する手法です。ここで言う共起性とは「ある物とある物は同時に購入される事が多い」と言うニュアンスで、レコメンデーションに用いられる事もあります。ただ同時に買われている物同士をレコメンドすればいい訳ではなく**Confidence（信頼度）**、**Support（支持度）**、**Lift（リフト）** と呼ばれる３つの指標を用い、総合的に評価されます。

# データ
今回使用するデータは以下のようなあるスーパーマーケットにおける顧客（5人）ごとの商品購入履歴です。

![qiita (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1044781/e58a813e-d8e2-b7ba-6a86-9dfb1daabf66.jpeg)


# Confidence（信頼度）
![30.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1044781/b8f7bde1-8048-7c7f-ff30-b708e152c9f1.jpeg)

Xを買った人がYも買う確率（割合）を **Confidence（信頼度）** と言います。
$$
\dfrac{P(X\cap Y)}{P(X)}
$$


例えば「ビールに対するオムツの信頼度」は以下のようになります。
$$
\dfrac{P(Beer\cap Diaper)}{P(Beer)} = \dfrac{\dfrac{3}{5}}{\dfrac{4}{5}} = 0.75
$$


# Support（支持度）

![31.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1044781/468e6929-228d-3cf4-e4c3-94bbff06a86b.jpeg)


XとYを買った人と全体との確率（割合）を **Support（支持度）** と言います。Support（支持度）はそもそもXとYのコンビがどのくらい買われているのかを意味します。なかなか売れないXとYが、たまたま２、３回同時に買われた商品では信頼度が大きくなってしまうので、この指標でそれを解消します。
$$
\dfrac{P(X\cap Y)}{P(U)} \qquad *where \ P(U) = 1 
$$

「ビールとオムツの支持度」は以下のようになります。
$$
\dfrac{P(Beer\cap Diaper)}{P(U)} = \dfrac{\dfrac{3}{5}}{1} = 0.6
$$


# Lift（リフト）
![32.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1044781/28eb1473-1109-7907-aecc-9f31e59ae887.jpeg)


Xから見たYの信頼度を、Yを買った人の割合で割った値を **Lift（リフト）** と言います。これは、もしYが元々売れ筋商品で、店にきた顧客のほとんどがYを買っていく傾向にある場合、XとYのレコメンデーションにアラートをかける事ができます。
$$
\dfrac{\dfrac{P(X\cap Y)}{P(X)}}{\dfrac{P(Y)}{P(U)}} \qquad *where \ P(U) = 1 
$$

例えば天然水は5人中5人が買っています。この時、*ビールと天然水の信頼度と支持度は共に高い数値を残します。しかし、ビールを買った人に天然水をレコメンドするのは直感的にもおかしい事がわかります。そこで、このリフトを利用します。

>*Note:
> $$
>信頼度= \dfrac{P(Beer\cap Water)}{P(Beer)} = \dfrac{\dfrac{4}{5}}{\dfrac{4}{5}} = 1　\\\\
>支持度= \dfrac{P(Beer\cap Water)}{P(U)} = \dfrac{\dfrac{4}{5}}{1} = 0.8
>$$

「ビールとオムツのリフト値」は以下のようになります。リフト値が1以上あるという事は十分にレコメンドする価値があるといえます。

$$
\dfrac{\dfrac{P(Beer\cap Diaper)}{P(Beer)}}{\dfrac{P(Diaper)}{P(U)}} 
= \dfrac{\dfrac{(3/5)}{(4/5)}}{\dfrac{(3/5)}{(1)}} = 1.25
$$

### 方向性
共起性とは違い、リフトや信頼度を考える際に重要になってくるのが**方向性**です。レコメンデーションにはしばしば方向性が存在し、洗濯機を買った人に洗剤をレコメンドする事は良い作戦ですが、洗剤を買った人に洗濯機をレコメンドする事や、スマホケースを買った人にスマホを提案する事はナンセンスな事が多いです。

![qiita (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1044781/e7561e75-2f9a-9fa7-b1ac-71a8fb963993.jpeg)


# おわりに

データマイニングの手法の１つのマーケットバスケット分析についてまとめました。ちなみに、なぜビールとオムツがこんなのも相関があるデータを使ったかというと、マーケットバスケット分析の有名な事例から来ています。なぜビールとオムツに相関があるのか、  気になる方はこちらの記事をご覧ください。

https://www.itmedia.co.jp/im/articles/0504/18/news086.html

#### 参考文献
https://www.albert2005.co.jp/knowledge/marketing/customer_product_analysis/abc_association
Lambda から直接ステータスコードを返す

# 問題
こんな感じで ステータスコード 400 を return する Lambda を書くとする。

```jsx
exports.handler = async (event, context) => {
  let response = {
      statusCode: '400',
      body: JSON.stringify({ error: 'Bad Request' }),
  };

    return response;
};
```

しかし、API Gateway を介してこの Lambda にアクセスしてみると、なぜかステータスコード 200 が返ってくる。
これは Lambda のレスポンスが API Gateway によりラッピングされてしまうことが原因。
なので、Lambda のレスポンス内容をもとにマッピングテンプレートを利用し API Gateway 側でレスポンスコードを指定する必要があった。
でも面倒なので、Lambda で直接ステータスコードを設定したい。

# 対処
`callback` にレスポンス内容を渡してあげれば中身がそのままレスポンスとして返る。
中身がそのまま返るからヘッダーとかちゃんと設定してあげる必要あるけど。

```jsx
exports.handler = async (event, context, callback) => {
  let response = {
      statusCode: '400',
      body: JSON.stringify({ error: 'Bad Request' }),
      headers: {
          'Content-Type': 'application/json',
      }
  };
  
  callback(null, response);
};
```
AWS API Gateway で Firebase Auth 認証済みユーザーのみアクセスできる API を作成する

# やりたいこと
Firebase Auth では認証ユーザーに対してトークン（JWT）が発行でき、これを用いることで API Gateway にて正しいトークンか確認することができる。（JWT認証）
これを使い、Firebase Auth で認証されているユーザーしかアクセスできない API を作成しよう。

# 動作の流れ

1. Firebase Auth によりトークンを発行
1. トークンを入れ込んで API にアクセス
1. API Gateway は受け取ったトークンが正しいかを Firebase Auth に対して確認する
1. 認証が通ったやつだけ Lambda へアクセス

# 実装
## アクセス成功確認用の Lambda を作成する

アクセス成功の確認がしたいだけなので、実質何でも OK。

```dart
exports.handler = async (event, context, callback) => {
    const { jwt } = event.requestContext.authorizer;
    let response = {  
      statusCode: 200,
      body: JSON.stringify({
        jwt: jwt,
      }),
      headers: {
          'Content-Type': 'application/json',
      }
    };
    callback(null, response);
};
```

## API Gateway で Lambda 設定

JWT 認証を使うため、API タイプ は `HTTP API` を選択する。
エンドポイントができたら適当にルートを作って、統合からさっき作った Lambda に接続する。

## API Gateway で JWT 認証を設定する

AWS コンソールの API Gateway のページへ。
左の認可タブを開き、さっき作ったルートを選んで「オーソライザーを作成してアタッチ」をクリックする。設定が開くので以下を入力して保存すれば OK。

- 名前: FirebaseAuthorizer
- ID ソース: $request.header.Authorization
- 発行者 URL: `https://securetoken.google.com/FirebaseのプロジェクトID`
    - プロジェクトIDは Firebase コンソールのプロジェクトの設定から見れる
- 対象者: プロジェクトID をそのまま入れる

これだけで終わり。

# Dart でアクセスしてみる

`getIdToken()` でトークンが取得できるので、それを使ってアクセスしてみよう。

```dart
import 'package:http/http.dart' as http;

final user = FirebaseAuth.instance.currentUser!;
final token = await user.getIdToken();

final headers = {
  'Authorization': 'Bearer ' + token // Bearer の後のスペースが大事
};
final url = Uri.parse('https://hogehogeapi...');
final response = await http.get(url, headers: headers);

if (response.statusCode == 200) {
  debugPrint(response.body);
} else {
  debugPrint(response.reasonPhrase);
}
```

ただ、いちいち `getIdToken()` のリクエストを送るのはもったいない。`idTokenChanges()` でトークンが変更をリッスンできるので、これで変更次第どっかに保存して使いまわす感じにするのが良さそう。

```dart
FirebaseAuth.instance
  .idTokenChanges()
  .listen((User? user) {
		// トークン取得してどっかに保存するとか
  });
```
GitHubでのリポジトリ作成からREADME作成まで

# はじめに
初めまして、@cats_2311と申します。
プログラミングの基礎から学び直そうと思い、初めての投稿です。
今まではSVN(Subversion)の使用だったため、Git初心者です。
「(1)GitHubリポジトリの新規作成〜(2)REDAME作成」について、Git初心者の方にもご参考になれば幸いです。

# 前提
- GitHubのアカウントを作成済であること。
- GitHubに公開鍵を登録済であること。

# 本題
## (1)GitHubリポジトリの新規作成
GitHubにログインした状態で「Repositories」タブから「New」を押下します。
![0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602721/53a5ffd9-9c4c-89a1-21e4-7a9f276a029f.png)

「Repository name」に任意のリポジトリ名を入力します。
※私は、Spring Bootを勉強しようと思い、このような名前にしてみました。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602721/89a17b8f-1ba0-10c3-caeb-575fdc162cfa.png)

「Add a README file」をチェックし、「Create repository」を押下し、リポジトリを作成します。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602721/f9470d82-a6f9-1fd9-1c48-67c0e8a93618.png)

## (2)READMEの編集
リポジトリの作成と同時に、READMEも作成されます。
作成時点のREADMEには、リポジトリ名のみが記載されていますので、鉛筆マークを押下して任意の内容に変更します。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602721/9cff887f-657c-fea7-b850-91bd226af1f2.png)

私は、Spring Bootの勉強用で作ってみたので、その旨の内容に変更し、「Commit changes」を押下します。
※READMEは、マークダウン形式で編集できます。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602721/d37fa625-6381-3b06-27cf-1e5fed927a06.png)

# まとめ
「失敗しても良いや」と思いながら始めてみると良いと思います。(お金もかからないですし笑)
※私は、リポジトリの作成や削除を何度か試して、操作は徐々に慣れました。もっと勉強していきます。

# 参考文献
分かりやすい情報に大変感謝致します。

https://qiita.com/ucan-lab/items/d594404d0d2c64a85a38

https://qiita.com/kamorits/items/6f342da395ad57468ae3
エンジニアの就職活動。社風や雰囲気の違いは面接で見抜きなさい。

# 結論

ある。

人がいるところ全てには雰囲気というものがある。
類は友を呼ぶとも言う。

このことわざは「信頼に足ることわざランキング」の世界TOP10以内に位置している。しらんけど。

雰囲気Aの人たちには雰囲気Aの人たちが、雰囲気Bの人たちには雰囲気Bの人たちが集まりやすい。
こうして組織の雰囲気というものが醸造されていく。

僕も業務委託やSES、自社開発、就職面接などで複数の会社やグループの人と話したことはあるが、それぞれ一緒に働きたい人たち、そうでない人たちというような雰囲気の違いは十分に感じた。

# 面接

面接の雰囲気を大事にしたい。

エンジニアであれば、そもそも面接官もエンジニアが担当していることが多いと思う。
彼らは愛想笑いをする練習なんておそらくしていない。わりと素のままで話しているかと思う。

作り笑いをしているにしても、素は隠しきれない。

ここでなんとなく感じる違和感のようなものがあれば、それを大事にしたい。

面接の雰囲気が良かったからと言って、会社全体も雰囲気が良いとは限らない。
だが少なくとも面接でさえ雰囲気が悪かったような会社は、全体の雰囲気も悪いこと請け合いだ。僕はそう思っている。

「あのさ、ORMって分かる？」と輩口調で聞いてきた、ミッキーマウスTシャツの男。
彼が面接官だった会社では何故か長大なSQLを書く「筆記」試験があり、僕は鉛筆と紙で臨んだが、ガラス越しに見えるたくさんの人達も、どことなく暗い顔をしながら働いているような気がした。

そのとき結果的には僕はカジュアルで話しやすい面接をしてくれた場所に就職し、社内の雰囲気もそのとおり、悪くなかったという経験がある。

会社も求職者を選ぶが、求職者も会社を選ぶ。今やエンジニアは売り手市場。
自分と合わない社風、雰囲気の組織にジョインしても、面白く働けるはずはない。

就職で大事なことはいくつもあるが、雰囲気も大事なファクターだ。どうぞお大事に。





<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->