diff --git a/docker-compose.yml b/docker-compose.yml index 062c7e3..7a3ed20 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,7 @@ services: - DB_USER=recording - DB_PASSWORD=recording - APPENDER_TYPE=json-console + - KIBANA=true depends_on: - db - elasticsearch diff --git a/pom.xml b/pom.xml index 83a95c8..a751e9a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ recording-bot com.wire.bots - 0.3.2 + 0.3.3 Recording Bot Recording Bot Service For Wire @@ -103,6 +103,21 @@ zip4j ${zip4j.version} + + io.prometheus + simpleclient + ${prometheus.version} + + + io.prometheus + simpleclient_dropwizard + ${prometheus.version} + + + io.prometheus + simpleclient_servlet + ${prometheus.version} + diff --git a/recording.yaml b/recording.yaml index 0f5a92b..e43963e 100644 --- a/recording.yaml +++ b/recording.yaml @@ -20,6 +20,9 @@ token: ${SERVICE_TOKEN:-} apiHost: ${WIRE_API_HOST:-https://prod-nginz-https.wire.com} url: ${PUBLIC_URL:-https://recording.services.wire.com} salt: ${SALT:-abc} +kibana: ${KIBANA:-} +delete: ${DELETE:-} +edit: ${EDIT:-} database: driverClass: org.postgresql.Driver diff --git a/src/main/java/com/wire/bots/recording/DAO/EventsDAO.java b/src/main/java/com/wire/bots/recording/DAO/EventsDAO.java index 71d35f5..5b05d32 100644 --- a/src/main/java/com/wire/bots/recording/DAO/EventsDAO.java +++ b/src/main/java/com/wire/bots/recording/DAO/EventsDAO.java @@ -35,4 +35,7 @@ int insert(@Bind("messageId") UUID messageId, @SqlUpdate("DELETE FROM Events WHERE messageId = :messageId") int delete(@Bind("messageId") UUID messageId); + + @SqlUpdate("DELETE FROM Events WHERE conversationId = :conversationId") + int clear(@Bind("conversationId") UUID conversationId); } diff --git a/src/main/java/com/wire/bots/recording/MessageHandler.java b/src/main/java/com/wire/bots/recording/MessageHandler.java index b1f34f6..2ed5ee3 100644 --- a/src/main/java/com/wire/bots/recording/MessageHandler.java +++ b/src/main/java/com/wire/bots/recording/MessageHandler.java @@ -13,9 +13,7 @@ import com.wire.lithium.ClientRepo; import com.wire.xenon.MessageHandlerBase; import com.wire.xenon.WireClient; -import com.wire.xenon.assets.FileAsset; -import com.wire.xenon.assets.FileAssetPreview; -import com.wire.xenon.assets.MessageText; +import com.wire.xenon.assets.*; import com.wire.xenon.backend.models.Conversation; import com.wire.xenon.backend.models.Member; import com.wire.xenon.backend.models.NewBot; @@ -45,7 +43,8 @@ public class MessageHandler extends MessageHandlerBase { private static final String HELP = "Available commands:\n" + "`/pdf` - receive previous messages in PDF format\n" + "`/public` - publish this conversation\n" + - "`/private` - stop publishing this conversation"; + "`/private` - stop publishing this conversation\n" + + "`/clear` - clear the history"; private final ChannelsDAO channelsDAO; private final StorageFactory storageFactory; @@ -183,7 +182,9 @@ public void onText(WireClient client, TextMessage msg) { persist(convId, userId, botId, messageId, type, msg); - kibana(type, msg, client); + if (config.kibana) { + kibana(type, msg, client); + } } catch (Exception e) { Logger.exception(e, "OnText: %s", client.getId()); } @@ -204,10 +205,15 @@ public void onEditText(WireClient client, EditedTextMessage msg) { try { persist(convId, userId, botId, messageId, type, msg); - /* UUID replacingMessageId = msg.getReplacingMessageId(); - int update = eventsDAO.update(replacingMessageId, type, payload); - */ - kibana(type, msg, client); + + if (config.edit) { + UUID replacingMessageId = msg.getReplacingMessageId(); + eventsDAO.update(replacingMessageId, type, msg.getText()); + } + + if (config.kibana) { + kibana(type, msg, client); + } } catch (Exception e) { Logger.exception(e, "onEditText: %s", client.getId()); } @@ -222,7 +228,10 @@ public void onDelete(WireClient client, DeletedTextMessage msg) { String type = "conversation.otr-message-add.delete-text"; persist(convId, userId, botId, messageId, type, msg); - //eventsDAO.delete(msg.getDeletedMessageId()); + + if (config.delete) { + eventsDAO.delete(msg.getDeletedMessageId()); + } } @Override @@ -344,13 +353,18 @@ public void onUserUpdate(UUID id, UUID userId) { } @Override - public void onEvent(WireClient client, UUID userId, Messages.GenericMessage genericMessage) { + public void onEvent(WireClient client, UUID userId, Messages.GenericMessage event) { UUID botId = client.getId(); UUID convId = client.getConversationId(); Logger.info("onEvent: bot: %s, conv: %s, from: %s", botId, convId, userId); generateHtml(client, botId, convId); + + // User clicked on a Poll Button + if (event.hasButtonAction()) { + handlePollAction(client, userId, event); + } } private void generateHtml(WireClient client, UUID botId, UUID convId) { @@ -367,6 +381,31 @@ private void generateHtml(WireClient client, UUID botId, UUID convId) { } } + private void handlePollAction(WireClient client, UUID userId, Messages.GenericMessage event) { + try { + final UUID conversationId = client.getConversationId(); + final Messages.ButtonAction action = event.getButtonAction(); + final UUID pollId = UUID.fromString(action.getReferenceMessageId()); + final String answer = action.getButtonId(); + + ButtonActionConfirmation confirmation = new ButtonActionConfirmation( + pollId, + answer); + + client.send(confirmation, userId); + + if (Objects.equals(answer, "yes")) { + int records = eventsDAO.clear(conversationId); + client.send(new MessageText(String.format("Deleted %d messages", records)), userId); + } + + // Delete the Poll message + client.send(new MessageDelete(pollId)); + } catch (Exception e) { + Logger.exception(e, "handlePollAction: %s", client.getId()); + } + } + private boolean command(WireClient client, UUID userId, UUID botId, UUID convId, String cmd) throws Exception { // Only owner of the bot can run commands NewBot state = storageFactory.create(botId).getState(); @@ -417,6 +456,14 @@ private boolean command(WireClient client, UUID userId, UUID botId, UUID convId, client.send(fileAsset, userId); return true; } + case "/clear": { + Poll poll = new Poll(); + poll.addText("Are you sure you want to delete the whole history for this group?\nThis cannot be undone!"); + poll.addButton("yes", "Yes"); + poll.addButton("no", "No"); + client.send(poll, userId); + return true; + } case "/public": { channelsDAO.insert(convId, botId); String key = Helper.key(convId.toString(), config.salt); diff --git a/src/main/java/com/wire/bots/recording/Service.java b/src/main/java/com/wire/bots/recording/Service.java index f1b56ff..bb69cee 100644 --- a/src/main/java/com/wire/bots/recording/Service.java +++ b/src/main/java/com/wire/bots/recording/Service.java @@ -30,11 +30,12 @@ import io.dropwizard.configuration.SubstitutingSourceProvider; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; -import io.federecio.dropwizard.swagger.SwaggerBundle; -import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration; - import java.util.concurrent.ExecutorService; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.dropwizard.DropwizardExports; +import io.prometheus.client.exporter.MetricsServlet; + public class Service extends Server { public static Service instance; @@ -71,6 +72,9 @@ protected MessageHandlerBase createHandler(Config config, Environment env) { } protected void onRun(Config config, Environment env) { + CollectorRegistry.defaultRegistry.register(new DropwizardExports(env.metrics())); + env.getApplicationContext().addServlet(MetricsServlet.class, "/metrics"); + ExecutorService warmup = env.lifecycle().executorService("warmup").build(); warmup.submit(() -> messageHandler.warmup(getRepo())); } diff --git a/src/main/java/com/wire/bots/recording/model/Config.java b/src/main/java/com/wire/bots/recording/model/Config.java index 3b5840c..b49a042 100644 --- a/src/main/java/com/wire/bots/recording/model/Config.java +++ b/src/main/java/com/wire/bots/recording/model/Config.java @@ -29,4 +29,8 @@ public class Config extends Configuration { public String url; @NotNull public String salt; + public boolean kibana; + + public boolean delete = true; + public boolean edit = true; }