async def _send_adaptive_card(self, turn_context: TurnContext, card_type: int): card = None if card_type == 1: card_path = card_path = os.path.join(os.getcwd(), "cards\\bot_action.json") with open(card_path, "rb") as in_file: card_data = json.load(in_file) card = CardFactory.adaptive_card(card_data) elif card_type == 2: card_path = card_path = os.path.join(os.getcwd(), "cards\\task_module.json") with open(card_path, "rb") as in_file: card_data = json.load(in_file) card = CardFactory.adaptive_card(card_data) elif card_type == 3: card_path = card_path = os.path.join(os.getcwd(), "cards\\submit_action.json") with open(card_path, "rb") as in_file: card_data = json.load(in_file) card = CardFactory.adaptive_card(card_data) else: raise Exception("Invalid card type. Must be 1, 2 or 3.") reply_activity = MessageFactory.attachment(card) await turn_context.send_activity(reply_activity)
def test_should_raise_error_for_adaptive_card_if_card_is_not_dict(self): try: CardFactory.adaptive_card(None) except TypeError: pass else: assert False, "should have raise TypeError"
async def _send_adaptive_card(self, turn_context: TurnContext, card_type: str): card = None card_path = "" if card_type == "botaction": if PLATFORM == WINDOWS: card_path = os.path.join(os.getcwd(), "cards\\bot_action.json") else: card_path = os.path.join(os.getcwd(), "cards/bot_action.json") with open(card_path, "rb") as in_file: card_data = json.load(in_file) card = CardFactory.adaptive_card(card_data) elif card_type == "taskmodule": if PLATFORM == WINDOWS: card_path = os.path.join(os.getcwd(), "cards\\task_module.json") else: card_path = os.path.join(os.getcwd(), "cards/task_module.json") with open(card_path, "rb") as in_file: card_data = json.load(in_file) card = CardFactory.adaptive_card(card_data) elif card_type == "submit": if PLATFORM == WINDOWS: card_path = os.path.join(os.getcwd(), "cards\\submit_action.json") else: card_path = os.path.join(os.getcwd(), "cards/submit_action.json") with open(card_path, "rb") as in_file: card_data = json.load(in_file) card = CardFactory.adaptive_card(card_data) else: raise Exception("Invalid card type. Must be botaction, taskmodule, submit, or hero.") reply_activity = MessageFactory.attachment(card) await turn_context.send_activity(reply_activity)
async def on_teams_task_module_fetch( self, turn_context: TurnContext, task_module_request: TaskModuleRequest ) -> TaskModuleResponse: await turn_context.send_activity( MessageFactory.text( f"on_teams_task_module_fetch:{json.dumps(task_module_request.data)}" ) ) card = CardFactory.adaptive_card( { "version": "1.0.0", "type": "AdaptiveCard", "body": [ {"type": "TextBlock", "text": "Enter Text Here",}, { "type": "Input.Text", "id": "usertext", "placeholder": "add some text and submit", "IsMultiline": "true", }, ], "actions": [{"type": "Action.Submit", "title": "Submit",}], } ) task_info = TaskModuleTaskInfo( card=card, title="Adaptive Card: Inputs", height=200, width=400 ) continue_response = TaskModuleContinueResponse(type="continue", value=task_info) return TaskModuleResponse(task=continue_response)
async def send_execsum(req: Request) -> Response: try: credentials = MicrosoftAppCredentials(CONFIG.APP_ID, CONFIG.APP_PASSWORD) client = ConnectorClient(credentials, 'https://smba.trafficmanager.net/fr/') teams_client = TeamsConnectorClient(credentials, 'https://smba.trafficmanager.net/fr/') teams_channels = teams_client.teams.get_teams_channels('19:[email protected]') general_channel = next(channel for channel in teams_channels.conversations if channel.name is None) conversation_parameters = ConversationParameters( is_group=True, channel_data={"channel": {"id": general_channel.id}}, activity=MessageFactory.attachment( CardFactory.adaptive_card({ "type": "AdaptiveCard", "version": "1.0", "body": [ { "type": "TextBlock", "text": "[email protected] sent an execsum", }, ], "actions": [ { "type": "Action.OpenUrl", "title": "View execsum", "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" } ] }) ), ) client.conversations.create_conversation(conversation_parameters) return Response(status=HTTPStatus.OK) except Exception: traceback.print_exc()
async def confirm_step(self, step_context: WaterfallStepContext) -> DialogTurnResult: LOGGER.debug(msg=f"{CreateDeliveryDialog.__name__}: confirmation step.") # Set the delivery destination to what they entered in response to the destination prompt. delivery: Delivery = step_context.values[Keys.DELIVERY_DIALOG_STATE.value] # capture the response from the previous step delivery.time = step_context.result[0].value message_text = f"""{ messages.DELIVERY_SCHEDULED % (delivery.item, delivery.destination, delivery.time)} {messages.IS_THAT_ALL}""" prompt_options = PromptOptions( prompt=MessageFactory.text(message_text) ) DeliveryCard["body"][0]["text"] = f"Item: {delivery.item}" DeliveryCard["body"][1]["text"] = f"Destination: {delivery.destination}" DeliveryCard["body"][2]["text"] = f"Time: {delivery.time}" await step_context.context.send_activity( Activity( type=ActivityTypes.message, text=MessageFactory.text(message_text), attachments=[ CardFactory.adaptive_card(DeliveryCard) ], ) ) return await step_context.prompt(ConfirmPrompt.__name__, prompt_options)
async def choice_step( self, step_context: WaterfallStepContext) -> DialogTurnResult: intent, recognizer_result = await LuisHelper.execute_luis_query( self.recognizer, step_context.context) step_context.values[self.REMINDER] = recognizer_result text = (step_context.context.activity.text if step_context.context.activity.text else "").lower() if intent == Intent.SNOOZE_REMINDER.value: await self._snooze_reminder(step_context.context, recognizer_result) return await step_context.end_dialog() elif text.startswith("delete"): await self._delete_reminder(step_context.context) return await step_context.end_dialog() elif text.startswith("show"): await self._show_reminders(step_context.context) return await step_context.end_dialog() elif intent == Intent.CREATE_REMINDER.value: return await step_context.next(None) elif intent == Intent.HELP.value: message = Activity( type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(Cards.help_card())], ) await step_context.context.send_activity(message) return await step_context.end_dialog() else: await step_context.context.send_activity(Messages.missed) await self._send_suggested_actions(step_context.context) return await step_context.end_dialog()
def _create_adaptive_card_attachment(self) -> Attachment: card_path = os.path.join(os.getcwd(), CARDS[0]) with open(card_path, "r") as in_file: card_data = json.load(in_file) card_data["body"][1]["text"] = AdaptiveCardsBot.name[0] card_data["body"][4]["columns"][0]["items"][0][ "text"] = AdaptiveCardsBot.source[0].title() card_data["body"][4]["columns"][2]["items"][0][ "text"] = AdaptiveCardsBot.destination[0].title() card_data["body"][7]["columns"][0]["items"][0][ "text"] = AdaptiveCardsBot.destination[0].title() card_data["body"][7]["columns"][2]["items"][0][ "text"] = AdaptiveCardsBot.source[0].title() card_data["body"][4]["columns"][0]["items"][1][ "text"] = AdaptiveCardsBot.a[AdaptiveCardsBot.source[0].title()] card_data["body"][4]["columns"][2]["items"][1][ "text"] = AdaptiveCardsBot.a[ AdaptiveCardsBot.destination[0].title()] card_data["body"][7]["columns"][0]["items"][1][ "text"] = AdaptiveCardsBot.a[ AdaptiveCardsBot.destination[0].title()] card_data["body"][7]["columns"][2]["items"][1][ "text"] = AdaptiveCardsBot.a[AdaptiveCardsBot.source[0].title()] with open(card_path, "w") as in_file: json.dump(card_data, in_file) return CardFactory.adaptive_card(card_data)
def __get_task_module_adaptive_card_options() -> Attachment: adaptive_card = { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.0", "type": "AdaptiveCard", "body": [ { "type": "TextBlock", "text": "Task Module Invocation from Adaptive Card", "weight": "bolder", "size": 3, }, ], "actions": [{ "type": "Action.Submit", "title": card_type.button_title, "data": { "msteams": { "type": "task/fetch" }, "data": card_type.id }, } for card_type in [ TaskModuleUIConstants.ADAPTIVE_CARD, TaskModuleUIConstants.CUSTOM_FORM, TaskModuleUIConstants.YOUTUBE, ]], } return CardFactory.adaptive_card(adaptive_card)
async def card_response(context: TurnContext) -> web.Response: response = context.activity.text.strip() # Creates choice dictionary for users to determine which fight they want to see # Numbers, <Red Corner> vs. <Blue Corner>, <Red_Corner>, and <Blue_Corner> all accepted fight_card_json, fighter_information = read_in_jsons() fight_night = create_fightnight_cards(fight_card_json, fighter_information) choice_dict = {} for i in fight_night: choice_dict[str(i[2])] = i[0] for j in i[1]: choice_dict[j] = i[0] # If the stripped response from the user is not found in our choice_dict, default to None choice = choice_dict.get(response, None) # If the user's choice was not found, respond saying the bot didn't understand the user's response. if not choice: not_found = await create_reply_activity(context.activity, 'Sorry, I didn\'t understand that. :( Please try again.') await context.send_activity(not_found) return web.Response(status=202) else: card = CardFactory.adaptive_card(choice) # for func in choice: # card = func() response = await create_reply_activity(context.activity, '', card) await context.send_activity(response) return web.Response(status=200)
async def list_deliveries( self, step_context: WaterfallStepContext) -> DialogTurnResult: LOGGER.debug(msg=f"{ListDeliveriesDialog.__name__}: list deliveries") recipient: ChannelAccount = step_context.context.activity.recipient data = await self.storage.read([recipient.id]) # get this member's state member_state = data.get(recipient.id, {}) delivery_list: DeliveryList = member_state.get( Keys.DELIVERY_LIST_STATE.value) if delivery_list: deliveries: [Delivery] = delivery_list.deliveries for delivery in deliveries: DeliveryCard["body"][0]["text"] = delivery.item DeliveryCard["body"][1]["text"] = delivery.destination DeliveryCard["body"][2]["text"] = delivery.time message = Activity( type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(DeliveryCard)], ) await step_context.context.send_activity(message) else: await step_context.context.send_activity(messages.NO_DELIVERIES) return await step_context.end_dialog()
async def _mention_adaptive_card_activity(self, turn_context: TurnContext): TeamsChannelAccount: member = None try: member = await TeamsInfo.get_member( turn_context, turn_context.activity.from_property.id) except Exception as e: if "MemberNotFoundInConversation" in e.args[0]: await turn_context.send_activity("Member not found.") return else: raise card_path = os.path.join(os.getcwd(), ADAPTIVECARDTEMPLATE) with open(card_path, "rb") as in_file: template_json = json.load(in_file) for t in template_json["body"]: t["text"] = t["text"].replace("${userName}", member.name) for e in template_json["msteams"]["entities"]: e["text"] = e["text"].replace("${userName}", member.name) e["mentioned"]["id"] = e["mentioned"]["id"].replace( "${userUPN}", member.user_principal_name) e["mentioned"]["id"] = e["mentioned"]["id"].replace( "${userAAD}", member.additional_properties["aadObjectId"]) e["mentioned"]["name"] = e["mentioned"]["name"].replace( "${userName}", member.name) adaptive_card_attachment = Activity( attachments=[CardFactory.adaptive_card(template_json)]) await turn_context.send_activity(adaptive_card_attachment)
async def on_members_added_activity(self, members_added: List[ChannelAccount], turn_context: TurnContext): for member_added in turn_context.activity.members_added: if member_added.id != turn_context.activity.recipient.id: activity: Activity = MessageFactory.attachment( CardFactory.adaptive_card(WELCOME_CARD)) await turn_context.send_activity(activity)
def get_azure_vms_card(vms: List[Vm], start_idx: int) -> Attachment: data = json.loads(AZURE_VMS_CARD_JSON_TEMPLATE) idx_col, name_col, rg_col = data["body"][1]["columns"] for i, vm in enumerate(vms): idx_col["items"].append(_get_vm_idx_dict(idx=start_idx+i)) name_col["items"].append(_get_vm_name_dict(name=vm.name)) rg_col["items"].append(_get_vm_rg_dict(rg=vm.rg)) return CardFactory.adaptive_card(card=data)
def _create_adaptive_card_attachment(self, card_name): relative_path = os.path.abspath(os.path.dirname(__file__)) path = os.path.join( relative_path, f"""../cards/{str(card_name)[0]}/{str(card_name)}.json""") with open(path) as in_file: card = json.load(in_file) return CardFactory.adaptive_card(card)
def hotel_card( info, img): #Display hotel card - with pictures and hotel info message imgs = [{"type": "Image", "url": i} for i in img] card = adaptive_list.hotel_card card['body'][0]['text'] = info card['body'][1]['images'] = imgs return CardFactory.adaptive_card(card)
def _populate_with_data(self, rel_path: list) -> Attachment: """pulls the stock data from the Yahoo Finance API, populates the Adaptive card with that data and creates an Attachment instance with that Card Args: rel_path (list): path to the Adaptive card json file structured as a list [folder, filename] Returns: Attachment: An Attachment instance ready to be attached to a message """ tikk = yf.Ticker("TIK1V.HE") res = tikk.history(period='2d') open_price = res.tail(1)['Open'][0] high_price = res.tail(1)['High'][0] low_price = res.tail(1)['Low'][0] # if we already have a closing price - use it, otherwise take an open # price actual_price = res.tail(1)['Close'][0] if res.tail( 1)['Close'][0] > 0 else res.tail(1)['Open'][0] day = res.tail(1).index[0] day = day.to_pydatetime() day = day.date().strftime("%B %d %Y") diff_open = res.diff()['Close'][1] if res.diff()[ 'Close'][1] != 0 else res.diff()['Open'][1] diff_open = round(diff_open, 2) # construct a string comparing the price with the previous day diff_percent = round((diff_open / actual_price) * 100, 2) if diff_open > 0: symbol, color = "▲", "Good" elif diff_open < 0: symbol, color = "▼", "Attention" else: symbol, color = "►", "Default" diff_percent_str = "(" + str(diff_percent) + "%)" price_string = " ".join([symbol, str(diff_open), diff_percent_str]) this_file = os.getcwd() full_path = os.path.join(this_file, *rel_path) with open(full_path, "r") as card_file: card_json = json.load(card_file) # atm there is no dynamic templating for Python Adaptive cards SDK, # hence the brute force approach card_json["body"][0]["items"][1]["text"] = day card_json["body"][1]["items"][0]["columns"][0]["items"][0]["text"] = actual_price card_json["body"][1]["items"][0]["columns"][0]["items"][1]["text"] = price_string card_json["body"][1]["items"][0]["columns"][0]["items"][1]["color"] = color card_json["body"][1]["items"][0]["columns"][1]["items"][0]["facts"][0]["value"] = open_price card_json["body"][1]["items"][0]["columns"][1]["items"][0]["facts"][1]["value"] = high_price card_json["body"][1]["items"][0]["columns"][1]["items"][0]["facts"][2]["value"] = low_price return CardFactory.adaptive_card(card_json)
def _create_adaptive_card_attachment(self) -> Attachment: """ Load attachment from file. :return: """ card_path = os.path.join(os.getcwd(), "cards/welcomeCard.json") with open(card_path, "rt") as in_file: card_data = json.load(in_file) return CardFactory.adaptive_card(card_data)
def get_gcp_instances_card(instances: List[Instance], start_idx: int) -> Attachment: data = json.loads(GCP_INSTANCES_CARD_JSON_TEMPLATE) idx_col, name_col, project_col = data["body"][1]["columns"] for i, instance in enumerate(instances): idx_col["items"].append(_get_instance_idx_dict(idx=start_idx + i)) name_col["items"].append(_get_instance_name_dict(name=instance.name)) project_col["items"].append( _get_instance_project_dict(project=instance.project)) return CardFactory.adaptive_card(card=data)
def _create_adaptive_card_attachment() -> Attachment: """ Load a random adaptive card attachment from file. :return: """ card_path = os.path.join(os.getcwd(), "resources/VideoCard.json") with open(card_path, "rb") as in_file: card_data = json.load(in_file) return CardFactory.adaptive_card(card_data)
def _create_adaptive_card_attachment(self) -> Attachment: """ Load a random adaptive card attachment from file. :return: """ random_card_index = random.randint(0, len(CARDS) - 1) card_path = os.path.join(os.getcwd(), CARDS[random_card_index]) with open(card_path, "rb") as in_file: card_data = json.load(in_file) return CardFactory.adaptive_card(card_data)
async def test_should_display_choices_on_hero_card_with_additional_attachment(self): size_choices = ["large", "medium", "small"] card = CardFactory.adaptive_card( { "type": "AdaptiveCard", "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.2", "body": [], } ) card_activity = Activity(attachments=[card]) async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results: DialogTurnResult = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions(prompt=card_activity, choices=size_choices) await dialog_context.prompt("prompt", options) elif results.status == DialogTurnStatus.Complete: selected_choice = results.result await turn_context.send_activity(selected_choice.value) await convo_state.save_changes(turn_context) def assert_expected_activity( activity: Activity, description ): # pylint: disable=unused-argument assert len(activity.attachments) == 2 assert ( activity.attachments[0].content_type == CardFactory.content_types.adaptive_card ) assert ( activity.attachments[1].content_type == CardFactory.content_types.hero_card ) adapter = TestAdapter(exec_test) convo_state = ConversationState(MemoryStorage()) dialog_state = convo_state.create_property("dialogState") dialogs = DialogSet(dialog_state) choice_prompt = ChoicePrompt("prompt") # Change the ListStyle of the prompt to ListStyle.none. choice_prompt.style = ListStyle.hero_card dialogs.add(choice_prompt) step1 = await adapter.send("Hello") await step1.assert_reply(assert_expected_activity)
async def genMessage(self) -> Activity: """Generate the Activity object containing the adapative card message.""" # Generate the JSON object for the card cardJsonStr = await self.card.to_json() cardJson = json.loads(cardJsonStr) # Create the Adaptive Card message message = Activity(type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(cardJson)]) # Return the message return message
async def function_BUILD_QUESTION(question: str, detales: str, language: str, email: str, ticket_id: str) -> Attachment: date = datetime.now().timetuple() if language == "English": title = "Question for operator:" last_sentence = "Best regards, \n mail " + email ticket_id = f"Ticket id {ticket_id}" elif language == "Русский": title = "Вопрос для отправки оператору:" last_sentence = "С наилучшими пожеланиями, \n mail " + email ticket_id = f"Номер запроса {ticket_id}" else: title = "Question for operator/Вопрос для отправки оператору:" last_sentence = "Best regards/С наилучшими пожеланиями, \n mail " + email ticket_id = f"Ticket id/Номер запроса {ticket_id}" return CardFactory.adaptive_card({ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.0", "type": "AdaptiveCard", "body": [ { "type": "TextBlock", "text": title, "weight": "bolder", "isSubtle": False, }, {"type": "TextBlock", "text": question, "weight": "bolder", "separator": True}, {"type": "TextBlock", "text": detales, "spacing": "none", "wrap": True}, { "type": "TextBlock", "text": last_sentence, "weight": "bolder", "spacing": "medium", }, { "type": "TextBlock", "text": ticket_id, "weight": "bolder", "spacing": "medium", }, { "type": "TextBlock", "text": str(date[1]) + "/" + str(date[2]) + "/" + str(date[0]), "weight": "bolder", "spacing": "none", }, ], } )
async def _show_reminders(self, turn_context: TurnContext): reminder_log = await self.reminders_accessor.get( turn_context, ReminderLog) reminder_list = reminder_log.new_reminders + reminder_log.old_reminders if len(reminder_list) == 0: await turn_context.send_activity(Messages.no_reminders) activity_mapping_state = await self.conversation_state_accessor.get( turn_context, ActivityMappingState) for reminder in reminder_list: reminder_card = Cards.reminder_card(reminder) message = Activity( type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(reminder_card)], ) sent_activity = await turn_context.send_activity(message) activity_mapping_state.activities[reminder.id] = sent_activity.id
async def __send_result_card(self, turn_context: TurnContext): ADAPTIVE_CARD_CONTENT = { "type": "AdaptiveCard", "body": [{ "type": "TextBlock", "size": "Medium", "weight": "Bolder", "text": "Result", "wrap": True }, { "type": "FactSet", "facts": [{ "title": "Name", "value": (f"{r_name}") }, { "title": "URN", "value": (f"{r_urn}") }, { "title": "Sender", "value": (f"{r_sender}") }, { "title": "message", "value": (f"{r_message}") }, { "title": "Url", "value": (f"{r_url}") }], "separator": True }], "actions": [{ "type": "Action.OpenUrl", "title": "View results", "url": (f"{r_url}") }], "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.2" } return await turn_context.send_activity( MessageFactory.attachment( CardFactory.adaptive_card(ADAPTIVE_CARD_CONTENT)))
async def __send_mdt_card(self, turn_context: TurnContext): ADAPTIVE_CARD_CONTENT = { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.2", "body": [ { "type": "TextBlock", "text": "List of Care Providers for MDT", "wrap": True, #"horizontalAlignment": "Center", "size": "Medium", "weight": "Bolder", "separator": True }, { "type": "TextBlock", #"horizontalAlignment": "Center", "text": (f"{cp_list}"), "wrap": True, "separator": True } ], "actions": [{ "type": "Action.Submit", "title": "Create a MDT meeting channel", "data": { "msteams": { "type": "imBack", "value": "standard channel" } } }], "id": "Mention Care Providers", "fallbackText": "Mention Care Providers" } return await turn_context.send_activity( MessageFactory.attachment( CardFactory.adaptive_card(ADAPTIVE_CARD_CONTENT)))
def __send_welcome_card(self, member_name) -> Attachment: ADAPTIVE_CARD_CONTENT = { "type": "AdaptiveCard", "version": "1.0", "body": [{ "type": "ImageSet", "images": [{ "type": "Image", "horizontalAlignment": "Center", "url": "https://www.smallbizgenius.net/wp-content/uploads/2019/10/chatbot-4071274_1920_710x473.jpg", "id": "Image1", "spacing": "None", "separator": True, "size": "Large", "height": "stretch" }], "horizontalAlignment": "Left", "id": "", "spacing": "Padding", "imageSize": "Large" }, { "type": "TextBlock", "text": "Welcome to WeChat! I am developed to provide you with latest news update", "horizontalAlignment": "Center", "wrap": True, "fontType": "Monospace", "color": "Dark", "size": "Large", }], "$schema": "http://adaptivecards.io/schemas/adaptive-card.json" } #print(type(CardFactory.adaptive_card(ADAPTIVE_CARD_CONTENT))) return CardFactory.adaptive_card(ADAPTIVE_CARD_CONTENT)
async def remind_user(turn_context: TurnContext, accessor): reminder_log = await accessor.get(turn_context, ReminderLog) timezone = pytz.timezone("Africa/Nairobi") now_local = datetime.now().astimezone(timezone) now = datetime.strftime(now_local, "%Y-%m-%d %I:%M") if len(reminder_log.new_reminders) > 0: reminder_time = datetime.strftime( sorted(reminder_log.new_reminders)[0].reminder_time, "%Y-%m-%d %I:%M %p", ) if now in reminder_time: reminder = reminder_log.new_reminders.pop(0) snooze_card = Cards.snooze_card(reminder) message = Activity( type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(snooze_card)], ) reminder.done = True reminder_log.old_reminders.append(reminder) await turn_context.send_activity(message)
async def confirm_step( self, step_context: WaterfallStepContext) -> DialogTurnResult: reminder: Reminder = step_context.values[self.REMINDER] reminder.reminder_time = (step_context.result[0].value if not reminder.reminder_time else reminder.reminder_time) if reminder.reminder_time < datetime.now().astimezone( pytz.timezone("Africa/Nairobi")): await step_context.context.send_activity(Messages.bad_time) return await step_context.end_dialog() await step_context.context.send_activity(Messages.done) reminder_card = Cards.reminder_card(reminder) await step_context.context.send_activity( Activity( type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(reminder_card)], )) await self._save_reminder(step_context) return await step_context.end_dialog()