mirror of
https://github.com/tribufu/ServerManagers
synced 2026-06-01 09:42:39 +00:00
Discord Plugin Source
Added discord plugin source to github
This commit is contained in:
parent
4a163627b8
commit
6f671a9d57
50 changed files with 3673 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
################################################################################
|
||||||
|
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
/Plugins/Discord/source/.vs
|
||||||
|
/Plugins/Discord/source/Plugin.Common/bin
|
||||||
|
/Plugins/Discord/source/Plugin.Common/obj
|
||||||
|
/Plugins/Discord/source/Plugin.Common/Publish
|
||||||
|
/Plugins/Discord/source/Plugin.Discord/bin
|
||||||
|
/Plugins/Discord/source/Plugin.Discord/obj
|
||||||
|
/Plugins/Discord/source/Plugin.Discord/Publish
|
||||||
15
Plugins/Discord/source/Plugin.Common/Enums/AlertTypeEnum.cs
Normal file
15
Plugins/Discord/source/Plugin.Common/Enums/AlertTypeEnum.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public enum AlertType
|
||||||
|
{
|
||||||
|
Error,
|
||||||
|
Shutdown,
|
||||||
|
ShutdownMessage,
|
||||||
|
ShutdownReason,
|
||||||
|
Startup,
|
||||||
|
Backup,
|
||||||
|
UpdateResults,
|
||||||
|
ServerStatusChange,
|
||||||
|
ModUpdateDetected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Simple extension for icon, to let you choose icon with specific size.
|
||||||
|
/// Usage sample:
|
||||||
|
/// Image Stretch="None" Source="{common:Icon /Controls;component/icons/custom.ico, 16}"
|
||||||
|
/// Or:
|
||||||
|
/// Image Source="{common:Icon Source={Binding IconResource}, Size=16}"
|
||||||
|
/// </summary>
|
||||||
|
public class IconExtension : MarkupExtension
|
||||||
|
{
|
||||||
|
private string _path;
|
||||||
|
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// Have to make full pack URI from short form, so System.Uri recognizes it.
|
||||||
|
_path = $"pack://application:,,,{value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Size { get; set; }
|
||||||
|
|
||||||
|
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
var decoder = BitmapDecoder.Create(new Uri(Path), BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnDemand);
|
||||||
|
|
||||||
|
var result = decoder.Frames.SingleOrDefault(f => f.Width == Size);
|
||||||
|
if (result == default(BitmapFrame))
|
||||||
|
{
|
||||||
|
result = decoder.Frames.OrderBy(f => f.Width).First();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public interface IAlertPlugin : IPlugin
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the alert message passed for the profile.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="alertType">The type of alert message.</param>
|
||||||
|
/// <param name="profileName">The name of the profile the alert message is associated with.</param>
|
||||||
|
/// <param name="alertMessage">The message of the alert.</param>
|
||||||
|
void HandleAlert(AlertType alertType, string profileName, string alertMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Plugins/Discord/source/Plugin.Common/Interfaces/IBeta.cs
Normal file
11
Plugins/Discord/source/Plugin.Common/Interfaces/IBeta.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public interface IBeta
|
||||||
|
{
|
||||||
|
bool BetaEnabled
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
Plugins/Discord/source/Plugin.Common/Interfaces/IPlugin.cs
Normal file
56
Plugins/Discord/source/Plugin.Common/Interfaces/IPlugin.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public interface IPlugin
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a values indicating if the plugin can be used
|
||||||
|
/// </summary>
|
||||||
|
bool Enabled
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating the code of the plugin
|
||||||
|
/// </summary>
|
||||||
|
string PluginCode
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating the name of the plugin
|
||||||
|
/// </summary>
|
||||||
|
string PluginName
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating the version of the plugin
|
||||||
|
/// </summary>
|
||||||
|
Version PluginVersion
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that indicates if the plugin has a configuration form.
|
||||||
|
/// </summary>
|
||||||
|
bool HasConfigForm
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs any initialization for the plugin.
|
||||||
|
/// </summary>
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the configuration form.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="owner">The owner window.</param>
|
||||||
|
void OpenConfigForm(Window owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Plugins/Discord/source/Plugin.Common/Plugin.Common.csproj
Normal file
24
Plugins/Discord/source/Plugin.Common/Plugin.Common.csproj
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<SccProjectName>%24/Development/ServerManagers/Main/Plugin.Common</SccProjectName>
|
||||||
|
<SccProvider>{4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}</SccProvider>
|
||||||
|
<SccAuxPath>https://dev.azure.com/bretthewitson</SccAuxPath>
|
||||||
|
<SccLocalPath>.</SccLocalPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net462</TargetFramework>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
<RootNamespace>ServerManagerTool.Plugin.Common</RootNamespace>
|
||||||
|
<AssemblyName>ServerManager.Plugin.Common</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
<Reference Include="System.Xaml" />
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
30
Plugins/Discord/source/Plugin.Common/PluginException.cs
Normal file
30
Plugins/Discord/source/Plugin.Common/PluginException.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Security;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public class PluginException : Exception
|
||||||
|
{
|
||||||
|
public PluginException()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PluginException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PluginException(string message, Exception innerException)
|
||||||
|
: base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[SecuritySafeCritical]
|
||||||
|
protected PluginException(SerializationInfo info, StreamingContext context)
|
||||||
|
: base(info, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
274
Plugins/Discord/source/Plugin.Common/PluginHelper.cs
Normal file
274
Plugins/Discord/source/Plugin.Common/PluginHelper.cs
Normal file
|
|
@ -0,0 +1,274 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public sealed class PluginHelper : IDisposable
|
||||||
|
{
|
||||||
|
private const string PLUGINFILE_FOLDER = "Plugins";
|
||||||
|
private const string PLUGINFILE_EXTENSION = "dll";
|
||||||
|
|
||||||
|
private static volatile PluginHelper _instance;
|
||||||
|
private static readonly object _syncLock = new object();
|
||||||
|
|
||||||
|
private readonly Object _syncLockProcessAlert = new Object();
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
private PluginHelper()
|
||||||
|
{
|
||||||
|
BetaEnabled = false;
|
||||||
|
Plugins = new ObservableCollection<PluginItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PluginHelper Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_instance != null)
|
||||||
|
return _instance;
|
||||||
|
|
||||||
|
lock(_syncLock)
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
_instance = new PluginHelper();
|
||||||
|
}
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PluginFolder
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? Environment.CurrentDirectory);
|
||||||
|
return Path.Combine(folder, PLUGINFILE_FOLDER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool BetaEnabled
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<PluginItem> Plugins
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AddPlugin(string folder, string pluginFile)
|
||||||
|
{
|
||||||
|
if (!CheckPluginFile(pluginFile))
|
||||||
|
throw new PluginException("The selected file does not contain server manager plugins or is for a previous version of server manager.");
|
||||||
|
|
||||||
|
var pluginFolder = Path.Combine(folder, PLUGINFILE_FOLDER);
|
||||||
|
if (!Directory.Exists(pluginFolder))
|
||||||
|
Directory.CreateDirectory(pluginFolder);
|
||||||
|
|
||||||
|
var newPluginFile = Path.Combine(pluginFolder, $"{Path.GetFileName(pluginFile)}");
|
||||||
|
if (File.Exists(newPluginFile))
|
||||||
|
throw new PluginException("A file with the same name already exists, delete the existing file and try again.");
|
||||||
|
|
||||||
|
File.Copy(pluginFile, newPluginFile, true);
|
||||||
|
|
||||||
|
LoadPlugin(newPluginFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool CheckPluginFile(string pluginFile)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(pluginFile))
|
||||||
|
return false;
|
||||||
|
if (!File.Exists(pluginFile))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Assembly assembly = Assembly.Load(File.ReadAllBytes(pluginFile));
|
||||||
|
if (assembly == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Type[] types;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
types = assembly.GetTypes();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if the file contains a plugin
|
||||||
|
foreach (Type type in types)
|
||||||
|
{
|
||||||
|
if (type.GetInterface(typeof(IPlugin).Name) != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DeleteAllPlugins()
|
||||||
|
{
|
||||||
|
for (int index = Plugins.Count - 1; index >= 0; index--)
|
||||||
|
{
|
||||||
|
var pluginFile = Plugins[index].PluginFile;
|
||||||
|
|
||||||
|
Plugins.RemoveAt(index);
|
||||||
|
|
||||||
|
if (File.Exists(pluginFile))
|
||||||
|
File.Delete(pluginFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DeletePlugin(string pluginFile)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(pluginFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int index = Plugins.Count - 1; index >= 0; index--)
|
||||||
|
{
|
||||||
|
if (Plugins[index].PluginFile.Equals(pluginFile, StringComparison.OrdinalIgnoreCase))
|
||||||
|
Plugins.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(pluginFile))
|
||||||
|
File.Delete(pluginFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LoadPlugin(string pluginFile)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(pluginFile))
|
||||||
|
return;
|
||||||
|
if (!File.Exists(pluginFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Assembly assembly = Assembly.Load(File.ReadAllBytes(pluginFile));
|
||||||
|
if (assembly == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Type[] types;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
types = assembly.GetTypes();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check if the file contains one or more plugins
|
||||||
|
foreach (Type type in types)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (type.GetInterface(typeof(IAlertPlugin).Name) != null)
|
||||||
|
{
|
||||||
|
var plugin = assembly.CreateInstance(type.FullName) as IAlertPlugin;
|
||||||
|
if (plugin != null && plugin.Enabled)
|
||||||
|
{
|
||||||
|
if (type.GetInterface(typeof(IBeta).Name) != null)
|
||||||
|
((IBeta)plugin).BetaEnabled = BetaEnabled;
|
||||||
|
plugin.Initialize();
|
||||||
|
|
||||||
|
Plugins.Add(new PluginItem { Plugin = plugin, PluginFile = pluginFile, PluginType = nameof(IAlertPlugin) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(LoadPlugin)} - {type.FullName}\r\n{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LoadPlugins(string folder, bool ClearExisting)
|
||||||
|
{
|
||||||
|
if (ClearExisting)
|
||||||
|
Plugins.Clear();
|
||||||
|
|
||||||
|
var pluginFolder = Path.Combine(folder, PLUGINFILE_FOLDER);
|
||||||
|
if (string.IsNullOrWhiteSpace(pluginFolder))
|
||||||
|
return;
|
||||||
|
if (!Directory.Exists(pluginFolder))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pluginFiles = Directory.GetFiles(pluginFolder, $"*.{PLUGINFILE_EXTENSION}");
|
||||||
|
foreach (var pluginFile in pluginFiles)
|
||||||
|
{
|
||||||
|
LoadPlugin(pluginFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OpenConfigForm(string pluginCode, Window owner)
|
||||||
|
{
|
||||||
|
if (Plugins == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pluginItem = Plugins.FirstOrDefault(p => p.Plugin.PluginCode.Equals(pluginCode, StringComparison.OrdinalIgnoreCase));
|
||||||
|
OpenConfigForm(pluginItem.Plugin, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OpenConfigForm(IPlugin plugin, Window owner)
|
||||||
|
{
|
||||||
|
if (plugin == null || !plugin.Enabled || !plugin.HasConfigForm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
plugin.OpenConfigForm(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool ProcessAlert(AlertType alertType, string profileName, string alertMessage)
|
||||||
|
{
|
||||||
|
if (Plugins == null || Plugins.Count == 0 || string.IsNullOrWhiteSpace(alertMessage))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var plugins = Plugins.Where(p => (p.PluginType is nameof(IAlertPlugin)) && (p.Plugin?.Enabled ?? false));
|
||||||
|
if (plugins.Count() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lock (_syncLockProcessAlert)
|
||||||
|
{
|
||||||
|
var message = alertMessage.Replace("\\r\\n", "\\n");
|
||||||
|
message = message.Replace("\\n", "\n");
|
||||||
|
|
||||||
|
foreach (var pluginItem in plugins)
|
||||||
|
{
|
||||||
|
((IAlertPlugin)pluginItem.Plugin).HandleAlert(alertType, profileName, message.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Plugins/Discord/source/Plugin.Common/PluginItem.cs
Normal file
27
Plugins/Discord/source/Plugin.Common/PluginItem.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public sealed class PluginItem
|
||||||
|
{
|
||||||
|
internal PluginItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPlugin Plugin
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PluginFile
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PluginType
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("ServerManager Common Plugin Library")]
|
||||||
|
[assembly: AssemblyDescription("The library is used to provide common plugin functionality to the server managers.")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Bletch1971")]
|
||||||
|
[assembly: AssemblyProduct("Server Managers")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2015-2020")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("679fe859-9a82-4ffb-a758-c1e8df915f58")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
[assembly: AssemblyVersion("1.0.1.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.1.0")]
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("ARK Server Manager")]
|
||||||
|
[assembly: InternalsVisibleTo("ConanServerManager")]
|
||||||
|
[assembly: InternalsVisibleTo("ServerManager")]
|
||||||
64
Plugins/Discord/source/Plugin.Common/Utils/JsonUtils.cs
Normal file
64
Plugins/Discord/source/Plugin.Common/Utils/JsonUtils.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.Serialization.Json;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public static class JsonUtils
|
||||||
|
{
|
||||||
|
public static T DeserializeFromFile<T>(string file)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(file) || !File.Exists(file))
|
||||||
|
return default(T);
|
||||||
|
|
||||||
|
StreamReader streamReader = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
streamReader = File.OpenText(file);
|
||||||
|
|
||||||
|
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
|
||||||
|
return (T)serializer.ReadObject(streamReader.BaseStream);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (streamReader != null)
|
||||||
|
streamReader.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SerializeToFile<T>(T value, string file)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
StreamWriter streamWriter = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var folder = Path.GetDirectoryName(file);
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
|
||||||
|
streamWriter = File.CreateText(file);
|
||||||
|
|
||||||
|
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
|
||||||
|
serializer.WriteObject(streamWriter.BaseStream, value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (streamWriter != null)
|
||||||
|
streamWriter.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Plugins/Discord/source/Plugin.Common/Utils/ResourceUtils.cs
Normal file
25
Plugins/Discord/source/Plugin.Common/Utils/ResourceUtils.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Common
|
||||||
|
{
|
||||||
|
public static class ResourceUtils
|
||||||
|
{
|
||||||
|
public static string GetResourceString(ResourceDictionary resources, string inKey)
|
||||||
|
{
|
||||||
|
if (resources == null)
|
||||||
|
throw new ArgumentNullException(nameof(resources), "parameter cannot be null.");
|
||||||
|
if (string.IsNullOrWhiteSpace(inKey))
|
||||||
|
throw new ArgumentNullException(nameof(inKey), "parameter cannot be null.");
|
||||||
|
|
||||||
|
if (resources.Contains(inKey) && resources[inKey] is string)
|
||||||
|
{
|
||||||
|
var resourceString = resources[inKey].ToString();
|
||||||
|
resourceString = resourceString.Replace("\\r", "\r");
|
||||||
|
resourceString = resourceString.Replace("\\n", "\n");
|
||||||
|
return resourceString;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Plugins/Discord/source/Plugin.Discord/Art/Add.ico
Normal file
BIN
Plugins/Discord/source/Plugin.Discord/Art/Add.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
Plugins/Discord/source/Plugin.Discord/Art/ChangeNotes.ico
Normal file
BIN
Plugins/Discord/source/Plugin.Discord/Art/ChangeNotes.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
Plugins/Discord/source/Plugin.Discord/Art/Delete.ico
Normal file
BIN
Plugins/Discord/source/Plugin.Discord/Art/Delete.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
Plugins/Discord/source/Plugin.Discord/Art/Download.ico
Normal file
BIN
Plugins/Discord/source/Plugin.Discord/Art/Download.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
Plugins/Discord/source/Plugin.Discord/Art/Edit.ico
Normal file
BIN
Plugins/Discord/source/Plugin.Discord/Art/Edit.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
Plugins/Discord/source/Plugin.Discord/Art/favicon.ico
Normal file
BIN
Plugins/Discord/source/Plugin.Discord/Art/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
158
Plugins/Discord/source/Plugin.Discord/Config.Designer.cs
generated
Normal file
158
Plugins/Discord/source/Plugin.Discord/Config.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
|
||||||
|
internal sealed partial class Config : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Config defaultInstance = ((Config)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Config())));
|
||||||
|
|
||||||
|
public static Config Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("http://servermanager.azurewebsites.net/api/plugin/call/{0}/{1}/")]
|
||||||
|
public string PluginCallUrlFormat {
|
||||||
|
get {
|
||||||
|
return ((string)(this["PluginCallUrlFormat"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("http://whatismyip.akamai.com/")]
|
||||||
|
public string PublicIPCheckUrl {
|
||||||
|
get {
|
||||||
|
return ((string)(this["PublicIPCheckUrl"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("12")]
|
||||||
|
public int CallHomeDelay {
|
||||||
|
get {
|
||||||
|
return ((int)(this["CallHomeDelay"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("5000")]
|
||||||
|
public int RequestTimeout {
|
||||||
|
get {
|
||||||
|
return ((int)(this["RequestTimeout"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("1B745000-6389-4770-9509-C6A05E209323")]
|
||||||
|
public string PluginCode {
|
||||||
|
get {
|
||||||
|
return ((string)(this["PluginCode"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("Discord Plugin")]
|
||||||
|
public string PluginName {
|
||||||
|
get {
|
||||||
|
return ((string)(this["PluginName"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" +
|
||||||
|
"d/latest.zip")]
|
||||||
|
public string LatestDownloadUrl {
|
||||||
|
get {
|
||||||
|
return ((string)(this["LatestDownloadUrl"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" +
|
||||||
|
"d/latest.txt")]
|
||||||
|
public string LatestVersionUrl {
|
||||||
|
get {
|
||||||
|
return ((string)(this["LatestVersionUrl"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" +
|
||||||
|
"d/beta/latest.zip")]
|
||||||
|
public string LatestBetaDownloadUrl {
|
||||||
|
get {
|
||||||
|
return ((string)(this["LatestBetaDownloadUrl"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" +
|
||||||
|
"d/beta/latest.txt")]
|
||||||
|
public string LatestBetaVersionUrl {
|
||||||
|
get {
|
||||||
|
return ((string)(this["LatestBetaVersionUrl"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("ServerManager.Plugin.Discord.zip")]
|
||||||
|
public string PluginZipFilename {
|
||||||
|
get {
|
||||||
|
return ((string)(this["PluginZipFilename"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("_discordplugin.cfg")]
|
||||||
|
public string ConfigFile {
|
||||||
|
get {
|
||||||
|
return ((string)(this["ConfigFile"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" +
|
||||||
|
"d/VersionFeed.xml")]
|
||||||
|
public string VersionFeedUrl {
|
||||||
|
get {
|
||||||
|
return ((string)(this["VersionFeedUrl"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" +
|
||||||
|
"d/beta/VersionFeed.xml")]
|
||||||
|
public string VersionBetaFeedUrl {
|
||||||
|
get {
|
||||||
|
return ((string)(this["VersionBetaFeedUrl"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
Plugins/Discord/source/Plugin.Discord/Config.settings
Normal file
48
Plugins/Discord/source/Plugin.Discord/Config.settings
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ServerManagerTool.Plugin.Discord" GeneratedClassName="Config">
|
||||||
|
<Profiles />
|
||||||
|
<Settings>
|
||||||
|
<Setting Name="PluginCallUrlFormat" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">http://servermanager.azurewebsites.net/api/plugin/call/{0}/{1}/</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="PublicIPCheckUrl" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">http://whatismyip.akamai.com/</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="CallHomeDelay" Type="System.Int32" Scope="Application">
|
||||||
|
<Value Profile="(Default)">12</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="RequestTimeout" Type="System.Int32" Scope="Application">
|
||||||
|
<Value Profile="(Default)">5000</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="PluginCode" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">1B745000-6389-4770-9509-C6A05E209323</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="PluginName" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">Discord Plugin</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="LatestDownloadUrl" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/latest.zip</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="LatestVersionUrl" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/latest.txt</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="LatestBetaDownloadUrl" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/latest.zip</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="LatestBetaVersionUrl" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/latest.txt</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="PluginZipFilename" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">ServerManager.Plugin.Discord.zip</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="ConfigFile" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">_discordplugin.cfg</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="VersionFeedUrl" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/VersionFeed.xml</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="VersionBetaFeedUrl" Type="System.String" Scope="Application">
|
||||||
|
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/VersionFeed.xml</Value>
|
||||||
|
</Setting>
|
||||||
|
</Settings>
|
||||||
|
</SettingsFile>
|
||||||
260
Plugins/Discord/source/Plugin.Discord/DiscordPlugin.cs
Normal file
260
Plugins/Discord/source/Plugin.Discord/DiscordPlugin.cs
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
using ServerManagerTool.Plugin.Common;
|
||||||
|
using ServerManagerTool.Plugin.Discord.Windows;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
public sealed class DiscordPlugin : IAlertPlugin, IBeta
|
||||||
|
{
|
||||||
|
private const int MAX_MESSAGE_LENGTH = 1980; // 2000 minus some formatting characters
|
||||||
|
|
||||||
|
private Object lockObject = new Object();
|
||||||
|
|
||||||
|
public DiscordPlugin()
|
||||||
|
{
|
||||||
|
BetaEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DiscordPluginConfig PluginConfig
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BetaEnabled
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Enabled => true;
|
||||||
|
|
||||||
|
public string PluginCode => Config.Default.PluginCode;
|
||||||
|
|
||||||
|
public string PluginName => Config.Default.PluginName;
|
||||||
|
|
||||||
|
public Version PluginVersion
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new Version();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasConfigForm => true;
|
||||||
|
|
||||||
|
private async Task CallHomeAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var publicIP = await NetworkUtils.DiscoverPublicIPAsync();
|
||||||
|
await NetworkUtils.PerformCallToAPIAsync(PluginCode, publicIP);
|
||||||
|
#if DEBUG
|
||||||
|
var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordApiCalls.log");
|
||||||
|
File.AppendAllLines(logFile, new[] { "CallHomeAsync successful" }, Encoding.Unicode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Failed calling home {ex.Message}");
|
||||||
|
#if DEBUG
|
||||||
|
var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordErrors.log");
|
||||||
|
File.AppendAllLines(logFile, new[] { $"Failed calling home {ex.Message}" }, Encoding.Unicode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleAlert(AlertType alertType, string profileName, string alertMessage)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(alertMessage))
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (lockObject)
|
||||||
|
{
|
||||||
|
var configProfiles = PluginConfig.ConfigProfiles.Where(cp => cp.IsEnabled
|
||||||
|
&& cp.AlertTypes.Any(pn => pn.Value.Equals(alertType))
|
||||||
|
&& cp.ProfileNames.Any(pn => pn.Value.Equals(profileName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
&& !string.IsNullOrWhiteSpace(cp.DiscordWebhookUrl));
|
||||||
|
if (configProfiles == null || configProfiles.Count() == 0)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordErrors.log");
|
||||||
|
File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} (No config profiles found)" }, Encoding.Unicode);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var configProfile in configProfiles)
|
||||||
|
{
|
||||||
|
HandleAlert(configProfile, alertType, profileName, alertMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void HandleAlert(ConfigProfile configProfile, AlertType alertType, string profileName, string alertMessage)
|
||||||
|
{
|
||||||
|
if (configProfile == null || string.IsNullOrWhiteSpace(configProfile.DiscordWebhookUrl) || string.IsNullOrWhiteSpace(alertMessage))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove any bad characters
|
||||||
|
var formattedProfileName = profileName?.Replace("&", "_") ?? string.Empty;
|
||||||
|
var formattedAlertMessage = alertMessage?.Replace("&", "_") ?? string.Empty;
|
||||||
|
|
||||||
|
// check if we need to add the profile name to the message
|
||||||
|
if (configProfile.PrefixMessageWithProfileName && !string.IsNullOrWhiteSpace(formattedProfileName))
|
||||||
|
formattedAlertMessage = $"({formattedProfileName}) {formattedAlertMessage}";
|
||||||
|
|
||||||
|
// check if the message is too long
|
||||||
|
if (formattedAlertMessage.Length > MAX_MESSAGE_LENGTH)
|
||||||
|
formattedAlertMessage = $"{formattedAlertMessage.Substring(0, MAX_MESSAGE_LENGTH - 3)}...";
|
||||||
|
|
||||||
|
// check if we need to apply any styles to the message
|
||||||
|
if (configProfile.MessageCodeBlock)
|
||||||
|
formattedAlertMessage = $"```{formattedAlertMessage}```";
|
||||||
|
if (configProfile.MessageBold)
|
||||||
|
formattedAlertMessage = $"**{formattedAlertMessage}**";
|
||||||
|
if (configProfile.MessageItalic)
|
||||||
|
formattedAlertMessage = $"*{formattedAlertMessage}*";
|
||||||
|
if (configProfile.MessageUnderlined)
|
||||||
|
formattedAlertMessage = $"__{formattedAlertMessage}__";
|
||||||
|
formattedAlertMessage = HttpUtility.UrlEncode(formattedAlertMessage);
|
||||||
|
|
||||||
|
var postData = string.Empty;
|
||||||
|
|
||||||
|
if (configProfile.DiscordUseTTS)
|
||||||
|
postData += $"&tts={configProfile.DiscordUseTTS}";
|
||||||
|
if (!string.IsNullOrWhiteSpace(configProfile.DiscordBotName))
|
||||||
|
postData += $"&username={configProfile.DiscordBotName.Replace("&", "_")}";
|
||||||
|
postData += $"&content={formattedAlertMessage}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = Encoding.UTF8.GetBytes(postData);
|
||||||
|
|
||||||
|
var url = configProfile.DiscordWebhookUrl;
|
||||||
|
url = url.Trim();
|
||||||
|
if (url.EndsWith("/"))
|
||||||
|
url = url.Substring(0, url.Length - 1);
|
||||||
|
|
||||||
|
var httpRequest = WebRequest.Create($"{url}?wait=true");
|
||||||
|
httpRequest.Timeout = Config.Default.RequestTimeout;
|
||||||
|
httpRequest.Method = "POST";
|
||||||
|
httpRequest.ContentType = "application/x-www-form-urlencoded";
|
||||||
|
httpRequest.ContentLength = data.Length;
|
||||||
|
|
||||||
|
using (var stream = httpRequest.GetRequestStream())
|
||||||
|
{
|
||||||
|
stream.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
|
||||||
|
var responseString = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd();
|
||||||
|
if (httpResponse.StatusCode == HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"{nameof(HandleAlert)}\r\nResponse: {responseString}");
|
||||||
|
#if DEBUG
|
||||||
|
var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordSuccess.log");
|
||||||
|
File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} ({responseString})" }, Encoding.Unicode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"{nameof(HandleAlert)}\r\n{httpResponse.StatusCode}: {responseString}");
|
||||||
|
#if DEBUG
|
||||||
|
var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordErrors.log");
|
||||||
|
File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} ({responseString})" }, Encoding.Unicode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(HandleAlert)}\r\n{ex.Message}");
|
||||||
|
#if DEBUG
|
||||||
|
var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordExceptions.log");
|
||||||
|
File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} ({ex.Message})" }, Encoding.Unicode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
LoadConfig();
|
||||||
|
|
||||||
|
if (PluginConfig.LastCallHome.AddHours(Config.Default.CallHomeDelay) < DateTime.Now)
|
||||||
|
{
|
||||||
|
//CallHomeAsync().DoNotWait();
|
||||||
|
|
||||||
|
PluginConfig.LastCallHome = DateTime.Now;
|
||||||
|
SaveConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadConfig()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PluginConfig = null;
|
||||||
|
|
||||||
|
var configFile = Path.Combine(PluginHelper.PluginFolder, Config.Default.ConfigFile);
|
||||||
|
PluginConfig = JsonUtils.DeserializeFromFile<DiscordPluginConfig>(configFile);
|
||||||
|
|
||||||
|
if ((PluginConfig?.ConfigProfiles?.Count ?? 0) == 0)
|
||||||
|
{
|
||||||
|
PluginConfig = new DiscordPluginConfig();
|
||||||
|
|
||||||
|
SaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginConfig?.CommitChanges();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginConfig = new DiscordPluginConfig();
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(LoadConfig)}\r\n{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenConfigForm(Window owner)
|
||||||
|
{
|
||||||
|
var window = new ConfigWindow(this, this.PluginConfig);
|
||||||
|
window.Owner = owner;
|
||||||
|
|
||||||
|
var dialogResult = window.ShowDialog();
|
||||||
|
if (dialogResult.HasValue && dialogResult.Value)
|
||||||
|
{
|
||||||
|
SaveConfig();
|
||||||
|
LoadConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveConfig()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configFile = Path.Combine(PluginHelper.PluginFolder, Config.Default.ConfigFile);
|
||||||
|
JsonUtils.SerializeToFile(PluginConfig, configFile);
|
||||||
|
PluginConfig?.CommitChanges();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(SaveConfig)}\r\n{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||||
|
>
|
||||||
|
|
||||||
|
<!--#region Global -->
|
||||||
|
<sys:String x:Key="Global_CancelButtonLabel">Cancel</sys:String>
|
||||||
|
<sys:String x:Key="Global_CloseButtonLabel">Close</sys:String>
|
||||||
|
<sys:String x:Key="Global_OkButtonLabel">OK</sys:String>
|
||||||
|
<sys:String x:Key="Global_SaveButtonLabel">Save</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="Global_BetaModeLabel"> (Beta Mode)</sys:String>
|
||||||
|
<sys:String x:Key="Global_NewVersionAvailableLabel">A new version of the plugin is available.</sys:String>
|
||||||
|
<sys:String x:Key="Global_DownloadNewVersionTooltip">Download the latest version.</sys:String>
|
||||||
|
<!--#endregion-->
|
||||||
|
|
||||||
|
<!--#region Config Window -->
|
||||||
|
<sys:String x:Key="ConfigWindow_Title">Discord Plugin Configuration</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigWindow_PatchNotesTooltip">View the Patch Notes.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_AddConfigProfileTooltip">Add Profile</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_ClearConfigProfilesTooltip">Delete All Profiles</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DeleteConfigProfileTooltip">Delete Profile</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_EditConfigProfileTooltip">Edit Profile</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigWindow_NameColumnLabel">Name</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigWindow_AddErrorTitle">Add Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_AddErrorLabel">An error occurred while trying to add a profile.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_ClearTitle">Confirm Delete All Action</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_ClearLabel">Click 'Yes' to confirm you want to delete all.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_ClearErrorTitle">Delete All Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_ClearErrorLabel">An error occurred while trying to delete all profiles.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_CloseTitle">Confirm Close Action</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_CloseLabel">Click 'Yes' to confirm you want to close the form. If you have any unsaved changes they will be lost.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DeleteTitle">Confirm Delete Action</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DeleteLabel">Click 'Yes' to confirm you want to perform the delete.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DeleteErrorTitle">Delete Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DeleteErrorLabel">An error occurred while trying to delete the profile.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DownloadErrorTitle">Download Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DownloadErrorLabel">An error occurred while trying to perform the download.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DownloadSuccessTitle">Download Successful</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_DownloadSuccessLabel">The latest version has been saved to your desktop.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_EditErrorTitle">Edit Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_EditErrorLabel">An error occurred while trying to edit a profile.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_SaveErrorTitle">Save Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigWindow_SaveErrorLabel">An error occurred while trying to perform the save.</sys:String>
|
||||||
|
<!--#endregion-->
|
||||||
|
|
||||||
|
<!--#region Config Item Window -->
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_Title">Profile Configuration</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_NameLabel">Name:</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_NameTooltip">The name of your config profile.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_IsEnabledLabel">Enabled</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_IsEnabledTooltip">Is the config profile enabled.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_WebhookLabel">Webhook Url:</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_WebhookTooltip">This is your Webhook Url provided by discord.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_BotNameLabel">Bot Name:</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_BotNameTooltip">This will override the name you setup in your webhook. Leave blank to use default.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_UseTTSLabel">Use Text to Speech (TTS)</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_UseTTSTooltip">If enabled, will make the bot announce with Text to Speech, otherwise will turn off Text to Speech.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_PrefixMessageWithProfileNameLabel">Prefix Message with Server Manager Profile Name</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_PrefixMessageWithProfileNameTooltip">If enabled, the alert message will be sent prefixed with the server manager profile name.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsLabel">Message Options</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsBoldLabel">Bold</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsBoldTooltip">If enabled, will show the message in bold.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsUnderlineLabel">Underline</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsUnderlineTooltip">If enabled, will show the message with an underline.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsItalicLabel">Italic</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsItalicTooltip">If enabled, will show the message in italic.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsCodeBlockLabel">Embedded Code Block</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_MessageOptionsCodeBlockTooltip">CIf enabled, will show the message in an embedded code block.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ProfileNamesLabel">Profile Names</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_AlertTypesLabel">Alert Types</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_AddProfileNameTooltip">Add Server Manager Profile Name</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ClearProfileNamesTooltip">Delete All Server Manager Profile Names</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_DeleteProfileNameTooltip">Delete Server Manager Profile Name</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_AddAlertTypeTooltip">Add Alert Type</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ClearAlertTypesTooltip">Delete All Alert Types</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_DeleteAlertTypeTooltip">Delete Alert Type</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ProfileNameColumnLabel">Server Manager Profile Name</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_AlertTypeColumnLabel">Alert Type</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_TestButtonLabel">Test</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_AddErrorTitle">Add Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_AddAlertTypeErrorLabel">An error occurred while trying to add an alert type.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_AddProfileNameErrorLabel">An error occurred while trying to add a profile name.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ClearTitle">Confirm Delete All Action</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ClearLabel">Click 'Yes' to confirm you want to delete all.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ClearErrorTitle">Delete All Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ClearAlertTypesErrorLabel">An error occurred while trying to delete all alert types.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_ClearProfileNamesErrorLabel">An error occurred while trying to delete all profile names.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_CloseTitle">Confirm Close Action</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_CloseLabel">Click 'Yes' to confirm you want to close the form. If you have any unsaved changes they will be lost.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_DeleteTitle">Confirm Delete Action</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_DeleteLabel">Click 'Yes' to confirm you want to perform the delete.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_DeleteErrorTitle">Delete Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_DeleteAlertTypeErrorLabel">An error occurred while trying to delete the alert type.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_DeleteProfileNameErrorLabel">An error occurred while trying to delete the profile name.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_TestErrorTitle">Test Action Error</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_TestErrorLabel">An error occurred while trying to test the config profile.</sys:String>
|
||||||
|
<sys:String x:Key="ConfigProfileWindow_TestEnabledErrorLabel">The profile is not enabled and cannot be tested.</sys:String>
|
||||||
|
<!--#endregion-->
|
||||||
|
|
||||||
|
<!--#region Version Feed Window -->
|
||||||
|
<sys:String x:Key="VersionFeedWindow_Title">Discord Plugin Version Details</sys:String>
|
||||||
|
<sys:String x:Key="VersionFeedWindow_Load_FailedTitle">Load Feed Error</sys:String>
|
||||||
|
|
||||||
|
<sys:String x:Key="VersionFeedWindow_VersionFilterLabel">Version:</sys:String>
|
||||||
|
<sys:String x:Key="VersionFeedWindow_VersionFilterTooltip">Select the version to view details.</sys:String>
|
||||||
|
<!--#endregion-->
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
internal interface IBindable
|
||||||
|
{
|
||||||
|
bool HasChanges { get; set; }
|
||||||
|
|
||||||
|
bool HasAnyChanges { get; }
|
||||||
|
|
||||||
|
void CommitChanges();
|
||||||
|
|
||||||
|
void BeginUpdate();
|
||||||
|
|
||||||
|
void EndUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Plugins/Discord/source/Plugin.Discord/Lib/BrowserBehavior.cs
Normal file
32
Plugins/Discord/source/Plugin.Discord/Lib/BrowserBehavior.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
public static class BrowserBehavior
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty HtmlProperty = DependencyProperty.RegisterAttached("Html", typeof(string), typeof(BrowserBehavior), new FrameworkPropertyMetadata(OnHtmlChanged));
|
||||||
|
|
||||||
|
[AttachedPropertyBrowsableForType(typeof(WebBrowser))]
|
||||||
|
public static string GetHtml(WebBrowser d)
|
||||||
|
{
|
||||||
|
return (string)d.GetValue(HtmlProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetHtml(WebBrowser d, string value)
|
||||||
|
{
|
||||||
|
d.SetValue(HtmlProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnHtmlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.NewValue == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (d is WebBrowser wb)
|
||||||
|
{
|
||||||
|
wb.NavigateToString(e.NewValue as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using ServerManagerTool.Plugin.Common;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
internal class AlertTypeValue : Bindable
|
||||||
|
{
|
||||||
|
public AlertTypeValue()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Value = AlertType.Error;
|
||||||
|
OriginalValue = Value;
|
||||||
|
HasChanges = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlertTypeValue(AlertType value)
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
OriginalValue = Value;
|
||||||
|
HasChanges = !Value.Equals(OriginalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlertTypeValue(AlertType value, AlertType originalValue)
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
OriginalValue = originalValue;
|
||||||
|
HasChanges = !Value.Equals(OriginalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public AlertType Value
|
||||||
|
{
|
||||||
|
get { return Get<AlertType>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlertType OriginalValue
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HasAnyChanges => base.HasChanges && !Value.Equals(OriginalValue);
|
||||||
|
|
||||||
|
public override void CommitChanges()
|
||||||
|
{
|
||||||
|
base.CommitChanges();
|
||||||
|
|
||||||
|
OriginalValue = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
internal class AlertTypeValueList : List<AlertTypeValue>, IBindable, INotifyCollectionChanged
|
||||||
|
{
|
||||||
|
private bool _hasChanges = false;
|
||||||
|
|
||||||
|
public bool HasChanges
|
||||||
|
{
|
||||||
|
get => _hasChanges;
|
||||||
|
set => _hasChanges = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasAnyChanges => _hasChanges || this.Any(a => a?.HasAnyChanges ?? false);
|
||||||
|
|
||||||
|
public void BeginUpdate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CommitChanges()
|
||||||
|
{
|
||||||
|
HasChanges = false;
|
||||||
|
|
||||||
|
foreach (var alertType in this)
|
||||||
|
{
|
||||||
|
alertType.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndUpdate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region INotifyCollectionChanged
|
||||||
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CollectionChanged?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyAdd(AlertTypeValue item, bool setChanged = true)
|
||||||
|
{
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
|
||||||
|
if (setChanged)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyClear(bool setChanged = true)
|
||||||
|
{
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||||
|
if (setChanged)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyRemove(AlertTypeValue item, int index, bool setChanged = true)
|
||||||
|
{
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
|
||||||
|
if (setChanged)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
Plugins/Discord/source/Plugin.Discord/Models/Bindable.cs
Normal file
85
Plugins/Discord/source/Plugin.Discord/Models/Bindable.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
internal class Bindable : INotifyPropertyChanged, IBindable
|
||||||
|
{
|
||||||
|
private Dictionary<string, object> _properties = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
protected bool _isUpdating = false;
|
||||||
|
|
||||||
|
public Bindable()
|
||||||
|
{
|
||||||
|
_properties = new Dictionary<string, object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected T Get<T>([CallerMemberName] string name = null)
|
||||||
|
{
|
||||||
|
object value = null;
|
||||||
|
if (_properties?.TryGetValue(name, out value) ?? false)
|
||||||
|
return value == null ? default(T) : (T)value;
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Set<T>(T value, bool setChanged = true, [CallerMemberName] string name = null)
|
||||||
|
{
|
||||||
|
if (Equals(value, Get<T>(name)))
|
||||||
|
return;
|
||||||
|
if (_properties == null)
|
||||||
|
_properties = new Dictionary<string, object>();
|
||||||
|
_properties[name] = value;
|
||||||
|
OnPropertyChanged(name);
|
||||||
|
if (!_isUpdating && setChanged)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
#region INotifyPropertyChanged
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IBindable
|
||||||
|
public bool HasChanges
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value, false); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool HasAnyChanges => HasChanges || (_properties?.Any(p => (p.Value as IBindable)?.HasAnyChanges ?? false) ?? false);
|
||||||
|
|
||||||
|
public virtual void CommitChanges()
|
||||||
|
{
|
||||||
|
HasChanges = false;
|
||||||
|
|
||||||
|
if (_properties == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var property in _properties)
|
||||||
|
{
|
||||||
|
var bindable = property.Value as IBindable;
|
||||||
|
if (bindable != null)
|
||||||
|
bindable.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginUpdate()
|
||||||
|
{
|
||||||
|
_isUpdating = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndUpdate()
|
||||||
|
{
|
||||||
|
_isUpdating = false;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
188
Plugins/Discord/source/Plugin.Discord/Models/ConfigProfile.cs
Normal file
188
Plugins/Discord/source/Plugin.Discord/Models/ConfigProfile.cs
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
internal sealed class ConfigProfile : Bindable
|
||||||
|
{
|
||||||
|
public ConfigProfile()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Name = "New Discord Profile";
|
||||||
|
ProfileNames = new ProfileNameValueList();
|
||||||
|
AlertTypes = new AlertTypeValueList();
|
||||||
|
DiscordWebhookUrl = string.Empty;
|
||||||
|
DiscordBotName = string.Empty;
|
||||||
|
DiscordUseTTS = false;
|
||||||
|
PrefixMessageWithProfileName = false;
|
||||||
|
MessageBold = false;
|
||||||
|
MessageUnderlined = false;
|
||||||
|
MessageItalic = false;
|
||||||
|
MessageCodeBlock = false;
|
||||||
|
IsEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return Get<string>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public ProfileNameValueList ProfileNames
|
||||||
|
{
|
||||||
|
get { return Get<ProfileNameValueList>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public AlertTypeValueList AlertTypes
|
||||||
|
{
|
||||||
|
get { return Get<AlertTypeValueList>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string DiscordWebhookUrl
|
||||||
|
{
|
||||||
|
get { return Get<string>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string DiscordBotName
|
||||||
|
{
|
||||||
|
get { return Get<string>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool DiscordUseTTS
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool PrefixMessageWithProfileName
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool IsEnabled
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool MessageBold
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool MessageUnderlined
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool MessageItalic
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool MessageCodeBlock
|
||||||
|
{
|
||||||
|
get { return Get<bool>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigProfile Clone()
|
||||||
|
{
|
||||||
|
var clone = new ConfigProfile();
|
||||||
|
clone.Name = this.Name;
|
||||||
|
|
||||||
|
foreach (var profileName in this.ProfileNames)
|
||||||
|
{
|
||||||
|
clone.ProfileNames.Add(new ProfileNameValue(profileName.Value, profileName.OriginalValue) { HasChanges = profileName.HasChanges });
|
||||||
|
}
|
||||||
|
clone.ProfileNames.HasChanges = this.ProfileNames.HasChanges;
|
||||||
|
foreach (var alertType in this.AlertTypes)
|
||||||
|
{
|
||||||
|
clone.AlertTypes.Add(new AlertTypeValue(alertType.Value, alertType.OriginalValue) { HasChanges = alertType.HasChanges });
|
||||||
|
}
|
||||||
|
clone.AlertTypes.HasChanges = this.AlertTypes.HasChanges;
|
||||||
|
|
||||||
|
clone.DiscordWebhookUrl = this.DiscordWebhookUrl;
|
||||||
|
clone.DiscordBotName = this.DiscordBotName;
|
||||||
|
clone.DiscordUseTTS = this.DiscordUseTTS;
|
||||||
|
clone.PrefixMessageWithProfileName = this.PrefixMessageWithProfileName;
|
||||||
|
clone.MessageBold = this.MessageBold;
|
||||||
|
clone.MessageUnderlined = this.MessageUnderlined;
|
||||||
|
clone.MessageItalic = this.MessageItalic;
|
||||||
|
clone.MessageCodeBlock = this.MessageCodeBlock;
|
||||||
|
clone.IsEnabled = this.IsEnabled;
|
||||||
|
clone.HasChanges = this.HasChanges;
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyFrom(ConfigProfile source)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.BeginUpdate();
|
||||||
|
|
||||||
|
this.Name = source.Name;
|
||||||
|
|
||||||
|
this.ProfileNames.BeginUpdate();
|
||||||
|
this.ProfileNames.Clear();
|
||||||
|
foreach (var profileName in source.ProfileNames)
|
||||||
|
{
|
||||||
|
this.ProfileNames.Add(new ProfileNameValue(profileName.Value, profileName.OriginalValue));
|
||||||
|
}
|
||||||
|
if (source.ProfileNames.HasChanges)
|
||||||
|
this.ProfileNames.HasChanges = true;
|
||||||
|
this.ProfileNames.EndUpdate();
|
||||||
|
|
||||||
|
this.AlertTypes.BeginUpdate();
|
||||||
|
this.AlertTypes.Clear();
|
||||||
|
foreach (var alertType in source.AlertTypes)
|
||||||
|
{
|
||||||
|
this.AlertTypes.Add(new AlertTypeValue(alertType.Value, alertType.OriginalValue));
|
||||||
|
}
|
||||||
|
if (source.AlertTypes.HasChanges)
|
||||||
|
this.AlertTypes.HasChanges = true;
|
||||||
|
this.AlertTypes.EndUpdate();
|
||||||
|
|
||||||
|
this.DiscordWebhookUrl = source.DiscordWebhookUrl;
|
||||||
|
this.DiscordBotName = source.DiscordBotName;
|
||||||
|
this.DiscordUseTTS = source.DiscordUseTTS;
|
||||||
|
this.PrefixMessageWithProfileName = source.PrefixMessageWithProfileName;
|
||||||
|
this.MessageBold = source.MessageBold;
|
||||||
|
this.MessageUnderlined = source.MessageUnderlined;
|
||||||
|
this.MessageItalic = source.MessageItalic;
|
||||||
|
this.MessageCodeBlock = source.MessageCodeBlock;
|
||||||
|
this.IsEnabled = source.IsEnabled;
|
||||||
|
|
||||||
|
if (source.HasChanges)
|
||||||
|
this.HasChanges = true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.EndUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
internal sealed class DiscordPluginConfig : Bindable
|
||||||
|
{
|
||||||
|
public DiscordPluginConfig()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
LastCallHome = DateTime.MinValue;
|
||||||
|
ConfigProfiles = new ObservableList<ConfigProfile>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public DateTime LastCallHome
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public ObservableList<ConfigProfile> ConfigProfiles
|
||||||
|
{
|
||||||
|
get { return Get<ObservableList<ConfigProfile>>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HasAnyChanges
|
||||||
|
{
|
||||||
|
get => base.HasChanges || (ConfigProfiles?.HasAnyChanges ?? false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
131
Plugins/Discord/source/Plugin.Discord/Models/ObservableList.cs
Normal file
131
Plugins/Discord/source/Plugin.Discord/Models/ObservableList.cs
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
internal class ObservableList<T> : Bindable, IList<T>, INotifyCollectionChanged
|
||||||
|
{
|
||||||
|
private List<T> _listObject = null;
|
||||||
|
|
||||||
|
public ObservableList()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
_listObject = new List<T>();
|
||||||
|
CommitChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HasAnyChanges => base.HasChanges || (_listObject?.Any(i => (i as IBindable)?.HasAnyChanges ?? false) ?? false);
|
||||||
|
|
||||||
|
public override void CommitChanges()
|
||||||
|
{
|
||||||
|
base.CommitChanges();
|
||||||
|
|
||||||
|
if (_listObject == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (T item in _listObject)
|
||||||
|
{
|
||||||
|
var bindable = item as IBindable;
|
||||||
|
if (bindable != null)
|
||||||
|
bindable.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
internal List<T> List
|
||||||
|
{
|
||||||
|
get => _listObject;
|
||||||
|
set => _listObject = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IList<T>
|
||||||
|
public T this[int index]
|
||||||
|
{
|
||||||
|
get => _listObject[index];
|
||||||
|
set
|
||||||
|
{
|
||||||
|
T oldValue = _listObject[index];
|
||||||
|
_listObject[index] = value;
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _listObject.Count;
|
||||||
|
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
_listObject.Add(item);
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_listObject.Clear();
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(T item)
|
||||||
|
{
|
||||||
|
return _listObject.Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
_listObject.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _listObject.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(T item)
|
||||||
|
{
|
||||||
|
return _listObject.IndexOf(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, T item)
|
||||||
|
{
|
||||||
|
_listObject.Insert(index, item);
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(T item)
|
||||||
|
{
|
||||||
|
int index = _listObject.IndexOf(item);
|
||||||
|
var result = _listObject.Remove(item);
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
T item = _listObject[index];
|
||||||
|
_listObject.RemoveAt(index);
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return _listObject.GetEnumerator();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region INotifyCollectionChanged
|
||||||
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||||
|
|
||||||
|
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CollectionChanged?.Invoke(this, e);
|
||||||
|
if (!_isUpdating)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
internal class ProfileNameValue : Bindable
|
||||||
|
{
|
||||||
|
public ProfileNameValue()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Value = string.Empty;
|
||||||
|
OriginalValue = Value;
|
||||||
|
HasChanges = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileNameValue(string value)
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
OriginalValue = Value;
|
||||||
|
HasChanges = !Value.Equals(OriginalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileNameValue(string value, string originalValue)
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
OriginalValue = originalValue;
|
||||||
|
HasChanges = !Value.Equals(OriginalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string Value
|
||||||
|
{
|
||||||
|
get { return Get<string>(); }
|
||||||
|
set { Set(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OriginalValue
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HasAnyChanges => base.HasChanges && !Value.Equals(OriginalValue);
|
||||||
|
|
||||||
|
public override void CommitChanges()
|
||||||
|
{
|
||||||
|
base.CommitChanges();
|
||||||
|
|
||||||
|
OriginalValue = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
internal class ProfileNameValueList : List<ProfileNameValue>, IBindable, INotifyCollectionChanged
|
||||||
|
{
|
||||||
|
private bool _hasChanges = false;
|
||||||
|
|
||||||
|
public bool HasChanges
|
||||||
|
{
|
||||||
|
get => _hasChanges;
|
||||||
|
set => _hasChanges = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasAnyChanges => _hasChanges || this.Any(p => p?.HasAnyChanges ?? false);
|
||||||
|
|
||||||
|
public void BeginUpdate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CommitChanges()
|
||||||
|
{
|
||||||
|
HasChanges = false;
|
||||||
|
|
||||||
|
foreach (var profileName in this)
|
||||||
|
{
|
||||||
|
profileName.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndUpdate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region INotifyCollectionChanged
|
||||||
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CollectionChanged?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyAdd(ProfileNameValue item, bool setChanged = true)
|
||||||
|
{
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
|
||||||
|
if (setChanged)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyClear(bool setChanged = true)
|
||||||
|
{
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||||
|
if (setChanged)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyRemove(ProfileNameValue item, int index, bool setChanged = true)
|
||||||
|
{
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
|
||||||
|
if (setChanged)
|
||||||
|
HasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Plugins/Discord/source/Plugin.Discord/Models/VersionFeed.cs
Normal file
16
Plugins/Discord/source/Plugin.Discord/Models/VersionFeed.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
public class VersionFeed
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
public string SubTitle { get; set; } = string.Empty;
|
||||||
|
public Uri Link { get; set; } = null;
|
||||||
|
public DateTimeOffset Updated { get; set; } = DateTimeOffset.Now;
|
||||||
|
|
||||||
|
public List<VersionFeedEntry> Entries { get; set; } = new List<VersionFeedEntry>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
public class VersionFeedEntry
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
public string Summary { get; set; } = string.Empty;
|
||||||
|
public Uri Link { get; set; } = null;
|
||||||
|
public DateTimeOffset Updated { get; set; } = DateTimeOffset.Now;
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
public string Author { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool IsCurrent { get; set; } = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
Plugins/Discord/source/Plugin.Discord/Plugin.Discord.csproj
Normal file
73
Plugins/Discord/source/Plugin.Discord/Plugin.Discord.csproj
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<SccProjectName>%24/Development/ServerManagers/Main/Plugin.Discord</SccProjectName>
|
||||||
|
<SccProvider>{4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}</SccProvider>
|
||||||
|
<SccAuxPath>https://dev.azure.com/bretthewitson</SccAuxPath>
|
||||||
|
<SccLocalPath>.</SccLocalPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net462</TargetFramework>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
<ApplicationIcon>Art\favicon.ico</ApplicationIcon>
|
||||||
|
<AssemblyName>ServerManager.Plugin.Discord</AssemblyName>
|
||||||
|
<RootNamespace>ServerManagerTool.Plugin.Discord</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Publish\**" />
|
||||||
|
<EmbeddedResource Remove="Publish\**" />
|
||||||
|
<None Remove="Publish\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Art\Add.ico" />
|
||||||
|
<None Remove="Art\ChangeNotes.ico" />
|
||||||
|
<None Remove="Art\Delete.ico" />
|
||||||
|
<None Remove="Art\Download.ico" />
|
||||||
|
<None Remove="Art\Edit.ico" />
|
||||||
|
<None Remove="Art\favicon.ico" />
|
||||||
|
<None Remove="Globalization\en-US\en-US.xaml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Include="Windows\ConfigProfileWindow.xaml" />
|
||||||
|
<Page Include="Windows\ConfigWindow.xaml" />
|
||||||
|
<Page Include="Windows\VersionFeedWindow.xaml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Plugin.Common\Plugin.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.ServiceModel" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Xaml" />
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Art\Add.ico" />
|
||||||
|
<Resource Include="Art\ChangeNotes.ico" />
|
||||||
|
<Resource Include="Art\Delete.ico" />
|
||||||
|
<Resource Include="Art\Download.ico" />
|
||||||
|
<Resource Include="Art\Edit.ico" />
|
||||||
|
<Resource Include="Art\favicon.ico" />
|
||||||
|
<Resource Include="Globalization\en-US\en-US.xaml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Config.Designer.cs">
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Config.settings</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Config.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Config.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("ServerManager Discord Plugin")]
|
||||||
|
[assembly: AssemblyDescription("A Discord plugin that can be used with the server managers.")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Bletch1971")]
|
||||||
|
[assembly: AssemblyProduct("Server Managers")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2015-2020")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("936ef260-fecf-4e9e-a21e-092d65931c7d")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
[assembly: AssemblyVersion("1.0.16.1")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.16.1")]
|
||||||
90
Plugins/Discord/source/Plugin.Discord/Utils/NetworkUtils.cs
Normal file
90
Plugins/Discord/source/Plugin.Discord/Utils/NetworkUtils.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
internal static class NetworkUtils
|
||||||
|
{
|
||||||
|
public static async Task<IPAddress> DiscoverPublicIPAsync()
|
||||||
|
{
|
||||||
|
using (var webClient = new WebClient())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var publicIP = await webClient.DownloadStringTaskAsync(Config.Default.PublicIPCheckUrl);
|
||||||
|
|
||||||
|
if (IPAddress.TryParse(publicIP, out IPAddress address))
|
||||||
|
return address;
|
||||||
|
|
||||||
|
return IPAddress.None;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(DiscoverPublicIPAsync)}\r\n{ex.Message}");
|
||||||
|
return IPAddress.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<Version> CheckLatestVersionAsync(bool betaEnabled)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var webClient = new WebClient())
|
||||||
|
{
|
||||||
|
string latestVersion = null;
|
||||||
|
|
||||||
|
if (betaEnabled)
|
||||||
|
latestVersion = await webClient.DownloadStringTaskAsync(Config.Default.LatestBetaVersionUrl);
|
||||||
|
else
|
||||||
|
latestVersion = await webClient.DownloadStringTaskAsync(Config.Default.LatestVersionUrl);
|
||||||
|
|
||||||
|
if (Version.TryParse(latestVersion, out Version version))
|
||||||
|
return version;
|
||||||
|
|
||||||
|
return new Version();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(CheckLatestVersionAsync)}\r\n{ex.Message}");
|
||||||
|
return new Version();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DownloadLatestVersion(string sourceUrl, string destinationFile)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var client = new WebClient())
|
||||||
|
{
|
||||||
|
client.DownloadFile(sourceUrl, destinationFile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(DownloadLatestVersion)}\r\n{ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task PerformCallToAPIAsync(string pluginCode, IPAddress ipAddress)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var client = new WebClient())
|
||||||
|
{
|
||||||
|
var url = string.Format(Config.Default.PluginCallUrlFormat, pluginCode, ipAddress);
|
||||||
|
await client.DownloadStringTaskAsync(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(PerformCallToAPIAsync)} - {pluginCode}; {ipAddress}\r\n{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Plugins/Discord/source/Plugin.Discord/Utils/TaskUtils.cs
Normal file
15
Plugins/Discord/source/Plugin.Discord/Utils/TaskUtils.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
internal static class TaskUtils
|
||||||
|
{
|
||||||
|
public static readonly Task FinishedTask = Task.FromResult(true);
|
||||||
|
|
||||||
|
public static void DoNotWait(this Task task)
|
||||||
|
{
|
||||||
|
// Do nothing, let the task continue. Eliminates compiler warning about non-awaited tasks in an async method.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.ServiceModel.Syndication;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord
|
||||||
|
{
|
||||||
|
public static class VersionFeedUtils
|
||||||
|
{
|
||||||
|
public static VersionFeed LoadVersionFeed(string inputUri, string currentVersion)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var reader = XmlReader.Create(inputUri);
|
||||||
|
var feed = SyndicationFeed.Load(reader);
|
||||||
|
|
||||||
|
var versionFeed = new VersionFeed
|
||||||
|
{
|
||||||
|
Id = feed.Id,
|
||||||
|
Title = feed.Title?.Text,
|
||||||
|
SubTitle = feed.Description?.Text,
|
||||||
|
Link = feed.Links?[0].Uri,
|
||||||
|
Updated = feed.LastUpdatedTime.ToLocalTime(),
|
||||||
|
};
|
||||||
|
|
||||||
|
//Loop through all items in the SyndicationFeed
|
||||||
|
foreach (var item in feed.Items)
|
||||||
|
{
|
||||||
|
var textContent = item.Content as TextSyndicationContent;
|
||||||
|
|
||||||
|
var versionFeedEntry = new VersionFeedEntry
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
Title = item.Title?.Text,
|
||||||
|
Summary = item.Summary?.Text,
|
||||||
|
Link = item.Links?[0].Uri,
|
||||||
|
Updated = item.LastUpdatedTime.ToLocalTime(),
|
||||||
|
Content = textContent?.Text,
|
||||||
|
Author = item.Authors?[0].Name,
|
||||||
|
|
||||||
|
IsCurrent = (item.Summary?.Text ?? string.Empty).Equals(currentVersion),
|
||||||
|
};
|
||||||
|
versionFeed.Entries.Add(versionFeedEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionFeed;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return new VersionFeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
292
Plugins/Discord/source/Plugin.Discord/VersionFeed.xml
Normal file
292
Plugins/Discord/source/Plugin.Discord/VersionFeed.xml
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
|
||||||
|
<id>urn:uuid:C93B24FC-C8DB-44F3-8B6F-A27A52E7AF9F</id>
|
||||||
|
<title>Discord Plugin Version Feed</title>
|
||||||
|
<subtitle>This is the Discord Plugin release version feed.</subtitle>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2020-06-12T00:00:01Z</updated>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:A7158CB1-C5B2-4505-B171-CDD54918B227</id>
|
||||||
|
<title>1.0.16 (1.0.16.1)</title>
|
||||||
|
<summary>1.0.16.1</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2020-06-12T00:00:01Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">BUGFIX</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Added url encoding so that special characters are sent to the webhook correctly.</li>
|
||||||
|
<li>Changed the text encoding from ASCII to UTF8, so that unicode languages should work correctly.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:6ADCC571-3933-44E1-91FB-4DC8F8B836F2</id>
|
||||||
|
<title>1.0.15 (1.0.15.1)</title>
|
||||||
|
<summary>1.0.15.1</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2020-03-03T00:00:01Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">CHANGE</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Config Save - nows creates a backup file (.bak) of the config file before the new changes are saved.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:6ADCC571-3933-44E1-91FB-4DC8F8B836F2</id>
|
||||||
|
<title>1.0.14 (1.0.14.2)</title>
|
||||||
|
<summary>1.0.14.2</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-08-16T00:00:01Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">CHANGE</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Download Location Changed - Have moved the server manager files to a new location for hosting.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:6ADCC571-3933-44E1-91FB-4DC8F8B836F2</id>
|
||||||
|
<title>1.0.13 (1.0.13.1)</title>
|
||||||
|
<summary>1.0.13.1</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-06-13T04:40:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">NEW</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Message Options - Added new message options to display the messages in Bold, Italic, Underlined and Embedded Code Block.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:21C68E55-E915-4337-8CFB-7E96FE6967CB</id>
|
||||||
|
<title>1.0.12 (1.0.12.1)</title>
|
||||||
|
<summary>1.0.12.1</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">NEW</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Added version feed window.</li>
|
||||||
|
</ul>
|
||||||
|
<u style="font-size: .9em;">CHANGE</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>DotNet Framework updated.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:C1ED0129-58CF-4AA4-8203-DDFA199E17DE</id>
|
||||||
|
<title>1.0.11 (1.0.11.0)</title>
|
||||||
|
<summary>1.0.11.0</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">CHANGE</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Icons - all icons have be cleaned up and updated.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:58894ADA-FDC4-4F5A-A904-B81D9130FD00</id>
|
||||||
|
<title>1.0.10 (1.0.10.0)</title>
|
||||||
|
<summary>1.0.10.0</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">BUGFIX</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Attempt to fix the save config bug, where the config file is blank.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:20DB658C-44FA-4642-923B-A94926B0FDC3</id>
|
||||||
|
<title>1.0.9 (1.0.9.0)</title>
|
||||||
|
<summary>1.0.9.0</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">BUGFIX</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Some minor bugfixes and code cleanup.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:3B0D107D-77DB-4E70-BE8A-336A20A55F8A</id>
|
||||||
|
<title>1.0.8 (1.0.8.0)</title>
|
||||||
|
<summary>1.0.8.0</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">CHANGE</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Changed the download location of the plugin.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:CDA4857D-FC4A-44AB-A11E-9CC1CDA11BCD</id>
|
||||||
|
<title>1.0.7 (1.0.7.0)</title>
|
||||||
|
<summary>1.0.7.0</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">CHANGE</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Minor code clean-up and tweaks.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:C6CEC6B2-E405-4DF6-B991-FC1D4EEDF3E8</id>
|
||||||
|
<title>1.0.6 (1.0.6.0)</title>
|
||||||
|
<summary>1.0.6.0</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">CHANGE</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Have changed the storage host for the discord plugin.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:9C4A9557-3A74-428F-A22C-727CBBFB6E91</id>
|
||||||
|
<title>1.0.5 (1.0.5.0)</title>
|
||||||
|
<summary>1.0.5.0</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2018-05-24T00:20:00Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">NEW</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>New discord plugin to send Server Manager alerts to one or more channels on your discord server.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
</feed>
|
||||||
34
Plugins/Discord/source/Plugin.Discord/VersionFeedBeta.xml
Normal file
34
Plugins/Discord/source/Plugin.Discord/VersionFeedBeta.xml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
|
||||||
|
<id>urn:uuid:6BB39661-8638-425E-B5F7-0C75AACC1D26</id>
|
||||||
|
<title>Discord Plugin Version Feed</title>
|
||||||
|
<subtitle>This is the Discord Plugin beta version feed.</subtitle>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2020-06-12T00:00:01Z</updated>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<id>urn:uuid:A7158CB1-C5B2-4505-B171-CDD54918B227</id>
|
||||||
|
<title>1.0.16 (1.0.16.1)</title>
|
||||||
|
<summary>1.0.16.1</summary>
|
||||||
|
<link href="" />
|
||||||
|
<updated>2020-06-12T00:00:01Z</updated>
|
||||||
|
<content type="xhtml">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
|
||||||
|
<p>
|
||||||
|
<u style="font-size: .9em;">BUGFIX</u>
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>Added url encoding so that special characters are sent to the webhook correctly.</li>
|
||||||
|
<li>Changed the text encoding from ASCII to UTF8, so that unicode languages should work correctly.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
<author>
|
||||||
|
<name>bletch</name>
|
||||||
|
<email>bletch1971@hotmail.com</email>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
</feed>
|
||||||
|
|
@ -0,0 +1,228 @@
|
||||||
|
<Window x:Class="ServerManagerTool.Plugin.Discord.Windows.ConfigProfileWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||||
|
xmlns:pc="clr-namespace:ServerManagerTool.Plugin.Common;assembly=ServerManager.Plugin.Common"
|
||||||
|
Title="{DynamicResource ConfigProfileWindow_Title}"
|
||||||
|
Icon="/ServerManager.Plugin.Discord;component/Art/favicon.ico"
|
||||||
|
Width="640" Height="520" MinWidth="640" MinHeight="480" ResizeMode="CanResizeWithGrip" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" Closing="ConfigProfileWindow_Closing">
|
||||||
|
<Window.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="/ServerManager.Plugin.Discord;component/Globalization/en-US/en-US.xaml"/>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
|
||||||
|
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
|
||||||
|
<GradientStop Color="#FFECE1D4" Offset="1"/>
|
||||||
|
<GradientStop Color="#FFEAE8E6"/>
|
||||||
|
</LinearGradientBrush>
|
||||||
|
|
||||||
|
<ObjectDataProvider x:Key="AlertTypes" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
|
||||||
|
<ObjectDataProvider.MethodParameters>
|
||||||
|
<x:Type TypeName="pc:AlertType" />
|
||||||
|
</ObjectDataProvider.MethodParameters>
|
||||||
|
</ObjectDataProvider>
|
||||||
|
|
||||||
|
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Grid Background="{StaticResource BeigeGradient}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<DockPanel Margin="5" ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||||
|
<Grid DockPanel.Dock="Top">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" MinWidth="100"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource ConfigProfileWindow_NameLabel}" ToolTip="{DynamicResource ConfigProfileWindow_NameTooltip}"/>
|
||||||
|
<TextBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="1" Text="{Binding Profile.Name}" ToolTip="{DynamicResource ConfigProfileWindow_NameTooltip}" VerticalContentAlignment="Center" />
|
||||||
|
<CheckBox Grid.Row="0" Grid.Column="3" Margin="5,2,5,2" Content="{DynamicResource ConfigProfileWindow_IsEnabledLabel}" IsChecked="{Binding Profile.IsEnabled}" ToolTip="{DynamicResource ConfigProfileWindow_IsEnabledTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource ConfigProfileWindow_WebhookLabel}" ToolTip="{DynamicResource ConfigProfileWindow_WebhookTooltip}"/>
|
||||||
|
<TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="1" Text="{Binding Profile.DiscordWebhookUrl}" ToolTip="{DynamicResource ConfigProfileWindow_WebhookTooltip}" VerticalContentAlignment="Center" />
|
||||||
|
|
||||||
|
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource ConfigProfileWindow_BotNameLabel}" ToolTip="{DynamicResource ConfigProfileWindow_BotNameTooltip}"/>
|
||||||
|
<TextBox Grid.Row="2" Grid.Column="1" Margin="1" Text="{Binding Profile.DiscordBotName}" ToolTip="{DynamicResource ConfigProfileWindow_BotNameTooltip}" VerticalContentAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<GroupBox DockPanel.Dock="Top" HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="{DynamicResource ConfigProfileWindow_MessageOptionsLabel}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</GroupBox.Header>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<CheckBox Grid.Row="0" Grid.Column="0" Margin="0,2,20,2" Content="{DynamicResource ConfigProfileWindow_PrefixMessageWithProfileNameLabel}" IsChecked="{Binding Profile.PrefixMessageWithProfileName}" ToolTip="{DynamicResource ConfigProfileWindow_PrefixMessageWithProfileNameTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
<CheckBox Grid.Row="0" Grid.Column="1" Margin="10,2,20,2" Content="{DynamicResource ConfigProfileWindow_UseTTSLabel}" IsChecked="{Binding Profile.DiscordUseTTS}" ToolTip="{DynamicResource ConfigProfileWindow_UseTTSTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
|
||||||
|
<CheckBox Grid.Row="1" Grid.Column="0" Margin="0,2,20,2" Content="{DynamicResource ConfigProfileWindow_MessageOptionsBoldLabel}" IsChecked="{Binding Profile.MessageBold}" ToolTip="{DynamicResource ConfigProfileWindow_MessageOptionsBoldTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
<CheckBox Grid.Row="1" Grid.Column="1" Margin="10,2,20,2" Content="{DynamicResource ConfigProfileWindow_MessageOptionsUnderlineLabel}" IsChecked="{Binding Profile.MessageUnderlined}" ToolTip="{DynamicResource ConfigProfileWindow_MessageOptionsUnderlineTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
|
||||||
|
<CheckBox Grid.Row="2" Grid.Column="0" Margin="0,2,20,2" Content="{DynamicResource ConfigProfileWindow_MessageOptionsItalicLabel}" IsChecked="{Binding Profile.MessageItalic}" ToolTip="{DynamicResource ConfigProfileWindow_MessageOptionsItalicTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
<CheckBox Grid.Row="2" Grid.Column="1" Margin="10,2,20,2" Content="{DynamicResource ConfigProfileWindow_MessageOptionsCodeBlockLabel}" IsChecked="{Binding Profile.MessageCodeBlock}" ToolTip="{DynamicResource ConfigProfileWindow_MessageOptionsCodeBlockTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<Grid DockPanel.Dock="Bottom" Margin="0,0,10,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Button Grid.Column="0" Content="{DynamicResource ConfigProfileWindow_TestButtonLabel}" Margin="5" MinWidth="75" Click="Test_Click"/>
|
||||||
|
<Button Grid.Column="2" Content="{DynamicResource Global_OkButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Right" Click="Ok_Click"/>
|
||||||
|
<Button Grid.Column="3" Content="{DynamicResource Global_CancelButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" IsCancel="True"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="10"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="{DynamicResource ConfigProfileWindow_ProfileNamesLabel}"/>
|
||||||
|
<Button Width="22" Height="22" Click="AddProfileName_Click" Margin="20,0,0,0" ToolTip="{DynamicResource ConfigProfileWindow_AddProfileNameTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Add.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
<Button Width="22" Height="22" Click="ClearProfileNames_Click" Margin="10,0,0,0" ToolTip="{DynamicResource ConfigProfileWindow_ClearProfileNamesTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Delete.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</GroupBox.Header>
|
||||||
|
|
||||||
|
<DataGrid ItemsSource="{Binding Profile.ProfileNames}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserSortColumns="true" SelectionMode="Single" CanUserResizeColumns="False" CanUserResizeRows="False" RowHeaderWidth="25">
|
||||||
|
<DataGrid.Resources>
|
||||||
|
<Style TargetType="{x:Type DataGridRow}">
|
||||||
|
<Style.Resources>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
|
||||||
|
</Style.Resources>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.Resources>
|
||||||
|
|
||||||
|
<DataGrid.HorizontalGridLinesBrush>
|
||||||
|
<SolidColorBrush Color="#FFB4B4B4"/>
|
||||||
|
</DataGrid.HorizontalGridLinesBrush>
|
||||||
|
<DataGrid.VerticalGridLinesBrush>
|
||||||
|
<SolidColorBrush Color="#FFB4B4B4"/>
|
||||||
|
</DataGrid.VerticalGridLinesBrush>
|
||||||
|
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Width="*" Binding="{Binding Value}">
|
||||||
|
<DataGridTextColumn.Header>
|
||||||
|
<TextBlock Text="{DynamicResource ConfigProfileWindow_ProfileNameColumnLabel}"/>
|
||||||
|
</DataGridTextColumn.Header>
|
||||||
|
</DataGridTextColumn>
|
||||||
|
<DataGridTemplateColumn Width="30" CanUserReorder="False" IsReadOnly="True">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Width="22" Height="22" Margin="0" IsTabStop="False" HorizontalAlignment="Center" VerticalAlignment="Center" Click="DeleteProfileName_Click" ToolTip="{DynamicResource ConfigProfileWindow_DeleteProfileNameTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Delete.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1" Grid.Column="2" HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="{DynamicResource ConfigProfileWindow_AlertTypesLabel}"/>
|
||||||
|
<Button Width="22" Height="22" Click="AddAlertType_Click" Margin="20,0,0,0" ToolTip="{DynamicResource ConfigProfileWindow_AddAlertTypeTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Add.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
<Button Width="22" Height="22" Click="ClearAlertTypes_Click" Margin="10,0,0,0" ToolTip="{DynamicResource ConfigProfileWindow_ClearAlertTypesTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Delete.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</GroupBox.Header>
|
||||||
|
|
||||||
|
<DataGrid ItemsSource="{Binding Profile.AlertTypes}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserSortColumns="true" SelectionMode="Single" CanUserResizeColumns="False" CanUserResizeRows="False" RowHeaderWidth="25">
|
||||||
|
<DataGrid.Resources>
|
||||||
|
<Style TargetType="{x:Type DataGridRow}">
|
||||||
|
<Style.Resources>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
|
||||||
|
</Style.Resources>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.Resources>
|
||||||
|
|
||||||
|
<DataGrid.HorizontalGridLinesBrush>
|
||||||
|
<SolidColorBrush Color="#FFB4B4B4"/>
|
||||||
|
</DataGrid.HorizontalGridLinesBrush>
|
||||||
|
<DataGrid.VerticalGridLinesBrush>
|
||||||
|
<SolidColorBrush Color="#FFB4B4B4"/>
|
||||||
|
</DataGrid.VerticalGridLinesBrush>
|
||||||
|
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTemplateColumn Width="*">
|
||||||
|
<DataGridTemplateColumn.Header>
|
||||||
|
<TextBlock Text="{DynamicResource ConfigProfileWindow_AlertTypeColumnLabel}"/>
|
||||||
|
</DataGridTemplateColumn.Header>
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ComboBox IsEditable="False" ItemsSource="{Binding Source={StaticResource AlertTypes}}" SelectedValue="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" PreviewMouseWheel="ComboBox_PreviewMouseWheel"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTemplateColumn Width="30" CanUserReorder="False" IsReadOnly="True">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Width="22" Height="22" Margin="0" IsTabStop="False" HorizontalAlignment="Center" VerticalAlignment="Center" Click="DeleteAlertType_Click" ToolTip="{DynamicResource ConfigProfileWindow_DeleteAlertTypeTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Delete.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
</Grid>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
using ServerManagerTool.Plugin.Common;
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord.Windows
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for ConfigProfileWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class ConfigProfileWindow : Window
|
||||||
|
{
|
||||||
|
private static readonly DependencyProperty ProfileProperty = DependencyProperty.Register(nameof(Profile), typeof(ConfigProfile), typeof(ConfigProfileWindow));
|
||||||
|
|
||||||
|
internal ConfigProfileWindow(DiscordPlugin plugin, ConfigProfile profile)
|
||||||
|
{
|
||||||
|
this.Plugin = plugin ?? new DiscordPlugin();
|
||||||
|
this.OriginalProfile = profile;
|
||||||
|
this.Profile = profile.Clone();
|
||||||
|
this.Profile.CommitChanges();
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (plugin.BetaEnabled)
|
||||||
|
Title = $"{Title} {ResourceUtils.GetResourceString(this.Resources, "Global_BetaModeLabel")}";
|
||||||
|
|
||||||
|
this.DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigProfile OriginalProfile
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigProfile Profile
|
||||||
|
{
|
||||||
|
get { return GetValue(ProfileProperty) as ConfigProfile; }
|
||||||
|
set { SetValue(ProfileProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private DiscordPlugin Plugin
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigProfileWindow_Closing(object sender, CancelEventArgs e)
|
||||||
|
{
|
||||||
|
if (DialogResult.HasValue && DialogResult.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.Profile.HasAnyChanges)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_CloseLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_CloseTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ComboBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||||
|
{
|
||||||
|
var comboBox = sender as ComboBox;
|
||||||
|
if (comboBox == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (comboBox.IsDropDownOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Ok_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (this.Profile.HasAnyChanges)
|
||||||
|
{
|
||||||
|
this.OriginalProfile.CopyFrom(this.Profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Test_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!Profile.IsEnabled)
|
||||||
|
{
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestEnabledErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var profileName in Profile.ProfileNames)
|
||||||
|
{
|
||||||
|
foreach (var alertType in Profile.AlertTypes)
|
||||||
|
{
|
||||||
|
Plugin.HandleAlert(Profile, alertType.Value, profileName.Value, $"Test '{alertType.Value}' message for profile name '{profileName.Value}'.");
|
||||||
|
Task.Delay(1000).Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(Test_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAlertType_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var alertType = new AlertTypeValue(AlertType.Error);
|
||||||
|
|
||||||
|
Profile.AlertTypes.Add(alertType);
|
||||||
|
Profile.AlertTypes.NotifyAdd(alertType);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(AddAlertType_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddAlertTypeErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearAlertTypes_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Profile.AlertTypes.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Profile.AlertTypes.Clear();
|
||||||
|
Profile.AlertTypes.NotifyClear();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(ClearAlertTypes_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearAlertTypesErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteAlertType_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var alertType = ((AlertTypeValue)((Button)e.Source).DataContext);
|
||||||
|
var index = Profile.AlertTypes.IndexOf(alertType);
|
||||||
|
Profile.AlertTypes.Remove(alertType);
|
||||||
|
Profile.AlertTypes.NotifyRemove(alertType, index);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(DeleteAlertType_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteAlertTypeErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddProfileName_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var profileName = new ProfileNameValue();
|
||||||
|
|
||||||
|
Profile.ProfileNames.Add(profileName);
|
||||||
|
Profile.ProfileNames.NotifyAdd(profileName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(AddProfileName_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddProfileNameErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearProfileNames_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Profile.ProfileNames.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Profile.ProfileNames.Clear();
|
||||||
|
Profile.ProfileNames.NotifyClear();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(ClearProfileNames_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearProfileNamesErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteProfileName_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var profileName = ((ProfileNameValue)((Button)e.Source).DataContext);
|
||||||
|
var index = Profile.ProfileNames.IndexOf(profileName);
|
||||||
|
Profile.ProfileNames.Remove(profileName);
|
||||||
|
Profile.ProfileNames.NotifyRemove(profileName, index);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(DeleteProfileName_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteProfileNameErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
137
Plugins/Discord/source/Plugin.Discord/Windows/ConfigWindow.xaml
Normal file
137
Plugins/Discord/source/Plugin.Discord/Windows/ConfigWindow.xaml
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
<Window x:Class="ServerManagerTool.Plugin.Discord.Windows.ConfigWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:pc="clr-namespace:ServerManagerTool.Plugin.Common;assembly=ServerManager.Plugin.Common"
|
||||||
|
Title="{DynamicResource ConfigWindow_Title}"
|
||||||
|
Icon="/ServerManager.Plugin.Discord;component/Art/favicon.ico"
|
||||||
|
Width="640" Height="480" MinWidth="640" MinHeight="480" ResizeMode="CanResizeWithGrip" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" Closing="ConfigWindow_Closing" Loaded="ConfigWindow_Loaded">
|
||||||
|
<Window.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="/ServerManager.Plugin.Discord;component/Globalization/en-US/en-US.xaml"/>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
|
||||||
|
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
|
||||||
|
<GradientStop Color="#FFECE1D4" Offset="1"/>
|
||||||
|
<GradientStop Color="#FFEAE8E6"/>
|
||||||
|
</LinearGradientBrush>
|
||||||
|
|
||||||
|
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Grid Background="{StaticResource BeigeGradient}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||||
|
<StackPanel.Style>
|
||||||
|
<Style TargetType="{x:Type StackPanel}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding NewVersionAvailable}" Value="False">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Style>
|
||||||
|
|
||||||
|
<TextBlock Text="{DynamicResource Global_NewVersionAvailableLabel}" TextWrapping="Wrap" VerticalAlignment="Center" FontWeight="Bold"/>
|
||||||
|
<Button Width="22" Height="22" Click="DownloadPlugin_Click" Margin="10,0,0,0" ToolTip="{DynamicResource Global_DownloadNewVersionTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Download.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1" HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
|
||||||
|
<Button Width="22" Height="22" Click="PatchNotes_Click" ToolTip="{DynamicResource ConfigWindow_PatchNotesTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/ChangeNotes.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
<Button Width="22" Height="22" Click="AddConfigProfile_Click" Margin="20,0,0,0" ToolTip="{DynamicResource ConfigWindow_AddConfigProfileTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Add.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
<Button Width="22" Height="22" Click="ClearConfigProfiles_Click" Margin="10,0,0,0" ToolTip="{DynamicResource ConfigWindow_ClearConfigProfilesTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Delete.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</GroupBox.Header>
|
||||||
|
|
||||||
|
<DataGrid ItemsSource="{Binding PluginConfig.ConfigProfiles}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserSortColumns="true" SelectionMode="Single" CanUserResizeColumns="False" CanUserResizeRows="False" RowHeaderWidth="25">
|
||||||
|
<DataGrid.Resources>
|
||||||
|
<Style TargetType="{x:Type DataGridRow}">
|
||||||
|
<Style.Resources>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
|
||||||
|
</Style.Resources>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.Resources>
|
||||||
|
|
||||||
|
<DataGrid.HorizontalGridLinesBrush>
|
||||||
|
<SolidColorBrush Color="#FFB4B4B4"/>
|
||||||
|
</DataGrid.HorizontalGridLinesBrush>
|
||||||
|
<DataGrid.VerticalGridLinesBrush>
|
||||||
|
<SolidColorBrush Color="#FFB4B4B4"/>
|
||||||
|
</DataGrid.VerticalGridLinesBrush>
|
||||||
|
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="{x:Type DataGridRow}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
|
||||||
|
<Setter Property="Background" Value="Beige" />
|
||||||
|
<Setter Property="Foreground" Value="Red" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Width="*" Binding="{Binding Name}" IsReadOnly="True">
|
||||||
|
<DataGridTextColumn.Header>
|
||||||
|
<TextBlock Text="{DynamicResource ConfigWindow_NameColumnLabel}"/>
|
||||||
|
</DataGridTextColumn.Header>
|
||||||
|
<DataGridTextColumn.CellStyle>
|
||||||
|
<Style TargetType="{x:Type DataGridCell}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
|
||||||
|
<Setter Property="Foreground" Value="Red" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.CellStyle>
|
||||||
|
</DataGridTextColumn>
|
||||||
|
<DataGridTemplateColumn Width="30" CanUserReorder="False" IsReadOnly="True">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Width="22" Height="22" Margin="0" IsTabStop="False" HorizontalAlignment="Center" VerticalAlignment="Center" Click="EditConfigProfile_Click" ToolTip="{DynamicResource ConfigWindow_EditConfigProfileTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Edit.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTemplateColumn Width="30" CanUserReorder="False" IsReadOnly="True">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Width="22" Height="22" Margin="0" IsTabStop="False" HorizontalAlignment="Center" VerticalAlignment="Center" Click="DeleteConfigProfile_Click" ToolTip="{DynamicResource ConfigWindow_DeleteConfigProfileTooltip}">
|
||||||
|
<Image Source="{pc:Icon Path=/ServerManager.Plugin.Discord;component/Art/Delete.ico,Size=32}" />
|
||||||
|
</Button>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,10,0">
|
||||||
|
<Button Content="{DynamicResource Global_SaveButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Right" Click="Save_Click"/>
|
||||||
|
<Button Content="{DynamicResource Global_CloseButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" IsCancel="True"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
|
@ -0,0 +1,271 @@
|
||||||
|
using ServerManagerTool.Plugin.Common;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord.Windows
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for ConfigWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class ConfigWindow : Window
|
||||||
|
{
|
||||||
|
private static readonly DependencyProperty PluginConfigProperty = DependencyProperty.Register(nameof(PluginConfig), typeof(DiscordPluginConfig), typeof(ConfigWindow));
|
||||||
|
public static readonly DependencyProperty LatestVersionProperty = DependencyProperty.Register(nameof(LatestVersion), typeof(Version), typeof(ConfigWindow), new PropertyMetadata(new Version()));
|
||||||
|
public static readonly DependencyProperty NewVersionAvailableProperty = DependencyProperty.Register(nameof(NewVersionAvailable), typeof(bool), typeof(ConfigWindow), new PropertyMetadata(false));
|
||||||
|
|
||||||
|
internal ConfigWindow(DiscordPlugin plugin, DiscordPluginConfig pluginConfig)
|
||||||
|
{
|
||||||
|
this.Plugin = plugin ?? new DiscordPlugin();
|
||||||
|
this.PluginConfig = pluginConfig ?? new DiscordPluginConfig();
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (plugin.BetaEnabled)
|
||||||
|
Title = $"{Title} {ResourceUtils.GetResourceString(this.Resources, "Global_BetaModeLabel")}";
|
||||||
|
|
||||||
|
this.DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DiscordPlugin Plugin
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DiscordPluginConfig PluginConfig
|
||||||
|
{
|
||||||
|
get { return GetValue(PluginConfigProperty) as DiscordPluginConfig; }
|
||||||
|
set { SetValue(PluginConfigProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version LatestVersion
|
||||||
|
{
|
||||||
|
get { return (Version)GetValue(LatestVersionProperty); }
|
||||||
|
set { SetValue(LatestVersionProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool NewVersionAvailable
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(NewVersionAvailableProperty); }
|
||||||
|
set { SetValue(NewVersionAvailableProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
if (DialogResult.HasValue && DialogResult.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (PluginConfig.HasAnyChanges)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_CloseLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_CloseTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigWindow_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
CheckLatestVersionAsync().DoNotWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DownloadPlugin_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DownloadLatestVersion();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(DownloadPlugin_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DownloadErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DownloadErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddConfigProfile_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var profile = new ConfigProfile();
|
||||||
|
|
||||||
|
if (EditProfile(profile))
|
||||||
|
PluginConfig.ConfigProfiles.Add(profile);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(AddConfigProfile_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_AddErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearConfigProfiles_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_ClearLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_ClearTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (PluginConfig.ConfigProfiles.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PluginConfig.ConfigProfiles.Clear();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(ClearConfigProfiles_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_ClearErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteConfigProfile_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DeleteLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DeleteTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var profile = ((ConfigProfile)((Button)e.Source).DataContext);
|
||||||
|
PluginConfig.ConfigProfiles.Remove(profile);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(DeleteConfigProfile_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DeleteErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditConfigProfile_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var profile = ((ConfigProfile)((Button)e.Source).DataContext);
|
||||||
|
EditProfile(profile);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(EditConfigProfile_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_EditErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_EditErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PatchNotes_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var url = string.Empty;
|
||||||
|
if (Plugin.BetaEnabled)
|
||||||
|
url = Config.Default.VersionBetaFeedUrl;
|
||||||
|
else
|
||||||
|
url = Config.Default.VersionFeedUrl;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(url))
|
||||||
|
{
|
||||||
|
var window = new VersionFeedWindow(Plugin, url);
|
||||||
|
window.Owner = this;
|
||||||
|
window.ShowDialog();
|
||||||
|
this.BringIntoView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Save_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BackupExistingConfig();
|
||||||
|
SaveConfig();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(Save_Click)}\r\n{ex.Message}");
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_SaveErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_SaveErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BackupExistingConfig()
|
||||||
|
{
|
||||||
|
var configFile = Path.Combine(PluginHelper.PluginFolder, Config.Default.ConfigFile);
|
||||||
|
if (!File.Exists(configFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var backupFile = Path.ChangeExtension(configFile, "bak");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Copy(configFile, backupFile, true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// do nothing, just exit if cannot backup existing config file
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckLatestVersionAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var newVersion = await NetworkUtils.CheckLatestVersionAsync(Plugin.BetaEnabled);
|
||||||
|
|
||||||
|
this.LatestVersion = newVersion;
|
||||||
|
this.NewVersionAvailable = Plugin.PluginVersion < newVersion;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(CheckLatestVersionAsync)}\r\n{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DownloadLatestVersion()
|
||||||
|
{
|
||||||
|
var cursor = this.Cursor;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.Cursor = Cursors.Wait;
|
||||||
|
Task.Delay(500).Wait();
|
||||||
|
|
||||||
|
var latestZip = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), Config.Default.PluginZipFilename);
|
||||||
|
|
||||||
|
var sourceUrl = string.Empty;
|
||||||
|
if (Plugin.BetaEnabled)
|
||||||
|
sourceUrl = Config.Default.LatestBetaDownloadUrl;
|
||||||
|
else
|
||||||
|
sourceUrl = Config.Default.LatestDownloadUrl;
|
||||||
|
|
||||||
|
NetworkUtils.DownloadLatestVersion(sourceUrl, latestZip);
|
||||||
|
|
||||||
|
MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DownloadSuccessLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigWindow_DownloadSuccessTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: {nameof(DownloadLatestVersion)}\r\n{ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.Cursor = cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EditProfile(ConfigProfile profile)
|
||||||
|
{
|
||||||
|
if (profile == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var window = new ConfigProfileWindow(Plugin, profile);
|
||||||
|
window.Owner = this;
|
||||||
|
|
||||||
|
var dialogResult = window.ShowDialog();
|
||||||
|
this.BringIntoView();
|
||||||
|
|
||||||
|
return dialogResult.HasValue && dialogResult.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveConfig()
|
||||||
|
{
|
||||||
|
var configFile = Path.Combine(PluginHelper.PluginFolder, Config.Default.ConfigFile);
|
||||||
|
JsonUtils.SerializeToFile(PluginConfig, configFile);
|
||||||
|
PluginConfig?.CommitChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
<Window x:Class="ServerManagerTool.Plugin.Discord.Windows.VersionFeedWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:ServerManagerTool.Plugin.Discord"
|
||||||
|
xmlns:pc="clr-namespace:ServerManagerTool.Plugin.Common;assembly=ServerManager.Plugin.Common"
|
||||||
|
MinWidth="400" MinHeight="400" Width="640" Height="480" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" ResizeMode="CanResizeWithGrip"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
Icon="../Art/favicon.ico" Title="{DynamicResource VersionFeedWindow_Title}">
|
||||||
|
<Window.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="/ServerManager.Plugin.Discord;component/Globalization/en-US/en-US.xaml"/>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
|
<Style x:Key="Version" TargetType="Label">
|
||||||
|
<Setter Property="Content" Value="{Binding Title}"/>
|
||||||
|
<Setter Property="FontWeight" Value="Normal"/>
|
||||||
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsCurrent}" Value="True">
|
||||||
|
<Setter Property="FontWeight" Value="Bold"/>
|
||||||
|
<Setter Property="Foreground" Value="#0066CC"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Grid Background="{DynamicResource GradientBackground}">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Label Grid.Row="0" Grid.Column="0" Margin="5,5,0,0" Content="{DynamicResource VersionFeedWindow_VersionFilterLabel}"/>
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="5,5,5,0" ItemsSource="{Binding FeedEntries}" SelectedValue="{Binding SelectedFeedEntry}" ToolTip="{DynamicResource VersionFeedWindow_VersionFilterTooltip}">
|
||||||
|
<ComboBox.ItemContainerStyle>
|
||||||
|
<Style TargetType="{x:Type ComboBoxItem}" >
|
||||||
|
<Setter Property="Height" Value="20" />
|
||||||
|
</Style>
|
||||||
|
</ComboBox.ItemContainerStyle>
|
||||||
|
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type local:VersionFeedEntry}">
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||||
|
<Label Padding="0,-1,0,-1" VerticalAlignment="Center" Style="{DynamicResource Version}"/>
|
||||||
|
<TextBlock Text="{Binding Updated, StringFormat= - {0:G}}" Margin="5,0,0,0" Padding="0,-1,0,-1" VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<WebBrowser Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="5" local:BrowserBehavior.Html="{Binding SelectedFeedEntry.Content}"/>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
using ServerManagerTool.Plugin.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ServerManagerTool.Plugin.Discord.Windows
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for VersionFeedWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class VersionFeedWindow : Window
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty FeedEntriesProperty = DependencyProperty.Register(nameof(FeedEntries), typeof(ObservableCollection<VersionFeedEntry>), typeof(VersionFeedWindow), new PropertyMetadata(new ObservableCollection<VersionFeedEntry>()));
|
||||||
|
public static readonly DependencyProperty SelectedFeedEntryProperty = DependencyProperty.Register(nameof(SelectedFeedEntry), typeof(VersionFeedEntry), typeof(VersionFeedWindow), new PropertyMetadata(null));
|
||||||
|
|
||||||
|
private string feedUri = string.Empty;
|
||||||
|
|
||||||
|
public VersionFeedWindow(DiscordPlugin plugin, string feedUri)
|
||||||
|
{
|
||||||
|
this.Plugin = plugin ?? new DiscordPlugin();
|
||||||
|
this.feedUri = feedUri;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DiscordPlugin Plugin
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<VersionFeedEntry> FeedEntries
|
||||||
|
{
|
||||||
|
get { return (ObservableCollection<VersionFeedEntry>)GetValue(FeedEntriesProperty); }
|
||||||
|
set { SetValue(FeedEntriesProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionFeedEntry SelectedFeedEntry
|
||||||
|
{
|
||||||
|
get { return (VersionFeedEntry)GetValue(SelectedFeedEntryProperty); }
|
||||||
|
set { SetValue(SelectedFeedEntryProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadFeed();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show(ex.Message, ResourceUtils.GetResourceString(this.Resources, "VersionFeedWindow_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFeed()
|
||||||
|
{
|
||||||
|
FeedEntries.Clear();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(this.feedUri))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var versionFeed = VersionFeedUtils.LoadVersionFeed(this.feedUri, Plugin.PluginVersion.ToString());
|
||||||
|
if (versionFeed == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var entry in versionFeed.Entries)
|
||||||
|
{
|
||||||
|
if (entry == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
FeedEntries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedFeedEntry = FeedEntries.OrderByDescending(e => e.Updated).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Plugins/Discord/source/Plugin.Discord/app.config
Normal file
54
Plugins/Discord/source/Plugin.Discord/app.config
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<section name="ServerManagerTool.Plugin.Discord.Config" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
|
||||||
|
</sectionGroup>
|
||||||
|
</configSections>
|
||||||
|
<applicationSettings>
|
||||||
|
<ServerManagerTool.Plugin.Discord.Config>
|
||||||
|
<setting name="PluginCallUrlFormat" serializeAs="String">
|
||||||
|
<value>http://servermanager.azurewebsites.net/api/plugin/call/{0}/{1}/</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="PublicIPCheckUrl" serializeAs="String">
|
||||||
|
<value>http://whatismyip.akamai.com/</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="CallHomeDelay" serializeAs="String">
|
||||||
|
<value>12</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="RequestTimeout" serializeAs="String">
|
||||||
|
<value>5000</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="PluginCode" serializeAs="String">
|
||||||
|
<value>1B745000-6389-4770-9509-C6A05E209323</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="PluginName" serializeAs="String">
|
||||||
|
<value>Discord Plugin</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="LatestDownloadUrl" serializeAs="String">
|
||||||
|
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/latest.zip</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="LatestVersionUrl" serializeAs="String">
|
||||||
|
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/latest.txt</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="LatestBetaDownloadUrl" serializeAs="String">
|
||||||
|
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/latest.zip</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="LatestBetaVersionUrl" serializeAs="String">
|
||||||
|
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/latest.txt</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="PluginZipFilename" serializeAs="String">
|
||||||
|
<value>ServerManager.Plugin.Discord.zip</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="ConfigFile" serializeAs="String">
|
||||||
|
<value>_discordplugin.cfg</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="VersionFeedUrl" serializeAs="String">
|
||||||
|
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/VersionFeed.xml</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="VersionBetaFeedUrl" serializeAs="String">
|
||||||
|
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/VersionFeed.xml</value>
|
||||||
|
</setting>
|
||||||
|
</ServerManagerTool.Plugin.Discord.Config>
|
||||||
|
</applicationSettings>
|
||||||
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
|
||||||
43
Plugins/Discord/source/ServerManagerTool.Plugins.sln
Normal file
43
Plugins/Discord/source/ServerManagerTool.Plugins.sln
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30225.117
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.Common", "Plugin.Common\Plugin.Common.csproj", "{FCFB17CF-BFE4-49AC-AB04-4990CACE6756}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.Discord", "Plugin.Discord\Plugin.Discord.csproj", "{ADD31DE3-6EFE-4BAA-932F-66E59F590E02}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{FCFB17CF-BFE4-49AC-AB04-4990CACE6756}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FCFB17CF-BFE4-49AC-AB04-4990CACE6756}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FCFB17CF-BFE4-49AC-AB04-4990CACE6756}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FCFB17CF-BFE4-49AC-AB04-4990CACE6756}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{ADD31DE3-6EFE-4BAA-932F-66E59F590E02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{ADD31DE3-6EFE-4BAA-932F-66E59F590E02}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{ADD31DE3-6EFE-4BAA-932F-66E59F590E02}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{ADD31DE3-6EFE-4BAA-932F-66E59F590E02}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {9AB9121A-35E6-486F-9096-E2F08AD9CF18}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(TeamFoundationVersionControl) = preSolution
|
||||||
|
SccNumberOfProjects = 3
|
||||||
|
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
|
||||||
|
SccTeamFoundationServer = https://dev.azure.com/bretthewitson
|
||||||
|
SccLocalPath0 = .
|
||||||
|
SccProjectUniqueName1 = Plugin.Common\\Plugin.Common.csproj
|
||||||
|
SccProjectName1 = Plugin.Common
|
||||||
|
SccLocalPath1 = Plugin.Common
|
||||||
|
SccProjectUniqueName2 = Plugin.Discord\\Plugin.Discord.csproj
|
||||||
|
SccProjectName2 = Plugin.Discord
|
||||||
|
SccLocalPath2 = Plugin.Discord
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
Loading…
Add table
Add a link
Reference in a new issue