use async_trait::async_trait;
use serde_json::{json, Value};
use synaptic_core::{SynapticError, Tool};
use crate::{api::chat::ChatApi, LarkConfig};
pub struct LarkChatTool {
api: ChatApi,
}
impl LarkChatTool {
pub fn new(config: LarkConfig) -> Self {
Self {
api: ChatApi::new(config),
}
}
}
#[async_trait]
impl Tool for LarkChatTool {
fn name(&self) -> &'static str {
"lark_chat"
}
fn description(&self) -> &'static str {
"Manage Feishu/Lark group chats. \
Use action='list' to list all chats; \
action='get' to get a specific chat; \
action='create' to create a new group chat; \
action='update' to update chat settings; \
action='list_members' to list members; \
action='add_members' or action='remove_members' to manage membership."
}
fn parameters(&self) -> Option<Value> {
Some(json!({
"type": "object",
"properties": {
"action": {
"type": "string",
"description": "Operation: list | get | create | update | list_members | add_members | remove_members",
"enum": ["list", "get", "create", "update", "list_members", "add_members", "remove_members"]
},
"chat_id": {
"type": "string",
"description": "Chat ID (oc_xxx) — required for get, update, list_members, add_members, remove_members"
},
"name": {
"type": "string",
"description": "For 'create': group name (required); for 'update': new name"
},
"description": {
"type": "string",
"description": "For 'create'/'update': group description"
},
"member_open_ids": {
"type": "array",
"items": { "type": "string" },
"description": "For 'create'/'add_members'/'remove_members': list of member open_ids"
},
"page_token": {
"type": "string",
"description": "For 'list': pagination token from previous response"
}
},
"required": ["action"]
}))
}
async fn call(&self, args: Value) -> Result<Value, SynapticError> {
let action = args["action"]
.as_str()
.ok_or_else(|| SynapticError::Tool("missing 'action'".to_string()))?;
match action {
"list" => {
let page_token = args["page_token"].as_str();
let (chats, next_token) = self.api.list_chats(page_token).await?;
let mut result = json!({ "chats": chats });
if let Some(t) = next_token {
result["next_page_token"] = json!(t);
}
Ok(result)
}
"get" => {
let chat_id = args["chat_id"]
.as_str()
.ok_or_else(|| SynapticError::Tool("missing 'chat_id'".to_string()))?;
let chat = self.api.get_chat(chat_id).await?;
Ok(json!({ "chat": chat }))
}
"create" => {
let name = args["name"]
.as_str()
.ok_or_else(|| SynapticError::Tool("missing 'name'".to_string()))?;
let description = args["description"].as_str();
let open_ids: Vec<String> = args["member_open_ids"]
.as_array()
.map(|a| {
a.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
})
.unwrap_or_default();
let chat_id = self.api.create_chat(name, description, &open_ids).await?;
Ok(json!({ "chat_id": chat_id }))
}
"update" => {
let chat_id = args["chat_id"]
.as_str()
.ok_or_else(|| SynapticError::Tool("missing 'chat_id'".to_string()))?;
let name = args["name"].as_str();
let description = args["description"].as_str();
self.api.update_chat(chat_id, name, description).await?;
Ok(json!({ "chat_id": chat_id, "status": "updated" }))
}
"list_members" => {
let chat_id = args["chat_id"]
.as_str()
.ok_or_else(|| SynapticError::Tool("missing 'chat_id'".to_string()))?;
let members = self.api.list_members(chat_id).await?;
Ok(json!({ "members": members }))
}
"add_members" => {
let chat_id = args["chat_id"]
.as_str()
.ok_or_else(|| SynapticError::Tool("missing 'chat_id'".to_string()))?;
let open_ids: Vec<String> = args["member_open_ids"]
.as_array()
.map(|a| {
a.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
})
.unwrap_or_default();
if open_ids.is_empty() {
return Err(SynapticError::Tool(
"add_members requires 'member_open_ids'".to_string(),
));
}
self.api.add_members(chat_id, &open_ids).await?;
Ok(json!({ "status": "added" }))
}
"remove_members" => {
let chat_id = args["chat_id"]
.as_str()
.ok_or_else(|| SynapticError::Tool("missing 'chat_id'".to_string()))?;
let open_ids: Vec<String> = args["member_open_ids"]
.as_array()
.map(|a| {
a.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
})
.unwrap_or_default();
if open_ids.is_empty() {
return Err(SynapticError::Tool(
"remove_members requires 'member_open_ids'".to_string(),
));
}
self.api.remove_members(chat_id, &open_ids).await?;
Ok(json!({ "status": "removed" }))
}
other => Err(SynapticError::Tool(format!(
"unknown action '{other}': expected list | get | create | update | list_members | add_members | remove_members"
))),
}
}
}