Skip to content

Commit

Permalink
feat: Add Chat Apps with Gemini resources (#270)
Browse files Browse the repository at this point in the history
* Initiate Google IO Gemini code lab

* Fix accessory function and storage

* Enable private messages

* Minimize oauth scope

* Removed private message logic because unsupported with accessory widgets, added other app resources

* Broke down the issue management app in code lab stagesBroke d

* Improve the auth credentials to be in the configuration script

* Fix typo

* Fix progressive oauth scopes

* Remove unnecessary services

* Switch to dynamic pubsub configuration

* Delete subscription at issue closure

* Remove unnecessary REST API implementation of app auth messaging

* Improve prompt for inclusivity help

* Support default agent response instead of custom payload for simplicity

---------

Co-authored-by: pierrick <[email protected]>
  • Loading branch information
PierrickVoulet and pierrick authored Mar 18, 2024
1 parent afef33f commit 81d0ba6
Show file tree
Hide file tree
Showing 51 changed files with 3,496 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ service-account.json
**/python-3*/
**/.history/
.vscode/
**/.clasp.json
8 changes: 8 additions & 0 deletions apps-script/chat-apps-gemini/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Build apps for Google Chat with Gemini

This code example creates Google Chat apps with the following advanced features: app home cards,
Google Workspace Chat events, accessory widgets, Dialogflow CX agents, and Vertex AI's
Gemini-powered AI models.

Please see the related
[code lab instructions](https://developers.google.com/codelabs/chat-apps-gemini).
24 changes: 24 additions & 0 deletions apps-script/chat-apps-gemini/feedback-app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

const functions = require('@google-cloud/functions-framework');

/**
* Handles all incoming requests.
*/
functions.http('feedback-app-backend', (request, response) => {
response.send('OK');
});
9 changes: 9 additions & 0 deletions apps-script/chat-apps-gemini/feedback-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "feedback-app-backend",
"version": "0.0.1",
"description": "Mocks a feedback app backend.",
"license": "Apache-2.0",
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
}
}
30 changes: 30 additions & 0 deletions apps-script/chat-apps-gemini/feedback-app/service-openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
openapi: 3.1.0
info:
title: Review Store
license:
name: Apache 2.0
identifier: Apache-2.0
version: 0.0.1
servers:
- url: $URL
paths:
/store-review:
post:
operationId: storeReview
summary: Store a new review
requestBody:
content:
application/json:
schema:
type: object
properties:
stars:
type: integer
format: int64
responses:
'200':
description: success
content:
application/json:
schema:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// This script contains the Google Chat-specific callback and utilities functions.

/**
* Responds to a MESSAGE event in Google Chat.
*
* Handles slash commands.
*
* @param {Object} event the event object from Google Chat
*/
function onMessage(event) {
if (event.message.slashCommand) {
return processSlashCommand(event);
}

return { text: "Available slash commands to manage issues are '/create' and '/close'." };
}

/**
* Responds to a CARD_CLICKED event in Google Chat.
*
* Handles action funtions.
*
* @param {Object} event the event object from Google Chat
*/
function onCardClick(event) {
// Issue creation dialog form submission
if (event.action.actionMethodName === "createIssue") {
return createIssue(event);
}
}

/**
* Responds to a MESSAGE event with a slash command in Google Chat.
*
* @param {Object} event the event object from Google Chat
*/
function processSlashCommand(event) {
if (event.message.slashCommand.commandId == CREATE_COMMAND_ID) {
// Opens the issue creation dialog.
return { actionResponse: {
type: "DIALOG",
dialogAction: { dialog: { body: { sections: [{
header: "Create",
widgets: [{ textInput: {
label: "Title",
name: "title"
}}, { textInput: {
label: "Description",
type: "MULTIPLE_LINE",
name: "description"
}}, { buttonList: { buttons: [{
text: "Create",
onClick: { action: {
function: "createIssue"
}}
}]}
}]
}]}}}
}};
}

if (event.message.slashCommand.commandId == CLOSE_COMMAND_ID && event.message.space.type !== "DM") {
// Closes the issue associated to the space.
const spaceId = event.message.space.name;
const resolution = event.message.argumentText;
const issue = JSON.parse(appProperties.getProperty(spaceId));
const docUrl = createReport(issue.title, issue.description, resolution);
saveClosedIssue(spaceId, resolution, docUrl);

return {
actionResponse: { type: "NEW_MESSAGE" },
text: `The issue is closed and its report generated:\n${docUrl}`
};
}

return { text: "The command isn't supported." };
}

