Pular para conteúdo

GOOGLE_CALENDAR - Integração com Google Calendar

O que é este Node?

O GOOGLE_CALENDAR é o node responsável por gerenciar eventos no Google Calendar, permitindo criar, listar e deletar compromissos automaticamente via flow.

Por que este Node existe?

Agendamentos precisam ser gerenciados automaticamente. O GOOGLE_CALENDAR existe para:

  1. Agendar automaticamente: Criar eventos sem intervenção manual
  2. Enviar convites: Adicionar participantes e enviar notificações
  3. Consultar agenda: Verificar disponibilidade e eventos futuros
  4. Integrar sistemas: Sincronizar agendamentos entre plataformas
  5. Lembrar compromissos: Garantir que eventos sejam registrados

Como funciona internamente?

Quando o GOOGLE_CALENDAR é executado, o sistema:

  1. Autentica via OAuth2 com Google (access_token e refresh_token)
  2. Conecta à API do Google Calendar v3
  3. Executa operação solicitada (createEvent, listEvents, deleteEvent)
  4. Processa datas em formato ISO 8601 (2025-01-15T10:30:00)
  5. Gerencia participantes automaticamente com envio de emails
  6. Retorna evento criado com ID e link de visualização
  7. Se erro: Retorna mensagem de erro detalhada

Código interno (google-executors.service.ts:353-421):

async executeGoogleCalendar(data: any, variables: Record<string, any>): Promise<any> {
  try {
    this.logger.log('📅 [GOOGLE CALENDAR] Executing operation');

    const oauth2Client = new OAuth2Client(
      process.env.GOOGLE_CLIENT_ID,
      process.env.GOOGLE_CLIENT_SECRET,
      process.env.GOOGLE_REDIRECT_URI
    );

    oauth2Client.setCredentials({
      access_token: data.accessToken,
      refresh_token: data.refreshToken,
    });

    const calendar = google.calendar({ version: 'v3', auth: oauth2Client });

    switch (data.operation) {
      case 'createEvent':
        const createResult = await calendar.events.insert({
          calendarId: data.calendarId || 'primary',
          requestBody: {
            summary: data.summary,
            description: data.description,
            start: { dateTime: data.startDateTime, timeZone: data.timeZone || 'America/Sao_Paulo' },
            end: { dateTime: data.endDateTime, timeZone: data.timeZone || 'America/Sao_Paulo' },
            attendees: data.attendees?.map((email: string) => ({ email })) || [],
          },
        });

        return {
          success: true,
          eventId: createResult.data.id,
          htmlLink: createResult.data.htmlLink,
        };

      case 'listEvents':
        const listResult = await calendar.events.list({
          calendarId: data.calendarId || 'primary',
          timeMin: data.timeMin || new Date().toISOString(),
          maxResults: data.maxResults || 10,
          singleEvents: true,
          orderBy: 'startTime',
        });

        return {
          success: true,
          events: listResult.data.items || [],
        };

      case 'deleteEvent':
        await calendar.events.delete({
          calendarId: data.calendarId || 'primary',
          eventId: data.eventId,
        });

        return {
          success: true,
          message: 'Event deleted successfully',
        };

      default:
        throw new Error(`Unknown Google Calendar operation: ${data.operation}`);
    }
  } catch (error) {
    this.logger.error('Google Calendar execution error:', error);
    throw error;
  }
}

Quando você DEVE usar este Node?

Use GOOGLE_CALENDAR sempre que precisar gerenciar eventos automaticamente:

Casos de uso

  1. Agendar consultas: "Criar evento para consulta médica às 14h"
  2. Reuniões automáticas: "Agendar reunião com time de vendas"
  3. Lembretes: "Criar lembrete de follow-up com cliente"
  4. Integração CRM: "Sincronizar agendamentos do sistema"
  5. Consultar disponibilidade: "Verificar próximos compromissos"
  6. Cancelar eventos: "Deletar reunião cancelada"

Quando NÃO usar GOOGLE_CALENDAR

  • Agendamentos internos simples: Use sistema próprio se não precisa do Google
  • Eventos sem data/hora: Calendar requer datetime específico
  • Alta frequência: Considere rate limits da API

Parâmetros

