A Slack/Geekbot/Google Calendar App to sweep through 2i2c team members, assign Team Roles, and track them in our Team Roles calendar
This repository is a collection of Python code that tracks which 2i2c team members are currently serving in our Team Roles (Meeting Facilitator and Support Triager). The code works out which team member is due to take over a given role, and dynamically generates Geekbot standups in Slack to visibly and explicitly notify the given team member that they are due to take over the role. It also creates events in the Team Roles Google Calendar to make the role changes more visible.
:fire: The Meeting Facilitator role has been retired. While the code still remains in this repository, it is no longer actively run as part of CI/CD. :fire:
API docs:
Tutorials:
Installing the dependencies requires poetry
.
You can install the dependencies by running:
poetry install
sops
for secret decryptionSecrets required to execute the code in this repository are stored in the secrets
folder and are encrypted with sops
.
You will therefore need to have sops
installed to run this code locally.
See this guide to setup sops
before executing any code here.
Which 2i2c teams members are serving (or have served) in a given role are stored in the team-roles.json
file, which has the below structure.
We keep track of both a team member's Slack display name and user ID. This is because interacting with the Slack and Geekbot APIs requires the user ID, but it is more human-readable to also have the names.
For the Support Triager, we track both the current and incoming team members as we have two people overlapping in this role.
We have an extra role here called standup_manager
.
This is the team member who created GEEKBOT_API_KEY
and will be added to all standups.
This is because Geekbot only provides personal API keys and these keys do not have permission to see any standups the owner of said key is not a member of.
:fire: If you are changing this role, you will need to recreate GEEKBOT_API_KEY
. :fire:
{
"standup_manager": {
"name": "display_name",
"id": "slack_id"
},
"meeting_facilitator": {
"name": "display_name",
"id": "slack_id"
},
"support_triager": {
"incoming": {
"name": "display_name",
"id": "slack_id"
},
"current": {
"name": "display_name",
"id": "slack_id"
}
}
}
All scripts are written in Python and are located in the src
folder.
get_slack_usergroup_members.py
This script interacts with the Slack API to produce a dictionary of Slack users who are members of a given Slack usergroup and their IDs.
The script requires the usergroup_name
variable to be set, which is the name of the Slack usergroup to list members of, e.g., meeting-facilitators
or support-triagers
.
The script will generate a dictionary of members of usergroup_name
where the keys are the users' display names, and the values are their associated user IDs.
The dictionary is ordered alphabetically by its keys.
Command line usage:
Running the following command will print the dictionary of team members' names and IDs to the console.
poetry run list-members
Help info:
usage: list-members [-h] usergroup_name
List the members and IDs of a Slack usergroup
positional arguments:
usergroup_name The name of the Slack usergroup to list members of
optional arguments:
-h, --help show this help message and exit
update_team_roles.py
This script generates the next team member to serve in a given role by iterating one place through the appropriate Slack usergroup (either meeting-facilitators
or support-triagers
).
It depends on get_slack_usergroup_members.py
to pull the list of usergroup members from Slack.
The desired usergroup to pull the members of is parsed to the script via the USERGROUP_NAME
environment variable.
The team member currently serving in the role is pulled from the current event in the Team Roles calendar.
If no event is found, the current team member is read from the team-roles.json
file.
The updated team roles are written back to the same file.
There are command line options to determine which role is to be updated.
Command line usage:
To execute, run the following command:
poetry run update-team-role { meeting-facilitator | support-triager }
Help info:
usage: update-team-role [-h] {meeting-facilitator,support-triager}
Update our Team Roles by iterating through 2i2c team members
positional arguments:
{meeting-facilitator,support-triager}
The role to update
optional arguments:
-h, --help show this help message and exit
create_geekbot_standup.py
This script reads in team-roles.json
after it has been modified by update_team_roles.py
and generates a Geekbot standup to notify the incoming team member for their upcoming role.
The MeetingFacilitatorStandup
broadcasts to the team-updates
Slack channel, and the SupportTriagerStandup
broadcasts to the support-freshdesk
channel.
The Geekbot app needs to be installed to the Slack workspace and invited to the channels to which it will broadcast.
Command line options are provided to select which role a standup should be created for.
Command line usage:
To execute, run the following command:
poetry run create-standup { meeting-facilitator | support-triager }
Help info:
usage: create-standup [-h] {meeting-facilitator,support-triager}
Create Geekbot standup apps to manage the transition of Team Roles through 2i2c team members
positional arguments:
{meeting-facilitator,support-triager}
The role to create a Geekbot Standup to transition
optional arguments:
-h, --help show this help message and exit
set_current_roles.py
This script is used to initialise the team-roles.json
file with manual input.
It depends upon get_slack_usergroup_members.py
to convert Slack display names into user IDs.
This script requires the following environment variables to be set:
USERGROUP_NAMES
: The name of the Slack usergroup to list members of, e.g., meeting-facilitators
or support-triagers
.
Multiple usergroups can be provided by separating them with a comma.CURRENT_SUPPORT_TRIAGER
: The Slack display name of the team member currently serving in the Support Triager role (i.e. for more than one week)INCOMING_SUPPORT_TRIAGER
: The Slack display name of the team member most recently taking up service in the Support Triager role (i.e. for less than one week)STANDUP_MANAGER
: This is the Slack display name of the team member who created geekbot_api_token.json
and will be added to all standups.
This role is required since Geekbot only offers personal API keys and the script won't be able to see any exisitng standups that the owner of the key is not a member of.
:fire: If you are changing this role, you will need to recreate geekbot_api_token.json
. :fire:This script is paired with the populate-current-roles
workflow to commit the updated team-roles.json
file to the repo for future CI/CD runs of the bot.
Note: You can additionally provide the following environment variable, but it is no longer required since the Meeting Facilitator role is now retired:
CURRENT_MEETING_FACILITATOR
: The Slack display name of the team member currently serving in the Meeting Facilitator roleCommand line usage:
To execute this script, run:
poetry run populate-current-roles
create_events_rolling_update.py
This script is used to create the next event for a Team Role given that a series of events already exist in a Google Calendar.
It calculates the required metadata for the new event from the last event available on the calendar.
It depends upon get_slack_usergroup_members.py
to get an ordered list of the team members who fulfil these roles.
The desired usergroup is parsed to the script via the USERGROUP_NAME
environment variable.
Command line usage:
To execute this script, run:
poetry run create-next-event { meeting-facilitator | support-triager }
Help info:
usage: create-next-event [-h] {meeting-facilitator,support-triager}
Create the next event in a series for a Team Role in a Google Calendar
positional arguments:
{meeting-facilitator,support-triager}
The role to create an event for
optional arguments:
-h, --help show this help message and exit
create_events_bulk.py
This script is used to generate a large number of events for a Team Role in a Google Calendar in bulk.
It begins generating events either from the day the script is executed or from a provided reference date.
It depends upon get_slack_usergroup_members.py
to get an ordered list of the team members who fulfil these roles.
The desired usergroup is parsed to the script via the USERGROUP_NAME
environment variable.
Our Support Triager role starts and ends on Wednesdays for a period of 2 weeks with a team member rotating on/off the role every week.
The create_events_bulk.py
script accounts for this by adjusting the reference date to the next Wednesday in the calendar.
However, the next Wednesday might not necessarily line up with the 1/2 weekly cycle of the Support Triager.
So take caution when running this script and choose a reference date carefully before executing.
The two create_events_*.py
scripts can't delete events and so, if they are repeatedly run, will create duplicate events.
See delete_events_bulk.py
for information on deleting events from the calendar.
Command line usage:
To execute this script, run:
poetry run create-bulk-events { meeting-facilitator | support-triager }
Help info:
usage: create-bulk-events [-h] [-n N_EVENTS] [-d DATE] [-m TEAM_MEMBER] {meeting-facilitator,support-triager}
Bulk create a series of Team Role events in a Google Calendar
positional arguments:
{meeting-facilitator,support-triager}
The role to create events for
options:
-h, --help show this help message and exit
-n N_EVENTS, --n-events N_EVENTS
The number of role events to create. Defaults to 12 for Meeting Facilitator and 26 for Support Triager (both 1 year's worth).
-d DATE, --date DATE A reference date to begin creating events from. Defaults to appending events from the last in the series, or TODAY if no events exist. WARNING: EXPERIMENTAL
FEATURE. This flag is MUTUALLY INCLUSIVE with --team-member [-m].
-m TEAM_MEMBER, --team-member TEAM_MEMBER
The name of the team member currently serving in the role. Defaults to being pulled from either the last calendar event, or team-roles.json if a calendar event
doesn't not exist. This flag is MUTUALLY INCLUSIVE with --date [-d].
delete_events_bulk.py
This script is used to delete all upcoming events in the calendar for a role from a reference date.
We may wish to run this script when a team member has been onboarded/off-boarded from a role and we need to update the calendar en masse.
And so we can clear the upcoming events with this script, and regenerate events with create_events_bulk.py
.
A date from which to select events for deletion can be provided, and events whose start date is after this reference date will be retrieved. For instance, if you run the program on 2022-09-15, events that have start dates after that date will be retrieved.
This script requires parsing of the USERGROUP_NAME
environment variable because the CalendarEventHandler
class pulls members of a Slack usergroup when instantiated.
In the future, we should allow this to be overridden since this script only needs to the interact with the Google Calendar API.
Command line usage:
poetry run delete-bulk-events { meeting-facilitator | support-triager }
Help info:
usage: delete-bulk-events [-h] [-d DATE] {meeting-facilitator,support-triager}
Bulk delete all upcoming Team Role events in a Google Calendar
positional arguments:
{meeting-facilitator,support-triager}
The role to delete events for
options:
-h, --help show this help message and exit
-d DATE, --date DATE A reference date to begin creating events from. Defaults to TODAY.
gcal_api_auth.py
This script is a helper script that returns an authenticated instance of the Google Calendar API for the create_events_rolling_update.py
and create_events_bulk.py
to create events in a Google Calendar.
event_handling.py
This script is a helper script that centralises logic around generating metadata for events, creating them, and deleting them from a Google Calendar.
sops.py
This is a helper script that securely decrypts secrets using sops
into a temporary file for use throughout the package.
All our CI/CD workflows are powered by GitHub Actions and the configuration is located in the .github/workflows
folder.
populate-current-roles.yaml
This workflow runs the set_current_roles.py
script to generate an initial team-roles.json
file and commit it to the repo for use in future GitHub Actions workflow runs.
It can be triggered manually and requires the environment variables required by set_current_roles.py
and get_slack_usergroup_members.py
to be provided as inputs.
meeting-facilitator.yaml
:fire: The Meeting Facilitator role has been retired and, even though the workflow file still exists, it has been manually disabled. If we wish to bring back the Meeting Facilitator role, we can enable the workflow again. :fire:
This workflow file contains two jobs: create-standup
and update-calendar
.
It is scheduled to run at midnight UTC on the 28th of each month and can also be triggered manually using workflow dispatch.
The create-standup
job runs the create_geekbot_standup.py
script to update the Meeting Facilitator role in the team-roles.json
file and create/update a Geekbot Standup App to notify the new team member serving in the role.
When manually triggered, updating the team roles file is optional, for example if you'd just like to reset the Geekbot App.
The Geekbot App is configured to notify the next Meeting Facilitator on the first Monday of each month.
The update-calendar
job runs the create_events_rolling_update.py
script to create the next event in the series, keeping the calendar populated roughly one year in advance.
If running manually, this job can be skipped completely.
support-triager.yaml
This workflow file contains two jobs: create-standup
and update-calendar
.
It is scheduled to run at midnight UTC weekly on Mondays and can also be manually triggered using workflow dispatch.
The Geekbot App is configured to notify the next Support Triager every Wednesday.
The create-standup
job runs the create_geekbot_standup.py
to update the Support Triager role in the team-roles.json
file and create/update a Geekbot Standup App to notify the new team member serving in the role.
When manually triggered, updating the team roles file is optional, for example if you'd just like to reset the Geekbot App.
The update-calendar
job runs the create_events_rolling_update.py
script to create the next event in the series, keeping the calendar populated roughly one year in advance.
If running manually, this job can be skipped completely.
The following secrets with stated permissions are stored in the secrets
folder.
calendar_id.json
: The ID of a Google Calendar to which a GCP Service Account has permission to manage eventsgcp_service_account.json
: A Google Cloud Service Account Key with permissions to access Google's Calendar APIgeekbot_api_token.json
: A personal API token from the STANDUP_MANAGER
's account to authenticate against the Geekbot APIslack_bot_token.json
: A bot user token for a Slack App installed into the workspace.
The bot requires the usergroups:read
and users:read
permission scopes to operate.
It does not need to be a member of any channels in the Slack workspace.