Python 团队机器人在尝试发回消息时抛出未经授权的错误
我正在尝试使用Python机器人框架SDK构建一个简单的MS团队机器人。使用模拟器在本地测试我的机器人时,一切正常。我在这里使用旧门户注册了bot,因为我不想创建Azure订阅 我将appid和appsecret添加到bot中,并使用API网关(带有HTTP代理集成)将其部署在EC2机器上,以获取消息传递端点的HTTPS url 部署后,代码能够从dev framework页面上的测试功能和团队中实际部署的应用程序接收和解析消息。但是,当尝试响应消息时,我收到一条未经授权的错误消息 以下是堆栈跟踪:Python 团队机器人在尝试发回消息时抛出未经授权的错误,python,amazon-web-services,botframework,Python,Amazon Web Services,Botframework,我正在尝试使用Python机器人框架SDK构建一个简单的MS团队机器人。使用模拟器在本地测试我的机器人时,一切正常。我在这里使用旧门户注册了bot,因为我不想创建Azure订阅 我将appid和appsecret添加到bot中,并使用API网关(带有HTTP代理集成)将其部署在EC2机器上,以获取消息传递端点的HTTPS url 部署后,代码能够从dev framework页面上的测试功能和团队中实际部署的应用程序接收和解析消息。但是,当尝试响应消息时,我收到一条未经授权的错误消息 以下是堆栈跟
[on_turn_error] unhandled error: Operation returned an invalid status code 'Unauthorized'
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/bot_adapter.py", line 103, in run_pipeline
context, callback
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/middleware_set.py", line 69, in receive_activity_with_status
return await self.receive_activity_internal(context, callback)
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/middleware_set.py", line 79, in receive_activity_internal
return await callback(context)
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/activity_handler.py", line 28, in on_turn
await self.on_message_activity(turn_context)
File "/home/ec2-user/bot/bot.py", line 24, in on_message_activity
return await turn_context.send_activity(response)
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 165, in send_activity
result = await self.send_activities([activity_or_text])
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 198, in send_activities
return await self._emit(self._on_send_activities, output, logic())
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 276, in _emit
return await logic
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 193, in logic
responses = await self.adapter.send_activities(self, output)
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/bot_framework_adapter.py", line 444, in send_activities
raise error
File "/usr/local/lib/python3.7/site-packages/botbuilder/core/bot_framework_adapter.py", line 431, in send_activities
activity.conversation.id, activity.reply_to_id, activity
File "/usr/local/lib/python3.7/site-packages/botframework/connector/aio/operations_async/_conversations_operations_async.py", line 533, in reply_to_activity
raise models.ErrorResponseException(self._deserialize, response)
botbuilder.schema._models_py3.ErrorResponseException: Operation returned an invalid status code 'Unauthorized'
我的应用程序代码是:
CONFIG = DefaultConfig()
SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD,
CONFIG.APP_AUTH_TENANT, CONFIG.APP_OAUTH_ENDPOINT)
ADAPTER = BotFrameworkAdapter(SETTINGS)
# Catch-all for errors.
async def on_error(context: TurnContext, error: Exception):
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
traceback.print_exc()
# Send a message to the user
await context.send_activity("The bot encountered an error or bug.")
await context.send_activity("To continue to run this bot, please fix the bot source code.")
# Send a trace activity if we're talking to the Bot Framework Emulator
if context.activity.channel_id == "emulator":
# Create a trace activity that contains the error object
trace_activity = Activity(
label="TurnError",
name="on_turn_error Trace",
timestamp=datetime.utcnow(),
type=ActivityTypes.trace,
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)
# Send a trace activity, which will be displayed in Bot Framework Emulator
await context.send_activity(trace_activity)
ADAPTER.on_turn_error = on_error
APP_ID = SETTINGS.app_id
dynamodb = boto3.resource("dynamodb")
CONVERSATION_REFERENCES = dynamodb.Table("ConversationReferences")
# Create the Bot
BOT = MyBot(CONVERSATION_REFERENCES)
# Listen for incoming requests on /api/messages
async def messages(req):
print(f"Message Received - {str(datetime.now())}")
json_request = await req.json()
print(f"Request Body: {json_request}")
activity = Activity().deserialize(json_request)
print("Request successfully deserialized")
auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""
try:
print("Sending activity to adapter")
response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
if response:
return Response(status=response.status, text=response.body)
return Response(status=201)
except Exception as exception:
raise exception
async def health(req):
return Response(status=200, text="Working")
APP = web.Application(middlewares=[aiohttp_error_middleware])
APP.router.add_post("/api/messages", messages)
APP.router.add_get("/health", health)
if __name__ == "__main__":
web.run_app(APP)
我的机器人代码是:
class MyBot(ActivityHandler):
def __init__(self, conversation_references):
self.conversation_references = conversation_references
async def on_message_activity(self, turn_context: TurnContext):
print("Message received by bot adapter")
# The next two lines also cause an unauthorized error. I commented them out to try and simplify
# team_details = await teams.TeamsInfo.get_members(turn_context)
# user = team_details[1].email
user = "test@test.com"
conversation = self.conversation_references.get_item(Key={"user": user})
if "Item" in conversation:
response = "You are already registered"
else:
conversation = TurnContext.get_conversation_reference(turn_context.activity)
item = {"user": user, "conversation": conversation.as_dict()}
self.conversation_references.put_item(Item=item)
response = "You have been successfully registered!"
return await turn_context.send_activity(response)
更新:在本地测试时,我没有向模拟器添加应用程序id和密码。当我这样做时,在调试模式下,我会收到以下错误消息:“将“/INSPECT open”命令发布到对话xxxxxx | livechat:400时出错:机器人的Microsoft应用程序ID或Microsoft应用程序密码不正确。”
但是,我100%确信id和密码是正确的,因为我已经从注册页面手动使用令牌端点来获取具有这些凭据的访问令牌。这可能与以下事实有关:在代码中,通过手动使用端点,我可以指定目录(租户)ID,而不能使用emulator来指定
另一个奇怪的地方是,当模拟器返回该响应时,它实际上似乎并没有向我的本地端点发出请求,因此我甚至不确定400响应来自何处。下面是您将从团队获得的内容。 它有一个密钥serviceURL,用于连接回您的bot,bot使用以下json中提供的其他参数部署在您的团队中,我已将其替换为>之间的逻辑名称
{文本:“帮助”,
textFormat:'普通',
键入:“消息”,
时间戳:2020-03-05T12:29:26.830Z,
本地时间戳:2020-03-05T12:29:26.830Z,
id:'158341366810',
channelId:'msteams',
服务URL:'https://smba.trafficmanager.net/emea/',
发件人:
{id:
'',
名称:“”,
aadObjectId:'},
对话:
{conversationType:'个人',
租户:'',
身份证件:
'' },
收件人:
{id:'',名称:'Sybot'},
实体:
[{地区:'en US',
国家:"美国",,
平台:“Windows”,
键入:“clientInfo'}],
channelData:{租户:{id:'}},
地区:'en US'}
收到此消息后,您必须使用以下代码将服务url设置为受信任的url,以便在将消息发送回用户时不会出现未经授权的错误
const { MicrosoftAppCredentials } = require('botbuilder/node_modules/botframework-connector');
const { BotFrameworkAdapter } = require('botbuilder');
const { TurnContext } = require('botbuilder');
const adapter = new BotFrameworkAdapter({
appId: <<Your App ID>>,
appPassword: <<Your App Password>>,
});
turnContext = new TurnContext(adapter, contextActivity);
if (!MicrosoftAppCredentials.isTrustedServiceUrl(serviceUrl)) {
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
}
await context.sendActivity(`Hello World`);
const{MicrosoftAppCredentials}=require('botbuilder/node_modules/botframework connector');
const{BotFrameworkAdapter}=require('botbuilder');
const{TurnContext}=require('botbuilder');
const adapter=新的BotFrameworkAdapter({
appId:,
appPassword:,
});
turnContext=新的turnContext(适配器、contextActivity);
如果(!MicrosoftAppCredentials.isTrustedServiceUrl(serviceUrl)){
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
}
wait context.sendActivity(`Hello World`);
您需要确保使用多租户AAD应用程序凭据,如前所述。我可以看到您正在配置适配器以使用应用程序ID和密码,但您的bot配置是否实际包含要提供给适配器的应用程序ID和密码?在本地测试bot时,是否向模拟器提供应用程序ID和密码?尝试在团队或网络聊天时进行测试。另外,我们是要假设这真的是你的整个机器人,还是你真的在使用这些会话引用来发送主动消息?团队要求您调用TrustServiceUrl
。感谢您的响应,在本地测试时,我没有向模拟器提供应用程序ID和密码。当我这样做时,它会告诉我我的应用程序id或密码是错误的,但我已经三次检查了这些值,甚至要求输入新密码。我将在问题中添加调试模式的错误消息。我还在postman中手动使用了令牌端点,并获得了一个访问令牌。我将发送带有会话引用的主动消息,但目前我只是尝试让初始呼叫和响应正常工作。你的应用程序ID和密码是否在你的bot的config.py中?模拟器直接与您的机器人通信,因此您对机器人注册或AAD应用注册所做的任何配置都不会影响模拟器。如果您的bot未经身份验证(意味着它没有凭据或未编程为使用凭据),那么这就是为什么您只能在模拟器中连接到它而不指定凭据的原因。你可以读到@KyleDelany,我认为仿真器除了本地托管的bot之外,实际上还连接到应用程序注册数据,因为初始会话请求被发送到另一个端点。我采纳了您提供的链接中的信息建议,即在应用程序注册中从单租户切换到多租户,从而解决了身份验证问题。非常感谢您的帮助,如果您想将该链接添加到答案中,我很乐意接受。这看起来更像Node.js而不是PythonYes这是Node js,但概念类似。我们必须使服务URL成为受信任的URL。@KulandaiGeorgeMariasingaray我认为默认情况下机器人信任团队服务URL。添加此代码的python版本并没有改变任何事情。
const { MicrosoftAppCredentials } = require('botbuilder/node_modules/botframework-connector');
const { BotFrameworkAdapter } = require('botbuilder');
const { TurnContext } = require('botbuilder');
const adapter = new BotFrameworkAdapter({
appId: <<Your App ID>>,
appPassword: <<Your App Password>>,
});
turnContext = new TurnContext(adapter, contextActivity);
if (!MicrosoftAppCredentials.isTrustedServiceUrl(serviceUrl)) {
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
}
await context.sendActivity(`Hello World`);