8000 Skillrequest handler pending · snifhex/botbuilder-python@a35f565 · GitHub
[go: up one dir, main page]

Skip to content

Commit a35f565

Browse files
committed
Skillrequest handler pending
1 parent 705080f commit a35f565

File tree

8 files changed

+207
-140
lines changed

8 files changed

+207
-140
lines changed

.pylintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ disable=print-statement,
158158
too-many-return-statements,
159159
import-error,
160160
no-name-in-module,
161-
too-many-branches
161+
too-many-branches,
162+
pointless-string-statement
162163

163164
# Enable the message, report, category or checker with the given id(s). You can
164165
# either give multiple identifier separated by comma (,) or put this option
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import json
5+
from typing import Dict
6+
import requests
7+
8+
from botbuilder.core import InvokeResponse
9+
from botbuilder.schema import Activity
10+
from botframework.connector.auth import (
11+
ChannelProvider,
12+
CredentialProvider,
13< 8000 /td>+
GovernmentConstants,
14+
MicrosoftAppCredentials,
15+
)
16+
17+
# from .referr_info import ReferrInfo
18+
from .skill_conversation import SkillConversation
19+
20+
21+
class BotFrameworkHttpClient:
22+
23+
"""
24+
A skill host adapter implements API to forward activity to a skill and
25+
implements routing ChannelAPI calls from the Skill up through the bot/adapter.
26+
"""
27+
28+
INVOKE_ACTIVITY_NAME = "SkillEvents.ChannelApiInvoke"
29+
_BOT_IDENTITY_KEY = "BotIdentity"
30+
_APP_CREDENTIALS_CACHE: Dict[str:MicrosoftAppCredentials] = {}
31+
32+
def __init__(
33+
self,
34+
credential_provider: CredentialProvider,
35+
channel_provider: ChannelProvider = None,
36+
logger: object = None,
37+
):
38+
if not credential_provider:
39+
raise TypeError("credential_provider can't be None")
40+
41+
self._credential_provider = credential_provider
42+
self._channel_provider = channel_provider
43+
self._logger = logger
44+
45+
async def post_activity(
46+
self,
47+
from_bot_id: str,
48+
to_bot_id: str,
49+
to_url: str,
50+
service_url: str,
51+
conversation_id: str,
52+
activity: Activity,
53+
) -> InvokeResponse:
54+
app_credentials = await self._get_app_credentials(from_bot_id, to_bot_id)
55+
56+
if not app_credentials:
57+
raise RuntimeError("Unable to get appCredentials to connect to the skill")
58+
59+
# Get token for the skill call
60+
token = app_credentials.get_access_token()
61+
62+
# Capture current activity settings before changing them.
63+
# TODO: DO we need to set the activity ID? (events that are created manually don't have it).
64+
original_conversation_id = activity.conversation.id
65+
original_service_url = activity.service_url
66+
67+
try:
68+
# TODO: figure out a better way of passing the original ServiceUrl when calling
69+
# the skill so we don't have to encode it in the conversation ID.
70+
# Encode original bot service URL and ConversationId in the new conversation ID so we can unpack it later.
71+
skill_conversation = SkillConversation(
72+
service_url=activity.service_url, conversation_id=conversation_id
73+
)
74+
activity.conversation.id = skill_conversation.get_skill_conversation_id()
75+
activity.service_url = service_url
76+
77+
# TODO: Review the rest of the code and see if we can remove this. Gabo
78+
# TODO: can we use this property back to store the source conversation ID and the ServiceUrl?
79+
"""
80+
activity.recipient.properties["skillId"] = to_bot_id
81+
referr = ReferrInfo(
82+
from_bot_id=from_bot_id,
83+
to_bot_id=to_bot_id,
84+
conversation_id=original_conversation_id,
85+
service_url=original_service_url
86+
)
87+
88+
activity.recipient.properties[ReferrInfo.KEY] = referr.serialize()
89+
"""
90+
json_content = json.dumps(activity.serialize())
91+
with requests.Session() as session:
92+
resp = session.post(
93+
to_url,
94+
data=json_content.encode("utf-8"),
95+
headers={
96+
"Authorization": f"Bearer:{token}",
97+
"Content-type": "application/json; charset=utf-8",
98+
},
99+
)
100+
resp.raise_for_status()
101+
content = resp.json
102+
103+
if content:
104+
return InvokeResponse(status=resp.status_code, body=content)
105+
106+
finally:
107+
# Restore activity properties.
108+
activity.conversation.id = original_conversation_id
109+
activity.service_url = original_service_url
110+
111+
async def _get_app_credentials(
112+
self, app_id: str, oauth_scope: str
113+
) -> MicrosoftAppCredentials:
114+
if not app_id:
115+
return MicrosoftAppCredentials(None, None)
116+
117+
cache_key = f"{app_id}{oauth_scope}"
118+
app_credentials = BotFrameworkHttpClient._APP_CREDENTIALS_CACHE.get(cache_key)
119+
120+
if app_credentials:
121+
return app_credentials
122+
123+
app_password = await self._credential_provider.get_app_password(app_id)
124+
app_credentials = MicrosoftAppCredentials(
125+
app_id, app_password, oauth_scope=oauth_scope
126+
)
127+
if self._channel_provider.is_government():
128+
app_credentials.oauth_endpoint = (
129+
GovernmentConstants.TO_CHANNEL_FROM_BOT_LOGIN_URL
130+
)
131+
app_credentials.oauth_scope = (
132+
GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
133+
)
134+
135+
BotFrameworkHttpClient._APP_CREDENTIALS_CACHE[cache_key] = app_credentials
136+
return app_credentials

