Skip to main content

systemprompt_models/analytics/
behavioral_bot.rs

1//! The one behavioural-bot classifier: every reason a session is stamped
2//! `is_behavioral_bot`, with the score each reason contributes.
3//!
4//! `user_sessions.behavioral_bot_reason` stores [`BehavioralBotReason::as_str`]
5//! and `behavioral_bot_score` stores [`BehavioralBotReason::score`]; the
6//! repository that writes the flag and the detector that sums signals both
7//! read the numbers from here.
8//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum BehavioralBotReason {
17    RequestCountExceeded,
18    GhostSession,
19    NoJavascript,
20}
21
22impl BehavioralBotReason {
23    pub const ALL: [Self; 3] = [
24        Self::RequestCountExceeded,
25        Self::GhostSession,
26        Self::NoJavascript,
27    ];
28
29    #[must_use]
30    pub const fn as_str(self) -> &'static str {
31        match self {
32            Self::RequestCountExceeded => "request_count_exceeded",
33            Self::GhostSession => "ghost_session",
34            Self::NoJavascript => "no_javascript",
35        }
36    }
37
38    #[must_use]
39    pub const fn score(self) -> i32 {
40        match self {
41            Self::RequestCountExceeded => 30,
42            Self::GhostSession => 35,
43            Self::NoJavascript => 20,
44        }
45    }
46
47    #[must_use]
48    pub fn parse(value: &str) -> Option<Self> {
49        Self::ALL
50            .into_iter()
51            .find(|reason| reason.as_str() == value)
52    }
53}
54
55impl std::fmt::Display for BehavioralBotReason {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        f.write_str(self.as_str())
58    }
59}