lib.praqma.toggl-bot
Tag 0
Scripts querying Toggl and applying certain rules on Praqma's workspace
  Published by praqma on Jan 16th 2018, 3:03 PM
  # toggl-rules Scripts querying Toggl and applying certain custom rules on Praqma's workspace Read more about the project seed provided by `stdlib` [here](stdlib.md) The document on the Design Decisions can be found [here](docs/design-and-usage-decisions.md). Have a look at our [Roadmap](docs/design-and-usage-decisions.md#roadmap) too! ## StdLib - function as service Our business logic is deployed to stdlib.com, a standard library for JavaScript functions as microservices. The application endpoints are exposed via a REST API, but can also be accessed in a number of different ways (from terminal, as package etc.) This is what a REST API endpoint looks that will trigger the function execution: `https://praqma.lib.id/toggl-bot@dev/mail_invoices/` 1. `toggl-bot` defines the service name 1. `@dev` will use the code deployed to the development environment. In production it references the version number, like `@1.3.0` 1. `mail_invoices` is the name of the function we want to call 1. Any URL parameter is appended to the URL, like `.../mail_invoices/?user=epe&name=eddie` ## Versions |Version|Changes| |---|---| |0.3.0|Added feat. to auto-join people to the toggl channel.| |0.4.0|Added feat. to PM people who did not log time the day before| |0.4.1|Fixed a bug in dailyCheckup [#70](https://github.com/Praqma/toggl-rules/issues/70)| |0.5.0|Added function notifying about missing task descriptions. <br> Improved security with token based authentication.| |0.5.1|Added more details to messages on missing task descriptions| |0.5.2|Fixed bug that would cause posting in the testing channel| ## Functions in Service ### 1. Daily Checkup This function fetches all time entries for the day before and runs a check on them to see if they have a task and project properely assigned. The users who did not enter their time registration correctly will be mentioned the next morning in a Slack post in the timelogging channel. Later the day, an evening check will follow up to make sure people fixed thier registrations. #### Rules - [x] No time entry without a Praqma client -> post to Slack - [x] No time entry without a task -> post to Slack - [x] Users are invited if not a member of the channel - [ ] No project without client -> notify project manager Using the stdlib web interface we can set Scheduled Tasks that will trigger our functions periodically. This function is set to execute every weekday morning at 9am local time. Scheduled Tasks' settings can be found [here](https://dashboard.stdlib.com/dashboard/#/tasks). #### Scheduling Tasks When a function is released into production (see the release steps below) it can be set to trigger periodically in the visual scheduler of stdlib. This is a manual step each time a new version is released. - Log in with your credentials and navigate to the [StdLib Dashboard](https://dashboard.stdlib.com/dashboard) - Create a new scheduled task: ![Deployment step 1](docs/deployment-step1.png) - Task configuration ![Deployment step 2](docs/deployment-step2.png) 1. Choose a name. E.g. Morning Check / Evening Reminder 1. Prefer the most recent version. 1. The function `daily_checkup` should be triggered in the daily checks. 1. To schedule a morning checkup choose `false`. If you are setting up an evening reminder, pick `true`. 1. Set the schedule. Note that times are in UTC (+1), so for a reminder at 9AM pick 8AM instead. 1. Verify your schedule will perform at the time when you expect it. 1. Save the setting. - Repeat the above steps for setting up a second reminder (e.g evening check). - When you created new ones, delete the already existing scheduled tasks, so they won't be duplicate. The channel field can be left blank and will default to our `toggl-plz-fix` channel. Change it if you want to post to a different channel. #### Inviting users Users that are not members of the `toggl-plz-fix` channel will automatically be joined if their name is mentioned. Since invitation to channels cannot be done with a bot token, we will use a user's token which has the required access to the API. A Slack user called `Bot User` was set up with the email `pintern@praqma.com` who is a contributor to our Toggl-Bot Slack app. Thus we can use its access token to join people to channels. ### 2. Mail Invoices (aka Invoice Lines) See issue [#48](https://github.com/Praqma/toggl-rules/issues/48) on the invoice report and #57 on the email setup This program generates the invoices for the current month as a CSV file. When triggered it will send this file to **invoicelines@praqma.net**. ![Email exmaple](docs/email-example.png) Initially, it was proposed to use a template slack application provided by *stdlib* to hook up to our slash commands. This application would authenticate itself when installing it to the workspace using the provided credentials. Thus the exposed APIs would only be accessible for our application. However, due to an error with the authentication, this solution was not implemented. **Solution**: We ended up publicly exposing an API endpoint (also using *stdlib*) which when called will trigger the function sending the email. This endpoint then could be directly attached to the slash command. Even writing a response back to the user. #### Slash comands The API endpoint for the *Mail Invoices* function is attached to a slash command and can be triggered from Praqma's Slack workspace. Typing `/invoices` will run the program and deliver the invoice report to the right person's inbox. ![Slash command exmaple](docs/slash-command-example.png) In the settings we can configure the command. ![Slash command settings](docs/slash-command-setting.png) You can read about the template app and how to build a Slack bot with slash commands [here](https://medium.com/slack-developer-blog/build-a-serverless-slack-bot-in-9-minutes-with-node-js-and-stdlib-b993cfa15358). #### Security See issue [#62](https://github.com/Praqma/toggl-rules/issues/62) on security This function is not protected with OATH nor other forms of authentication, as the API endpoint is publicly exposed. Since the report can only be sent to the hard-coded address, this publicly available function call is not a security concern. In the worst case someone can spam a little our inboxes. But only a bit as unauthenticated calls from outside stdlib have been set a limit of 100 requests per hour. Our services run being authenticated to the platform. ### 3. Comments (Projects requiring task description) See issue [#32](https://github.com/Praqma/toggl-rules/issues/32) If users forget to add a description to their Toggl time entry, this program will check that for the day before and notify them on Slack so they can add it. It is a scheduled task on StdLib that will run every weekday at 3pm. A filter is defined in the configuration that will allow for certain projects/clients to not require a description. This function also joins users that are not members of the channel. ### 4. Daily Logs PM See issue [#67](https://github.com/Praqma/toggl-rules/issues/67) When a user did not log any time the previous working day Toggl-Bot will notify them about it in a PM (private message). ## Authentication All of our functions except the **invoice lines** are protected with token based authentication by StdLib and can only be access with Praqma's library token found on StdLib. When a function is scheduled for execution it is triggered directly by the Praqma account and the token is automatically passed on. Authentication over HTTP happens via POST requests. Add an `Authentication` header to the request with the value `Bearer <token>`. You can find the token under `authorizationToken` in the [toggl-rules-configuration](https://github.com/Praqma/toggl-rules-configuration/blob/f1f70585deddda8455a79b2780a6d4d46651a19a/credentials.json). Slash commands in Slack use GET requests to perform an action. Therefore, we need to leave the **invoice_lines** function unprotected as Slack cannot perform POST requests at the moment. We will look into other forms of authentication for the **invoice_lines** in the future. Read more about [Authenticating your StdLib microservices](http://docs.stdlib.com/main/#/calling-services/authentication) ## Credentials Learn more: [Separate the what from the how](docs/design-and-usage-decisions.md#separate-the-what-from-the-how). See also issue [#53](https://github.com/Praqma/toggl-rules/issues/53). We use token based authentication for our bot interactions with Slack and Toggl. A private repo is imported as submodule under `/toggl-rules-configuration` which contains all credentials and configuration. The *Daily Checkup* function uses a token provided by Toggl to query the time registrations from their API. Can be found in `credentials.json > toggl.autorizationToken`. The token belonging to the bot created in the web interface of Slack will be used to authenticate messages to our channels. See in `credentials.json > slackBot.apiToken` ## Contributing ### Getting a running project on your computer 1. Since this repo contains a submodule, clone it with the `recursive` flag. `git clone --recursive <this-repo>` 1. Install project dependencies - inside the project run the command: ```sh npm install ``` If you are not from Praqma and do not have access to the private submodule containing the credentials, go over to our [Contributions Guidelines](CONTRIBUTING.md) ### Deploying the changes This project is deployed to `StdLib.com`, a funcion as service platform becoming popular after Amazons's Lambda service. First make sure you have the npm package `lib` installed globally ```sh sudo npm install -g lib ``` #### Running the function locally To test the `daily_checkup` function, run this command in your terminal in the root of your project: ```sh lib .daily_checkup ``` If the execution was successful it will return the following message potentially with some logs on top ```sh { "ok": true, "message": "Message delivered to Slack channel." } ``` #### Deploying for testing in the cloud ```sh lib up dev ``` #### Releasing a production version - First, up the version number in `package.json` - Publish to stdlib with the command: ```sh lib release ``` #### Logging ```shell $ lib logs praqma.toggl-bot # shows all logs, all envs + fns $ lib logs praqma.toggl-bot[@dev] # main fn logs $ lib logs praqma.toggl-bot[@dev].start # start logs $ lib logs praqma.toggl-bot[@dev].shutdown # shutdown logs $ lib logs praqma.toggl-bot[@dev]* # all dev fn logs ```
