├── ExcelBot ├── Global.asax ├── Model │ ├── UserData.cs │ ├── ObjectType.cs │ └── ChartAttachment.cs ├── Helpers │ ├── RequestHelper.cs │ ├── IndexOf.cs │ ├── TelemetryHelper.cs │ ├── BotStateHelper.cs │ ├── ServicesHelper.cs │ ├── LuisHelper.cs │ └── ExcelHelper.cs ├── Constants │ └── Constants.cs ├── Forms │ ├── OpenWorkbookForm.cs │ └── SelectWorksheetForm.cs ├── PrivateSettings.config.example ├── loggedin.htm ├── Properties │ └── AssemblyInfo.cs ├── chat.htm ├── Web.Debug.config ├── App_Start │ └── WebApiConfig.cs ├── Web.Release.config ├── default.htm ├── ExcelBot.csproj.user ├── Global.asax.cs ├── Dialogs │ ├── OpenWorkbookDialog.cs │ ├── GraphDialog.cs │ ├── DialogExtensions.cs │ ├── WorkbooksDialog.cs │ ├── ChartsDialog.cs │ ├── ExcelBotDialog.cs │ ├── TablesDialog.cs │ ├── NamedItemsDialog.cs │ ├── WorksheetsDialog.cs │ └── CellsDialog.cs ├── Workers │ ├── WorkbookWorker.cs │ ├── CellWorker.cs │ ├── WorksheetWorker.cs │ └── ChartsWorker.cs ├── Controllers │ ├── ChartController.cs │ └── MessagesController.cs ├── packages.config ├── ApplicationInsights.config └── Web.config ├── readme-images ├── appID.png ├── create.png ├── idandkey.png ├── luisimport.png ├── testpublish.png ├── aad-application-id.PNG ├── aad-register-an-app.PNG ├── aad-copy-client-secret.png ├── aad-new-client-secret.png └── aad-portal-app-registrations.png ├── .gitignore ├── ExcelBot.sln ├── LICENSE ├── .gitattributes ├── README-localized ├── README-zh-cn.md ├── README-ja-jp.md ├── README-pt-br.md ├── README-ru-ru.md ├── README-es-es.md └── README-fr-fr.md ├── README.md └── CONTRIBUTING.md /ExcelBot/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="ExcelBot.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /readme-images/appID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/appID.png -------------------------------------------------------------------------------- /readme-images/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/create.png -------------------------------------------------------------------------------- /readme-images/idandkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/idandkey.png -------------------------------------------------------------------------------- /readme-images/luisimport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/luisimport.png -------------------------------------------------------------------------------- /readme-images/testpublish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/testpublish.png -------------------------------------------------------------------------------- /readme-images/aad-application-id.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/aad-application-id.PNG -------------------------------------------------------------------------------- /readme-images/aad-register-an-app.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/aad-register-an-app.PNG -------------------------------------------------------------------------------- /readme-images/aad-copy-client-secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/aad-copy-client-secret.png -------------------------------------------------------------------------------- /readme-images/aad-new-client-secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/aad-new-client-secret.png -------------------------------------------------------------------------------- /readme-images/aad-portal-app-registrations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/HEAD/readme-images/aad-portal-app-registrations.png -------------------------------------------------------------------------------- /ExcelBot/Model/UserData.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | namespace ExcelBot.Model 7 | { 8 | public class UserData 9 | { 10 | public string AuthResult { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /ExcelBot/Model/ObjectType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | namespace ExcelBot.Model 7 | { 8 | public enum ObjectType 9 | { 10 | Cell, 11 | NamedItem, 12 | Chart, 13 | Table 14 | } 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /ExcelBot/bin 6 | /ExcelBot/obj 7 | /packages 8 | /ExcelBotEmbedded/ExcelBotEmbedded/bin 9 | /.vs 10 | ExcelBot/PrivateSettings.config 11 | PublishProfiles/ 12 | -------------------------------------------------------------------------------- /ExcelBot/Model/ChartAttachment.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | namespace ExcelBot.Model 7 | { 8 | public class ChartAttachment 9 | { 10 | public string WorkbookId { get; set; } 11 | public string WorksheetId { get; set; } 12 | public string ChartId { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /ExcelBot/Helpers/RequestHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | /* 4 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 5 | * See LICENSE in the project root for license information. 6 | */ 7 | 8 | namespace ExcelBot.Helpers 9 | { 10 | public static class RequestHelper 11 | { 12 | #region Properties 13 | public static Uri RequestUri { get; set; } 14 | #endregion 15 | 16 | #region Methods 17 | #endregion 18 | } 19 | } -------------------------------------------------------------------------------- /ExcelBot/Constants/Constants.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using System.Configuration; 7 | 8 | namespace ExcelBot 9 | { 10 | public static class Constants 11 | { 12 | internal static string microsoftAppId = ConfigurationManager.AppSettings["MicrosoftAppId"]; 13 | internal static string microsoftAppPassword = ConfigurationManager.AppSettings["MicrosoftAppPassword"]; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /ExcelBot/Helpers/IndexOf.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | 10 | namespace ExcelBot.Helpers 11 | { 12 | public static class Extensions 13 | { 14 | public static int IndexOf(this IEnumerable list, Predicate condition) 15 | { 16 | int i = -1; 17 | return list.Any(x => { i++; return condition(x); }) ? i : -1; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /ExcelBot/Forms/OpenWorkbookForm.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.Bot.Builder.FormFlow; 7 | using System; 8 | 9 | namespace ExcelBot.Forms 10 | { 11 | [Serializable] 12 | public class OpenWorkbookForm 13 | { 14 | [Prompt("What is the name of the workbook you want to work with?")] 15 | public string WorkbookName; 16 | 17 | public static IForm BuildForm() 18 | { 19 | return new FormBuilder() 20 | .AddRemainingFields() 21 | .Build(); 22 | } 23 | }; 24 | } -------------------------------------------------------------------------------- /ExcelBot/PrivateSettings.config.example: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ExcelBot.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2011 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelBot", "ExcelBot\ExcelBot.csproj", "{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {A77CB6A6-C57C-45F1-BBE6-435D1532D80F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Microsoft-Graph-Bot-Framework-CSharp-ExcelBot-Rest-Sample 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | "Software"), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | -------------------------------------------------------------------------------- /ExcelBot/loggedin.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Excel Bot 7 | 8 | 18 | 19 | 20 |

Sign-in Successful

21 |

You can now chat with the bot.

22 | 23 | 24 | -------------------------------------------------------------------------------- /ExcelBot/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("ExcelBot")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ExcelBot")] 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 | -------------------------------------------------------------------------------- /ExcelBot/chat.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Excel Bot 7 | 8 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /ExcelBot/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 19 | 20 | 31 | 32 | -------------------------------------------------------------------------------- /ExcelBot/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Serialization; 8 | using System.Web.Http; 9 | 10 | namespace ExcelBot 11 | { 12 | public static class WebApiConfig 13 | { 14 | public static void Register(HttpConfiguration config) 15 | { 16 | // Json settings 17 | config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; 18 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 19 | config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; 20 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings() 21 | { 22 | ContractResolver = new CamelCasePropertyNamesContractResolver(), 23 | Formatting = Newtonsoft.Json.Formatting.Indented, 24 | NullValueHandling = NullValueHandling.Ignore, 25 | }; 26 | 27 | // Web API configuration and services 28 | 29 | // Web API routes 30 | config.MapHttpAttributeRoutes(); 31 | 32 | config.Routes.MapHttpRoute( 33 | name: "DefaultApi", 34 | routeTemplate: "api/{controller}/{id}", 35 | defaults: new { id = RouteParameter.Optional } 36 | ); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ExcelBot/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 19 | 20 | 21 | 32 | 33 | -------------------------------------------------------------------------------- /ExcelBot/default.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 18 | 19 | 20 |

ExcelBot

21 |

Describe your bot here and your terms of use etc.

22 |

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

