diff --git a/.gitignore b/.gitignore index 24c53ce4..a56597c8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ Plex/settings.json Emby/settings.invalid.json Emby/settings.json VideoNodes/test.settings.dev.json +Apprise/settings.json diff --git a/Apprise/Apprise.csproj b/Apprise/Apprise.csproj new file mode 100644 index 00000000..3939804f Binary files /dev/null and b/Apprise/Apprise.csproj differ diff --git a/Apprise/Apprise.en.json b/Apprise/Apprise.en.json new file mode 100644 index 00000000..9b1a080e --- /dev/null +++ b/Apprise/Apprise.en.json @@ -0,0 +1,34 @@ +{ + "Plugins": { + "Apprise": { + "Description": "A plugin that allows you to send messages to a Apprise server.", + "Fields": { + "ServerUrl": "Server", + "ServerUrl-Placeholder": "http://apprise.lan", + "ServerUrl-Help": "The URL of the Apprise server", + "Endpoint": "Endpoint", + "Endpoint-Placeholder": "notify/apprise", + "Endpoint-Help": "The endpoint on the Apprise server, for example notify/apprise" + } + } + }, + "Flow": { + "Parts": { + "Apprise": { + "Outputs": { + "1": "Apprise message sent", + "2": "Apprise message failed to send" + }, + "Description": "Sends a message to a Apprise server.", + "Fields": { + "Message": "Message", + "Message-Help": "The message to send to the Apprise server", + "Tag": "Tag", + "Tag-Help": "A list of tags to send this message as, if empty it will be tagged with ''all''.", + "MessageType": "Type", + "MessageType-Help": "The type of message to be sent" + } + } + } + } +} \ No newline at end of file diff --git a/Apprise/Communication/Apprise.cs b/Apprise/Communication/Apprise.cs new file mode 100644 index 00000000..ab6ba821 --- /dev/null +++ b/Apprise/Communication/Apprise.cs @@ -0,0 +1,92 @@ +using System.ComponentModel; +using System.Text.Json; + +namespace FileFlows.Apprise.Communication; + +public class Apprise: Node +{ + public override int Inputs => 1; + public override int Outputs => 2; + public override FlowElementType Type => FlowElementType.Communication; + public override string Icon => "fas fa-bell"; + public override bool FailureNode => true; + + [Required] + [TextVariable(1)] + public string Message { get; set; } + + [StringArray(2)] + public string[] Tag { get; set; } + + [DefaultValue("info")] + [Select(nameof(MessageTypeOptions), 3)] + public string MessageType { get; set; } + + private static List _MessageTypeOptions; + public static List MessageTypeOptions + { + get + { + if (_MessageTypeOptions == null) + { + _MessageTypeOptions = new List + { + new ListOption { Label = "Information", Value = "info"}, + new ListOption { Label = "Success", Value = "success"}, + new ListOption { Label = "Warning", Value = "warning" }, + new ListOption { Label = "Failure", Value = "failure"} + }; + } + return _MessageTypeOptions; + } + } + + public override int Execute(NodeParameters args) + { + var settings = args.GetPluginSettings(); + + if (string.IsNullOrWhiteSpace(settings?.Endpoint)) + { + args.Logger?.WLog("No endpoint set"); + return 2; + } + if (string.IsNullOrWhiteSpace(settings?.ServerUrl)) + { + args.Logger?.WLog("No server URL set"); + return 2; + } + + string url = settings.ServerUrl; + if (url.EndsWith("/") == false) + url += "/"; + if (settings.Endpoint.EndsWith("/")) + url += settings.Endpoint[1..]; + else + url += settings.Endpoint; + + string message = args.ReplaceVariables(this.Message); + if (string.IsNullOrWhiteSpace(message)) + { + args.Logger?.WLog("No message to send"); + return 2; + } + + object data = new + { + body = message, + tag= Tag?.Any() != true ? "all" : String.Join(";", this.Tag), + type = this.MessageType?.EmptyAsNull() ?? "info" + }; + + var content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json"); + + using var httpClient = new HttpClient(); + var response = httpClient.PostAsync(url, content).Result; + if (response.IsSuccessStatusCode) + return 1; + + string error = response.Content.ReadAsStringAsync().Result; + args.Logger?.WLog("Error from Apprise: " + error); + return 2; + } +} diff --git a/Apprise/ExtensionMethods.cs b/Apprise/ExtensionMethods.cs new file mode 100644 index 00000000..589ccdf7 --- /dev/null +++ b/Apprise/ExtensionMethods.cs @@ -0,0 +1,6 @@ +namespace FileFlows.Apprise; + +internal static class ExtensionMethods +{ + public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; +} diff --git a/Apprise/GlobalUsings.cs b/Apprise/GlobalUsings.cs new file mode 100644 index 00000000..9c301eba --- /dev/null +++ b/Apprise/GlobalUsings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Text; +global using System.ComponentModel.DataAnnotations; +global using FileFlows.Plugin; +global using FileFlows.Plugin.Attributes; \ No newline at end of file diff --git a/Apprise/Plugin.cs b/Apprise/Plugin.cs new file mode 100644 index 00000000..f12c5128 --- /dev/null +++ b/Apprise/Plugin.cs @@ -0,0 +1,11 @@ +namespace FileFlows.Apprise; + +public class Plugin : FileFlows.Plugin.IPlugin +{ + public string Name => "Apprise Nodes"; + public string MinimumVersion => "0.5.2.690"; + + public void Init() + { + } +} diff --git a/Apprise/PluginSettings.cs b/Apprise/PluginSettings.cs new file mode 100644 index 00000000..19eba52e --- /dev/null +++ b/Apprise/PluginSettings.cs @@ -0,0 +1,18 @@ +namespace FileFlows.Apprise +{ + using FileFlows.Plugin; + using FileFlows.Plugin.Attributes; + using System; + using System.ComponentModel.DataAnnotations; + + public class PluginSettings:IPluginSettings + { + [Text(1)] + [Required] + public string ServerUrl { get; set; } + + [Text(2)] + [Required] + public string Endpoint { get; set; } + } +} diff --git a/Apprise/Tests/AppriseTests.cs b/Apprise/Tests/AppriseTests.cs new file mode 100644 index 00000000..aedc91b9 --- /dev/null +++ b/Apprise/Tests/AppriseTests.cs @@ -0,0 +1,56 @@ +#if(DEBUG) + +using FileFlows.Apprise.Communication; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace FileFlows.Apprise.Tests; + +[TestClass] +public class AppriseTests +{ + [TestMethod] + public void Apprise_Basic_All() + { + var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty); + args.GetPluginSettingsJson = (string input) => + { + return File.ReadAllText("../../../settings.json"); + }; + + var node = new FileFlows.Apprise.Communication.Apprise(); + node.Message = "a message"; + Assert.AreEqual(1, node.Execute(args)); + } + + [TestMethod] + public void Apprise_Basic_Valid() + { + var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty); + args.GetPluginSettingsJson = (string input) => + { + return File.ReadAllText("../../../settings.json"); + }; + + var node = new FileFlows.Apprise.Communication.Apprise(); + node.Message = "a message"; + node.Tag = new[] { "test" }; + Assert.AreEqual(1, node.Execute(args)); + } + + [TestMethod] + public void Apprise_Basic_Invalid() + { + var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty); + args.GetPluginSettingsJson = (string input) => + { + return File.ReadAllText("../../../settings.json"); + }; + + var node = new FileFlows.Apprise.Communication.Apprise(); + node.Message = "a message"; + node.Tag = new[] { "invalid" }; + Assert.AreEqual(2, node.Execute(args)); + } +} + +#endif \ No newline at end of file diff --git a/Apprise/Tests/TestLogger.cs b/Apprise/Tests/TestLogger.cs new file mode 100644 index 00000000..e0ed1a17 --- /dev/null +++ b/Apprise/Tests/TestLogger.cs @@ -0,0 +1,59 @@ +#if(DEBUG) + +namespace FileFlows.Apprise.Tests; + +using FileFlows.Plugin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +internal class TestLogger : ILogger +{ + private List Messages = new List(); + + public void DLog(params object[] args) => Log("DBUG", args); + + public void ELog(params object[] args) => Log("ERRR", args); + + public void ILog(params object[] args) => Log("INFO", args); + + public void WLog(params object[] args) => Log("WARN", args); + private void Log(string type, object[] args) + { + if (args == null || args.Length == 0) + return; + string message = type + " -> " + + string.Join(", ", args.Select(x => + x == null ? "null" : + x.GetType().IsPrimitive || x is string ? x.ToString() : + System.Text.Json.JsonSerializer.Serialize(x))); + Messages.Add(message); + } + + public bool Contains(string message) + { + if (string.IsNullOrWhiteSpace(message)) + return false; + + string log = string.Join(Environment.NewLine, Messages); + return log.Contains(message); + } + + public override string ToString() + { + return String.Join(Environment.NewLine, this.Messages.ToArray()); + } + + public string GetTail(int length = 50) + { + if (length <= 0) + length = 50; + if (Messages.Count <= length) + return string.Join(Environment.NewLine, Messages); + return string.Join(Environment.NewLine, Messages.TakeLast(length)); + } +} + +#endif \ No newline at end of file diff --git a/BasicNodes/BasicNodes.csproj b/BasicNodes/BasicNodes.csproj index 030305f6..116c87a6 100644 Binary files a/BasicNodes/BasicNodes.csproj and b/BasicNodes/BasicNodes.csproj differ diff --git a/ChecksumNodes/ChecksumNodes.csproj b/ChecksumNodes/ChecksumNodes.csproj index 04d76dc8..1d99970a 100644 Binary files a/ChecksumNodes/ChecksumNodes.csproj and b/ChecksumNodes/ChecksumNodes.csproj differ diff --git a/CollectionNodes/CollectionNodes.csproj b/CollectionNodes/CollectionNodes.csproj index 27d22a01..5627a2e9 100644 Binary files a/CollectionNodes/CollectionNodes.csproj and b/CollectionNodes/CollectionNodes.csproj differ diff --git a/DiscordNodes/DiscordNodes.csproj b/DiscordNodes/DiscordNodes.csproj index 16a6afdd..5e10e0fc 100644 Binary files a/DiscordNodes/DiscordNodes.csproj and b/DiscordNodes/DiscordNodes.csproj differ diff --git a/EmailNodes/EmailNodes.csproj b/EmailNodes/EmailNodes.csproj index 820e631d..83df4e63 100644 Binary files a/EmailNodes/EmailNodes.csproj and b/EmailNodes/EmailNodes.csproj differ diff --git a/Emby/Emby.csproj b/Emby/Emby.csproj index bd30f7ff..a36fa260 100644 Binary files a/Emby/Emby.csproj and b/Emby/Emby.csproj differ diff --git a/FileFlowsPlugins.sln b/FileFlowsPlugins.sln index 050da2a3..4d126a06 100644 --- a/FileFlowsPlugins.sln +++ b/FileFlowsPlugins.sln @@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plex", "Plex\Plex.csproj", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby", "Emby\Emby.csproj", "{C73A4E1F-E1CD-4803-BE6D-886DCBE18727}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Apprise", "Apprise\Apprise.csproj", "{CA750701-C4CF-482F-B5F3-A40E188F3E14}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,6 +77,10 @@ Global {C73A4E1F-E1CD-4803-BE6D-886DCBE18727}.Debug|Any CPU.Build.0 = Debug|Any CPU {C73A4E1F-E1CD-4803-BE6D-886DCBE18727}.Release|Any CPU.ActiveCfg = Release|Any CPU {C73A4E1F-E1CD-4803-BE6D-886DCBE18727}.Release|Any CPU.Build.0 = Release|Any CPU + {CA750701-C4CF-482F-B5F3-A40E188F3E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA750701-C4CF-482F-B5F3-A40E188F3E14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA750701-C4CF-482F-B5F3-A40E188F3E14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA750701-C4CF-482F-B5F3-A40E188F3E14}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Gotify/Gotify.csproj b/Gotify/Gotify.csproj index e80345ec..9d914b67 100644 Binary files a/Gotify/Gotify.csproj and b/Gotify/Gotify.csproj differ diff --git a/MetaNodes/MetaNodes.csproj b/MetaNodes/MetaNodes.csproj index b7d5b6bb..65c863fa 100644 Binary files a/MetaNodes/MetaNodes.csproj and b/MetaNodes/MetaNodes.csproj differ diff --git a/MusicNodes/MusicNodes.csproj b/MusicNodes/MusicNodes.csproj index dc765482..4434cbc9 100644 Binary files a/MusicNodes/MusicNodes.csproj and b/MusicNodes/MusicNodes.csproj differ diff --git a/Plex/Plex.csproj b/Plex/Plex.csproj index 9b0f99cf..7fdb8613 100644 Binary files a/Plex/Plex.csproj and b/Plex/Plex.csproj differ diff --git a/VideoNodes/VideoNodes.csproj b/VideoNodes/VideoNodes.csproj index bf57204e..b656063c 100644 Binary files a/VideoNodes/VideoNodes.csproj and b/VideoNodes/VideoNodes.csproj differ diff --git a/build/utils/PluginInfoGenerator/FileFlows.ServerShared.dll b/build/utils/PluginInfoGenerator/FileFlows.ServerShared.dll index c6683349..853263af 100644 Binary files a/build/utils/PluginInfoGenerator/FileFlows.ServerShared.dll and b/build/utils/PluginInfoGenerator/FileFlows.ServerShared.dll differ diff --git a/build/utils/PluginInfoGenerator/FileFlows.ServerShared.pdb b/build/utils/PluginInfoGenerator/FileFlows.ServerShared.pdb index d7ffb30b..f603d988 100644 Binary files a/build/utils/PluginInfoGenerator/FileFlows.ServerShared.pdb and b/build/utils/PluginInfoGenerator/FileFlows.ServerShared.pdb differ diff --git a/build/utils/PluginInfoGenerator/FileFlows.Shared.dll b/build/utils/PluginInfoGenerator/FileFlows.Shared.dll index f04c8977..186a6f83 100644 Binary files a/build/utils/PluginInfoGenerator/FileFlows.Shared.dll and b/build/utils/PluginInfoGenerator/FileFlows.Shared.dll differ diff --git a/build/utils/PluginInfoGenerator/FileFlows.Shared.pdb b/build/utils/PluginInfoGenerator/FileFlows.Shared.pdb index e46e35aa..3ea68a7a 100644 Binary files a/build/utils/PluginInfoGenerator/FileFlows.Shared.pdb and b/build/utils/PluginInfoGenerator/FileFlows.Shared.pdb differ