libraries/botbuilder-skills/botbuilder/skills/channel_api_middleware.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,16 @@
88
from botbuilder.schema import Activity, ActivityTypes, ResourceResponse
99
from .channel_api_args import ChannelApiArgs
1010
from .channel_api_methods import ChannelApiMethods
11-
from .bot_framework_skill_client import BotFrameworkSkillClient
11+
from .skill_handler import SkillHandler
1212

1313

1414
class ChannelApiMiddleware(Middleware):
15-
def __init__(self, skill_adapter: BotFrameworkSkillClient):
16-
self._skill_adapter = skill_adapter
17-
1815
async def on_turn(
1916
self, context: TurnContext, logic: Callable[[TurnContext], Awaitable]
2017
):
2118
if (
2219
context.activity.type == ActivityTypes.invoke
23-
and context.activity.name == BotFrameworkSkillClient.INVOKE_ACTIVITY_NAME
20+
and context.activity.name == SkillHandler.INVOKE_ACTIVITY_NAME
2421
):
2522
invoke_args: ChannelApiArgs = context.activity.value
2623

libraries/botbuilder-skills/botbuilder/skills/integration/actions.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414

1515
from botframework.connector.auth import ClaimsIdentity
1616

17-
from ..bot_framework_skill_client import BotFrameworkSkillClient
17+
from ..bot_framework_skill_client import BotFrameworkClient
1818
from ..http_helper import HttpHelper
1919

2020

