Skip to content

Commit 7f25144

Browse files
authored
BAEL-8112 (#17172)
* BAEL-8112 BAEL-8112 * Update ChatbotService.java
1 parent 7f11e8d commit 7f25144

File tree

9 files changed

+322
-0
lines changed

9 files changed

+322
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Relevant Articles
2+
3+
insert here
4+
5+
- More articles: [[<-- prev]](/spring-boot-modules/spring-boot-3-2)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<artifactId>spring-boot-3-3</artifactId>
6+
<version>0.0.1-SNAPSHOT</version>
7+
<name>spring-boot-3-3</name>
8+
<description>Demo project for Spring Boot</description>
9+
<parent>
10+
<groupId>com.baeldung.spring-boot-modules</groupId>
11+
<artifactId>spring-boot-modules</artifactId>
12+
<version>1.0.0-SNAPSHOT</version>
13+
</parent>
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.springframework.boot</groupId>
17+
<artifactId>spring-boot-starter-web</artifactId>
18+
</dependency>
19+
<dependency>
20+
<groupId>org.apache.camel.springboot</groupId>
21+
<artifactId>camel-spring-boot-starter</artifactId>
22+
<version>${apache-camel.version}</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.apache.camel.springboot</groupId>
26+
<artifactId>camel-http-starter</artifactId>
27+
<version>${apache-camel.version}</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.apache.camel</groupId>
31+
<artifactId>camel-jackson</artifactId>
32+
<version>${apache-camel.version}</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>dev.langchain4j</groupId>
36+
<artifactId>langchain4j-core</artifactId>
37+
<version>${langchain4j.version}</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>dev.langchain4j</groupId>
41+
<artifactId>langchain4j-ollama</artifactId>
42+
<version>${langchain4j.version}</version>
43+
</dependency>
44+
</dependencies>
45+
<build>
46+
<plugins>
47+
<plugin>
48+
<groupId>org.springframework.boot</groupId>
49+
<artifactId>spring-boot-maven-plugin</artifactId>
50+
</plugin>
51+
<plugin>
52+
<groupId>org.apache.maven.plugins</groupId>
53+
<artifactId>maven-compiler-plugin</artifactId>
54+
<configuration>
55+
<source>${java.version}</source>
56+
<target>${java.version}</target>
57+
<compilerArgs>
58+
<arg>-parameters</arg>
59+
</compilerArgs>
60+
</configuration>
61+
</plugin>
62+
</plugins>
63+
</build>
64+
<properties>
65+
<spring-boot.version>3.2.4</spring-boot.version>
66+
<apache-camel.version>4.7.0</apache-camel.version>
67+
<langchain4j.version>0.33.0</langchain4j.version>
68+
</properties>
69+
</project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.baeldung.chatbot;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class ChatbotApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(ChatbotApplication.class, args);
11+
}
12+
13+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.baeldung.chatbot.controller;
2+
3+
import com.baeldung.chatbot.service.ChatbotService;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.RequestParam;
7+
import org.springframework.web.bind.annotation.RestController;
8+
9+
@RestController
10+
public class ChatbotController {
11+
@Autowired
12+
private ChatbotService chatbotService;
13+
14+
@GetMapping("/api/chatbot/send")
15+
public String getChatbotResponse(@RequestParam String question) {
16+
return chatbotService.getResponse(question);
17+
}
18+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.baeldung.chatbot.controller;
2+
3+
import com.baeldung.chatbot.service.WhatsAppService;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.beans.factory.annotation.Value;
6+
import org.springframework.web.bind.annotation.*;
7+
8+
@RestController
9+
public class WhatsAppController {
10+
11+
@Value("${whatsapp.verify_token}")
12+
private String verifyToken;
13+
14+
@Autowired
15+
private WhatsAppService whatsAppService;
16+
17+
@PostMapping("/api/whatsapp/send")
18+
public String sendWhatsAppMessage(@RequestParam String to, @RequestParam String message) {
19+
whatsAppService.sendWhatsAppMessage(to, message);
20+
return "Message sent";
21+
}
22+
23+
@GetMapping("/webhook")
24+
public String verifyWebhook(@RequestParam("hub.mode") String mode,
25+
@RequestParam("hub.verify_token") String token,
26+
@RequestParam("hub.challenge") String challenge) {
27+
if ("subscribe".equals(mode) && verifyToken.equals(token)) {
28+
return challenge;
29+
} else {
30+
return "Verification failed";
31+
}
32+
}
33+
34+
@PostMapping("/webhook")
35+
public void receiveMessage(@RequestBody String payload) {
36+
whatsAppService.processIncomingMessage(payload);
37+
}
38+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.baeldung.chatbot.service;
2+
3+
import java.time.Duration;
4+
5+
import org.apache.hc.core5.http.HttpStatus;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
import org.springframework.beans.factory.annotation.Value;
9+
import org.springframework.stereotype.Service;
10+
import org.springframework.web.server.ResponseStatusException;
11+
12+
import dev.langchain4j.model.ollama.OllamaChatModel;
13+
import jakarta.annotation.PostConstruct;
14+
15+
@Service
16+
public class ChatbotService {
17+
18+
private static final Logger logger = LoggerFactory.getLogger(ChatbotService.class);
19+
20+
@Value("${ollama.api_url}")
21+
private String apiUrl;
22+
23+
@Value("${ollama.model}")
24+
private String modelName;
25+
26+
@Value("${ollama.timeout}")
27+
private int timeout;
28+
29+
@Value("${ollama.max_response_length}")
30+
private int maxResponseLength;
31+
32+
private OllamaChatModel ollamaChatModel;
33+
34+
@PostConstruct
35+
public void init() {
36+
this.ollamaChatModel = OllamaChatModel.builder()
37+
.baseUrl(apiUrl)
38+
.modelName(modelName)
39+
.timeout(Duration.ofSeconds(timeout))
40+
.numPredict(maxResponseLength)
41+
.build();
42+
}
43+
44+
public String getResponse(String question) {
45+
logger.debug("Sending to Ollama: {}", question);
46+
String answer = ollamaChatModel.generate(question);
47+
logger.debug("Receiving from Ollama: {}", answer);
48+
if (answer != null && !answer.isEmpty()) {
49+
return answer;
50+
} else {
51+
logger.error("Invalid Ollama response for:\n\n" + question);
52+
throw new ResponseStatusException(
53+
HttpStatus.SC_INTERNAL_SERVER_ERROR,
54+
"Ollama didn't generate a valid response",
55+
null);
56+
}
57+
}
58+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.baeldung.chatbot.service;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import org.apache.camel.CamelContext;
7+
import org.apache.camel.ProducerTemplate;
8+
import org.apache.camel.builder.RouteBuilder;
9+
import org.apache.camel.component.jackson.JacksonDataFormat;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.beans.factory.annotation.Value;
14+
import org.springframework.stereotype.Service;
15+
16+
import com.fasterxml.jackson.databind.JsonNode;
17+
import com.fasterxml.jackson.databind.ObjectMapper;
18+
19+
import jakarta.annotation.PostConstruct;
20+
21+
@Service
22+
public class WhatsAppService {
23+
24+
private static final Logger logger = LoggerFactory.getLogger(WhatsAppService.class);
25+
26+
@Value("${whatsapp.api_url}")
27+
private String apiUrl;
28+
29+
@Value("${whatsapp.access_token}")
30+
private String apiToken;
31+
32+
@Autowired
33+
private CamelContext camelContext;
34+
35+
@Autowired
36+
private ObjectMapper objectMapper;
37+
38+
@Autowired
39+
private ProducerTemplate producerTemplate;
40+
41+
@Autowired
42+
private ChatbotService chatbotService;
43+
44+
@PostConstruct
45+
public void init() throws Exception {
46+
camelContext.addRoutes(new RouteBuilder() {
47+
@Override
48+
public void configure() {
49+
JacksonDataFormat jacksonDataFormat = new JacksonDataFormat();
50+
jacksonDataFormat.setPrettyPrint(true);
51+
52+
from("direct:sendWhatsAppMessage")
53+
.setHeader("Authorization", constant("Bearer " + apiToken))
54+
.setHeader("Content-Type", constant("application/json"))
55+
.marshal(jacksonDataFormat)
56+
.process(exchange -> {
57+
logger.debug("Sending JSON: {}", exchange.getIn().getBody(String.class));
58+
}).to(apiUrl).process(exchange -> {
59+
logger.debug("Response: {}", exchange.getIn().getBody(String.class));
60+
});
61+
}
62+
});
63+
}
64+
65+
public void sendWhatsAppMessage(String toNumber, String message) {
66+
Map<String, Object> body = new HashMap<>();
67+
body.put("messaging_product", "whatsapp");
68+
body.put("to", toNumber);
69+
body.put("type", "text");
70+
71+
Map<String, String> text = new HashMap<>();
72+
text.put("body", message);
73+
body.put("text", text);
74+
75+
producerTemplate.sendBody("direct:sendWhatsAppMessage", body);
76+
}
77+
78+
public void processIncomingMessage(String payload) {
79+
try {
80+
JsonNode jsonNode = objectMapper.readTree(payload);
81+
JsonNode messages = jsonNode.at("/entry/0/changes/0/value/messages");
82+
if (messages.isArray() && messages.size() > 0) {
83+
String receivedText = messages.get(0).at("/text/body").asText();
84+
String fromNumber = messages.get(0).at("/from").asText();
85+
logger.debug(fromNumber + " sent the message: " + receivedText);
86+
this.sendWhatsAppMessage(fromNumber, chatbotService.getResponse(receivedText));
87+
}
88+
} catch (Exception e) {
89+
logger.error("Error processing incoming payload: {} ", payload, e);
90+
}
91+
}
92+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# WhatsApp API configuration
2+
whatsapp.verify_token=BaeldungDemo-Verify-Token
3+
whatsapp.api_url=https://graph.facebook.com/v20.0/PHONE_NUMBER_ID/messages
4+
whatsapp.access_token=ACCESS_TOKEN
5+
6+
# Ollama API configuration
7+
ollama.api_url=http://localhost:11434/
8+
ollama.model=qwen2:1.5b
9+
ollama.timeout=30
10+
ollama.max_response_length=1000
11+
12+
# Logging configuration
13+
logging.level.root=INFO
14+
logging.level.com.baeldung.chatbot=DEBUG
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.chatbot;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.boot.test.context.SpringBootTest;
5+
6+
import static org.assertj.core.api.Assertions.assertThat;
7+
8+
@SpringBootTest
9+
class ChatbotApplicationTests {
10+
11+
@Test
12+
void contextLoads() {
13+
assertThat(true).isTrue();
14+
}
15+
}

0 commit comments

Comments
 (0)