23 | 24 | 25 | -------------------------------------------------------------------------------- /ExcelBot/ExcelBot.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | excelbotv3-staging - Web Deploy 6 | ProjectFiles 7 | Debug|Any CPU 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | default.htm 20 | SpecificPage 21 | True 22 | False 23 | False 24 | False 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | True 34 | True 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ExcelBot/Global.asax.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Autofac; 7 | using Microsoft.Bot.Builder.Azure; 8 | using Microsoft.Bot.Builder.Dialogs; 9 | using Microsoft.Bot.Builder.Dialogs.Internals; 10 | using Microsoft.Bot.Connector; 11 | using System; 12 | using System.Configuration; 13 | using System.Reflection; 14 | using System.Web.Http; 15 | 16 | namespace ExcelBot 17 | { 18 | public class WebApiApplication : System.Web.HttpApplication 19 | { 20 | protected void Application_Start() 21 | { 22 | // Need to register a bot state data store 23 | Conversation.UpdateContainer( 24 | builder => 25 | { 26 | builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly())); 27 | 28 | // This will create a CosmosDB store, suitable for production 29 | // NOTE: Requires an actual CosmosDB instance and configuration in 30 | // PrivateSettings.config 31 | var databaseUri = new Uri(ConfigurationManager.AppSettings["Database.Uri"]); 32 | var databaseKey = ConfigurationManager.AppSettings["Database.Key"]; 33 | var store = new DocumentDbBotDataStore(databaseUri, databaseKey); 34 | 35 | builder.Register(c => store) 36 | .Keyed>(AzureModule.Key_DataStore) 37 | .AsSelf() 38 | .SingleInstance(); 39 | }); 40 | 41 | GlobalConfiguration.Configure(WebApiConfig.Register); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ExcelBot/Forms/SelectWorksheetForm.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.Bot.Builder.FormFlow; 7 | using Microsoft.Bot.Builder.FormFlow.Advanced; 8 | using System; 9 | 10 | namespace ExcelBot.Forms 11 | { 12 | [Serializable] 13 | public class SelectWorksheetForm 14 | { 15 | public static string[] Worksheets; 16 | public string WorksheetName; 17 | 18 | public static IForm BuildForm() 19 | { 20 | return new FormBuilder() 21 | .Field(new FieldReflector(nameof(WorksheetName)) 22 | .SetType(null) 23 | .SetActive((state) => true) 24 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 25 | .SetDefine(async (state, field) => 26 | { 27 | foreach (var worksheet in Worksheets) 28 | { 29 | field 30 | .AddDescription(worksheet, worksheet) 31 | .AddTerms(worksheet, worksheet, worksheet.ToLower()); 32 | } 33 | field 34 | .SetPrompt(new PromptAttribute("Which worksheet do you want to work with? {||}") { ChoiceFormat = @"{0}. {1}"}); 35 | return true; 36 | }) 37 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 38 | ) 39 | .Build(); 40 | } 41 | }; 42 | } -------------------------------------------------------------------------------- /ExcelBot/Helpers/TelemetryHelper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.ApplicationInsights; 7 | using Microsoft.Bot.Builder.Dialogs; 8 | using Microsoft.Bot.Builder.Luis.Models; 9 | using Microsoft.Bot.Connector; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using System.Web; 13 | 14 | namespace ExcelBot.Helpers 15 | { 16 | public static class TelemetryHelper 17 | { 18 | #region Methods 19 | public static void SetIds(Activity activity) 20 | { 21 | HttpContext.Current.Items["UserId"] = activity.From.Id; 22 | HttpContext.Current.Items["ConversationId"] = activity.Conversation.Id; 23 | HttpContext.Current.Items["ChannelId"] = activity.ChannelId; 24 | } 25 | public static void TrackEvent(string eventName, Dictionary properties = null, Dictionary metrics = null) 26 | { 27 | var tc = new TelemetryClient(); 28 | 29 | tc.Context.User.AccountId = (string)(HttpContext.Current.Items["UserId"]); 30 | tc.Context.User.Id = (string)(HttpContext.Current.Items["UserId"]); 31 | tc.Context.Session.Id = (string)(HttpContext.Current.Items["ConversationId"]); 32 | tc.Context.Device.Type = (string)(HttpContext.Current.Items["ChannelId"]); 33 | 34 | tc.TrackEvent(eventName, properties, metrics); 35 | } 36 | 37 | public static void TrackDialog(IDialogContext context, LuisResult result, string moduleName, string dialogName) 38 | { 39 | var properties = new Dictionary(); 40 | properties.Add("Module", moduleName); 41 | properties.Add("Dialog", dialogName); 42 | properties.Add("Query", result.Query); 43 | 44 | var metrics = new Dictionary(); 45 | metrics.Add("EntityCount", result.Entities.Count); 46 | 47 | var entityTypes = new StringBuilder(); 48 | var separator = ""; 49 | foreach (var entity in result.Entities) 50 | { 51 | entityTypes.Append($"{separator}{entity.Type}"); 52 | separator = ","; 53 | } 54 | properties.Add("EntityTypes", entityTypes.ToString()); 55 | 56 | TrackEvent($"Dialog/{moduleName}/{dialogName}", properties, metrics); 57 | } 58 | #endregion 59 | } 60 | } -------------------------------------------------------------------------------- /ExcelBot/Helpers/BotStateHelper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.Azure.Documents; 7 | using Microsoft.Azure.Documents.Client; 8 | using Microsoft.Bot.Connector; 9 | using System; 10 | using System.Configuration; 11 | using System.Net; 12 | using System.Threading.Tasks; 13 | 14 | namespace ExcelBot.Helpers 15 | { 16 | public static class BotStateHelper 17 | { 18 | private static readonly string databaseUri = ConfigurationManager.AppSettings["Database.Uri"]; 19 | private static readonly string databaseKey = ConfigurationManager.AppSettings["Database.Key"]; 20 | 21 | private static readonly string databaseId = "botdb"; 22 | private static readonly string collectionId = "botcollection"; 23 | 24 | private static DocumentClient client = null; 25 | 26 | private static void EnsureClient() 27 | { 28 | if (client == null) 29 | { 30 | client = new DocumentClient(new Uri(databaseUri), databaseKey); 31 | } 32 | } 33 | 34 | public static async Task GetUserDataAsync(string channelId, string userId) 35 | { 36 | // Construct user doc id 37 | string userDocId = string.Format("{0}:user{1}", channelId, userId); 38 | 39 | return await GetDocumentAsync(userDocId); 40 | } 41 | 42 | public static async Task GetConversationDataAsync(string channelId, string conversationId) 43 | { 44 | // Construct user doc id 45 | string conversationDocId = string.Format("{0}:conversation{1}", channelId, conversationId); 46 | 47 | return await GetDocumentAsync(conversationDocId); 48 | } 49 | 50 | private static async Task GetDocumentAsync(string docId) 51 | { 52 | EnsureClient(); 53 | 54 | try 55 | { 56 | Document document = await client.ReadDocumentAsync( 57 | UriFactory.CreateDocumentUri(databaseId, collectionId, docId)); 58 | 59 | return (BotData)(dynamic)document; 60 | } 61 | catch (DocumentClientException ex) 62 | { 63 | if (HttpStatusCode.NotFound == ex.StatusCode) 64 | { 65 | return null; 66 | } 67 | 68 | throw; 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ExcelBot/Dialogs/OpenWorkbookDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Forms; 7 | using ExcelBot.Helpers; 8 | using ExcelBot.Workers; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Builder.FormFlow; 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace ExcelBot.Dialogs 15 | { 16 | [Serializable] 17 | public class ConfirmOpenWorkbookDialog : GraphDialog, IDialog 18 | { 19 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 20 | public override async Task StartAsync(IDialogContext context) 21 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 22 | { 23 | PromptDialog.Confirm(context, AfterConfirming_OpenWorkbook, $"I don't have a workbook open. Do you want me to open a workbook?"); 24 | } 25 | 26 | private async Task AfterConfirming_OpenWorkbook(IDialogContext context, IAwaitable confirmation) 27 | { 28 | if (await confirmation) 29 | { 30 | // Call the OpenWorkbook Form 31 | context.Call( 32 | new FormDialog(new OpenWorkbookForm(), OpenWorkbookForm.BuildForm, FormOptions.PromptInStart), 33 | OpenWorkbook_FormComplete); 34 | } 35 | else 36 | { 37 | await context.PostAsync("Okay! I will just sit tight until you tell me which workbook we should work with"); 38 | context.Done(false); 39 | } 40 | } 41 | 42 | private async Task OpenWorkbook_FormComplete(IDialogContext context, IAwaitable result) 43 | { 44 | OpenWorkbookForm form = null; 45 | try 46 | { 47 | form = await result; 48 | } 49 | catch 50 | { 51 | } 52 | 53 | if (form != null) 54 | { 55 | // Get access token to see if user is authenticated 56 | ServicesHelper.AccessToken = await GetAccessToken(context); 57 | 58 | // Open the workbook 59 | await WorkbookWorker.DoOpenWorkbookAsync(context, form.WorkbookName); 60 | context.Done(true); 61 | } 62 | else 63 | { 64 | await context.PostAsync("Okay! I will just sit tight until you tell me which workbook we should work with"); 65 | context.Done(false); 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /ExcelBot/Workers/WorkbookWorker.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using Microsoft.Bot.Builder.Dialogs; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | namespace ExcelBot.Workers 12 | { 13 | public static class WorkbookWorker 14 | { 15 | public static async Task DoOpenWorkbookAsync(IDialogContext context, string workbookName) 16 | { 17 | try 18 | { 19 | // Add extension to filename, if needed 20 | var filename = workbookName.ToLower(); 21 | if (!(filename.EndsWith(".xlsx"))) 22 | { 23 | filename = $"{filename}.xlsx"; 24 | } 25 | 26 | // Get meta data for the workbook 27 | var itemRequest = ServicesHelper.GraphClient.Me.Drive.Root.ItemWithPath(filename).Request(); 28 | var item = await itemRequest.GetAsync(); 29 | await ServicesHelper.LogGraphServiceRequest(context, itemRequest); 30 | 31 | context.UserData.SetValue("WorkbookId", item.Id); 32 | context.ConversationData.SetValue("WorkbookName", item.Name); 33 | context.ConversationData.SetValue("WorkbookWebUrl", item.WebUrl); 34 | 35 | context.UserData.RemoveValue("Type"); 36 | context.UserData.RemoveValue("Name"); 37 | context.UserData.RemoveValue("CellAddress"); 38 | context.UserData.RemoveValue("TableName"); 39 | context.UserData.RemoveValue("RowIndex"); 40 | 41 | // Get the first worksheet in the workbook 42 | var headers = ServicesHelper.GetWorkbookSessionHeader( 43 | ExcelHelper.GetSessionIdForRead(context)); 44 | 45 | var worksheetsRequest = ServicesHelper.GraphClient.Me.Drive.Items[item.Id] 46 | .Workbook.Worksheets.Request(headers).Top(1); 47 | 48 | var worksheets = await worksheetsRequest.GetAsync(); 49 | await ServicesHelper.LogGraphServiceRequest(context, worksheetsRequest); 50 | 51 | context.UserData.SetValue("WorksheetId", worksheets[0].Name); 52 | 53 | // Respond 54 | await context.PostAsync($"We are ready to work with **{worksheets[0].Name}** in {ExcelHelper.GetWorkbookLinkMarkdown(context)}"); 55 | } 56 | catch (Exception ex) 57 | { 58 | await context.PostAsync($"Sorry, something went wrong when I tried to open the **{workbookName}** workbook on your OneDrive for Business ({ex.Message})"); 59 | } 60 | } 61 | 62 | public static async Task DoGetActiveWorkbookAsync(IDialogContext context) 63 | { 64 | await context.PostAsync($"We are working with the {ExcelHelper.GetWorkbookLinkMarkdown(context)} workbook"); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /ExcelBot/Helpers/ServicesHelper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.Bot.Builder.Dialogs; 7 | using Microsoft.Graph; 8 | using Newtonsoft.Json; 9 | using System.Collections.Generic; 10 | using System.Threading.Tasks; 11 | using System.Web; 12 | 13 | namespace ExcelBot.Helpers 14 | { 15 | public static class ServicesHelper 16 | { 17 | private static bool doLogging = false; 18 | 19 | #region Properties 20 | public static string AccessToken 21 | { 22 | get 23 | { 24 | return (string)(HttpContext.Current.Items["AccessToken"]); 25 | } 26 | set 27 | { 28 | HttpContext.Current.Items["AccessToken"] = value; 29 | } 30 | } 31 | 32 | public static GraphServiceClient GraphClient 33 | { 34 | get 35 | { 36 | if (!(HttpContext.Current.Items.Contains("GraphClient"))) 37 | { 38 | var client = new GraphServiceClient( 39 | new DelegateAuthenticationProvider( 40 | #pragma warning disable CS1998 41 | async (requestMessage) => 42 | { 43 | requestMessage.Headers.Authorization = 44 | new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", AccessToken); 45 | })); 46 | #pragma warning restore CS1998 47 | 48 | HttpContext.Current.Items["GraphClient"] = client; 49 | } 50 | 51 | return (GraphServiceClient)HttpContext.Current.Items["GraphClient"]; 52 | } 53 | } 54 | 55 | public static List GetWorkbookSessionHeader(string sessionId) 56 | { 57 | return new List() 58 | { 59 | new HeaderOption("workbook-session-id", sessionId) 60 | }; 61 | } 62 | #endregion 63 | 64 | #region Methods 65 | public static void StartLogging(bool verbose) 66 | { 67 | doLogging = verbose; 68 | } 69 | 70 | public static async Task LogGraphServiceRequest(IDialogContext context, IBaseRequest request, object payload = null) 71 | { 72 | if (doLogging) 73 | { 74 | if (request.Method == "POST" || request.Method == "PATCH") 75 | { 76 | string prettyPayload = JsonConvert.SerializeObject(payload, Formatting.Indented); 77 | await context.PostAsync($"```\n{request.Method} {request.RequestUrl}\n\n{prettyPayload}\n```"); 78 | } 79 | else 80 | { 81 | await context.PostAsync($"`{request.Method} {request.RequestUrl}`"); 82 | } 83 | } 84 | } 85 | #endregion 86 | } 87 | } -------------------------------------------------------------------------------- /ExcelBot/Dialogs/GraphDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using BotAuth; 7 | using BotAuth.AADv2; 8 | using BotAuth.Dialogs; 9 | using BotAuth.Models; 10 | using ExcelBot.Helpers; 11 | using Microsoft.Bot.Builder.Dialogs; 12 | using Microsoft.Bot.Connector; 13 | using System; 14 | using System.Configuration; 15 | using System.Threading; 16 | using System.Threading.Tasks; 17 | 18 | namespace ExcelBot.Dialogs 19 | { 20 | [Serializable] 21 | public class GraphDialog : LuisDialog 22 | { 23 | protected static AuthenticationOptions authOptions = new AuthenticationOptions() 24 | { 25 | Authority = ConfigurationManager.AppSettings["ActiveDirectory.EndpointUrl"], 26 | ClientId = ConfigurationManager.AppSettings["ActiveDirectory.ClientId"], 27 | ClientSecret = ConfigurationManager.AppSettings["ActiveDirectory.ClientSecret"], 28 | Scopes = new string[] { "User.Read", "Files.ReadWrite" }, 29 | RedirectUrl = ConfigurationManager.AppSettings["ActiveDirectory.RedirectUrl"], 30 | }; 31 | 32 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 33 | public override async Task StartAsync(IDialogContext context) 34 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 35 | { 36 | context.Wait(MessageReceived); 37 | } 38 | 39 | protected override async Task MessageReceived(IDialogContext context, IAwaitable item) 40 | { 41 | var message = await item; 42 | 43 | // Try to get token silently 44 | ServicesHelper.AccessToken = await GetAccessToken(context); 45 | 46 | if (string.IsNullOrEmpty(ServicesHelper.AccessToken)) 47 | { 48 | // Do prompt 49 | await context.Forward(new AuthDialog(new MSALAuthProvider(), authOptions), 50 | ResumeAfterAuth, message, CancellationToken.None); 51 | } 52 | else if (message.Text == "logout") 53 | { 54 | await new MSALAuthProvider().Logout(authOptions, context); 55 | context.Wait(this.MessageReceived); 56 | } 57 | else 58 | { 59 | // Process incoming message 60 | await base.MessageReceived(context, item); 61 | } 62 | } 63 | 64 | protected async Task GetAccessToken(IDialogContext context) 65 | { 66 | var provider = new MSALAuthProvider(); 67 | var authResult = await provider.GetAccessToken(authOptions, context); 68 | 69 | return (authResult == null ? string.Empty : authResult.AccessToken); 70 | } 71 | 72 | private async Task ResumeAfterAuth(IDialogContext context, IAwaitable result) 73 | { 74 | var message = await result; 75 | 76 | await context.PostAsync("Now that you're logged in, what can I do for you?"); 77 | context.Wait(MessageReceived); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ExcelBot/Dialogs/DialogExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.Bot.Builder.Dialogs; 7 | using Microsoft.Bot.Builder.Luis.Models; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | namespace ExcelBot.Dialogs 12 | { 13 | public static class DialogExtensions 14 | { 15 | public static void NotifyLongRunningOperation(this Task operation, IDialogContext context, Func handler) 16 | { 17 | operation.ContinueWith( 18 | async (t, ctx) => 19 | { 20 | var messageText = handler(t.Result); 21 | await NotifyUser((IDialogContext)ctx, messageText); 22 | }, 23 | context); 24 | } 25 | 26 | public static void NotifyLongRunningOperation(this Task operation, IDialogContext context, Func handler) 27 | { 28 | operation.ContinueWith( 29 | async (t, ctx) => 30 | { 31 | var messageText = handler(t.Result, (IDialogContext)ctx); 32 | await NotifyUser((IDialogContext)ctx, messageText); 33 | }, 34 | context); 35 | } 36 | 37 | public static string GetEntityOriginalText(this EntityRecommendation recommendation, string query) 38 | { 39 | if (recommendation.StartIndex.HasValue && recommendation.EndIndex.HasValue) 40 | { 41 | return query.Substring(recommendation.StartIndex.Value, recommendation.EndIndex.Value - recommendation.StartIndex.Value + 1); 42 | } 43 | 44 | return null; 45 | } 46 | 47 | public static async Task NotifyUser(this IDialogContext context, string messageText) 48 | { 49 | if (!string.IsNullOrEmpty(messageText)) 50 | { 51 | await context.PostAsync(messageText); 52 | } 53 | } 54 | } 55 | } 56 | 57 | //********************************************************* 58 | // 59 | //AuthBot, https://github.com/microsoftdx/AuthBot 60 | // 61 | //Copyright (c) Microsoft Corporation 62 | //All rights reserved. 63 | // 64 | // MIT License: 65 | // Permission is hereby granted, free of charge, to any person obtaining 66 | // a copy of this software and associated documentation files (the 67 | // ""Software""), to deal in the Software without restriction, including 68 | // without limitation the rights to use, copy, modify, merge, publish, 69 | // distribute, sublicense, and/or sell copies of the Software, and to 70 | // permit persons to whom the Software is furnished to do so, subject to 71 | // the following conditions: 72 | 73 | 74 | 75 | 76 | // The above copyright notice and this permission notice shall be 77 | // included in all copies or substantial portions of the Software. 78 | 79 | 80 | 81 | 82 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 83 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 84 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 85 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 86 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 87 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 88 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 89 | // 90 | //********************************************************* 91 | -------------------------------------------------------------------------------- /ExcelBot/Dialogs/WorkbooksDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Forms; 7 | using ExcelBot.Helpers; 8 | using ExcelBot.Workers; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Builder.FormFlow; 11 | using Microsoft.Bot.Builder.Luis.Models; 12 | using System; 13 | using System.Threading.Tasks; 14 | 15 | namespace ExcelBot.Dialogs 16 | { 17 | public partial class ExcelBotDialog : GraphDialog 18 | { 19 | #region Intents 20 | [LuisIntent("openWorkbook")] 21 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 22 | public async Task OpenWorkbook(IDialogContext context, LuisResult result) 23 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 24 | { 25 | // Telemetry 26 | TelemetryHelper.TrackDialog(context, result, "Workbooks", "OpenWorkbook"); 27 | 28 | // Create the Open workbook form and extract the workbook name from the query 29 | var form = new OpenWorkbookForm(); 30 | form.WorkbookName = (string)(LuisHelper.GetValue(result)); 31 | 32 | // Call the OpenWorkbook Form 33 | context.Call( 34 | new FormDialog(form, OpenWorkbookForm.BuildForm, FormOptions.PromptInStart), 35 | OpenWorkbookFormComplete); 36 | } 37 | 38 | private async Task OpenWorkbookFormComplete(IDialogContext context, IAwaitable result) 39 | { 40 | OpenWorkbookForm form = null; 41 | try 42 | { 43 | form = await result; 44 | } 45 | catch 46 | { 47 | await context.PostAsync("You canceled opening a workbook. No problem! I can move on to something else"); 48 | return; 49 | } 50 | 51 | if (form != null) 52 | { 53 | // Get access token to see if user is authenticated 54 | ServicesHelper.AccessToken = await GetAccessToken(context); 55 | 56 | // Open workbook 57 | await WorkbookWorker.DoOpenWorkbookAsync(context, form.WorkbookName); 58 | } 59 | else 60 | { 61 | await context.PostAsync("Sorry, something went wrong (form is empty)"); 62 | } 63 | context.Wait(MessageReceived); 64 | } 65 | 66 | [LuisIntent("getActiveWorkbook")] 67 | public async Task GetActiveWorkbook(IDialogContext context, LuisResult result) 68 | { 69 | // Telemetry 70 | TelemetryHelper.TrackDialog(context, result, "Workbooks", "GetActiveWorkbook"); 71 | 72 | string workbookId = String.Empty; 73 | context.UserData.TryGetValue("WorkbookId", out workbookId); 74 | 75 | if ((workbookId != null) && (workbookId != String.Empty)) 76 | { 77 | await WorkbookWorker.DoGetActiveWorkbookAsync(context); 78 | context.Wait(MessageReceived); 79 | } 80 | else 81 | { 82 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_GetActiveWorkbook); 83 | } 84 | } 85 | 86 | public async Task AfterConfirm_GetActiveWorkbook(IDialogContext context, IAwaitable result) 87 | { 88 | if (await result) 89 | { 90 | await WorkbookWorker.DoGetActiveWorkbookAsync(context); 91 | } 92 | context.Wait(MessageReceived); 93 | } 94 | 95 | #endregion 96 | } 97 | } -------------------------------------------------------------------------------- /ExcelBot/Dialogs/ChartsDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using ExcelBot.Workers; 8 | using Microsoft.Bot.Builder.Dialogs; 9 | using Microsoft.Bot.Builder.Luis.Models; 10 | using System; 11 | using System.Threading.Tasks; 12 | 13 | namespace ExcelBot.Dialogs 14 | { 15 | public partial class ExcelBotDialog : GraphDialog 16 | { 17 | #region Properties 18 | #endregion 19 | 20 | #region Intents 21 | #region - List Charts 22 | [LuisIntent("listCharts")] 23 | public async Task ListCharts(IDialogContext context, LuisResult result) 24 | { 25 | // Telemetry 26 | TelemetryHelper.TrackDialog(context, result, "Charts", "ListCharts"); 27 | 28 | string workbookId = String.Empty; 29 | context.UserData.TryGetValue("WorkbookId", out workbookId); 30 | 31 | if ((workbookId != null) && (workbookId != String.Empty)) 32 | { 33 | await ChartsWorker.DoListCharts(context); 34 | context.Wait(MessageReceived); 35 | } 36 | else 37 | { 38 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_ListCharts); 39 | } 40 | } 41 | public async Task AfterConfirm_ListCharts(IDialogContext context, IAwaitable result) 42 | { 43 | if (await result) 44 | { 45 | await ChartsWorker.DoListCharts(context); 46 | } 47 | context.Wait(MessageReceived); 48 | } 49 | 50 | #endregion 51 | #region - Get Chart Image 52 | [LuisIntent("getChartImage")] 53 | public async Task GetChartImage(IDialogContext context, LuisResult result) 54 | { 55 | // Telemetry 56 | TelemetryHelper.TrackDialog(context, result, "Charts", "GetChartImage"); 57 | 58 | var name = LuisHelper.GetChartEntity(result.Entities); 59 | context.UserData.SetValue("ChartName", name); 60 | 61 | string workbookId = String.Empty; 62 | context.UserData.TryGetValue("WorkbookId", out workbookId); 63 | 64 | if ((workbookId != null) && (workbookId != String.Empty)) 65 | { 66 | if (result.Entities.Count > 0) 67 | { 68 | string worksheetId = String.Empty; 69 | context.UserData.TryGetValue("WorksheetId", out worksheetId); 70 | 71 | if ((worksheetId != null) && (worksheetId != String.Empty)) 72 | { 73 | await ChartsWorker.DoGetChartImage(context); 74 | } 75 | else 76 | { 77 | await context.PostAsync($"You need to provide the name of a worksheet to get a chart"); 78 | } 79 | } 80 | else 81 | { 82 | await context.PostAsync($"You need to provide the name of the chart"); 83 | } 84 | context.Wait(MessageReceived); 85 | } 86 | else 87 | { 88 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_GetChartImage); 89 | } 90 | } 91 | public async Task AfterConfirm_GetChartImage(IDialogContext context, IAwaitable result) 92 | { 93 | if (await result) 94 | { 95 | await ChartsWorker.DoGetChartImage(context); 96 | } 97 | context.Wait(MessageReceived); 98 | } 99 | #endregion 100 | #endregion 101 | } 102 | } -------------------------------------------------------------------------------- /README-localized/README-zh-cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-excel 5 | - ms-graph 6 | languages: 7 | - csharp 8 | description: "Excel 机器人是一个使用 Microsoft Bot Framework 生成的机器人,它演示了如何将 Excel 与 Microsoft Graph API 结合使用" 9 | extensions: 10 | contentType: samples 11 | technologies: 12 | - Microsoft Graph 13 | - Microsoft Bot Framework 14 | services: 15 | - Excel 16 | createdDate: 9/15/2016 10:30:08 PM 17 | --- 18 | 19 | # Excel 机器人 20 | 21 | ## 目录。 ## 22 | 23 | [简介。](#introduction) 24 | 25 | [先决条件。](#prerequisites) 26 | 27 | [复制或下载此存储库。](#Cloning-or-downloading-this-repository) 28 | 29 | [配置 Azure AD 租户。](#Configure-your-Azure-AD-tenant) 30 | 31 | [注册机器人。](#Register-the-bot) 32 | 33 | [提供反馈](#Give-us-your-feedback) 34 | 35 | ## 简介。 36 | 37 | Excel 机器人是一个示例,演示如何使用 [Microsoft Graph](https://graph.microsoft.io)、特别是 [ Excel REST API](https://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/excel) ,并通过会话用户界面访问存储在 OneDrive for Business 中的 Excel 工作簿。采用 C# 编写,使用了[Microsoft 机器人框架](https://dev.botframework.com/)和[语言理解智能服务(LUIS)](https://www.luis.ai/)。 38 | 39 | *注意*:此示例中的代码最初专为用户体验原型编写,并不一定说明如何生成生产指令代码。 40 | 41 | ## 先决条件。 42 | 43 | 44 | 此示例要求如下: 45 | 46 | - Visual Studio 2017。 47 | - Office 365 商业版帐户。你可以注册 [Office 365 开发人员订阅](https://msdn.microsoft.com/en-us/office/office365/howto/setup-development-environment),其中包含你开始构建 Office 365 应用所需的资源。 48 | 49 | ## 复制或下载此存储库。 50 | 51 | 52 | - 复制此存储库至本地文件夹 53 | 54 | ` git clone https://github.com/nicolesigei/botframework-csharp-excelbot-rest-sample.git ` 55 | 56 | 57 | ## 配置 Azure AD 租户。 58 | 59 | 1. 打开浏览器,并转到 [Azure Active Directory 管理中心](https://aad.portal.azure.com)。使用**工作或学校帐户**登录。 60 | 61 | 1. 选择左侧导航栏中的**Azure Active Directory**,再选择**管理**下的**应用注册**。 62 | 63 | ![“应用注册”的屏幕截图](readme-images/aad-portal-app-registrations.png) 64 | 65 | 1. 选择**“新注册”**。在“**注册应用**”页上,按如下方式设置值。 66 | 67 | - 设置首选**名称** ,如`Excel Bot App`。 68 | - 将“**支持的帐户类型**”设置为“**任何组织目录中的帐户**”。 69 | - 在“**重定向 URI**”下,将第一个下拉列表设置为“`Web`”,并将值设置为 http://localhost:3978/callback。 70 | 71 | ![“注册应用程序”页的屏幕截图](readme-images/aad-register-an-app.PNG) 72 | 73 | > **注意:**如果在本地和 Azure 上运行,应在此添加两个重定向 URL,一个至本地实例,一个至 Azure web 应用。 74 | 75 | 1. 选择“**注册**”。在“**Excel 机器人应用程序**”页面上,复制“**应用程序(客户端)ID**”值并保存,在配置应用程序时将会使用此数值。 76 | 77 | ![新应用注册的应用程序 ID 的屏幕截图](readme-images/aad-application-id.PNG) 78 | 79 | 1. 选择“**管理**”下的“**证书和密码**”。选择**新客户端密码**按钮。在**说明**中输入值,并选择一个**过期**选项,再选择**添加**。 80 | 81 | ![“添加客户端密码”对话框的屏幕截图](readme-images/aad-new-client-secret.png) 82 | 83 | 1. 离开此页前,先复制客户端密码值。你将需要它来配置应用。 84 | 85 | > [重要提示!] 86 | > 此客户端密码不会再次显示,所以请务必现在就复制它。 87 | 88 | ![新添加的客户端密码的屏幕截图](readme-images/aad-copy-client-secret.png) 89 | 90 | ## 注册机器人。 91 | 92 | 完成下列步骤设置开发环境,以创建和测试 Excel 机器人: 93 | 94 | - 下载并安装“[Azure Cosmos DB 模拟器](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator)” 95 | 96 | - 在同一目录中创建 **GraphWebHooks/PrivateSettings.example.config** 的副本。将文件命名为 **PrivateSettings.config**。 97 | - 打开 ExcelBot.sln 解决方案文件 98 | - 将机器人注册至“[机器人框架](https://dev.botframework.com/bots/new)” 99 | - 将机器人 MicrosoftAppId 和 MicrosoftAppPassword 复制到 PrivateSettings.config 文件中 100 | - 注册机器人以调用 Microsoft Graph。 101 | - 复制 Azure Active Directory “**客户端 Id**”和“**密码**”至 PrivateSettings.config 文件 102 | - 在 [LUIS](https://www.luis.ai) 服务中新建模型 103 | - 导入 LUIS\\excelbot.json 文件至 LUIS 中 104 | - 训练并发布 LUIS 模型 105 | - 将 LUIS 模型 ID 和订阅密钥复制到 Dialogs\\ExcelBotDialog.cs 文件 106 | - (可选)在机器人框架中启用机器人网络聊天并复制网络聊天嵌入模板至 chat.htm 文件 107 | - (可选)若要获取机器人以发送遥测至 [Visual Studio Application Insights](https://azure.microsoft.com/en-us/services/application-insights/),复制检测密钥至下列文件:ApplicationInsights.config, default.htm, loggedin.htm, chat.htm 108 | - 构建解决方案 109 | - 按下 F5 以本地启动机器人 110 | - 使用[机器人框架模拟器](https://docs.botframework.com/en-us/tools/bot-framework-emulator)来本地测试机器人 111 | - 在使用 SQL API 的 Azure 中创建 Azure Cosmos DB 112 | - 在 PrivateSettings.config 文件中替换机器人主机名 113 | - 在 PrivateSettings.config 文件中替换数据库 URI 和密钥 114 | - 发布解决方案至 Azure web 应用 115 | - 通过浏览至 chat.htm 页面使用网络聊天控件测试部署的机器人 116 | 117 | ## 提供反馈 118 | 119 | 120 | 121 | 我们非常重视你的反馈意见。 122 | 123 | 查看示例代码并在此存储库中直接[提交问题](https://github.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/issues),告诉我们发现的任何疑问和问题。在任何打开话题中提供存储库步骤、控制台输出、错误消息。 124 | 125 | 此项目已采用 [Microsoft 开放源代码行为准则](https://opensource.microsoft.com/codeofconduct/)。有关详细信息,请参阅[行为准则常见问题解答](https://opensource.microsoft.com/codeofconduct/faq/)。如有其他任何问题或意见,也可联系 [opencode@microsoft.com](mailto:opencode@microsoft.com)。 126 | 127 | ## 版权信息 128 | 129 | 版权所有 (c) 2019 Microsoft。保留所有权利。 130 | 131 | -------------------------------------------------------------------------------- /ExcelBot/Dialogs/ExcelBotDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using Microsoft.Bot.Builder.Dialogs; 8 | using Microsoft.Bot.Builder.Luis; 9 | using Microsoft.Bot.Builder.Luis.Models; 10 | using System; 11 | using System.Threading.Tasks; 12 | 13 | namespace ExcelBot.Dialogs 14 | { 15 | [LuisModel("LUIS MODEL ID", "LUIS SUBSCRIPTION KEY", LuisApiVersion.V2)] 16 | [Serializable] 17 | public partial class ExcelBotDialog : GraphDialog 18 | { 19 | #region Constructor 20 | public ExcelBotDialog() 21 | { 22 | } 23 | #endregion 24 | 25 | #region Intents 26 | [LuisIntent("")] 27 | public async Task None(IDialogContext context, LuisResult result) 28 | { 29 | // Telemetry 30 | TelemetryHelper.TrackDialog(context, result, "Bot", "None"); 31 | 32 | // Respond 33 | await context.PostAsync(@"Sorry, I don't understand what you want to do. Type ""help"" to see a list of things I can do."); 34 | context.Wait(MessageReceived); 35 | } 36 | 37 | 38 | [LuisIntent("sayHello")] 39 | public async Task SayHello(IDialogContext context, LuisResult result) 40 | { 41 | try 42 | { 43 | // Telemetry 44 | TelemetryHelper.TrackDialog(context, result, "Bot", "SayHello"); 45 | 46 | // Did the bot already greet the user? 47 | bool saidHello = false; 48 | context.PrivateConversationData.TryGetValue("SaidHello", out saidHello); 49 | 50 | // Get the user data 51 | var userRequest = ServicesHelper.GraphClient.Me.Request(); 52 | var user = await userRequest.GetAsync(); 53 | await ServicesHelper.LogGraphServiceRequest(context, userRequest); 54 | 55 | // Respond 56 | if (saidHello) 57 | { 58 | await context.PostAsync($"Hi again, {user.GivenName}!"); 59 | } 60 | else 61 | { 62 | await context.PostAsync($"Hi, {user.GivenName}!"); 63 | } 64 | 65 | // Record that the bot said hello 66 | context.PrivateConversationData.SetValue("SaidHello", true); 67 | } 68 | catch (Exception ex) 69 | { 70 | await context.PostAsync($"Sorry, something went wrong trying to get information about you ({ex.Message})"); 71 | } 72 | context.Wait(MessageReceived); 73 | } 74 | 75 | [LuisIntent("showHelp")] 76 | public async Task ShowHelp(IDialogContext context, LuisResult result) 77 | { 78 | // Telemetry 79 | TelemetryHelper.TrackDialog(context, result, "Bot", "ShowHelp"); 80 | 81 | // Respond 82 | await context.PostAsync($@"Here is a list of things I can do for you: 83 | * Open a workbook on your OneDrive for Business. For example, type ""look at sales 2016"" if you want to work with ""Sales 2016.xlsx"" in the root folder of your OneDrive for Business 84 | * List worksheets in the workbook and select a worksheet. For example, ""which worksheets are in the workbook?"", ""select worksheet"" or ""select Sheet3"" 85 | * Get and set the value of a cell. For example, type ""what is the value of A1?"" or ""change B57 to 5"" 86 | * List names defined in the workbook. For example, type ""Which names are in the workbook?"" 87 | * Get and set the value of a named item, for example, type ""show me TotalSales"" or ""set cost to 100"" 88 | * List the tables in the workbook. For example, type ""Show me the tables"" 89 | * Show the rows in a table. For example, type ""Show customers"" 90 | * Look up a row in a table. For example, type ""Lookup Contoso in customers"" or ""lookup Contoso"" 91 | * Add a row to a table. For example, type ""Add breakfast for $10 to expenses"" 92 | * Change the value of a cell in a table row. For example, first type ""lookup contoso in customers"", then type ""change segment to enterprise"" 93 | * List the charts in the workbook. For example, type ""Which charts are in the workbook?"" 94 | * Get the image of a chart. For example, type ""Show me Chart 1"""); 95 | 96 | await context.PostAsync($@"Remember I'm just a bot. There are many things I still need to learn, so please tell me what you want me to get better at."); 97 | 98 | context.Wait(MessageReceived); 99 | } 100 | 101 | #endregion 102 | } 103 | } -------------------------------------------------------------------------------- /ExcelBot/Workers/CellWorker.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using Microsoft.Bot.Builder.Dialogs; 8 | using Microsoft.Graph; 9 | using Newtonsoft.Json.Linq; 10 | using System; 11 | using System.Threading.Tasks; 12 | 13 | namespace ExcelBot.Workers 14 | { 15 | public static class CellWorker 16 | { 17 | #region Get Cell Values 18 | public static async Task DoGetCellValue(IDialogContext context) 19 | { 20 | var workbookId = context.UserData.GetValue("WorkbookId"); 21 | var worksheetId = context.UserData.GetValue("WorksheetId"); 22 | var cellAddress = context.UserData.GetValue("CellAddress"); 23 | 24 | await ReplyWithValue(context, workbookId, worksheetId, cellAddress); 25 | } 26 | #endregion 27 | 28 | #region Set Cell Values 29 | public static async Task DoSetCellNumberValue(IDialogContext context, double value) 30 | { 31 | var workbookId = context.UserData.GetValue("WorkbookId"); 32 | var worksheetId = context.UserData.GetValue("WorksheetId"); 33 | var cellAddress = context.UserData.GetValue("CellAddress"); 34 | 35 | await SetCellValue(context, workbookId, worksheetId, cellAddress, value); 36 | } 37 | 38 | public static async Task DoSetCellStringValue(IDialogContext context, string value) 39 | { 40 | var workbookId = context.UserData.GetValue("WorkbookId"); 41 | var worksheetId = context.UserData.GetValue("WorksheetId"); 42 | var cellAddress = context.UserData.GetValue("CellAddress"); 43 | 44 | await SetCellValue(context, workbookId, worksheetId, cellAddress, value); 45 | } 46 | 47 | public static async Task DoSetCellValue(IDialogContext context, object value) 48 | { 49 | var workbookId = context.UserData.GetValue("WorkbookId"); 50 | var worksheetId = context.UserData.GetValue("WorksheetId"); 51 | var cellAddress = context.UserData.GetValue("CellAddress"); 52 | 53 | await SetCellValue(context, workbookId, worksheetId, cellAddress, value); 54 | } 55 | #endregion 56 | 57 | #region Helpers 58 | public static async Task SetCellValue(IDialogContext context, string workbookId, string worksheetId, string cellAddress, object value) 59 | { 60 | try 61 | { 62 | var newValue = new WorkbookRange() 63 | { 64 | Values = JToken.Parse($"[[\"{value}\"]]") 65 | }; 66 | 67 | var headers = ServicesHelper.GetWorkbookSessionHeader( 68 | await ExcelHelper.GetSessionIdForUpdateAsync(context)); 69 | 70 | var updateRangeRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 71 | .Workbook.Worksheets[worksheetId] 72 | .Range(cellAddress).Request(headers); 73 | 74 | var range = await updateRangeRequest.PatchAsync(newValue); 75 | await ServicesHelper.LogGraphServiceRequest(context, updateRangeRequest, newValue); 76 | 77 | await context.PostAsync($"**{cellAddress}** is now **{range.Text[0][0]}**"); 78 | } 79 | catch (Exception ex) 80 | { 81 | await context.PostAsync($"Sorry, something went wrong setting the value of **{cellAddress}** to **{value.ToString()}** ({ex.Message})"); 82 | } 83 | } 84 | 85 | public static async Task ReplyWithValue(IDialogContext context, string workbookId, string worksheetId, string cellAddress) 86 | { 87 | try 88 | { 89 | var headers = ServicesHelper.GetWorkbookSessionHeader( 90 | ExcelHelper.GetSessionIdForRead(context)); 91 | 92 | var rangeRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 93 | .Workbook.Worksheets[worksheetId].Range(cellAddress).Request(headers); 94 | 95 | var range = await rangeRequest.GetAsync(); 96 | await ServicesHelper.LogGraphServiceRequest(context, rangeRequest); 97 | 98 | if ((string)(range.ValueTypes[0][0]) != "Empty") 99 | { 100 | await context.PostAsync($"**{cellAddress}** is **{range.Text[0][0]}**"); 101 | } 102 | else 103 | { 104 | await context.PostAsync($"**{cellAddress}** is empty"); 105 | } 106 | } 107 | catch (Exception ex) 108 | { 109 | await context.PostAsync($"Sorry, something went wrong getting the value of **{cellAddress}** ({ex.Message})"); 110 | } 111 | } 112 | #endregion 113 | } 114 | } -------------------------------------------------------------------------------- /ExcelBot/Dialogs/TablesDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using ExcelBot.Model; 8 | using ExcelBot.Workers; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Builder.Luis.Models; 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace ExcelBot.Dialogs 15 | { 16 | public partial class ExcelBotDialog : GraphDialog 17 | { 18 | #region Properties 19 | internal object[] Rows { get; set; } 20 | #endregion 21 | 22 | #region Intents 23 | #region - List Tables 24 | [LuisIntent("listTables")] 25 | public async Task ListTables(IDialogContext context, LuisResult result) 26 | { 27 | // Telemetry 28 | TelemetryHelper.TrackDialog(context, result, "Tables", "ListTables"); 29 | 30 | string workbookId = String.Empty; 31 | context.UserData.TryGetValue("WorkbookId", out workbookId); 32 | 33 | if (!(String.IsNullOrEmpty(workbookId))) 34 | { 35 | await TablesWorker.DoListTables(context); 36 | context.Wait(MessageReceived); 37 | } 38 | else 39 | { 40 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_ListTables); 41 | } 42 | } 43 | public async Task AfterConfirm_ListTables(IDialogContext context, IAwaitable result) 44 | { 45 | if (await result) 46 | { 47 | await TablesWorker.DoListTables(context); 48 | } 49 | context.Wait(MessageReceived); 50 | } 51 | 52 | #endregion 53 | #region - Lookup Table Row 54 | [LuisIntent("lookupTableRow")] 55 | public async Task LookupTableRow(IDialogContext context, LuisResult result) 56 | { 57 | // Telemetry 58 | TelemetryHelper.TrackDialog(context, result, "Tables", "LookupTableRow"); 59 | 60 | string workbookId = String.Empty; 61 | context.UserData.TryGetValue("WorkbookId", out workbookId); 62 | 63 | var name = LuisHelper.GetNameEntity(result.Entities); 64 | if (name != null) 65 | { 66 | context.UserData.SetValue("TableName", name); 67 | 68 | context.UserData.SetValue("Type", ObjectType.Table); 69 | context.UserData.SetValue("Name", name); 70 | } 71 | 72 | Value = (LuisHelper.GetValue(result))?.ToString(); 73 | 74 | if (!(String.IsNullOrEmpty(workbookId))) 75 | { 76 | await TablesWorker.DoLookupTableRow(context, (string)Value); 77 | context.Wait(MessageReceived); 78 | } 79 | else 80 | { 81 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_LookupTableRow); 82 | } 83 | } 84 | public async Task AfterConfirm_LookupTableRow(IDialogContext context, IAwaitable result) 85 | { 86 | if (await result) 87 | { 88 | await TablesWorker.DoLookupTableRow(context, (string)Value); 89 | } 90 | context.Wait(MessageReceived); 91 | } 92 | 93 | #endregion 94 | #region - Add Table Row 95 | [LuisIntent("addTableRow")] 96 | public async Task AddTableRow(IDialogContext context, LuisResult result) 97 | { 98 | // Telemetry 99 | TelemetryHelper.TrackDialog(context, result, "Tables", "AddTableRow"); 100 | 101 | // Extract the name of the table from the query and save it 102 | var name = LuisHelper.GetNameEntity(result.Entities); 103 | if (name != null) 104 | { 105 | context.UserData.SetValue("TableName", name); 106 | 107 | context.UserData.SetValue("Type", ObjectType.Table); 108 | context.UserData.SetValue("Name", name); 109 | } 110 | 111 | // Get the new table row from the query and persist it in the dialog 112 | Rows = new object[] 113 | { 114 | LuisHelper.GetTableRow(result.Entities, result.Query) 115 | }; 116 | 117 | // Check if there is an open workbook and add the row to the table 118 | string workbookId = String.Empty; 119 | context.UserData.TryGetValue("WorkbookId", out workbookId); 120 | 121 | if (!(String.IsNullOrEmpty(workbookId))) 122 | { 123 | await TablesWorker.DoAddTableRow(context, Rows); 124 | context.Wait(MessageReceived); 125 | } 126 | else 127 | { 128 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_AddTableRow); 129 | } 130 | } 131 | public async Task AfterConfirm_AddTableRow(IDialogContext context, IAwaitable result) 132 | { 133 | if (await result) 134 | { 135 | await TablesWorker.DoAddTableRow(context, Rows); 136 | } 137 | context.Wait(MessageReceived); 138 | } 139 | 140 | #endregion 141 | #endregion 142 | } 143 | } -------------------------------------------------------------------------------- /README-localized/README-ja-jp.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-excel 5 | - ms-graph 6 | languages: 7 | - csharp 8 | description: "Excel Bot は Microsoft Bot Framework を使用してビルドされたボットで、Excel で Microsoft Graph API を使用する方法を示しています" 9 | extensions: 10 | contentType: samples 11 | technologies: 12 | - Microsoft Graph 13 | - Microsoft Bot Framework 14 | services: 15 | - Excel 16 | createdDate: 9/15/2016 10:30:08 PM 17 | --- 18 | 19 | # Excel Bot 20 | 21 | ## 目次 ## 22 | 23 | [概要](#introduction) 24 | 25 | [前提条件](#prerequisites) 26 | 27 | [このリポジトリの複製またはダウンロード](#Cloning-or-downloading-this-repository) 28 | 29 | [Azure AD テナントを構成する](#Configure-your-Azure-AD-tenant) 30 | 31 | [ボットを登録する](#Register-the-bot) 32 | 33 | [フィードバックをお寄せください](#Give-us-your-feedback) 34 | 35 | ## 概要 36 | 37 | Excel Bot は、[Microsoft Graph](https://graph.microsoft.io)、特に [Excel REST API](https://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/excel) を使用して、会話型ユーザー インターフェイスを介して OneDrive for Business に保存されている Excel ブックにアクセスする方法を示すサンプルです。C#で記述されており、[Microsoft Bot Framework](https://dev.botframework.com/) と [Language Understanding Intelligent Service (LUIS)](https://www.luis.ai/) を使用します。 38 | 39 | *注*: このサンプルのコードは、もともとユーザー エクスペリエンス プロトタイプ用に作成されたものであり、必ずしも生産品質コードの作成方法を示すものではありません。 40 | 41 | ## 前提条件 42 | 43 | 44 | このサンプルを実行するには次のものが必要です。 45 | 46 | - Visual Studio 2017。 47 | - ビジネス向けの Office 365 アカウント。Office 365 アプリのビルドを開始するために必要なリソースを含む [Office 365 Developer サブスクリプション](https://msdn.microsoft.com/en-us/office/office365/howto/setup-development-environment)にサインアップできます。 48 | 49 | ## このリポジトリの複製またはダウンロード。 50 | 51 | 52 | - このリポジトリをローカル フォルダーに複製する 53 | 54 | ` git clone https://github.com/nicolesigei/botframework-csharp-excelbot-rest-sample.git ` 55 | 56 | 57 | ## Azure AD テナントを構成します。 58 | 59 | 1. ブラウザーを開き、[Azure Active Directory 管理センター](https://aad.portal.azure.com)に移動します。**職場または学校のアカウント**を使用してログインします。 60 | 61 | 1. 左側のナビゲーションで [**Azure Active Directory**] を選択し、次に [**管理**] で [**アプリの登録**] を選択します。 62 | 63 | ![アプリの登録のスクリーンショット ](readme-images/aad-portal-app-registrations.png) 64 | 65 | 1. **[新規登録]** を選択します。[**アプリケーションの登録**] ページで、次のように値を設定します。 66 | 67 | - `Excel Bot App` のように、好みの **[名前]** を設定します。 68 | - [**サポートされているアカウントの種類**] を [**任意の組織のディレクトリ内のアカウント**] に設定します。 69 | - [**リダイレクト URI**] で、最初のドロップダウン リストを [`Web`] に設定し、それから http://localhost:3978/callback に値を設定します。 70 | 71 | ![[アプリケーションを登録する] ページのスクリーンショット](readme-images/aad-register-an-app.PNG) 72 | 73 | > **注:**これをローカルおよび Azure で実行している場合、2 つのリダイレクト URL を追加する必要があります。1 つはローカル インスタンスに、もう 1 つは Azure Web アプリに追加します。 74 | 75 | 1. [**登録**] を選択します。**Excel Bot App** ページで、**アプリケーション (クライアント) の ID** の値をコピーして保存します。この値はアプリの構成に必要になります。 76 | 77 | ![新しいアプリ登録のアプリケーション ID のスクリーンショット](readme-images/aad-application-id.PNG) 78 | 79 | 1. [**管理**] で [**証明書とシークレット**] を選択します。[**新しいクライアント シークレット**] ボタンを選択します。[**説明**] に値を入力し、[**有効期限**] のオプションのいずれかを選び、[**追加**] を選択します。 80 | 81 | ![[クライアント シークレットの追加] ダイアログのスクリーンショット](readme-images/aad-new-client-secret.png) 82 | 83 | 1. このページを離れる前に、クライアント シークレットの値をコピーします。この値はアプリの構成に必要になります。 84 | 85 | > [重要!] 86 | > このクライアント シークレットは今後表示されないため、この段階で必ずコピーするようにしてください。 87 | 88 | ![新規追加されたクライアント シークレットのスクリーンショット](readme-images/aad-copy-client-secret.png) 89 | 90 | ## ボットを登録します。 91 | 92 | 以下の手順を実行して、開発環境をセットアップし、Excel ボットをビルドおよびテストします。 93 | 94 | - [Azure Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator) をダウンロードしてインストールします 95 | 96 | - **./ExcelBot/PrivateSettings.config.example** のコピーを同じディレクトリに作成します。ファイルに **PrivateSettings.config** という名前を付けます。 97 | - ExcelBot.sln ソリューション ファイルを開きます 98 | - [Bot Framework](https://dev.botframework.com/bots/new) にボットを登録する 99 | - ボットの MicrosoftAppId および MicrosoftAppPassword を PrivateSettings.config ファイルにコピーします 100 | - ボットを登録して、Microsoft Graph を呼び出します。 101 | - Azure Active Directory **クライアント ID** と**シークレット**を PrivateSettings.config ファイルにコピーします 102 | - [LUIS](https://www.luis.ai) サービスに新しいモデルを作成します 103 | - LUIS\\excelbot.json ファイルを LUIS にインポートします 104 | - LUIS モデルをトレーニングして発行します 105 | - LUIS モデル ID とサブスクリプション キーを Dialogs\\ExcelBotDialog.cs ファイルにコピーします 106 | - (オプション) Bot Framework でボットの Web チャットを有効にし、Web チャットの埋め込みテンプレート chat.htm ファイルをコピーします 107 | - (オプション) ボットが [Visual Studio Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) にテレメトリを送信できるようにするには、インストルメンテーション キーを次のファイルにコピーします。ApplicationInsights.config、default.htm、loggedin.htm、chat.htm 108 | - ソリューションをビルドします 109 | - F5 キーを押してボットをローカルで起動します 110 | - [Bot Framework Emulator](https://docs.botframework.com/en-us/tools/bot-framework-emulator) を使用してボットをローカルでテストします 111 | - SQL API を使用する Azure で Azure Cosmos DB を作成します 112 | - PrivateSettings.config ファイル内のボットのホスト名を置き換えます 113 | - PrivateSettings.config ファイル内のデータベース URI およびキーを置き換えます 114 | - ソリューションを Azure Web アプリに発行する 115 | - chat.htm ページを参照することにより、Web チャット コントロールを使用して展開されたボットをテストします 116 | 117 | ## フィードバックをお寄せください 118 | 119 | 120 | 121 | お客様からのフィードバックを重視しています。 122 | 123 | サンプル コードを確認していだだき、質問や問題があれば、直接このリポジトリに[問題を送信](https://github.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/issues)してお知らせください。発生したエラーの再現手順、コンソール出力、およびエラー メッセージをご提供ください。 124 | 125 | このプロジェクトでは、[Microsoft オープン ソース倫理規定](https://opensource.microsoft.com/codeofconduct/) が採用されています。詳細については、「[倫理規定の FAQ](https://opensource.microsoft.com/codeofconduct/faq/)」を参照してください。また、その他の質問やコメントがあれば、[opencode@microsoft.com](mailto:opencode@microsoft.com) までお問い合わせください。 126 | 127 | ## 著作権 128 | 129 | Copyright (c) 2019 Microsoft.All rights reserved. 130 | 131 | -------------------------------------------------------------------------------- /ExcelBot/Dialogs/NamedItemsDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using ExcelBot.Model; 8 | using ExcelBot.Workers; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Builder.Luis.Models; 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace ExcelBot.Dialogs 15 | { 16 | public partial class ExcelBotDialog : GraphDialog 17 | { 18 | #region Properties 19 | #endregion 20 | 21 | #region Intents 22 | #region - List Named Items 23 | [LuisIntent("listNamedItems")] 24 | public async Task ListNamedItems(IDialogContext context, LuisResult result) 25 | { 26 | // Telemetry 27 | TelemetryHelper.TrackDialog(context, result, "NamedItems", "ListNamedItems"); 28 | 29 | string workbookId = String.Empty; 30 | context.UserData.TryGetValue("WorkbookId", out workbookId); 31 | 32 | if (!(String.IsNullOrEmpty(workbookId))) 33 | { 34 | await NamedItemsWorker.DoListNamedItems(context); 35 | context.Wait(MessageReceived); 36 | } 37 | else 38 | { 39 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_ListNamedItems); 40 | } 41 | } 42 | public async Task AfterConfirm_ListNamedItems(IDialogContext context, IAwaitable result) 43 | { 44 | if (await result) 45 | { 46 | await NamedItemsWorker.DoListNamedItems(context); 47 | } 48 | context.Wait(MessageReceived); 49 | } 50 | 51 | #endregion 52 | #region - Get Value of Named Item 53 | [LuisIntent("getNamedItemValue")] 54 | public async Task GetNamedItemValue(IDialogContext context, LuisResult result) 55 | { 56 | // Telemetry 57 | TelemetryHelper.TrackDialog(context, result, "NamedItems", "GetNamedItemValue"); 58 | 59 | var name = LuisHelper.GetNameEntity(result.Entities); 60 | 61 | if (!(String.IsNullOrEmpty(name))) 62 | { 63 | context.UserData.SetValue("Name", name); 64 | context.UserData.SetValue("Type", ObjectType.NamedItem); 65 | 66 | string workbookId = String.Empty; 67 | context.UserData.TryGetValue("WorkbookId", out workbookId); 68 | 69 | if (!(String.IsNullOrEmpty(workbookId))) 70 | { 71 | await NamedItemsWorker.DoGetNamedItemValue(context); 72 | context.Wait(MessageReceived); 73 | } 74 | else 75 | { 76 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_GetNamedItemValue); 77 | } 78 | } 79 | else 80 | { 81 | await context.PostAsync($"You need to provide a name to get the value"); 82 | context.Wait(MessageReceived); 83 | } 84 | } 85 | public async Task AfterConfirm_GetNamedItemValue(IDialogContext context, IAwaitable result) 86 | { 87 | if (await result) 88 | { 89 | await NamedItemsWorker.DoGetNamedItemValue(context); 90 | } 91 | context.Wait(MessageReceived); 92 | } 93 | #endregion 94 | #region - Set Value of Named Item 95 | 96 | [LuisIntent("setNamedItemValue")] 97 | public async Task SetNamedItemValue(IDialogContext context, LuisResult result) 98 | { 99 | // Telemetry 100 | TelemetryHelper.TrackDialog(context, result, "NamedItems", "SetNamedItemValue"); 101 | 102 | ObjectType type; 103 | if (!(context.UserData.TryGetValue("Type", out type))) 104 | { 105 | type = ObjectType.NamedItem; 106 | context.UserData.SetValue("Type", type); 107 | } 108 | 109 | var name = LuisHelper.GetNameEntity(result.Entities); 110 | if (name != null) 111 | { 112 | context.UserData.SetValue("Name", name); 113 | } 114 | 115 | Value = LuisHelper.GetValue(result); 116 | 117 | string workbookId = String.Empty; 118 | context.UserData.TryGetValue("WorkbookId", out workbookId); 119 | 120 | if (!(String.IsNullOrEmpty(workbookId))) 121 | { 122 | string worksheetId = String.Empty; 123 | context.UserData.TryGetValue("WorksheetId", out worksheetId); 124 | 125 | if (name != null) 126 | { 127 | await NamedItemsWorker.DoSetNamedItemValue(context, Value); 128 | } 129 | else 130 | { 131 | await context.PostAsync($"You need to provide a name to set the value"); 132 | } 133 | context.Wait(MessageReceived); 134 | } 135 | else 136 | { 137 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_SetNamedItem); 138 | } 139 | } 140 | 141 | public async Task AfterConfirm_SetNamedItem(IDialogContext context, IAwaitable result) 142 | { 143 | if (await result) 144 | { 145 | await NamedItemsWorker.DoSetNamedItemValue(context, Value); 146 | } 147 | context.Wait(MessageReceived); 148 | } 149 | #endregion 150 | #endregion 151 | } 152 | } -------------------------------------------------------------------------------- /ExcelBot/Workers/WorksheetWorker.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using Microsoft.Bot.Builder.Dialogs; 8 | using Microsoft.Graph; 9 | using System; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace ExcelBot.Workers 15 | { 16 | public static class WorksheetWorker 17 | { 18 | #region List Worksheets 19 | public static async Task DoListWorksheetsAsync(IDialogContext context) 20 | { 21 | var workbookId = context.UserData.GetValue("WorkbookId"); 22 | var worksheetId = context.UserData.GetValue("WorksheetId"); 23 | 24 | try 25 | { 26 | var headers = ServicesHelper.GetWorkbookSessionHeader( 27 | ExcelHelper.GetSessionIdForRead(context)); 28 | 29 | var worksheetsRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 30 | .Workbook.Worksheets.Request(headers); 31 | 32 | var worksheets = await worksheetsRequest.GetAsync(); 33 | await ServicesHelper.LogGraphServiceRequest(context, worksheetsRequest); 34 | 35 | var reply = new StringBuilder(); 36 | 37 | if (worksheets.Count == 1) 38 | { 39 | reply.Append($"There is **1** worksheet in the workbook:\n"); 40 | } 41 | else 42 | { 43 | reply.Append($"There are **{worksheets.Count}** worksheets in the workbook:\n"); 44 | } 45 | 46 | var active = ""; 47 | foreach (var worksheet in worksheets) 48 | { 49 | active = (worksheet.Name.ToLower() == worksheetId.ToLower()) ? " (active)" : ""; 50 | reply.Append($"* **{worksheet.Name}**{active}\n"); 51 | } 52 | await context.PostAsync(reply.ToString()); 53 | } 54 | catch (Exception ex) 55 | { 56 | await context.PostAsync($"Sorry, something went wrong getting the worksheets ({ex.Message})"); 57 | } 58 | } 59 | #endregion 60 | 61 | #region Select Worksheet 62 | public static async Task DoSelectWorksheetAsync(IDialogContext context, string worksheetName) 63 | { 64 | try 65 | { 66 | var workbookId = context.UserData.GetValue("WorkbookId"); 67 | var worksheetId = context.UserData.GetValue("WorksheetId"); 68 | 69 | // Check if we are already working with the new worksheet 70 | if (worksheetName.ToLower() == worksheetId.ToLower()) 71 | { 72 | await context.PostAsync($"We are already working with the **{worksheetId}** worksheet"); 73 | return; 74 | } 75 | 76 | // Check if the new worksheet exist 77 | var headers = ServicesHelper.GetWorkbookSessionHeader( 78 | ExcelHelper.GetSessionIdForRead(context)); 79 | 80 | var worksheetsRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 81 | .Workbook.Worksheets.Request(headers); 82 | 83 | var worksheets = await worksheetsRequest.GetAsync(); 84 | await ServicesHelper.LogGraphServiceRequest(context, worksheetsRequest); 85 | 86 | var lowerWorksheetName = worksheetName.ToLower(); 87 | var worksheet = worksheets.FirstOrDefault(w => w.Name.ToLower() == lowerWorksheetName); 88 | if (worksheet == null) 89 | { 90 | await context.PostAsync($@"**{worksheetName}** is not a worksheet in the workbook. Type ""select worksheet"" to select the worksheet from a list"); 91 | return; 92 | } 93 | 94 | // Save the worksheet id 95 | context.UserData.SetValue("WorksheetId", worksheet.Name); 96 | 97 | // Respond 98 | await context.PostAsync($"We are ready to work with the **{worksheet.Name}** worksheet"); 99 | } 100 | catch (Exception ex) 101 | { 102 | await context.PostAsync($"Sorry, something went wrong selecting the {worksheetName} worksheet ({ex.Message})"); 103 | } 104 | } 105 | #endregion 106 | 107 | #region Get Active Worksheet 108 | public static async Task DoGetActiveWorksheetAsync(IDialogContext context) 109 | { 110 | var worksheetId = context.UserData.GetValue("WorksheetId"); 111 | 112 | // Respond 113 | await context.PostAsync($"We are on the **{worksheetId}** worksheet"); 114 | } 115 | #endregion 116 | 117 | #region Helpers 118 | public async static Task GetWorksheetNamesAsync(IDialogContext context, string workbookId) 119 | { 120 | try 121 | { 122 | var headers = ServicesHelper.GetWorkbookSessionHeader( 123 | ExcelHelper.GetSessionIdForRead(context)); 124 | 125 | var worksheetsRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 126 | .Workbook.Worksheets.Request(headers); 127 | 128 | var worksheets = await worksheetsRequest.GetAsync(); 129 | await ServicesHelper.LogGraphServiceRequest(context, worksheetsRequest); 130 | 131 | return worksheets.Select(w => w.Name).ToArray(); 132 | } 133 | catch (Exception) 134 | { 135 | return new string[] { }; 136 | } 137 | } 138 | #endregion 139 | } 140 | } -------------------------------------------------------------------------------- /ExcelBot/Workers/ChartsWorker.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using ExcelBot.Model; 8 | using Microsoft.Bot.Builder.Dialogs; 9 | using Microsoft.Graph; 10 | using System; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Web; 14 | 15 | namespace ExcelBot.Workers 16 | { 17 | public static class ChartsWorker 18 | { 19 | #region List Charts 20 | public static async Task DoListCharts(IDialogContext context) 21 | { 22 | var workbookId = context.UserData.GetValue("WorkbookId"); 23 | var worksheetId = context.UserData.GetValue("WorksheetId"); 24 | 25 | try 26 | { 27 | var headers = ServicesHelper.GetWorkbookSessionHeader( 28 | ExcelHelper.GetSessionIdForRead(context)); 29 | 30 | var chartsRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 31 | .Workbook.Worksheets[worksheetId].Charts.Request(headers); 32 | 33 | var charts = await chartsRequest.GetAsync(); 34 | await ServicesHelper.LogGraphServiceRequest(context, chartsRequest); 35 | 36 | if (charts.Count > 0) 37 | { 38 | var reply = new StringBuilder(); 39 | 40 | if (charts.Count == 1) 41 | { 42 | reply.Append($"There is **1** chart on **{worksheetId}**:\n"); 43 | } 44 | else 45 | { 46 | reply.Append($"There are **{charts.Count}** on **{worksheetId}**:\n"); 47 | } 48 | 49 | foreach (var chart in charts) 50 | { 51 | reply.Append($"* **{chart.Name}**\n"); 52 | } 53 | await context.PostAsync(reply.ToString()); 54 | } 55 | else 56 | { 57 | await context.PostAsync($"There are no charts on {worksheetId}"); 58 | } 59 | } 60 | catch (Exception ex) 61 | { 62 | await context.PostAsync($"Sorry, something went wrong getting the charts ({ex.Message})"); 63 | } 64 | } 65 | #endregion 66 | #region Get the Image of a Chart 67 | public static async Task DoGetChartImage(IDialogContext context) 68 | { 69 | var workbookId = context.UserData.GetValue("WorkbookId"); 70 | var worksheetId = context.UserData.GetValue("WorksheetId"); 71 | var name = context.UserData.GetValue("ChartName"); 72 | 73 | // Get the chart 74 | var chart = await GetChart(context, workbookId, worksheetId, name); 75 | if (chart != null) 76 | { 77 | await ReplyWithChart(context, workbookId, worksheetId, chart); 78 | } 79 | else 80 | { 81 | await context.PostAsync($"**{name}** is not a chart on **{worksheetId}**"); 82 | } 83 | } 84 | #endregion 85 | 86 | #region Helpers 87 | // Lookup a name assuming that it is named item, return null if it doesn't exist 88 | public static async Task GetChart(IDialogContext context, string workbookId, string worksheetId, string name) 89 | { 90 | WorkbookChart chart = null; 91 | try 92 | { 93 | var headers = ServicesHelper.GetWorkbookSessionHeader( 94 | ExcelHelper.GetSessionIdForRead(context)); 95 | 96 | var chartRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 97 | .Workbook.Worksheets[worksheetId].Charts[name].Request(headers); 98 | 99 | chart = await chartRequest.GetAsync(); 100 | await ServicesHelper.LogGraphServiceRequest(context, chartRequest); 101 | } 102 | catch 103 | { 104 | } 105 | return chart; 106 | } 107 | 108 | public static async Task ReplyWithChart(IDialogContext context, string workbookId, string worksheetId, WorkbookChart chart) 109 | { 110 | try 111 | { 112 | // Create and save user nonce 113 | var userNonce = (Guid.NewGuid()).ToString(); 114 | context.ConversationData.SetValue( 115 | userNonce, 116 | new ChartAttachment() 117 | { 118 | WorkbookId = workbookId, 119 | WorksheetId = worksheetId, 120 | ChartId = chart.Id 121 | }); 122 | await context.FlushAsync(context.CancellationToken); 123 | 124 | // Reply with chart URL attached 125 | var reply = context.MakeMessage(); 126 | reply.Recipient.Id = (reply.Recipient.Id != null) ? reply.Recipient.Id : (string)(HttpContext.Current.Items["UserId"]); 127 | 128 | var image = new Microsoft.Bot.Connector.Attachment() 129 | { 130 | ContentType = "image/png", 131 | ContentUrl = $"{RequestHelper.RequestUri.Scheme}://{RequestHelper.RequestUri.Authority}/api/{reply.ChannelId}/{reply.Conversation.Id}/{reply.Recipient.Id}/{userNonce}/image" 132 | }; 133 | 134 | reply.Attachments.Add(image); 135 | await context.PostAsync(reply); 136 | } 137 | catch (Exception ex) 138 | { 139 | await context.PostAsync($"Sorry, something went wrong getting the **{chart.Name}** chart ({ex.Message})"); 140 | } 141 | } 142 | #endregion 143 | } 144 | } -------------------------------------------------------------------------------- /ExcelBot/Helpers/LuisHelper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.Bot.Builder.Luis.Models; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | 12 | namespace ExcelBot.Helpers 13 | { 14 | public static class LuisHelper 15 | { 16 | public static string GetCellEntity(IList entities) 17 | { 18 | var entity = entities.FirstOrDefault((e) => e.Type == "Cell"); 19 | return (entity != null) ? entity.Entity.ToUpper() : null; 20 | } 21 | 22 | public static string GetNameEntity(IList entities) 23 | { 24 | var index = entities.IndexOf((e) => e.Type == "Name"); 25 | if (index >= 0) 26 | { 27 | var name = new StringBuilder(); 28 | var separator = ""; 29 | while ((index < entities.Count) && (entities[index].Type == "Name")) 30 | { 31 | name.Append($"{separator}{entities[index].Entity}"); 32 | separator = " "; 33 | ++index; 34 | } 35 | return name.ToString().Replace(" _ ","_").Replace(" - ", "-"); 36 | } 37 | else 38 | { 39 | return null; 40 | } 41 | } 42 | 43 | public static string GetChartEntity(IList entities) 44 | { 45 | var names = entities.Where((e) => (e.Type == "Name")); 46 | if (names != null) 47 | { 48 | var name = new StringBuilder(); 49 | var separator = ""; 50 | foreach (var entitiy in names) 51 | { 52 | name.Append($"{separator}{entitiy.Entity}"); 53 | separator = " "; 54 | } 55 | return name.ToString(); 56 | } 57 | else 58 | { 59 | return null; 60 | } 61 | } 62 | 63 | public static object GetValue(LuisResult result) 64 | { 65 | if (result.Entities.Count == 0) 66 | { 67 | // There is no entities in the query 68 | return null; 69 | } 70 | 71 | // Check for a string value 72 | var first = result.Entities.FirstOrDefault(er => ((er.Type == "builtin.number") || (er.Type == "Text") || (er.Type == "Workbook"))); 73 | if (first != null) 74 | { 75 | var startIndex = (int)(result.Entities.Where(er => ((er.Type == "builtin.number") || (er.Type == "Text") || (er.Type == "Workbook"))).Min(er => er.StartIndex)); 76 | var endIndex = (int)(result.Entities.Where(er => ((er.Type == "builtin.number") || (er.Type == "Text") || (er.Type == "Workbook"))).Max(er => er.EndIndex)); 77 | return result.Query.Substring(startIndex, endIndex - startIndex + 1); 78 | } 79 | 80 | // Check for a number value 81 | var numberEntity = result.Entities.FirstOrDefault(er => er.Type == "builtin.number"); 82 | if (numberEntity != null) 83 | { 84 | // There is a number entity in the query 85 | return Double.Parse(numberEntity.Entity.Replace(" ", "")); 86 | } 87 | 88 | // No value was found 89 | return null; 90 | } 91 | 92 | public static string GetFilenameEntity(IList entities) 93 | { 94 | var sb = new StringBuilder(); 95 | var separator = ""; 96 | foreach (var entity in entities) 97 | { 98 | if (entity.Entity != "xlsx") 99 | { 100 | sb.Append(separator); 101 | sb.Append(entity.Entity); 102 | separator = " "; 103 | } 104 | } 105 | var filename = sb.ToString().Replace(" _ ", "_").Replace(" - ", "-"); 106 | return filename; 107 | } 108 | 109 | public static object[] GetTableRow(IList entities, string query) 110 | { 111 | var items = new List(); 112 | var sb = new StringBuilder(); 113 | var separator = ""; 114 | foreach (var entity in entities.OrderBy(e => e.StartIndex)) 115 | { 116 | if (entity.Type.ToLower() == "text") 117 | { 118 | if ((entity.Entity == ",") || (entity.Entity == ";")) 119 | { 120 | if (sb.Length > 0) 121 | { 122 | items.Add(sb.ToString()); 123 | sb.Clear(); 124 | separator = ""; 125 | } 126 | } 127 | else 128 | { 129 | sb.Append($"{separator}{query.Substring(entity.StartIndex ?? 0, entity.EndIndex - entity.StartIndex + 1 ?? 0)}"); 130 | separator = " "; 131 | } 132 | } 133 | else if (entity.Type.ToLower() == "builtin.number") 134 | { 135 | if (sb.Length > 0) 136 | { 137 | items.Add(sb.ToString()); 138 | sb.Clear(); 139 | separator = ""; 140 | } 141 | items.Add(Double.Parse(entity.Entity)); 142 | } 143 | } 144 | if (sb.Length > 0) 145 | { 146 | items.Add(sb.ToString()); 147 | } 148 | return items.ToArray(); 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /ExcelBot/Controllers/ChartController.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using ExcelBot.Model; 8 | using Newtonsoft.Json.Linq; 9 | using System; 10 | using System.IO; 11 | using System.Net; 12 | using System.Net.Http; 13 | using System.Net.Http.Headers; 14 | using System.Threading.Tasks; 15 | using System.Web.Http; 16 | 17 | namespace ExcelBot 18 | { 19 | public class ChartController : ApiController 20 | { 21 | [HttpGet, Route("api/{channelId}/{conversationId}/{userId}/{userNonce}/image")] 22 | public async Task Image(string channelId, string conversationId, string userId, string userNonce) 23 | { 24 | // Save the request url 25 | RequestHelper.RequestUri = Request.RequestUri; 26 | 27 | ChartAttachment chartAttachment = null; 28 | 29 | try 30 | { 31 | var conversationData = await BotStateHelper.GetConversationDataAsync(channelId, conversationId); 32 | chartAttachment = conversationData.GetProperty(userNonce); 33 | 34 | if (chartAttachment == null) 35 | { 36 | throw new ArgumentException("User nounce not found"); 37 | } 38 | 39 | // Get access token 40 | BotAuth.Models.AuthResult authResult = null; 41 | try 42 | { 43 | var userData = await BotStateHelper.GetUserDataAsync(channelId, userId); 44 | authResult = userData.GetProperty($"MSALAuthProvider{BotAuth.ContextConstants.AuthResultKey}"); 45 | } 46 | catch 47 | { 48 | } 49 | 50 | if (authResult != null) 51 | { 52 | ServicesHelper.AccessToken = authResult.AccessToken; 53 | 54 | var headers = ServicesHelper.GetWorkbookSessionHeader( 55 | ExcelHelper.GetSessionIdForRead(conversationData, chartAttachment.WorkbookId)); 56 | 57 | // Get the chart image 58 | #region Graph client bug workaround 59 | // Workaround for following issue: 60 | // https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/107 61 | 62 | // Proper call should be: 63 | // var imageAsString = await ServicesHelper.GraphClient.Me.Drive.Items[chartAttachment.WorkbookId] 64 | // .Workbook.Worksheets[chartAttachment.WorksheetId] 65 | // .Charts[chartAttachment.ChartId].Image(0, 0, "fit").Request(headers).GetAsync(); 66 | 67 | // Get the request URL just to the chart because the image 68 | // request builder is broken 69 | string chartRequestUrl = ServicesHelper.GraphClient.Me.Drive.Items[chartAttachment.WorkbookId] 70 | .Workbook.Worksheets[chartAttachment.WorksheetId] 71 | .Charts[chartAttachment.ChartId].Request().RequestUrl; 72 | 73 | // Append the proper image request segment 74 | string chartImageRequestUrl = $"{chartRequestUrl}/image(width=0,height=0,fittingMode='fit')"; 75 | 76 | // Create an HTTP request message 77 | var imageRequest = new HttpRequestMessage(HttpMethod.Get, chartImageRequestUrl); 78 | 79 | // Add session header 80 | imageRequest.Headers.Add(headers[0].Name, headers[0].Value); 81 | 82 | // Add auth 83 | await ServicesHelper.GraphClient.AuthenticationProvider.AuthenticateRequestAsync(imageRequest); 84 | 85 | // Send request 86 | var imageResponse = await ServicesHelper.GraphClient.HttpProvider.SendAsync(imageRequest); 87 | 88 | if (!imageResponse.IsSuccessStatusCode) 89 | { 90 | return Request.CreateResponse(HttpStatusCode.NotFound); 91 | } 92 | 93 | // Parse the response for the base 64 image string 94 | var imageObject = JObject.Parse(await imageResponse.Content.ReadAsStringAsync()); 95 | var imageAsString = imageObject.GetValue("value").ToString(); 96 | #endregion 97 | 98 | // Convert the image from a string to an image 99 | byte[] byteBuffer = Convert.FromBase64String(imageAsString); 100 | 101 | var memoryStream = new MemoryStream(byteBuffer); 102 | memoryStream.Position = 0; 103 | 104 | // Send the image back in the response 105 | var response = Request.CreateResponse(HttpStatusCode.OK); 106 | response.Headers.AcceptRanges.Add("bytes"); 107 | response.Content = new StreamContent(memoryStream); 108 | response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("render"); 109 | response.Content.Headers.ContentDisposition.FileName = "chart.png"; 110 | response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png"); 111 | response.Content.Headers.ContentLength = memoryStream.Length; 112 | response.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true, NoStore = true }; 113 | return response; 114 | } 115 | else 116 | { 117 | return Request.CreateResponse(HttpStatusCode.Forbidden); 118 | } 119 | } 120 | catch 121 | { 122 | // The user nonce was not found in user state 123 | return Request.CreateResponse(HttpStatusCode.NotFound); 124 | } 125 | 126 | } 127 | } 128 | } 129 | 130 | -------------------------------------------------------------------------------- /ExcelBot/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 | 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 | -------------------------------------------------------------------------------- /ExcelBot/Controllers/MessagesController.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Autofac; 7 | using ExcelBot.Dialogs; 8 | using ExcelBot.Helpers; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Builder.Dialogs.Internals; 11 | using Microsoft.Bot.Connector; 12 | using System; 13 | using System.Net; 14 | using System.Net.Http; 15 | using System.Threading; 16 | using System.Threading.Tasks; 17 | using System.Web.Http; 18 | 19 | namespace ExcelBot 20 | { 21 | [BotAuthentication] 22 | public class MessagesController : ApiController 23 | { 24 | 25 | /// 26 | /// POST: api/Messages 27 | /// Receive a message from a user and reply to it 28 | /// 29 | public async Task Post([FromBody]Activity activity) 30 | { 31 | ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl)); 32 | 33 | // Add User, Conversation and Channel Id to instrumentation 34 | TelemetryHelper.SetIds(activity); 35 | 36 | // Save the request url 37 | RequestHelper.RequestUri = Request.RequestUri; 38 | 39 | // Remove bot mention from message text 40 | Mention[] m = activity.GetMentions(); 41 | for (int i = 0; i < m.Length; i++) 42 | { 43 | if (m[i].Mentioned.Id == activity.Recipient.Id) 44 | { 45 | // Bot is in the @mention list. 46 | // Strip the bot name out of the message, so it can parse it as if it wasn't included. 47 | if (m[i].Text != null) 48 | activity.Text = activity.Text.Replace(m[i].Text, ""); 49 | } 50 | } 51 | 52 | using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) 53 | { 54 | var botDataStore = scope.Resolve>(); 55 | var key = Address.FromActivity(activity); 56 | 57 | var conversationData = await botDataStore.LoadAsync(key, BotStoreType.BotConversationData, CancellationToken.None); 58 | 59 | // Process the message 60 | if ((activity.Type == ActivityTypes.Message) && (activity.Text.StartsWith("!"))) 61 | { 62 | var reply = HandleCommandMessage(activity, conversationData); 63 | await botDataStore.SaveAsync(key, BotStoreType.BotConversationData, conversationData, CancellationToken.None); 64 | await botDataStore.FlushAsync(key, CancellationToken.None); 65 | await connector.Conversations.ReplyToActivityAsync(reply); 66 | } 67 | else if (activity.Type == ActivityTypes.Message) 68 | { 69 | var verbose = conversationData.GetProperty("Verbose"); 70 | ServicesHelper.StartLogging(verbose); 71 | 72 | // Send isTyping message 73 | var reply = activity.CreateReply(String.Empty); 74 | reply.Type = ActivityTypes.Typing; 75 | await connector.Conversations.ReplyToActivityAsync(reply); 76 | 77 | // Process message 78 | await Conversation.SendAsync(activity, () => new ExcelBotDialog()); 79 | } 80 | else 81 | { 82 | HandleSystemMessage(activity); 83 | } 84 | } 85 | 86 | return Request.CreateResponse(HttpStatusCode.OK); 87 | } 88 | 89 | private Activity HandleCommandMessage(Activity activity, BotData conversationData) 90 | { 91 | Activity reply = activity.CreateReply(); 92 | 93 | var messageParts = activity.Text.ToLower().Split(' '); 94 | 95 | switch (messageParts[0]) 96 | { 97 | case "!verbose": 98 | if ((messageParts.Length >= 2) && (messageParts[1] == "on")) 99 | { 100 | conversationData.SetProperty("Verbose", true); 101 | reply.Text = @"Verbose mode is **On**"; 102 | } 103 | else if ((messageParts.Length >= 2) && (messageParts[1] == "off")) 104 | { 105 | conversationData.SetProperty("Verbose", false); 106 | reply.Text = @"Verbose mode is **Off**"; 107 | } 108 | else 109 | { 110 | var verbose = conversationData.GetProperty("Verbose"); 111 | var verboseState = verbose ? "On" : "Off"; 112 | reply.Text = $@"Verbose mode is **{verboseState}**"; 113 | } 114 | break; 115 | default: 116 | reply.Text = @"Sorry, I don't understand what you want to do."; 117 | break; 118 | } 119 | return reply; 120 | } 121 | 122 | private Activity HandleSystemMessage(Activity message) 123 | { 124 | if (message.Type == ActivityTypes.DeleteUserData) 125 | { 126 | // Implement user deletion here 127 | // If we handle user deletion, return a real message 128 | } 129 | else if (message.Type == ActivityTypes.ConversationUpdate) 130 | { 131 | // Handle conversation state changes, like members being added and removed 132 | // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info 133 | // Not available in all channels 134 | } 135 | else if (message.Type == ActivityTypes.ContactRelationUpdate) 136 | { 137 | // Handle add/remove from contact lists 138 | // Activity.From + Activity.Action represent what happened 139 | } 140 | else if (message.Type == ActivityTypes.Typing) 141 | { 142 | // Handle knowing tha the user is typing 143 | } 144 | else if (message.Type == ActivityTypes.Ping) 145 | { 146 | } 147 | 148 | return null; 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /ExcelBot/Dialogs/WorksheetsDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Forms; 7 | using ExcelBot.Helpers; 8 | using ExcelBot.Workers; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Builder.FormFlow; 11 | using Microsoft.Bot.Builder.Luis.Models; 12 | using System; 13 | using System.Threading.Tasks; 14 | 15 | namespace ExcelBot.Dialogs 16 | { 17 | public partial class ExcelBotDialog : GraphDialog 18 | { 19 | #region Properties 20 | internal string WorksheetName { get; set; } 21 | #endregion 22 | 23 | #region Intents 24 | #region - List Worksheets 25 | [LuisIntent("listWorksheets")] 26 | public async Task ListWorksheets(IDialogContext context, LuisResult result) 27 | { 28 | // Telemetry 29 | TelemetryHelper.TrackDialog(context, result, "Worksheets", "ListWorksheets"); 30 | 31 | string workbookId = String.Empty; 32 | context.UserData.TryGetValue("WorkbookId", out workbookId); 33 | 34 | if ((workbookId != null) && (workbookId != String.Empty)) 35 | { 36 | await WorksheetWorker.DoListWorksheetsAsync(context); 37 | context.Wait(MessageReceived); 38 | } 39 | else 40 | { 41 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_ListWorksheets); 42 | } 43 | } 44 | public async Task AfterConfirm_ListWorksheets(IDialogContext context, IAwaitable result) 45 | { 46 | if (await result) 47 | { 48 | await WorksheetWorker.DoListWorksheetsAsync(context); 49 | } 50 | context.Wait(MessageReceived); 51 | } 52 | #endregion 53 | 54 | [LuisIntent("selectWorksheet")] 55 | public async Task SelectWorksheet(IDialogContext context, LuisResult result) 56 | { 57 | // Telemetry 58 | TelemetryHelper.TrackDialog(context, result, "Worksheets", "SelectWorksheet"); 59 | 60 | string workbookId = String.Empty; 61 | context.UserData.TryGetValue("WorkbookId", out workbookId); 62 | 63 | WorksheetName = LuisHelper.GetNameEntity(result.Entities); 64 | 65 | if (!(String.IsNullOrEmpty(workbookId))) 66 | { 67 | if (!(String.IsNullOrEmpty(WorksheetName))) 68 | { 69 | await WorksheetWorker.DoSelectWorksheetAsync(context, WorksheetName); 70 | context.Wait(MessageReceived); 71 | } 72 | else 73 | { 74 | // Call the SelectWorksheet Form 75 | SelectWorksheetForm.Worksheets = await WorksheetWorker.GetWorksheetNamesAsync(context, workbookId); 76 | 77 | context.Call( 78 | new FormDialog(new SelectWorksheetForm(), SelectWorksheetForm.BuildForm, FormOptions.PromptInStart), 79 | SelectWorksheet_FormComplete); 80 | } 81 | } 82 | else 83 | { 84 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_SelectWorksheet); 85 | } 86 | } 87 | public async Task AfterConfirm_SelectWorksheet(IDialogContext context, IAwaitable result) 88 | { 89 | if (await result) 90 | { 91 | // Get access token to see if user is authenticated 92 | ServicesHelper.AccessToken = await GetAccessToken(context); 93 | 94 | string workbookId = String.Empty; 95 | context.UserData.TryGetValue("WorkbookId", out workbookId); 96 | 97 | // Call the SelectWorksheet Form 98 | SelectWorksheetForm.Worksheets = await WorksheetWorker.GetWorksheetNamesAsync(context, workbookId); 99 | 100 | context.Call( 101 | new FormDialog(new SelectWorksheetForm(), SelectWorksheetForm.BuildForm, FormOptions.PromptInStart), 102 | SelectWorksheet_FormComplete); 103 | } 104 | context.Wait(MessageReceived); 105 | } 106 | 107 | private async Task SelectWorksheet_FormComplete(IDialogContext context, IAwaitable result) 108 | { 109 | SelectWorksheetForm form = null; 110 | try 111 | { 112 | form = await result; 113 | } 114 | catch 115 | { 116 | } 117 | 118 | if (form != null) 119 | { 120 | // Get access token to see if user is authenticated 121 | ServicesHelper.AccessToken = await GetAccessToken(context); 122 | 123 | // Select worksheet 124 | await WorksheetWorker.DoSelectWorksheetAsync(context, form.WorksheetName); 125 | context.Done(true); 126 | } 127 | else 128 | { 129 | await context.PostAsync("Okay! I will just sit tight until you tell me what to do"); 130 | context.Done(false); 131 | } 132 | } 133 | 134 | [LuisIntent("getActiveWorksheet")] 135 | public async Task GetActiveWorksheet(IDialogContext context, LuisResult result) 136 | { 137 | // Telemetry 138 | TelemetryHelper.TrackDialog(context, result, "Worksheets", "GetActiveWorksheet"); 139 | 140 | string workbookId = String.Empty; 141 | context.UserData.TryGetValue("WorkbookId", out workbookId); 142 | 143 | if ((workbookId != null) && (workbookId != String.Empty)) 144 | { 145 | await WorksheetWorker.DoGetActiveWorksheetAsync(context); 146 | context.Wait(MessageReceived); 147 | } 148 | else 149 | { 150 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_GetActiveWorksheet); 151 | } 152 | } 153 | 154 | public async Task AfterConfirm_GetActiveWorksheet(IDialogContext context, IAwaitable result) 155 | { 156 | if (await result) 157 | { 158 | await WorksheetWorker.DoGetActiveWorksheetAsync(context); 159 | } 160 | context.Wait(MessageReceived); 161 | } 162 | 163 | #endregion 164 | } 165 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [ARCHIVED] Excel Bot 2 | 3 | ## IMPORTANT 4 | 5 | **This project is being archived and replaced with the [ApprovalBot sample](https://github.com/microsoftgraph/botframework-csharp-approvalbot-sample). As part of the archival process, we're closing all open issues and pull requests.** 6 | 7 | **You can continue to use this sample "as-is", but it won't be maintained moving forward. We apologize for any inconvenience.** 8 | 9 | ## Table of Contents. ## 10 | 11 | [Introduction.](#introduction) 12 | 13 | [Prerequisites.](#prerequisites) 14 | 15 | [Cloning or downloading this repository.](#Cloning-or-downloading-this-repository) 16 | 17 | [Configure your Azure AD tenant.](#Configure-your-Azure-AD-tenant) 18 | 19 | [Register the bot.](#Register-the-bot) 20 | 21 | [Give us your feedback](#Give-us-your-feedback) 22 | 23 | ## Introduction. 24 | 25 | Excel Bot is a sample that demonstrates how to use the [Microsoft Graph](https://graph.microsoft.io) and specifically the [Excel REST API](https://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/excel) to access Excel workbooks stored in OneDrive for Business through a conversational user interface. It is written in C# and it uses the [Microsoft Bot Framework](https://dev.botframework.com/) and the [Language Understanding Intelligent Service (LUIS)](https://www.luis.ai/). 26 | 27 | *Note*: The code in this sample was originally written for a user experience prototype and does not necessarily demonstrate how to create production quality code. 28 | 29 | ## Prerequisites. 30 | 31 | 32 | This sample requires the following: 33 | 34 | - Visual Studio 2017. 35 | - An Office 365 for business account. You can sign up for an [Office 365 Developer subscription](https://msdn.microsoft.com/en-us/office/office365/howto/setup-development-environment) that includes the resources that you need to start building Office 365 apps. 36 | 37 | ## Cloning or downloading this repository. 38 | 39 | 40 | - Clone this repo to a local folder 41 | 42 | ` git clone https://github.com/nicolesigei/botframework-csharp-excelbot-rest-sample.git ` 43 | 44 | 45 | ## Configure your Azure AD tenant. 46 | 47 | 1. Open a browser and navigate to the [Azure Active Directory admin center](https://aad.portal.azure.com). Login using a **Work or School Account**. 48 | 49 | 1. Select **Azure Active Directory** in the left-hand navigation, then select **App registrations** under **Manage**. 50 | 51 | ![A screenshot of the App registrations ](readme-images/aad-portal-app-registrations.png) 52 | 53 | 1. Select **New registration**. On the **Register an application** page, set the values as follows. 54 | 55 | - Set a preferred **Name** e.g. `Excel Bot App`. 56 | - Set **Supported account types** to **Accounts in any organizational directory**. 57 | - Under **Redirect URI**, set the first drop-down to `Web` and set the value to http://localhost:3978/callback. 58 | 59 | ![A screenshot of the Register an application page](readme-images/aad-register-an-app.PNG) 60 | 61 | > **Note:** If you are running this locally and on Azure, you should add two redirect URLs here, one to your local instance and one to your Azure web app. 62 | 63 | 1. Choose **Register**. On the **Excel Bot App** page, copy the value of the **Application (client) ID** and save it, you will need it to configure the app. 64 | 65 | ![A screenshot of the application ID of the new app registration](readme-images/aad-application-id.PNG) 66 | 67 | 1. Select **Certificates & secrets** under **Manage**. Select the **New client secret** button. Enter a value in **Description** and select one of the options for **Expires** and choose **Add**. 68 | 69 | ![A screenshot of the Add a client secret dialog](readme-images/aad-new-client-secret.png) 70 | 71 | 1. Copy the client secret value before you leave this page. You will need it to configure the app. 72 | 73 | > [!IMPORTANT] 74 | > This client secret is never shown again, so make sure you copy it now. 75 | 76 | ![A screenshot of the newly added client secret](readme-images/aad-copy-client-secret.png) 77 | 78 | ## Register the bot. 79 | 80 | Complete the these steps to setup your development environment to build and test the Excel bot: 81 | 82 | - Download and install the [Azure Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator) 83 | 84 | - Make a copy of **./ExcelBot/PrivateSettings.config.example** in the same directory. Name the file to **PrivateSettings.config**. 85 | - Open the ExcelBot.sln solution file 86 | - Register the bot in the [Bot Framework](https://dev.botframework.com/bots/new) 87 | - Copy the bot MicrosoftAppId and MicrosoftAppPassword to the PrivateSettings.config file 88 | - Register the bot to call the Microsoft Graph. 89 | - Copy the Azure Active Directory **Client Id** and **Secret** to the PrivateSettings.config file 90 | - Create a new model in the [LUIS](https://www.luis.ai) service 91 | - Import the LUIS\excelbot.json file into LUIS 92 | - Train and publish the LUIS model 93 | - Copy the LUIS model id and subscription key to the Dialogs\ExcelBotDialog.cs file 94 | - (Optional) Enable Web Chat for the bot in the Bot Framework and copy the Web Chat embed template the chat.htm file 95 | - (Optional) To get the bot to send telemetry to [Visual Studio Application Insights](https://azure.microsoft.com/en-us/services/application-insights/), copy the instrumentation key to the following files: ApplicationInsights.config, default.htm, loggedin.htm, chat.htm 96 | - Build the solution 97 | - Press F5 to start the bot locally 98 | - Test the bot locally with the [Bot Framework Emulator](https://docs.botframework.com/en-us/tools/bot-framework-emulator) 99 | - Create an Azure Cosmos DB in Azure that uses the SQL API 100 | - Replace the bots host name in the PrivateSettings.config file 101 | - Replace the database URI and key in the PrivateSettings.config file 102 | - Publish the solution to the Azure web app 103 | - Test the deployed bot using the Web Chat control by browsing to the chat.htm page 104 | 105 | ## Give us your feedback 106 | 107 | 108 | 109 | Your feedback is important to us. 110 | 111 | Check out the sample code and let us know about any questions and issues you find by [submitting an issue](https://github.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/issues) directly in this repository. Provide repro steps, console output, and error messages in any issue you open. 112 | 113 | 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. 114 | 115 | ## Copyright 116 | 117 | Copyright (c) 2019 Microsoft. All rights reserved. 118 | 119 | -------------------------------------------------------------------------------- /ExcelBot/Helpers/ExcelHelper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using Microsoft.Bot.Builder.Dialogs; 7 | using Microsoft.Bot.Connector; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | namespace ExcelBot.Helpers 12 | { 13 | public static class ExcelHelper 14 | { 15 | #region Workbook Link 16 | public static string GetWorkbookLinkMarkdown(IDialogContext context) 17 | { 18 | string workbookWebUrl = String.Empty; 19 | if (context.ConversationData.TryGetValue("WorkbookWebUrl", out workbookWebUrl)) 20 | { 21 | var workbookName = context.ConversationData.GetValue("WorkbookName"); 22 | return $"[{workbookName}]({workbookWebUrl})"; 23 | } 24 | else 25 | { 26 | return "NOT IMPLEMENTED"; 27 | } 28 | } 29 | #endregion 30 | 31 | #region Session 32 | public static string GetSessionIdForRead(IDialogContext context) 33 | { 34 | string sessionId = String.Empty; 35 | if (TryGetSession(context, out sessionId)) 36 | { 37 | return sessionId; 38 | } 39 | else 40 | { 41 | return string.Empty; 42 | } 43 | } 44 | 45 | public static string GetSessionIdForRead(BotData conversationData, string workbookId) 46 | { 47 | string sessionId = String.Empty; 48 | if (TryGetSession(conversationData, workbookId, out sessionId)) 49 | { 50 | return sessionId; 51 | } 52 | else 53 | { 54 | return string.Empty; 55 | } 56 | } 57 | 58 | public static async Task GetSessionIdForUpdateAsync(IDialogContext context) 59 | { 60 | string sessionId = String.Empty; 61 | if (TryGetSession(context, out sessionId)) 62 | { 63 | return sessionId; 64 | } 65 | else 66 | { 67 | return await CreateSession(context); 68 | } 69 | } 70 | 71 | private static async Task CreateSession(IDialogContext context) 72 | { 73 | var workbookId = context.UserData.GetValue("WorkbookId"); 74 | var createSessionRequest = ServicesHelper.GraphClient.Me.Drive.Items[workbookId] 75 | .Workbook.CreateSession(true).Request(); 76 | 77 | var sessionId = (await createSessionRequest.PostAsync()).Id; 78 | await ServicesHelper.LogGraphServiceRequest(context, createSessionRequest); 79 | 80 | context.ConversationData.SetValue("SessionId", sessionId); 81 | context.ConversationData.SetValue("SessionWorkbookId", workbookId); 82 | context.ConversationData.SetValue("SessionExpiresOn", DateTime.Now.AddMinutes(5)); 83 | return sessionId; 84 | } 85 | 86 | private static bool TryGetSession(IDialogContext context, out string sessionId) 87 | { 88 | sessionId = String.Empty; 89 | if (context.ConversationData.TryGetValue("SessionId", out sessionId)) 90 | { 91 | // Check that the session is for the right workbook 92 | var workbookId = context.UserData.GetValue("WorkbookId"); 93 | 94 | var sessionWorkbookId = ""; 95 | if ((context.ConversationData.TryGetValue("SessionWorkbookId", out sessionWorkbookId)) && (workbookId != sessionWorkbookId)) 96 | { 97 | // Session is with another workbook 98 | sessionId = ""; 99 | return false; 100 | } 101 | 102 | // Check that the session hasn't expired 103 | var sessionExpiresOn = DateTime.MinValue; 104 | if ((context.ConversationData.TryGetValue("SessionExpiresOn", out sessionExpiresOn)) && (DateTime.Compare(DateTime.Now, sessionExpiresOn) < 0)) 105 | { 106 | // Session is still valid 107 | context.ConversationData.SetValue("SessionExpiresOn", DateTime.Now.AddMinutes(5)); 108 | return true; 109 | } 110 | } 111 | // Session was not found or has expired 112 | sessionId = ""; 113 | return false; 114 | } 115 | 116 | private static bool TryGetSession(BotData conversationData, string workbookId, out string sessionId) 117 | { 118 | var sessionIdObj = conversationData.GetProperty("SessionId"); 119 | if (sessionIdObj != string.Empty) 120 | { 121 | // Check that the session is for the right workbook 122 | var sessionWorkbookIdObj = conversationData.GetProperty("SessionWorkbookId"); ; 123 | if ((sessionWorkbookIdObj != string.Empty) && (workbookId != (string)sessionWorkbookIdObj)) 124 | { 125 | // Session is with another workbook 126 | sessionId = ""; 127 | return false; 128 | } 129 | 130 | // Check that the session hasn't expired 131 | var sessionExpiresOnObj = conversationData.GetProperty("SessionExpiresOn"); 132 | if (DateTime.Compare(DateTime.Now, (DateTime)sessionExpiresOnObj) < 0) 133 | { 134 | sessionId = (string)sessionIdObj; 135 | return true; 136 | } 137 | } 138 | // Session was not found or has expired 139 | sessionId = ""; 140 | return false; 141 | } 142 | #endregion 143 | 144 | #region Helpers 145 | public static string GetWorksheetName(string address) 146 | { 147 | var pos = address.IndexOf("!"); 148 | if (pos >= 0) 149 | { 150 | return address.Substring(0, pos).Replace("'","").Replace(@"""",""); 151 | } 152 | else 153 | { 154 | return ""; 155 | } 156 | } 157 | 158 | public static string GetCellAddress(string address) 159 | { 160 | var pos = address.IndexOf("!"); 161 | if (pos >= 0) 162 | { 163 | return address.Substring(pos+1); 164 | } 165 | else 166 | { 167 | return address; 168 | } 169 | } 170 | 171 | 172 | private static string[] columns = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; 173 | 174 | public static string GetRangeAddress(int column, int row, int width, int height) 175 | { 176 | return $"{columns[column]}{row + 1}:{columns[column + width - 1]}{row + height}"; 177 | } 178 | 179 | #endregion 180 | } 181 | 182 | } -------------------------------------------------------------------------------- /README-localized/README-pt-br.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-excel 5 | - ms-graph 6 | languages: 7 | - csharp 8 | description: "O Bot do Excel é um bot criado com o Microsoft Bot Framework e que demonstra como usar o Excel com a API do Microsoft Graph" 9 | extensions: 10 | contentType: samples 11 | technologies: 12 | - Microsoft Graph 13 | - Microsoft Bot Framework 14 | services: 15 | - Excel 16 | createdDate: 9/15/2016 10:30:08 PM 17 | --- 18 | 19 | # Bot do Excel 20 | 21 | ## Sumário. ## 22 | 23 | [Introdução.](#introduction) 24 | 25 | [Pré-requisitos.](#prerequisites) 26 | 27 | [Clonar ou baixar esse repositório.](#Cloning-or-downloading-this-repository) 28 | 29 | [Configure seu locatário do Azure AD.](#Configure-your-Azure-AD-tenant) 30 | 31 | [Registrar o bot.](#Register-the-bot) 32 | 33 | [Envie-nos os seus comentários](#Give-us-your-feedback) 34 | 35 | ## Introdução. 36 | 37 | O Bot do Excel é um exemplo que demonstra como usar o [Microsoft Graph](https://graph.microsoft.io) e, especificamente, a [API REST do Excel](https://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/excel) para acessar pastas de trabalho do Excel armazenadas no OneDrive for Business por meio de uma interface de usuário de conversação. Ele é escrito no C# e usa o [Microsoft Bot Framework](https://dev.botframework.com/) e o [LUIS (Serviço Inteligente de Reconhecimento de Voz)](https://www.luis.ai/). 38 | 39 | *Observação*: o código neste exemplo foi escrito por um protótipo da experiência do usuário e não demonstra necessariamente como criar código de qualidade de produção. 40 | 41 | ## Pré-requisitos. 42 | 43 | 44 | Esse exemplo requer o seguinte: 45 | 46 | - Visual Studio 2017. 47 | - Uma conta do Office 365 para empresas. Inscreva-se para uma [assinatura de Desenvolvedor do Office 365](https://msdn.microsoft.com/en-us/office/office365/howto/setup-development-environment), que inclui os recursos necessários para começar a criar aplicativos do Office 365. 48 | 49 | ## Clonar ou baixar esse repositório. 50 | 51 | 52 | - Clone este repositório na pasta local 53 | 54 | ` git clone https://github.com/nicolesigei/botframework-csharp-excelbot-rest-sample.git ` 55 | 56 | 57 | ## Configure seu locatário do Azure AD. 58 | 59 | 1. Abra um navegador e navegue até o [centro de administração do Azure Active Directory](https://aad.portal.azure.com). Faça o login usando uma **Conta Corporativa ou de Estudante**. 60 | 61 | 1. Selecione **Azure Active Directory** na navegação à esquerda e, em seguida, selecione **Registros de aplicativos** em **Gerenciar**. 62 | 63 | ![Captura de tela dos Registros de aplicativo](readme-images/aad-portal-app-registrations.png) 64 | 65 | 1. Selecione **Novo registro**. Na página **Registrar um aplicativo**, defina os valores da seguinte forma. 66 | 67 | - Defina um **nome** preferencial, por exemplo, `Aplicativo de Bot do Excel`. 68 | - Defina os **tipos de conta com suporte** para **Contas em qualquer diretório organizacional**. 69 | - Em **URI de Redirecionamento**, defina o primeiro menu suspenso para `Web` e defina o valor como http://localhost:3978/callback. 70 | 71 | ![Captura de tela da página registrar um aplicativo](readme-images/aad-register-an-app.PNG) 72 | 73 | > **Observação:** Se você estiver executando isso localmente e no Azure, adicione aqui duas URLs de redirecionamento, uma para sua instância local e outra para o aplicativo Web do Azure. 74 | 75 | 1. Escolha **Registrar**. Na página **Aplicativo de Bot de Excel**, copie o valor da **ID do aplicativo (cliente)** e salve-o, você precisará dele para configurar o aplicativo. 76 | 77 | ![Captura de tela da ID do aplicativo do novo registro do aplicativo](readme-images/aad-application-id.PNG) 78 | 79 | 1. Selecione **Certificados e segredos** em **Gerenciar**. Selecione o botão **Novo segredo do cliente**. Insira um valor em **Descrição**, selecione uma das opções para **Expira** e escolha **Adicionar**. 80 | 81 | ![Uma captura de tela da caixa de diálogo Adicionar um segredo do cliente](readme-images/aad-new-client-secret.png) 82 | 83 | 1. Copie o valor de segredo do cliente antes de sair desta página. Será necessário para configurar o aplicativo. 84 | 85 | > [!IMPORTANTE] 86 | > Este segredo do cliente nunca é mostrado novamente, portanto, copie-o agora. 87 | 88 | ![Uma captura de tela do segredo do cliente recém adicionado](readme-images/aad-copy-client-secret.png) 89 | 90 | ## Registre o bot. 91 | 92 | Execute as etapas a seguir para configurar seu ambiente de desenvolvimento para criar e testar o Bot do Excel: 93 | 94 | - Baixe e instale o [Emulador de banco de dados do Azure Cosmos](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator) 95 | 96 | - Faça uma cópia do **./ExcelBot/PrivateSettings.config.example** no mesmo diretório. Nomeie o arquivo para **PrivateSettings.config**. 97 | - Abra o arquivo de solução ExcelBot.sln 98 | - Registre o bot no [Bot Framework](https://dev.botframework.com/bots/new) 99 | - Copie o bot MicrosoftAppId e MicrosoftAppPassword para o arquivo PrivateSettings.config 100 | - Registre o bot para chamar o Microsoft Graph. 101 | - Copie a **ID do cliente** do Azure Active Directory e o **Segredo** para o arquivo PrivateSettings.config 102 | - Crie um novo modelo no serviço [LUIS](https://www.luis.ai) 103 | - Importe o arquivo LUIS\\excelbot.json no LUIS 104 | - Treine e publique o modelo LUIS 105 | - Copie a ID do modelo LUIS e a chave de assinatura para o arquivo Dialogs\\ExcelBotDialog.cs 106 | - (Opcional) Habilite o chat da Web para o bot no Bot Framework e copie o modelo de inserção de chat na Web no arquivo chat.htm 107 | - (Opcional) Para que o bot envie a telemetria para o [Application Insights do Visual Studio](https://azure.microsoft.com/en-us/services/application-insights/), copie a chave da instrumentação para os seguintes arquivos: ApplicationInsights.config, default.htm, loggedin.htm, chat.htm 108 | - Crie a solução 109 | - Pressione F5 para iniciar o bot localmente 110 | - Teste o bot localmente com o [Emulator do Bot Framework](https://docs.botframework.com/en-us/tools/bot-framework-emulator) 111 | - Crie um Azure Cosmos DB no Azure que usa a API do SQL 112 | - Substitua o nome de host dos bots no arquivo PrivateSettings.config 113 | - Substitua o URI do banco de dados e a chave no arquivo PrivateSettings.config 114 | - Publique a solução no aplicativo Web do Azure 115 | - Teste o bot implantado usando o controle de chat Web, navegando até a página chat.htm 116 | 117 | ## Envie-nos os seus comentários 118 | 119 | 120 | 121 | Seus comentários são importantes para nós. 122 | 123 | Confira o código de amostra e fale conosco caso tenha alguma dúvida ou problema para encontrá-los [enviando um problema](https://github.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/issues) diretamente nesse repositório. Forneça etapas de reprodução, saída do console e mensagens de erro em qualquer edição que você abrir. 124 | 125 | Este projeto adotou o [Código de Conduta de Código Aberto da Microsoft](https://opensource.microsoft.com/codeofconduct/). Para saber mais, confira as [Perguntas frequentes sobre o Código de Conduta](https://opensource.microsoft.com/codeofconduct/faq/) ou entre em contato pelo [opencode@microsoft.com](mailto:opencode@microsoft.com) se tiver outras dúvidas ou comentários. 126 | 127 | ## Direitos autorais 128 | 129 | Copyright (c) 2019 Microsoft. Todos os direitos reservados. 130 | 131 | -------------------------------------------------------------------------------- /README-localized/README-ru-ru.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-excel 5 | - ms-graph 6 | languages: 7 | - csharp 8 | description: "Excel Bot - это бот, созданный на основе Microsoft Bot Framework, который демонстрирует, как использовать Excel с Microsoft Graph API" 9 | extensions: 10 | contentType: samples 11 | technologies: 12 | - Microsoft Graph 13 | - Microsoft Bot Framework 14 | services: 15 | - Excel 16 | createdDate: 9/15/2016 10:30:08 PM 17 | --- 18 | 19 | # Excel Bot 20 | 21 | ## Содержание ## 22 | 23 | [Вступление.](#introduction) 24 | 25 | [Требования.](#prerequisites) 26 | 27 | [Клонирование или скачивание этого репозитория.](#Cloning-or-downloading-this-repository) 28 | 29 | [Настройка клиента Azure AD.](#Configure-your-Azure-AD-tenant) 30 | 31 | [Зарегистрировать Bot.](#Register-the-bot) 32 | 33 | [Оставьте свой отзыв](#Give-us-your-feedback) 34 | 35 | ## Вступление. 36 | 37 | Excel Bot — это пример, в котором показано, как использовать [Microsoft Graph](https://graph.microsoft.io), а именно [Excel REST API](https://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/excel) для доступа к книгам Excel, хранящимся в службе OneDrive для бизнеса с помощью диалогового пользовательского интерфейса. Он написан на C # и использует [Microsoft Bot Framework](https://dev.botframework.com/) и [интеллектуальную службу понимания языка (LUIS)](https://www.luis.ai/). 38 | 39 | *Примечание*. Код в этом примере изначально был написан для прототипа пользовательского интерфейса и не обязательно демонстрирует, как создать производственный качественный код. 40 | 41 | ## Требования. 42 | 43 | 44 | Для этого примера требуются приведенные ниже компоненты. 45 | 46 | - Visual Studio 2017. 47 | - Учетная запись Office 365 для бизнеса. Вы можете зарегистрироваться для получения подписки на [Office 365 для разработчиков ](https://msdn.microsoft.com/en-us/office/office365/howto/setup-development-environment) с ресурсами, которые необходимо приступить к созданию приложений Office 365. 48 | 49 | ## Клонирование или скачивание этого репозитория. 50 | 51 | 52 | - Клонирование этого репозитория в локальную папку 53 | 54 | ` git clone https://github.com/nicolesigei/botframework-csharp-excelbot-rest-sample.git ` 55 | 56 | 57 | ## Настройка клиента Azure AD. 58 | 59 | 1. Откройте браузер и перейдите в [Центр администрирования Azure Active Directory](https://aad.portal.azure.com). Войдите с помощью **рабочей или учебной учетной записи**. 60 | 61 | 1. Выберите **Azure Active Directory** на панели навигации слева, а затем — пункт **Регистрация приложения** в разделе **Управление**. 62 | 63 | ![Снимок экрана: пункт "Регистрация приложения"](readme-images/aad-portal-app-registrations.png) 64 | 65 | 1. Выберите **Новая регистрация**. На странице **Регистрация приложения** задайте необходимые значения следующим образом: 66 | 67 | - Установите предпочтительное **Имя**, например, `Приложение Excel Bot`. 68 | - Задайте для параметра **Поддерживаемые типы учетных записей** значение **Учетные записи в любом каталоге организации**. 69 | - В разделе **перенаправления URI** выберите в первом раскрывающемся списке значение `веб-` и задайте для него значение http://localhost:3978/callback. 70 | 71 | ![Снимок экрана: страница "Регистрация приложения"](readme-images/aad-register-an-app.PNG) 72 | 73 | > **Примечание.** Если вы используете это локально и в Azure, вам нужно добавить здесь два URL-адреса перенаправления, один для локального экземпляра и один для веб-приложения Azure. 74 | 75 | 1. Нажмите кнопку **Зарегистрировать**. На странице **приложения Excel Bot** скопируйте значение **идентификатора приложения (клиента)** и сохраните его, оно понадобится вам для настройки приложения. 76 | 77 | ![Снимок экрана: идентификатор приложения для новой регистрации](readme-images/aad-application-id.PNG) 78 | 79 | 1. Выберите **Сертификаты и секреты** в разделе **Управление**. Нажмите кнопку **Новый секрет клиента**. Введите значение в поле **Описание**, выберите один из вариантов для **Срок действия** и нажмите **Добавить**. 80 | 81 | ![Снимок экрана: диалоговое окно "Добавление секрета клиента"](readme-images/aad-new-client-secret.png) 82 | 83 | 1. Скопируйте значение секрета клиента, а затем покиньте эту страницу. Он понадобится вам для настройки приложения. 84 | 85 | > [ВАЖНО!] 86 | > Этот секрет клиента больше не будет отображаться, поэтому обязательно скопируйте его. 87 | 88 | ![Снимок экрана: только что добавленный секрет клиента](readme-images/aad-copy-client-secret.png) 89 | 90 | ## Зарегистрировать Bot. 91 | 92 | Выполните эти шаги, чтобы настроить среду разработки для сборки и тестирования бота Excel: 93 | 94 | - Загрузите и установите [эмулятор Azure Cosmos DB](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator) 95 | 96 | - Сделайте копию **./ExcelBot/PrivateSettings.config.example** в том же каталоге. Назовите файл **PrivateSettings.config**. 97 | - Откройте файл решения ExcelBot.sln 98 | - Зарегистрируйте бота в [Bot Framework](https://dev.botframework.com/bots/new) 99 | - Скопируйте бот MicrosoftAppId и MicrosoftAppPassword в файл PrivateSettings.config 100 | - Зарегистрируйте бота, чтобы позвонить в Microsoft Graph. 101 | - Скопируйте **идентификатор клиента** Azure Active Directory и **секретный файл** в файл PrivateSettings.config. 102 | - Создание новой модели в службе [LUIS](https://www.luis.ai) 103 | - Импортируйте файл LUIS \\ excelbot.json в LUIS 104 | - Обучите и опубликуйте модель LUIS 105 | - Скопируйте идентификатор модели LUIS и ключ подписки в файл Dialogs \\ ExcelBotDialog.cs. 106 | - (Необязательно) Включите веб-чат для бота в Bot Framework и скопируйте шаблон для встраивания веб-чата в файл chat.htm 107 | - Желательно Чтобы сделать так, чтобы в [приложении Visual Studio](https://azure.microsoft.com/en-us/services/application-insights/)Application Insights, скопируйте ключ инструментирования в следующие файлы: ApplicationInsights.config, default.htm, loggedin.htm, chat.htm 108 | - Постройте решение 109 | - Нажмите F5, чтобы запустить бот локально 110 | - Протестируйте бот локально с [эмулятором Bot Framework](https://docs.botframework.com/en-us/tools/bot-framework-emulator) 111 | - Создайте базу данных Azure Cosmos в Azure, которая использует SQL API 112 | - Замените имя хоста ботов в файле PrivateSettings.config 113 | - Замените URI базы данных и введите ключ в файл PrivateSettings.config. 114 | - Опубликуйте решение в веб-приложении Azure. 115 | - Протестируйте развернутого бота с помощью элемента управления веб-чата, перейдя на страницу chat.htm 116 | 117 | ## Оставьте свой отзыв 118 | 119 | 120 | 121 | Ваш отзыв важен для нас. 122 | 123 | Ознакомьтесь с образцом кода и сообщите нам о любых возникших вопросах и проблемах, с которыми вы столкнулись, [отправив сообщение](https://github.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/issues) в этом репозитории. Укажите выполненные действия, выходное сообщение консоли и сообщения об ошибках при любой проблеме. 124 | 125 | Этот проект соответствует [Правилам поведения разработчиков открытого кода Майкрософт](https://opensource.microsoft.com/codeofconduct/). Дополнительные сведения см. в разделе [Часто задаваемые вопросы о правилах поведения](https://opensource.microsoft.com/codeofconduct/faq/). Если у вас возникли вопросы или замечания, напишите нам по адресу [opencode@microsoft.com](mailto:opencode@microsoft.com). 126 | 127 | ## Авторские права 128 | 129 | (c) Корпорация Майкрософт (Microsoft Corporation), 2019. Все права защищены. 130 | 131 | -------------------------------------------------------------------------------- /ExcelBot/ApplicationInsights.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | YOUR APPLICATION INSIGHTS KEY 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | search|spider|crawl|Bot|Monitor|AlwaysOn 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | core.windows.net 36 | core.chinacloudapi.cn 37 | core.cloudapi.de 38 | core.usgovcloudapi.net 39 | localhost 40 | 127.0.0.1 41 | 42 | 43 | Microsoft.Azure.EventHubs 44 | Microsoft.Azure.ServiceBus 45 | 46 | 47 | 48 | 65 | 66 | 67 | 68 | 69 | 70 | 72 | 73 | 74 | 75 | 80 | Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler 81 | System.Web.StaticFileHandler 82 | System.Web.Handlers.AssemblyResourceLoader 83 | System.Web.Optimization.BundleHandler 84 | System.Web.Script.Services.ScriptHandlerFactory 85 | System.Web.Handlers.TraceHandler 86 | System.Web.Services.Discovery.DiscoveryRequestHandler 87 | System.Web.HttpDebugHandler 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 5 98 | Event 99 | 100 | 101 | 5 102 | Event 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /README-localized/README-es-es.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-excel 5 | - ms-graph 6 | languages: 7 | - csharp 8 | description: "El bot de Excel es un bot creado con Microsoft Bot Framework que muestra cómo usar Excel con la API de Microsoft Graph" 9 | extensions: 10 | contentType: samples 11 | technologies: 12 | - Microsoft Graph 13 | - Microsoft Bot Framework 14 | services: 15 | - Excel 16 | createdDate: 9/15/2016 10:30:08 PM 17 | --- 18 | 19 | # Bot de Excel 20 | 21 | ## Tabla de contenido ## 22 | 23 | [Introducción](#introduction) 24 | 25 | [Requisitos previos](#prerequisites) 26 | 27 | [Clonar o descargar el repositorio](#Cloning-or-downloading-this-repository) 28 | 29 | [Configurar el inquilino de Azure AD](#Configure-your-Azure-AD-tenant) 30 | 31 | [Registrar el bot](#Register-the-bot) 32 | 33 | [Envíenos sus comentarios](#Give-us-your-feedback) 34 | 35 | ## Introducción 36 | 37 | El bot de Excel es un ejemplo en el que se demuestra cómo usar [Microsoft Graph](https://graph.microsoft.io) y específicamente la [API de REST de Excel](https://graph.microsoft.io/es-es/docs/api-reference/v1.0/resources/excel) para tener acceso a los libros de Excel almacenados en OneDrive para la Empresa mediante una interfaz de usuario de conversación. Esta escrito en C# y usa [Microsoft Bot Framework](https://dev.botframework.com/) y [Language Understanding Intelligent Service (LUIS)](https://www.luis.ai/). 38 | 39 | *Nota*: El código de este ejemplo se ha escrito originalmente para un prototipo de experiencia de usuario y no muestra necesariamente cómo crear un código con calidad de producción. 40 | 41 | ## Requisitos previos 42 | 43 | 44 | Para este ejemplo se necesita lo siguiente: 45 | 46 | - Visual Studio 2017. 47 | - Una cuenta de Office 365 para empresas. Puede registrarse para obtener una [suscripción de Office 365 Developer](https://msdn.microsoft.com/es-es/office/office365/howto/setup-development-environment) que incluye los recursos que necesita para comenzar a crear aplicaciones de Office 365. 48 | 49 | ## Clonar o descargar el repositorio 50 | 51 | 52 | - Clone este repositorio en una carpeta local. 53 | 54 | ` git clone https://github.com/nicolesigei/botframework-csharp-excelbot-rest-sample.git ` 55 | 56 | 57 | ## Configurar el inquilino de Azure AD 58 | 59 | 1. Abra el explorador y vaya al [Centro de administración de Azure Active Directory](https://aad.portal.azure.com). Inicie sesión con **una cuenta profesional o educativa**. 60 | 61 | 1. Seleccione **Azure Active Directory** en el panel de navegación izquierdo y, después seleccione**registros de aplicaciones** en **Administrar**. 62 | 63 | ![Captura de pantalla de los registros de la aplicación ](readme-images/aad-portal-app-registrations.png) 64 | 65 | 1. Seleccione **Nuevo registro**. En la página **Registrar una aplicación**, establezca los valores siguientes. 66 | 67 | - Establezca un **Nombre** de preferencia; por ejemplo, `Aplicación de bot de Excel`. 68 | - Establezca los **Tipos de cuenta compatibles** en **Cuentas de cualquier directorio organizativo**. 69 | - En **URI de redirección**, establezca la primera lista desplegable en `Web` y el valor en http://localhost:3978/callback. 70 | 71 | ![Captura de pantalla de la página Registrar una aplicación](readme-images/aad-register-an-app.PNG) 72 | 73 | > **Nota:** Si está ejecutando de forma local y en Azure, debe agregar dos URL de redirección aquí, una a la instancia local y otra a la aplicación web de Azure. 74 | 75 | 1. Haga clic en **Registrar**. En la página de **Aplicación de bot de Excel**, copie el valor de **Id. de aplicación (cliente)** y guárdelo, lo necesitará para configurar la aplicación. 76 | 77 | ![Captura de pantalla del Id. de aplicación del nuevo registro](readme-images/aad-application-id.PNG) 78 | 79 | 1. Seleccione **Certificados y secretos** en **Administrar**. Seleccione el botón **Nuevo secreto de cliente**. Escriba un valor en **Descripción** y seleccione una de las opciones de **Expirar** y luego seleccione **Agregar**. 80 | 81 | ![Captura de pantalla del diálogo Agregar un cliente secreto](readme-images/aad-new-client-secret.png) 82 | 83 | 1. Copie el valor del secreto de cliente antes de salir de esta página. Lo necesitará para configurar la aplicación. 84 | 85 | > [¡IMPORTANTE!] 86 | > El secreto de cliente no se mostrará otra vez, asegúrese de copiarlo en este momento. 87 | 88 | ![Captura de pantalla del nuevo secreto de cliente agregado](readme-images/aad-copy-client-secret.png) 89 | 90 | ## Registrar el bot 91 | 92 | Realice estos pasos para configurar el entorno de desarrollo a fin de crear y probar el bot de Excel: 93 | 94 | - Descargue e instale [Azure Cosmos DB Emulator](https://docs.microsoft.com/es-es/azure/cosmos-db/local-emulator). 95 | 96 | - Realice una copia de **./ExcelBot/PrivateSettings.config.example** en el mismo directorio. Cambie el nombre del archivo a **PrivateSettings.config**. 97 | - Abra el archivo de solución ExcelBot.sln. 98 | - Registre el bot en [Bot Framework](https://dev.botframework.com/bots/new). 99 | - Copie los valores de MicrosoftAppId y MicrosoftAppPassword del bot al archivo PrivateSettings.config 100 | - Registre el bot para llamar a Microsoft Graph. 101 | - Copie el **Id. de cliente** y el **secreto** de Azure Active Directory al archivo PrivateSettings.config. 102 | - Cree un nuevo modelo en el servicio [LUIS](https://www.luis.ai). 103 | - Importe el archivo LUIS\\excelbot.json a LUIS. 104 | - Entrene y publique el modelo de LUIS. 105 | - Copie el Id. de modelo y la clave de suscripción de LUIS al archivo Dialogs\\ExcelBotDialog.cs. 106 | - (Opcional) Habilite el chat web para el bot en Bot Framework y copie la plantilla de inserción de web chat en el archivo chat.htm 107 | - (Opcional) Para que el bot envíe la telemetría a [Visual Studio Application Insights](https://azure.microsoft.com/es-es/services/application-insights/), copie la clave de instrumentación en los siguientes archivos: ApplicationInsights.config, default.htm, loggedin.htm, chat.htm 108 | - Compile la solución. 109 | - Presione F5 para iniciar el bot de forma local. 110 | - Pruebe el bot de forma local mediante [Bot Framework Emulator](https://docs.botframework.com/es-es/tools/bot-framework-emulator). 111 | - Cree una base de datos de Azure Cosmos DB en Azure que use la API de SQL. 112 | - Reemplace el nombre de host del bot en el archivo PrivateSettings.config. 113 | - Reemplace el URI y la clave de la base datos en el archivo PrivateSettings.config. 114 | - Publique la solución en la aplicación web de Azure. 115 | - Navegue a la página chat.htm para probar el bot implementado mediante el control de web chat. 116 | 117 | ## Envíenos sus comentarios 118 | 119 | 120 | 121 | Su opinión es importante para nosotros. 122 | 123 | Revise el código de ejemplo y háganos saber todas las preguntas y las dificultades que encuentre [enviando un problema](https://github.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/issues) directamente en este repositorio. Incluya los pasos de reproducción, las salidas de la consola y los mensajes de error en cualquier problema que envíe. 124 | 125 | Este proyecto ha adoptado el [Código de conducta de código abierto de Microsoft](https://opensource.microsoft.com/codeofconduct/). Para obtener más información, vea [Preguntas frecuentes sobre el código de conducta](https://opensource.microsoft.com/codeofconduct/faq/) o póngase en contacto con [opencode@microsoft.com](mailto:opencode@microsoft.com) si tiene otras preguntas o comentarios. 126 | 127 | ## Copyright 128 | 129 | Copyright (c) 2019 Microsoft. Todos los derechos reservados. 130 | 131 | -------------------------------------------------------------------------------- /README-localized/README-fr-fr.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-excel 5 | - ms-graph 6 | languages: 7 | - csharp 8 | description: "Le bot Excel est un bot construit avec Microsoft Bot Framework qui montre comment utiliser Excel avec l’API Microsoft Graph" 9 | extensions: 10 | contentType: samples 11 | technologies: 12 | - Microsoft Graph 13 | - Microsoft Bot Framework 14 | services: 15 | - Excel 16 | createdDate: 9/15/2016 10:30:08 PM 17 | --- 18 | 19 | # Bot Excel 20 | 21 | ## Table des matières ## 22 | 23 | [Introduction.](#introduction) 24 | 25 | [Conditions préalables.](#prerequisites) 26 | 27 | [Clonage ou téléchargement de ce référentiel.](#Cloning-or-downloading-this-repository) 28 | 29 | [Configuration de votre client Azure AD.](#Configure-your-Azure-AD-tenant) 30 | 31 | 32 | [Inscription du bot.](#Register-the-bot) 33 | 34 | [Donnez-nous votre avis.](#Give-us-your-feedback) 35 | 36 | ## Introduction. 37 | 38 | Le bot Excel est un exemple qui montre comment utiliser [Microsoft Graph](https://graph.microsoft.io) et en particulier l’[API REST Excel](https://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/excel) pour accéder aux classeurs Excel stockés dans OneDrive Entreprise via une interface utilisateur de conversation. Il est écrit en C# et utilise [Microsoft Bot Framework](https://dev.botframework.com/) et le service [Language Understanding Intelligent Service (LUIS)](https://www.luis.ai/). 39 | 40 | *Remarque* : Le code dans cet exemple a été écrit pour un prototype d’expérience utilisateur et ne montre pas nécessairement comment créer un code de qualité de production. 41 | 42 | ## Conditions préalables. 43 | 44 | 45 | Cet exemple nécessite les éléments suivants : 46 | 47 | - Visual Studio 2017. 48 | - Un compte professionnel Office 365. Vous pouvez vous inscrire un [abonnement Office 365 Developer](https://msdn.microsoft.com/en-us/office/office365/howto/setup-development-environment) qui inclut les ressources dont vous avez besoin pour commencer à créer des applications Office 365. 49 | 50 | ## Clonage ou téléchargement de ce référentiel. 51 | 52 | 53 | - Clonez ce référentiel.dans un dossier local 54 | 55 | ` git clone https://github.com/nicolesigei/botframework-csharp-excelbot-rest-sample.git ` 56 | 57 | 58 | ## Configuration de votre client Azure AD. 59 | 60 | 61 | 1. Ouvrez un navigateur et accédez au [Centre d’administration Azure Active Directory](https://aad.portal.azure.com). Connectez-vous en utilisant un **compte professionnel ou scolaire**. 62 | 63 | 1. Sélectionnez **Azure Active Directory** dans le volet de navigation gauche, puis sélectionnez **Inscriptions d’applications** sous **Gérer**. 64 | 65 | ![Une capture d’écran des inscriptions d’applications ](readme-images/aad-portal-app-registrations.png) 66 | 67 | 1. Sélectionnez **Nouvelle inscription**. Sur la page **Inscrire une application**, définissez les valeurs comme suit. 68 | 69 | - Donnez un **nom**favori, par ex. `Application de bot Excel`. 70 | - Définissez les **Types de comptes pris en charge** sur **Comptes figurant dans un annuaire organisationnel**. 71 | - Sous **URI de redirection**, définissez la première liste déroulante sur `Web` et la valeur sur http://localhost:3978/callback. 72 | 73 | ![Capture d’écran de la page Inscrire une application](readme-images/aad-register-an-app.PNG) 74 | 75 | > **Remarque :** Si vous exécutez cette fonction localement et sur Azure, vous devez ajouter deux URL de redirection ici, une à votre instance locale et une autre à votre application Web Azure. 76 | 77 | 1. Choisissez **Inscrire**. Sur la page **Application de bot Excel**, copiez la valeur de l’**ID d’application (client)** et enregistrez-la car vous en aurez besoin pour configurer l’application. 78 | 79 | ![Une capture d’écran de l’ID d’application de la nouvelle inscription d'application](readme-images/aad-application-id.PNG) 80 | 81 | 1. Sélectionnez **Certificats et secrets** sous **Gérer**. Sélectionnez le bouton **Nouvelle clé secrète client**. Entrez une valeur dans la **Description**, sélectionnez l'une des options pour **Expire le**, puis choisissez **Ajouter**. 82 | 83 | ![Une capture d’écran de la boîte de dialogue Ajouter une clé secrète client](readme-images/aad-new-client-secret.png) 84 | 85 | 1. Copiez la valeur due la clé secrète client avant de quitter cette page. Vous en aurez besoin pour configurer l’application. 86 | 87 | > \[!IMPORTANT] Cette clé secrète client n’apparaîtra plus, aussi veillez à la copier maintenant. 88 | 89 | ![Capture d’écran de la clé secrète client récemment ajoutée](readme-images/aad-copy-client-secret.png) 90 | ## Inscription du bot. 91 | 92 | Procédez comme suit pour configurer votre environnement de développement afin de créer et tester le bot Excel : 93 | 94 | - Téléchargez et installez l’[Émulateur Azure Cosmos DB](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator). 95 | 96 | - Effectuez une copie de **./ExcelBot/PrivateSettings.config.example** dans le même répertoire. Renommez le fichier **PrivateSettings.config**. 97 | - Ouvrir le fichier de solution ExcelBot.sln. 98 | - Inscrivez le bot dans [Bot Framework](https://dev.botframework.com/bots/new). 99 | - Copiez le bot MicrosoftAppId et MicrosoftAppPassword dans le fichier PrivateSettings.config. 100 | - Inscrivez le bot pour appeler Microsoft Graph. 101 | - Copier l’**ID client** et la **clé secrète** Azure Active Directory dans le fichier PrivateSettings.config. 102 | - Créer un modèle dans le service [LUIS](https://www.luis.ai). 103 | - Importez le fichier LUIS\\excelbot.json dans LUIS. 104 | - Adaptez et publiez le modèle LUIS. 105 | - Copier l’ID de modèle LUIS et la clé d’abonnement dans le fichier Dialogs\\ExcelBotDialog.cs. 106 | - (Facultatif) Activez la conversation Web pour le bot dans Bot Framework et copiez le modèle d’incorporation de conversation Web dans le fichier chat.htm. 107 | - (Facultatif) Pour que le bot envoie la télémétrie à [Visual Studio Application Insights](https://azure.microsoft.com/en-us/services/application-insights/), copiez la clé d’instrumentation dans les fichiers suivants : ApplicationInsights.config, default.htm, loggedin.htm, chat.htm 108 | - Générez la solution. 109 | - Appuyez sur F5 pour lancer le bot localement. 110 | - Testez le robot localement à l’aide de [Bot Framework Emulator](https://docs.botframework.com/en-us/tools/bot-framework-emulator). 111 | - Créez une base de données Azure Cosmos dans Azure qui utilise l’API SQL. 112 | - Remplacez le nom d’hôte du bot dans le fichier PrivateSettings.config. 113 | - Remplacez l’URI et la clé de la base de données dans le fichier PrivateSettings.config. 114 | - Publiez la solution sur l’application Web Azure. 115 | - Testez le bot déployé à l’aide du contrôle de conversation Web en accédant à la page chat.htm. 116 | 117 | ## Donnez-nous votre avis. 118 | 119 | 120 | 121 | Votre avis compte beaucoup pour nous. 122 | 123 | Consultez les codes d’exemple et signalez-nous toute question ou tout problème vous trouvez en [soumettant une question](https://github.com/microsoftgraph/botframework-csharp-excelbot-rest-sample/issues) directement dans ce référentiel. Fournissez des étapes de reproduction, de sortie de la console et des messages d’erreur dans tout problème que vous ouvrez. 124 | 125 | Ce projet a adopté le [Code de conduite Open Source de Microsoft](https://opensource.microsoft.com/codeofconduct/). Pour en savoir plus, reportez-vous à la [FAQ relative au code de conduite](https://opensource.microsoft.com/codeofconduct/faq/) ou contactez [opencode@microsoft.com](mailto:opencode@microsoft.com) pour toute question ou tout commentaire. 126 | 127 | ## Copyright 128 | 129 | Copyright (c) 2019 Microsoft. Tous droits réservés. 130 | 131 | -------------------------------------------------------------------------------- /ExcelBot/Dialogs/CellsDialog.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | using ExcelBot.Helpers; 7 | using ExcelBot.Model; 8 | using ExcelBot.Workers; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Builder.Luis.Models; 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace ExcelBot.Dialogs 15 | { 16 | public partial class ExcelBotDialog : GraphDialog 17 | { 18 | #region Properties 19 | internal object Value { get; set; } 20 | #endregion 21 | 22 | #region Intents 23 | #region - Get Cell Values 24 | [LuisIntent("getCellValue")] 25 | public async Task GetCellValue(IDialogContext context, LuisResult result) 26 | { 27 | // Telemetry 28 | TelemetryHelper.TrackDialog(context, result, "Cells", "GetCellValue"); 29 | 30 | var cellAddress = result.Entities[0].Entity.ToUpper(); 31 | context.UserData.SetValue("CellAddress", cellAddress); 32 | 33 | context.UserData.SetValue("Type", ObjectType.Cell); 34 | context.UserData.SetValue("Name", cellAddress); 35 | 36 | string workbookId = String.Empty; 37 | context.UserData.TryGetValue("WorkbookId", out workbookId); 38 | 39 | if (!(String.IsNullOrEmpty(workbookId))) 40 | { 41 | await CellWorker.DoGetCellValue(context); 42 | context.Wait(MessageReceived); 43 | } 44 | else 45 | { 46 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_GetCellValue); 47 | } 48 | } 49 | public async Task AfterConfirm_GetCellValue(IDialogContext context, IAwaitable result) 50 | { 51 | if (await result) 52 | { 53 | await CellWorker.DoGetCellValue(context); 54 | } 55 | context.Wait(MessageReceived); 56 | } 57 | 58 | [LuisIntent("getActiveCellValue")] 59 | public async Task GetActiveCellValue(IDialogContext context, LuisResult result) 60 | { 61 | // Telemetry 62 | TelemetryHelper.TrackDialog(context, result, "Cells", "GetActiveCellValue"); 63 | 64 | string workbookId = String.Empty; 65 | context.UserData.TryGetValue("WorkbookId", out workbookId); 66 | 67 | if (!(String.IsNullOrEmpty(workbookId))) 68 | { 69 | await NamedItemsWorker.DoGetNamedItemValue(context); 70 | context.Wait(MessageReceived); 71 | } 72 | else 73 | { 74 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_GetActiveCellValue); 75 | } 76 | } 77 | public async Task AfterConfirm_GetActiveCellValue(IDialogContext context, IAwaitable result) 78 | { 79 | if (await result) 80 | { 81 | await NamedItemsWorker.DoGetNamedItemValue(context); 82 | } 83 | context.Wait(MessageReceived); 84 | } 85 | 86 | #endregion 87 | #region - Set Cell Value 88 | [LuisIntent("setCellNumberValue")] 89 | public async Task SetCellNumberValue(IDialogContext context, LuisResult result) 90 | { 91 | // Telemetry 92 | TelemetryHelper.TrackDialog(context, result, "Cells", "SetCellNumberValue"); 93 | 94 | var cellAddress = LuisHelper.GetCellEntity(result.Entities); 95 | context.UserData.SetValue("CellAddress", cellAddress); 96 | 97 | context.UserData.SetValue("Type", ObjectType.Cell); 98 | context.UserData.SetValue("Name", cellAddress); 99 | 100 | Value = LuisHelper.GetValue(result); 101 | 102 | string workbookId = String.Empty; 103 | context.UserData.TryGetValue("WorkbookId", out workbookId); 104 | 105 | if (!(String.IsNullOrEmpty(workbookId))) 106 | { 107 | if (cellAddress != null) 108 | { 109 | await CellWorker.DoSetCellValue(context, Value); 110 | } 111 | else 112 | { 113 | await context.PostAsync($"You need to provide the address of a cell to set the value"); 114 | } 115 | context.Wait(MessageReceived); 116 | } 117 | else 118 | { 119 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_SetCellNumberValue); 120 | } 121 | } 122 | 123 | public async Task AfterConfirm_SetCellNumberValue(IDialogContext context, IAwaitable result) 124 | { 125 | if (await result) 126 | { 127 | await CellWorker.DoSetCellValue(context, Value); 128 | } 129 | context.Wait(MessageReceived); 130 | } 131 | 132 | [LuisIntent("setCellStringValue")] 133 | public async Task SetCellStringValue(IDialogContext context, LuisResult result) 134 | { 135 | // Telemetry 136 | TelemetryHelper.TrackDialog(context, result, "Cells", "SetCellStringValue"); 137 | 138 | var cellAddress = LuisHelper.GetCellEntity(result.Entities); 139 | context.UserData.SetValue("CellAddress", cellAddress); 140 | 141 | context.UserData.SetValue("Type", ObjectType.Cell); 142 | context.UserData.SetValue("Name", cellAddress); 143 | 144 | Value = LuisHelper.GetValue(result); 145 | 146 | string workbookId = String.Empty; 147 | context.UserData.TryGetValue("WorkbookId", out workbookId); 148 | 149 | if (!(String.IsNullOrEmpty(workbookId))) 150 | { 151 | if (cellAddress != null) 152 | { 153 | await CellWorker.DoSetCellValue(context, Value); 154 | } 155 | else 156 | { 157 | await context.PostAsync($"You need to provide the name of a cell to set the value"); 158 | } 159 | context.Wait(MessageReceived); 160 | } 161 | else 162 | { 163 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_SetCellStringValue); 164 | } 165 | } 166 | 167 | public async Task AfterConfirm_SetCellStringValue(IDialogContext context, IAwaitable result) 168 | { 169 | if (await result) 170 | { 171 | await CellWorker.DoSetCellValue(context, Value); 172 | } 173 | context.Wait(MessageReceived); 174 | } 175 | 176 | [LuisIntent("setActiveCellValue")] 177 | public async Task SetActiveCellValue(IDialogContext context, LuisResult result) 178 | { 179 | // Telemetry 180 | TelemetryHelper.TrackDialog(context, result, "Cells", "SetActiveCellValue"); 181 | 182 | ObjectType? type = null; 183 | context.UserData.TryGetValue("Type", out type); 184 | 185 | var name = LuisHelper.GetNameEntity(result.Entities); 186 | if (!string.IsNullOrEmpty(name)) 187 | { 188 | context.UserData.SetValue("Name", name); 189 | } 190 | 191 | Value = LuisHelper.GetValue(result); 192 | 193 | string workbookId = String.Empty; 194 | context.UserData.TryGetValue("WorkbookId", out workbookId); 195 | 196 | if (!(String.IsNullOrEmpty(workbookId))) 197 | { 198 | await NamedItemsWorker.DoSetNamedItemValue(context, Value); 199 | context.Wait(MessageReceived); 200 | } 201 | else 202 | { 203 | context.Call(new ConfirmOpenWorkbookDialog(), AfterConfirm_SetActiveCellValue); 204 | } 205 | } 206 | 207 | public async Task AfterConfirm_SetActiveCellValue(IDialogContext context, IAwaitable result) 208 | { 209 | if (await result) 210 | { 211 | await NamedItemsWorker.DoSetNamedItemValue(context, Value); 212 | } 213 | context.Wait(MessageReceived); 214 | } 215 | 216 | #endregion 217 | #endregion 218 | } 219 | } -------------------------------------------------------------------------------- /ExcelBot/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 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 | 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 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to this code sample 2 | 3 | Thank you for your interest in our sample 4 | 5 | * [Ways to contribute](#ways-to-contribute) 6 | * [To contribute using Git](#to-contribute-using-git) 7 | * [Contribute code](#contribute-code) 8 | * [FAQ](#faq) 9 | * [More resources](#more-resources) 10 | 11 | ## Ways to contribute 12 | 13 | Here are some ways you can contribute to this sample: 14 | 15 | * Add better comments to the sample code. 16 | * Fix issues opened in GitHub against this sample. 17 | * Add a new feature to the sample. 18 | 19 | We want your contributions. Help the developer community by improving this sample. 20 | Contributions can include Bug fixes, new features, and better code documentation. 21 | Submit code comment contributions where you want a better explanation of what's going on. 22 | See a good example of [code commenting](https://github.com/OfficeDev/O365-Android-Microsoft-Graph-Connect/blob/master/app/src/main/java/com/microsoft/office365/connectmicrosoftgraph/AuthenticationManager.java). 23 | 24 | Another great way to improve the sample in this repository is to take on some of the open issues filed against the repository. You may have a solution to an bug in the sample code that hasn't been addressed. Fix the issue and then create a pull request following our [Contribute code](#contribute-code) guidance. 25 | 26 | If you want to add a new feature to the sample, be sure you have the agreement of the repository owner before writing the code. Start by opening an issue in the repository. Use the new issue to propose the feature. The repository owner will respond and will usually ask you for more information. When the owner agrees to take the new feature, code it and submit a pull request. 27 | 28 | ## To contribute using Git 29 | For most contributions, you'll be asked to sign a Contribution License Agreement (CLA). For those contributions that need it, The Office 365 organization on GitHub will send a link to the CLA that we want you to sign via email. 30 | By signing the CLA, you acknowledge the rights of the GitHub community to use any code that you submit. The intellectual property represented by the code contribution is licensed for use by Microsoft open source projects. 31 | 32 | If Office 365 emails an CLA to you, you need to sign it before you can contribute large submissions to a project. You only need to complete and submit it once. 33 | Read the CLA carefully. You may need to have your employer sign it. 34 | 35 | Signing the CLA does not grant you rights to commit to the main repository, but it does mean that the Office Developer and Office Developer Content Publishing teams will be able to review and approve your contributions. You will be credited for your submissions. 36 | 37 | Pull requests are typically reviewed within 10 business days. 38 | 39 | ## Use GitHub, Git, and this repository 40 | 41 | **Note:** Most of the information in this section can be found in [GitHub Help] articles. If you're familiar with Git and GitHub, skip to the **Contribute code** section for the specifics of the code contributions for this repository. 42 | 43 | ### To set up your fork of the repository 44 | 45 | 1. Set up a GitHub account so you can contribute to this project. If you haven't done this, go to [GitHub](https://github.com/join) and do it now. 46 | 2. Install Git on your computer. Follow the steps in the [Setting up Git Tutorial] [Set Up Git]. 47 | 3. Create your own fork of this repository. To do this, at the top of the page, choose the **Fork** button. 48 | 4. Copy your fork to your computer. To do this, open Git Bash. At the command prompt enter: 49 | 50 | git clone https://github.com//.git 51 | 52 | Next, create a reference to the root repository by entering these commands: 53 | 54 | cd 55 | git remote add upstream https://github.com/OfficeDev/.git 56 | git fetch upstream 57 | 58 | Congratulations! You've now set up your repository. You won't need to repeat these steps again. 59 | 60 | ## Contribute code 61 | 62 | To make the contribution process as seamless as possible, follow these steps. 63 | 64 | ### To contribute code 65 | 66 | 1. Create a new branch. 67 | 2. Add new code or modify existing code. 68 | 3. Submit a pull request to the main repository. 69 | 4. Await notification of acceptance and merge. 70 | 5. Delete the branch. 71 | 72 | 73 | ### To create a new branch 74 | 75 | 1. Open Git Bash. 76 | 2. At the Git Bash command prompt, type `git pull upstream master:`. This creates a new branch locally that is copied from the latest OfficeDev master branch. 77 | 3. At the Git Bash command prompt, type `git push origin `. This alerts GitHub to the new branch. You should now see the new branch in your fork of the repository on GitHub. 78 | 4. At the Git Bash command prompt, type `git checkout ` to switch to your new branch. 79 | 80 | ### Add new code or modify existing code 81 | 82 | Navigate to the repository on your computer. On a Windows PC, the repository files are in `C:\Users\\`. 83 | 84 | Use the IDE of your choice to modify and build the sample. Once you have completed your change, commented your code, and test, check the code 85 | into the remote branch on GitHub. 86 | 87 | #### Code contribution checklist 88 | Be sure to satisfy all of the requirements in the following list before submitting a pull request: 89 | - Follow the code style found in the cloned repository code. Our Android code follows the style conventions found in the [Code Style for Contributors](https://source.android.com/source/code-style.html) guide. 90 | - Code must be tested. 91 | - Test the sample UI thoroughly to be sure nothing has been broken by your change. 92 | - Keep the size of your code change reasonable. if the repository owner cannot review your code change in 4 hours or less, your pull request may not be reviewed and approved quickly. 93 | - Avoid unnecessary changes to cloned or forked code. The reviewer will use a tool to find the differences between your code and the original code. Whitespace changes are called out along with your code. Be sure your changes will help improve the content. 94 | 95 | ### Push your code to the remote GitHub branch 96 | The files in `C:\Users\\` are a working copy of the new branch that you created in your local repository. Changing anything in this folder doesn't affect the local repository until you commit a change. To commit a change to the local repository, type the following commands in GitBash: 97 | 98 | git add . 99 | git commit -v -a -m "" 100 | 101 | The `add` command adds your changes to a staging area in preparation for committing them to the repository. The period after the `add` command specifies that you want to stage all of the files that you added or modified, checking subfolders recursively. (If you don't want to commit all of the changes, you can add specific files. You can also undo a commit. For help, type `git add -help` or `git status`.) 102 | 103 | The `commit` command applies the staged changes to the repository. The switch `-m` means you are providing the commit comment in the command line. The -v and -a switches can be omitted. The -v switch is for verbose output from the command, and -a does what you already did with the add command. 104 | 105 | You can commit multiple times while you are doing your work, or you can commit once when you're done. 106 | 107 | ### Submit a pull request to the master repository 108 | 109 | When you're finished with your work and are ready to have it merged into the master repository, follow these steps. 110 | 111 | #### To submit a pull request to the master repository 112 | 113 | 1. In the Git Bash command prompt, type `git push origin `. In your local repository, `origin` refers to your GitHub repository that you cloned the local repository from. This command pushes the current state of your new branch, including all commits made in the previous steps, to your GitHub fork. 114 | 2. On the GitHub site, navigate in your fork to the new branch. 115 | 3. Choose the **Pull Request** button at the top of the page. 116 | 4. Verify the Base branch is `OfficeDev/@master` and the Head branch is `/@`. 117 | 5. Choose the **Update Commit Range** button. 118 | 6. Add a title to your pull request, and describe all the changes you're making. 119 | 7. Submit the pull request. 120 | 121 | One of the site administrators will process your pull request. Your pull request will surface on the `OfficeDev/` site under Issues. When the pull request is accepted, the issue will be resolved. 122 | 123 | ### Repository owner code review 124 | The owner of the repository will review your pull request to be sure that all requirements are met. If the reviewer 125 | finds any issues, she will communicate with you and ask you to address them and then submit a new pull request. If your pull 126 | request is accepted, then the repository owner will tell you that your pull request is to be merged. 127 | 128 | ### Create a new branch after merge 129 | 130 | After a branch is successfully merged (that is, your pull request is accepted), don't continue working in that local branch. This can lead to merge conflicts if you submit another pull request. To do another update, create a new local branch from the successfully merged upstream branch, and then delete your initial local branch. 131 | 132 | For example, if your local branch X was successfully merged into the OfficeDev/O365-Android-Microsoft-Graph-Connect master branch and you want to make additional updates to the code that was merged. Create a new local branch, X2, from the OfficeDev/O365-Android-Microsoft-Graph-Connect branch. To do this, open GitBash and execute the following commands: 133 | 134 | cd 135 | git pull upstream master:X2 136 | git push origin X2 137 | 138 | You now have local copies (in a new local branch) of the work that you submitted in branch X. The X2 branch also contains all the work other developers have merged, so if your work depends on others' work (for example, a base class), it is available in the new branch. You can verify that your previous work (and others' work) is in the branch by checking out the new branch... 139 | 140 | git checkout X2 141 | 142 | ...and verifying the code. (The `checkout` command updates the files in `C:\Users\\O365-Android-Microsoft-Graph-Connect` to the current state of the X2 branch.) Once you check out the new branch, you can make updates to the code and commit them as usual. However, to avoid working in the merged branch (X) by mistake, it's best to delete it (see the following **Delete a branch** section). 143 | 144 | ### Delete a branch 145 | 146 | Once your changes are successfully merged into the main repository, delete the branch you used because you no longer need it. Any additional work should be done in a new branch. 147 | 148 | #### To delete a branch 149 | 150 | 1. In the Git Bash command prompt, type `git checkout master`. This ensures that you aren't in the branch to be deleted (which isn't allowed). 151 | 2. Next, at the command prompt, type `git branch -d `. This deletes the branch on your computer only if it has been successfully merged to the upstream repository. (You can override this behavior with the `-D` flag, but first be sure you want to do this.) 152 | 3. Finally, type `git push origin :` at the command prompt (a space before the colon and no space after it). This will delete the branch on your github fork. 153 | 154 | Congratulations, you have successfully contributed to the sample app! 155 | 156 | 157 | ## FAQ 158 | 159 | ### How do I get a GitHub account? 160 | 161 | Fill out the form at [Join GitHub](https://github.com/join) to open a free GitHub account. 162 | 163 | ### Where do I get a Contributor's License Agreement? 164 | 165 | You will automatically be sent a notice that you need to sign the Contributor's License Agreement (CLA) if your pull request requires one. 166 | 167 | As a community member, **you must sign the CLA before you can contribute large submissions to this project**. You only need complete and submit the CLA document once. Carefully review the document. You may be required to have your employer sign the document. 168 | 169 | ### What happens with my contributions? 170 | 171 | When you submit your changes, via a pull request, our team will be notified and will review your pull request. You will receive notifications about your pull request from GitHub; you may also be notified by someone from our team if we need more information. If your pull request is approved, we'll update the documentation on GitHub and on MSDN. We reserve the right to edit your submission for legal, style, clarity, or other issues. 172 | 173 | ### Who approves pull requests? 174 | 175 | The owner of the sample repository approves pull requests. 176 | 177 | ### How soon will I get a response about my change request? 178 | 179 | Pull requests are typically reviewed within 10 business days. 180 | 181 | 182 | ## More resources 183 | 184 | * To learn more about Markdown, go to the Git creator's site [Daring Fireball]. 185 | * To learn more about using Git and GitHub, check out the [GitHub Help section] [GitHub Help]. 186 | 187 | [GitHub Home]: http://github.com 188 | [GitHub Help]: http://help.github.com/ 189 | [Set Up Git]: http://help.github.com/win-set-up-git/ 190 | [Markdown Home]: http://daringfireball.net/projects/markdown/ 191 | [Daring Fireball]: http://daringfireball.net/ 192 | --------------------------------------------------------------------------------