20210504のNode.jsに関する記事は7件です。

【Node.js+Express+MongoDB on Docker】環境構築 2021 (CTFのNoSQLi練習用サーバー)

これは? MongoDBに対するNoSQL Injectionを題材としたCTFの問題サーバーを用意したく作成したので実用には向いてません。 m1z0r3というCTFチームの勉強会用に作成したので所々m1z0r3とかmizoreとかあります。 https://qiita.com/sho_U/items/43f6483aac8ca45a12f6 の記事を参考に作らせていただきました。 用意するファイルたち 全体像 ├── .env ├── .gitignore ├── Dockerfile ├── challenge │   ├── controller │   │   └── initUserController.js │   ├── index.js │   ├── models │   │   └── User.js │   ├── package.json │   ├── routes │   │   └── index.js │   └── views │   ├── index.ejs │   └── js │   └── main.js ├── data │   └── db (空ディレクトリ) ├── docker-compose.yml ├── secret_file │   ├── db.env │   └── db_init │   └── mongo_init_user.js ├── setup.sh └── src (空ディレクトリ) 各ファイルの中身 .env MONGO_INITDB_ROOT_USERNAME=<mongoDBのrootのユーザー名> MONGO_INITDB_ROOT_PASSWORD=<mongoDBのrootのパスワード> MONGO_INITDB_DATABASE=<mongoDBのデータベース名> .gitignore node_modules/ data/ secret_file/ Dockerfile FROM node:12 WORKDIR /app RUN apt-get update && apt-get install -y vim RUN npm install docker-compose.yml version: '3' services: app: build: ./ container_name: nosqli-web ports: - "3004:3000" restart: always working_dir: /app tty: true volumes: - ./src:/app env_file: - ./secret_file/db.env command: bash networks: - mizore-network depends_on: - mongo mongo: image: mongo:latest container_name: nosqli-db ports: - "3005:27017" restart: always environment: MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME} MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD} MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE} volumes: - ./data/db:/data/db - ./secret_file/db_init/:/docker-entrypoint-initdb.d env_file: - ./secret_file/db.env command: - mongod networks: - mizore-network networks: mizore-network: (このネットワーク名は適当に変える) external: true secret_file/db.env DB_USER=<mongoDBのユーザー名(自分は.envと同じにした)> DB_PASS=<mongoDBのパスワード(自分は.envと同じにした)> DB_NAME=<mongoDBのデータベース名(自分は.envと同じにした)> secret_file/db_init/mongo_init_user.js let users = [ { user: "<mongoDBのユーザー名(これも自分は.envと同じにした)>", pwd: "<mongoDBのパスワード(これも自分は.envと同じにした)>", roles: [ { role: "dbOwner", db: "<mongoDBのデータベース名(これも自分は.envと同じにした)>" } ] } ]; for (let i = 0, length = users.length; i < length; ++i) { db.createUser(users[i]); } challenge/controller/initUserController.js const InitUser = require('../models/User'); const user = () => { let initUser = new InitUser({ username: "admin", password: "m1z0r3{...flag....}" }) initUser.save((error, data) => { if (error) { console.log(error); } console.log(data); }) let initUser2 = new InitUser({ username: "admin", password: "mmmmmmimmmmmmm_mm_mmmmmi" }) initUser2.save((error, data) => { if (error) { console.log(error); } console.log(data); }) let initUser3 = new InitUser({ username: "test", password: "passwd" }) initUser3.save((error, data) => { if (error) { console.log(error); } console.log(data); }) } module.exports = { user }; challenge/index.js const express = require("express"); const app = express(); const bodyParser = require("body-parser"); const routes = require("./routes"); const mongoose = require("mongoose"); mongoose.connect( `mongodb://${process.env.DB_USER}:${process.env.DB_PASS}@mongo:27017/<先程のmongoDBのデータベース名>`, { useNewUrlParser: true, useUnifiedTopology: true } ); // "@mongo" のmongoはdocker-compose.ymlの "mongo:" に対応しているのでlocalhostとかじゃできないので注意 // 後ポートの27017はコンテナ側のポート(":"で区切った時の右の方) // { useNewUrlParser: true, useUnifiedTopology: true } の部分はこれを丸コピ(他のだとうまく行かないという記事をみた) app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.set('view engine', "ejs"); app.use(routes); // 最初 /initUser にアクセスしてmongoDBにユーザー(データとしてのユーザー、mongoDBの認証関連のユーザーじゃない)のデータを入れる。 const initUserController = require("./controller/initUserController"); app.get("/initUser", initUserController.user); app.all("*", (req, res) => { return res.status(404).send({ message: '404 page not found' }); }); app.listen(3000, () => console.log("Listening on port 3004")); // 3000はコンテナの方のポートで、3004はホストで実際に開いてるポート challenge/package.json { "name": "mizore-app", "version": "0.0.0", "private": true, "scripts": { "start": "nodemon ./bin/www" }, "dependencies": { "bcrypt": "^5.0.0", "body-parser": "^1.19.0", "connect-flash": "^0.1.1", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "ejs": "^3.1.5", "express": "~4.16.1", "express-ejs-layouts": "^2.5.0", "express-generator": "^4.16.1", "express-session": "^1.17.1", "express-validator": "^6.7.0", "http-errors": "~1.6.3", "http-status-codes": "^2.1.4", "method-override": "^3.0.0", "mongoose": "^5.11.9", "morgan": "~1.9.1", "nodemon": "^2.0.6", "passport": "^0.4.1", "passport-local-mongoose": "^6.0.1" } } challenge/models/User.js const mongoose = require("mongoose"); const Schema = mongoose.Schema; let User = new Schema({ username: { type: String }, password: { type: String } }, { collection: 'users' }); module.exports = mongoose.model("User", User); challenge/routes/index.js var express = require('express'); var router = express.Router(); var User = require("../models/User"); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); router.post("/login", (req, res) => { let { username, password } = req.body; if(username && password) { return User.find({ username, password }) .then((user) => { if(user.length == 1) { return res.json({logged: 1, message: `Login Successful, welcome back ${user[0].username} : ${user[0].password}` }); } else { return res.json({logged: 0, message: `Login Failed`}); } }) .catch(() => res.json({ message: "Something went wrong" })); } return res.json({ message: "Invalid username or password" }); }); module.exports = router; challenge/views/index.ejs <!DOCTYPE html> <html> <head> <title>NoSQLi Practice</title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1>NoSQLi Practice</h1> <p>Search User here</p> <form action="/login" method="post"> <label for="username">username:</label> <input type="text" id="username" name="username"><br/> <label for="password">password:</label> <input type="text" id="password" name="password"><br/> <input type="submit" value="login"> </form> </body> </html> challenge/views/js/main.js const login = document.getElementById("login"); const response = document.getElementById("response"); login.addEventListener("submit", e => { e.preventDefault(); fetch("/login", { method: "POST", body: new URLSearchParams(new FormData(e.target)) }) .then(resp => resp.json()) .then(data => { if(data.logged) { login.remove(); response.innerHTML = data.message; } else { response.innerHTML = data.message; } }); }); 構築する とりあえず以下のsetup.shを作る。 setup.sh # usage: ./setup.sh <containerID> # sudo rm -rf data/db/* && sudo rm -rf src/* # dc build # docker network create mizore-network # dc run app /bin/bash docker restart $1 && \ docker exec $1 npx express-generator -f --view=ejs && \ docker cp ./challenge/index.js $1:/app/ && echo "[OK] index.js" && \ docker cp ./challenge/package.json $1:/app/ && echo "[OK] package.json" && \ docker exec $1 mkdir /app/models && \ docker cp ./challenge/models/User.js $1:/app/models/ && echo "[OK] models/User.js" && \ docker cp ./challenge/routes/index.js $1:/app/routes/ && echo "[OK] routes/index.js" && \ docker cp ./challenge/views/index.ejs $1:/app/views/ && echo "[OK] views/index.ejs" && \ docker exec $1 mkdir /app/views/js && \ docker cp ./challenge/views/js/main.js $1:/app/views/js/ && echo "[OK] views/js/main.js" && \ docker exec $1 mkdir /app/controller && \ docker cp ./challenge/controller/initUserController.js $1:/app/controller/ && echo "[OK] controller/initUserController.js" && \ docker exec $1 npm install && \ docker-compose up -d && \ docker stop $1 && docker rm $1 && \ docker-compose exec app bash # docker-compose exec app node /app/index.js (コメントアウトしてるやつはコメントアウトされたままでいい) chmod +x setup.sh をした後、まずはdocker networkを以下のコマンドで作成する。 docker network create mizore-network # ネットワーク名はdocker-compose.ymlで定義したやつ 次に、以下のコマンドを実行する。 docker-compose build 最後らへんちょろっと数行赤いエラーが出るが気にしない。そのまま以下のコマンドを実行。 docker-compose run app /bin/bash これをするとコンテナが作成されてそのコンテナにbashで入ると思うので、exitで抜け出し、以下のコマンドを実行してそのコンテナIDをコピーする。 docker ps -a コンテナIDがコピーできたら、先程の setup.sh を以下のようにして実行する。 ./setup.sh <コピーしたコンテナID> うまく行けば、最後の docker-compose exec app bashでbashに入れる。 mongoDBにデータをセット + Webサーバー立ち上げ bashに入ったあと、/app ディレクトリにいると思うので、そのまま以下を実行。 node index.js そうするとエラーがなければ、console.log した Listening on 3004 みたいに表示されるはずなので、http://localhost:3004 にアクセスしてちゃんとサイトが表示されてるか確認する。 この段階ではまだmongoDBの中に何もデータが入っていない状態なので、どんなusername/passwordを入れても "Login Failed" になるはず。ちゃんとサイトが表示されたら、今度は http://localhost:3004/initUser にアクセスしてmongoDBにフラグがパスワードのadminとかのユーザーのデータを入れる(node index.jsしたコンソールにユーザーのデータが表示されたらちゃんとデータが入ったということ)。 その後、/initUserにまたアクセスしちゃうと重複してデータが追加されちゃうので、一回データが入ってることを確認したら、challenge/index.jsのapp.get("/initUser")みたいな所をコメントアウトする。 一通りちゃんと動きそうなら、docker-compose upの際に自動でnode index.jsをしてWebサーバーを起動してほしいので、docker-compose.ymlのcommand:のところを以下のように変更する。 docker-compose.yml app: # ... <略> ... command: bash # 上記を下記に変更!! app: # ... <略> ... command: node /app/index.js mongoDBの中の覗き方 mongoDBとうまく連携できなかったので詰まったので、実際にデータがちゃんと入ってるのか確認するため、mongoDBの中を確認する方法が下記。 まず、mongoの方のコンテナに以下のようにして入る。 docker-compose exec mongo bash これでmongoの方のコンテナのbashに入れるので、その後以下を実行する。 mongo <.envに書いたデータベース名> -u <.envに書いたユーザー名> -p これを実行すると "Enter Password" 聞かれるので、.envで書いたパスワードを入れる。 無事入れたら、以下のコマンド実行すれば代々できてるかどうかわかる。 > show collections # usersとか表示される > db.users.find() # これでフラグがパスワードのadminとか出てきたらちゃんと連携されてる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScript でlambdaを書いてAPI GWとつなぐ(CDK・aws-lambda-nodejs)

