이 빠른 시작에서는 홈페이지, 문맥 트리거, 서드 파티 API 연결을 보여주는 간단한 Google Workspace 부가기능을 만듭니다.
이 부가기능은 Gmail, Calendar, Drive에서 문맥 인터페이스와 비문맥 인터페이스를 만듭니다. 이 부가기능은 고양이의 임의 이미지를 표시하고 이미지 위에 텍스트를 오버레이합니다. 텍스트는 홈페이지의 경우 정적 텍스트이거나 문맥 트리거의 경우 호스트 애플리케이션 컨텍스트에서 가져옵니다.
목표
- 환경을 설정합니다.
- 스크립트를 설정합니다.
- 스크립트를 실행합니다.
기본 요건
이 샘플을 사용하려면 다음 기본 요건이 필요합니다.
- Google 계정 (Google Workspace 계정의 경우 관리자 승인이 필요할 수 있음)
인터넷에 액세스할 수 있는 웹브라우저
환경 설정
Google Cloud 콘솔에서 Cloud 프로젝트 열기
아직 열려 있지 않으면 이 샘플에 사용할 Cloud 프로젝트를 엽니다.
- Google Cloud 콘솔에서 프로젝트 선택 페이지로 이동합니다.
- 사용할 Google Cloud 프로젝트를 선택합니다. 또는 프로젝트 만들기를 클릭하고 화면에 표시된 안내를 따릅니다. Google Cloud 프로젝트를 만드는 경우 프로젝트에 결제를 사용 설정해야 할 수 있습니다.
OAuth 동의 화면 구성
Google Workspace 부가기능에는 동의 화면 구성이 필요합니다. 부가기능의 OAuth 동의 화면을 구성하면 Google에서 사용자에게 표시할 내용이 정의됩니다.
- Google Cloud 콘솔에서 메뉴 > API 및 서비스 > OAuth 동의 화면으로 이동합니다.
- 사용자 유형에서 내부를 선택한 다음 만들기를 클릭합니다.
- 앱 등록 양식을 작성한 후 저장 및 계속을 클릭합니다.
지금은 범위 추가를 건너뛰고 저장하고 계속을 클릭해도 됩니다. 향후 Google Workspace 조직 외부에서 사용할 앱을 만들 때는 사용자 유형을 외부로 변경한 후 앱에 필요한 승인 범위를 추가해야 합니다.
- 앱 등록 요약을 검토합니다. 변경하려면 수정을 클릭합니다. 앱 등록이 올바른 것 같으면 대시보드로 돌아가기를 클릭합니다.
스크립트 설정
Apps Script 프로젝트 만들기
- 새 Apps Script 프로젝트를 만들려면 script.new로 이동합니다.
- 제목 없는 프로젝트를 클릭합니다.
- Apps Script 프로젝트 이름을 고양이로 바꾸고 이름 바꾸기를 클릭합니다.
Code.gs
파일 옆에 있는 더보기 > 이름 바꾸기를 클릭합니다. 파일 이름을Common
로 지정합니다.- 파일 추가 > 스크립트를 클릭합니다. 파일 이름을
Gmail
로 지정합니다. - 5단계를 반복하여
Calendar
및Drive
라는 스크립트 파일을 2개 더 만듭니다. 완료하면 4개의 별도 스크립트 파일이 생성됩니다. 각 파일의 콘텐츠를 다음과 같은 해당 코드로 바꿉니다.
Common.gs
/** * This simple Google Workspace Add-on shows a random image of a cat in the * sidebar. When opened manually (the homepage card), some static text is * overlayed on the image, but when contextual cards are opened a new cat image * is shown with the text taken from that context (such as a message's subject * line) overlaying the image. There is also a button that updates the card with * a new random cat image. * * Click "File > Make a copy..." to copy the script, and "Publish > Deploy from * manifest > Install add-on" to install it. */ /** * The maximum number of characters that can fit in the cat image. */ var MAX_MESSAGE_LENGTH = 40; /** * Callback for rendering the homepage card. * @return {CardService.Card} The card to show to the user. */ function onHomepage(e) { console.log(e); var hour = Number(Utilities.formatDate(new Date(), e.userTimezone.id, 'H')); var message; if (hour >= 6 && hour < 12) { message = 'Good morning'; } else if (hour >= 12 && hour < 18) { message = 'Good afternoon'; } else { message = 'Good night'; } message += ' ' + e.hostApp; return createCatCard(message, true); } /** * Creates a card with an image of a cat, overlayed with the text. * @param {String} text The text to overlay on the image. * @param {Boolean} isHomepage True if the card created here is a homepage; * false otherwise. Defaults to false. * @return {CardService.Card} The assembled card. */ function createCatCard(text, isHomepage) { // Explicitly set the value of isHomepage as false if null or undefined. if (!isHomepage) { isHomepage = false; } // Use the "Cat as a service" API to get the cat image. Add a "time" URL // parameter to act as a cache buster. var now = new Date(); // Replace forward slashes in the text, as they break the CataaS API. var caption = text.replace(/\//g, ' '); var imageUrl = Utilities.formatString('https://cataas.com/cat/says/%s?time=%s', encodeURIComponent(caption), now.getTime()); var image = CardService.newImage() .setImageUrl(imageUrl) .setAltText('Meow') // Create a button that changes the cat image when pressed. // Note: Action parameter keys and values must be strings. var action = CardService.newAction() .setFunctionName('onChangeCat') .setParameters({text: text, isHomepage: isHomepage.toString()}); var button = CardService.newTextButton() .setText('Change cat') .setOnClickAction(action) .setTextButtonStyle(CardService.TextButtonStyle.FILLED); var buttonSet = CardService.newButtonSet() .addButton(button); // Create a footer to be shown at the bottom. var footer = CardService.newFixedFooter() .setPrimaryButton(CardService.newTextButton() .setText('Powered by cataas.com') .setOpenLink(CardService.newOpenLink() .setUrl('https://cataas.com'))); // Assemble the widgets and return the card. var section = CardService.newCardSection() .addWidget(image) .addWidget(buttonSet); var card = CardService.newCardBuilder() .addSection(section) .setFixedFooter(footer); if (!isHomepage) { // Create the header shown when the card is minimized, // but only when this card is a contextual card. Peek headers // are never used by non-contexual cards like homepages. var peekHeader = CardService.newCardHeader() .setTitle('Contextual Cat') .setImageUrl('https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png') .setSubtitle(text); card.setPeekCardHeader(peekHeader) } return card.build(); } /** * Callback for the "Change cat" button. * @param {Object} e The event object, documented {@link * https://developers.google.com/gmail/add-ons/concepts/actions#action_event_objects * here}. * @return {CardService.ActionResponse} The action response to apply. */ function onChangeCat(e) { console.log(e); // Get the text that was shown in the current cat image. This was passed as a // parameter on the Action set for the button. var text = e.parameters.text; // The isHomepage parameter is passed as a string, so convert to a Boolean. var isHomepage = e.parameters.isHomepage === 'true'; // Create a new card with the same text. var card = createCatCard(text, isHomepage); // Create an action response that instructs the add-on to replace // the current card with the new one. var navigation = CardService.newNavigation() .updateCard(card); var actionResponse = CardService.newActionResponseBuilder() .setNavigation(navigation); return actionResponse.build(); } /** * Truncate a message to fit in the cat image. * @param {string} message The message to truncate. * @return {string} The truncated message. */ function truncate(message) { if (message.length > MAX_MESSAGE_LENGTH) { message = message.slice(0, MAX_MESSAGE_LENGTH); message = message.slice(0, message.lastIndexOf(' ')) + '...'; } return message; }
Gmail.gs
/** * Callback for rendering the card for a specific Gmail message. * @param {Object} e The event object. * @return {CardService.Card} The card to show to the user. */ function onGmailMessage(e) { console.log(e); // Get the ID of the message the user has open. var messageId = e.gmail.messageId; // Get an access token scoped to the current message and use it for GmailApp // calls. var accessToken = e.gmail.accessToken; GmailApp.setCurrentMessageAccessToken(accessToken); // Get the subject of the email. var message = GmailApp.getMessageById(messageId); var subject = message.getThread().getFirstMessageSubject(); // Remove labels and prefixes. subject = subject .replace(/^([rR][eE]|[fF][wW][dD])\:\s*/, '') .replace(/^\[.*?\]\s*/, ''); // If neccessary, truncate the subject to fit in the image. subject = truncate(subject); return createCatCard(subject); } /** * Callback for rendering the card for the compose action dialog. * @param {Object} e The event object. * @return {CardService.Card} The card to show to the user. */ function onGmailCompose(e) { console.log(e); var header = CardService.newCardHeader() .setTitle('Insert cat') .setSubtitle('Add a custom cat image to your email message.'); // Create text input for entering the cat's message. var input = CardService.newTextInput() .setFieldName('text') .setTitle('Caption') .setHint('What do you want the cat to say?'); // Create a button that inserts the cat image when pressed. var action = CardService.newAction() .setFunctionName('onGmailInsertCat'); var button = CardService.newTextButton() .setText('Insert cat') .setOnClickAction(action) .setTextButtonStyle(CardService.TextButtonStyle.FILLED); var buttonSet = CardService.newButtonSet() .addButton(button); // Assemble the widgets and return the card. var section = CardService.newCardSection() .addWidget(input) .addWidget(buttonSet); var card = CardService.newCardBuilder() .setHeader(header) .addSection(section); return card.build(); } /** * Callback for inserting a cat into the Gmail draft. * @param {Object} e The event object. * @return {CardService.UpdateDraftActionResponse} The draft update response. */ function onGmailInsertCat(e) { console.log(e); // Get the text that was entered by the user. var text = e.formInput.text; // Use the "Cat as a service" API to get the cat image. Add a "time" URL // parameter to act as a cache buster. var now = new Date(); var imageUrl = 'https://cataas.com/cat'; if (text) { // Replace forward slashes in the text, as they break the CataaS API. var caption = text.replace(/\//g, ' '); imageUrl += Utilities.formatString('/says/%s?time=%s', encodeURIComponent(caption), now.getTime()); } var imageHtmlContent = '<img style="display: block; max-height: 300px;" src="' + imageUrl + '"/>'; var response = CardService.newUpdateDraftActionResponseBuilder() .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction() .addUpdateContent(imageHtmlContent,CardService.ContentType.MUTABLE_HTML) .setUpdateType(CardService.UpdateDraftBodyType.IN_PLACE_INSERT)) .build(); return response; }
Calendar.gs
/** * Callback for rendering the card for a specific Calendar event. * @param {Object} e The event object. * @return {CardService.Card} The card to show to the user. */ function onCalendarEventOpen(e) { console.log(e); var calendar = CalendarApp.getCalendarById(e.calendar.calendarId); // The event metadata doesn't include the event's title, so using the // calendar.readonly scope and fetching the event by it's ID. var event = calendar.getEventById(e.calendar.id); if (!event) { // This is a new event still being created. return createCatCard('A new event! Am I invited?'); } var title = event.getTitle(); // If necessary, truncate the title to fit in the image. title = truncate(title); return createCatCard(title); }
Drive.gs
/** * Callback for rendering the card for specific Drive items. * @param {Object} e The event object. * @return {CardService.Card} The card to show to the user. */ function onDriveItemsSelected(e) { console.log(e); var items = e.drive.selectedItems; // Include at most 5 items in the text. items = items.slice(0, 5); var text = items.map(function(item) { var title = item.title; // If neccessary, truncate the title to fit in the image. title = truncate(title); return title; }).join('\n'); return createCatCard(text); }
프로젝트 설정 를 클릭합니다.
편집기에 'appsscript.json' 매니페스트 파일 표시 체크박스를 선택합니다.
편집기
를 클릭합니다.appsscript.json
파일을 열고 콘텐츠를 다음 코드로 바꾼 다음 저장 을 클릭합니다.appsscript.json
{ "timeZone": "America/New_York", "dependencies": { }, "exceptionLogging": "STACKDRIVER", "oauthScopes": [ "https://www.googleapis.com/auth/calendar.addons.execute", "https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/drive.addons.metadata.readonly", "https://www.googleapis.com/auth/gmail.addons.current.action.compose", "https://www.googleapis.com/auth/gmail.addons.current.message.readonly", "https://www.googleapis.com/auth/gmail.addons.execute", "https://www.googleapis.com/auth/script.locale"], "runtimeVersion": "V8", "addOns": { "common": { "name": "Cats", "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png", "useLocaleFromApp": true, "homepageTrigger": { "runFunction": "onHomepage", "enabled": true }, "universalActions": [{ "label": "Learn more about Cataas", "openLink": "https://cataas.com" }] }, "gmail": { "contextualTriggers": [{ "unconditional": { }, "onTriggerFunction": "onGmailMessage" }], "composeTrigger": { "selectActions": [{ "text": "Insert cat", "runFunction": "onGmailCompose" }], "draftAccess": "NONE" } }, "drive": { "onItemsSelectedTrigger": { "runFunction": "onDriveItemsSelected" } }, "calendar": { "eventOpenTrigger": { "runFunction": "onCalendarEventOpen" } } } }
Cloud 프로젝트 번호 복사
- Google Cloud 콘솔에서 메뉴 > IAM 및 관리자 > 설정으로 이동합니다.
- 프로젝트 번호 필드에서 값을 복사합니다.
Apps Script 프로젝트의 Cloud 프로젝트 설정
- Apps Script 프로젝트에서 프로젝트 설정 을 클릭합니다.
- Google Cloud Platform(GCP) 프로젝트에서 프로젝트 변경을 클릭합니다.
- GCP 프로젝트 번호에 Google Cloud 프로젝트 번호를 붙여넣습니다.
- 프로젝트 설정을 클릭합니다.
테스트 배포 설치
- Apps Script 프로젝트에서 편집기 를 클릭합니다.
- 배포 > 배포 테스트를 클릭합니다.
- 설치 > 완료를 클릭합니다.
스크립트 실행
- Gmail로 이동합니다.
- 부가기능을 열려면 오른쪽 측면 패널에서 고양이 를 클릭합니다.
- 메시지가 표시되면 부가기능을 승인합니다.
- 부가기능은 고양이 이미지와 텍스트를 표시합니다. 이미지를 변경하려면 카테고리 변경을 클릭합니다.
- 부가기능이 열려 있는 동안 이메일을 열면 이미지가 새로고침되고 텍스트가 이메일의 제목으로 변경됩니다(너무 길면 잘림).
Calendar 및 Drive에서도 유사한 기능을 확인할 수 있습니다. 이러한 호스트 앱에서 부가기능을 사용하기 위해 부가기능을 다시 승인할 필요는 없습니다.