- 投稿日:2020-12-07T21:58:45+09:00
SAMを使った環境毎のデプロイ方法のまとめ
はじめに
SAMを使ったサーバーレス環境の構築時に、1つのtemplate.yamlを使って、develop, staging, production毎に環境変数を切り替える方法を忘れるため、備忘もかねて記事にしました
目的
SAMのtemplateを使って、各環境毎に変数値を変えてlambdaを実行できるようにする
どうやるのか?
SAMテンプレートの中の、ParametersとMappingsを使うと、一つの環境変数でKey-Value方式で、複数の値を切り替えることができる
実例
まず、ParametersとMappingを記述する
Parameters: Environment: Type: String AllowedValues: - development # 開発環境 - staging # ステージング環境 - production # 本番環境 Default: development Mappings: EnvironmentMap: development: HELLO: 'developmen Hello World' staging: HELLO: 'staging Hello World' production: HELLO: 'production Hello World'それを使いたい時は、以下のように指定すれば、環境変数がKeyになり、Valueが取得できる
Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello-world/ Handler: app.lambdaHandler Runtime: nodejs12.x Events: HelloWorld: Type: Api Properties: Path: /hello Method: get Environment: Variables: SAMPLE_ENV: !FindInMap [ EnvironmentMap, !Ref Environment, HELLO]動作確認
# ビルド sam build # 実行 sam local start-api --parameter-overrides Environment={書き換えたい環境変数}アクセスして確認する
curl 'http://localhost:3000/hello'CI/CDをどうするか?
CIに関しては、一つのtemplate.yamlをビルドするだけで良い
CDの時に、--parameter-overrides
を使って、Parametersを書き換えてデプロイするフローになる❯ sam deploy --guided Configuring SAM deploy ====================== Looking for config file [samconfig.toml] : Found Reading default arguments : Success Setting default arguments for 'sam deploy' ========================================= Stack Name [SAMPLE-ENV]: AWS Region [ap-northeast-1]: Parameter Environment [staging]: ← ここで使いたい環境を指定するだけで良い #Shows you resources changes to be deployed and require a 'Y' to initiate deploy ---- 省略 ----まとめ
かなり雑にまとめてしまいましたが、templateのParametersだけを使う場合だと、環境変数が多くなると管理が大変になるので、Mappingsを活用すると使いたい環境を指定するだけで、セットアップができる
誰かのご参考になれば幸いです
- 投稿日:2020-12-07T21:24:26+09:00
Azure Static Web AppsでGoogle Spread Sheetのデータを取得して表示したメモ
概要
スプレッドシートをWebAPI化するサービスの作り方をみて、簡単に取得できるかもと試してみた。
デプロイ先には簡単に関数をおけそうなAzureStaticWebAppsを選んだ。ベータ版だけど、無料で使えるので。結果、そう簡単にはいかなかった。ハマリポイントは以下。
Googleのサービスアカウントの認証情報をどう渡すか
- サンプルの通り、Credentials のJSONファイルを読み込ませようとしたが失敗。
- → Azure FunctionsにGitHub Actionsからどうデプロイしてどう読み込ませるか分からない。gitに含めるわけにもいかんし。
- 環境変数で読み込ませることで一応対応できた。
- →
\
が\\
になる仕様であった。プログラム側で置換して対応。。。作業メモ
サービスアカウントの作成とスプレッドシートの許可
サービスアカウントの Credentials は以下のようなものが取得できる。
keyFile.json{ "type": "service_account", "project_id": "プロジェクトID", "private_key_id": "省略", "private_key": "-----BEGIN PRIVATE KEY-----\n省略\n-----END PRIVATE KEY-----\n", "client_email": "hoge@fuga.iam.gserviceaccount.com", "client_id": "00000000000", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/fuga.iam.gserviceaccount.com" }Spread Sheetの共有許可
環境変数の設定
この画面で設定すると、以下のようなエラーが発生。
秘密鍵がちゃんとしてないぽい。
ローカルではちゃんと動いたのに。{ "library": "PEM routines", "function": "get_name", "reason": "no start line", "code": "ERR_OSSL_PEM_NO_START_LINE" }process.envを出力してみた。
環境変数は、以下のようになっている。\\\\
ってめっちゃ多いな。{ "env": { "SERVICE_ACCOUNT_PRIVATE_KEY": "-----BEGIN PRIVATE KEY-----\\\\n省略\\\\n-----END PRIVATE KEY-----\\\\n"この段階で、以下のようになっていた。
{ "SERVICE_ACCOUNT_PRIVATE_KEY": "-----BEGIN PRIVATE KEY-----\\n省略\\n-----END PRIVATE KEY-----\\n" }直接編集して、一つは減らすことができたが、それ以上の対応は思いつかず。
ソースコードを以下のように修正して対応した。+ const private_key = process.env.SERVICE_ACCOUNT_PRIVATE_KEY.replace('\\\\','\\') const auth = await google.auth.getClient({ credentials: { client_email: process.env.SERVICE_ACCOUNT_CLIENT_EMAIL, private_key, }, scopes: SCOPES })
参考
Azure Static Web Apps
スプレッドシートをWebAPI化するサービスの作り方
Googleスプレッドシートをプログラムから操作
- 投稿日:2020-12-07T17:03:26+09:00
Node.jsを改めて理解する
前提
Node.jsを理解する前に、いくつか確認しておきます。
そもそもJavaScriptとは
HTMLやCSSで作られたWebページをプログラム(JavaScript)で制御することができるのが特徴です。
通常、クライアントサイドで用いられ、次のような場面でJavaScriptは使われています。
・ブラウザ上で画像をクリックすると拡大する
・メールアドレスを入力フォームに入力した時に、適切な形式で入力されているかチェックをする
...etcクライアントサイドとサーバサイドって?
クライアントサイド
クライアントサイドプログラムは、Webブラウザ上で動作するプログラムのことです。
代表的なものとしてはJavaScriptがあります。
クライアントサイドプログラムの仕組みとして、クライアント側(ブラウザ)がサーバにリクエストすることによって、Webサーバー上のプログラムがクライアント側に送信され、そのプログラムをWebブラウザが実行するという形です。サーバサイド
サーバサイドプログラムは、Webサーバー上で動作するプログラムのことです。
代表的なものにPHP、Ruby、Pythonなどがあります。
クライアントからのリクエストに対し、サーバ側でプログラムを実行し、実行結果をクライアント側へ送るという仕組みです。クライアントサイドとサーバサイドの違い
クライアントサイドプログラムとサーバサイドプログラムの違いは、ブラウザ上で動作するのか、それともサーバ上で動作するのかの違いです。
本題
いよいよNode.jsの説明です。
Node.jsとは
Node.jsとは、一言で言うとサーバサイドのJavaScript実行環境(プラットフォーム)のことで、Node.jsを使用する上で使用する言語はJavaScriptです。
Node.jsとは、通常クライアントサイドで使用するJavaScriptをサーバサイドで実行できるようにしてくれるものです。
サーバサイドで動かせると言うと、PHPやRubyなどと同じように思われるかもしれませんが、厳密には少し違います。
Node.jsは単にサーバサイドでアプリケーションを動かすことができるだけでなく、Node.jsではアクセスされたURLを識別して、そのURLごとに別々の要素を表示させるなど、データベースで行う動作も実装できます。特徴
- 大量のデータ処理が可能
- メモリの消費が少なく動作も速い
- 処理速度が非常に速い
- サーバサイドで使える
npmとは
npmは、Node.jsのパッケージ管理ツールです。
PHPにおけるcomposer、Rubyにおけるgemのようなものです。Node.jsを使う場面とは
メモリ消費量が少ないため、小さな規模の開発・運用時に、他の環境と比べるとよりパフォーマンスを出すことが可能です。同時に大量のトラフィックを捌くことができ、動画配信やチャットアプリなどで使われます。
- 投稿日:2020-12-07T03:00:23+09:00
TypeScriptのアップデート中に EEXIST のエラーが表示されてちょっと詰まったことの解決手順
今回は今までなぜかグローバルのTSのアプデができずに無視してたところついに立ち向かった(面倒くさがり)
そのせいでバージョン 1.5.3 に甘んじていました。npm i -g typescript@latestで最新版のTypeScriptにアプデしようとしたら案の定エラーが表示。
npm ERR! code EEXIST npm ERR! syscall symlink npm ERR! path ../lib/node_modules/typescript/bin/tsc npm ERR! dest /Users/myuser/.nodenv/versions/12.16.0/bin/tsc npm ERR! errno -17 npm ERR! EEXIST: file already exists, symlink '../lib/node_modules/typescript/bin/tsc' -> '/Users/myuser/.nodenv/versions/12.16.0/bin/tsc' npm ERR! File exists: /Users/myuser/.nodenv/versions/12.16.0/bin/tsc npm ERR! Remove the existing file and try again, or run npm npm ERR! with --force to overwrite files recklessly. npm ERR! A complete log of this run can be found in: npm ERR! /Users/myuser/.npm/_logs/2020-12-06T17_00_41_920Z-debug.lognodenvを使っている人が遭遇したことあるかもしれないエラー。npm installなどでも遭遇する人がいるらしいが詳しくは見ていない。
再インストールであっさり解決?
結果としては
npm ERR! Remove the existing file and try again, or run npmこれの言う通りにして解決した。このエラーで言うところの existing file は
npm ERR! dest /Users/myuser/.nodenv/versions/12.16.0/bin/tscのことだから、直接 tsc に対して " rm -rf tsc " で削除した。
これでOK。なはず。typescriptアップデートの再チャレンジ
しかしここで再びアプデコマンドを打ったところなんとまた同じエラーが発生。
今度は、「tscフォルダ」ではなく「tsserverフォルダ」に対して怒られた。そのため tsc と同じように削除した。3度目の正直でついに
その後、再び
npm i -g typescript@latestで無事に最新版にアプデできた。バージョンは 4.1.2。
1.5.3から4.1.2はなかなかの跳躍。npm ERR! syscall symlink ってなんだ
タイトルにもある通り、つまづいた理由はこのエラーがよくわからなくて放置。ちゃんと調べてなかったのでよくわからなかった笑
調べたところ symlink とはショートカットのディレクトリ名を意味しており、プログラミングとは関係なく日常でもショートカットのファイルなどを作成する機会があると思うがそれと同じものになるとの理解であってるかと。つまりsymlinkによってAというショートカットのディレクトリ(中身は入っていない)を呼び出すことで、本体であるBを呼び出すことができるらしい。
今回のケースで言えば具体的には
Aがこれ(npm ERR! path ../lib/node_modules/typescript/bin/tsc)
Bがこれ(npm ERR! dest /Users/myuser/.nodenv/versions/12.16.0/bin/tsc)
に当たると思われる。というか path は文字通りの意味で、 dest が目的地であり本体のことを指しているはず。そのため今回は本体のフォルダを削除することで再インストールできたということになります。
nodenvの場合は各nodeバージョン毎に入っているライブラリバージョンも異なるためショートカットという形で呼び出しているのかなと思います。問題は解決できたけれど...
しかし結局のところなぜエラーが生じたのかわからかった。。
他のライブラリやモジュールでも同じエラー生じるはずなので。npm i -g typescript@latestバージョンが古すぎてそのまま上書きアプデできなかったとか??
- 投稿日:2020-12-07T00:56:51+09:00
LINEボットでtodoist連携
todoistにやることリストを追加したり、今日のやることリストを取得してくれるLINEボットを作成します。
前回の投稿「 AlexaとTodoistでやることリスト・お買い物リスト 」の応用です。今回作成するのは、Node.jsで作成するLINEボットサーバです。
図の点線矢印は、最初の設定のときのみで、それが終わってしまえば、あとは、何回でもLINEアプリからtodoistの操作ができるようになります。とりあえず、LINEアプリからは以下ができるようにします。これ以外は、直接todoistアプリから操作してください。
・タスクの新規登録、および担当者のアサイン、期限の設定
・今日、明日、明後日以降、期限切れのタスクの一覧を取得
・タスクの詳細を表示
・タスクの完了を設定毎度の通り、ソースコード一式をGitHubに上げておきました。
poruruba/line-todoist
https://github.com/poruruba/line_todoist(修正) 2020/12/08
todoist認証において、stateに推測されにくい乱数を指定するようにしました。コメントありがとうございました。LINEボットの設定
LINEデベロッパーコンソールから登録します。
LINEデベロッパーコンソール
https://developers.line.biz/console/プロバイダを選択または新規作成したのち、チャネル設定の新規チャネルを選択します。
チャネルの種類は、Messaging APIです。
適当に入力し、利用規約に同意して、「作成」ボタンを押下します。
作成後に表示されるチャネル基本設定にあるチャネルシークレットとMessaging API設定にあるチャネルアクセストークン(長期)の発行ボタンで生成されるチャネルアクセストークン(長期)を覚えておきます。
また、Messaging API設定の応答メッセージは無効に変更しておきます。グループ・複数人チャットへの参加を許可するも有効にしておきます。
Node.jsの立ち上げ
GitHubからZIPダウンロードしてください。
https://github.com/poruruba/line_todoist> unzip line_todoist-master.zip > cd line_todoist > mkdir data > mkdir data\line-todoist > mkdir cert > npm install > node app.jsHTTPSである必要があるため、certフォルダにSSL証明書を格納します。ファイル名は、app.jpを参照してください。
まずは、LINEボットとして応答できるようにするためには、最低限以下の記載があればよいです。
api/controllers/line-todoist/index.js'use strict'; const config = { channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN || '【LINEチャネルアクセストークン(長期)】', channelSecret: process.env.LINE_CHANNEL_SECRET || '【LINEチャネルシークレット】', }; const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/'; const line = require('@line/bot-sdk'); const LineUtils = require(HELPER_BASE + 'line-utils'); const app = new LineUtils(line, config); app.message(async (event, client) =>{ console.log(event); var text = event.message.text const echo = { type: 'text', text: text + 'だよ' }; return client.replyMessage(event.replyToken, echo); }); exports.fulfillment = app.lambda();Swaggerファイルには以下を最低限記載します。
api/controllers/line-todoist/swagger.yamlswagger: '2.0' info: version: 'first version' title: Lambda Laboratory Server paths: /line-todoist: post: x-handler: fulfillment parameters: - in: body name: body schema: $ref: "#/definitions/CommonRequest" responses: 200: description: Success schema: $ref: "#/definitions/CommonResponse"【LINEチャネルアクセストークン(長期)】と【LINEチャネルシークレット】のところに、先ほど覚えておいた値を付記しておきます。
細かな処理は、api/helpers/line-utils.js で処理させています。以下のnpmモジュールを使っています。
line/line-bot-sdk-nodejs
https://github.com/line/line-bot-sdk-nodejsLINEボットのWebhook設定
LINEアプリでの発話をnode.jsサーバに通知してもらうためにWebhookの設定をします。
LINEデベロッパーの、Messagin API設定に、Webhook設定があります。
ここに、立ち上げたNode.jsサーバのURLを指定します。以下のような感じです。https://【Node.jsサーバのホスト名】/line-todoist
これで、Webhookの利用をOKにして、検証ボタンを押下すると、「成功」と表示されるかと思います。
ついでに、同じページに表示されているQRコードから、LINEアプリでこのLINEボットを友達として登録しておきましょう。LINEとtodoistの連携
LINEとtodoistの連携は、このLINEボットが担います。
連携のための設定情報は、Node.jsサーバのdata/line-todoist/
フォルダにJSONファイルとして保存します。
目標とするConfigファイル名と中身は以下の通りです。ファイル名:user-【LINEのユーザID】 またはgroup-【LINEのグループID】 ファイルの中身: { "token": { "access_token": "【todoistのAPIトークン】", "token_type": "Bearer" }, "apikey": "【本ファイルをアクセスするためのAPIキー】", "project_id": 【todoistの対象プロジェクトID】 }まず、ファイル名がLINEのユーザIDまたはグループIDとなっています。
LINEボットをお友達にしたのち直接LINEボットと会話する場合と、LINEのグループにLINEボットを招待して会話する場合の両方があるためです。対象となるLINEのIDは、前者がユーザID、後者がグループIDとなります。それらのLINEのIDとtodoistをどうやって結びつけるかというと、いったんLINEボットからLINEのIDとともに、これから立ち上げるWebページに飛ばし、そこでユーザによりtodoistと認証した結果をLINEボットに戻すことで、行っています。
todoistのアプリ登録
todoistで認証できるようにアプリ登録します。
Todoist App Management
https://developer.todoist.com/appconsole.html「Create a new app」ボタンを押下します。
大事なのは、OAuth redirect URLの部分です。
さきほど立ち上げたNode.jsサーバのURLを指定します。https://【Node.jsサーバのホスト名】/admin/index.html
public/admin/フォルダに、静的ページを用意しておきました。
todoist認証完了後、todoistの認可コード付きでこのページに戻るようになります。Client IDとClient secretも使うので覚えておきます。
流れはこんな感じです。
①LINEアプリからtodoist認証ページへ切り替える
以下の部分です。
todoistのAPIトークンがすでにある場合は、直接静的ページ/admin/index.htmlに飛びますが、ない場合は、todoistの認証URLを返しています。api/controllers/line-todoist/index.jsvar message; if( !conf.token ){ // 新規登録 conf.state = sourceId + '_' + Buffer.from(crypto.randomBytes(12)).toString('hex'); await writeConfigFile(sourceId, conf); message = { type: "template", altText: "設定", template: { type: "buttons", text: "まだTodoistが設定されていません。", actions: [ { type: "uri", label: "設定ページ", uri: "https://todoist.com/oauth/authorize?client_id=" + TODOIST_CLIENT_ID + "&scope=data:read_write&state=" + conf.state + "&openExternalBrowser=1" } ] } } }else{ // 登録済み message = { type: "template", altText: "設定", template: { type: "buttons", text: "すでにTodoistが設定されています。", actions: [ { type: "uri", label: "設定ページ", uri: ADMIN_PAGE_URL + "?openExternalBrowser=1" } ] } } } return client.replyMessage(event.replyToken, message);上記処理は、LINEチャットから以下を入力することで起動します。
/todoist configちなみに、「/(スラッシュ)」で続くチャットのみ、今回作成するLINEボットで扱うようにしています。
②認可コードをNode.jsサーバにわたす。
todoistのユーザ認証が完了すると、認可コードとともに、静的ページに戻ってきます。
静的ページでは、LINEのID、todoistの認可コード、パスワードとして使うapikeyをNode.jsに渡します。以下の部分です。public/admin/js/start.jsif( searchs.code ){ // todoist認証後の認可コード var state = searchs.state; var code = searchs.code; history.replaceState(null, null, '.'); var apikey = prompt("API Keyを指定してください。"); if( !apikey ) return; // todoistのトークン設定 var param = { code: code, state: state }; do_post_apikey(base_url + '/line-todoist-callback', param, apikey) .then(json =>{ this.apikey = apikey; this.sourceId = json.sourceId; Cookies.set("line_apikey", this.apikey, { expires: EXPIRES }); Cookies.set("line_sourceId", this.sourceId, { expires: EXPIRES }); this.get_config(); }); }else{③認可コードからAPIトークンを取得する
まずは、ブラウザから、POST呼び出しを受け付けるために、HTTP POSTエンドポイントを作成します。
以下の部分です。api/controllers/line-todoist/index.jsexports.handler = async (event, context, callback) => { var body = JSON.parse(event.body); if( event.path == '/line-todoist-callback' ){ ・・・ } };また、swagger.xmlにエンドポイントを定義します。
api/controllers/line-todoist/swagger.yamlpaths: ・・・ /line-todoist-callback: post: security: - apikeyAuth: [] parameters: - in: body name: body schema: $ref: "#/definitions/CommonRequest" responses: 200: description: Success schema: $ref: "#/definitions/CommonResponse"で、認可コードを受け取って、todoistのWebAPIを使ってAPIトークンの取得する処理は以下の部分です。
api/controllers/line-todoist/index.jsconst TODOIST_CLIENT_ID = process.env.TODOIST_CLIENT_ID || "【todoistのClient ID】"; const TODOIST_CLIENT_SECRET = process.env.TODOIST_CLIENT_SECRET || "【todoistのClient secret】"; ・・・ // トークン取得処理 var apikey = event.requestContext.apikeyAuth.apikey; var params = body.state.split('_', 2) var sourceId = params[0]; var conf = await readConfigFile(sourceId); if( !conf ) throw "invalid state"; if( conf.state != body.state ) throw "invalid state"; // トークン取得呼び出し var param = { client_id: TODOIST_CLIENT_ID, client_secret: TODOIST_CLIENT_SECRET, code: body.code }; var json = await do_post("https://todoist.com/oauth/access_token", param ); // sourceIdのConfigファイル更新 if( !conf.apikey ){ conf.apikey = apikey; }else if( conf.apikey != apikey ){ throw 'invalid apikey'; } conf.token = json; await writeConfigFile(sourceId, conf); return new Response({ sourceId: sourceId });④LINEボットがタスク登録するtodoistプロジェクトの選択
静的ページには、todoistに作成してあるプロジェクト一覧が表示されているかと思います。
そのうちから、LINEボットが登録する先のプロジェクトをセレクトボックスから選択して、「Select Project」ボタンを押下します。以下の部分が呼ばれて、project_idがConfigファイルに設定されます。
api/controllers/line-todoist/index.jsif( event.path == '/line-todoist-set-config' ){ // project_id設定要求 var apikey = event.requestContext.apikeyAuth.apikey; var sourceId = body.sourceId; // sourceIdのConfigファイル更新 var conf = await readConfigFile(sourceId); if( conf.apikey != apikey ) throw 'apikey mismatch'; conf.project_id = body.project_id; await writeConfigFile(sourceId, conf); return new Response({});これで、Configファイルに必要な値が埋まりました。
タスク(やることリスト)の登録
LINEチャットで以下のように入力します。
/todo add タスク名以下の部分が呼ばれます。
api/controllers/line-todoist/index.jsconst Todoist = require('todoist').v8; ・・・ if( command.cmd == 'todo' && command.ope == 'add' ){ // タスクの追加 // sourceIdからConfigファイル取得 var conf = await readConfigFile(sourceId); if( !conf || !conf.token ) return client.replyMessage(event.replyToken, { type: 'text', text: 'todoistが設定されていません。' }); // 指定プロジェクトにタスクの追加 const todoist = Todoist(conf.token.access_token); const newItem = await todoist.items.add({ content: command.param, project_id: conf.project_id }); var item_id = newItem.id; return client.replyMessage(event.replyToken, make_item_suggestion(item_id, "やることリストに追加しました。"));Configファイルに保存しておいたtodoistのAPIトークンを使ってタスクを登録しています。
npm node-todoistを利用していますが、使い方は以下を参照してください。romgrk/node-todoist
https://github.com/romgrk/node-todoist「やることリストに追加しました」と返答がかえってきました。
合わせて、続けて設定できるように「アサイン設定」「期限(日付)」がサジェスチョンされてます。
「アサイン設定」は後述の処理を動かすためのポストバック、「期限(日付)」はユーザに日付を選択してもらうための日時選択アクションをサジェスチョンに設定しています。アサイン設定のサジェスチョンを選択すると以下の処理が走ります。
api/controllers/line-todoist/index.jsif( data[0] == 'todo_asign_select'){ // アサイン選択要求(todo_asign_select,item_id) var item_id = parseInt(data[1]); // todoist.sync()呼び出し const todoist = Todoist(conf.token.access_token); // タスクの検索 await todoist.sync(); var task = todoist.items.get().find(item => item.id == item_id); const collaborators = todoist.sharing.collaborators(); const collaboratorStates = todoist.state.collaborator_states; // 指定プロジェクトの共有ユーザリストの抽出 var members = collaboratorStates.filter(item => item.project_id == conf.project_id ); if( members.length > 0){ var message = { type: 'text', text: "アサインする人を選択してください。", quickReply: { items: [] } }; members.forEach(member =>{ const user = collaborators.find(item => member.user_id == item.id); const param = { type: "action", action: { type: "postback", label: user.full_name, data: "todo_asign," + item_id + ',' + user.id, displayText: user.full_name } } message.quickReply.items.push(param); }); return client.replyMessage(event.replyToken, message); }else{ return client.replyMessage(event.replyToken, make_item_suggestion(item_id, "アサインできる人がいません。", (task.due) ? task.due.date : undefined)); }対象プロジェクトの共有者をリストアップし、アサイン候補者としてサジェスチョンを表示してます。
期限(日付)のサジェスチョンを選択した場合、以下の処理が動きます。ユーザが日付を選択した後に呼ばれます。
api/controllers/line-todoist/index.jsif(data[0] == 'todo_duedate'){ // 期限(日付)設定要求(todo_duedate,item_id) var item_id = parseInt(data[1]); const todoist = Todoist(conf.token.access_token); todoist.items.update({ id: item_id, due: { date: event.postback.params.date, lang: 'ja' }}); return client.replyMessage(event.replyToken, make_item_suggestion(item_id, "変更しました。", event.postback.params.date));さっそく、todoistアプリから覗いてみましょう。
上記のタスクが登録されているのではないでしょうか。今日のタスクの一覧取得
タスク一覧を取得します。
操作しやすいように、LINEアプリにチャットとして「/」を入力することで、「今日」「明日」「明後日以降」「期限切れ」「いつか」を選択できるサジェスチョンを表示し、それを選択してもらうと使いやすいかと思いました。「/」がチャットされたときには以下が動きます。
api/controllers/line-todoist/index.jsif( command.cmd == 'default' ){ return client.replyMessage(event.replyToken, make_tasksearch_suggestion());そして、ユーザによりサジェスチョンを選択されると、以下が動きます。
api/controllers/line-todoist/index.jsif( data[0] == 'todo_today' || data[0] == 'todo_tommorow' || data[0] == 'todo_other' || data[0] == 'todo_someday' || data[0] == 'todo_expire'){ // タスクリスト取得要求(todo_xxx) // todoist.sync()呼び出し const todoist = Todoist(conf.token.access_token); await todoist.sync(); const collaborators = todoist.sharing.collaborators(); var items = todoist.items.get(); if( conf.project_id ) items = items.filter(item => item.project_id == conf.project_id); // 対象プロジェクトにフィルタリング // 境界時間の算出 var today = new Date(); today.setHours(0, 0, 0, 0); var todayTime = today.getTime(); // 今日の開始 var tomorrow = new Date(); tomorrow.setHours(0, 0, 0, 0); tomorrow.setDate(tomorrow.getDate() + 1); var tomorrowTime = tomorrow.getTime(); var aftertomorrow = new Date(); // 明日の開始 aftertomorrow.setHours(0, 0, 0, 0); aftertomorrow.setDate(aftertomorrow.getDate() + 2); var aftertomorrowTime = aftertomorrow.getTime(); // 明後日の開始 var target_list; var title; if( data[0] == 'todo_today' ){ // 期限が今日の始めから明日の始め title = "今日"; target_list = items.filter( item => item.due && Date.parse(item.due.date) >= todayTime && Date.parse(item.due.date) < tomorrowTime ); }else if( data[0] == 'todo_tommorow' ){ // 期限が明日の初めから明後日の始め title = "明日"; target_list = items.filter( item => item.due && Date.parse(item.due.date) >= tomorrowTime && Date.parse(item.due.date) < aftertomorrowTime ); }else if( data[0] == 'todo_other' ){ // 期限が明後日の始め以降 title = "明後日以降"; target_list = items.filter( item => item.due && Date.parse(item.due.date) >= aftertomorrowTime ); }else if( data[0] == 'todo_expire' ){ // 期限が今日の始め以前 title = "期限切れ"; target_list = items.filter( item => item.due && Date.parse(item.due.date) < todayTime ); }else{ // 期限が未設定 title = 'いつか'; target_list = items.filter( item => !item.due ); } console.log(target_list); var message = make_todolist_message(title + "のやることリスト", target_list, collaborators); return client.replyMessage(event.replyToken, message);例えば、期限を明日に設定していると、明日を選択すると以下のように、リストの中の1つとして表示されています。
サジェスチョンに、項目番号をいれておきました。それを選択すると、そのタスクの詳細が表示されるようにしました。
以下の部分です。
api/controllers/line-todoist/index.jsif( data[0] == 'todo_detail'){ // タスクの詳細表示要求(todo_detail,item_id) var item_id = parseInt(data[1]); // todoist.sync()呼び出し const todoist = Todoist(conf.token.access_token); await todoist.sync(); var items = todoist.items.get(); const collaborators = todoist.sharing.collaborators(); var notes = todoist.notes.get(); // タスクの検索 var task = items.find(item => item.id == item_id ); var message = make_tododetail_message(task, collaborators, notes); return client.replyMessage(event.replyToken, message);もし、コメントを残しているようであれば、それも表示するようにしています。あと、ラベルも表示するようにしています。
「完了する」というサジェスチョンも用意しておきました。言葉どおり、タスクを完了状態にします。
以下の処理が動きます。api/controllers/line-todoist/index.jsif( data[0] == 'todo_complete'){ // タスク完了要求(todo_complete,item_id) var item_id = parseInt(data[1]); const todoist = Todoist(conf.token.access_token); todoist.items.complete({ id: item_id} ); return client.replyMessage(event.replyToken, { type: 'text', text: "完了にしました" });終わりに
ちょっと説明が少なく、わかりにくかったかもしれません。
機能拡張の機会があれば、その時にもう少し補足しようかな。。。以下も参考にしてください。
AlexaとTodoistでやることリスト・お買い物リスト
Dialogflowと連携してLINE Botを作る
LINE Beaconを自宅に住まわせる以上