Skip to main content

dsc/api/
user_actions.rs

1use super::client::DiscourseClient;
2use super::error::http_error;
3use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5
6/// One row from /user_actions.json. Distilled — Discourse returns more
7/// fields than most callers need.
8#[derive(Debug, Deserialize, Serialize, Clone)]
9pub struct UserAction {
10    pub action_type: u32,
11    pub created_at: String,
12    #[serde(default)]
13    pub title: Option<String>,
14    #[serde(default)]
15    pub slug: Option<String>,
16    pub topic_id: u64,
17    #[serde(default)]
18    pub post_id: Option<u64>,
19    #[serde(default)]
20    pub post_number: Option<u64>,
21    #[serde(default)]
22    pub username: Option<String>,
23    #[serde(default)]
24    pub excerpt: Option<String>,
25}
26
27#[derive(Debug, Deserialize)]
28struct UserActionsResponse {
29    #[serde(default)]
30    user_actions: Vec<UserAction>,
31}
32
33impl DiscourseClient {
34    /// Fetch a page of a user's activity. `filter_types` is a slice of
35    /// Discourse's numeric action-type filters (e.g. 4 = new_topic, 5 = reply);
36    /// they're joined with commas. `offset` paginates — Discourse returns
37    /// ~10 items per page.
38    pub fn fetch_user_actions(
39        &self,
40        username: &str,
41        filter_types: &[u32],
42        offset: u32,
43    ) -> Result<Vec<UserAction>> {
44        let types_csv = filter_types
45            .iter()
46            .map(|n| n.to_string())
47            .collect::<Vec<_>>()
48            .join(",");
49        let path = format!(
50            "/user_actions.json?username={}&filter={}&offset={}",
51            username, types_csv, offset
52        );
53        let response = self.get(&path)?;
54        let status = response.status();
55        let text = response.text().context("reading user actions response")?;
56        if !status.is_success() {
57            return Err(http_error("user actions request", status, &text));
58        }
59        let body: UserActionsResponse =
60            serde_json::from_str(&text).context("parsing user actions response")?;
61        Ok(body.user_actions)
62    }
63}