- 投稿日:2021-03-04T18:34:15+09:00
LINE × スプレッドシートのススメ(家計簿アプリ編)
はじめに
最近ですが、LINEでやり取りするトランザクションデータをデータベースではなく、ユーザのスプレッドシートで直接管理することを推してます。
メリットとしてはこんな感じです。
1. サービス内でデータを保持しないということは、データ保持の責任リスクを軽減できる
2. ユーザに馴染みのあるExcel形式であり、スプレッドシートの機能で可視化できるのでデータ分析もしやすいこのメリットを活かすサービスとして、家計簿アプリを作りました。
家計簿アプリについて
コンセプト
- 後で家計簿に書こうとしても、ついつい忘れてしまう。なので、モバイルアプリで買ったその場で登録!!
- それでも人間は忘れてしまう。そんな時は、モバイルアプリでちまちま登録せず、PCでまとめて登録しよう!!
- 使い慣れたExcel形式だから、自分の好きなようにデータ分析もできる!!
一応、以前作ったプレゼンですが、こちらでもコンセプトを語ってます。
今回の改良点
今までは単純に買ったものをスプレッドシートに書き込むだけでした。
巷でこんな声を聞きました。
「買い物してたら、ついつい買いすぎちゃって、月の生活費の予算を超えちゃったわ」・・・なるほど。
じゃあ、月のトータル金額が見れるようにしましょう。サーバー環境
サーバサイドはNode.jsで作っており、スプレッドシートの操作は以下のモジュールを使ってます。
google-spreadsheet※Githubにも全ソースを上げてますが、以下のソースコードはQiitaに載せるにあたって分かりやすくするためにGithubのソースコードとは多少異なります。
まずは月の集計金額を出す必要があるので、query関数をスプレッドシートに書き込んでます。
let doc = new GoogleSpreadsheet(sheetId);// スプレッドシートIDを指定 // 認証関連のコードは省略します(※そのうちQiitaに記事書きます) // スプレッドシート(doc)から、対象のシート(sheet)を取得したところから書きます。 // query関数を書き込むセルの指定 await sheet.loadCells('F1'); let cell = sheet.getCellByA1('F1'); // 関数を書き込む際は「formula」を使います。 cell.formula = `=query(A:C,"select B,sum(C) where B is not null group by B label B 'sum 買ったもの'",1)`; sheet.saveUpdatedCells();これだけではツマラナイので、グラフも入れて分かりやすくしてみます。
とはいえ、スプレッドシートの関数でグラフ化ができるのは今のところ少ないようです。※本当は円グラフを使いたかったのですが、関数ではできないようなので棒グラフにしてみました。
// for文でぐるぐる回して、SPARKLINE関数を使って棒グラフを追加してます for (let i = 2; i <= index; i++) { await sheet.loadCells(`H${i}`); let cell = sheet.getCellByA1(`H${i}`); cell.formula = `=SPARKLINE(G${i}, {"charttype","bar";"max",MAX(G:G)})`; sheet.saveUpdatedCells(); } sheet.saveUpdatedCells();今後の改良点
実は、買ったもの(商品名)で集計しているので、例えば「生活費」みたいなカテゴリで集計する形にはまだなってません。
今度はその辺りを直したいと思ってます。最後に
今回は改良した部分を中心に記事を書きましたが、そもそもユーザのスプレッドシートに書き込むための設定方法については書いてません。
次回はその辺りを書きたいと思います。
- 投稿日:2021-03-04T16:28:58+09:00
Discord.js 技術メモ #1
アクティビティを設定する
Botのアクティビティを設定する
client.on("ready", () => { client.user.setActivity("Yuki | https://discord.gg/CN4dYAVYXW", {type: 'PLAYING'}); });ファイル読み込み
ファイルを読み込み配列に入れる
const fs = require('fs'); const readline = require("readline"); let readdata = [""]; var stream = fs.createReadStream("yuki/read.txt", "utf8"); var reader = readline.createInterface({ input: stream }); reader.on("line", (data) => { readdata.push(data) })ファイル書き込み
ファイルを書き込む配列に入れる
const fs = require('fs'); fs.writeFileSync('yuki/write.txt', 'data');特定のチャンネルにメッセージを送信する
特定のチャンネルにメッセージを送信する
client.channels.cache.get('000000000000000000').send({embed: { author: { name: "結貴 - Yuki による宣伝", icon_url: client.user.avatarURL() }, description: "公式サーバーの招待URL \n https://discord.gg/CN4dYAVYXW", color: 10181046, timestamp: new Date(), footer: { icon_url: client.user.avatarURL(), text: "結貴 - Yuki" } }} )メンバーにロールを付与する (Role Name)
メッセージの送信者にロールを付与する
const member = message.guild.members.cache.find((member) => member.id === message.author.id) member.roles.add(role)メンバーからロールを剥奪する (Role ID)
メッセージ送信者からロールを剥奪する
const role = message.guild.roles.cache.find((role) => role.id === "000000000000000000") const member = message.guild.members.cache.find((member) => member.id === message.author.id) member.roles.remove(role)わからないことがあったら
ここで質問を受け付けております
- 投稿日:2021-03-04T12:24:01+09:00
【vue.js】フロントエンド開発にDockerを使う。つらみもあるよ
今までローカルでフロントエンド開発やってたけど、Docker使ってみるか〜。
でもDocker難しくてわからないよ〜〜〜〜〜〜〜!!!!!!
対象読者はvue-cli開発経験のある方です。
最終的なコードはこちら(github)なぜDockerを使うのか
バックエンドの人たちがDocker信者すぎてフロントエンド開発者の肩身が狭いから「環境を揃えたいよね」というフワッとした動機から。
実際使ってみて今のところメリットは感じていないですが、vue-cliのバージョンを固定したりするのは追々嬉しいことになるのかも、と思います。未来への投資だと思ってやっていきます。「Dockerでフロントエンド開発する」とは
どこまでDockerに担わせるか、というところですが、調べた感じだと「実行環境」をDockerで構築するのがベターみたいですね。
クジラの写真なかった。vueをDockerで動かすまでの手順
vue-cliプロジェクトの作成
ローカル環境でプロジェクトを作成します。
ローカルの環境はこんな感じです。新規プロジェクトを始めるときに慌てて最新バージョンにしたりしてます。$ node --version v14.15.5 $ npm --version 6.14.11 $ vue --version @vue/cli 4.5.11あとはいつも通り
$ vue create docker-vue$ ls -a . .gitignore node_modules public .. README.md package-lock.json src .git babel.config.js package.jsonDokcerfileを作成
プロジェクトディレクトリに作ります
DockerfileFROM node:14.15.5-alpine WORKDIR /usr/src/app COPY package*.json ./ RUN apk update \ && npm install -g npm@6.14.11 @vue/cli@4.5.11 \ && npm install
FROM
でnodeイメージのバージョンを指定します。今回はローカルと同じにします。
WORKDIR
はDockerコンテナ内でのプロジェクトディレクトリです。なんでもよいです。
COPY
でpackage.jsonとpackage-lock.jsonをWORKDIRにコピーします。
RUN
でパッケージ等インストールし、環境構築します。ここでnpmとvue-cliのバージョンを固定します。docker-compose.ymlの作成
docker-compose.ymlversion: '3' services: app: container_name: docker-vue-test build: . ports: - 8080:8080 volumes: - .:/usr/src/app - /usr/src/app/node_modules stdin_open: true tty: true command: "npm run serve"コンテナ名を
docker-vue-test
と敢えてつけてます。あとで使います。
最後npm run serve
を命令することで、コンテナ起動と同時にvueサーバーを起動させます。動作確認する
まずDockerイメージをビルドします。
$ docker-compose buildそしてコンテナを起動。
$ docker-compose up -dhttp://localhost:8080/ にアクセスすると、いつものVueの画面が表示されます。
(任意)ポートを変える
ここまででDockerで実行環境を作るという目標は達成しましたが、
8080ポートは何かと他のプロジェクトでも使ったりするので、明示的にポートを指定してみます。vue.config.jsの作成
まず、vue-cliをどのポートを使って起動させるか指定します。
プロジェクトディレクトリにvue.config.jsを作成します。vue.config.jsmodule.exports = { devServer: { port: 9000, # 好きな数字にする host: '0.0.0.0', disableHostCheck: true, }, };docker-compose.ymlの編集
docker-compose.ymlの
ports
を、先ほど指定した数字に合わせて修正します。ports: - 9000:9000Docker再起動
$ docker-compose stop $ docker-compose up -d今度は http://localhost:9000/ で画面が確認できます。
所感
良いところ
いつも通りローカル開発でき、ホットリロードで画面に反映されます。普通です。
うーん…ってなったところ
ホットリロードしなくなった時、ローカル実行ならブラウザのキャッシュをクリアすれば大体治っていたのですが、
Dockerで実行してるとキャッシュを消しても治らないことがあります。
そういう時はコンテナをstop/startするか、全然関係ない箇所をいじったりすると治りました。ここはちょっとよくわからない…また、追加でライブラリなどを入れる時はコンテナ側にインストールする必要があります。
$ docker exec -it docker-vue-test sh /usr/src/app # npm install hogehoge...さいごに
開発初期段階ではまだ「Dockerにして幸せだなあ」と思うことはないです。
ただ手順もそんなに多くはないですし、git cloneしたあとに
以前はnodebrewでnodeバージョンを変えてnpm installしてrun serveして…という手数を踏んでいたところが
Dockerのイメージビルド、コンテナ起動だけになるのは少しシンプルになって小気味好いかもしれませんね。知らんけど。Dockerに頼りきりにならず、中で何をしているか理解することもとても重要だと思っているので、
それを心に留めつつ今後もDockerと仲良くしていきたいと思います。参考記事
- 投稿日:2021-03-04T12:24:01+09:00
【Vue.js】フロントエンド開発にDockerを使う。つらみもあるよ
今までローカルでフロントエンド開発やってたけど、Docker使ってみるか〜。
でもDocker難しくてわからないよ〜〜〜〜〜〜〜!!!!!!
対象読者はvue-cli開発経験のある方です。
最終的なコードはこちら(github)なぜDockerを使うのか
バックエンドの人たちがDocker信者すぎてフロントエンド開発者の肩身が狭いから「環境を揃えたいよね」というフワッとした動機から。
実際使ってみて今のところメリットは感じていないですが、vue-cliのバージョンを固定したりするのは追々嬉しいことになるのかも、と思います。未来への投資だと思ってやっていきます。「Dockerでフロントエンド開発する」とは
どこまでDockerに担わせるか、というところですが、調べた感じだと「実行環境」をDockerで構築するのがベターみたいですね。
クジラの写真なかった。vueをDockerで動かすまでの手順
vue-cliプロジェクトの作成
ローカル環境でプロジェクトを作成します。
ローカルの環境はこんな感じです。新規プロジェクトを始めるときに慌てて最新バージョンにしたりしてます。$ node --version v14.15.5 $ npm --version 6.14.11 $ vue --version @vue/cli 4.5.11あとはいつも通り
$ vue create docker-vue$ ls -a . .gitignore node_modules public .. README.md package-lock.json src .git babel.config.js package.jsonDokcerfileを作成
プロジェクトディレクトリに作ります
DockerfileFROM node:14.15.5-alpine WORKDIR /usr/src/app COPY package*.json ./ RUN apk update \ && npm install -g npm@6.14.11 @vue/cli@4.5.11 \ && npm install
FROM
でnodeイメージのバージョンを指定します。今回はローカルと同じにします。
WORKDIR
はDockerコンテナ内でのプロジェクトディレクトリです。なんでもよいです。
COPY
でpackage.jsonとpackage-lock.jsonをWORKDIRにコピーします。
RUN
でパッケージ等インストールし、環境構築します。ここでnpmとvue-cliのバージョンを固定します。docker-compose.ymlの作成
docker-compose.ymlversion: '3' services: app: container_name: docker-vue-test build: . ports: - 8080:8080 volumes: - .:/usr/src/app - /usr/src/app/node_modules stdin_open: true tty: true command: "npm run serve"コンテナ名を
docker-vue-test
と敢えてつけてます。あとで使います。
最後npm run serve
を命令することで、コンテナ起動と同時にvueサーバーを起動させます。動作確認する
まずDockerイメージをビルドします。
$ docker-compose buildそしてコンテナを起動。
$ docker-compose up -dhttp://localhost:8080/ にアクセスすると、いつものVueの画面が表示されます。
(任意)ポートを変える
ここまででDockerで実行環境を作るという目標は達成しましたが、
8080ポートは何かと他のプロジェクトでも使ったりするので、明示的にポートを指定してみます。vue.config.jsの作成
まず、vue-cliをどのポートを使って起動させるか指定します。
プロジェクトディレクトリにvue.config.jsを作成します。vue.config.jsmodule.exports = { devServer: { port: 9000, # 好きな数字にする host: '0.0.0.0', disableHostCheck: true, }, };docker-compose.ymlの編集
docker-compose.ymlの
ports
を、先ほど指定した数字に合わせて修正します。ports: - 9000:9000Docker再起動
$ docker-compose stop $ docker-compose up -d今度は http://localhost:9000/ で画面が確認できます。
所感
良いところ
いつも通りローカル開発でき、ホットリロードで画面に反映されます。普通です。
うーん…ってなったところ
ホットリロードしなくなった時、ローカル実行ならブラウザのキャッシュをクリアすれば大体治っていたのですが、
Dockerで実行してるとキャッシュを消しても治らないことがあります。
そういう時はコンテナをstop/startするか、全然関係ない箇所をいじったりすると治りました。ここはちょっとよくわからない…また、追加でライブラリなどを入れる時はコンテナ側にインストールする必要があります。
$ docker exec -it docker-vue-test sh /usr/src/app # npm install hogehoge...さいごに
開発初期段階ではまだ「Dockerにして幸せだなあ」と思うことはないです。
ただ手順もそんなに多くはないですし、git cloneしたあとに
以前はnodebrewでnodeバージョンを変えてnpm installしてrun serveして…という手数を踏んでいたところが
Dockerのイメージビルド、コンテナ起動だけになるのは少しシンプルになって小気味好いかもしれませんね。知らんけど。Dockerに頼りきりにならず、中で何をしているか理解することもとても重要だと思っているので、
それを心に留めつつ今後もDockerと仲良くしていきたいと思います。参考記事
- 投稿日:2021-03-04T09:01:56+09:00
nodeのバージョンを切り替える(複数管理する)
始める前に
※対象はMacになります
流れとしては
・nodebrewのインストール
・環境変数の設定
・nodeのインストール
・nodeのバージョンの切り替え
となります。nodebrewのインストール
brew install nodebrewnodebrew -vバージョンが表示されればOK
環境変数の設定
で設定ファイルを開きます
vi ~/.bash_profile以下の1行を追加
export PATH=$HOME/.nodebrew/current/bin:$PATH設定を反映させる
source ~/.bash_profileセットアップ
nodebrew setupnodeのインストール
インストール可能なバージョンを確認
nodebrew ls-remotenodebrew install-binary <version>
nodebrew install
でもインストールできますが、上記のコマンドの方が早い複数のバージョンをインストールできます
ちなみに<version>には以下の表記が使用できる
・v12.16.3
・12.16.3
・stable (安定版)
・latest (最新版)以下のコマンドで現在インストールしているnodeのバージョンを確認できる
nodebrew listnodeのバージョンの切り替え
nodebrew use <version>node -vバージョンが表示されればOK
- 投稿日:2021-03-04T01:34:49+09:00
【書きかけ】Lambdaからクリアテキスト署名メールを送ろうとして2ヶ月かかった話
- 投稿日:2021-03-04T01:34:49+09:00
Lambdaからクリアテキスト署名メールを送ろうとして2ヶ月かかった話
SMIMEでメールを暗号化して送信していましたが、SMIMEに対応していないメーラーだと本文が読めないので クリアテキスト署名 という形式で送ることになりました。
やり方が全然わからずプレッシャーに押しつぶされそうになりながらもなんとか解決できた経緯を書いていきます。環境
- Lambda(Node.js 12.x)
- SES
クリアテキスト署名とは
SMIMEでメールを暗号化して送信すると、SMIMEに対応していないメーラーだとメールの本文が読めません。
メール本文と署名データを別にして、メール本文をplaintextで送る手法です。node-forgeで対応しようとしたが断念
PKCS#7 という形式で署名データを生成するらしいが、クリアテキスト署名の生成方法がわからず。
PKCS #7 分離署名
3.4.3 multipart/signed フォーマットを使った署名
さらに調べていくと、PKCS #7 分離署名という言葉を見つける。
detached mode
node-forgeのReadmeを読んでいると、detached modeという記載を見つける。
おそらくこれが分離署名にあたるのではないかと仮定し、実装してみる。// PKCS#7 Sign in detached mode. // Includes the signature and certificate without the signed data. p7.sign({detached: true});node-forgeのIssuesに、detached modeについて言及しているものがあった
PKCS#7 detached is not that much detached #607
内容を読んでみると、detached modeに不具合があるもよう。
Closedになってるのでもう直っているのかなと思い、該当のソースを確認してみると…// TODO: optimize away duplication
いや直ってないんかいwww
node-forgeは諦めよう。openssl_pkcs7_sign() というPHPの関数を見つける
$message = realpath('message.txt'); $sign = realpath('sign.txt'); $cert = 'file://' .realpath('./cert.txt'); $key = 'file://' .realpath('./key.txt'); $headers = [ 'signing-time' => (new DateTime())->format('o-m-d H:i:s'), ]; $certfile = file_get_contents($cert); $pkeyfile = file_get_contents($key); openssl_pkcs7_sign($message, $sign, $certfile, array($pkeyfile, ''), $headers, PKCS7_TEXT | PKCS7_DETACHED);出力される「sign.txt」の中身をそのままRawMessageとして、 ses.sendRawEmail()を送信したところうまくいった。
この関数はおそらく裏でopensslコマンドを実行しているだけだと思われる。
Lambdaでopensslを使えば解決するのでは。Lambdaでopensslコマンドを使用する
参考サイトのままだとうまくいかないので補足します。
opensslに実行権限を付与
opensslに実行権限を付与してからzipを生成すること。
zipの作り方に注意
opensslをディレクトリに入れてzipを作ると階層が1つ深くなってしまうので、以下の方法でzipを生成すること
zip -r openssl.zip openssl
最終的にこんなコードになりました。
const execSync = require('child_process').execSync const toAddresses = '送信先メールアドレス' const cert = 'PEM形式の証明書' const privateKey = 'PEM形式の秘密鍵' let mailBody = 'メール本文' const certPath = '/tmp/cert.txt' fs.writeFileSync(certPath, cert) const privateKeyPath = '/tmp/privateKey.txt' fs.writeFileSync(privateKeyPath, privateKey) mailBody = 'Content-Type: text/plain; charset=ISO-2022-JP\r\n\r\n' + mailBody const mailBodyPath = '/tmp/mailBody.txt' fs.writeFileSync(mailBodyPath, mailBody ) // opensslコマンドで署名データ生成 const opensslCommand = `/opt/openssl smime -pk7out -sign -in ${mailBodyPath} -signer ${certPath} -inkey ${privateKeyPath} -md SHA256 -from "${sender}" -to "${toAddresses}" -subject "${subject}"` const rawMessage = execSync(opensslCommand).toString() const eParams = { Destinations: [toAddresses], RawMessage: { Data: Buffer.from(rawMessage), }, Source: sender, } await ses.sendRawEmail(eParams).promise()おそらく-pk7out が分離署名にあたるのではないかと思われます。
証明書などのデータはファイルから読み込む方法しかないようなので、無理やりですが、/tmp/ディレクトリに保存して読み込んでいます。