К сожалению, похоже, что это проблема в самой Bot Framework , в настоящее время для решения этой проблемы необходимо зарегистрировать DocumentDbBotDataStore
вместо TableBotDataStore
, в вас Global.asax.cs_ApplicationStart
поместите эти:
var uri = new Uri(ConfigurationManager.AppSettings["DocumentDBUri"]);
var key = ConfigurationManager.AppSettings["DocumentDBKey"];
var store = new DocumentDbBotDataStore(uri, key);
Conversation.UpdateContainer(
builder =>
{
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
});
Редактировать:
Есть исправление огромный клейкой ленты, я не хотел публиковать, что я реализовал некоторое время назад иудалось заставить его работать.Идея состоит в том, чтобы найти сервис устранения неполадок и взломать структуру бота, переопределив его и заставив повторить попытку.В какой-то момент менеджер кэша токенов в фреймворке становится неисправным, и токен доступа не обновляется вовремя, прежде чем сделать запрос, мы вызываем Unauthorized Error
, поэтому мы также взламываем его и заставляем обновить токен.
Вот как:
Мы перезаписываем IBotToUser
декоратор платформы ботов, который создает HttpRequest
, который вызывает ошибку:
public class RetryHandlerDecorator : IBotToUser
{
private readonly IMessageActivity _toBot;
private readonly IConnectorClient _client;
public RetryHandlerDecorator(IMessageActivity toBot, IConnectorClient client)
{
SetField.NotNull(out _toBot, nameof(toBot), toBot);
SetField.NotNull(out _client, nameof(client), client);
}
IMessageActivity IBotToUser.MakeMessage()
{
var toBotActivity = (Activity)_toBot;
return toBotActivity.CreateReply();
}
async Task IBotToUser.PostAsync(IMessageActivity message, CancellationToken cancellationToken)
{
try
{
await _client.Conversations.ReplyToActivityAsync((Activity)message, cancellationToken);
}
catch (Exception e)
{
if (IsTransientError(e))
{
await HandleRetry(message, cancellationToken);
}
else
{
throw;
}
}
}
private async Task HandleRetry(IMessageActivity activity, CancellationToken token)
{
await ForceRefreshTokenAsync();
await _client.Conversations.ReplyToActivityAsync((Activity)activity, token);
}
private async Task ForceRefreshTokenAsync()
{
var credentialsManager = new MicrosoftAppCredentials(
ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppIdKey],
ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppPasswordKey]);
// force the generation of a new token
// this will store the token in a static cache list, so no harm in creating a new instance of MicrosoftAppCredentials
await credentialsManager.GetTokenAsync(true);
}
private static bool IsTransientError(Exception e)
{
switch (e)
{
case ErrorResponseException ex:
return ex.Response.StatusCode == HttpStatusCode.Unauthorized;
default:
return false;
}
}
}
Мы переопределяем модуль каркаса бота, который регистрирует IBotToUser
service:
public class BotToUserModuleOverride : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<RetryHandlerDecorator>().Keyed<IBotToUser>(typeof(RetryHandlerDecorator))
.InstancePerLifetimeScope();
RegisterAdapterChain<IBotToUser>(builder,
typeof(RetryHandlerDecorator), // this was previously AlwaysSendDirect_BotToUser
typeof(AutoInputHint_BotToUser),
typeof(MapToChannelData_BotToUser),
typeof(LogBotToUser)
)
.InstancePerLifetimeScope();
}
public static IRegistrationBuilder<TLimit, SimpleActivatorData, SingleRegistrationStyle> RegisterAdapterChain<TLimit>(ContainerBuilder builder, params Type[] types)
{
return
builder
.Register(c =>
{
var service = default(TLimit);
return types.Aggregate(service,
(current, t) => c.ResolveKeyed<TLimit>(t, TypedParameter.From(current)));
})
.As<TLimit>();
}
}
И, наконец, мы взломаем контейнер IoC фреймворка ботов, зарегистрировав наш модуль, который переопределяет сервис, это нужно сделать в Global.asax.cs ApplicationStart
:
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new BotToUserModuleOverride());
});
Так что случится так, что всякий раз, когда UnauthorizedError
поднимается методом ReplyToActivityAsync
, будет процесс, заставляющий обновить токен доступа и повторить попытку.