Integrate webex webhooks
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user