#.net #botframework #microsoft-teams #microsoft-graph-sdks #microsoft-graph-teams
#.net #botframework #microsoft-команды #microsoft-graph-sdks #microsoft-graph-teams
Вопрос:
Я разрабатываю бот-уведомление для своей организации в Microsoft Teams, используя ASP.NET Core, Graph SDK (от Microsoft.Identity.Web.MicrosoftGraphBeta). У меня возникли трудности с разработкой простой системы уведомлений для конкретных пользователей в моем клиенте. Эти пользователи идентифицируются по адресу электронной почты. Я уже внедрил упреждающую установку приложения для пользователей и команд, выполнив:
Контроллер, который получает сообщение для отправки в MS Teams определенному пользователю
[HttpPost]
[Route("notify-approver")]
public async Task<List<string>> NotifyApprover([FromBody] ApproverMessage approverMessage)
{
var approvers = await _graphServiceClient.Users.Request().Filter($"mail eq '{approverMessage.Email}'").GetAsync();
var targetApprover = approvers.CurrentPage.ToList().FirstOrDefault();
var appInstallation= await _graphServiceClient.InstallIfNotAlreadyForUser(targetApprover.Id, _configuration.GetTeamsAppId());
// PER IL MOMENTO SEMBRA BUG
var chatId = await _graphServiceClient.GetChatThreadId(targetApprover.Id, appInstallation.Id)
}
Установка работает нормально и возвращает UserScopeTeamsAppInstallation.
Это функция, которую я создал для упреждающей установки:
public static async Task<UserScopeTeamsAppInstallation> InstallIfNotAlreadyForUser(this IGraphServiceClient graphServiceClient, string userId, string teamsAppId)
{
var appsCollectionPage = await graphServiceClient.Users[userId].Teamwork.InstalledApps.Request().Expand("teamsAppDefinition")
.GetAsync();
var appInstallation = appsCollectionPage.CurrentPage.ToList()
.Find(a => a.TeamsAppDefinition.TeamsAppId.Equals(teamsAppId));
if (appInstallation != null) return appInstallation;
var userScopeTeamsAppInstallation = new UserScopeTeamsAppInstallation()
{
AdditionalData = new Dictionary<string, object>
{
{"teamsApp@odata.bind", $"https://graph.microsoft.com/beta/appCatalogs/teamsApps/{teamsAppId}"}
}
};
var freshInstallation = await graphServiceClient.Users[userId].Teamwork.InstalledApps.Request().AddAsync(userScopeTeamsAppInstallation);
return freshInstallation;
}
До сих пор все было в порядке, но плохая часть заключается в пробной отправке упреждающего сообщения; как предложено в документации, я продолжаю получать этот идентификатор чата, который должен быть полезен. Самое смешное, что если я использую Graph Client версии v1.0, я получаю внутреннюю ошибку сервера при получении этого объекта чата ниже, затем я переключился на бета-клиент, и это … вроде работает.
public static async Task<string> GetChatThreadId(this IGraphServiceClient graphServiceClient, string userId, string teamsAppIdInstallation)
{
// forse ci vuole AppInstallationId anziché il semplice teamsAppId della app
//var chat = await graphServiceClient.Users[userId].Teamwork.InstalledApps[teamsAppIdInstallation].Chat.Request().GetAsync();
/*
* Perché dá internal server error?? Possibile bug della API di graph
* perché viceversa da graph explorer funziona e restituisce l'id della chat
*
* Per adesso sono costretto a salvarmi in un database tutte le conversation references.
*/
var chat = graphServiceClient.Users[userId].Teamwork.InstalledApps[teamsAppIdInstallation].Chat.Request().GetAsync();
return chat.Id.ToString();
//return "CIAONE";
}
С этого момента я все больше и больше запутываюсь в количестве разбросанной информации.
Я видел, что обычно это должно быть:
- При обратном вызове бота OnConversationUpdateActivityAsync ссылка ConversationReference должна быть сохранена, поскольку она запускается при установке бота. Но тогда этот объект хранится в ConcurrentDictionary, и его время жизни ограничено временем выполнения. В моем случае я должен сохранить его, но я не могу найти, какие свойства сохранить и как воссоздать объект ConversationReference, который в примерах всегда сохраняется как есть, но без примера с сохранением.
- Учитывая вышеуказанную проблему, я не могу использовать на адаптере бота метод ContinueConversationAsync.
- В любом случае, если мы посмотрим на документацию, даже если в ней явно указано, чтобы получить этот идентификатор чата, он выполняет операцию с помощью кэшированных объектов ConversationReference.
Как я должен делать такую «простую» вещь, как отправка простого сообщения человеку, в то время как это выглядит так запутанно?
Ответ №1:
Чтобы сохранить свойства ссылки на диалог, проверьте концепцию уровня хранения в документах состояния бота
Вы можете продолжить и попробовать, поместив этот пример кода в файл startup.cs:
services.AddSingleton<IStorage>(new AzureBlobStorage(Configuration["_connectionstring"], Configuration["BlobName"]));
services.AddSingleton<UserState>();
services.AddSingleton<ConversationState>();
services.AddSingleton<BotStateService>();
А также, пожалуйста, ознакомьтесь с примером кода proactive-messages.
Комментарии:
1. спасибо за ответ. В итоге я нашел «простое» решение без особых проблем, сохранив каждый объект ConversationReference в локальной базе данных Sqlite: я сериализую его в JSON, а затем сохраняю как строку json, и когда я извлекаю его, я десериализую его из строки json и использую, как описано в документации по упреждающим сообщениям
Ответ №2:
Простым решением, которое я нашел, было сохранение объекта ConversationReference в обратном вызове OnTeamsMembersAddedAsync, когда бот установлен для пользователя, путем сериализации его в json и сохранения в базе данных Sqlite. Для его извлечения я десериализую его и использую