/**
* Handles create issue dialog form submissions in Google Chat.
*
* @param {Object} event the event object from Google Chat
*/
function createIssue(event) {
// Retrieves the form inputs.
const title = event.common.formInputs.title[""].stringInputs.value[0];
const description = event.common.formInputs.description[""].stringInputs.value[0];
const spaceUrl = createIssueSpace(title, description);
const createdIssue = saveCreatedIssue(title, description, spaceUrl);

return {
actionResponse: { type: "NEW_MESSAGE" },
text: `The issue and its dedicated space were created:\n`
+ `https://mail.google.com/mail/u/0/#chat/space/${createdIssue.spaceId}`
};
}

/**
* Initializes a Google Chat space dedicated to a new issue.
*
* The app adds itself and sends a first message to the newly created space.
*
* @param {string} title the title of the issue
* @param {string} description the description of the isue
* @return {string} the ID of the new space
*/
function createIssueSpace(title, description) {
// Creates the space.
const spaceId = Chat.Spaces.setup({
space: {
displayName: title,
spaceType: "SPACE"
}
}).name;

// Adds itself to the space.
Chat.Spaces.Members.create({
member: {
name: "users/app",
type: "BOT"
}
}, spaceId);

// Sends a first message to the space
createAppMessageUsingChatService({ text: description}, spaceId);
return spaceId;
}

/**
* Responds to a REMOVED_FROM_SPACE event in Google Chat.
*
* @param {Object} event the event object from Google Chat
*/
function onRemoveFromSpace(event) {}

/**
* Responds to a ADDED_TO_SPACE event in Google Chat.
*
* @param {Object} event the event object from Google Chat
*/
function onAddToSpace(event) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// This script contains utilities functions based on app authentication.

/**
* Creates a new message by using the Advanced Chat Service.
*
* @param {Object} message the message object to send to Google Chat
* @param {string} spaceId the space ID where to create the message
*/
function createAppMessageUsingChatService(message, spaceId) {
Chat.Spaces.Messages.create(message, spaceId, {}, {
'Authorization': 'Bearer ' + getService_().getAccessToken()
});
}

/**
* Authenticates the app service by using the OAuth2 library.
*
* Warning: This example uses a service account private key for simplicity's sake, it should always
* be stored in an secure location.
*
* @return {Object} the authenticated app service
*/
function getService_() {
return OAuth2.createService(CHAT_CREDENTIALS.client_email)
.setTokenUrl(CHAT_CREDENTIALS.token_uri)
.setPrivateKey(CHAT_CREDENTIALS.private_key)
.setIssuer(CHAT_CREDENTIALS.client_email)
.setSubject(CHAT_CREDENTIALS.client_email)
.setScope('https://www.googleapis.com/auth/chat.bot')
.setPropertyStore(appProperties);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// This script initiates the app configuration.

const CREATE_COMMAND_ID = 1;
const CLOSE_COMMAND_ID = 2;
const CHAT_CREDENTIALS = {
// Replace with the Google Chat credentials to use for app authenticatio, the service account
// private key's JSON.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// This script contains the Google Docs-specific utilities functions.

/**
* Creates an issue report as a Google Doc in the user's Google Drive.
*
* @param {string} title the title of the issue
* @param {string} description the description of the issue
* @param {string} resolution the resolution of the issue
* @return {string} the URL of the created report
*/
function createReport(title, description, resolution) {
let doc = DocumentApp.create(title);
let body = doc.getBody();
body.appendParagraph(`Issue Report: ${title}`).setHeading(DocumentApp.ParagraphHeading.TITLE);
body.appendParagraph("Description").setHeading(DocumentApp.ParagraphHeading.HEADING1);
body.appendParagraph(description);
body.appendParagraph("Resolution").setHeading(DocumentApp.ParagraphHeading.HEADING1);
body.appendParagraph(resolution);
return doc.getUrl();
}
Loading

0 comments on commit 81d0ba6

Please sign in to comment.