8000 Merge pull request #83 from Microsoft/v-stgum/consoleAdapter · umarfarook882/botbuilder-python@c74d2b0 · GitHub
[go: up one dir, main page]

Skip to content

Commit c74d2b0

Browse files
authored
Merge pull request microsoft#83 from Microsoft/v-stgum/consoleAdapter
add Console-EchoBot sample
2 parents 37d8c07 + 409082e commit c74d2b0

File tree

6 files changed

+235
-1
lines changed

6 files changed

+235
-1
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@
3838
'StoreItem',
3939
'TestAdapter',
4040
'UserState',
41-
'__version__',]
41+
'__version__']

samples/Console-EchoBot/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Console EchoBot
2+
3+
## To try this sample
4+
- Clone the repository
5+
```bash
6+
git clone https://github.com/Microsoft/botbuilder-python.git
7+
```
8+
9+
10+
### Visual studio code
11+
- open `botbuilder-python\samples\Console-EchoBot` folder
12+
- Bring up a terminal, navigate to `botbuilder-python\samples\Console-EchoBot` folder
13+
- type 'python main.py'
14+
15+
16+
# Adapters
17+
[Adapters](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0#the-bot-adapter) provide an abstraction for your bot to work with a variety of environments.
18+
19+
A bot is directed by it's adapter, which can be thought of as the conductor for your bot. The adapter is responsible for directing incoming and outgoing communication, authentication, and so on. The adapter differs based on it's environment (the adapter internally works differently locally versus on Azure) but in each instance it achieves the same goal.
20+
21+
In most situations we don't work with the adapter directly, such as when creating a bot from a template, but it's good to know it's there and what it does.
22+
The bot adapter encapsulates authentication processes and sends activities to and receives activities from the Bot Connector Service. When your bot receives an activity, the adapter wraps up everything about that activity, creates a [context object](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0#turn-context), passes it to your bot's application logic, and sends responses generated by your bot back to the user's channel.
23+
24+
25+
# Further reading
26+
27+
- [Azure Bot Service Introduction](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
28+
- [Bot basics](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
29+
- [Channels and Bot Connector service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
30+
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .console_adapter import ConsoleAdapter
5+
6+
__all__ = ['ConsoleAdapter']
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import datetime
5+
import asyncio
6+
import warnings
7+
from typing import List, Callable
8+
9+
from botbuilder.schema import (Activity, ActivityTypes,
10+
6D40 ChannelAccount, ConversationAccount,
11+
ResourceResponse, ConversationReference)
12+
from botbuilder.core.bot_context import BotContext
13+
from botbuilder.core.bot_adapter import BotAdapter
14+
15+
16+
class ConsoleAdapter(BotAdapter):
17+
"""
18+
Lets a user communicate with a bot from a console window.
19+
20+
:Example:
21+
import asyncio
22+
from botbuilder.core import ConsoleAdapter
23+
24+
async def logic(context):
25+
await context.send_activity('Hello World!')
26+
27+
adapter = ConsoleAdapter()
28+
loop = asyncio.get_event_loop()
29+
if __name__ == "__main__":
30+
try:
31+
loop.run_until_complete(adapter.process_activity(logic))
32+
except KeyboardInterrupt:
33+
pass
34+
finally:
35+
loop.stop()
36+
loop.close()
37+
"""
38+
def __init__(self, reference: ConversationReference = None):
39+
super(ConsoleAdapter, self).__init__()
40+
41+
self.reference = ConversationReference(channel_id='console',
42+
user=ChannelAccount(id='user', name='User1'),
43+
bot=ChannelAccount(id='bot', name='Bot'),
44+
conversation=ConversationAccount(id='convo1', name='', is_group=False),
45+
service_url='')
46+
47+
# Warn users to pass in an instance of a ConversationReference, otherwise the parameter will be ignored.
48+
if reference is not None and not isinstance(reference, ConversationReference):
49+
warnings.warn('ConsoleAdapter: `reference` argument is not an instance of ConversationReference and will '
50+
'be ignored.')
51+
else:
52+
self.reference.channel_id = getattr(reference, 'channel_id', self.reference.channel_id)
53+
self.reference.user = getattr(reference, 'user', self.reference.user)
54+
self.reference.bot = getattr(reference, 'bot', self.reference.bot)
55+
self.reference.conversation = getattr(reference, 'conversation', self.reference.conversation)
56+
self.reference.service_url = getattr(reference, 'service_url', self.reference.service_url)
57+
# The only attribute on self.reference without an initial value is activity_id, so if reference does not
58+
# have a value for activity_id, default self.reference.activity_id to None
59+
self.reference.activity_id = getattr(reference, 'activity_id', None)
60+
61+
self._next_id = 0
62+
63+
async def process_activity(self, logic: Callable):
64+
"""
65+
Begins listening to console input.
66+
:param logic:
67+
:return:
68+
"""
69+
while True:
70+
msg = input()
71+
if msg is None:
72+
pass
73+
else:
74+
self._next_id += 1
75+
activity = Activity(text=msg,
76+
channel_id='console',
77+
from_property=ChannelAccount(id='user', name='User1'),
78+
recipient=ChannelAccount(id='bot', name='Bot'),
79+
conversation=ConversationAccount(id='Convo1'),
80+
type=ActivityTypes.message,
81+
timestamp=datetime.datetime.now(),
82+
id=str(self._next_id))
83+
84+
activity = BotContext.apply_conversation_reference(activity, self.reference, True)
85+
context = BotContext(self, activity)
86+
await self.run_middleware(context, logic)
87+
88+
async def send_activities(self, context: BotContext, activities: List[Activity]):
89+
"""
90+
Logs a series of activities to the console.
91+
:param context:
92+
:param activities:
93+
:return:
94+
"""
95+
if context is None:
96+
raise TypeError('ConsoleAdapter.send_activities(): `context` argument cannot be None.')
97+
if type(activities) != list:
98+
raise TypeError('ConsoleAdapter.send_activities(): `activities` argument must be a list.')
99+
if len(activities) == 0:
100+
raise ValueError('ConsoleAdapter.send_activities(): `activities` argument cannot have a length of 0.')
101+
102+
async def next_activity(i: int):
103+
responses = []
104+
105+
if i < len(activities):
106+
responses.append(ResourceResponse())
107+
a = activities[i]
108+
109+
if a.type == 'delay':
110+
await asyncio.sleep(a.delay)
111+
await next_activity(i + 1)
112+
elif a.type == ActivityTypes.message:
113+
if a.attachments is not None and len(a.attachments) > 0:
114+
append = '(1 attachment)' if len(a.attachments) == 1 else f'({len(a.attachments)} attachments)'
115+
print(f'{a.text} {append}')
116+
else:
117+
print(a.text)
118+
await next_activity(i + 1)
119+
else:
120+
print(f'[{a.type}]')
121+
await next_activity(i + 1)
122+
else:
123+
return responses
124+
125+
await next_activity(0)
126+
127+
async def delete_activity(self, context: BotContext, reference: ConversationReference):
128+
"""
129+
Not supported for the ConsoleAdapter. Calling this method or `BotContext.delete_activity()`
130+
will result an error being returned.
131+
:param context:
132+
:param reference:
133+
:return:
134+
"""
135+
raise NotImplementedError('ConsoleAdapter.delete_activity(): not supported.')
136+
137+
async def update_activity(self, context: BotContext, activity: Activity):
138+
"""
139+
Not supported for the ConsoleAdapter. Calling this method or `BotContext.update_activity()`
140+
will result an error being returned.
141+
:param context:
142+
:param activity:
143+
:return:
144+
"""
145+
raise NotImplementedError('ConsoleAdapter.update_activity(): not supported.')

samples/Console-EchoBot/main.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import asyncio
5+
from botbuilder.core import BotContext, ConversationState, UserState, MemoryStorage
6+
from botbuilder.schema import ActivityTypes
7+
8+
from adapter import ConsoleAdapter
9+
10+
# Create adapter
11+
adapter = ConsoleAdapter()
12+
13+
# Create MemoryStorage, UserState and ConversationState
14+
memory = MemoryStorage()
15+
# Commented out user_state because it's not being used.
16+
# user_state = UserState(memory)
17+
conversation_state = ConversationState(memory)
18+
19+
# Register both State middleware on the adapter.
20+
# Commented out user_state because it's not being used.
21+
# adapter.use(user_state)
22+
adapter.use(conversation_state)
23+
24+
25+
async def logic(context: BotContext):
26+
if context.activity.type == ActivityTypes.message:
27+
state = await conversation_state.get(context)
28+
29+
# If our conversation_state already has the 'count' attribute, increment state.count by 1
30+
# Otherwise, initialize state.count with a value of 1
31+
if hasattr(state, 'count'):
32+
state.count += 1
33+
else:
34+
state.count = 1
35+
await context.send_activity(f'{state.count}: You said "{context.activity.text}"')
36+
else:
37+
await context.send_activity(f'[{context.activity.type} event detected]')
38+
39+
loop = asyncio.get_event_loop()
40+
41+
if __name__ == "__main__":
42+
try:
43+
# Greet user
44+
print("Hi... I'm an echobot. Whatever you say I'll echo back.")
45+
46+
loop.run_until_complete(adapter.process_activity(logic))
47+
except KeyboardInterrupt:
48+
pass
49+
finally:
50+
loop.stop()
51+
loop.close()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
git+https://github.com/Azure/msrest-for-python@async2
2+
botbuilder-core>=4.0.0.a4

0 commit comments

Comments
 (0)
0