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

npm run buildとは

はじめに Reactでアプリを作る際、create-react-appと同様にターミナルで行っている、Node.jsのbuild toolである「npm run build」 なんとな〜くの理解で機械的に行っていたので、その概要を整理する npm run buildは何をしているのか ずばり「JSファイルやCSSファイルをマージ(合併)している」 ではなぜマージするのか? 開発段階では以下のように1つのアプリに対しJSやCSSファイルが複数存在することがほとんど css/ mpp.css design.css visuals.css ... js/ service.js validator.js container.js ... しかしアプリの運用環境では複数のファイルが存在することはサイトの読み込みが遅くなることに繋がります (ユーザーがサイトにアクセスすると、各ファイルに追加のHTTPリクエストが必要になる為) なのでこの複数存在するファイルをマージしbuildという小さなディレクトリに圧縮する魔法のコマンド、それが「npm run build」 実行すると、、 build/ static/ css/ main.css js/ main.js buildディレクトリが作成されスッキリ これによりユーザーが取得するファイルの数とサイズを最小限に抑えることができました おわりに buildによるサイト表示速度改善問題 しかし規模の大きい開発ならまだしも、個人開発レベルでは恩恵は皆無といっていいそうな
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js用語

用語 npm Node.jsインストール時に付属するパッケージマネージャー Node.jsのパッケージの管理や実行を取り扱う npx npmパッケージを簡単に実行できるコマンド run-scriptを使用せずにローカルインストールしたコマンドを実行する グローバルインストールしていないコマンドを実行することができる GitHubやGistで公開されているスクリプトを実行することができる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】Express-generatorでwebアプリを開発する手順。

express-generatorを使ったwebアプリ開発の手順をメモっときます。 基本的にはExpress のアプリケーション生成プログラム の通り。 generatorをnpm経由でインストール npm install express-generator -g -gオプションをつけて、グローバルにインストールします。 コマンド確認 どんなコマンドがあるか知っておくと良いかも。 express -h //実行結果 Usage: express [options] [dir] Options: --version output the version number -e, --ejs add ejs engine support --pug add pug engine support --hbs add handlebars engine support -H, --hogan add hogan.js engine support -v, --view <engine> add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) --no-view use static html instead of view engine -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory -h, --help output usage information アプリ作成 express --view=pug myapp これでmyappというフォルダができる。 --viewのところはテンプレートエンジンを記述。 デフォルトはpug(Jade)が使われており、--view=ejsとかにすればEJSを使える。 テンプレートエンジンが不要な場合は--no-viewでOK。 npm -i 依存関係をインストールします。 npm -i または npm install npm start npm start これでサーバーが起動します。 http://localhost:3000/にアクセスすれば表示されるはず。 あとはガンガン開発していけばOKですね。簡単!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sequelize-cliのmigrationディレクトリを変更したい

毎回忘れるので書いておく。 まとめ $ npx sequelize migration:generate \ --config db/config.js \ --migrations-path db/migrations \ --seeders-path db/seeders \ --name user または、.sequelizercに書き出す。 s.eedersrc module.exports = { 'config': 'db/config.js', 'migrations-path': 'db/migrations', 'seeders-path': 'db/seeders', } バージョン sequelize-cli v6.2.0 調べるとき ヘルプに書いてある。 $ npx sequelize-cli migration:generate --help Sequelize CLI [Node: 15.11.0, CLI: 6.2.0, ORM: 6.6.2] オプション: --version バージョンを表示 [真偽] --help ヘルプを表示 [真偽] --env The environment to run the command in [文字列] [デフォルト: "development"] --config The path to the config file [文字列] --options-path The path to a JSON file with additional options [文字列] --migrations-path The path to the migrations folder [文字列] [デフォルト: "migrations"] --seeders-path The path to the seeders folder [文字列] [デフォルト: "seeders"] --models-path The path to the models folder [文字列] [デフォルト: "models"] --url The database connection string to use. Alternative to using --config files [文字列] --debug When available show various debug information [真偽] [デフォルト: false] --name Defines the name of the migration [文字列] [必須] --underscored Use snake case for the timestamp's attribute names [真偽] [デフォルト: false]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)を Node.js で動かすために公式情報をたどる&実際に試す

