중요: 이 빠른 시작은 웹 회의 제공업체만 사용할 수 있습니다.
다음 Google Workspace 부가기능 빠른 시작에서는 Google Calendar를 확장하여 My Web Conferencing이라는 가상의 웹 회의 서비스와 동기화합니다. 부가기능을 사용하면 사용자가 캘린더 일정을 수정할 때 내 웹 회의를 회의 옵션으로 볼 수 있습니다.
빠른 시작에서는 회의 생성 및 이벤트 동기화를 보여줍니다. 하지만 회의 솔루션 API에 연결하기 전에는 작동하지 않습니다.
목표
- 환경을 설정합니다.
- 스크립트를 설정합니다.
- 스크립트를 실행합니다.
기본 요건
- 인터넷에 액세스할 수 있는 웹브라우저
- Google Workspace 계정(관리자 승인이 필요할 수 있음)
- Google Cloud 프로젝트
환경 설정
Google Cloud 콘솔에서 Cloud 프로젝트 열기
아직 열려 있지 않으면 이 샘플에 사용할 Cloud 프로젝트를 엽니다.
- Google Cloud 콘솔에서 프로젝트 선택 페이지로 이동합니다.
- 사용할 Google Cloud 프로젝트를 선택합니다. 또는 프로젝트 만들기를 클릭하고 화면에 표시된 안내를 따릅니다. Google Cloud 프로젝트를 만드는 경우 프로젝트에 결제를 사용 설정해야 할 수 있습니다.
Calendar API 사용 설정
이 빠른 시작에서는 Calendar API에 액세스하는 Calendar 고급 서비스를 사용합니다.
Google API를 사용하려면 먼저 Google Cloud 프로젝트에서 사용 설정해야 합니다. 단일 Google Cloud 프로젝트에서 하나 이상의 API를 사용 설정할 수 있습니다.Google Cloud 프로젝트에서 Calendar API를 사용 설정합니다.
OAuth 동의 화면 구성
Google Workspace 부가기능에는 동의 화면 구성이 필요합니다. Google Workspace 부가기능의 OAuth 동의 화면을 구성하면 Google에서 사용자에게 표시할 내용이 정의됩니다.
- Google Cloud 콘솔에서 메뉴 > API 및 서비스 > OAuth 동의 화면으로 이동합니다.
- 사용자 유형에서 내부를 선택한 다음 만들기를 클릭합니다.
- 앱 등록 양식을 작성한 후 저장 및 계속을 클릭합니다.
지금은 범위 추가를 건너뛰고 저장하고 계속을 클릭해도 됩니다. 향후 Google Workspace 조직 외부에서 사용할 앱을 만들 때는 사용자 유형을 외부로 변경한 후 앱에 필요한 승인 범위를 추가해야 합니다.
- 앱 등록 요약을 검토합니다. 변경하려면 수정을 클릭합니다. 앱 등록이 올바른 것 같으면 대시보드로 돌아가기를 클릭합니다.
스크립트 설정
Apps Script 프로젝트 만들기
- 새 Apps Script 프로젝트를 만들려면 script.new로 이동합니다.
- 제목 없는 프로젝트를 클릭합니다.
- Apps Script 프로젝트 회의 부가기능의 이름을 바꾸고 이름 바꾸기를 클릭합니다.
Code.gs
파일 옆에 있는 더보기 > 이름 바꾸기를 클릭합니다. 파일 이름을CreateConf
로 지정합니다.- 파일 추가 > 스크립트를 클릭합니다.
- 파일 이름을
Syncing
으로 지정합니다. 각 파일의 콘텐츠를 다음과 같은 해당 코드로 바꿉니다.
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. } }
프로젝트 설정 를 클릭합니다.
편집기에 'appsscript.json' 매니페스트 파일 표시 체크박스를 선택합니다.
편집기
를 클릭합니다.appsscript.json
파일을 열고 콘텐츠를 다음 코드로 바꾼 다음 저장 을 클릭합니다.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" ] }
Cloud 프로젝트 번호 복사
- Google Cloud 콘솔에서 메뉴 > IAM 및 관리자 > 설정으로 이동합니다.
- 프로젝트 번호 필드에서 값을 복사합니다.
Apps Script 프로젝트의 Cloud 프로젝트 설정
- Apps Script 프로젝트에서 프로젝트 설정 을 클릭합니다.
- Google Cloud Platform(GCP) 프로젝트에서 프로젝트 변경을 클릭합니다.
- GCP 프로젝트 번호에 Google Cloud 프로젝트 번호를 붙여넣습니다.
- 프로젝트 설정을 클릭합니다.
테스트 배포 설치
- Apps Script 프로젝트에서 편집기 를 클릭합니다.
CreateConf.gs
파일을 열고 Run을 클릭합니다. 메시지가 표시되면 스크립트를 승인합니다.- 배포 > 배포 테스트를 클릭합니다.
- 설치 > 완료를 클릭합니다.
스크립트 실행
- calendar.google.com으로 이동합니다.
- 새로운 일정을 만들거나 기존 일정을 엽니다.
- Google Meet 화상 회의 추가 옆의 아래쪽 화살표 > 내 웹 회의를 클릭합니다. 부가기능이 실제 서드 파티 회의 솔루션에 연결되어 있지 않으므로 회의를 만들지 못했습니다 오류가 표시됩니다.