Campo Tipo Obrigatório Descrição
operation string Sim createEvent, listEvents, deleteEvent
summary string Condicional Título do evento (para createEvent)
description string Não Descrição detalhada do evento
startDateTime string Condicional Data/hora início (ISO 8601)
endDateTime string Condicional Data/hora fim (ISO 8601)
timeZone string Não Timezone (padrão: America/Sao_Paulo)
attendees array Não Lista de emails dos participantes
calendarId string Não ID do calendar (padrão: primary)
eventId string Condicional ID do evento (para deleteEvent)
timeMin string Não Data mínima para listEvents
maxResults number Não Máximo de eventos (padrão: 10)
accessToken string Sim Token OAuth2 do Google
refreshToken string Sim Refresh token OAuth2 do Google

Operações Disponíveis

1. createEvent - Criar evento

O que faz: Cria novo evento no Google Calendar.

Parâmetros obrigatórios: - summary - startDateTime - endDateTime - accessToken - refreshToken

Parâmetros opcionais: - description - attendees - timeZone - calendarId

2. listEvents - Listar eventos

O que faz: Lista próximos eventos do calendar.

Parâmetros obrigatórios: - accessToken - refreshToken

Parâmetros opcionais: - calendarId - timeMin (data inicial) - maxResults

3. deleteEvent - Deletar evento

O que faz: Remove evento do calendar.

Parâmetros obrigatórios: - eventId - accessToken - refreshToken

Parâmetros opcionais: - calendarId

Exemplo 1: Agendar Consulta Médica

Objetivo: Criar evento de consulta com horário específico.

JSON para Importar

