מענה לתקריות שימוש ב-Google Chat, ב-Vertex AI וב-Apps Script

במדריך הזה נסביר איך ליצור אפליקציית Google Chat שמגיבה לתקריות בזמן אמת. כשמגיבים לאירוע, האפליקציה יוצרת ומאכלסת מרחב משותף ב-Chat, עוזרת לפתור תקריות עם הודעות, פקודות לוכסן ותיבות דו-שיח, ומשתמשת ב-AI כדי לסכם את התגובה לתקרית במסמך ב-Google Docs.

אירוע הוא אירוע שדורש תשומת לב מיידית של צוות אנשים כדי לטפל בו. דוגמאות לאירועים:

  • בפלטפורמת 'ניהול קשרי לקוחות' (CRM), נוצר בקשת תמיכה תלוית זמן, וצוות השירות נדרש לשתף פעולה כדי לפתור את הבעיה.
  • המערכת עוברת למצב אופליין, ונשלחת התראה לקבוצה של מהנדסי Site Reliability (SRE) כדי שיוכלו לעבוד יחד כדי להחזיר אותה למצב אונליין.
  • מתרחשת רעידת אדמה בעוצמה גבוהה, ועובדי שירותי החירום צריכים לתאם את התגובה שלהם.

במדריך הזה, התרעת אירוע מתחילה כשמישהו מדווח על האירוע בלחיצה על לחצן מתוך דף אינטרנט. דף האינטרנט מדמה תקרית ומבקשים מהמשתמשים להזין פרטים בסיסיים לתקרית: שם, תיאור וכתובות אימייל של המשיבים.

כדי לראות את אפליקציית Chat לניהול אירועי אבטחה:

  • האתר שבו מתחיל התקרית.
    איור 1. האתר שבו אפשר לדווח על תקרית.
  • התראה על כך שנוצר מרחב משותף ב-Chat.
    איור 2. התראה על כך שנוצר מרחב משותף ב-Chat.
  • מרחב משותף ב-Chat של התגובה לאירוע.
    איור 3. מרחב משותף ב-Chat של התגובה לאירוע.
  • פתרון התקרית באמצעות פקודה של שורת הפקודות.
    איור 4. לסיים את התקרית באמצעות פקודה דרך שורת הפקודות.
  • תיבת דו-שיח לפתרון אירוע.
    איור 5. תיבת דו-שיח לפתרון אירוע.
  • מסמך Google Docs לפתרון תקרית שותף במרחב המשותף.
    איור 6. מסמך Google Docs לפתרון התקרית שותף במרחב המשותף.
  • סיכום האירוע ב-Google Docs עם סיכום של בעיית ה-AI.
    איור 7. מסמך ב-Google Docs בנושא סיכום אירוע ה-AI.

דרישות מוקדמות

אם אתם רוצים להפעיל בארגון את הדרישות המוקדמות הבאות, תוכלו לבקש מהאדמין ב-Google Workspace להפעיל אותן:

  • חשבון Google Workspace עם גישה ל-Google Chat.
  • להפעיל את הספרייה (שיתוף אנשי קשר) ב-Google Workspace. אפליקציית התקרית משתמשת בספרייה כדי לחפש את הפרטים ליצירת קשר של המשיבים, כמו השם וכתובת האימייל שלהם. האנשים שזקוקים לאירועים צריכים להיות משתמשים עם חשבון Google Chat בארגון שלהם ב-Google Workspace.

מטרות

  • אתם יכולים לפתח אפליקציית Chat שמגיבה לאירועים.
  • כדי לעזור למשתמשים להגיב לאירועים:
    • ליצור אזורי תגובה לאירועים.
    • פרסום הודעות שמסכמת אירועים ותגובות.
    • תמיכה בשיתופי פעולה בעזרת תכונות אינטראקטיביות באפליקציית Chat.
  • מסכמים שיחות ופתרונות באמצעות Vertex AI.

ארכיטקטורה

בתרשים הבא מוצגת הארכיטקטורה של המשאבים ב-Google Workspace וב-Google Cloud שבהם משתמשת אפליקציית Google Chat בתגובה לתקרית.

הארכיטקטורה של אפליקציית Google Chat של התגובה לאירוע

