Skip to content

Commit

Permalink
Added meeting reminder (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
ReneZumtobel authored and mburtscher committed Jul 16, 2018
1 parent 5ee2d31 commit 0be8aeb
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
.idea/*
config.json
config/config.json
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ A complete [config.json](config.example.json) could look like this:
Currently the following providers are supported:

* [Bring](https://getbring.com/)
* Meeting Reminder (sends a reminder for a Google Calender event via Slack)
* [Slack](https://slack.com/)

If the provider you are looking for is currently not supported, feel free to integrate it into the project. Therefore have a look at the Contribute section.
Expand Down
25 changes: 25 additions & 0 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
"text": ":grin:",
"icon_emoji": "true"
}
},
{
"mac": "xx:xx:xx:xx:xx:xx",
"provider": "meetingReminder",
"providerConfig": {
"calendarId": "primary",
"time": "now"
}
}
],
"providerConfig": {
Expand All @@ -36,6 +44,23 @@
},
"slack": {
"token": "xxxx"
},
"meetingReminder": {
"slackToken": "xxxxx",
"secretFile": {
"installed": {
"client_id": "xxxxx",
"project_id": "xxxx",
"auth_uri": "xxxxx",
"token_uri": "xxxxx",
"auth_provider_x509_cert_url": "xxxxx",
"client_secret": "xxxxx",
"redirect_uris": [
"xxxxx",
"http://localhost"
]
}
}
}
}
}
1 change: 1 addition & 0 deletions files/opt/dash-button/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"node-dash-button": "^0.6.1",
"request": "^2.81.0",
"node-fs": "^0.1.7",
"googleapis": "^32.0.0",
"@slack/client": "^4.3.1"
}
}
1 change: 1 addition & 0 deletions files/opt/dash-button/scripts/providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ A README.md files for a specific provider is located in each provider folder.
Currently the following providers are supported:

* [Bring](bring/README.md)
* [Meeting Reminder](meetingReminder/README.md)
* [Slack](slack/README.md)

Note: Please be aware that the provider must have the same name as the js file in the providers folder in order to match the provider.
39 changes: 39 additions & 0 deletions files/opt/dash-button/scripts/providers/meetingReminder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Configure Meeting Reminder

The Meeting Reminder takes an event from a given Google Calender and sends a reminder to all attendants on Slack.
(E-Mail addresses are for finding attendants on Slack)

You will have to set up a project and enable it on Google in order to use the Calender api.
You can follow Step 1 of Googles Node.js Quickstart [here](https://developers.google.com/calendar/quickstart/nodejs).
After creating and downloading the client configuration, copy the content of the json file to the "secretFile" field in your config.json.

The time parameter allows to control when the event has to have it's endtime. (If you insert 10 it will take the first event with an endtime after the next 10 minutes)

Here is an example configuration:

```json
{
"buttons": [
{
"mac": "xx:xx:xx:xx:xx:xx",
"provider": "meetingReminder",
"providerConfig": {
"calendarId": "primary",
"time": "now"
}
},
{
"mac": "xx:xx:xx:xx:xx:xx",
"provider": "meetingReminder",
"providerConfig": {
"calendarId": "primary",
"time": "10"
}
}
],
"providerConfig": {

}
}

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// require modules needed to send Slack messages
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const { WebClient } = require('@slack/client');

const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];

// export module for main script
module.exports = {
dashHandler : function (sender, providerConfig) {
meetingReminderHandler(sender, providerConfig);
}
};

function meetingReminderHandler(sender, providerConfig) {
try {
const content = providerConfig.secretFile;
authorize(content, readEvent, sender, providerConfig);
} catch (err) {
return console.log('Error loading client secret file:', err);
}
}

// Create an OAuth2 client with the given credentials, and then execute the given callback function.
function authorize(credentials, callback, sender, providerConfig) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
let token = {};
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);

if (providerConfig.googleToken != undefined) {
token = providerConfig.googleToken;
} else {
getAccessToken(oAuth2Client, callback, sender, providerConfig);
}

// Check if we have previously stored a token.
if (providerConfig.googleToken != undefined) {
token = providerConfig.googleToken;
} else {
return getAccessToken(oAuth2Client, callback, sender, providerConfig, function (res) {
console.log(res);
});
}
oAuth2Client.setCredentials(token);
callback(oAuth2Client, sender, providerConfig);
}

// Get and store new token after prompting for user authorization, and then
// execute the given callback with the authorized OAuth2 client.
function getAccessToken(oAuth2Client, callback, sender, providerConfig) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return callback(err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
try {
providerConfig['googleToken'] = token;
const updateProviderConfig = require('../../script');
updateProviderConfig.updateProviderConfig(providerConfig,'meetingReminder');
} catch (err) {
console.error(err);
}
callback(oAuth2Client, sender, providerConfig);
});
});
}

// Reads google calender event and calls send message function
function readEvent(auth, sender, providerConfig) {
const calendar = google.calendar({version: 'v3', auth});
var startTime = new Date();
var time = sender.providerConfig.time;

if (time == 'now') {
startTime = (new Date()).toISOString();
} else {
time = startTime.getTime() + time * 60000;
startTime = (new Date(time)).toISOString();
}

calendar.events.list({
calendarId: sender.providerConfig.calendarId,
timeMin: startTime,
maxResults: 1,
singleEvents: true,
orderBy: 'startTime',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const events = res.data.items;
let emails = {};

events[0].attendees.forEach(value => {
emails[value.email] = value.email;
});
sendSlackReminder(emails, events[0], sender, providerConfig);
});
}

// Searches for userIds to send Slack message and calls send function
function sendSlackReminder(emails, appointment, sender, providerConfig) {
const web = new WebClient(providerConfig.slackToken);

let arguments = {
'token': providerConfig.slackToken
};

web.users.list(arguments).then((res) => {
res.members.forEach(value => {
if (emails[value.profile.email] != undefined) {
sendSlackMessage(value.id, appointment, providerConfig.slackToken);
}
});
}).catch(error => {
console.error('Slack: an error occurred: ', error);
});
}

// Sends reminder for appointment via Slack
function sendSlackMessage(id, appointment, token) {
const web = new WebClient(token);

var messageText = 'Reminder: ' + appointment.summary;
var start;
var now = new Date();

if (appointment.start.dateTime != undefined) {
start = new Date(appointment.start.dateTime);
} else {
start = new Date(appointment.start.date);
}

if (now < start) {
msec = start - now;
var min = Math.floor(msec / 1000 / 60);
var hour = Math.floor(msec / 1000 / 60 /60);
messageText = messageText + ' starts in ' + min % 60 + ' minutes' + (hour != 0 ? ' and ' + hour + ' hours!' : '!');
} else {
msec = now - start;
var min = Math.floor(msec / 1000 / 60);
messageText = messageText + ' started ' + min + ' minutes ago!';
}

let arguments = {
'token': token,
'channel': id,
'as_user': 'false',
'text': messageText
};

web.chat.postMessage(arguments).then((res) => {
console.log('Slack: Sent message: ', res.message.text);
}).catch(error => {
console.error('Slack: an error occurred: ', error);
});
}
31 changes: 27 additions & 4 deletions files/opt/dash-button/scripts/script.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const button = require('node-dash-button');
const fs = require('node-fs');
const pathToConfig = ('/opt/dash-button/config/config.json');

// Load config.json
const configJSON = fs.readFileSync('/config.json');
const config = JSON.parse(configJSON);
const buttonConfig = config.buttons;
const providerConfig = config.providerConfig;
let config = readConfig(pathToConfig);
let buttonConfig = config['buttons'];
let providerConfig = config['providerConfig'];

// Load providers
let providers = {};
Expand All @@ -26,3 +26,26 @@ buttonConfig.forEach(value => {
function dashHandler(sender) {
providers[sender.provider].dashHandler(sender, providerConfig[sender.provider]);
}

function readConfig(pathToConfig) {
try {
return JSON.parse(fs.readFileSync('/opt/dash-button/config/config.json'));
} catch (error) {
console.log(error);
}
}

module.exports = {
// Function for updating config.json if providerData changed
updateProviderConfig: function (provider, providerName) {
try {
providerConfig[providerName] = provider;
config.providerConfig = providerConfig;
fs.writeFile(pathToConfig, JSON.stringify(config), 'utf8', function (res) {
console.log(res);
});
} catch (error) {
console.log(error);
}
}
};
2 changes: 1 addition & 1 deletion run
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash -e

# Privileged is needed for being able to inspect packages in the host system
docker run -d --name dash-button --net=host --restart unless-stopped --privileged -v $(pwd)/config.json:/config.json fusonic/amazon-dash-buttons:latest
docker run -d --name dash-button --net=host --restart unless-stopped --privileged -v $(pwd)/config:/opt/dash-button/config fusonic/amazon-dash-buttons:latest
2 changes: 1 addition & 1 deletion rundev
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash -e

# Privileged is needed for being able to inspect packages in the host system
docker run -it --rm --net=host --privileged -v $(pwd)/config.json:/config.json -v ${PWD}/files/opt/dash-button/scripts:/opt/dash-button/scripts dev/dash-buttons
docker run -it --rm --net=host --privileged -v $(pwd)/config:/opt/dash-button/config -v ${PWD}/files/opt/dash-button/scripts:/opt/dash-button/scripts dev/dash-buttons

0 comments on commit 0be8aeb

Please sign in to comment.