Skip to content

Commit 51465fb

Browse files
authored
Merge pull request #17013 from hardikSinghBehl/codebase/twilio-whatsapp-BAEL-8246
codebase/sending-whatsapp-messages-in-spring-boot-using-twilio [BAEL-8246]
2 parents fd5fc37 + 5c569d9 commit 51465fb

File tree

11 files changed

+441
-1
lines changed

11 files changed

+441
-1
lines changed

saas-modules/pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<module>slack</module>
2323
<module>stripe</module>
2424
<module>twilio</module>
25+
<module>twilio-whatsapp</module>
2526
<module>twitter4j</module>
2627
</modules>
2728

@@ -40,4 +41,4 @@
4041
<start-class>com.baeldung.Main</start-class>
4142
</properties>
4243

43-
</project>
44+
</project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<artifactId>twilio-whatsapp</artifactId>
7+
<version>0.0.1</version>
8+
<packaging>jar</packaging>
9+
<name>twilio-whatsapp</name>
10+
<description>codebase to demonstrate sending whatsapp messages using Twilio</description>
11+
12+
<parent>
13+
<groupId>com.baeldung</groupId>
14+
<artifactId>saas-modules</artifactId>
15+
<version>1.0.0-SNAPSHOT</version>
16+
</parent>
17+
18+
<dependencies>
19+
<dependency>
20+
<groupId>org.springframework.boot</groupId>
21+
<artifactId>spring-boot-starter-web</artifactId>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter-validation</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.springframework.boot</groupId>
29+
<artifactId>spring-boot-configuration-processor</artifactId>
30+
<optional>true</optional>
31+
</dependency>
32+
<dependency>
33+
<groupId>com.twilio.sdk</groupId>
34+
<artifactId>twilio</artifactId>
35+
<version>${twilio.version}</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.json</groupId>
39+
<artifactId>json</artifactId>
40+
<version>${json.version}</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.mock-server</groupId>
44+
<artifactId>mockserver-netty</artifactId>
45+
<version>${mockserver.version}</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.mock-server</groupId>
49+
<artifactId>mockserver-spring-test-listener</artifactId>
50+
<version>${mockserver.version}</version>
51+
</dependency>
52+
</dependencies>
53+
54+
<properties>
55+
<java.version>17</java.version>
56+
<json.version>20240303</json.version>
57+
<twilio.version>10.4.1</twilio.version>
58+
<mockserver.version>5.15.0</mockserver.version>
59+
</properties>
60+
61+
</project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.baeldung.twilio.whatsapp;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class Application {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(Application.class, args);
11+
}
12+
13+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.baeldung.twilio.whatsapp;
2+
3+
import org.springframework.http.ResponseEntity;
4+
import org.springframework.web.bind.annotation.PostMapping;
5+
import org.springframework.web.bind.annotation.RequestParam;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
@RestController
9+
public class ReplyController {
10+
11+
private final WhatsAppMessageDispatcher whatsAppMessageDispatcher;
12+
13+
public ReplyController(WhatsAppMessageDispatcher whatsAppMessageDispatcher) {
14+
this.whatsAppMessageDispatcher = whatsAppMessageDispatcher;
15+
}
16+
17+
@PostMapping(value = "/api/v1/whatsapp-message-reply")
18+
public ResponseEntity<Void> reply(@RequestParam("ProfileName") String username,
19+
@RequestParam("WaId") String phoneNumber) {
20+
whatsAppMessageDispatcher.dispatchReplyMessage(phoneNumber, username);
21+
return ResponseEntity.ok().build();
22+
}
23+
24+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.baeldung.twilio.whatsapp;
2+
3+
import org.springframework.boot.context.properties.ConfigurationProperties;
4+
import org.springframework.validation.annotation.Validated;
5+
6+
import jakarta.validation.Valid;
7+
import jakarta.validation.constraints.NotBlank;
8+
import jakarta.validation.constraints.Pattern;
9+
10+
@Validated
11+
@ConfigurationProperties(prefix = "com.baeldung.twilio")
12+
public class TwilioConfigurationProperties {
13+
14+
@NotBlank(message = "Twilio account SID must be configured")
15+
@Pattern(regexp = "^AC[0-9a-fA-F]{32}$", message = "Invalid Twilio account SID format")
16+
private String accountSid;
17+
18+
@NotBlank(message = "Twilio auth token must be configured")
19+
private String authToken;
20+
21+
@NotBlank(message = "Twilio messaging SID must be configured")
22+
@Pattern(regexp = "^MG[0-9a-fA-F]{32}$", message = "Invalid Twilio messaging SID format")
23+
private String messagingSid;
24+
25+
@Valid
26+
private NewArticleNotification newArticleNotification = new NewArticleNotification();
27+
28+
public String getAccountSid() {
29+
return accountSid;
30+
}
31+
32+
public void setAccountSid(String accountSid) {
33+
this.accountSid = accountSid;
34+
}
35+
36+
public String getAuthToken() {
37+
return authToken;
38+
}
39+
40+
public void setAuthToken(String authToken) {
41+
this.authToken = authToken;
42+
}
43+
44+
public String getMessagingSid() {
45+
return messagingSid;
46+
}
47+
48+
public void setMessagingSid(String messagingSid) {
49+
this.messagingSid = messagingSid;
50+
}
51+
52+
public NewArticleNotification getNewArticleNotification() {
53+
return newArticleNotification;
54+
}
55+
56+
public void setNewArticleNotification(NewArticleNotification newArticleNotification) {
57+
this.newArticleNotification = newArticleNotification;
58+
}
59+
60+
public class NewArticleNotification {
61+
62+
@NotBlank(message = "Content SID must be configured")
63+
@Pattern(regexp = "^HX[0-9a-fA-F]{32}$", message = "Invalid content SID format")
64+
private String contentSid;
65+
66+
public String getContentSid() {
67+
return contentSid;
68+
}
69+
70+
public void setContentSid(String contentSid) {
71+
this.contentSid = contentSid;
72+
}
73+
74+
}
75+
76+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.baeldung.twilio.whatsapp;
2+
3+
import org.springframework.boot.ApplicationArguments;
4+
import org.springframework.boot.ApplicationRunner;
5+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
6+
import org.springframework.stereotype.Component;
7+
8+
import com.twilio.Twilio;
9+
10+
@Component
11+
@EnableConfigurationProperties(TwilioConfigurationProperties.class)
12+
public class TwilioInitializer implements ApplicationRunner {
13+
14+
private final TwilioConfigurationProperties twilioConfigurationProperties;
15+
16+
public TwilioInitializer(TwilioConfigurationProperties twilioConfigurationProperties) {
17+
this.twilioConfigurationProperties = twilioConfigurationProperties;
18+
}
19+
20+
@Override
21+
public void run(ApplicationArguments args) {
22+
String accountSid = twilioConfigurationProperties.getAccountSid();
23+
String authToken = twilioConfigurationProperties.getAuthToken();
24+
Twilio.init(accountSid, authToken);
25+
}
26+
27+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.baeldung.twilio.whatsapp;
2+
3+
import org.json.JSONObject;
4+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
5+
import org.springframework.stereotype.Service;
6+
7+
import com.twilio.rest.api.v2010.account.Message;
8+
import com.twilio.type.PhoneNumber;
9+
10+
@Service
11+
@EnableConfigurationProperties(TwilioConfigurationProperties.class)
12+
public class WhatsAppMessageDispatcher {
13+
14+
private final TwilioConfigurationProperties twilioConfigurationProperties;
15+
16+
public WhatsAppMessageDispatcher(TwilioConfigurationProperties twilioConfigurationProperties) {
17+
this.twilioConfigurationProperties = twilioConfigurationProperties;
18+
}
19+
20+
public void dispatchNewArticleNotification(String phoneNumber, String articleUrl) {
21+
String messagingSid = twilioConfigurationProperties.getMessagingSid();
22+
String contentSid = twilioConfigurationProperties.getNewArticleNotification().getContentSid();
23+
PhoneNumber toPhoneNumber = new PhoneNumber(String.format("whatsapp:%s", phoneNumber));
24+
25+
JSONObject contentVariables = new JSONObject();
26+
contentVariables.put("ArticleURL", articleUrl);
27+
28+
Message.creator(toPhoneNumber, messagingSid, "")
29+
.setContentSid(contentSid)
30+
.setContentVariables(contentVariables.toString())
31+
.create();
32+
}
33+
34+
public void dispatchReplyMessage(String phoneNumber, String username) {
35+
String messagingSid = twilioConfigurationProperties.getMessagingSid();
36+
PhoneNumber toPhoneNumber = new PhoneNumber(String.format("whatsapp:%s", phoneNumber));
37+
38+
String message = String.format("Hey %s, our team will get back to you shortly.", username);
39+
Message.creator(toPhoneNumber, messagingSid, message).create();
40+
}
41+
42+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
com:
2+
baeldung:
3+
twilio:
4+
account-sid: ${TWILIO_ACCOUNT_SID}
5+
auth-token: ${TWILIO_AUTH_TOKEN}
6+
messaging-sid: ${TWILIO_MESSAGING_SID}
7+
new-article-notification:
8+
content-sid: ${NEW_ARTICLE_NOTIFICATION_CONTENT_SID}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.baeldung.twilio.whatsapp;
2+
3+
import javax.net.ssl.SSLContext;
4+
5+
import org.apache.http.HttpHost;
6+
import org.apache.http.impl.client.HttpClientBuilder;
7+
import org.apache.http.ssl.SSLContextBuilder;
8+
9+
import com.twilio.http.HttpClient;
10+
import com.twilio.http.NetworkHttpClient;
11+
import com.twilio.http.TwilioRestClient;
12+
import com.twilio.http.TwilioRestClient.Builder;
13+
14+
class TwilioProxyClient {
15+
16+
private final String accountSid;
17+
private final String authToken;
18+
private final String host;
19+
private final int port;
20+
21+
public TwilioProxyClient(String accountSid, String authToken, String host, int port) {
22+
this.accountSid = accountSid;
23+
this.authToken = authToken;
24+
this.host = host;
25+
this.port = port;
26+
}
27+
28+
public TwilioRestClient createHttpClient() throws Exception {
29+
SSLContext sslContext = SSLContextBuilder.create()
30+
.loadTrustMaterial((chain, authType) -> true)
31+
.build();
32+
33+
HttpClientBuilder clientBuilder = HttpClientBuilder.create()
34+
.setSSLContext(sslContext)
35+
.setProxy(new HttpHost(host, port));
36+
37+
HttpClient httpClient = new NetworkHttpClient(clientBuilder);
38+
return new Builder(accountSid, authToken)
39+
.httpClient(httpClient)
40+
.build();
41+
}
42+
43+
}

0 commit comments

Comments
 (0)