タイトルの通りの内容で、以下の記事を書いた際にやった「ブラウザ上での実行」の部分を、「Node.js での実行」にしたものです。 Teachable Machine について等の話はこの記事では省略しますので、それらに関する情報をご覧になりたい方は以下の記事内のものをご覧ください。 ●Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)をダウンロードしてブラウザで動かす【2021年4月版】 - Qiita  https://qiita.com/youtoy/items/3be502c6266cfb61e49a まずは公式情報をたどる 冒頭に書いた記事では、Teachable Machine で出力した画像分類用の機械学習モデル(TensorFlow.js用)をローカルにダウンロードし、それを用いた推論をブラウザ上で行いました。JavaScript の処理を含む HTMLファイルを使った形です。 今回は、推論を行うプログラムを Node.js で実行します。そのやり方については、公式で掲載された FAQ の情報からたどって探していきます。 公式のよくある質問から情報をたどる Teachable Machine のページの上部メニューを見ると、右のほうに「よくある質問」と書かれた部分があります。そしてその中を見ていくと「保存とエクスポート」と書かれた部分があり、さらにその中で「他のライブラリやプラットフォームでも Teachable Machine のモデルを使用できますか?」という質問がありました(※以下の画像の部分)。 上記の画像中の文章で、「Teachable Machine コミュニティの Github リポジトリをご覧ください。」とあるので、その Github リポジトリのページへと進んでいきます。 Teachable Machine コミュニティの Github リポジトリ 上記の Github リポジトリを見ると、「Community Contributions and Projects」という項目の中で、以下の画像の記載を見つけました。 この「Teachable Machine Node Library for image models」という部分をたどっていきます。 プロジェクトのページとその先 上記の続きで「tr7zw/teachablemachine-node-example」というページにたどり着きました。その説明を見ていこうとすると、以下の画像にあるように「最新版はまた別のページにある」という記載と誘導用のリンクが・・・。 上記のリンク先は「drinkspiller/teachablemachine-node-example」というページでした。さらにこの先へ誘導される、ということはなさそうです(笑) このページで「How It Works」と書かれた部分を見てみます。 リクエストに対して、JSON でレスポンスを返すような動きをしてくれそうな記載がありました。 サンプルを動かしてみる 上記の記載があった箇所の少し下を見ると、使い方などが書かれた部分がありました。 その手順通りに進めてみます。 インストール リポジトリの内容をクローンして、パッケージのインストールを行います。どこか適当なフォルダで、以下のコマンドを実行していきましょう。 # クローン git clone https://github.com/drinkspiller/teachablemachine-node-example.git # パッケージのインストール cd teachablemachine-node-example/ npm install 自分の環境で実行したところ、以下のエラーが・・・。 少し調べてみると、自分が Node.js の v15 を使っているのが関係していそうで、Node.js を v14 にすると良さそうでした。Node.js のバージョン管理ツールを使っていたので、サクッと v14 に変更してから再度 npm install を実行して、その後は無事にインストールがエラーもなく完了となりました。 なお、今回の内容の package.json の中身を見てみると、dependencies は以下となっているようです。 app.js の書きかえ 次の手順は、app.js の書きかえです。 以下の部分を見ると、モデルの URL の指定方法について書かれており、ファイルシステムのパスは利用不可と書いてあります。 そして、デフォルトの設定として、事前に用意されたモデルの URL が別の行に書いてありました。具体的には以下の部分です。 この部分を独自の機械学習モデル用の設定に置きかえます。 Teachable Machine の機械学習モデルをクラウドにアップロードするやり方をとっていた場合は、そのアップロード先の URL を上記の部分に書くだけで大丈夫です。 そのやり方がシンプルな方法ではありましたが、今回は、冒頭に書いた前の記事と同様に、ローカルに機械学習モデルの関連ファイル一式をダウンロードするやり方ができないかを試していきました。 手順が少なくてすみそうなやり方で進めてみます。適当にフォルダを作成し、その中にダウンロードした機械学習モデル関連のファイル一式を置きましょう(ここで「関連ファイル」と書いた部分の詳細は、冒頭に掲載した記事の「モデルのダウンロード」の部分をご参照ください)。 そして、そのファイル一式を置いたフォルダで、サーバーを立ち上げます。冒頭に掲載した記事の「機械学習モデルのファイルに関する準備をする(+ローカルでサーバーを動かす)」の部分にも書いている、ワンライナーでサーバーを立ち上げるコマンドを使うのが簡単かと思います。 以下にコマンドの部分のみ記載しますが、どれを使っても http://localhost:8080/ でローカルにサーバーが立ち上がります。 # 2.x系 $ python -m SimpleHTTPServer 8080 # 3.x系 $ python -m http.server 8080 # npmのバージョン5.2.0で導入された「npx」を利用 $ npx install http-server このコマンドを機械学習モデル関連のファイルが置かれた場所で実行していれば、 http://localhost:8080/ にアクセスすることで、機械学習モデル関連のファイルを参照可能になります。app.js の中の URL を指定する部分は、以下のように書きかえれば OK です。 function configureEndPoints() { addEndpoint('test', 'http://localhost:8080/'); } addEndpoint('test', ... の部分は、この後の手順に関わってくる設定ですが、デフォルトのままで使っていきます。 以下に、ここで用いたソースコード全体を掲載します(サンプルをほぼそのまま使ったような感じですが)。 const canvas = require("canvas"); const express = require("express"); const JSDOM = require("jsdom").JSDOM; require("@tensorflow/tfjs-node"); const tmImage = require("@teachablemachine/image"); const app = express(); function init() { configureBodyParser(); configureEndPoints(); configureBrowserPolyFills(); app.listen(3000, () => { console.log("Server running on port 3000"); }); } async function addEndpoint(name, baseUrl) { const modelURL = baseUrl + "model.json"; const metadataURL = baseUrl + "metadata.json"; const model = await tmImage.load(modelURL, metadataURL); app.post("/" + name, (request, response) => { const base64Image = Buffer.from(request.body).toString("base64"); const contentType = request.get("Content-Type"); getPrediction(model, base64Image, contentType, (output) => { response.send(output); }); }); } function configureBodyParser() { app.use(require("body-parser").raw({ type: "image/jpeg", limit: "3MB" })); } function configureBrowserPolyFills() { global.window = new JSDOM(` <body> <script> document.body.appendChild(document.createElement("hr")); </script> </body>`).window; global.document = window.document; global.fetch = require("node-fetch"); global.HTMLVideoElement = class HTMLVideoElement {}; } function configureEndPoints() { addEndpoint("test", "http://localhost:8080/"); } async function getPrediction(model, imageData, contentType, responseFunction) { const imageCanvas = canvas.createCanvas(64, 64); const canvasContext = imageCanvas.getContext("2d"); const canvasImage = new canvas.Image(); canvasImage.onload = async () => { canvasContext.drawImage(canvasImage, 0, 0, 64, 64); const prediction = await model.predict(imageCanvas); console.log(prediction); responseFunction(prediction); }; canvasImage.onerror = (error) => { throw error; }; canvasImage.src = `data:${contentType};base64,` + imageData; } init(); Node.js でプログラムを実行 ここまで準備ができたら、app.js を実行します。 node app.js を実行すると以下のような出力がされて、先ほどローカルで実行したサーバーとは別のサーバーが待ち受け状態になります(こちらのサーバーのポート番号は、app.js のデフォルト設定を変更せずにいた場合 3000番になります)。 画像をポストして結果を得る あとは、先ほどの app.js を実行して立ち上がったサーバーのほうに、画像を POST で送信すれば OK です。今回参照している GitHub のリポジトリのページでは、curl を利用した方法のコマンドの例が掲載されています。 自分は Mac で試しているので、上記の上側のコマンド例を利用しました。とりあえず動作確認ができれば良かったので、そのコマンド(※以下)をそのまま流用します。 以下のコマンドで、POST する画像のパスが '@./sample_images/person-using-iphone-1194760.jpg' となっていますが、これは今回クローンしたリポジトリのファイル・フォルダ一式の中で、app.js と同じ階層にあるフォルダの中に置かれた画像の 1つを指定しているようです。 $ curl -X POST -H "Content-Type: image/jpeg" --data-binary '@./sample_images/person-using-iphone-1194760.jpg' http://localhost:3000/test リポジトリのほうで具体的に見ていくと、 teachablemachine-node-example/sample_images/ の中にある画像のうちの 1つです。 これをそのまま流用するために、上記の curlコマンドを実行する場所は app.js と同じ階層にします。これで既に用意されている画像を流用して POST を実行できそうです。試してみた際の出力は、以下となりました。 上記のとおり、レスポンスとして推論の結果が JSON形式で得られました。 今回使ったモデルは「Class 1、Class 2」という、Teachable Machine のデフォルト設定のクラス名を使った 2つのクラスを用意していたのですが、出力ではそれらのラベル対する confidence の値が出力されたのが確認できたと思います。 終わりに 今回、Teachable Machine で出力した画像分類用の機械学習モデル(TensorFlow.js用)を Node.js で動かすことができました。 個人的には「もう少しだけ手順をシンプルにできないかな」と思う部分があるので、改善の余地がないかをもう少し見ていこうと思っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript + Node.js(Express)でポートフォリオを作ってみた

