Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google calendar #438

Merged
merged 20 commits into from
Jun 24, 2023
5 changes: 5 additions & 0 deletions gui/pages/api/DashboardService.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,9 @@ export const installAgentTemplate = (templateId) => {

export const updatePermissions = (permissionId, data) => {
return api.put(`/agentexecutionpermissions/update/status/${permissionId}`, data)
}


export const authenticateGoogleCred = (toolKitId) => {
return api.get(`/google/get_google_creds/toolkit_id/${toolKitId}`);
}
47 changes: 47 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

import pickle
import superagi
from superagi.agent.agent_prompt_builder import AgentPromptBuilder
from superagi.config.config import get_config
Expand All @@ -38,6 +39,7 @@
from superagi.models.types.login_request import LoginRequest
from superagi.models.user import User
from superagi.tools.base_tool import BaseTool
from superagi.models.tool_config import ToolConfig

app = FastAPI()

Expand Down Expand Up @@ -346,6 +348,45 @@ def login(request: LoginRequest, Authorize: AuthJWT = Depends()):
# access_token = Authorize.create_access_token(subject=user_email)
# return access_token

@app.get('/oauth-calendar')
async def google_auth_calendar(code: str = Query(...), Authorize: AuthJWT = Depends()):
client_id = db.session.query(ToolConfig).filter(ToolConfig.key == "GOOGLE_CLIENT_ID").first()
client_id = client_id.value
client_secret = db.session.query(ToolConfig).filter(ToolConfig.key == "GOOGLE_CLIENT_SECRET").first()
client_secret = client_secret.value
token_uri = 'https://oauth2.googleapis.com/token'
scope = 'https://www.googleapis.com/auth/calendar'
params = {
'client_id': client_id,
'client_secret': client_secret,
'redirect_uri': "http://localhost:3000/api/oauth-calendar",
'scope': scope,
'grant_type': 'authorization_code',
'code': code,
'access_type': 'offline'
}
response = requests.post(token_uri, data=params)
response = response.json()
expire_time = datetime.utcnow() + timedelta(seconds=response['expires_in'])
expire_time = expire_time - timedelta(minutes=5)
response['expiry'] = expire_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
root_dir = superagi.config.config.get_config('RESOURCES_OUTPUT_ROOT_DIR')
file_name = "credential_token.pickle"
final_path = file_name
if root_dir is not None:
root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir
root_dir = root_dir if root_dir.endswith("/") else root_dir + "/"
final_path = root_dir + file_name
else:
final_path = os.getcwd() + "/" + file_name
try:
with open(final_path, mode="wb") as file:
pickle.dump(response, file)
except Exception as err:
return f"Error: {err}"
frontend_url = superagi.config.config.get_config("FRONTEND_URL", "http://localhost:3000")
return RedirectResponse(frontend_url)

@app.get('/github-login')
def github_login():
"""GitHub login"""
Expand Down Expand Up @@ -429,6 +470,12 @@ async def root(Authorize: AuthJWT = Depends()):
except:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")

@app.get("/google/get_google_creds/toolkit_id/{toolkit_id}")
def get_google_calendar_tool_configs(toolkit_id: int):
google_calendar_config = db.session.query(ToolConfig).filter(ToolConfig.tool_kit_id == toolkit_id,ToolConfig.key == "GOOGLE_CLIENT_ID").first()
return {
"client_id": google_calendar_config.value
}

# #Unprotected route
@app.get("/hello/{name}")
Expand Down
103 changes: 103 additions & 0 deletions superagi/helper/calendar_date.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from datetime import datetime, timedelta, timezone
import pytz
from dateutil import tz

class CalendarDate():
def get_date_utc(self,start_date,end_date,start_time,end_time,service):
local_tz = self.get_time_zone(service)
local_tz = pytz.timezone(local_tz)
gmt_tz = pytz.timezone("GMT")
if start_date == 'None':
start_date = datetime.now(timezone.utc)
start_datetime = start_date.replace(day=1,hour=0, minute=0, second=0, microsecond=0)
else:
start_date = str(start_date)
start_date = datetime.strptime(start_date, "%Y-%m-%d")
if start_time == 'None':
start_datetime = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
else:
start_time = str(start_time)
time_obj = datetime.strptime(start_time, "%H:%M:%S")
start_datetime = start_date.replace(hour=time_obj.hour, minute=time_obj.minute, second=time_obj.second)

local_start_date = local_tz.localize(start_datetime)
start_datetime = local_start_date.astimezone(gmt_tz)
if end_date == 'None':
end_datetime = start_date + timedelta(days=30) - timedelta(microseconds=1)
else:
end_date = str(end_date)
end_date = datetime.strptime(end_date, "%Y-%m-%d")
if end_time == 'None':
end_datetime = end_date.replace(hour=23, minute=59, second=59, microsecond=999999)
else:
end_time = str(end_time)
time_obj = datetime.strptime(end_time, "%H:%M:%S")
end_datetime = end_date.replace(hour=time_obj.hour, minute=time_obj.minute, second=time_obj.second)

local_end_date = local_tz.localize(end_datetime)
end_datetime = local_end_date.astimezone(gmt_tz)

start_datetime_utc = start_datetime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
end_datetime_utc = end_datetime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
date_utc = {
"start_datetime_utc": start_datetime_utc,
"end_datetime_utc": end_datetime_utc
}

return date_utc

def get_time_zone(self,service):
calendar = service.calendars().get(calendarId='primary').execute()
timedetail = calendar['timeZone']
return timedetail

def create_event_dates(self, service, start_date, start_time, end_date, end_time):
timeZone = self.get_time_zone(service)
local_tz = pytz.timezone(timeZone)
gmt_tz = pytz.timezone('GMT')
if start_date == 'None' and start_time == 'None':
start_datetime = datetime.now(timezone.utc)
else:
if start_date == 'None':
date = datetime.now().date()
given_time = datetime.strptime(start_time,"%H:%M:%S")
given_time = given_time.time()
start_datetime = datetime.combine(date,given_time)
elif start_time == 'None':
given_date = datetime.strptime(start_date, "%Y-%m-%d")
time_obj = datetime.now(timezone.utc).time()
start_date = given_date.replace(hour=time_obj.hour, minute=time_obj.minute, second=time_obj.second)
local_start_date = gmt_tz.localize(start_date)
start_datetime = local_start_date.astimezone(local_tz)
else:
given_date = datetime.strptime(start_date, "%Y-%m-%d")
given_time = datetime.strptime(start_time, "%H:%M:%S")
start_datetime = given_date.replace(hour=given_time.hour, minute=given_time.minute, second=given_time.second)

if end_date == 'None' and end_time == 'None':
end_datetime = start_datetime + timedelta(hours=1)
else:
if end_date == 'None':
date = datetime.now().date()
given_time = datetime.strptime(start_time,"%H:%M:%S")
given_time = given_time.time()
end_datetime = datetime.combine(date,given_time)
elif end_time == 'None':
given_date = datetime.strptime(end_date, "%Y-%m-%d")
time_obj = datetime.now(timezone.utc).time()
end_date = given_date.replace(hour=time_obj.hour, minute=time_obj.minute, second=time_obj.second)
local_end_date = gmt_tz.localize(end_date)
end_datetime = local_end_date.astimezone(local_tz)
else:
given_date = datetime.strptime(end_date, "%Y-%m-%d")
given_time = datetime.strptime(end_time, "%H:%M:%S")
end_datetime = given_date.replace(hour=given_time.hour, minute=given_time.minute, second=given_time.second)

start_datetime_utc = start_datetime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
end_datetime_utc = end_datetime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
date_utc = {
"start_datetime_utc": start_datetime_utc,
"end_datetime_utc": end_datetime_utc,
"timeZone": timeZone
}
return date_utc
59 changes: 59 additions & 0 deletions superagi/helper/google_calendar_creds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pickle
import os
import json
from datetime import datetime
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import Flow
from google.auth.transport.requests import Request
from superagi.config.config import get_config
from googleapiclient.discovery import build
from sqlalchemy.orm import sessionmaker
from superagi.models.db import connect_db
from superagi.models.tool_config import ToolConfig

class GoogleCalendarCreds:

def get_credentials(self, toolkit_id):
file_name = "credential_token.pickle"
root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR')
file_path = file_name
if root_dir is not None:
root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir
root_dir = root_dir if root_dir.endswith("/") else root_dir + "/"
file_path = root_dir + file_name
else:
file_path = os.getcwd() + "/" + file_name
if os.path.exists(file_path):
engine = connect_db()
Session = sessionmaker(bind=engine)
session = Session()
with open(file_path,'rb') as file:
creds = pickle.load(file)
if isinstance(creds, str):
creds = json.loads(creds)
expire_time = datetime.strptime(creds["expiry"], "%Y-%m-%dT%H:%M:%S.%fZ")
google_creds = session.query(ToolConfig).filter(ToolConfig.tool_kit_id == toolkit_id).all()
TransformerOptimus marked this conversation as resolved.
Show resolved Hide resolved
client_id = ""
client_secret = ""
for credentials in google_creds:
credentials = credentials.__dict__
if credentials["key"] == "GOOGLE_CLIENT_ID":
client_id = credentials["value"]
else:
client_secret = credentials["value"]
Tarraann marked this conversation as resolved.
Show resolved Hide resolved
creds = Credentials.from_authorized_user_info(info={
"client_id": client_id,
"client_secret": client_secret,
"refresh_token": creds["refresh_token"],
"scopes": "https://www.googleapis.com/auth/calendar"
})
if expire_time < datetime.utcnow():
creds.refresh(Request())
creds_json = creds.to_json()
with open(file_path,'wb') as file:
pickle.dump(creds_json,file)
else:
return {"success": False}
service = build('calendar','v3',credentials=creds)
return {"success": True, "service": service}

62 changes: 62 additions & 0 deletions superagi/tools/google_calendar/create_calendar_event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from typing import Any, Type
from pydantic import BaseModel, Field
from superagi.tools.base_tool import BaseTool
from superagi.helper.google_calendar_creds import GoogleCalendarCreds
from superagi.helper.calendar_date import CalendarDate

class CreateEventCalendarInput(BaseModel):
event_name: str = Field(..., description="Name of the event/meeting to be scheduled, if not given craete a name depending on description.")
description: str = Field(..., description="Description of the event/meeting to be scheduled.")
start_date: str = Field(..., description="Start date of the event to be scheduled in format 'yyyy-mm-dd', if no value is given keep the default value as 'None'.")
start_time: str = Field(..., description="Start time of the event to be scheduled in format 'hh:mm:ss', if no value is given keep the default value as 'None'.")
end_date: str = Field(..., description="End Date of the event to be scheduled in format 'yyyy-mm-dd', if no value is given keep the default value as 'None'.")
end_time: str = Field(..., description="End Time of the event to be scheduled in format 'hh:mm:ss', if no value is given keep the default value as 'None'.")
attendees: list = Field(..., description="List of attendees email ids to be invited for the event.")
location: str = Field(..., description="Geographical location of the event. if no value is given keep the default value as 'None'")

class CreateEventCalendarTool(BaseTool):
name: str = "Create Google Calendar Event"
args_schema: Type[BaseModel] = CreateEventCalendarInput
description: str = "Create an event for Google Calendar"

def _execute(self, event_name: str, description: str, attendees: list, start_date: str = 'None', start_time: str = 'None', end_date: str = 'None', end_time: str = 'None', location: str = 'None'):
toolkit_id = self.tool_kit_config.tool_kit_id
service = GoogleCalendarCreds().get_credentials(toolkit_id)
if service["success"]:
service = service["service"]
else:
return f"Kindly connect to Google Calendar"
date_utc = CalendarDate().create_event_dates(service, start_date, start_time, end_date, end_time)
attendees_list = []
for attendee in attendees:
email_id = {
"email": attendee
}
attendees_list.append(email_id)
event = {
"summary": event_name,
"description": description,
"start": {
"dateTime": date_utc["start_datetime_utc"],
"timeZone": date_utc["timeZone"]
},
"end": {
"dateTime": date_utc["end_datetime_utc"],
"timeZone": date_utc["timeZone"]
},
"attendees": attendees_list
}
if location != "None":
event["location"] = location
else:
event["conferenceData"] = {
"createRequest": {
"requestId": f"meetSample123",
"conferenceSolutionKey": {
"type": "hangoutsMeet"
},
},
}
event = service.events().insert(calendarId="primary", body=event, conferenceDataVersion=1).execute()
output_str = f"Event {event_name} at {date_utc['start_datetime_utc']} created successfully, link for the event {event.get('htmlLink')}"
return output_str
28 changes: 28 additions & 0 deletions superagi/tools/google_calendar/delete_calendar_event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Any, Type
from pydantic import BaseModel, Field
from superagi.tools.base_tool import BaseTool
from superagi.helper.google_calendar_creds import GoogleCalendarCreds

class DeleteCalendarEventInput(BaseModel):
event_id: str = Field(..., description="The id of event to be deleted from Google Calendar. default value is None")

class DeleteCalendarEventTool(BaseTool):
name: str = "Delete Google Calendar Event"
args_schema: Type[BaseModel] = DeleteCalendarEventInput
description: str = "Delete an event from Google Calendar"

def _execute(self, event_id: str):
toolkit_id = self.tool_kit_config.tool_kit_id
service = GoogleCalendarCreds().get_credentials(toolkit_id)
if service["success"]:
service = service["service"]
else:
return f"Kindly connect to Google Calendar"
if event_id == "None":
return f"Add Event ID to delete an event from Google Calendar"
else:
result = service.events().delete(
calendarId = "primary",
eventId = event_id
).execute()
return f"Event Successfully deleted from your Google Calendar"
43 changes: 43 additions & 0 deletions superagi/tools/google_calendar/event_details_calendar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import base64
from typing import Any, Type
from pydantic import BaseModel, Field
from superagi.tools.base_tool import BaseTool
from superagi.helper.google_calendar_creds import GoogleCalendarCreds

class EventDetailsCalendarInput(BaseModel):
event_id: str = Field(..., description="The id of event to be fetched from Google Calendar. if no value is given keep default value is None")

class EventDetailsCalendarTool(BaseTool):
name: str = "Fetch Google Calendar Event"
args_schema: Type[BaseModel] = EventDetailsCalendarInput
description: str = "Fetch an event from Google Calendar"

def _execute(self, event_id: str):
toolkit_id = self.tool_kit_config.tool_kit_id
service = GoogleCalendarCreds().get_credentials(toolkit_id)
if service["success"]:
service = service["service"]
else:
return f"Kindly connect to Google Calendar"
if event_id == "None":
return f"Add Event ID to fetch details of an event from Google Calendar"
else:
decoded_id = base64.b64decode(event_id)
eid = decoded_id.decode("utf-8")
eid = eid.split(" ", 1)[0]
Tarraann marked this conversation as resolved.
Show resolved Hide resolved
result = service.events().get(
calendarId = "primary",
eventId = eid
).execute()
if "summary" in result:
summary = result['summary']
if result['start'] and result['end']:
start_date = result['start']['dateTime']
end_date = result['end']['dateTime']
attendees = []
if "attendees" in result:
for attendee in result['attendees']:
attendees.append(attendee['email'])
attendees_str = ','.join(attendees)
output_str = f"Event details for the event id '{event_id}' is - \nSummary : {summary}\nStart Date and Time : {start_date}\nEnd Date and Time : {end_date}\nAttendees : {attendees_str}"
return output_str
Loading