Pular para conteúdo

GOOGLE_SLIDES - Integração com Google Slides

O que é este Node?

O GOOGLE_SLIDES é o node responsável por criar e manipular apresentações no Google Slides, permitindo criar, ler, adicionar slides e fazer atualizações em lote automaticamente.

Por que este Node existe?

Apresentações precisam ser geradas dinamicamente. O GOOGLE_SLIDES existe para:

  1. Gerar apresentações automáticas: Criar slides de relatórios, propostas
  2. Adicionar slides dinamicamente: Inserir novos slides com conteúdo
  3. Automatizar criação: Eliminar trabalho manual repetitivo
  4. Personalizar em escala: Criar múltiplas apresentações customizadas
  5. Integrar com dados: Conectar apresentações com bases de dados

Como funciona internamente?

Quando o GOOGLE_SLIDES é executado, o sistema:

  1. Autentica via OAuth2 com Google (access_token e refresh_token)
  2. Conecta à API do Google Slides v1
  3. Executa operação solicitada (create, get, batchUpdate, addSlide)
  4. Processa estrutura da apresentação (slides, elementos, layouts)
  5. Retorna presentationId, slides ou confirmação
  6. Se erro: Retorna mensagem de erro detalhada

Código interno (google-executors.service.ts:823-904):

