├── MicrosoftGraphBot ├── Global.asax ├── Models │ ├── EntityType.cs │ ├── ItemType.cs │ ├── File.cs │ ├── Mail.cs │ ├── Plan.cs │ ├── Bucket.cs │ ├── ItemBase.cs │ ├── OperationType.cs │ ├── BaseEntity.cs │ ├── Group.cs │ ├── PlanTask.cs │ ├── User.cs │ └── QueryOperation.cs ├── default.htm ├── Dialog │ ├── ResourceTypes │ │ ├── PhotoDialog.cs │ │ ├── DirectReportsDialog.cs │ │ ├── MembersDialog.cs │ │ ├── ManagerDialog.cs │ │ ├── FilesDialog.cs │ │ └── TasksDialog.cs │ ├── PlanLookupDialog.cs │ ├── BucketLookupDialog.cs │ ├── AuthDialog.cs │ ├── UserLookupDialog.cs │ ├── GroupLookupDialog.cs │ ├── EntityLookupDialog.cs │ └── GraphDialog.cs ├── Global.asax.cs ├── Web.Debug.config ├── Web.Release.config ├── App_Start │ └── WebApiConfig.cs ├── Properties │ └── AssemblyInfo.cs ├── Controllers │ └── MessagesController.cs ├── packages.config ├── Web.config ├── Resource.resx ├── Resource.Designer.cs ├── MicrosoftGraphBot.csproj └── Extensions.cs ├── MicrosoftGraphBot.Tests ├── MailTests.cs ├── EventsTests.cs ├── FilesTests.cs ├── GroupsTests.cs ├── ManagerTests.cs ├── MembersTests.cs ├── PeopleTests.cs ├── PersonTests.cs ├── PhotoTests.cs ├── PlansTests.cs ├── TasksTests.cs ├── ContactsTests.cs ├── NotebooksTests.cs ├── TrendingTests.cs ├── WorkingWithTests.cs ├── DirectReportsTests.cs ├── Properties │ └── AssemblyInfo.cs └── MicrosoftGraphBot.Tests.csproj ├── botframework-csharp-graph-explorer.yml ├── README.md ├── LICENSE ├── MicrosoftGraphBot.sln ├── SECURITY.md └── .gitignore /MicrosoftGraphBot/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="MicrosoftGraphBot.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/EntityType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MicrosoftGraphBot.Models 4 | { 5 | [Serializable] 6 | public enum EntityType 7 | { 8 | Me, 9 | User, 10 | Group, 11 | Plan 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/MailTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class MailTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/EventsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class EventsTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/FilesTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class FilesTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/GroupsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class GroupsTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/ManagerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class ManagerTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/MembersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class MembersTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/PeopleTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class PeopleTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/PersonTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class PersonTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/PhotoTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class PhotoTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/PlansTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class PlansTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/TasksTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class TasksTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/ContactsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class ContactsTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/NotebooksTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class NotebooksTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/TrendingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class TrendingTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/WorkingWithTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class WorkingWithTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/DirectReportsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace MicrosoftGraphBot.Tests 5 | { 6 | [TestClass] 7 | public class DirectReportsTests 8 | { 9 | [TestMethod] 10 | public void TestMethod1() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/ItemType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MicrosoftGraphBot.Models 4 | { 5 | [Serializable] 6 | public enum ItemType 7 | { 8 | File, 9 | Folder, 10 | Mail, 11 | Event, 12 | Contact, 13 | Person, 14 | Group, 15 | NavNext, 16 | NavPrevious, 17 | NavUp, 18 | Cancel, 19 | Plan, 20 | Bucket, 21 | Task 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /botframework-csharp-graph-explorer.yml: -------------------------------------------------------------------------------- 1 | page_type: sample 2 | products: 3 | - ms-graph 4 | languages: 5 | - csharp 6 | extensions: 7 | contentType: tools 8 | technologies: 9 | - Microsoft Graph 10 | - Microsoft Bot Framework 11 | createdDate: '7/20/2016 10:34:50 PM' 12 | title: botframework-csharp-graph-explorer 13 | description: The Microsoft Graph Bot is a bot built with the Microsoft Bot Framework that allows interactive exploration of Microsoft Graph. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BotFramework-CSharp-Graph-Explorer 2 | Repository for the Microsoft Graph Bot, which is a Bot for exploring the information contained in the Microsoft cloud. 3 | 4 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/default.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

MicrosoftGraphBot

9 |

Describe your bot here and your terms of use etc.

10 |

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

