├── .dockerignore ├── .gitignore ├── _files ├── icon1_00000.jpg └── icon_00000.jpg ├── AliceHook ├── Models │ ├── Abstract │ │ ├── ICloneable.cs │ │ ├── AliceResponseBase.cs │ │ └── AliceRequestBase.cs │ ├── AliceRequest.cs │ ├── Webhook.cs │ ├── AliceResponse.cs │ ├── UserData.cs │ ├── UserState.cs │ ├── SessionState.cs │ ├── Example.cs │ └── Phrase.cs ├── AliceHook.csproj ├── Program.cs ├── Engine │ ├── Modifiers │ │ ├── ModifierCancel.cs │ │ ├── Abstract │ │ │ ├── ModifierBase.cs │ │ │ └── ModifierBaseKeywords.cs │ │ ├── ModifierExit.cs │ │ ├── ModifierAddPhrase.cs │ │ ├── ModifierEnter.cs │ │ ├── ModifierUnknown.cs │ │ ├── ModifierExampleList.cs │ │ ├── ModifierAddWebhook.cs │ │ ├── ModifierDelete.cs │ │ ├── ModifierFinalWebhook.cs │ │ ├── ModifierTestRequest.cs │ │ ├── ModifierWebhookResponse.cs │ │ ├── ModifierExample.cs │ │ ├── ModifierList.cs │ │ ├── ModifierHelp.cs │ │ └── ModifierRunWebhook.cs │ └── AliceService.cs ├── Startup.cs ├── Controllers │ └── AliceController.cs └── Utils.cs ├── README.md ├── Dockerfile ├── AliceHook.sln.DotSettings └── AliceHook.sln /.dockerignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | /.idea/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | /.idea/ 5 | -------------------------------------------------------------------------------- /_files/icon1_00000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DenisNP/AliceHook/HEAD/_files/icon1_00000.jpg -------------------------------------------------------------------------------- /_files/icon_00000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DenisNP/AliceHook/HEAD/_files/icon_00000.jpg -------------------------------------------------------------------------------- /AliceHook/Models/Abstract/ICloneable.cs: -------------------------------------------------------------------------------- 1 | namespace AliceHook.Models.Abstract 2 | { 3 | public interface ICloneable 4 | { 5 | T Clone(); 6 | } 7 | } -------------------------------------------------------------------------------- /AliceHook/Models/AliceRequest.cs: -------------------------------------------------------------------------------- 1 | using AliceHook.Models.Abstract; 2 | 3 | namespace AliceHook.Models 4 | { 5 | public class AliceRequest : AliceRequestBase { } 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AliceHook 2 | 3 | [Навык](https://dialogs.yandex.ru/store/skills/85384c00-moj-ispolnitel) для интеграции Алисы с IFTTT, Zapier и Integromat. 4 | 5 | Первая версия делалась на [обучающем стриме](https://www.youtube.com/watch?v=vBg60I3P9Wg). Но текущая уже многим отличается. 6 | -------------------------------------------------------------------------------- /AliceHook/Models/Webhook.cs: -------------------------------------------------------------------------------- 1 | using Google.Cloud.Firestore; 2 | 3 | namespace AliceHook.Models 4 | { 5 | [FirestoreData] 6 | public class Webhook 7 | { 8 | [FirestoreProperty] 9 | public int Id { get; set; } 10 | [FirestoreProperty] 11 | public string Phrase { get; set; } 12 | [FirestoreProperty] 13 | public string Url { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy, restore and build 5 | COPY . . 6 | RUN dotnet restore 7 | RUN dotnet publish -c Release -o out 8 | 9 | # Build runtime image 10 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 11 | WORKDIR /app 12 | COPY --from=build-env /app/out . 13 | EXPOSE 80 14 | 15 | # Main run command 16 | ENTRYPOINT ["dotnet", "AliceHook.dll"] -------------------------------------------------------------------------------- /AliceHook/Models/AliceResponse.cs: -------------------------------------------------------------------------------- 1 | using AliceHook.Models.Abstract; 2 | 3 | namespace AliceHook.Models 4 | { 5 | public class AliceResponse : AliceResponseBase 6 | { 7 | public AliceResponse( 8 | AliceRequest request, 9 | SessionState sessionState = default, 10 | UserState userState = default 11 | ) : base(request, sessionState, userState) { } 12 | } 13 | } -------------------------------------------------------------------------------- /AliceHook/Models/UserData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Google.Cloud.Firestore; 3 | 4 | namespace AliceHook.Models 5 | { 6 | [FirestoreData] 7 | public class UserData 8 | { 9 | [FirestoreProperty] 10 | public string Id { get; set; } 11 | [FirestoreProperty] 12 | public string Token { get; set; } 13 | [FirestoreProperty] 14 | public List Webhooks { get; set; } = new List(); 15 | } 16 | } -------------------------------------------------------------------------------- /AliceHook.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True 3 | True -------------------------------------------------------------------------------- /AliceHook/AliceHook.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | <_ContentIncludedByDefault Remove="Properties\launchSettings.json" /> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /AliceHook/Models/UserState.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace AliceHook.Models 5 | { 6 | public class UserState 7 | { 8 | public List Webhooks { get; } = new List(); 9 | public Webhook FindWebhook(string phrase) 10 | { 11 | return Webhooks.FirstOrDefault(w => 12 | { 13 | string shorten = w.Phrase.Replace(" ", ""); 14 | string startPhrase = phrase.SafeSubstring(shorten.Length); 15 | return Utils.LevenshteinRatio(startPhrase, shorten) < Utils.PossibleRatio(shorten.Length); 16 | }); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /AliceHook/Models/SessionState.cs: -------------------------------------------------------------------------------- 1 | namespace AliceHook.Models 2 | { 3 | public class SessionState 4 | { 5 | public Step Step { get; set; } = Step.None; 6 | public string TempUrl { get; set; } = ""; 7 | public string LastResult { get; set; } = ""; 8 | public string LastError { get; set; } = ""; 9 | 10 | public void Clear() 11 | { 12 | Step = Step.None; 13 | TempUrl = ""; 14 | LastError = ""; 15 | LastResult = ""; 16 | } 17 | } 18 | 19 | public enum Step 20 | { 21 | None, 22 | AwaitForUrl, 23 | AwaitForKeyword, 24 | AwaitWebhookResponse 25 | } 26 | } -------------------------------------------------------------------------------- /AliceHook/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace AliceHook 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | StartServer(); 11 | } 12 | 13 | private static void StartServer() 14 | { 15 | new WebHostBuilder() 16 | .UseKestrel() 17 | #if DEBUG 18 | .ConfigureLogging(logging => 19 | { 20 | logging.AddDebug(); 21 | logging.AddConsole(); 22 | }) 23 | #endif 24 | .UseStartup() 25 | .Build() 26 | .Run(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /AliceHook.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AliceHook", "AliceHook\AliceHook.csproj", "{FB49A21C-0EEB-43AB-AF4F-45C3CE3BF6CD}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {FB49A21C-0EEB-43AB-AF4F-45C3CE3BF6CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {FB49A21C-0EEB-43AB-AF4F-45C3CE3BF6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {FB49A21C-0EEB-43AB-AF4F-45C3CE3BF6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {FB49A21C-0EEB-43AB-AF4F-45C3CE3BF6CD}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierCancel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AliceHook.Engine.Modifiers.Abstract; 3 | using AliceHook.Models; 4 | 5 | namespace AliceHook.Engine.Modifiers 6 | { 7 | public class ModifierCancel : ModifierBaseKeywords 8 | { 9 | protected override List Keywords { get; } = new List 10 | { 11 | "отмен" 12 | }; 13 | 14 | protected override bool CheckState(SessionState state) 15 | { 16 | return state.Step != Step.None; 17 | } 18 | 19 | protected override Phrase Respond(AliceRequest request) 20 | { 21 | request.State.Session.Clear(); 22 | return new Phrase( 23 | "Отменено. Что теперь?", 24 | new []{ "Добавить вебхук", "Примеры", "Список", "Выход" } 25 | ); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/Abstract/ModifierBase.cs: -------------------------------------------------------------------------------- 1 | using AliceHook.Models; 2 | 3 | namespace AliceHook.Engine.Modifiers.Abstract 4 | { 5 | public abstract class ModifierBase 6 | { 7 | public bool Run(AliceRequest request, out AliceResponse response) 8 | { 9 | if (!Check(request)) 10 | { 11 | response = null; 12 | return false; 13 | } 14 | 15 | response = CreateResponse(request); 16 | return true; 17 | } 18 | 19 | protected virtual AliceResponse CreateResponse(AliceRequest request) 20 | { 21 | Phrase phrase = Respond(request); 22 | return phrase.Generate(request); 23 | } 24 | 25 | protected abstract bool Check(AliceRequest request); 26 | protected abstract Phrase Respond(AliceRequest request); 27 | } 28 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierExit.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AliceHook.Engine.Modifiers.Abstract; 3 | using AliceHook.Models; 4 | 5 | namespace AliceHook.Engine.Modifiers 6 | { 7 | public class ModifierExit : ModifierBaseKeywords 8 | { 9 | protected override List Keywords { get; } = new List 10 | { 11 | "выход", 12 | "выйти", 13 | "пока" 14 | }; 15 | 16 | protected override bool CheckState(SessionState state) 17 | { 18 | return true; // any state 19 | } 20 | 21 | protected override AliceResponse CreateResponse(AliceRequest request) 22 | { 23 | AliceResponse resp = base.CreateResponse(request); 24 | resp.Response.EndSession = true; 25 | return resp; 26 | } 27 | 28 | protected override Phrase Respond(AliceRequest request) 29 | { 30 | return new Phrase("Выхож[+]у. Хорошего дня."); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /AliceHook/Startup.cs: -------------------------------------------------------------------------------- 1 | using AliceHook.Engine; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Server.Kestrel.Core; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace AliceHook 9 | { 10 | public class Startup 11 | { 12 | public Startup(IConfiguration configuration) 13 | { 14 | Configuration = configuration; 15 | } 16 | 17 | public IConfiguration Configuration { get; } 18 | 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddControllers(); 22 | services.AddSingleton(); 23 | services.Configure(options => { options.AllowSynchronousIO = true; }); 24 | } 25 | 26 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 27 | { 28 | app.UseRouting(); 29 | app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierAddPhrase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using AliceHook.Engine.Modifiers.Abstract; 3 | using AliceHook.Models; 4 | 5 | namespace AliceHook.Engine.Modifiers 6 | { 7 | public class ModifierAddPhrase : ModifierBase 8 | { 9 | public static readonly Regex UrlRegex 10 | = new Regex(@"^(?:http(s)?:\/\/)[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"); 11 | 12 | protected override bool Check(AliceRequest request) 13 | { 14 | return request.State.Session.Step == Step.AwaitForUrl && UrlRegex.IsMatch(request.Request.OriginalUtterance); 15 | } 16 | 17 | protected override Phrase Respond(AliceRequest request) 18 | { 19 | request.State.Session.Step = Step.AwaitForKeyword; 20 | request.State.Session.TempUrl = request.Request.OriginalUtterance; 21 | 22 | return new Phrase( 23 | "А теперь назовите фразу активации:", 24 | new []{ "Отмена", "Помощь", "Выход"} 25 | ); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/Abstract/ModifierBaseKeywords.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using AliceHook.Models; 4 | 5 | namespace AliceHook.Engine.Modifiers.Abstract 6 | { 7 | public abstract class ModifierBaseKeywords : ModifierBase 8 | { 9 | protected abstract List Keywords { get; } 10 | 11 | protected abstract bool CheckState(SessionState state); 12 | 13 | protected override bool Check(AliceRequest request) 14 | { 15 | return CheckState(request.State.Session) && CheckTokens(request); 16 | } 17 | 18 | protected bool CheckTokens(AliceRequest request) 19 | { 20 | return CheckTokens(request.Request.Nlu.Tokens, Keywords.ToArray()); 21 | } 22 | 23 | private bool CheckTokens(IEnumerable tokens, params string[] expected) 24 | { 25 | return expected.Any(expectedString => 26 | { 27 | string[] expectedTokens = expectedString.Split(" "); 28 | return expectedTokens.All(tokens.ContainsStartWith); 29 | }); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierEnter.cs: -------------------------------------------------------------------------------- 1 | using AliceHook.Engine.Modifiers.Abstract; 2 | using AliceHook.Models; 3 | 4 | namespace AliceHook.Engine.Modifiers 5 | { 6 | public class ModifierEnter : ModifierBase 7 | { 8 | protected override bool Check(AliceRequest request) 9 | { 10 | return request.Request.Command == ""; 11 | } 12 | 13 | protected override Phrase Respond(AliceRequest request) 14 | { 15 | if (request.State.User.Webhooks.Count > 0) 16 | { 17 | return new Phrase( 18 | "Слушаю", 19 | new []{ "Добавить вебхук", "Список", "Примеры", "Выход" } 20 | ); 21 | } 22 | 23 | return ModifierHelp.GetHelp(Step.None, request.HasScreen()); 24 | } 25 | 26 | protected override AliceResponse CreateResponse(AliceRequest request) 27 | { 28 | AliceResponse response = base.CreateResponse(request); 29 | if (request.State.User.Webhooks.Count == 0) 30 | { 31 | ModifierHelp.AddExamplesTo(response); 32 | } 33 | 34 | return response; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierUnknown.cs: -------------------------------------------------------------------------------- 1 | using AliceHook.Engine.Modifiers.Abstract; 2 | using AliceHook.Models; 3 | 4 | namespace AliceHook.Engine.Modifiers 5 | { 6 | public class ModifierUnknown : ModifierBase 7 | { 8 | protected override bool Check(AliceRequest request) 9 | { 10 | return true; 11 | } 12 | 13 | protected override Phrase Respond(AliceRequest request) 14 | { 15 | if (request.State.Session.Step == Step.None) 16 | { 17 | if (request.HasScreen()) 18 | { 19 | return new Phrase( 20 | "Команда не распознана. Вы можете добавить в[screen|е][voice|э]бх[+]ук, " + 21 | "или посмотреть список ключевых фраз. Что хотите сделать?", 22 | new []{ "Добавить вебхук", "Список", "Помощь", "Выход" } 23 | ); 24 | } 25 | 26 | // no screen 27 | return new Phrase( 28 | "Команда не распознана. Вы можете прослушать список ключевых фраз либо выйти. " + 29 | "Что хотите сделать?" 30 | ); 31 | } 32 | return ModifierHelp.GetHelp(request.State.Session.Step, request.HasScreen()); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierExampleList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AliceHook.Engine.Modifiers.Abstract; 3 | using AliceHook.Models; 4 | 5 | namespace AliceHook.Engine.Modifiers 6 | { 7 | public class ModifierExampleList : ModifierBaseKeywords 8 | { 9 | protected override List Keywords { get; } = new List 10 | { 11 | "пример", 12 | "например" 13 | }; 14 | 15 | protected override Phrase Respond(AliceRequest request) 16 | { 17 | return new Phrase( 18 | "Вот список некоторых пользовательских сценариев, которые возможны с помощью этого навыка.", 19 | new []{ "Добавить вебхук", "Список вебхуков", "Выход" } 20 | ); 21 | } 22 | 23 | protected override AliceResponse CreateResponse(AliceRequest request) 24 | { 25 | AliceResponse response = base.CreateResponse(request); 26 | ModifierHelp.AddExamplesTo(response); 27 | 28 | return response; 29 | } 30 | 31 | protected override bool CheckState(SessionState state) 32 | { 33 | return state.Step == Step.None; 34 | } 35 | 36 | protected override bool Check(AliceRequest request) 37 | { 38 | return request.HasScreen() && base.Check(request); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierAddWebhook.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AliceHook.Engine.Modifiers.Abstract; 3 | using AliceHook.Models; 4 | 5 | namespace AliceHook.Engine.Modifiers 6 | { 7 | public class ModifierAddWebhook : ModifierBaseKeywords 8 | { 9 | protected override List Keywords { get; } = new List 10 | { 11 | "добав вебхук", 12 | "новый вебхук", 13 | "созда вебхук", 14 | "добав webhook", 15 | "новый webhook", 16 | "созда webhook", 17 | "добав веб хук", 18 | "новый веб хук", 19 | "созда веб хук", 20 | }; 21 | 22 | protected override bool CheckState(SessionState state) 23 | { 24 | return state.Step == Step.None; 25 | } 26 | 27 | protected override Phrase Respond(AliceRequest request) 28 | { 29 | if (!request.HasScreen()) 30 | { 31 | return new Phrase("Добавлять в[screen|е][voice|э]бх[+]уки можно только на устройстве с экраном"); 32 | } 33 | 34 | if (request.State.User.Webhooks.Count >= 20) 35 | { 36 | return new Phrase( 37 | "У вас очень много в[screen|е][voice|э]бх[+]уков, удалите что-нибудь сначала с помощью команды \"Список\"", 38 | new []{ "Список", "Помощь", "Выход" } 39 | ); 40 | } 41 | 42 | request.State.Session.Step = Step.AwaitForUrl; 43 | return new Phrase( 44 | "Введите URL в[screen|е][voice|э]бх[+]ука:", 45 | new []{ "Отмена", "Помощь", "Выход" } 46 | ); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierDelete.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using AliceHook.Engine.Modifiers.Abstract; 4 | using AliceHook.Models; 5 | 6 | namespace AliceHook.Engine.Modifiers 7 | { 8 | public class ModifierDelete : ModifierBase 9 | { 10 | protected override bool Check(AliceRequest request) 11 | { 12 | return request.State.Session.Step == Step.None 13 | && request.Request.Command.ToLower().StartsWith("удали") 14 | && request.Request.Nlu.Tokens.Count > 1 15 | && GetWebhook(request) != null; 16 | } 17 | 18 | protected override Phrase Respond(AliceRequest request) 19 | { 20 | Webhook w = GetWebhook(request); 21 | 22 | if (w == null) 23 | { 24 | return new Phrase( 25 | "Не могу найти в[screen|е][voice|э]бх[+]ук по вашему запросу. Что хотите сделать сейчас?", 26 | new []{ "Добавить вебхук", "Примеры", "Список", "Выход" } 27 | ); 28 | } 29 | 30 | request.State.User.Webhooks.Remove(w); 31 | 32 | return new Phrase( 33 | $"Удален в[screen|е][voice|э]бх[+]ук: {w.Phrase.CapitalizeFirst()}. Что теперь?", 34 | new []{ "Добавить вебхук", "Список", "Примеры", "Выход" } 35 | ); 36 | } 37 | 38 | private Webhook GetWebhook(AliceRequest request) 39 | { 40 | string[] tokens = Regex.Split(request.Request.Command.ToLower(), "\\s+"); 41 | if (tokens.Length <= 1) 42 | { 43 | return null; 44 | } 45 | string trimmedCommand = tokens.Skip(1).Join("").Trim(); 46 | return request.State.User.FindWebhook(trimmedCommand); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierFinalWebhook.cs: -------------------------------------------------------------------------------- 1 | using AliceHook.Engine.Modifiers.Abstract; 2 | using AliceHook.Models; 3 | 4 | namespace AliceHook.Engine.Modifiers 5 | { 6 | public class ModifierFinalWebhook : ModifierBase 7 | { 8 | protected override bool Check(AliceRequest request) 9 | { 10 | return request.State.Session.Step == Step.AwaitForKeyword && request.HasScreen(); 11 | } 12 | 13 | protected override Phrase Respond(AliceRequest request) 14 | { 15 | Webhook exists = request.State.User.FindWebhook( 16 | request.Request.Command 17 | .ToLower() 18 | .Trim() 19 | .Replace(" ", "") 20 | ); 21 | 22 | if (exists != null) 23 | { 24 | return new Phrase( 25 | $"У вас уже есть вебхук с похожей ключевой фразой: {exists.Phrase.CapitalizeFirst()}. " + 26 | "Назовите другую фразу.", 27 | new []{ "Отмена", "Помощь", "Выход" } 28 | ); 29 | } 30 | 31 | var webhook = new Webhook 32 | { 33 | Phrase = request.Request.Command, 34 | Url = request.State.Session.TempUrl 35 | }; 36 | 37 | request.State.User.Webhooks.Add(webhook); 38 | request.State.Session.Clear(); 39 | 40 | return new Phrase( 41 | $"Теперь, когда вы скажете фразу, которая начинается на \"{webhook.Phrase.CapitalizeFirst()}" + 42 | "\", я вызову этот адрес и передам туда весь ваш текст в параметр [screen|value1][voice|вэлью 1]" + 43 | ".[p|1000]\n\nЧто делаем дальше?", 44 | new []{ "Добавить вебхук", "Список", "Примеры", "Выход" } 45 | ); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierTestRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using System.Net.Http; 4 | using AliceHook.Engine.Modifiers.Abstract; 5 | using AliceHook.Models; 6 | 7 | namespace AliceHook.Engine.Modifiers 8 | { 9 | public class ModifierTestRequest : ModifierBase 10 | { 11 | protected override bool Check(AliceRequest request) 12 | { 13 | if (request.State.Session.Step != Step.None || !request.Request.Command.StartsWith("http")) 14 | { 15 | return false; 16 | } 17 | 18 | return ModifierAddPhrase.UrlRegex.IsMatch(request.Request.OriginalUtterance); 19 | } 20 | 21 | protected override Phrase Respond(AliceRequest request) 22 | { 23 | string url = request.Request.OriginalUtterance; 24 | 25 | // test post 26 | using var handler = new HttpClientHandler 27 | { 28 | ServerCertificateCustomValidationCallback 29 | = (message, certificate2, arg3, arg4) => true 30 | }; 31 | using var client = new HttpClient(handler); 32 | var data = new FormUrlEncodedContent(new Dictionary 33 | { 34 | {"value1", "тестовый запрос — сокращённая фраза"}, 35 | {"value2", "тестовый запрос — полная фраза (ключевая + сокращённая)"}, // full command 36 | {"value3", "тестовый запрос — ключевая фраза"} 37 | }); 38 | 39 | HttpResponseMessage response = client.PostAsync(url, data).Result; 40 | 41 | // response 42 | return new Phrase( 43 | response.StatusCode == HttpStatusCode.OK 44 | ? "Отправила тестовый запрос на этот адрес. Что теперь?" 45 | : "Какая-то ошибка в процессе вызова адреса. Статус ответа: " + response.StatusCode + ". Что дальше?", 46 | new []{ "Добавить вебхук", "Список", "Примеры", "Выход" } 47 | ); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierWebhookResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using AliceHook.Engine.Modifiers.Abstract; 3 | using AliceHook.Models; 4 | 5 | namespace AliceHook.Engine.Modifiers 6 | { 7 | public class ModifierWebhookResponse : ModifierBase 8 | { 9 | private readonly string[] Yes = { 10 | "да", 11 | "ага", 12 | "давай", 13 | "узнай", 14 | "ок", 15 | "хорошо", 16 | "угу" 17 | }; 18 | 19 | protected override bool Check(AliceRequest request) 20 | { 21 | return request.State.Session.Step == Step.AwaitWebhookResponse; 22 | } 23 | 24 | protected override Phrase Respond(AliceRequest request) 25 | { 26 | request.State.Session.Step = Step.None; 27 | 28 | string command = request.Request.Command; 29 | if (Yes.Any(x => command.StartsWith(x))) 30 | { 31 | // yes 32 | if (!request.State.Session.LastResult.IsNullOrEmpty()) 33 | { 34 | return new Phrase( 35 | "Вебхук ответил:\n" + request.State.Session.LastResult, 36 | new []{ "Список", "Помощь", "Выход" } 37 | ); 38 | } 39 | 40 | if (!request.State.Session.LastError.IsNullOrEmpty()) 41 | { 42 | return new Phrase( 43 | "К сожалению, с вызовом произошла ошибка. Что теперь?", 44 | new []{ "Список", "Помощь", "Выход" } 45 | ); 46 | } 47 | 48 | return new Phrase( 49 | "К сожалению, получить ответ от вебхука не удалось. Что теперь?", 50 | new[] {"Список", "Помощь", "Выход"} 51 | ); 52 | } 53 | 54 | return new Phrase( 55 | "Ожидание ответа от вебхука прекращено. Что дальше?", 56 | new []{ "Добавить вебхук", "Список", "Примеры", "Выход" } 57 | ); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /AliceHook/Engine/AliceService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using AliceHook.Engine.Modifiers; 5 | using AliceHook.Engine.Modifiers.Abstract; 6 | using AliceHook.Models; 7 | using AliceHook.Models.Abstract; 8 | 9 | namespace AliceHook.Engine 10 | { 11 | public class AliceService 12 | { 13 | private static readonly List Modifiers = new List 14 | { 15 | new ModifierEnter(), 16 | new ModifierTestRequest(), 17 | new ModifierExample(), 18 | new ModifierExampleList(), 19 | new ModifierWebhookResponse(), 20 | new ModifierHelp(), 21 | new ModifierExit(), 22 | new ModifierList(), 23 | new ModifierCancel(), 24 | new ModifierDelete(), 25 | new ModifierAddWebhook(), 26 | new ModifierAddPhrase(), 27 | new ModifierFinalWebhook(), 28 | new ModifierRunWebhook(), 29 | new ModifierUnknown() 30 | }; 31 | 32 | public AliceResponse HandleRequest(AliceRequest aliceRequest) 33 | { 34 | // respond 35 | AliceResponse response = null; 36 | try 37 | { 38 | if (!Modifiers.Any(modifier => modifier.Run(aliceRequest, out response))) { 39 | throw new NotSupportedException("No default modifier"); 40 | } 41 | } 42 | catch (Exception e) 43 | { 44 | Console.WriteLine("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ERROR"); 45 | Console.WriteLine(e); 46 | response = new AliceResponse(aliceRequest) 47 | { 48 | Response = new Response 49 | { 50 | Text = "Произошла какая-то ошибка на сервере навыка, разработчик уже уведомлён. " + 51 | "Приносим извинения." 52 | } 53 | }; 54 | Console.WriteLine(""); 55 | } 56 | 57 | return response; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /AliceHook/Engine/Modifiers/ModifierExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using AliceHook.Engine.Modifiers.Abstract; 5 | using AliceHook.Models; 6 | using AliceHook.Models.Abstract; 7 | 8 | namespace AliceHook.Engine.Modifiers 9 | { 10 | public class ModifierExample : ModifierBase 11 | { 12 | protected override bool Check(AliceRequest request) 13 | { 14 | if (request.State.Session.Step != Step.None) 15 | { 16 | return false; 17 | } 18 | 19 | Example example = FindExample(request.Request.Command); 20 | return example != null; 21 | } 22 | 23 | protected override Phrase Respond(AliceRequest request) 24 | { 25 | throw new NotSupportedException("Method should not be invoked"); 26 | } 27 | 28 | protected override AliceResponse CreateResponse(AliceRequest request) 29 | { 30 | var response = new AliceResponse(request); 31 | Example example = FindExample(request.Request.Command); 32 | request.State.Session.Step = Step.None; 33 | 34 | response.Response.Text = example.Description + " По кнопке подробная видеоинструкция о том, как это сделать."; 35 | response.Response.Tts = example.DescriptionTts + " - По кнопке подробная видеоинструкция о том, как это сделать."; 36 | response.Response.Buttons = new List