בארכיטקטורה אפשר לראות איך אפליקציית Google Chat מעבדת את האירוע ואת הטיפול בו.

  1. משתמש מתחיל אירוע מאתר חיצוני שמתארח ב-Apps Script.

  2. האתר שולח בקשת HTTP אסינכרונית לאפליקציית Google Chat, שמתארחת גם ב-Apps Script.

  3. אפליקציית Google Chat מעבדת את הבקשה בתגובה לתקרית:

    1. שירות Apps Script Admin SDK מקבל מידע על חברי צוות, כמו מזהה משתמש וכתובת אימייל.

    2. בזכות סדרה של בקשות HTTP ל-Chat API באמצעות שירות הצ'אט המתקדם של Apps Script, אפליקציית Google Chat בתגובה לתקרית יוצרת מרחב משותף ב-Chat, מאכלסת אותו בחברי הצוות ושולחת הודעה למרחב המשותף.

  4. חברי הצוות דנים בתקרית במרחב המשותף ב-Chat.

  5. אחד מחברי הצוות מפעיל פקודה דרך שורת הפקודות כדי לסמן פתרון לתקרית.

    1. קריאת HTTP ל-Chat API באמצעות Apps Script שירות הצ'אט המתקדם מציג את כל ההודעות במרחב המשותף ב-Chat.

    2. Vertex AI מקבל את ההודעות הרשומות ויוצר סיכום.

    3. שירות Apps Script DocumentApp יוצר מסמך ב-Docs ומוסיף את הסיכום של Vertex AI למסמך.

    4. התגובה לתקרית מ-Google Chat קוראת ל-Chat API כדי לשלוח הודעה שמשותפת קישור לסיכום של המסמך ב-Docs.

מכינים את הסביבה

בקטע הזה נסביר איך ליצור ולהגדיר פרויקט ב-Google Cloud לאפליקציית Chat.

יצירת פרויקט של Google Cloud

מסוף Google Cloud

  1. במסוף Google Cloud, נכנסים לתפריט > IAM & Admin > Create a Project.

    כניסה לדף Create a Project

  2. בשדה Project Name (שם הפרויקט), מזינים שם תיאורי לפרויקט.

    אם רוצים לערוך את Project ID, לוחצים על Edit. לא ניתן לשנות את מזהה הפרויקט אחרי שיוצרים את הפרויקט, לכן כדאי לבחור מזהה שמתאים לצרכים שלכם לכל משך החיים של הפרויקט.

  3. בשדה Location לוחצים על Browse כדי להציג את המיקומים הפוטנציאליים של הפרויקט. לאחר מכן לוחצים על בחירה.
  4. לוחצים על יצירה. נכנסים לדף Dashboard במסוף Google Cloud ותוך כמה דקות נוצר הפרויקט.

CLI של gcloud

נכנסים ל-CLI של Google Cloud ('gcloud'): באחת מסביבות הפיתוח הבאות:

  • Cloud Shell: כדי להשתמש בטרמינל אונליין שבו כבר מוגדר ה-CLI של gcloud, צריך להפעיל את Cloud Shell.
    הפעלת Cloud Shell
  • Local Shell: כדי להשתמש בסביבת פיתוח מקומית צריך להתקין וinitialize את ה-CLI של gcloud.
    כדי ליצור פרויקט ב-Cloud, משתמשים בפקודה 'gcloud projects create':
    gcloud projects create PROJECT_ID
    מחליפים את PROJECT_ID על ידי הגדרת המזהה של הפרויקט שרוצים ליצור.

הפעלת החיוב בפרויקט ב-Cloud

מסוף Google Cloud

  1. נכנסים לקטע Billing במסוף Google Cloud. לוחצים על תפריט > חיוב > הפרויקטים שלי.

    כניסה לדף Billing for My Projects

  2. בקטע Select an Organization, בוחרים את הארגון שמשויך לפרויקט ב-Google Cloud.
  3. בשורת הפרויקט, פותחים את התפריט Actions (), לוחצים על Change billing ובוחרים את החשבון לחיוב ב-Cloud.
  4. לוחצים על Set account.

CLI של gcloud

  1. כדי להציג את החשבונות הזמינים לחיוב, מריצים את הפקודה:
    gcloud billing accounts list
  2. מקשרים את החשבון לחיוב לפרויקט ב-Google Cloud:
    gcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID

    מחליפים את מה שכתוב בשדות הבאים:

    • PROJECT_ID - מזהה הפרויקט של הפרויקט ב-Cloud שבו רוצים להפעיל את החיוב.
    • BILLING_ACCOUNT_ID הוא מספר החשבון לחיוב שרוצים לקשר לפרויקט ב-Google Cloud.

הפעלת ממשקי ה-API

מסוף Google Cloud

  1. במסוף Google Cloud, מפעילים את Google Chat API, את Google Docs API, את Admin SDK API ואת Vertex AI API.

    הפעלת ממשקי ה-API

  2. מוודאים שאתם מפעילים את ממשקי ה-API בפרויקט הנכון ב-Cloud, ולוחצים על Next.

  3. מוודאים שאתם מפעילים את ממשקי ה-API הנכונים ולוחצים על Enable (הפעלה).

