8000 add CardFactory, MessageFactory and tests · Pucadopr/botbuilder-python@4b9c5c0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4b9c5c0

Browse files
committed
add CardFactory, MessageFactory and tests
1 parent 4301de4 commit 4b9c5c0

File tree

5 files changed

+968
-0
lines changed

5 files changed

+968
-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: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import warnings
5+
from typing import Any, List, Union
6+
from botbuilder.schema import (ActionTypes, AnimationCard, Attachment, AudioCard,
7+
CardAction, CardImage, Fact, HeroCard,
8+
MediaUrl, OAuthCard, ReceiptCard,
9+
ReceiptItem, SigninCard, ThumbnailCard, VideoCard)
10+
11+
12+
class ContentTypes:
13+
adaptive_card = 'application/vnd.microsoft.card.adaptive'
14+
animation_card = 'application/vnd.microsoft.card.animation'
15+
audio_card = 'application/vnd.microsoft.card.audio'
16+
hero_card = 'application/vnd.microsoft.card.hero'
17+
receipt_card = 'application/vnd.microsoft.card.receipt'
18+
oauth_card = 'application/vnd.microsoft.card.oauth'
19+
signin_card = 'application/vnd.microsoft.card.signin'
20+
thumbnail_card = 'application/vnd.microsoft.card.thumbnail'
21+
video_card = 'application/vnd.microsoft.card.video'
22+
23+
24+
class CardFactory:
25+
content_types = ContentTypes
26+
27+
@staticmethod
28+
def adaptive_card(card: Any) -> Attachment:
29+
"""
30+
Returns an attachment for an adaptive card. The attachment will contain the card and the
31+
appropriate 'contentType'.
32+
:param card:
33+
:return:
34+
"""
35+
return Attachment(content_type=CardFactory.content_types.adaptive_card,
36+
content=card)
37+
38+
@staticmethod
39+
def animation_card(title: str, media: List[Union[MediaUrl, str]],
40+
buttons: List[Union< B41A /span>[CardAction, str]] = None, other: AnimationCard = None) -> Attachment:
41+
"""
42+
Returns an attachment for an animation card. Will emit an error if the `other` parameter is not an
43+
AnimationCard.
44+
:param title:
45+
:param media:
46+
:param buttons:
47+
:param other:
48+
:return:
49+
"""
50+
card = AnimationCard()
51+
if isinstance(other, AnimationCard):
52+
card = other
53+
elif other is not None:
54+
warnings.warn('CardFactory.animation_card(): `other` parameter is not an instance of an AnimationCard, '
55+
'ignoring `other` in card generation.')
56+
if title:
57+
card.title = title
58+
if media:
59+
card.media = CardFactory.media(media)
60+
if buttons:
61+
card.buttons = CardFactory.actions(buttons)
62+
63+
return Attachment(content_type=CardFactory.content_types.animation_card,
64+
content=card)
65+
66+
@staticmethod
67+
def audio_card(title: str, media: List[Union[str, MediaUrl]],
68+
buttons: List[Union[str, CardAction]] = None, other: AudioCard = None) -> Attachment:
69+
card = AudioCard()
70+
if isinstance(other, AudioCard):
71+
card = other
72+
elif other is not None:
73+
warnings.warn('CardFactory.audio_card(): `other` parameter is not an instance of an AudioCard, ignoring '
74+
'`other` in card generation.')
75+
if title:
76+
card.title = title
77+
if media:
78+
card.media = CardFactory.media(media)
79+
if buttons:
80+
card.buttons = CardFactory.actions(buttons)
81+
82+
return Attachment(content_type=CardFactory.content_types.audio_card,
83+
content=card)
84+
85+
@staticmethod
86+
def hero_card(title: str, text: str = None, images: List[Union[str, CardImage]] = None,
87+
buttons: List[Union[str, CardImage]] = None, other: HeroCard = None) -> Attachment:
88+
"""
89+
Returns an attachment for a hero card.
90+
91+
Hero cards tend to have one dominant full width image and the cards text & buttons can
92+
usually be found below the image.
93+
:return:
94+
"""
95+
card = HeroCard()
96+
if isinstance(other, HeroCard):
97+
card = other
98+
elif other is not None:
99+
warnings.warn('CardFactory.hero_card(): `other` parameter is not an instance of an HeroCard, '
100+
'ignoring `other` in card generation.')
101+
if title:
102+
card.title = title
103+
if text:
104+
card.text = text
105+
if images:
106+
card.images = CardFactory.images(images)
107+
if buttons:
108+
card.buttons = CardFactory.actions(buttons)
109+
110+
return Attachment(content_type=CardFactory.content_types.hero_card,
111+
content=card)
112+
113+
@staticmethod
114+
def oauth_card(connection_name, title: str, text: str = None) -> Attachment:
115+
"""
116+
Returns an attachment for an OAuth card used by the Bot Frameworks Single Sign On (SSO) service.
117+
:param connection_name:
118+
:param title:
119+
:param text:
120+
:return:
121+
"""
122+
button = CardAction(type=ActionTypes.signin, title=title, value=None)
123+
card = OAuthCard(connection_name=connection_name, buttons=[button], text=text)
124+
125+
return Attachment(content_type=CardFactory.content_types.oauth_card,
126+
content=card)
127+
128+
@staticmethod
129+
def receipt_card(title: str = None, facts: List[Fact] = None, items: List[ReceiptItem] = None,
130+
tap: CardAction = None,
131+
total: str = None, tax: str = None, vat: str = None,
132+
buttons: List[CardAction] = None, other: ReceiptCard = None) -> Attachment:
133+
"""
134+
Returns an attachment for a receipt card. The attachment will contain the parameters and the appropriate
135+
`contentType`.
136+
:param title:
137+
:param facts:
138+
:param items:
139+
:param tap:
140+
:param total:
141+
:param tax:
142+
:param vat:
143+
:param buttons:
144+
:param other:
145+
:return:
146+
"""
147+
card = ReceiptCard()
148+
if isinstance(other, ReceiptCard):
149+
card = other
150+
elif other is not None:
151+
warnings.warn('CardFactory.receipt_card(): `other` parameter is not an instance of an ReceiptCard, '
152+
'ignoring `other` in card generation.')
153+
154+
if title is not None:
155+
card.title = title
156+
if facts is not None:
157+
card.facts = facts
158+
if items is not None:
159+
card.items = items
160+
if tap is not None:
161+
card.tap = tap
162+
if total is not None:
163+
card.total = total
164+
if tax is not None:
165+
card.tax = tax
166+
if vat is not None:
167+
card.vat = vat
168+
if buttons is not None:
169+
card.buttons = buttons
170+
171+
return Attachment(content_type=CardFactory.content_types.receipt_card,
172+
content=card)
173+
174+
@staticmethod
175+
def signin_card(title: str, url: str, text: str = None) -> Attachment:
176+
"""
177+
Returns an attachment for a signin card. For channels that don't natively support signin cards an alternative
178+
message will be rendered.
179+
:param title:
180+
:param url:
181+
:param text:
182+
:return:
183+
"""
184+
button = CardAction(type=ActionTypes.signin, title=title, value=url)
185+
card = SigninCard(text=text, buttons=[button])
186+
187+
return Attachment(content_type=CardFactory.content_types.signin_card,
188+
content=card)
189+
190+
@staticmethod
191+
def thumbnail_card(title: str, text: str = None, images: List[Union[str, CardImage]] = None,
192+
buttons: List[Union[CardAction, str]] = None, other: ThumbnailCard = None) -> Attachment:
193+
"""
194+
Returns an attachment for a thumbnail card. Thumbnail cards are similar to [hero cards](#herocard)
195+
but instead of a full width image, they're typically rendered with a smaller thumbnail version of
196+
the image on either side and the text will be rendered in column next to the image. Any buttons
197+
will typically show up under the card.
198+
:param title:
199+
:param text:
200+
:param images:
201+
:param buttons:
202+
:param other:
203+
:return:
204+
"""
205+
card = ThumbnailCard()
206+
if isinstance(other, ThumbnailCard):
207+
card = other
208+
elif other is not None:
209+
warnings.warn('CardFactory.thumbnail_card(): `other` parameter is not an instance of an ThumbnailCard, '
210+
'ignoring `other` in card generation.')
211+
if title:
212+
card.title = title
213+
if text:
214+
card.text = text
215+
if images:
216+
card.images = CardFactory.images(images)
217+
if buttons:
218+
card.buttons = CardFactory.actions(buttons)
219+
220+
return Attachment(content_type=CardFactory.content_types.thumbnail_card,
221+
content=card)
222+
223+
@staticmethod
224+
def video_card(title: str, media: List[Union[str, MediaUrl]],
225+
buttons: List[Union[CardAction, str]] = None, other: VideoCard = None) -> Attachment:
226+
"""
227+
Returns an attachment for a video card.
228+
:param title:
229+
:param media:
230+
:param buttons:
231+
:param other:
232+
:return:
233+
"""
234+
card = VideoCard()
235+
if isinstance(other, VideoCard):
236+
card = other
237+
elif other is not None:
238+
warnings.warn('CardFactory.video_card(): `other` parameter is not an instance of an VideoCard, '
239+
'ignoring `other` in card generation.')
240+
if title:
241+
card.title = title
242+
if media:
243+
card.media = CardFactory.media(media)
244+
if buttons:
245+
card.buttons = CardFactory.actions(buttons)
246+
247+
return Attachment(content_type=CardFactory.content_types.video_card,
248+
content=card)
249+
250+
@staticmethod
251+
def actions(actions: List[Union[CardAction, str]]) -> List[CardAction]:
252+
if actions is None:
253+
return []
254+
255+
def prepare_action(action_or_str):
256+
if isinstance(action_or_str, CardAction):
257+
return action_or_str
258+
else:
259+
return CardAction(type=ActionTypes.im_back, value=str(action_or_str), title=str(action_or_str))
260+
261+
return [prepare_action(action) for action in actions]
262+
263+
@staticmethod
264+
def images(images: List[Union[CardImage, str]]) -> List[CardImage]:
265+
if images is None:
266+
return []
267+
268+
def prepare_image(image_or_str):
269+
if isinstance(image_or_str, CardImage):
270+
return image_or_str
271+
else:
272+
return CardImage(url=image_or_str)
273+
274+
return [prepare_image(image) for image in images]
275+
276+
@staticmethod
277+
def media(links: List[Union[MediaUrl, str]]) -> List[MediaUrl]:
278+
if links is None:
279+
return []
280+
281+
def prepare_media(media_or_str):
282+
if isinstance(media_or_str, MediaUrl):
283+
return media_or_str
284+
else:
285+
return MediaUrl(url=media_or_str)
286+
287+
return [prepare_media(link) for link in links]

0 commit comments

Comments
 (0)
0