# toggl-rules Scripts querying Toggl and applying certain custom rules on Praqma's workspace Read more about the project seed provided by `stdlib` [here](stdlib.md) The document on the Design Decisions can be found [here](docs/design-and-usage-decisions.md). Have a look at our [Roadmap](docs/design-and-usage-decisions.md#roadmap) too! ## StdLib - function as service Our business logic is deployed to stdlib.com, a standard library for JavaScript functions as microservices. The application endpoints are exposed via a REST API, but can also be accessed in a number of different ways (from terminal, as package etc.) This is what a REST API endpoint looks that will trigger the function execution: `https://praqma.lib.id/toggl-bot@dev/mail_invoices/` 1. `toggl-bot` defines the service name 1. `@dev` will use the code deployed to the development environment. In production it references the version number, like `@1.3.0` 1. `mail_invoices` is the name of the function we want to call 1. Any URL parameter is appended to the URL, like `.../mail_invoices/?user=epe&name=eddie` ## Versions |Version|Changes| |---|---| |0.3.0|Added feat. to auto-join people to the toggl channel.| |0.4.0|Added feat. to PM people who did not log time the day before| |0.4.1|Fixed a bug in dailyCheckup [#70](https://github.com/Praqma/toggl-rules/issues/70)| |0.5.0|Added function notifying about missing task descriptions. <br> Improved security with token based authentication.| |0.5.1|Added more details to messages on missing task descriptions| |0.5.2|Fixed bug that would cause posting in the testing channel| ## Functions in Service ### 1. Daily Checkup This function fetches all time entries for the day before and runs a check on them to see if they have a task and project properely assigned. The users who did not enter their time registration correctly will be mentioned the next morning in a Slack post in the timelogging channel. Later the day, an evening check will follow up to make sure people fixed thier registrations. #### Rules - [x] No time entry without a Praqma client -> post to Slack - [x] No time entry without a task -> post to Slack - [x] Users are invited if not a member of the channel - [ ] No project without client -> notify project manager Using the stdlib web interface we can set Scheduled Tasks that will trigger our functions periodically. This function is set to execute every weekday morning at 9am local time. Scheduled Tasks' settings can be found [here](https://dashboard.stdlib.com/dashboard/#/tasks). #### Scheduling Tasks When a function is released into production (see the release steps below) it can be set to trigger periodically in the visual scheduler of stdlib. This is a manual step each time a new version is released. - Log in with your credentials and navigate to the [StdLib Dashboard](https://dashboard.stdlib.com/dashboard) - Create a new scheduled task: ![Deployment step 1](docs/deployment-step1.png) - Task configuration ![Deployment step 2](docs/deployment-step2.png) 1. Choose a name. E.g. Morning Check / Evening Reminder 1. Prefer the most recent version. 1. The function `daily_checkup` should be triggered in the daily checks. 1. To schedule a morning checkup choose `false`. If you are setting up an evening reminder, pick `true`. 1. Set the schedule. Note that times are in UTC (+1), so for a reminder at 9AM pick 8AM instead. 1. Verify your schedule will perform at the time when you expect it. 1. Save the setting. - Repeat the above steps for setting up a second reminder (e.g evening check). - When you created new ones, delete the already existing scheduled tasks, so they won't be duplicate. The channel field can be left blank and will default to our `toggl-plz-fix` channel. Change it if you want to post to a different channel. #### Inviting users Users that are not members of the `toggl-plz-fix` channel will automatically be joined if their name is mentioned. Since invitation to channels cannot be done with a bot token, we will use a user's token which has the required access to the API. A Slack user called `Bot User` was set up with the email `pintern@praqma.com` who is a contributor to our Toggl-Bot Slack app. Thus we can use its access token to join people to channels. ### 2. Mail Invoices (aka Invoice Lines) See issue [#48](https://github.com/Praqma/toggl-rules/issues/48) on the invoice report and #57 on the email setup This program generates the invoices for the current month as a CSV file. When triggered it will send this file to **invoicelines@praqma.net**. ![Email exmaple](docs/email-example.png) Initially, it was proposed to use a template slack application provided by *stdlib* to hook up to our slash commands. This application would authenticate itself when installing it to the workspace using the provided credentials. Thus the exposed APIs would only be accessible for our application. However, due to an error with the authentication, this solution was not implemented. **Solution**: We ended up publicly exposing an API endpoint (also using *stdlib*) which when called will trigger the function sending the email. This endpoint then could be directly attached to the slash command. Even writing a response back to the user. #### Slash comands The API endpoint for the *Mail Invoices* function is attached to a slash command and can be triggered from Praqma's Slack workspace. Typing `/invoices` will run the program and deliver the invoice report to the right person's inbox. ![Slash command exmaple](docs/slash-command-example.png) In the settings we can configure the command. ![Slash command settings](docs/slash-command-setting.png) You can read about the template app and how to build a Slack bot with slash commands [here](https://medium.com/slack-developer-blog/build-a-serverless-slack-bot-in-9-minutes-with-node-js-and-stdlib-b993cfa15358). #### Security See issue [#62](https://github.com/Praqma/toggl-rules/issues/62) on security This function is not protected with OATH nor other forms of authentication, as the API endpoint is publicly exposed. Since the report can only be sent to the hard-coded address, this publicly available function call is not a security concern. In the worst case someone can spam a little our inboxes. But only a bit as unauthenticated calls from outside stdlib have been set a limit of 100 requests per hour. Our services run being authenticated to the platform. ### 3. Comments (Projects requiring task description) See issue [#32](https://github.com/Praqma/toggl-rules/issues/32) If users forget to add a description to their Toggl time entry, this program will check that for the day before and notify them on Slack so they can add it. It is a scheduled task on StdLib that will run every weekday at 3pm. A filter is defined in the configuration that will allow for certain projects/clients to not require a description. This function also joins users that are not members of the channel. ### 4. Daily Logs PM See issue [#67](https://github.com/Praqma/toggl-rules/issues/67) When a user did not log any time the previous working day Toggl-Bot will notify them about it in a PM (private message). ## Authentication All of our functions except the **invoice lines** are protected with token based authentication by StdLib and can only be access with Praqma's library token found on StdLib. When a function is scheduled for execution it is triggered directly by the Praqma account and the token is automatically passed on. Authentication over HTTP happens via POST requests. Add an `Authentication` header to the request with the value `Bearer <token>`. You can find the token under `authorizationToken` in the [toggl-rules-configuration](https://github.com/Praqma/toggl-rules-configuration/blob/f1f70585deddda8455a79b2780a6d4d46651a19a/credentials.json). Slash commands in Slack use GET requests to perform an action. Therefore, we need to leave the **invoice_lines** function unprotected as Slack cannot perform POST requests at the moment. We will look into other forms of authentication for the **invoice_lines** in the future. Read more about [Authenticating your StdLib microservices](http://docs.stdlib.com/main/#/calling-services/authentication) ## Credentials Learn more: [Separate the what from the how](docs/design-and-usage-decisions.md#separate-the-what-from-the-how). See also issue [#53](https://github.com/Praqma/toggl-rules/issues/53). We use token based authentication for our bot interactions with Slack and Toggl. A private repo is imported as submodule under `/toggl-rules-configuration` which contains all credentials and configuration. The *Daily Checkup* function uses a token provided by Toggl to query the time registrations from their API. Can be found in `credentials.json > toggl.autorizationToken`. The token belonging to the bot created in the web interface of Slack will be used to authenticate messages to our channels. See in `credentials.json > slackBot.apiToken` ## Contributing ### Getting a running project on your computer 1. Since this repo contains a submodule, clone it with the `recursive` flag. `git clone --recursive <this-repo>` 1. Install project dependencies - inside the project run the command: ```sh npm install ``` If you are not from Praqma and do not have access to the private submodule containing the credentials, go over to our [Contributions Guidelines](CONTRIBUTING.md) ### Deploying the changes This project is deployed to `StdLib.com`, a funcion as service platform becoming popular after Amazons's Lambda service. First make sure you have the npm package `lib` installed globally ```sh sudo npm install -g lib ``` #### Running the function locally To test the `daily_checkup` function, run this command in your terminal in the root of your project: ```sh lib .daily_checkup ``` If the execution was successful it will return the following message potentially with some logs on top ```sh { "ok": true, "message": "Message delivered to Slack channel." } ``` #### Deploying for testing in the cloud ```sh lib up dev ``` #### Releasing a production version - First, up the version number in `package.json` - Publish to stdlib with the command: ```sh lib release ``` #### Logging ```shell $ lib logs praqma.toggl-bot # shows all logs, all envs + fns $ lib logs praqma.toggl-bot[@dev] # main fn logs $ lib logs praqma.toggl-bot[@dev].start # start logs $ lib logs praqma.toggl-bot[@dev].shutdown # shutdown logs $ lib logs praqma.toggl-bot[@dev]* # all dev fn logs ```
The "Add to Slack" landing page for your app. To modify the template, check out /pages/index.ejs.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters
(no parameters expected)
  code
