diff --git a/index.js b/index.js index e8f7d6b..61cd980 100644 --- a/index.js +++ b/index.js @@ -152,6 +152,10 @@ module.exports = async function(robot) { require('./integrations/github')(robot, kredits); require('./integrations/gitea')(robot, kredits); + if (typeof process.env.KREDITS_ZOOM_JWT !== 'undefined') { + require('./integrations/zoom')(robot, kredits); + } + if (typeof process.env.KREDITS_MEDIAWIKI_URL !== 'undefined') { require('./integrations/mediawiki')(robot, kredits); } diff --git a/integrations/zoom.js b/integrations/zoom.js new file mode 100644 index 0000000..817ca31 --- /dev/null +++ b/integrations/zoom.js @@ -0,0 +1,97 @@ +const fetch = require('node-fetch'); + +module.exports = async function(robot, kredits) { + + function messageRoom(message) { + robot.messageRoom(process.env.KREDITS_ROOM, message); + } + + const { Contributor, Contribution } = kredits; + + const kreditsContributionAmount = 500; + const kreditsContributionKind = 'community'; + + const zoomAccessToken = process.env.KREDITS_ZOOM_JWT; + + const walletTransactionCount = await kredits.provider.getTransactionCount(kredits.signer.address); + let nonce = walletTransactionCount; + + async function createContributionFor (displayName, meeting) { + const contributor = await getContributorByZoomDisplayName(displayName); + + if (!contributor) { + robot.logger.info(`[hubot-kredits] Contributor not found: Zoom display name: ${displayName}`); + messageRoom(`I tried to add a contribution for zoom user ${displayName}, but did not find a matching contributor profile.`); + return Promise.resolve(); + } + + const contribution = { + contributorId: contributor.id, + contributorIpfsHash: contributor.ipfsHash, + amount: kreditsContributionAmount, + kind: kreditsContributionKind, + description: 'Team/Community Call', + date: meeting.end_time.split('T')[0], + time: meeting.end_time.split('T')[1] + } + + return Contribution.add(contribution, { nonce: nonce++ }) + .then(tx => { + robot.logger.info(`[hubot-kredits] Contribution created: ${tx.hash}`); + }) + .catch(error => { + robot.logger.error(`[hubot-kredits] Adding contribution for Zoom call failed:`, error); + }); + } + + function getContributorByZoomDisplayName(displayName) { + return Contributor.findByAccount({ site: 'zoom.us', username: displayName }); + } + + function request(path) { + return fetch( + `https://api.zoom.us/v2${path}`, + {headers: {authorization: `Bearer ${zoomAccessToken}`}} + ); + } + + function getMeetingParticipants(meetingUUID) { + return request(`/past_meetings/${meetingUUID}/participants`) + .then(response => response.json()) + .then(json => json.participants) + } + + function getMeetingDetails(meetingUUID) { + return request(`/past_meetings/${meetingUUID}`) + .then(r => r.json()); + } + + async function handleZoomMeetingEnded(data) { + const meetingDetails = await getMeetingDetails(data.uuid); + const participants = await getMeetingParticipants(data.uuid); + + if (meetingDetails.duration < 15 || meetingDetails.participants_count < 3) { + robot.logger.info(`[hubot-kredits] Ignoring zoom call ${data.uuid} (duration: ${meetingDetails.duration}, participants_count: ${meetingDetails.participants_count})`); + return; + } + + const names = Array.from(new Set(participants.map(p => p.name))); + + for (const displayName of names) { + await createContributionFor(displayName, meetingDetails); + }; + } + + robot.router.post('/incoming/kredits/zoom/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => { + let data = req.body; + const eventName = data.event; + const payload = data.payload; + const object = payload.object; + + if (eventName === 'meeting.ended') { + handleZoomMeetingEnded(object); + } + + res.sendStatus(200); + }) +}