TypeScript でlambdaを書いてAPI GWとつなぐ(CDK・aws-lambda-nodejs) github このサンプルコードをベースにします 概要 郵便番号を https://example.com/nayn?postcode=136-0074 のように投げたら。その住所の情報を返してくれるAPIをlambdaでつくる。 コピペして似た構成のシステムのベースとして、また、参考にできるようにシンプルにしたい。 郵便番号APIはこれを使用 https://github.com/madefor/postal-code-api cloneからデプロイまでの手順 cloneする git clone git@github.com:meiyu-inc/apigw-lambda-cdk-ts.git セッティング cd apigw-lambda-cdk-ts/lambda npm install npm test bootstrapする ~/.aws の設定を行う。 npx cdk bootstrap --profile [profile名] デプロイする npx cdk deploy --profile [profile名]。 初回は確認のy/nがでるので、yと入力しましょう。(参考 : https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html) githun actionsでのデプロイ 注意 : 初回デプロイでは同意の確認がでるため、手動でデプロイしましょう 下準備 secretsに AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY を登録しましょう Pull Req 自動テスト cdk diff をおこないます。 master commit デプロイ をおこないます。 コードの構成 TBD 構築方法 githubのものを作るまでの手順 cdkのセッティング TBD 記述 TBD
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Twitter カードの画像(OGP)を PHP で動的に変更する