CLI של gcloud

  1. אם צריך, מגדירים את הפרויקט הנוכחי ב-Cloud לפרויקט שיצרתם באמצעות הפקודה gcloud config set project:

    gcloud config set project PROJECT_ID
    

    מחליפים את PROJECT_ID ב-Project ID של הפרויקט שיצרתם ב-Cloud.

  2. מפעילים את Google Chat API, Google Docs API, Admin SDK API ו-Vertex AI API באמצעות הפקודה gcloud services enable:

    gcloud services enable chat.googleapis.com docs.googleapis.com admin.googleapis.com aiplatform.googleapis.com
    

הגדרת אימות והרשאה

האימות וההרשאה מאפשרים לאפליקציית Chat לגשת למשאבים ב-Google Workspace וב-Google Cloud כדי לעבד תגובות לאירועים.

במדריך הזה מפרסמים את האפליקציה באופן פנימי, כדי שמותר להשתמש בה בפרטי placeholder. לפני פרסום האפליקציה באופן חיצוני, צריך להחליף את ה-placeholder של פרטים במידע אמיתי במסך ההסכמה.

  1. במסוף Google Cloud, נכנסים אל תפריט > APIs & Services > מסך הסכמה של OAuth.

    מעבר למסך ההסכמה של OAuth

  2. בקטע סוג משתמש, בוחרים באפשרות פנימי ולוחצים על יצירה.

  3. בשם האפליקציה, מקלידים Incident Management.

  4. בקטע User support email, בוחרים את כתובת האימייל שלכם או את קבוצת Google המתאימה.

  5. מזינים את כתובת האימייל בקטע פרטים ליצירת קשר עם המפתח.

  6. לוחצים על שמירה והמשך.

  7. לוחצים על הוספה או הסרה של היקפים. תופיע חלונית עם רשימה של היקפי ההרשאות לכל API שהפעלתם בפרויקט ב-Cloud.

  8. בקטע הוספה ידנית של היקפי הרשאות, מדביקים את ההיקפים הבאים:

    • https://www.googleapis.com/auth/chat.spaces.create
    • https://www.googleapis.com/auth/chat.memberships
    • https://www.googleapis.com/auth/chat.memberships.app
    • https://www.googleapis.com/auth/chat.messages
    • https://www.googleapis.com/auth/documents
    • https://www.googleapis.com/auth/admin.directory.user.readonly
    • https://www.googleapis.com/auth/script.external_request
    • https://www.googleapis.com/auth/userinfo.email
    • https://www.googleapis.com/auth/cloud-platform
  9. לוחצים על הוספה לטבלה.

  10. לוחצים על עדכון.

  11. לוחצים על שמירה והמשך.

  12. בודקים את סיכום רישום האפליקציה ואז לוחצים על Back to Dashboard (חזרה למרכז השליטה).

יצירה ופריסה של אפליקציית Chat

בחלק הבא מעתיקים ומעדכנים פרויקט שלם של Apps Script שמכיל את כל קוד האפליקציה הנדרש לאפליקציית Chat, כך שלא צריך להעתיק ולהדביק כל קובץ.

חלק מהפונקציות כוללות קווים תחתונים בסוף השמות, למשל processSlashCommand_() מה-ChatApp.gs. הקו התחתון מסתיר את הפונקציה מדף האינטרנט של אתחול התקרית כשהוא פתוח בדפדפן. למידע נוסף קראו את המאמר פונקציות פרטיות.

ב-Apps Script יש תמיכה בשני סוגי קבצים: .gs סקריפטים ו-.html קבצים. כדי לעמוד בתמיכה הזו, ה-JavaScript של האפליקציה בצד הלקוח כלול בתגי <script /> וה-CSS שלה כלול בתוך תגי <style /> בתוך קובץ HTML.

אפשר גם להציג את הפרויקט כולו ב-GitHub.

הצגת הקוד ב-GitHub

הנה סקירה כללית של כל קובץ:

Consts.gs

מגדיר קבועים שקובצי קוד אחרים מפנים אליהם, כולל מזהה הפרויקט ב-Cloud, מזהה המיקום ב-Vertex AI ומזהה הפקודה של הלוכסן לסגירת אירוע.

הצגת הקוד של Consts.gs

apps-script/incident-response/Consts.gs
const PROJECT_ID = 'replace-with-your-project-id';
const VERTEX_AI_LOCATION_ID = 'us-central1';
const CLOSE_INCIDENT_COMMAND_ID = 1;
ChatApp.gs

טיפול באירועי אינטראקציה ב-Chat, כולל הודעות, לחיצות על כרטיסים, פקודות לוכסן ותיבות דו-שיח. כדי להגיב לפקודה /closeIncident באמצעות הלוכסן, פותחים תיבת דו-שיח שבה אפשר לאסוף פרטים על פתרון התקרית. קריאת ההודעות במרחב המשותף באמצעות קריאה ל-method spaces.messages.list ב-Chat API. מקבל מזהי משתמשים באמצעות שירות Admin SDK Directory ב-Apps Script.

הצגת הקוד של ChatApp.gs