はじめに フロントエンドはJavaScript、Bootstrap、バックエンドはNode.js(FW:Express)といった構成です。 この記事では、アプリ開発にあたって苦労した点や、 各機能実装の際に参考にした記事や教材についてもご紹介していければと思います。 アプリの概要 LINEをプラットフォームに「自動予約ツール」をテーマにしたアプリです。 日本国民の8000万人が使っているLINEで店舗の予約を自動受付 LINE上で予約の取得、次回予約の確認、予約のキャンセルが可能であり、ユーザーが簡単に操作できる 制作背景 私の現職(医療、サービス)での課題が、プログラミングを学習スタートさせたきっかけです。 Web制作会社と連携して、新規顧客の獲得のため、SEO、MEO対策に取り組みました。 その効果は大きく、アクセス数は増加し、新規顧客の多くはHPから獲得することができ、 日々、公式LINE、メールにてたくさんの患者さんからのご予約、問い合わせが寄せられます。 しかし、ここで問題が発生・・・ メールや公式LINEでの問合せが増加した事で、スタッフの対応が遅延しクレームに繋がる事もあったのです。 自動予約を受付する機能を作って!!と会社に提案しましたが、見事通らず。 エンジニアの友人に話を持ちかけた時に、自分で作ればいいんじゃない?と言われ、 たしかに(笑)となったことが背景となります。 使用画面のイメージ 予約の取得/次回予約の確認/予約のキャンセル 管理画面でのCRUD このアプリの特徴 リッチメニューを選択することで FlexMessageにより自動対話で選択されるボタンを配置して、 どんな操作が可能なのか直感的に認識してもらえるようにしました。 予約の取得、次回予約の確認、予約のキャンセルがLINE上で操作 管理画面でユーザーネーム、施術時間の可変を行える 使用技術 フロントエンド HTML / EJS / Bootstrap / CSS JavaScript バックエンド Node.js FW:Express LINEMessagingAPI インフラ Heroku PostgresSQL その他 Fontawesome Postman ngrok FlexMessage Simulator バックエンドのロジックはExpressでプログラミングし、 フロントの大枠はBootstrapとCSSで整え、JavaScriptでのコーディングもしています。 LINEBOTは適宜、動作確認が必要となりますのでngrokを使いました。 ローカルPC上で稼働しているネットワーク(TCP)サービスを外部公開できるサービスです。この存在を知り、開発がスムーズになりました。 選定理由 実装したい機能として3点ありました。 管理画面にて施術時間を可変、ユーザーの削除ができる 予約を自動対話で行えるようにする  ⑴来店希望日を表示  ⑵希望時間帯の表示 ユーザーが次回予約の確認、キャンセルをLINE上で行える このような機能があれば、LINEBOTでの自動予約受付として成立すると考えました。そして、そのために必要な技術を自身なりに調べてみたところ、フロントとバックを両方で活用できるのがJavaScriptである事がわかり、現役のエンジニアの方にもDMで実現可能か確認して、JavaScriptで実装することを決めました。 システムの全体像 今回の趣旨としてLINEBOTを制作することなのでインフラは公開が初学者でも扱いやすそうなHeroku、データベースはHerokuのアドオンであるPostgresSQLを選択。 機能一覧 CRUD処理 患者さんによって治療内容や時間が異なるケースがあるため、管理者が施術時間を変更できる LINEに登録している、ユーザーネームが管理画面に表示され、登録されたLINE登録している名前で、誰が予約をしたのか判断できない場合もあるため、管理者が変更できるようにした リッチメニューから予約の取得 リッチメニューから次回予約の確認 患者さんから次回の予約はいつでした?と問い合わせがあったことがきっかけで、LINE上で確認がとれたら楽になると思い実装し、従業員の負担も軽減できる事も考慮しました。 リッチメニューから予約のキャンセル 就業時間外でのキャンセルもあるため、自由にキャンセルできるように機能をつけました。 ドラック&ドロップ 管理画面にて、施術時間を表示したカードをドラック&ドロップできる 苦労したこと 選んだ日付、選んだ時間帯の内容をpostback.dataに記録していかなければならなかった点です。ここをアンパサンド(&)で数珠繋ぎのように過去の選択を記録していく方法をとりました。 また、次回予約を確認する時に、postback.dataで返ってきたデータが全て文字列型なので時間の表示を全て数字型に変換する点に苦労しました。 データベースを今まで学習してこなかったため、設計がさっぱりわからずでした(笑) MVCモデルの理解 知識定着のために、Qiitaに投稿しました。 参考にした学習教材 / 参考サイト 基本的にドットインストールとUdemyの動画教材でおおまかな流れをインプットし、公式ドキュメントで必要なメソッドを検索しながら行いました。またQiitaの記事で環境構築などとても参考になりました。 またMENTAのサービスで実装したいアプリに特化したメンターを契約し適宜、分からない点など相談にのっていただきました。本当にお世話になり、返信もはぐれメタル級に早く、感謝の気持ちでいっぱいです。 Node.js / Express 【Udemy】 Node.js速習講座 Part1 導入&基礎編 【Udemy】 Node.js速習講座 Part2 Express編 JavaScript 【参考サイト】 MDN 【Udemy】 【JS】初級者から中級者になるためのJavaScriptメカニズム LINEBOT環境構築 【LINE Developers】 LINEDevelopers公式 【Qiita】 1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest 今後の課題 FlexMessageを1時間単位で指定しているため、予約枠である20分間隔でのメッセージの処理 管理画面へのログイン機能 予約を取得した時の管理画面でのソート機能 Dialogflowというサービスを使い、メッセージに対し学習していくAIの導入を検討しております。患者さんから予約の関係以外で質問やメッセージを受けられるように、自動で応答するAIを育てたいと思っています。 LINEでのFlexMessageをもっと見やすくする方法はないか。 セキュリティ 開発期間中のGitHubの草の生え方はこんな感じです!濃く緑になっているところは、振り返ってみるとかなり集中していたのでしょう。 まだまだ課題が多く、完成系には程遠いですがひとつずつ改善していきたく思います。 少し長くなってしまいましたが、ここまで読んでくださりありがとうございました!!٩( ᐛ )و
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

