private static async Task CreateSubscription(string channelId, QandAModel model, GraphServiceClient graph) { var subscription = new Subscription { Resource = $"teams/{model.teamId}/channels/{model.channelId}/messages", ChangeType = "created,updated,deleted", NotificationUrl = ConfigurationManager.AppSettings["NotificationUrl"], ClientState = Guid.NewGuid().ToString(), //ExpirationDateTime = DateTime.UtcNow + new TimeSpan(days: 0, hours: 0, minutes: 10, seconds: 0), ExpirationDateTime = DateTime.UtcNow + new TimeSpan(days: 0, hours: 0, minutes: 1, seconds: 0), IncludeProperties = false, LifecycleNotificationUrl = "https://qna.ngrok.io/webhookLifecyle", AdditionalData = new Dictionary <string, object>() { ["includeResourceData"] = false, ["encryptionCertificate"] = SelfSignedCert, ["encryptionCertificateId"] = "testcert", } }; try { if (channelToSubscription.ContainsKey(channelId)) { // refresh subscription var subId = channelToSubscription[channelId]; // Since this is a fake encryption subscription, we can't update the encryption properties subscription.AdditionalData = null; var newSubscription = await graph.Subscriptions[subId].Request().UpdateAsync(subscription); } else { try { var newSubscription = await graph.Subscriptions.Request().AddAsync(subscription); channelToSubscription[channelId] = newSubscription.Id; } catch (Exception e) when(e.Message.Contains("has reached its limit of 1 TEAMS")) { // ignore, we're still being notified } } } catch (Exception) { // Bail on subscriptions without killing the whole demo } }
public ActionResult MarkAsUnanswered( [FromUri(Name = "tenantId")] string tenantId, [FromUri(Name = "teamId")] string teamId, [FromUri(Name = "channelId")] string channelId, [FromUri(Name = "messageId")] string messageId) //, //[FromQuery(Name = "replyId")] string replyId) { QandAModel model = GetModel(tenantId, teamId, channelId, ""); //messageId); //model.IsQuestionAnswered[replyId] = true; model.IsQuestionAnswered[messageId] = false; string url = $"~/First?tenantId={tenantId}&teamId={teamId}&channelId={channelId}&messageId={messageId}&skipRefresh=true&useRSC=false"; return(Redirect(url)); }
public async Task RefreshQandA(QandAModel qAndA, GraphServiceClient graph) { var handle = graph.Teams[qAndA.teamId].Channels[qAndA.channelId] .Messages.Request().Top(30); try { var msgs = await handle.GetAsync(); //var msgs = await graph.Teams[qAndA.teamId].Channels[qAndA.channelId] // .Messages.Request().Top(30).GetAsync(); ////var msgs = await graph.Teams[qAndA.teamId].Channels[qAndA.channelId] // .Messages[qAndA.messageId].Replies.Request().Top(50).GetAsync(); // merge w/ existing questions var questions = from m in msgs where IsQuestion(m) select new Question() { MessageId = m.Id, Text = StripHTML(m.Body.Content), Votes = m.Reactions.Count() }; qAndA.Questions = questions.OrderByDescending(m => m.Votes).ToList(); foreach (var q in questions) { if (!qAndA.IsQuestionAnswered.ContainsKey(q.MessageId)) { qAndA.IsQuestionAnswered[q.MessageId] = false; } } //await UpdateCard(qAndA); } catch (Exception e) { string m = String.Format("{0}\n {1}\n {2}\n {3}\n --- trace {4}", handle.GetHttpRequestMessage().GetRequestContext().ClientRequestId, handle.GetHttpRequestMessage().Method, handle.GetHttpRequestMessage().RequestUri, handle.GetHttpRequestMessage().Content, //handle.GetHttpRequestMessage().Headers.Authorization.Parameter, e.StackTrace); throw new Exception(m); } }
public ActionResult MarkAsAnswered( [FromUri(Name = "tenantId")] string tenantId, [FromUri(Name = "teamId")] string teamId, [FromUri(Name = "channelId")] string channelId, [FromUri(Name = "messageId")] string messageId) //, //[FromQuery(Name = "replyId")] string replyId) { QandAModel model = GetModel(tenantId, teamId, channelId, ""); //messageId); //model.IsQuestionAnswered[replyId] = true; model.IsQuestionAnswered[messageId] = true; // to-do -- consider escaping these parameters, even though they aren't trusted on the other end string url = $"~/First?tenantId={tenantId}&teamId={teamId}&channelId={channelId}&messageId={messageId}&skipRefresh=true&useRSC=false"; return(Redirect(url)); }
public async Task <ActionResult> First( [FromUri(Name = "tenantId")] string tenantId, [FromUri(Name = "teamId")] string teamId, [FromUri(Name = "channelId")] string channelId, [FromUri(Name = "skipRefresh")] Nullable <bool> skipRefresh, [FromUri(Name = "useRSC")] Nullable <bool> useRSC ) { bool usingRSC = (useRSC != false); try { // Do our auth check first GraphServiceClient graph = await Authorization.GetGraphClient(teamId, Request.Cookies, Response.Cookies, usingRSC); QandAModel model = GetModel(tenantId, teamId, channelId, ""); QandAModelWrapper wrapper = new QandAModelWrapper() { useRSC = usingRSC, showLogin = false, model = model }; if (skipRefresh != true) { await RefreshQandA(model, graph); GraphServiceClient graphForWebhooks = await Authorization.GetGraphClientForCreatingWebhooks(teamId, Request.Cookies, Response.Cookies, usingRSC); await CreateSubscription(channelId, model, graphForWebhooks); } ViewBag.MyModel = model; return(View("First", wrapper)); } catch (Exception e) when(e.Message.Contains("Unauthorized") || e.Message.Contains("Access token has expired.")) { return(ShowSignin(usingRSC)); } }
private QandAModel GetModel(string tenantId, string teamId, string channelId, string messageId) { // TODO: Validate the user has access to the team (or at least the tenant) before retrieving the model. // It's not critical right now since we'll fail out when we make the inevitable Graph calls to // update the model, but it's a little fragile. string key = QandAModel.Encode(tenantId, teamId, channelId, messageId); QandAModel model; if (QandAModel.qAndALookup.ContainsKey(key)) { model = QandAModel.qAndALookup[key]; } else { model = new QandAModel() { tenantId = tenantId, teamId = teamId, channelId = channelId, messageId = messageId }; QandAModel.qAndALookup[key] = model; } return(model); }