/

Google Slides Creator - Automated Presentation Builder

Copy script

Copied

Copy script

Copied

Google Slides Creator - Automated Presentation Builder

Created by

andrew van beek - Keyboard Team

Creates professional Google Slides presentations programmatically with customizable themes, multiple slides, and automatic sharing options. Perfect for automating presentation creation, generating reports, or building slide decks from templates. Supports various themes, bullet points, custom content, and can make presentations publicly accessible.

Requirements

google.com logo

Script

Copy script

Copied

Copy script

Copied

const { google } = require('googleapis');

// Initialize Google APIs with user token
const oauth2Client = new google.auth.OAuth2();
oauth2Client.setCredentials({
  access_token: process.env.KEYBOARD_PROVIDER_USER_TOKEN_FOR_GOOGLE
});

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

async function createGoogleSlidesPresentation() {
  try {
    console.log('šŸŽØ Creating Google Slides presentation...');
    console.log(`šŸ“ Title: {{presentationTitle}}`);
    console.log(`šŸŽ­ Theme: {{theme}}`);
    
    // Parse slides data
    const slidesData = {{slides}};
    console.log(`šŸ“Š Number of slides: ${slidesData.length}`);
    
    // Create the presentation
    const presentation = await slides.presentations.create({
      resource: {
        title: '{{presentationTitle}}'
      }
    });
    
    const presentationId = presentation.data.presentationId;
    console.log(`āœ… Presentation created with ID: ${presentationId}`);
    console.log(`šŸ”— URL: https://docs.google.com/presentation/d/${presentationId}/edit`);
    
    // Get the presentation to work with existing slides
    const currentPresentation = await slides.presentations.get({
      presentationId: presentationId
    });
    
    const existingSlides = currentPresentation.data.slides;
    const requests = [];
    
    // Delete the default slide if we have custom slides
    if (slidesData.length > 0 && existingSlides.length > 0) {
      requests.push({
        deleteObject: {
          objectId: existingSlides[0].objectId
        }
      });
    }
    
    // Create slides first
    slidesData.forEach((slideData, index) => {
      const slideId = `slide_${index}`;
      
      // Create slide
      requests.push({
        createSlide: {
          objectId: slideId,
          slideLayoutReference: {
            predefinedLayout: 'TITLE_AND_BODY'
          }
        }
      });
    });
    
    // Execute slide creation first
    if (requests.length > 0) {
      console.log('šŸ”§ Creating slides...');
      await slides.presentations.batchUpdate({
        presentationId: presentationId,
        resource: {
          requests: requests
        }
      });
    }
    
    // Now get the updated presentation to find text boxes
    const updatedPresentation = await slides.presentations.get({
      presentationId: presentationId
    });
    
    // Add content to each slide
    for (let index = 0; index < slidesData.length; index++) {
      const slideData = slidesData[index];
      const slide = updatedPresentation.data.slides[index];
      const textRequests = [];
      
      // Find title and body text boxes
      let titleElementId = null;
      let bodyElementId = null;
      
      if (slide.pageElements) {
        slide.pageElements.forEach(element => {
          if (element.shape && element.shape.placeholder) {
            if (element.shape.placeholder.type === 'TITLE') {
              titleElementId = element.objectId;
            } else if (element.shape.placeholder.type === 'BODY') {
              bodyElementId = element.objectId;
            }
          }
        });
      }
      
      // Add title text
      if (slideData.title && titleElementId) {
        textRequests.push({
          insertText: {
            objectId: titleElementId,
            text: slideData.title,
            insertionIndex: 0
          }
        });
      }
      
      // Add body text
      if (slideData.content && bodyElementId) {
        let contentText = '';
        if (Array.isArray(slideData.content)) {
          // Handle bullet points
          contentText = slideData.content.map(item => `• ${item}`).join('\n');
        } else {
          contentText = slideData.content;
        }
        
        textRequests.push({
          insertText: {
            objectId: bodyElementId,
            text: contentText,
            insertionIndex: 0
          }
        });
      }
      
      // Execute text updates for this slide
      if (textRequests.length > 0) {
        console.log(`šŸ“ Adding content to slide ${index + 1}...`);
        await slides.presentations.batchUpdate({
          presentationId: presentationId,
          resource: {
            requests: textRequests
          }
        });
      }
    }
    
    // Share the presentation if requested
    if ({{shareWithAnyone}}) {
      console.log('🌐 Making presentation publicly viewable...');
      await drive.permissions.create({
        fileId: presentationId,
        resource: {
          role: 'reader',
          type: 'anyone'
        }
      });
      console.log('āœ… Presentation is now viewable by anyone with the link');
    }
    
    // Get final presentation details
    const finalPresentation = await slides.presentations.get({
      presentationId: presentationId
    });
    
    console.log('\nšŸ“‹ Presentation Summary:');
    console.log(`- Title: ${finalPresentation.data.title}`);
    console.log(`- Slides: ${finalPresentation.data.slides.length}`);
    console.log(`- ID: ${presentationId}`);
    console.log(`- Edit URL: https://docs.google.com/presentation/d/${presentationId}/edit`);
    console.log(`- View URL: https://docs.google.com/presentation/d/${presentationId}/preview`);
    
    if ({{shareWithAnyone}}) {
      console.log(`- Public URL: https://docs.google.com/presentation/d/${presentationId}/edit?usp=sharing`);
    }
    
    return {
      success: true,
      presentationId: presentationId,
      title: finalPresentation.data.title,
      slideCount: finalPresentation.data.slides.length,
      editUrl: `https://docs.google.com/presentation/d/${presentationId}/edit`,
      viewUrl: `https://docs.google.com/presentation/d/${presentationId}/preview`,
      publicUrl: {{shareWithAnyone}} ? `https://docs.google.com/presentation/d/${presentationId}/edit?usp=sharing` : null
    };
    
  } catch (error) {
    console.error('āŒ Error creating presentation:', error.message);
    
    if (error.message.includes('insufficient authentication')) {
      console.error('šŸ”‘ Authentication issue - please check your Google token permissions');
    } else if (error.message.includes('quota')) {
      console.error('šŸ“Š API quota exceeded - please try again later');
    }
    
    throw error;
  }
}

