Integrate webex webhooks
This commit is contained in:
@@ -8,13 +8,17 @@ services:
|
||||
- "8000:8080"
|
||||
- "5005:5005"
|
||||
environment:
|
||||
SSL_KEYSTORE_PATH: "/certs/keystore.p12"
|
||||
BROKER_URL: "tcp://api-broker:1883"
|
||||
BROKER_SECURE_URL: "tcp://api-broker:8883"
|
||||
NGROK_URL: "https://lipographic-angla-cistophoric.ngrok-free.dev"
|
||||
MONGODB_URI: "mongodb://board-mate-user:apx820kcng@mongodb:27017/board-mate-db"
|
||||
BROKER_USERNAME: "board-mate-api"
|
||||
BROKER_PASSWORD: "hepl"
|
||||
JWT_SECRET: "enY3OWU4djFyMTByNTZhcG9uY3Z0djQ5cnY0eDhhNWM0bjg5OTRjNDhidA=="
|
||||
SSL_KEYSTORE_PATH: "/certs/keystore.p12"
|
||||
MONGODB_URI: "mongodb://board-mate-user:apx820kcng@mongodb:27017/board-mate-db"
|
||||
WEBEX_BOT_TOKEN: "YmU0NTdkNDMtYTg1ZC00M2YyLTk3YzUtODA1MWFmOTk1NjA1ZmU3MTYxNGUtYWZm_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de"
|
||||
WEBEX_CLIENT_TOKEN: "N2U0ZGE1OWItZTBhOC00MmU5LWIwMzYtNjM0NDk3NGUwMmIyNjEyMmNiMTYtOTI3_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de"
|
||||
WEBEX_SHARED_SECRET: "cUxSc0ZjNVBZVG5oRmhqaVN0YUtMTEVZb0pIZW5EY2Rwa0hUaWdiVm9nWlJiY2t5aFdmTjhvWmQ5U3R3TDIxVE1CRTl4VGJldVI3TFdVa3lMbFVPZUVSMkZPSnBHTjk="
|
||||
DATABASE_NAME: "board-mate-db"
|
||||
JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
|
||||
depends_on:
|
||||
@@ -79,15 +83,21 @@ services:
|
||||
- ./mosquitto/log:/mosquitto/log
|
||||
- ./mosquitto/certs:/mosquitto/certs
|
||||
|
||||
#mongo-express:
|
||||
# image: mongo-express:latest
|
||||
# depends_on:
|
||||
# - mongodb
|
||||
# ports:
|
||||
# - "8401:8081"
|
||||
# environment:
|
||||
# - ME_CONFIG_MONGODB_SERVER=mongodb
|
||||
# - ME_CONFIG_MONGODB_PORT=27017
|
||||
# - ME_CONFIG_MONGODB_ADMINUSERNAME=root
|
||||
# - ME_CONFIG_MONGODB_ADMINPASSWORD=secret
|
||||
# - ME_CONFIG_MONGODB_AUTH_DATABASE=admin
|
||||
ngrok:
|
||||
image: ngrok/ngrok:latest
|
||||
command: http --log=stdout nginx:8600
|
||||
ports:
|
||||
- "8500:4040"
|
||||
environment:
|
||||
NGROK_AUTHTOKEN: 37gBJ6KZQIK9nwcq0mWlfRHqtvX_49hdE8PVLmRTzUVaNRJqx
|
||||
NGROK_PORT: nginx:8600
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: nginx
|
||||
ports:
|
||||
- "8600:8080"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
depends_on:
|
||||
- boardmate-api
|
||||
16
api/nginx/nginx.conf
Normal file
16
api/nginx/nginx.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
events {}
|
||||
|
||||
http {
|
||||
server {
|
||||
listen 8600;
|
||||
|
||||
location / {
|
||||
proxy_pass https://boardmate-api:8080;
|
||||
proxy_ssl_verify off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ public class Message {
|
||||
private String id;
|
||||
private String content;
|
||||
private String clientId;
|
||||
private int timeStamp;
|
||||
private long timeStamp;
|
||||
|
||||
public Message(String id, String content, String clientId, int timeStamp){
|
||||
public Message(String id, String content, String clientId, long timeStamp){
|
||||
this.id = id;
|
||||
this.content = content;
|
||||
this.clientId = clientId;
|
||||
@@ -38,7 +38,7 @@ public class Message {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public int getTimeStamp() {
|
||||
public long getTimeStamp() {
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public class AppSecurityConfig {
|
||||
"/docs/**",
|
||||
"/v1/docs/**",
|
||||
"/swagger-ui.html",
|
||||
"/message/webhook",
|
||||
"/authenticate",
|
||||
"/client/create").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
|
||||
@@ -1,25 +1,45 @@
|
||||
package be.naaturel.boardmateapi.configurations.configurations;
|
||||
|
||||
import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import be.naaturel.boardmateapi.configurations.properties.NgrokProperties;
|
||||
import be.naaturel.boardmateapi.configurations.properties.WebexProperties;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class WebexConfig {
|
||||
|
||||
private final WebexProperties properties;
|
||||
private final WebexProperties webexProperties;
|
||||
private final NgrokProperties ngrokProperties;
|
||||
|
||||
public WebexConfig(WebexProperties properties){
|
||||
this.properties = properties;
|
||||
public WebexConfig(WebexProperties webexProperties, NgrokProperties ngrokProperties){
|
||||
this.webexProperties = webexProperties;
|
||||
this.ngrokProperties = ngrokProperties;
|
||||
}
|
||||
|
||||
@Bean(name = "clientToken")
|
||||
public String clientToken(){
|
||||
return properties.clientToken;
|
||||
public String clientToken() {
|
||||
return webexProperties.clientToken;
|
||||
}
|
||||
|
||||
@Bean(name = "botToken")
|
||||
public String botToken(){
|
||||
return properties.botToken;
|
||||
public String botToken() {
|
||||
return this.webexProperties.botToken;
|
||||
}
|
||||
|
||||
@Bean(name = "sharedSecret")
|
||||
public String sharedSecret() {
|
||||
return webexProperties.sharedSecret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package be.naaturel.boardmateapi.configurations.properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class NgrokProperties {
|
||||
|
||||
@Value("${ngrok.url}")
|
||||
public String url;
|
||||
|
||||
}
|
||||
@@ -12,4 +12,7 @@ public class WebexProperties {
|
||||
@Value("${webex.bot.token}")
|
||||
public String botToken;
|
||||
|
||||
@Value("${webex.shared-secret}")
|
||||
public String sharedSecret;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package be.naaturel.boardmateapi.controllers;
|
||||
|
||||
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
|
||||
import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import be.naaturel.boardmateapi.common.models.Message;
|
||||
import be.naaturel.boardmateapi.common.models.Room;
|
||||
import be.naaturel.boardmateapi.controllers.dtos.MessageDto;
|
||||
import be.naaturel.boardmateapi.controllers.dtos.MessagePostRequestDto;
|
||||
import be.naaturel.boardmateapi.controllers.dtos.ResponseBody;
|
||||
import be.naaturel.boardmateapi.controllers.dtos.WebexWebhook;
|
||||
import be.naaturel.boardmateapi.services.MessageService;
|
||||
import be.naaturel.boardmateapi.services.MqttService;
|
||||
import be.naaturel.boardmateapi.services.WebexService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -19,11 +25,16 @@ public class ChatController {
|
||||
|
||||
private final MessageService messageService;
|
||||
private final WebexService webexService;
|
||||
private final MqttService mqttService;
|
||||
|
||||
@Autowired
|
||||
public ChatController(MessageService messageService, WebexService webexService){
|
||||
public ChatController(
|
||||
MessageService messageService,
|
||||
WebexService webexService,
|
||||
MqttService mqttService){
|
||||
this.messageService = messageService;
|
||||
this.webexService = webexService;
|
||||
this.mqttService = mqttService;
|
||||
}
|
||||
|
||||
@PostMapping("/message/send")
|
||||
@@ -81,4 +92,38 @@ public class ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/message/webhook")
|
||||
public ResponseEntity<ResponseBody<Void>> handleWebhook(
|
||||
@RequestHeader("X-Spark-Signature") String signature,
|
||||
@RequestBody String rawPayload) {
|
||||
|
||||
Logger.displayInfo("========================");
|
||||
ResponseBody<Void> result = ResponseBody.createEmpty();
|
||||
|
||||
if (!webexService.verifySignature(rawPayload, signature)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(result);
|
||||
}
|
||||
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
WebexWebhook payload = mapper.readValue(rawPayload, WebexWebhook.class);
|
||||
|
||||
Room room = webexService.getByRoom(payload.getData().getRoomId());
|
||||
Message msg = webexService.fetchMessage(payload.getData().getId());
|
||||
this.messageService.save(msg);
|
||||
|
||||
this.mqttService.publish("/chat/" + room.getClientId() + "/message", msg);
|
||||
|
||||
Logger.displayInfo(msg.getContent());
|
||||
Logger.displayInfo(msg.getId());
|
||||
Logger.displayInfo(msg.getClientId());
|
||||
|
||||
result.setSuccess(true);
|
||||
return ResponseEntity.ok(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
result.setMessage("Unable to handle request");
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ public class MessageDto {
|
||||
|
||||
private String content;
|
||||
|
||||
private int timestamp;
|
||||
private long timestamp;
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
@@ -15,11 +15,11 @@ public class MessageDto {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package be.naaturel.boardmateapi.controllers.dtos;
|
||||
|
||||
public class WebexWebhook {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String targetUrl;
|
||||
private String resource;
|
||||
private String event;
|
||||
private String orgId;
|
||||
private String createdBy;
|
||||
private String appId;
|
||||
private String ownedBy;
|
||||
private String status;
|
||||
private String created;
|
||||
private String actorId;
|
||||
|
||||
private WebexData data;
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getTargetUrl() { return targetUrl; }
|
||||
public void setTargetUrl(String targetUrl) { this.targetUrl = targetUrl; }
|
||||
|
||||
public String getResource() { return resource; }
|
||||
public void setResource(String resource) { this.resource = resource; }
|
||||
|
||||
public String getEvent() { return event; }
|
||||
public void setEvent(String event) { this.event = event; }
|
||||
|
||||
public String getOrgId() { return orgId; }
|
||||
public void setOrgId(String orgId) { this.orgId = orgId; }
|
||||
|
||||
public String getCreatedBy() { return createdBy; }
|
||||
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
|
||||
|
||||
public String getAppId() { return appId; }
|
||||
public void setAppId(String appId) { this.appId = appId; }
|
||||
|
||||
public String getOwnedBy() { return ownedBy; }
|
||||
public void setOwnedBy(String ownedBy) { this.ownedBy = ownedBy; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getCreated() { return created; }
|
||||
public void setCreated(String created) { this.created = created; }
|
||||
|
||||
public String getActorId() { return actorId; }
|
||||
public void setActorId(String actorId) { this.actorId = actorId; }
|
||||
|
||||
public WebexData getData() { return data; }
|
||||
public void setData(WebexData data) { this.data = data; }
|
||||
|
||||
public static class WebexData {
|
||||
private String id;
|
||||
private String roomId;
|
||||
private String roomType;
|
||||
private String personId;
|
||||
private String personEmail;
|
||||
private String created;
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
|
||||
public String getRoomId() { return roomId; }
|
||||
public void setRoomId(String roomId) { this.roomId = roomId; }
|
||||
|
||||
public String getRoomType() { return roomType; }
|
||||
public void setRoomType(String roomType) { this.roomType = roomType; }
|
||||
|
||||
public String getPersonId() { return personId; }
|
||||
public void setPersonId(String personId) { this.personId = personId; }
|
||||
|
||||
public String getPersonEmail() { return personEmail; }
|
||||
public void setPersonEmail(String personEmail) { this.personEmail = personEmail; }
|
||||
|
||||
public String getCreated() { return created; }
|
||||
public void setCreated(String created) { this.created = created; }
|
||||
}
|
||||
}
|
||||
@@ -9,4 +9,6 @@ import java.util.Optional;
|
||||
|
||||
public interface RoomRepo extends MongoRepository<RoomDto, String> {
|
||||
Optional<RoomDto> findByClientId(String clientId);
|
||||
|
||||
Optional<RoomDto> findByRoomId(String roomdId);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class MessageDto {
|
||||
private String content;
|
||||
|
||||
@Field("timestamp")
|
||||
private int timestamp;
|
||||
private long timestamp;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -43,11 +43,11 @@ public class MessageDto {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package be.naaturel.boardmateapi.repository.dtos;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class WebexMessageDto {
|
||||
|
||||
private String id;
|
||||
private String roomId;
|
||||
private String roomType;
|
||||
private String text;
|
||||
private String personId;
|
||||
private String personEmail;
|
||||
private Instant created;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getRoomId() {
|
||||
return roomId;
|
||||
}
|
||||
|
||||
public void setRoomId(String roomId) {
|
||||
this.roomId = roomId;
|
||||
}
|
||||
|
||||
public String getRoomType() {
|
||||
return roomType;
|
||||
}
|
||||
|
||||
public void setRoomType(String roomType) {
|
||||
this.roomType = roomType;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getPersonId() {
|
||||
return personId;
|
||||
}
|
||||
|
||||
public void setPersonId(String personId) {
|
||||
this.personId = personId;
|
||||
}
|
||||
|
||||
public String getPersonEmail() {
|
||||
return personEmail;
|
||||
}
|
||||
|
||||
public void setPersonEmail(String personEmail) {
|
||||
this.personEmail = personEmail;
|
||||
}
|
||||
|
||||
public Instant getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
@JsonProperty("created")
|
||||
public void setCreated(String created) {
|
||||
this.created = Instant.parse(created);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package be.naaturel.boardmateapi.services;
|
||||
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
|
||||
import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import be.naaturel.boardmateapi.common.models.MqttMessageWrapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -32,14 +33,23 @@ public class MqttService {
|
||||
this.onMessageReceived = consumer;
|
||||
}
|
||||
|
||||
public void publish(String topic, String payload) {
|
||||
public void publish(String topic, Object data) throws ServiceException {
|
||||
try {
|
||||
String payload = new ObjectMapper().writeValueAsString(data);
|
||||
publish(topic, payload);
|
||||
} catch (Exception e){
|
||||
throw new ServiceException("Unable to serialize data", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void publish(String topic, String payload) throws ServiceException {
|
||||
try {
|
||||
connect();
|
||||
MqttMessage message = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8));
|
||||
message.setQos(1);
|
||||
brokerClient.publish(topic, message);
|
||||
} catch (MqttException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new ServiceException("Failed to publish on broker", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,66 +4,93 @@ import be.naaturel.boardmateapi.common.exceptions.ServiceException;
|
||||
import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import be.naaturel.boardmateapi.common.models.Message;
|
||||
import be.naaturel.boardmateapi.common.models.Room;
|
||||
import be.naaturel.boardmateapi.configurations.properties.NgrokProperties;
|
||||
import be.naaturel.boardmateapi.configurations.properties.WebexProperties;
|
||||
import be.naaturel.boardmateapi.repository.RoomRepo;
|
||||
import be.naaturel.boardmateapi.repository.dtos.RoomDto;
|
||||
import be.naaturel.boardmateapi.repository.dtos.WebexMessageDto;
|
||||
import be.naaturel.boardmateapi.repository.mappings.RoomMapper;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class WebexService {
|
||||
|
||||
private final String botToken;
|
||||
private final String clientToken;
|
||||
private final String sharedSecret;
|
||||
private final String ngrokUrl;
|
||||
private final RoomRepo repo;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
@Autowired
|
||||
public WebexService(
|
||||
RoomRepo repo,
|
||||
@Qualifier("botToken") String botToken){
|
||||
WebexProperties webexProperties,
|
||||
NgrokProperties ngrokProperties) {
|
||||
this.repo = repo;
|
||||
this.botToken = botToken;
|
||||
this.botToken = webexProperties.botToken;
|
||||
this.clientToken = webexProperties.clientToken;
|
||||
this.sharedSecret = webexProperties.sharedSecret;
|
||||
this.ngrokUrl = ngrokProperties.url;
|
||||
this.mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initializeWebhooks() {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
|
||||
List<Map<String, Object>> rooms = fetchRooms(client);
|
||||
List<Map<String, Object>> existingWebhooks = fetchWebhooks(client);
|
||||
String targetUrl = ngrokUrl + "/message/webhook";
|
||||
|
||||
for (Map<String, Object> room : rooms) {
|
||||
String roomId = (String) room.get("id");
|
||||
String roomTitle = (String) room.get("title");
|
||||
|
||||
if (!webhookExists(existingWebhooks, roomId, targetUrl)) {
|
||||
createWebhook(client, roomId, targetUrl);
|
||||
Logger.displayInfo("Webhook created for room: " + roomTitle);
|
||||
} else {
|
||||
Logger.displayInfo("Webhook already exists for room: " + roomTitle + "(ID:" + roomId + ")" );
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Logger.displayError("Failed to init webhooks: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void post(Message m) throws ServiceException {
|
||||
try(HttpClient client = HttpClient.newHttpClient()) {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
|
||||
Room room = getClientRoom(m.getClientId());
|
||||
if(room == null){
|
||||
Room room = getByClient(m.getClientId());
|
||||
if (room == null) {
|
||||
room = createRoom(m.getClientId());
|
||||
inviteMemberToRoom(room.getId(), "laurent.crema@student.hepl.be");
|
||||
}
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String jsonBody = mapper.writeValueAsString(
|
||||
Map.of(
|
||||
Map<String, Object> body = Map.of(
|
||||
"roomId", room.getId(),
|
||||
"text", m.getContent()
|
||||
)
|
||||
);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://webexapis.com/v1/messages"))
|
||||
.header("Authorization", "Bearer " + this.botToken)
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response =
|
||||
client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() >= 300) {
|
||||
throw new RuntimeException("Webex error " + response.statusCode() + " : " + response.body());
|
||||
}
|
||||
sendPostRequest(client, "https://webexapis.com/v1/messages", botToken, body);
|
||||
|
||||
} catch (Exception e) {
|
||||
Logger.displayError(Arrays.toString(e.getStackTrace()));
|
||||
@@ -71,44 +98,143 @@ public class WebexService {
|
||||
}
|
||||
}
|
||||
|
||||
public Room createRoom(String clientId) throws ServiceException {
|
||||
public Message fetchMessage(String messageId) throws ServiceException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String jsonBody = mapper.writeValueAsString(Map.of("title", "Support"));
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://webexapis.com/v1/rooms"))
|
||||
.header("Authorization", "Bearer " + this.botToken)
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() >= 300) {
|
||||
throw new RuntimeException("Webex error " + response.statusCode() + " : " + response.body());
|
||||
}
|
||||
|
||||
JsonNode jsonNode = mapper.readTree(response.body());
|
||||
String id = jsonNode.get("id").asText();
|
||||
String title = jsonNode.get("title").asText();
|
||||
RoomDto dto = new RoomDto();
|
||||
dto.setTitle(title);
|
||||
dto.setClientId(clientId);
|
||||
dto.setRoomId(id);
|
||||
repo.save(dto);
|
||||
|
||||
return new Room(id, title, clientId);
|
||||
String response = sendGetRequest(client,
|
||||
"https://webexapis.com/v1/messages/" + messageId, clientToken);
|
||||
WebexMessageDto dto = mapper.readValue(response, WebexMessageDto.class);
|
||||
return new Message(dto.getId(), dto.getText(), dto.getPersonId(), dto.getCreated().getEpochSecond());
|
||||
} catch (Exception e) {
|
||||
Logger.displayError(Arrays.toString(e.getStackTrace()));
|
||||
throw new ServiceException("Failed to create private room : " + e.getMessage(), e);
|
||||
throw new ServiceException("Failed to fetch Webex message", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Room getClientRoom(String clientId){
|
||||
Optional<RoomDto> dto = repo.findByClientId(clientId);
|
||||
return dto.map(RoomMapper::toModel).orElse(null);
|
||||
public Room createRoom(String clientId) throws ServiceException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
Map<String, Object> body = Map.of("title", "Support");
|
||||
String response = sendPostRequest(client, "https://webexapis.com/v1/rooms", botToken, body);
|
||||
|
||||
JsonNode jsonNode = mapper.readTree(response);
|
||||
String id = jsonNode.get("id").asText();
|
||||
String title = jsonNode.get("title").asText();
|
||||
|
||||
RoomDto dto = new RoomDto();
|
||||
dto.setRoomId(id);
|
||||
dto.setTitle(title);
|
||||
dto.setClientId(clientId);
|
||||
repo.save(dto);
|
||||
|
||||
return new Room(id, title, clientId);
|
||||
|
||||
} catch (Exception e) {
|
||||
Logger.displayError(Arrays.toString(e.getStackTrace()));
|
||||
throw new ServiceException("Failed to create room: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void inviteMemberToRoom(String roomId, String email) throws ServiceException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
Map<String, Object> body = Map.of(
|
||||
"roomId", roomId,
|
||||
"personEmail", email
|
||||
);
|
||||
sendPostRequest(client, "https://webexapis.com/v1/memberships", botToken, body);
|
||||
} catch (Exception e) {
|
||||
Logger.displayError(Arrays.toString(e.getStackTrace()));
|
||||
throw new ServiceException("Failed to invite member: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verifySignature(String payload, String signature) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
mac.init(new SecretKeySpec(sharedSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
|
||||
byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] rawHmac = mac.doFinal(payloadBytes);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : rawHmac) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
|
||||
String expected = sb.toString();
|
||||
return expected.equals(signature.trim());
|
||||
} catch (Exception e) {
|
||||
Logger.displayError(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Room getByClient(String clientId) {
|
||||
return repo.findByClientId(clientId).map(RoomMapper::toModel).orElse(null);
|
||||
}
|
||||
|
||||
public Room getByRoom(String roomId) {
|
||||
return repo.findByRoomId(roomId).map(RoomMapper::toModel).orElse(null);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> fetchRooms(HttpClient client) throws Exception {
|
||||
String response = sendGetRequest(client, "https://webexapis.com/v1/rooms", clientToken);
|
||||
JsonNode items = mapper.readTree(response).get("items");
|
||||
return mapper.readValue(items.toString(), List.class);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> fetchWebhooks(HttpClient client) throws Exception {
|
||||
String response = sendGetRequest(client, "https://webexapis.com/v1/webhooks", clientToken);
|
||||
JsonNode items = mapper.readTree(response).get("items");
|
||||
return mapper.readValue(items.toString(), List.class);
|
||||
}
|
||||
|
||||
private void createWebhook(HttpClient client, String roomId, String targetUrl) throws Exception {
|
||||
Map<String, Object> body = Map.of(
|
||||
"name", "BoardMate Webhook",
|
||||
"targetUrl", targetUrl,
|
||||
"resource", "messages",
|
||||
"event", "created",
|
||||
"roomId", roomId,
|
||||
"secret", sharedSecret
|
||||
);
|
||||
sendPostRequest(client, "https://webexapis.com/v1/webhooks", clientToken, body);
|
||||
}
|
||||
|
||||
private boolean webhookExists(List<Map<String, Object>> webhooks, String roomId, String targetUrl) {
|
||||
return webhooks.stream().anyMatch(w ->
|
||||
targetUrl.equals(w.get("targetUrl")) &&
|
||||
"messages".equals(w.get("resource")) &&
|
||||
"created".equals(w.get("event")) &&
|
||||
roomId.equals(w.get("roomId"))
|
||||
);
|
||||
}
|
||||
|
||||
private String sendGetRequest(HttpClient client, String url, String token) throws Exception {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() >= 300) {
|
||||
throw new RuntimeException("Webex GET error " + response.statusCode() + " : " + response.body());
|
||||
}
|
||||
return response.body();
|
||||
}
|
||||
|
||||
private String sendPostRequest(HttpClient client, String url, String token, Map<String, Object> body) throws Exception {
|
||||
String jsonBody = mapper.writeValueAsString(body);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() >= 300) {
|
||||
throw new RuntimeException("Webex POST error " + response.statusCode() + " : " + response.body());
|
||||
}
|
||||
return response.body();
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,11 @@ mqtt.username=${BROKER_USERNAME}
|
||||
mqtt.password=${BROKER_PASSWORD}
|
||||
|
||||
#=============WEBEX=============
|
||||
webex.client.token=N2E0ODMyZDUtY2JmZi00YjlhLWFjZmEtOTU0MmFlNjY3ZDE2M2ZhYWYzNzAtNzFm_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de
|
||||
webex.bot.token=MGM4ZDYzYzctZTZiMi00MjNlLWI3YzEtOTFhNDlmOGM1YzVjYWJhYTk0NzctNjBj_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de
|
||||
webex.client.token=${WEBEX_CLIENT_TOKEN}
|
||||
webex.bot.token=${WEBEX_BOT_TOKEN}
|
||||
webex.shared-secret=${WEBEX_SHARED_SECRET}
|
||||
#=============NGROK=============
|
||||
ngrok.url=${NGROK_URL}
|
||||
|
||||
#=============METRICS=============
|
||||
management.endpoint.health.show-details=always
|
||||
|
||||
Reference in New Issue
Block a user