/*
* 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);
}
}
}