// Execute the function
console.log('šŸš€ Starting Google Slides creation process...\n');
createGoogleSlidesPresentation()
  .then(result => {
    console.log('\nšŸŽ‰ Google Slides presentation created successfully!');
    return result;
  })
  .catch(error => {
    console.error('\nšŸ’„ Failed to create presentation');
    throw error;
  });

Create Notion Blog Post with Native Cover Images

Automatically creates professional blog posts in your Notion database with native cover images, SEO optimization, and rich content blocks.

**Required Database Schema:**

Your Notion database should have these properties (with exact names):

- **Title** (Title property) - Main blog post title

- **Status** (Status property) - Publication status (Draft, In Review, Published)

- **Category** (Select property) - Blog categories (Technology, Health & Wellness, Business, Lifestyle, Travel, Personal Development, Food & Recipes, Education)

- **Tags** (Multi-select property) - Blog tags (Technology, Personal, Tutorial, News, Opinion, Review, Travel, Health)

- **Publish Date** (Date property) - Publication date

- **Author** (People property) - Blog post author

- **Framer Slug** (Rich text property) - SEO-friendly URL slug

- **SEO Description** (Rich text property) - Meta description for search engines

- **Content** (Rich text property) - Brief content description

- **Featured Image** (URL property) - Optional, but script uses native cover images instead

**Features:**

- Auto-finds any database with "Blog" in the title

- Uses Notion's native cover image API (displays properly in gallery view)

- 15 curated high-quality cover images (1500px optimized)

- Generates SEO-friendly slugs automatically

- Creates rich content blocks with proper formatting

- Full verification of created blog posts

- Works with any blog database schema matching the above structure

Next