- 投稿日:2020-12-27T17:16:11+09:00
glitchからGASにpost通信する
初書:2020/12/27
前書き
GlitchからGoogle App Script(GAS)にPOST通信をしたかったので、その時のメモ。
ちなみに、今回はGlitchを使用しているのでGlitchと書いているが、内部はnode.jsなので、実質node.jsとGASのpost通信と言っても過言ではない(ハズ)glitch側
内部がnode.jsのため、普通のXMLHttpRequestやajaxは使用できない1
そのため、今回はaxiosというのを使用してみるちなみにgoogle検索をかけると、requestというのもあるが、どうやら既に非推奨らしい。
インストール
glitchではpackage.jsonに書き足すだけでいいので、単純にpackage.jsonの"dependencies"の欄に
"axios": "^0.21.0"
を書く。post通信
では実際に通信していく。今回はjsonを使用してデータのやり取りを行うことにする。
const axiosBase = require("axios"); // どこかに書いておく。 const url = "/macros/s/xxxxx/exec"; // gasのドメイン以降のurl const data = {"key" : "value"}; // 送信するデータ const axios = axiosBase.create({ baseURL: "https://script.google.com", // gas以外の場合はそれぞれ書き換え headers: { "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest", }, responseType: "json", }); axios.post(url, data) .then(async function (response) { const responsedata = response.data; // 受け取ったデータ一覧(object) }) .catch(function (error) { console.log("ERROR!! occurred in Backend."); console.log(error); });簡単な説明
urlのところはgasから取得できるurlを入れる。
dataは送信するpostデータを入れる。ちなみにconst設定しているが、後に追加するならletとかの方がいいかもしれないaxios.post(url, data)で送信し、thenで受け取る。エラーがあればcatchへ行く。
受け取ったデータはresponseに入るので、ここで自分の好きなようにデータを扱う。GAS側
GAS側はdoPostで受け取るだけなのだが、通常の
e.parameter
では取得できないため、e.postData.contents
を使用する。function doPost(e) { let parameter = {}; // post値を受け取った時 try{ parameter = JSON.parse(e.postData.contents); }catch(e){ // エラー処理 } // 通常処理 const obj = {}; return ContentService.createTextOutput(JSON.stringify(obj)); }
parameter
に受け取ったpost値が入っているので、これを使用して処理する。
そして最後にobj
をjsonにして返す。終わりに
一度書ければあとは使いまわせる簡単なものなので、とりあえずこれを使おうかなと思う。
ちなみにglitch及びGASは両方触りたてなので、もしおかしいところがあれば突っ込んでください。参考サイト
[axios] axios の導入と簡単な使い方 - Qiita
厳密には専用のnpmをインストールしたら出来そうではあるが、試してない ↩
- 投稿日:2020-12-27T17:12:30+09:00
オウム返しWebチャットボットにQ&Aコミュニケーション機能を追加する
オウム返しWebチャットボットにQ&Aコミュニケーション機能を追加する
はじめに
この記事は前回記事の続きです
https://qiita.com/abemaki/items/cd0bfa99cc2fab4660f5
説明が細かくなりすぎないように今回の記事は完成品のソースコードと
各ソースコードに対して簡易な説明を入れるようにしています。
完成品のサンプル画面はこちら
※herokuなので初回立ち上げ少し遅いです。
https://ponkotsueasychatbot.herokuapp.com/
すべて無料で実現できる
ので、お勉強や検証やアイディア創出に最適かと思います。
この記事でやること
前回作成した単純オウム返しのWebチャットボットに簡単なQ&Aコミュニケーション機能を追加します
Q&Aコミュニケーションのナレッジベースはエクセルで管理します。
全文検索を用いたマッチ度で質問を判定して、回答を返します。
使用するライブラリ と 導入手順
Sheets.js
エクセルを読みこんでプログラム上で利用するために活用するライブラリ。
https://www.npmjs.com/package/xlsxElasticlunr.js
ブラウザ検索およびオフライン検索用のJavascriptの軽量全文検索エンジン。
http://elasticlunr.com/導入方法は前に書いた以下の記事と同様
https://qiita.com/abemaki/items/2f1d5ec7e09ce7371431
※今回は完成品ソースコードを公開しているので、細かい導入手順は割愛。
各ソースに対して簡易な説明を書いています。
エクセル読み込みライブラリ と 全文検索ライブラリでQ&Aコミュニケーションボットを実現するためのイメージ図
ソースコード一式 と 解説
ソースコード一式
https://github.com/makiabe/easyqnawebbot
解説
knowledgeBaseフォルダ
QANDA.xlsx
Q&Aやコミュニケーションのためのナレッジを保持しています。
登録しているデータは以下の通り。
Question Answer こんにちは こんにちは。 お元気ですか? おはよう 今起きたのですね。 おはようございます。 お腹がすいた 僕もお腹がすきました 寒いね 寒いんですか! 風邪ひかないようにしてください。 暑いね 暑いんですか? 僕はロボなので暑さは感じません。 おやすみなさい もう寝るんですか? おやすみなさい。 2020年の流行語大賞は? 2020年の流行語大賞は以下のようになっています。 ■年間大賞 3密(小池百合子さん/東京都知事) ■トップテン 愛の不時着(ヒョンビンさん/俳優) あつ森(あつまれ どうぶつの森)/任天堂株式会社 あつまれ どうぶつの森 開発チームさん アベノマスク(特定非営利活動法人サラダボウルの皆さん) アマビエ(湯本豪一さん) オンライン○○(株式会社 東北新社の皆さん) 鬼滅の刃(吾峠呼世晴さん) GoToキャンペーン(GoToトラベル、イートを活用した皆さん) ソロキャンプ(ヒロシ さん/芸人) フワちゃん(フワちゃん さん/ユーチューバー、芸人) ぴえん 悲しいんですか? 私もぴえんです ソーシャルディスタンスってなんですか? 社会距離拡大戦略は、感染症の拡散を停止または減速させることを目的とした、医薬品を使わない感染抑制のための手段のことです。 社会距離拡大戦略ってなんですか? 社会距離拡大戦略は、感染症の拡散を停止または減速させることを目的とした、医薬品を使わない感染抑制のための手段のことです。 libフォルダ
bot.js
botの基本動作を管理するために作成
※LINE BOT用のコードが少し混じっているので、後日削除します。elasticlunrsearch.js
全文検索ライブラリ・形態素解析ライブラリを活用して
ユーザーからの質問に対し回答を作成するために作成knowlegeBase.js
エクセルからデータを抽出して、QとAにマッピングするために作成lunr.jp.js
lunr.stemmer.support.js
検索エンジンを日本語対応させるために必要TinySegmenterのインスタンス生成処理を若干修正しています
//var segmenter = new lunr.TinySegmenter(); // インスタンス生成
var segmenter = new (require('./tiny_segmenter-0.2.js'))(); // インスタンス生成tiny_segmenter-0.2.js
日本語トークナイザ
requireできずにエラーになるので最終行に以下を追加しました
module.exports = TinySegmenter;
.env
BOTが質問を理解できないと判断するさいの閾値をデフォルトで25%としています。
BOTの確信度が25%を超えている場合、質問に対する回答を返答。
25%を下回る場合、オウム返し。
BOTが質問を正確に理解するために必要な点は以下の2点
・形態素解析の精度を高める
・ナレッジの精度を高める上記以外だと中間閾値で複数選択回答をする等のやり方もありますが
Webでこれを実現する場合、複数選択用のデザインを作成しなければいけないため今回は割愛しました。次回はGoogle Analyticsと連携して
質問の問い合わせの傾向分析 や メンテナンスについて記事を書こうかと思います。
- 投稿日:2020-12-27T07:25:46+09:00
NestJS+ClamscanでセキュアファイルアップロードAPI
はじめに
Webシステムでファイルアップロードする場面って色々ありますよね。
イントラ内の特定利用者が使うシステムならある程度信頼してもいいのかもしれませんが、
コンシューマー向けで不特定多数の人が利用する場合、悪意が無くてもウイルスファイルをアップされるリスクありますよね?
ファイルを受領した側が送信者の言われるがままに、拡張子を変えたりしてマルウェアに感染ってことも想定されます。今ではSassサービスなどもあると思いますが、ここではバックエンドAPIでClamAVを使ったスキャンの例を実装してみます。
前提/環境/注意事項
<前提>
NodeJS / Express / NestJS がある程度分かっている人(細かい説明は割愛するので)<環境>
・Windows10
・VSCode
・DockerDesktop
・Node(Express)
・NestJs<注意以降>
・セキュリティに関わる部分なので、あくまで参考程度にお願いいたします。
実際は、外部から受領するファイルに関して、様々なバリデーション(拡張子、MIME、ファイルヘッダ)が必要となります。
実際のサービスに実装する場合、テストを十分に行い、セキュリティ専門家の監査を受けた上でサービスインする事をお勧めします。準備1(プロジェクトスケルトン作成)
Nest CLIを使い、プロジェクトスケルトンを作成します。
$ nest new uploadfile-viruscheck-example
準備2(ライブラリインストール)
利用するライブラリ(nestjs-clamscan)をnpm install します。
利用するライブラリ(multer)をnpm install します。$ cd fileupload-api $ npm i nestjs-clamscan --save $ npm install multer --save準備3(clamAVサーバー起動)
NestJSはVSCode上でnpmスクリプトからビルド&実行します。clamAVに関しては、Dockerで予め起動させておきます。
$ docker run --name clamav -d -p 3310:3310 quay.io/ukhomeofficedigital/clamav:latest $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES abb51b742e80 quay.io/ukhomeofficedigital/clamav:latest "/docker-entrypoint.…" 44 seconds ago Up 36 seconds 0.0.0.0:3310->3310/tcp clamav停止は docker stop clamav
2回目以降の起動は docker start clamav実装1コントローラー作成
クライアント側から、「multipart/form-data」で送られてきたデータを受け取り、
レスポンスは「HTTP 202 Accepted 」でレスポンスを返します。内部では、ウイルススキャンを実行しファイルを格納します。
NestJSでは一連の処理をデコレーターとインタセプタで記述できるので、Express素で実装するよりコード量が減ります。app.controller.tsimport { Controller, HttpCode, HttpStatus, Param, Post, UploadedFile, UseInterceptors, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { memoryStorage } from 'multer'; import { AppService } from './app.service'; @Controller('api/v1') export class AppController { constructor(private readonly appService: AppService) {} @Post('files/:id') @UseInterceptors( FileInterceptor('file', { storage: memoryStorage(), }), ) @HttpCode(HttpStatus.ACCEPTED) async uploadFile( @Param('id') id: string, @UploadedFile('file') file: Express.Multer.File, ): Promise<void> { await this.appService.scanFile(id, file); } }実装2ウイルススキャン実施部分実装
サービスクラスでウイルススキャンを行います。
今回はclamdjsをNestJSのServiceとして提供されている「Nestjs-clamscan」を利用します。
・npm
・gitapp.service.tsimport { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ClamScanService } from 'nestjs-clamscan'; @Injectable() export class AppService { constructor(private readonly clamScanService: ClamScanService) {} async scanFile(id: string, file: Express.Multer.File): Promise<void> { const result = await this.clamScanService.scanBuffer(file.buffer); if (!result) { console.log('file is bad(infect)'); console.log(`result=>${result}`); throw new HttpException('upload file is bad', HttpStatus.BAD_REQUEST); } console.log('file is good'); } }動作確認(テスト)
動作確認に関しては、定番?のテスト用マルウェア(無害)を用いいます。
これが検知できればOKです。(実際のマルウェアではくれぐれも試さないでください)
今回は、ファイルとしてではなく、Bufferとしてオンコードしました。app.controller.spec.tsimport { Test, TestingModule } from '@nestjs/testing'; import { ClamscanModule } from 'nestjs-clamscan'; import { AppController } from './app.controller'; import { AppService } from './app.service'; describe('AppController', () => { let appController: AppController; const testFileContents = { good: 'test-contents', bad: 'X5O!P%@AP[4PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*', }; beforeEach(async () => { const app: TestingModule = await Test.createTestingModule({ imports: [ ClamscanModule.forRoot({ host: process.env.HOST || '127.0.0.1', port: Number(process.env.PORT) || 3310, }), ], controllers: [AppController], providers: [AppService], }).compile(); appController = app.get<AppController>(AppController); }); describe('uploadFile', () => { it('file is good should be output log "file is good"', async () => { const logSpy = jest.spyOn(console, 'log'); await appController.uploadFile('f0001', createFile(testFileContents.good)); expect(logSpy).toHaveBeenCalledWith('file is good'); return; }); it('file is bad should be output log "file is bad(infect)"', async () => { const logSpy = jest.spyOn(console, 'log'); await appController.uploadFile('f0002', createFile(testFileContents.bad)); expect(logSpy).toHaveBeenCalledWith('file is bad(infect)'); return; }); }); const createFile = (contents: string): Express.Multer.File => { return { fieldname: 'file', originalname: 'file0001', encoding: 'UTF-8', mimetype: 'text/plain', size: Buffer.from(contents, 'utf-8').length, buffer: Buffer.from(contents, 'utf-8'), stream: undefined, destination: undefined, filename: undefined, path: undefined, }; }; });test/app.e2e-spec.tsimport { Test, TestingModule } from '@nestjs/testing'; import { HttpException, HttpStatus, INestApplication } from '@nestjs/common'; import * as request from 'supertest'; import { AppModule } from './../src/app.module'; describe('AppController (e2e)', () => { let app: INestApplication; beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); await app.init(); }); describe('POST /api/v1/files/:id', () => { it('good file is status 202', async () => { return request(app.getHttpServer()) .post('/api/v1/files/001') .attach('file', Buffer.from('test', 'utf-8'), { filename: 'good.txt', contentType: 'text/plain', }) .expect(202); }); it('bad file is status 400', async () => { return request(app.getHttpServer()) .post('/api/v1/files/002') .attach( 'file', Buffer.from( 'X5O!P%@AP[4PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*', 'utf-8', ), { filename: 'bad.txt', contentType: 'text/plain', }, ) .expect(400, HttpException.createBody('', 'upload file is bad', HttpStatus.BAD_REQUEST)); }); }); });その他
ファイルアップロードのバックエンドAPIの基本的な部分を作成してみました。
後は、さらに応用で作りこみですね!要件によっては、ウイルススキャン後にオブジェクトストレージへ格納したり、
データしてインポートしたり等になると思います。
データとしてインポートする際には、スキーマバリデーションなども必要かもしれません。
ファイルメタデータも管理しないと、いつだれが送ったファイルかわかりませんしね。
- 投稿日:2020-12-27T03:48:06+09:00
M1 MacでNode.js(arm64)をNVMを使ってインストールする
最新のNVMとNode.jsをインストールすればいけると思ってたら罠でした。
多くの方は迷わずインストールできると思いますが、arm64版以外のNode.jsをインTL;DL
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash $ nvm install 15これをbashなりzshなりで実行するだけです。現在バイナリが用意されていないので、ソースコードをダウンロードした後にビルドが始まります。
ビルド完了後、node -p process.arch
と入力しarm64
と表示されれば成功です。x64
と表示された方はもう少しお付き合いください。環境
$ uname -a Darwin Mac-mini.local 20.2.0 Darwin Kernel Version 20.2.0: Wed Dec 2 20:40:21 PST 2020; root:xnu-7195.60.75~1/RELEASE_ARM64_T8101 arm64 $ sw_vers ProductName: macOS ProductVersion: 11.1 BuildVersion: 20C69 $ nvm --version 0.37.2arm64に対応しているNode.jsのバージョンは15以上です。
node -p process.arch
でarm64
以外が表示されるPythonのアーキテクチャがx86_64の場合にNode.jsのビルドに失敗します。arm64版のHomebrewからPythonを再インストールし終えたらNode.js v15の再インストールを行ってください。
結果
$node -p process.arch arm64参考
- 投稿日:2020-12-27T00:19:25+09:00
【解決!】Vue-Cliをインストール...できない!
状況
・Vue-Cliを使った本格的なアプリ開発をするためにVue-Cliをインストールしようとした
・過去にもVue-Cliをインストールして使った経験あり!
・コマンドプロンプトを開いて「いざ!やるぞ!」と思っていた矢先、早速出鼻をくじかれた...という話です。起こったこと①
・コマンドプロンプトで下のようにVue-Cliをインストール
npm -g install vue-cli・以下エラーが発生
npm WARN deprecated vue-cli@2.9.6: This package has been deprecated in favour of @vue/cli npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142 npm WARN deprecated coffee-script@1.12.7: CoffeeScript on NPM has moved to "coffeescript" (no hyphen) npm WARN deprecated har-validator@5.1.5: this library is no longer supported npm ERR! code EEXIST npm ERR! path C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue npm ERR! dest C:\Program Files (x86)\Nodist\bin\*vue.cmd* npm ERR! EEXIST: file already exists, cmd shim 'C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue' ->'C:\Program Files (x86)\Nodist\bin\vue.cmd' npm ERR! File exists: C:\Program Files (x86)\Nodist\bin\vue.cmd 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! C:\Users\Owner\AppData\Roaming\npm-cache\_logs\2020-12-24T15_55_11_169Z-debug.log対策①
ポイントは5行目~11行目。
要約すると
「既にインストールしているデータがある時のエラーだよ!」(5行目)
「具体的にはProgram Files (x86)\Nodist\bin\vue.cmd
のことだよ!」(8~9行目)
「このfileを消してもう一回やってみて!」(10行目)
とある。
・冒頭書いた通り、一度Vue-Cliはインストールしたことあるので、その時インストールしたファイルたちが残っていた。
どうもそのファイルが今回のインストールを邪魔している、らしい。
・なので、一旦Program Files (x86)\Nodist\bin
にあるvue.cmd
を消して再度npm -g install vue-cli
を実行起こったこと②
・開発へ進める!と思ったら、またしても以下エラー発生
npm WARN deprecated vue-cli@2.9.6: This package has been deprecated in favour of @vue/cli npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142 npm WARN deprecated coffee-script@1.12.7: CoffeeScript on NPM has moved to "coffeescript" (no hyphen) npm WARN deprecated har-validator@5.1.5: this library is no longer supported npm ERR! code EEXIST npm ERR! path C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue npm ERR! dest C:\Program Files (x86)\Nodist\bin\vue npm ERR! EEXIST: file already exists, cmd shim 'C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue' -> 'C:\Program Files (x86)\Nodist\bin\*vue*' npm ERR! File exists: C:\Program Files (x86)\Nodist\bin\vue npm ERR! Remove the existing file and try again, or run npm npm ERR! with --force to overwrite files recklessly.・さっきと同じようなエラー。よくよく見ると...
npm ERR! EEXIST: file already exists, cmd shim 'C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue' -> 'C:\Program Files (x86)\Nodist\bin\vue' npm ERR! File exists: C:\Program Files (x86)\Nodist\bin\vue・消すべきファイルの名前が変わっている。
対策②
・なるほど。過去インストールしたいくつかのファイルが邪魔して今回インストールできないわけだ、と悟り/同じく今回もvueファイルを消す。
・結局、「消す⇒npm -g install vue-cli
実行⇒エラー(○○を消してね!)⇒消す⇒...」を10回ほど繰り返した
○○に指定されて消したファイルは以下の通り
・こんなに何回もエラーを頻発した理由は、これらのファイルたちはnpm -g install vue-cli
を実行するとまた最新verが生まれてくるということ。
で、その生まれたファイルたちがエラーの原因となってしまうこともあった。
・結論、上図のファイルたちを一気に消してnpm -g install vue-cli
で正常にインストール完了!エラーの原因
・今回のエラーの原因はどうも先ほどお見せしたファイルたちのインストールした日時らしい。
・元々インストールしたファイルたちは2020/12/20にインストールしたモノだったので、これがある限りエラーを発生しまくった。
・そこで上記のようにファイルたちを一気に消してnpm -g install vue-cli
を実行し最新版のファイルが作成され、うまくいった!という流れ。最後に
大したことではないですが、ググって同じようなエラーコード出ている方々はいらっしゃいましたが、ほとんどの方はMacユーザーの方でした。
そういった方の対策案を参考にした自分はこのエラーも出していました。ここで初めてWindowユーザーである自分は"sudo"や"rm"などのLinuxが使えない、ということを学びました。
参考までに。