From b1958250cae1c890defef1c60b3adb4f0726e26e Mon Sep 17 00:00:00 2001 From: ilim Date: Wed, 27 Feb 2019 12:07:53 +0900 Subject: [PATCH 1/4] Fix bug --- photoAPI.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/photoAPI.js b/photoAPI.js index 79d9d36..fadb53f 100644 --- a/photoAPI.js +++ b/photoAPI.js @@ -90,7 +90,7 @@ module.exports.getAlbumList = async oAuth2Client => { method: "GET", headers: headers }) - .then(response => response["albums"]) + .then(response => JSON.parse(response)["albums"]) }; @@ -182,7 +182,8 @@ module.exports.createAlbumMediaItem = async (oAuth2Client, albumID, uploadToken, headers: headers, body: JSON.stringify(body) }) - .then(response => response["newMediaItemResults"][0]) + .then(response => JSON.parse(response)["newMediaItemResults"][0]) + .catch(e => console.error(e)) }; @@ -255,6 +256,7 @@ module.exports.getMediaItem = async (oAuth2Client, mediaItemID) => { method: "GET", headers: headers, }) + .then(response => JSON.parse(response)) }; /** From c02210d3cae7a2bec6c4eb97c0968c9f9fe78056 Mon Sep 17 00:00:00 2001 From: ilim Date: Wed, 27 Feb 2019 12:55:08 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E3=81=AA=E3=81=AB=E3=81=8B=E9=80=81?= =?UTF-8?q?=E4=BF=A1=E3=81=99=E3=82=8B=E3=81=A8URL=E3=82=92=E8=BF=94?= =?UTF-8?q?=E4=BF=A1=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 6 +- package-lock.json | 248 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + slack.js | 52 ++++++++++ 4 files changed, 306 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 5d1af7c..939121b 100644 --- a/app.js +++ b/app.js @@ -12,6 +12,7 @@ const main = async () => { process.exit(1) } const oAuth2Client = await photoapi.getOAuthToken(client_id, client_secret); + const bot = new slack.Slack(); //共有するためアルバムを指定 const albumTitle = "bushitsuchan_album"; @@ -23,6 +24,8 @@ const main = async () => { await photoapi.shareAlbum(oAuth2Client, album.id) } + bot.start(); + //定期的に撮影した写真の共有リンクをslackbotで送信 //https://developers.google.com/photos/library/guides/api-limits-quotas に抵触しないように!! /** @@ -49,7 +52,8 @@ const main = async () => { const shortURL = await photoapi.getShortURL(url); // ここをカスタマイズしてください - slack.send(shortURL) + bot.url = shortURL; + // slack.send(shortURL); }, interval) }; diff --git a/package-lock.json b/package-lock.json index d3e6f1d..49f9467 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,92 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@slack/client": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@slack/client/-/client-4.10.0.tgz", + "integrity": "sha512-NQNfSUhnx1rzFOIjH+oSbt+7M187GTJvXuRfVMm26yLKEMaYQGTVWrIXSlxnc+V4V9T7IqKMNFdk9qltATn0gw==", + "requires": { + "@types/form-data": "^2.2.1", + "@types/is-stream": "^1.1.0", + "@types/node": ">=6.0.0", + "@types/p-cancelable": "^0.3.0", + "@types/p-queue": "^2.3.1", + "@types/p-retry": "^1.0.1", + "@types/retry": "^0.10.2", + "@types/ws": "^5.1.1", + "axios": "^0.18.0", + "eventemitter3": "^3.0.0", + "finity": "^0.5.4", + "form-data": "^2.3.1", + "is-stream": "^1.1.0", + "object.entries": "^1.0.4", + "object.getownpropertydescriptors": "^2.0.3", + "object.values": "^1.0.4", + "p-cancelable": "^0.3.0", + "p-queue": "^2.3.0", + "p-retry": "^2.0.0", + "retry": "^0.12.0", + "ws": "^5.2.0" + } + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" + }, + "@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "11.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz", + "integrity": "sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==" + }, + "@types/p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@types/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-sP+9Ivnpil7cdmvr5O+145aXm65YX8Y+Lrul1ojdYz6yaE05Dqonn6Z9v5eqJCQ0UeSGcTRtepMlZDh9ywdKgw==" + }, + "@types/p-queue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/p-queue/-/p-queue-2.3.2.tgz", + "integrity": "sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==" + }, + "@types/p-retry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/p-retry/-/p-retry-1.0.1.tgz", + "integrity": "sha512-HgQPG9kkUb4EpTeUv2taH2nBZsVUb5aOTSw3X2YozcTG1ttmGcLaLKx1MbAz1evVfUEDTCAPmdz2HiFztIyWrw==", + "requires": { + "@types/retry": "*" + } + }, + "@types/retry": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.10.2.tgz", + "integrity": "sha512-LqJkY4VQ7S09XhI7kA3ON71AxauROhSv74639VsNXC9ish4IWHnIi98if+nP1MxQV3RMPqXSCYgpPsDHjlg9UQ==" + }, + "@types/ws": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz", + "integrity": "sha512-NkTXUKTYdXdnPE2aUUbGOXE1XfMK527SCvU/9bj86kyFF6kZ9ZnOQ3mK5jADn98Y2vEUD/7wKDgZa7Qst2wYOg==", + "requires": { + "@types/events": "*", + "@types/node": "*" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -41,6 +127,11 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -117,6 +208,14 @@ "ms": "2.0.0" } }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -139,6 +238,29 @@ "safe-buffer": "^5.0.1" } }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-promise": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", @@ -152,6 +274,11 @@ "es6-promise": "^4.0.3" } }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==" + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -172,6 +299,11 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "finity": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/finity/-/finity-0.5.4.tgz", + "integrity": "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA==" + }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -195,6 +327,11 @@ "mime-types": "^2.1.12" } }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, "gcp-metadata": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz", @@ -292,6 +429,19 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -321,6 +471,37 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -450,6 +631,42 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==" + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "os-homedir": { "version": "1.0.2", "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -469,6 +686,24 @@ "os-tmpdir": "^1.0.0" } }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "p-queue": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-2.4.2.tgz", + "integrity": "sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==" + }, + "p-retry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-2.0.0.tgz", + "integrity": "sha512-ZbCuzAmiwJ45q4evp/IG9D+5MUllGSUeCWwPt3j/tdYSi1KPkSD+46uqmAA1LhccDhOXv8kYZKNb8x78VflzfA==", + "requires": { + "retry": "^0.12.0" + } + }, "path": { "version": "0.12.7", "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", @@ -571,6 +806,11 @@ "lodash": "^4.13.1" } }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, "retry-axios": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", @@ -677,6 +917,14 @@ "extsprintf": "^1.2.0" } }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", diff --git a/package.json b/package.json index a9ce858..8fd0d5e 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "homepage": "https://github.com/TUS-OSK/bushitsuchan-PC#readme", "dependencies": { + "@slack/client": "^4.10.0", "googleapis": "^35.0.0", "node-webcam": "^0.4.6", "path": "^0.12.7", diff --git a/slack.js b/slack.js index eb92dbc..2090570 100644 --- a/slack.js +++ b/slack.js @@ -1,4 +1,56 @@ "use strict"; +const {RTMClient, WebClient} = require('@slack/client'); +const rp = require("request-promise"); + + +const rpap = rp.defaults({ + transform: (body, response) => { + const constentType = response.headers["content-type"].split(";")[0]; + if (constentType === "application/json") { + return JSON.parse(body); + } else if (constentType === "text/plain") { + return body; + } else { + return body; + } + } +}); + +const wait = time => new Promise(resolve => setTimeout(resolve, time)); + +module.exports.Slack = class Slack { + constructor() { + const {slack_token} = process.env; + this.rtm = new RTMClient(slack_token); + this.web = new WebClient(slack_token); + this.url = "not yet"; + } + + start() { + this.rtm.start(); + this.rtm.on("message", message => this.reply(message)); + } + + async reply(message) { + // console.debug(`message: ${JSON.stringify(message)}\n`); + const {user, text, channel, subtype, ts} = message; + if (user === this.rtm.activeUserId) { + return + } else if (!text || text === "/") { + return + } + + const replyText = this.url; + + const response = await this.web.chat.postMessage({ + channel: channel, + text: replyText, + as_user: true, + thread_ts: message.thread_ts || message.event_ts + }); + console.info(`Message sent: ${response.message.text}`); + } +}; module.exports.send = value => { From 93031d6e3770ffe24f399f616c7bccece31c7306 Mon Sep 17 00:00:00 2001 From: ilim Date: Wed, 27 Feb 2019 13:38:48 +0900 Subject: [PATCH 3/4] Bugfix --- slack.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/slack.js b/slack.js index 2090570..0a14c29 100644 --- a/slack.js +++ b/slack.js @@ -34,6 +34,19 @@ module.exports.Slack = class Slack { async reply(message) { // console.debug(`message: ${JSON.stringify(message)}\n`); const {user, text, channel, subtype, ts} = message; + if (subtype === "bot_message" || subtype === "channel_join" || subtype === "group_join") { + return + } else if (subtype === "message_changed") { + return + } else if (subtype === "message_deleted") { + return + } else if (subtype === "message_replied") { + return + } else if (subtype) { + return + } + + console.log(user, this.rtm.activeUserId); if (user === this.rtm.activeUserId) { return } else if (!text || text === "/") { From 5b4edea0246ef9f245a55ac6228f29c24abb93e9 Mon Sep 17 00:00:00 2001 From: ilim Date: Fri, 1 Mar 2019 00:57:56 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E6=92=AE=E5=BD=B1=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E3=82=92openCV=E3=81=B8=E5=A4=89=E6=9B=B4=20=E5=AE=B9=E9=87=8F?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E8=A7=A3=E6=B1=BA=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=81=9F=E3=82=81mention=E3=81=8C=E3=81=82=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=81=A8=E3=81=8D=E3=81=AE=E3=81=BF=E5=86=99=E7=9C=9F=E3=82=92?= =?UTF-8?q?=E6=92=AE=E5=BD=B1=E3=81=97=E8=BF=94=E3=81=99=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 60 ++++++++++--------------------- caputure.js | 48 ++++--------------------- package.json | 1 + photoAPI.js | 62 +++++++++++--------------------- slack.js | 100 +++++++++++++++++++++++++-------------------------- utils.js | 49 +++++++++++++++++++++++++ 6 files changed, 143 insertions(+), 177 deletions(-) create mode 100644 utils.js diff --git a/app.js b/app.js index 939121b..2cb7db5 100644 --- a/app.js +++ b/app.js @@ -3,58 +3,34 @@ const photoapi = require("./photoAPI"); const {capture} = require("./caputure"); const slack = require("./slack"); +const utils = require("./utils"); const main = async () => { - //認証鍵の設定 - const {client_id, client_secret} = process.env; - if (!client_id || !client_secret) { - console.log("READMEに従ってGoogle Photos APIsの認証鍵を設定してください"); - process.exit(1) - } + //環境設定 + const {client_id, client_secret, slack_token} = process.env; const oAuth2Client = await photoapi.getOAuthToken(client_id, client_secret); - const bot = new slack.Slack(); + const slackBot = new slack.Slack(slack_token); - //共有するためアルバムを指定 + //アルバム, 共有のの設定 const albumTitle = "bushitsuchan_album"; const albums = await photoapi.getAlbumList(oAuth2Client); - let album = albums.filter((album) => album.title === albumTitle)[0]; - - if (album === undefined) { + let album = albums.filter(album => album.title === albumTitle)[0]; + if (!album) { album = await photoapi.createAlbum(oAuth2Client, albumTitle); await photoapi.shareAlbum(oAuth2Client, album.id) } - bot.start(); - - //定期的に撮影した写真の共有リンクをslackbotで送信 - //https://developers.google.com/photos/library/guides/api-limits-quotas に抵触しないように!! - /** - * 何msに一回実行するか あまり小さくしすぎるとエラーが発生します - * @type {number} - */ - const interval = 10 * 1000; - if (60 * 60 * 24 * 1000 / 10000 * 3 > interval) { - console.log(`注意: 1日あたり${(60 * 60 * 24 * 3 / interval * 1000).toLocaleString()}回PhotoAPIを叩く設定で,1日の上限10,000回を越してしまいます`) - } - setInterval(async () => { - const url = await capture(oAuth2Client, album).catch(e => { - console.error(e.name); - if (e.name === "StatusCodeError") { - console.error(JSON.parse(e.error).error.message); - return - } - console.error(e.message) - // console.error(e) - }); - if (!url) { - return - } - const shortURL = await photoapi.getShortURL(url); - - // ここをカスタマイズしてください - bot.url = shortURL; - // slack.send(shortURL); - }, interval) + slackBot.getReplyText = async () => { + const photo = await capture(0, ".png"); + const uploadToken = await photoapi.uploadPhoto(oAuth2Client, photo, Date().toLocaleString()); + const {mediaItem} = await photoapi.createAlbumMediaItem(oAuth2Client, album.id, uploadToken, ""); + const {baseUrl} = await photoapi.getMediaItem(oAuth2Client, mediaItem.id); + const shortUrl = await utils.getShortURL(baseUrl); + return shortUrl; + }; + + //slackbotの開始 + slackBot.start(); }; diff --git a/caputure.js b/caputure.js index 406d0db..0400b67 100644 --- a/caputure.js +++ b/caputure.js @@ -1,45 +1,9 @@ "use strict"; -const photoapi = require("./photoAPI"); -const NodeWebcam = require('node-webcam'); -const slack = require("./slack"); +const cv = require('opencv4nodejs'); - -const Webcam = NodeWebcam.create({ - width: 1280, - height: 720, - quality: 100, - - delay: 0, - saveShots: true, - device: false, - callbackReturn: "buffer", - verbose: false -}); - -/** - * 写真を撮影しGooglePhotoへとアップロード,その共有リンクを取得します - * @param {oAuth2Client} oAuth2Client - photoapi.getOAuthToken関数で取得します - * @param {Object} album - * @returns {Promise} - */ -module.exports.capture = async (oAuth2Client, album) => { - const photo = await new Promise((resolve, reject) => { - Webcam.capture("capture", (err, photo) => { - if (err) { - reject(err) - } - resolve(photo) - }) - }).catch(e => { - console.error(e); - console.error( - "READMEに従ってカメラを使えるようにしてください\n" + - "また,OSやセキュリティソフトでカメラへのアクセスをブロックしている可能性もあります 解除してください\n"); - process.exit(1) - }); - const uploadToken = await photoapi.uploadPhoto(oAuth2Client, photo, Date().toLocaleString()); - const {mediaItem} = await photoapi.createAlbumMediaItem(oAuth2Client, album.id, uploadToken, ""); - const {baseUrl} = await photoapi.getMediaItem(oAuth2Client, mediaItem.id); - return baseUrl -}; \ No newline at end of file +module.exports.capture = async (devicePort = 0, ext = ".png") => { + const cap = new cv.VideoCapture(devicePort); + const frame = cap.read(); + return cv.imencode(ext, frame); +}; diff --git a/package.json b/package.json index 8fd0d5e..5860b13 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@slack/client": "^4.10.0", "googleapis": "^35.0.0", "node-webcam": "^0.4.6", + "opencv4nodejs": "^4.14.1", "path": "^0.12.7", "readline": "^1.3.0", "request": "^2.88.0", diff --git a/photoAPI.js b/photoAPI.js index fadb53f..b816da4 100644 --- a/photoAPI.js +++ b/photoAPI.js @@ -4,25 +4,11 @@ const fs = require("fs"); const {google} = require("googleapis"); const readline = require("readline"); const path = require("path"); -const rp = require("request-promise"); +const {rpap} = require("./utils"); // const assert = require("assert"); - -const rpap = rp.defaults({ - "transform": (body, response) => { - const constentType = response.headers["content-type"].split("")[0]; - if (constentType === "application/json") { - return JSON.parse(body) - } else if (constentType === "text/plain") { - return body - } else { - return body - } - } -}); - /** - * @typedef {Object} oAuth2Client + * @typedef {Object} OAuth2Client * @property {function: string} getAccessToken */ @@ -30,9 +16,14 @@ const rpap = rp.defaults({ * 認証鍵を取得します * @param {string} client_id - GCPで取得したクライアントID * @param {string} client_secret - GCPで取得したクライアントシークレット - * @returns {Promise} + * @returns {Promise} */ -module.exports.getOAuthToken = (client_id, client_secret) => { +module.exports.getOAuthToken = async (client_id, client_secret) => { + if (!client_id || !client_secret) { + throw new TypeError("Google Photos APIsの認証キーがロードできていません。" + + "READMEに従ってdirenvの設定をしてください。"); + } + const oAuth2Client = new google.auth.OAuth2( client_id, client_secret, @@ -46,13 +37,13 @@ module.exports.getOAuthToken = (client_id, client_secret) => { const tokenPath = path.join(__dirname, "token.json"); if (fs.existsSync(tokenPath)) { oAuth2Client.setCredentials(require(tokenPath)); - return oAuth2Client + return oAuth2Client; } + const authURL = oAuth2Client.generateAuthUrl({ access_type: "offline", scope: scopes }); - console.log(`以下のサイトを開き,認証したあと表示される文字列をここに貼り付けてください\n${authURL}`); const rl = readline.createInterface({ input: process.stdin, @@ -76,7 +67,7 @@ module.exports.getOAuthToken = (client_id, client_secret) => { /** * アルバム一覧の取得 - * @param {oAuth2Client} oAuth2Client - getOAuthToken関数で取得します + * @param {OAuth2Client} OAuth2Client - getOAuthToken関数で取得します * @returns {Promise>} */ module.exports.getAlbumList = async oAuth2Client => { @@ -90,13 +81,13 @@ module.exports.getAlbumList = async oAuth2Client => { method: "GET", headers: headers }) - .then(response => JSON.parse(response)["albums"]) + .then(response => response["albums"]) }; /** * 画像のバイナリデータを送信します - * @param {oAuth2Client} oAuth2Client - getOAuthToken関数で取得します + * @param {OAuth2Client} OAuth2Client - getOAuthToken関数で取得します * @param photo * @param {string} filename * @returns {Promise} uploadToken @@ -120,7 +111,7 @@ module.exports.uploadPhoto = async (oAuth2Client, photo, filename) => { /** * アップロードした画像を単なる写真として保存します - * @param {oAuth2Client} oAuth2Client - getOAuthToken関数で取得します + * @param {OAuth2Client} OAuth2Client - getOAuthToken関数で取得します * @param {string} uploadToken - uploadPhoto関数で取得します * @param {string} description * @returns {Promise>} @@ -153,7 +144,7 @@ module.exports.createMediaItem = async (oAuth2Client, uploadToken, description) /** * アップロードした画像をアルバムに追加します - * @param {oAuth2Client} oAuth2Client - getOAuthToken関数で取得します + * @param {OAuth2Client} OAuth2Client - getOAuthToken関数で取得します * @param {string} albumID * @param {string} uploadToken - uploadPhoto関数で取得します * @param {string} description @@ -182,14 +173,14 @@ module.exports.createAlbumMediaItem = async (oAuth2Client, albumID, uploadToken, headers: headers, body: JSON.stringify(body) }) - .then(response => JSON.parse(response)["newMediaItemResults"][0]) - .catch(e => console.error(e)) + .then(response => response["newMediaItemResults"][0]) + // .catch(e => console.error(e)) }; /** * アルバムを作成します - * @param {oAuth2Client} oAuth2Client - getOAuthToken関数で取得します + * @param {OAuth2Client} OAuth2Client - getOAuthToken関数で取得します * @param {string} title * @returns {Promise} */ @@ -214,7 +205,7 @@ module.exports.createAlbum = async (oAuth2Client, title) => { /** * アルバムを共有します - * @param {oAuth2Client} oAuth2Client - getOAuthToken関数で取得します + * @param {OAuth2Client} OAuth2Client - getOAuthToken関数で取得します * @param {string} albumID * @returns {Promise} */ @@ -241,7 +232,7 @@ module.exports.shareAlbum = async (oAuth2Client, albumID) => { /** * アップロード済みの写真に関する情報を取得します - * @param {oAuth2Client} oAuth2Client - getOAuthToken関数で取得します + * @param {OAuth2Client} OAuth2Client - getOAuthToken関数で取得します * @param {string} mediaItemID * @returns {Promise} */ @@ -256,17 +247,6 @@ module.exports.getMediaItem = async (oAuth2Client, mediaItemID) => { method: "GET", headers: headers, }) - .then(response => JSON.parse(response)) -}; - -/** - * 与えられたURLの短縮URLを取得します - * @param {string} url - * @returns {Promise} 短縮URL - */ -module.exports.getShortURL = url => { - return rpap.get(`http://is.gd/create.php?format=simple&format=json&url=${url}`) - .then(result => JSON.parse(result)["shorturl"]) }; async function main() { diff --git a/slack.js b/slack.js index 0a14c29..cc22708 100644 --- a/slack.js +++ b/slack.js @@ -1,26 +1,17 @@ "use strict"; -const {RTMClient, WebClient} = require('@slack/client'); -const rp = require("request-promise"); - -const rpap = rp.defaults({ - transform: (body, response) => { - const constentType = response.headers["content-type"].split(";")[0]; - if (constentType === "application/json") { - return JSON.parse(body); - } else if (constentType === "text/plain") { - return body; - } else { - return body; - } - } -}); - -const wait = time => new Promise(resolve => setTimeout(resolve, time)); +const {RTMClient, WebClient} = require('@slack/client'); +const utils = require("./utils"); module.exports.Slack = class Slack { - constructor() { - const {slack_token} = process.env; + /** + * @param {string} slack_token + */ + constructor(slack_token) { + if (!slack_token) { + throw new TypeError("Slcak botの認証キーがロードできていません。" + + "READMEに従ってdirenvの設定をしてください。"); + } this.rtm = new RTMClient(slack_token); this.web = new WebClient(slack_token); this.url = "not yet"; @@ -28,44 +19,49 @@ module.exports.Slack = class Slack { start() { this.rtm.start(); + this.rtm.on("ready", () => console.log("ready")); this.rtm.on("message", message => this.reply(message)); } - async reply(message) { - // console.debug(`message: ${JSON.stringify(message)}\n`); - const {user, text, channel, subtype, ts} = message; - if (subtype === "bot_message" || subtype === "channel_join" || subtype === "group_join") { - return - } else if (subtype === "message_changed") { - return - } else if (subtype === "message_deleted") { - return - } else if (subtype === "message_replied") { - return - } else if (subtype) { - return - } + async getReplyText() { + throw Error("NotImplementedError"); + } - console.log(user, this.rtm.activeUserId); - if (user === this.rtm.activeUserId) { - return - } else if (!text || text === "/") { - return + /** + * @param{Object} receiveMessage + */ + reply(receiveMessage) { + const {user, text, channel, subtype, ts} = receiveMessage; + if (subtype) { + // console.log(subtype); + return; + } else if (user === this.rtm.activeUserId) { + return; + } else if (!text) { + return; + } else if (!text.match(new RegExp(`<@${this.rtm.activeUserId}>`))) { + return; + } + if (text.match(/ip$/)) { + const ips = utils.getLocalIps(); + this.web.chat.postMessage({ + channel: channel, + text: ips.map(ip => `address: ${ip}`).join("\n"), + as_user: true, + thread_ts: ts + }); + return; } - const replyText = this.url; - - const response = await this.web.chat.postMessage({ - channel: channel, - text: replyText, - as_user: true, - thread_ts: message.thread_ts || message.event_ts - }); - console.info(`Message sent: ${response.message.text}`); + this.getReplyText() + .then(replyText => + this.web.chat.postMessage({ + channel: channel, + text: replyText, + as_user: true, + thread_ts: ts + }) + ) + // console.info(`Message sent: ${response.message.text}` } -}; - - -module.exports.send = value => { - console.log(`未実装ですが以下を投稿したことになりました ${value}`) }; \ No newline at end of file diff --git a/utils.js b/utils.js new file mode 100644 index 0000000..e65788e --- /dev/null +++ b/utils.js @@ -0,0 +1,49 @@ +"use strict"; + +const rp = require("request-promise"); +const os = require("os"); + + +const rpap = rp.defaults({ + "transform": (body, response) => { + const constentType = response.headers["content-type"]; + if (constentType.match(/application\/json/)) { + return JSON.parse(body) + } else if (constentType.match(/"text\/plain/)) { + return body + } else { + return body + } + } +}); +module.exports.rpap = rpap; + +/** + * 与えられたURLの短縮URLを取得します + * @param {string} url + * @returns {Promise} 短縮URL + */ +module.exports.getShortURL = async url => { + const response = await rpap.get(`http://is.gd/create.php?format=simple&format=json&url=${url}`); + return JSON.parse(response)["shorturl"]; +}; + +/** + * @return {Array} + */ +module.exports.getLocalIps = () => { + const interfaces = os.networkInterfaces(); + const ipList = []; + for (const i in interfaces) { + for (const iface of interfaces[i]) { + if (iface.family !== 'IPv4' || iface.internal) { + continue; + } + ipList.push(iface.address); + } + } + if (ipList.length === 0) { + throw Error("IP Adressが見つかりません"); + } + return ipList; +}; \ No newline at end of file