やりたいこと 自分のホームページを Twitter で宣伝したい。 そのときに Twitter カードという形式で表示したい。 また、表示する画像を幾つか用意して、同じサイトでも違う画像で表示したい。 どうすれば良いのか? まず、Twitter カードの設定が必要。 下記の meta タグを<head></head>のなかに入れます。 <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:site" content="@safetyinternetz" /> <meta property="og:url" content="http://safetyinternet001.ddns.net/" /> <meta property="og:title" content="安全にインターネットを楽しもう" /> <meta property="og:description" content="美少女の画像、動画を安全に楽しむ方法をお伝えします。" /> <meta property="og:image" content="http://safetyinternet001.ddns.net/images/card_images/001.jpg" /> 一つ目が、大きなカードで表示。 二つ目が、自分の Twitter アカウント 三つ目が、ホームページのアドレス。 四つ目が、タイトル。 五つ目が、HPの要約。 六つ目が、Twitter にアドレスを貼ったときに表示される画像。この画像部分を動的に変更したい。 ちなみに画像のパスは、相対パスではなく絶対パスで記述する必要があります。ご注意を。 この情報が入ったホームページを作成し、Card validatorというサイトで登録してみましょう。URL を入れて、Preview cardを押すと、URL を貼ったときに、どのような画像が表示されるかがわかります。 で、画像が 1 つだけの場合はこれで良いんですけど、複数の画像をひとつのサイトにつけたいときに少々問題があるわけです。 もともと svelte でつくったサイトで、svelte で動的に変えてもダメでした。 仕方なく php で変更することに……。 PHP で何をすれば良いのか? OGP は、同じ URL ではひとつの画像しか生成されません。 よって異なる URL として認識させた上で、その URL に応じて画像を設定できればいいわけです。 つまり、http://safetyinternet001.ddns.net/?id=001 このときに 001 の画像を表示し、http://safetyinternet001.ddns.net/?id=002 このときに 002 の画像を表示すればいいわけです。 冒頭に下記のようなコードを入れてみましょう。 <?php if(!$_GET){ $id = '001'; } else { $id = $_GET['id']; } ?> PHP には詳しくないので動くだけのソースですが、GET で ID のパラメータがなければ自動的に 001 を設定、もしパラメータがあれば$idという変数に 002 とか 003 の数値が入るわけです。 さらに、さきほどの head 部分を少し変えてみましょう。 <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:site" content="@safetyinternetz" /> <meta property="og:url" content="http://safetyinternet001.ddns.net/" /> <meta property="og:title" content="安全にインターネットを楽しもう" /> <meta property="og:description" content="美少女の画像、動画を安全に楽しむ方法をお伝えします。" /> <meta property="og:image" content="http://safetyinternet001.ddns.net/images/card_images/<?php echo "$id"?>.jpg" /> まあ画像のアドレスを id という変数にしただけです。 これで、さきほどのCard validatorでhttp://safetyinternet001.ddns.net/?id=201などを入れてみたら、201 の画像が表示されます。 あとは何かしらでhttp://safetyinternet001.ddns.net/?id=001~240とか、自分の用意した画像の分だけアドレスを作成し、BOT に突っ込めば URL を投稿したときに自動的に異なる画像が表示されるようになります。 僕が使っている BOT サービスはBot Birdです。 id=001とかの部分は、好きなようにつくっていいと思います。 僕は無骨に node.js で書いてみました。 const R = require("ramda"); const fs = require("fs"); const rangeArray = R.range(1, 243); console.log(rangeArray); const zeroPaddedArray = rangeArray.map((item) => item.toString().padStart(3, "0")); const write = (item) => { fs.appendFileSync( "./out.text", `http://safetyinternet001.ddns.net/?id=${item}` + "\n", (err) => { if (err) throw err; console.log("text追記"); } ); }; zeroPaddedArray.map((item) => write(item)); 普通に画像の個数(今回は 243 個)分の配列を作成し、padStartでゼロ埋めをし、テキストファイルに書き出すだけです。 ここで 243 個と画像が決まっていれば、php のほうで 243 以上の数字、あとは他の文字列だったら 001 にするとか、そういう処理を入れてもいいかも。 そんな感じで、1 時間に 1 回、違う画像で自分のホームページを Twitter で宣伝できるようになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud Foundry(IBM Cloud)でNode.jsのバージョン指定【.nvmrc】

