- 投稿日:2020-05-20T21:37:07+09:00
Vue.jsでAPIを返してみる
概要
Vue.jsで作成してVercel(旧now)とfreenomで独自ドメインのサイトを作るというものです。
できたもの
vue.jsでnekoBotで表示する写真をパラパラと見れるものを作った。
— 3yaka (@3yaka4) May 20, 2020
Deployしたら動かなくなったけど。。。https://t.co/ifiicmRP6b#protoout pic.twitter.com/bzu9fOOGHg
2020/05/23
動いたのでソースコード修正しました!!!環境
macOS Catalina Visual Studio Code 1.45.1 Node.js: 12.8.1構成
api(vercelはapiフォルダにメインで使うJSをいれるらしい) - server.js public(staticファイルはpublicフォルダの中へ) - index.html - style.css package.json vercel.json package-lock.json node_modulesコード
<html> <head> <title>Hello My WebSite!</title> <link rel="stylesheet" media="all" href="style.css"> </head> <body> <div id="app" class="waku"> <h1>3yakaさん家のにゃんこはなにしてる?</h1> <p>猫カメラが撮った写真の最新10件がランダムに表示されるよ</p> <button id="testbutton" v-on:click="getData()"></button> <p class="mes"> {{ message }} <img class="imgsiz" v-bind:src="src" /><!-- データバインディングの場合はカッコをくくらなくて呼び出せます --> </p> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'Hello Vue!', src:'https://i.gyazo.com/9360a06096a20ab93a79a4793f7670dd.jpg' // 画像のメッセージ初期値 }, methods: { getData: async function(){ let e; const response = await axios.get('/api') for (var i = 0 ; i < 9 ; i++){ e = Math.floor(Math.random () * 10); } const date = new Date(response.data[e].created_at); let datef = date.toLocaleString(); this.message = `この写真を撮った時間は${datef}だよ`; this.src = response.data[e].url; // 取得した画像差し替え console.log(response.data[e].url); }, }, mounted: function(){ this.getData(); } }) </script> </body> </html>node.jsconst express = require('express'); const app = express(); const PORT = process.env.PORT || 3000; const Gyazo = require('gyazo-api'); const gyazoclient = new Gyazo('***'); // axiosライブラリを呼び出す const axios = require('axios'); // ローカルでもサーバー上でもみれるようにする app.use(express.static(__dirname + '/../public')); app.get('/api', async function(req, res) { let response; try { response = await gyazoclient.list(); } catch (error) { console.error(error); } //結果をJSONに割り当てる res.json(response.data); }); (process.env.NOW_REGION) ? module.exports = app : app.listen(PORT); console.log(`Server running at ${PORT}`);上記でローカルのサーバーだと動くけどVercellにDeployするとapiが読めないと返ってくる。
vercelから出るIntended Nameserversの番号が4日くらい前にProtoOut Studioでやったときとだいぶ違う感じになってた。a.zeit-world.co.uk c.zeit-world.org e.zeit-world.com f.zeit-world.netこれが、こんな感じに
ns1.vercel-dns.com ns2.vercel-dns.comvercel特有のフォルダ構成とかに悩まされた
参考サイト
爆速!Vercelとfreenomで独自ドメインのサイトを無料で作成する - Qiita
感想
久々にコンソールログが真っ赤なサイトを見た。
- 投稿日:2020-05-20T19:36:31+09:00
[discord.js] グローバルチャットを作る!
概要
discord.jsのバージョンがv12になり、グローバルチャットの機構を作ったので、メモしとこうかなと思います。
(ほかの人も記事を書いているので参考程度に...)
解説とありますが、まだまだすごいdiscord.jsが扱えるわけではないので、間違っているかもしれないので
気軽に見てってください。BOTの導入やNode.js等のインストールはすでに済んでいるものとします。
環境
名前 versionとか Node.js v12.16.3 discord.js v12.2.0 OS : Windows10
コード
index.jsconst discord = require('discord.js');// --1 const client = new discord.Client(); client.on("message", async message => { // --2 if (message.channel.name === "チャンネル名"){ // --3 if (message.author.bot) return; // --4 if (message.attachments.size <= 0) { message.delete() } client.channels.cache.forEach(channel => { // --5 if (message.attachments.size <= 0){ const embed = new discord.MessageEmbed() // --6 .setAuthor(message.author.tag, message.author.avatarURL()) .setDescription(message.content) .setColor(0x2C2F33) if (channel.name === "チャンネル名"){ // --7 channel.send(embed) return } return } if (!message.attachments.forEach(attachment =>{ //--8 const embed = new discord.MessageEmbed() .setAuthor(message.author.tag, message.author.avatarURL()) .setImage(attachment.url) .setDescription(attachment.url) .setColor(0x2C2F33) if (channel.name === "チャンネル名"){ channel.send(embed) return } return })); return }); } }); client.login("BOT_TOKEN") // --9解説
--1 : discord.jsの読み込みをしています
--2 : Message イベントが起こったときに、この括弧内の処理がされます
--3 : Message が送信されたチャンネルがチャンネル名
だった場合になるように条件分岐をしています
--4 : 再帰を防ぐために、Messageを送信した人がBOTだった場合は処理を行わないようにreturn
を書いています--5 : BOTが参加しているすべてのサーバーのチャンネルを取得しています(簡単に言うとforEachはforに似たようなもの)
--6 : v11.xではdiscord.RichEmbed()
でしたが、v12.xではdiscord.MessageEmbed()
になりました
--7 : 取得したチャンネルのうちチャンネルの名前がチャンネル名
か否かを判断する条件分岐です
--8 : 写真等が送信されたときに処理が行われます (解説が書けそうでしたら後日書きます)
--9 : これがなければBOTにはログインしてくれませんまとめ
グローバルチャットはBOTが導入されているサーバーで会話することができます。
ただトラブル等起きやすいので、グローバルチャットを利用するときは常識を守りましょう。上のコードはシンプルな方です。まだまだいろんな機能を付け加えられると思うのでぜひチャレンジしてみてください!
- 投稿日:2020-05-20T16:03:40+09:00
Box UI ElementsのContent OpenWithでファイルの更新に反応してみた クライアントサイド編
この記事のシリーズ:
Box UI ElementsのContent OpenWithでBox Editをつかってみた
Box UI ElementsのContent OpenWithでG Suiteを開いてみた
Box UI ElementsのContent OpenWithでファイルの更新に反応してみた
Box UI ElementsのContent OpenWithでファイルの更新に反応してみた クライアントサイド編 ← この記事コードは、Githubでも確認いただけます。
前回の内容と今回試すこと
前回、Box UI ElementsのContent OpenWithでファイルの更新に反応してみた という記事の中で、Box UI ElementsのOpenWithを使って、カスタム画面から変更した際に、サーバーサイドでロングポーリングをおこない、変更を検知して再描画するという内容を書きました。
@daichiiiiiii さんから、再度コメント以下のようなコメントいただきました。ありがとうございます!
BoxのWebアプリの場合、ClientサイドでLong PollingとEvent logチェックしています。
同じAppUserを複数ユーザが使う場合には、データ流出等につながる可能性があるので適しませんが、1:1(AppUser:実User)の場合であればクライアントサイドに実装しても良い気もします。確かにアクセストークンが漏れても構わない場合、クライアントサイドで行ったほうが効率が良さそうです。
とはいえ、OpenWithやPreviewを表示するためにすでにダウンスコープしたアクセストークンを露出させています。
同じアクセストークンでEventを購読できるなら問題なさそうです。→ 同じトークンでEventを取得できます。
というわけで、早速こちらもためしてみました。変えたところ
変更の検知をクライアントサイドに寄せるので、再びサーバー側のロジックはシンプルなものになります。
app_client_long_polling.jsconst express = require("express"); const boxSDK = require("box-node-sdk"); const config = require("./config.js"); const app = express(); app.set("views", "."); app.set("view engine", "ejs"); /** * setup.jsで作成したファイルとユーザー */ const USER_ID = "12771965844"; const FILE_ID = "665319803554"; app.get("/", async (req, res) => { try { const sdk = boxSDK.getPreconfiguredInstance(config); // AppUserの権限でClientオブジェクトを作成 const auClient = sdk.getAppAuthClient("user", USER_ID); // トークンをダウンスコープする // APIリファレンスには載っていないが、UI Elementsの説明には書いてあるAPI // ここでは、OpenWithで必要なものと、Previewで必要なものを両方スコープにいれてトークンをダウンスコープする const downToken = await auClient.exchangeToken( [ "item_execute_integration", "item_readwrite", "item_preview", "root_readwrite", ], `https://api.box.com/2.0/folders/0` ); // テンプレートにパラメータを渡して、HTMLを返す res.render("index_client_long_polling", { fileId: FILE_ID, token: downToken.accessToken, }); } catch (e) { console.error(e.toString()); } }); const port = process.env.PORT || 3000; app.listen(port, () => { console.log(`express started on port ${port}`); });次に、クライアントコードでイベントを購読するようにします。
index_client_long_polling.ejs<!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>Sample</title> <link href="https://cdn01.boxcdn.net/platform/elements/11.0.2/ja-JP/openwith.css" rel="stylesheet" type="text/css"></link> <link href="https://cdn01.boxcdn.net/platform/preview/2.34.0/ja-JP/preview.css" rel="stylesheet" type="text/css"></link> <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6,Intl"></script> <script src="https://cdn01.boxcdn.net/polyfills/core-js/2.5.3/core.min.js"></script> <script src="https://cdn01.boxcdn.net/platform/elements/11.0.2/ja-JP/openwith.js"></script> <script src="https://cdn01.boxcdn.net/platform/preview/2.34.0/ja-JP/preview.js"></script> <style> .openwith-container { margin-left: 250px; } .preview-container { height: 800px; width: 100%; } </style> </head> <body> <h3>File Id: <%= fileId %></h3> <div id="container"> <div class="openwith-container"></div> <div class="preview-container"></div> </div> <script> // app.jsから渡されたパラメータ const fileId = "<%= fileId %>" const token = "<%= token %>" const openWith = new Box.ContentOpenWith(); openWith.show(fileId, token, { container: ".openwith-container"}) let preview = new Box.Preview(); preview.show(fileId, token, { container: ".preview-container", autoFocus: false }); openWith.addListener("execute", async () => { // openWithが開かれたので、ロングポーリング開始 // リアルタイムサーバーのURLを取得 const optionsRes = await fetch("https://api.box.com/2.0/events", { method: "OPTIONS", headers: { "Content-Type": "application/json; charset=utf-8", "Authorization": `Bearer ${token}`, } }); const optionsJRes = await optionsRes.json(); let lastSequenceId = 0; // 最後のSequenceID const subscribe = async (streamPosition = "now") => { // リアルタイムサーバーに対してロングポーリングを行う // CORSエラーを避けるため、シンプルなリクエストを使う let rtsRes = await fetch(optionsJRes.entries[0].url, { method: "GET", mode: "no-cors", headers: { "Content-Type": "application/x-www-form-urlencoded" } }) // ロングポーリングからはtype: "opaque"というのが帰ってくるのだけど、、よくわからない・・・。 // とはいえ、応答が帰ってくると、何かのイベントが発生したことはわかる。 // console.log("rtServer res", rtsRes) // 何かのイベントが発生したので、EventAPIをGETで叩き、詳細情報を取り出す。 // 上のロングポーリングだけに反応して再描画すると、再描画のプレビューイベントを拾ってしまい、 // 無限ループしてしまうので、イベントを更新に絞って確認する必要がある。 const qs = new URLSearchParams(); qs.set("event_type", "ITEM_UPLOAD"); // 更新だけに絞る。 qs.set("stream_type", "sync"); qs.set("stream_position", streamPosition); // 初回は"now", 2回め以降はnext_stream_positionが入る const getRes = await fetch(`https://api.box.com/2.0/events?${qs.toString()}`, { method: "GET", headers: { "Content-Type": "application/json; charset=utf-8", "Authorization": `Bearer ${token}`, } }); const getJRes = await getRes.json(); // 複数もどってくるイベントを、対象ファイルの更新のもので、最新のものに絞る const latestEvent = getJRes.entries.reduce((acc, cur) => { if(cur.event_type === "ITEM_UPLOAD" && cur.source && cur.source.type === "file" && cur.source.id === fileId) { if(!acc) { return cur; } return (cur.source.sequence_id > acc.source.sequence_id) ? cur : acc; } return null; }, null) // 前回処理したイベントより進んでいるときだけリロードする // 初回にstreamPosition === "now"でイベントを取得すると、イベントが何も帰ってこない。 // 対象のファイルの更新イベントじゃないかもしれないけどリフレッシュかける。ここはもうちょっとうまくやれそうな気もする。 if(streamPosition === "now" || latestEvent && latestEvent.source.sequence_id > lastSequenceId) { // 今回処理するイベントのsequence_idを保存 if(latestEvent) { lastSequenceId = latestEvent.source.sequence_id; } // previewだけを描画し直す。 preview = new Box.Preview(); // 毎回プレビューの位置までスクロールされたくないのでautoFocus:false preview.show(fileId, token, { container: ".preview-container", autoFocus: false }); } // 次のイベントへ await subscribe(getJRes.next_stream_position) } // イベントの購読開始 await subscribe(); }) </script> </body> </html>まとめ
コードはもうすこし改善の余地はありそうですが、とりあえずクライアントサイドからポーリングし、画面を再描画することができました。
クライアントサイドだけだと、SDKが利用できないので、少しコードを書くのが大変です。
- 投稿日:2020-05-20T10:12:58+09:00
簡単に始める仮想通貨EOSプログラミング(eosjs v16.0.9)
今回プログラミング初心者でも、仮想通貨EOSプログラミングを始められるように、コピペで実装できるプログラム総集編を紹介したいと思います。
javascriptのnode.jsとscatterウォレット操作を行う基本的なプログラミングになります。
もしプログラミングを始める際にはテスト環境であるテストネットのアカウントを作っておきましょう。
仮想通貨EOSをわざわざ購入せずにテスト版のEOSでプログラミングをすることが可能です。node.js動作プログラミング
node.jsによるEOSの操作についてのまとめです、トリガー処理とボット処理を組み合わせれば色んな使い方ができると思います。
・ EOSプログラミングの始め方
・ 送金プログラミング
・ ステーキングプログラミング
・ アンステーキングプログラミング
・ RAM購入プログラミング
・ RAM売却プログラミング
・ EOSアカウント作成プログラミングScatter動作プログラミング
デスクトップウォレットScatter(スキャッター)によるEOSの操作についてのまとめです。HTMLに組み込んでdApps作成などの用途に使用できます。
・ Scatterプログラミングの始め方
・ 送金プログラミング
・ ステーキングプログラミング
・ アンステーキングプログラミング
・ RAM購入プログラミング
・ RAM売却プログラミング
・ EOSアカウント作成プログラミング最後に
仮想通貨EOSの基本的には英語圏と中華圏しかドキュメントがないので、日本向けになにか役に立てばと思っています。
- 投稿日:2020-05-20T09:53:21+09:00
Promiseでお手軽に並列処理
概要
ある処理を行うのに事前に複数の事前処理が必要な場合、並列してできると処理が速くなる場合がある。コールバック地獄にならない記述がnodeだとできる。
例
3つのタスクが全て完了したら本処理を実行する例。
var Task1 = new Promise(function(resolve, reject) { //事前処理1 resolve(); //これで事前処理1が終わったことを宣言する }); var Task2 = new Promise(function(resolve, reject) { //事前処理2 resolve(); //これで事前処理2が終わったことを宣言する }); var Task3 = new Promise(function(resolve, reject) { //事前処理3 resolve(); //これで事前処理3が終わったことを宣言する }); Promise.all([Task1, Task2, Task3]).then(function () { //本処理 });スッキリ。
- 投稿日:2020-05-20T00:43:35+09:00
HTTP APIでLambda関数にPOSTしたbodyの値が '[object Object]' になる時の解決法
AWS Lambdaの関数にHTTP APIで値をPOSTする際につまづいたのでメモ。
環境
- macOS
- AWS Cloud9
- Node.js 12
問題
Node.jsのfetch()メソッドで、自作したLambda関数にPOSTで値を送信しました。
Lambdaの呼び出しはAPI GatewayのHTTP APIを用いています。Lambda関数のコード
デバックするために、Lambda関数では一旦eventを返すようにしています。
console.logで出力してCloudWacth Logsで確認しても良いと思います。exports.handler = async(event, context) => { console.log(event) ... return event };実行コード
require('dotenv').config() const env = process.env var fetch = require("node-fetch") fetch(env.LAMBDA_URL, { // Lambda関数のAPI URL method: 'POST', headers: { 'Content-type': 'application/json' }, body: { "massage": "test massage" }, }) .then(function(res) { return res.json(); }) .then(function(event) { console.log(event); })eventの中身を確認すると、
{ version: '2.0', routeKey: 'XXXXXXXXXX', ... body: '[object Object]', isBase64Encoded: false }bodyの値が
'[object Object]'
になっています。解決方法
どうやら、Lambda関数に値を渡すときは、Payloadとして渡す必要があるようで、JSON形式のデータはJSONの文字列に変換した状態で送る必要があるようです。
Payloadに指定した値は文字列に変換されるので、オブジェクトをそのまま指定すると、[object Object]
に変換されてしまいます。
※オブジェクトにtoString()メソッドを適用すると、返り値は[object Object]
なるPOSTするbodyの値を、JSON.stringify()で文字列に変換して送ることで解決できます。
修正後のコード
fetch(env.LAMBDA_URL, { method: 'POST', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ // JSONを文字列に変換する "massage": "test massage" }), }) .then(function(res) { return res.json(); }) .then(function(event) { console.log(event); })出力を見てみると、
{ version: '2.0', routeKey: 'XXXXXXXXXX', ... body: '{"massage":"test massage"}', isBase64Encoded: false }POSTしたbodyの値をきちんと確認できました。
- 投稿日:2020-05-20T00:29:59+09:00
Railsで「Could not find a JavaScript runtime」というエラーが出てrails s ができなくなった
rails s ができない
久しぶりにRailsを触り、rails sでサーバー起動させると
長文のエラーが発生し、一番下に「Could not find a JavaScript runtime」と書いてありました。解決策
調べたところ解決策は2つあるみたいです。
・gem 'therubyracer'
をGem fileに追記しbundle install
を実行する。
・Node.jsをhttps://nodejs.org/en/からインストールする。GitHub ExecJS
https://github.com/sstephenson/execjs
私はNode.jsをインストールし
「rails s」できるようになりました。