Slack Actions (Interactive Messages) Response Handler This function receives actions (interactive messages) from Slack and dispatches the appropriate handler. You should use this function as the endpoint for all actions, and place action handlers in /functions/actions/NAME.js, where NAME is the name of your action. You can test from the command line using: lib .actions.NAME [username] [channel name] For more about interactive messages and how to respond to them, see Slack's documentation: https://api.slack.com/docs/message-buttons
  info
context
enabled
bg
info
pricing
free
rate limits
  parameters
(no parameters expected)
  code
example.js Basic example action handler. Called in response to an input from an interactive message. All Actions in response to interactive messages use this template, simply create additional files with different names to add actions. See https://api.slack.com/docs/message-buttons for more details.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
user
(required)
The user id of the user that invoked this command (name is usable as well)
{string}
channel
(required)
The channel id the command was executed in (name is usable as well)
{object}
action
= {}
The full Slack action object
{string}
botToken
= null
The bot token for the Slack bot you have activated
  code
Authorization HTML page to grant Slack App OAuth Permission To modify the template, check out /pages/auth.ejs.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
code
= null
Slack-provided authorization code
{string}
error
= ""
Slack-provided error
  code
Slack Slash Command Handler: This function receives slash commands from Slack and dispatches the appropriate handler. You should use this function as the endpoint for all commands, and place commands in /functions/commands/NAME.js, where NAME is the name of your command. You can test individual slash commands from the command line with: $ lib .commands.NAME [username] [channel] [text]
  info
