1use oxios_gateway::format::ChannelFormatter;
4use oxios_gateway::message::{ErrorKind, OutgoingMessage};
5
6pub struct TelegramFormatter;
11
12impl ChannelFormatter for TelegramFormatter {
13 fn format_success(&self, msg: &OutgoingMessage) -> String {
14 let mut out = msg.content.clone();
15
16 if let Some(meta) = &msg.meta {
17 let mut footer_parts = Vec::new();
18 if !meta.phase.is_empty() {
19 let eval = if meta.evaluation_passed {
20 "β
"
21 } else {
22 "β οΈ"
23 };
24 footer_parts.push(format!("{} {}", eval, meta.phase));
25 }
26 if let Some(tag) = &meta.project_tag {
27 footer_parts.push(tag.clone());
28 }
29 if let Some(dur) = meta.duration_ms {
30 footer_parts.push(format!("{:.1}s", dur as f64 / 1000.0));
31 }
32 if !footer_parts.is_empty() {
33 out.push_str(&format!("\n\n_{}_", footer_parts.join(" Β· ")));
34 }
35 }
36
37 out
38 }
39
40 fn format_error(&self, msg: &OutgoingMessage) -> String {
41 let meta = msg.meta.as_ref();
42 let kind = meta.and_then(|m| m.error.as_ref()).map(|e| e.kind);
43
44 let icon = match kind {
45 Some(ErrorKind::ProviderError) => "π",
46 Some(ErrorKind::Timeout) => "β±οΈ",
47 _ => "β",
48 };
49
50 let mut out = format!("{} {}", icon, msg.content);
51
52 if let Some(err) = meta.and_then(|m| m.error.as_ref()) {
53 if let Some(s) = &err.suggestion {
54 out.push_str(&format!("\n\nπ‘ _{s}_"));
55 }
56 }
57
58 out
59 }
60
61 fn format_progress(&self, phase: &str) -> String {
62 match phase {
63 "Interview" => "π λΆμ μ€...",
64 "Seed" => "π κ³ν μ립 μ€...",
65 "Execute" => "β‘ μ€ν μ€...",
66 "Evaluate" => "π νκ° μ€...",
67 "Evolve" => "π κ°μ μ€...",
68 _ => "β³ μ²λ¦¬ μ€...",
69 }
70 .into()
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use oxios_gateway::message::{ResponseMeta, UserFacingError};
78 use std::collections::HashMap;
79
80 fn make_msg(content: &str, meta: Option<ResponseMeta>) -> OutgoingMessage {
81 OutgoingMessage {
82 id: uuid::Uuid::new_v4(),
83 channel: "telegram".to_string(),
84 user_id: "123".to_string(),
85 content: content.to_string(),
86 timestamp: chrono::Utc::now(),
87 metadata: HashMap::new(),
88 meta,
89 }
90 }
91
92 #[test]
93 fn format_success_no_meta() {
94 let msg = make_msg("Hello", None);
95 let fmt = TelegramFormatter;
96 assert_eq!(fmt.format_success(&msg), "Hello");
97 }
98
99 #[test]
100 fn format_success_with_phase() {
101 let meta = ResponseMeta {
102 session_id: None,
103 project_id: None,
104 project_tag: Some("[π§ Test]".to_string()),
105 seed_id: None,
106 phase: "Execute".to_string(),
107 evaluation_passed: true,
108 duration_ms: Some(3500),
109 error: None,
110 };
111 let msg = make_msg("Done!", Some(meta));
112 let fmt = TelegramFormatter;
113 let result = fmt.format_success(&msg);
114 assert!(result.contains("Done!"));
115 assert!(result.contains("β
Execute"));
116 assert!(result.contains("[π§ Test]"));
117 assert!(result.contains("3.5s"));
118 }
119
120 #[test]
121 fn format_error_internal() {
122 let meta = ResponseMeta {
123 session_id: None,
124 project_id: None,
125 project_tag: None,
126 seed_id: None,
127 phase: String::new(),
128 evaluation_passed: false,
129 duration_ms: None,
130 error: Some(UserFacingError {
131 message: "λ΄λΆ μ€λ₯".to_string(),
132 kind: ErrorKind::Internal,
133 suggestion: None,
134 }),
135 };
136 let msg = make_msg("λ΄λΆ μ€λ₯", Some(meta));
137 let fmt = TelegramFormatter;
138 let result = fmt.format_error(&msg);
139 assert!(result.starts_with("β"));
140 }
141
142 #[test]
143 fn format_error_provider_with_suggestion() {
144 let meta = ResponseMeta {
145 session_id: None,
146 project_id: None,
147 project_tag: None,
148 seed_id: None,
149 phase: String::new(),
150 evaluation_passed: false,
151 duration_ms: None,
152 error: Some(UserFacingError {
153 message: "AI μλΉμ€ μ€λ₯".to_string(),
154 kind: ErrorKind::ProviderError,
155 suggestion: Some("1-2λΆ ν λ€μ μλνμΈμ.".to_string()),
156 }),
157 };
158 let msg = make_msg("AI μλΉμ€ μ€λ₯", Some(meta));
159 let fmt = TelegramFormatter;
160 let result = fmt.format_error(&msg);
161 assert!(result.starts_with("π"));
162 assert!(result.contains("π‘"));
163 }
164
165 #[test]
166 fn format_progress_known_phases() {
167 let fmt = TelegramFormatter;
168 assert_eq!(fmt.format_progress("Interview"), "π λΆμ μ€...");
169 assert_eq!(fmt.format_progress("Execute"), "β‘ μ€ν μ€...");
170 assert_eq!(fmt.format_progress("Evolve"), "π κ°μ μ€...");
171 }
172
173 #[test]
174 fn format_progress_unknown() {
175 let fmt = TelegramFormatter;
176 assert_eq!(fmt.format_progress("Unknown"), "β³ μ²λ¦¬ μ€...");
177 }
178}