Back to "Umami.नेट और बोट पता लगाना"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Umami

Umami.नेट और बोट पता लगाना

Thursday, 12 September 2024

परिचय

तो मैं एक LOT प्रकाशित किया पिछले समय में एक स्वयं---प्रबंधित वातावरण में विश्लेषण के लिए उमामी का उपयोग करने पर और यहाँ तक कि प्रकाशित किया Umami.Net Nuget pacakgeहालांकि मुझे एक समस्या थी जहां मैं अपने आरएसएस फीड के उपयोगकर्ताओं को ट्रैक करना चाहता था

समस्या

समस्या यह है कि आरएसएस फीड पाठक पास करने की कोशिश करते हैं उपयोगी फीड के अनुरोध करते समय उपयोगकर्ता एजेंट. यह अनुमति देता है अनुपालक उपयोगकर्ताओं की संख्या और फीड उपभोग करने वाले उपयोगकर्ता के प्रकार को ट्रैक करने के लिए प्रदाता बोट अनुरोधों. यह मेरे उपयोग के लिए एक समस्या है क्योंकि इसका परिणाम यह अनुरोध को अनदेखा किया जाता है और ट्रैक नहीं करता है

फीडबिन उपयोक्ता एजेंट इस तरह दिखता है

Feedbin feed-id:1234 - 21 subscribers

तो काफी उपयोगी सही, यह आपके फीड आईडी क्या है के बारे में कुछ उपयोगी विवरण पारित करता है। {"beep": "boop"} इसका अर्थ है कि यह एक बोट अनुरोध के रूप में पहचाना जाता है

जब हम इस परिवर्तन के बाद Umami Dashboard पर देखते हैं मैं अब मूल UserAgents देख सकता है जो फीड के लिए अनुरोध किया था

उमामी डैशबोर्ड

तो हम वहाँ देख सकते हैं-' अब एक गुच्छा है जो बॉट पता लगाने के ट्रिगर होगा लेकिन अब हम इन और अनुरोधों को ट्रैक कर सकते हैं

समाधान

तो क्या है इस के लिए समाधान ' मैं यह सब अनुरोधों को हस्तचालित रूप से विश्लेषण कर सकता हूँ और पता लगा सकते हैं कि Umami उन्हें एक bot के रूप में पता लगाएगा या नहीं। तो मुझे Umami तक पहुंचने से पहले अनुरोध को अवरोधित करने की जरूरत है और उपयोगकर्ता एजेंट को कुछ में बदलना होगा कि Umami विशिष्ट अनुरोधों के लिए स्वीकार करेगा

तो अब मैं अपने ट्रैकिंग विधियों में Umami में कुछ अतिरिक्त पैरामीटर जोड़ा।

विधियां

मेरे पर UmamiBackgroundSender मैंने निम्नलिखित जोड़ा:

   public async Task TrackPageView(string url, string title, UmamiPayload? payload = null,
        UmamiEventData? eventData = null, bool useDefaultUserAgent = false)

यह वहाँ सभी ट्रैकिंग विधियों पर मौजूद है और सिर्फ पर एक पैरामीटर सेट करता है UmamiPayload वस्तु.

चालू करें UmamiClient ये निम्नानुसार सेट किया जा सकता है

    [Fact]
    public async Task TrackPageView_WithUrl()
    {
        var handler = JwtResponseHandler.Create();
        var umamiClient = GetServices(handler);
        var response = await umamiClient.TrackPageViewAndDecode("https://example.com", "Example Page",
            new UmamiPayload { UseDefaultUserAgent = true });
        Assert.NotNull(response);
        Assert.Equal(UmamiDataResponse.ResponseStatus.Success, response.Status);
    }