apps-script/incident-response/ChatApp.gs
/**
 * Responds to a MESSAGE event in Google Chat.
 *
 * This app only responds to a slash command with the ID 1 ("/closeIncident").
 * It will respond to any other message with a simple "Hello" text message.
 *
 * @param {Object} event the event object from Google Chat
 */
function onMessage(event) {
  if (event.message.slashCommand) {
    return processSlashCommand_(event);
  }
  return { "text": "Hello from Incident Response app!" };
}

/**
 * Responds to a CARD_CLICKED event in Google Chat.
 *
 * This app only responds to one kind of dialog (Close Incident).
 *
 * @param {Object} event the event object from Google Chat
 */
function onCardClick(event) {
  if (event.isDialogEvent) {
    if (event.dialogEventType == 'SUBMIT_DIALOG') {
      return processSubmitDialog_(event);
    }
    return {
      actionResponse: {
        type: "DIALOG",
        dialogAction: {
          actionStatus: "OK"
        }
      }
    };
  }
}

/**
 * Responds to a MESSAGE event with a Slash command in Google Chat.
 *
 * This app only responds to a slash command with the ID 1 ("/closeIncident")
 * by returning a Dialog.
 *
 * @param {Object} event the event object from Google Chat
 */
function processSlashCommand_(event) {
  if (event.message.slashCommand.commandId != CLOSE_INCIDENT_COMMAND_ID) {
    return {
      "text": "Command not recognized. Use the command `/closeIncident` to close the incident managed by this space."
    };
  }
  const sections = [
    {
      header: "Close Incident",
      widgets: [
        {
          textInput: {
            label: "Please describe the incident resolution",
            type: "MULTIPLE_LINE",
            name: "description"
          }
        },
        {
          buttonList: {
            buttons: [
              {
                text: "Close Incident",
                onClick: {
                  action: {
                    function: "closeIncident"
                  }
                }
              }
            ]
          }
        }
      ]
    }
  ];
  return {
    actionResponse: {
      type: "DIALOG",
      dialogAction: {
        dialog: {
          body: {
            sections,
          }
        }
      }
    }
  };
}

/**
 * Responds to a CARD_CLICKED event with a Dialog submission in Google Chat.
 *
 * This app only responds to one kind of dialog (Close Incident).
 * It creates a Doc with a summary of the incident information and posts a message
 * to the space with a link to the Doc.
 *
 * @param {Object} event the event object from Google Chat
 */
function processSubmitDialog_(event) {
  const resolution = event.common.formInputs.description[""].stringInputs.value[0];
  const chatHistory = concatenateAllSpaceMessages_(event.space.name);
  const chatSummary = summarizeChatHistory_(chatHistory);
  const docUrl = createDoc_(event.space.displayName, resolution, chatHistory, chatSummary);
  return {
    actionResponse: {
      type: "NEW_MESSAGE",
    },
    text: `Incident closed with the following resolution: ${resolution}\n\nHere is the automatically generated post-mortem:\n${docUrl}`
  };
}

/**
 * Lists all the messages in the Chat space, then concatenate all of them into
 * a single text containing the full Chat history.
 *
 * For simplicity for this demo, it only fetches the first 100 messages.
 *
 * Messages with slash commands are filtered out, so the returned history will
 * contain only the conversations between users and not app command invocations.
 *
 * @return {string} a text containing all the messages in the space in the format:
 *          Sender's name: Message
 */
function concatenateAllSpaceMessages_(spaceName) {
  // Call Chat API method spaces.messages.list
  const response = Chat.Spaces.Messages.list(spaceName, { 'pageSize': 100 });
  const messages = response.messages;
  // Fetch the display names of the message senders and returns a text
  // concatenating all the messages.
  let userMap = new Map();
  return messages
    .filter(message => message.slashCommand === undefined)
    .map(message => `${getUserDisplayName_(userMap, message.sender.name)}: ${message.text}`)
    .join('\n');
}

/**
 * Obtains the display name of a user by using the Admin Directory API.
 *
 * The fetched display name is cached in the provided map, so we only call the API
 * once per user.
 *
 * If the user does not have a display name, then the full name is used.
 *
 * @param {Map} userMap a map containing the display names previously fetched
 * @param {string} userName the resource name of the user
 * @return {string} the user's display name
 */
function getUserDisplayName_(userMap, userName) {
  if (userMap.has(userName)) {
    return userMap.get(userName);
  }
  let displayName = 'Unknown User';
  try {
    const user = AdminDirectory.Users.get(
      userName.replace("users/", ""),
      { projection: 'BASIC', viewType: 'domain_public' });
    displayName = user.name.displayName ? user.name.displayName : user.name.fullName;
  } catch (e) {
    // Ignore error if the API call fails (for example, because it's an
    // out-of-domain user or Chat app)) and just use 'Unknown User'.
  }
  userMap.set(userName, displayName);
  return displayName;
}
ChatSpaceCreator.gs

