mirror of
https://github.com/aljazceru/goose.git
synced 2026-02-03 05:34:32 +01:00
feat: google_drive write tools and read comment tool (#1650)
This commit is contained in:
@@ -26,7 +26,10 @@ kill_tree = "0.2.4"
|
||||
shellexpand = "3.1.0"
|
||||
indoc = "2.0.5"
|
||||
xcap = "0.0.14"
|
||||
reqwest = { version = "0.11", features = ["json", "rustls-tls"] , default-features = false}
|
||||
reqwest = { version = "0.11", features = [
|
||||
"json",
|
||||
"rustls-tls",
|
||||
], default-features = false }
|
||||
async-trait = "0.1"
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
etcetera = "0.8.0"
|
||||
|
||||
@@ -3,9 +3,12 @@ mod token_storage;
|
||||
use indoc::indoc;
|
||||
use regex::Regex;
|
||||
use serde_json::{json, Value};
|
||||
use std::{env, fs, future::Future, path::Path, pin::Pin, sync::Arc};
|
||||
use token_storage::{CredentialsManager, KeychainTokenStorage};
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::sync::Arc;
|
||||
use std::{env, fs, future::Future, path::Path, pin::Pin};
|
||||
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::{
|
||||
handler::{PromptError, ResourceError, ToolError},
|
||||
@@ -17,6 +20,7 @@ use mcp_core::{
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
use mcp_server::Router;
|
||||
|
||||
use google_drive3::common::ReadSeek;
|
||||
use google_drive3::{
|
||||
self,
|
||||
api::{File, Scope},
|
||||
@@ -64,6 +68,12 @@ impl InstalledFlowDelegate for LocalhostBrowserDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FileOperation {
|
||||
Create { name: String },
|
||||
Update { file_id: String },
|
||||
}
|
||||
|
||||
pub struct GoogleDriveRouter {
|
||||
tools: Vec<Tool>,
|
||||
instructions: String,
|
||||
@@ -232,6 +242,246 @@ impl GoogleDriveRouter {
|
||||
}),
|
||||
);
|
||||
|
||||
let upload_tool = Tool::new(
|
||||
"upload".to_string(),
|
||||
indoc! {r#"
|
||||
Upload a file to Google Drive.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The desired filename to use for the uploaded file.",
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string",
|
||||
"description": "The MIME type of the file.",
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"description": "Plain text body of the file to upload. Mutually exclusive with path.",
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Path to the file to upload. Mutually exclusive with body.",
|
||||
},
|
||||
"parent_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["name", "mimeType"],
|
||||
}),
|
||||
);
|
||||
|
||||
let create_doc_tool = Tool::new(
|
||||
"create_doc".to_string(),
|
||||
indoc! {r#"
|
||||
Create a Google Doc from markdown text in Google Drive.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the file to create",
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"description": "Markdown text of the file to create.",
|
||||
},
|
||||
"parent_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["name", "body"],
|
||||
}),
|
||||
);
|
||||
|
||||
let create_sheets_tool = Tool::new(
|
||||
"create_sheets".to_string(),
|
||||
indoc! {r#"
|
||||
Create a Google Sheets document from csv text in Google Drive.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the file to create",
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"description": "CSV text of the file to create.",
|
||||
},
|
||||
"parent_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["name", "body"],
|
||||
}),
|
||||
);
|
||||
|
||||
let create_slides_tool = Tool::new(
|
||||
"create_slides".to_string(),
|
||||
indoc! {r#"
|
||||
Create a Google Slides document in Google Drive by converting a PowerPoint file.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the file to create",
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Path to a PowerPoint file to upload.",
|
||||
},
|
||||
"parent_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the parent folder in which to create the file. (default: creates files in the root of 'My Drive')",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["name", "path"],
|
||||
}),
|
||||
);
|
||||
|
||||
let update_tool = Tool::new(
|
||||
"update".to_string(),
|
||||
indoc! {r#"
|
||||
Update a Google Drive file with new content.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
"type": "string",
|
||||
"description": "The ID of the file to update.",
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string",
|
||||
"description": "The MIME type of the file.",
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"description": "Plain text body of the file to upload. Mutually exclusive with path.",
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Path to a local file to use to update the Google Drive file. Mutually exclusive with body.",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["fileId", "mimeType"],
|
||||
}),
|
||||
);
|
||||
|
||||
let update_doc_tool = Tool::new(
|
||||
"update_doc".to_string(),
|
||||
indoc! {r#"
|
||||
Update a Google Doc from markdown text.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
"type": "string",
|
||||
"description": "ID of the file to update",
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"description": "Complete markdown text of the file to update.",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["fileId", "body"],
|
||||
}),
|
||||
);
|
||||
|
||||
let update_sheets_tool = Tool::new(
|
||||
"update_sheets".to_string(),
|
||||
indoc! {r#"
|
||||
Update a Google Sheets document from csv text.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
"type": "string",
|
||||
"description": "ID of the file to update",
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"description": "Complete CSV text of the updated file.",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["fileId", "body"],
|
||||
}),
|
||||
);
|
||||
|
||||
let update_slides_tool = Tool::new(
|
||||
"update_slides".to_string(),
|
||||
indoc! {r#"
|
||||
Updatea Google Slides document in Google Drive by converting a PowerPoint file.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
"type": "string",
|
||||
"description": "ID of the file to update",
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Path to a PowerPoint file to upload to replace the existing file.",
|
||||
},
|
||||
"allow_shared_drives": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow access to shared drives or just your personal drive (default: false)",
|
||||
}
|
||||
},
|
||||
"required": ["fileId", "path"],
|
||||
}),
|
||||
);
|
||||
|
||||
let sheets_tool = Tool::new(
|
||||
"sheets_tool".to_string(),
|
||||
indoc! {r#"
|
||||
@@ -267,6 +517,28 @@ impl GoogleDriveRouter {
|
||||
}),
|
||||
);
|
||||
|
||||
let comment_list_tool = Tool::new(
|
||||
"comment_list".to_string(),
|
||||
indoc! {r#"
|
||||
List comments for a file in google drive by id, given an input file id.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
"type": "string",
|
||||
"description": "Id of the file to list comments for.",
|
||||
},
|
||||
"pageSize": {
|
||||
"type": "number",
|
||||
"description": "How many items to return from the search query, default 10, max 100",
|
||||
}
|
||||
},
|
||||
"required": ["fileId"],
|
||||
}),
|
||||
);
|
||||
|
||||
let instructions = indoc::formatdoc! {r#"
|
||||
Google Drive MCP Server Instructions
|
||||
|
||||
@@ -336,7 +608,20 @@ impl GoogleDriveRouter {
|
||||
"#};
|
||||
|
||||
Self {
|
||||
tools: vec![search_tool, read_tool, sheets_tool],
|
||||
tools: vec![
|
||||
search_tool,
|
||||
read_tool,
|
||||
upload_tool,
|
||||
create_doc_tool,
|
||||
create_sheets_tool,
|
||||
create_slides_tool,
|
||||
update_tool,
|
||||
update_doc_tool,
|
||||
update_sheets_tool,
|
||||
update_slides_tool,
|
||||
sheets_tool,
|
||||
comment_list_tool,
|
||||
],
|
||||
instructions,
|
||||
drive,
|
||||
sheets,
|
||||
@@ -810,6 +1095,443 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn upload_to_drive(
|
||||
&self,
|
||||
operation: FileOperation,
|
||||
content: Box<dyn ReadSeek>,
|
||||
source_mime_type: &str,
|
||||
target_mime_type: &str,
|
||||
parent: Option<&str>,
|
||||
support_all_drives: bool,
|
||||
) -> Result<Vec<Content>, ToolError> {
|
||||
let mut req = File {
|
||||
mime_type: Some(target_mime_type.to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
if let Some(p) = parent {
|
||||
req.parents = Some(vec![p.to_string()]);
|
||||
}
|
||||
|
||||
let builder = self.drive.files();
|
||||
let result = match operation {
|
||||
FileOperation::Create { ref name } => {
|
||||
req.name = Some(name.to_string());
|
||||
builder
|
||||
.create(req)
|
||||
.use_content_as_indexable_text(true)
|
||||
.supports_all_drives(support_all_drives)
|
||||
.upload(content, source_mime_type.parse().unwrap())
|
||||
.await
|
||||
}
|
||||
FileOperation::Update { ref file_id } => {
|
||||
builder
|
||||
.update(req, file_id)
|
||||
.use_content_as_indexable_text(true)
|
||||
.supports_all_drives(support_all_drives)
|
||||
.upload(content, source_mime_type.parse().unwrap())
|
||||
.await
|
||||
}
|
||||
};
|
||||
match result {
|
||||
Err(e) => Err(ToolError::ExecutionError(format!(
|
||||
"Failed to upload google drive file {:?}, {}.",
|
||||
operation, e
|
||||
))),
|
||||
Ok(r) => Ok(vec![Content::text(format!(
|
||||
"{} ({}) (uri: {})",
|
||||
r.1.name.unwrap_or_default(),
|
||||
r.1.mime_type.unwrap_or_default(),
|
||||
r.1.id.unwrap_or_default()
|
||||
))]),
|
||||
}
|
||||
}
|
||||
|
||||
async fn upload(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let filename =
|
||||
params
|
||||
.get("name")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The name param is required".to_string(),
|
||||
))?;
|
||||
let mime_type =
|
||||
params
|
||||
.get("mimeType")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The mimeType param is required".to_string(),
|
||||
))?;
|
||||
let body = params.get("body").and_then(|q| q.as_str());
|
||||
let path = params.get("path").and_then(|q| q.as_str());
|
||||
let reader: Box<dyn ReadSeek> = match (body, path) {
|
||||
(None, None) | (Some(_), Some(_)) => {
|
||||
return Err(ToolError::InvalidParameters(
|
||||
"Either the body or path param is required".to_string(),
|
||||
))
|
||||
}
|
||||
(Some(b), None) => Box::new(Cursor::new(b.as_bytes().to_owned())),
|
||||
(None, Some(p)) => Box::new(std::fs::File::open(p).map_err(|e| {
|
||||
ToolError::ExecutionError(format!("Error opening {}: {}", p, e).to_string())
|
||||
})?),
|
||||
};
|
||||
let parent = params.get("parent").and_then(|q| q.as_str());
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
self.upload_to_drive(
|
||||
FileOperation::Create {
|
||||
name: filename.to_string(),
|
||||
},
|
||||
reader,
|
||||
mime_type,
|
||||
mime_type,
|
||||
parent,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_doc(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let filename =
|
||||
params
|
||||
.get("name")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The name param is required".to_string(),
|
||||
))?;
|
||||
let body =
|
||||
params
|
||||
.get("body")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The body param is required".to_string(),
|
||||
))?;
|
||||
let source_mime_type = "text/markdown";
|
||||
let target_mime_type = "application/vnd.google-apps.document";
|
||||
let parent = params.get("parent").and_then(|q| q.as_str());
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
|
||||
self.upload_to_drive(
|
||||
FileOperation::Create {
|
||||
name: filename.to_string(),
|
||||
},
|
||||
cursor,
|
||||
source_mime_type,
|
||||
target_mime_type,
|
||||
parent,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_sheets(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let filename =
|
||||
params
|
||||
.get("name")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The name param is required".to_string(),
|
||||
))?;
|
||||
let body =
|
||||
params
|
||||
.get("body")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The body param is required".to_string(),
|
||||
))?;
|
||||
let source_mime_type = "text/csv";
|
||||
let target_mime_type = "application/vnd.google-apps.spreadsheet";
|
||||
let parent = params.get("parent").and_then(|q| q.as_str());
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
|
||||
self.upload_to_drive(
|
||||
FileOperation::Create {
|
||||
name: filename.to_string(),
|
||||
},
|
||||
cursor,
|
||||
source_mime_type,
|
||||
target_mime_type,
|
||||
parent,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_slides(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let filename =
|
||||
params
|
||||
.get("name")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The name param is required".to_string(),
|
||||
))?;
|
||||
let path =
|
||||
params
|
||||
.get("path")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The path param is required".to_string(),
|
||||
))?;
|
||||
let reader = Box::new(std::fs::File::open(path).map_err(|e| {
|
||||
ToolError::ExecutionError(format!("Error opening {}: {}", path, e).to_string())
|
||||
})?);
|
||||
let source_mime_type =
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
let target_mime_type = "application/vnd.google-apps.presentation";
|
||||
let parent = params.get("parent").and_then(|q| q.as_str());
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
self.upload_to_drive(
|
||||
FileOperation::Create {
|
||||
name: filename.to_string(),
|
||||
},
|
||||
reader,
|
||||
source_mime_type,
|
||||
target_mime_type,
|
||||
parent,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let file_id =
|
||||
params
|
||||
.get("fileId")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The fileId param is required".to_string(),
|
||||
))?;
|
||||
let mime_type =
|
||||
params
|
||||
.get("mimeType")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The mimeType param is required".to_string(),
|
||||
))?;
|
||||
let body = params.get("body").and_then(|q| q.as_str());
|
||||
let path = params.get("path").and_then(|q| q.as_str());
|
||||
let reader: Box<dyn ReadSeek> = match (body, path) {
|
||||
(None, None) | (Some(_), Some(_)) => {
|
||||
return Err(ToolError::InvalidParameters(
|
||||
"Either the body or path param is required".to_string(),
|
||||
))
|
||||
}
|
||||
(Some(b), None) => Box::new(Cursor::new(b.as_bytes().to_owned())),
|
||||
(None, Some(p)) => Box::new(std::fs::File::open(p).map_err(|e| {
|
||||
ToolError::ExecutionError(format!("Error opening {}: {}", p, e).to_string())
|
||||
})?),
|
||||
};
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
|
||||
self.upload_to_drive(
|
||||
FileOperation::Update {
|
||||
file_id: file_id.to_string(),
|
||||
},
|
||||
reader,
|
||||
mime_type,
|
||||
mime_type,
|
||||
None,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_doc(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let file_id =
|
||||
params
|
||||
.get("fileId")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The fileId param is required".to_string(),
|
||||
))?;
|
||||
let body =
|
||||
params
|
||||
.get("body")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The body param is required".to_string(),
|
||||
))?;
|
||||
let source_mime_type = "text/markdown";
|
||||
let target_mime_type = "application/vnd.google-apps.document";
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
|
||||
self.upload_to_drive(
|
||||
FileOperation::Update {
|
||||
file_id: file_id.to_string(),
|
||||
},
|
||||
cursor,
|
||||
source_mime_type,
|
||||
target_mime_type,
|
||||
None,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_sheets(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let file_id =
|
||||
params
|
||||
.get("fileId")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The fileId param is required".to_string(),
|
||||
))?;
|
||||
let body =
|
||||
params
|
||||
.get("body")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The body param is required".to_string(),
|
||||
))?;
|
||||
let source_mime_type = "text/csv";
|
||||
let target_mime_type = "application/vnd.google-apps.spreadsheet";
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
let cursor = Box::new(Cursor::new(body.as_bytes().to_owned()));
|
||||
self.upload_to_drive(
|
||||
FileOperation::Update {
|
||||
file_id: file_id.to_string(),
|
||||
},
|
||||
cursor,
|
||||
source_mime_type,
|
||||
target_mime_type,
|
||||
None,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_slides(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let file_id =
|
||||
params
|
||||
.get("fileId")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The fileId param is required".to_string(),
|
||||
))?;
|
||||
let path =
|
||||
params
|
||||
.get("path")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The path param is required".to_string(),
|
||||
))?;
|
||||
let reader = Box::new(std::fs::File::open(path).map_err(|e| {
|
||||
ToolError::ExecutionError(format!("Error opening {}: {}", path, e).to_string())
|
||||
})?);
|
||||
let source_mime_type =
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
let target_mime_type = "application/vnd.google-apps.presentation";
|
||||
let support_all_drives = params
|
||||
.get("supportAllDrives")
|
||||
.and_then(|q| q.as_bool())
|
||||
.unwrap_or_default();
|
||||
self.upload_to_drive(
|
||||
FileOperation::Update {
|
||||
file_id: file_id.to_string(),
|
||||
},
|
||||
reader,
|
||||
source_mime_type,
|
||||
target_mime_type,
|
||||
None,
|
||||
support_all_drives,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn comment_list(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let file_id =
|
||||
params
|
||||
.get("fileId")
|
||||
.and_then(|q| q.as_str())
|
||||
.ok_or(ToolError::InvalidParameters(
|
||||
"The fileId param is required".to_string(),
|
||||
))?;
|
||||
|
||||
// extract pageSize, and convert it to an i32, default to 10
|
||||
let page_size: i32 = params
|
||||
.get("pageSize")
|
||||
.map(|s| {
|
||||
s.as_i64()
|
||||
.and_then(|n| i32::try_from(n).ok())
|
||||
.ok_or_else(|| ToolError::InvalidParameters(format!("Invalid pageSize: {}", s)))
|
||||
.and_then(|n| {
|
||||
if (0..=100).contains(&n) {
|
||||
Ok(n)
|
||||
} else {
|
||||
Err(ToolError::InvalidParameters(format!(
|
||||
"pageSize must be between 0 and 100, got {}",
|
||||
n
|
||||
)))
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or(Ok(10))?;
|
||||
|
||||
let result = self
|
||||
.drive
|
||||
.comments()
|
||||
.list(file_id)
|
||||
.page_size(page_size)
|
||||
.param(
|
||||
"fields",
|
||||
"comments(author, content, createdTime, modifiedTime, id, anchor, resolved)",
|
||||
)
|
||||
.clear_scopes()
|
||||
.add_scope(Scope::Readonly)
|
||||
.doit()
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Err(e) => Err(ToolError::ExecutionError(format!(
|
||||
"Failed to execute google drive comment list, {}.",
|
||||
e
|
||||
))),
|
||||
Ok(r) => {
|
||||
let content =
|
||||
r.1.comments
|
||||
.map(|comments| {
|
||||
comments.into_iter().map(|c| {
|
||||
format!(
|
||||
"Author:{:?} Content: {} (created time: {}) (modified time: {})(anchor: {}) (resolved: {}) (id: {})",
|
||||
c.author.unwrap_or_default(),
|
||||
c.content.unwrap_or_default(),
|
||||
c.created_time.unwrap_or_default(),
|
||||
c.modified_time.unwrap_or_default(),
|
||||
c.anchor.unwrap_or_default(),
|
||||
c.resolved.unwrap_or_default(),
|
||||
c.id.unwrap_or_default()
|
||||
)
|
||||
})
|
||||
})
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
Ok(vec![Content::text(content.to_string())])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Router for GoogleDriveRouter {
|
||||
@@ -843,7 +1565,16 @@ impl Router for GoogleDriveRouter {
|
||||
match tool_name.as_str() {
|
||||
"search" => this.search(arguments).await,
|
||||
"read" => this.read(arguments).await,
|
||||
"upload" => this.upload(arguments).await,
|
||||
"create_doc" => this.create_doc(arguments).await,
|
||||
"create_sheets" => this.create_sheets(arguments).await,
|
||||
"create_slides" => this.create_slides(arguments).await,
|
||||
"update" => this.update(arguments).await,
|
||||
"update_doc" => this.update_doc(arguments).await,
|
||||
"update_sheets" => this.update_sheets(arguments).await,
|
||||
"update_slides" => this.update_slides(arguments).await,
|
||||
"sheets_tool" => this.sheets_tool(arguments).await,
|
||||
"comment_list" => this.comment_list(arguments).await,
|
||||
_ => Err(ToolError::NotFound(format!("Tool {} not found", tool_name))),
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user