20210509のNode.jsに関する記事は6件です。

迷えるあなたに意思決定LINEBot!エヴァのMAGIが判断してくれます!

こんな時ありませんか? Yes or Noの二択で迷うとき! よくありますよね、こんな時にぜひ使ってほしいLINEBotを作製しました! 作製したLINEBot(なんちゃってMAGI)の概要 LINEでユーザーが質問事項を入力するとBotが内容を審議したように見せて、可決(賛成)か否決(反対)の二択で答えてくれます。 エヴァンゲリオンに出てくるスーパーコンピューターMAGIをイメージして作製しました。 エヴァを知らない方のために簡単にMAGIを説明すると3種類のスパコンが内蔵されており、3種類それぞれが違うアルゴリズムで回答を出します。(MAGI (まぎ)とは) 今回作製したBotはランダムで3回YesorNoを返すAPIを使用してMAGIに似せています。 また内容を確認しているように見せたかったので10文字以下の入力の場合はエヴァのキャラ、アスカに怒られる仕様となっています。 ・MAGI回答 ・アスカに怒られちゃいました。 環境 node.js v15.12.0 axios v0.21.1 Line/bot-sdk v7.3.0 ngrok 2.3.39 ソースコード 今回はYES NO ジェネレーターを使用して賛否を取得しています。 3回呼び出すことにより3つの意見を表現しています。 // ########## ▼▼▼ なんちゃってMAGI部分 ▼▼▼ ########## const flakyMAGI = async (event) => { //それっぽく見せるため10文字以下ならアスカの「あんたばかぁ」を返信 if (event.message.text.length < 10) { let ngText = `あんたばかぁ?そんな10文字以下の文章じゃMAGIも判断できるわけないでしょ\nどうせ「${event.message.text}」とか入力したんでしょ!` return client.replyMessage(event.replyToken, { type: 'text', text: ngText }); } else { // 「リプライ」を使って先に返事しておきます await client.replyMessage(event.replyToken, { type: 'text', text: '審議中……' }); let pushText = ''; //返信メッセージ let countYes = 0; //可決カウント let countNo = 0; //否決カウント let magiAnswer = '';//多数決の結果 try { //YesNoジェネレータで3つの可否を取得 const res1 = await axios.get('https://yesno.wtf/api'); const res2 = await axios.get('https://yesno.wtf/api'); const res3 = await axios.get('https://yesno.wtf/api'); const resAnswer1 = res1.data.answer; const resAnswer2 = res2.data.answer; const resAnswer3 = res3.data.answer; //取得結果から可決、否決をカウント if(resAnswer1 == 'yes'){ countYes = countYes+ 1; } else { countNo = countNo + 1; } if(resAnswer2 == 'yes'){ countYes = countYes+ 1; } else { countNo = countNo + 1; } if(resAnswer3 == 'yes'){ countYes = countYes+ 1; } else { countNo = countNo + 1; } //可決が3票以上の場合結果を可決とする if(countYes == 3){ magiAnswer = '可決'; } else { magiAnswer = '否決'; } //結果文言 pushText = `MAGIは可決${countYes},否決${countNo}で、${magiAnswer}を回答しています`; } catch (error) { pushText = 'MAGI、完全に沈黙!信号受け付けません'; // APIからエラーが返ってきたらターミナルに表示する console.error(error); } // 「プッシュ」で後からユーザーに通知します return client.pushMessage(event.source.userId, { type: 'text', text: pushText, }); } }; うまくいかなかったこと プログラム自体はとても簡単でAPIの使用部分だけ書ければ後は単純なif文ばかりでしたので苦戦するのことはないだろうと踏んでいたら意外なところで失敗が。。。 LINEDeveroperサイト自体を自動翻訳で日本語にしていると、Botのチャネルアクセストークンをまでもおかしな翻訳をされてしまうみたいです。 それに気づかず、翻訳後のチャネルアクセストークンを使用しなんちゃってMAGIから全然返信が来ない。 なんてことになっていました。 チャネルアクセストークンを発行、再発行の時には自動翻訳をOFFにして発行しましょう! これを期に英語覚えようかな笑 ちなみにこちらの記事を参考にLINEBotのアカウントを作製しました 1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest まとめ 今回はLINEBotでMAGIを作ってみました。 正直今回の出来はあまりよくなかったかなと思っています。 本当は賛否の判定はテキトーでも同じ質問に対して同じ回答を出すというところまで作ってみたかったのです。 しかしいい方法が思い浮かばず今回のようなランダム回答となってしまいました。 さっきと言っていることが違うので原作のMAGIには程遠い(´;ω;`) 方法で今回は時間をかけてしまい、手を動かした時間が短ったので次回作製するものはプログラムにも注力できたらと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud Foundryのデプロイで急にNode.jsが動かなくなった件