{
  "name": "Agendar Consulta no Google Calendar",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Nome Paciente",
        "parameters": {
          "message": "Nome do paciente:",
          "variable": "paciente_nome"
        }
      }
    },
    {
      "id": "email_1",
      "type": "email",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Email Paciente",
        "parameters": {
          "message": "Email do paciente:",
          "variable": "paciente_email"
        }
      }
    },
    {
      "id": "input_2",
      "type": "input",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Data e Hora",
        "parameters": {
          "message": "Data/hora da consulta (Ex: 2025-01-20 14:00):",
          "variable": "data_hora"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Calcular Fim",
        "parameters": {
          "operation": "set",
          "name": "data_hora_fim",
          "value": "{{data_hora}} + 1 hour"
        }
      }
    },
    {
      "id": "google_calendar_1",
      "type": "google_calendar",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Criar Evento",
        "parameters": {
          "operation": "createEvent",
          "summary": "Consulta - {{paciente_nome}}",
          "description": "Consulta médica agendada",
          "startDateTime": "{{data_hora}}",
          "endDateTime": "{{data_hora_fim}}",
          "timeZone": "America/Sao_Paulo",
          "attendees": ["{{paciente_email}}"],
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "✅ Consulta agendada!\n\n👤 Paciente: {{paciente_nome}}\n📅 Data/Hora: {{data_hora}}\n📧 Convite enviado para: {{paciente_email}}\n\n🔗 Ver no Calendar: {{htmlLink}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1500, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "email_1" },
    { "source": "email_1", "target": "input_2" },
    { "source": "input_2", "target": "variable_1" },
    { "source": "variable_1", "target": "google_calendar_1" },
    { "source": "google_calendar_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Nome do paciente:
Usuário: João Silva
Sistema: Email do paciente:
Usuário: joao@example.com
Sistema: Data/hora da consulta (Ex: 2025-01-20 14:00):
Usuário: 2025-01-20T14:00:00
Sistema: ✅ Consulta agendada!

👤 Paciente: João Silva
📅 Data/Hora: 2025-01-20T14:00:00
📧 Convite enviado para: joao@example.com

🔗 Ver no Calendar: https://calendar.google.com/event?eid=...

Exemplo 2: Listar Próximos Compromissos

Objetivo: Mostrar próximos 5 eventos do calendar.

JSON para Importar

{
  "name": "Listar Compromissos do Google Calendar",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "google_calendar_1",
      "type": "google_calendar",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Buscar Eventos",
        "parameters": {
          "operation": "listEvents",
          "maxResults": 5,
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Mostrar Lista",
        "parameters": {
          "message": "📅 Seus próximos compromissos:\n\n(Lista processada dos eventos)"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "google_calendar_1" },
    { "source": "google_calendar_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Exemplo 3: Criar Reunião com Equipe

Objetivo: Agendar reunião e enviar convites para múltiplos participantes.

JSON para Importar

{
  "name": "Agendar Reunião de Equipe",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Título",
        "parameters": {
          "message": "Título da reunião:",
          "variable": "titulo"
        }
      }
    },
    {
      "id": "input_2",
      "type": "input",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Descrição",
        "parameters": {
          "message": "Pauta da reunião:",
          "variable": "pauta"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Definir Participantes",
        "parameters": {
          "operation": "set",
          "name": "participantes",
          "value": ["maria@empresa.com", "joao@empresa.com", "ana@empresa.com"]
        }
      }
    },
    {
      "id": "google_calendar_1",
      "type": "google_calendar",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Criar Reunião",
        "parameters": {
          "operation": "createEvent",
          "summary": "{{titulo}}",
          "description": "{{pauta}}",
          "startDateTime": "2025-01-25T10:00:00",
          "endDateTime": "2025-01-25T11:00:00",
          "timeZone": "America/Sao_Paulo",
          "attendees": "{{participantes}}",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "✅ Reunião criada!\n\n📋 {{titulo}}\n👥 3 participantes convidados\n📅 25/01/2025 às 10:00\n\n🔗 {{htmlLink}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1300, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "input_2" },
    { "source": "input_2", "target": "variable_1" },
    { "source": "variable_1", "target": "google_calendar_1" },
    { "source": "google_calendar_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Resposta do Node

Operação createEvent

{
  "success": true,
  "eventId": "abc123xyz...",
  "htmlLink": "https://www.google.com/calendar/event?eid=..."
}

Operação listEvents

{
  "success": true,
  "events": [
    {
      "id": "event123",
      "summary": "Reunião com Cliente",
      "start": {
        "dateTime": "2025-01-20T14:00:00-03:00"
      },
      "end": {
        "dateTime": "2025-01-20T15:00:00-03:00"
      },
      "htmlLink": "https://www.google.com/calendar/event?eid=..."
    }
  ]
}

Operação deleteEvent

{
  "success": true,
  "message": "Event deleted successfully"
}

Formato de Data/Hora (ISO 8601)

O Google Calendar requer datas no formato ISO 8601:

2025-01-20T14:00:00     (14h do dia 20/01/2025)
2025-01-20T14:00:00-03:00   (com timezone explícito)

Exemplos práticos:

Descrição Formato
Hoje 14h 2025-01-15T14:00:00
Amanhã 9h 2025-01-16T09:00:00
Próxima semana 10h30 2025-01-22T10:30:00

Timezones Comuns

Timezone Descrição
America/Sao_Paulo Brasília (UTC-3)
America/New_York Nova York (UTC-5/4)
Europe/London Londres (UTC+0/1)
Asia/Tokyo Tóquio (UTC+9)

Como obter eventId

O eventId é retornado ao criar o evento e também está na URL:

https://calendar.google.com/event?eid=abc123xyz...
                                      ^^^^^^^^^^^
                                      eventId (codificado)

Use a API para obter o ID real ou salve-o ao criar o evento.

Participantes (Attendees)

O parâmetro attendees aceita array de emails:

["maria@example.com", "joao@example.com"]

Cada participante receberá: - Email de convite automático - Opções de Aceitar/Recusar - Notificações de alterações

Boas Práticas

SIM:

  • Sempre especifique timezone para evitar confusão
  • Use descrição clara do evento
  • Adicione emails válidos em attendees
  • Salve eventId para operações futuras
  • Configure lembretes via API se necessário

NÃO:

  • Não crie eventos sem validar datas
  • Não envie convites para emails inválidos
  • Não delete eventos sem confirmação
  • Não ignore timezone (pode causar horário errado)
  • Não crie eventos duplicados

Dicas

💡 Convites automáticos: attendees recebem email automaticamente

💡 Duração padrão: Use 1 hora de duração se não especificado

💡 Eventos recorrentes: Use regras RRULE para eventos que se repetem

💡 Cores: Configure cores diferentes para categorias de eventos

💡 Links úteis: htmlLink permite abrir diretamente no Google Calendar

Troubleshooting

Erro: "Invalid dateTime value"

Causa: Formato de data incorreto

Solução: Use formato ISO 8601: 2025-01-20T14:00:00

Erro: "Event not found"

Causa: eventId inválido ou evento já deletado

Solução: Liste eventos para obter IDs válidos

Erro: "Insufficient permissions"

Causa: Token sem scope de calendar

Solução: Adicione scope https://www.googleapis.com/auth/calendar

Erro: "Invalid attendee email"

Causa: Email inválido em attendees

Solução: Valide emails antes de adicionar como participantes

Próximo Node

GOOGLE_SHEETS - Integrar com Google Sheets → GOOGLE_DRIVE - Gerenciar arquivos no Drive → GOOGLE_DOCS - Criar e editar documentos