מקבל את נתוני הטופס שהמשתמשים מזינים בדף האינטרנט של אתחול התקרית, ומשתמש בו כדי להגדיר מרחב ב-Chat על ידי יצירה ואכלוס שלו, ואז מפרסם הודעה בקשר לאירוע.

הצגת הקוד של ChatSpaceCreator.gs

apps-script/incident-response/ChatSpaceCreator.gs
/**
 * Creates a space in Google Chat with the provided title and members, and posts an
 * initial message to it.
 *
 * @param {Object} formData the data submitted by the user. It should contain the fields
 *                          title, description, and users.
 * @return {string} the resource name of the new space.
 */
function createChatSpace(formData) {
  const users = formData.users.trim().length > 0 ? formData.users.split(',') : [];
  const spaceName = setUpSpace_(formData.title, users);
  addAppToSpace_(spaceName);
  createMessage_(spaceName, formData.description);
  return spaceName;
}

/**
 * Creates a space in Google Chat with the provided display name and members.
 *
 * @return {string} the resource name of the new space.
 */
function setUpSpace_(displayName, users) {
  const memberships = users.map(email => ({
    member: {
      name: `users/${email}`,
      type: "HUMAN"
    }
  }));
  const request = {
    space: {
      displayName: displayName,
      spaceType: "SPACE",
      externalUserAllowed: true
    },
    memberships: memberships
  };
  // Call Chat API method spaces.setup
  const space = Chat.Spaces.setup(request);
  return space.name;
}

/**
 * Adds this Chat app to the space.
 *
 * @return {string} the resource name of the new membership.
 */
function addAppToSpace_(spaceName) {
  const request = {
    member: {
      name: "users/app",
      type: "BOT"
    }
  };
  // Call Chat API method spaces.members.create
  const membership = Chat.Spaces.Members.create(request, spaceName);
  return membership.name;
}

/**
 * Posts a text message to the space on behalf of the user.
 *
 * @return {string} the resource name of the new message.
 */
function createMessage_(spaceName, text) {
  const request = {
    text: text
  };
  // Call Chat API method spaces.messages.create
  const message = Chat.Spaces.Messages.create(request, spaceName);
  return message.name;
}
DocsApi.gs

קורא ל-Google Docs API כדי ליצור מסמך Google Docs ב-Google Drive של המשתמש וכותב למסמך סיכום של פרטי האירוע שנוצר ב-VertexAiApi.gs.

הצגת הקוד של DocsApi.gs

apps-script/incident-response/DocsApi.gs
/**
 * Creates a Doc in the user's Google Drive and writes a summary of the incident information to it.
 *
 * @param {string} title The title of the incident
 * @param {string} resolution Incident resolution described by the user
 * @param {string} chatHistory The whole Chat history be included in the document
 * @param {string} chatSummary A summary of the Chat conversation to be included in the document
 * @return {string} the URL of the created Doc
 */
function createDoc_(title, resolution, chatHistory, chatSummary) {
  let doc = DocumentApp.create(title);
  let body = doc.getBody();
  body.appendParagraph(`Post-Mortem: ${title}`).setHeading(DocumentApp.ParagraphHeading.TITLE);
  body.appendParagraph("Resolution").setHeading(DocumentApp.ParagraphHeading.HEADING1);
  body.appendParagraph(resolution);
  body.appendParagraph("Summary of the conversation").setHeading(DocumentApp.ParagraphHeading.HEADING1);
  body.appendParagraph(chatSummary);
  body.appendParagraph("Full Chat history").setHeading(DocumentApp.ParagraphHeading.HEADING1);
  body.appendParagraph(chatHistory);
  return doc.getUrl();
}
VertexAiApi.gs

מסכם את השיחה במרחב המשותף ב-Chat באמצעות Vertex AI. הסיכום הזה מתפרסם במסמך שנוצר במיוחד ב-DocsAPI.gs.

הצגת הקוד של VertexAiApi.gs

apps-script/incident-response/VertexAiApi.gs
/**
 * Summarizes a Chat conversation using the Vertex AI text prediction API.
 *
 * @param {string} chatHistory The Chat history that will be summarized.
 * @return {string} The content from the text prediction response.
 */
function summarizeChatHistory_(chatHistory) {
  const prompt =
    "Summarize the following conversation between Engineers resolving an incident"
      + " in a few sentences. Use only the information from the conversation.\n\n"
      + chatHistory;
  const request = {
    instances: [
      { prompt: prompt }
    ],
    parameters: {
      temperature: 0.2,
      maxOutputTokens: 256,
      topK: 40,
      topP: 0.95
    }
  }
  const fetchOptions = {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() },
    contentType: 'application/json',
    payload: JSON.stringify(request)
  }
  const response = UrlFetchApp.fetch(
    `https://${VERTEX_AI_LOCATION_ID}-aiplatform.googleapis.com/v1`
      + `/projects/${PROJECT_ID}/locations/${VERTEX_AI_LOCATION_ID}`
      + "/publishers/google/models/text-bison:predict",
    fetchOptions);
  const payload = JSON.parse(response.getContentText());
  return payload.predictions[0].content;
}
WebController.gs

