├── docs ├── QuickStart.md └── uml │ └── ComponentDiagram.html ├── src ├── UI │ ├── DCLogo.ico │ ├── appsettings.json │ ├── Views │ │ ├── AboutView.xaml.cs │ │ ├── PluginView.xaml.cs │ │ ├── TerminalView.xaml.cs │ │ ├── AppSettingsView.xaml.cs │ │ ├── TwitchBotView.xaml.cs │ │ ├── TwitchStreamerView.xaml.cs │ │ ├── AboutView.xaml │ │ ├── AppSettingsView.xaml │ │ ├── TwitchStreamerView.xaml │ │ ├── TwitchBotView.xaml │ │ ├── TerminalView.xaml │ │ └── PluginView.xaml │ ├── ViewModels │ │ ├── AboutViewModel.cs │ │ ├── SettingsViewModel.cs │ │ ├── AccountsViewModel.cs │ │ ├── TwitchBotViewModel.cs │ │ ├── TwitchStreamerViewModel.cs │ │ ├── MenuItemViewModel.cs │ │ ├── PluginViewModel.cs │ │ ├── TerminalViewModel.cs │ │ ├── MainViewModel.cs │ │ └── TwitchAccountViewModel.cs │ ├── AssemblyInfo.cs │ ├── MainWindow.xaml.cs │ ├── Web │ │ ├── Controllers │ │ │ └── TwitchAuthController.cs │ │ └── Startup.cs │ ├── Converters │ │ └── SelectedItemToContentConverter.cs │ ├── AccountsWindow.xaml.cs │ ├── wwwroot │ │ └── index.html │ ├── DependencyInjectionExtensions.cs │ ├── ChatterBot.UI.csproj │ ├── AccountsWindow.xaml │ ├── App.xaml │ ├── App.xaml.cs │ └── MainWindow.xaml ├── Core │ ├── ChatterBot │ │ ├── BaseViewModel.cs │ │ ├── Interfaces │ │ │ ├── IPlugin.cs │ │ │ ├── IMessageHandler.cs │ │ │ └── IMenuItemViewModel.cs │ │ ├── PluginRuntime.cs │ │ ├── Auth │ │ │ ├── AuthenticationType.cs │ │ │ ├── IDataProtection.cs │ │ │ ├── ITwitchConnection.cs │ │ │ ├── ITwitchAuthentication.cs │ │ │ ├── TwitchCredentials.cs │ │ │ └── AccessTokenReceived.cs │ │ ├── Access.cs │ │ ├── Data │ │ │ └── IDataStore.cs │ │ ├── Config │ │ │ ├── ApplicationSettings.cs │ │ │ └── SettingsBuilder.cs │ │ ├── Extensions │ │ │ └── EncodingExtensions.cs │ │ ├── Plugin.cs │ │ ├── ChatterBot.csproj │ │ ├── State │ │ │ └── IMainMenuItemsSet.cs │ │ ├── ChatMessage.cs │ │ └── BaseBindable.cs │ └── ChatterBot.Domain │ │ ├── DependencyInjectionExtensions.cs │ │ ├── ChatterBot.Domain.csproj │ │ ├── State │ │ └── MainMenuItemsSet.cs │ │ └── Auth │ │ ├── DataProtection.cs │ │ ├── AccessTokenRecorder.cs │ │ └── TwitchAuthentication.cs ├── Plugins │ └── SimpleCommands │ │ ├── Validation │ │ ├── ICustomCommandValidator.cs │ │ └── CustomCommandValidator.cs │ │ ├── CommandsView.xaml.cs │ │ ├── ICommandsSet.cs │ │ ├── SimpleCommandsMessageHandler.cs │ │ ├── DependencyInjectionExtensions.cs │ │ ├── SimpleCommands.csproj │ │ ├── CommandsSet.cs │ │ ├── CommandsViewModel.cs │ │ ├── SimpleCommandsPlugin.cs │ │ ├── CustomCommand.cs │ │ └── CommandsView.xaml ├── Infra │ ├── Infra.Twitch │ │ ├── DependencyInjectionExtensions.cs │ │ ├── Extensions │ │ │ ├── DomainMappers.cs │ │ │ └── CredentialsExtensions.cs │ │ ├── ChatterBot.Infra.Twitch.csproj │ │ ├── TwitchBot.cs │ │ └── TwitchConnection.cs │ ├── Infra │ │ ├── DependencyInjectionExtensions.cs │ │ └── ChatterBot.Infra.csproj │ └── Infra.LiteDb │ │ ├── ChatterBot.Infra.LiteDb.csproj │ │ ├── DependencyInjectionExtensions.cs │ │ └── DataStore.cs ├── ChatterBot.code-workspace ├── Tests │ ├── ChatterBot.Tests.csproj │ ├── Domain │ │ └── Validation │ │ │ └── CustomCommandValidator_Should.cs │ ├── DependencyInjectionExtensions_UnitTest.cs │ └── CustomCommandTests │ │ └── GetCommandsToRun_Should.cs └── ChatterBot.sln ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── PullRequest.yml ├── LICENSE ├── README.md └── .gitignore /docs/QuickStart.md: -------------------------------------------------------------------------------- 1 | # Quick Start 2 | -------------------------------------------------------------------------------- /src/UI/DCLogo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevChatter/ChatterBot/HEAD/src/UI/DCLogo.ico -------------------------------------------------------------------------------- /src/UI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Entropy": "SaltyMcSaltFace", 3 | "LightDbConnection": "Filename=Main.db" 4 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/BaseViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot 2 | { 3 | public abstract class BaseViewModel : BaseBindable 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Interfaces/IPlugin.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.Interfaces 2 | { 3 | public interface IPlugin 4 | { 5 | void Initialize(); 6 | } 7 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/PluginRuntime.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot 2 | { 3 | public enum PluginRuntime 4 | { 5 | ChatterPlugin, 6 | PythonScript, 7 | } 8 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Auth/AuthenticationType.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.Auth 2 | { 3 | public enum AuthenticationType 4 | { 5 | Unknown = 0, 6 | TwitchBot = 1, 7 | TwitchStreamer = 2, 8 | } 9 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Auth/IDataProtection.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.Auth 2 | { 3 | public interface IDataProtection 4 | { 5 | byte[] Protect(byte[] data); 6 | byte[] Unprotect(byte[] data); 7 | } 8 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Auth/ITwitchConnection.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.Auth 2 | { 3 | public interface ITwitchConnection 4 | { 5 | void Connect(TwitchCredentials credentials); 6 | void Disconnect(); 7 | } 8 | } -------------------------------------------------------------------------------- /src/UI/Views/AboutView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.UI.Views 2 | { 3 | public partial class AboutView 4 | { 5 | public AboutView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/UI/Views/PluginView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.UI.Views 2 | { 3 | public partial class PluginView 4 | { 5 | public PluginView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Core/ChatterBot/Interfaces/IMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ChatterBot.Interfaces 4 | { 5 | public interface IMessageHandler 6 | { 7 | void Handle(ChatMessage chatMessage, Action respond); 8 | } 9 | } -------------------------------------------------------------------------------- /src/UI/Views/TerminalView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.UI.Views 2 | { 3 | public partial class TerminalView 4 | { 5 | public TerminalView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/UI/Views/AppSettingsView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.UI.Views 2 | { 3 | public partial class AppSettingsView 4 | { 5 | public AppSettingsView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/Validation/ICustomCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace ChatterBot.Plugins.SimpleCommands.Validation 4 | { 5 | public interface ICustomCommandValidator : IValidator 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/CommandsView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.Plugins.SimpleCommands 2 | { 3 | public partial class CommandsView 4 | { 5 | public CommandsView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Core/ChatterBot/Access.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ChatterBot 4 | { 5 | [Flags] 6 | public enum Access : uint 7 | { 8 | Everyone = 1, 9 | Followers = 2, 10 | Subscribers = 4, 11 | VIPs = 8, 12 | Moderators = 16, 13 | Streamer = 32, 14 | } 15 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Data/IDataStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ChatterBot.Data 4 | { 5 | public interface IDataStore 6 | { 7 | void EnsureSchema(); 8 | List GetEntities(); 9 | void SaveEntities(IEnumerable entities); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Interfaces/IMenuItemViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot.Interfaces 2 | { 3 | public interface IMenuItemViewModel 4 | { 5 | object? Icon { get; } 6 | object? Label { get; } 7 | object? ToolTip { get; } 8 | bool IsVisible { get; set; } 9 | bool IsOption { get; } 10 | object? Content { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Config/ApplicationSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ChatterBot.Config 4 | { 5 | public class ApplicationSettings 6 | { 7 | public string Entropy { get; set; } = string.Empty; 8 | public byte[] SaltBytes => Encoding.UTF8.GetBytes(Entropy); 9 | public string LightDbConnection { get; set; } = string.Empty; 10 | } 11 | } -------------------------------------------------------------------------------- /src/UI/Views/TwitchBotView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace ChatterBot.UI.Views 4 | { 5 | public partial class TwitchBotView 6 | { 7 | public TwitchBotView() 8 | { 9 | InitializeComponent(); 10 | } 11 | 12 | private void PasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Core/ChatterBot/Extensions/EncodingExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ChatterBot.Extensions 4 | { 5 | public static class EncodingExtensions 6 | { 7 | public static string BytesToString(this byte[] bytes) 8 | => Encoding.UTF8.GetString(bytes); 9 | public static byte[] StringToBytes(this string text) 10 | => Encoding.UTF8.GetBytes(text); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace ChatterBot 2 | { 3 | public class Plugin : BaseBindable 4 | { 5 | public string Name { get; set; } = string.Empty; 6 | public string Version { get; set; } = string.Empty; 7 | public string Location { get; set; } = string.Empty; 8 | public PluginRuntime PluginRuntime { get; set; } 9 | public bool Enabled { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/UI/Views/TwitchStreamerView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace ChatterBot.UI.Views 4 | { 5 | public partial class TwitchStreamerView 6 | { 7 | public TwitchStreamerView() 8 | { 9 | InitializeComponent(); 10 | } 11 | 12 | private void PasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Core/ChatterBot/Auth/ITwitchAuthentication.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ChatterBot.Auth 4 | { 5 | public interface ITwitchAuthentication 6 | { 7 | string GetUrl(AuthenticationType authenticationType); 8 | 9 | Dictionary Credentials { get; set; } 10 | Dictionary States { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/ICommandsSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | 4 | namespace ChatterBot.Plugins.SimpleCommands 5 | { 6 | public interface ICommandsSet 7 | { 8 | BindingList CustomCommands { get; } 9 | IEnumerable GetCommandsToRun(ChatMessage chatMessage); 10 | void Initialize(List commands); 11 | } 12 | } -------------------------------------------------------------------------------- /src/UI/ViewModels/AboutViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.UI.Views; 2 | using MahApps.Metro.IconPacks; 3 | 4 | namespace ChatterBot.UI.ViewModels 5 | { 6 | public class AboutViewModel : MenuItemViewModel 7 | { 8 | public AboutViewModel() 9 | : base(new PackIconMaterial { Kind = PackIconMaterialKind.Help }, 10 | "About", "About the Bot", new AboutView(), true) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Core/ChatterBot/Auth/TwitchCredentials.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ChatterBot.Auth 4 | { 5 | public class TwitchCredentials 6 | { 7 | public AuthenticationType AuthType { get; set; } = AuthenticationType.Unknown; 8 | public byte[] AuthToken { get; set; } = Array.Empty(); 9 | public string Username { get; set; } = string.Empty; 10 | public string Channel { get; set; } = string.Empty; 11 | } 12 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/ChatterBot.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | enable 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Infra/Infra.Twitch/DependencyInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Infra.Twitch; 3 | 4 | namespace Microsoft.Extensions.DependencyInjection 5 | { 6 | public static class DependencyInjectionExtensions 7 | { 8 | public static void AddInfrastructureForTwitch(this IServiceCollection services) 9 | { 10 | services.AddTransient(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/UI/ViewModels/SettingsViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.UI.Views; 2 | using MahApps.Metro.IconPacks; 3 | 4 | namespace ChatterBot.UI.ViewModels 5 | { 6 | public class SettingsViewModel : MenuItemViewModel 7 | { 8 | public SettingsViewModel() 9 | : base(new PackIconMaterial { Kind = PackIconMaterialKind.Cog }, 10 | "Settings", "Application Settings", new AppSettingsView(), true) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Infra/Infra.Twitch/Extensions/DomainMappers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ChatterBot.Infra.Twitch.Extensions 4 | { 5 | public static class DomainMappers 6 | { 7 | // TODO: #28 Replace DomainMappers with AutoMapper profiles. 8 | public static ChatMessage ToDomain(this TwitchLib.Client.Models.ChatMessage message) => 9 | new ChatMessage(DateTime.UtcNow, 10 | message.DisplayName, message.ColorHex, message.Message); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Infra/Infra/DependencyInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Config; 2 | 3 | namespace Microsoft.Extensions.DependencyInjection 4 | { 5 | public static class DependencyInjectionExtensions 6 | { 7 | public static void AddInfrastructure(this IServiceCollection services, ApplicationSettings appSettings) 8 | { 9 | services.AddInfrastructureForLiteDb(appSettings); 10 | services.AddInfrastructureForTwitch(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/State/IMainMenuItemsSet.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | 5 | namespace ChatterBot.State 6 | { 7 | public interface IMainMenuItemsSet 8 | { 9 | BindingList MenuItems { get; } 10 | BindingList MenuOptionItems { get; } 11 | void Initialize(IEnumerable menuItems, IEnumerable menuOptionItems); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Infra/Infra/ChatterBot.Infra.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | enable 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Infra/Infra.LiteDb/ChatterBot.Infra.LiteDb.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | enable 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Core/ChatterBot/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ChatterBot 4 | { 5 | public class ChatMessage 6 | { 7 | public DateTime TimeStamp { get; set; } 8 | public string DisplayName { get; set; } 9 | public string UserColor { get; set; } 10 | public string Text { get; set; } 11 | 12 | public ChatMessage(DateTime timeStamp, string displayName, string userColor, string text) 13 | { 14 | TimeStamp = timeStamp; 15 | DisplayName = displayName; 16 | Text = text; 17 | UserColor = userColor; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/UI/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /src/Infra/Infra.LiteDb/DependencyInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Config; 2 | using ChatterBot.Data; 3 | using ChatterBot.Infra.LiteDb; 4 | 5 | namespace Microsoft.Extensions.DependencyInjection 6 | { 7 | public static class DependencyInjectionExtensions 8 | { 9 | public static void AddInfrastructureForLiteDb(this IServiceCollection services, ApplicationSettings appSettings) 10 | { 11 | services.AddSingleton(provider => 12 | { 13 | var dataStore = new DataStore(appSettings); 14 | dataStore.EnsureSchema(); 15 | return dataStore; 16 | }); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/ChatterBot.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".." 5 | } 6 | ], 7 | "settings": { 8 | "omnisharp.enableEditorConfigSupport": true, 9 | "omnisharp.enableRoslynAnalyzers": true, 10 | "files.exclude": { 11 | "**/bin/": true, 12 | "**/obj/": true, 13 | "**/.cr/": true, 14 | "**/.vs/": true, 15 | "**/.git": true, 16 | }, 17 | }, 18 | "extensions": { 19 | "recommendations": [ 20 | "ms-dotnettools.csharp", 21 | "editorconfig.editorconfig", 22 | "donjayamanne.githistory", 23 | "eamodio.gitlens", 24 | "yzhang.markdown-all-in-one", 25 | "davidanson.vscode-markdownlint", 26 | "shardulm94.trailing-spaces" 27 | ] 28 | } 29 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: suggestion 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/UI/Views/AboutView.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Infra/Infra.Twitch/ChatterBot.Infra.Twitch.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | enable 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Core/ChatterBot.Domain/DependencyInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Config; 3 | using ChatterBot.Domain.Auth; 4 | using ChatterBot.Domain.State; 5 | using ChatterBot.State; 6 | 7 | namespace Microsoft.Extensions.DependencyInjection 8 | { 9 | public static class DependencyInjectionExtensions 10 | { 11 | public static void AddDomain(this IServiceCollection services, ApplicationSettings appSettings) 12 | { 13 | services.AddSingleton(new DataProtection(appSettings)); 14 | services.AddSingleton(); 15 | services.AddSingleton(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/Auth/AccessTokenReceived.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ChatterBot.Auth 4 | { 5 | public class AccessTokenReceived : IRequest 6 | { 7 | public string AccessToken { get; set; } = string.Empty; 8 | public string Scope { get; set; } = string.Empty; 9 | public string State { get; set; } = string.Empty; 10 | public string TokenType { get; set; } = string.Empty; 11 | 12 | public AccessTokenReceived() 13 | { 14 | } 15 | 16 | public AccessTokenReceived(string accessToken, 17 | string scope, string state, string tokenType) 18 | { 19 | AccessToken = accessToken; 20 | Scope = scope; 21 | State = state; 22 | TokenType = tokenType; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Infra/Infra.Twitch/Extensions/CredentialsExtensions.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Extensions; 3 | using TwitchLib.Client.Models; 4 | 5 | namespace ChatterBot.Infra.Twitch.Extensions 6 | { 7 | public static class CredentialsExtensions 8 | { 9 | // TODO: #28 Replace CredentialsExtensions with AutoMapper profiles. 10 | public static ConnectionCredentials ToTwitchLib(this TwitchCredentials credentials, IDataProtection dataProtection) 11 | { 12 | if (credentials.AuthToken == null) 13 | throw new System.ArgumentException("Unable to convert null AuthToken", nameof(credentials)); 14 | 15 | return new ConnectionCredentials(credentials.Username, dataProtection.Unprotect(credentials.AuthToken).BytesToString()); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /.github/workflows/PullRequest.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - dev 7 | - main 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: windows-latest #Can't use anything else as WPF build has a windows dependency - waiting on .NET MAUI 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup .NET Core 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 3.1.301 21 | 22 | - name: Install dependencies 23 | run: dotnet restore ./src/ChatterBot.sln 24 | 25 | - name: Build 26 | run: dotnet build ./src/ChatterBot.sln --configuration Release --no-restore 27 | 28 | - name: Test 29 | run: dotnet test ./src/ChatterBot.sln --configuration Release --no-build --verbosity normal 30 | -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/SimpleCommandsMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ChatterBot.Plugins.SimpleCommands 6 | { 7 | internal class SimpleCommandsMessageHandler : IMessageHandler 8 | { 9 | private readonly ICommandsSet _commandsSet; 10 | 11 | public SimpleCommandsMessageHandler(ICommandsSet commandsSet) 12 | { 13 | _commandsSet = commandsSet; 14 | } 15 | 16 | public void Handle(ChatMessage chatMessage, Action respond) 17 | { 18 | IEnumerable toRun = _commandsSet.GetCommandsToRun(chatMessage); 19 | 20 | foreach (CustomCommand command in toRun) 21 | { 22 | command.Run(respond); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/DependencyInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using ChatterBot.Plugins.SimpleCommands; 3 | using ChatterBot.Plugins.SimpleCommands.Validation; 4 | 5 | namespace Microsoft.Extensions.DependencyInjection 6 | { 7 | public static class DependencyInjectionExtensions 8 | { 9 | public static void AddSimpleCommandsPlugin(this IServiceCollection services) 10 | { 11 | // TODO: Replace this with scanning for IPlugin when loading assemblies 12 | services.AddSingleton(); 13 | services.AddTransient(); 14 | services.AddSingleton(); 15 | services.AddSingleton(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Software Info (please complete the following information):** 27 | - OS: [e.g. Win10] 28 | - Broadcast Software [e.g. OBS, StreamLabs OBS] 29 | - Bot Version [e.g. v1.2.0.0] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /src/UI/ViewModels/AccountsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace ChatterBot.UI.ViewModels 4 | { 5 | public class AccountsViewModel : BaseViewModel 6 | { 7 | private ObservableCollection _menuItems = new ObservableCollection(); 8 | 9 | public AccountsViewModel(TwitchBotViewModel botViewModel, TwitchStreamerViewModel streamerViewModel) 10 | { 11 | MenuItems = new ObservableCollection 12 | { 13 | botViewModel, 14 | streamerViewModel, 15 | }; 16 | } 17 | 18 | public ObservableCollection MenuItems 19 | { 20 | get => _menuItems; 21 | set => SetProperty(ref _menuItems, value); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot.Domain/ChatterBot.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | enable 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/UI/ViewModels/TwitchBotViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using MahApps.Metro.IconPacks; 3 | using TwitchBotView = ChatterBot.UI.Views.TwitchBotView; 4 | 5 | namespace ChatterBot.UI.ViewModels 6 | { 7 | public class TwitchBotViewModel : TwitchAccountViewModel 8 | { 9 | public TwitchBotViewModel(ITwitchAuthentication auth, ITwitchConnection twitchConnection) 10 | : base(auth, twitchConnection, AuthenticationType.TwitchBot, 11 | new PackIconMaterial { Kind = PackIconMaterialKind.Robot }, 12 | "Bot Account", "Twitch Bot Account Settings", new TwitchBotView()) 13 | { 14 | } 15 | 16 | public string Channel 17 | { 18 | get => Credentials.Channel; 19 | set => SetProperty(() => Credentials.Channel, x => Credentials.Channel = x, value); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/UI/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using ChatterBot.UI.ViewModels; 3 | using MahApps.Metro.Controls; 4 | using System.Linq; 5 | 6 | namespace ChatterBot.UI 7 | { 8 | public partial class MainWindow 9 | { 10 | public MainWindow(MainViewModel mainViewModel) 11 | { 12 | DataContext = mainViewModel; 13 | InitializeComponent(); 14 | ChangeView(mainViewModel.MenuItems.First()); 15 | } 16 | 17 | private void HamburgerMenuControl_OnItemClick(object sender, ItemClickEventArgs e) 18 | { 19 | ChangeView((IMenuItemViewModel)e.ClickedItem); 20 | } 21 | 22 | private void ChangeView(IMenuItemViewModel menuItem) 23 | { 24 | HamburgerMenu.Content = menuItem.Content; 25 | HamburgerMenu.IsPaneOpen = false; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Core/ChatterBot.Domain/State/MainMenuItemsSet.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using ChatterBot.State; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | 7 | namespace ChatterBot.Domain.State 8 | { 9 | internal class MainMenuItemsSet : IMainMenuItemsSet 10 | { 11 | public BindingList MenuItems { get; private set; } = new BindingList(); 12 | public BindingList MenuOptionItems { get; private set; } = new BindingList(); 13 | 14 | public void Initialize(IEnumerable menuItems, IEnumerable menuOptionItems) 15 | { 16 | MenuItems = new BindingList(menuItems.ToList()); 17 | MenuOptionItems = new BindingList(menuOptionItems.ToList()); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/UI/Web/Controllers/TwitchAuthController.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using MediatR; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Threading.Tasks; 5 | 6 | namespace ChatterBot.UI.Web.Controllers 7 | { 8 | [Route("api/[controller]")] 9 | [ApiController] 10 | public class TwitchAuthController : ControllerBase 11 | { 12 | private readonly IMediator _mediator; 13 | 14 | public TwitchAuthController(IMediator mediator) 15 | { 16 | _mediator = mediator; 17 | } 18 | 19 | [HttpPost] 20 | public async Task Post(AccessTokenReceived accessTokenReceived) 21 | { 22 | bool result = await _mediator.Send(accessTokenReceived); 23 | if (result) 24 | { 25 | return Ok(); 26 | } 27 | 28 | return Problem("Failed to save auth token."); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/UI/Converters/SelectedItemToContentConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Windows.Data; 5 | 6 | namespace ChatterBot.UI.Converters 7 | { 8 | public class SelectedItemToContentConverter : IMultiValueConverter 9 | { 10 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | // first value is selected menu item, second value is selected option item 13 | if (values != null && values.Length > 1) 14 | { 15 | return values[0] ?? values[1]; 16 | } 17 | return null!; 18 | } 19 | 20 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 21 | { 22 | return targetTypes.Select(t => Binding.DoNothing).ToArray(); 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/SimpleCommands.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | enable 7 | latest 8 | ChatterBot.Plugins.SimpleCommands 9 | ChatterBot.Plugins.SimpleCommands 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Core/ChatterBot/Config/SettingsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace ChatterBot.Config 5 | { 6 | public static class SettingsBuilder 7 | { 8 | private const int SaltLengthLimit = 32; 9 | private static readonly RNGCryptoServiceProvider CryptoService = new RNGCryptoServiceProvider(); 10 | 11 | public static ApplicationSettings CreateDefaultSettings() 12 | { 13 | return new ApplicationSettings 14 | { 15 | Entropy = Encoding.UTF8.GetString(GetSalt(SaltLengthLimit)), 16 | }; 17 | } 18 | 19 | private static byte[] GetSalt(int maximumSaltLength) 20 | { 21 | var salt = new byte[maximumSaltLength]; 22 | using (RNGCryptoServiceProvider random = CryptoService) 23 | { 24 | random.GetNonZeroBytes(salt); 25 | } 26 | 27 | return salt; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/UI/ViewModels/TwitchStreamerViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.UI.Views; 3 | using MahApps.Metro.IconPacks; 4 | 5 | namespace ChatterBot.UI.ViewModels 6 | { 7 | public class TwitchStreamerViewModel : TwitchAccountViewModel 8 | { 9 | public TwitchStreamerViewModel(ITwitchAuthentication auth, ITwitchConnection twitchConnection) 10 | : base(auth, twitchConnection, AuthenticationType.TwitchStreamer, 11 | new PackIconOcticons { Kind = PackIconOcticonsKind.DeviceCameraVideo }, 12 | "Streamer Account", "Twitch Stream Account Settings", new TwitchStreamerView()) 13 | { 14 | } 15 | 16 | public override string Username 17 | { 18 | get => Credentials.Username; 19 | set 20 | { 21 | Credentials.Channel = value; 22 | SetProperty(() => Credentials.Username, x => Credentials.Username = x, value); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/UI/AccountsWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Data; 3 | using ChatterBot.UI.ViewModels; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Linq; 7 | 8 | namespace ChatterBot.UI 9 | { 10 | public partial class AccountsWindow 11 | { 12 | private readonly IDataStore _dataStore; 13 | private readonly AccountsViewModel _accountsViewModel; 14 | 15 | public AccountsWindow(AccountsViewModel accountsViewModel, IDataStore dataStore) 16 | { 17 | _dataStore = dataStore; 18 | _accountsViewModel = accountsViewModel; 19 | DataContext = accountsViewModel; 20 | InitializeComponent(); 21 | } 22 | 23 | private void AccountsWindow_OnClosing(object sender, CancelEventArgs e) 24 | { 25 | IEnumerable credentials = _accountsViewModel.MenuItems.Select(x => x.Credentials); 26 | _dataStore.SaveEntities(credentials); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/UI/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Home Page 6 | 7 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/UI/DependencyInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using ChatterBot.UI; 3 | using ChatterBot.UI.ViewModels; 4 | 5 | namespace Microsoft.Extensions.DependencyInjection 6 | { 7 | public static class DependencyInjectionExtensions 8 | { 9 | public static void AddUI(this IServiceCollection services) 10 | { 11 | services.AddSingleton(); 12 | services.AddSingleton(); 13 | services.AddSingleton(); 14 | services.AddSingleton(); 15 | services.AddSingleton(); 16 | services.AddSingleton(); 17 | services.AddTransient(); 18 | services.AddTransient(); 19 | 20 | services.AddSingleton(); 21 | services.AddSingleton(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/CommandsSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | 6 | namespace ChatterBot.Plugins.SimpleCommands 7 | { 8 | internal class CommandsSet : ICommandsSet 9 | { 10 | public BindingList CustomCommands { get; private set; } = new BindingList(); 11 | 12 | public IEnumerable GetCommandsToRun(ChatMessage chatMessage) => 13 | CustomCommands 14 | .Where(command => 15 | command.Enabled 16 | && TextMatchesCommand(chatMessage, command)); 17 | 18 | private static bool TextMatchesCommand(ChatMessage chatMessage, CustomCommand command) 19 | => chatMessage.Text.StartsWith(command.CommandWord, StringComparison.InvariantCultureIgnoreCase); 20 | 21 | public void Initialize(List commands) 22 | { 23 | CustomCommands = new BindingList(commands); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/CommandsViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using MahApps.Metro.IconPacks; 3 | using System.ComponentModel; 4 | 5 | namespace ChatterBot.Plugins.SimpleCommands 6 | { 7 | internal class CommandsViewModel : BaseViewModel, IMenuItemViewModel 8 | { 9 | private readonly ICommandsSet _commandsSet; 10 | 11 | public BindingList CustomCommands => _commandsSet.CustomCommands; 12 | 13 | public CommandsViewModel(ICommandsSet commandsSet) 14 | { 15 | _commandsSet = commandsSet; 16 | Icon = new PackIconFontAwesome { Kind = PackIconFontAwesomeKind.ExclamationSolid }; 17 | Label = "Commands"; 18 | ToolTip = "Custom Commands"; 19 | IsOption = false; 20 | Content = new CommandsView { DataContext = this }; 21 | } 22 | 23 | public object Icon { get; } 24 | public object Label { get; } 25 | public object ToolTip { get; } 26 | public bool IsVisible { get; set; } 27 | public bool IsOption { get; } 28 | public object Content { get; } 29 | } 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 DevChatter 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 | -------------------------------------------------------------------------------- /src/UI/ViewModels/MenuItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Interfaces; 2 | using MahApps.Metro.Controls; 3 | using System.Windows.Controls; 4 | 5 | namespace ChatterBot.UI.ViewModels 6 | { 7 | public abstract class MenuItemViewModel 8 | : BaseBindable, IHamburgerMenuItemBase, IMenuItemViewModel 9 | { 10 | private bool _isVisible = true; 11 | 12 | protected MenuItemViewModel(object icon, object label, object toolTip, 13 | UserControl content, bool isOption = false) 14 | { 15 | Icon = icon; 16 | Label = label; 17 | ToolTip = toolTip; 18 | content.DataContext = this; 19 | Content = content; 20 | IsOption = isOption; 21 | } 22 | 23 | public object? Icon { get; } 24 | public object? Label { get; } 25 | public object? ToolTip { get; } 26 | 27 | public bool IsVisible 28 | { 29 | get => _isVisible; 30 | set => SetProperty(ref _isVisible, value); 31 | } 32 | 33 | public bool IsOption { get; } 34 | public object? Content { get; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/UI/ViewModels/PluginViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.UI.Views; 2 | using MahApps.Metro.IconPacks; 3 | using System.Collections.ObjectModel; 4 | 5 | namespace ChatterBot.UI.ViewModels 6 | { 7 | public class PluginViewModel : MenuItemViewModel 8 | { 9 | public ObservableCollection Plugins { get; set; } 10 | 11 | public PluginViewModel() 12 | : base(new PackIconMaterial { Kind = PackIconMaterialKind.Puzzle }, "Plugins", 13 | "Custom Plugins", new PluginView()) 14 | { 15 | Plugins = new ObservableCollection 16 | { 17 | new Plugin { Name = "Wasteful Game", Location = "\\WastefulGame\\", 18 | Enabled = true, PluginRuntime = PluginRuntime.ChatterPlugin, Version = "1.0.0.0"}, 19 | new Plugin { Name = "DevChatter Arcade", Location = "\\DevChatterArcade\\", 20 | Enabled = true, PluginRuntime = PluginRuntime.ChatterPlugin, Version = "1.0.0.0"}, 21 | new Plugin { Name = "Jedi/Sith Detector", Location = "\\JediSithDetector\\", 22 | Enabled = true, PluginRuntime = PluginRuntime.PythonScript, Version = "1.0.0.0"}, 23 | }; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot.Domain/Auth/DataProtection.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Config; 3 | using System; 4 | using System.Security.Cryptography; 5 | 6 | namespace ChatterBot.Domain.Auth 7 | { 8 | internal class DataProtection : IDataProtection 9 | { 10 | private readonly byte[] _entropy; 11 | 12 | public DataProtection(ApplicationSettings appSettings) 13 | { 14 | _entropy = appSettings.SaltBytes; 15 | } 16 | 17 | public byte[] Protect(byte[] data) 18 | { 19 | try 20 | { 21 | return ProtectedData.Protect(data, _entropy, DataProtectionScope.CurrentUser); 22 | } 23 | catch (Exception) 24 | { 25 | // TODO: Log exception 26 | return Array.Empty(); 27 | } 28 | } 29 | 30 | public byte[] Unprotect(byte[] data) 31 | { 32 | try 33 | { 34 | return ProtectedData.Unprotect(data, _entropy, DataProtectionScope.CurrentUser); 35 | } 36 | catch (Exception) 37 | { 38 | // TODO: Log exception 39 | return Array.Empty(); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot/BaseBindable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace ChatterBot 7 | { 8 | public abstract class BaseBindable : INotifyPropertyChanged 9 | { 10 | public event PropertyChangedEventHandler? PropertyChanged; 11 | 12 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") 13 | { 14 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 15 | } 16 | 17 | protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = "") 18 | { 19 | if (EqualityComparer.Default.Equals(storage, value)) return false; 20 | 21 | storage = value; 22 | OnPropertyChanged(propertyName); 23 | return true; 24 | } 25 | 26 | protected bool SetProperty(Func getter, Action setter, T value, [CallerMemberName] string propertyName = "") 27 | { 28 | if (EqualityComparer.Default.Equals(getter(), value)) return false; 29 | 30 | setter(value); 31 | OnPropertyChanged(propertyName); 32 | return true; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /docs/uml/ComponentDiagram.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

