# Umami.Net和植物探测

# 一. 导言导言 导言 导言 导言

因此,I've [张贴的LOT](/blog/category/Umami) @-_在自己托管的环境中使用Umami进行分析, [Umami. Net Nuget Pakakge 网](https://www.nuget.org/packages/Umami.Net/)@._#然而我想追踪我的RSS feed;的用户,

[TOC]

<!--category-- ASP.NET, Umami -->
<datetime class="hidden">2024-09-12T14:50</datetime>

# 问题

问题在于 RSS 种子阅读器试图通过 *有用* 用户在请求 feed @ .} 时使用代理器 **遵守情况** 用于追踪用户数量和消费种子的用户类型的提供者@.}然而,}这也意味着Umami将确认这些请求为 *立方体* 这是需要我使用的一个问题, 因为它导致请求被忽略而不被追踪@.}

Feedbin 用户代理器看起来像此@ : @%

```plaintext
Feedbin feed-id:1234 - 21 subscribers
```

相当实用的右转@,}它传递一些有用的细节, 说明您的 feed id 是 @,}用户数量和使用者代理 *%.}然而这也是个问题 因为这意味着 Umami 会忽略请求;}事实上 它会返回一个“200”状态 但内容包含 `{"beep": "boop"}` 意思是这被确定为 bot 请求@. @ this is a bot request as I can '}%t 处理普通错误处理 {(}_it'}s a @200,}不要说“403}等=).}。

当我们在做出这一修改后查看 Umami Dashboard 时, 我现在可以看到原始用户 Agency 请求 feed\ : {}

![Umami 仪表板](user-agents.png?width=600&format=webp&quality=30)

这样我们就能看到现在有一堆 触发机器人检测的机器 但是我们现在可以看见这些 然后追踪请求

# 解决方案

@ ' @ @ maks@ msk0 @ plackage@ MS K1} 我手动分析所有这些请求并检测 Umami是否会将它们检测成一个bot;} 它会使用 IsBot < (\ @ httpsóms://}#www>.npmjs}/}MSQ}MSK8_ package{/isbortMSQK10}来检测请求是否是博特或非博特( MSSK11) 。 @ there @ MPK12}没有 C□#} 等值, 以及它是否正在更改列表, 这样我就可以使用“ MSC15” 甚至将来的列表@(}我可能会变得聪明一些, 并且用此列表检测请求是不是博特或者不是).}
所以我需要截听请求 在它到达乌马米之前 将用户代理 改为U马米会接受的具体要求

所以现在我为我的追踪方法添加了一些额外的参数, 在 Umami.NetMS K1 中这些允许您指定新的“ 'Default用户” 代理@' 将发送到 Umami 而不是原始的用户 Agent. 这让我可以具体说明用户代理人应该更改为特定请求的具体值 @.}

## 方法和方法

在我的上 `UmamiBackgroundSender` 我添加了以下内容:

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

它存在于所有的跟踪方法上 仅设置一个参数 `UmamiPayload` 对象@. @%

于 `UmamiClient` 这些可设定如下: @:}

```csharp
    [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 符号 @ ( n如果它是一个bot, 它将无效 。 这样可以检查@ MS K3 和请求的状态=.\ {

## `PayloadService`

这一切都在 `Payload` 负责弹射有效载荷对象的服务@.}这是 `UseDefaultUserAgent` 是设置.

默认情况下,我从 `HttpContext` 所以通常您会得到这个设置的正确@ ; @ I'}稍后将显示它从 Umami_ MS K2 调回到哪个位置Name

```csharp
    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` 中指定请求对象的位置@' @s data set up:{}

```csharp
    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);
     }        
        
```

您将会看到此选项定义了文件“ (” 顶部的新用户代理 。 I'@ t *目前* 在方法中检测用户代理是否为无效的 {( 是否应该发生 除非它在没有 HttpContext@)}情况下从代码调用%s `UseDefaultUserAgent` 设置“. ” 如果设定为默认值,则将用户代理设置到默认值上,并将原始用户代理添加到数据对象@.

然后将此登录为日志, 这样您就可以看到用户代理正在使用什么@ . @ {}

## 解码回应 *.*

在Umami.}网@0.3.0}我添加了一些新的方法 返回a `UmamiDataResponse` 对象@.}此对象包含已解码的 JWT 代号@ .

```csharp
    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` \ ( for bot 检测@). @ 如果找到它们, 它会记录警告并返回 a `BotDetected` 状态@.}如果能找到它们,它会解码 JWT 符号并返回有效载荷.}

JWT 符号本身只是一个加密字符串, 它包含 Umami 已存储的数据@.}这是解码并返回的 `UmamiDataResponse` 对象@. @%

此消息的完整来源如下: @:

<details>
<summary>Response Decoder</summary>

```csharp
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
        };
    }
}
```

</details>
您可以看到此选项包含关于 Umami 已存储@ . @ 请求的一大批有用信息, 如果您想要显示基于本地端 < ,\ > 语言% MS K2} 浏览器等不同内容的话, 这样可以让您进行 < I.> *

```csharp
    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.Net {0.4.0}中一些新功能的短位, 它允许您指定特定请求的默认用户代理@ ._ 这对跟踪Umami 否则会忽略\ MS K3}的请求很有帮助