1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! Dregs: fraud and abuse scoring for the users of your application.
//!
//! Send events from your backend, read back the scores and the observations behind them:
//!
//! ```no_run
//! use dregs::{Client, Event};
//! use serde_json::json;
//!
//! # async fn hold_for_review(_: &str) {}
//! # async fn run() -> dregs::Result<()> {
//! let client = Client::from_env()?;
//!
//! client
//! .track(
//! Event::new("user.signup", "user_12345")
//! .data(json!({ "plan": "pro" }))
//! .identity_data(json!({ "email": "ada@example.com" })),
//! )
//! .await?;
//!
//! let scores = client.identities().scores("user_12345").await?;
//!
//! if scores.authenticity().is_some_and(|authenticity| authenticity < 40) {
//! hold_for_review("user_12345").await;
//! }
//! # Ok(())
//! # }
//! ```
//!
//! Scoring is asynchronous, so scores appear moments after the events that move them rather than
//! in the same breath. See <https://dregs.com/manual/api/> for the API this wraps.
//!
//! The client is async and runs on [Tokio](https://tokio.rs), through
//! [reqwest](https://docs.rs/reqwest).
//!
//! # Groups on track
//!
//! Pass the organizations, teams, or workspaces the user is acting in, and Dregs makes the
//! identity a member of each. An event carries at most one group of each type:
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::{Event, Group};
//! use serde_json::json;
//!
//! client
//! .track(
//! Event::new("user.login", "user_12345")
//! .group(Group::organization("org_678").data(json!({ "name": "Acme Inc" })))
//! .group(Group::new("team", "team_42")),
//! )
//! .await?;
//! # Ok(())
//! # }
//! ```
//!
//! # History, links, and past cycles
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! let history = client.identities().history("user_12345", Some(10)).await?;
//!
//! if let Some(cycle_id) = history.last().and_then(|entry| entry.cycle_id) {
//! let older = client.identities().analysis_cycle("user_12345", cycle_id).await?;
//! }
//!
//! for link in client.identities().links("user_12345").await? {
//! println!("{:?} {:?}", link.identity_id, link.link_type);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Disregarding an identity
//!
//! Leave your own staff and test accounts out of scoring and escalations:
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::IdentityUpdate;
//!
//! client
//! .identities()
//! .update("qa_bot_7", IdentityUpdate::new().disregarded(true))
//! .await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Asking for a review
//!
//! Dregs's AI reviewer looks over an identity's analysis in the background; read the result back
//! later with [`Identities::reviews`].
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! let review = client
//! .identities()
//! .review("user_12345", Some("This is a long-standing customer."))
//! .await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Listing and paging
//!
//! Every namespace with a `list()` returns one [`Page`] at a time, numbered from 0, and a
//! `list_all()` that walks every page as a stream. Filters you leave out are not sent.
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::{ListIdentities, Order};
//! use futures_util::TryStreamExt;
//!
//! let page = client
//! .identities()
//! .list(ListIdentities::new().term("example.com").page_size(50))
//! .await?;
//!
//! println!("{} match", page.total_elements);
//!
//! let mut identities = client.identities().list_all(
//! ListIdentities::new().group("org_678").sort("humanityScore").order(Order::Asc),
//! );
//!
//! while let Some(identity) = identities.try_next().await? {
//! println!("{:?}", identity.id);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Groups
//!
//! [`Client::groups`] reads groups back, and keeps Dregs in step when something changes outside an
//! event:
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::Group;
//! use serde_json::json;
//!
//! client
//! .groups()
//! .create(Group::organization("org_678").data(json!({ "name": "Acme Inc" })))
//! .await?;
//!
//! client.groups().add_member("team", "team_42", "user_12345").await?;
//! client.groups().remove_member("team", "team_42", "user_12345").await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Events, devices, and escalations
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::{EscalationStatus, ListEscalations, ListEvents};
//! use futures_util::TryStreamExt;
//!
//! let events = client.events().list(ListEvents::new().identity("user_12345")).await?;
//!
//! client.devices().update("a1b2c3d4e5", true).await?;
//!
//! let mut open = client
//! .escalations()
//! .list_all(ListEscalations::new().status(EscalationStatus::Open));
//!
//! while let Some(escalation) = open.try_next().await? {
//! if let Some(id) = escalation.id {
//! client.escalations().update(id, EscalationStatus::Acknowledged).await?;
//! }
//! }
//!
//! let summary = client.escalations().summary().await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Features
//!
//! - `rustls` (default): TLS through rustls, verifying certificates with the platform's verifier.
//! - `native-tls`: TLS through the operating system's library instead. Turn off default features
//! to drop rustls.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;