1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Serialize, Deserialize, Clone)]
11pub struct WebhookUser {
12 pub avatar_template: String,
14 pub id: i32,
16 pub name: Option<String>,
18 pub username: String,
20}
21
22#[derive(Debug, Serialize, Deserialize, Clone)]
23pub struct TopicWebhookEvent {
24 pub topic: WebhookTopic,
25}
26
27#[derive(Debug, Serialize, Deserialize, Clone)]
28pub struct WebhookTopic {
29 pub archetype: String,
30 pub archived: bool,
31 pub bookmarked: bool,
32 pub category_id: i32,
33 pub closed: bool,
34 pub created_at: DateTime<Utc>,
35 pub created_by: WebhookUser,
36 pub deleted_at: Option<DateTime<Utc>>,
37 pub deleted_by: Option<WebhookUser>,
38 pub fancy_title: String,
39 pub featured_link: Option<String>,
40 pub has_deleted: bool,
41 pub highest_post_number: i32,
42 pub id: i32,
43 pub last_posted_at: DateTime<Utc>,
44 pub last_poster: WebhookUser,
45 pub like_count: i32,
46 pub participant_count: i32,
47 pub pinned: bool,
48 pub pinned_at: Option<DateTime<Utc>>,
49 pub pinned_globally: bool,
50 pub pinned_until: Option<DateTime<Utc>>,
51 pub posts_count: i32,
52 pub reply_count: i32,
53 pub slug: String,
54 pub tags: Vec<String>,
55 pub tags_descriptions: serde_json::Value,
56 pub thumbnails: Option<serde_json::Value>,
57 pub title: String,
58 pub unpinned: Option<DateTime<Utc>>,
59 pub user_id: i32,
60 pub views: i32,
61 pub visible: bool,
62 pub word_count: i32,
63}
64
65#[derive(Debug, Serialize, Deserialize, Clone)]
66pub struct PostWebhookEvent {
67 pub post: WebhookPost,
68}
69
70#[derive(Debug, Serialize, Deserialize, Clone)]
71pub struct WebhookPost {
72 pub admin: bool,
73 pub avatar_template: String,
74 pub bookmarked: bool,
75 pub category_id: i32,
76 pub category_slug: String,
77 pub cooked: String,
78 pub created_at: DateTime<Utc>,
79 pub deleted_at: Option<DateTime<Utc>>,
80 pub deleted_by: Option<WebhookUser>,
81 pub display_username: Option<String>,
82 pub edit_reason: Option<String>,
83 pub flair_group_id: Option<i32>,
84 pub flair_name: Option<String>,
85 pub hidden: bool,
86 pub id: i32,
87 pub incoming_link_count: i32,
88 pub moderator: bool,
89 pub name: Option<String>,
90 pub post_number: i32,
91 pub post_type: i32,
92 pub post_url: String,
93 pub posts_count: i32,
94 pub primary_group_name: Option<String>,
95 pub quote_count: i32,
96 pub raw: String,
97 pub reads: i32,
98 pub reply_count: i32,
99 pub reply_to_post_number: Option<i32>,
100 pub reviewable_id: Option<i32>,
101 pub reviewable_score_count: i32,
102 pub reviewable_score_pending_count: i32,
103 pub score: f64,
104 pub staff: bool,
105 pub topic_archetype: String,
106 pub topic_filtered_posts_count: i32,
107 pub topic_id: i32,
108 pub topic_posts_count: i32,
109 pub topic_slug: String,
110 pub topic_title: String,
111 pub trust_level: i32,
112 pub updated_at: DateTime<Utc>,
113 pub user_deleted: bool,
114 pub user_id: i32,
115 pub user_title: Option<String>,
116 pub username: String,
117 pub version: i32,
118 pub wiki: bool,
119}
120
121#[derive(Debug, Serialize, Deserialize)]
122#[serde(untagged)]
123pub enum WebhookEventPayload {
124 TopicEvent(TopicWebhookEvent),
125 PostEvent(PostWebhookEvent),
126 Generic(serde_json::Value),
128}
129
130pub fn parse_webhook_payload(
131 event_type: &str,
132 payload: serde_json::Value,
133) -> Result<WebhookEventPayload, serde_json::Error> {
134 match event_type {
135 "topic_created" | "topic_edited" | "topic_destroyed" | "topic_recovered" => {
136 let topic_event: TopicWebhookEvent = serde_json::from_value(payload)?;
137 Ok(WebhookEventPayload::TopicEvent(topic_event))
138 }
139 "post_created" | "post_edited" | "post_destroyed" | "post_recovered" => {
140 let post_event: PostWebhookEvent = serde_json::from_value(payload)?;
141 Ok(WebhookEventPayload::PostEvent(post_event))
142 }
143 _ => Ok(WebhookEventPayload::Generic(payload)),
144 }
145}