Comments (13)
Great work @rampaged!
from botbuilder-python.
Thanks @BasileBerckmoes, I can look into this.
from botbuilder-python.
@rampaged thank you! I was exploring the code myself to see if i could contribute but i need to progress my understanding of the framework first
from botbuilder-python.
Hi @BasileBerckmoes,
I'm able to reproduce this issue in Teams. I used sample 58.teams-start-thread-in-channel to test this issue.
I added this piece of code to reproduce the issue in on_message_activity
method:
team_details = await TeamsInfo.get_team_details(turn_context)
if team_details:
team_id = team_details.id
# Retrieve all members of the team
team_members = await TeamsInfo.get_team_members(turn_context, team_id)
for member in team_members:
if member.id != turn_context.activity.recipient.id: # Avoid messaging the bot itself
# Prepare the message
await turn_context.send_activity(
"some message here"
)
Attached a bot that demonstrates the problem. Here are the steps I followed to verify it:
58.teams-thread.zip
- Unzip the attached bot and follow this readme to set up and configure the bot locally
- Sideload the bot in Teams and add it to a Teams team in a channel
- start a new post and @ mention the bot (TeamsStartThreadInChannel)
- Notice the error:
[on_turn_error] unhandled error: 'CloudAdapter' object has no attribute 'create_connector_client'
Traceback (most recent call last):
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/venv/lib/python3.10/site-packages/botbuild
er/core/bot_adapter.py", line 174, in run_pipeline
return await self._middleware.receive_activity_with_status(
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/venv/lib/python3.10/site-packages/botbuild
er/core/middleware_set.py", line 69, in receive_activity_with_status
return await self.receive_activity_internal(context, callback)
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/venv/lib/python3.10/site-packages/botbuild
er/core/middleware_set.py", line 79, in receive_activity_internal
return await callback(context)
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/venv/lib/python3.10/site-packages/botbuild
er/core/activity_handler.py", line 70, in on_turn
await self.on_message_activity(turn_context)
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/bots/teams_start_thread_in_channel.py", li
ne 17, in on_message_activity
team_details = await TeamsInfo.get_team_details(turn_context)
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/venv/lib/python3.10/site-packages/botbuild
er/core/teams/teams_info.py", line 120, in get_team_details
teams_connector = await TeamsInfo.get_teams_connector_client(turn_context)
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/venv/lib/python3.10/site-packages/botbuild
er/core/teams/teams_info.py", line 305, in get_teams_connector_client
connector_client = await TeamsInfo._get_connector_client(turn_context)
File "/home/rampage/Documents/bots/BotBuilder-Samples/archive/samples/python/58.teams-start-thread-in-channel/venv/lib/python3.10/site-packages/botbuild
er/core/teams/teams_info.py", line 321, in _get_connector_client
return await turn_context.adapter.create_connector_client(
AttributeError: 'CloudAdapter' object has no attribute 'create_connector_client'
Call stack diagram:
teams_start_thread_in_channel.py
|__ on_message_activity (line 17)
|
|__ TeamsInfo.get_team_details
|
|__ TeamsInfo.get_teams_connector_client
|
|__ TeamsInfo._get_connector_client (line 320)
|
|__ turn_context.adapter.create_connector_client (line 321) <---- error occurs
from botbuilder-python.
CC @stevkan to handle this issue internally with Microsoft engineers.
from botbuilder-python.
I'm in the same boat, teaching myself the framework as well.
I explored the python bot framework sdk source code and tweaked cloud_adapter.py and teams_info.py, which I think resolved the issue. However, I'm still uncertain if this is the right approach since I'm learning the framework and need feedback from the engineers.
Observation:
In the javascript sdk side, the implementation detail for getConnectorClient in teamsInfo.ts is as follows:
private static getConnectorClient(context: TurnContext): ConnectorClient {
const client =
context.adapter && 'createConnectorClient' in context.adapter
? (context.adapter as BotFrameworkAdapter).createConnectorClient(context.activity.serviceUrl)
: context.turnState?.get<ConnectorClient>(context.adapter.ConnectorClientKey);
if (!client) {
throw new Error('This method requires a connector client.');
}
return client;
}
as opposed to _get_connector_client method in teams_info.py on the python sdk:
@staticmethod
async def _get_connector_client(turn_context: TurnContext) -> ConnectorClient:
return await turn_context.adapter.create_connector_client(
turn_context.activity.service_url
)
In JavaScript, getConnectorClient
checks if the "createConnectorClient" method exists in "context.adapter" and calls it, otherwise it retrieves a "ConnectorClient" from "context.turnState" using a predefined key.
So my idea was to implement the same logic as getConnectorClient
method but in python sdk.
Changes:
updated _get_connector_client method in teams_info.py:
+ from botbuilder.integration.aiohttp import CloudAdapter
async def _get_connector_client(turn_context: TurnContext) -> ConnectorClient:
+ if isinstance(turn_context.adapter, CloudAdapter):
+ return await turn_context.adapter.create_connector_client(turn_context)
+ else:
return await turn_context.adapter.create_connector_client(
turn_context.activity.service_url
)
and in CloudAdapter class, I added this method:
async def create_connector_client(self, turn_context: TurnContext):
connector_client: ConnectorClient = turn_context.turn_state.get(
self.BOT_CONNECTOR_CLIENT_KEY
)
return connector_client
finally, in 58.teams-start-thread-in-channel bot sample(not sdk), in file teams_start_thread_in_channel.py, i changed:
connector_client = await turn_context.adapter.create_connector_client(turn_context.activity.service_url)
to:
connector_client = await turn_context.adapter.create_connector_client(turn_context)
Result:
# Get the team details
team_details = await TeamsInfo.get_team_details(turn_context)
if team_details:
team_id = team_details.id
# Retrieve all members of the team
team_members = await TeamsInfo.get_team_members(turn_context, team_id)
for member in team_members:
if member.id != turn_context.activity.recipient.id: # Avoid messaging the bot itself
# Prepare the message
await turn_context.send_activity(
f"name {member.name}"
)
from botbuilder-python.
@rampaged Hi Ram. Not sure why JS does what it does. But I avoid type checking ('isinstance(turn_context.adapter, CloudAdapter)) unless I absolutely need to. The JS version is just check if the 'createConnectorClient' method is available (no idea why, could be historical).
It would be typical for the stack to store the Connector in the turnState, and I wonder if we also don't have to check that out. That is, it's created and stored for that turn to avoid repeatedly creating new ones.
I'll compare to DotNet. My guess is something was missed when porting the CloudAdapter.
from botbuilder-python.
@rampaged Check out the impl of TeamsInfo in DotNet. It has methods for GetConnectorClient (which just gets from TurnState), and GetTeamsConnectorClient (which creates a new TeamConnectorClient).
ConnectorClient would be added to TurnState in difference places for BotFrameworkAdapter vs CloudAdapter. DotNet will also remove it from TurnState in specific places.
from botbuilder-python.
Thanks for the feedback @tracyboehrer. Looking into it.
from botbuilder-python.
Thanks for the pointers @tracyboehrer.
I made an update to the _get_connector_client method to retrieve the ConnectorClient directly from turn_context.turn_state
.
Now it uses hasattr to check if the create_connector_client
method exists, which seems to keep things compatible with different adapters without needing type checking.
@staticmethod
async def _get_connector_client(turn_context: TurnContext) -> ConnectorClient:
if hasattr(turn_context.adapter, 'create_connector_client'):
return await turn_context.adapter.create_connector_client(turn_context.activity.service_url)
connector_client = turn_context.turn_state.get(
BotAdapter.BOT_CONNECTOR_CLIENT_KEY
)
if connector_client is None:
raise ValueError('This method requires a connector client.')
return connector_client
Then for bot sample 58.teams-start-thread-in-channel, in file teams_start_thread_in_channel.py, i changed:
# works only with BotFrameworkAdapter
connector_client = await turn_context.adapter.create_connector_client(turn_context.activity.service_url)
to
# works for both CloudAdapter and BotFrameworkAdapter
connector_client = await TeamsInfo._get_connector_client(turn_context)
This change seems to maintain compatibility with both CloudAdapter and BotFrameworkAdapter.
from botbuilder-python.
I've updated the PR #2062. Let me know your thoughts.
from botbuilder-python.
@rampaged is there any way i can ask you some questions? The teams samples are not ported yet to the CloudAdaptor and im having a few difficulties. Should i put my questions here or can we chat somewhere else?
from botbuilder-python.
Hey @BasileBerckmoes, Of course. Happy to help.
Note that for "how-to" questions, the Microsoft Bot Framework team prefers posting questions on
Stack Overflow with the #botframework tag.
The official Bot Framework Github repos are the preferred platform for submitting bug fixes and feature requests.
from botbuilder-python.
Related Issues (20)
- port: CertificateServiceClientCredentialsFactory
- Ability to pass BotFrameworkConnectorConfiguration into ConfigurationBotFrameworkAuthentication
- Add AllowedCallersClaimsValidator, AllowedSkillsClaimsValidator
- ESRP in build pipelines HOT 1
- aiohttp.web_exceptions.HTTPNotFound: Not Found HOT 7
- Recommended change to 3.8.6 or above HOT 2
- Add connect_named_pipe method to CloudAdapter in support of DirectLine Speech
- Can't deploy echo-bot.py. ModuleNotFoundError: No module named 'aiohttp' HOT 3
- CVE-2020-22083 security vulnerability in botbuilder-python SDK HOT 2
- botbuilder-integration-aiohttp - Python 3.12 compatibility HOT 4
- TeamsChannelAccount model missing aadObjectId
- Bot gives same response multiple times when deployed with azure bot service HOT 4
- BotBuilder-Python SSO not working HOT 5
- SSO: AttributeError: 'CloudAdapter' object has no attribute 'exchange_token' HOT 2
- Occasional unexpected ConversationNotFound
- AttributeError: 'CloudAdapter' object has no attribute 'sign_out_user' HOT 4
- Starting from 4.15.0 - <Request POST /api/messages > has failed with exception: KeyError('access_token') HOT 3
- Need to upgrade aiohttp dependency to 3.9.4 HOT 1
- use managed identity in Python bot HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from botbuilder-python.