Отправка уведомления в чате один на один пользователям в тенанте

Я разрабатываю бот для уведомлений для своей организации в 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>
                {
                    {"[email protected]", $"https://graph.microsoft.com/beta/appCatalogs/teamsApps/{teamsAppId}"}
                }
            };
            var freshInstallation = await graphServiceClient.Users[userId].Teamwork.InstalledApps.Request().AddAsync(userScopeTeamsAppInstallation);
            return freshInstallation;

        }

До сих пор это нормально, но плохо в пробной версии - проактивно отправлять сообщения; как предложено в документация Я продолжаю получать этот chatId, который должен быть полезен. Забавно то, что если я использую 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";
        }

Объект чата, полученный при получении в функции GetChatThreadId С этого момента меня все больше и больше смущает количество разрозненной информации. Я увидел, что обычно это должно быть:

  • При обратном вызове бота OnConversationUpdateActivityAsync ConversationReference должен быть сохранен, поскольку он запускается при установке бота. Но тогда этот объект сохраняется в ConcurrentDictionary, и его время жизни ограничено временем выполнения. В моем случае я должен сохранить его, но я не могу найти, какие свойства хранить и как воссоздать объект ConversationReference, который в примерах всегда хранится как есть, но нет примера с постоянством.
  • Учитывая указанную выше проблему, я не могу использовать в адаптере бота метод ContinueConversationAsync.
  • В любом случае, если мы посмотрим на документация, даже если он явно говорит, что нужно получить этот ChatId, он выполняет операцию, используя кэшированные объекты ConversationReference.

Как я могу сделать такую ​​простую вещь, как отправить простое сообщение человеку, когда это выглядит так запутанно?


person exrezzo    schedule 20.11.2020    source источник


Ответы (2)


Чтобы сохранить свойства ссылки разговора, проверьте концепцию уровня хранения в Документы состояния бота

Вы можете пойти дальше и попробовать, поместив этот образец кода в файл startup.cs:

services.AddSingleton<IStorage>(new AzureBlobStorage(Configuration["_connectionstring"], Configuration["BlobName"]));
services.AddSingleton<UserState>();
services.AddSingleton<ConversationState>();
services.AddSingleton<BotStateService>(); 

А также ознакомьтесь с proactive- messages пример кода.

person Rama-MSFT    schedule 26.11.2020
comment
Спасибо за ответ. В конце концов, я нашел простое решение без особых проблем, сохранив каждый объект ConversationReference в локальной базе данных Sqlite: я сериализую его в JSON, а затем сохраняю его как строку json, а когда я получаю его, я десериализую его из строки json и использую его как описано в документации по проактивному обмену сообщениями - person exrezzo; 26.11.2020

Я нашел простое решение - сохранить объект ConversationReference в обратном вызове OnTeamsMembersAddedAsync, когда бот установлен для пользователя, путем его сериализации в json и сохранения в базе данных Sqlite. Для его получения я десериализую его и использую

person exrezzo    schedule 26.11.2020