いつも通りにCI経由でデプロイしてたらデプロイが失敗してました。 .nvmrcにバージョンを記載するやり方でバージョン指定をしていましたが、突如指定しているバージョンが利用できなくなってフェイルするという事案が発生しました。 参考: Cloud Foundry(IBM Cloud)でNode.jsのバージョン指定【.nvmrc】 no match found for 15.12.0 以下が急に発生したデプロイエラーです。 .nvmcrには確かにv15.12.0で記載してました。 API がファイルの処理を完了するのを待機しています... アプリをステージングし、ログをトレースしています... Downloading nodejs_buildpack... Downloaded nodejs_buildpack Cell f4f7f92d-7651-4673-be44-27fba5eccf8c creating container for instance 92dbe423-2c84-410c-9721-b2ebe13f7c1f Cell f4f7f92d-7651-4673-be44-27fba5eccf8c successfully created container for instance 92dbe423-2c84-410c-9721-b2ebe13f7c1f Downloading app package... Downloading build artifacts cache... Downloaded build artifacts cache (43.6M) Downloaded app package (13.4M) -----> Nodejs Buildpack version 1.7.48 **WARNING** buildpack version changed from 1.7.46 to 1.7.48 -----> Installing binaries engines.node (package.json): unspecified engines.npm (package.json): unspecified (use default) **WARNING** Using the node version specified in your .nvmrc See: http://docs.cloudfoundry.org/buildpacks/node/node-tips.html **ERROR** Unable to install node: no match found for 15.12.0 in [10.24.0 10.24.1 12.22.0 12.22.1 14.16.0 14.16.1 15.13.0 15.14.0] Failed to compile droplet: Failed to run all supply scripts: exit status 14 Exit status 223 Cell f4f7f92d-7651-4673-be44-27fba5eccf8c stopping instance 92dbe423-2c84-410c-9721-b2ebe13f7c1f Cell f4f7f92d-7651-4673-be44-27fba5eccf8c destroying container for instance 92dbe423-2c84-410c-9721-b2ebe13f7c1f アプリケーションのステージング時にエラーが発生しました: App staging failed in the buildpack compile phase 失敗 特に何も記載の変更はしてなく、 急に15.12.0が使えなくなるという。。 こんなことあるんですね。 buildpacksの指定 結局前にやったBuildpacksの指定をするのとpackage.jsonに指定をすることで回避ができました。 参考: Cloud Foundry(IBM Cloud)でNode.jsの最新バージョンを利用する manifest.yml applications: - name: My-APP random-route: true memory: 128M command: npm start buildpacks: - https://github.com/cloudfoundry/nodejs-buildpack package.jsonでバージョン固定にすると今回みたいな事象が起こるかも知れないなと思い、最新の一つ前くらいを指定して、それ以上という指定の仕方にするようにしました。 package.json "engines": { "node": ">=15.0.0" }, nodejs-buildpackの現状の最新を見ると16.1.0まで対応してるっぽいので最新版を使えそうです。 //省略 -----> Installing node 16.1.0 Download [https://buildpacks.cloudfoundry.org/dependencies/node/node_16.1.0_linux_x64_cflinuxfs3_aebbbe59.tgz] Using default npm version: 7.11.2 デプロイすると16.1.0がインストールされてました。 まとめ 特定のNode.jsのバージョン固定で指定しているかつBuildpacksの指定をしていない状態で発生する問題なのかもしれません。 Cloud Foundry側で自動的に読み込むbuildpacksのバージョンに対応しているNode.jsのバージョンと、手元で指定したNode.jsのバージョンの食い違いが発生するといった感じでしょうか。 他のPaaSなどを使ってても出会ったことがない初めてのケースでビビりました。 15.0.0などの固定ではなく、>=15.0.0などの指定にした方が無難ですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【覚書】Gist を簡易的な DB として使う