ChatterBot Logical Dependency Diagram

7 |
8 | graph TD 9 | subgraph Entry Points 10 | id1(ChatterBot.UI) 11 | style id1 fill:#C6E0B4 12 | id2(ChatterBot.Tests) 13 | style id2 fill:#FFE699 14 | end 15 | 16 | subgraph Core 17 | id3(ChatterBot) 18 | style id3 fill:#B4C6E7 19 | id4(ChatterBot.DomainModel) 20 | style id4 fill:#B4C6E7 21 | end 22 | 23 | subgraph Infrastructure 24 | id5(ChatterBot.Infra) 25 | style id5 fill:#f4b084 26 | id6(ChatterBot.Infra.LiteDb) 27 | style id6 fill:#f4b084 28 | id7(ChatterBot.Infra.Twitch) 29 | style id7 fill:#f4b084 30 | end 31 | 32 | id1 --> id3 33 | id1 --> id4 34 | id1 --> id5 35 | 36 | id2 --> id5 37 | id2 --> id3 38 | 39 | id4 --> id3 40 | 41 | id5 --> id3 42 | id5 --> id6 43 | id5 --> id7 44 | 45 | id6 --> id3 46 | 47 | id7 --> id3 48 |
49 | 50 | -------------------------------------------------------------------------------- /src/Infra/Infra.Twitch/TwitchBot.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Infra.Twitch.Extensions; 3 | using ChatterBot.Interfaces; 4 | using System; 5 | using System.Collections.Generic; 6 | using TwitchLib.Client.Events; 7 | 8 | namespace ChatterBot.Infra.Twitch 9 | { 10 | internal class TwitchBot : TwitchConnection 11 | { 12 | private readonly IEnumerable _messageHandlers; 13 | 14 | public TwitchBot(IDataProtection dataProtection, IEnumerable messageHandlers) 15 | : base(dataProtection) 16 | { 17 | _messageHandlers = messageHandlers; 18 | } 19 | 20 | protected override void Client_OnJoinedChannel(object? sender, OnJoinedChannelArgs e) 21 | { 22 | string message = "Hey everyone! I am a bot connected via TwitchLib!"; 23 | Console.WriteLine(message); 24 | Client.SendMessage(e.Channel, message); 25 | } 26 | 27 | protected override void Client_OnMessageReceived(object? sender, OnMessageReceivedArgs e) 28 | { 29 | ChatMessage chatMessage = e.ChatMessage.ToDomain(); 30 | foreach (IMessageHandler messageHandler in _messageHandlers) 31 | { 32 | messageHandler.Handle(chatMessage, 33 | response => Client.SendMessage(e.ChatMessage.Channel, response)); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/SimpleCommandsPlugin.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Data; 2 | using ChatterBot.Interfaces; 3 | using ChatterBot.State; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | 7 | namespace ChatterBot.Plugins.SimpleCommands 8 | { 9 | internal class SimpleCommandsPlugin : IPlugin 10 | { 11 | private readonly IDataStore _dataStore; 12 | private readonly ICommandsSet _commandsSet; 13 | private readonly IMainMenuItemsSet _menuItemsSet; 14 | 15 | public SimpleCommandsPlugin(IDataStore dataStore, 16 | ICommandsSet commandsSet, IMainMenuItemsSet menuItemsSet) 17 | { 18 | _dataStore = dataStore; 19 | _commandsSet = commandsSet; 20 | _menuItemsSet = menuItemsSet; 21 | } 22 | 23 | public void Initialize() 24 | { 25 | _commandsSet.Initialize(_dataStore.GetEntities()); 26 | 27 | _menuItemsSet.MenuItems.Add(new CommandsViewModel(_commandsSet)); // TODO: Move to Enable() 28 | 29 | _commandsSet.CustomCommands.ListChanged += CustomCommandsOnListChanged; // TODO: Move to Enable() 30 | } 31 | 32 | private void CustomCommandsOnListChanged(object sender, ListChangedEventArgs e) 33 | { 34 | var toSave = _commandsSet.CustomCommands.Where(x => string.IsNullOrEmpty(x.Error)); 35 | _dataStore.SaveEntities(toSave); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Core/ChatterBot.Domain/Auth/AccessTokenRecorder.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Extensions; 3 | using MediatR; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace ChatterBot.Domain.Auth 8 | { 9 | public class AccessTokenRecorder : IRequestHandler 10 | { 11 | private readonly ITwitchAuthentication _twitchAuthentication; 12 | private readonly IDataProtection _dataProtection; 13 | 14 | public AccessTokenRecorder(ITwitchAuthentication twitchAuthentication, 15 | IDataProtection dataProtection) 16 | { 17 | _twitchAuthentication = twitchAuthentication; 18 | _dataProtection = dataProtection; 19 | } 20 | 21 | public Task Handle(AccessTokenReceived request, CancellationToken cancellationToken) 22 | { 23 | if (_twitchAuthentication.States.TryGetValue(request.State, out AuthenticationType authType) 24 | && request.TokenType == "bearer") // TODO: Constant or Enum this! 25 | { 26 | byte[] encrypted = _dataProtection.Protect(request.AccessToken.StringToBytes()); 27 | // TODO: Be sure there *is* an entry in the dictionary. 28 | _twitchAuthentication.Credentials[authType].AuthToken = encrypted; 29 | 30 | return Task.FromResult(true); 31 | } 32 | 33 | return Task.FromResult(false); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/UI/Views/AppSettingsView.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Tests/ChatterBot.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | enable 6 | latest 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | PreserveNewest 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/Infra/Infra.LiteDb/DataStore.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Auth; 2 | using ChatterBot.Config; 3 | using ChatterBot.Data; 4 | using LiteDB; 5 | using System.Collections.Generic; 6 | 7 | namespace ChatterBot.Infra.LiteDb 8 | { 9 | internal class DataStore : IDataStore 10 | { 11 | private readonly ApplicationSettings _appSettings; 12 | 13 | public DataStore(ApplicationSettings appSettings) 14 | { 15 | _appSettings = appSettings; 16 | } 17 | 18 | public void EnsureSchema() 19 | { 20 | BsonMapper.Global.Entity().Id(x => x.AuthType); 21 | 22 | using (LiteDatabase db = new LiteDatabase(_appSettings.LightDbConnection)) 23 | { 24 | // Create Collection if it doesn't exist. 25 | _ = db.GetCollection(nameof(TwitchCredentials)); 26 | } 27 | } 28 | 29 | public List GetEntities() 30 | { 31 | using (LiteDatabase db = new LiteDatabase(_appSettings.LightDbConnection)) 32 | { 33 | ILiteCollection col = db.GetCollection(typeof(TEntity).Name); 34 | 35 | return col.Query().ToList(); 36 | } 37 | } 38 | 39 | public void SaveEntities(IEnumerable entities) 40 | { 41 | using (LiteDatabase db = new LiteDatabase(_appSettings.LightDbConnection)) 42 | { 43 | ILiteCollection col = db.GetCollection(typeof(TEntity).Name); 44 | 45 | col.Upsert(entities); 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/UI/ViewModels/TerminalViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.UI.Views; 2 | using MahApps.Metro.IconPacks; 3 | using Microsoft.Xaml.Behaviors.Core; 4 | using System; 5 | using System.Collections.ObjectModel; 6 | using System.Windows.Input; 7 | using System.Windows.Media; 8 | 9 | namespace ChatterBot.UI.ViewModels 10 | { 11 | public class TerminalViewModel : MenuItemViewModel 12 | { 13 | public ObservableCollection Messages { get; } = new ObservableCollection(); 14 | 15 | private string _text = string.Empty; 16 | public string Text 17 | { 18 | get => _text; 19 | set => SetProperty(ref _text, value); 20 | } 21 | 22 | private string _selectedUser = "DevChatter"; 23 | public string SelectedUser 24 | { 25 | get => _selectedUser; 26 | set => SetProperty(ref _selectedUser, value); 27 | } 28 | 29 | public Color UserColor { get; set; } = Color.FromRgb(255, 0, 0); 30 | 31 | public TerminalViewModel() 32 | : base(new PackIconMaterialDesign { Kind = PackIconMaterialDesignKind.Chat }, 33 | "Chat", "Console and Chat", new TerminalView()) 34 | { 35 | } 36 | 37 | public void SendMessage() 38 | { 39 | Messages.Add(Message); 40 | Text = string.Empty; 41 | } 42 | 43 | private ICommand? _sendMessageCommand; 44 | public ICommand SendMessageCommand => _sendMessageCommand ??= new ActionCommand(SendMessage); 45 | 46 | public ChatMessage Message => new ChatMessage(DateTime.UtcNow, 47 | SelectedUser, 48 | UserColor.ToString(), 49 | Text); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Plugins/SimpleCommands/Validation/CustomCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace ChatterBot.Plugins.SimpleCommands.Validation 5 | { 6 | [SuppressMessage("Build", "CA1812", Justification = "Compiler doesn't understand dependency injection")] 7 | [SuppressMessage("Naming", "CA1710:Identifiers should have correct suffix", Justification = "Fluent Validators are never used as collections directly")] 8 | internal class CustomCommandValidator : AbstractValidator, ICustomCommandValidator 9 | { 10 | private const int TwitchMessageMaximum = 500; 11 | 12 | public CustomCommandValidator() 13 | { 14 | RuleFor(x => x.CommandWord) 15 | .NotEmpty().WithMessage("{PropertyName} must not be empty.") 16 | .MaximumLength(TwitchMessageMaximum).WithMessage("{PropertyName} length cannot exceed {MaxLength}. Entered {TotalLength}."); 17 | 18 | RuleFor(x => x.Response) 19 | .NotEmpty().WithMessage("{PropertyName} must not be empty.") 20 | .MaximumLength(TwitchMessageMaximum).WithMessage("{PropertyName} length cannot exceed {MaxLength}. Entered {TotalLength}."); 21 | 22 | RuleFor(x => x.CooldownTime) 23 | .GreaterThanOrEqualTo(0).WithMessage("{PropertyName} cannot be negative.") 24 | .LessThanOrEqualTo(1440).WithMessage("{PropertyName} cannot be more than a day."); 25 | 26 | RuleFor(x => x.UserCooldownTime) 27 | .GreaterThanOrEqualTo(0).WithMessage("{PropertyName} cannot be negative.") 28 | .LessThanOrEqualTo(1440).WithMessage("{PropertyName} cannot be more than a day."); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/UI/ViewModels/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Data; 2 | using ChatterBot.Interfaces; 3 | using ChatterBot.State; 4 | using Microsoft.Xaml.Behaviors.Core; 5 | using System.ComponentModel; 6 | using System.Windows; 7 | using System.Windows.Input; 8 | 9 | namespace ChatterBot.UI.ViewModels 10 | { 11 | public class MainViewModel : BaseViewModel 12 | { 13 | private AccountsWindow? _settingsWindow; 14 | private readonly AccountsViewModel _accountsViewModel; 15 | private readonly IMainMenuItemsSet _mainMenuItemsSet; 16 | private readonly IDataStore _dataStore; 17 | 18 | public MainViewModel(AccountsViewModel accountsViewModel, 19 | IMainMenuItemsSet mainMenuItemsSet, 20 | IDataStore dataStore) 21 | { 22 | _accountsViewModel = accountsViewModel; 23 | _mainMenuItemsSet = mainMenuItemsSet; 24 | _dataStore = dataStore; 25 | ShowAccountsWindowCommand = new ActionCommand(ShowAccountsWindow); 26 | } 27 | 28 | public BindingList MenuItems => _mainMenuItemsSet.MenuItems; 29 | 30 | public BindingList MenuOptionItems => _mainMenuItemsSet.MenuOptionItems; 31 | 32 | public ICommand ShowAccountsWindowCommand { get; set; } 33 | 34 | private void ShowAccountsWindow() 35 | { 36 | if (_settingsWindow != null) 37 | { 38 | _settingsWindow.Activate(); 39 | return; 40 | } 41 | 42 | _settingsWindow = new AccountsWindow(_accountsViewModel, _dataStore); 43 | _settingsWindow.Owner = Application.Current.MainWindow; 44 | _settingsWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner; 45 | _settingsWindow.Closed += (o, args) => _settingsWindow = null; 46 | _settingsWindow.Show(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/UI/Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using ChatterBot.Config; 2 | using ChatterBot.Domain.Auth; 3 | using MediatR; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | 9 | namespace ChatterBot.UI.Web 10 | { 11 | public class Startup 12 | { 13 | public IHostEnvironment HostEnvironment { get; } 14 | public IConfiguration Configuration { get; } 15 | 16 | public Startup(IHostEnvironment hostEnvironment, IConfiguration configuration) 17 | { 18 | HostEnvironment = hostEnvironment; 19 | Configuration = configuration; 20 | } 21 | 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers(); 25 | services.AddMvc(); 26 | services.AddSignalR(); 27 | 28 | services.AddMediatR(typeof(Startup), typeof(AccessTokenRecorder)); 29 | 30 | var appSettings = Configuration.Get(); 31 | services.AddDomain(appSettings); 32 | services.AddInfrastructure(appSettings); 33 | services.AddUI(); 34 | 35 | services.AddSimpleCommandsPlugin(); 36 | } 37 | 38 | public void Configure(IApplicationBuilder app) 39 | { 40 | if (HostEnvironment.IsDevelopment()) 41 | { 42 | app.UseDeveloperExceptionPage(); 43 | } 44 | else 45 | { 46 | app.UseHsts(); 47 | } 48 | 49 | app.UseHttpsRedirection(); 50 | 51 | app.UseDefaultFiles(); 52 | app.UseStaticFiles(); 53 | 54 | app.UseRouting(); 55 | 56 | app.UseEndpoints(endpoints => 57 | { 58 | endpoints.MapControllers(); 59 | }); 60 | } 61 | 62 | } 63 | } -------------------------------------------------------------------------------- /src/UI/Views/TwitchStreamerView.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 |