using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Markdown;
namespace TelegramBotBase.Sessions
{
///
/// Base class for a device/chat session
///
public class DeviceSession : IDeviceSession
{
///
/// Device or chat id
///
public long DeviceId { get; set; }
///
/// Username of user or group
///
public String ChatTitle { get; set; }
///
/// Returns the ChatTitle depending on groups/channels or users
///
///
public String GetChatTitle()
{
return LastMessage?.Chat.Title
?? LastMessage?.Chat.Username
?? LastMessage?.Chat.FirstName
?? ChatTitle;
}
///
/// When did any last action happend (message received or button clicked)
///
public DateTime LastAction { get; set; }
///
/// Returns the form where the user/group is at the moment.
///
public FormBase ActiveForm { get; set; }
///
/// Returns the previous shown form
///
public FormBase PreviousForm { get; set; }
///
/// contains if the form has been switched (navigated)
///
public bool FormSwitched { get; set; } = false;
///
/// Returns the ID of the last received message.
///
public int LastMessageId
{
get
{
return this.LastMessage?.MessageId ?? -1;
}
}
///
/// Returns the last received message.
///
public Message LastMessage { get; set; }
private MessageClient Client
{
get
{
return this.ActiveForm.Client;
}
}
///
/// Returns if the messages is posted within a group.
///
public bool IsGroup
{
get
{
return this.LastMessage != null && (this.LastMessage.Chat.Type == ChatType.Group | this.LastMessage.Chat.Type == ChatType.Supergroup);
}
}
///
/// Returns if the messages is posted within a channel.
///
public bool IsChannel
{
get
{
return this.LastMessage != null && this.LastMessage.Chat.Type == ChatType.Channel;
}
}
private EventHandlerList __Events = new EventHandlerList();
private static object __evMessageSent = new object();
private static object __evMessageReceived = new object();
private static object __evMessageDeleted = new object();
public DeviceSession()
{
}
public DeviceSession(long DeviceId)
{
this.DeviceId = DeviceId;
}
public DeviceSession(long DeviceId, FormBase StartForm)
{
this.DeviceId = DeviceId;
this.ActiveForm = StartForm;
this.ActiveForm.Device = this;
}
///
/// Edits the text message
///
///
///
///
///
public async Task Edit(int messageId, String text, ButtonForm buttons = null, ParseMode parseMode = ParseMode.Default)
{
InlineKeyboardMarkup markup = buttons;
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
try
{
return await API(a => a.EditMessageTextAsync(this.DeviceId, messageId, text, parseMode, replyMarkup: markup));
}
catch
{
}
return null;
}
///
/// Edits the text message
///
///
///
///
///
public async Task Edit(int messageId, String text, InlineKeyboardMarkup markup, ParseMode parseMode = ParseMode.Default)
{
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
try
{
return await API(a => a.EditMessageTextAsync(this.DeviceId, messageId, text, parseMode, replyMarkup: markup));
}
catch
{
}
return null;
}
///
/// Edits the text message
///
///
///
///
///
public async Task Edit(Message message, ButtonForm buttons = null, ParseMode parseMode = ParseMode.Default)
{
InlineKeyboardMarkup markup = buttons;
if (message.Text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(message.Text.Length);
}
try
{
return await API(a => a.EditMessageTextAsync(this.DeviceId, message.MessageId, message.Text, parseMode, replyMarkup: markup));
}
catch
{
}
return null;
}
///
/// Edits the reply keyboard markup (buttons)
///
///
///
///
public async Task EditReplyMarkup(int messageId, ButtonForm bf)
{
try
{
return await API(a => a.EditMessageReplyMarkupAsync(this.DeviceId, messageId, bf));
}
catch
{
}
return null;
}
///
/// Sends a simple text message
///
///
///
///
///
///
public async Task Send(long deviceId, String text, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default, bool MarkdownV2AutoEscape = true)
{
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons;
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
if (parseMode == ParseMode.MarkdownV2 && MarkdownV2AutoEscape)
{
text = text.MarkdownV2Escape();
}
try
{
var t = API(a => a.SendTextMessageAsync(deviceId, text, parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
}
catch
{
return null;
}
}
///
/// Sends a simple text message
///
///
///
///
///
///
public async Task Send(String text, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default, bool MarkdownV2AutoEscape = true)
{
return await Send(this.DeviceId, text, buttons, replyTo, disableNotification, parseMode, MarkdownV2AutoEscape);
}
///
/// Sends a simple text message
///
///
///
///
///
///
public async Task Send(String text, InlineKeyboardMarkup markup, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default, bool MarkdownV2AutoEscape = true)
{
if (this.ActiveForm == null)
return null;
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
if (parseMode == ParseMode.MarkdownV2 && MarkdownV2AutoEscape)
{
text = text.MarkdownV2Escape();
}
try
{
var t = API(a => a.SendTextMessageAsync(this.DeviceId, text, parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
}
catch
{
return null;
}
}
///
/// Sends a simple text message
///
///
///
///
///
///
public async Task Send(String text, ReplyMarkupBase markup, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default, bool MarkdownV2AutoEscape = true)
{
if (this.ActiveForm == null)
return null;
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
if (parseMode == ParseMode.MarkdownV2 && MarkdownV2AutoEscape)
{
text = text.MarkdownV2Escape();
}
try
{
var t = API(a => a.SendTextMessageAsync(this.DeviceId, text, parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
}
catch
{
return null;
}
}
///
/// Sends an image
///
///
///
///
///
///
public async Task SendPhoto(InputOnlineFile file, String caption = null, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default)
{
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons;
try
{
var t = API(a => a.SendPhotoAsync(this.DeviceId, file, caption: caption, parseMode: parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
}
catch
{
return null;
}
}
///
/// Sends an image
///
///
///
///
///
///
///
public async Task SendPhoto(Image image, String name, String caption, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false)
{
using (var fileStream = Tools.Images.ToStream(image, ImageFormat.Png))
{
InputOnlineFile fts = new InputOnlineFile(fileStream, name);
return await SendPhoto(fts, caption: caption, buttons, replyTo, disableNotification);
}
}
///
/// Sends an image
///
///
///
///
///
///
///
public async Task SendPhoto(Bitmap image, String name, String caption, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false)
{
using (var fileStream = Tools.Images.ToStream(image, ImageFormat.Png))
{
InputOnlineFile fts = new InputOnlineFile(fileStream, name);
return await SendPhoto(fts, caption: caption, buttons, replyTo, disableNotification);
}
}
///
/// Sends an video
///
///
///
///
///
///
public async Task SendVideo(InputOnlineFile file, String caption = null, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default)
{
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons;
try
{
var t = API(a => a.SendVideoAsync(this.DeviceId, file, caption: caption, parseMode: parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
}
catch
{
return null;
}
}
///
/// Sends an video
///
///
///
///
///
///
public async Task SendVideo(String url, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default)
{
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons;
try
{
var t = API(a => a.SendVideoAsync(this.DeviceId, new InputOnlineFile(url), parseMode: parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
}
catch
{
return null;
}
}
///
/// Sends an document
///
///
///
///
///
///
///
///
public async Task SendDocument(String filename, byte[] document, String caption = "", ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false)
{
MemoryStream ms = new MemoryStream(document);
InputOnlineFile fts = new InputOnlineFile(ms, filename);
return await SendDocument(fts, caption, buttons, replyTo, disableNotification);
}
///
/// Generates a Textfile from scratch with the specified encoding. (Default is UTF8)
///
///
///
/// Default is UTF8
///
///
///
///
///
public async Task SendTextFile(String filename, String textcontent, Encoding encoding = null, String caption = "", ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false)
{
encoding = encoding ?? Encoding.UTF8;
var ms = new MemoryStream();
var sw = new StreamWriter(ms, encoding);
sw.Write(textcontent);
sw.Flush();
var content = ms.ToArray();
return await SendDocument(filename, content, caption, buttons, replyTo, disableNotification);
}
///
/// Sends an document
///
///
///
///
///
///
///
public async Task SendDocument(InputOnlineFile document, String caption = "", ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false)
{
InlineKeyboardMarkup markup = null;
if (buttons != null)
{
markup = buttons;
}
try
{
var t = API(a => a.SendDocumentAsync(this.DeviceId, document, caption, replyMarkup: markup, disableNotification: disableNotification, replyToMessageId: replyTo));
var o = GetOrigin(new StackTrace());
OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
}
catch
{
return null;
}
}
///
/// Set a chat action (showed to the user)
///
///
///
public async Task SetAction(ChatAction action)
{
await API(a => a.SendChatActionAsync(this.DeviceId, action));
}
///
/// Requests the contact from the user.
///
///
///
///
///
public async Task RequestContact(String buttonText = "Send your contact", String requestMessage = "Give me your phone number!", bool OneTimeOnly = true)
{
var rck = new ReplyKeyboardMarkup(KeyboardButton.WithRequestContact(buttonText));
rck.OneTimeKeyboard = OneTimeOnly;
return await API(a => a.SendTextMessageAsync(this.DeviceId, requestMessage, replyMarkup: rck));
}
///
/// Requests the location from the user.
///
///
///
///
///
public async Task RequestLocation(String buttonText = "Send your location", String requestMessage = "Give me your location!", bool OneTimeOnly = true)
{
var rcl = new ReplyKeyboardMarkup(KeyboardButton.WithRequestLocation(buttonText));
rcl.OneTimeKeyboard = OneTimeOnly;
return await API(a => a.SendTextMessageAsync(this.DeviceId, requestMessage, replyMarkup: rcl));
}
public async Task HideReplyKeyboard(String closedMsg = "Closed", bool autoDeleteResponse = true)
{
try
{
var m = await this.Send(closedMsg, new ReplyKeyboardRemove());
if (autoDeleteResponse && m != null)
{
await this.DeleteMessage(m);
}
return m;
}
catch
{
}
return null;
}
///
/// Deletes a message
///
///
///
public virtual async Task DeleteMessage(int messageId = -1)
{
await RAW(a => a.DeleteMessageAsync(this.DeviceId, messageId));
OnMessageDeleted(new MessageDeletedEventArgs(messageId));
return true;
}
///
/// Deletes the given message
///
///
///
public virtual async Task DeleteMessage(Message message)
{
return await DeleteMessage(message.MessageId);
}
public virtual async Task ChangeChatPermissions(ChatPermissions permissions)
{
try
{
await API(a => a.SetChatPermissionsAsync(this.DeviceId, permissions));
}
catch
{
}
}
private Type GetOrigin(StackTrace stackTrace)
{
for (int i = 0; i < stackTrace.FrameCount; i++)
{
var methodBase = stackTrace.GetFrame(i).GetMethod();
//Debug.WriteLine(methodBase.Name);
if (methodBase.DeclaringType.IsSubclassOf(typeof(FormBase)) | methodBase.DeclaringType.IsSubclassOf(typeof(ControlBase)))
{
return methodBase.DeclaringType;
}
}
return null;
}
#region "Users"
public virtual async Task RestrictUser(long userId, ChatPermissions permissions, DateTime until = default(DateTime))
{
try
{
await API(a => a.RestrictChatMemberAsync(this.DeviceId, userId, permissions, until));
}
catch
{
}
}
public virtual async Task GetChatUser(long userId)
{
try
{
return await API(a => a.GetChatMemberAsync(this.DeviceId, userId));
}
catch
{
}
return null;
}
public virtual async Task KickUser(long userId, DateTime until = default(DateTime))
{
try
{
await API(a => a.KickChatMemberAsync(this.DeviceId, userId, until));
}
catch
{
}
}
public virtual async Task UnbanUser(long userId)
{
try
{
await API(a => a.UnbanChatMemberAsync(this.DeviceId, userId));
}
catch
{
}
}
#endregion
///
/// Gives access to the original TelegramClient without any Exception catchings.
///
///
///
///
public T RAW(Func call)
{
return call(this.Client.TelegramClient);
}
///
/// This will call a function on the TelegramClient and automatically Retry if an limit has been exceeded.
///
///
///
///
public async Task API(Func> call)
{
var numberOfTries = 0;
while (numberOfTries < DeviceSession.MaxNumberOfRetries)
{
try
{
return await call(Client.TelegramClient);
}
catch (ApiRequestException ex)
{
if (ex.ErrorCode != 429)
throw;
if (ex.Parameters != null)
await Task.Delay(ex.Parameters.RetryAfter * 1000);
numberOfTries++;
}
}
return default(T);
}
///
/// This will call a function on the TelegramClient and automatically Retry if an limit has been exceeded.
///
///
///
public async Task API(Func call)
{
var numberOfTries = 0;
while (numberOfTries < DeviceSession.MaxNumberOfRetries)
{
try
{
await call(Client.TelegramClient);
return;
}
catch (ApiRequestException ex)
{
if (ex.ErrorCode != 429)
throw;
if (ex.Parameters != null)
await Task.Delay(ex.Parameters.RetryAfter * 1000);
numberOfTries++;
}
}
}
#region "Events"
///
/// Eventhandler for sent messages
///
public event EventHandler MessageSent
{
add
{
this.__Events.AddHandler(__evMessageSent, value);
}
remove
{
this.__Events.RemoveHandler(__evMessageSent, value);
}
}
public void OnMessageSent(MessageSentEventArgs e)
{
(this.__Events[__evMessageSent] as EventHandler)?.Invoke(this, e);
}
///
/// Eventhandler for received messages
///
public event EventHandler MessageReceived
{
add
{
this.__Events.AddHandler(__evMessageReceived, value);
}
remove
{
this.__Events.RemoveHandler(__evMessageReceived, value);
}
}
public void OnMessageReceived(MessageReceivedEventArgs e)
{
(this.__Events[__evMessageReceived] as EventHandler)?.Invoke(this, e);
}
///
/// Eventhandler for deleting messages
///
public event EventHandler MessageDeleted
{
add
{
this.__Events.AddHandler(__evMessageDeleted, value);
}
remove
{
this.__Events.RemoveHandler(__evMessageDeleted, value);
}
}
public void OnMessageDeleted(MessageDeletedEventArgs e)
{
(this.__Events[__evMessageDeleted] as EventHandler)?.Invoke(this, e);
}
#endregion
#region "Static"
///
/// Indicates the maximum number of times a request that received error
/// 429 will be sent again after a timeout until it receives code 200 or an error code not equal to 429.
///
public static uint MaxNumberOfRetries { get; set; }
#endregion "Static"
}
}