8000 Merge pull request #81 from Microsoft/v-stgum/factories · umarfarook882/botbuilder-python@37d8c07 · GitHub
[go: up one dir, main page]

Skip to content

Commit 37d8c07

Browse files
authored
Merge pull request microsoft#81 from Microsoft/v-stgum/factories
add CardFactory, MessageFactory and tests
2 parents 4301de4 + e9d768c commit 37d8c07

File tree

5 files changed

+719
-0
lines changed

5 files changed

+719
-0
lines changed

libraries/botbuilder-core/botbuilder/core/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
from .bot_framework_adapter import BotFrameworkAdapter, BotFrameworkAdapterSettings
1212
from .bot_context import BotContext
1313
from .bot_state import BotState
14+
from .card_factory import CardFactory
1415
from .conversation_state import ConversationState
1516
from .memory_storage import MemoryStorage
17+
from .message_factory import MessageFactory
1618
from .middleware_set import AnonymousReceiveMiddleware, Middleware, MiddlewareSet
1719
from .storage import Storage, StoreItem, StorageKeyFactory, calculate_change_hash
1820
from .test_adapter import TestAdapter
@@ -25,8 +27,10 @@
2527
'BotFrameworkAdapterSettings',
2628
'BotState',
2729
'calculate_change_hash',
30+
'CardFactory',
2831
'ConversationState',
2932
'MemoryStorage',
33+
'MessageFactory',
3034
'Middleware',
3135
'MiddlewareSet',
3236
'Storage',
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from botbuilder.schema import (AnimationCard, Attachment, AudioCard,
5+
HeroCard, OAuthCard, ReceiptCard,
6+
SigninCard, ThumbnailCard, VideoCard)
7+
8+
9+
class ContentTypes:
10+
adaptive_card = 'application/vnd.microsoft.card.adaptive'
11+
animation_card = 'application/vnd.microsoft.card.animation'
12+
audio_card = 'application/vnd.microsoft.card.audio'
13+
hero_card = 'application/vnd.microsoft.card.hero'
14+
receipt_card = 'application/vnd.microsoft.card.receipt'
15+
oauth_card = 'application/vnd.microsoft.card.oauth'
16+
signin_card = 'application/vnd.microsoft.card.signin'
17+
thumbnail_card = 'application/vnd.microsoft.card.thumbnail'
18+
video_card = 'application/vnd.microsoft.card.video'
19+
20+
21+
class CardFactory:
22+
content_types = ContentTypes
23+
24+
@staticmethod
25+
def adaptive_card(card: dict) -> Attachment:
26+
"""
27+
Returns an attachment for an adaptive card. The attachment will contain the card and the
28+
appropriate 'contentType'. Will raise a TypeError if the 'card' argument is not an
29+
dict.
30+
:param card:
31+
:return:
32+
"""
33+
if not type(card) == dict:
34+
raise TypeError('CardFactory.adaptive_card(): `card` argument is not of type dict, unable to prepare '
35+
'attachment.')
36+
37+
return Attachment(content_type=CardFactory.content_types.adaptive_card,
38+
content=card)
39+
40+
@staticmethod
41+
def animation_card(card: AnimationCard) -> Attachment:
42+
"""
43+
Returns an attachment for an animation card. Will raise a TypeError if the 'card' argument is not an
44+
AnimationCard.
45+
:param card:
46+
:return:
47+
"""
48+
if not isinstance(card, AnimationCard):
49+
raise TypeError('CardFactory.animation_card(): `card` argument is not an instance of an AnimationCard, '
50+
'unable to prepare attachment.')
51+
52+
return Attachment(content_type=CardFactory.content_types.animation_card,
53+
content=card)
54+
55+
@staticmethod
56+
def audio_card(card: AudioCard) -> Attachment:
57+
"""
58+
Returns an attachment for an audio card. Will raise a TypeError if 'card' argument is not an AudioCard.
59+
:param card:
60+
:return:
61+
"""
62+
if not isinstance(card, AudioCard):
63+
raise TypeError('CardFactory.audio_card(): `card` argument is not an instance of an AudioCard, '
64+
'unable to prepare attachment.')
65+
66+
return Attachment(content_type=CardFactory.content_types.audio_card,
67+
content=card)
68+
69+
@staticmethod
70+
def hero_card(card: HeroCard) -> Attachment:
71+
"""
72+
Returns an attachment for a hero card. Will raise a TypeError if 'card' argument is not a HeroCard.
73+
74+
Hero cards tend to have one dominant full width image and the cards text & buttons can
75+
usually be found below the image.
76+
:return:
77+
"""
78+
if not isinstance(card, HeroCard):
79+
raise TypeError('CardFactory.hero_card(): `card` argument is not an instance of an HeroCard, '
80+
'unable to prepare attachment.')
81+
82+
return Attachment(content_type=CardFactory.content_types.hero_card,
83+
content=card)
84+
85+
@staticmethod
86+
def oauth_card(card: OAuthCard) -> Attachment:
87+
"""
88+
Returns an attachment for an OAuth card used by the Bot Frameworks Single Sign On (SSO) service. Will raise a
89+
TypeError if 'card' argument is not a OAuthCard.
90+
:param card:
91+
:return:
92+
"""
93+
if not isinstance(card, OAuthCard):
94+
raise TypeError('CardFactory.oauth_card(): `card` argument is not an instance of an OAuthCard, '
95+
'unable to prepare attachment.')
96+
97+
return Attachment(content_type=CardFactory.content_types.oauth_card,
98+
content=card)
99+
100+
@staticmethod
101+
def receipt_card(card: ReceiptCard) -> Attachment:
102+
"""
103+
Returns an attachment for a receipt card. Will raise a TypeError if 'card' argument is not a ReceiptCard.
104+
:param card:
105+
:return:
106+
"""
107+
if not isinstance(card, ReceiptCard):
108+
raise TypeError('CardFactory.receipt_card(): `card` argument is not an instance of an ReceiptCard, '
109+
'unable to prepare attachment.')
110+
111+
return Attachment(content_type=CardFactory.content_types.receipt_card,
112+
content=card)
113+
114+
@staticmethod
115+
def signin_card(card: SigninCard) -> Attachment:
116+
"""
117+
Returns an attachment for a signin card. For channels that don't natively support signin cards an alternative
118+
message will be rendered. Will raise a TypeError if 'card' argument is not a SigninCard.
119+
:param card:
120+
:return:
121+
"""
122+
if not isinstance(card, SigninCard):
123+
raise TypeError('CardFactory.signin_card(): `card` argument is not an instance of an SigninCard, '
124+
'unable to prepare attachment.')
125+
126+
return Attachment(content_type=CardFactory.content_types.signin_card,
127+
content=card)
128+
129+
@staticmethod
130+
def thumbnail_card(card: ThumbnailCard) -> Attachment:
131+
"""
132+
Returns an attachment for a thumbnail card. Thumbnail cards are similar to
133+
but instead of a full width image, they're typically rendered with a smaller thumbnail version of
134+
the image on either side and the text will be rendered in column next to the image. Any buttons
135+
will typically show up under the card. Will raise a TypeError if 'card' argument is not a ThumbnailCard.
136+
:param card:
137+
:return:
138+
"""
139+
if not isinstance(card, ThumbnailCard):
140+
raise TypeError('CardFactory.thumbnail_card(): `card` argument is not an instance of an ThumbnailCard, '
141+
'unable to prepare attachment.')
142+
143+
return Attachment(content_type=CardFactory.content_types.thumbnail_card,
144+
content=card)
145+
146+
@staticmethod
147+
def video_card(card: VideoCard) -> Attachment:
148+
"""
149+
Returns an attachment for a video card. Will raise a TypeError if 'card' argument is not a VideoCard.
150+
:param card:
151+
:return:
152+
"""
153+
if not isinstance(card, VideoCard):
154+
raise TypeError('CardFactory.video_card(): `card` argument is not an instance of an VideoCard, '
155+
'unable to prepare attachment.')
156+
157+
return Attachment(content_type=CardFactory.content_types.video_card,
158+
content=card)
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from typing import List, Union
5+
from botbuilder.schema import (ActivityTypes, Activity, Attachment,
6+
AttachmentLayoutTypes, CardAction, CardImage, SuggestedActions, InputHints)
7+
8+
9+
def attachment_activity(attachment_layout: AttachmentLayoutTypes, attachments: List[Attachment], text: str = None,
10+
speak: str = None, input_hint: Union[InputHints, str] = InputHints.accepting_input) -> Activity:
11+
message = Activity(type=ActivityTypes.message, attachment_layout=attachment_layout, attachments=attachments,
12+
input_hint=input_hint)
13+
if text:
14+
message.text = text
15+
if speak:
16+
message.speak = speak
17+
return message
18+
19+
20+
class MessageFactory:
21+
"""
22+
A set of utility functions designed to assist with the formatting of the various message types a
23+
bot can return.
24+
"""
25+
26+
@staticmethod
27+
def text(text: str, speak: str = None, input_hint: Union[InputHints, str] = InputHints.accepting_input) -> Activity:
28+
"""
29+
Returns a simple text message.
30+
31+
:Example:
32+
message = MessageFactory.text('Greetings from example message')
33+
await context.send_activity(message)
34+
35+
:param text:
36+
:param speak:
37+
:param input_hint:
38+
:return:
39+
"""
40+
message = Activity(type=ActivityTypes.message, text=text, input_hint=input_hint)
41+
if speak:
42+
message.speak = speak
43+
44+
return message
45+
46+
@staticmethod
47+
def suggested_actions(actions: List[CardAction], text: str = None, speak: str = None,
48+
input_hint: Union[InputHints, str] = InputHints.accepting_input) -> Activity:
49+
"""
50+
Returns a message that includes a set of suggested actions and optional text.
51+
52+
:Example:
53+
message = MessageFactory.suggested_actions([CardAction(title='a', type=ActionTypes.im_back, value='a'),
54+
CardAction(title='b', type=ActionTypes.im_back, value='b'),
55+
CardAction(title='c', type=ActionTypes.im_back, value='c')], 'Choose a color')
56+
await context.send_activity(message)
57+
58+
:param actions:
59+
:param text:
60+
:param speak:
61+
:param input_hint:
62+
:return:
63+
"""
64+
actions = SuggestedActions(actions=actions)
65+
message = Activity(type=ActivityTypes.message, input_hint=input_hint, suggested_actions=actions)
66+
if text:
67+
message.text = text
68+
if speak:
69+
message.speak = speak
70+
return message
71+
72+
@staticmethod
73+
def attachment(attachment: Attachment, text: str = None, speak: str = None,
74+
input_hint: Union[InputHints, str] = None):
75+
"""
76+
Returns a single message activity containing an attachment.
77+
78+
:Example:
79+
message = MessageFactory.attachment(CardFactory.hero_card(HeroCard(title='White T-Shirt',
80+
images=[CardImage(url='https://example.com/whiteShirt.jpg')],
81+
buttons=[CardAction(title='buy')])))
82+
await context.send_activity(message)
83+
84+
:param attachment:
85+
:param text:
86+
:param speak:
87+
:param input_hint:
88+
:return:
89+
"""
90+
return attachment_activity(AttachmentLayoutTypes.list, [attachment], text, speak, input_hint)
91+
92+
@staticmethod
93+
def list(attachments: List[Attachment], text: str = None, speak: str = None,
94+
input_hint: Union[InputHints, str] = None) -> Activity:
95+
"""
96+
Returns a message that will display a set of attachments in list form.
97+
98+
:Example:
99+
message = MessageFactory.list([CardFactory.hero_card(HeroCard(title='title1',
100+
images=[CardImage(url='imageUrl1')],
101+
buttons=[CardAction(title='button1')])),
102+
CardFactory.hero_card(HeroCard(title='title2',
103+
images=[CardImage(url='imageUrl2')],
104+
buttons=[CardAction(title='button2')])),
105+
CardFactory.hero_card(HeroCard(title='title3',
106+
images=[CardImage(url='imageUrl3')],
107+
buttons=[CardAction(title='button3')]))])
108+
await context.send_activity(message)
109+
110+
:param attachments:
111+
:param text:
112+
:param speak:
113+
:param input_hint:
114+
:return:
115+
"""
116+
return attachment_activity(AttachmentLayoutTypes.list, attachments, text, speak, input_hint)
117+
118+
@staticmethod
119+
def carousel(attachments: List[Attachment], text: str = None, speak: str = None,
120+
input_hint: Union[InputHints, str] = None) -> Activity:
121+
"""
122+
Returns a message that will display a set of attachments using a carousel layout.
123+
124+
:Example:
125+
message = MessageFactory.carousel([CardFactory.hero_card(HeroCard(title='title1',
126+
images=[CardImage(url='imageUrl1')],
127+
buttons=[CardAction(title='button1')])),
128+
CardFactory.hero_card(HeroCard(title='title2',
129+
images=[CardImage(url='imageUrl2')],
130+
buttons=[CardAction(title='button2')])),
131+
CardFactory.hero_card(HeroCard(title='title3',
132+
images=[CardImage(url='imageUrl3')],
133+
buttons=[CardAction(title='button3')]))])
134+
await context.send_activity(message)
135+
136+
:param attachments:
137+
:param text:
138+
:param speak:
139+
:param input_hint:
140+
:return:
141+
"""
142+
return attachment_activity(AttachmentLayoutTypes.carousel, attachments, text, speak, input_hint)
143+
144+
@staticmethod
145+
def content_url(url: str, content_type: str, name: str = None, text: str = None, speak: str = None,
146+
input_hint: Union[InputHints, str] = None):
147+
"""
148+
Returns a message that will display a single image or video to a user.
149+
150+
:Example:
151+
message = MessageFactory.content_url('https://example.com/hawaii.jpg', 'image/jpeg',
152+
'Hawaii Trip', 'A photo from our family vacation.')
153+
await context.send_activity(message)
154+
155+
:param url:
156+
:param content_type:
157+
:param name:
158+
:param text:
159+
:param speak:
160+
:param input_hint:
161+
:return:
162+
"""
163+
attachment = Attachment(content_type=content_type, content_url=url)
164+
if name:
165+
attachment.name = name
166+
return attachment_activity(AttachmentLayoutTypes.list, [attachment], text, speak, input_hint)

0 commit comments

Comments
 (0)
0