2121
async def get_activity_members_action(
2222
adapter: BotAdapter,
23-
skill_client: BotFrameworkSkillClient,
23+
skill_client: BotFrameworkClient,
2424
bot: Bot,
2525
claims_identity: ClaimsIdentity,
2626
request: Request, # pylint: disable=unused-argument
@@ -37,7 +37,7 @@ async def get_activity_members_action(
3737

3838
async def send_conversation_history_action(
3939
adapter: BotAdapter,
40-
skill_client: BotFrameworkSkillClient,
40+
skill_client: BotFrameworkClient,
4141
bot: Bot,
4242
claims_identity: ClaimsIdentity,
4343
request: Request,
@@ -51,7 +51,7 @@ async def send_conversation_history_action(
5151

5252
async def reply_to_activity_action(
5353
adapter: BotAdapter,
54-
skill_client: BotFrameworkSkillClient,
54+
skill_client: BotFrameworkClient,
5555
bot: Bot,
5656
claims_identity: ClaimsIdentity,
5757
request: Request,
@@ -70,7 +70,7 @@ async def reply_to_activity_action(
7070

7171
async def update_activity_action(
7272
adapter: BotAdapter,
73-
skill_client: BotFrameworkSkillClient,
73+
skill_client: BotFrameworkClient,
7474
bot: Bot,
7575
claims_identity: ClaimsIdentity,
7676
request: Request,
@@ -89,7 +89,7 @@ async def update_activity_action(
8989

9090
async def delete_activity_action(
9191
adapter: BotAdapter,
92-
skill_client: BotFrameworkSkillClient,
92+
skill_client: BotFrameworkClient,
9393
bot: Bot,
9494
claims_identity: ClaimsIdentity,
9595
request: Request, # pylint: disable=unused-argument
@@ -107,7 +107,7 @@ async def delete_activity_action(
107107

108108
async def send_to_conversaiton_action(
109109
adapter: BotAdapter,
110-
skill_client: BotFrameworkSkillClient,
110+
skill_client: BotFrameworkClient,
111111
bot: Bot,
112112
claims_identity: ClaimsIdentity,
113113
request: Request,
@@ -125,7 +125,7 @@ async def send_to_conversaiton_action(
125125

126126
async def delete_conversation_member_action(
127127
adapter: BotAdapter,
128-
skill_client: BotFrameworkSkillClient,
128+
skill_client: BotFrameworkClient,
129129
bot: Bot,
130130
claims_identity: ClaimsIdentity,
131131
request: Request, # pylint: disable=unused-argument
@@ -143,7 +143,7 @@ async def delete_conversation_member_action(
143143

144144
async def upload_attachment_action(
145145
adapter: BotAdapter,
146-
skill_client: BotFrameworkSkillClient,
146+
skill_client: BotFrameworkClient,
147147
bot: Bot,
148148
claims_identity: ClaimsIdentity,
149149
request: Request,
@@ -157,7 +157,7 @@ async def upload_attachment_action(
157157

158158
async def get_conversation_members_action(
159159
adapter: BotAdapter,
160-
skill_client: BotFrameworkSkillClient,
160+
skill_client: BotFrameworkClient,
161161
bot: Bot,
162162
claims_identity: ClaimsIdentity,
163163
request: Request, # pylint: disable=unused-argument
@@ -170,7 +170,7 @@ async def get_conversation_members_action(
170170

171171
async def get_conversation_paged_members_action(
172172
adapter: BotAdapter,
173-
skill_client: BotFrameworkSkillClient,
173+
skill_client: BotFrameworkClient,
174174
bot: Bot,
175175
claims_identity: ClaimsIdentity,
176176
request: Request, # pylint: disable=unused-argument
@@ -189,7 +189,7 @@ async def get_conversation_paged_members_action(
189189

190190
async def get_conversations_action(
191191
adapter: BotAdapter,
192-
skill_client: BotFrameworkSkillClient,
192+
skill_client: BotFrameworkClient,
193193
bot: Bot,
194194
claims_identity: ClaimsIdentity,
195195
request: Request, # pylint: disable=unused-argument
@@ -202,7 +202,7 @@ async def get_conversations_action(
202202

203203
async def create_conversation_action(
204204
adapter: BotAdapter,
205-
skill_client: BotFrameworkSkillClient,
205+
skill_client: BotFrameworkClient,
206206
bot: Bot,
207207
claims_identity: ClaimsIdentity,
208208
request: Request,

libraries/botbuilder-skills/botbuilder/skills/integration/bot_framework_skill_request_handler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030
get_conversations_action,
3131
create_conversation_action,
3232
)
33-
from ..bot_framework_skill_client import BotFrameworkSkillClient
33+
from ..bot_framework_skill_client import BotFrameworkClient
3434
from ..channel_api_methods import ChannelApiMethods
3535
from ..http_helper import HttpHelper
3636

3737
RouteAction = Callable[
38-
[BotAdapter, BotFrameworkSkillClient, Bot, ClaimsIdentity, Request, Dict[str, str]],
38+
[BotAdapter, BotFrameworkClient, Bot, ClaimsIdentity, Request, Dict[str, str]],
3939
Awaitable[object],
4040
]
4141

@@ -163,7 +163,7 @@ class BotFrameworkSkillRequestHandler:
163163

164164
def __init__(
165165
self,
166-
skill_client: BotFrameworkSkillClient,
166+
skill_client: BotFrameworkClient,
167167
credential_provider: CredentialProvider,
168168
auth_config: AuthenticationConfiguration,
169169
channel_provider: ChannelProvider = None,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from msrest.serialization import Model
5+
6+
7+
class ReferrInfo(Model):
8+
KEY = "botbuilder.skills.ReferrInfo"
9+
10+
_attribute_map = {
11+
"from_bot_id": {"key": "fromBotId", "type": "str"},
12+
"to_bot_id": {"key": "toBotId", "type": "str"},
13+
"conversation_id": {"key": "conversationId", "type": "str"},
14+
"service_url": {"key": "serviceUrl", "type": "str"},
15+
}
16+
17+
def __init__(
18+
self,
19+
*,
20+
from_bot_id: str = None,
21+
to_bot_id: str = None,
22+
conversation_id: str = None,
23+
service_url: str = None,
24+
**kwargs
25+
):
26+
super(ReferrInfo, self).__init__(**kwargs)
27+
self.from_bot_id = from_bot_id
28+
self.to_bot_id = to_bot_id
29+
self.conversation_id = conversation_id
30+
self.service_url = service_url

libraries/botbuilder-skills/botbuilder/skills/skill_conversation.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88

99

1010
class SkillConversation:
11-
def __init__(self, packed_conversation_id: str = None):
11+
def __init__(
12+
self,
13+
packed_conversation_id: str = None,
14+
conversation_id: str = None,
15+
service_url: str = None,
16+
):
1217
if packed_conversation_id:
1318
parts: List[str] = json.loads(
1419
base64.b64decode(packed_conversation_id).decode("utf-8")
@@ -17,8 +22,8 @@ def __init__(self, packed_conversation_id: str = None):
1722
self.conversation_id = parts[0]
1823
self.service_url = parts[1]
1924
else:
20-
self.conversation_id = None
21-
self.service_url = None
25+
self.conversation_id = conversation_id
26+
self.service_url = service_url
2227

2328
def get_skill_conversation_id(self) -> str:
2429
"""

0 commit comments

Comments
 (0)
0