241 lines
9.6 KiB
Java
241 lines
9.6 KiB
Java
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.Client;
|
|
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.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;
|
|
|
|
@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,
|
|
WebexProperties webexProperties,
|
|
NgrokProperties ngrokProperties) {
|
|
this.repo = repo;
|
|
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()) {
|
|
|
|
Room room = getRoomByClient(m.getClientId());
|
|
if (room == null) {
|
|
room = createRoom(m.getClientId());
|
|
inviteMemberToRoom(room.getId(), "laurent.crema@student.hepl.be");
|
|
}
|
|
|
|
Map<String, Object> body = Map.of(
|
|
"roomId", room.getId(),
|
|
"text", m.getContent()
|
|
);
|
|
|
|
sendPostRequest(client, "https://webexapis.com/v1/messages", botToken, body);
|
|
|
|
} catch (Exception e) {
|
|
Logger.displayError(Arrays.toString(e.getStackTrace()));
|
|
throw new ServiceException("Failed to post message");
|
|
}
|
|
}
|
|
|
|
public Message fetchMessage(String messageId) throws ServiceException {
|
|
try (HttpClient client = HttpClient.newHttpClient()) {
|
|
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) {
|
|
throw new ServiceException("Failed to fetch Webex message", e);
|
|
}
|
|
}
|
|
|
|
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 getRoomByClient(String clientId) {
|
|
return repo.findByClientId(clientId).map(RoomMapper::toModel).orElse(null);
|
|
}
|
|
|
|
public Room getRoomById(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();
|
|
}
|
|
} |