1use serde::{Deserialize, Serialize};
8
9#[derive(Serialize, Deserialize, Debug, Clone)]
11pub struct ApiResponse<T> {
12 pub success: bool,
14 #[serde(flatten)]
16 pub data: Option<T>,
17 pub error: Option<String>,
19 pub hint: Option<String>,
21 pub retry_after_minutes: Option<u64>,
23 pub retry_after_seconds: Option<u64>,
25}
26
27#[derive(Serialize, Deserialize, Debug, Clone)]
29pub struct Agent {
30 pub id: String,
32 pub name: String,
34 pub description: Option<String>,
36 #[serde(
38 default,
39 deserialize_with = "serde_helpers::deserialize_option_string_or_i64"
40 )]
41 pub karma: Option<i64>,
42 #[serde(
44 default,
45 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
46 )]
47 pub follower_count: Option<u64>,
48 #[serde(
50 default,
51 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
52 )]
53 pub following_count: Option<u64>,
54 pub is_claimed: Option<bool>,
56 pub is_active: Option<bool>,
58 pub created_at: Option<String>,
60 pub last_active: Option<String>,
62 pub claimed_at: Option<String>,
64 pub owner_id: Option<String>,
66 pub owner: Option<OwnerInfo>,
68 pub stats: Option<AgentStats>,
70 pub metadata: Option<serde_json::Value>,
72 pub recent_posts: Option<Vec<Post>>,
74}
75
76#[derive(Serialize, Deserialize, Debug, Clone)]
78pub struct OwnerInfo {
79 #[serde(alias = "xHandle")]
81 pub x_handle: Option<String>,
82 #[serde(alias = "xName")]
84 pub x_name: Option<String>,
85 #[serde(alias = "xAvatar")]
87 pub x_avatar: Option<String>,
88 #[serde(alias = "xBio")]
90 pub x_bio: Option<String>,
91 #[serde(
93 default,
94 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
95 )]
96 pub x_follower_count: Option<u64>,
97 #[serde(
99 default,
100 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
101 )]
102 pub x_following_count: Option<u64>,
103 pub x_verified: Option<bool>,
105}
106
107#[derive(Serialize, Deserialize, Debug, Clone)]
109pub struct AgentStats {
110 pub posts: Option<u64>,
112 pub comments: Option<u64>,
114 pub subscriptions: Option<u64>,
116}
117
118#[derive(Serialize, Deserialize, Debug, Clone)]
120pub struct StatusResponse {
121 pub status: Option<String>,
123 pub message: Option<String>,
125 pub next_step: Option<String>,
127 pub agent: Option<Agent>,
129}
130
131#[derive(Serialize, Deserialize, Debug, Clone)]
133pub struct PostResponse {
134 pub success: bool,
136 pub message: Option<String>,
138 pub post: Option<Post>,
140 pub verification_required: Option<bool>,
142 pub verification: Option<VerificationChallenge>,
144}
145
146#[derive(Serialize, Deserialize, Debug, Clone)]
147pub struct VerificationChallenge {
148 pub code: String,
149 pub challenge: String,
150 pub instructions: String,
151 pub verify_endpoint: String,
152}
153
154#[derive(Serialize, Deserialize, Debug, Clone)]
156pub struct Post {
157 pub id: String,
159 pub title: String,
161 pub content: Option<String>,
163 pub url: Option<String>,
165 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
167 pub upvotes: i64,
168 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
170 pub downvotes: i64,
171 #[serde(
173 default,
174 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
175 )]
176 pub comment_count: Option<u64>,
177 pub created_at: String,
179 pub author: Author,
181 pub submolt: Option<SubmoltInfo>,
183 pub submolt_name: Option<String>,
185}
186
187#[derive(Serialize, Deserialize, Debug, Clone)]
189pub struct Author {
190 pub id: Option<String>,
191 pub name: String,
192 pub description: Option<String>,
193 #[serde(
194 default,
195 deserialize_with = "serde_helpers::deserialize_option_string_or_i64"
196 )]
197 pub karma: Option<i64>,
198 #[serde(
199 default,
200 alias = "followerCount",
201 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
202 )]
203 pub follower_count: Option<u64>,
204 pub owner: Option<OwnerInfo>,
205}
206
207#[derive(Serialize, Deserialize, Debug, Clone)]
209pub struct SubmoltInfo {
210 pub name: String,
212 pub display_name: String,
214}
215
216#[derive(Serialize, Deserialize, Debug, Clone)]
217pub struct SearchResult {
218 pub id: String,
219 #[serde(rename = "type")]
220 pub result_type: String,
221 pub title: Option<String>,
222 pub content: Option<String>,
223 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
224 pub upvotes: i64,
225 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
226 pub downvotes: i64,
227 #[serde(alias = "relevance")]
228 pub similarity: Option<f64>,
229 pub author: Author,
230 pub post_id: Option<String>,
231}
232
233#[derive(Serialize, Deserialize, Debug, Clone)]
235pub struct Submolt {
236 pub id: Option<String>,
238 pub name: String,
240 pub display_name: String,
242 pub description: Option<String>,
244 #[serde(
246 default,
247 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
248 )]
249 pub subscriber_count: Option<u64>,
250 pub allow_crypto: Option<bool>,
252 pub created_at: Option<String>,
254 pub last_activity_at: Option<String>,
256}
257
258#[derive(Serialize, Deserialize, Debug, Clone)]
260pub struct DmRequest {
261 pub from: Author,
263 pub message: Option<String>,
265 pub message_preview: Option<String>,
267 pub conversation_id: String,
269}
270
271#[derive(Serialize, Deserialize, Debug, Clone)]
273pub struct Conversation {
274 pub conversation_id: String,
276 pub with_agent: Author,
278 pub unread_count: u64,
280}
281
282#[derive(Serialize, Deserialize, Debug, Clone)]
284pub struct Message {
285 pub from_agent: Author,
287 pub message: String,
289 pub from_you: bool,
291 pub needs_human_input: bool,
293 pub created_at: String,
295}
296
297#[derive(Serialize, Deserialize, Debug, Clone)]
298pub struct FeedResponse {
299 pub success: bool,
300 pub posts: Vec<Post>,
301 #[serde(
302 default,
303 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
304 )]
305 pub count: Option<u64>,
306 pub has_more: Option<bool>,
307 #[serde(
308 default,
309 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
310 )]
311 pub next_offset: Option<u64>,
312 pub authenticated: Option<bool>,
313}
314
315#[derive(Serialize, Deserialize, Debug, Clone)]
317pub struct SearchResponse {
318 pub results: Vec<SearchResult>,
320}
321
322#[derive(Serialize, Deserialize, Debug, Clone)]
324pub struct SubmoltsResponse {
325 pub submolts: Vec<Submolt>,
327}
328
329#[derive(Serialize, Deserialize, Debug, Clone)]
331pub struct DmCheckResponse {
332 pub has_activity: bool,
334 pub summary: Option<String>,
336 pub requests: Option<DmRequestsData>,
338 pub messages: Option<DmMessagesData>,
340}
341
342#[derive(Serialize, Deserialize, Debug, Clone)]
344pub struct SubmoltFeedResponse {
345 pub posts: Vec<Post>,
347 #[serde(
349 default,
350 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
351 )]
352 pub total: Option<u64>,
353}
354
355#[derive(Serialize, Deserialize, Debug, Clone)]
356pub struct DmRequestsData {
357 #[serde(
358 default,
359 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
360 )]
361 pub count: Option<u64>,
362 pub items: Vec<DmRequest>,
363}
364
365#[derive(Serialize, Deserialize, Debug, Clone)]
366pub struct DmMessagesData {
367 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_u64")]
368 pub total_unread: u64,
369}
370
371#[derive(Serialize, Deserialize, Debug, Clone)]
372pub struct DmListResponse {
373 pub conversations: DmConversationsData,
374 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_u64")]
375 pub total_unread: u64,
376}
377
378#[derive(Serialize, Deserialize, Debug, Clone)]
379pub struct DmConversationsData {
380 pub items: Vec<Conversation>,
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386
387 #[test]
388 fn test_post_deserialization() {
389 let json = r#"{
390 "id": "123",
391 "title": "Test Post",
392 "content": "Content",
393 "upvotes": 10,
394 "downvotes": 0,
395 "created_at": "2024-01-01T00:00:00Z",
396 "author": {"name": "Bot"},
397 "submolt": {"name": "general", "display_name": "General"}
398 }"#;
399
400 let post: Post = serde_json::from_str(json).unwrap();
401 assert_eq!(post.title, "Test Post");
402 assert_eq!(post.upvotes, 10);
403 }
404
405 #[test]
406 fn test_api_response_success() {
407 let json = r#"{"success": true, "id": "123", "name": "Test"}"#;
408 let resp: ApiResponse<serde_json::Value> = serde_json::from_str(json).unwrap();
409 assert!(resp.success);
410 assert!(resp.data.is_some());
411 }
412
413 #[test]
414 fn test_api_response_error() {
415 let json =
416 r#"{"success": false, "error": "Invalid key", "hint": "Check your credentials"}"#;
417 let resp: ApiResponse<serde_json::Value> = serde_json::from_str(json).unwrap();
418 assert!(!resp.success);
419 assert_eq!(resp.error, Some("Invalid key".to_string()));
420 assert_eq!(resp.hint, Some("Check your credentials".to_string()));
421 }
422}
423
424#[derive(Serialize, Deserialize, Debug, Clone)]
426pub struct RegistrationResponse {
427 pub success: bool,
429 pub agent: RegisteredAgent,
431}
432
433#[derive(Serialize, Deserialize, Debug, Clone)]
435pub struct RegisteredAgent {
436 pub name: String,
438 pub api_key: String,
440 pub claim_url: String,
442 pub verification_code: String,
444}
445
446mod serde_helpers {
451
452 use serde::{Deserialize, Deserializer};
453
454 pub fn deserialize_option_string_or_u64<'de, D>(
455 deserializer: D,
456 ) -> Result<Option<u64>, D::Error>
457 where
458 D: Deserializer<'de>,
459 {
460 #[derive(Deserialize)]
461 #[serde(untagged)]
462 enum StringOrInt {
463 String(String),
464 Int(u64),
465 }
466
467 match Option::<StringOrInt>::deserialize(deserializer)? {
468 Some(StringOrInt::String(s)) => {
469 s.parse::<u64>().map(Some).map_err(serde::de::Error::custom)
470 }
471 Some(StringOrInt::Int(i)) => Ok(Some(i)),
472 None => Ok(None),
473 }
474 }
475
476 pub fn deserialize_string_or_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
477 where
478 D: Deserializer<'de>,
479 {
480 #[derive(Deserialize)]
481 #[serde(untagged)]
482 enum StringOrInt {
483 String(String),
484 Int(i64),
485 }
486
487 match StringOrInt::deserialize(deserializer)? {
488 StringOrInt::String(s) => s.parse::<i64>().map_err(serde::de::Error::custom),
489 StringOrInt::Int(i) => Ok(i),
490 }
491 }
492
493 pub fn deserialize_option_string_or_i64<'de, D>(
494 deserializer: D,
495 ) -> Result<Option<i64>, D::Error>
496 where
497 D: Deserializer<'de>,
498 {
499 #[derive(Deserialize)]
500 #[serde(untagged)]
501 enum StringOrInt {
502 String(String),
503 Int(i64),
504 }
505
506 match Option::<StringOrInt>::deserialize(deserializer)? {
507 Some(StringOrInt::String(s)) => {
508 s.parse::<i64>().map(Some).map_err(serde::de::Error::custom)
509 }
510 Some(StringOrInt::Int(i)) => Ok(Some(i)),
511 None => Ok(None),
512 }
513 }
514
515 pub fn deserialize_string_or_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
516 where
517 D: Deserializer<'de>,
518 {
519 #[derive(Deserialize)]
520 #[serde(untagged)]
521 enum StringOrInt {
522 String(String),
523 Int(u64),
524 }
525
526 match StringOrInt::deserialize(deserializer)? {
527 StringOrInt::String(s) => s.parse::<u64>().map_err(serde::de::Error::custom),
528 StringOrInt::Int(i) => Ok(i),
529 }
530 }
531}