שירות באתר לאתחול התקרית.

הצגת הקוד של WebController.gs

apps-script/incident-response/WebController.gs
/**
 * Serves the web page from Index.html.
 */
function doGet() {
  return HtmlService
    .createTemplateFromFile('Index')
    .evaluate();
}

/**
 * Serves the web content from the specified filename.
 */
function include(filename) {
  return HtmlService
    .createHtmlOutputFromFile(filename)
    .getContent();
}

/**
 * Returns the email address of the user running the script.
 */
function getUserEmail() {
  return Session.getActiveUser().getEmail();
}
Index.html

קוד ה-HTML של האתר לאתחול התקרית.

הצגת הקוד של Index.html

apps-script/incident-response/Index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>
    <?!= include('Stylesheet'); ?>
  </head>
  <body>
    <div class="container">
      <div class="content">
        <h1>Incident Manager</h1>
        <form id="incident-form" onsubmit="handleFormSubmit(this)">
          <div id="form">
            <p>
              <label for="title">Incident title</label><br/>
              <input type="text" name="title" id="title" />
            </p>
            <p>
              <label for="users">Incident responders</label><br/>
              <small>
                Please enter a comma-separated list of email addresses of the users
                that should be added to the space.
                Do not include <?= getUserEmail() ?> as it will be added automatically.
              </small><br/>
              <input type="text" name="users" id="users" />
            </p>
            <p>
              <label for="description">Initial message</label></br>
              <small>This message will be posted after the space is created.</small><br/>
              <textarea name="description" id="description"></textarea>
            </p>
            <p class="text-center">
              <input type="submit" value="CREATE CHAT SPACE" />
            </p>
          </div>
          <div id="output" class="hidden"></div>
          <div id="clear" class="hidden">
            <input type="reset" value="CREATE ANOTHER INCIDENT" onclick="onReset()" />
          </div>
        </form>
      </div>
    </div>
    <?!= include('JavaScript'); ?>
  </body>
</html>
JavaScript.html

טיפול בהתנהגות של הטופס, כולל שליחות, שגיאות ומחיקה באתר לאתחול התקרית. הוא כלול ב-Index.html על ידי הפונקציה include בהתאמה אישית ב-WebController.gs.

הצגת הקוד של JavaScript.html

apps-script/incident-response/JavaScript.html
<script>
  var formDiv = document.getElementById('form');
  var outputDiv = document.getElementById('output');
  var clearDiv = document.getElementById('clear');

  function handleFormSubmit(formObject) {
    event.preventDefault();
    outputDiv.innerHTML = 'Please wait while we create the space...';
    hide(formDiv);
    show(outputDiv);
    google.script.run
      .withSuccessHandler(updateOutput)
      .withFailureHandler(onFailure)
      .createChatSpace(formObject);
  }

  function updateOutput(response) {
    var spaceId = response.replace('spaces/', '');
    outputDiv.innerHTML =
      '<p>Space created!</p><p><a href="https://mail.google.com/chat/#chat/space/'
        + spaceId
        + '" target="_blank">Open space</a></p>';
    show(outputDiv);
    show(clearDiv);
  }

  function onFailure(error) {
    outputDiv.innerHTML = 'ERROR: ' + error.message;
    outputDiv.classList.add('error');
    show(outputDiv);
    show(clearDiv);
  }

  function onReset() {
    outputDiv.innerHTML = '';
    outputDiv.classList.remove('error');
    show(formDiv);
    hide(outputDiv);
    hide(clearDiv);
  }

  function hide(element) {
    element.classList.add('hidden');
  }

  function show(element) {
    element.classList.remove('hidden');
  }
</script>
Stylesheet.html

שירות ה-CSS של האתר לאתחול התקרית. הוא כלול ב-Index.html על ידי הפונקציה include בהתאמה אישית ב-WebController.gs.

הצגת הקוד של Stylesheet.html

