Guia de início rápido de videoconferências do Agenda

Importante: este guia de início rápido é apenas para provedores de videoconferência na Web.

Este guia de início rápido do complemento do Google Workspace estende o Google Agenda para sincronização com um serviço fictício de videoconferência na Web chamado My Web Conferencing. Ao editar eventos do Agenda, o complemento permite que os usuários vejam Minha videoconferência como uma opção de videoconferência.

O guia de início rápido mostra a criação de conferências e a sincronização de eventos, mas não estará operacional até que você o conecte à API da solução de videoconferência.

Objetivos

  • Configurar o ambiente.
  • Configure o script.
  • Execute o script.

Pré-requisitos

configurar o ambiente

Abra seu projeto do Cloud no console do Google Cloud.

Se ainda não estiver aberto, abra o projeto do Cloud que você pretende usar para esta amostra:

  1. No console do Google Cloud, acesse a página Selecionar um projeto.

    Selecionar um projeto do Cloud

  2. Selecione o projeto do Google Cloud que você quer usar. Ou clique em Criar projeto e siga as instruções na tela. Se você criar um projeto do Google Cloud, talvez seja necessário ativar o faturamento dele.

Ativar a API Calendar

Neste guia de início rápido, usamos o serviço avançado do Agenda, que acessa a API Calendar. Antes de usar as APIs do Google, é preciso ativá-las em um projeto do Google Cloud. É possível ativar uma ou mais APIs em um único projeto do Google Cloud.

Os complementos do Google Workspace exigem uma configuração de tela de consentimento. A configuração da tela de consentimento OAuth do complemento do Google Workspace define o que o Google exibe aos usuários.

  1. No console do Google Cloud, acesse Menu > APIs e serviços > Tela de permissão OAuth.

    Acessar a tela de permissão OAuth

  2. Em Tipo de usuário, selecione Interno e clique em Criar.
  3. Preencha o formulário de registro do app e clique em Salvar e continuar.
  4. Por enquanto, é possível pular a adição de escopos e clicar em Salvar e continuar. No futuro, quando você criar um app para uso fora da sua organização do Google Workspace, será necessário alterar o Tipo de usuário para Externo e, em seguida, adicionar os escopos de autorização exigidos pelo app.

  5. Analise o resumo de registro do seu app. Para fazer mudanças, clique em Editar. Se o registro do app estiver correto, clique em Voltar para o painel.

Configurar o script