個人的な利用の範囲で、めちゃくちゃ楽な方法でクラウドにデータを保管したいと思ったので、Gist を使ってみた。 GitHub 側の設定 https://github.com/settings/tokens でアクセストークンを取得して控えておく。 https://gist.github.com/ で db.json を作成して URL を控えておく。 JS 側の実装 /** * Gist の db.json の内容を取得 * @param {string} id Gist ID /Example: https://gist.github.com/username/{HERE} * @returns {Promise<object>} 内容 */ const getGist = async (id) => (await (await fetch("https://api.github.com/gists/" + id)).json()).files[ "db.json" ].content; /** * Gist の db.json を書き換え * @param {string} id Gist ID /Example: https://gist.github.com/username/{HERE} * @param {string} token アクセストークン * @param {object} body 書き換える内容 * @returns {Promise<object>} レスポンス */ const setGist = async (id, token, body) => await ( await fetch("https://api.github.com/gists/" + id, { method: "PATCH", headers: { Accept: "application/vnd.github.v3+json", Authorization: "token " + token, }, body: JSON.stringify({ description: "Updated at " + new Date().toLocaleString(), files: { "db.json": { content: JSON.stringify(body), }, }, }), }) ).json(); これで OK 。 注意 アクセストークンが漏洩すると色々とヤバいので、見えるところに置くのはガチでやめたほうがいい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【要注意】node.jsでActive directoryライブラリによるAD検索結果をテキストとして利用出来ない

やりたいこと PromiseオブジェクトをStringに変換して再利用したいのです。再利用の目的はHTMLやMarkDownのテキストの中に埋め込んで使うことです。 しかし、わたしは実力不足でこれができずに、貴重な時間を無駄にしてしまいました( ; ; ) 困ったこと PromiseオブジェクトをStringに変換出来ません。必ずConverting Object Promise to String in Javascriptエラーが発生します Activedirectoryライブラリ利用 const ActiveDirectory = require( 'activedirectory' ) const AD = { config : { url : 'LDAP://hogehoge.ad.foo.baa.jp' , baseDN : 'dc=ad,dc=foo,dc=baa,dc=jp' , username : process.env.ADUSER , password : process.env.ADPASSWORD , secure : true , timeLimit : 3000 , attributes : { user:[ 'sAMAccountName', 'displayName', 'department', 'mail' ] } } , query : '(mail={{mailAddress}})' } function getDepartmentFromAD(mailAddress){ return new Promise((resolve,reject) => { ad.find(AD.query.replace('{{mailAddress}}',mailAddress), (err, results) => { if ((err) || (! results)) { console.log('ERROR : ' + JSON.stringify(err)); return } resolve( results.users[0].department ) }) }) } function getIdNameMarkdownFromDepartment(department){ return new Promise((resolve,reject) => { resolve(department.replace(' ','|') + '|' + '|') }) } const createBelongsIdName = async function( kind, mailAddress, markdownList ) { try { if( kind == INFO.account.kind.employees ) { getDepartmentFromAD(mailAddress).then(getIdNameMarkdownFromDepartment).then((idNameMarkdown)=>{ setTimeout(()=>{markdownList += idNameMarkdown},INFO.timeOut) }) } else { markdownList += '-|' + '-|' } } catch( err ) { console.error( 'createBelongsIdName() : ' + err ) return null; } return markdownList } エラーの発生箇所 返されたPromiseオブジェクトをString型の変数markdownListに格納 markdownList += idNameMarkdown エラーの内容 String型のmarkdownListにPromiseオブジェクト型のidNameMarkdown代入できないと言うエラーが出ます。色々試行錯誤しましたが、Promiseオブジェクト型をString型に型変換することはできないようです。 GitHubなどに公開されている多くのコードがconsole.log()メソッドでPrpmiseオブジェクトが返した値をコンソールに表示するだけです。わたしがしたいのはPromiseオブジェクトをString型に型変換して再利用することです。 わたしの解決策(逃げ) node.jsを使うのを諦めて、PythonのActivedirectoryライブラリを利用しました。上記のPromise問題は起こらず、すんなり解決しました。しかし、あくまでもこれは代替手段の逃げです。個人的にはかなりモヤモヤしています。 お願い どなたか良い解決策をご存知ないでしょうか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodejsでtextlintを動かしてみる

