use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct TraceInfo {
pub trace_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub span_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema,PartialEq)]
pub struct Participant {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub channel_specific_id: Option<String>,
}
impl Participant {
pub fn new(id: String, display_name: Option<String>, channel_specific_id: Option<String>) -> Self {
Self{id, display_name,channel_specific_id}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema,)]
pub struct FileMetadata {
pub file_name: String,
pub mime_type: String,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub size_bytes: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema,)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum MediaType {
IMAGE,
VIDEO,
AUDIO,
BINARY,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema,)]
pub struct MediaMetadata {
pub kind: MediaType,
pub file: FileMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema,)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum MessageContent {
Text { text: String },
File { file: FileMetadata },
Media { media: MediaMetadata },
Event { event: Event },
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema,)]
pub struct Event {
pub event_type: String,
#[serde(default, skip_serializing_if = "Value::is_null")]
pub event_payload: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema,PartialEq)]
pub struct ChannelMessage {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
pub direction: MessageDirection,
pub channel: String,
pub from: Participant,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub to: Vec<Participant>,
pub timestamp: String,
pub content: Vec<MessageContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thread_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply_to_id: Option<String>,
#[serde(default, skip_serializing_if = "Value::is_null")]
pub metadata: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct EventType {
pub event_type: String, pub description: String, pub payload_schema: Option<Value>, }
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema,)]
pub struct ChannelCapabilities {
pub name: String, pub supports_sending: bool,
pub supports_receiving: bool,
pub supports_text: bool,
pub supports_files: bool,
pub supports_media: bool,
pub supports_events: bool,
pub supports_typing: bool,
pub supports_routing: bool,
pub supports_threading: bool,
pub supports_reactions: bool,
pub supports_call: bool,
pub supports_buttons: bool,
pub supports_links: bool,
pub supports_custom_payloads: bool,
pub supported_events: Vec<EventType>, }
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct MessageInResult {
pub message: ChannelMessage,
pub error: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct MessageOutParams {
pub message: ChannelMessage,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct MessageOutResult {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "lowercase")]
pub struct TextMessage {
pub text: String
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ChannelState {
STARTING,
RUNNING,
DRAINING,
#[default]
STOPPED,
FAILED,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash, Default)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Trace,
Debug,
#[default]
Info,
Warn,
Error,
Critical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "lowercase")]
pub enum MessageDirection {
#[default]
Incoming,
Outgoing,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
pub struct InitParams {
pub version: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub config: Vec<(String, String)>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub secrets: Vec<(String, String)>,
pub log_level: LogLevel,
#[serde(skip_serializing_if = "Option::is_none")]
pub log_dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub otel_endpoint: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct InitResult {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct DrainResult {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct StopResult {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct VersionResult {
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct StateResult {
pub state: ChannelState,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct HealthResult {
pub healthy: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct WaitUntilDrainedParams {
pub timeout_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct WaitUntilDrainedResult {
pub stopped: bool,
pub error: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct SetConfigParams {
pub config: Vec<(String, String)>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct SetSecretsParams {
pub secrets: Vec<(String, String)>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema,)]
pub struct ListKeysResult {
pub required_keys: Vec<(String,Option<String>)>, pub optional_keys: Vec<(String,Option<String>)>, }
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct SetConfigResult {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct ListConfigKeysResult {
pub keys: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct SetSecretsResult {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct NameResult {
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct CapabilitiesResult {
pub capabilities: ChannelCapabilities,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema,)]
pub struct InvalidateSessionParams {
pub key: String,
}
pub fn make_session_key(plugin_id: &str, key: &str) -> String {
format!("{plugin_id}|{key}")
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn channel_message_roundtrip() {
let cm = ChannelMessage {
id: "1".into(),
session_id: None,
direction: MessageDirection::Incoming,
channel: "telegram".into(),
from: Participant { id: "user".into(), display_name: None, channel_specific_id: None },
to: vec![],
timestamp: "2025-06-25T12:00:00Z".into(),
content: vec![MessageContent::Text { text: "hi".into() }],
thread_id: None,
reply_to_id: None,
metadata: json!({}),
};
let js = serde_json::to_string(&cm).unwrap();
let de: ChannelMessage = serde_json::from_str(&js).unwrap();
assert_eq!(de.id, "1");
}
}