Skip to content

Commit 3067d8a

Browse files
PierrickVouletpierrick
andauthored
feat: add preview link code sample (#265)
* feat: add preview link code sample * fix indent --------- Co-authored-by: pierrick <[email protected]>
1 parent acc1817 commit 3067d8a

File tree

20 files changed

+1875
-27
lines changed

20 files changed

+1875
-27
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Google Chat preview link app as Google Workspace add-on
2+
3+
Provide more information for a link.
4+
5+
Please see related guide about [preview link](https://developers.google.com/workspace/add-ons/chat/preview-links).
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* Copyright 2025 Google LLC.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Handle messages that have links whose URLs match patterns configured
19+
* for link previewing.
20+
*
21+
* - Reply with text messages that echo "text.example.com" link URLs in messages.
22+
* - Attach cards to messages with "support.example.com" link URLs.
23+
*
24+
* @param {Object} event The event object from Google Workspace add-on.
25+
* @return {Object} The action response.
26+
*/
27+
function onMessage(event) {
28+
// Stores the Google Chat event as a variable.
29+
const chatMessage = event.chat.messagePayload.message;
30+
31+
// If the Chat app doesn't detect a link preview URL pattern, reply
32+
// with a text message that says so.
33+
if (!chatMessage.matchedUrl) {
34+
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
35+
text: 'No matchedUrl detected.'
36+
}}}}};
37+
}
38+
39+
// [START preview_links_text]
40+
// Reply with a text message for URLs of the subdomain "text".
41+
if (chatMessage.matchedUrl.url.includes("text.example.com")) {
42+
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
43+
text: 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.matchedUrl.url
44+
}}}}};
45+
}
46+
// [END preview_links_text]
47+
48+
// [START preview_links_card]
49+
// Attach a card to the message for URLs of the subdomain "support".
50+
if (chatMessage.matchedUrl.url.includes("support.example.com")) {
51+
// A hard-coded card is used in this example. In a real-life scenario,
52+
// the case information would be fetched and used to build the card.
53+
return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: { cardsV2: [{
54+
cardId: 'attachCard',
55+
card: {
56+
header: {
57+
title: 'Example Customer Service Case',
58+
subtitle: 'Case summary',
59+
},
60+
sections: [{ widgets: [
61+
{ decoratedText: { topLabel: 'Case ID', text: 'case123'}},
62+
{ decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},
63+
{ decoratedText: { topLabel: 'Status', text: 'Open'}},
64+
{ decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
65+
{ buttonList: { buttons: [{
66+
text: 'OPEN CASE',
67+
onClick: { openLink: {
68+
url: 'https://support.example.com/orders/case123'
69+
}},
70+
}, {
71+
text: 'RESOLVE CASE',
72+
onClick: { openLink: {
73+
url: 'https://support.example.com/orders/case123?resolved=y',
74+
}},
75+
}, {
76+
text: 'ASSIGN TO ME',
77+
// Clicking this button triggers the execution of the function
78+
// "assign" from the Apps Script project.
79+
onClick: { action: { function: 'assign'}}
80+
}]}}
81+
]}]
82+
}
83+
}]}}}};
84+
}
85+
// [END preview_links_card]
86+
}
87+
88+
// [START preview_links_assign]
89+
/**
90+
* Assigns and updates the card that's attached to a message with a
91+
* previewed link of the pattern "support.example.com".
92+
*
93+
* @param {Object} event The event object from the Google Workspace add-on.
94+
* @return {Object} Action response depending on the message author.
95+
*/
96+
function assign(event) {
97+
// Creates the updated card that displays "You" for the assignee
98+
// and that disables the button.
99+
//
100+
// A hard-coded card is used in this example. In a real-life scenario,
101+
// an actual assign action would be performed before building the card.
102+
const message = { cardsV2: [{
103+
cardId: 'attachCard',
104+
card: {
105+
header: {
106+
title: 'Example Customer Service Case',
107+
subtitle: 'Case summary',
108+
},
109+
sections: [{ widgets: [
110+
{ decoratedText: { topLabel: 'Case ID', text: 'case123'}},
111+
// The assignee is now "You"
112+
{ decoratedText: { topLabel: 'Assignee', text: 'You'}},
113+
{ decoratedText: { topLabel: 'Status', text: 'Open'}},
114+
{ decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
115+
{ buttonList: { buttons: [{
116+
text: 'OPEN CASE',
117+
onClick: { openLink: {
118+
url: 'https://support.example.com/orders/case123'
119+
}},
120+
}, {
121+
text: 'RESOLVE CASE',
122+
onClick: { openLink: {
123+
url: 'https://support.example.com/orders/case123?resolved=y',
124+
}},
125+
}, {
126+
text: 'ASSIGN TO ME',
127+
// The button is now disabled
128+
disabled: true,
129+
onClick: { action: { function: 'assign'}}
130+
}]}}
131+
]}]
132+
}
133+
}]};
134+
135+
// Use the adequate action response type. It depends on whether the message
136+
// the preview link card is attached to was created by a human or a Chat app.
137+
if(event.chat.buttonClickedPayload.message.sender.type === 'HUMAN') {
138+
return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: message }}};
139+
} else {
140+
return { hostAppDataAction: { chatDataAction: { updateMessageAction: message }}};
141+
}
142+
}
143+
// [END preview_links_assign]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# This file specifies files that are *not* uploaded to Google Cloud
2+
# using gcloud. It follows the same syntax as .gitignore, with the addition of
3+
# "#!include" directives (which insert the entries of the given .gitignore-style
4+
# file at that point).
5+
#
6+
# For more information, run:
7+
# $ gcloud topic gcloudignore
8+
#
9+
.gcloudignore
10+
# If you would like to upload your .git directory, .gitignore file or files
11+
# from your .gitignore file, remove the corresponding line
12+
# below:
13+
.git
14+
.gitignore
15+
16+
# Target directory for maven builds
17+
target/