इस परीक्षण में मैं नया उपयोग करता हूँ TrackPageViewAndDecode विधि जो एक वापस करता है UmamiDataResponse वस्तु. इस वस्तु में डीकोड किया गया JWT टोकन ( समाहित है जो अवैध है यदि यह है तो यह एक बोट है इसलिए यह जाँच करने के लिए उपयोगी है

PayloadService

यह सब में संभाला जाता है Payload सेवा जो पेलोड ऑब्जेक्ट populating के लिए उत्तरदायी है UseDefaultUserAgent सेट है

डिफ़ॉल्ट से मैं पेलोड से populate HttpContext तो आप सामान्यतः यह सेट सही मिल जाता है

    private UmamiPayload GetPayload(string? url = null, UmamiEventData? data = null)
    {
        var httpContext = httpContextAccessor.HttpContext;
        var request = httpContext?.Request;

        var payload = new UmamiPayload
        {
            Website = settings.WebsiteId,
            Data = data,
            Url = url ?? httpContext?.Request?.Path.Value,
            IpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString(),
            UserAgent = request?.Headers["User-Agent"].FirstOrDefault(),
            Referrer = request?.Headers["Referer"].FirstOrDefault(),
            Hostname = request?.Host.Host
        };

        return payload;
    }

फिर मेरे पास एक कोड टुकड़ा है जिसका नाम PopulateFromPayload जहाँ अनुरोध ऑब्जेक्ट इसे प्राप्त करता है

    public static string DefaultUserAgent =>
        $"Mozilla/5.0 (Windows 11)  Umami.Net/{Assembly.GetAssembly(typeof(UmamiClient))!.GetName().Version}";

    public UmamiPayload PopulateFromPayload(UmamiPayload? payload, UmamiEventData? data)
    {
        var newPayload = GetPayload(data: data);
        ...
        
        newPayload.UserAgent = payload.UserAgent ?? DefaultUserAgent;

        if (payload.UseDefaultUserAgent)
        {
            var userData = newPayload.Data ?? new UmamiEventData();
            userData.TryAdd("OriginalUserAgent", newPayload.UserAgent ?? "");
            newPayload.UserAgent = DefaultUserAgent;
            newPayload.Data = userData;
        }


        logger.LogInformation("Using UserAgent: {UserAgent}", newPayload.UserAgent);
     }        
        

आप देखेंगे कि यह फ़ाइल के शीर्ष पर एक नया उपयोक्ता अभिकर्ता परिभाषित करता है वर्तमान में एक bot के रूप में पता चला है). फिर विधि में यह पता लगाता है कि क्या या तो UserAgent शून्य है (जो नहीं होना चाहिए ' जब तक यह नहीं होता है तब तक कि यह ' HttpContext के बिना कोड से बुलाया जाता है UseDefaultUserAgent सेट है. यदि यह है तो यह मूलभूत userAgent को सेट करता है और डेटा ऑब्जेक्ट में मूल UserAgent जोड़ता है

यह तब लॉग किया जाता है ताकि आप देख सकते हैं कि क्या UserAgent का उपयोग किया जा रहा है

प्रतिक्रिया को डिकोडिंग

में Umami .Net 0.3.0 मैं एक नए तरीके जोड़ा है जो एक वापस करने के लिए UmamiDataResponse वस्तु. इस वस्तु में कूटबद्ध JWT टोकन है

    public async Task<UmamiDataResponse?> TrackPageViewAndDecode(
        string? url = "",
        string? title = "",
        UmamiPayload? payload = null,
        UmamiEventData? eventData = null)
    {
        var response = await TrackPageView(url, title, payload, eventData);
        return await DecodeResponse(response);
    }
    
        private async Task<UmamiDataResponse?> DecodeResponse(HttpResponseMessage responseMessage)
    {
        var responseString = await responseMessage.Content.ReadAsStringAsync();

        switch (responseMessage.IsSuccessStatusCode)
        {
            case false:
                return new UmamiDataResponse(UmamiDataResponse.ResponseStatus.Failed);
            case true when responseString.Contains("beep") && responseString.Contains("boop"):
                logger.LogWarning("Bot detected data not stored in Umami");
                return new UmamiDataResponse(UmamiDataResponse.ResponseStatus.BotDetected);

            case true:
                var decoded = await jwtDecoder.DecodeResponse(responseString);
                if (decoded == null)
                {
                    logger.LogError("Failed to decode response from Umami");
                    return null;
                }

                var payload = UmamiDataResponse.Decode(decoded);

                return payload;
        }
    }

आप देख सकते हैं कि यह सामान्य में कॉल करता है TrackPageView विधि फिर एक विधि बुलाया जाता है DecodeResponse जो प्रतिक्रिया के लिए जाँच करता है beep और boop स्ट्रिंग (बॉट पता लगाने के लिए). यदि यह उन्हें पाता है तो यह एक चेतावनी लॉग करता है और एक वापस करता है BotDetected स्थिति. यदि यह उन्हें नहीं पाता है तो यह JWT टोकन को डिकोड करता है और पेलोड वापस देता है

JWT टोकन स्वयं बस एक आधार 64 एनकोडित स्ट्रिंग है जो उन डेटा को समाविष्ट करता है कि Umami भंडारित किया गया है | . | यह डिकोड किया जाता है और एक के रूप में वापस UmamiDataResponse वस्तु.

इसके लिए पूरा स्रोत नीचे है

Response Decoder
using System.IdentityModel.Tokens.Jwt;

namespace Umami.Net.Models;

public class UmamiDataResponse
{
    public enum ResponseStatus
    {
        Failed,
        BotDetected,
        Success
    }

    public UmamiDataResponse(ResponseStatus status)
    {
        Status = status;
    }

    public ResponseStatus Status { get; set; }

    public Guid Id { get; set; }
    public Guid WebsiteId { get; set; }
    public string? Hostname { get; set; }
    public string? Browser { get; set; }
    public string? Os { get; set; }
    public string? Device { get; set; }
    public string? Screen { get; set; }
    public string? Language { get; set; }
    public string? Country { get; set; }
    public string? Subdivision1 { get; set; }
    public string? Subdivision2 { get; set; }
    public string? City { get; set; }
    public DateTime CreatedAt { get; set; }
    public Guid VisitId { get; set; }
    public long Iat { get; set; }

    public static UmamiDataResponse Decode(JwtPayload? payload)
    {
        if (payload == null) return new UmamiDataResponse(ResponseStatus.Failed);
        payload.TryGetValue("visitId", out var visitIdObj);
        payload.TryGetValue("iat", out var iatObj);
        //This should only happen then the payload is dummy.
        if (payload.Count == 2)
        {
            var visitId = visitIdObj != null ? Guid.Parse(visitIdObj.ToString()!) : Guid.Empty;
            var iat = iatObj != null ? long.Parse(iatObj.ToString()!) : 0;

            return new UmamiDataResponse(ResponseStatus.Success)
            {
                VisitId = visitId,
                Iat = iat
            };
        }

        payload.TryGetValue("id", out var idObj);
        payload.TryGetValue("websiteId", out var websiteIdObj);
        payload.TryGetValue("hostname", out var hostnameObj);
        payload.TryGetValue("browser", out var browserObj);
        payload.TryGetValue("os", out var osObj);
        payload.TryGetValue("device", out var deviceObj);
        payload.TryGetValue("screen", out var screenObj);
        payload.TryGetValue("language", out var languageObj);
        payload.TryGetValue("country", out var countryObj);
        payload.TryGetValue("subdivision1", out var subdivision1Obj);
        payload.TryGetValue("subdivision2", out var subdivision2Obj);
        payload.TryGetValue("city", out var cityObj);
        payload.TryGetValue("createdAt", out var createdAtObj);

        return new UmamiDataResponse(ResponseStatus.Success)
        {
            Id = idObj != null ? Guid.Parse(idObj.ToString()!) : Guid.Empty,
            WebsiteId = websiteIdObj != null ? Guid.Parse(websiteIdObj.ToString()!) : Guid.Empty,
            Hostname = hostnameObj?.ToString(),
            Browser = browserObj?.ToString(),
            Os = osObj?.ToString(),
            Device = deviceObj?.ToString(),
            Screen = screenObj?.ToString(),
            Language = languageObj?.ToString(),
            Country = countryObj?.ToString(),
            Subdivision1 = subdivision1Obj?.ToString(),
            Subdivision2 = subdivision2Obj?.ToString(),
            City = cityObj?.ToString(),
            CreatedAt = createdAtObj != null ? DateTime.Parse(createdAtObj.ToString()!) : DateTime.MinValue,
            VisitId = visitIdObj != null ? Guid.Parse(visitIdObj.ToString()!) : Guid.Empty,
            Iat = iatObj != null ? long.Parse(iatObj.ToString()!) : 0
        };
    }
}
आप देख सकते हैं कि यह Umami द्वारा भंडारित अनुरोध के बारे में उपयोगी सूचनाओं का एक गुच्छा समाहित करता है
    public Guid Id { get; set; }
    public Guid WebsiteId { get; set; }
    public string? Hostname { get; set; }
    public string? Browser { get; set; }
    public string? Os { get; set; }
    public string? Device { get; set; }
    public string? Screen { get; set; }
    public string? Language { get; set; }
    public string? Country { get; set; }
    public string? Subdivision1 { get; set; }
    public string? Subdivision2 { get; set; }
    public string? City { get; set; }
    public DateTime CreatedAt { get; set; }
    public Guid VisitId { get; set; }
    public long Iat { get; set; }

अंत में

तो सिर्फ एक छोटा पोस्ट Umami में कुछ नए प्रकार्य को शामिल करता है.नेट 0.4.0 जो आपको विशिष्ट अनुरोधों के लिए डिफ़ॉल्ट उपयोक्ता एजेंट निर्दिष्ट करने देता है

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.