青空文庫:蟹工船 よりサンプル利用 概要 textlintをnode.jsで動かす - Qiita を 俺俺textlintルールを検討してみた - Qiita で書き直し 構成 textlint_app/ // フォルダー名をtextlintにすると衝突するので注意 + app.js + .textlintrc + views/ + index.ejs app.js // vim:set ts=2 et: // https://qiita.com/chenglin/items/5e563e50d1c32dadf4c3 express.jsのcors対応 const TextLintEngine = require('textlint').TextLintEngine; const express = require('express'); const cors = require('cors') const bodyParser = require('body-parser'); const app = express(); const path = require("path"); // 他からAPIリクエストできるように許可 app.set("view engine", "ejs"); // postデータのjsonをパースするおまじない app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); // allow cors app.use(cors()); // 8080番ポートで待ちうける let listen_port = 8080 app.listen(listen_port, () => { console.log('Running at Port %i...', listen_port); }); app.post('/api/textlint', (req, res, next) => { const req_text = req.body.text; const engine = new TextLintEngine({ configFile: path.join(__dirname, ".textlintrc"), }); engine.executeOnText(req_text).then(results => { res.json({ messages: results[0].messages }); }); }); app.get("/", function (req, res) { res.render("index.ejs"); }); // その他のリクエストに対する404エラー app.use((req, res) => { var url = req.protocol + '://' + req.headers.host + req.url; console.log(url); res.sendStatus(404); }); .textlintrc { "rules": { "preset-ja-technical-writing": { "ja-no-mixed-period": false, // 「。」のつけ忘れのチェックを除外 }, "preset-ja-spacing": true, "no-start-duplicated-conjunction": { "interval" : 2 // interval of sentences }, "no-surrogate-pair": true, "no-mixed-zenkaku-and-hankaku-alphabet": true, "ja-hiragana-fukushi": true, "ja-hiragana-hojodoushi": true, "@textlint-ja/textlint-rule-no-insert-dropping-sa": true, "prefer-tari-tari": true, "@textlint-ja/no-synonyms": true, "ja-no-orthographic-variants": true, "use-si-units": true, "jis-charset": true, "no-hoso-kinshi-yogo": true, "ja-no-inappropriate-words": true, } } ※ホントはJSONに//でコメント入れてはいけない views/index.js <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </head> <body> <form> <div class="form-group"> <label for="lint_textarea">Example textarea</label> <textarea class="form-control" id="lint_textarea" rows="10" placeholder="Write something here..."></textarea> </div> </form> <div> <p><strong>lint結果</strong><p> <ul id="textlint_output"></ul> </div> <script type="text/javascript"> (function () { 'use strict'; $('#lint_textarea').on('input',function () { let text = $('#lint_textarea').val(); let textData = JSON.stringify( { 'text': text, }); let textlint_url = location.protocol + "//" + location.host + "/api/textlint"; $.ajax({ type: 'POST', url: textlint_url, data: textData, contentType: 'application/json', }).done(function (data, textStatus, jqXHR) { console.log(textStatus); // https://qiita.com/georgeOsdDev@github/items/34197e63d0fad307fba6 $("#textlint_output").empty(); let lines = text.split('\n'); data.messages.forEach(function(m){ let li = document.createElement('li'); $(li).text(m.line + "行目" + m.column + "文字目 [" + m.ruleId + "]: <" + m.message + ">「" + lines[m.line -1] + "」") $("#textlint_output").append(li); }); }).fail(function (jqXHR, textStatus, errorThrown) { console.log("failed"); }); }); }()); </script> </body> </html> $ cd textlint_app $ npm init --yes $ npm install textlint \ textlint-rule-preset-ja-technical-writing \ textlint-rule-preset-ja-spacing \ textlint-rule-no-start-duplicated-conjunction \ textlint-rule-no-surrogate-pair \ textlint-rule-no-mixed-zenkaku-and-hankaku-alphabet \ textlint-rule-ja-hiragana-fukushi \ textlint-rule-ja-hiragana-hojodoushi \ @textlint-ja/textlint-rule-no-insert-dropping-sa \ textlint-rule-prefer-tari-tari \ @textlint-ja/textlint-rule-no-synonyms sudachi-synonyms-dictionary \ textlint-rule-ja-no-orthographic-variants \ textlint-rule-use-si-units \ textlint-rule-jis-charset \ textlint-rule-no-hoso-kinshi-yogo \ textlint-rule-ja-no-inappropriate-words $ npm install express \ ejs \ cors $ node app.js Running at Port 8080... Browser上でtextlintを実行する - Qiita textlint-browser-runner/index.html at master · mobilusoss/textlint-browser-runner
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

タイトルを呟いたらポスターとあらすじを答える映画どろbotを作ってみた