java/chat/preview-link/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Google Chat preview link app as Google Workspace add-on
2+
3+
Provide more information for a link.
4+
5+
Please see related guide about [preview link](https://developers.google.com/workspace/add-ons/chat/preview-links).

java/chat/preview-link/pom.xml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright 2025 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
19+
<modelVersion>4.0.0</modelVersion>
20+
21+
<name>preview-link</name>
22+
23+
<groupId>com.google.chat</groupId>
24+
<artifactId>preview-link</artifactId>
25+
<version>1.0-SNAPSHOT</version>
26+
27+
<properties>
28+
<maven.compiler.target>17</maven.compiler.target>
29+
<maven.compiler.source>17</maven.compiler.source>
30+
</properties>
31+
32+
<dependencyManagement>
33+
<dependencies>
34+
<dependency>
35+
<!-- Import dependency management from Spring Boot -->
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot-dependencies</artifactId>
38+
<version>3.2.3</version>
39+
<type>pom</type>
40+
<scope>import</scope>
41+
</dependency>
42+
43+
<dependency>
44+
<groupId>org.springframework.cloud</groupId>
45+
<artifactId>spring-cloud-dependencies</artifactId>
46+
<version>Greenwich.SR1</version>
47+
<type>pom</type>
48+
<scope>import</scope>
49+
</dependency>
50+
51+
<dependency>
52+
<groupId>com.fasterxml.jackson</groupId>
53+
<artifactId>jackson-bom</artifactId>
54+
<version>2.17.2</version> <type>pom</type>
55+
<scope>import</scope>
56+
</dependency>
57+
</dependencies>
58+
</dependencyManagement>
59+
60+
<dependencies>
61+
<dependency>
62+
<groupId>org.springframework.boot</groupId>
63+
<artifactId>spring-boot-starter-web</artifactId>
64+
<version>3.2.3</version>
65+
<exclusions>
66+
<!-- Exclude the Tomcat dependency -->
67+
<exclusion>
68+
<groupId>org.springframework.boot</groupId>
69+
<artifactId>spring-boot-starter-tomcat</artifactId>
70+
</exclusion>
71+
</exclusions>
72+
</dependency>
73+
74+
<dependency>
75+
<groupId>org.springframework.boot</groupId>
76+
<artifactId>spring-boot-starter-jetty</artifactId>
77+
<version>3.2.3</version>
78+
</dependency>
79+
80+
<dependency>
81+
<groupId>com.google.apis</groupId>
82+
<artifactId>google-api-services-chat</artifactId>
83+
<version>v1-rev20241008-2.0.0</version>
84+
</dependency>
85+
86+
<dependency>
87+
<groupId>com.fasterxml.jackson.core</groupId>
88+
<artifactId>jackson-databind</artifactId>
89+
</dependency>
90+
</dependencies>
91+
92+
<build>
93+
<plugins>
94+
<plugin>
95+
<groupId>org.springframework.boot</groupId>
96+
<artifactId>spring-boot-maven-plugin</artifactId>
97+
<version>3.2.3</version>
98+
<executions>
99+
<execution>
100+
<goals>
101+
<goal>repackage</goal>
102+
</goals>
103+
</execution>
104+
</executions>
105+
</plugin>
106+
107+
<plugin>
108+
<groupId>com.google.cloud.tools</groupId>
109+
<artifactId>appengine-maven-plugin</artifactId>
110+
<version>2.7.0</version>
111+
<configuration>
112+
<version>GCLOUD_CONFIG</version>
113+
</configuration>
114+
</plugin>
115+
</plugins>
116+
</build>
117+
</project>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
runtime: java17
16+
instance_class: F1
17+
18+
# Explicitly set the memory limit and maximum heap size for the Spring Boot app
19+
env_variables:
20+
JAVA_TOOL_OPTIONS: "-XX:MaxRAM=256m -XX:ActiveProcessorCount=2 -Xmx32m"

0 commit comments

Comments
 (0)