async executeGoogleSlides(data: any, variables: Record<string, any>): Promise<any> {
  try {
    this.logger.log('🎞️ [GOOGLE SLIDES] 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 slides = google.slides({ version: 'v1', auth: oauth2Client });

    switch (data.operation) {
      case 'create':
        const createResult = await slides.presentations.create({
          requestBody: {
            title: data.title,
          },
        });

        return {
          success: true,
          presentationId: createResult.data.presentationId,
          title: createResult.data.title,
        };

      case 'get':
        const getResult = await slides.presentations.get({
          presentationId: data.presentationId,
        });

        return {
          success: true,
          presentation: getResult.data,
          slides: getResult.data.slides,
        };

      case 'batchUpdate':
        const updateResult = await slides.presentations.batchUpdate({
          presentationId: data.presentationId,
          requestBody: {
            requests: data.requests,
          },
        });

        return {
          success: true,
          replies: updateResult.data.replies,
        };

      case 'addSlide':
        const addSlideResult = await slides.presentations.batchUpdate({
          presentationId: data.presentationId,
          requestBody: {
            requests: [{
              createSlide: {
                slideLayoutReference: {
                  predefinedLayout: data.layout || 'BLANK',
                },
              },
            }],
          },
        });

        return {
          success: true,
          slideId: addSlideResult.data.replies?.[0]?.createSlide?.objectId,
        };

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

Quando você DEVE usar este Node?

Use GOOGLE_SLIDES sempre que precisar criar apresentações automaticamente:

Casos de uso

  1. Relatórios visuais: "Gerar apresentação de resultados mensais"
  2. Propostas comerciais: "Criar deck de vendas personalizado"
  3. Apresentações de dados: "Transformar dados em slides"
  4. Templates dinâmicos: "Personalizar apresentação base"
  5. Dashboards visuais: "Criar slides com gráficos automáticos"
  6. Documentação visual: "Gerar apresentação de projeto"

Quando NÃO usar GOOGLE_SLIDES

  • Design muito complexo: Templates manuais são melhores
  • Imagens pesadas: Considere performance da API
  • Edição manual posterior: Verifique se automação vale a pena

Parâmetros

Campo Tipo Obrigatório Descrição
operation string Sim create, get, batchUpdate, addSlide
title string Condicional Título da apresentação (para create)
presentationId string Condicional ID da apresentação (para get/update)
layout string Não Layout do slide (BLANK, TITLE, etc)
requests array Condicional Array de requests (para batchUpdate)
accessToken string Sim Token OAuth2 do Google
refreshToken string Sim Refresh token OAuth2 do Google

Operações Disponíveis

1. create - Criar apresentação

O que faz: Cria nova apresentação vazia no Google Slides.

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

2. get - Ler apresentação

O que faz: Obtém estrutura completa da apresentação.

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

3. addSlide - Adicionar slide

O que faz: Adiciona novo slide à apresentação.

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

Parâmetros opcionais: - layout (BLANK, TITLE, TITLE_AND_BODY, etc)

4. batchUpdate - Atualizações em lote

O que faz: Executa múltiplas operações de edição em uma chamada.

Parâmetros obrigatórios: - presentationId - requests (array de operações) - accessToken - refreshToken

Exemplo 1: Criar Apresentação de Vendas

Objetivo: Criar apresentação com dados de vendas do mês.

JSON para Importar

{
  "name": "Gerar Apresentação de Vendas",
  "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": "Mês",
        "parameters": {
          "message": "Mês do relatório:",
          "variable": "mes"
        }
      }
    },
    {
      "id": "number_1",
      "type": "number",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Total Vendas",
        "parameters": {
          "message": "Total de vendas (R$):",
          "variable": "total_vendas",
          "decimals": 2
        }
      }
    },
    {
      "id": "google_slides_1",
      "type": "google_slides",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Criar Apresentação",
        "parameters": {
          "operation": "create",
          "title": "Relatório de Vendas - {{mes}}",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "google_slides_2",
      "type": "google_slides",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Adicionar Slide Título",
        "parameters": {
          "operation": "addSlide",
          "presentationId": "{{presentationId}}",
          "layout": "TITLE",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "google_slides_3",
      "type": "google_slides",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Adicionar Slide Dados",
        "parameters": {
          "operation": "addSlide",
          "presentationId": "{{presentationId}}",
          "layout": "TITLE_AND_BODY",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "✅ Apresentação criada!\n\n📊 {{title}}\n💰 Total: R$ {{total_vendas}}\n\n🔗 https://docs.google.com/presentation/d/{{presentationId}}/edit"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1500, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "number_1" },
    { "source": "number_1", "target": "google_slides_1" },
    { "source": "google_slides_1", "target": "google_slides_2" },
    { "source": "google_slides_2", "target": "google_slides_3" },
    { "source": "google_slides_3", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Mês do relatório:
Usuário: Janeiro 2025
Sistema: Total de vendas (R$):
Usuário: 150000.00
Sistema: ✅ Apresentação criada!

📊 Relatório de Vendas - Janeiro 2025
💰 Total: R$ 150000.00

🔗 https://docs.google.com/presentation/d/abc123.../edit

Exemplo 2: Ler Apresentação Existente

Objetivo: Obter informações de apresentação existente.

JSON para Importar

{
  "name": "Ler Apresentação do Google Slides",
  "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": "ID Apresentação",
        "parameters": {
          "message": "Cole o ID da apresentação:",
          "variable": "presentation_id"
        }
      }
    },
    {
      "id": "google_slides_1",
      "type": "google_slides",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Obter Info",
        "parameters": {
          "operation": "get",
          "presentationId": "{{presentation_id}}",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Mostrar Dados",
        "parameters": {
          "message": "📊 Apresentação carregada!\n\nTítulo: {{presentation.title}}\nTotal de slides: {{slides.length}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "google_slides_1" },
    { "source": "google_slides_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Exemplo 3: Criar Proposta com Múltiplos Slides

Objetivo: Gerar deck completo de proposta comercial.

JSON para Importar

{
  "name": "Deck de Proposta Comercial",
  "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": "Cliente",
        "parameters": {
          "message": "Nome do cliente:",
          "variable": "cliente"
        }
      }
    },
    {
      "id": "google_slides_1",
      "type": "google_slides",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Criar Deck",
        "parameters": {
          "operation": "create",
          "title": "Proposta - {{cliente}}",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "google_slides_2",
      "type": "google_slides",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Slide 1: Capa",
        "parameters": {
          "operation": "addSlide",
          "presentationId": "{{presentationId}}",
          "layout": "TITLE",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "google_slides_3",
      "type": "google_slides",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Slide 2: Sobre Nós",
        "parameters": {
          "operation": "addSlide",
          "presentationId": "{{presentationId}}",
          "layout": "TITLE_AND_BODY",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "google_slides_4",
      "type": "google_slides",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Slide 3: Solução",
        "parameters": {
          "operation": "addSlide",
          "presentationId": "{{presentationId}}",
          "layout": "TITLE_AND_BODY",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "google_slides_5",
      "type": "google_slides",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Slide 4: Investimento",
        "parameters": {
          "operation": "addSlide",
          "presentationId": "{{presentationId}}",
          "layout": "TITLE_AND_BODY",
          "accessToken": "{{google_access_token}}",
          "refreshToken": "{{google_refresh_token}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1500, "y": 100 },
      "data": {
        "label": "Deck Pronto",
        "parameters": {
          "message": "✅ Deck de proposta criado!\n\n🏢 Cliente: {{cliente}}\n📊 4 slides criados\n\n🔗 https://docs.google.com/presentation/d/{{presentationId}}/edit"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "google_slides_1" },
    { "source": "google_slides_1", "target": "google_slides_2" },
    { "source": "google_slides_2", "target": "google_slides_3" },
    { "source": "google_slides_3", "target": "google_slides_4" },
    { "source": "google_slides_4", "target": "google_slides_5" },
    { "source": "google_slides_5", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Resposta do Node

Operação create

{
  "success": true,
  "presentationId": "1abc123XYZ...",
  "title": "Relatório de Vendas - Janeiro"
}

Operação get

{
  "success": true,
  "presentation": {
    "presentationId": "1abc123...",
    "title": "Minha Apresentação",
    "slides": [
      {
        "objectId": "slide1",
        "slideProperties": {}
      }
    ]
  },
  "slides": [/* array de slides */]
}

Operação addSlide

{
  "success": true,
  "slideId": "slide_abc123"
}

Operação batchUpdate

{
  "success": true,
  "replies": [
    /* array de respostas */
  ]
}

Como obter presentationId

O presentationId está na URL do Google Slides:

https://docs.google.com/presentation/d/1abc123XYZ.../edit
                                        ^^^^^^^^^^^
                                        presentationId

Layouts Predefinidos

Layout Descrição
BLANK Slide em branco
TITLE Slide de título (capa)
TITLE_AND_BODY Título e corpo de texto
TITLE_AND_TWO_COLUMNS Título com 2 colunas
TITLE_ONLY Apenas título
SECTION_HEADER Cabeçalho de seção
SECTION_TITLE_AND_DESCRIPTION Seção com descrição
ONE_COLUMN_TEXT Uma coluna de texto
MAIN_POINT Ponto principal
BIG_NUMBER Número grande

Requests do batchUpdate

Operações disponíveis via batchUpdate:

{
  "requests": [
    {
      "createSlide": {
        "slideLayoutReference": {
          "predefinedLayout": "TITLE_AND_BODY"
        }
      }
    },
    {
      "insertText": {
        "objectId": "slide_id",
        "text": "Texto do slide"
      }
    },
    {
      "createShape": {
        "objectId": "shape_id",
        "shapeType": "TEXT_BOX",
        "elementProperties": {
          "pageObjectId": "slide_id",
          "size": {
            "width": { "magnitude": 300, "unit": "PT" },
            "height": { "magnitude": 100, "unit": "PT" }
          }
        }
      }
    }
  ]
}

Principais operações: - createSlide - deleteObject - insertText - createShape - createImage - updateTextStyle - updateShapeProperties

Estrutura da Apresentação

Presentation
├── presentationId
├── title
├── slides []
│   ├── objectId
│   ├── slideProperties
│   └── pageElements []
│       ├── shape
│       ├── image
│       ├── video
│       └── table
└── layouts []

Boas Práticas

SIM:

  • Use layouts predefinidos para consistência
  • Salve presentationId para operações futuras
  • Agrupe operações em batchUpdate
  • Nomeie apresentações de forma descritiva
  • Organize por pastas no Drive

NÃO:

  • Não crie slides duplicados desnecessariamente
  • Não faça muitas operações individuais (use batch)
  • Não ignore limites de tamanho de imagens
  • Não delete slides sem backup
  • Não exponha tokens em logs

Dicas

💡 Templates: Crie apresentação base e clone para personalizar

💡 Imagens: Use Drive API para fazer upload antes de inserir

💡 Performance: batchUpdate é mais eficiente que chamadas múltiplas

💡 Design: Mantenha layouts consistentes entre slides

💡 Colaboração: Configure permissões via Drive API após criar

Troubleshooting

Erro: "Presentation not found"

Causa: presentationId inválido ou sem permissão

Solução: Verifique ID na URL e permissões de acesso

Erro: "Invalid layout"

Causa: Nome de layout incorreto

Solução: Use layouts da tabela acima (BLANK, TITLE, etc)

Erro: "Invalid requests"

Causa: Formato incorreto em batchUpdate

Solução: Siga estrutura da documentação oficial

Erro: "Object not found"

Causa: slideId ou objectId inválido

Solução: Use get para obter IDs válidos

Exportar para PDF

Para converter apresentação em PDF, use Drive API:

GET https://www.googleapis.com/drive/v3/files/{presentationId}/export?mimeType=application/pdf

Próximo Node

GOOGLE_DOCS - Criar documentos → GOOGLE_SHEETS - Trabalhar com planilhas → GOOGLE_DRIVE - Gerenciar arquivos