Синхронизация изменений конференции в календаре

Пользователи могут свободно обновлять или удалять свои мероприятия в Google Календаре. Если пользователь обновляет мероприятие после создания конференции, вашему дополнению может потребоваться отреагировать на это изменение, обновив данные конференции. Если ваша сторонняя система конференц-связи требует отслеживания данных о мероприятиях, отсутствие обновления конференции при изменении события может сделать конференцию непригодной для использования и ухудшить пользовательский опыт.

Процесс обновления данных конференции с учётом изменений в мероприятии Google Календаря называется синхронизацией . Вы можете синхронизировать изменения событий, создав устанавливаемый триггер Apps Script, который срабатывает при каждом изменении событий в заданном календаре. К сожалению, триггер не сообщает, какие события изменились, и вы не можете ограничить его только мероприятиями с конференциями, созданными вами. Вместо этого необходимо запросить список всех изменений, внесённых в календарь с момента последней синхронизации, отфильтровать список событий и внести соответствующие обновления.

Общая процедура синхронизации следующая:

  1. Когда пользователь создает конференцию в первый раз, инициализируется процесс синхронизации.
  2. Всякий раз, когда пользователь создает, обновляет или удаляет одно из событий своего календаря, триггер выполняет функцию триггера в вашем проекте надстройки.
  3. Функция триггера проверяет набор изменений событий с момента последней синхронизации и определяет, требуется ли обновление связанной сторонней конференции.
  4. Все необходимые обновления конференций вносятся путем отправки сторонних API-запросов.
  5. Новый токен синхронизации сохраняется, чтобы при следующем выполнении триггера достаточно было проверить только самые последние изменения в календаре.

Инициализировать синхронизацию

После того как дополнение успешно создало конференцию в сторонней системе, оно должно создать устанавливаемый триггер , который реагирует на изменения событий в этом календаре, если триггер еще не существует.

После создания триггера инициализация должна завершиться созданием начального токена синхронизации. Это делается путём непосредственного выполнения функции триггера.

Создать триггер календаря

Для синхронизации ваше дополнение должно отслеживать изменение события в календаре, к которому привязана конференция. Это достигается созданием устанавливаемого триггера EventUpdated . Вашему дополнению требуется только один триггер для каждого календаря, и вы можете создавать их программно.

Наиболее подходящий момент для создания триггера — это момент создания пользователем своей первой конференции, поскольку именно в этот момент он начинает использовать дополнение. После создания конференции и проверки отсутствия ошибок ваше дополнение должно проверить, существует ли триггер для этого пользователя, и, если нет, создать его.

Реализовать функцию синхронного триггера

Функции триггера выполняются, когда Apps Script обнаруживает условие, вызывающее срабатывание триггера. Триггеры EventUpdated Calendar срабатывают , когда пользователь создаёт, изменяет или удаляет любое событие в указанном календаре.

Необходимо реализовать функцию-триггер, используемую вашим дополнением. Эта функция-триггер должна выполнять следующие действия:

  1. Вызовите расширенную службу Calendar.Events.list() с помощью syncToken , чтобы получить список событий, изменившихся с момента последней синхронизации. Использование токена синхронизации сокращает количество событий, которые должно проверять ваше дополнение.

    Когда функция триггера выполняется без действительного токена синхронизации, она возвращается к полной синхронизации . Полная синхронизация просто пытается получить все события в течение заданного периода времени, чтобы сгенерировать новый действительный токен синхронизации.

  2. Каждое измененное событие проверяется на предмет наличия связанной с ним сторонней конференции.
  3. Если у события есть конференция, она проверяется на предмет изменений. В зависимости от изменений может потребоваться изменение связанной конференции. Например, если событие было удалено, дополнение, вероятно, должно удалить и конференцию.
  4. Любые необходимые изменения в конференции вносятся путем API-вызовов к сторонней системе.
  5. После внесения всех необходимых изменений сохраните nextSyncToken , возвращаемый методом Calendar.Events.list() . Этот токен синхронизации находится на последней странице результатов, возвращаемых вызовом Calendar.Events.list() .

Обновление события в Google Календаре

В некоторых случаях может потребоваться обновить событие в Google Календаре при синхронизации. В этом случае обновите событие с помощью соответствующего расширенного запроса на обслуживание Google Календаря . Обязательно используйте условное обновление с заголовком If-Match . Это предотвратит случайную перезапись ваших изменений, внесённых пользователем в другом клиенте.

Пример

В следующем примере показано, как настроить синхронизацию событий календаря и связанных с ними конференций.

/**
 *  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.

  }
}