| from googleapiclient.discovery import build | |
| from datetime import datetime, timedelta | |
| from utils.auth import authenticate_google_services | |
| class GoogleCalendar_Agent: | |
| def __init__(self, creds=None, timezone="Asia/Taipei"): | |
| if not creds: | |
| creds = authenticate_google_services() | |
| self.service = build("calendar", "v3", credentials=creds) | |
| self.timezone = timezone | |
| def list_events(self, n: int=5, queries=None) -> str: | |
| now = datetime.utcnow().isoformat() + 'Z' | |
| events_result = self.service.events().list( | |
| calendarId='primary', | |
| timeMin=now, | |
| maxResults=n, | |
| singleEvents=True, | |
| orderBy='startTime' | |
| ).execute() | |
| events = events_result.get('items', []) | |
| if not events: | |
| return "🔕 找不到即將到來的行事曆事件。" | |
| output = "📅 您接下來的行事曆事件如下:\n" | |
| for event in events: | |
| start = event['start'].get('dateTime', event['start'].get('date')) | |
| summary = event.get('summary', 'No title') | |
| output += f"• {summary} at {start}\n" | |
| return output | |
| def create_event(self, | |
| summary: str, | |
| start_time: str, | |
| end_time: str, | |
| location: str = None, | |
| description: str = None, | |
| attendees: list = None, | |
| reminder_minutes: int = None) -> str: | |
| event = { | |
| 'summary': summary, | |
| 'start': {'dateTime': start_time, 'timeZone': self.timezone}, | |
| 'end': {'dateTime': end_time, 'timeZone': self.timezone} | |
| } | |
| if location: | |
| event['location'] = location | |
| if description: | |
| event['description'] = description | |
| if attendees: | |
| event['attendees'] = [{'email': email} for email in attendees] | |
| if reminder_minutes: | |
| event['reminders'] = { | |
| 'useDefault': False, | |
| 'overrides': [{'method': 'popup', 'minutes': reminder_minutes}] | |
| } | |
| try: | |
| created_event = self.service.events().insert( | |
| calendarId='primary', | |
| sendUpdates='all', | |
| body=event | |
| ).execute() | |
| return f"""✅ 已成功建立事件:\n標題 : {summary}\n開始時間 : {start_time}\n結束時間 : {end_time}\n地點 : {location or '無'}\n說明 : {description or '無'}\n參與者 : {', '.join(attendees) if attendees else '無'}\n提醒時間 : {reminder_minutes if reminder_minutes is not None else '使用預設提醒'}\n時區 : {self.timezone}\n事件連結 : {created_event.get('htmlLink')}""" | |
| except Exception as e: | |
| return f"❌ 建立事件失敗,以下是錯誤訊息(英文): {e}" | |
| def find_event(self, query: str,) -> list: | |
| now = datetime.utcnow().isoformat() + 'Z' | |
| events_result = self.service.events().list( | |
| calendarId='primary', | |
| timeMin=now, | |
| singleEvents=True, | |
| orderBy='startTime', | |
| q=query | |
| ).execute() | |
| return events_result.get('items', []) | |
| def update_event(self, query: str, | |
| new_summary=None, | |
| new_location=None, | |
| new_description=None, | |
| new_start=None, | |
| new_end=None, | |
| new_attendees=None, | |
| reminder_minutes=None): | |
| events = self.find_event(query) | |
| if not events: | |
| return f"❌ 找不到包含「{query}」的事件" | |
| if all(arg is None for arg in [ | |
| new_summary, new_location, new_description, | |
| new_start, new_end, new_attendees, reminder_minutes | |
| ]): | |
| return f"⚠️ 沒有指定任何要更新的欄位,未執行更新。" | |
| for event in events: | |
| event_id = event['id'] | |
| try: | |
| if new_summary: | |
| event['summary'] = new_summary | |
| if new_location: | |
| event['location'] = new_location | |
| if new_description: | |
| event['description'] = new_description | |
| if new_attendees: | |
| event['attendees'] = [{'email': email} for email in new_attendees] | |
| if reminder_minutes: | |
| event['reminders'] = { | |
| 'useDefault': False, | |
| 'overrides': [{'method': 'popup', 'minutes': reminder_minutes}] | |
| } | |
| if new_start or new_end: | |
| event['start']['dateTime'] = new_start | |
| event['end']['dateTime'] = new_end | |
| event['start']['timeZone'] = 'Asia/Taipei' | |
| event['end']['timeZone'] = 'Asia/Taipei' | |
| updated_event = self.service.events().update( | |
| calendarId='primary', | |
| eventId=event_id, | |
| sendUpdates='all', | |
| body=event | |
| ).execute() | |
| return f"✅ 已成功更新事件:{updated_event['summary']}" | |
| except Exception as e: | |
| return f"❌ 更新事件失敗,以下是錯誤訊息(英文): {e}" | |
| def delete_event(self, query: str): | |
| now = datetime.utcnow().isoformat() + 'Z' | |
| event_results = self.find_event(query) | |
| if len(event_results) == 0: | |
| return f"❌ 找不到包含「{query}」的事件" | |
| for event in event_results: | |
| target_id = event["id"] | |
| try: | |
| self.service.events().delete( | |
| calendarId='primary', | |
| eventId=target_id, | |
| sendUpdates='all' | |
| ).execute() | |
| return f"🗑️ 已刪除事件:{event['summary']}(ID: {target_id})" | |
| except Exception as e: | |
| return f"❌ 刪除事件失敗(ID: {target_id}),以下是錯誤訊息(英文): {e}" | |
| def get_event_details(self, query: str): | |
| events = self.find_event(query) | |
| if not events: | |
| return f"❌ 找不到符合 '{query}' 的事件!" | |
| event = events[0] | |
| summary = event.get('summary', 'No Title') | |
| start_time = event['start'].get('dateTime', event['start'].get('date')) | |
| end_time = event['end'].get('dateTime', event['end'].get('date')) | |
| formatted_string = f"事件: {summary}\n開始時間: {start_time}\n結束時間: {end_time}" | |
| attendees = event.get('attendees', []) | |
| emails = [attendee.get('email') for attendee in attendees if attendee.get('email')] | |
| if emails: | |
| formatted_string += f"\n參與者: {', '.join(emails)}" | |
| else: | |
| formatted_string += "\n無參與者" | |
| return formatted_string | |
| def get_all_tools(self): | |
| LLM_tools = [] | |
| LLM_tools.append({ | |
| "name": "create_Calendar_event", | |
| "description": "Create a new Google Calendar event by specifying summary, start time, and end time, and optionally location, description, attendees, and reminder.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "summary": { | |
| "type": "string", | |
| "description": "Title or summary of the event (e.g., 'Lunch with Alice')" | |
| }, | |
| "start_time": { | |
| "type": "string", | |
| "format": "date-time", | |
| "description": "Start time of the event in ISO format (e.g., '2025-07-01T14:00:00')" | |
| }, | |
| "end_time": { | |
| "type": "string", | |
| "format": "date-time", | |
| "description": "End time of the event in ISO format (e.g., '2025-07-01T15:00:00')" | |
| }, | |
| "location": { | |
| "type": "string", | |
| "description": "Location of the event (e.g., 'Taipei 101')" | |
| }, | |
| "description": { | |
| "type": "string", | |
| "description": "Detailed description of the event" | |
| }, | |
| "attendees": { | |
| "type": "array", | |
| "items": {"type": "string"}, | |
| "description": "List of email addresses to invite to the event" | |
| }, | |
| "reminder_minutes": { | |
| "type": "integer", | |
| "description": "Number of minutes before the event to trigger a popup reminder" | |
| } | |
| }, | |
| "required": ["summary", "start_time", "end_time"] | |
| }, | |
| }) | |
| LLM_tools.append({ | |
| "name": "list_Calendar_events", | |
| "description": "List your upcoming Google Calendar events.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "n": { | |
| "type": "integer", | |
| "default": 5, | |
| "description": "Number of upcoming events to list (default: 5)" | |
| } | |
| } | |
| } | |
| }) | |
| LLM_tools.append({ | |
| "name": "update_Calendar_event", | |
| "description": "Update a Google Calendar event using a keyword query to find it. You can update summary, location, description, start/end time, attendees, or reminders.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "query": { | |
| "type": "string", | |
| "description": "Keyword to find the event to update (e.g., 'meeting with Bob')" | |
| }, | |
| "new_summary": { | |
| "type": "string", | |
| "description": "New summary/title for the event" | |
| }, | |
| "new_location": { | |
| "type": "string", | |
| "description": "New location for the event" | |
| }, | |
| "new_description": { | |
| "type": "string", | |
| "description": "New description for the event" | |
| }, | |
| "new_start": { | |
| "type": "string", | |
| "format": "date-time", | |
| "description": "New start time in ISO format" | |
| }, | |
| "new_end": { | |
| "type": "string", | |
| "format": "date-time", | |
| "description": "New end time in ISO format" | |
| }, | |
| "new_attendees": { | |
| "type": "array", | |
| "items": {"type": "string"}, | |
| "description": "New list of attendees (email addresses)" | |
| }, | |
| "reminder_minutes": { | |
| "type": "integer", | |
| "description": "Updated popup reminder time in minutes" | |
| } | |
| }, | |
| "required": ["query"] | |
| } | |
| }) | |
| LLM_tools.append({ | |
| "name": "delete_Calendar_event", | |
| "description": "Delete a Google Calendar event using a keyword query to find it.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "query": { | |
| "type": "string", | |
| "description": "Keyword to find the event to delete (e.g., 'meeting with Bob')" | |
| } | |
| }, | |
| "required": ["query"] | |
| } | |
| }) | |
| LLM_tools.append({ | |
| "name": "get_event_details", | |
| "description": "Find the attendee email addresses and the summary of a specific event, based on a keyword.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "query": { | |
| "type": "string", | |
| "description": "Keyword to find the event and extract attendees and summary of that event." | |
| } | |
| }, | |
| "required": ["query"] | |
| } | |
| }) | |
| return LLM_tools | |