context
enabled
bg
info
pricing
free
rate limits
  parameters
(no parameters expected)
  code
/hello Basic "Hello World" command. All Commands use this template, simply create additional files with different names to add commands. See https://api.slack.com/slash-commands for more details.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
user
(required)
The user id of the user that invoked this command (name is usable as well)
{string}
channel
(required)
The channel id the command was executed in (name is usable as well)
{string}
text
= ""
The text contents of the command
{object}
command
= {}
The full Slack command object
{string}
botToken
= null
The bot token for the Slack bot you have activated
  code
/invoices See https://api.slack.com/slash-commands for more details.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
user
(required)
The user id of the user that invoked this command (name is usable as well)
{string}
channel
(required)
The channel id the command was executed in (name is usable as well)
{string}
text
= ""
The text contents of the command
{object}
command
= {}
The full Slack command object
{string}
botToken
= null
The bot token for the Slack bot you have activated
  code
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters
(no parameters expected)
  code
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
channel
= ""
(no description)
{boolean}
isEveningReminder
= false
(no description)
  code
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters
(no parameters expected)
  code
Slack Event Handler: This function receives events from Slack and dispatches the appropriate handler. If an event has no subtype, it will invoke /functions/events/TYPE.js or /functions/events/TYPE/__main__.js, otherwise it will invoke /functions/events/TYPE/SUBTYPE.js. You can test individual events from the command line with: $ lib .events.TYPE.SUBTYPE [username] [channel] [text] The "@bg params" line below tells StdLib that when this function is invoked as a background function over HTTP it should just respond with whatever parameters were passed in as a JSON object. (This handles Slack's "challenge" parameter.)
  info
context
enabled
bg
params
pricing
free
rate limits
  parameters
(no parameters expected)
  code
message event All events use this template, simply create additional files with different names to add event responses See https://api.slack.com/events-api for more details.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
user
(required)
The user id of the user that invoked this event (name is usable as well)
{string}
channel
(required)
The channel id the event was executed in (name is usable as well)
{string}
text
= ""
The text contents of the event
{object}
event
= {}
The full Slack event object
{string}
botToken
= null
The bot token for the Slack bot you have activated
  code
channel_join event See https://api.slack.com/events-api for more details.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
user
(required)
The user id of the user that invoked this event (name is usable as well)
{string}
channel
(required)
The channel id the event was executed in (name is usable as well)
{string}
text
= ""
The text contents of the event
{object}
event
= {}
The full Slack event object
{string}
botToken
= null
The bot token for the Slack bot you have activated
  code
/invoices See https://api.slack.com/slash-commands for more details.
  info
context
disabled
bg
info
pricing
free
rate limits
  parameters (click to modify)
{string}
token
(required)
(no description)
  code