apps-script/incident-response/Stylesheet.html
<style>
  * {
    box-sizing: border-box;
  }
  body {
    font-family: Roboto, Arial, Helvetica, sans-serif;
  }
  div.container {
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    top: 0; bottom: 0; left: 0; right: 0;
  }
  div.content {
    width: 80%;
    max-width: 1000px;
    padding: 1rem;
    border: 1px solid #999;
    border-radius: 0.25rem;
    box-shadow: 0 2px 2px 0 rgba(66, 66, 66, 0.08), 0 2px 4px 2px rgba(66, 66, 66, 0.16);
  }
  h1 {
    text-align: center;
    padding-bottom: 1rem;
    margin: 0 -1rem 1rem -1rem;
    border-bottom: 1px solid #999;
  }
 #output {
    text-align: center;
    min-height: 250px;
  }
  div#clear {
    text-align: center;
    padding-top: 1rem;
    margin: 1rem -1rem 0 -1rem;
    border-top: 1px solid #999;
  }
  input[type=text], textarea {
    width: 100%;
    padding: 1rem 0.5rem;
    margin: 0.5rem 0;
    border: 0;
    border-bottom: 1px solid #999;
    background-color: #f0f0f0;
  }
  textarea {
    height: 5rem;
  }
  small {
    color: #999;
  }
  input[type=submit], input[type=reset] {
    padding: 1rem;
    border: none;
    background-color: #6200ee;
    color: #fff;
    border-radius: 0.25rem;
    width: 25%;
  }
  .hidden {
    display: none;
  }
  .text-center {
    text-align: center;
  }
  .error {
    color: red;
  }
</style>

איתור המספר והמזהה של הפרויקט ב-Cloud

  1. נכנסים לפרויקט Cloud במסוף Google Cloud.

    כניסה למסוף Google Cloud

  2. לוחצים על Settings and Utilities (הגדרות וכלי תחזוקה) > Project settings (הגדרות הפרויקט).

  3. בודקים את הערכים בשדות Project number ו-Project ID. תוכלו להשתמש בהם בקטעים הבאים.

יצירת פרויקט Apps Script

כדי ליצור פרויקט Apps Script ולקשר אותו לפרויקט ב-Cloud:

  1. לוחצים על הלחצן הבא כדי לפתוח את פרויקט Apps Script, תגובה לאירועים באמצעות Google Chat.
    פתיחת הפרויקט
  2. לוחצים על סקירה כללית.
  3. בדף הסקירה הכללית, לוחצים על הסמל ליצירת עותק יצירת עותק.
  4. צריך לתת שם לעותק של פרויקט Apps Script:

    1. לוחצים על עותק של התגובה לאירועים ב-Google Chat.

    2. בשדה שם הפרויקט, מקלידים Incident Management Chat app.

    3. לוחצים על Rename.

  5. בעותק של פרויקט Apps Script, נכנסים לקובץ Consts.gs ומחליפים את YOUR_PROJECT_ID במזהה של הפרויקט ב-Cloud.

הגדרת הפרויקט בענן של פרויקט Apps Script

  1. בפרויקט Apps Script, לוחצים על הסמל של הגדרות הפרויקט Project Settings.
  2. בקטע פרויקט Google Cloud Platform (GCP), לוחצים על שינוי פרויקט.
  3. בקטע מספר פרויקט GCP, מדביקים את מספר הפרויקט ב-Cloud.
  4. לוחצים על Set project. הפרויקט ב-Cloud ופרויקט Apps Script מחוברים עכשיו.

יצירת פריסה של Apps Script

עכשיו, אחרי שכל הקוד מוכן, צריך לפרוס את פרויקט Apps Script. משתמשים במזהה הפריסה כשמגדירים את אפליקציית Chat ב-Google Cloud.

  1. ב-Apps Script, פותחים את הפרויקט של אפליקציית התגובה לאירוע.

    כניסה אל Apps Script

  2. לוחצים על פריסה > פריסה חדשה.

  3. אם האפשרויות Add-on ו-Web app לא מסומנות, לצד Select type לוחצים על סוגי הפריסה הסמל של הגדרות הפרויקט ובוחרים את Add-on ו-Web app.

  4. בקטע תיאור, מזינים תיאור של הגרסה הזו, למשל Complete version of incident management app.

  5. בקטע ביצוע בתור, בוחרים באפשרות משתמש ניגש לאפליקציית האינטרנט

  6. בקטע למי יש גישה, בוחרים באפשרות כל אחד בארגון ב-Workspace, כאשר 'הארגון שלך ב-Workspace' הוא השם של ארגון Google Workspace.

  7. לוחצים על פריסה. ב-Apps Script מדווח על פריסה מוצלחת ומספק מזהה פריסה וכתובת URL לדף האינטרנט של האתחול של התקרית.

  8. רשמו לעצמכם את כתובת ה-URL של אפליקציית האינטרנט כדי להיכנס אליה מאוחר יותר כשתתחילו אירוע. מעתיקים את Deployment ID. המזהה הזה משמש להגדרה של אפליקציית Chat במסוף Google Cloud.

  9. לוחצים על סיום.

הגדרת האפליקציה של Chat במסוף Google Cloud