Node.jsアプリケーションをそのままデプロイしようとすると以下の警告が出ます。 **WARNING** Node version not specified in package.json or .nvmrc. See: http://docs.cloudfoundry.org/buildpacks/node/node-tips.html Attempting to install: 10.24.0 package.jsonか.nvmrcに記述しろと書いてありますね。 Support .nvmrc file as an alternative to engine.version setting in package.json 前に書いた記事ではpackage.jsonのenginesに記載するやり方でしたが、今回は.nvmrcに記述します。 Cloud Foundry(IBM Cloud)でNode.jsの最新バージョンを利用する .nvmrcを作成してデプロイ 今更ですがnvmを内部で使ってたんですね。 macなどの場合はプロジェクトのルートで以下を実行します。 $ node -v >> .nvmrc これで.nvmrcが作成されてローカルのバージョンが.nvmrcに記載されます。 ただ最新は動かない場合が多い 先程のコマンドを実施した.nvmrcの中身はv16.0.0でした。 v16.0.0 この状態でデプロイしたら以下のエラーが発生 **ERROR** Unable to install node: no match found for 16.0.0 in [10.23.3 10.24.0 12.20.2 12.21.0 14.15.5 14.16.0 15.11.0 15.12.0] 2021-05-04T02:26:19.14+0900 [STG/0] ERR Failed to compile droplet: Failed to run all supply scripts: exit status 14 まぁつまりv15.12.0までしか対応してないよとのことです。 v15.12.0 と変更してデプロイしなおすとエラー回避できます。 時期によって対応状況は違うので、まずは最新版でデプロイして出たエラーメッセージにどのバージョンで対応済みかを知るのが良いかもしれません。 最新を使いたい時はbuildpacks指定の方法も Cloud Foundry(IBM Cloud)でNode.jsの最新バージョンを利用するの記事にあるbuildpacksでURL指定などをすると最新が使えるかもしれません。 所感 package.jsonのenginesに指定していると他の環境で利用しようとした時や利用パッケージによって変な挙動が発生することがあるので、pacakge.jsonのenginesに指定よりは楽な印象です。 個人的に今後も触ることがあれば.nvmrcに記載の方を選ぶ気がします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud Foundry(IBM Cloud)にDiscord BOTをデプロイしようとした際にハマったメモ