『世界侵略: ロサンゼルス決戦』と『スカイライン -征服-』ってどっちがどっちでしたっけ? ラストに主人公が〇〇になって「うぉぉぉ!」とテンション上がったほうを見返したいんですけど、どっちでしたっけ? という時にあらすじとポスターを調べてくれるLINE botを作りました。 実際に動いているところの動画 手軽にネタバレなしのあらすじを知りたいときに タイトルをGoogleやWikipediaで調べてしまうと記事タイトルでネタバレを見てしまうリスクがあります。TMDb(The Movie Database)から取得するので公式のあらすじとポスターだけを知ることができます。 こんな時に使えます。 - 映画をおすすめされた時にあらすじとポスターを知りたい - 自分が長期シリーズの何作目まで観たのか思い出したい 環境 Node.js 15.12.0 npm 7.6.3 axios 0.21.1 LINE bot TMDb API コード app01.js 'use strict'; // ######################################## // 初期設定など // ######################################## // パッケージを使用します const express = require('express'); const line = require('@line/bot-sdk'); const axios = require('axios'); // ローカル(自分のPC)でサーバーを公開するときのポート番号です const PORT = process.env.PORT || 3000; // Messaging APIで利用するクレデンシャル(秘匿情報)です。 const config = { channelSecret: '【LINEのAPIキーを入力】', channelAccessToken: '【LINEのアクセストークンを入力】' }; // ########## ▼▼▼ メイン関数 ▼▼▼ ########## const sampleFunction = async (event) => { let pushTitle = ''; let pushOriginalTitle = ''; let pushOverview = ''; let pushPosterUrl = ''; let pushBackdropUrl = ''; try { //APIの説明ページは https://developers.themoviedb.org/3/search/search-movies です。 let response = await axios.get( 'https://api.themoviedb.org/3/search/movie?api_key=【TMDbのAPIキーを入力】&language=ja&query='+ encodeURIComponent(event.message.text)+'&page=1&include_adult=false' ); //検索結果が複数の場合に備えてループする for (let i = 0; i < response.data.total_results; i++) { pushTitle = response.data.results[i].title; pushOriginalTitle = response.data.results[i].original_title; pushOverview = response.data.results[i].overview; pushPosterUrl = 'https://image.tmdb.org/t/p/original' + response.data.results[i].poster_path; pushBackdropUrl = 'https://image.tmdb.org/t/p/original' + response.data.results[i].backdrop_path; await client.pushMessage(event.source.userId, { type: 'text', text: pushTitle, }); await client.pushMessage(event.source.userId, { type: 'text', text: pushOriginalTitle, }); await client.pushMessage(event.source.userId, { type: 'text', text: pushOverview, }); await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: pushPosterUrl, previewImageUrl:pushPosterUrl }); await client.pushMessage(event.source.userId, { type: 'image', originalContentUrl: pushBackdropUrl, previewImageUrl:pushBackdropUrl }); } } catch (error) { // APIからエラーが返ってきたらメッセージに表示する await client.pushMessage(event.source.userId, { type: 'text', text: "ごめんなさい、エラーです・・・", })}; }; // ########## ▲▲▲ メイン関数 ▲▲▲ ########## // ######################################## // LINEサーバーからのWebhookデータを処理する部分 // ######################################## // LINE SDKを初期化します const client = new line.Client(config); // 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サーバーを実行中です…`); 詰まったところをシェアします 前回QiitaAPIを使って記事の作成日を取得したときにはdataが配列でしたが、TMDb APIはdataより1段下のresultsが配列でした。TMDb APIの仕様書をちゃんと読みましょう。 QiitaAPI: response.data[i].created_at TMDb API: response.data.results[i].original_title LINE botで画像として送るにはhttpではダメで、httpsにする必要がありました。LINE botの仕様書をちゃんと読みましょう。 おわりに Googleでタイトルを検索すれば同じことはできるのですがLINE botに呟くほうが圧倒的に簡単で余計な情報を目にする必要もなく、ポスターの画質も良くて満足です。今回はngrokで仮で動かしましたが自分用に常時起動しておいてもいいと思ってしまうほどでした。 TMDbのサイトをブラウザで見ると劇中画像が複数枚あったり、各国のポスターも閲覧できるので、これもAPIで取れないか今後やってみたいです。 ━-━-━-━-━-━-━-━-━-━-━-━-━━-━-━-━-━-━-━-━-━ 2021年4月からプロトアウト(プロトタイプ+アウトプット)スタジオに参加して、技術を学んだり自身を深掘りして卒業制作=クラウドファンディングのテーマを決めたりしています。 金融系SEという安定稼働を最優先にガチガチに設計書を作ってバグは許さぬ、という世界で十数年やってきました。自分の性格としても独創的なアイディア出しは苦手で、決まったことを正確に効率的にこなすことが得意です。 そんな私が無事クラウドファンディングに辿り着いて成功できるのか、見守っていただけましたら幸いです。 〇情報発信 ・自身とテーマ深掘り的な記事 → note ・開発中のつぶやき → Twitter 参考URL TMDbのAPIの映画情報をPythonで取得してみた Web APIを利用して映画のポスターやあらすじを自分のサイトに表示する ジェイソン・ステイサムで妄想するのが日課になっていたので、いっそBOTにしてみた。 LINE Messaging API でできることまとめ【送信編】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む