diff --git a/FileFlowsPlugins.sln b/FileFlowsPlugins.sln index 3be1c211..2a12a945 100644 --- a/FileFlowsPlugins.sln +++ b/FileFlowsPlugins.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AudioNodes", "AudioNodes\Au EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComicNodes", "ComicNodes\ComicNodes.csproj", "{45568FCB-00FF-4AEA-AA43-DC569D667B04}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telegram", "Telegram\Telegram.csproj", "{BFAD621E-4C82-47FC-A9C8-03F09FF9926C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,6 +89,10 @@ Global {45568FCB-00FF-4AEA-AA43-DC569D667B04}.Debug|Any CPU.Build.0 = Debug|Any CPU {45568FCB-00FF-4AEA-AA43-DC569D667B04}.Release|Any CPU.ActiveCfg = Release|Any CPU {45568FCB-00FF-4AEA-AA43-DC569D667B04}.Release|Any CPU.Build.0 = Release|Any CPU + {BFAD621E-4C82-47FC-A9C8-03F09FF9926C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFAD621E-4C82-47FC-A9C8-03F09FF9926C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFAD621E-4C82-47FC-A9C8-03F09FF9926C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFAD621E-4C82-47FC-A9C8-03F09FF9926C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Gotify/Communication/Gotify.cs b/Gotify/Communication/Gotify.cs index 3facebb6..7e2485e1 100644 --- a/Gotify/Communication/Gotify.cs +++ b/Gotify/Communication/Gotify.cs @@ -3,26 +3,58 @@ using System.Text.Json; namespace FileFlows.Gotify.Communication; +/// +/// A Gotify flow element that sends a message +/// public class Gotify: Node { + /// + /// Gets the number of inputs to this flow element + /// public override int Inputs => 1; + /// + /// Gets the number of outputs to this flow element + /// public override int Outputs => 2; - public override FlowElementType Type => FlowElementType.Communication; + /// + /// Gets the type of flow element + /// + public override FlowElementType Type => FlowElementType.Communication; + /// + /// Gets the icon for this flow element + /// public override string Icon => "fas fa-bell"; + /// + /// Gets if this can be used in a failure flow + /// public override bool FailureNode => true; + /// + /// Gets or sets the message to send + /// [Required] [TextVariable(1)] public string Message { get; set; } - + + /// + /// Gets or sets the title of the message + /// [TextVariable(2)] public string Title { get; set; } + /// + /// Gets or sets the priority of the message + /// [NumberInt(3)] [Range(1, 100)] [DefaultValue(2)] public int Priority { get; set; } = 2; + /// + /// Executes the flow element + /// + /// the node parameters + /// the output to call next public override int Execute(NodeParameters args) { try diff --git a/Gotify/ExtensionMethods.cs b/Gotify/ExtensionMethods.cs index 6177cfcc..584c0978 100644 --- a/Gotify/ExtensionMethods.cs +++ b/Gotify/ExtensionMethods.cs @@ -1,6 +1,14 @@ namespace FileFlows.Gotify; +/// +/// Extension methods +/// internal static class ExtensionMethods { + /// + /// Returns an empty string as null, otherwise returns the original string + /// + /// the input string + /// the string or null if empty public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; } diff --git a/Gotify/Plugin.cs b/Gotify/Plugin.cs index 734125d5..c35d2be3 100644 --- a/Gotify/Plugin.cs +++ b/Gotify/Plugin.cs @@ -1,11 +1,28 @@ namespace FileFlows.Gotify; +/// +/// A Gotify Plugin +/// public class Plugin : FileFlows.Plugin.IPlugin { + /// + /// Gets the UID for this plugin + /// public Guid Uid => new Guid("3d8e13f2-819f-437f-b177-be40147c6e2b"); + + /// + /// Gets the name of this plugin + /// public string Name => "Gotify Nodes"; + + /// + /// Gets the minimum version of FileFlows required for this plugin + /// public string MinimumVersion => "1.0.4.2019"; + /// + /// Initializes this plugin + /// public void Init() { } diff --git a/Gotify/PluginSettings.cs b/Gotify/PluginSettings.cs index 825558be..73d8a105 100644 --- a/Gotify/PluginSettings.cs +++ b/Gotify/PluginSettings.cs @@ -1,18 +1,21 @@ -namespace FileFlows.Gotify +namespace FileFlows.Gotify; + +/// +/// The plugin settings for this plugin +/// +public class PluginSettings : IPluginSettings { - using FileFlows.Plugin; - using FileFlows.Plugin.Attributes; - using System; - using System.ComponentModel.DataAnnotations; + /// + /// Gets or sets the URL to the server to send messages to + /// + [Text(1)] + [Required] + public string ServerUrl { get; set; } - public class PluginSettings:IPluginSettings - { - [Text(1)] - [Required] - public string ServerUrl { get; set; } - - [Text(2)] - [Required] - public string AccessToken { get; set; } - } + /// + /// Gets or sets the Access Token for the server + /// + [Text(2)] + [Required] + public string AccessToken { get; set; } } diff --git a/Telegram/Communication/Telegram.cs b/Telegram/Communication/Telegram.cs new file mode 100644 index 00000000..9b13c94b --- /dev/null +++ b/Telegram/Communication/Telegram.cs @@ -0,0 +1,101 @@ +namespace FileFlows.Telegram.Communication; + +/// +/// A Telegram flow element that sends a message +/// +public class Telegram: Node +{ + /// + /// Gets the number of inputs to this flow element + /// + public override int Inputs => 1; + /// + /// Gets the number of outputs to this flow element + /// + public override int Outputs => 2; + /// + /// Gets the type of flow element + /// + public override FlowElementType Type => FlowElementType.Communication; + /// + /// Gets the icon for this flow element + /// + public override string Icon => "fas fa-bell"; + /// + /// Gets if this can be used in a failure flow + /// + public override bool FailureNode => true; + + /// + /// Gets or sets the message to send + /// + [Required] + [TextVariable(1)] + public string Message { get; set; } + + /// + /// Sends a telegram message + /// + /// the bot token + /// the chat id + /// the message to send + /// true if successful, otherwise false + internal static bool SendMessage(string botToken, string chatId, string message) + { + using (HttpClient client = new HttpClient()) + { + string apiUrl = $"https://api.telegram.org/bot{botToken}/sendMessage"; + + var content = new FormUrlEncodedContent(new[] + { + new KeyValuePair("chat_id", chatId.ToString()), + new KeyValuePair("text", message) + }); + + var response = client.PostAsync(apiUrl, content).Result; + return response.IsSuccessStatusCode; + } + } + + /// + /// Executes the flow element + /// + /// the node parameters + /// the output to call next + public override int Execute(NodeParameters args) + { + try + { + var settings = args.GetPluginSettings(); + + if (settings == null) + { + args.Logger?.ILog("Failed to load plugin settings"); + return 2; + } + + if (string.IsNullOrWhiteSpace(settings?.BotToken)) + { + args.Logger?.WLog("No Bot Token set"); + return 2; + } + + if (string.IsNullOrWhiteSpace(settings?.ChatId)) + { + args.Logger?.WLog("No Chat ID set"); + return 2; + } + + var message = args.ReplaceVariables(Message); + + var result = SendMessage(settings.BotToken, settings.ChatId, message); + + return result ? 1 : 2; + } + catch (Exception ex) + { + args.Logger?.WLog("Error sending message: " + ex.Message); + return 2; + } + } +} diff --git a/Telegram/ExtensionMethods.cs b/Telegram/ExtensionMethods.cs new file mode 100644 index 00000000..af5a3c9f --- /dev/null +++ b/Telegram/ExtensionMethods.cs @@ -0,0 +1,14 @@ +namespace FileFlows.Telegram; + +/// +/// Extension methods +/// +internal static class ExtensionMethods +{ + /// + /// Returns an empty string as null, otherwise returns the original string + /// + /// the input string + /// the string or null if empty + public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; +} diff --git a/Telegram/GlobalUsings.cs b/Telegram/GlobalUsings.cs new file mode 100644 index 00000000..9c301eba --- /dev/null +++ b/Telegram/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/Telegram/Plugin.cs b/Telegram/Plugin.cs new file mode 100644 index 00000000..355f9264 --- /dev/null +++ b/Telegram/Plugin.cs @@ -0,0 +1,29 @@ +namespace FileFlows.Telegram; + +/// +/// A Telegram Plugin +/// +public class Plugin : FileFlows.Plugin.IPlugin +{ + /// + /// Gets the UID for this plugin + /// + public Guid Uid => new Guid("a610837d-c6d6-438b-8470-33a407ea7c98"); + + /// + /// Gets the name of this plugin + /// + public string Name => "Telegram"; + + /// + /// Gets the minimum version of FileFlows required for this plugin + /// + public string MinimumVersion => "1.0.4.2019"; + + /// + /// Initializes this plugin + /// + public void Init() + { + } +} diff --git a/Telegram/PluginSettings.cs b/Telegram/PluginSettings.cs new file mode 100644 index 00000000..930d3f34 --- /dev/null +++ b/Telegram/PluginSettings.cs @@ -0,0 +1,21 @@ +namespace FileFlows.Telegram; + +/// +/// The plugin settings for this plugin +/// +public class PluginSettings : IPluginSettings +{ + /// + /// Gets or sets the bot token + /// + [Text(1)] + [Required] + public string BotToken { get; set; } + + /// + /// Gets or sets the chat ID + /// + [Text(1)] + [Required] + public string ChatId { get; set; } +} \ No newline at end of file diff --git a/Telegram/Telegram.en.json b/Telegram/Telegram.en.json new file mode 100644 index 00000000..7c7b5c4b --- /dev/null +++ b/Telegram/Telegram.en.json @@ -0,0 +1,28 @@ +{ + "Plugins": { + "Telegram": { + "Description": "A plugin that allows you to send Telegram messages.", + "Fields": { + "BotToken": "Bot Token", + "BotToken-Help":"Your telegram bot token.", + "ChatId": "Chat ID", + "ChatId-Help":"The ID of the chat to send messages to." + } + } + }, + "Flow": { + "Parts": { + "Telegram": { + "Outputs": { + "1": "Telegram message sent", + "2": "Telegram message failed to send" + }, + "Description": "Sends a Telegram message.", + "Fields": { + "Message": "Message", + "Message-Help": "The message to send." + } + } + } + } +} \ No newline at end of file diff --git a/Telegram/Tests/TelegramTests.cs b/Telegram/Tests/TelegramTests.cs new file mode 100644 index 00000000..0f59d48e --- /dev/null +++ b/Telegram/Tests/TelegramTests.cs @@ -0,0 +1,23 @@ +#if(DEBUG) + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace FileFlows.Telegram.Tests; + +[TestClass] +public class TelegramTests +{ + [TestMethod] + public void SendMessage() + { + string botToken = "sometoken"; + string chatId = "somechat"; + + var result = + Communication.Telegram.SendMessage(botToken, chatId, "this is a test"); + + Assert.IsTrue(result); + } +} + +#endif \ No newline at end of file diff --git a/Telegram/Tests/TestLogger.cs b/Telegram/Tests/TestLogger.cs new file mode 100644 index 00000000..054d3552 --- /dev/null +++ b/Telegram/Tests/TestLogger.cs @@ -0,0 +1,59 @@ +#if(DEBUG) + +namespace FileFlows.Telegram.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