Criar o projeto do Apps Script

  1. Para criar um novo projeto do Apps Script, acesse script.new.
  2. Clique em Projeto sem título.
  3. Renomeie o projeto do Apps Script como Complemento de videoconferências e clique em Renomear.
  4. Ao lado do arquivo Code.gs, clique em Mais > Renomear. Nomeie o arquivo como CreateConf.
  5. Clique em Adicionar um arquivo > Script.
  6. Nomeie o arquivo Syncing.
  7. Substitua o conteúdo de cada arquivo pelo seguinte código correspondente:

    CreateConf.gs

    /**
     *  Creates a conference, then builds and returns a ConferenceData object
     *  with the corresponding conference information. This method is called
     *  when a user selects a conference solution defined by the add-on that
     *  uses this function as its 'onCreateFunction' in the add-on manifest.
     *
     *  @param {Object} arg The default argument passed to a 'onCreateFunction';
     *      it carries information about the Google Calendar event.
     *  @return {ConferenceData}
     */
    function createConference(arg) {
      const eventData = arg.eventData;
      const calendarId = eventData.calendarId;
      const eventId = eventData.eventId;
    
      // Retrieve the Calendar event information using the Calendar
      // Advanced service.
      var calendarEvent;
      try {
        calendarEvent = Calendar.Events.get(calendarId, eventId);
      } catch (err) {
        // The calendar event does not exist just yet; just proceed with the
        // given event ID and allow the event details to sync later.
        console.log(err);
        calendarEvent = {
          id: eventId,
        };
      }
    
      // Create a conference on the third-party service and return the
      // conference data or errors in a custom JSON object.
      var conferenceInfo = create3rdPartyConference(calendarEvent);
    
      // Build and return a ConferenceData object, either with conference or
      // error information.
      var dataBuilder = ConferenceDataService.newConferenceDataBuilder();
    
      if (!conferenceInfo.error) {
        // No error, so build the ConferenceData object from the
        // returned conference info.
    
        var phoneEntryPoint = ConferenceDataService.newEntryPoint()
            .setEntryPointType(ConferenceDataService.EntryPointType.PHONE)
            .setUri('tel:+' + conferenceInfo.phoneNumber)
            .setPin(conferenceInfo.phonePin);
    
        var adminEmailParameter = ConferenceDataService.newConferenceParameter()
            .setKey('adminEmail')
            .setValue(conferenceInfo.adminEmail);
    
        dataBuilder.setConferenceId(conferenceInfo.id)
            .addEntryPoint(phoneEntryPoint)
            .addConferenceParameter(adminEmailParameter)
            .setNotes(conferenceInfo.conferenceLegalNotice);
    
        if (conferenceInfo.videoUri) {
          var videoEntryPoint = ConferenceDataService.newEntryPoint()
              .setEntryPointType(ConferenceDataService.EntryPointType.VIDEO)
              .setUri(conferenceInfo.videoUri)
              .setPasscode(conferenceInfo.videoPasscode);
          dataBuilder.addEntryPoint(videoEntryPoint);
        }
    
        // Since the conference creation request succeeded, make sure that
        // syncing has been enabled.
        initializeSyncing(calendarId, eventId, conferenceInfo.id);
    
      } else if (conferenceInfo.error === 'AUTH') {
        // Authenentication error. Implement a function to build the correct
        // authenication URL for the third-party conferencing system.
        var authenticationUrl = getAuthenticationUrl();
        var error = ConferenceDataService.newConferenceError()
            .setConferenceErrorType(
                ConferenceDataService.ConferenceErrorType.AUTHENTICATION)
            .setAuthenticationUrl(authenticationUrl);
        dataBuilder.setError(error);
    
      } else {
        // Other error type;
        var error = ConferenceDataService.newConferenceError()
            .setConferenceErrorType(
                ConferenceDataService.ConferenceErrorType.TEMPORARY);
        dataBuilder.setError(error);
      }
    
      // Don't forget to build the ConferenceData object.
      return dataBuilder.build();
    }
    
    
    /**
     *  Contact the third-party conferencing system to create a conference there,
     *  using the provided calendar event information. Collects and retuns the
     *  conference data returned by the third-party system in a custom JSON object
     *  with the following fields:
     *
     *    data.adminEmail - the conference administrator's email
     *    data.conferenceLegalNotice - the conference legal notice text
     *    data.error - Only present if there was an error during
     *         conference creation. Equal to 'AUTH' if the add-on user needs to
     *         authorize on the third-party system.
     *    data.id - the conference ID
     *    data.phoneNumber - the conference phone entry point phone number
     *    data.phonePin - the conference phone entry point PIN
     *    data.videoPasscode - the conference video entry point passcode
     *    data.videoUri - the conference video entry point URI
     *
     *  The above fields are specific to this example; which conference information
     *  your add-on needs is dependent on the third-party conferencing system
     *  requirements.
     *
     * @param {Object} calendarEvent A Calendar Event resource object returned by
     *     the Google Calendar API.
     * @return {Object}
     */
    function create3rdPartyConference(calendarEvent) {
      var data = {};
    
      // Implementation details dependent on the third-party system API.
      // Typically one or more API calls are made to create the conference and
      // acquire its relevant data, which is then put in to the returned JSON
      // object.
    
      return data;
    }
    
    /**
     *  Return the URL used to authenticate the user with the third-party
     *  conferencing system.
     *
     *  @return {String}
     */
    function getAuthenticationUrl() {
      var url;
      // Implementation details dependent on the third-party system.
    
      return url;
    }
    
    

    Syncing.gs

    /**
     *  Initializes syncing of conference data by creating a sync trigger and
     *  sync token if either does not exist yet.
     *
     *  @param {String} calendarId The ID of the Google Calendar.
     */
    function initializeSyncing(calendarId) {
      // Create a syncing trigger if it doesn't exist yet.
      createSyncTrigger(calendarId);
    
      // Perform an event sync to create the initial sync token.
      syncEvents({'calendarId': calendarId});
    }
    
    /**
     *  Creates a sync trigger if it does not exist yet.
     *
     *  @param {String} calendarId The ID of the Google Calendar.
     */
    function createSyncTrigger(calendarId) {
      // Check to see if the trigger already exists; if does, return.
      var allTriggers = ScriptApp.getProjectTriggers();
      for (var i = 0; i < allTriggers.length; i++) {
        var trigger = allTriggers[i];
        if (trigger.getTriggerSourceId() == calendarId) {
          return;
        }
      }
    
      // Trigger does not exist, so create it. The trigger calls the
      // 'syncEvents()' trigger function when it fires.
      var trigger = ScriptApp.newTrigger('syncEvents')
          .forUserCalendar(calendarId)
          .onEventUpdated()
          .create();
    }
    
    /**
     *  Sync events for the given calendar; this is the syncing trigger
     *  function. If a sync token already exists, this retrieves all events
     *  that have been modified since the last sync, then checks each to see
     *  if an associated conference needs to be updated and makes any required
     *  changes. If the sync token does not exist or is invalid, this
     *  retrieves future events modified in the last 24 hours instead. In
     *  either case, a new sync token is created and stored.
     *
     *  @param {Object} e If called by a event updated trigger, this object
     *      contains the Google Calendar ID, authorization mode, and
     *      calling trigger ID. Only the calendar ID is actually used here,
     *      however.
     */
    function syncEvents(e) {
      var calendarId = e.calendarId;
      var properties = PropertiesService.getUserProperties();
      var syncToken = properties.getProperty('syncToken');
    
      var options;
      if (syncToken) {
        // There's an existing sync token, so configure the following event
        // retrieval request to only get events that have been modified
        // since the last sync.
        options = {
          syncToken: syncToken
        };
      } else {
        // No sync token, so configure to do a 'full' sync instead. In this
        // example only recently updated events are retrieved in a full sync.
        // A larger time window can be examined during a full sync, but this
        // slows down the script execution. Consider the trade-offs while
        // designing your add-on.
        var now = new Date();
        var yesterday = new Date();
        yesterday.setDate(now.getDate() - 1);
        options = {
          timeMin: now.toISOString(),          // Events that start after now...
          updatedMin: yesterday.toISOString(), // ...and were modified recently
          maxResults: 50,   // Max. number of results per page of responses
          orderBy: 'updated'
        }
      }
    
      // Examine the list of updated events since last sync (or all events
      // modified after yesterday if the sync token is missing or invalid), and
      // update any associated conferences as required.
      var events;
      var pageToken;
      do {
        try {
          options.pageToken = pageToken;
          events = Calendar.Events.list(calendarId, options);
        } catch (err) {
          // Check to see if the sync token was invalidated by the server;
          // if so, perform a full sync instead.
          if (err.message ===
                "Sync token is no longer valid, a full sync is required.") {
            properties.deleteProperty('syncToken');
            syncEvents(e);
            return;
          } else {
            throw new Error(err.message);
          }
        }
    
        // Read through the list of returned events looking for conferences
        // to update.
        if (events.items && events.items.length > 0) {
          for (var i = 0; i < events.items.length; i++) {
             var calEvent = events.items[i];
             // Check to see if there is a record of this event has a
             // conference that needs updating.
             if (eventHasConference(calEvent)) {
               updateConference(calEvent, calEvent.conferenceData.conferenceId);
             }
          }
        }
    
        pageToken = events.nextPageToken;
      } while (pageToken);
    
      // Record the new sync token.
      if (events.nextSyncToken) {
        properties.setProperty('syncToken', events.nextSyncToken);
      }
    }
    
    /**
     *  Returns true if the specified event has an associated conference
     *  of the type managed by this add-on; retuns false otherwise.
     *
     *  @param {Object} calEvent The Google Calendar event object, as defined by
     *      the Calendar API.
     *  @return {boolean}
     */
    function eventHasConference(calEvent) {
      var name = calEvent.conferenceData.conferenceSolution.name || null;
    
      // This version checks if the conference data solution name matches the
      // one of the solution names used by the add-on. Alternatively you could
      // check the solution's entry point URIs or other solution-specific
      // information.
      if (name) {
        if (name === "My Web Conference" ||
            name === "My Recorded Web Conference") {
          return true;
        }
      }
      return false;
    }
    
    /**
     *  Update a conference based on new Google Calendar event information.
     *  The exact implementation of this function is highly dependant on the
     *  details of the third-party conferencing system, so only a rough outline
     *  is shown here.
     *
     *  @param {Object} calEvent The Google Calendar event object, as defined by
     *      the Calendar API.
     *  @param {String} conferenceId The ID used to identify the conference on
     *      the third-party conferencing system.
     */
    function updateConference(calEvent, conferenceId) {
      // Check edge case: the event was cancelled
      if (calEvent.status === 'cancelled' || eventHasConference(calEvent)) {
        // Use the third-party API to delete the conference too.
    
    
      } else {
        // Extract any necessary information from the event object, then
        // make the appropriate third-party API requests to update the
        // conference with that information.
    
      }
    }
    
    
  8. Clique em Configurações do projeto O ícone das configurações do projeto.

  9. Marque a caixa Mostrar arquivo de manifesto "appsscript.json" no editor.

  10. Clique em Editor .

  11. Abra o arquivo appsscript.json, substitua o conteúdo pelo código a seguir e clique em Save Ícone &quot;Salvar&quot;.

    appsscript.json

    {
      "addOns": {
        "calendar": {
          "conferenceSolution": [{
            "id": 1,
            "name": "My Web Conference",
            "logoUrl": "https://lh3.googleusercontent.com/...",
            "onCreateFunction": "createConference"
          }],
          "currentEventAccess": "READ_WRITE"
        },
        "common": {
          "homepageTrigger": {
            "enabled": false
          },
          "logoUrl": "https://lh3.googleusercontent.com/...",
          "name": "My Web Conferencing"
        }
      },
      "timeZone": "America/New_York",
      "dependencies": {
        "enabledAdvancedServices": [
        {
          "userSymbol": "Calendar",
          "serviceId": "calendar",
          "version": "v3"
        }
        ]
      },
      "webapp": {
        "access": "ANYONE",
        "executeAs": "USER_ACCESSING"
      },
      "exceptionLogging": "STACKDRIVER",
      "oauthScopes": [
        "https://www.googleapis.com/auth/calendar.addons.execute",
        "https://www.googleapis.com/auth/calendar.events.readonly",
        "https://www.googleapis.com/auth/calendar.addons.current.event.read",
        "https://www.googleapis.com/auth/calendar.addons.current.event.write",
        "https://www.googleapis.com/auth/script.external_request",
        "https://www.googleapis.com/auth/script.scriptapp"
      ]
    }
    
    

Copie o número do projeto do Cloud.

  1. No console do Google Cloud, acesse Menu > IAM e administrador > Configurações.

    Acessar as configurações de IAM e administrador

  2. No campo Número do projeto, copie o valor.

Definir o projeto do Cloud do projeto do Apps Script

  1. No projeto do Apps Script, clique em Configurações do projeto O ícone das configurações do projeto.
  2. Em Projeto do Google Cloud Platform (GCP), clique em Mudar projeto.
  3. Em Número do projeto do GCP, cole o número do projeto do Google Cloud.
  4. Clique em Configurar projeto.

Instalar uma implantação de teste

  1. No seu projeto do Apps Script, clique em Editor .
  2. Abra o arquivo CreateConf.gs e clique em Run. Quando solicitado, autorize o script.
  3. Clique em Implantar > Testar implantações.
  4. Clique em Instalar > Concluído.

Executar o script

  1. Acesse calendar.google.com.
  2. Crie um novo evento ou abra um evento já existente.
  3. Ao lado de Adicionar videoconferência do Google Meet, clique na seta para baixo > Minha videoconferência na Web. O erro Falha ao criar a videoconferência é exibido porque o complemento não está conectado a uma solução real de videoconferência de terceiros.

Próximas etapas