手元で作成したDiscord BOTのNode.jsアプリケーションをCloud Foundry(IBM Cloud)にデプロイする際にハマりました。 デプロイ時間長すぎて検証に時間かかるのが大変...... おさらい: ログの調べ方 デプロイや起動に失敗した際は $ ibmcloud cf logs アプリ名 --recent でエラーログを見れます。 ERR Failed to make TCP connection to port 8080: デプロイ時後の起動時にエラーが発生してました。 2021-05-04T02:37:11.28+0900 [HEALTH/0] ERR Failed to make TCP connection to port 8080: connection refused (out of memory) 2021-05-04T02:37:11.28+0900 [CELL/0] ERR Failed after 1m0.927s: readiness health check never passed. 待ち受けるサーバーが無いよと怒られてる模様です。 Discord.jsを利用してますが、このライブラリはHTTP サーバーを立てる方式ではない模様です。 Discord.js does not expose any kind of port nor a http server. 参考: https://github.com/discordjs/discord.js/issues/3577 以下のようなDiscrod.jsのサンプルのままだとうまく起動してくれませんでした。 const Discord = require('discord.js'); const client = new Discord.Client(); client.on('ready', () => { console.log(`Logged in as ${client.user.tag}!`); }); client.on('message', async msg => { if (msg.content === 'ping') { msg.reply('Pong!'); } }); client.login(process.env.DISCORD_TOKEN); ちなみにこのときのmanifest.yamlはこんな感じです。 manifest.yaml applications: - name: アプリ名 random-route: true memory: 64M command: npm start 解決策1: expressを追加してサーバーを起動 待ち受けるサーバーが無くて怒られてるのでexpressを追加します。 const Discord = require('discord.js'); const client = new Discord.Client(); client.on('ready', () => { console.log(`Logged in as ${client.user.tag}!`); }); client.on('message', async msg => { if (msg.content === 'ping') { msg.reply('Pong!'); } }); client.login(process.env.DISCORD_TOKEN); /*expressを追加*/ const express = require('express') const app = express() app.get('/', function (req, res) { res.send('Hello World') }) app.listen(process.env.PORT || 3000); これでOKです。 解決策2: manifest.yaml(の変更でできる気がする) ProcessのTypeがWebでは無く別の指定にしたり、no-route: trueなども試してサーバーを追加しなくても起動するようにしたかったけどエラー解消されなかったです汗  参考: https://docs.cloudfoundry.org/devguide/deploy-apps/manifest-attributes.html 一旦諦め。 ERR Killed こちらもアプリ起動後に発生したエラーです。 [2021-05-04T10:52:45.49+0900 [APP/PROC/WEB/0] ERR Killed 例えば、 OOM Killed とあれば「メモリー不足」を意味し、コンテナーがリソース制限によってクラッシュしている 参考: https://www.ibm.com/docs/ja/bluemix_stage/containers/cs_troubleshoot_clusters.html?view=kc みたいな話をググったら見つけたので64Mに指定してメモリを128Mにしてみました。 manifest.yaml applications: - name: アプリ名 random-route: true memory: 128M command: npm start たまたま感ありますがこれで解決出来ました。 express入れた分でメモリ多く使ってしまったのかもしれません。 ひとりごと。 デプロイの時間が長すぎる問題 Cloud Foundryのmanifest.yamlだけでどうにかなる気がしてますが、検証しようにもデプロイの時間が長すぎる問題があります。 デプロイボタン押したらモンハンで一狩り行ってました。それくらい遅い。 ちゃちゃっと試したいのに時間長すぎるので拘りなければHerokuがいい気がするなぁ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINEBOTとCOTOHA APIで自分を励ましてみた。

