├── ApprovalBot
├── Global.asax
├── Models
│ ├── FileInfo.cs
│ ├── ActionableEmailResponse.cs
│ ├── ApproverInfo.cs
│ ├── Approval.cs
│ └── ActionData.cs
├── OutlookAdaptive
│ ├── AdaptiveToggleVisibilityAction.cs
│ ├── AdaptiveActionSet.cs
│ └── AdaptiveHttpAction.cs
├── default.htm
├── Helpers
│ ├── TimeZoneHelper.cs
│ ├── EmailHelper.cs
│ ├── DatabaseHelper.cs
│ ├── ApprovalStatusHelper.cs
│ ├── ApprovalRequestHelper.cs
│ └── GraphHelper.cs
├── PrivateSettings.example.config
├── App_Start
│ └── WebApiConfig.cs
├── Web.Debug.config
├── Web.Release.config
├── Properties
│ └── AssemblyInfo.cs
├── Global.asax.cs
├── Dialogs
│ ├── ExceptionHandlerDialog.cs
│ └── RootDialog.cs
├── Controllers
│ ├── MessagesController.cs
│ └── ResponsesController.cs
├── packages.config
├── Web.config
└── ApprovalBot.csproj
├── readme-images
├── ngrok1.PNG
├── ngrok2.PNG
├── hello-bot.PNG
├── aad-application-id.PNG
├── aad-register-an-app.PNG
├── configure-emulator.PNG
├── aad-new-client-secret.png
├── aad-copy-client-secret.png
└── aad-portal-app-registrations.png
├── CODE_OF_CONDUCT.md
├── ApprovalBot.sln
├── LICENSE
├── SECURITY.md
├── .gitignore
└── README.md
/ApprovalBot/Global.asax:
--------------------------------------------------------------------------------
1 | <%@ Application Codebehind="Global.asax.cs" Inherits="ApprovalBot.WebApiApplication" Language="C#" %>
2 |
--------------------------------------------------------------------------------
/readme-images/ngrok1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/ngrok1.PNG
--------------------------------------------------------------------------------
/readme-images/ngrok2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/ngrok2.PNG
--------------------------------------------------------------------------------
/readme-images/hello-bot.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/hello-bot.PNG
--------------------------------------------------------------------------------
/readme-images/aad-application-id.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/aad-application-id.PNG
--------------------------------------------------------------------------------
/readme-images/aad-register-an-app.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/aad-register-an-app.PNG
--------------------------------------------------------------------------------
/readme-images/configure-emulator.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/configure-emulator.PNG
--------------------------------------------------------------------------------
/readme-images/aad-new-client-secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/aad-new-client-secret.png
--------------------------------------------------------------------------------
/readme-images/aad-copy-client-secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/aad-copy-client-secret.png
--------------------------------------------------------------------------------
/readme-images/aad-portal-app-registrations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoftgraph/botframework-csharp-approvalbot-sample/HEAD/readme-images/aad-portal-app-registrations.png
--------------------------------------------------------------------------------
/ApprovalBot/Models/FileInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web;
5 |
6 | namespace ApprovalBot.Models
7 | {
8 | public class ApprovalFileInfo
9 | {
10 | public string Id { get; set; }
11 | public string Name { get; set; }
12 | public string SharingUrl { get; set; }
13 | public string ThumbnailUrl { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/ApprovalBot/Models/ActionableEmailResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web;
5 |
6 | namespace ApprovalBot.Models
7 | {
8 | public class ActionableEmailResponse
9 | {
10 | public string UserEmail { get; set; }
11 | public string ApprovalId { get; set; }
12 | public string Response { get; set; }
13 | public string Notes { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/ApprovalBot/OutlookAdaptive/AdaptiveToggleVisibilityAction.cs:
--------------------------------------------------------------------------------
1 | using AdaptiveCards;
2 | using Newtonsoft.Json;
3 |
4 | namespace ApprovalBot.OutlookAdaptive
5 | {
6 | public class AdaptiveToggleVisibilityAction : AdaptiveAction
7 | {
8 | public const string TypeName = "Action.ToggleVisibility";
9 |
10 | public override string Type { get; set; } = TypeName;
11 |
12 | [JsonRequired]
13 | public string[] TargetElements { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/ApprovalBot/default.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | ApprovalBot
9 | Describe your bot here and your terms of use etc.
10 | Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to
https://your_bots_hostname/api/messages
11 |
12 |
13 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 | - Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support)
11 |
--------------------------------------------------------------------------------
/ApprovalBot/Models/ApproverInfo.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Web;
6 |
7 | namespace ApprovalBot.Models
8 | {
9 | public enum ResponseStatus
10 | {
11 | NotResponded,
12 | Approved,
13 | Rejected
14 | }
15 |
16 | public class ApproverInfo
17 | {
18 | [JsonProperty(PropertyName = "emailAddress")]
19 | public string EmailAddress { get; set; }
20 | [JsonProperty(PropertyName = "response")]
21 | public ResponseStatus Response { get; set; }
22 | [JsonProperty(PropertyName = "responseNote")]
23 | public string ResponseNote { get; set; }
24 | }
25 | }
--------------------------------------------------------------------------------
/ApprovalBot/Models/Approval.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | namespace ApprovalBot.Models
6 | {
7 | public class Approval
8 | {
9 | [JsonProperty(PropertyName = "id")]
10 | public string Id { get; set; }
11 | [JsonProperty(PropertyName = "requestor")]
12 | public string Requestor { get; set; }
13 | [JsonProperty(PropertyName = "file")]
14 | public ApprovalFileInfo File { get; set; }
15 | [JsonProperty(PropertyName = "approvers")]
16 | public List Approvers { get; set; }
17 | [JsonProperty(PropertyName = "requestDate")]
18 | public DateTimeOffset RequestDate { get; set; }
19 | }
20 | }
--------------------------------------------------------------------------------
/ApprovalBot/Helpers/TimeZoneHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web;
5 |
6 | namespace ApprovalBot.Helpers
7 | {
8 | public static class TimeZoneHelper
9 | {
10 | public static string GetAdaptiveDateTimeString(DateTimeOffset dateTimeOffset)
11 | {
12 | var rfc3389String = $"{dateTimeOffset.UtcDateTime.ToString("s")}Z";
13 |
14 | // Returns string like
15 | // {{DATE(2018-04-26T07:00:00Z,SHORT)}} {{TIME(2018-04-26T07:00:00Z)}}
16 | // See docs at https://docs.microsoft.com/en-us/adaptive-cards/create/textfeatures#datetime-function-rules
17 | return $"{{{{DATE({rfc3389String},SHORT)}}}} {{{{TIME({rfc3389String})}}}}";
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/ApprovalBot/OutlookAdaptive/AdaptiveActionSet.cs:
--------------------------------------------------------------------------------
1 | using AdaptiveCards;
2 | using Newtonsoft.Json;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 |
6 | namespace ApprovalBot.OutlookAdaptive
7 | {
8 | public class AdaptiveActionSet : AdaptiveElement
9 | {
10 | public const string TypeName = "ActionSet";
11 |
12 | public override string Type { get; set; } = TypeName;
13 |
14 | public AdaptiveActionSet(){}
15 |
16 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
17 | [DefaultValue(typeof(AdaptiveHorizontalAlignment), "left")]
18 | public AdaptiveHorizontalAlignment HorizontalAlignment { get; set; }
19 |
20 | [JsonRequired]
21 | public List Actions { get; set; } = new List();
22 | }
23 | }
--------------------------------------------------------------------------------
/ApprovalBot/Models/ActionData.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Linq;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Web;
6 |
7 | namespace ApprovalBot.Models
8 | {
9 | public static class CardActionTypes
10 | {
11 | public static string SelectFile = "selectFile";
12 | public static string WrongFile = "wrongFile";
13 | public static string SendApprovalRequest = "sendApproval";
14 | public static string SelectApproval = "selectApproval";
15 | }
16 |
17 | public class ActionData
18 | {
19 | public string CardAction { get; set; }
20 | public string SelectedFile { get; set; }
21 | public string Approvers { get; set; }
22 | public string SelectedApproval { get; set; }
23 |
24 | public static ActionData Parse(object obj)
25 | {
26 | return ((JToken)obj).ToObject();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/ApprovalBot/Helpers/EmailHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Mail;
3 |
4 | namespace ApprovalBot.Helpers
5 | {
6 | public static class EmailHelper
7 | {
8 | public static bool IsValidSmtpAddress(string address)
9 | {
10 | try
11 | {
12 | new MailAddress(address);
13 | return true;
14 | }
15 | catch (FormatException)
16 | {
17 | return false;
18 | }
19 | }
20 |
21 | public static string[] ConvertDelimitedAddressStringToArray(string addressString)
22 | {
23 | string[] addresses = addressString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
24 |
25 | foreach(string address in addresses)
26 | {
27 | if (!IsValidSmtpAddress(address))
28 | {
29 | return null;
30 | }
31 | }
32 |
33 | return addresses;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/ApprovalBot.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2037
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApprovalBot", "ApprovalBot\ApprovalBot.csproj", "{4E426B56-D589-44EA-B7A1-72ED5343B354}"
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 | {4E426B56-D589-44EA-B7A1-72ED5343B354}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {4E426B56-D589-44EA-B7A1-72ED5343B354}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {4E426B56-D589-44EA-B7A1-72ED5343B354}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {4E426B56-D589-44EA-B7A1-72ED5343B354}.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 = {2013BD2B-0FBF-4CE3-AF48-A537C5836238}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/ApprovalBot/OutlookAdaptive/AdaptiveHttpAction.cs:
--------------------------------------------------------------------------------
1 | using AdaptiveCards;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Converters;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 |
8 | namespace ApprovalBot.OutlookAdaptive
9 | {
10 | [JsonConverter(typeof(StringEnumConverter), true)]
11 | public enum AdaptiveHttpActionMethod
12 | {
13 | GET,
14 | POST
15 | }
16 |
17 | public class AdaptiveHttpActionHeader
18 | {
19 | [JsonRequired]
20 | public string Name { get; set; }
21 |
22 | [JsonRequired]
23 | public string Value { get; set; }
24 | }
25 |
26 | public class AdaptiveHttpAction : AdaptiveAction
27 | {
28 | public const string TypeName = "Action.Http";
29 |
30 | public override string Type { get; set; } = TypeName;
31 |
32 | [JsonRequired]
33 | public AdaptiveHttpActionMethod Method { get; set; }
34 |
35 | [JsonRequired]
36 | public Uri Url { get; set; }
37 |
38 | [DefaultValue(null)]
39 | public List Headers { get; set; }
40 |
41 | [DefaultValue(null)]
42 | public string Body { get; set; }
43 | }
44 | }
--------------------------------------------------------------------------------
/ApprovalBot/PrivateSettings.example.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 |
--------------------------------------------------------------------------------
/ApprovalBot/App_Start/WebApiConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Http;
2 |
3 | namespace ApprovalBot
4 | {
5 | public static class WebApiConfig
6 | {
7 | public static void Register(HttpConfiguration config)
8 | {
9 | // Json settings
10 | //config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
11 | //config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
12 | //config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
13 | //JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
14 | //{
15 | // ContractResolver = new CamelCasePropertyNamesContractResolver(),
16 | // Formatting = Newtonsoft.Json.Formatting.Indented,
17 | // NullValueHandling = NullValueHandling.Ignore,
18 | //};
19 |
20 | // Web API configuration and services
21 |
22 | // Web API routes
23 | config.MapHttpAttributeRoutes();
24 |
25 | config.Routes.MapHttpRoute(
26 | name: "DefaultApi",
27 | routeTemplate: "api/{controller}/{id}",
28 | defaults: new { id = RouteParameter.Optional }
29 | );
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ApprovalBot/Web.Debug.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
29 |
30 |
--------------------------------------------------------------------------------
/ApprovalBot/Web.Release.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
30 |
31 |
--------------------------------------------------------------------------------
/ApprovalBot/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("ApprovalBot")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ApprovalBot")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("4e426b56-d589-44ea-b7a1-72ed5343b354")]
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 |
--------------------------------------------------------------------------------
/ApprovalBot/Global.asax.cs:
--------------------------------------------------------------------------------
1 | using ApprovalBot.Helpers;
2 | using Autofac;
3 | using Microsoft.Bot.Builder.Azure;
4 | using Microsoft.Bot.Builder.Dialogs;
5 | using Microsoft.Bot.Builder.Dialogs.Internals;
6 | using Microsoft.Bot.Connector;
7 | using System;
8 | using System.Configuration;
9 | using System.Reflection;
10 | using System.Web.Http;
11 |
12 | namespace ApprovalBot
13 | {
14 | public class WebApiApplication : System.Web.HttpApplication
15 | {
16 | protected void Application_Start()
17 | {
18 | GlobalConfiguration.Configure(WebApiConfig.Register);
19 |
20 | Conversation.UpdateContainer(
21 | builder =>
22 | {
23 | builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
24 |
25 | // This will create a CosmosDB store, suitable for production
26 | // NOTE: Requires an actual CosmosDB instance and configuration in
27 | // PrivateSettings.config
28 | var databaseUri = new Uri(ConfigurationManager.AppSettings["DatabaseUri"]);
29 | var databaseKey = ConfigurationManager.AppSettings["DatabaseKey"];
30 | var store = new DocumentDbBotDataStore(databaseUri, databaseKey);
31 |
32 | builder.Register(c => store)
33 | .Keyed>(AzureModule.Key_DataStore)
34 | .AsSelf()
35 | .SingleInstance();
36 | });
37 |
38 | // Initialize approvals database
39 | DatabaseHelper.Initialize();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ApprovalBot/Dialogs/ExceptionHandlerDialog.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Bot.Builder.Dialogs;
2 | using System;
3 | using System.Configuration;
4 | using System.Threading.Tasks;
5 |
6 | namespace ApprovalBot.Dialogs
7 | {
8 | [Serializable]
9 | public class ExceptionHandlerDialog : IDialog