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
28#[derive(Serialize, Deserialize, Debug, Clone)]
30pub struct Agent {
31 pub id: String,
33 pub name: String,
35 pub description: Option<String>,
37 #[serde(
39 default,
40 deserialize_with = "serde_helpers::deserialize_option_string_or_i64"
41 )]
42 pub karma: Option<i64>,
43 #[serde(
45 default,
46 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
47 )]
48 pub follower_count: Option<u64>,
49 #[serde(
51 default,
52 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
53 )]
54 pub following_count: Option<u64>,
55 pub is_claimed: Option<bool>,
57 pub is_active: Option<bool>,
59 pub created_at: Option<String>,
61 pub last_active: Option<String>,
63 pub claimed_at: Option<String>,
65 pub owner_id: Option<String>,
67 pub owner: Option<OwnerInfo>,
69 pub stats: Option<AgentStats>,
71 pub metadata: Option<serde_json::Value>,
73 pub recent_posts: Option<Vec<Post>>,
75}
76
77
78#[derive(Serialize, Deserialize, Debug, Clone)]
80pub struct OwnerInfo {
81 #[serde(alias = "xHandle")]
83 pub x_handle: Option<String>,
84 #[serde(alias = "xName")]
86 pub x_name: Option<String>,
87 #[serde(alias = "xAvatar")]
89 pub x_avatar: Option<String>,
90 #[serde(alias = "xBio")]
92 pub x_bio: Option<String>,
93 #[serde(
95 default,
96 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
97 )]
98 pub x_follower_count: Option<u64>,
99 #[serde(
101 default,
102 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
103 )]
104 pub x_following_count: Option<u64>,
105 pub x_verified: Option<bool>,
107}
108
109
110#[derive(Serialize, Deserialize, Debug, Clone)]
112pub struct AgentStats {
113 pub posts: Option<u64>,
115 pub comments: Option<u64>,
117 pub subscriptions: Option<u64>,
119}
120
121
122#[derive(Serialize, Deserialize, Debug, Clone)]
124pub struct StatusResponse {
125 pub status: Option<String>,
127 pub message: Option<String>,
129 pub next_step: Option<String>,
131 pub agent: Option<Agent>,
133}
134
135
136#[derive(Serialize, Deserialize, Debug, Clone)]
138pub struct PostResponse {
139 pub success: bool,
141 pub message: Option<String>,
143 pub post: Option<Post>,
145 pub verification_required: Option<bool>,
147 pub verification: Option<VerificationChallenge>,
149}
150
151
152#[derive(Serialize, Deserialize, Debug, Clone)]
153pub struct VerificationChallenge {
154 pub code: String,
155 pub challenge: String,
156 pub instructions: String,
157 pub verify_endpoint: String,
158}
159
160#[derive(Serialize, Deserialize, Debug, Clone)]
162pub struct Post {
163 pub id: String,
165 pub title: String,
167 pub content: Option<String>,
169 pub url: Option<String>,
171 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
173 pub upvotes: i64,
174 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
176 pub downvotes: i64,
177 #[serde(
179 default,
180 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
181 )]
182 pub comment_count: Option<u64>,
183 pub created_at: String,
185 pub author: Author,
187 pub submolt: Option<SubmoltInfo>,
189 pub submolt_name: Option<String>,
191}
192
193#[derive(Serialize, Deserialize, Debug, Clone)]
195pub struct Author {
196 pub id: Option<String>,
197 pub name: String,
198 pub description: Option<String>,
199 #[serde(
200 default,
201 deserialize_with = "serde_helpers::deserialize_option_string_or_i64"
202 )]
203 pub karma: Option<i64>,
204 #[serde(
205 default,
206 alias = "followerCount",
207 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
208 )]
209 pub follower_count: Option<u64>,
210 pub owner: Option<OwnerInfo>,
211}
212
213#[derive(Serialize, Deserialize, Debug, Clone)]
215pub struct SubmoltInfo {
216 pub name: String,
218 pub display_name: String,
220}
221
222
223#[derive(Serialize, Deserialize, Debug, Clone)]
224pub struct SearchResult {
225 pub id: String,
226 #[serde(rename = "type")]
227 pub result_type: String,
228 pub title: Option<String>,
229 pub content: Option<String>,
230 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
231 pub upvotes: i64,
232 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_i64")]
233 pub downvotes: i64,
234 #[serde(alias = "relevance")]
235 pub similarity: Option<f64>,
236 pub author: Author,
237 pub post_id: Option<String>,
238}
239
240#[derive(Serialize, Deserialize, Debug, Clone)]
242pub struct Submolt {
243 pub id: Option<String>,
245 pub name: String,
247 pub display_name: String,
249 pub description: Option<String>,
251 #[serde(
253 default,
254 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
255 )]
256 pub subscriber_count: Option<u64>,
257 pub allow_crypto: Option<bool>,
259 pub created_at: Option<String>,
261 pub last_activity_at: Option<String>,
263}
264
265#[derive(Serialize, Deserialize, Debug, Clone)]
267pub struct DmRequest {
268 pub from: Author,
270 pub message: Option<String>,
272 pub message_preview: Option<String>,
274 pub conversation_id: String,
276}
277
278#[derive(Serialize, Deserialize, Debug, Clone)]
280pub struct Conversation {
281 pub conversation_id: String,
283 pub with_agent: Author,
285 pub unread_count: u64,
287}
288
289#[derive(Serialize, Deserialize, Debug, Clone)]
291pub struct Message {
292 pub from_agent: Author,
294 pub message: String,
296 pub from_you: bool,
298 pub needs_human_input: bool,
300 pub created_at: String,
302}
303
304
305#[derive(Serialize, Deserialize, Debug, Clone)]
306pub struct FeedResponse {
307 pub success: bool,
308 pub posts: Vec<Post>,
309 #[serde(
310 default,
311 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
312 )]
313 pub count: Option<u64>,
314 pub has_more: Option<bool>,
315 #[serde(
316 default,
317 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
318 )]
319 pub next_offset: Option<u64>,
320 pub authenticated: Option<bool>,
321}
322
323#[derive(Serialize, Deserialize, Debug, Clone)]
325pub struct SearchResponse {
326 pub results: Vec<SearchResult>,
328}
329
330
331#[derive(Serialize, Deserialize, Debug, Clone)]
333pub struct SubmoltsResponse {
334 pub submolts: Vec<Submolt>,
336}
337
338
339#[derive(Serialize, Deserialize, Debug, Clone)]
341pub struct DmCheckResponse {
342 pub has_activity: bool,
344 pub summary: Option<String>,
346 pub requests: Option<DmRequestsData>,
348 pub messages: Option<DmMessagesData>,
350}
351
352
353#[derive(Serialize, Deserialize, Debug, Clone)]
355pub struct SubmoltFeedResponse {
356 pub posts: Vec<Post>,
358 #[serde(
360 default,
361 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
362 )]
363 pub total: Option<u64>,
364}
365
366
367#[derive(Serialize, Deserialize, Debug, Clone)]
368pub struct DmRequestsData {
369 #[serde(
370 default,
371 deserialize_with = "serde_helpers::deserialize_option_string_or_u64"
372 )]
373 pub count: Option<u64>,
374 pub items: Vec<DmRequest>,
375}
376
377#[derive(Serialize, Deserialize, Debug, Clone)]
378pub struct DmMessagesData {
379 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_u64")]
380 pub total_unread: u64,
381}
382
383#[derive(Serialize, Deserialize, Debug, Clone)]
384pub struct DmListResponse {
385 pub conversations: DmConversationsData,
386 #[serde(deserialize_with = "serde_helpers::deserialize_string_or_u64")]
387 pub total_unread: u64,
388}
389
390#[derive(Serialize, Deserialize, Debug, Clone)]
391pub struct DmConversationsData {
392 pub items: Vec<Conversation>,
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 #[test]
400 fn test_post_deserialization() {
401 let json = r#"{
402 "id": "123",
403 "title": "Test Post",
404 "content": "Content",
405 "upvotes": 10,
406 "downvotes": 0,
407 "created_at": "2024-01-01T00:00:00Z",
408 "author": {"name": "Bot"},
409 "submolt": {"name": "general", "display_name": "General"}
410 }"#;
411
412 let post: Post = serde_json::from_str(json).unwrap();
413 assert_eq!(post.title, "Test Post");
414 assert_eq!(post.upvotes, 10);
415 }
416
417 #[test]
418 fn test_api_response_success() {
419 let json = r#"{"success": true, "id": "123", "name": "Test"}"#;
420 let resp: ApiResponse<serde_json::Value> = serde_json::from_str(json).unwrap();
421 assert!(resp.success);
422 assert!(resp.data.is_some());
423 }
424
425 #[test]
426 fn test_api_response_error() {
427 let json =
428 r#"{"success": false, "error": "Invalid key", "hint": "Check your credentials"}"#;
429 let resp: ApiResponse<serde_json::Value> = serde_json::from_str(json).unwrap();
430 assert!(!resp.success);
431 assert_eq!(resp.error, Some("Invalid key".to_string()));
432 assert_eq!(resp.hint, Some("Check your credentials".to_string()));
433 }
434}
435
436#[derive(Serialize, Deserialize, Debug, Clone)]
438pub struct RegistrationResponse {
439 pub success: bool,
441 pub agent: RegisteredAgent,
443}
444
445#[derive(Serialize, Deserialize, Debug, Clone)]
447pub struct RegisteredAgent {
448 pub name: String,
450 pub api_key: String,
452 pub claim_url: String,
454 pub verification_code: String,
456}
457
458mod serde_helpers {
463
464 use serde::{Deserialize, Deserializer};
465
466 pub fn deserialize_option_string_or_u64<'de, D>(
467 deserializer: D,
468 ) -> Result<Option<u64>, D::Error>
469 where
470 D: Deserializer<'de>,
471 {
472 #[derive(Deserialize)]
473 #[serde(untagged)]
474 enum StringOrInt {
475 String(String),
476 Int(u64),
477 }
478
479 match Option::<StringOrInt>::deserialize(deserializer)? {
480 Some(StringOrInt::String(s)) => {
481 s.parse::<u64>().map(Some).map_err(serde::de::Error::custom)
482 }
483 Some(StringOrInt::Int(i)) => Ok(Some(i)),
484 None => Ok(None),
485 }
486 }
487
488 pub fn deserialize_string_or_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
489 where
490 D: Deserializer<'de>,
491 {
492 #[derive(Deserialize)]
493 #[serde(untagged)]
494 enum StringOrInt {
495 String(String),
496 Int(i64),
497 }
498
499 match StringOrInt::deserialize(deserializer)? {
500 StringOrInt::String(s) => s.parse::<i64>().map_err(serde::de::Error::custom),
501 StringOrInt::Int(i) => Ok(i),
502 }
503 }
504
505 pub fn deserialize_option_string_or_i64<'de, D>(
506 deserializer: D,
507 ) -> Result<Option<i64>, D::Error>
508 where
509 D: Deserializer<'de>,
510 {
511 #[derive(Deserialize)]
512 #[serde(untagged)]
513 enum StringOrInt {
514 String(String),
515 Int(i64),
516 }
517
518 match Option::<StringOrInt>::deserialize(deserializer)? {
519 Some(StringOrInt::String(s)) => {
520 s.parse::<i64>().map(Some).map_err(serde::de::Error::custom)
521 }
522 Some(StringOrInt::Int(i)) => Ok(Some(i)),
523 None => Ok(None),
524 }
525 }
526
527 pub fn deserialize_string_or_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
528 where
529 D: Deserializer<'de>,
530 {
531 #[derive(Deserialize)]
532 #[serde(untagged)]
533 enum StringOrInt {
534 String(String),
535 Int(u64),
536 }
537
538 match StringOrInt::deserialize(deserializer)? {
539 StringOrInt::String(s) => s.parse::<u64>().map_err(serde::de::Error::custom),
540 StringOrInt::Int(i) => Ok(i),
541 }
542 }
543}