最初に LINE BOTの勉強がてら、NTTコミュニケーションズのCOTOHA APIを使ってみたので、やった事メモを残しておく。 環境 MacOS Catalina 10.15.7 Node.js 16.0.0 android 11 LINE設定 まず、LINEです。LINEアカウントを取得し、LINEデベロッパーに登録。 プロバイダーを作成 MessagingAPIの新規チャネルを作成 アンケートと規約に合意する。 webhook設定で、ngrokで作られる自分のPCの公開用URL+/webhookを指定する。 参考 COTOHA設定 COTOHA apiのデベロッパー登録を行う。 これでclientIdとClientSecretを取得できる。 さらにアクセストークンが必要なので、curlで行った。 アクセストークンの有効時間は24時間。 24時間経ったら取り直し。 curl -X POST -H "Content-Type:application/json" -d '{ "grantType": "client_credentials", "clientId": "[clientId]", "clientSecret": "[clientSecret]" }' https://api.ce-cotoha.com/v1/oauth/accesstokens 参考 本家 https://api.ce-cotoha.com/contents/index.html NGROK設定 LINEのWeb-hookを自分のPCに通すためNGROKを使用した。 以下インストールと起動。 nom i g ngrok ngrok http 3000 参考 https://qiita.com/Marusoccer/items/7033c1bb9c85bf6789bd Node.js アプリ 作ったアプリはこんな感じ。 server.js 'use strict'; // ######################################## // 初期設定など // ######################################## // パッケージを使用します const express = require('express'); const line = require('@line/bot-sdk'); const axios = require('axios'); const { message } = require('statuses'); const { response } = require('express'); // ローカル(自分のPC)でサーバーを公開するときのポート番号です const PORT = process.env.PORT || 3000; // Messaging APIで利用するクレデンシャル(秘匿情報)です。 const config = { channelSecret: 'foo', channelAccessToken: 'foo' }; // ########## ▼▼▼ サンプル関数 ▼▼▼ ########## const sampleFunction = async (event) => { let postconfig = { headers: { 'Content-Type' : 'application/json;charset=UTF-8', 'Authorization' : 'curlで取得したCOTOHAのアクセストークン', } }; let postdata = { "sentence" : event.message.text }; let URL="https://api.ce-cotoha.com/api/dev/nlp/v1/sentiment"; try { await axios  .post(URL, postdata, postconfig)  .then(response =>{ const a = JSON.parse(JSON.stringify(response.data.result)); console.log(a); switch (a.sentiment){ case 'Negative': pushtext = "頑張ろー"; break; case 'Positive': pushtext = "ノリノリですね" break; default: pushtext = "何か面白い事はないのですか。" } }); } catch (error) { pushtext = '検索中にエラーが発生しました。ごめんね。'; // APIからエラーが返ってきたらターミナルに表示する console.log(error); } return client.pushMessage(event.source.userId, { type: 'text', text: pushtext }); }; // ######################################## // LINEサーバーからのWebhookデータを処理する部分 // ######################################## // LINE SDKを初期化します const client = new line.Client(config); let pushtext=""; // LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます async function handleEvent(event) { // 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視します if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } // サンプル関数を実行します return sampleFunction(event); } // ######################################## // Expressによるサーバー部分 // ######################################## // expressを初期化します const app = express(); // HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします app.post('/webhook', line.middleware(config), (req, res) => { // 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行 if (req.body.events.length === 0) { res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します(なくてもよい) console.log('検証イベントを受信しました!'); // ターミナルに表示します return; // これより下は実行されません } else { // 通常のメッセージなど … Webhookの中身を確認用にターミナルに表示します console.log('受信しました:', req.body.events); } // あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、 // 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result)); }); // 最初に決めたポート番号でサーバーをPC内だけに公開します // (環境によってはローカルネットワーク内にも公開されます) app.listen(PORT); console.log(`ポート${PORT}番でExpressサーバーを実行中です…`); 結果 会話するとこんな感じ。 cotohaの感情分析APIは文章をNegative、Positive、Neutralの3つに分類する。 今回は、Positiveなら、「ノリノリですね。」、Negativeなら「頑張ろー」、Neutralなら「何か面白い事はないのですか。」と返してみた。 「今日は彼女ができた。」はNeutralと判定されている。どうも、好きとか最高とかの形容詞がないとPositive判定されないようだ。 今回どハマりしたのは、COTOHAのレスポンスを解釈するところ。 結局下記URLの対処で切り抜けた。 もう一工夫欲しいかもだけど、今日はここまで。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでGoogle Spread Sheetsを読み取る 【Sheet API v4】

