Apps Script로 Google Workspace 부가기능 빌드

이 빠른 시작에서는 다음을 보여주는 간단한 Google Workspace 부가기능을 만듭니다. 홈페이지, 문맥 트리거, 서드 파티 API 연결

이 부가기능은 컨텍스트 기반 및 비맥락적 콘텐츠를 만듭니다. Gmail, Calendar, Drive의 인터페이스입니다. 부가기능은 고양이가 있는 무작위 고양이 이미지를 표시하고 표시됩니다. 텍스트는 홈페이지에 대해 정적이거나 컨텍스트 기반 트리거를 위한 호스트 애플리케이션 컨텍스트가 필요합니다.

목표

  • 환경을 설정합니다.
  • 스크립트를 설정합니다.
  • 스크립트를 실행합니다.

기본 요건

이 샘플을 사용하려면 다음과 같은 기본 요건이 필요합니다.

  • Google 계정 (Google Workspace 계정은 관리자의 승인이 필요함)
  • 인터넷에 액세스할 수 있는 웹브라우저

  • Google Cloud 프로젝트

환경 설정

Google Cloud 콘솔에서 Cloud 프로젝트 열기

아직 열려 있지 않으면 사용할 Cloud 프로젝트를 엽니다. 다음을 참조하세요.

  1. Google Cloud 콘솔에서 프로젝트 선택 페이지로 이동합니다.

    Cloud 프로젝트 선택

  2. 사용할 Google Cloud 프로젝트를 선택합니다. 또는 프로젝트 만들기를 클릭하고 화면에 표시된 안내를 따릅니다. Google Cloud 프로젝트를 만드는 경우 프로젝트에 결제를 사용 설정해야 할 수도 있습니다.

Google Workspace 부가기능을 사용하려면 동의 화면을 구성해야 합니다. 구성 부가기능의 OAuth 동의 화면에는 Google 사용자에게 표시됩니다.

  1. Google Cloud 콘솔에서 메뉴 로 이동합니다. > API 및 서비스 > OAuth 동의 화면.

    OAuth 동의 화면으로 이동

  2. 사용자 유형으로 내부를 선택한 다음 만들기를 클릭합니다.
  3. 앱 등록 양식을 작성한 다음 저장하고 계속하기를 클릭합니다.
  4. 지금은 범위 추가를 건너뛰고 저장 후 계속을 클릭할 수 있습니다. 나중에 외부에서 사용하기 위해 앱을 만들 때 Google Workspace 조직에서 사용하는 경우 사용자 유형외부로 변경한 다음 앱에 필요한 승인 범위를 추가합니다.

  5. 앱 등록 요약을 검토합니다. 변경하려면 수정을 클릭합니다. 앱이 등록이 확인되면 대시보드로 돌아가기를 클릭합니다.

스크립트 설정

Apps Script 프로젝트 만들기

  1. 새 Apps Script 프로젝트를 만들려면 다음으로 이동하세요. script.new.
  2. 제목 없는 프로젝트를 클릭합니다.
  3. Apps Script 프로젝트의 이름을 Cats로 변경하고 이름 바꾸기를 클릭합니다.
  4. Code.gs 파일 옆의 더보기 를 클릭합니다. > 이름 바꾸기를 탭합니다. 파일 이름을 Common로 지정합니다.
  5. 파일 추가 를 클릭합니다. > 스크립트를 선택합니다. 파일 이름을 Gmail로 지정합니다.
  6. 5단계를 반복하여 CalendarDrive라는 스크립트 파일을 2개 더 만듭니다. 완료되면 4개의 별도 스크립트 파일이 생성됩니다.
  7. 각 파일의 내용을 해당하는 다음 코드로 바꿉니다.

    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);
    }
    
      

  8. 프로젝트 설정 프로젝트 설정 아이콘를 클릭합니다.

  9. Show 'appsscript.json' 체크박스를 선택합니다. 매니페스트 파일을 선택합니다.

  10. 편집기 를 클릭합니다.

  11. 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 프로젝트 번호를 복사합니다.

  1. Google Cloud 콘솔에서 메뉴 로 이동합니다. &gt; IAM 및 관리자 &gt; 설정을 탭합니다.

    IAM & 관리자 설정으로 이동

  2. 프로젝트 번호 필드에 값을 복사합니다.

Apps Script 프로젝트의 Cloud 프로젝트 설정

  1. Apps Script 프로젝트에서 프로젝트 설정 프로젝트 설정 아이콘을 클릭합니다.
  2. Google Cloud Platform(GCP) 프로젝트에서 프로젝트 변경을 클릭합니다.
  3. GCP 프로젝트 번호에 Google Cloud 프로젝트 번호를 붙여넣습니다.
  4. 프로젝트 설정을 클릭합니다.

테스트 배포 설치

  1. Apps Script 프로젝트에서 편집기를 클릭합니다. 입니다.
  2. 배포 &gt; 배포 테스트를 클릭합니다.
  3. 설치 &gt; 완료를 클릭합니다.

스크립트 실행

  1. Gmail로 이동합니다.
  2. 부가기능을 열려면 오른쪽 측면 패널에서 고양이 .
  3. 메시지가 표시되면 부가기능을 승인합니다.
  4. 부가기능에 고양이 이미지와 텍스트가 표시됩니다. 변경하려면 고양이 변경을 클릭합니다.
  5. 부가기능이 열려 있는 상태에서 이메일을 열면 이미지가 새로고침되고 텍스트가 이메일의 제목 줄로 변경됩니다. (너무 길면 잘립니다.)

Calendar 및 드라이브. 새 버전을 사용하기 위해 호스트 앱에서 사용할 수 있습니다.

다음 단계