< Summary

Line coverage
92%
Covered lines: 291
Uncovered lines: 23
Coverable lines: 314
Total lines: 1229
Line coverage: 92.6%
Branch coverage
84%
Covered branches: 133
Total branches: 158
Branch coverage: 84.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/_/src/TeleFlow.Framework/Internal/Handlers/TelegramHandlerSelector.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Collections.ObjectModel;
 3using System.Globalization;
 4using System.Reflection;
 5using System.Text.Json;
 6using System.Text.RegularExpressions;
 7using Microsoft.Extensions.Logging;
 8using TeleFlow.Annotations;
 9using TeleFlow.Framework.Callbacks;
 10using TeleFlow.Telegram.Internal;
 11using TeleFlow.Telegram.Schema.Types;
 12
 13namespace TeleFlow.Telegram.Internal.Handlers;
 14
 15/// <summary>
 16/// Matches incoming Telegram contexts to registered handler routes and binds route
 17/// values or typed callback payloads before the dispatcher invokes a handler.
 18/// </summary>
 19internal sealed partial class TelegramHandlerSelector
 20{
 121    private static readonly IReadOnlyDictionary<string, object?> EmptyRouteValues =
 122        new ReadOnlyDictionary<string, object?>(
 123            new Dictionary<string, object?>(StringComparer.Ordinal));
 124    private static readonly ConcurrentDictionary<Type, CallbackPayloadDeserializer> CallbackPayloadDeserializers = new()
 25
 26    private readonly TelegramHandlerTable _table;
 27    private readonly TelegramBotIdentity _botIdentity;
 28    private readonly ILogger<TelegramHandlerSelector> _logger;
 29
 30    public TelegramHandlerSelector(
 31        TelegramHandlerTable table,
 32        TelegramBotIdentity botIdentity,
 33        ILoggerFactory loggerFactory)
 34    {
 20135        ArgumentNullException.ThrowIfNull(table);
 20136        ArgumentNullException.ThrowIfNull(botIdentity);
 20137        ArgumentNullException.ThrowIfNull(loggerFactory);
 38
 20139        _table = table;
 20140        _botIdentity = botIdentity;
 20141        _logger = loggerFactory.CreateLogger<TelegramHandlerSelector>();
 20142    }
 43
 34444    public bool HasStatefulHandlers => _table.HasStatefulHandlers;
 45
 46    public async ValueTask<TelegramRouteSelection?> SelectMessageHandlerAsync(
 47        MessageContext context,
 48        string? currentState,
 49        CancellationToken cancellationToken)
 50    {
 25851        var selection = await SelectMessageHandlerAsync(
 25852            context,
 25853            _table.CommandHandlerCandidates,
 25854            currentState,
 25855            cancellationToken).ConfigureAwait(false);
 56
 25857        if (selection is not null)
 58        {
 8059            return selection;
 60        }
 61
 17862        return await SelectMessageHandlerAsync(
 17863            context,
 17864            _table.MessageHandlerCandidates,
 17865            currentState,
 17866            cancellationToken).ConfigureAwait(false);
 25667    }
 68
 69    public async ValueTask<TelegramRouteSelection?> SelectCallbackHandlerAsync(
 70        CallbackQueryContext context,
 71        string? currentState,
 72        CancellationToken cancellationToken)
 73    {
 7274        if (HasCurrentState(currentState))
 75        {
 176            var state = currentState!;
 177            var statefulPayloadSelection = await SelectCallbackHandlerPassAsync(
 178                context,
 179                _table.CallbackHandlerCandidates.GetStatefulTypedCandidates(state),
 180                cancellationToken).ConfigureAwait(false);
 81
 182            if (statefulPayloadSelection is not null)
 83            {
 084                return statefulPayloadSelection;
 85            }
 86
 187            var statefulRawSelection = await SelectCallbackHandlerPassAsync(
 188                context,
 189                _table.CallbackHandlerCandidates.GetStatefulRawCandidates(state),
 190                cancellationToken).ConfigureAwait(false);
 91
 192            if (statefulRawSelection is not null)
 93            {
 194                return statefulRawSelection;
 95            }
 096        }
 97
 7198        var statelessPayloadSelection = await SelectCallbackHandlerPassAsync(
 7199            context,
 71100            _table.CallbackHandlerCandidates.StatelessTyped,
 71101            cancellationToken).ConfigureAwait(false);
 102
 69103        if (statelessPayloadSelection is not null)
 104        {
 9105            return statelessPayloadSelection;
 106        }
 107
 60108        return await SelectCallbackHandlerPassAsync(
 60109            context,
 60110            _table.CallbackHandlerCandidates.StatelessRaw,
 60111            cancellationToken).ConfigureAwait(false);
 70112    }
 113
 114    public async ValueTask<TelegramRouteSelection?> SelectChatMemberHandlerAsync(
 115        ChatMemberUpdatedContext context,
 116        TelegramRouteKind updateRouteKind,
 117        string? currentState,
 118        CancellationToken cancellationToken)
 119    {
 14120        if (HasCurrentState(currentState))
 121        {
 0122            var state = currentState!;
 0123            var statefulSelection = await SelectChatMemberHandlerPassAsync(
 0124                context,
 0125                _table.ChatMemberHandlerCandidates.GetStatefulCandidates(state),
 0126                updateRouteKind,
 0127                cancellationToken).ConfigureAwait(false);
 128
 0129            if (statefulSelection is not null)
 130            {
 0131                return statefulSelection;
 132            }
 133        }
 134
 14135        return await SelectChatMemberHandlerPassAsync(
 14136            context,
 14137            _table.ChatMemberHandlerCandidates.Stateless,
 14138            updateRouteKind,
 14139            cancellationToken).ConfigureAwait(false);
 14140    }
 141
 142    private async ValueTask<TelegramRouteSelection?> SelectMessageHandlerAsync(
 143        MessageContext context,
 144        TelegramHandlerCandidateSet candidates,
 145        string? currentState,
 146        CancellationToken cancellationToken)
 147    {
 436148        if (HasCurrentState(currentState))
 149        {
 39150            var state = currentState!;
 39151            var statefulSelection = await SelectMessageHandlerPassAsync(
 39152                context,
 39153                candidates.GetStatefulCandidates(state),
 39154                cancellationToken).ConfigureAwait(false);
 155
 39156            if (statefulSelection is not null)
 157            {
 17158                return statefulSelection;
 159            }
 160        }
 161
 419162        return await SelectMessageHandlerPassAsync(
 419163            context,
 419164            candidates.Stateless,
 419165            cancellationToken).ConfigureAwait(false);
 434166    }
 167
 168    private async ValueTask<TelegramRouteSelection?> SelectMessageHandlerPassAsync(
 169        MessageContext context,
 170        IReadOnlyList<TelegramHandlerCandidate> candidates,
 171        CancellationToken cancellationToken)
 172    {
 2408173        for (var index = 0; index < candidates.Count; index++)
 174        {
 991175            var candidate = candidates[index];
 991176            var route = candidate.Route;
 177
 991178            if (!TryMatchRoute(context.TelegramMessage, route, out var routeValues))
 179            {
 180                continue;
 181            }
 182
 396183            if (!await TelegramFilterEvaluator.MatchesAsync(
 396184                    context,
 396185                    candidate.Filters,
 396186                    cancellationToken).ConfigureAwait(false))
 187            {
 151188                LogRejectedByFiltersIfEnabled(context, candidate, route);
 151189                continue;
 190            }
 191
 243192            return new TelegramRouteSelection(candidate.Handler, route, routeValues, callbackPayload: null);
 193        }
 194
 213195        return null;
 456196    }
 197
 198    private async ValueTask<TelegramRouteSelection?> SelectCallbackHandlerPassAsync(
 199        CallbackQueryContext context,
 200        IReadOnlyList<TelegramHandlerCandidate> candidates,
 201        CancellationToken cancellationToken)
 202    {
 456203        for (var index = 0; index < candidates.Count; index++)
 204        {
 162205            var candidate = candidates[index];
 162206            var route = candidate.Route;
 207
 162208            if (route.CallbackPayloadType is null)
 209            {
 132210                if (!await TelegramFilterEvaluator.MatchesAsync(
 132211                        context,
 132212                        candidate.Filters,
 132213                        cancellationToken).ConfigureAwait(false))
 214                {
 76215                    LogRejectedByFiltersIfEnabled(context, candidate, route);
 76216                    continue;
 217                }
 218
 56219                return new TelegramRouteSelection(candidate.Handler, route, EmptyRouteValues, callbackPayload: null);
 220            }
 221
 30222            if (string.IsNullOrWhiteSpace(context.TelegramCallbackQuery.Data))
 223            {
 224                continue;
 225            }
 226
 227            object? callbackPayload;
 228
 229            try
 230            {
 30231                if (!TryDeserializeCallbackPayload(
 30232                        context,
 30233                        route.CallbackPayloadType,
 30234                        out callbackPayload))
 235                {
 17236                    continue;
 237                }
 9238            }
 2239            catch (CallbackDataRouteDeserializationException exception)
 240            {
 2241                if (_logger.IsEnabled(LogLevel.Warning))
 242                {
 2243                    LogCallbackDataDeserializationFailed(
 2244                        _logger,
 2245                        exception,
 2246                        context.Update.UpdateId,
 2247                        exception.PayloadType.FullName ?? exception.PayloadType.Name,
 2248                        TelegramUpdateLogFormatter.FormatHandler(candidate.Handler),
 2249                        TelegramUpdateLogFormatter.FormatRoute(route),
 2250                        exception.PayloadByteCount);
 251                }
 252
 2253                continue;
 254            }
 255
 9256            if (!await TelegramFilterEvaluator.MatchesAsync(
 9257                    context,
 9258                    candidate.Filters,
 9259                    cancellationToken).ConfigureAwait(false))
 260            {
 0261                LogRejectedByFiltersIfEnabled(context, candidate, route);
 0262                continue;
 263            }
 264
 9265            return new TelegramRouteSelection(candidate.Handler, route, EmptyRouteValues, callbackPayload);
 266        }
 267
 66268        return null;
 131269    }
 270
 271    private async ValueTask<TelegramRouteSelection?> SelectChatMemberHandlerPassAsync(
 272        ChatMemberUpdatedContext context,
 273        IReadOnlyList<TelegramHandlerCandidate> candidates,
 274        TelegramRouteKind updateRouteKind,
 275        CancellationToken cancellationToken)
 276    {
 38277        for (var index = 0; index < candidates.Count; index++)
 278        {
 19279            var candidate = candidates[index];
 19280            var route = candidate.Route;
 281
 19282            if (route.RouteKind != updateRouteKind)
 283            {
 284                continue;
 285            }
 286
 19287            if (!MatchesChatMemberTransition(context, route))
 288            {
 289                continue;
 290            }
 291
 14292            if (!await TelegramFilterEvaluator.MatchesAsync(
 14293                    context,
 14294                    candidate.Filters,
 14295                    cancellationToken).ConfigureAwait(false))
 296            {
 0297                LogRejectedByFiltersIfEnabled(context, candidate, route);
 0298                continue;
 299            }
 300
 14301            return new TelegramRouteSelection(candidate.Handler, route, EmptyRouteValues, callbackPayload: null);
 302        }
 303
 0304        return null;
 14305    }
 306
 307    private void LogRejectedByFiltersIfEnabled(
 308        TelegramUpdateContext context,
 309        TelegramHandlerCandidate candidate,
 310        TelegramRouteDescriptor route)
 311    {
 227312        if (!_logger.IsEnabled(LogLevel.Debug))
 313        {
 226314            return;
 315        }
 316
 1317        LogHandlerRejectedByFilters(
 1318            _logger,
 1319            context.Update.UpdateId,
 1320            TelegramUpdateLogFormatter.GetUpdateType(context.Update),
 1321            TelegramUpdateLogFormatter.FormatHandler(candidate.Handler),
 1322            TelegramUpdateLogFormatter.FormatRoute(route));
 1323    }
 324
 325    private bool TryMatchRoute(
 326        Message message,
 327        TelegramRouteDescriptor route,
 328        out IReadOnlyDictionary<string, object?> routeValues)
 329    {
 991330        routeValues = EmptyRouteValues;
 331
 991332        return route.RouteKind switch
 991333        {
 314334            TelegramRouteKind.MessageAny => MatchesTextFilters(message, route),
 123335            TelegramRouteKind.TextExact => MatchesTextFilters(message, route),
 30336            TelegramRouteKind.TextTemplate => TryMatchTextPattern(message.Text, route, isTemplate: true, out routeValues
 3337            TelegramRouteKind.TextRegex => TryMatchTextPattern(message.Text, route, isTemplate: false, out routeValues),
 438338            TelegramRouteKind.CommandExact => TryGetCommandBody(message.Text, route, out var body, out var isPrefixLess)
 438339                                               TryMatchExactCommand(body, route, isPrefixLess),
 78340            TelegramRouteKind.CommandTemplate => TryGetCommandBody(message.Text, route, out var body, out _) &&
 78341                                                 TryMatchCommandPattern(body, route, isTemplate: true, out routeValues),
 5342            TelegramRouteKind.CommandRegex => TryGetCommandBody(message.Text, route, out var body, out _) &&
 5343                                              TryMatchCommandPattern(body, route, isTemplate: false, out routeValues),
 0344            _ => false
 991345        };
 346    }
 347
 348    private static bool HasCurrentState(string? currentState)
 349    {
 522350        return !string.IsNullOrWhiteSpace(currentState);
 351    }
 352
 353    private static bool TryDeserializeCallbackPayload(
 354        CallbackQueryContext context,
 355        Type payloadType,
 356        out object? payload)
 357    {
 30358        var data = context.TelegramCallbackQuery.Data!;
 359
 30360        if (context.CallbackData is ICallbackDataRouteDeserializer routeDeserializer)
 361        {
 362            try
 363            {
 26364                return routeDeserializer.TryDeserializeForRoute(payloadType, data, out payload);
 365            }
 16366            catch (Exception exception) when (IsCallbackPayloadNoMatchException(exception))
 367            {
 14368                payload = null;
 14369                return false;
 370            }
 371        }
 372
 4373        var deserializer = CallbackPayloadDeserializers.GetOrAdd(payloadType, CreateCallbackPayloadDeserializer);
 374
 375        try
 376        {
 4377            payload = deserializer(context.CallbackData, data);
 1378            return true;
 379        }
 3380        catch (Exception exception) when (IsCallbackPayloadNoMatchException(exception))
 381        {
 1382            payload = null;
 1383            return false;
 384        }
 26385    }
 386
 387    private static bool IsCallbackPayloadNoMatchException(Exception exception)
 388    {
 19389        return exception is JsonException or FormatException or OverflowException;
 390    }
 391
 392    private static CallbackPayloadDeserializer CreateCallbackPayloadDeserializer(Type payloadType)
 393    {
 1394        var deserializeMethod = typeof(TelegramHandlerSelector)
 1395            .GetMethod(nameof(DeserializeCallbackPayload), BindingFlags.NonPublic | BindingFlags.Static)!
 1396            .MakeGenericMethod(payloadType);
 397
 1398        return deserializeMethod.CreateDelegate<CallbackPayloadDeserializer>();
 399    }
 400
 401    private static object? DeserializeCallbackPayload<TPayload>(
 402        ICallbackDataSerializer serializer,
 403        string data)
 404    {
 4405        return serializer.Deserialize<TPayload>(data);
 406    }
 407
 408    private bool TryGetCommandBody(
 409        string? text,
 410        TelegramRouteDescriptor route,
 411        out string commandBody,
 412        out bool isPrefixLess)
 413    {
 521414        commandBody = string.Empty;
 521415        isPrefixLess = false;
 416
 521417        if (string.IsNullOrWhiteSpace(text))
 418        {
 0419            return false;
 420        }
 421
 521422        switch (route.CommandPolicy.PrefixMode)
 423        {
 424            case CommandPrefixMode.Required:
 435425                return TryGetPrefixedCommandBody(text, route, out commandBody);
 426
 427            case CommandPrefixMode.Optional:
 80428                if (TryGetPrefixedCommandBody(text, route, out commandBody))
 429                {
 17430                    return true;
 431                }
 432
 63433                isPrefixLess = true;
 63434                return TryGetPrefixLessCommandBody(text, out commandBody);
 435
 436            case CommandPrefixMode.NoPrefix:
 6437                isPrefixLess = true;
 6438                return TryGetPrefixLessCommandBody(text, out commandBody);
 439
 440            default:
 0441                return false;
 442        }
 443    }
 444
 445    private bool TryGetPrefixedCommandBody(
 446        string text,
 447        TelegramRouteDescriptor route,
 448        out string commandBody)
 449    {
 515450        commandBody = string.Empty;
 451
 1948452        foreach (var prefix in route.CommandPolicy.Prefixes)
 453        {
 549454            if (!text.StartsWith(prefix, route.CommandPolicy.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringCom
 455            {
 456                continue;
 457            }
 458
 180459            commandBody = text[prefix.Length..];
 460
 180461            if (route.CommandPolicy.AllowSpaceAfterPrefix)
 462            {
 3463                commandBody = commandBody.TrimStart(' ', '\t');
 464            }
 465
 180466            if (prefix == "/")
 467            {
 169468                if (!TryTrimSlashCommandBotMention(commandBody, out commandBody))
 469                {
 25470                    return false;
 471                }
 472            }
 473
 155474            return !string.IsNullOrWhiteSpace(commandBody);
 475        }
 476
 335477        return false;
 180478    }
 479
 480    private static bool TryGetPrefixLessCommandBody(
 481        string text,
 482        out string commandBody)
 483    {
 69484        commandBody = text;
 485
 69486        return !string.IsNullOrWhiteSpace(commandBody);
 487    }
 488
 489    private bool TryTrimSlashCommandBotMention(
 490        string commandBody,
 491        out string trimmedCommandBody)
 492    {
 169493        var tokenEnd = commandBody.IndexOfAny([' ', '\t', '\r', '\n']);
 169494        var token = tokenEnd < 0 ? commandBody : commandBody[..tokenEnd];
 169495        var mentionIndex = token.IndexOf('@', StringComparison.Ordinal);
 496
 169497        if (mentionIndex < 0)
 498        {
 140499            trimmedCommandBody = commandBody;
 140500            return true;
 501        }
 502
 29503        if (!_botIdentity.MatchesMention(token.AsSpan()[(mentionIndex + 1)..]))
 504        {
 25505            trimmedCommandBody = string.Empty;
 25506            return false;
 507        }
 508
 4509        var trimmedToken = token[..mentionIndex];
 510
 4511        trimmedCommandBody = tokenEnd < 0
 4512            ? trimmedToken
 4513            : trimmedToken + commandBody[tokenEnd..];
 4514        return true;
 515    }
 516
 517    private static bool TryMatchExactCommand(
 518        string commandBody,
 519        TelegramRouteDescriptor route,
 520        bool isPrefixLess)
 521    {
 168522        if (string.IsNullOrWhiteSpace(route.Pattern))
 523        {
 0524            return false;
 525        }
 526
 168527        if (isPrefixLess)
 528        {
 33529            return string.Equals(
 33530                TelegramCommandTextNormalizer.Normalize(commandBody.Trim()),
 33531                route.Pattern,
 33532                route.CommandPolicy.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
 533        }
 534
 135535        var tokenEnd = commandBody.IndexOfAny([' ', '\t', '\r', '\n']);
 135536        var token = tokenEnd < 0 ? commandBody : commandBody[..tokenEnd];
 537
 135538        return string.Equals(
 135539            TelegramCommandTextNormalizer.Normalize(token),
 135540            route.Pattern,
 135541            route.CommandPolicy.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
 542    }
 543
 544    private static bool TryMatchTextPattern(
 545        string? text,
 546        TelegramRouteDescriptor route,
 547        bool isTemplate,
 548        out IReadOnlyDictionary<string, object?> routeValues)
 549    {
 33550        routeValues = EmptyRouteValues;
 551
 33552        if (string.IsNullOrWhiteSpace(text))
 553        {
 0554            return false;
 555        }
 556
 33557        return TryMatchPattern(text, route, isTemplate, route.CommandPolicy.IgnoreCase, out routeValues);
 558    }
 559
 560    private static bool TryMatchCommandPattern(
 561        string commandBody,
 562        TelegramRouteDescriptor route,
 563        bool isTemplate,
 564        out IReadOnlyDictionary<string, object?> routeValues)
 565    {
 56566        var value = isTemplate
 56567            ? TelegramCommandTextNormalizer.Normalize(commandBody)
 56568            : commandBody;
 569
 56570        return TryMatchPattern(value, route, isTemplate, route.CommandPolicy.IgnoreCase, out routeValues);
 571    }
 572
 573    private static bool TryMatchPattern(
 574        string value,
 575        TelegramRouteDescriptor route,
 576        bool isTemplate,
 577        bool ignoreCase,
 578        out IReadOnlyDictionary<string, object?> routeValues)
 579    {
 89580        routeValues = EmptyRouteValues;
 581
 89582        if (string.IsNullOrWhiteSpace(route.Pattern))
 583        {
 0584            return false;
 585        }
 586
 89587        var regex = route.Matcher.Regex ?? (isTemplate
 89588            ? TelegramTemplateRouteParser.BuildRegex(route.Pattern, ignoreCase)
 89589            : new Regex(route.Pattern, TelegramTemplateRouteParser.GetRegexOptions(ignoreCase)));
 89590        var match = regex.Match(value);
 591
 89592        if (!match.Success)
 593        {
 57594            return false;
 595        }
 596
 32597        return TryBindRouteValues(route, match, out routeValues);
 598    }
 599
 600    private static bool TryBindRouteValues(
 601        TelegramRouteDescriptor route,
 602        Match match,
 603        out IReadOnlyDictionary<string, object?> routeValues)
 604    {
 32605        routeValues = EmptyRouteValues;
 606
 32607        if (route.RouteValues.Count == 0)
 608        {
 4609            return true;
 610        }
 611
 28612        var values = new Dictionary<string, object?>(StringComparer.Ordinal);
 613
 110614        foreach (var valueDescriptor in route.RouteValues)
 615        {
 28616            var group = match.Groups[valueDescriptor.Name];
 617
 28618            if (!group.Success)
 619            {
 4620                if (!valueDescriptor.IsOptional)
 621                {
 1622                    return false;
 623                }
 624
 3625                values[valueDescriptor.Name] = null;
 3626                continue;
 627            }
 628
 24629            if (!TryConvertRouteValue(valueDescriptor, group.Value, out var convertedValue))
 630            {
 1631                return false;
 632            }
 633
 23634            values[valueDescriptor.Name] = convertedValue;
 635        }
 636
 26637        routeValues = values;
 26638        return true;
 2639    }
 640
 641    private static bool TryConvertRouteValue(
 642        TelegramRouteValueDescriptor descriptor,
 643        string value,
 644        out object? convertedValue)
 645    {
 24646        if (descriptor.ValueType == typeof(string))
 647        {
 4648            convertedValue = value;
 4649            return true;
 650        }
 651
 20652        if (descriptor.ValueType == typeof(int) &&
 20653            int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
 654        {
 9655            convertedValue = intValue;
 9656            return true;
 657        }
 658
 11659        if (descriptor.ValueType == typeof(long) &&
 11660            long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue))
 661        {
 10662            convertedValue = longValue;
 10663            return true;
 664        }
 665
 1666        convertedValue = null;
 667
 1668        if (descriptor.ValueType == typeof(int) ||
 1669            descriptor.ValueType == typeof(long))
 670        {
 1671            return false;
 672        }
 673
 0674        throw new InvalidOperationException(
 0675            $"Route value '{descriptor.Name}' uses unsupported type {descriptor.ValueType.Name}.");
 676    }
 677
 678    private static bool MatchesTextFilters(Message message, TelegramRouteDescriptor route)
 679    {
 437680        var textFilters = route.TextFilters;
 681
 914682        for (var index = 0; index < textFilters.Count; index++)
 683        {
 152684            if (!textFilters[index].Matches(message.Text))
 685            {
 132686                return false;
 687            }
 688        }
 689
 305690        return true;
 691    }
 692
 693    private static bool MatchesChatMemberTransition(
 694        ChatMemberUpdatedContext context,
 695        TelegramRouteDescriptor route)
 696    {
 19697        if (route.ChatMemberTransitions.Count == 0)
 698        {
 5699            return true;
 700        }
 701
 14702        var oldStatus = TelegramChatMemberClassifier.GetStatus(context.OldChatMember);
 14703        var newStatus = TelegramChatMemberClassifier.GetStatus(context.NewChatMember);
 14704        var transitions = route.ChatMemberTransitions;
 705
 38706        for (var index = 0; index < transitions.Count; index++)
 707        {
 14708            var transition = transitions[index];
 709
 14710            if ((transition.OldStatus & oldStatus) != 0 &&
 14711                (transition.NewStatus & newStatus) != 0)
 712            {
 9713                return true;
 714            }
 715        }
 716
 5717        return false;
 718    }
 719
 720    private delegate object? CallbackPayloadDeserializer(ICallbackDataSerializer serializer, string data);
 721}

/_/src/TeleFlow.Framework/obj/Release/net10.0/Microsoft.Extensions.Logging.Generators/Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator/LoggerMessage.g.cs

File '/_/src/TeleFlow.Framework/obj/Release/net10.0/Microsoft.Extensions.Logging.Generators/Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator/LoggerMessage.g.cs' does not exist (any more).

Methods/Properties

.cctor()
.ctor(TeleFlow.Telegram.Internal.Handlers.TelegramHandlerTable,TeleFlow.Telegram.Internal.TelegramBotIdentity,Microsoft.Extensions.Logging.ILoggerFactory)
get_HasStatefulHandlers()
SelectMessageHandlerAsync()
SelectCallbackHandlerAsync()
SelectChatMemberHandlerAsync()
SelectMessageHandlerAsync()
SelectMessageHandlerPassAsync()
SelectCallbackHandlerPassAsync()
SelectChatMemberHandlerPassAsync()
LogRejectedByFiltersIfEnabled(TeleFlow.Telegram.TelegramUpdateContext,TeleFlow.Telegram.Internal.Handlers.TelegramHandlerCandidate,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor)
TryMatchRoute(TeleFlow.Telegram.Schema.Types.Message,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>&)
HasCurrentState(System.String)
TryDeserializeCallbackPayload(TeleFlow.Telegram.CallbackQueryContext,System.Type,System.Object&)
IsCallbackPayloadNoMatchException(System.Exception)
CreateCallbackPayloadDeserializer(System.Type)
DeserializeCallbackPayload(TeleFlow.Framework.Callbacks.ICallbackDataSerializer,System.String)
TryGetCommandBody(System.String,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.String&,System.Boolean&)
TryGetPrefixedCommandBody(System.String,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.String&)
TryGetPrefixLessCommandBody(System.String,System.String&)
TryTrimSlashCommandBotMention(System.String,System.String&)
TryMatchExactCommand(System.String,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.Boolean)
TryMatchTextPattern(System.String,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.Boolean,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>&)
TryMatchCommandPattern(System.String,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.Boolean,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>&)
TryMatchPattern(System.String,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.Boolean,System.Boolean,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>&)
TryBindRouteValues(TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor,System.Text.RegularExpressions.Match,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>&)
TryConvertRouteValue(TeleFlow.Telegram.Internal.Handlers.TelegramRouteValueDescriptor,System.String,System.Object&)
MatchesTextFilters(TeleFlow.Telegram.Schema.Types.Message,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor)
MatchesChatMemberTransition(TeleFlow.Telegram.ChatMemberUpdatedContext,TeleFlow.Telegram.Internal.Handlers.TelegramRouteDescriptor)
.cctor()