Google Sheet API v4版です。手順など定期的にアップデートしてますが、改めてまとめてみました。 過去記事: Node.jsでGoogle SpreadSheetsを操作してみよう。【GAS不使用】 async/awaitで書いたり、モジュールも使ったりだいぶシンプルに出来るようになってきました。 GAS不要なのでNode.jsでやりたい人向けですね。 環境 Node.js v16.0.0 googleapis v73.0.0 google-auth-token-generator v0.1.2 手順 作業フォルダ&利用モジュール準備 $ mkdir sheet $ cd sheet $ npm init -y $ npm i googleapis google-auth-token-generator credentialsファイルを作成 公式のNode.js quickstartを見てcredentials.jsonをDLしましょう。 アプリケーション種類はデスクトップアプリにします。 client_secret.jsonやclient_secret_xxxxxxx.jsonといった名前の場合もありますが、credentials.jsonにリネームします。 また、先ほど作成したsheetフォルダにcredentials.jsonを設置します。 tokenの生成 パーミッションを選択してtokenを作成します。 $ npx google-auth-token-generator 今回は既存のシートの読み取りだけしたいので、パーミッション選択ではspreadsheets.readonlyを選択します。 認証など進めるとtoken.jsonが生成されます。 Sheet APIでスプレッドシートの値を見る サンプルコードが以下です。 'use strict'; const {google} = require('googleapis'); const {oAuth2ClientGen} = require('google-auth-token-generator'); (async () => { const auth = oAuth2ClientGen({library: google}); const sheets = google.sheets({version: 'v4', auth}); try { const res = await sheets.spreadsheets.values.get({ spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', range: 'Class Data!A2:E', }); const rows = res.data.values; if (rows.length) { console.log('Name, Major:'); // Print columns A and E, which correspond to indices 0 and 4. rows.map((row) => { console.log(`${row[0]}, ${row[4]}`); }); } else { console.log('No data found.'); } } catch (error) { console.log('The API returned an error: ' + error); } })(); 実行 $ node app.js Name, Major: Alexandra, English Andrew, Math Anna, English Becky, Art Benjamin, English Carl, Art Carrie, English Dorothy, Math Dylan, Math Edward, English Ellen, Physics Fiona, Art John, Physics Jonathan, Math 実態は公式チュートリアルにもあるこちらのシートの内容を読み込んでます。 https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit?usp=sharing よもやま google-auth-token-generatorは自作モジュールなので公式の手順と若干異なりますが、YoutubeやGmailなどGoogle系のAPIで同じ処理を書くのが辛いのでnpmパッケージ化してみました。 今回cliツールだけじゃなくてrequireできるモジュールとしてもアップデートさせてみましたが個人的にはめちゃ便利です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む