Removed doubled filters variations for reply-chain message filtering

Instead using MessageRepliedAttribute to filter messages in reply-chain
Old types renamed to new framework name
This commit is contained in:
2025-07-25 01:26:32 +04:00
parent b60b4c5f01
commit 78c7f41489
26 changed files with 158 additions and 1458 deletions
@@ -0,0 +1,18 @@
using Telegrator.Filters;
namespace Telegrator.Annotations
{
/// <summary>
/// Attribute for filtering messages with reply to messages of this bot.
/// </summary>
public class MeRepliedAttribute()
: MessageFilterAttribute(new MeRepliedFilter())
{ }
/// <summary>
/// Attribute for filtering messages in reply chain.
/// </summary>
public class MessageRepliedAttribute(int replyDepth = 1)
: MessageFilterAttribute(new MessageRepliedFilter(replyDepth))
{ }
}
@@ -1,44 +0,0 @@
using Telegram.Bot.Types.Enums;
using Telegrator.Filters;
namespace Telegrator.Annotations
{
/// <summary>
/// Attribute for filtering messages that are replies to messages containing mentions.
/// Allows handlers to respond to messages that reply to messages with specific mentions.
/// </summary>
public class RepliedMentionedAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes a new instance of the RepliedMentionedAttribute that matches replies to any mention.
/// </summary>
/// <param name="replyDepth">The depth of the reply chain to check (default: 1).</param>
public RepliedMentionedAttribute(int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(MessageEntityType.Mention, 0, null, replyDepth), new RepliedMentionedFilter(replyDepth)) { }
/// <summary>
/// Initializes a new instance of the RepliedMentionedAttribute that matches replies to mentions at a specific offset.
/// </summary>
/// <param name="offset">The offset position where the mention should occur in the replied message.</param>
/// <param name="replyDepth">The depth of the reply chain to check (default: 1).</param>
public RepliedMentionedAttribute(int offset, int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(MessageEntityType.Mention, offset, null, replyDepth), new RepliedMentionedFilter(replyDepth)) { }
/// <summary>
/// Initializes a new instance of the RepliedMentionedAttribute that matches replies to a specific mention.
/// </summary>
/// <param name="mention">The specific mention text to match in the replied message.</param>
/// <param name="replyDepth">The depth of the reply chain to check (default: 1).</param>
public RepliedMentionedAttribute(string mention, int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(MessageEntityType.Mention, replyDepth), new RepliedMentionedFilter(mention, replyDepth)) { }
/// <summary>
/// Initializes a new instance of the RepliedMentionedAttribute that matches replies to a specific mention at a specific offset.
/// </summary>
/// <param name="mention">The specific mention text to match in the replied message.</param>
/// <param name="offset">The offset position where the mention should occur in the replied message.</param>
/// <param name="replyDepth">The depth of the reply chain to check (default: 1).</param>
public RepliedMentionedAttribute(string mention, int offset, int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(MessageEntityType.Mention, offset, null, replyDepth), new RepliedMentionedFilter(mention, replyDepth)) { }
}
}
@@ -1,102 +0,0 @@
using Telegram.Bot.Types.Enums;
using Telegrator.Filters;
namespace Telegrator.Annotations
{
/// <summary>
/// Attribute for filtering messages where the replied-to message was sent in a forum chat.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedChatIsForumAttribute(int replyDepth = 1)
: MessageFilterAttribute(new RepliedMessageChatIsForumFilter(replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message was sent in a specific chat by ID.
/// </summary>
/// <param name="id">The chat ID to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedChatIdAttribute(long id, int replyDepth = 1)
: MessageFilterAttribute(new RepliedMessageChatIdFilter(id, replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message was sent in a chat of a specific type.
/// </summary>
/// <param name="type">The chat type to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedChatTypeAttribute(ChatType type, int replyDepth = 1)
: MessageFilterAttribute(new RepliedMessageChatTypeFilter(type, replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages based on the chat title of the replied-to message.
/// </summary>
public class RepliedChatTitleAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a chat with a specific title and comparison method.
/// </summary>
/// <param name="title">The chat title to match</param>
/// <param name="comparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedChatTitleAttribute(string? title, StringComparison comparison, int replyDepth = 1)
: base(new RepliedMessageChatTitleFilter(title, comparison, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a chat with a specific title.
/// </summary>
/// <param name="title">The chat title to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedChatTitleAttribute(string? title, int replyDepth = 1)
: base(new RepliedMessageChatTitleFilter(title, StringComparison.InvariantCulture, replyDepth)) { }
}
/// <summary>
/// Attribute for filtering messages based on the chat username of the replied-to message.
/// </summary>
public class RepliedChatUsernameAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a chat with a specific username and comparison method.
/// </summary>
/// <param name="userName">The chat username to match</param>
/// <param name="comparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedChatUsernameAttribute(string? userName, StringComparison comparison, int replyDepth = 1)
: base(new RepliedMessageChatUsernameFilter(userName, comparison, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a chat with a specific username.
/// </summary>
/// <param name="userName">The chat username to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedChatUsernameAttribute(string? userName, int replyDepth = 1)
: base(new RepliedMessageChatUsernameFilter(userName, replyDepth)) { }
}
/// <summary>
/// Attribute for filtering messages based on the chat name of the replied-to message.
/// </summary>
public class RepliedChatNameAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a chat with specific first and last names.
/// </summary>
/// <param name="firstName">The first name to match</param>
/// <param name="lastName">The last name to match (optional)</param>
/// <param name="comparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedChatNameAttribute(string? firstName, string? lastName, StringComparison comparison, int replyDepth = 1)
: base(new RepliedMessageChatNameFilter(firstName, lastName, comparison, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a chat with specific first and last names.
/// </summary>
/// <param name="firstName">The first name to match</param>
/// <param name="lastName">The last name to match (optional)</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedChatNameAttribute(string? firstName, string? lastName, int replyDepth = 1)
: base(new RepliedMessageChatNameFilter(firstName, lastName, StringComparison.InvariantCulture, replyDepth)) { }
}
}
@@ -1,114 +0,0 @@
using Telegram.Bot.Types.Enums;
using Telegrator.Filters;
namespace Telegrator.Annotations
{
/// <summary>
/// Attribute for filtering messages that are replies to other messages.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class MessageRepliedAttribute(int replyDepth = 1)
: MessageFilterAttribute(new MessageRepliedFilter(replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message contains dice throws with specific values.
/// </summary>
public class RepliedDiceThrowedAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message contains a dice throw with a specific value.
/// </summary>
/// <param name="value">The dice value to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedDiceThrowedAttribute(int value, int replyDepth = 1)
: base(new RepliedDiceThrowedFilter(value, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message contains a dice throw with a specific type and value.
/// </summary>
/// <param name="diceType">The type of dice</param>
/// <param name="value">The dice value to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedDiceThrowedAttribute(DiceType diceType, int value, int replyDepth = 1)
: base(new RepliedDiceThrowedFilter(diceType, value, replyDepth)) { }
}
/// <summary>
/// Attribute for filtering messages where the replied-to message was automatically forwarded.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedIsAutomaticFormwardMessageAttribute(int replyDepth = 1)
: MessageFilterAttribute(new RepliedIsAutomaticFormwardMessageFilter(replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message was sent while the user was offline.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedIsFromOfflineMessageAttribute(int replyDepth = 1)
: MessageFilterAttribute(new RepliedIsFromOfflineMessageFilter(replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message is a service message.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedIsServiceMessageMessageAttribute(int replyDepth = 1)
: MessageFilterAttribute(new RepliedIsServiceMessageMessageFilter(replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message is a topic message in forum chats.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedIsTopicMessageMessageAttribut(int replyDepth = 1)
: MessageFilterAttribute(new RepliedIsServiceMessageMessageFilter(replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages based on entities in the replied-to message.
/// </summary>
public class RepliedMessageHasEntityAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message has a specific entity type.
/// </summary>
/// <param name="type">The entity type to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedMessageHasEntityAttribute(MessageEntityType type, int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(type, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message has a specific entity type at a specific position.
/// </summary>
/// <param name="type">The entity type to match</param>
/// <param name="offset">The starting position of the entity</param>
/// <param name="length">The length of the entity (optional)</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedMessageHasEntityAttribute(MessageEntityType type, int offset, int? length, int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(type, offset, length, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message has a specific entity type with specific content.
/// </summary>
/// <param name="type">The entity type to match</param>
/// <param name="content">The content that the entity should contain</param>
/// <param name="stringComparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedMessageHasEntityAttribute(MessageEntityType type, string content, StringComparison stringComparison = StringComparison.CurrentCulture, int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(type, content, stringComparison, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message has a specific entity type at a specific position with specific content.
/// </summary>
/// <param name="type">The entity type to match</param>
/// <param name="offset">The starting position of the entity</param>
/// <param name="length">The length of the entity (optional)</param>
/// <param name="content">The content that the entity should contain</param>
/// <param name="stringComparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedMessageHasEntityAttribute(MessageEntityType type, int offset, int? length, string content, StringComparison stringComparison = StringComparison.CurrentCulture, int replyDepth = 1)
: base(new RepliedMessageHasEntityFilter(type, offset, length, content, stringComparison, replyDepth)) { }
}
}
@@ -1,94 +0,0 @@
using Telegrator.Filters;
namespace Telegrator.Annotations
{
/// <summary>
/// Attribute for filtering messages based on the username of the sender of the replied-to message.
/// </summary>
public class RepliedFromUsernameAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a specific username.
/// </summary>
/// <param name="username">The username to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedFromUsernameAttribute(string username, int replyDepth = 1)
: base(new RepliedUsernameFilter(username, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a specific username with custom comparison.
/// </summary>
/// <param name="username">The username to match</param>
/// <param name="comparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedFromUsernameAttribute(string username, StringComparison comparison, int replyDepth = 1)
: base(new RepliedUsernameFilter(username, comparison, replyDepth)) { }
}
/// <summary>
/// Attribute for filtering messages based on the name of the sender of the replied-to message.
/// </summary>
public class RepliedFromUserAttribute : MessageFilterAttribute
{
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a user with specific names.
/// </summary>
/// <param name="firstName">The first name to match</param>
/// <param name="lastName">The last name to match (optional)</param>
/// <param name="comparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedFromUserAttribute(string firstName, string? lastName, StringComparison comparison, int replyDepth = 1)
: base(new RepliedUserFilter(firstName, lastName, comparison, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a user with specific names.
/// </summary>
/// <param name="firstName">The first name to match</param>
/// <param name="lastName">The last name to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedFromUserAttribute(string firstName, string lastName, int replyDepth = 1)
: base(new RepliedUserFilter(firstName, lastName, StringComparison.InvariantCulture, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a user with a specific first name.
/// </summary>
/// <param name="firstName">The first name to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedFromUserAttribute(string firstName, int replyDepth = 1)
: base(new RepliedUserFilter(firstName, null, StringComparison.InvariantCulture, replyDepth)) { }
/// <summary>
/// Initializes the attribute to filter messages where the replied-to message is from a user with a specific first name and custom comparison.
/// </summary>
/// <param name="firstName">The first name to match</param>
/// <param name="comparison">The string comparison method</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public RepliedFromUserAttribute(string firstName, StringComparison comparison, int replyDepth = 1)
: base(new RepliedUserFilter(firstName, null, comparison, replyDepth)) { }
}
/// <summary>
/// Attribute for filtering messages based on the user ID of the sender of the replied-to message.
/// </summary>
/// <param name="userId">The user ID to match</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedUserIdAttribute(long userId, int replyDepth = 1)
: MessageFilterAttribute(new RepliedUserIdFilter(userId, replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message was sent by a bot.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class ReplyFromBotAttribute(int replyDepth = 1)
: MessageFilterAttribute(new ReplyFromBotFilter(replyDepth))
{ }
/// <summary>
/// Attribute for filtering messages where the replied-to message was sent by a premium user.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class ReplyFromPremiumUserAttribute(int replyDepth = 1)
: MessageFilterAttribute(new ReplyFromPremiumUserFilter(replyDepth))
{ }
}
@@ -1,52 +0,0 @@
using Telegrator.Filters;
namespace Telegrator.Annotations
{
/// <summary>
/// Attribute for filtering updates where the replied-to message's text starts with the specified content.
/// </summary>
/// <param name="content">The string that the replied message's text should start with</param>
/// <param name="comparison">The string comparison type</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedTextStartsWithAttribute(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: MessageFilterAttribute(new RepliedTextStartsWithFilter(content, comparison, replyDepth))
{ }
/// <summary>
/// Attribute for filtering updates where the replied-to message's text ends with the specified content.
/// </summary>
/// <param name="content">The string that the replied message's text should end with</param>
/// <param name="comparison">The string comparison type</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedTextEndsWithAttribute(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: MessageFilterAttribute(new RepliedTextEndsWithFilter(content, comparison, replyDepth))
{ }
/// <summary>
/// Attribute for filtering updates where the replied-to message's text contains the specified content.
/// </summary>
/// <param name="content">The string that the replied message's text should contain</param>
/// <param name="comparison">The string comparison type</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedTextContainsAttribute(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: MessageFilterAttribute(new RepliedTextContainsFilter(content, comparison, replyDepth))
{ }
/// <summary>
/// Attribute for filtering updates where the replied-to message's text equals the specified content.
/// </summary>
/// <param name="content">The string that the replied message's text should equal</param>
/// <param name="comparison">The string comparison type</param>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedTextEqualsAttribute(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: MessageFilterAttribute(new RepliedTextEqualsFilter(content, comparison, replyDepth))
{ }
/// <summary>
/// Attribute for filtering updates where the replied-to message contains any non-empty text.
/// </summary>
/// <param name="replyDepth">How many levels up the reply chain to check (default: 1)</param>
public class RepliedHasTextAttribute(int replyDepth = 1)
: MessageFilterAttribute(new RepliedTextNotNullOrEmptyFilter(replyDepth))
{ }
}
@@ -1,11 +0,0 @@
using Telegrator.Filters;
namespace Telegrator.Annotations
{
/// <summary>
/// Attribute for filtering messages with reply to messages of this bot.
/// </summary>
public class RepliedToMeAttribute()
: MessageFilterAttribute(new RepliedToMeFilter())
{ }
}
+4 -4
View File
@@ -7,7 +7,7 @@ namespace Telegrator.Filters
/// Filter that checks if a message contains a mention of the bot or a specific user.
/// Requires a <see cref="MessageHasEntityFilter"/> to be applied first to identify mention entities.
/// </summary>
public class MentionedFilter : Filter<Message>
public class MentionedFilter : MessageFilterBase
{
/// <summary>
/// The username to check for in the mention (null means check for bot's username).
@@ -39,14 +39,14 @@ namespace Telegrator.Filters
/// <param name="context">The filter execution context containing the message and completed filters.</param>
/// <returns>True if the message contains the specified mention; otherwise, false.</returns>
/// <exception cref="ArgumentNullException">Thrown when the bot username is null and no specific mention is provided.</exception>
public override bool CanPass(FilterExecutionContext<Message> context)
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (context.Input.Text == null)
if (Target.Text == null)
return false;
string userName = Mention ?? context.BotInfo.User.Username ?? throw new ArgumentNullException(nameof(context), "MentionedFilter requires BotInfo to be initialized");
MessageHasEntityFilter entityFilter = context.CompletedFilters.Get<MessageHasEntityFilter>(0);
return entityFilter.FoundEntities.Any(ent => context.Input.Text.Substring(ent.Offset + 1, ent.Length - 1) == userName);
return entityFilter.FoundEntities.Any(ent => Target.Text.Substring(ent.Offset + 1, ent.Length - 1) == userName);
}
}
}
+5 -5
View File
@@ -7,7 +7,7 @@ namespace Telegrator.Filters
/// <summary>
/// Base class for filters that operate on the chat of the message being processed.
/// </summary>
public abstract class MessageChatFilter : Filter<Message>
public abstract class MessageChatFilter : MessageFilterBase
{
/// <summary>
/// Gets the chat of the message being processed.
@@ -15,18 +15,18 @@ namespace Telegrator.Filters
public Chat Chat { get; private set; } = null!;
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
Chat = context.Input.Chat;
Chat = Target.Chat;
return CanPassNext(context.CreateChild(Chat));
}
/// <summary>
/// Determines whether the filter passes for the given chat context.
/// </summary>
/// <param name="_">The filter execution context for the chat.</param>
/// <param name="context">The filter execution context for the chat.</param>
/// <returns>True if the filter passes; otherwise, false.</returns>
protected abstract bool CanPassNext(FilterExecutionContext<Chat> _);
protected abstract bool CanPassNext(FilterExecutionContext<Chat> context);
}
/// <summary>
+49 -24
View File
@@ -1,15 +1,40 @@
using System.Text.RegularExpressions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegrator;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
/// <summary>
/// Base abstract class for all filter of <see cref="Message"/> updates
/// </summary>
public abstract class MessageFilterBase : Filter<Message>
{
/// <summary>
/// Target message for filterring
/// </summary>
protected Message Target { get; private set; } = null!;
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
{
MessageRepliedFilter? repliedFilter = context.CompletedFilters.Get<MessageRepliedFilter>().SingleOrDefault();
Target = repliedFilter?.Reply ?? context.Input;
return CanPassNext(context);
}
/// <summary>
/// Determines whether the filter can pass for the given context.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
protected abstract bool CanPassNext(FilterExecutionContext<Message> context);
}
/// <summary>
/// Filters messages by their <see cref="MessageType"/>.
/// </summary>
public class MessageTypeFilter : Filter<Message>
public class MessageTypeFilter : MessageFilterBase
{
private readonly MessageType type;
@@ -20,54 +45,54 @@ namespace Telegrator.Filters
public MessageTypeFilter(MessageType type) => this.type = type;
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
=> context.Input.Type == type;
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> Target.Type == type;
}
/// <summary>
/// Filters messages that are automatic forwards.
/// </summary>
public class IsAutomaticFormwardMessageFilter : Filter<Message>
public class IsAutomaticFormwardMessageFilter : MessageFilterBase
{
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
=> context.Input.IsAutomaticForward;
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> Target.IsAutomaticForward;
}
/// <summary>
/// Filters messages that are sent from offline.
/// </summary>
public class IsFromOfflineMessageFilter : Filter<Message>
public class IsFromOfflineMessageFilter : MessageFilterBase
{
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
=> context.Input.IsFromOffline;
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> Target.IsFromOffline;
}
/// <summary>
/// Filters service messages (e.g., chat events).
/// </summary>
public class IsServiceMessageMessageFilter : Filter<Message>
public class IsServiceMessageMessageFilter : MessageFilterBase
{
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
=> context.Input.IsServiceMessage;
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> Target.IsServiceMessage;
}
/// <summary>
/// Filters messages that are topic messages.
/// </summary>
public class IsTopicMessageMessageFilter : Filter<Message>
public class IsTopicMessageMessageFilter : MessageFilterBase
{
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
=> context.Input.IsTopicMessage;
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> Target.IsTopicMessage;
}
/// <summary>
/// Filters messages by dice throw value and optionally by dice type.
/// </summary>
public class DiceThrowedFilter : Filter<Message>
public class DiceThrowedFilter : MessageFilterBase
{
private readonly DiceType? Dice;
private readonly int Value;
@@ -89,15 +114,15 @@ namespace Telegrator.Filters
public DiceThrowedFilter(DiceType diceType, int value) : this(value) => Dice = diceType;
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (context.Input.Dice == null)
if (Target.Dice == null)
return false;
if (Dice != null && context.Input.Dice.Emoji != GetEmojyForDiceType(Dice))
if (Dice != null && Target.Dice.Emoji != GetEmojyForDiceType(Dice))
return false;
return context.Input.Dice.Value == Value;
return Target.Dice.Value == Value;
}
private static string? GetEmojyForDiceType(DiceType? diceType) => diceType switch
@@ -136,7 +161,7 @@ namespace Telegrator.Filters
/// <summary>
/// Filters messages that contain a specific entity type, content, offset, or length.
/// </summary>
public class MessageHasEntityFilter : Filter<Message>
public class MessageHasEntityFilter : MessageFilterBase
{
private readonly StringComparison _stringComparison = StringComparison.CurrentCulture;
private readonly MessageEntityType? EntityType;
@@ -202,12 +227,12 @@ namespace Telegrator.Filters
}
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (context.Input is not { Entities.Length: > 0 })
return false;
FoundEntities = context.Input.Entities.Where(entity => FilterEntity(context.Input.Text, entity)).ToArray();
FoundEntities = Target.Entities.Where(entity => FilterEntity(Target.Text, entity)).ToArray();
return FoundEntities.Length != 0;
}
@@ -0,0 +1,62 @@
using Telegram.Bot.Types;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
/// <summary>
/// Abstract base class for filters that operate on replied messages.
/// Provides functionality to traverse reply chains and access replied message content.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class MessageRepliedFilter(int replyDepth = 1) : Filter<Message>
{
/// <summary>
/// Gets the replied message at the specified depth in the reply chain.
/// </summary>
public Message Reply { get; private set; } = null!;
/// <summary>
/// Gets the depth of reply chain traversal.
/// </summary>
public int ReplyDepth { get; private set; } = replyDepth;
/// <summary>
/// Determines if the message can pass through the filter by first validating
/// the reply chain and then applying specific filter logic.
/// </summary>
/// <param name="context">The filter execution context containing the message.</param>
/// <returns>True if the message passes both reply validation and specific filter criteria; otherwise, false.</returns>
public override bool CanPass(FilterExecutionContext<Message> context)
{
Message reply = context.Input;
for (int i = ReplyDepth; i > 0; i--)
{
if (reply.ReplyToMessage is not { Id: > 0 } replyMessage)
return false;
reply = replyMessage;
}
Reply = reply;
return true;
}
}
/// <summary>
/// Filter that checks if the replied message was sent by the bot itself.
/// <para>( ! ): REQUIRES <see cref="MessageRepliedFilter"/> before</para>
/// </summary>
public class MeRepliedFilter : Filter<Message>
{
/// <summary>
/// Checks if the replied message was sent by the bot.
/// </summary>
/// <param name="context">The filter execution context containing bot information.</param>
/// <returns>True if the replied message was sent by the bot; otherwise, false.</returns>
public override bool CanPass(FilterExecutionContext<Message> context)
{
MessageRepliedFilter repliedFilter = context.CompletedFilters.Get<MessageRepliedFilter>(0);
return context.BotInfo.User == repliedFilter.Reply.From;
}
}
}
+5 -10
View File
@@ -7,7 +7,7 @@ namespace Telegrator.Filters
/// Abstract base class for filters that operate on message senders.
/// Provides functionality to access and validate the user who sent the message.
/// </summary>
public abstract class MessageSenderFilter : Filter<Message>
public abstract class MessageSenderFilter : MessageFilterBase
{
/// <summary>
/// Gets the user who sent the message.
@@ -22,20 +22,15 @@ namespace Telegrator.Filters
/// <returns>True if the message has a valid sender; otherwise, false.</returns>
public override bool CanPass(FilterExecutionContext<Message> context)
{
User = context.Input.From!;
if (!base.CanPass(context))
return false;
User = Target.From!;
if (User is not { Id: > 0 })
return false;
return CanPassNext(context);
}
/// <summary>
/// Abstract method that must be implemented by derived classes to perform
/// specific filtering logic on the message sender.
/// </summary>
/// <param name="context">The filter execution context.</param>
/// <returns>True if the sender passes the specific filter criteria; otherwise, false.</returns>
protected abstract bool CanPassNext(FilterExecutionContext<Message> context);
}
/// <summary>
+4 -9
View File
@@ -9,7 +9,7 @@ namespace Telegrator.Filters
/// Abstract base class for filters that operate on message text content.
/// Provides common functionality for extracting and validating message text.
/// </summary>
public abstract class MessageTextFilter : Filter<Message>
public abstract class MessageTextFilter : MessageFilterBase
{
/// <summary>
/// Gets the current message being processed by the filter.
@@ -29,6 +29,9 @@ namespace Telegrator.Filters
/// <returns>True if the message is valid and can be processed further; otherwise, false.</returns>
public override bool CanPass(FilterExecutionContext<Message> context)
{
if (!base.CanPass(context))
return false;
Message = context.Update.Message!;
if (Message is not { Id: > 0 })
return false;
@@ -36,14 +39,6 @@ namespace Telegrator.Filters
Text = Message.Text ?? string.Empty;
return CanPassNext(context);
}
/// <summary>
/// Abstract method that must be implemented by derived classes to perform
/// specific text-based filtering logic.
/// </summary>
/// <param name="_">The filter execution context (unused in this context).</param>
/// <returns>True if the text content passes the filter criteria; otherwise, false.</returns>
protected abstract bool CanPassNext(FilterExecutionContext<Message> _);
}
/// <summary>
@@ -1,56 +0,0 @@
using Telegram.Bot.Types;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
/// <summary>
/// Filter that checks if a replied message contains a mention of the bot or a specific user.
/// Requires a <see cref="MessageHasEntityFilter"/> to be applied first to identify mention entities.
/// </summary>
public class RepliedMentionedFilter : RepliedMessageFilter
{
/// <summary>
/// The username to check for in the mention (null means check for bot's username).
/// </summary>
private readonly string? Mention;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMentionedFilter"/> class that checks for bot mentions.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedMentionedFilter(int replyDepth = 1) : base(replyDepth)
{
Mention = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMentionedFilter"/> class that checks for specific user mentions.
/// </summary>
/// <param name="mention">The username to check for in the mention.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedMentionedFilter(string mention, int replyDepth = 1) : base(replyDepth)
{
Mention = mention;
}
/// <summary>
/// Checks if the replied message contains a mention of the specified user or bot.
/// This filter requires a <see cref="MessageHasEntityFilter"/> to be applied first
/// to identify mention entities in the replied message.
/// </summary>
/// <param name="context">The filter execution context containing the message and completed filters.</param>
/// <returns>True if the replied message contains the specified mention; otherwise, false.</returns>
/// <exception cref="ArgumentNullException">Thrown when the bot username is null and no specific mention is provided.</exception>
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (Reply.Text == null)
return false;
string userName = Mention ?? context.BotInfo.User.Username ?? throw new ArgumentNullException(nameof(context), "RepliedMentionedFilter requires BotInfo to be initialized");
MessageEntity entity = context.CompletedFilters.Get<MessageHasEntityFilter>(0).FoundEntities.ElementAt(0);
string mention = Reply.Text.Substring(entity.Offset + 1, entity.Length - 1);
return userName == mention;
}
}
}
@@ -1,211 +0,0 @@
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
/// <summary>
/// Base class for filters that operate on the chat of a replied message (the message being replied to).
/// The replyDepth parameter determines how many levels up the reply chain to search for the target message.
/// </summary>
public abstract class RepliedMessageChatFilter : RepliedMessageFilter
{
/// <summary>
/// Gets the chat of the replied message (the message being replied to at the specified depth).
/// </summary>
public Chat Chat { get; private set; } = null!;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatFilter"/> class.
/// </summary>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
protected RepliedMessageChatFilter(int replyDepth = 1) : base(replyDepth) { }
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
{
if (!CanPassReply(context))
return false;
Chat = Reply.Chat;
return CanPassNext(context);
}
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose chat is a forum.
/// </summary>
public class RepliedMessageChatIsForumFilter : RepliedMessageChatFilter
{
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatIsForumFilter"/> class.
/// </summary>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatIsForumFilter(int replyDepth = 1)
: base(replyDepth) { }
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Chat.IsForum;
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose chat ID matches the specified value.
/// </summary>
public class RepliedMessageChatIdFilter : RepliedMessageChatFilter
{
private readonly long Id;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatIdFilter"/> class.
/// </summary>
/// <param name="id">The chat ID to match.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatIdFilter(long id, int replyDepth = 1) : base(replyDepth) => Id = id;
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Chat.Id == Id;
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose chat type matches the specified value.
/// </summary>
public class RepliedMessageChatTypeFilter : RepliedMessageChatFilter
{
private readonly ChatType Type;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatTypeFilter"/> class.
/// </summary>
/// <param name="type">The chat type to match.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatTypeFilter(ChatType type, int replyDepth = 1) : base(replyDepth) => Type = type;
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Chat.Type == Type;
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose chat title matches the specified value.
/// </summary>
public class RepliedMessageChatTitleFilter : RepliedMessageChatFilter
{
private readonly string? Title;
private readonly StringComparison Comparison = StringComparison.InvariantCulture;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatTitleFilter"/> class.
/// </summary>
/// <param name="title">The chat title to match.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatTitleFilter(string? title, int replyDepth = 1) : base(replyDepth) => Title = title;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatTitleFilter"/> class with a specific string comparison.
/// </summary>
/// <param name="title">The chat title to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatTitleFilter(string? title, StringComparison comparison, int replyDepth = 1)
: this(title, replyDepth) => Comparison = comparison;
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
{
if (Chat.Title == null)
return false;
return Chat.Title.Equals(Title, Comparison);
}
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose chat username matches the specified value.
/// </summary>
public class RepliedMessageChatUsernameFilter : RepliedMessageChatFilter
{
private readonly string? UserName;
private readonly StringComparison Comparison = StringComparison.InvariantCulture;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatUsernameFilter"/> class.
/// </summary>
/// <param name="userName">The chat username to match.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatUsernameFilter(string? userName, int replyDepth = 1) : base(replyDepth) => UserName = userName;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatUsernameFilter"/> class with a specific string comparison.
/// </summary>
/// <param name="userName">The chat username to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatUsernameFilter(string? userName, StringComparison comparison, int replyDepth = 1)
: this(userName, replyDepth) => Comparison = comparison;
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
{
if (Chat.Username == null)
return false;
return Chat.Username.Equals(UserName, Comparison);
}
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose chat first and/or last name matches the specified values.
/// </summary>
public class RepliedMessageChatNameFilter : RepliedMessageChatFilter
{
private readonly string? FirstName;
private readonly string? LastName;
private readonly StringComparison Comparison = StringComparison.InvariantCulture;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatNameFilter"/> class.
/// </summary>
/// <param name="firstName">The chat first name to match.</param>
/// <param name="lastName">The chat last name to match.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatNameFilter(string? firstName, string? lastName, int replyDepth = 1) : base(replyDepth)
{
FirstName = firstName;
LastName = lastName;
}
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageChatNameFilter"/> class with a specific string comparison.
/// </summary>
/// <param name="firstName">The chat first name to match.</param>
/// <param name="lastName">The chat last name to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedMessageChatNameFilter(string? firstName, string? lastName, StringComparison comparison, int replyDepth = 1)
: this(firstName, lastName, replyDepth) => Comparison = comparison;
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
{
if (LastName != null)
{
if (Chat.LastName == null)
return false;
if (Chat.LastName.Equals(LastName, Comparison))
return false;
}
if (FirstName != null)
{
if (Chat.FirstName == null)
return false;
if (Chat.FirstName.Equals(FirstName, Comparison))
return false;
}
return true;
}
}
}
-383
View File
@@ -1,383 +0,0 @@
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegrator;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
/// <summary>
/// Abstract base class for filters that operate on replied messages.
/// Provides functionality to traverse reply chains and access replied message content.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public abstract class RepliedMessageFilter(int replyDepth = 1) : Filter<Message>
{
/// <summary>
/// Gets the replied message at the specified depth in the reply chain.
/// </summary>
public Message Reply { get; private set; } = null!;
/// <summary>
/// Gets the depth of reply chain traversal.
/// </summary>
public int ReplyDepth { get; private set; } = replyDepth;
/// <summary>
/// Validates that the message has a valid reply chain at the specified depth.
/// </summary>
/// <param name="context">The filter execution context containing the message.</param>
/// <returns>True if the reply chain is valid at the specified depth; otherwise, false.</returns>
protected bool CanPassReply(FilterExecutionContext<Message> context)
{
Message reply = context.Input;
for (int i = ReplyDepth; i > 0; i--)
{
if (reply.ReplyToMessage is not { Id: > 0 } replyMessage)
return false;
reply = replyMessage;
}
Reply = reply;
return true;
}
/// <summary>
/// Determines if the message can pass through the filter by first validating
/// the reply chain and then applying specific filter logic.
/// </summary>
/// <param name="context">The filter execution context containing the message.</param>
/// <returns>True if the message passes both reply validation and specific filter criteria; otherwise, false.</returns>
public override bool CanPass(FilterExecutionContext<Message> context)
{
if (!CanPassReply(context))
return false;
return CanPassNext(context);
}
/// <summary>
/// Abstract method that must be implemented by derived classes to perform
/// specific filtering logic on the replied message.
/// </summary>
/// <param name="context">The filter execution context.</param>
/// <returns>True if the replied message passes the specific filter criteria; otherwise, false.</returns>
protected abstract bool CanPassNext(FilterExecutionContext<Message> context);
}
/// <summary>
/// Filter that checks if a message is a reply to another message at the specified depth.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class MessageRepliedFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Always returns true if the reply chain is valid at the specified depth.
/// </summary>
/// <param name="context">The filter execution context (unused).</param>
/// <returns>True if the reply chain is valid; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> true;
}
/// <summary>
/// Filter that checks if the replied message was sent by the bot itself.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class MeRepliedFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message was sent by the bot.
/// </summary>
/// <param name="context">The filter execution context containing bot information.</param>
/// <returns>True if the replied message was sent by the bot; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> context.BotInfo.User == Reply.From;
}
/// <summary>
/// Filter that checks if the replied message has non-empty text content.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedTextNotNullOrEmptyFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message text is not null or empty.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message has non-empty text; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> !string.IsNullOrEmpty(Reply.Text);
}
/// <summary>
/// Filter that checks if the replied message is of a specific type.
/// </summary>
/// <param name="type">The message type to check for.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedMessageTypeFilter(MessageType type, int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message is of the specified type.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message is of the specified type; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Reply.Type == type;
}
/// <summary>
/// Filter that checks if the replied message is an automatic forward.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedIsAutomaticFormwardMessageFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message is an automatic forward.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message is an automatic forward; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Reply.IsAutomaticForward;
}
/// <summary>
/// Filter that checks if the replied message is from an offline user.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedIsFromOfflineMessageFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message is from an offline user.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message is from an offline user; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Reply.IsFromOffline;
}
/// <summary>
/// Filter that checks if the replied message is a service message.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedIsServiceMessageMessageFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message is a service message.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message is a service message; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Reply.IsServiceMessage;
}
/// <summary>
/// Filter that checks if the replied message is a topic message.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedIsTopicMessageMessageFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message is a topic message.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message is a topic message; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Reply.IsTopicMessage;
}
/// <summary>
/// Filter that checks if the replied message contains a dice with a specific value.
/// </summary>
/// <param name="value">The dice value to check for.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedDiceThrowedFilter(int value, int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// The dice type to check for (optional).
/// </summary>
private readonly DiceType? Dice = null;
/// <summary>
/// The dice value to check for.
/// </summary>
private readonly int Value = value;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedDiceThrowedFilter"/> class with a specific dice type and value.
/// </summary>
/// <param name="diceType">The dice type to check for.</param>
/// <param name="value">The dice value to check for.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedDiceThrowedFilter(DiceType diceType, int value, int replyDepth = 1)
: this(value, replyDepth) => Dice = diceType;
/// <summary>
/// Checks if the replied message contains a dice with the specified value and optionally the specified type.
/// </summary>
/// <param name="context">The filter execution context containing the message.</param>
/// <returns>True if the replied message contains a dice with the specified criteria; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (context.Input.Dice == null)
return false;
if (Dice != null && context.Input.Dice.Emoji != GetEmojyForDiceType(Dice))
return false;
return context.Input.Dice.Value == Value;
}
/// <summary>
/// Gets the emoji representation for a specific dice type.
/// </summary>
/// <param name="diceType">The dice type to get the emoji for.</param>
/// <returns>The emoji string for the dice type, or null if not found.</returns>
private static string? GetEmojyForDiceType(DiceType? diceType) => diceType switch
{
DiceType.Dice => "🎲",
DiceType.Darts => "🎯",
DiceType.Bowling => "🎳",
DiceType.Basketball => "🏀",
DiceType.Football => "⚽",
DiceType.Casino => "🎰",
_ => null
};
}
/// <summary>
/// Filter that checks if the replied message contains specific message entities.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedMessageHasEntityFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// The string comparison type to use for content matching.
/// </summary>
private readonly StringComparison _stringComparison = StringComparison.CurrentCulture;
/// <summary>
/// The entity type to filter by (optional).
/// </summary>
private readonly MessageEntityType? EntityType;
/// <summary>
/// The content to match in the entity (optional).
/// </summary>
private readonly string? Content;
/// <summary>
/// The offset position to check (optional).
/// </summary>
private readonly int? Offset;
/// <summary>
/// The length to check (optional).
/// </summary>
private readonly int? Length;
/// <summary>
/// Gets the found entities that match the filter criteria.
/// </summary>
public MessageEntity[]? FoundEntities { get; set; } = null!;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageHasEntityFilter"/> class with a specific entity type.
/// </summary>
/// <param name="type">The entity type to filter by.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedMessageHasEntityFilter(MessageEntityType type, int replyDepth = 1) : this(replyDepth)
{
EntityType = type;
}
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageHasEntityFilter"/> class with position criteria.
/// </summary>
/// <param name="type">The entity type to filter by.</param>
/// <param name="offset">The offset position to check.</param>
/// <param name="length">The length to check (optional).</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedMessageHasEntityFilter(MessageEntityType type, int offset, int? length, int replyDepth = 1) : this(replyDepth)
{
EntityType = type;
Offset = offset;
Length = length;
}
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageHasEntityFilter"/> class with content criteria.
/// </summary>
/// <param name="type">The entity type to filter by.</param>
/// <param name="content">The content to match in the entity.</param>
/// <param name="stringComparison">The string comparison type to use.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedMessageHasEntityFilter(MessageEntityType type, string content, StringComparison stringComparison = StringComparison.CurrentCulture, int replyDepth = 1) : this(replyDepth)
{
EntityType = type;
Content = content;
_stringComparison = stringComparison;
}
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageHasEntityFilter"/> class with all criteria.
/// </summary>
/// <param name="type">The entity type to filter by.</param>
/// <param name="offset">The offset position to check.</param>
/// <param name="length">The length to check (optional).</param>
/// <param name="content">The content to match in the entity.</param>
/// <param name="stringComparison">The string comparison type to use.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedMessageHasEntityFilter(MessageEntityType type, int offset, int? length, string content, StringComparison stringComparison = StringComparison.CurrentCulture, int replyDepth = 1) : this(replyDepth)
{
EntityType = type;
Offset = offset;
Length = length;
Content = content;
_stringComparison = stringComparison;
}
/// <summary>
/// Checks if the replied message contains entities that match the specified criteria.
/// </summary>
/// <param name="context">The filter execution context containing the message.</param>
/// <returns>True if matching entities are found; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (context.Input is not { Entities.Length: > 0 })
return false;
FoundEntities = context.Input.Entities.Where(entity => FilterEntity(context.Input.Text, entity)).ToArray();
return FoundEntities.Length != 0;
}
/// <summary>
/// Filters an entity based on the specified criteria.
/// </summary>
/// <param name="text">The message text containing the entity.</param>
/// <param name="entity">The entity to filter.</param>
/// <returns>True if the entity matches all specified criteria; otherwise, false.</returns>
private bool FilterEntity(string? text, MessageEntity entity)
{
if (EntityType != null && entity.Type != EntityType)
return false;
if (Offset != null && entity.Offset != Offset)
return false;
if (Length != null && entity.Length != Length)
return false;
if (Content != null)
{
if (text is not { Length: > 0 })
return false;
if (!text.Substring(entity.Offset, entity.Length).Equals(Content, _stringComparison))
return false;
}
return true;
}
}
}
@@ -1,192 +0,0 @@
using Telegram.Bot.Types;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
/// <summary>
/// Abstract base class for filters that operate on the sender of replied messages.
/// Provides functionality to access and validate the user who sent the replied message.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public abstract class RepliedMessageSenderFilter(int replyDepth = 1) : RepliedMessageFilter(replyDepth)
{
/// <summary>
/// Gets the user who sent the replied message.
/// </summary>
public User User { get; private set; } = null!;
/// <summary>
/// Determines if the message can pass through the filter by validating the reply chain
/// and ensuring the replied message has a valid sender.
/// </summary>
/// <param name="context">The filter execution context containing the message.</param>
/// <returns>True if the reply chain is valid and has a sender; otherwise, false.</returns>
public override bool CanPass(FilterExecutionContext<Message> context)
{
if (!CanPassReply(context))
return false;
if (Reply.From is not { Id: > 0 } from)
return false;
User = from;
return CanPassNext(context);
}
}
/// <summary>
/// Filter that checks if the replied message sender has a specific username.
/// </summary>
/// <param name="username">The username to check for.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedUsernameFilter(string username, int replyDepth = 1) : RepliedMessageSenderFilter(replyDepth)
{
/// <summary>
/// The username to check for.
/// </summary>
private readonly string _username = username;
/// <summary>
/// The string comparison type to use for username matching.
/// </summary>
private readonly StringComparison _comparison = StringComparison.InvariantCulture;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedUsernameFilter"/> class with custom string comparison.
/// </summary>
/// <param name="username">The username to check for.</param>
/// <param name="comparison">The string comparison type to use.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedUsernameFilter(string username, StringComparison comparison, int replyDepth = 1)
: this(username, replyDepth) => _comparison = comparison;
/// <summary>
/// Checks if the replied message sender has the specified username.
/// </summary>
/// <param name="context">The filter execution context (unused).</param>
/// <returns>True if the sender has the specified username; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> context)
=> User.Username != null && User.Username.Equals(_username, _comparison);
}
/// <summary>
/// Filter that checks if the replied message sender has specific first and/or last name.
/// </summary>
/// <param name="firstName">The first name to check for.</param>
/// <param name="lastName">The last name to check for (optional).</param>
/// <param name="comparison">The string comparison type to use.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedUserFilter(string firstName, string? lastName, StringComparison comparison, int replyDepth = 1) : RepliedMessageSenderFilter(replyDepth)
{
/// <summary>
/// The first name to check for.
/// </summary>
private readonly string _firstName = firstName;
/// <summary>
/// The last name to check for (optional).
/// </summary>
private readonly string? _lastName = lastName;
/// <summary>
/// The string comparison type to use for name matching.
/// </summary>
private readonly StringComparison _comparison = comparison;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedUserFilter"/> class with first and last name.
/// </summary>
/// <param name="firstName">The first name to check for.</param>
/// <param name="lastName">The last name to check for.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedUserFilter(string firstName, string lastName, int replyDepth = 1)
: this(firstName, lastName, StringComparison.InvariantCulture, replyDepth) { }
/// <summary>
/// Initializes a new instance of the <see cref="RepliedUserFilter"/> class with first name only.
/// </summary>
/// <param name="firstName">The first name to check for.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedUserFilter(string firstName, int replyDepth = 1)
: this(firstName, null, StringComparison.InvariantCulture, replyDepth) { }
/// <summary>
/// Initializes a new instance of the <see cref="RepliedUserFilter"/> class with first name and custom comparison.
/// </summary>
/// <param name="firstName">The first name to check for.</param>
/// <param name="comparison">The string comparison type to use.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public RepliedUserFilter(string firstName, StringComparison comparison, int replyDepth = 1)
: this(firstName, null, comparison, replyDepth) { }
/// <summary>
/// Checks if the replied message sender has the specified first and/or last name.
/// </summary>
/// <param name="context">The filter execution context (unused).</param>
/// <returns>True if the sender has the specified name(s); otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (User.LastName != null)
{
if (_lastName == null)
return false;
if (!_firstName.Equals(User.LastName, _comparison))
return false;
}
return User.FirstName.Equals(_firstName, _comparison);
}
}
/// <summary>
/// Filter that checks if the replied message sender has a specific user ID.
/// </summary>
/// <param name="userId">The user ID to check for.</param>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class RepliedUserIdFilter(long userId, int replyDepth = 1) : RepliedMessageSenderFilter(replyDepth)
{
/// <summary>
/// The user ID to check for.
/// </summary>
private readonly long _userId = userId;
/// <summary>
/// Checks if the replied message sender has the specified user ID.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the sender has the specified user ID; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> User.Id == _userId;
}
/// <summary>
/// Filter that checks if the replied message was sent by a bot.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class ReplyFromBotFilter(int replyDepth = 1) : RepliedMessageSenderFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message was sent by a bot.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message was sent by a bot; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> User.IsBot;
}
/// <summary>
/// Filter that checks if the replied message was sent by a premium user.
/// </summary>
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
public class ReplyFromPremiumUserFilter(int replyDepth = 1) : RepliedMessageSenderFilter(replyDepth)
{
/// <summary>
/// Checks if the replied message was sent by a premium user.
/// </summary>
/// <param name="_">The filter execution context (unused).</param>
/// <returns>True if the replied message was sent by a premium user; otherwise, false.</returns>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> User.IsPremium;
}
}
@@ -1,123 +0,0 @@
using Telegram.Bot.Types;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
/// <summary>
/// Base class for filters that operate on the text of a replied message (the message being replied to).
/// The replyDepth parameter determines how many levels up the reply chain to search for the target message.
/// </summary>
public abstract class RepliedMessageTextFilters : RepliedMessageFilter
{
/// <summary>
/// Gets the text of the replied message (the message being replied to at the specified depth).
/// </summary>
public string Text { get; private set; } = null!;
/// <summary>
/// The content to match in the replied message.
/// </summary>
protected readonly string Content;
/// <summary>
/// The string comparison to use for matching.
/// </summary>
protected readonly StringComparison Comparison;
/// <summary>
/// Initializes a new instance of the <see cref="RepliedMessageTextFilters"/> class.
/// </summary>
/// <param name="content">The content to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
protected RepliedMessageTextFilters(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: base(replyDepth)
{
Content = content;
Comparison = comparison;
}
/// <inheritdoc/>
public override bool CanPass(FilterExecutionContext<Message> context)
{
Text = Reply.Text ?? string.Empty;
return CanPassNext(context);
}
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose text starts with the specified content.
/// </summary>
public class RepliedTextStartsWithFilter : RepliedMessageTextFilters
{
/// <summary>
/// Initializes a new instance of the <see cref="RepliedTextStartsWithFilter"/> class.
/// </summary>
/// <param name="content">The content to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedTextStartsWithFilter(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: base(content, comparison, replyDepth) { }
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Text.StartsWith(Content, Comparison);
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose text ends with the specified content.
/// </summary>
public class RepliedTextEndsWithFilter : RepliedMessageTextFilters
{
/// <summary>
/// Initializes a new instance of the <see cref="RepliedTextEndsWithFilter"/> class.
/// </summary>
/// <param name="content">The content to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedTextEndsWithFilter(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: base(content, comparison, replyDepth) { }
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Text.EndsWith(Content, Comparison);
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose text contains the specified content.
/// </summary>
public class RepliedTextContainsFilter : RepliedMessageTextFilters
{
/// <summary>
/// Initializes a new instance of the <see cref="RepliedTextContainsFilter"/> class.
/// </summary>
/// <param name="content">The content to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedTextContainsFilter(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: base(content, comparison, replyDepth) { }
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Text.IndexOf(Content, Comparison) >= 0;
}
/// <summary>
/// Filters replied messages (the message being replied to at the specified depth) whose text equals the specified content.
/// </summary>
public class RepliedTextEqualsFilter : RepliedMessageTextFilters
{
/// <summary>
/// Initializes a new instance of the <see cref="RepliedTextEqualsFilter"/> class.
/// </summary>
/// <param name="content">The content to match.</param>
/// <param name="comparison">The string comparison to use.</param>
/// <param name="replyDepth">The reply depth to search up the reply chain for the target message.</param>
public RepliedTextEqualsFilter(string content, StringComparison comparison = StringComparison.InvariantCulture, int replyDepth = 1)
: base(content, comparison, replyDepth) { }
/// <inheritdoc/>
protected override bool CanPassNext(FilterExecutionContext<Message> _)
=> Text.Equals(Content, Comparison);
}
}
-16
View File
@@ -1,16 +0,0 @@
using Telegram.Bot.Types;
using Telegrator.Filters.Components;
namespace Telegrator.Filters
{
public class RepliedToMeFilter : RepliedMessageFilter
{
protected override bool CanPassNext(FilterExecutionContext<Message> context)
{
if (Reply.From == null)
return false;
return Reply.From.Id == (context.BotInfo?.User.Id ?? throw new ArgumentNullException(nameof(context), "MentionedFilter requires BotInfo to be initialized"));
}
}
}
@@ -6,7 +6,7 @@ namespace Telegrator
/// Interface for reactive Telegram bot implementations.
/// Defines the core properties and capabilities of a reactive bot.
/// </summary>
public interface IReactiveTelegramBot
public interface ITelegratorBot
{
/// <summary>
/// Gets the update router for handling incoming updates.
+1 -1
View File
@@ -11,7 +11,7 @@ namespace Telegrator
/// Main client class for the Telegrator library.
/// Extends TelegramBotClient with reactive capabilities for handling updates.
/// </summary>
public class TelegratorClient : TelegramBotClient, IReactiveTelegramBot, ICollectingProvider
public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingProvider
{
/// <summary>
/// The update router for handling incoming updates.