Files
Flowise/packages/components/nodes/tools/GoogleCalendar/core.ts
T
Henry Heng 30c4180d97 Feature/Add teams, gmail, outlook tools (#4577)
* add teams, gmail, outlook tools

* update docs link

* update credentials for oauth2

* add jira tool

* add google drive, google calendar, google sheets tools, powerpoint, excel, word doc loader

* update jira logo

* Refactor Gmail and Outlook tools to remove maxOutputLength parameter and enhance request handling. Update response formatting to include parameters in the output. Adjust Google Drive tools to simplify success messages by removing unnecessary parameter details.
2025-06-06 19:52:04 +01:00

865 lines
28 KiB
TypeScript

import { z } from 'zod'
import fetch from 'node-fetch'
import { DynamicStructuredTool } from '../OpenAPIToolkit/core'
import { TOOL_ARGS_PREFIX } from '../../../src/agents'
export const desc = `Use this when you want to access Google Calendar API for managing events and calendars`
export interface Headers {
[key: string]: string
}
export interface Body {
[key: string]: any
}
export interface RequestParameters {
headers?: Headers
body?: Body
url?: string
description?: string
name?: string
actions?: string[]
accessToken?: string
defaultParams?: any
}
// Define schemas for different Google Calendar operations
// Event Schemas
const ListEventsSchema = z.object({
calendarId: z.string().default('primary').describe('Calendar ID (use "primary" for primary calendar)'),
timeMin: z.string().optional().describe('Lower bound for event search (RFC3339 timestamp)'),
timeMax: z.string().optional().describe('Upper bound for event search (RFC3339 timestamp)'),
maxResults: z.number().optional().default(250).describe('Maximum number of events to return'),
singleEvents: z.boolean().optional().default(true).describe('Whether to expand recurring events into instances'),
orderBy: z.enum(['startTime', 'updated']).optional().describe('Order of events returned'),
query: z.string().optional().describe('Free text search terms')
})
const CreateEventSchema = z.object({
calendarId: z.string().default('primary').describe('Calendar ID where the event will be created'),
summary: z.string().describe('Event title/summary'),
description: z.string().optional().describe('Event description'),
location: z.string().optional().describe('Event location'),
startDateTime: z.string().optional().describe('Event start time (ISO 8601 format)'),
endDateTime: z.string().optional().describe('Event end time (ISO 8601 format)'),
startDate: z.string().optional().describe('Start date for all-day events (YYYY-MM-DD)'),
endDate: z.string().optional().describe('End date for all-day events (YYYY-MM-DD)'),
timeZone: z.string().optional().describe('Time zone (e.g., America/New_York)'),
attendees: z.string().optional().describe('Comma-separated list of attendee emails'),
recurrence: z.string().optional().describe('Recurrence rules (RRULE format)'),
reminderMinutes: z.number().optional().describe('Minutes before event to send reminder'),
visibility: z.enum(['default', 'public', 'private', 'confidential']).optional().describe('Event visibility')
})
const GetEventSchema = z.object({
calendarId: z.string().default('primary').describe('Calendar ID'),
eventId: z.string().describe('Event ID')
})
const UpdateEventSchema = z.object({
calendarId: z.string().default('primary').describe('Calendar ID'),
eventId: z.string().describe('Event ID'),
summary: z.string().optional().describe('Updated event title/summary'),
description: z.string().optional().describe('Updated event description'),
location: z.string().optional().describe('Updated event location'),
startDateTime: z.string().optional().describe('Updated event start time (ISO 8601 format)'),
endDateTime: z.string().optional().describe('Updated event end time (ISO 8601 format)'),
startDate: z.string().optional().describe('Updated start date for all-day events (YYYY-MM-DD)'),
endDate: z.string().optional().describe('Updated end date for all-day events (YYYY-MM-DD)'),
timeZone: z.string().optional().describe('Updated time zone'),
attendees: z.string().optional().describe('Updated comma-separated list of attendee emails'),
recurrence: z.string().optional().describe('Updated recurrence rules'),
reminderMinutes: z.number().optional().describe('Updated reminder minutes'),
visibility: z.enum(['default', 'public', 'private', 'confidential']).optional().describe('Updated event visibility')
})
const DeleteEventSchema = z.object({
calendarId: z.string().default('primary').describe('Calendar ID'),
eventId: z.string().describe('Event ID to delete')
})
const QuickAddEventSchema = z.object({
calendarId: z.string().default('primary').describe('Calendar ID'),
quickAddText: z.string().describe('Natural language text for quick event creation')
})
// Calendar Schemas
const ListCalendarsSchema = z.object({
showHidden: z.boolean().optional().describe('Whether to show hidden calendars'),
minAccessRole: z.enum(['freeBusyReader', 'reader', 'writer', 'owner']).optional().describe('Minimum access role')
})
const CreateCalendarSchema = z.object({
summary: z.string().describe('Calendar title/name'),
description: z.string().optional().describe('Calendar description'),
location: z.string().optional().describe('Calendar location'),
timeZone: z.string().optional().describe('Calendar time zone (e.g., America/New_York)')
})
const GetCalendarSchema = z.object({
calendarId: z.string().describe('Calendar ID')
})
const UpdateCalendarSchema = z.object({
calendarId: z.string().describe('Calendar ID'),
summary: z.string().optional().describe('Updated calendar title/name'),
description: z.string().optional().describe('Updated calendar description'),
location: z.string().optional().describe('Updated calendar location'),
timeZone: z.string().optional().describe('Updated calendar time zone')
})
const DeleteCalendarSchema = z.object({
calendarId: z.string().describe('Calendar ID to delete')
})
const ClearCalendarSchema = z.object({
calendarId: z.string().describe('Calendar ID to clear (removes all events)')
})
// Freebusy Schemas
const QueryFreebusySchema = z.object({
timeMin: z.string().describe('Lower bound for freebusy query (RFC3339 timestamp)'),
timeMax: z.string().describe('Upper bound for freebusy query (RFC3339 timestamp)'),
calendarIds: z.string().describe('Comma-separated list of calendar IDs to check for free/busy info'),
groupExpansionMax: z.number().optional().describe('Maximum number of calendars for which FreeBusy information is to be provided'),
calendarExpansionMax: z.number().optional().describe('Maximum number of events that can be expanded for each calendar')
})
class BaseGoogleCalendarTool extends DynamicStructuredTool {
protected accessToken: string = ''
constructor(args: any) {
super(args)
this.accessToken = args.accessToken ?? ''
}
async makeGoogleCalendarRequest({
endpoint,
method = 'GET',
body,
params
}: {
endpoint: string
method?: string
body?: any
params?: any
}): Promise<string> {
const url = `https://www.googleapis.com/calendar/v3/${endpoint}`
const headers = {
Authorization: `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
Accept: 'application/json',
...this.headers
}
const response = await fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined
})
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Google Calendar API Error ${response.status}: ${response.statusText} - ${errorText}`)
}
const data = await response.text()
return data + TOOL_ARGS_PREFIX + JSON.stringify(params)
}
}
// Event Tools
class ListEventsTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'list_events',
description: 'List events from Google Calendar',
schema: ListEventsSchema,
baseUrl: '',
method: 'GET',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
const queryParams = new URLSearchParams()
if (params.timeMin) queryParams.append('timeMin', params.timeMin)
if (params.timeMax) queryParams.append('timeMax', params.timeMax)
if (params.maxResults) queryParams.append('maxResults', params.maxResults.toString())
if (params.singleEvents !== undefined) queryParams.append('singleEvents', params.singleEvents.toString())
if (params.orderBy) queryParams.append('orderBy', params.orderBy)
if (params.query) queryParams.append('q', params.query)
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}/events?${queryParams.toString()}`
try {
const response = await this.makeGoogleCalendarRequest({ endpoint, params })
return response
} catch (error) {
return `Error listing events: ${error}`
}
}
}
class CreateEventTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'create_event',
description: 'Create a new event in Google Calendar',
schema: CreateEventSchema,
baseUrl: '',
method: 'POST',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const eventData: any = {
summary: params.summary
}
if (params.description) eventData.description = params.description
if (params.location) eventData.location = params.location
// Handle date/time
if (params.startDate && params.endDate) {
// All-day event
eventData.start = { date: params.startDate }
eventData.end = { date: params.endDate }
} else if (params.startDateTime && params.endDateTime) {
// Timed event
eventData.start = {
dateTime: params.startDateTime,
timeZone: params.timeZone || 'UTC'
}
eventData.end = {
dateTime: params.endDateTime,
timeZone: params.timeZone || 'UTC'
}
}
// Handle attendees
if (params.attendees) {
eventData.attendees = params.attendees.split(',').map((email: string) => ({
email: email.trim()
}))
}
// Handle recurrence
if (params.recurrence) {
eventData.recurrence = [params.recurrence]
}
// Handle reminders
if (params.reminderMinutes !== undefined) {
eventData.reminders = {
useDefault: false,
overrides: [
{
method: 'popup',
minutes: params.reminderMinutes
}
]
}
}
if (params.visibility) eventData.visibility = params.visibility
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}/events`
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'POST', body: eventData, params })
return response
} catch (error) {
return `Error creating event: ${error}`
}
}
}
class GetEventTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'get_event',
description: 'Get a specific event from Google Calendar',
schema: GetEventSchema,
baseUrl: '',
method: 'GET',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}/events/${encodeURIComponent(params.eventId)}`
const response = await this.makeGoogleCalendarRequest({ endpoint, params })
return response
} catch (error) {
return `Error getting event: ${error}`
}
}
}
class UpdateEventTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'update_event',
description: 'Update an existing event in Google Calendar',
schema: UpdateEventSchema,
baseUrl: '',
method: 'PUT',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const updateData: any = {}
if (params.summary) updateData.summary = params.summary
if (params.description) updateData.description = params.description
if (params.location) updateData.location = params.location
// Handle date/time updates
if (params.startDate && params.endDate) {
updateData.start = { date: params.startDate }
updateData.end = { date: params.endDate }
} else if (params.startDateTime && params.endDateTime) {
updateData.start = {
dateTime: params.startDateTime,
timeZone: params.timeZone || 'UTC'
}
updateData.end = {
dateTime: params.endDateTime,
timeZone: params.timeZone || 'UTC'
}
}
if (params.attendees) {
updateData.attendees = params.attendees.split(',').map((email: string) => ({
email: email.trim()
}))
}
if (params.recurrence) {
updateData.recurrence = [params.recurrence]
}
if (params.reminderMinutes !== undefined) {
updateData.reminders = {
useDefault: false,
overrides: [
{
method: 'popup',
minutes: params.reminderMinutes
}
]
}
}
if (params.visibility) updateData.visibility = params.visibility
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}/events/${encodeURIComponent(params.eventId)}`
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'PUT', body: updateData, params })
return response
} catch (error) {
return `Error updating event: ${error}`
}
}
}
class DeleteEventTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'delete_event',
description: 'Delete an event from Google Calendar',
schema: DeleteEventSchema,
baseUrl: '',
method: 'DELETE',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}/events/${encodeURIComponent(params.eventId)}`
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'DELETE', params })
return response || 'Event deleted successfully'
} catch (error) {
return `Error deleting event: ${error}`
}
}
}
class QuickAddEventTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'quick_add_event',
description: 'Quick add event to Google Calendar using natural language',
schema: QuickAddEventSchema,
baseUrl: '',
method: 'POST',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const queryParams = new URLSearchParams()
queryParams.append('text', params.quickAddText)
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}/events/quickAdd?${queryParams.toString()}`
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'POST', params })
return response
} catch (error) {
return `Error quick adding event: ${error}`
}
}
}
// Calendar Tools
class ListCalendarsTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'list_calendars',
description: 'List calendars from Google Calendar',
schema: ListCalendarsSchema,
baseUrl: '',
method: 'GET',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
const queryParams = new URLSearchParams()
if (params.showHidden !== undefined) queryParams.append('showHidden', params.showHidden.toString())
if (params.minAccessRole) queryParams.append('minAccessRole', params.minAccessRole)
const endpoint = `users/me/calendarList?${queryParams.toString()}`
try {
const response = await this.makeGoogleCalendarRequest({ endpoint, params })
return response
} catch (error) {
return `Error listing calendars: ${error}`
}
}
}
class CreateCalendarTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'create_calendar',
description: 'Create a new calendar in Google Calendar',
schema: CreateCalendarSchema,
baseUrl: '',
method: 'POST',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const calendarData: any = {
summary: params.summary
}
if (params.description) calendarData.description = params.description
if (params.location) calendarData.location = params.location
if (params.timeZone) calendarData.timeZone = params.timeZone
const endpoint = 'calendars'
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'POST', body: calendarData, params })
return response
} catch (error) {
return `Error creating calendar: ${error}`
}
}
}
class GetCalendarTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'get_calendar',
description: 'Get a specific calendar from Google Calendar',
schema: GetCalendarSchema,
baseUrl: '',
method: 'GET',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}`
const response = await this.makeGoogleCalendarRequest({ endpoint, params })
return response
} catch (error) {
return `Error getting calendar: ${error}`
}
}
}
class UpdateCalendarTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'update_calendar',
description: 'Update an existing calendar in Google Calendar',
schema: UpdateCalendarSchema,
baseUrl: '',
method: 'PUT',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const updateData: any = {}
if (params.summary) updateData.summary = params.summary
if (params.description) updateData.description = params.description
if (params.location) updateData.location = params.location
if (params.timeZone) updateData.timeZone = params.timeZone
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}`
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'PUT', body: updateData, params })
return response
} catch (error) {
return `Error updating calendar: ${error}`
}
}
}
class DeleteCalendarTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'delete_calendar',
description: 'Delete a calendar from Google Calendar',
schema: DeleteCalendarSchema,
baseUrl: '',
method: 'DELETE',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}`
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'DELETE', params })
return response || 'Calendar deleted successfully'
} catch (error) {
return `Error deleting calendar: ${error}`
}
}
}
class ClearCalendarTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'clear_calendar',
description: 'Clear all events from a Google Calendar',
schema: ClearCalendarSchema,
baseUrl: '',
method: 'POST',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const endpoint = `calendars/${encodeURIComponent(params.calendarId)}/clear`
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'POST', params })
return response || 'Calendar cleared successfully'
} catch (error) {
return `Error clearing calendar: ${error}`
}
}
}
// Freebusy Tools
class QueryFreebusyTool extends BaseGoogleCalendarTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'query_freebusy',
description: 'Query free/busy information for a set of calendars',
schema: QueryFreebusySchema,
baseUrl: '',
method: 'POST',
headers: {}
}
super({
...toolInput,
accessToken: args.accessToken
})
this.defaultParams = args.defaultParams || {}
}
async _call(arg: any): Promise<string> {
const params = { ...arg, ...this.defaultParams }
try {
const freebusyData: any = {
timeMin: params.timeMin,
timeMax: params.timeMax,
items: params.calendarIds.split(',').map((id: string) => ({
id: id.trim()
}))
}
if (params.groupExpansionMax !== undefined) {
freebusyData.groupExpansionMax = params.groupExpansionMax
}
if (params.calendarExpansionMax !== undefined) {
freebusyData.calendarExpansionMax = params.calendarExpansionMax
}
const endpoint = 'freeBusy'
const response = await this.makeGoogleCalendarRequest({ endpoint, method: 'POST', body: freebusyData, params })
return response
} catch (error) {
return `Error querying freebusy: ${error}`
}
}
}
export const createGoogleCalendarTools = (args?: RequestParameters): DynamicStructuredTool[] => {
const tools: DynamicStructuredTool[] = []
const actions = args?.actions || []
const accessToken = args?.accessToken || ''
const defaultParams = args?.defaultParams || {}
// Event tools
if (actions.includes('listEvents')) {
tools.push(
new ListEventsTool({
accessToken,
defaultParams: defaultParams.listEvents
})
)
}
if (actions.includes('createEvent')) {
tools.push(
new CreateEventTool({
accessToken,
defaultParams: defaultParams.createEvent
})
)
}
if (actions.includes('getEvent')) {
tools.push(
new GetEventTool({
accessToken,
defaultParams: defaultParams.getEvent
})
)
}
if (actions.includes('updateEvent')) {
tools.push(
new UpdateEventTool({
accessToken,
defaultParams: defaultParams.updateEvent
})
)
}
if (actions.includes('deleteEvent')) {
tools.push(
new DeleteEventTool({
accessToken,
defaultParams: defaultParams.deleteEvent
})
)
}
if (actions.includes('quickAddEvent')) {
tools.push(
new QuickAddEventTool({
accessToken,
defaultParams: defaultParams.quickAddEvent
})
)
}
// Calendar tools
if (actions.includes('listCalendars')) {
tools.push(
new ListCalendarsTool({
accessToken,
defaultParams: defaultParams.listCalendars
})
)
}
if (actions.includes('createCalendar')) {
tools.push(
new CreateCalendarTool({
accessToken,
defaultParams: defaultParams.createCalendar
})
)
}
if (actions.includes('getCalendar')) {
tools.push(
new GetCalendarTool({
accessToken,
defaultParams: defaultParams.getCalendar
})
)
}
if (actions.includes('updateCalendar')) {
tools.push(
new UpdateCalendarTool({
accessToken,
defaultParams: defaultParams.updateCalendar
})
)
}
if (actions.includes('deleteCalendar')) {
tools.push(
new DeleteCalendarTool({
accessToken,
defaultParams: defaultParams.deleteCalendar
})
)
}
if (actions.includes('clearCalendar')) {
tools.push(
new ClearCalendarTool({
accessToken,
defaultParams: defaultParams.clearCalendar
})
)
}
// Freebusy tools
if (actions.includes('queryFreebusy')) {
tools.push(
new QueryFreebusyTool({
accessToken,
defaultParams: defaultParams.queryFreebusy
})
)
}
return tools
}