diff --git a/AutomaticClockOuts/clock_out.js b/AutomaticClockOuts/clock_out.js index 7e77862..52de7d4 100644 --- a/AutomaticClockOuts/clock_out.js +++ b/AutomaticClockOuts/clock_out.js @@ -1,9 +1,11 @@ +const _ = require('lodash'); const CosmosClient = require("@azure/cosmos").CosmosClient; const config = require("./config"); const TimeEntry = require('./time_entry'); const axios = require('axios'); -const MsalClient = require('./msal_client') -const TimeEntryDao = require('./time_entry_dao') +const MsalClient = require('./msal_client'); +const TimeEntryDao = require('./time_entry_dao'); +const SlackClient = require('./slack_client'); const doClockOut = async (context) => { context.log(`I am going to check how many entries were not clocked out ${new Date()}`); @@ -16,21 +18,27 @@ const doClockOut = async (context) => { const response = await MsalClient.findUsersInMS(); const users = response.data.value; + const slackUsers = await SlackClient.findUsersInSlack(); + const {resources: entries} = await timeEntryDao.getEntriesWithNoEndDate(); context.log(`Checking for time-entries that need to be clocked out`); let totalClockOutsExecuted = 0; - const usersWithClockOut = [] + const usersWithClockOut = []; await Promise.all(entries.map(async (timeEntryAsJson) => { const timeEntry = new TimeEntry(timeEntryAsJson) if (timeEntry.needsToBeClockedOut()) { - usersWithClockOut.push(findUser(users, timeEntry.timeEntry.owner_id)); + const user_email = findUserEmail(users, timeEntry.timeEntry.owner_id); + const userId = findSlackUserId(slackUsers,user_email); + if(userId){ + usersWithClockOut.push("<@"+userId+">"); + } timeEntryAsJson.end_date = timeEntry.getTimeToClockOut() await container.item(timeEntryAsJson.id, timeEntryAsJson.tenant_id).replace(timeEntryAsJson) totalClockOutsExecuted++ } })); - if (totalClockOutsExecuted > 0) { + if (usersWithClockOut.length) { axios.post(slackWebHook, { "text": `OMG, you have been working more than 12 hours in a row. \nPlease take a break and visit https://timetracker.ioet.com/ to set the right end time for your entries, we just did a clock out for you :wink: \n- ${usersWithClockOut.join('\n- ')}` @@ -46,9 +54,14 @@ const doClockOut = async (context) => { context.log(`I just clocked out ${totalClockOutsExecuted} entries, thanks are not needed...`); } -const findUser = (users, id) => { +const findUserEmail = (users, id) => { const user = users.find(user => user.objectId === id) - return user.displayName + return _.first(user.otherMails) +} + +const findSlackUserId = (users,email)=>{ + const user = users.find(user => user.email === email); + return user? user.id:null } module.exports = {doClockOut}; diff --git a/AutomaticClockOuts/config.js b/AutomaticClockOuts/config.js index c2028d3..84328ca 100644 --- a/AutomaticClockOuts/config.js +++ b/AutomaticClockOuts/config.js @@ -6,7 +6,8 @@ const config = { clientId: process.env["CLIENT_ID"], authority: process.env["AUTHORITY"], clientSecret: process.env["CLIENT_SECRET"], - slackWebHook: process.env["SLACK_WEBHOOK"] + slackWebHook: process.env["SLACK_WEBHOOK_NOTIFY"], + slackApiToken: process.env["SLACK_TOKEN_NOTIFY"], }; module.exports = config; diff --git a/AutomaticClockOuts/package-lock.json b/AutomaticClockOuts/package-lock.json index 0cc7627..1ccab33 100644 --- a/AutomaticClockOuts/package-lock.json +++ b/AutomaticClockOuts/package-lock.json @@ -200,6 +200,46 @@ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true }, + "@slack/logger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-2.0.0.tgz", + "integrity": "sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==", + "requires": { + "@types/node": ">=8.9.0" + } + }, + "@slack/types": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-1.10.0.tgz", + "integrity": "sha512-tA7GG7Tj479vojfV3AoxbckalA48aK6giGjNtgH6ihpLwTyHE3fIgRrvt8TWfLwW8X8dyu7vgmAsGLRG7hWWOg==" + }, + "@slack/web-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.1.0.tgz", + "integrity": "sha512-9MVHw+rDBaFvkvzm8lDNH/nlkvJCDKRIjFGMdpbyZlVLsm4rcht4qyiL71bqdyLATHXJnWknb/sl0FQGLLobIA==", + "requires": { + "@slack/logger": ">=1.0.0 <3.0.0", + "@slack/types": "^1.7.0", + "@types/is-stream": "^1.1.0", + "@types/node": ">=12.0.0", + "axios": "^0.21.1", + "eventemitter3": "^3.1.0", + "form-data": "^2.5.0", + "is-stream": "^1.1.0", + "p-queue": "^6.6.1", + "p-retry": "^4.0.0" + }, + "dependencies": { + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + } + } + }, "@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -230,6 +270,14 @@ "@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/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -239,8 +287,7 @@ "@types/node": { "version": "14.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz", - "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==", - "dev": true + "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -248,6 +295,11 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "acorn": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", @@ -384,6 +436,11 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "ava": { "version": "3.11.1", "resolved": "https://registry.npmjs.org/ava/-/ava-3.11.1.tgz", @@ -718,6 +775,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", @@ -906,6 +971,11 @@ } } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1002,6 +1072,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", @@ -1083,6 +1158,16 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1719,6 +1804,19 @@ "picomatch": "^2.0.5" } }, + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + }, + "mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "requires": { + "mime-db": "1.46.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -1915,6 +2013,39 @@ "aggregate-error": "^3.0.0" } }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + } + } + }, + "p-retry": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.4.0.tgz", + "integrity": "sha512-gVB/tBsG+3AHI1SyDHRrX6n9ZL0Bcbifps9W9/Bgu3Oyu4/OrAh8SvDzDsvpP0oxfCt3oWNT+0fQ9LyUGwBTLg==", + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -2226,6 +2357,11 @@ "signal-exit": "^3.0.2" } }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", diff --git a/AutomaticClockOuts/package.json b/AutomaticClockOuts/package.json index e04938c..4947c76 100644 --- a/AutomaticClockOuts/package.json +++ b/AutomaticClockOuts/package.json @@ -9,6 +9,7 @@ "dependencies": { "@azure/cosmos": "3.5.2", "@azure/msal-node": "^1.0.0-alpha.5", + "@slack/web-api": "^6.0.0", "axios": "^0.20.0", "dotenv": "^8.2.0", "moment": "^2.27.0", diff --git a/AutomaticClockOuts/readme.md b/AutomaticClockOuts/readme.md index 6da3082..1832f44 100644 --- a/AutomaticClockOuts/readme.md +++ b/AutomaticClockOuts/readme.md @@ -37,6 +37,7 @@ KEY='XXX' CLIENT_ID='XXX' AUTHORITY='XXX' CLIENT_SECRET='XXX' -SLACK_WEBHOOK='XXX' +SLACK_WEBHOOK_NOTIFY='XXX' +SLACK_TOKEN_NOTIFY='XXX' ``` Check the pinned message in our slack channel to get these values diff --git a/AutomaticClockOuts/slack_client.js b/AutomaticClockOuts/slack_client.js new file mode 100644 index 0000000..fcd49a6 --- /dev/null +++ b/AutomaticClockOuts/slack_client.js @@ -0,0 +1,18 @@ +const { WebClient, LogLevel } = require("@slack/web-api"); +const { slackApiToken } = require("./config"); + +const client = new WebClient(slackApiToken,{logLevel: LogLevel.DEBUG}); + +const findUsersInSlack = async () => { + const response = await client.users.list(); + let usersIdAndEmails = []; + if (response.ok) { + slackUsers = response.members; + usersIdAndEmails = slackUsers + .filter((user) => user.profile.hasOwnProperty("email") && !user.deleted) + .map((user) => ({ id: user.id, email: user.profile.email })); + } + return usersIdAndEmails; +}; + +module.exports = { findUsersInSlack }; diff --git a/nodejs-functions/.serverless/nodejs-functions.zip b/nodejs-functions/.serverless/nodejs-functions.zip index ed734cf..f107a08 100644 Binary files a/nodejs-functions/.serverless/nodejs-functions.zip and b/nodejs-functions/.serverless/nodejs-functions.zip differ diff --git a/nodejs-functions/src/handlers/automatic-clock-outs/clock_out.js b/nodejs-functions/src/handlers/automatic-clock-outs/clock_out.js index 7e77862..52de7d4 100644 --- a/nodejs-functions/src/handlers/automatic-clock-outs/clock_out.js +++ b/nodejs-functions/src/handlers/automatic-clock-outs/clock_out.js @@ -1,9 +1,11 @@ +const _ = require('lodash'); const CosmosClient = require("@azure/cosmos").CosmosClient; const config = require("./config"); const TimeEntry = require('./time_entry'); const axios = require('axios'); -const MsalClient = require('./msal_client') -const TimeEntryDao = require('./time_entry_dao') +const MsalClient = require('./msal_client'); +const TimeEntryDao = require('./time_entry_dao'); +const SlackClient = require('./slack_client'); const doClockOut = async (context) => { context.log(`I am going to check how many entries were not clocked out ${new Date()}`); @@ -16,21 +18,27 @@ const doClockOut = async (context) => { const response = await MsalClient.findUsersInMS(); const users = response.data.value; + const slackUsers = await SlackClient.findUsersInSlack(); + const {resources: entries} = await timeEntryDao.getEntriesWithNoEndDate(); context.log(`Checking for time-entries that need to be clocked out`); let totalClockOutsExecuted = 0; - const usersWithClockOut = [] + const usersWithClockOut = []; await Promise.all(entries.map(async (timeEntryAsJson) => { const timeEntry = new TimeEntry(timeEntryAsJson) if (timeEntry.needsToBeClockedOut()) { - usersWithClockOut.push(findUser(users, timeEntry.timeEntry.owner_id)); + const user_email = findUserEmail(users, timeEntry.timeEntry.owner_id); + const userId = findSlackUserId(slackUsers,user_email); + if(userId){ + usersWithClockOut.push("<@"+userId+">"); + } timeEntryAsJson.end_date = timeEntry.getTimeToClockOut() await container.item(timeEntryAsJson.id, timeEntryAsJson.tenant_id).replace(timeEntryAsJson) totalClockOutsExecuted++ } })); - if (totalClockOutsExecuted > 0) { + if (usersWithClockOut.length) { axios.post(slackWebHook, { "text": `OMG, you have been working more than 12 hours in a row. \nPlease take a break and visit https://timetracker.ioet.com/ to set the right end time for your entries, we just did a clock out for you :wink: \n- ${usersWithClockOut.join('\n- ')}` @@ -46,9 +54,14 @@ const doClockOut = async (context) => { context.log(`I just clocked out ${totalClockOutsExecuted} entries, thanks are not needed...`); } -const findUser = (users, id) => { +const findUserEmail = (users, id) => { const user = users.find(user => user.objectId === id) - return user.displayName + return _.first(user.otherMails) +} + +const findSlackUserId = (users,email)=>{ + const user = users.find(user => user.email === email); + return user? user.id:null } module.exports = {doClockOut}; diff --git a/nodejs-functions/src/handlers/automatic-clock-outs/config.js b/nodejs-functions/src/handlers/automatic-clock-outs/config.js index c2028d3..84328ca 100644 --- a/nodejs-functions/src/handlers/automatic-clock-outs/config.js +++ b/nodejs-functions/src/handlers/automatic-clock-outs/config.js @@ -6,7 +6,8 @@ const config = { clientId: process.env["CLIENT_ID"], authority: process.env["AUTHORITY"], clientSecret: process.env["CLIENT_SECRET"], - slackWebHook: process.env["SLACK_WEBHOOK"] + slackWebHook: process.env["SLACK_WEBHOOK_NOTIFY"], + slackApiToken: process.env["SLACK_TOKEN_NOTIFY"], }; module.exports = config; diff --git a/nodejs-functions/src/handlers/automatic-clock-outs/package-lock.json b/nodejs-functions/src/handlers/automatic-clock-outs/package-lock.json index 0cc7627..1ccab33 100644 --- a/nodejs-functions/src/handlers/automatic-clock-outs/package-lock.json +++ b/nodejs-functions/src/handlers/automatic-clock-outs/package-lock.json @@ -200,6 +200,46 @@ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true }, + "@slack/logger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-2.0.0.tgz", + "integrity": "sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==", + "requires": { + "@types/node": ">=8.9.0" + } + }, + "@slack/types": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-1.10.0.tgz", + "integrity": "sha512-tA7GG7Tj479vojfV3AoxbckalA48aK6giGjNtgH6ihpLwTyHE3fIgRrvt8TWfLwW8X8dyu7vgmAsGLRG7hWWOg==" + }, + "@slack/web-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.1.0.tgz", + "integrity": "sha512-9MVHw+rDBaFvkvzm8lDNH/nlkvJCDKRIjFGMdpbyZlVLsm4rcht4qyiL71bqdyLATHXJnWknb/sl0FQGLLobIA==", + "requires": { + "@slack/logger": ">=1.0.0 <3.0.0", + "@slack/types": "^1.7.0", + "@types/is-stream": "^1.1.0", + "@types/node": ">=12.0.0", + "axios": "^0.21.1", + "eventemitter3": "^3.1.0", + "form-data": "^2.5.0", + "is-stream": "^1.1.0", + "p-queue": "^6.6.1", + "p-retry": "^4.0.0" + }, + "dependencies": { + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + } + } + }, "@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -230,6 +270,14 @@ "@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/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -239,8 +287,7 @@ "@types/node": { "version": "14.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz", - "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==", - "dev": true + "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -248,6 +295,11 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "acorn": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", @@ -384,6 +436,11 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "ava": { "version": "3.11.1", "resolved": "https://registry.npmjs.org/ava/-/ava-3.11.1.tgz", @@ -718,6 +775,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", @@ -906,6 +971,11 @@ } } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1002,6 +1072,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", @@ -1083,6 +1158,16 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1719,6 +1804,19 @@ "picomatch": "^2.0.5" } }, + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + }, + "mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "requires": { + "mime-db": "1.46.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -1915,6 +2013,39 @@ "aggregate-error": "^3.0.0" } }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + } + } + }, + "p-retry": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.4.0.tgz", + "integrity": "sha512-gVB/tBsG+3AHI1SyDHRrX6n9ZL0Bcbifps9W9/Bgu3Oyu4/OrAh8SvDzDsvpP0oxfCt3oWNT+0fQ9LyUGwBTLg==", + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -2226,6 +2357,11 @@ "signal-exit": "^3.0.2" } }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", diff --git a/nodejs-functions/src/handlers/automatic-clock-outs/package.json b/nodejs-functions/src/handlers/automatic-clock-outs/package.json index e04938c..ceb28ad 100644 --- a/nodejs-functions/src/handlers/automatic-clock-outs/package.json +++ b/nodejs-functions/src/handlers/automatic-clock-outs/package.json @@ -12,6 +12,7 @@ "axios": "^0.20.0", "dotenv": "^8.2.0", "moment": "^2.27.0", + "@slack/web-api": "^6.0.0", "msal": "^1.4.0" }, "author": "", diff --git a/nodejs-functions/src/handlers/automatic-clock-outs/readme.md b/nodejs-functions/src/handlers/automatic-clock-outs/readme.md index 6da3082..1832f44 100644 --- a/nodejs-functions/src/handlers/automatic-clock-outs/readme.md +++ b/nodejs-functions/src/handlers/automatic-clock-outs/readme.md @@ -37,6 +37,7 @@ KEY='XXX' CLIENT_ID='XXX' AUTHORITY='XXX' CLIENT_SECRET='XXX' -SLACK_WEBHOOK='XXX' +SLACK_WEBHOOK_NOTIFY='XXX' +SLACK_TOKEN_NOTIFY='XXX' ``` Check the pinned message in our slack channel to get these values diff --git a/nodejs-functions/src/handlers/automatic-clock-outs/slack_client.js b/nodejs-functions/src/handlers/automatic-clock-outs/slack_client.js new file mode 100644 index 0000000..fcd49a6 --- /dev/null +++ b/nodejs-functions/src/handlers/automatic-clock-outs/slack_client.js @@ -0,0 +1,18 @@ +const { WebClient, LogLevel } = require("@slack/web-api"); +const { slackApiToken } = require("./config"); + +const client = new WebClient(slackApiToken,{logLevel: LogLevel.DEBUG}); + +const findUsersInSlack = async () => { + const response = await client.users.list(); + let usersIdAndEmails = []; + if (response.ok) { + slackUsers = response.members; + usersIdAndEmails = slackUsers + .filter((user) => user.profile.hasOwnProperty("email") && !user.deleted) + .map((user) => ({ id: user.id, email: user.profile.email })); + } + return usersIdAndEmails; +}; + +module.exports = { findUsersInSlack };