Sending questions¶
Let’s write our first part of 1 of ours AWS Lambda functions.
Now we are going to work with standup_bot/scheduled.py
file which represents AWS Lambda function shecudeld-events
from schema
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Standalone lambda function triggered by CWE.
"""
import datetime as dt
import json
import logging
from slack import WebClient
from standup_bot.config import (
SLACK_TOKEN,
SLACK_CHANNEL,
QUESTIONS,
)
from standup_bot.models import Report
from standup_bot.msg_templates import standup_menu_block, report_block
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
# synchronous slack client
SC = WebClient(SLACK_TOKEN)
|
In highlighted lines above, we:
import
the functionality needed later,- initialize
logging
- initialize
Slack client
- note that we are using synchronous client in order to keep the code beginner friendly. However you it is possible to make the function async using asyncio and asynchronous slack client.
Let’s jump to the very bottom of the file and look at our lambda_handler
.
We will pay more attention to28-83
bit later.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | def lambda_handler(lambda_event, lambda_context):
"""Main lambda handler"""
LOGGER.debug(lambda_context)
LOGGER.info(lambda_event)
LOGGER.info("lambda_event starts:")
LOGGER.info(json.dumps(lambda_event))
# is it our CWE event?
if {"type", "time", "source"}.issubset(lambda_event):
report_id = dt.datetime.strptime(lambda_event["time"], '%Y-%m-%dT%H:%M:%S%z').strftime("%Y%m%d")
LOGGER.info("Report ID: %s", report_id)
if lambda_event["type"] == "send_report":
send_report(report_id)
elif lambda_event["type"] == "send_questions":
send_questions(report_id)
|
Event source (trigger) of our lambda function will be modified input from a cloudwatch event.
We are going to deal with two event types send_questions
and send_report
.
Code breakdown:
- L90-93 - Logging of input, so we can investigate what is going on via CloudWatch Logs.
- L98 - generate
report_id
in formatYYYYMMDD
- Then trigger entry point function based on event type
We are going to start with Sending a daily menu to the members of our SLACK_CHANNEL
.
Implement functions: send_menu
, send_menus
and send_questions
.
To send a menu to the user via private message, we need to first open the conversation,
then we can send a message.
Main body of our message will be a menu_block
, which consists of 2 buttons.
Where 1 button opens a dialog
with questions and second button allows user to skip todays report.
28 29 30 31 32 33 34 35 | def send_menu(user_id, menu_block):
"""Send menu as private message to the user."""
response = SC.conversations_open(users=[user_id])
post_response = SC.chat_postMessage(
channel=response["channel"]["id"], text="Daily menu", blocks=menu_block
),
return user_id, post_response
|
On line 33
we are using pre-built layout block from standup_bot/msg_templates
.
In function send_menus
we ask slack to get a list of channel members and send_menu
to each member.
38 39 40 41 42 | def send_menus(menu_block):
"""Send menu to all users from the channel."""
members = SC.conversations_members(channel=SLACK_CHANNEL)
for user_id in members['members']:
yield send_menu(user_id, menu_block)
|
As a last step we define our entry-point function send_questions
.
Where we generate menu_block
part of the slack message
and gather the delivery status responses.
45 46 47 48 49 | def send_questions(report_id):
"""Entry point for daily menu."""
menu_block = standup_menu_block(report_id)
results = list(send_menus(menu_block))
LOGGER.info(results)
|
Our menu_block
is a function which generates message blocks
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | def standup_menu_block(report_id):
"""
Message block with the menu sent on daily basis.
Contains Open Dialog and Skip buttons.
"""
return [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hello, it is time report on daily standup.",
},
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": True,
"text": "Open Report",
},
"style": "primary",
"value": report_id,
"action_id": "standup.action.open_dialog",
},
{
"type": "button",
"text": {"type": "plain_text", "emoji": True, "text": "Skip today"},
"action_id": "standup.action.skip_today",
},
],
},
]
|
Above we have generated 2 blocks with types section
and actions
.
We have given a specific action_id
to each element in order to recognize which button was clicked by user.
Processing of actions is further explained in the next section.
We can now test this part of our code and invoke our function locally with command:
sls invoke local -f scheduled-events --path example-data/cwe-questions.json
.
Make sure you are running this command from within same directory whereserverless.yml
is located. (sls_app
)
If the invocation was successful, you should receive a private message from your application which looks similar to what you can see in a picture below.