Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9138

今朝のおススメの曲を届けてくれるLINE BOT

$
0
0
はじめに  朝は、一日のはじまりなので、良いスタートを切りたい。  そんなとき、心地よい音楽を聴いて、気分を整えたいと思います。  LINEは、起き掛けにメッセージが入っていないか見るので、  おススメの音楽が届いていたら、うれしい気がします。 完成イメージ 環境 Node.js LINE Messaging API Spotify SDK SpotifiApi VSCode Github Actions 作り方 LINE Messaging APIの利用登録とチャネル作成* LINEログイン APIの利用登録とLIFFアプリ作成* Spotify APIの利用登録とWebアプリ作成* Github Actionsの設定* *ほかのQiita記事に掲載あり ソースコード 3. Webアプリ Webアプリについて紹介します。 サーバ /** * 今朝のおススメの曲 */ // ライブラリ const express = require('./node_modules/express'); // Spotify用設定 const spotifyApp = express(); spotifyApp.use(express.json()); spotifyApp.use(express.urlencoded({ extended: true })); const spotifyRouter = require('./spotify-router'); spotifyApp.use('/', spotifyRouter); // LINE用設定 const lineApp = express(); const lineRouter = require('./line-router'); lineApp.use('/line', lineRouter); // Spotify用ポート spotifyApp.listen(8888, () => console.log( 'HTTP Server up. Now go to http://localhost:8888/login in your browser.' ) ); // LINE用ポート lineApp.listen(8000, () => console.log( 'HTTP Server up. Now go to http://localhost:8000/line/login in your browser.' ) ); Line用ルータ /** * LINE用ルータ */ // ライブラリ const express = require('./node_modules/express/index'); const line = require('@line/bot-sdk'); const axios = require('./node_modules/axios'); var util = require('./node_modules/util'); // LINE用設定 const app = express.Router(); const config = { channelSecret: '<シークレットキー>', channelAccessToken: '<アクセストークン>' }; const client = new line.Client(config); function makeFlexMessage(data) { var flexMessage = { type: "flex", altText: "this", contents: {} }; var flexCarousel = { type: "carousel", contents: [] }; flexCarousel.contents = makeBubbles(data); flexMessage.contents = flexCarousel; return flexMessage; } function makeBubbles(data) { var flexBubbles = []; for(let i = 0; i < data.length; i++) { if (i > 2) break; flexBubbles.push(makeBubble(data[i])); } return flexBubbles; } function makeBubble(data) { var flexBubble = { type: "bubble", size: "", direction: "", hero: {}, body: {}, footer: {} }; flexBubble.size = "micro"; flexBubble.direction = "ltr"; flexBubble.hero = makeHero(data); // FlexImage; flexBubble.body = makeBody(data); // FlexBox; flexBubble.footer = makeFooter(data); // FlexBox; return flexBubble; } function makeHero(data) { var flexImage = { type: "image", url: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip10.jpg", size: "full", aspectRatio: "320:213", aspectMode: "cover" }; return flexImage; } function makeBody(data) { var flexBox = { type: "box", layout: "vertical", contents: [], spacing: "sm", paddingAll: "13px" }; flexBox.contents.push(makeBodyText(data)); //FlexComponent[] flexBox.contents.push(makeBodyHeader(data)); flexBox.contents.push(makeBodyMain(data)); return flexBox; } function makeBodyText(data) { var flexText = { type: "text", text: data.name, size: "sm", wrap: true, weight: "bold", style: "normal", }; return flexText; } function makeBodyHeader(data) { var flexBox = { type: "box", layout: "baseline", contents: [] }; var stars = Math.round(data.popularity/10); if (stars > 5) { stars = 5; } for(let i = 0; i < stars; i++) { flexBox.contents.push(makeBodyHeaderEvaluationIcon(data)); //FlexComponent[]; } flexBox.contents.push(makeBodyHeaderEvaluationNumber(data)); //FlexComponent[]; return flexBox; } function makeBodyHeaderEvaluationIcon(data) { var flexIcon = { type: "icon", url: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: "xs" }; return flexIcon; } function makeBodyHeaderEvaluationNumber(data) { var flexText = { type: "text", text: "", size: "xs", color: "#8c8c8c", margin: "md", flex: 0 }; var value = data.popularity; if (value === null || value === undefined) { value = " "; } flexText.text = "'" + value + "'"; return flexText; } function makeBodyMain(data) { var flexBox = { type: "box", layout: "vertical", contents: [] }; flexBox.contents.push(makeBodyMainContext(data)); //FlexComponent[]; return flexBox; } function makeBodyMainContext(data) { var flexBox = { type: "box", layout: "baseline", spacing: "sm", contents: [] }; var flexText = { type: "text", text: data.artist, size: "xs", wrap: true, color: "#8c8c8c", flex: 5 }; flexBox.contents.push(flexText); return flexBox; } function makeFooter(data) { var flexBox = { type: "box", layout: "vertical", spacing: "sm", contents: [] }; var flexButton = { type: "button", style: "primary", color: "#228b22", action: "" }; var action = {}; action.label = "Play"; action.type = "uri"; action.uri = data.url; flexButton.action = action; flexBox.contents.push(flexButton); return flexBox; } async function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } var msg = '今朝のおススメの曲'; let url = "http://localhost:8888/getmusic"; let response = await axios.get(url); var flexMessage = makeFlexMessage(response.data); console.log(util.inspect(flexMessage)); // ユーザーにリプライメッセージを送ります。 return client.replyMessage(event.replyToken, flexMessage); } //================= // ルータ //================= app.get('/', (req, res) => res.send('Hello LINE BOT! (HTTP GET)')); app.post('/webhook', line.middleware(config), (req, res) => { if (req.body.events.length === 0) { res.send('Hello LINE BOT! (HTTP POST)'); console.log('検証イベントを受信しました!'); return; } else { console.log('受信しました:', req.body.events); } Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result)); }); module.exports = app; Spotify用ルータ /** * Spotify用ルータ */ // ライブラリ const ServerMethods = require('./src/server-methods'); const SpotifyWebApi = require('./src/spotify-web-api'); const express = require('./node_modules/express'); var util = require('./node_modules/util'); // Spotify用設定 const app = express.Router(); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 操作 ※ただし、Primeプラン限定あり const scopes = [ 'ugc-image-upload', 'user-read-playback-state', 'user-modify-playback-state', 'user-read-currently-playing', 'streaming', 'app-remote-control', 'user-read-email', 'user-read-private', 'playlist-read-collaborative', 'playlist-modify-public', 'playlist-read-private', 'playlist-modify-private', 'user-library-modify', 'user-library-read', 'user-top-read', 'user-read-playback-position', 'user-read-recently-played', 'user-follow-read', 'user-follow-modify' ]; // Spotify API SDK 設定 SpotifyWebApi._addMethods(ServerMethods); const spotifyApi = new SpotifyWebApi({ redirectUri: 'http://localhost:8888/callback', clientId: process.argv.slice(2)[0], // <クライアントID> clientSecret: process.argv.slice(2)[1] // <クライアントシークレット> }); //================= // ルータ //================= // ログイン app.get('/login', (req, res) => { res.redirect(spotifyApi.createAuthorizeURL(scopes)); }); // Spotify からのコールバック app.get('/callback', (req, res) => { const error = req.query.error; const code = req.query.code; const state = req.query.state; if (error) { console.error('Callback Error:', error); res.send(`Callback Error: ${error}`); return; } // トークン取得 spotifyApi .authorizationCodeGrant(code) .then(data => { const access_token = data.body['access_token']; const refresh_token = data.body['refresh_token']; const expires_in = data.body['expires_in']; spotifyApi.setAccessToken(access_token); spotifyApi.setRefreshToken(refresh_token); console.log('access_token:', access_token); console.log('refresh_token:', refresh_token); console.log( `Sucessfully retreived access token. Expires in ${expires_in} s.` ); res.send('Success! You can now close the window.'); // トークン・リフレッシュ※期限の半分を過ぎたら setInterval(async () => { const data = await spotifyApi.refreshAccessToken(); const access_token = data.body['access_token']; console.log('The access token has been refreshed!'); console.log('access_token:', access_token); spotifyApi.setAccessToken(access_token); }, expires_in / 2 * 1000); }) .catch(error => { console.error('Error getting Tokens:', error); res.send(`Error getting Tokens: ${error}`); }); }); // 今朝のおススメ曲取得※20曲 app.get('/getmusic', (req, res) => { spotifyApi .searchTracks('Good Morning') .then(function(data) { // Print some information about the results console.log('I got ' + data.body.tracks.total + ' results!'); // Go through the first page of results var firstPage = data.body.tracks.items; console.log('The tracks in the first page are (popularity in parentheses):'); let results = []; firstPage.forEach(function(track, index) { results.push({'index' :index , 'name' :track.name , 'duration' :Math.round(track.duration_ms/60000) , 'popularity' :track.popularity , 'url' :track.external_urls.spotify , 'preview_url' :track.preview_url , 'artist' :track.artists[0].name }); }); results.sort(function(first, second){ return second.popularity - first.popularity; }); console.log(util.inspect(results)); return res.json(results); }).catch(function(err) { console.log('Something went wrong:', err.message); }); }); module.exports = app; おわりに  LINE公式アプリを見ると、FlexMessage や メニュー を上手く使用して UX を高めているのがわかります。  見習いたいですね。  デバッグについて書き添えます。  FlexMessage は、  1. LINE FlexMessage Simulator で作成して、コーディングして、  2. コンソール等のログにメッセージのJSONを出力して、  3. それを LINE FlexMessage Simulator で正しく表示されるかを確認するとよいと思います。  コードだけだとエラーがわかりにくいです。 参考 LINE FlexMessage Simulator

Viewing all articles
Browse latest Browse all 9138

Trending Articles