בקטע הזה נסביר איך מגדירים את Google Chat API במסוף Google Cloud עם מידע על אפליקציית Chat, כולל מזהה הפריסה שיצרתם מפרויקט Apps Script.

  1. במסוף Google Cloud, לוחצים על תפריט > מוצרים נוספים > Google Workspace > ספריית מוצרים > Google Chat API > ניהול > הגדרות.

    לדף ההגדרות של Chat API

  2. בשם האפליקציה, מקלידים Incident Management.

  3. בכתובת ה-URL של הדמות, מקלידים https://developers.google.com/chat/images/quickstart-app-avatar.png.

  4. בתיאור, מקלידים Responds to incidents..

  5. מעבירים את המתג הפעלת תכונות אינטראקטיביות למצב פעיל.

  6. בקטע פונקציונליות, בוחרים באפשרות קבלת הודעות אישיות או הצטרפות למרחבים משותפים ולשיחות קבוצתיות.

  7. בקטע הגדרות חיבור, בוחרים באפשרות פרויקט Apps Script.

  8. בקטע Deployment ID, מדביקים את מזהה הפריסה של Apps Script שהעתקתם קודם מפריסת הפרויקט של Apps Script.

  9. רושמים פקודת לוכסן שמשתמשת באפליקציית Chat שמוטמעת במלואה:

    1. בקטע פקודות Slash, לוחצים על Add a slash command (הוספת פקודה של לוכסן).

    2. בשדה שם, מקלידים /closeIncident.

    3. בקטע Command ID, מקלידים 1.

    4. בתיאור, מקלידים Closes the incident being discussed in the space.

    5. בוחרים באפשרות פתיחה של תיבת דו-שיח.

    6. לוחצים על סיום. פקודת הלוכסן רשומה ומופיעה ברשימה.

  10. בקטע חשיפה, בוחרים באפשרות הגדרת האפליקציה הזו ל-Chat למשתמשים ספציפיים ולקבוצות ספציפיות בדומיין שלך ב-Workspace ומזינים את כתובת האימייל.

  11. בקטע Logs (יומנים), בוחרים באפשרות Log errors to Logging.

  12. לוחצים על שמירה. תופיע הודעה על שמירת הגדרות, כלומר האפליקציה מוכנה לבדיקה.

בדיקת האפליקציה של Chat

כדי לבדוק את אפליקציית Chat לניהול אירועי אבטחה, צריך להתחיל אירוע מדף האינטרנט ולוודא שאפליקציית Chat פועלת כצפוי:

  1. נכנסים לכתובת ה-URL של אפליקציית האינטרנט לפריסת Apps Script.

  2. כש-Apps Script מבקש הרשאת גישה לנתונים שלכם, לוחצים על Review permissions, נכנסים באמצעות חשבון Google מתאים בדומיין Google Workspace ולוחצים על Allow.

  3. דף האינטרנט של אתחול התקרית ייפתח. מזינים את פרטי הבדיקה:

    1. בשדה שם האירוע, מקלידים The First Incident.
    2. בקטע תגובה לאירועי אבטחה, אפשר גם להזין את כתובות האימייל של המשיבים לתקרית. כדי ליצור מרחב משותף, הם צריכים להיות משתמשים עם חשבון Google Chat בארגון שלכם ב-Google Workspace. אל תזין את כתובת האימייל שלך כי היא נכללת באופן אוטומטי.
    3. בהודעה הראשונית, מקלידים Testing the incident management Chat app.
  4. לוחצים על יצירת מרחב משותף ב-Chat. תופיע ההודעה creating space.

  5. אחרי שיוצרים את המרחב המשותף מופיעה ההודעה Space created!. לוחצים על פתיחת המרחב המשותף. המרחב המשותף ב-Chat ייפתח בכרטיסייה חדשה.

  6. גם אתם וגם שאר המשיבים יכולים לשלוח הודעות במרחב המשותף. האפליקציה מסכמת את ההודעות האלה באמצעות Vertex AI ומשתפת מסמך רטרוספקטיבי.

  7. כדי לסיים את התגובה לאירוע ולהתחיל בתהליך הטיפול, כותבים /closeIncident במרחב המשותף ב-Chat. תיפתח תיבת דו-שיח לניהול אירועי אבטחה.

  8. בשדה Close in אירוע, מזינים תיאור של פתרון התקרית, למשל Test complete.

  9. לוחצים על סגירת האירוע.

אפליקציית ניהול אירועים מכילה רשימה של ההודעות במרחב המשותף, מסכמת את ההודעות באמצעות Vertex AI, מדביקה את הסיכום במסמך ב-Google Docs ומשתפת את המסמך במרחב המשותף.

הסרת המשאבים

על מנת לא לצבור חיובים לחשבון Google Cloud עבור המשאבים שבהם השתמשתם במדריך הזה, מומלץ למחוק את הפרויקט ב-Cloud.

  1. במסוף Google Cloud, עוברים לדף Manage resources. לוחצים על תפריט > IAM ואדמין > ניהול משאבים.

    כניסה למנהל המשאבים

  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete .
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.