Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SLACK_BOT_TOKEN=
SLACK_SIGNING_SECRET=
SLACK_CHANNEL_ID=
SHEET_ID=
SHEET_RANGE=Sheet1!A2:C
7 changes: 7 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extends: [ airbnb-base ]
env:
node: true
rules:
no-console:
- error
- allow: [ error ]
36 changes: 1 addition & 35 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,40 +1,6 @@
# General
.DS_Store
.AppleDouble
.LSOverride

# Development
node_modules
*.swp
.tern-port
.nvimrc
*.pyc
*.ds_store
.env

# Secret things
credentials.json
output.csv
token.json

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
.serverless
51 changes: 32 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,45 @@

A Slackbot that reads from a Google Sheet to post scheduled, fun updates.

# Setup
## Prerequisites

1. Create a .env file containing the `GOOGLE_SHEET_ID` from which the bot will
read event details.
- Node
- NPM
- A Google account

2. Update the `config.json` to point to the correct columns in the
configuration Google Sheet.
## Getting Started

# Installation
This project requires a Google account with the Google Sheets API enabled. See Google's [Node.js Quickstart](https://developers.google.com/sheets/api/quickstart/nodejs) for instructions on enabling the API.

More details coming soon.
Once the Google Sheets API has been enabled, an option for downloading client configuration is presented. Save the file as `credentials.json` in the project directory. These credentials will be used to generate access and refresh tokens thereby granting access to data stored in Google Sheets.

# Slash Commands
1. Clone the repo `git clone git@github.com:FreshworksStudio/hurray.git`
2. Navigate to project directory `cd hurray`
3. Install dependencies `npm install`
4. If running the program for the first time, access and refresh tokens must be generated
1. Run `npm run token`
2. Open a web browser and navigte to the link generated by the previous command
3. Log in to Google and allow the application access
4. Copy the code generated after logging in and paste it into the terminal
5. Confirm that a `token.json` file is now located in the project directory
5. Run `npm start` to start the server
6. Navigate to [localhost:3000](https://localhost:3000)

List all workiversaries for a specific month:
```
/hurray work [month]
```
## Create a Slack Bot

Example:
```
/hurray work september
```
TODO

## Slash Commands

Slash commands are used within Slack to interact with a bot. The following commands are made available. All commands are prefaced with `/hurray`.

Command | Arguments | Description
--------|-----------|------------
work | Month | List all employees with a workiversary in the specified month

As an example, all employees with workiversaries in September can be listed by typing `/hurray work september` in Slack. The bot will return a response in the following format.

Example response:
```
Jane Smith - 13
Billy Bob - 22
Jane Smith - September 13
Billy Bob - September 22
```
4 changes: 0 additions & 4 deletions config.json

This file was deleted.

104 changes: 104 additions & 0 deletions handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const { App, ExpressReceiver } = require('@slack/bolt');
const awsServerlessExpress = require('aws-serverless-express');
const { getSheetContent } = require('./sheets.js');
const {
reportingDates, employeeFromRow, workiversariesByDates, workiversariesByMonth,
} = require('./workiversaries.js');

const expressReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
processBeforeResponse: true,
});
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
receiver: expressReceiver,
processBeforeResponse: true,
});
const months = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];

const titleCase = (s) => `${s.charAt(0).toUpperCase()}${s.slice(1)}`;

const queryView = (month, blocks) => ({
type: 'modal',
title: {
type: 'plain_text',
text: `${titleCase(month)} Workiversaries`,
},
close: {
type: 'plain_text',
text: 'Close',
},
blocks,
});

const errorView = (month) => ({
type: 'modal',
title: {
type: 'plain_text',
text: 'Error',
},
close: {
type: 'plain_text',
text: 'Close',
},
blocks: [{
type: 'section',
text: { type: 'mrkdwn', text: `Invalid month: "${month}"` },
}],
});

const employeeBlocks = (employees) => {
if (employees.length <= 0) {
return [{
type: 'section',
text: { type: 'mrkdwn', text: 'No employees with workiversaries this period' },
}];
}
return employees.map((employee) => ({
type: 'section',
text: {
type: 'mrkdwn',
text: `*${employee.preferred}* ${employee.hireDate.format('MMMM D')}`,
},
}));
};

app.command('/hurray', async ({ body, ack, context }) => {
try {
await ack();
console.log(body);

const data = await getSheetContent(process.env.SHEET_ID, process.env.SHEET_RANGE);
const employees = data[0].values.map(employeeFromRow);

if (typeof body.text !== 'string' || body.text.trim() === '') { // Empty
const workiversaries = workiversariesByDates(employees, reportingDates());
await app.client.chat.postMessage({
token: process.env.SLACK_BOT_TOKEN,
channel: process.env.SLACK_CHANNEL_ID,
text: { blocks: employeeBlocks(workiversaries) },
});
} else if (months.includes(body.text.trim().toLowerCase())) { // Valid month
const month = body.text.trim();
const workiversaries = workiversariesByMonth(employees, month);
await app.client.views.open({
token: context.botToken,
trigger_id: body.trigger_id,
view: queryView(month, employeeBlocks(workiversaries)),
});
} else { // Invalid month
await app.client.views.open({
token: context.botToken,
trigger_id: body.trigger_id,
view: errorView(body.text),
});
}
} catch (error) {
console.error(error);
}
});

const server = awsServerlessExpress.createServer(expressReceiver.app);
module.exports.dispatcher = (event, context) => {
awsServerlessExpress.proxy(server, event, context);
};
93 changes: 0 additions & 93 deletions index.js

This file was deleted.

Loading