/** * 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 */functiononMessage(event){if(event.message.slashCommand){returnprocessSlashCommand_(event);}return{"text":"HellofromIncidentResponseapp!"};}/** * 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 */functiononCardClick(event){if(event.isDialogEvent){if(event.dialogEventType=='SUBMIT_DIALOG'){returnprocessSubmitDialog_(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 */functionprocessSlashCommand_(event){if(event.message.slashCommand.commandId!=CLOSE_INCIDENT_COMMAND_ID){return{"text":"Commandnotrecognized.Usethecommand`/closeIncident`toclosetheincidentmanagedbythisspace."
};}constsections=[{header:"CloseIncident",widgets:[{textInput:{label:"Pleasedescribetheincidentresolution",type:"MULTIPLE_LINE",name:"description"
}},{buttonList:{buttons:[{text:"CloseIncident",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 */functionprocessSubmitDialog_(event){constresolution=event.common.formInputs.description[""].stringInputs.value[0];constchatHistory=concatenateAllSpaceMessages_(event.space.name);constchatSummary=summarizeChatHistory_(chatHistory);constdocUrl=createDoc_(event.space.displayName,resolution,chatHistory,chatSummary);return{actionResponse:{type:"NEW_MESSAGE",},text:`Incidentclosedwiththefollowingresolution:${resolution}\n\nHereistheautomaticallygeneratedpost-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 */functionconcatenateAllSpaceMessages_(spaceName){// Call Chat API method spaces.messages.listconstresponse=Chat.Spaces.Messages.list(spaceName,{'pageSize':100});constmessages=response.messages;// Fetch the display names of the message senders and returns a text// concatenating all the messages.letuserMap=newMap();returnmessages.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 */functiongetUserDisplayName_(userMap,userName){if(userMap.has(userName)){returnuserMap.get(userName);}letdisplayName='UnknownUser';try{constuser=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);returndisplayName;}
/** * 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. */functioncreateChatSpace(formData){constusers=formData.users.trim().length > 0?formData.users.split(','):[];constspaceName=setUpSpace_(formData.title,users);addAppToSpace_(spaceName);createMessage_(spaceName,formData.description);returnspaceName;}/** * Creates a space in Google Chat with the provided display name and members. * * @return {string} the resource name of the new space. */functionsetUpSpace_(displayName,users){constmemberships=users.map(email=>({member:{name:`users/${email}`,type:"HUMAN"
}}));constrequest={space:{displayName:displayName,spaceType:"SPACE",externalUserAllowed:true},memberships:memberships};// Call Chat API method spaces.setupconstspace=Chat.Spaces.setup(request);returnspace.name;}/** * Adds this Chat app to the space. * * @return {string} the resource name of the new membership. */functionaddAppToSpace_(spaceName){constrequest={member:{name:"users/app",type:"BOT"
}};// Call Chat API method spaces.members.createconstmembership=Chat.Spaces.Members.create(request,spaceName);returnmembership.name;}/** * Posts a text message to the space on behalf of the user. * * @return {string} the resource name of the new message. */functioncreateMessage_(spaceName,text){constrequest={text:text};// Call Chat API method spaces.messages.createconstmessage=Chat.Spaces.Messages.create(request,spaceName);returnmessage.name;}
DocsApi.gs
调用 Google 文档 API 以在
用户的 Google 云端硬盘,并撰写事件信息摘要,
(在 VertexAiApi.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 */functioncreateDoc_(title,resolution,chatHistory,chatSummary){letdoc=DocumentApp.create(title);letbody=doc.getBody();body.appendParagraph(`Post-Mortem:${title}`).setHeading(DocumentApp.ParagraphHeading.TITLE);body.appendParagraph("Resolution").setHeading(DocumentApp.ParagraphHeading.HEADING1);body.appendParagraph(resolution);body.appendParagraph("Summaryoftheconversation").setHeading(DocumentApp.ParagraphHeading.HEADING1);body.appendParagraph(chatSummary);body.appendParagraph("FullChathistory").setHeading(DocumentApp.ParagraphHeading.HEADING1);body.appendParagraph(chatHistory);returndoc.getUrl();}
/** * 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. */functionsummarizeChatHistory_(chatHistory){constprompt="SummarizethefollowingconversationbetweenEngineersresolvinganincident"
+ " inafewsentences.Useonlytheinformationfromtheconversation.\n\n"
+chatHistory;constrequest={instances:[{prompt:prompt}],parameters:{temperature:0.2,maxOutputTokens:256,topK:40,topP:0.95}}constfetchOptions={method:'POST',headers:{Authorization:'Bearer ' +ScriptApp.getOAuthToken()},contentType:'application/json',payload:JSON.stringify(request)}constresponse=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);constpayload=JSON.parse(response.getContentText());returnpayload.predictions[0].content;}
/** * Serves the web page from Index.html. */functiondoGet(){returnHtmlService.createTemplateFromFile('Index').evaluate();}/** * Serves the web content from the specified filename. */functioninclude(filename){returnHtmlService.createHtmlOutputFromFile(filename).getContent();}/** * Returns the email address of the user running the script. */functiongetUserEmail(){returnSession.getActiveUser().getEmail();}
突发事件响应应用使用用户凭据进行身份验证,以调用 API
以及调用 Chat API 和 Vertex AI API 等 Google Cloud 服务。
这意味着,初始化突发事件的用户必须拥有
服务。为了提高应用的稳健性,请考虑调用 Vertex AI API
而不是调用
/closeIncident 斜杠命令。有关配置服务账号的帮助
如需了解 Vertex AI API,请参阅
使用自定义服务账号。