socket.ioにおけるroom機能のサンプル(初心者向け)

初心者なので、いろいろ試して挫折した ここまで検証できたことを記事として作成してみた。 ■前提条件 node.js導入済 ■検証環境 Windows 10、 VSCode フォルダーを作成して、 下記の3つファイルをフォルダーに配置、VSCODEでF5をぽっち。 launch.jsonの細かい記述に詳しくないが、 compoundsのところで 記述することで同時にクライアントとサーバーを一緒に起動できる。 launch.json { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Server", "program": "${workspaceFolder}/socketIOserver.js", "outputCapture": "std" }, { "type": "node", "request": "launch", "name": "Client", "program": "${workspaceFolder}/socketIOclient.js", "outputCapture": "std" }, ], "compounds": [ { "name": "Server/Client", "configurations": ["Server", "Client"], } ] } クライアントのサンプル(冗長の記述で悪いが、そのままでわかりやすいかも) socketIOclient.js 'use strict' const io = require('socket.io-client') //クライアント接続を3つ確立、ただしmultiplex(多重化)はなく、同じソケットを共用する const socket1 = io('http://localhost:3000') const socket2 = io('http://localhost:3000') const socket3 = io('http://localhost:3000') //registerUserイベントをトリガーさせ、socket.broadcast.emit(...)の挙動を確認 socket1.emit('registerUser', 'Hoge') socket2.emit('registerUser', 'Piyo') socket3.emit('registerUser', 'Foo') //各クライアント接続にてのイベントリスナーの動作を定義 socket1.on('entryNotify', message=> console.log(message)) // hello socket1.on('newUserNotify', message => console.log(`socket1 receives ${message}`)) // <registerUser> joined socket1.on('roomNotify', message =>console.log(`socket1 receives ${message}`)) socket2.on('entryNotify', message=> console.log(message)) // hello socket2.on('newUserNotify', message =>console.log(`socket2 recieves ${message}`)) // <registerUser> joined socket2.on('roomNotify', message =>console.log( `socket2 receives ${message}`)) socket3.on('entryNotify', message=> console.log(message)) // hello socket3.on('newUserNotify', message =>console.log(`socket3 receives ${message}`)) // <registerUser> joined socket3.on('roomNotify', message =>console.log( `socket3 receives ${message}`)) //サーバー側のchatイベントをトリガーさせ、io.to(room).emit(...)の挙動を確認 socket1.emit('chat', {'msg': 'Can you hear me?? ', 'room': 'roomA'}) socket1.emit('chat', {'msg': 'Can you hear me?? 2x ', 'room': 'roomA'}) socket1.emit('chat', {'msg': 'Can you hear me?? 3x ', 'room': 'roomA'}) socket2.emit('chat', {'msg': 'Stop spamming!', 'room': 'roomA'}) socket3.emit('chat', {'msg': 'I can hear you!', 'room': 'roomA'}) サーバー(別にhttpサーバーで立てなくてもいい Expressなどでもいい) socketIOserver.js 'use strict' const http = require('http') const Server = require('socket.io') const server = http.createServer((req, res) => {}).listen(3000) //ポート3000でソケットを接続させることにする const io = Server(server) //クライアント接続ができたときに、connectionイベントが発生 io.on('connection', socket => { //クライアント側のentryNotifyイベントをトリガーさせ、挨拶する socket.emit('entryNotify', 'welcome!') //ルームroomAに参加 socket.join('roomA') //registerUserイベントリスナー socket.on('registerUser', username => { //登録者以外のクライアント接続を通知、クライアント側のnewUserNotifyイベントをトリガー socket.broadcast.emit('newUserNotify', `BroadCast: new user ${username} has registered`) }) // chatイベントリスナー socket.on('chat', ({msg, room}) => { //同じRoomにいるクラインと接続に投稿を通知、クライアント側のroomNotifyイベントをトリガー io.to(room).emit("roomNotify", msg) }) }) ■動作結果(デバッグコンソールにて確認可能) イベント通知はいつも通りの非同期の処理 2 welcome! socket1 receives BroadCast: new user Piyo has registered socket2 recieves BroadCast: new user Hoge has registered welcome! socket3 receives BroadCast: new user Hoge has registered socket3 receives BroadCast: new user Piyo has registered socket1 receives BroadCast: new user Foo has registered socket1 receives Can you hear me?? socket1 receives Can you hear me?? 2x socket1 receives Can you hear me?? 3x socket1 receives Stop spamming! socket2 recieves BroadCast: new user Foo has registered socket2 receives Can you hear me?? socket2 receives Can you hear me?? 2x socket2 receives Can you hear me?? 3x socket2 receives Stop spamming! socket3 receives Can you hear me?? socket3 receives Can you hear me?? 2x socket3 receives Can you hear me?? 3x socket3 receives Stop spamming! socket3 receives I can hear you! socket1 receives I can hear you! socket2 receives I can hear you!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む