mirror of
https://github.com/tribufu/ServerManagers
synced 2026-05-06 15:17:34 +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