/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using Newtonsoft.Json; using QuantConnect.Util; namespace QuantConnect.Notifications { /// /// Local/desktop implementation of messaging system for Lean Engine. /// [JsonConverter(typeof(NotificationJsonConverter))] public abstract class Notification { /// /// Method for sending implementations of notification object types. /// /// SMS, Email and Web are all handled by the QC Messaging Handler. To implement your own notification type implement it here. public virtual void Send() { // } } /// /// Web Notification Class /// public class NotificationWeb : Notification { /// /// Optional email headers /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public Dictionary Headers { get; set; } /// /// Send a notification message to this web address /// public string Address { get; set; } /// /// Object data to send. /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public object Data { get; set; } /// /// Constructor for sending a notification SMS to a specified phone number /// /// Address to send to /// Data to send /// Optional headers to use public NotificationWeb(string address, object data = null, Dictionary headers = null) { Address = address; Data = data; Headers = headers; } } /// /// Sms Notification Class /// public class NotificationSms : Notification { /// /// Send a notification message to this phone number /// public string PhoneNumber { get; set; } /// /// Message to send. Limited to 160 characters /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Message { get; set; } /// /// Constructor for sending a notification SMS to a specified phone number /// /// /// public NotificationSms(string number, string message) { PhoneNumber = number; Message = message; } } /// /// Email notification data. /// public class NotificationEmail : Notification { /// /// Optional email headers /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public Dictionary Headers { get; set; } /// /// Send to address: /// public string Address { get; set; } /// /// Email subject /// public string Subject { get; set; } /// /// Message to send. /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Message { get; set; } /// /// Email Data /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Data { get; set; } /// /// Default constructor for sending an email notification /// /// Address to send to. Will throw if invalid /// /// Subject of the email. Will set to if null /// Message body of the email. Will set to if null /// Data to attach to the email. Will set to if null /// Optional email headers to use public NotificationEmail(string address, string subject = "", string message = "", string data = "", Dictionary headers = null) { if (!Validate.EmailAddress(address)) { throw new ArgumentException(Messages.NotificationEmail.InvalidEmailAddress(address)); } Address = address; Data = data ?? string.Empty; Message = message ?? string.Empty; Subject = subject ?? string.Empty; Headers = headers; } } /// /// Telegram notification data /// public class NotificationTelegram : Notification { /// /// Send a notification message to this user on Telegram /// Can be either a personal ID or Group ID. /// public string Id { get; set; } /// /// Message to send. Limited to 4096 characters /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Message { get; set; } /// /// Token to use /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Token { get; set; } /// /// Constructor for sending a telegram notification to a specific User ID /// or group ID. Note: The bot must have an open chat with the user or be /// added to the group for messages to deliver. /// /// User Id or Group Id to send the message too /// Message to send /// Bot token to use, if null defaults to "telegram-token" /// in config on send public NotificationTelegram(string id, string message, string token = null) { Id = id; Message = message; Token = token; } } /// /// FTP notification data /// public class NotificationFtp : Notification { private static readonly Regex HostnameProtocolRegex = new(@"^[s]?ftp\:\/\/", RegexOptions.IgnoreCase | RegexOptions.Compiled); private const int DefaultPort = 21; /// /// Whether to use SFTP or FTP. /// [JsonProperty("secure")] public bool Secure { get; } /// /// The FTP server hostname. /// [JsonProperty("host")] public string Hostname { get; } /// /// The FTP server port. /// [JsonProperty("port")] public int Port { get; } /// /// The FTP server username. /// [JsonProperty("username")] public string Username { get; } /// /// The FTP server password. /// [JsonProperty("password", DefaultValueHandling = DefaultValueHandling.Ignore)] public string Password { get; } /// /// The path to file on the FTP server. /// [JsonProperty("fileDestinationPath")] public string FilePath { get; } /// /// The contents of the file to send. /// [JsonProperty("fileContent")] public string FileContent { get; private set; } /// /// The private key to use for authentication (optional). /// [JsonProperty("privateKey", DefaultValueHandling = DefaultValueHandling.Ignore)] public string PrivateKey { get; } /// /// The passphrase for the private key (optional). /// [JsonProperty("passphrase", DefaultValueHandling = DefaultValueHandling.Ignore)] public string PrivateKeyPassphrase { get; } private NotificationFtp(string hostname, string username, string filePath, byte[] fileContent, bool secure, int? port) { Hostname = NormalizeHostname(hostname); Port = port ?? DefaultPort; Username = username; FilePath = filePath; FileContent = Convert.ToBase64String(fileContent); Secure = secure; } /// /// Constructor for a notification to sent as a file to an FTP server using password authentication. /// /// FTP server hostname /// The FTP server username /// The FTP server password /// The path to file on the FTP server /// The contents of the file /// Whether to use SFTP or FTP. Defaults to true /// The FTP server port. Defaults to 21 public NotificationFtp(string hostname, string username, string password, string filePath, byte[] fileContent, bool secure = true, int? port = null) : this(hostname, username, filePath, fileContent, secure, port) { if (string.IsNullOrEmpty(password)) { throw new ArgumentException(Messages.NotificationFtp.MissingPassword); } Password = password; } /// /// Constructor for a notification to sent as a file to an FTP server over SFTP using SSH keys. /// /// FTP server hostname /// The FTP server username /// The private SSH key to use for authentication /// The optional passphrase to decrypt the private key. /// This can be empty or null if the private key is not encrypted /// The path to file on the FTP server /// The contents of the file /// The FTP server port. Defaults to 21 public NotificationFtp(string hostname, string username, string privateKey, string privateKeyPassphrase, string filePath, byte[] fileContent, int? port = null) : this(hostname, username, filePath, fileContent, true, port) { if (string.IsNullOrEmpty(privateKey)) { throw new ArgumentException(Messages.NotificationFtp.MissingSSHKey); } PrivateKey = privateKey; PrivateKeyPassphrase = privateKeyPassphrase; } /// /// Constructor for a notification to sent as a file to an FTP server using password authentication. /// /// FTP server hostname /// The FTP server username /// The FTP server password /// The path to file on the FTP server /// The contents of the file /// Whether to use SFTP or FTP. Defaults to true /// The FTP server port. Defaults to 21 public NotificationFtp(string hostname, string username, string password, string filePath, string fileContent, bool secure = true, int? port = null) : this(hostname, username, password, filePath, Encoding.ASCII.GetBytes(fileContent), secure, port) { } /// /// Constructor for a notification to sent as a file to an FTP server over SFTP using SSH keys. /// /// FTP server hostname /// The FTP server username /// The private SSH key to use for authentication /// The optional passphrase to decrypt the private key. /// This can be empty or null if the private key is not encrypted /// The path to file on the FTP server /// The contents of the file /// The FTP server port. Defaults to 21 public NotificationFtp(string hostname, string username, string privateKey, string privateKeyPassphrase, string filePath, string fileContent, int? port = null) : this(hostname, username, privateKey, privateKeyPassphrase, filePath, Encoding.ASCII.GetBytes(fileContent), port) { } private static string NormalizeHostname(string hostname) { // Remove trailing slashes hostname = hostname.Trim().TrimEnd('/'); // Remove protocol if present return HostnameProtocolRegex.Replace(hostname, ""); } /// /// Factory method for Json deserialization: the file contents are already encoded /// internal static NotificationFtp FromEncodedData(string hostname, string username, string password, string filePath, string encodedFileContent, bool secure, int? port) { var notification = new NotificationFtp(hostname, username, password, filePath, Array.Empty(), secure, port); notification.FileContent = encodedFileContent; return notification; } /// /// Factory method for Json deserialization: the file contents are already encoded /// internal static NotificationFtp FromEncodedData(string hostname, string username, string privateKey, string privateKeyPassphrase, string filePath, string encodedFileContent, int? port) { var notification = new NotificationFtp(hostname, username, privateKey, privateKeyPassphrase, filePath, Array.Empty(), port); notification.FileContent = encodedFileContent; return notification; } } /// /// Extension methods for /// public static class NotificationExtensions { /// /// Check if the notification can be sent (implements the method) /// /// The notification /// Whether the notification can be sent public static bool CanSend(this Notification notification) { if (notification == null) { return false; } var type = notification.GetType(); return type != typeof(NotificationEmail) && type != typeof(NotificationWeb) && type != typeof(NotificationSms) && type != typeof(NotificationTelegram) && type != typeof(NotificationFtp); } } }