Skip to content
This repository has been archived by the owner on Aug 19, 2023. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
devinjones812 committed Jul 29, 2023
2 parents b57f0f9 + 9738c5c commit 0b868ac
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 1 deletion.
6 changes: 6 additions & 0 deletions griptape/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
from .aws_s3_client.tool import AwsS3Client
from .computer.tool import Computer
from .proxycurl_client.tool import ProxycurlClient
from .base_google_client import BaseGoogleClient
from .google_gmail.tool import GoogleGmailClient
from .google_cal.tool import GoogleCalendarClient

__all__ = [
"BaseAwsClient",
"AwsIamClient",
"AwsS3Client",
"BaseGoogleClient",
"GoogleGmailClient",
"GoogleCalendarClient",
"Calculator",
"WebSearch",
"WebScraper",
Expand Down
1 change: 0 additions & 1 deletion griptape/tools/base_aws_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from griptape.core import BaseTool
from griptape.core.decorators import activity


@define
class BaseAwsClient(BaseTool, ABC):
session: boto3.session = field(kw_only=True)
Expand Down
7 changes: 7 additions & 0 deletions griptape/tools/base_google_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from abc import ABC
from attr import define, field
from griptape.core import BaseTool

@define
class BaseGoogleClient(BaseTool, ABC):
service_account_credentials: dict = field(kw_only=True)
Empty file.
5 changes: 5 additions & 0 deletions griptape/tools/google_cal/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: "v1"
name: Google Calendar Tool
description: Tool for working with Google Calendar.
contact_email: [email protected]
legal_info_url: https://www.griptape.ai/legal
1 change: 1 addition & 0 deletions griptape/tools/google_cal/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
google-api-python-client
129 changes: 129 additions & 0 deletions griptape/tools/google_cal/tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
from __future__ import annotations
import logging
import datetime
from schema import Schema, Literal, Optional
from attr import define
from griptape.artifacts import TextArtifact, ErrorArtifact, InfoArtifact
from griptape.core.decorators import activity
from griptape.tools import BaseGoogleClient


@define
class GoogleCalendarClient(BaseGoogleClient):
CREATE_EVENT_SCOPES = ['https://www.googleapis.com/auth/calendar']
GET_UPCOMING_EVENTS_SCOPES = ['https://www.googleapis.com/auth/calendar']

@activity(config={
"description": "Can be used to get upcoming events from a google calendar",
"schema": Schema({
Literal(
"calendar_id",
description="id of the google calendar such as 'primary'"
): str,
Literal(
"calendar_owner_email",
description="email of the calendar's owner"
): str,
Literal(
"max_events",
description="maximum number of events to return"
): int
})
})
def get_upcoming_events(self, params: dict) -> list[TextArtifact] | ErrorArtifact:
from google.oauth2 import service_account
from googleapiclient.discovery import build

values = params["values"]

try:
credentials = service_account.Credentials.from_service_account_info(
self.service_account_credentials, scopes=self.GET_UPCOMING_EVENTS_SCOPES
)
delegated_credentials = credentials.with_subject(values["calendar_owner_email"])
service = build('calendar', 'v3', credentials=delegated_credentials)
now = datetime.datetime.utcnow().isoformat() + 'Z'

events_result = service.events().list(
calendarId=values["calendar_id"], timeMin=now,
maxResults=values['max_events'], singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
return [TextArtifact(str(e)) for e in events]
except Exception as e:
logging.error(e)
return ErrorArtifact(f"error retrieving calendar events {e}")

@activity(config={
"description": "Can be used to create an event on a google calendar",
"schema": Schema({
Literal(
"calendar_owner_email",
description="email of the calendar's owner"
): str,
Literal(
"start_datetime",
description="combined date-time value in string format according to RFC3399 excluding the timezone for when the meeting starts"
): str,
Literal(
"start_time_zone",
description="time zone in which the start time is specified in string format according to IANA time zone data base name, such as 'Europe/Zurich'"
): str,
Literal(
"end_datetime",
description="combined date-time value in string format according to RFC3399 excluding the timezone for when the meeting ends"
): str,
Literal(
"end_time_zone",
description="time zone in which the end time is specified in string format according to IANA time zone data base name, such as 'Europe/Zurich'"
): str,
Literal(
"title",
description="title of the event"
): str,
Literal(
"description",
description="description of the event"
): str,
Literal(
"attendees",
description="list of the email addresses of attendees using 'email' as key"
): list,
Optional(Literal(
"location",
description="location of the event"
)): str
})
})
def create_event(self, params: dict) -> InfoArtifact | ErrorArtifact:
from google.oauth2 import service_account
from googleapiclient.discovery import build

values = params['values']

try:
credentials = service_account.Credentials.from_service_account_info(
self.service_account_credentials, scopes=self.CREATE_EVENT_SCOPES
)
delegated_credentials = credentials.with_subject(values["calendar_owner_email"])
service = build('calendar', 'v3', credentials=delegated_credentials)

event = {
'summary': values['title'],
'location': values.get('location'),
'description': values['description'],
'start': {
'dateTime': values['start_datetime'],
'timeZone': values['start_time_zone']
},
'end': {
'dateTime': values['end_datetime'],
'timeZone': values['end_time_zone']
},
'attendees': values['attendees']
}
event = service.events().insert(calendarId='primary', body=event).execute()
return InfoArtifact(f'A calendar event was successfully created. (Link:{event.get("htmlLink")})')
except Exception as e:
logging.error(e)
return ErrorArtifact(f"error creating calendar event: {e}")
Empty file.
5 changes: 5 additions & 0 deletions griptape/tools/google_gmail/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: "v1"
name: Google Gmail Client
description: Tool for working with Google Gmail.
contact_email: [email protected]
legal_info_url: https://www.griptape.ai/legal
1 change: 1 addition & 0 deletions griptape/tools/google_gmail/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
google-api-python-client
71 changes: 71 additions & 0 deletions griptape/tools/google_gmail/tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from __future__ import annotations
import logging
import base64
from email.message import EmailMessage
from schema import Schema, Literal
from attr import define
from griptape.artifacts import InfoArtifact, ErrorArtifact
from griptape.core.decorators import activity
from griptape.tools import BaseGoogleClient


@define
class GoogleGmailClient(BaseGoogleClient):
CREATE_DRAFT_EMAIL_SCOPES = ['https://www.googleapis.com/auth/gmail.compose']
@activity(config={
"description": "Can be used to create a draft email in GMail",
"schema": Schema({
Literal(
"to",
description="email address which to send to"
): str,
Literal(
"subject",
description="subject of the email"
): str,
Literal(
"from",
description="email address which to send from"
): str,
Literal(
"body",
description="body of the email"
): str,
Literal(
"inbox_owner",
description="email address of the inbox owner where the draft will be created. if not provided, use the from address"
): str
})
})
def create_draft_email(self, params: dict) -> InfoArtifact | ErrorArtifact:
from google.oauth2 import service_account
from googleapiclient.discovery import build

values = params["values"]

try:
credentials = service_account.Credentials.from_service_account_info(
self.service_account_credentials, scopes=self.CREATE_DRAFT_EMAIL_SCOPES
)

delegated_creds = credentials.with_subject(values["inbox_owner"])
service = build('gmail', 'v1', credentials=delegated_creds)

message = EmailMessage()
message.set_content(values["body"])
message['To'] = values["to"]
message['From'] = values["from"]
message['Subject'] = values["subject"]

encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
create_message = {
'message': {
'raw': encoded_message
}
}
draft = service.users().drafts().create(userId='me', body=create_message).execute()
return InfoArtifact(f'An email draft was successfully created (ID: {draft["id"]})')

except Exception as error:
logging.error(error)
return ErrorArtifact(f'error creating draft email: {error}')
28 changes: 28 additions & 0 deletions tests/unit/test_google_cal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from griptape.tools import GoogleCalendarClient


class TestGoogleCalClient:
def test_get_upcoming_events(self):
value = {
"calendar_id": "primary",
"calendar_owner_email": "[email protected]",
"max_events": 10
}
assert "error retrieving calendar events" in GoogleCalendarClient(
service_account_credentials={}
).get_upcoming_events({"values": value}).value

def test_create_event(self):
value = {
"calendar_owner_email": "[email protected]",
"start_datetime": "2023-07-28T13:00:00",
"start_time_zone": "America/Los_Angeles",
"end_datetime": "2023-07-28T14:00:00",
"end_time_zone": "America/Los_Angeles",
"title": "could have been an email",
"description": "why wasn't this an email?",
"location": "not rto"
}
assert "error creating calendar event" in GoogleCalendarClient(
service_account_credentials={}
).create_event({"values": value}).value
15 changes: 15 additions & 0 deletions tests/unit/test_google_gmail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from griptape.tools import GoogleGmailClient


class TestGoogleGmailClient:
def test_create_draft_email(self):
value = {
"to": "[email protected]",
"subject": "stacey's mom",
"from": "[email protected]",
"body": "got it going on",
"inbox_owner": "[email protected]"
}
assert "error creating draft email" in GoogleGmailClient(
service_account_credentials={}
).create_draft_email({"values": value}).value

0 comments on commit 0b868ac

Please sign in to comment.