mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-03 11:24:57 -05:00
Compare commits
2 Commits
fix/copilo
...
codex/add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f818ef157 | ||
|
|
636574a720 |
@@ -9,6 +9,8 @@ from pydantic import BaseModel
|
|||||||
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
|
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
|
||||||
from backend.data.model import SchemaField
|
from backend.data.model import SchemaField
|
||||||
from backend.util.settings import Settings
|
from backend.util.settings import Settings
|
||||||
|
from backend.util.file import MediaFileType, store_media_file, get_exec_file_path
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from ._auth import (
|
from ._auth import (
|
||||||
GOOGLE_OAUTH_IS_CONFIGURED,
|
GOOGLE_OAUTH_IS_CONFIGURED,
|
||||||
@@ -28,6 +30,7 @@ class Attachment(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class Email(BaseModel):
|
class Email(BaseModel):
|
||||||
|
threadId: str
|
||||||
id: str
|
id: str
|
||||||
subject: str
|
subject: str
|
||||||
snippet: str
|
snippet: str
|
||||||
@@ -82,6 +85,7 @@ class GmailReadBlock(Block):
|
|||||||
(
|
(
|
||||||
"email",
|
"email",
|
||||||
{
|
{
|
||||||
|
"threadId": "t1",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"subject": "Test Email",
|
"subject": "Test Email",
|
||||||
"snippet": "This is a test email",
|
"snippet": "This is a test email",
|
||||||
@@ -97,6 +101,7 @@ class GmailReadBlock(Block):
|
|||||||
"emails",
|
"emails",
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"threadId": "t1",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"subject": "Test Email",
|
"subject": "Test Email",
|
||||||
"snippet": "This is a test email",
|
"snippet": "This is a test email",
|
||||||
@@ -113,6 +118,7 @@ class GmailReadBlock(Block):
|
|||||||
test_mock={
|
test_mock={
|
||||||
"_read_emails": lambda *args, **kwargs: [
|
"_read_emails": lambda *args, **kwargs: [
|
||||||
{
|
{
|
||||||
|
"threadId": "t1",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"subject": "Test Email",
|
"subject": "Test Email",
|
||||||
"snippet": "This is a test email",
|
"snippet": "This is a test email",
|
||||||
@@ -185,6 +191,7 @@ class GmailReadBlock(Block):
|
|||||||
attachments = self._get_attachments(service, msg)
|
attachments = self._get_attachments(service, msg)
|
||||||
|
|
||||||
email = Email(
|
email = Email(
|
||||||
|
threadId=msg["threadId"],
|
||||||
id=msg["id"],
|
id=msg["id"],
|
||||||
subject=headers.get("subject", "No Subject"),
|
subject=headers.get("subject", "No Subject"),
|
||||||
snippet=msg["snippet"],
|
snippet=msg["snippet"],
|
||||||
@@ -528,3 +535,180 @@ class GmailRemoveLabelBlock(Block):
|
|||||||
if label["name"] == label_name:
|
if label["name"] == label_name:
|
||||||
return label["id"]
|
return label["id"]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class GmailGetThreadBlock(Block):
|
||||||
|
class Input(BlockSchema):
|
||||||
|
credentials: GoogleCredentialsInput = GoogleCredentialsField(
|
||||||
|
["https://www.googleapis.com/auth/gmail.readonly"]
|
||||||
|
)
|
||||||
|
threadId: str = SchemaField(description="Gmail thread ID")
|
||||||
|
includeSpamTrash: bool = SchemaField(
|
||||||
|
description="Include messages from Spam and Trash", default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Output(BlockSchema):
|
||||||
|
thread: dict = SchemaField(description="Raw Gmail thread resource")
|
||||||
|
error: str = SchemaField(description="Error message if any")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
id="21a79166-9df7-4b5f-9f36-96f639d86112",
|
||||||
|
description="Get a full Gmail thread by ID",
|
||||||
|
categories={BlockCategory.COMMUNICATION},
|
||||||
|
input_schema=GmailGetThreadBlock.Input,
|
||||||
|
output_schema=GmailGetThreadBlock.Output,
|
||||||
|
disabled=not GOOGLE_OAUTH_IS_CONFIGURED,
|
||||||
|
test_input={"threadId": "t1", "credentials": TEST_CREDENTIALS_INPUT},
|
||||||
|
test_credentials=TEST_CREDENTIALS,
|
||||||
|
test_output=[("thread", {"id": "t1"})],
|
||||||
|
test_mock={
|
||||||
|
"_get_thread": lambda *args, **kwargs: {"id": "t1"}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self, input_data: Input, *, credentials: GoogleCredentials, **kwargs
|
||||||
|
) -> BlockOutput:
|
||||||
|
service = GmailReadBlock._build_service(credentials, **kwargs)
|
||||||
|
thread = self._get_thread(
|
||||||
|
service, input_data.threadId, input_data.includeSpamTrash
|
||||||
|
)
|
||||||
|
yield "thread", thread
|
||||||
|
|
||||||
|
def _get_thread(self, service, thread_id: str, include_spam_trash: bool) -> dict:
|
||||||
|
return (
|
||||||
|
service.users()
|
||||||
|
.threads()
|
||||||
|
.get(
|
||||||
|
userId="me",
|
||||||
|
id=thread_id,
|
||||||
|
format="full",
|
||||||
|
includeSpamTrash=include_spam_trash,
|
||||||
|
)
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GmailReplyBlock(Block):
|
||||||
|
class Input(BlockSchema):
|
||||||
|
credentials: GoogleCredentialsInput = GoogleCredentialsField(
|
||||||
|
["https://www.googleapis.com/auth/gmail.send"]
|
||||||
|
)
|
||||||
|
threadId: str = SchemaField(description="Thread ID to reply in")
|
||||||
|
parentMessageId: str = SchemaField(
|
||||||
|
description="ID of the message being replied to"
|
||||||
|
)
|
||||||
|
to: list[str] = SchemaField(description="To recipients", default_factory=list)
|
||||||
|
cc: list[str] = SchemaField(description="CC recipients", default_factory=list)
|
||||||
|
bcc: list[str] = SchemaField(description="BCC recipients", default_factory=list)
|
||||||
|
subject: str = SchemaField(description="Email subject", default="")
|
||||||
|
body: str = SchemaField(description="Email body")
|
||||||
|
attachments: list[MediaFileType] = SchemaField(
|
||||||
|
description="Files to attach", default_factory=list, advanced=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Output(BlockSchema):
|
||||||
|
messageId: str = SchemaField(description="Sent message ID")
|
||||||
|
threadId: str = SchemaField(description="Thread ID")
|
||||||
|
message: dict = SchemaField(description="Raw Gmail message object")
|
||||||
|
error: str = SchemaField(description="Error message if any")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
id="12bf5a24-9b90-4f40-9090-4e86e6995e60",
|
||||||
|
description="Reply to a Gmail thread",
|
||||||
|
categories={BlockCategory.COMMUNICATION},
|
||||||
|
input_schema=GmailReplyBlock.Input,
|
||||||
|
output_schema=GmailReplyBlock.Output,
|
||||||
|
disabled=not GOOGLE_OAUTH_IS_CONFIGURED,
|
||||||
|
test_input={
|
||||||
|
"threadId": "t1",
|
||||||
|
"parentMessageId": "m1",
|
||||||
|
"body": "Thanks",
|
||||||
|
"credentials": TEST_CREDENTIALS_INPUT,
|
||||||
|
},
|
||||||
|
test_credentials=TEST_CREDENTIALS,
|
||||||
|
test_output=[
|
||||||
|
("messageId", "m2"),
|
||||||
|
("threadId", "t1"),
|
||||||
|
],
|
||||||
|
test_mock={
|
||||||
|
"_reply": lambda *args, **kwargs: {
|
||||||
|
"id": "m2",
|
||||||
|
"threadId": "t1",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self, input_data: Input, *, credentials: GoogleCredentials, graph_exec_id: str, **kwargs
|
||||||
|
) -> BlockOutput:
|
||||||
|
service = GmailReadBlock._build_service(credentials, **kwargs)
|
||||||
|
message = self._reply(
|
||||||
|
service,
|
||||||
|
input_data,
|
||||||
|
graph_exec_id,
|
||||||
|
)
|
||||||
|
yield "messageId", message["id"]
|
||||||
|
yield "threadId", message.get("threadId", input_data.threadId)
|
||||||
|
yield "message", message
|
||||||
|
|
||||||
|
def _reply(self, service, input_data: Input, graph_exec_id: str) -> dict:
|
||||||
|
parent = (
|
||||||
|
service.users()
|
||||||
|
.messages()
|
||||||
|
.get(
|
||||||
|
userId="me",
|
||||||
|
id=input_data.parentMessageId,
|
||||||
|
format="metadata",
|
||||||
|
metadataHeaders=["Subject", "References", "Message-ID"],
|
||||||
|
)
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
headers = {h["name"].lower(): h["value"] for h in parent.get("payload", {}).get("headers", [])}
|
||||||
|
subject = input_data.subject or (
|
||||||
|
f"Re: {headers.get('subject', '')}".strip()
|
||||||
|
)
|
||||||
|
references = headers.get("references", "").split()
|
||||||
|
if headers.get("message-id"):
|
||||||
|
references.append(headers["message-id"])
|
||||||
|
|
||||||
|
from email.mime.base import MIMEBase
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email import encoders
|
||||||
|
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
if input_data.to:
|
||||||
|
msg["To"] = ", ".join(input_data.to)
|
||||||
|
if input_data.cc:
|
||||||
|
msg["Cc"] = ", ".join(input_data.cc)
|
||||||
|
if input_data.bcc:
|
||||||
|
msg["Bcc"] = ", ".join(input_data.bcc)
|
||||||
|
msg["Subject"] = subject
|
||||||
|
if headers.get("message-id"):
|
||||||
|
msg["In-Reply-To"] = headers["message-id"]
|
||||||
|
if references:
|
||||||
|
msg["References"] = " ".join(references)
|
||||||
|
msg.attach(MIMEText(input_data.body, "html" if "<" in input_data.body else "plain"))
|
||||||
|
|
||||||
|
for attach in input_data.attachments:
|
||||||
|
local_path = store_media_file(graph_exec_id, attach, return_content=False)
|
||||||
|
abs_path = get_exec_file_path(graph_exec_id, local_path)
|
||||||
|
part = MIMEBase("application", "octet-stream")
|
||||||
|
with open(abs_path, "rb") as f:
|
||||||
|
part.set_payload(f.read())
|
||||||
|
encoders.encode_base64(part)
|
||||||
|
part.add_header("Content-Disposition", f"attachment; filename={Path(abs_path).name}")
|
||||||
|
msg.attach(part)
|
||||||
|
|
||||||
|
import base64
|
||||||
|
|
||||||
|
raw = base64.urlsafe_b64encode(msg.as_bytes()).decode("utf-8")
|
||||||
|
return (
|
||||||
|
service.users()
|
||||||
|
.messages()
|
||||||
|
.send(userId="me", body={"threadId": input_data.threadId, "raw": raw})
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ Below is a comprehensive list of all available blocks, categorized by their prim
|
|||||||
| Block Name | Description |
|
| Block Name | Description |
|
||||||
|------------|-------------|
|
|------------|-------------|
|
||||||
| [Gmail Read](google/gmail.md#gmail-read) | Retrieves and reads emails from a Gmail account |
|
| [Gmail Read](google/gmail.md#gmail-read) | Retrieves and reads emails from a Gmail account |
|
||||||
|
| [Gmail Get Thread](google/gmail.md#gmail-get-thread) | Returns every message in a Gmail thread |
|
||||||
|
| [Gmail Reply](google/gmail.md#gmail-reply) | Sends a reply that stays in the same thread |
|
||||||
| [Gmail Send](google/gmail.md#gmail-send) | Sends emails using a Gmail account |
|
| [Gmail Send](google/gmail.md#gmail-send) | Sends emails using a Gmail account |
|
||||||
| [Gmail List Labels](google/gmail.md#gmail-list-labels) | Retrieves all labels from a Gmail account |
|
| [Gmail List Labels](google/gmail.md#gmail-list-labels) | Retrieves all labels from a Gmail account |
|
||||||
| [Gmail Add Label](google/gmail.md#gmail-add-label) | Adds a label to a specific email in a Gmail account |
|
| [Gmail Add Label](google/gmail.md#gmail-add-label) | Adds a label to a specific email in a Gmail account |
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ The block connects to the user's Gmail account using their credentials, performs
|
|||||||
### Outputs
|
### Outputs
|
||||||
| Output | Description |
|
| Output | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| Email | Detailed information about a single email |
|
| Email | Detailed information about a single email (now includes `threadId`) |
|
||||||
| Emails | A list of email data for multiple emails |
|
| Emails | A list of email data for multiple emails |
|
||||||
| Error | An error message if something goes wrong during the process |
|
| Error | An error message if something goes wrong during the process |
|
||||||
|
|
||||||
@@ -141,4 +141,64 @@ The block first finds the ID of the specified label in the user's Gmail account.
|
|||||||
| Error | An error message if something goes wrong during the process |
|
| Error | An error message if something goes wrong during the process |
|
||||||
|
|
||||||
### Possible use case
|
### Possible use case
|
||||||
Automatically removing the "Unread" label from emails after they have been processed by a customer service representative.
|
Automatically removing the "Unread" label from emails after they have been processed by a customer service representative.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gmail Get Thread
|
||||||
|
|
||||||
|
### What it is
|
||||||
|
A block that retrieves an entire Gmail thread.
|
||||||
|
|
||||||
|
### What it does
|
||||||
|
Given a `threadId`, this block fetches all messages in that thread. You can optionally include messages in Spam and Trash.
|
||||||
|
|
||||||
|
### Inputs
|
||||||
|
| Input | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| Credentials | The user's Gmail account credentials for authentication |
|
||||||
|
| threadId | The ID of the thread to fetch |
|
||||||
|
| includeSpamTrash | Whether to include messages from Spam and Trash |
|
||||||
|
|
||||||
|
### Outputs
|
||||||
|
| Output | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| Thread | The raw Gmail thread resource |
|
||||||
|
| Error | An error message if something goes wrong |
|
||||||
|
|
||||||
|
### Possible use case
|
||||||
|
Checking if a recipient replied in an existing conversation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gmail Reply
|
||||||
|
|
||||||
|
### What it is
|
||||||
|
A block that sends a reply within an existing Gmail thread.
|
||||||
|
|
||||||
|
### What it does
|
||||||
|
This block builds a properly formatted reply email and sends it so Gmail keeps it in the same conversation.
|
||||||
|
|
||||||
|
### Inputs
|
||||||
|
| Input | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| Credentials | The user's Gmail account credentials for authentication |
|
||||||
|
| threadId | The thread to reply in |
|
||||||
|
| parentMessageId | The ID of the message you are replying to |
|
||||||
|
| To | List of recipients |
|
||||||
|
| Cc | List of CC recipients |
|
||||||
|
| Bcc | List of BCC recipients |
|
||||||
|
| Subject | Optional subject (defaults to `Re:` prefix) |
|
||||||
|
| Body | The email body |
|
||||||
|
| Attachments | Optional files to include |
|
||||||
|
|
||||||
|
### Outputs
|
||||||
|
| Output | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| MessageId | The ID of the sent message |
|
||||||
|
| ThreadId | The thread the reply belongs to |
|
||||||
|
| Message | Full Gmail message object |
|
||||||
|
| Error | Error message if something goes wrong |
|
||||||
|
|
||||||
|
### Possible use case
|
||||||
|
Automatically respond "Thanks, see you then" to a scheduling email while keeping the conversation tidy.
|
||||||
|
|||||||
Reference in New Issue
Block a user