11 | 12 | 13 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/File.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MicrosoftGraphBot.Models 8 | { 9 | [Serializable] 10 | public class File : ItemBase 11 | { 12 | public File() : base() { } 13 | public File(string text, string endpoint, ItemType type) : base(text, endpoint, type) { } 14 | 15 | public int size { get; set; } 16 | public string webUrl { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/Mail.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MicrosoftGraphBot.Models 8 | { 9 | [Serializable] 10 | public class Mail : ItemBase 11 | { 12 | public Mail() : base() { } 13 | public Mail(string text, string endpoint, ItemType type) : base(text, endpoint, type) { } 14 | 15 | public string importance { get; set; } 16 | public string senderName { get; set; } 17 | public string senderEmail { get; set; } 18 | public DateTime sentDate { get; set; } 19 | public bool isRead { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/Plan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace MicrosoftGraphBot.Models 5 | { 6 | [Serializable] 7 | public class Plan : ItemBase 8 | { 9 | [JsonProperty("@odata.etag")] 10 | public string ETag { get; set; } 11 | 12 | public string Id 13 | { 14 | get { return id; } 15 | set 16 | { 17 | id = value; 18 | navEndpoint = $"/tasks/{value}"; 19 | } 20 | } 21 | 22 | public string Title 23 | { 24 | get { return text; } 25 | set 26 | { 27 | text = value; 28 | } 29 | } 30 | 31 | public string Owner { get; set; } 32 | 33 | public string CreatedBy { get; set; } 34 | 35 | public Plan() 36 | : base(null, null, ItemType.Plan) 37 | { 38 | 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/Bucket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace MicrosoftGraphBot.Models 5 | { 6 | [Serializable] 7 | public class Bucket : ItemBase 8 | { 9 | [JsonProperty("@odata.etag")] 10 | public string ETag { get; set; } 11 | 12 | public string Id 13 | { 14 | get { return id; } 15 | set 16 | { 17 | id = value; 18 | navEndpoint = $"/tasks/{value}"; 19 | } 20 | } 21 | 22 | public string Name 23 | { 24 | get { return text; } 25 | set 26 | { 27 | text = value; 28 | } 29 | } 30 | 31 | public string PlanId { get; set; } 32 | 33 | public string OrderHint { get; set; } 34 | 35 | public Bucket() 36 | : base(null, null, ItemType.Bucket) 37 | { 38 | 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/ItemBase.cs: -------------------------------------------------------------------------------- 1 | using MicrosoftGraphBot.Dialog; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MicrosoftGraphBot.Models 11 | { 12 | [Serializable] 13 | public class ItemBase 14 | { 15 | public ItemBase() { } 16 | public ItemBase(string text, string endpoint, ItemType type) 17 | { 18 | this.text = text; 19 | this.navEndpoint = endpoint; 20 | this.itemType = type; 21 | } 22 | 23 | public string id { get; set; } 24 | public string text { get; set; } 25 | public string navEndpoint { get; set; } 26 | public ItemType itemType { get; set; } 27 | 28 | public override string ToString() 29 | { 30 | return this.text; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/OperationType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MicrosoftGraphBot.Models 8 | { 9 | [Serializable] 10 | public enum OperationType 11 | { 12 | Manager, 13 | DirectReports, 14 | Photo, 15 | Files, 16 | Mail, 17 | Events, 18 | Contacts, 19 | Groups, 20 | WorkingWith, 21 | TrendingAround, 22 | People, 23 | Notebooks, 24 | Tasks, 25 | Plans, 26 | Members, 27 | Conversations, 28 | 29 | //Navigation choices 30 | Next, 31 | Previous, 32 | Up, 33 | StartOver, 34 | ChangeDialogEntity, 35 | ShowOperations, 36 | Create, 37 | Delete, 38 | Download, 39 | Upload, 40 | Folder, 41 | InProgress, 42 | Complete 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MicrosoftGraphBot.Models 4 | { 5 | [Serializable] 6 | public class BaseEntity 7 | { 8 | public BaseEntity() { } 9 | public BaseEntity(User user, EntityType type) 10 | { 11 | this.id = user.id; 12 | this.text = user.ToString(); 13 | this.entityType = type; 14 | } 15 | 16 | public BaseEntity(Group group) 17 | { 18 | this.id = group.id; 19 | this.text = group.ToString(); 20 | this.entityType = EntityType.Group; 21 | } 22 | 23 | public BaseEntity(Plan plan) 24 | { 25 | this.id = plan.id; 26 | this.text = plan.Title; 27 | this.entityType = EntityType.Plan; 28 | } 29 | 30 | public string id { get; set; } 31 | public string text { get; set; } 32 | public EntityType entityType { get; set; } 33 | 34 | public override string ToString() 35 | { 36 | return this.text; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/ResourceTypes/PhotoDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace MicrosoftGraphBot.Dialog.ResourceTypes 10 | { 11 | public class PhotoDialog : IDialog 12 | { 13 | /// 14 | /// Called to start a dialog 15 | /// 16 | /// IDialogContext 17 | /// 18 | public async Task StartAsync(IDialogContext context) 19 | { 20 | context.Wait(MessageReceivedAsync); 21 | } 22 | 23 | /// 24 | /// Processes messages received on new thread 25 | /// 26 | /// IDialogContext 27 | /// Awaitable IMessageActivity 28 | /// Task 29 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 30 | { 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Linq; 5 | using System.Web; 6 | using System.Web.Http; 7 | using System.Web.Routing; 8 | 9 | namespace MicrosoftGraphBot 10 | { 11 | public class WebApiApplication : System.Web.HttpApplication 12 | { 13 | protected void Application_Start() 14 | { 15 | GlobalConfiguration.Configure(WebApiConfig.Register); 16 | 17 | AuthBot.Models.AuthSettings.Mode = ConfigurationManager.AppSettings["ActiveDirectory.Mode"]; 18 | AuthBot.Models.AuthSettings.EndpointUrl = ConfigurationManager.AppSettings["ActiveDirectory.EndpointUrl"]; 19 | AuthBot.Models.AuthSettings.Tenant = ConfigurationManager.AppSettings["ActiveDirectory.Tenant"]; 20 | AuthBot.Models.AuthSettings.RedirectUrl = ConfigurationManager.AppSettings["ActiveDirectory.RedirectUrl"]; 21 | AuthBot.Models.AuthSettings.ClientId = ConfigurationManager.AppSettings["ActiveDirectory.ClientId"]; 22 | AuthBot.Models.AuthSettings.ClientSecret = ConfigurationManager.AppSettings["ActiveDirectory.ClientSecret"]; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/PlanLookupDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Bot.Builder.Dialogs; 4 | using MicrosoftGraphBot.Models; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace MicrosoftGraphBot.Dialog 8 | { 9 | [Serializable] 10 | public class PlanLookupDialog : EntityLookupDialog 11 | { 12 | public override string LookupPrompt => "Which plan are you interested in (lookup by full/partial name)?"; 13 | 14 | public override string NoLookupPrompt => "Which plan are you interested in?"; 15 | 16 | public override string NoChoicesPrompt => "No choices found... do you want to try again?"; 17 | 18 | public override string MultipleChoicesPrompt => "Multiple choices found... which plan would you like to choose?"; 19 | 20 | public override string GetRequestUri(IDialogContext dialogContext) 21 | { 22 | var user = dialogContext.ConversationData.Get("Me"); 23 | return $"https://graph.microsoft.com/beta/users/{user.id}/plans"; 24 | } 25 | 26 | public override List DeserializeArray(JArray array) 27 | { 28 | return array.ToPlanList(); 29 | } 30 | 31 | public override bool FilterEntity(Plan entity, string query) 32 | { 33 | return entity.Title.ToLower().Contains(query); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/BucketLookupDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Bot.Builder.Dialogs; 4 | using MicrosoftGraphBot.Models; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace MicrosoftGraphBot.Dialog 8 | { 9 | [Serializable] 10 | public class BucketLookupDialog : EntityLookupDialog 11 | { 12 | public override string LookupPrompt => "Which bucket are you interested in (lookup by full/partial name)?"; 13 | 14 | public override string NoLookupPrompt => "Which bucket are you interested in?"; 15 | 16 | public override string NoChoicesPrompt => "No choices found... do you want to try again?"; 17 | 18 | public override string MultipleChoicesPrompt => "Multiple choices found... which bucket would you like to choose?"; 19 | 20 | public override string GetRequestUri(IDialogContext dialogContext) 21 | { 22 | var plan = dialogContext.ConversationData.Get("Plan"); 23 | return $"https://graph.microsoft.com/beta/plans/{plan.id}/buckets"; 24 | } 25 | 26 | public override List DeserializeArray(JArray array) 27 | { 28 | return array.ToBucketList(); 29 | } 30 | 31 | public override bool FilterEntity(Bucket entity, string query) 32 | { 33 | return entity.Name.ToLower().Contains(query); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Web.Http; 7 | 8 | namespace MicrosoftGraphBot 9 | { 10 | public static class WebApiConfig 11 | { 12 | public static void Register(HttpConfiguration config) 13 | { 14 | // Json settings 15 | config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; 16 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 17 | config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; 18 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings() 19 | { 20 | ContractResolver = new CamelCasePropertyNamesContractResolver(), 21 | Formatting = Newtonsoft.Json.Formatting.Indented, 22 | NullValueHandling = NullValueHandling.Ignore, 23 | }; 24 | 25 | // Web API configuration and services 26 | 27 | // Web API routes 28 | config.MapHttpAttributeRoutes(); 29 | 30 | config.Routes.MapHttpRoute( 31 | name: "DefaultApi", 32 | routeTemplate: "api/{controller}/{id}", 33 | defaults: new { id = RouteParameter.Optional } 34 | ); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MicrosoftGraphBot")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MicrosoftGraphBot")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a8ba1066-5695-4d71-abb4-65e5a5e0c3d4")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MicrosoftGraphBot.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MicrosoftGraphBot.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("fabe0c7e-0b52-4148-a4ce-d8d3de79876d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Controllers/MessagesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using System.Web.Http; 7 | using System.Web.Http.Description; 8 | using Microsoft.Bot.Connector; 9 | using Newtonsoft.Json; 10 | using Microsoft.Bot.Builder.Dialogs; 11 | using MicrosoftGraphBot.Dialog; 12 | 13 | namespace MicrosoftGraphBot 14 | { 15 | [BotAuthentication] 16 | public class MessagesController : ApiController 17 | { 18 | /// 19 | /// POST: api/Messages 20 | /// Receive a message from a user and reply to it 21 | /// 22 | [BotAuthentication] 23 | public virtual async Task Post([FromBody]Activity activity) 24 | { 25 | if (activity != null && activity.GetActivityType() == ActivityTypes.Message) 26 | { 27 | await Conversation.SendAsync(activity, () => new AuthDialog()); 28 | } 29 | else 30 | { 31 | this.HandleSystemMessage(activity); 32 | } 33 | return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted); 34 | } 35 | 36 | private Activity HandleSystemMessage(Activity message) 37 | { 38 | if (message.Type == ActivityTypes.Ping) 39 | { 40 | Activity reply = message.CreateReply(); 41 | reply.Type = ActivityTypes.Ping; 42 | return reply; 43 | } 44 | 45 | return null; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/Group.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using MicrosoftGraphBot.Dialog; 9 | using System.Text.RegularExpressions; 10 | using Newtonsoft.Json.Linq; 11 | using Microsoft.Bot.Builder.Dialogs; 12 | using Microsoft.Bot.Builder.Luis.Models; 13 | 14 | namespace MicrosoftGraphBot.Models 15 | { 16 | [Serializable] 17 | public class Group : ItemBase 18 | { 19 | public Group() : base() { } 20 | public Group(string text, string endpoint, ItemType type) : base(text, endpoint, type) { } 21 | public string description { get; set; } 22 | public string mail { get; set; } 23 | public string[] groupTypes { get; set; } 24 | public string visibility { get; set; } 25 | 26 | public override string ToString() 27 | { 28 | return this.text; 29 | } 30 | 31 | public static async Task> Lookup(string token, string searchPhrase) 32 | { 33 | List groups = new List(); 34 | HttpClient client = new HttpClient(); 35 | 36 | //search for the user 37 | var endpoint = String.Format("https://graph.microsoft.com/v1.0/groups?$filter=startswith(displayName,'{0}')%20or%20startswith(mail,'{0}')", searchPhrase); 38 | var json = await client.MSGraphGET(token, endpoint); 39 | groups = ((JArray)json["value"]).ToGroupList(); 40 | 41 | return groups; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicrosoftGraphBot", "MicrosoftGraphBot\MicrosoftGraphBot.csproj", "{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicrosoftGraphBot.Tests", "MicrosoftGraphBot.Tests\MicrosoftGraphBot.Tests.csproj", "{FABE0C7E-0B52-4148-A4CE-D8D3DE79876D}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {FABE0C7E-0B52-4148-A4CE-D8D3DE79876D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {FABE0C7E-0B52-4148-A4CE-D8D3DE79876D}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {FABE0C7E-0B52-4148-A4CE-D8D3DE79876D}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {FABE0C7E-0B52-4148-A4CE-D8D3DE79876D}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/PlanTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace MicrosoftGraphBot.Models 5 | { 6 | [Serializable] 7 | public class PlanTask : ItemBase 8 | { 9 | [JsonProperty("@odata.etag")] 10 | public string ETag { get; set; } 11 | 12 | public string Id 13 | { 14 | get { return id; } 15 | set 16 | { 17 | id = value; 18 | navEndpoint = $"/tasks/{value}"; 19 | } 20 | } 21 | 22 | public string PlanId { get; set; } 23 | 24 | public string BucketId { get; set; } 25 | 26 | public string Title 27 | { 28 | get { return text; } 29 | set 30 | { 31 | text = value; 32 | } 33 | } 34 | 35 | public string CreatedBy { get; set; } 36 | 37 | public string AssignedTo { get; set; } 38 | 39 | public string OrderHint { get; set; } 40 | 41 | public string AssigneePriority { get; set; } 42 | 43 | public int PercentComplete { get; set; } 44 | 45 | public string StartDateTime { get; set; } 46 | 47 | public string AssignedDateTime { get; set; } 48 | 49 | public string CreatedDateTime { get; set; } 50 | 51 | public string AssignedBy { get; set; } 52 | 53 | public string DueDateTime { get; set; } 54 | 55 | public string PreviewType { get; set; } 56 | 57 | public string CompletedDateTime { get; set; } 58 | 59 | public string ConversationThreadId { get; set; } 60 | 61 | public PlanTask() 62 | : base(null, null, ItemType.Task) 63 | { 64 | 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/AuthDialog.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using AuthBot.Dialogs; 3 | using Microsoft.Bot.Builder.Dialogs; 4 | using Microsoft.Bot.Connector; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace MicrosoftGraphBot.Dialog 13 | { 14 | [Serializable] 15 | public class AuthDialog : IDialog 16 | { 17 | public async Task StartAsync(IDialogContext context) 18 | { 19 | context.Wait(MessageReceivedAsync); 20 | } 21 | 22 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 23 | { 24 | //original message if we need to resume 25 | var message = await item; 26 | 27 | //try to get access token 28 | var token = await context.GetAccessToken("https://graph.microsoft.com/"); 29 | if (string.IsNullOrEmpty(token)) 30 | { 31 | //invoke the AuthBot to help get authenticated 32 | await context.Forward(new AzureAuthDialog("https://graph.microsoft.com/"), this.resumeAfterAuth, message, CancellationToken.None); 33 | } 34 | else 35 | { 36 | //token exists...forward to GraphDialog 37 | await context.Forward(new GraphDialog(), null, message, CancellationToken.None); 38 | } 39 | } 40 | 41 | private async Task resumeAfterAuth(IDialogContext context, IAwaitable result) 42 | { 43 | //post the response message and then go back into the MessageReceivedAsync flow 44 | var message = await result; 45 | await context.PostAsync(message); 46 | 47 | //now that token exists...forward to GraphDialog 48 | await context.Forward(new GraphDialog(), null, message, CancellationToken.None); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/User.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using MicrosoftGraphBot.Dialog; 9 | using System.Text.RegularExpressions; 10 | using Newtonsoft.Json.Linq; 11 | using Microsoft.Bot.Builder.Dialogs; 12 | using Microsoft.Bot.Builder.Luis.Models; 13 | 14 | namespace MicrosoftGraphBot.Models 15 | { 16 | [Serializable] 17 | public class User : ItemBase 18 | { 19 | public User() : base() { } 20 | public User(string text, string endpoint, ItemType type) : base(text, endpoint, type) { } 21 | public bool isMe { get; set; } 22 | public string givenName { get; set; } 23 | public string surname { get; set; } 24 | public string jobTitle { get; set; } 25 | public string mail { get; set; } 26 | public string userPrincipalName { get; set; } 27 | public string mobilePhone { get; set; } 28 | public string officeLocation { get; set; } 29 | 30 | public override string ToString() 31 | { 32 | return this.text; 33 | } 34 | 35 | 36 | public static async Task Me(string token) 37 | { 38 | HttpClient client = new HttpClient(); 39 | 40 | //return the current user 41 | var json = await client.MSGraphGET(token, "https://graph.microsoft.com/v1.0/me"); 42 | return json.ToUser(); 43 | } 44 | 45 | public static async Task> Lookup(string token, string searchPhrase) 46 | { 47 | List users = new List(); 48 | HttpClient client = new HttpClient(); 49 | 50 | //search for the user 51 | var endpoint = String.Format("https://graph.microsoft.com/v1.0/users?$filter=startswith(givenName,'{0}')%20or%20startswith(surname,'{0}')%20or%20startswith(displayName,'{0}')%20or%20startswith(userPrincipalName,'{0}')", searchPhrase); 52 | var json = await client.MSGraphGET(token, endpoint); 53 | users = ((JArray)json["value"]).ToUserList(); 54 | 55 | return users; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/UserLookupDialog.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Connector; 4 | using MicrosoftGraphBot.Models; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace MicrosoftGraphBot.Dialog 12 | { 13 | [Serializable] 14 | public class UserLookupDialog : IDialog 15 | { 16 | public async Task StartAsync(IDialogContext context) 17 | { 18 | context.Wait(MessageReceivedAsync); 19 | } 20 | 21 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 22 | { 23 | //don't await the message...we won't use it 24 | PromptDialog.Text(context, async (IDialogContext textContext, IAwaitable textResult) => 25 | { 26 | //get the result and perform the user lookup 27 | var searchText = await textResult; 28 | var token = await textContext.GetAccessToken("https://graph.microsoft.com"); 29 | var matches = await User.Lookup(token, searchText); 30 | 31 | //check the number of matches and respond accordingly 32 | if (matches.Count == 0) 33 | { 34 | //no matches...allow retry 35 | PromptDialog.Confirm(textContext, async (IDialogContext retryContext, IAwaitable retryResult) => 36 | { 37 | //check retry result and handle accordingly 38 | var retry = await retryResult; 39 | if (retry) 40 | await MessageReceivedAsync(retryContext, null); //retry 41 | else 42 | retryContext.Done(null); //return null 43 | }, "No matches found...want to try again?"); 44 | } 45 | else if (matches.Count == 1) 46 | { 47 | //resolve the exact match 48 | textContext.Done(matches[0]); 49 | } 50 | else 51 | { 52 | //multiple matches...give choice 53 | PromptDialog.Choice(textContext, async (IDialogContext choiceContext, IAwaitable choiceResult) => 54 | { 55 | var selection = await choiceResult; 56 | choiceContext.Done(selection); 57 | }, matches, "Multiple matches found...which user would you like to explore?"); 58 | } 59 | }, "What user are you interested in (you can lookup by full/partial name or alias)?"); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/GroupLookupDialog.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Connector; 4 | using MicrosoftGraphBot.Models; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace MicrosoftGraphBot.Dialog 12 | { 13 | [Serializable] 14 | public class GroupLookupDialog : IDialog 15 | { 16 | public async Task StartAsync(IDialogContext context) 17 | { 18 | context.Wait(MessageReceivedAsync); 19 | } 20 | 21 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 22 | { 23 | //don't await the message...we won't use it 24 | PromptDialog.Text(context, async (IDialogContext textContext, IAwaitable textResult) => 25 | { 26 | //get the result and perform the group lookup 27 | var searchText = await textResult; 28 | var token = await textContext.GetAccessToken("https://graph.microsoft.com"); 29 | var matches = await Group.Lookup(token, searchText); 30 | 31 | //check the number of matches and respond accordingly 32 | if (matches.Count == 0) 33 | { 34 | //no matches...allow retry 35 | PromptDialog.Confirm(textContext, async (IDialogContext retryContext, IAwaitable retryResult) => 36 | { 37 | //check retry result and handle accordingly 38 | var retry = await retryResult; 39 | if (retry) 40 | await MessageReceivedAsync(retryContext, null); //retry 41 | else 42 | retryContext.Done(null); //return null 43 | }, "No matches found...want to try again?"); 44 | } 45 | else if (matches.Count == 1) 46 | { 47 | //resolve the exact match 48 | textContext.Done(matches[0]); 49 | } 50 | else 51 | { 52 | //multiple matches...give choice 53 | PromptDialog.Choice(textContext, async (IDialogContext choiceContext, IAwaitable choiceResult) => 54 | { 55 | var selection = await choiceResult; 56 | choiceContext.Done(selection); 57 | }, matches, "Multiple matches found...which user would you like to explore?"); 58 | } 59 | }, "What group are you interested in (you can lookup by full/partial name or alias)?"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Models/QueryOperation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace MicrosoftGraphBot.Models 9 | { 10 | [Serializable] 11 | public class QueryOperation 12 | { 13 | public string Text { get; set; } 14 | public OperationType Type { get; set; } 15 | public string Endpoint { get; set; } 16 | public object ContextObject { get; set; } 17 | 18 | public override string ToString() 19 | { 20 | return Text; 21 | } 22 | 23 | public T GetContextObjectAs() where T : class 24 | { 25 | if (ContextObject is T) 26 | { 27 | return (T)ContextObject; 28 | } 29 | if (ContextObject is JObject) 30 | { 31 | return ((JObject)ContextObject).ToObject(); 32 | } 33 | return null; 34 | } 35 | 36 | public static List GetEntityResourceTypes(EntityType entityType) 37 | { 38 | if (entityType == EntityType.Me) 39 | return meResourceTypes.ToQueryOperations(); 40 | else if (entityType == EntityType.User) 41 | return userResourceTypes.ToQueryOperations(); 42 | else if (entityType == EntityType.Group) 43 | return groupResourceTypes.ToQueryOperations(); 44 | else 45 | return null; 46 | } 47 | 48 | private static List meResourceTypes = new List() { 49 | OperationType.Manager, 50 | OperationType.DirectReports, 51 | OperationType.Photo, 52 | OperationType.Files, 53 | OperationType.Mail, 54 | OperationType.Events, 55 | OperationType.Contacts, 56 | OperationType.Groups, 57 | OperationType.WorkingWith, 58 | OperationType.TrendingAround, 59 | OperationType.People, 60 | OperationType.Notebooks, 61 | OperationType.Tasks, 62 | OperationType.Plans 63 | }; 64 | private static List userResourceTypes = new List() { 65 | OperationType.Manager, 66 | OperationType.DirectReports, 67 | OperationType.Photo, 68 | OperationType.Files, 69 | OperationType.Groups, 70 | OperationType.WorkingWith, 71 | OperationType.TrendingAround, 72 | OperationType.People, 73 | OperationType.Notebooks, 74 | OperationType.Tasks, 75 | OperationType.Plans 76 | }; 77 | private static List groupResourceTypes = new List() { 78 | OperationType.Members, 79 | OperationType.Files, 80 | OperationType.Conversations, 81 | OperationType.Events, 82 | OperationType.Photo, 83 | OperationType.Notebooks 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/ResourceTypes/DirectReportsDialog.cs: -------------------------------------------------------------------------------- 1 | using MicrosoftGraphBot; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Connector; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Newtonsoft.Json.Linq; 11 | 12 | namespace MicrosoftGraphBot.Dialog.ResourceTypes 13 | { 14 | public class DirectReportsDialog : IDialog 15 | { 16 | /// 17 | /// Called to start a dialog 18 | /// 19 | /// IDialogContext 20 | /// 21 | public async Task StartAsync(IDialogContext context) 22 | { 23 | context.Wait(MessageReceivedAsync); 24 | } 25 | 26 | /// 27 | /// Processes messages received on new thread 28 | /// 29 | /// IDialogContext 30 | /// Awaitable IMessageActivity 31 | /// Task 32 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 33 | { 34 | var entity = context.ConversationData.GetDialogEntity(); 35 | var directReports = await getDirectReports(context); 36 | 37 | //build a list of valid operations the user can take 38 | List operations = new List(); 39 | foreach (var report in directReports) 40 | operations.Add(new Models.QueryOperation() { Text = report.text, Type = Models.OperationType.ChangeDialogEntity, ContextObject = report }); 41 | operations.Add(new Models.QueryOperation() { Text = String.Format("(Other {0} queries)", entity.ToString()), Type = Models.OperationType.ShowOperations }); 42 | operations.Add(new Models.QueryOperation() { Text = "(Start over)", Type = Models.OperationType.StartOver }); 43 | 44 | //prepare the message 45 | var msg = String.Format("{0} does not have direct reports lists. What would you like to do next:", entity.text); 46 | if (directReports.Count == 0) 47 | msg = String.Format("{0} has the following direct reports. What would you like to do next:", entity.text); 48 | 49 | //Allow the user to select next path 50 | PromptDialog.Choice(context, async (IDialogContext choiceContext, IAwaitable choiceResult) => 51 | { 52 | var option = await choiceResult; 53 | 54 | switch (option.Type) 55 | { 56 | case Models.OperationType.ChangeDialogEntity: 57 | var user = (Models.User)option.ContextObject; 58 | var eType = (user.IsMe(choiceContext)) ? Models.EntityType.Me : Models.EntityType.User; 59 | choiceContext.ConversationData.SaveDialogEntity(new Models.BaseEntity(user, eType)); 60 | choiceContext.Done(false); //return to parent WITHOUT start over 61 | break; 62 | case Models.OperationType.ShowOperations: 63 | choiceContext.Done(false); //return to parent WITHOUT start over 64 | break; 65 | case Models.OperationType.StartOver: 66 | choiceContext.Done(true); //return to parent WITH start over 67 | break; 68 | } 69 | }, operations, msg); 70 | } 71 | 72 | private async Task> getDirectReports(IDialogContext context) 73 | { 74 | //Get the manager for the DialogEntity 75 | var entity = context.ConversationData.GetDialogEntity(); 76 | HttpClient client = new HttpClient(); 77 | var token = await context.GetAccessToken(); 78 | var results = await client.MSGraphGET(token, String.Format("https://graph.microsoft.com/v1.0/users/{0}/directReports", entity.id)); 79 | return ((JArray)results["value"]).ToUserList(); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 32 | 33 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/EntityLookupDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using Microsoft.Bot.Builder.Dialogs; 7 | using Microsoft.Bot.Connector; 8 | using MicrosoftGraphBot.Models; 9 | using Newtonsoft.Json.Linq; 10 | 11 | namespace MicrosoftGraphBot.Dialog 12 | { 13 | [Serializable] 14 | public abstract class EntityLookupDialog : IDialog 15 | { 16 | private List _entities; 17 | 18 | public abstract string LookupPrompt { get; } 19 | 20 | public abstract string NoLookupPrompt { get; } 21 | 22 | public abstract string NoChoicesPrompt { get; } 23 | 24 | public abstract string MultipleChoicesPrompt { get; } 25 | 26 | public abstract string GetRequestUri(IDialogContext dialogContext); 27 | 28 | public abstract List DeserializeArray(JArray array); 29 | 30 | public abstract bool FilterEntity(T entity, string query); 31 | 32 | #pragma warning disable 1998 33 | public async Task StartAsync(IDialogContext context) 34 | #pragma warning restore 1998 35 | { 36 | context.Wait(ShowTextDialogAsync); 37 | } 38 | 39 | #pragma warning disable 1998 40 | public async Task ShowTextDialogAsync(IDialogContext context, IAwaitable item) 41 | #pragma warning restore 1998 42 | { 43 | // Get entities. 44 | var accessToken = await context.GetAccessToken(); 45 | _entities = await GetEntitesAsync(context, accessToken); 46 | 47 | // No matches, retry. 48 | if (_entities.Count == 0) 49 | { 50 | Retry(context); 51 | } 52 | else 53 | { 54 | // If the entities are below five, let the user pick 55 | // one right away. If not, let the user do a lookup. 56 | if (_entities.Count <= 5) 57 | { 58 | ShowChoices(context, _entities, true); 59 | } 60 | else 61 | { 62 | PromptDialog.Text(context, OnTextDialogResumeAsync, LookupPrompt); 63 | } 64 | } 65 | } 66 | 67 | public async Task> GetEntitesAsync(IDialogContext context, string accessToken) 68 | { 69 | // Create HTTP Client and get the response. 70 | var httpClient = new HttpClient(); 71 | var requestUri = GetRequestUri(context); 72 | var json = await httpClient.MSGraphGET(accessToken, requestUri); 73 | 74 | // Deserialize the response. 75 | var response = DeserializeArray((JArray)json["value"]); 76 | return response; 77 | } 78 | 79 | private async Task OnTextDialogResumeAsync(IDialogContext context, IAwaitable result) 80 | { 81 | // Filter the entities.. 82 | var query = (await result).ToLower(); 83 | var matches = _entities.Where(e => FilterEntity(e, query)) 84 | .Take(5) 85 | .ToList(); 86 | 87 | // Check the number of matches. 88 | switch (matches.Count) 89 | { 90 | case 0: 91 | // No matches, retry. 92 | Retry(context); 93 | break; 94 | case 1: 95 | // Resolve the exact match. 96 | context.Done(matches[0]); 97 | break; 98 | default: 99 | ShowChoices(context, matches, false); 100 | break; 101 | } 102 | } 103 | 104 | private void Retry(IDialogContext context) 105 | { 106 | PromptDialog.Confirm(context, async (retryContext, retryResult) => 107 | { 108 | // Check retry result. 109 | var retry = await retryResult; 110 | if (retry) 111 | { 112 | await ShowTextDialogAsync(retryContext, null); 113 | } 114 | else 115 | { 116 | retryContext.Done(null); 117 | } 118 | }, NoChoicesPrompt); 119 | } 120 | 121 | private void ShowChoices(IDialogContext context, IEnumerable choices, bool firstPrompt) 122 | { 123 | PromptDialog.Choice(context, async (choiceContext, choiceResult) => 124 | { 125 | var selection = await choiceResult; 126 | choiceContext.Done(selection); 127 | }, choices, firstPrompt ? NoLookupPrompt : MultipleChoicesPrompt); 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/ResourceTypes/MembersDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace MicrosoftGraphBot.Dialog.ResourceTypes 12 | { 13 | [Serializable] 14 | public class MembersDialog : IDialog 15 | { 16 | /// 17 | /// Called to start a dialog 18 | /// 19 | /// IDialogContext 20 | /// 21 | public async Task StartAsync(IDialogContext context) 22 | { 23 | context.Wait(MessageReceivedAsync); 24 | } 25 | 26 | /// 27 | /// Processes messages received on new thread 28 | /// 29 | /// IDialogContext 30 | /// Awaitable IMessageActivity 31 | /// Task 32 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 33 | { 34 | var entity = context.ConversationData.GetDialogEntity(); 35 | await processMembers(context, String.Format("https://graph.microsoft.com/v1.0/groups/{0}/members?$top=5", entity.id)); 36 | 37 | } 38 | 39 | private async Task processMembers(IDialogContext context, string endpoint) 40 | { 41 | //save the current endpoint and retrieve the Dialog Entity 42 | context.SaveNavCurrent(endpoint); 43 | var entity = context.ConversationData.GetDialogEntity(); 44 | var token = await context.GetAccessToken(); 45 | 46 | //get the members from the database based on provided endpoint 47 | HttpClient client = new HttpClient(); 48 | var json = await client.MSGraphGET(token, endpoint); 49 | var members = ((JArray)json["value"]).ToUserList(); 50 | 51 | //convert to operations 52 | List operations = new List(); 53 | foreach (var m in members) 54 | operations.Add(new Models.QueryOperation() { Text = m.text, Type = Models.OperationType.People, Endpoint = String.Format("https://graph.microsoft.com/v1.0/users/{0}", m.id) }); 55 | 56 | //add paging 57 | operations.InitializePaging(context, json); 58 | 59 | //add other operations and start over 60 | operations.Add(new Models.QueryOperation() { Text = String.Format("(Other {0} queries)", entity.ToString()), Type = Models.OperationType.ShowOperations }); 61 | operations.Add(new Models.QueryOperation() { Text = "(Start over)", Type = Models.OperationType.StartOver }); 62 | 63 | //prompt the next selection 64 | PromptDialog.Choice(context, async (IDialogContext choiceContext, IAwaitable choiceResult) => 65 | { 66 | var operation = await choiceResult; 67 | switch (operation.Type) 68 | { 69 | case Models.OperationType.People: 70 | //change the dialog entity and return without start over 71 | HttpClient c = new HttpClient(); 72 | var t = await choiceContext.GetAccessToken(); 73 | var j = await c.MSGraphGET(t, operation.Endpoint); 74 | var newEntity = j.ToUser(); 75 | var etype = (newEntity.IsMe(choiceContext)) ? Models.EntityType.Me : Models.EntityType.User; 76 | choiceContext.ConversationData.SaveDialogEntity(new Models.BaseEntity(newEntity, etype)); 77 | choiceContext.Done(false); //return with false to stick with same user 78 | break; 79 | case Models.OperationType.Next: 80 | choiceContext.NavPushItem(choiceContext.GetNavCurrent()); 81 | await processMembers(choiceContext, operation.Endpoint); 82 | break; 83 | case Models.OperationType.Previous: 84 | choiceContext.NavPopItem(); 85 | await processMembers(choiceContext, operation.Endpoint); 86 | break; 87 | case Models.OperationType.ShowOperations: 88 | choiceContext.Done(false); //return with false to stick with same user 89 | break; 90 | case Models.OperationType.StartOver: 91 | choiceContext.Done(true); //return with true to start over 92 | break; 93 | } 94 | }, operations, String.Format("{0} has the following members:", entity.text)); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /MicrosoftGraphBot.Tests/MicrosoftGraphBot.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {FABE0C7E-0B52-4148-A4CE-D8D3DE79876D} 7 | Library 8 | Properties 9 | MicrosoftGraphBot.Tests 10 | MicrosoftGraphBot.Tests 11 | v4.5.2 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | False 76 | 77 | 78 | False 79 | 80 | 81 | False 82 | 83 | 84 | False 85 | 86 | 87 | 88 | 89 | 90 | 91 | 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/ResourceTypes/ManagerDialog.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using MicrosoftGraphBot; 3 | using Microsoft.Bot.Builder.Dialogs; 4 | using Microsoft.Bot.Connector; 5 | using Microsoft.Identity.Client; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Net.Http; 12 | using System.Threading; 13 | 14 | namespace MicrosoftGraphBot.Dialog.ResourceTypes 15 | { 16 | [Serializable] 17 | public class ManagerDialog : IDialog 18 | { 19 | /// 20 | /// Called to start a dialog 21 | /// 22 | /// IDialogContext 23 | /// 24 | public async Task StartAsync(IDialogContext context) 25 | { 26 | context.Wait(MessageReceivedAsync); 27 | } 28 | 29 | /// 30 | /// Processes messages received on new thread 31 | /// 32 | /// IDialogContext 33 | /// Awaitable IMessageActivity 34 | /// Task 35 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 36 | { 37 | var entity = context.ConversationData.GetDialogEntity(); 38 | var manager = await getManager(context); 39 | 40 | //build a list of valid operations the user can take 41 | List operations = new List(); 42 | if (manager != null) 43 | { 44 | operations.Add(new Models.QueryOperation() { Text = String.Format("{0}'s photo", manager.ToString()), Type = Models.OperationType.Photo }); 45 | operations.Add(new Models.QueryOperation() { Text = String.Format("{0}'s manager", manager.ToString()), Type = Models.OperationType.Manager }); 46 | operations.Add(new Models.QueryOperation() { Text = String.Format("{0}'s direct reports", manager.ToString()), Type = Models.OperationType.DirectReports }); 47 | operations.Add(new Models.QueryOperation() { Text = String.Format("(Other {0} queries)", manager.ToString()), Type = Models.OperationType.ChangeDialogEntity }); 48 | } 49 | operations.Add(new Models.QueryOperation() { Text = String.Format("(Other {0} queries)", entity.ToString()), Type = Models.OperationType.ShowOperations }); 50 | operations.Add(new Models.QueryOperation() { Text = "(Start over)", Type = Models.OperationType.StartOver }); 51 | 52 | //prepare the message 53 | string msg = String.Format("{0} doesn't have a manager listed. What would you like to do next:", entity.text); 54 | if (manager != null) 55 | msg = String.Format("{0}'s manager is {1}. What would you like to do next:", entity.text, manager.ToString()); 56 | 57 | //Allow the user to select next path 58 | PromptDialog.Choice(context, async (IDialogContext choiceContext, IAwaitable choiceResult) => 59 | { 60 | var option = await choiceResult; 61 | 62 | switch (option.Type) 63 | { 64 | case Models.OperationType.Manager: 65 | case Models.OperationType.DirectReports: 66 | case Models.OperationType.ChangeDialogEntity: 67 | //change the dialog entity to the manager 68 | var newEntity = await getManager(choiceContext); 69 | var eType = (newEntity.IsMe(choiceContext)) ? Models.EntityType.Me : Models.EntityType.User; 70 | choiceContext.ConversationData.SaveDialogEntity(new Models.BaseEntity(newEntity, eType)); 71 | 72 | //proceed based on selection 73 | if (option.Type == Models.OperationType.DirectReports) 74 | //forward to the PhotoDialog 75 | await choiceContext.Forward(new PhotoDialog(), async (IDialogContext drContext, IAwaitable drResult) => 76 | { 77 | var startOver = await drResult; 78 | drContext.Done(startOver); //return to parent based on child start over value 79 | }, true, CancellationToken.None); 80 | else if (option.Type == Models.OperationType.Manager) 81 | await MessageReceivedAsync(choiceContext, null); //call back into this dialog to go up a level 82 | else if (option.Type == Models.OperationType.DirectReports) 83 | //forward to the DirectReportsDialog 84 | await choiceContext.Forward(new DirectReportsDialog(), async (IDialogContext drContext, IAwaitable drResult) => 85 | { 86 | var startOver = await drResult; 87 | drContext.Done(startOver); //return to parent based on child start over value 88 | }, true, CancellationToken.None); 89 | else if (option.Type == Models.OperationType.ChangeDialogEntity) 90 | choiceContext.Done(false); //return to partent WITHOUT start over 91 | break; 92 | case Models.OperationType.ShowOperations: 93 | choiceContext.Done(false); //return to parent WITHOUT start over 94 | break; 95 | case Models.OperationType.StartOver: 96 | choiceContext.Done(true); //return to parent WITH start over 97 | break; 98 | } 99 | }, operations, msg); 100 | } 101 | 102 | /// 103 | /// Performs the MS Graph query for a manager 104 | /// 105 | /// IDialogContext 106 | /// User 107 | private async Task getManager(IDialogContext context) 108 | { 109 | //Get the manager for the DialogEntity 110 | var entity = context.ConversationData.GetDialogEntity(); 111 | HttpClient client = new HttpClient(); 112 | var token = await context.GetAccessToken(); 113 | var results = await client.MSGraphGET(token, String.Format("https://graph.microsoft.com/v1.0/users/{0}/manager", entity.id)); 114 | return results.ToUser(); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/GraphDialog.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Connector; 4 | using MicrosoftGraphBot.Models; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using MicrosoftGraphBot.Dialog.ResourceTypes; 12 | 13 | namespace MicrosoftGraphBot.Dialog 14 | { 15 | [Serializable] 16 | public class GraphDialog : IDialog 17 | { 18 | /// 19 | /// Called to start a dialog 20 | /// 21 | /// IDialogContext 22 | /// 23 | public async Task StartAsync(IDialogContext context) 24 | { 25 | context.Wait(MessageReceivedAsync); 26 | } 27 | 28 | /// 29 | /// Processes messages received on new thread 30 | /// 31 | /// IDialogContext 32 | /// Awaitable IMessageActivity 33 | /// Task 34 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 35 | { 36 | //start by having the user select the entity type to query 37 | await context.Initialize(); 38 | PromptDialog.Choice(context, this.EntityTypeSelected, Enum.GetNames(typeof(EntityType)).Where(e => e != EntityType.Plan.ToString()), "Where do you want to start exploring?"); 39 | } 40 | 41 | /// 42 | /// Resume from user selecting the type of entity they want to query 43 | /// 44 | /// IDialogContext 45 | /// Awaitable string 46 | /// Task 47 | public async Task EntityTypeSelected(IDialogContext context, IAwaitable item) 48 | { 49 | EntityType intent = (EntityType)Enum.Parse(typeof(EntityType), await item); 50 | switch (intent) 51 | { 52 | case EntityType.Me: 53 | //get me 54 | var me = context.ConversationData.Me(); 55 | 56 | //save the entity and show available operations 57 | context.ConversationData.SaveDialogEntity(new BaseEntity(me, EntityType.Me)); 58 | await routeOperation(context); 59 | break; 60 | case EntityType.User: 61 | //prompt for user 62 | await context.Forward(new UserLookupDialog(), async (IDialogContext lookupContext, IAwaitable lookupResult) => 63 | { 64 | var user = await lookupResult; 65 | 66 | //save the entity and show available operations 67 | lookupContext.ConversationData.SaveDialogEntity(new BaseEntity(user, EntityType.User)); 68 | await routeOperation(lookupContext); 69 | }, new User(), CancellationToken.None); 70 | break; 71 | case EntityType.Group: 72 | //prompt for group 73 | await context.Forward(new GroupLookupDialog(), async (IDialogContext lookupContext, IAwaitable lookupResult) => 74 | { 75 | var group = await lookupResult; 76 | 77 | //save the entity and show available operations 78 | lookupContext.ConversationData.SaveDialogEntity(new BaseEntity(group)); 79 | await routeOperation(lookupContext); 80 | }, new Group(), CancellationToken.None); 81 | break; 82 | } 83 | } 84 | 85 | /// 86 | /// Prompts the user to chose an operation and routes it to the appropriate dialog 87 | /// 88 | /// IDialogContext 89 | /// Task 90 | private async Task routeOperation(IDialogContext context) 91 | { 92 | //initialize a new operation on the context 93 | context.NewOperation(); 94 | 95 | //check the entity type to determine the valid operations 96 | var entity = context.ConversationData.GetDialogEntity(); 97 | List operations = QueryOperation.GetEntityResourceTypes(entity.entityType); 98 | 99 | //prepare the prompt 100 | string prompt = "What would like to lookup for you?"; 101 | if (entity.entityType != EntityType.Me) 102 | prompt = String.Format("What would like to lookup for {0}?", entity.text); 103 | 104 | //add start over 105 | operations.Add(new Models.QueryOperation() { Text = "(Start over)", Type = Models.OperationType.StartOver }); 106 | 107 | //let the user select an operation 108 | PromptDialog.Choice(context, async (IDialogContext opContext, IAwaitable opResult) => 109 | { 110 | //check the operation selected and route appropriately 111 | var operation = await opResult; 112 | switch (operation.Type) 113 | { 114 | case OperationType.StartOver: 115 | await this.MessageReceivedAsync(opContext, null); 116 | break; 117 | case OperationType.Manager: 118 | await opContext.Forward(new ResourceTypes.ManagerDialog(), OperationComplete, true, CancellationToken.None); 119 | break; 120 | case OperationType.DirectReports: 121 | await opContext.Forward(new ResourceTypes.DirectReportsDialog(), OperationComplete, true, CancellationToken.None); 122 | break; 123 | case OperationType.Files: 124 | await opContext.Forward(new ResourceTypes.FilesDialog(), OperationComplete, true, CancellationToken.None); 125 | break; 126 | case OperationType.Members: 127 | await opContext.Forward(new ResourceTypes.MembersDialog(), OperationComplete, true, CancellationToken.None); 128 | break; 129 | case OperationType.Contacts: 130 | case OperationType.Conversations: 131 | case OperationType.Events: 132 | case OperationType.Groups: 133 | case OperationType.Mail: 134 | case OperationType.Notebooks: 135 | case OperationType.People: 136 | case OperationType.Photo: 137 | case OperationType.Plans: 138 | await opContext.Forward(new PlanLookupDialog(), OnPlanLookupDialogResumeAsync, new Plan(), CancellationToken.None); 139 | break; 140 | case OperationType.Tasks: 141 | await opContext.Forward(new TasksDialog(), OperationComplete, true, CancellationToken.None); 142 | break; 143 | case OperationType.TrendingAround: 144 | case OperationType.WorkingWith: 145 | await opContext.PostAsync("Operation not yet implemented"); 146 | opContext.Wait(MessageReceivedAsync); 147 | break; 148 | } 149 | 150 | }, operations, prompt); 151 | } 152 | 153 | private async Task OnPlanLookupDialogResumeAsync(IDialogContext context, IAwaitable result) 154 | { 155 | var plan = await result; 156 | 157 | context.ConversationData.SaveDialogEntity(new BaseEntity(plan)); 158 | await context.Forward(new TasksDialog(), OperationComplete, true, CancellationToken.None); 159 | } 160 | 161 | /// 162 | /// The resume from performing a Graph operation 163 | /// Allows the user to start over or select a different operation 164 | /// 165 | /// IDialogContext 166 | /// Awaitable bool indicating if start over 167 | /// Task 168 | public async Task OperationComplete(IDialogContext context, IAwaitable result) 169 | { 170 | var startOver = await result; 171 | if (startOver) 172 | await this.MessageReceivedAsync(context, null); 173 | else 174 | await this.routeOperation(context); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Resource.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Contacts 122 | 123 | 124 | /contacts 125 | 126 | 127 | Conversations 128 | 129 | 130 | /conversations 131 | 132 | 133 | Direct Reports 134 | 135 | 136 | /directReports 137 | 138 | 139 | Events 140 | 141 | 142 | /events 143 | 144 | 145 | Files 146 | 147 | 148 | /drive/root/children 149 | 150 | 151 | Groups 152 | 153 | 154 | /memberOf 155 | 156 | 157 | Mail 158 | 159 | 160 | /messages 161 | 162 | 163 | Manager 164 | 165 | 166 | /manager 167 | 168 | 169 | Group Members 170 | 171 | 172 | /members 173 | 174 | 175 | Notebooks 176 | 177 | 178 | /notes/notebooks 179 | 180 | 181 | Works With (w/Groups) 182 | 183 | 184 | /people 185 | 186 | 187 | Photo 188 | 189 | 190 | /photo/$value 191 | 192 | 193 | Plans 194 | 195 | 196 | /plans 197 | 198 | 199 | Tasks 200 | 201 | 202 | /tasks 203 | 204 | 205 | Tending Around 206 | 207 | 208 | /trendingAround 209 | 210 | 211 | Works With 212 | 213 | 214 | /workingWith 215 | 216 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Resource.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MicrosoftGraphBot { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resource { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resource() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MicrosoftGraphBot.Resource", typeof(Resource).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Contacts. 65 | /// 66 | public static string Contacts { 67 | get { 68 | return ResourceManager.GetString("Contacts", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to /contacts. 74 | /// 75 | public static string Contacts_Endpoint { 76 | get { 77 | return ResourceManager.GetString("Contacts_Endpoint", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Conversations. 83 | /// 84 | public static string Conversations { 85 | get { 86 | return ResourceManager.GetString("Conversations", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to /conversations. 92 | /// 93 | public static string Conversations_Endpoint { 94 | get { 95 | return ResourceManager.GetString("Conversations_Endpoint", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to Direct Reports. 101 | /// 102 | public static string DirectReports { 103 | get { 104 | return ResourceManager.GetString("DirectReports", resourceCulture); 105 | } 106 | } 107 | 108 | /// 109 | /// Looks up a localized string similar to /directReports. 110 | /// 111 | public static string DirectReports_Endpoint { 112 | get { 113 | return ResourceManager.GetString("DirectReports_Endpoint", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks up a localized string similar to Events. 119 | /// 120 | public static string Events { 121 | get { 122 | return ResourceManager.GetString("Events", resourceCulture); 123 | } 124 | } 125 | 126 | /// 127 | /// Looks up a localized string similar to /events. 128 | /// 129 | public static string Events_Endpoint { 130 | get { 131 | return ResourceManager.GetString("Events_Endpoint", resourceCulture); 132 | } 133 | } 134 | 135 | /// 136 | /// Looks up a localized string similar to Files. 137 | /// 138 | public static string Files { 139 | get { 140 | return ResourceManager.GetString("Files", resourceCulture); 141 | } 142 | } 143 | 144 | /// 145 | /// Looks up a localized string similar to /drive/root/children. 146 | /// 147 | public static string Files_Endpoint { 148 | get { 149 | return ResourceManager.GetString("Files_Endpoint", resourceCulture); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized string similar to Groups. 155 | /// 156 | public static string Groups { 157 | get { 158 | return ResourceManager.GetString("Groups", resourceCulture); 159 | } 160 | } 161 | 162 | /// 163 | /// Looks up a localized string similar to /memberOf. 164 | /// 165 | public static string Groups_Endpoint { 166 | get { 167 | return ResourceManager.GetString("Groups_Endpoint", resourceCulture); 168 | } 169 | } 170 | 171 | /// 172 | /// Looks up a localized string similar to Mail. 173 | /// 174 | public static string Mail { 175 | get { 176 | return ResourceManager.GetString("Mail", resourceCulture); 177 | } 178 | } 179 | 180 | /// 181 | /// Looks up a localized string similar to /messages. 182 | /// 183 | public static string Mail_Endpoint { 184 | get { 185 | return ResourceManager.GetString("Mail_Endpoint", resourceCulture); 186 | } 187 | } 188 | 189 | /// 190 | /// Looks up a localized string similar to Manager. 191 | /// 192 | public static string Manager { 193 | get { 194 | return ResourceManager.GetString("Manager", resourceCulture); 195 | } 196 | } 197 | 198 | /// 199 | /// Looks up a localized string similar to /manager. 200 | /// 201 | public static string Manager_Endpoint { 202 | get { 203 | return ResourceManager.GetString("Manager_Endpoint", resourceCulture); 204 | } 205 | } 206 | 207 | /// 208 | /// Looks up a localized string similar to Group Members. 209 | /// 210 | public static string Members { 211 | get { 212 | return ResourceManager.GetString("Members", resourceCulture); 213 | } 214 | } 215 | 216 | /// 217 | /// Looks up a localized string similar to /members. 218 | /// 219 | public static string Members_Endpoint { 220 | get { 221 | return ResourceManager.GetString("Members_Endpoint", resourceCulture); 222 | } 223 | } 224 | 225 | /// 226 | /// Looks up a localized string similar to Notebooks. 227 | /// 228 | public static string Notebooks { 229 | get { 230 | return ResourceManager.GetString("Notebooks", resourceCulture); 231 | } 232 | } 233 | 234 | /// 235 | /// Looks up a localized string similar to /notes/notebooks. 236 | /// 237 | public static string Notebooks_Endpoint { 238 | get { 239 | return ResourceManager.GetString("Notebooks_Endpoint", resourceCulture); 240 | } 241 | } 242 | 243 | /// 244 | /// Looks up a localized string similar to Works With (w/Groups). 245 | /// 246 | public static string People { 247 | get { 248 | return ResourceManager.GetString("People", resourceCulture); 249 | } 250 | } 251 | 252 | /// 253 | /// Looks up a localized string similar to /people. 254 | /// 255 | public static string People_Endpoint { 256 | get { 257 | return ResourceManager.GetString("People_Endpoint", resourceCulture); 258 | } 259 | } 260 | 261 | /// 262 | /// Looks up a localized string similar to Photo. 263 | /// 264 | public static string Photo { 265 | get { 266 | return ResourceManager.GetString("Photo", resourceCulture); 267 | } 268 | } 269 | 270 | /// 271 | /// Looks up a localized string similar to /photo/$value. 272 | /// 273 | public static string Photo_Endpoint { 274 | get { 275 | return ResourceManager.GetString("Photo_Endpoint", resourceCulture); 276 | } 277 | } 278 | 279 | /// 280 | /// Looks up a localized string similar to Plans. 281 | /// 282 | public static string Plans { 283 | get { 284 | return ResourceManager.GetString("Plans", resourceCulture); 285 | } 286 | } 287 | 288 | /// 289 | /// Looks up a localized string similar to /plans. 290 | /// 291 | public static string Plans_Endpoint { 292 | get { 293 | return ResourceManager.GetString("Plans_Endpoint", resourceCulture); 294 | } 295 | } 296 | 297 | /// 298 | /// Looks up a localized string similar to Tasks. 299 | /// 300 | public static string Tasks { 301 | get { 302 | return ResourceManager.GetString("Tasks", resourceCulture); 303 | } 304 | } 305 | 306 | /// 307 | /// Looks up a localized string similar to /tasks. 308 | /// 309 | public static string Tasks_Endpoint { 310 | get { 311 | return ResourceManager.GetString("Tasks_Endpoint", resourceCulture); 312 | } 313 | } 314 | 315 | /// 316 | /// Looks up a localized string similar to Tending Around. 317 | /// 318 | public static string TrendingAround { 319 | get { 320 | return ResourceManager.GetString("TrendingAround", resourceCulture); 321 | } 322 | } 323 | 324 | /// 325 | /// Looks up a localized string similar to /trendingAround. 326 | /// 327 | public static string TrendingAround_Endpoint { 328 | get { 329 | return ResourceManager.GetString("TrendingAround_Endpoint", resourceCulture); 330 | } 331 | } 332 | 333 | /// 334 | /// Looks up a localized string similar to Works With. 335 | /// 336 | public static string WorkingWith { 337 | get { 338 | return ResourceManager.GetString("WorkingWith", resourceCulture); 339 | } 340 | } 341 | 342 | /// 343 | /// Looks up a localized string similar to /workingWith. 344 | /// 345 | public static string WorkingWith_Endpoint { 346 | get { 347 | return ResourceManager.GetString("WorkingWith_Endpoint", resourceCulture); 348 | } 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/ResourceTypes/FilesDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Net.Http.Headers; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace MicrosoftGraphBot.Dialog.ResourceTypes 15 | { 16 | [Serializable] 17 | public class FilesDialog : IDialog 18 | { 19 | /// 20 | /// Called to start a dialog 21 | /// 22 | /// IDialogContext 23 | /// 24 | public async Task StartAsync(IDialogContext context) 25 | { 26 | context.Wait(MessageReceivedAsync); 27 | } 28 | 29 | /// 30 | /// Processes messages received on new thread 31 | /// 32 | /// IDialogContext 33 | /// Awaitable IMessageActivity 34 | /// Task 35 | public async Task MessageReceivedAsync(IDialogContext context, IAwaitable item) 36 | { 37 | var entity = context.ConversationData.GetDialogEntity(); 38 | await processFiles(context, String.Format("https://graph.microsoft.com/v1.0/users/{0}/drive/root/children?$top=5", entity.id)); 39 | } 40 | 41 | /// 42 | /// Processes a OneDrive container (root or folder) 43 | /// 44 | /// IDialogContext 45 | /// endpoint to query 46 | /// Task 47 | private async Task processFiles(IDialogContext context, string endpoint) 48 | { 49 | //save the current endpoint and retrieve the Dialog Entity 50 | context.SaveNavCurrent(endpoint); 51 | var entity = context.ConversationData.GetDialogEntity(); 52 | 53 | //perform the http request 54 | HttpClient client = new HttpClient(); 55 | var token = await context.GetAccessToken(); 56 | JObject json = await client.MSGraphGET(token, endpoint); 57 | var files = ((JArray)json["value"]).ToFileList(); 58 | 59 | //build a list of valid operations the user can take 60 | List operations = new List(); 61 | foreach (var file in files) 62 | { 63 | //check if file or folder 64 | if (file.itemType == Models.ItemType.Folder) 65 | { 66 | var folderEndpoint = String.Format("https://graph.microsoft.com/v1.0/users/{0}/drive/items/{1}/children?$top=5", entity.id, file.id); 67 | operations.Add(new Models.QueryOperation() { Text = file.ToString(), Type = Models.OperationType.Folder, Endpoint = folderEndpoint }); 68 | } 69 | else 70 | operations.Add(new Models.QueryOperation() { Text = file.ToString(), Type = Models.OperationType.Files, Endpoint = file.navEndpoint }); 71 | } 72 | 73 | //allow users to upload into their own OneDrive 74 | if (entity.entityType == Models.EntityType.Me) 75 | operations.Add(new Models.QueryOperation() { Text = "(Upload)", Type = Models.OperationType.Upload, Endpoint = endpoint.Substring(0, endpoint.IndexOf("/children")) }); 76 | 77 | //add paging for up, next, prev 78 | operations.InitializePaging(context, json); 79 | 80 | //add other operations and start over 81 | operations.Add(new Models.QueryOperation() { Text = String.Format("(Other {0} queries)", entity.ToString()), Type = Models.OperationType.ShowOperations }); 82 | operations.Add(new Models.QueryOperation() { Text = "(Start over)", Type = Models.OperationType.StartOver }); 83 | 84 | //Allow the user to select next path 85 | PromptDialog.Choice(context, async (IDialogContext choiceContext, IAwaitable choiceResult) => 86 | { 87 | //CONTEXT SWITCH TO choiceContext 88 | var operation = await choiceResult; 89 | 90 | switch (operation.Type) 91 | { 92 | case Models.OperationType.Files: 93 | //save the operation for recursive call 94 | choiceContext.ConversationData.SetValue("FileOperation", operation); 95 | 96 | //the user selected a file...go to it in navstack 97 | choiceContext.NavPushItem(choiceContext.GetNavCurrent()); 98 | choiceContext.NavPushLevel(); //push level to children 99 | 100 | //handle the selection 101 | await processFileSelection(choiceContext, operation); 102 | break; 103 | case Models.OperationType.Folder: 104 | choiceContext.NavPushItem(choiceContext.GetNavCurrent()); 105 | choiceContext.NavPushLevel(); //push level to children 106 | await processFiles(choiceContext, operation.Endpoint); 107 | break; 108 | case Models.OperationType.Upload: 109 | //set upload context 110 | choiceContext.ConversationData.SetValue("UploadContext", operation.Endpoint); 111 | PromptDialog.Attachment(choiceContext, async (IDialogContext attachmentContext, IAwaitable> attachmentResult) => 112 | { 113 | //CONTEXT SWITCH TO attachmentsContext 114 | var attachments = await attachmentResult; 115 | 116 | //process attachements 117 | var uploadToken = await attachmentContext.GetAccessToken(); 118 | var uploadEndpoint = attachmentContext.ConversationData.Get("UploadContext"); 119 | foreach (var attachment in attachments) 120 | { 121 | //parse the filename 122 | var filename = attachment.ContentUrl.Substring(attachment.ContentUrl.LastIndexOf("%5c") + 3); 123 | 124 | //perform the upload and give status along the way 125 | await attachmentContext.PostAsync(String.Format("Uploading {0}...", filename)); 126 | var success = await upload(uploadToken, String.Format("{0}:/{1}:/content", uploadEndpoint, filename), attachment.ContentUrl); 127 | if (success) 128 | await attachmentContext.PostAsync(String.Format("{0} uploaded!", filename)); 129 | else 130 | await attachmentContext.PostAsync(String.Format("{0} upload failed!", filename)); 131 | } 132 | 133 | //re-run the current query 134 | await processFiles(attachmentContext, attachmentContext.GetNavCurrent()); 135 | }, "Please select file(s) to upload."); 136 | break; 137 | case Models.OperationType.Up: 138 | //navigating up to parent...pop the level and then pop the last query on the parent 139 | choiceContext.NavPopLevel(); //pop level to parent 140 | await processFiles(choiceContext, choiceContext.NavPopItem()); 141 | break; 142 | case Models.OperationType.Next: 143 | choiceContext.NavPushItem(choiceContext.GetNavCurrent()); 144 | await processFiles(choiceContext, operation.Endpoint); 145 | break; 146 | case Models.OperationType.Previous: 147 | choiceContext.NavPopItem(); 148 | await processFiles(choiceContext, operation.Endpoint); 149 | break; 150 | case Models.OperationType.ShowOperations: 151 | choiceContext.Done(false); //return to parent WITHOUT start over 152 | break; 153 | case Models.OperationType.StartOver: 154 | choiceContext.Done(true); //return to parent WITH start over 155 | break; 156 | } 157 | }, operations, "What would you like to see next?"); 158 | } 159 | 160 | /// 161 | /// processes the selection of a file in a OneDrive container 162 | /// 163 | /// IDialogContext 164 | /// QueryOperation 165 | /// Task 166 | private async Task processFileSelection(IDialogContext context, Models.QueryOperation operation) 167 | { 168 | var token = await context.GetAccessToken(); 169 | var entity = context.ConversationData.GetDialogEntity(); 170 | HttpClient client = new HttpClient(); 171 | var file = (await client.MSGraphGET(token, String.Format("https://graph.microsoft.com/v1.0/users/{0}{1}", entity.id, operation.Endpoint))).ToFile(); 172 | 173 | //display the file and show new options 174 | var fileOperations = new List(); 175 | fileOperations.Add(new Models.QueryOperation() { Text = "(Delete)", Type = Models.OperationType.Delete, Endpoint = operation.Endpoint }); 176 | fileOperations.Add(new Models.QueryOperation() { Text = "(Download)", Type = Models.OperationType.Download, Endpoint = file.webUrl }); 177 | 178 | //add paging for up (empty JObject will leave off next/prev 179 | fileOperations.InitializePaging(context, new JObject()); 180 | 181 | //add other operations and start over 182 | fileOperations.Add(new Models.QueryOperation() { Text = String.Format("(Other {0} queries)", entity.ToString()), Type = Models.OperationType.ShowOperations }); 183 | fileOperations.Add(new Models.QueryOperation() { Text = "(Start over)", Type = Models.OperationType.StartOver }); 184 | 185 | //let the user choose what is next 186 | PromptDialog.Choice(context, async (IDialogContext fileContext, IAwaitable fileResult) => 187 | { 188 | //CONTEXT SWITCH TO fileContext 189 | var subOperation = await fileResult; 190 | switch (subOperation.Type) 191 | { 192 | case Models.OperationType.Delete: 193 | PromptDialog.Confirm(fileContext, async (IDialogContext confirmContext, IAwaitable confirmResult) => 194 | { 195 | //CONTEXT SWITCH TO confirmContext 196 | var confirm = await confirmResult; 197 | var fileOp = confirmContext.ConversationData.Get("FileOperation"); 198 | if (confirm) 199 | { 200 | //delete the file 201 | HttpClient deleteClient = new HttpClient(); 202 | var deleteToken = await confirmContext.GetAccessToken(); 203 | var deleteEntity = confirmContext.ConversationData.GetDialogEntity(); 204 | await confirmContext.PostAsync("Deleting file..."); 205 | var deleteSuccess = await deleteClient.MSGraphDELETE(deleteToken, String.Format("https://graph.microsoft.com/v1.0/users/{0}{1}", deleteEntity.id, fileOp.Endpoint)); 206 | if (deleteSuccess) 207 | await confirmContext.PostAsync("File deleted!"); 208 | else 209 | await confirmContext.PostAsync("Delete failed!"); 210 | 211 | //navigating up to parent...pop the level and then pop the last query on the parent 212 | confirmContext.NavPopLevel(); //pop level to parent 213 | await processFiles(confirmContext, confirmContext.NavPopItem()); 214 | } 215 | else 216 | { 217 | //show options again 218 | await processFileSelection(confirmContext, fileOp); 219 | } 220 | }, "Are you sure you want to delete the file?"); 221 | break; 222 | case Models.OperationType.Download: 223 | //display download link and then call show file options again 224 | await fileContext.PostAsync(String.Format("[{0}]({0})", subOperation.Endpoint)); 225 | await processFileSelection(fileContext, fileContext.ConversationData.Get("FileOperation")); 226 | break; 227 | case Models.OperationType.Up: 228 | //navigating up to parent...pop the level and then pop the last query on the parent 229 | fileContext.NavPopLevel(); //pop level to parent 230 | await processFiles(fileContext, fileContext.NavPopItem()); 231 | break; 232 | case Models.OperationType.ShowOperations: 233 | fileContext.Done(false); //return to parent WITHOUT start over 234 | break; 235 | case Models.OperationType.StartOver: 236 | fileContext.Done(true); //return to parent WITH start over 237 | break; 238 | } 239 | }, fileOperations, String.Format("{0} is {1} bytes. What would you like to see next?", file.text, file.size)); //TODO: show thumbnail??? 240 | } 241 | 242 | /// 243 | /// Performs an upload of a file into a OneDrive container 244 | /// 245 | /// AccessToken string 246 | /// endpoint of container to upload into 247 | /// path of the local cached file from the upload 248 | /// bool for success 249 | private async Task upload(string token, string endpoint, string path) 250 | { 251 | HttpClient client = new HttpClient(); 252 | client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); 253 | client.DefaultRequestHeaders.Add("Accept", "application/json;odata.metadata=full"); 254 | 255 | //read the file into a stream 256 | WebRequest readReq = WebRequest.Create(path); 257 | WebResponse readRes = readReq.GetResponse(); 258 | using (var stream = readRes.GetResponseStream()) 259 | { 260 | //prepare the content body 261 | var fileContent = new StreamContent(stream); 262 | fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 263 | var uploadRes = await client.PutAsync(endpoint, fileContent); 264 | return uploadRes.IsSuccessStatusCode; 265 | } 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/MicrosoftGraphBot.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | 2.0 10 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4} 11 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 12 | Library 13 | Properties 14 | MicrosoftGraphBot 15 | MicrosoftGraphBot 16 | v4.6 17 | true 18 | 44358 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | true 29 | full 30 | false 31 | bin\ 32 | DEBUG;TRACE 33 | prompt 34 | 4 35 | 36 | 37 | pdbonly 38 | true 39 | bin\ 40 | TRACE 41 | prompt 42 | 4 43 | 44 | 45 | 46 | ..\packages\AuthBot.3.0.2-alpha\lib\net40\AuthBot.dll 47 | True 48 | 49 | 50 | ..\packages\Autofac.4.0.0-rc3-309\lib\net451\Autofac.dll 51 | True 52 | 53 | 54 | ..\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll 55 | True 56 | 57 | 58 | ..\packages\Microsoft.Bot.Builder.3.0.1\lib\net46\Microsoft.Bot.Builder.dll 59 | True 60 | 61 | 62 | ..\packages\Microsoft.Bot.Builder.3.0.1\lib\net46\Microsoft.Bot.Connector.dll 63 | True 64 | 65 | 66 | 67 | ..\packages\Microsoft.Identity.Client.1.0.304142221-alpha\lib\net45\Microsoft.Identity.Client.dll 68 | True 69 | 70 | 71 | ..\packages\Microsoft.Identity.Client.1.0.304142221-alpha\lib\net45\Microsoft.Identity.Client.Platform.dll 72 | True 73 | 74 | 75 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.12.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll 76 | True 77 | 78 | 79 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.12.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll 80 | True 81 | 82 | 83 | ..\packages\Microsoft.IdentityModel.Logging.1.0.0\lib\net451\Microsoft.IdentityModel.Logging.dll 84 | True 85 | 86 | 87 | ..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.2.206221351\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll 88 | True 89 | 90 | 91 | ..\packages\Microsoft.IdentityModel.Tokens.5.0.0\lib\net451\Microsoft.IdentityModel.Tokens.dll 92 | True 93 | 94 | 95 | ..\packages\Microsoft.Rest.ClientRuntime.1.8.2\lib\net45\Microsoft.Rest.ClientRuntime.dll 96 | True 97 | 98 | 99 | ..\packages\Microsoft.Win32.Primitives.4.0.1\lib\net46\Microsoft.Win32.Primitives.dll 100 | True 101 | 102 | 103 | ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.1\lib\net40\Microsoft.WindowsAzure.Configuration.dll 104 | True 105 | 106 | 107 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 108 | True 109 | 110 | 111 | 112 | 113 | ..\packages\System.Diagnostics.DiagnosticSource.4.0.0\lib\net46\System.Diagnostics.DiagnosticSource.dll 114 | True 115 | 116 | 117 | ..\packages\System.IdentityModel.Tokens.Jwt.4.0.2.206221351\lib\net45\System.IdentityModel.Tokens.Jwt.dll 118 | True 119 | 120 | 121 | 122 | 123 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 124 | True 125 | 126 | 127 | 128 | 129 | ..\packages\System.Runtime.Serialization.Primitives.4.1.1\lib\net46\System.Runtime.Serialization.Primitives.dll 130 | True 131 | 132 | 133 | ..\packages\System.Security.Cryptography.Algorithms.4.2.0\lib\net46\System.Security.Cryptography.Algorithms.dll 134 | True 135 | 136 | 137 | ..\packages\System.Security.Cryptography.Encoding.4.0.0\lib\net46\System.Security.Cryptography.Encoding.dll 138 | True 139 | 140 | 141 | ..\packages\System.Security.Cryptography.Primitives.4.0.0\lib\net46\System.Security.Cryptography.Primitives.dll 142 | True 143 | 144 | 145 | ..\packages\System.Security.Cryptography.X509Certificates.4.1.0\lib\net46\System.Security.Cryptography.X509Certificates.dll 146 | True 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 159 | True 160 | 161 | 162 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 163 | True 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | Designer 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | Global.asax 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | True 214 | True 215 | Resource.resx 216 | 217 | 218 | 219 | 220 | Designer 221 | 222 | 223 | Web.config 224 | 225 | 226 | Web.config 227 | 228 | 229 | 230 | 231 | PublicResXFileCodeGenerator 232 | Resource.Designer.cs 233 | 234 | 235 | 236 | 237 | 10.0 238 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 239 | 240 | 241 | true 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | True 251 | True 252 | 3978 253 | / 254 | https://localhost:44300/ 255 | True 256 | https://localhost:44300/ 257 | False 258 | False 259 | 260 | 261 | False 262 | 263 | 264 | 265 | 266 | 273 | -------------------------------------------------------------------------------- /MicrosoftGraphBot/Dialog/ResourceTypes/TasksDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.Bot.Builder.Dialogs; 8 | using Microsoft.Bot.Connector; 9 | using MicrosoftGraphBot.Models; 10 | using Newtonsoft.Json.Linq; 11 | 12 | namespace MicrosoftGraphBot.Dialog.ResourceTypes 13 | { 14 | [Serializable] 15 | public class TasksDialog : IDialog 16 | { 17 | private const int PageSize = 5; 18 | 19 | public TasksDialog() 20 | { 21 | 22 | } 23 | 24 | /// 25 | /// Called to start a dialog. 26 | /// 27 | /// IDialogContext 28 | /// 29 | #pragma warning disable 1998 30 | public async Task StartAsync(IDialogContext context) 31 | #pragma warning restore 1998 32 | { 33 | context.Wait(ShowOperationsAsync); 34 | } 35 | 36 | /// 37 | /// Processes messages received on new thread. 38 | /// 39 | /// IDialogContext 40 | /// Awaitable IMessageActivity 41 | /// Task 42 | public async Task ShowOperationsAsync(IDialogContext context, 43 | IAwaitable item = null) 44 | { 45 | await ShowOperationsAsync(context, 0); 46 | } 47 | 48 | /// 49 | /// Processes messages received on new thread. 50 | /// 51 | /// IDialogContext 52 | /// int 53 | /// Task 54 | public async Task ShowOperationsAsync(IDialogContext context, int page) 55 | { 56 | // OData does currently not work well on /tasks (bug). Implementing a 57 | // custom navigation model. 58 | 59 | // Save the current page. 60 | context.ConversationData.SetValue("Page", page); 61 | 62 | // Get needed data for the HTTP request. 63 | var entity = context.ConversationData.GetDialogEntity(); 64 | var requestUrl = (entity.entityType == EntityType.Me || entity.entityType == EntityType.User) 65 | ? $"https://graph.microsoft.com/beta/users/{entity.id}/tasks" 66 | : $"https://graph.microsoft.com/beta/plans/{entity.id}/tasks"; 67 | var httpClient = new HttpClient(); 68 | var accessToken = await context.GetAccessToken(); 69 | 70 | // Perform the HTTP request. 71 | var response = await httpClient.MSGraphGET(accessToken, requestUrl); 72 | var allTasks = ((JArray)response["value"]).ToTasksList(); 73 | 74 | // Remove completed tasks. 75 | allTasks = new List(allTasks.Where(t => t.PercentComplete < 100)); 76 | 77 | // Not getting OData to work on /tasks, limiting in client instead. 78 | var tasks = new List(allTasks 79 | .OrderBy(t => t.CreatedDateTime) 80 | .Skip(page * PageSize) 81 | .Take(PageSize)); 82 | 83 | // TODO: Aggregate above filtering to single method. Current setup is for clarity. 84 | 85 | // Create tasks operations. 86 | var operations = new List(); 87 | operations.AddRange(tasks.Select(t => 88 | { 89 | // Create the text (trimmed if needed). 90 | var text = t.text.Length <= 20 91 | ? t.text 92 | : new string(t.text.Take(20).ToArray()).Trim() + "..."; 93 | return new QueryOperation 94 | { 95 | Text = text, 96 | Type = OperationType.Tasks, 97 | Endpoint = t.navEndpoint, 98 | ContextObject = t 99 | }; 100 | })); 101 | 102 | // Create new task operation. 103 | operations.Add(new QueryOperation 104 | { 105 | Text = "(Create task)", 106 | Type = OperationType.Create, 107 | Endpoint = requestUrl 108 | }); 109 | 110 | // Create previous page operation. 111 | if (page > 0) 112 | { 113 | operations.Add(new QueryOperation 114 | { 115 | Text = "(Previous page)", 116 | Type = OperationType.Previous, 117 | Endpoint = requestUrl 118 | }); 119 | } 120 | 121 | // Create next page operation. 122 | if ((page + 1) * PageSize < allTasks.Count) 123 | { 124 | operations.Add(new QueryOperation 125 | { 126 | Text = "(Next page)", 127 | Type = OperationType.Next, 128 | Endpoint = requestUrl 129 | }); 130 | } 131 | 132 | // Create other operations. 133 | var me = context.ConversationData.Me(); 134 | operations.Add(new QueryOperation 135 | { 136 | Text = $"(Other {me} queries)", 137 | Type = OperationType.ShowOperations 138 | }); 139 | 140 | // Create start over operation. 141 | operations.Add(new QueryOperation 142 | { 143 | Text = "(Start over)", 144 | Type = OperationType.StartOver 145 | }); 146 | 147 | // Allow user to select the operation. 148 | PromptDialog.Choice(context, OnOperationsChoiceDialogResume, operations, 149 | "What would you like to see next?"); 150 | } 151 | 152 | private async Task OnOperationsChoiceDialogResume(IDialogContext context, 153 | IAwaitable result) 154 | { 155 | var page = context.ConversationData.Get("Page"); 156 | 157 | // Get choice result. 158 | var operation = await result; 159 | switch (operation.Type) 160 | { 161 | case OperationType.Tasks: 162 | // Save the operation for recursive call. 163 | context.ConversationData.SetValue("TaskOperation", operation); 164 | 165 | // Handle the selection. 166 | await ShowTaskOperationsAsync(context, operation); 167 | break; 168 | case OperationType.Create: 169 | // Get the dialog entity and see if we can 170 | // skip a step (in case of plan). 171 | var dialogEntity = context.ConversationData.GetDialogEntity(); 172 | if (dialogEntity.entityType == EntityType.Me || dialogEntity.entityType == EntityType.User) 173 | { 174 | await context.Forward(new PlanLookupDialog(), OnPlanLookupDialogResume, 175 | new Plan(), CancellationToken.None); 176 | } 177 | else 178 | { 179 | // Save the plan. 180 | context.ConversationData.SetValue("Plan", dialogEntity); 181 | 182 | // Get a bucket. 183 | await context.Forward(new BucketLookupDialog(), OnBucketLookupDialogResume, 184 | new Bucket(), CancellationToken.None); 185 | } 186 | break; 187 | case OperationType.Next: 188 | // Move to the new page. 189 | await ShowOperationsAsync(context, page + 1); 190 | break; 191 | case OperationType.Previous: 192 | // Move to the previous page. 193 | await ShowOperationsAsync(context, page - 1); 194 | break; 195 | case OperationType.ShowOperations: 196 | // Reset the dialog entity. 197 | context.ConversationData.SaveDialogEntity(new BaseEntity( 198 | context.ConversationData.Me(), EntityType.Me)); 199 | context.Done(false); 200 | break; 201 | case OperationType.StartOver: 202 | // Reset the dialog entity. 203 | context.ConversationData.SaveDialogEntity(new BaseEntity( 204 | context.ConversationData.Me(), EntityType.Me)); 205 | context.Done(true); 206 | break; 207 | } 208 | } 209 | 210 | #region Create Task Operation 211 | 212 | private async Task OnPlanLookupDialogResume(IDialogContext context, 213 | IAwaitable result) 214 | { 215 | // Save the plan. 216 | var plan = await result; 217 | context.ConversationData.SetValue("Plan", plan); 218 | 219 | // Get a bucket. 220 | await context.Forward(new BucketLookupDialog(), OnBucketLookupDialogResume, new Bucket(), 221 | CancellationToken.None); 222 | } 223 | 224 | private async Task OnBucketLookupDialogResume(IDialogContext context, 225 | IAwaitable result) 226 | { 227 | // Save the bucket. 228 | var bucket = await result; 229 | context.ConversationData.SetValue("Bucket", bucket); 230 | 231 | // Get the task. 232 | PromptDialog.Text(context, OnCreateTaskDialogResume, 233 | "What is the task that you would like to create?"); 234 | } 235 | 236 | private async Task OnCreateTaskDialogResume(IDialogContext context, 237 | IAwaitable result) 238 | { 239 | // Get data needed to create a new task. 240 | var text = await result; 241 | var user = context.ConversationData.Get("Me"); 242 | var plan = context.ConversationData.Get("Plan"); 243 | var bucket = context.ConversationData.Get("Bucket"); 244 | 245 | // Create the task data. 246 | var task = new PlanTask 247 | { 248 | AssignedTo = user.id, 249 | PlanId = plan.Id, 250 | BucketId = bucket.Id, 251 | Title = text 252 | }; 253 | 254 | var httpClient = new HttpClient(); 255 | var accessToken = await context.GetAccessToken(); 256 | 257 | // Create the task. 258 | await context.PostAsync("Creating task..."); 259 | var response = await httpClient.MSGraphPOST(accessToken, 260 | "https://graph.microsoft.com/beta/tasks", task); 261 | await context.PostAsync(response ? "Task created!" : "Creation failed!"); 262 | 263 | // Clear data. 264 | context.ConversationData.RemoveValue("Plan"); 265 | context.ConversationData.RemoveValue("Bucket"); 266 | 267 | // Show operations. 268 | await ShowOperationsAsync(context); 269 | } 270 | 271 | #endregion 272 | 273 | #region Task Operations 274 | 275 | #pragma warning disable 1998 276 | private async Task ShowTaskOperationsAsync(IDialogContext context, QueryOperation operation) 277 | #pragma warning restore 1998 278 | { 279 | // Get the task. 280 | var task = operation.GetContextObjectAs(); 281 | 282 | // Create task operations. 283 | var operations = new List(); 284 | 285 | // Create in progress operation. 286 | if (task.PercentComplete == 0) 287 | { 288 | operations.Add(new QueryOperation 289 | { 290 | Text = "(In progress)", 291 | Type = OperationType.InProgress, 292 | Endpoint = operation.Endpoint, 293 | ContextObject = task 294 | }); 295 | } 296 | 297 | // Create complete operation. 298 | if (task.PercentComplete != 100) 299 | { 300 | operations.Add(new QueryOperation 301 | { 302 | Text = "(Complete)", 303 | Type = OperationType.Complete, 304 | Endpoint = operation.Endpoint, 305 | ContextObject = task 306 | }); 307 | } 308 | 309 | // Create delete operation. 310 | operations.Add(new QueryOperation 311 | { 312 | Text = "(Delete)", 313 | Type = OperationType.Delete, 314 | Endpoint = operation.Endpoint, 315 | ContextObject = task 316 | }); 317 | 318 | // Create up operation. 319 | operations.Add(new QueryOperation 320 | { 321 | Text = "(Up)", 322 | Type = OperationType.Up, 323 | Endpoint = operation.Endpoint 324 | }); 325 | 326 | // Create other operations. 327 | var me = context.ConversationData.Me(); 328 | operations.Add(new QueryOperation 329 | { 330 | Text = $"(Other {me} queries)", 331 | Type = OperationType.ShowOperations 332 | }); 333 | 334 | // Create start over operation. 335 | operations.Add(new QueryOperation 336 | { 337 | Text = "(Start over)", 338 | Type = OperationType.StartOver 339 | }); 340 | 341 | // Create prompt text. 342 | var promptText = $"Task \"{task.Title}\" is "; 343 | 344 | // Set completion status. 345 | switch (task.PercentComplete) 346 | { 347 | case 0: 348 | promptText += "not started"; 349 | break; 350 | case 100: 351 | promptText += "completed"; 352 | break; 353 | default: 354 | promptText += "in progress"; 355 | break; 356 | } 357 | promptText += ". What would you like to do next?"; 358 | 359 | // Allow user to select the operation. 360 | PromptDialog.Choice(context, OnTaskOperationsChoiceDialogResume, operations, promptText); 361 | } 362 | 363 | private async Task OnTaskOperationsChoiceDialogResume(IDialogContext context, 364 | IAwaitable result) 365 | { 366 | // Get choice result. 367 | switch ((await result).Type) 368 | { 369 | case OperationType.InProgress: 370 | PromptDialog.Confirm(context, OnTaskInProgressDialogResumeAsync, 371 | "Are you sure that you want to flag this task as in progress?"); 372 | break; 373 | case OperationType.Complete: 374 | PromptDialog.Confirm(context, OnTaskCompleteDialogResumeAsync, 375 | "Are you sure that you want to flag this task as completed?"); 376 | break; 377 | case OperationType.Delete: 378 | PromptDialog.Confirm(context, OnDeleteTaskDialogResume, 379 | "Are you sure that you want to delete the task?"); 380 | break; 381 | case OperationType.Up: 382 | var page = context.ConversationData.Get("Page"); 383 | await ShowOperationsAsync(context, page); 384 | break; 385 | case OperationType.ShowOperations: 386 | // Reset the dialog entity. 387 | context.ConversationData.SaveDialogEntity(new BaseEntity( 388 | context.ConversationData.Me(), EntityType.Me)); 389 | context.Done(false); 390 | break; 391 | case OperationType.StartOver: 392 | // Reset the dialog entity. 393 | context.ConversationData.SaveDialogEntity(new BaseEntity( 394 | context.ConversationData.Me(), EntityType.Me)); 395 | context.Done(true); 396 | break; 397 | } 398 | } 399 | 400 | #endregion 401 | 402 | #region Change Task Progress Operations 403 | 404 | private async Task OnTaskInProgressDialogResumeAsync(IDialogContext context, 405 | IAwaitable result) 406 | { 407 | await UpdateTaskProgressAsync(context, result, 50); 408 | } 409 | 410 | private async Task OnTaskCompleteDialogResumeAsync(IDialogContext context, 411 | IAwaitable result) 412 | { 413 | await UpdateTaskProgressAsync(context, result, 100); 414 | } 415 | 416 | private async Task UpdateTaskProgressAsync(IDialogContext context, 417 | IAwaitable result, int percentComplete) 418 | { 419 | var confirm = await result; 420 | var operation = context.ConversationData.Get("TaskOperation"); 421 | 422 | // Get the task. 423 | var task = operation.GetContextObjectAs(); 424 | 425 | if (confirm) 426 | { 427 | var httpClient = new HttpClient(); 428 | var accessToken = await context.GetAccessToken(); 429 | 430 | // Update the task. 431 | await context.PostAsync("Updating task..."); 432 | var response = await httpClient.MSGraphPATCH(accessToken, 433 | $"https://graph.microsoft.com/beta/{operation.Endpoint}", new 434 | { 435 | PercentComplete = percentComplete 436 | }, task.ETag); 437 | await context.PostAsync(response ? "Task updated!" : "Update failed!"); 438 | 439 | // Show operations. 440 | await ShowOperationsAsync(context); 441 | } 442 | else 443 | { 444 | // Show task operations. 445 | await ShowTaskOperationsAsync(context, operation); 446 | } 447 | } 448 | 449 | #endregion 450 | 451 | #region Task Delete Operation 452 | 453 | private async Task OnDeleteTaskDialogResume(IDialogContext context, 454 | IAwaitable result) 455 | { 456 | var confirm = await result; 457 | var operation = context.ConversationData.Get("TaskOperation"); 458 | 459 | // Get the task. 460 | var task = operation.GetContextObjectAs(); 461 | 462 | if (confirm) 463 | { 464 | var httpClient = new HttpClient(); 465 | var accessToken = await context.GetAccessToken(); 466 | 467 | // Delete the task. 468 | await context.PostAsync("Deleting task..."); 469 | var response = await httpClient.MSGraphDELETE(accessToken, 470 | $"https://graph.microsoft.com/beta/{operation.Endpoint}", task.ETag); 471 | await context.PostAsync(response ? "Task deleted!" : "Delete failed!"); 472 | 473 | // Navigating up to parent, pop the level and then pop the 474 | // last query on the parent. 475 | context.NavPopLevel(); 476 | context.NavPopItem(); 477 | await ShowOperationsAsync(context); 478 | } 479 | else 480 | { 481 | // Show task operations. 482 | await ShowTaskOperationsAsync(context, operation); 483 | } 484 | } 485 | 486 | #endregion 487 | } 488 | } -------------------------------------------------------------------------------- /MicrosoftGraphBot/Extensions.cs: -------------------------------------------------------------------------------- 1 | using AuthBot; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Builder.Luis.Models; 4 | using Microsoft.Bot.Connector; 5 | using MicrosoftGraphBot.Dialog; 6 | using MicrosoftGraphBot.Models; 7 | using Newtonsoft.Json.Linq; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Net.Http; 12 | using System.Net.Http.Headers; 13 | using System.Reflection; 14 | using System.Resources; 15 | using System.Text; 16 | using System.Threading.Tasks; 17 | using Newtonsoft.Json; 18 | 19 | namespace MicrosoftGraphBot 20 | { 21 | public static class Extensions 22 | { 23 | /// 24 | /// Simplifies the query for a token given our resource will always be the Microsoft Graph 25 | /// 26 | /// IBotContext 27 | /// AccessToken string 28 | public static async Task GetAccessToken(this IBotContext context) 29 | { 30 | return await context.GetAccessToken("https://graph.microsoft.com"); 31 | } 32 | 33 | /// 34 | /// Converts a list of Graph ResourceType enums to a list of QueryOperations 35 | /// 36 | /// List of ResourceType enums 37 | /// List of QueryOperation objects 38 | public static List ToQueryOperations(this List resourceTypes) 39 | { 40 | List operations = new List(); 41 | foreach (var op in resourceTypes) 42 | { 43 | operations.Add(new QueryOperation() 44 | { 45 | Type = op, 46 | Text = Resource.ResourceManager.GetString(op.ToString()), 47 | Endpoint = Resource.ResourceManager.GetString(String.Format("{0}_Endpoint", op.ToString())) 48 | }); 49 | } 50 | return operations; 51 | } 52 | 53 | /// 54 | /// Saves the dialog entity (who queries are being performed for) 55 | /// 56 | /// IBotDataBag 57 | /// BaseEntity 58 | public static void SaveDialogEntity(this IBotDataBag conversationData, BaseEntity entity) 59 | { 60 | conversationData.SetValue("DialogEntity", entity); 61 | } 62 | 63 | /// 64 | /// Gets the dialog entity (who queries are being performed for) 65 | /// 66 | /// IBotDataBag 67 | /// BaseEntity 68 | public static BaseEntity GetDialogEntity(this IBotDataBag conversationData) 69 | { 70 | return conversationData.Get("DialogEntity"); 71 | } 72 | 73 | /// 74 | /// Initializes the ConversationData by clearing it from the last thread 75 | /// 76 | /// IDialogContext 77 | /// LuisResult 78 | public static async Task Initialize(this IDialogContext context) 79 | { 80 | //reset the conversation data for new thead 81 | context.ConversationData.RemoveValue("DialogEntity"); 82 | 83 | //try to initialize me 84 | User me = null; 85 | if (!context.ConversationData.TryGetValue("Me", out me)) 86 | { 87 | var token = await context.GetAccessToken(); 88 | me = await User.Me(token); 89 | context.ConversationData.SetValue("Me", me); 90 | } 91 | } 92 | 93 | /// 94 | /// resets cached data for a new operation 95 | /// 96 | /// IDialogContext 97 | public static void NewOperation(this IDialogContext context) 98 | { 99 | //initialize the NavPagingStack...provides two-dimensional breadcrumb 100 | //First list is the level...second is the paging 101 | //ex: level = folder; paging = paging for files in that folder 102 | //ex: level = person; paging = paged list of direct reports for a person 103 | var navStack = new List>(); 104 | navStack.Add(new List()); 105 | context.ConversationData.SetValue>>("NavStack", navStack); 106 | context.ConversationData.RemoveValue("NavCurrent"); 107 | } 108 | 109 | /// 110 | /// Gets me 111 | /// 112 | /// IBotDataBag 113 | /// User 114 | public static User Me(this IBotDataBag conversationData) 115 | { 116 | return conversationData.Get("Me"); 117 | } 118 | 119 | /// 120 | /// checks if a specific user is ME 121 | /// 122 | /// User to check against 123 | /// IDialogContext 124 | /// bool 125 | public static bool IsMe(this User user, IDialogContext context) 126 | { 127 | var me = context.ConversationData.Get("Me"); 128 | return me.id.Equals(user.id, StringComparison.CurrentCultureIgnoreCase); 129 | } 130 | 131 | //START - THESE ARE ALL PAGING UTILITIES 132 | public static void SaveNavCurrent(this IDialogContext context, string endpoint) 133 | { 134 | context.ConversationData.SetValue("NavCurrent", endpoint); 135 | } 136 | 137 | public static string GetNavCurrent(this IDialogContext context) 138 | { 139 | return context.ConversationData.Get("NavCurrent"); 140 | } 141 | 142 | public static void NavPushLevel(this IDialogContext context) 143 | { 144 | //implemented as a list and not a real stack because serialization was corrupting order 145 | var stack = context.ConversationData.Get>>("NavStack"); 146 | stack.Insert(0, new List()); 147 | context.ConversationData.SetValue>>("NavStack", stack); 148 | } 149 | 150 | public static void NavPushItem(this IDialogContext context, string endpoint) 151 | { 152 | //implemented as a list and not a real stack because serialization was corrupting order 153 | var stack = context.ConversationData.Get>>("NavStack"); 154 | stack[0].Insert(0, endpoint); 155 | context.ConversationData.SetValue>>("NavStack", stack); 156 | } 157 | 158 | public static void NavPopLevel(this IDialogContext context) 159 | { 160 | //implemented as a list and not a real stack because serialization was corrupting order 161 | var stack = context.ConversationData.Get>>("NavStack"); 162 | stack.RemoveAt(0); 163 | context.ConversationData.SetValue>>("NavStack", stack); 164 | } 165 | 166 | public static string NavPopItem(this IDialogContext context) 167 | { 168 | //implemented as a list and not a real stack because serialization was corrupting order 169 | var stack = context.ConversationData.Get>>("NavStack"); 170 | var path = stack[0][0]; 171 | stack[0].RemoveAt(0); 172 | context.ConversationData.SetValue>>("NavStack", stack); 173 | return path; 174 | } 175 | 176 | public static string NavPeekLevel(this IDialogContext context) 177 | { 178 | //implemented as a list and not a real stack because serialization was corrupting order 179 | var stack = context.ConversationData.Get>>("NavStack"); 180 | if (stack.Count > 1) 181 | return stack[1][0]; 182 | else 183 | return null; 184 | } 185 | 186 | public static string NavPeekItem(this IDialogContext context) 187 | { 188 | //implemented as a list and not a real stack because serialization was corrupting order 189 | var stack = context.ConversationData.Get>>("NavStack"); 190 | if (stack[0].Count > 0) 191 | return stack[0][0]; 192 | else 193 | return null; 194 | } 195 | 196 | public static void InitializePaging(this List operations, IDialogContext context, JObject json) 197 | { 198 | //add next link to the end 199 | if (json["@odata.nextLink"] != null) 200 | { 201 | var next = json.Value("@odata.nextLink"); 202 | operations.Add(new QueryOperation() {Text = "(Next page)", Type = OperationType.Next, Endpoint = next}); 203 | } 204 | 205 | //add previous to the front 206 | if (!String.IsNullOrEmpty(context.NavPeekItem())) 207 | { 208 | var prev = context.NavPeekItem(); 209 | 210 | operations.Add(new QueryOperation() 211 | { 212 | Text = "(Prev page)", 213 | Type = OperationType.Previous, 214 | Endpoint = prev 215 | }); 216 | } 217 | 218 | //add parent nav up 219 | if (!String.IsNullOrEmpty(context.NavPeekLevel())) 220 | operations.Add(new QueryOperation() 221 | { 222 | Text = "(Up to parent)", 223 | Type = OperationType.Up, 224 | Endpoint = context.NavPeekLevel() 225 | }); 226 | } 227 | 228 | //START - THESE ARE ALL PAGING UTILITIES 229 | 230 | 231 | /// 232 | /// Performs a simple HTTP GET against the MSGraph given a token and endpoint 233 | /// 234 | /// HttpClient 235 | /// Access token string 236 | /// endpoint uri to perform GET on 237 | /// JObject 238 | public static async Task MSGraphGET(this HttpClient client, string token, string endpoint) 239 | { 240 | client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); 241 | client.DefaultRequestHeaders.Add("Accept", "application/json"); 242 | using (var response = await client.GetAsync(endpoint)) 243 | { 244 | if (response.IsSuccessStatusCode) 245 | { 246 | var json = await response.Content.ReadAsStringAsync(); 247 | return JObject.Parse(json); 248 | } 249 | else 250 | return null; 251 | } 252 | } 253 | 254 | /// 255 | /// Performs a HTTP DELETE against the MSGraph given an access token and request URI 256 | /// 257 | /// HttpClient 258 | /// Access token string 259 | /// Request URI to perform DELETE on 260 | /// Entity Tag header for Microsoft Graph item to perform DELETE on 261 | /// boolean for success 262 | public static async Task MSGraphDELETE(this HttpClient httpClient, string accessToken, 263 | string requestUri, string weakETag = null) 264 | { 265 | // Set Authorization and Accept header. 266 | httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken); 267 | httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); 268 | 269 | // Set (weak) If-Match header. 270 | if (weakETag != null) 271 | { 272 | var headers = httpClient.DefaultRequestHeaders; 273 | headers.IfMatch.Add(new EntityTagHeaderValue(weakETag.Substring(2, 274 | weakETag.Length - 2), true)); 275 | } 276 | 277 | using (var response = await httpClient.DeleteAsync(requestUri)) 278 | { 279 | return response.IsSuccessStatusCode; 280 | } 281 | } 282 | 283 | /// 284 | /// Performs a HTTP POST against the MSGraph given an access token and request URI 285 | /// 286 | /// HttpClient 287 | /// Access token string 288 | /// Request uri to perform POST on 289 | /// Request body data for the request 290 | /// Entity Tag header for Microsoft Graph item to perform POST on 291 | /// boolean for success 292 | public static async Task MSGraphPOST(this HttpClient httpClient, string accessToken, 293 | string requestUri, T data, string weakETag = null) where T : class 294 | { 295 | // Set Authorization and Accept header. 296 | httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken); 297 | httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); 298 | 299 | // Set If-Match header. 300 | if (weakETag != null) 301 | { 302 | var headers = httpClient.DefaultRequestHeaders; 303 | headers.IfMatch.Add(new EntityTagHeaderValue(weakETag.Substring(2, 304 | weakETag.Length - 2), true)); 305 | } 306 | 307 | // Create data. 308 | var json = JsonConvert.SerializeObject(data); 309 | var content = new StringContent(json, Encoding.UTF8, "application/json"); 310 | using (var response = await httpClient.PostAsync(requestUri, content)) 311 | { 312 | return response.IsSuccessStatusCode; 313 | } 314 | } 315 | 316 | /// 317 | /// Performs a HTTP PATCH against the MSGraph given an access token and request URI 318 | /// 319 | /// HttpClient 320 | /// Access token string 321 | /// Request uri to perform PATCH on 322 | /// Request body data for the request 323 | /// Entity Tag header for Microsoft Graph item to perform PATCH on 324 | /// boolean for success 325 | public static async Task MSGraphPATCH(this HttpClient httpClient, string accessToken, 326 | string requestUri, T data, string weakETag = null) where T : class 327 | { 328 | // Set Authorization and Accept header. 329 | httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken); 330 | httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); 331 | 332 | // Set If-Match header. 333 | if (weakETag != null) 334 | { 335 | var headers = httpClient.DefaultRequestHeaders; 336 | headers.IfMatch.Add(new EntityTagHeaderValue(weakETag.Substring(2, 337 | weakETag.Length - 2), true)); 338 | } 339 | 340 | using (var response = await httpClient.PatchAsJsonAsync(requestUri, data)) 341 | { 342 | return response.IsSuccessStatusCode; 343 | } 344 | } 345 | 346 | /// 347 | /// Implements the PATCH HTTP Method on the HttpClient. 348 | /// 349 | /// HttpClient 350 | /// Uri 351 | /// T 352 | /// HttpResponseMessage 353 | public static Task PatchAsJsonAsync(this HttpClient client, 354 | string requestUri, T value) where T : class 355 | { 356 | var request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri) 357 | { 358 | Content = new StringContent(JsonConvert.SerializeObject(value), 359 | Encoding.UTF8, "application/json") 360 | }; 361 | return client.SendAsync(request); 362 | } 363 | 364 | 365 | /// 366 | /// Below are extensions to parse JSON.NET objects to strongly-typed graph objects 367 | /// 368 | 369 | /// 370 | /// Parses JArray to generic List of File objects 371 | /// 372 | /// JArray 373 | /// List of File objects 374 | public static List ToFileList(this JArray array) 375 | { 376 | List files = new List(); 377 | foreach (var item in array) 378 | files.Add(item.ToFile()); 379 | return files; 380 | } 381 | 382 | /// 383 | /// Parses JToken to File object 384 | /// 385 | /// JToken 386 | /// File object 387 | public static File ToFile(this JToken obj) 388 | { 389 | File f = null; 390 | if (obj != null) 391 | { 392 | f = new File() 393 | { 394 | id = obj.Value("id"), 395 | text = obj.Value("name"), 396 | size = obj.Value("size"), 397 | webUrl = obj.Value("webUrl"), 398 | itemType = (obj["folder"] != null) ? ItemType.Folder : ItemType.File, 399 | navEndpoint = String.Format("/drive/items/{0}", obj.Value("id")) 400 | 401 | }; 402 | } 403 | 404 | return f; 405 | } 406 | 407 | 408 | /// 409 | /// Parses JArray to generic List of File objects 410 | /// 411 | /// JArray 412 | /// List of Mail objects 413 | public static List ToMailList(this JArray array) 414 | { 415 | List messages = new List(); 416 | foreach (var item in array) 417 | messages.Add(item.ToMail()); 418 | return messages; 419 | } 420 | 421 | /// 422 | /// Parses JToken to Mail object 423 | /// 424 | /// JToken 425 | /// Mail object 426 | public static Mail ToMail(this JToken obj) 427 | { 428 | Mail m = null; 429 | if (obj != null) 430 | { 431 | m = new Mail() 432 | { 433 | id = obj.Value("id"), 434 | text = obj.Value("subject"), 435 | isRead = obj.Value("isRead"), 436 | senderName = obj.SelectToken("sender.emailAddress").Value("name"), 437 | senderEmail = obj.SelectToken("sender.emailAddress").Value("address"), 438 | importance = obj.Value("importance"), 439 | sentDate = obj.Value("sentDateTime"), 440 | itemType = ItemType.Mail 441 | }; 442 | } 443 | 444 | return m; 445 | } 446 | 447 | 448 | /// 449 | /// Parses JArray to generic List of User objects 450 | /// 451 | /// JArray 452 | /// List of User objects 453 | public static List ToUserList(this JArray array) 454 | { 455 | List users = new List(); 456 | foreach (var item in array) 457 | users.Add(item.ToUser()); 458 | return users; 459 | } 460 | 461 | /// 462 | /// Parses JToken to User object 463 | /// 464 | /// JToken 465 | /// User object 466 | public static User ToUser(this JToken obj) 467 | { 468 | User u = null; 469 | if (obj != null) 470 | { 471 | u = new User() 472 | { 473 | id = obj.Value("id"), 474 | text = obj.Value("displayName"), 475 | givenName = obj.Value("givenName"), 476 | surname = obj.Value("surname"), 477 | jobTitle = obj.Value("jobTitle"), 478 | mail = obj.Value("mail"), 479 | userPrincipalName = obj.Value("userPrincipalName"), 480 | mobilePhone = obj.Value("mobilePhone"), 481 | officeLocation = obj.Value("officeLocation") 482 | }; 483 | } 484 | 485 | return u; 486 | } 487 | 488 | 489 | /// 490 | /// Parses JArray to generic List of Group objects 491 | /// 492 | /// JArray 493 | /// List of Group objects 494 | public static List ToGroupList(this JArray array) 495 | { 496 | List groups = new List(); 497 | foreach (var item in array) 498 | groups.Add(item.ToGroup()); 499 | return groups; 500 | } 501 | 502 | /// 503 | /// Parses JToken to User object 504 | /// 505 | /// JToken 506 | /// User object 507 | public static Group ToGroup(this JToken obj) 508 | { 509 | Group g = null; 510 | if (obj != null) 511 | { 512 | g = new Group() 513 | { 514 | id = obj.Value("id"), 515 | text = obj.Value("displayName"), 516 | description = obj.Value("description"), 517 | mail = obj.Value("mail"), 518 | itemType = ItemType.Group, 519 | visibility = obj.Value("visibility") 520 | }; 521 | } 522 | 523 | return g; 524 | } 525 | 526 | /// 527 | /// Parses JArray to generic List of Plan objects 528 | /// 529 | /// JArray 530 | /// List of Plan objects 531 | public static List ToPlanList(this JArray array) 532 | { 533 | return array.Select(item => item.ToPlan()).ToList(); 534 | } 535 | 536 | /// 537 | /// Parses JToken to Plan object 538 | /// 539 | /// JToken 540 | /// Plan object 541 | public static Plan ToPlan(this JToken token) 542 | { 543 | return token.ToObject(); 544 | } 545 | 546 | /// 547 | /// Parses JArray to generic List of Bucket objects 548 | /// 549 | /// JArray 550 | /// List of Bucket objects 551 | public static List ToBucketList(this JArray array) 552 | { 553 | return array.Select(item => item.ToBucket()).ToList(); 554 | } 555 | 556 | /// 557 | /// Parses JToken to Bucket object 558 | /// 559 | /// JToken 560 | /// Bucket object 561 | public static Bucket ToBucket(this JToken token) 562 | { 563 | return token.ToObject(); 564 | } 565 | 566 | /// 567 | /// Parses JArray to generic List of Task objects 568 | /// 569 | /// JArray 570 | /// List of Task objects 571 | public static List ToTasksList(this JArray array) 572 | { 573 | return array.Select(item => item.ToTask()).ToList(); 574 | } 575 | 576 | /// 577 | /// Parses JToken to Task object 578 | /// 579 | /// JToken 580 | /// Task object 581 | public static PlanTask ToTask(this JToken token) 582 | { 583 | return token.ToObject(); 584 | } 585 | } 586 | } 587 | --------------------------------------------------------------------------------