From b41ad56fcf1151441e7a2549ffbced77fa6c62a9 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud Date: Mon, 14 Aug 2023 20:32:14 +0200 Subject: [PATCH 1/5] feat: claim drop on message received --- .../api/ws/data/message/DropProgress.java | 25 ++++++++ .../api/ws/data/message/IPubSubMessage.java | 1 + .../dropprogress/DropProgressData.java | 29 ++++++++++ .../api/ws/data/request/topic/TopicName.java | 3 +- .../miner/factory/MinerFactory.java | 4 ++ .../factory/PubSubMessageHandlerFactory.java | 7 +++ .../miner/handler/ClaimDropHandler.java | 20 +++++++ .../handler/PubSubMessageHandlerAdapter.java | 3 + .../channelpointsminer/miner/miner/Miner.java | 16 +++-- ...witchPubSubWebSocketClientMessageTest.java | 26 +++++++++ .../miner/factory/MinerFactoryTest.java | 44 +++++++++----- .../PubSubMessageHandlerFactoryTest.java | 9 +++ .../miner/handler/ClaimDropHandlerTest.java | 58 +++++++++++++++++++ .../miner/miner/MinerTest.java | 18 ++++-- .../test/resources/api/ws/userDropEvent.json | 7 +++ 15 files changed, 242 insertions(+), 28 deletions(-) create mode 100644 miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropProgress.java create mode 100644 miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropprogress/DropProgressData.java create mode 100644 miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java create mode 100644 miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java create mode 100644 miner/src/test/resources/api/ws/userDropEvent.json diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropProgress.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropProgress.java new file mode 100644 index 00000000..2ea2e182 --- /dev/null +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropProgress.java @@ -0,0 +1,25 @@ +package fr.rakambda.channelpointsminer.miner.api.ws.data.message; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropprogress.DropProgressData; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; + +@JsonTypeName("drop-progress") +@Getter +@EqualsAndHashCode(callSuper = true) +@ToString +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DropProgress extends IPubSubMessage{ + @JsonProperty("data") + @NotNull + private DropProgressData data; +} diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java index 59d661c3..6edfcdfe 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java @@ -36,6 +36,7 @@ @JsonSubTypes.Type(value = CommunityMomentStart.class, name = "active"), @JsonSubTypes.Type(value = ActiveMultipliersUpdated.class, name = "active-multipliers-updated"), @JsonSubTypes.Type(value = ReadNotifications.class, name = "read-notifications"), + @JsonSubTypes.Type(value = DropProgress.class, name = "drop-progress"), }) @EqualsAndHashCode @ToString diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropprogress/DropProgressData.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropprogress/DropProgressData.java new file mode 100644 index 00000000..058b75c2 --- /dev/null +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropprogress/DropProgressData.java @@ -0,0 +1,29 @@ +package fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropprogress; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@EqualsAndHashCode +@ToString +@Builder +public class DropProgressData{ + @JsonProperty("drop_id") + @NotNull + private String dropId; + @JsonProperty("channel_id") + @NotNull + private String channelId; + @JsonProperty("current_progress_min") + private int currentProgressMin; + @JsonProperty("required_progress_min") + private int requiredProgressMin; +} diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/request/topic/TopicName.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/request/topic/TopicName.java index 09da20c5..63411152 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/request/topic/TopicName.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/request/topic/TopicName.java @@ -15,7 +15,8 @@ public enum TopicName{ RAID("raid", false), PREDICTIONS_CHANNEL_V1("predictions-channel-v1", false), ONSITE_NOTIFICATIONS("onsite-notifications", true), - COMMUNITY_MOMENTS_CHANNEL_V1("community-moments-channel-v1", false); + COMMUNITY_MOMENTS_CHANNEL_V1("community-moments-channel-v1", false), + USER_DROP_EVENTS("user-drop-events", true); @Getter(onMethod_ = @JsonValue) private final String value; diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java index 395024c4..3f601b16 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java @@ -30,12 +30,16 @@ public static Miner create(@NotNull AccountConfiguration config, @NotNull IEvent database, eventManager); + var syncInventory = MinerRunnableFactory.createSyncInventory(miner, eventManager); + miner.setSyncInventory(syncInventory); + miner.addPubSubHandler(PubSubMessageHandlerFactory.createClaimAvailableHandler(miner, eventManager)); miner.addPubSubHandler(PubSubMessageHandlerFactory.createStreamStartEndHandler(miner, eventManager)); miner.addPubSubHandler(PubSubMessageHandlerFactory.createFollowRaidHandler(miner)); miner.addPubSubHandler(PubSubMessageHandlerFactory.createPredictionsHandler(miner, BetPlacerFactory.created(miner), eventManager)); miner.addPubSubHandler(PubSubMessageHandlerFactory.createPointsHandler(miner, eventManager)); miner.addPubSubHandler(PubSubMessageHandlerFactory.createClaimMomentHandler(miner, eventManager)); + miner.addPubSubHandler(PubSubMessageHandlerFactory.createClaimDropHandler(syncInventory)); eventManager.addEventHandler(LogEventListenerFactory.createLogger()); if(Objects.nonNull(config.getDiscord().getUrl())){ diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java index 5d692e2a..6a16eec1 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java @@ -2,6 +2,7 @@ import fr.rakambda.channelpointsminer.miner.event.manager.IEventManager; import fr.rakambda.channelpointsminer.miner.handler.ClaimAvailableHandler; +import fr.rakambda.channelpointsminer.miner.handler.ClaimDropHandler; import fr.rakambda.channelpointsminer.miner.handler.ClaimMomentHandler; import fr.rakambda.channelpointsminer.miner.handler.FollowRaidHandler; import fr.rakambda.channelpointsminer.miner.handler.IPubSubMessageHandler; @@ -10,6 +11,7 @@ import fr.rakambda.channelpointsminer.miner.handler.StreamStartEndHandler; import fr.rakambda.channelpointsminer.miner.miner.IMiner; import fr.rakambda.channelpointsminer.miner.prediction.bet.BetPlacer; +import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; import lombok.NoArgsConstructor; import org.jetbrains.annotations.NotNull; import static lombok.AccessLevel.PRIVATE; @@ -45,4 +47,9 @@ public static IPubSubMessageHandler createPointsHandler(@NotNull IMiner miner, @ public static IPubSubMessageHandler createClaimMomentHandler(@NotNull IMiner miner, @NotNull IEventManager eventManager){ return new ClaimMomentHandler(miner, eventManager); } + + @NotNull + public static IPubSubMessageHandler createClaimDropHandler(@NotNull SyncInventory syncInventory){ + return new ClaimDropHandler(syncInventory); + } } diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java new file mode 100644 index 00000000..a05d961c --- /dev/null +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java @@ -0,0 +1,20 @@ +package fr.rakambda.channelpointsminer.miner.handler; + +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; +import fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.Topic; +import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; + +@RequiredArgsConstructor +public class ClaimDropHandler extends PubSubMessageHandlerAdapter{ + @NotNull + private final SyncInventory syncInventory; + + @Override + public void onDropProgress(@NotNull Topic topic, @NotNull DropProgress message){ + if(message.getData().getCurrentProgressMin() >= message.getData().getRequiredProgressMin()){ + syncInventory.run(); + } + } +} diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java index 98a1d326..e2671a16 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java @@ -7,6 +7,7 @@ import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CommunityMomentStart; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CreateNotification; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DeleteNotification; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.EventCreated; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.EventUpdated; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.GlobalLastViewedContentUpdated; @@ -85,6 +86,8 @@ public void onCommunityMomentStart(@NotNull Topic topic, @NotNull CommunityMomen public void onReadNotifications(@NotNull Topic topic, @NotNull ReadNotifications message){} + public void onDropProgress(@NotNull Topic topic, @NotNull DropProgress message){} + @Override public void handle(@NotNull Topic topic, @NotNull IPubSubMessage message){ for(var clazz : ClassWalker.range(message.getClass(), IPubSubMessage.class)){ diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/miner/Miner.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/miner/Miner.java index ed65e0d1..0ded2553 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/miner/Miner.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/miner/Miner.java @@ -30,6 +30,7 @@ import fr.rakambda.channelpointsminer.miner.streamer.Streamer; import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import lombok.extern.log4j.Log4j2; import org.apache.logging.log4j.ThreadContext; import org.jetbrains.annotations.NotNull; @@ -51,6 +52,7 @@ import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.PREDICTIONS_CHANNEL_V1; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.PREDICTIONS_USER_V1; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.RAID; +import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.USER_DROP_EVENTS; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.VIDEO_PLAYBACK_BY_ID; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -78,6 +80,8 @@ public class Miner implements AutoCloseable, IMiner, ITwitchPubSubMessageListene private final MinerData minerData; private UpdateStreamInfo updateStreamInfo; + @Setter + @Getter(value = AccessLevel.PUBLIC, onMethod_ = {@TestOnly}) private SyncInventory syncInventory; @Getter @@ -126,7 +130,7 @@ public void start(){ scheduledExecutor.scheduleWithFixedDelay(getUpdateStreamInfo(), 0, 2, MINUTES); scheduledExecutor.scheduleWithFixedDelay(MinerRunnableFactory.createSendMinutesWatched(this), 0, 1, MINUTES); scheduledExecutor.scheduleAtFixedRate(MinerRunnableFactory.createWebSocketPing(this), 25, 25, SECONDS); - scheduledExecutor.scheduleAtFixedRate(getSyncInventory(), 1, 15, MINUTES); + scheduledExecutor.scheduleAtFixedRate(syncInventory, 1, 15, MINUTES); var streamerConfigurationReload = MinerRunnableFactory.createStreamerConfigurationReload(this, eventManager, streamerSettingsFactory, accountConfiguration.isLoadFollows()); if(accountConfiguration.getReloadEvery() > 0){ @@ -180,14 +184,6 @@ private UpdateStreamInfo getUpdateStreamInfo(){ return updateStreamInfo; } - @NotNull - private SyncInventory getSyncInventory(){ - if(Objects.isNull(syncInventory)){ - syncInventory = MinerRunnableFactory.createSyncInventory(this, eventManager); - } - return syncInventory; - } - private void listenTopic(@NotNull TopicName name, @NotNull String target){ pubSubWebSocketPool.listenTopic(Topics.buildFromName(name, target, twitchLogin.getAccessToken())); } @@ -223,6 +219,7 @@ public void updateStreamer(@NotNull Streamer streamer){ } listenTopic(VIDEO_PLAYBACK_BY_ID, streamer.getId()); + listenTopic(USER_DROP_EVENTS, streamer.getId()); if(streamer.getSettings().isMakePredictions()){ listenTopic(PREDICTIONS_USER_V1, getTwitchLogin().fetchUserId(gqlApi)); @@ -267,6 +264,7 @@ public boolean removeStreamer(@NotNull Streamer streamer){ removeTopic(PREDICTIONS_CHANNEL_V1, streamer.getId()); removeTopic(COMMUNITY_MOMENTS_CHANNEL_V1, streamer.getId()); removeTopic(RAID, streamer.getId()); + removeTopic(USER_DROP_EVENTS, streamer.getId()); chatClient.leave(streamer.getUsername()); eventManager.onEvent(new StreamerRemovedEvent(streamer, TimeFactory.now())); diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java index 090e287b..6fad49e2 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java @@ -8,6 +8,7 @@ import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CommunityMomentStart; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CreateNotification; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DeleteNotification; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.PointsEarned; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.PointsSpent; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.PredictionMade; @@ -34,6 +35,7 @@ import fr.rakambda.channelpointsminer.miner.api.ws.data.message.createnotification.TextContent; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.createnotification.TextNotificationDataBlock; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.deletenotification.DeleteNotificationData; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropprogress.DropProgressData; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.pointsearned.Balance; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.pointsearned.PointsEarnedData; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.pointsspent.PointsSpentData; @@ -80,6 +82,7 @@ import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.ONSITE_NOTIFICATIONS; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.PREDICTIONS_USER_V1; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.RAID; +import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.USER_DROP_EVENTS; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.VIDEO_PLAYBACK_BY_ID; import static java.time.ZoneOffset.UTC; import static org.mockito.Mockito.timeout; @@ -812,6 +815,29 @@ void onReadNotifications(WebsocketMockServer server){ verify(listener, timeout(MESSAGE_TIMEOUT)).onWebSocketMessage(expected); } + @Test + void onUserDropEvent(WebsocketMockServer server){ + server.send(TestUtils.getAllResourceContent("api/ws/userDropEvent.json")); + + var expected = MessageResponse.builder() + .data(MessageData.builder() + .topic(Topic.builder() + .name(USER_DROP_EVENTS) + .target("123456789") + .build()) + .message(DropProgress.builder() + .data(DropProgressData.builder() + .dropId("drop-id") + .channelId("123456789") + .currentProgressMin(1) + .requiredProgressMin(15) + .build()) + .build()) + .build()) + .build(); + verify(listener, timeout(MESSAGE_TIMEOUT)).onWebSocketMessage(expected); + } + @BeforeEach void setUp(WebsocketMockServer server) throws InterruptedException{ var uri = URI.create("ws://127.0.0.1:" + server.getPort()); diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactoryTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactoryTest.java index cd62bbc1..caf17fd8 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactoryTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactoryTest.java @@ -11,6 +11,7 @@ import fr.rakambda.channelpointsminer.miner.database.IDatabase; import fr.rakambda.channelpointsminer.miner.event.manager.IEventManager; import fr.rakambda.channelpointsminer.miner.handler.ClaimAvailableHandler; +import fr.rakambda.channelpointsminer.miner.handler.ClaimDropHandler; import fr.rakambda.channelpointsminer.miner.handler.ClaimMomentHandler; import fr.rakambda.channelpointsminer.miner.handler.FollowRaidHandler; import fr.rakambda.channelpointsminer.miner.handler.PointsHandler; @@ -18,8 +19,9 @@ import fr.rakambda.channelpointsminer.miner.handler.StreamStartEndHandler; import fr.rakambda.channelpointsminer.miner.log.LoggerEventListener; import fr.rakambda.channelpointsminer.miner.log.discord.DiscordEventListener; +import fr.rakambda.channelpointsminer.miner.miner.IMiner; +import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; import fr.rakambda.channelpointsminer.miner.tests.ParallelizableTest; -import org.assertj.core.api.Assertions; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.junit.jupiter.api.BeforeEach; @@ -28,8 +30,10 @@ import java.net.MalformedURLException; import java.net.URL; import java.sql.SQLException; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; @@ -62,6 +66,8 @@ class MinerFactoryTest{ private ILoginMethod loginMethod; @Mock private IEventManager eventManager; + @Mock + private SyncInventory syncInventory; @BeforeEach void setUp(){ @@ -74,19 +80,23 @@ void setUp(){ @Test void nominal(){ - try(var apiFactory = mockStatic(ApiFactory.class)){ + try(var apiFactory = mockStatic(ApiFactory.class); + var minerRunnableFactory = mockStatic(MinerRunnableFactory.class)){ apiFactory.when(() -> ApiFactory.createLoginProvider(USERNAME, loginMethod, eventManager)).thenReturn(passportApi); + minerRunnableFactory.when(() -> MinerRunnableFactory.createSyncInventory(any(IMiner.class), eq(eventManager))).thenReturn(syncInventory); var miner = MinerFactory.create(accountConfiguration, eventManager); - Assertions.assertThat(miner.getPubSubMessageHandlers()) - .hasSize(6) + assertThat(miner.getSyncInventory()).isEqualTo(syncInventory); + assertThat(miner.getPubSubMessageHandlers()) + .hasSize(7) .hasAtLeastOneElementOfType(ClaimAvailableHandler.class) .hasAtLeastOneElementOfType(StreamStartEndHandler.class) .hasAtLeastOneElementOfType(FollowRaidHandler.class) .hasAtLeastOneElementOfType(PredictionsHandler.class) .hasAtLeastOneElementOfType(PointsHandler.class) - .hasAtLeastOneElementOfType(ClaimMomentHandler.class); + .hasAtLeastOneElementOfType(ClaimMomentHandler.class) + .hasAtLeastOneElementOfType(ClaimDropHandler.class); verify(eventManager).addEventHandler(any(LoggerEventListener.class)); verifyNoMoreInteractions(eventManager); @@ -97,24 +107,28 @@ void nominal(){ @Test void nominalWithDiscord() throws MalformedURLException{ - try(var apiFactory = mockStatic(ApiFactory.class)){ + try(var apiFactory = mockStatic(ApiFactory.class); + var minerRunnableFactory = mockStatic(MinerRunnableFactory.class)){ var discordWebhook = new URL("https://discord-webhook"); apiFactory.when(() -> ApiFactory.createLoginProvider(USERNAME, loginMethod, eventManager)).thenReturn(passportApi); apiFactory.when(() -> ApiFactory.createdDiscordApi(discordWebhook)).thenReturn(discordApi); + minerRunnableFactory.when(() -> MinerRunnableFactory.createSyncInventory(any(IMiner.class), eq(eventManager))).thenReturn(syncInventory); when(discordConfiguration.getUrl()).thenReturn(discordWebhook); var miner = MinerFactory.create(accountConfiguration, eventManager); - Assertions.assertThat(miner.getPubSubMessageHandlers()) - .hasSize(6) + assertThat(miner.getSyncInventory()).isEqualTo(syncInventory); + assertThat(miner.getPubSubMessageHandlers()) + .hasSize(7) .hasAtLeastOneElementOfType(ClaimAvailableHandler.class) .hasAtLeastOneElementOfType(StreamStartEndHandler.class) .hasAtLeastOneElementOfType(FollowRaidHandler.class) .hasAtLeastOneElementOfType(PredictionsHandler.class) .hasAtLeastOneElementOfType(PointsHandler.class) - .hasAtLeastOneElementOfType(ClaimMomentHandler.class); + .hasAtLeastOneElementOfType(ClaimMomentHandler.class) + .hasAtLeastOneElementOfType(ClaimDropHandler.class); verify(eventManager).addEventHandler(any(LoggerEventListener.class)); verify(eventManager).addEventHandler(any(DiscordEventListener.class)); @@ -127,24 +141,28 @@ void nominalWithDiscord() throws MalformedURLException{ @Test void nominalWithAnalytics() throws SQLException{ try(var apiFactory = mockStatic(ApiFactory.class); - var databaseFactory = mockStatic(DatabaseFactory.class)){ + var databaseFactory = mockStatic(DatabaseFactory.class); + var minerRunnableFactory = mockStatic(MinerRunnableFactory.class)){ apiFactory.when(() -> ApiFactory.createLoginProvider(USERNAME, loginMethod, eventManager)).thenReturn(passportApi); databaseFactory.when(() -> DatabaseFactory.createDatabase(databaseConfiguration)).thenReturn(database); databaseFactory.when(() -> DatabaseFactory.createDatabaseHandler(database, RECORD_USER_PREDICTIONS)).thenReturn(databaseEventHandler); + minerRunnableFactory.when(() -> MinerRunnableFactory.createSyncInventory(any(IMiner.class), eq(eventManager))).thenReturn(syncInventory); when(analyticsConfiguration.isEnabled()).thenReturn(true); when(analyticsConfiguration.getDatabase()).thenReturn(databaseConfiguration); var miner = MinerFactory.create(accountConfiguration, eventManager); - Assertions.assertThat(miner.getPubSubMessageHandlers()) - .hasSize(6) + assertThat(miner.getSyncInventory()).isEqualTo(syncInventory); + assertThat(miner.getPubSubMessageHandlers()) + .hasSize(7) .hasAtLeastOneElementOfType(ClaimAvailableHandler.class) .hasAtLeastOneElementOfType(StreamStartEndHandler.class) .hasAtLeastOneElementOfType(FollowRaidHandler.class) .hasAtLeastOneElementOfType(PredictionsHandler.class) .hasAtLeastOneElementOfType(PointsHandler.class) - .hasAtLeastOneElementOfType(ClaimMomentHandler.class); + .hasAtLeastOneElementOfType(ClaimMomentHandler.class) + .hasAtLeastOneElementOfType(ClaimDropHandler.class); verify(eventManager).addEventHandler(any(LoggerEventListener.class)); verify(eventManager).addEventHandler(any(DatabaseEventHandler.class)); diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java index da7323da..6466b643 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java @@ -2,6 +2,7 @@ import fr.rakambda.channelpointsminer.miner.event.manager.IEventManager; import fr.rakambda.channelpointsminer.miner.handler.ClaimAvailableHandler; +import fr.rakambda.channelpointsminer.miner.handler.ClaimDropHandler; import fr.rakambda.channelpointsminer.miner.handler.ClaimMomentHandler; import fr.rakambda.channelpointsminer.miner.handler.FollowRaidHandler; import fr.rakambda.channelpointsminer.miner.handler.PointsHandler; @@ -9,6 +10,7 @@ import fr.rakambda.channelpointsminer.miner.handler.StreamStartEndHandler; import fr.rakambda.channelpointsminer.miner.miner.IMiner; import fr.rakambda.channelpointsminer.miner.prediction.bet.BetPlacer; +import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; import fr.rakambda.channelpointsminer.miner.tests.ParallelizableTest; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -25,6 +27,8 @@ class PubSubMessageHandlerFactoryTest{ private BetPlacer betPlacer; @Mock private IEventManager eventManager; + @Mock + private SyncInventory syncInventory; @Test void createClaimAvailable(){ @@ -55,4 +59,9 @@ void createPointsHandler(){ void createClaimMomentHandler(){ assertThat(PubSubMessageHandlerFactory.createClaimMomentHandler(miner, eventManager)).isNotNull().isInstanceOf(ClaimMomentHandler.class); } + + @Test + void createClaimDropHandler(){ + assertThat(PubSubMessageHandlerFactory.createClaimDropHandler(syncInventory)).isNotNull().isInstanceOf(ClaimDropHandler.class); + } } \ No newline at end of file diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java new file mode 100644 index 00000000..7ab149dc --- /dev/null +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java @@ -0,0 +1,58 @@ +package fr.rakambda.channelpointsminer.miner.handler; + +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropprogress.DropProgressData; +import fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.Topic; +import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; +import fr.rakambda.channelpointsminer.miner.tests.ParallelizableTest; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ParallelizableTest +@ExtendWith(MockitoExtension.class) +class ClaimDropHandlerTest{ + @InjectMocks + private ClaimDropHandler tested; + + @Mock + private SyncInventory syncInventory; + @Mock + private Topic topic; + @Mock + private DropProgress dropProgress; + @Mock + private DropProgressData dropProgressData; + + @BeforeEach + void setUp(){ + lenient().when(dropProgress.getData()).thenReturn(dropProgressData); + } + + @Test + void dropNotProgressedEnough(){ + when(dropProgressData.getCurrentProgressMin()).thenReturn(1); + when(dropProgressData.getRequiredProgressMin()).thenReturn(2); + + tested.onDropProgress(topic, dropProgress); + + verify(syncInventory, never()).run(); + } + + @Test + void dropProgressedEnough(){ + when(dropProgressData.getCurrentProgressMin()).thenReturn(2); + when(dropProgressData.getRequiredProgressMin()).thenReturn(2); + + tested.onDropProgress(topic, dropProgress); + + verify(syncInventory).run(); + } +} \ No newline at end of file diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/miner/MinerTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/miner/MinerTest.java index a9a09262..264943e5 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/miner/MinerTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/miner/MinerTest.java @@ -51,6 +51,7 @@ import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.PREDICTIONS_CHANNEL_V1; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.PREDICTIONS_USER_V1; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.RAID; +import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.USER_DROP_EVENTS; import static fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.TopicName.VIDEO_PLAYBACK_BY_ID; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -130,6 +131,7 @@ class MinerTest{ @BeforeEach void setUp() throws LoginException, IOException{ tested = new Miner(accountConfiguration, passportApi, streamerSettingsFactory, webSocketPool, scheduledExecutorService, executorService, database, eventManager); + tested.setSyncInventory(syncInventory); lenient().when(accountConfiguration.getUsername()).thenReturn(USERNAME); lenient().when(accountConfiguration.getReloadEvery()).thenReturn(0); @@ -406,6 +408,7 @@ void addStreamerWithPredictions(){ verify(updateStreamInfo).run(streamer); verify(webSocketPool).listenTopic(Topics.buildFromName(PREDICTIONS_USER_V1, USER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(PREDICTIONS_CHANNEL_V1, STREAMER_ID, ACCESS_TOKEN)); verify(eventManager).onEvent(new StreamerAddedEvent(streamer, NOW)); } @@ -442,6 +445,7 @@ void addStreamerWithMoments(){ verify(updateStreamInfo).run(streamer); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(COMMUNITY_MOMENTS_CHANNEL_V1, STREAMER_ID, ACCESS_TOKEN)); verify(eventManager).onEvent(new StreamerAddedEvent(streamer, NOW)); } @@ -478,6 +482,7 @@ void addStreamerWithRaid(){ verify(updateStreamInfo).run(streamer); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(RAID, STREAMER_ID, ACCESS_TOKEN)); verify(eventManager).onEvent(new StreamerAddedEvent(streamer, NOW)); } @@ -515,6 +520,7 @@ void addStreamerWithIrcAndStreamerOffline(){ verify(updateStreamInfo).run(streamer); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(eventManager).onEvent(new StreamerAddedEvent(streamer, NOW)); verify(twitchChatClient, never()).join(any()); } @@ -553,6 +559,7 @@ void addStreamerWithIrcAndStreamerOnline(){ verify(updateStreamInfo).run(streamer); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(eventManager).onEvent(new StreamerAddedEvent(streamer, NOW)); verify(twitchChatClient).join(STREAMER_USERNAME); } @@ -588,6 +595,7 @@ void addDuplicateStreamer(){ verify(updateStreamInfo).run(streamer); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(eventManager).onEvent(new StreamerAddedEvent(streamer, NOW)); } } @@ -640,10 +648,8 @@ void getStreamerById(){ tested.addStreamer(streamer1); tested.addStreamer(streamer2); - assertThat(tested.getStreamerById(id1)).isPresent() - .get().isEqualTo(streamer1); - assertThat(tested.getStreamerById(id2)).isPresent() - .get().isEqualTo(streamer2); + assertThat(tested.getStreamerById(id1)).contains(streamer1); + assertThat(tested.getStreamerById(id2)).contains(streamer2); } } @@ -672,6 +678,7 @@ void removeStreamer(){ tested.removeStreamer(streamer); verify(webSocketPool).removeTopic(Topic.builder().name(VIDEO_PLAYBACK_BY_ID).target(STREAMER_ID).build()); + verify(webSocketPool).removeTopic(Topic.builder().name(USER_DROP_EVENTS).target(STREAMER_ID).build()); verify(webSocketPool).removeTopic(Topic.builder().name(PREDICTIONS_CHANNEL_V1).target(STREAMER_ID).build()); verify(webSocketPool).removeTopic(Topic.builder().name(COMMUNITY_MOMENTS_CHANNEL_V1).target(STREAMER_ID).build()); verify(webSocketPool).removeTopic(Topic.builder().name(RAID).target(STREAMER_ID).build()); @@ -748,6 +755,7 @@ void updateStreamerAllActivatedOnline(){ assertDoesNotThrow(() -> tested.updateStreamer(streamer)); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(PREDICTIONS_USER_V1, USER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(PREDICTIONS_CHANNEL_V1, STREAMER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(RAID, STREAMER_ID, ACCESS_TOKEN)); @@ -785,6 +793,7 @@ void updateStreamerAllActivatedOffline(){ assertDoesNotThrow(() -> tested.updateStreamer(streamer)); verify(webSocketPool).listenTopic(Topics.buildFromName(VIDEO_PLAYBACK_BY_ID, STREAMER_ID, ACCESS_TOKEN)); + verify(webSocketPool).listenTopic(Topics.buildFromName(USER_DROP_EVENTS, STREAMER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(PREDICTIONS_USER_V1, USER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(PREDICTIONS_CHANNEL_V1, STREAMER_ID, ACCESS_TOKEN)); verify(webSocketPool).listenTopic(Topics.buildFromName(RAID, STREAMER_ID, ACCESS_TOKEN)); @@ -882,7 +891,6 @@ void requestInventorySync(){ ircFactory.when(() -> TwitchChatFactory.createChat(tested, CHAT_MODE, false)).thenReturn(twitchChatClient); runnableFactory.when(() -> MinerRunnableFactory.createUpdateStreamInfo(tested)).thenReturn(updateStreamInfo); - runnableFactory.when(() -> MinerRunnableFactory.createSyncInventory(tested, eventManager)).thenReturn(syncInventory); assertDoesNotThrow(() -> tested.syncInventory()); diff --git a/miner/src/test/resources/api/ws/userDropEvent.json b/miner/src/test/resources/api/ws/userDropEvent.json new file mode 100644 index 00000000..29e3e9e4 --- /dev/null +++ b/miner/src/test/resources/api/ws/userDropEvent.json @@ -0,0 +1,7 @@ +{ + "type" : "MESSAGE", + "data" : { + "topic" : "user-drop-events.123456789", + "message" : "{\"type\":\"drop-progress\",\"data\":{\"drop_id\":\"drop-id\",\"channel_id\":\"123456789\",\"current_progress_min\":1,\"required_progress_min\":15}}" + } +} \ No newline at end of file From 853681c650f798ec5cdbe86794f14918c874382d Mon Sep 17 00:00:00 2001 From: Thomas Couchoud Date: Mon, 14 Aug 2023 21:32:29 +0200 Subject: [PATCH 2/5] feat: use drop claim event --- .../miner/api/ws/data/message/DropClaim.java | 25 +++++++ .../api/ws/data/message/IPubSubMessage.java | 1 + .../data/message/dropclaim/DropClaimData.java | 28 ++++++++ .../event/impl/DropClaimedChannelEvent.java | 41 +++++++++++ .../miner/factory/MinerFactory.java | 2 +- .../factory/PubSubMessageHandlerFactory.java | 5 +- .../miner/handler/ClaimDropHandler.java | 36 ++++++++-- .../handler/PubSubMessageHandlerAdapter.java | 3 + ...witchPubSubWebSocketClientMessageTest.java | 28 +++++++- .../PubSubMessageHandlerFactoryTest.java | 5 +- .../miner/handler/ClaimDropHandlerTest.java | 71 ++++++++++++++----- .../DiscordMessageBuilderEmbedTest.java | 17 +++++ .../DiscordMessageBuilderMessageTest.java | 12 ++++ .../src/test/resources/api/ws/dropClaim.json | 7 ++ .../{userDropEvent.json => dropProgress.json} | 0 15 files changed, 248 insertions(+), 33 deletions(-) create mode 100644 miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropClaim.java create mode 100644 miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropclaim/DropClaimData.java create mode 100644 miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropClaimedChannelEvent.java create mode 100644 miner/src/test/resources/api/ws/dropClaim.json rename miner/src/test/resources/api/ws/{userDropEvent.json => dropProgress.json} (100%) diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropClaim.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropClaim.java new file mode 100644 index 00000000..e38d03d1 --- /dev/null +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/DropClaim.java @@ -0,0 +1,25 @@ +package fr.rakambda.channelpointsminer.miner.api.ws.data.message; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropclaim.DropClaimData; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; + +@JsonTypeName("drop-claim") +@Getter +@EqualsAndHashCode(callSuper = true) +@ToString +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DropClaim extends IPubSubMessage{ + @JsonProperty("data") + @NotNull + private DropClaimData data; +} diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java index 6edfcdfe..d1a4129a 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/IPubSubMessage.java @@ -37,6 +37,7 @@ @JsonSubTypes.Type(value = ActiveMultipliersUpdated.class, name = "active-multipliers-updated"), @JsonSubTypes.Type(value = ReadNotifications.class, name = "read-notifications"), @JsonSubTypes.Type(value = DropProgress.class, name = "drop-progress"), + @JsonSubTypes.Type(value = DropClaim.class, name = "drop-claim"), }) @EqualsAndHashCode @ToString diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropclaim/DropClaimData.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropclaim/DropClaimData.java new file mode 100644 index 00000000..3c9ea50f --- /dev/null +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/api/ws/data/message/dropclaim/DropClaimData.java @@ -0,0 +1,28 @@ +package fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropclaim; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@EqualsAndHashCode +@ToString +@Builder +public class DropClaimData{ + @JsonProperty("drop_id") + @NotNull + private String dropId; + @JsonProperty("drop_instance_id") + @NotNull + private String dropInstanceId; + @JsonProperty("channel_id") + @NotNull + private String channelId; +} diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropClaimedChannelEvent.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropClaimedChannelEvent.java new file mode 100644 index 00000000..cda553ad --- /dev/null +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropClaimedChannelEvent.java @@ -0,0 +1,41 @@ +package fr.rakambda.channelpointsminer.miner.event.impl; + +import fr.rakambda.channelpointsminer.miner.event.AbstractLoggableStreamerEvent; +import fr.rakambda.channelpointsminer.miner.streamer.Streamer; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.time.Instant; + +@EqualsAndHashCode(callSuper = true) +@ToString +public class DropClaimedChannelEvent extends AbstractLoggableStreamerEvent{ + public DropClaimedChannelEvent(@NotNull String streamerId, @Nullable String streamerUsername, @Nullable Streamer streamer, @NotNull Instant instant){ + super(streamerId, streamerUsername, streamer, instant); + } + + @Override + @NotNull + public String getConsoleLogFormat(){ + return "Drop claimed on channel {streamer}"; + } + + @Override + @NotNull + public String getDefaultFormat(){ + return "[{username}] {emoji} : Drop claimed on channel {streamer}"; + } + + @Override + @NotNull + protected String getColor(){ + return COLOR_INFO; + } + + @Override + @NotNull + protected String getEmoji(){ + return "🎁"; + } +} diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java index 3f601b16..b137943a 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/MinerFactory.java @@ -39,7 +39,7 @@ public static Miner create(@NotNull AccountConfiguration config, @NotNull IEvent miner.addPubSubHandler(PubSubMessageHandlerFactory.createPredictionsHandler(miner, BetPlacerFactory.created(miner), eventManager)); miner.addPubSubHandler(PubSubMessageHandlerFactory.createPointsHandler(miner, eventManager)); miner.addPubSubHandler(PubSubMessageHandlerFactory.createClaimMomentHandler(miner, eventManager)); - miner.addPubSubHandler(PubSubMessageHandlerFactory.createClaimDropHandler(syncInventory)); + miner.addPubSubHandler(PubSubMessageHandlerFactory.createClaimDropHandler(miner, eventManager)); eventManager.addEventHandler(LogEventListenerFactory.createLogger()); if(Objects.nonNull(config.getDiscord().getUrl())){ diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java index 6a16eec1..dbba5f5d 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactory.java @@ -11,7 +11,6 @@ import fr.rakambda.channelpointsminer.miner.handler.StreamStartEndHandler; import fr.rakambda.channelpointsminer.miner.miner.IMiner; import fr.rakambda.channelpointsminer.miner.prediction.bet.BetPlacer; -import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; import lombok.NoArgsConstructor; import org.jetbrains.annotations.NotNull; import static lombok.AccessLevel.PRIVATE; @@ -49,7 +48,7 @@ public static IPubSubMessageHandler createClaimMomentHandler(@NotNull IMiner min } @NotNull - public static IPubSubMessageHandler createClaimDropHandler(@NotNull SyncInventory syncInventory){ - return new ClaimDropHandler(syncInventory); + public static IPubSubMessageHandler createClaimDropHandler(@NotNull IMiner miner, @NotNull IEventManager eventManager){ + return new ClaimDropHandler(miner, eventManager); } } diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java index a05d961c..67a369d8 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java @@ -1,20 +1,48 @@ package fr.rakambda.channelpointsminer.miner.handler; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropClaim; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; import fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.Topic; -import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; +import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; +import fr.rakambda.channelpointsminer.miner.event.manager.IEventManager; +import fr.rakambda.channelpointsminer.miner.factory.TimeFactory; +import fr.rakambda.channelpointsminer.miner.log.LogContext; +import fr.rakambda.channelpointsminer.miner.miner.IMiner; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import java.util.Objects; +@Slf4j @RequiredArgsConstructor public class ClaimDropHandler extends PubSubMessageHandlerAdapter{ @NotNull - private final SyncInventory syncInventory; + private IMiner miner; + @NotNull + private IEventManager eventManager; @Override public void onDropProgress(@NotNull Topic topic, @NotNull DropProgress message){ - if(message.getData().getCurrentProgressMin() >= message.getData().getRequiredProgressMin()){ - syncInventory.run(); + super.onDropProgress(topic, message); + } + + @Override + public void onDropClaim(@NotNull Topic topic, @NotNull DropClaim message){ + var channelId = message.getData().getChannelId(); + var streamer = miner.getStreamerById(channelId).orElse(null); + var username = Objects.isNull(streamer) ? null : streamer.getUsername(); + + try(var ignored = LogContext.with(miner).withStreamer(streamer)){ + miner.getGqlApi().dropsPageClaimDropRewards(message.getData().getDropInstanceId()) + .filter(r -> { + if(!r.isError()){ + return true; + } + log.error("Failed to claim drop due to `{}` | {}", r.getError(), r.getErrors()); + return false; + }) + .map(r -> new DropClaimedChannelEvent(channelId, username, streamer, TimeFactory.now())) + .ifPresent(eventManager::onEvent); } } } diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java index e2671a16..c8c3fb53 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/PubSubMessageHandlerAdapter.java @@ -7,6 +7,7 @@ import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CommunityMomentStart; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CreateNotification; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DeleteNotification; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropClaim; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.EventCreated; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.EventUpdated; @@ -88,6 +89,8 @@ public void onReadNotifications(@NotNull Topic topic, @NotNull ReadNotifications public void onDropProgress(@NotNull Topic topic, @NotNull DropProgress message){} + public void onDropClaim(@NotNull Topic topic, @NotNull DropClaim message){} + @Override public void handle(@NotNull Topic topic, @NotNull IPubSubMessage message){ for(var clazz : ClassWalker.range(message.getClass(), IPubSubMessage.class)){ diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java index 6fad49e2..9319a01e 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/api/ws/TwitchPubSubWebSocketClientMessageTest.java @@ -8,6 +8,7 @@ import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CommunityMomentStart; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.CreateNotification; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DeleteNotification; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropClaim; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.PointsEarned; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.PointsSpent; @@ -35,6 +36,7 @@ import fr.rakambda.channelpointsminer.miner.api.ws.data.message.createnotification.TextContent; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.createnotification.TextNotificationDataBlock; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.deletenotification.DeleteNotificationData; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropclaim.DropClaimData; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropprogress.DropProgressData; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.pointsearned.Balance; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.pointsearned.PointsEarnedData; @@ -816,8 +818,8 @@ void onReadNotifications(WebsocketMockServer server){ } @Test - void onUserDropEvent(WebsocketMockServer server){ - server.send(TestUtils.getAllResourceContent("api/ws/userDropEvent.json")); + void onDropProgress(WebsocketMockServer server){ + server.send(TestUtils.getAllResourceContent("api/ws/dropProgress.json")); var expected = MessageResponse.builder() .data(MessageData.builder() @@ -838,6 +840,28 @@ void onUserDropEvent(WebsocketMockServer server){ verify(listener, timeout(MESSAGE_TIMEOUT)).onWebSocketMessage(expected); } + @Test + void onDropClaimEvent(WebsocketMockServer server){ + server.send(TestUtils.getAllResourceContent("api/ws/dropClaim.json")); + + var expected = MessageResponse.builder() + .data(MessageData.builder() + .topic(Topic.builder() + .name(USER_DROP_EVENTS) + .target("123456789") + .build()) + .message(DropClaim.builder() + .data(DropClaimData.builder() + .dropId("drop-id") + .channelId("123456789") + .dropInstanceId("drop-instance-id") + .build()) + .build()) + .build()) + .build(); + verify(listener, timeout(MESSAGE_TIMEOUT)).onWebSocketMessage(expected); + } + @BeforeEach void setUp(WebsocketMockServer server) throws InterruptedException{ var uri = URI.create("ws://127.0.0.1:" + server.getPort()); diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java index 6466b643..074c25f6 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/factory/PubSubMessageHandlerFactoryTest.java @@ -10,7 +10,6 @@ import fr.rakambda.channelpointsminer.miner.handler.StreamStartEndHandler; import fr.rakambda.channelpointsminer.miner.miner.IMiner; import fr.rakambda.channelpointsminer.miner.prediction.bet.BetPlacer; -import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; import fr.rakambda.channelpointsminer.miner.tests.ParallelizableTest; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -27,8 +26,6 @@ class PubSubMessageHandlerFactoryTest{ private BetPlacer betPlacer; @Mock private IEventManager eventManager; - @Mock - private SyncInventory syncInventory; @Test void createClaimAvailable(){ @@ -62,6 +59,6 @@ void createClaimMomentHandler(){ @Test void createClaimDropHandler(){ - assertThat(PubSubMessageHandlerFactory.createClaimDropHandler(syncInventory)).isNotNull().isInstanceOf(ClaimDropHandler.class); + assertThat(PubSubMessageHandlerFactory.createClaimDropHandler(miner, eventManager)).isNotNull().isInstanceOf(ClaimDropHandler.class); } } \ No newline at end of file diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java index 7ab149dc..4e06a5f5 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java @@ -1,9 +1,16 @@ package fr.rakambda.channelpointsminer.miner.handler; -import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; -import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropprogress.DropProgressData; +import fr.rakambda.channelpointsminer.miner.api.gql.gql.GQLApi; +import fr.rakambda.channelpointsminer.miner.api.gql.gql.data.GQLResponse; +import fr.rakambda.channelpointsminer.miner.api.gql.gql.data.dropspageclaimdroprewards.DropsPageClaimDropRewardsData; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropClaim; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropclaim.DropClaimData; import fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.Topic; -import fr.rakambda.channelpointsminer.miner.runnable.SyncInventory; +import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; +import fr.rakambda.channelpointsminer.miner.event.manager.IEventManager; +import fr.rakambda.channelpointsminer.miner.factory.TimeFactory; +import fr.rakambda.channelpointsminer.miner.miner.IMiner; +import fr.rakambda.channelpointsminer.miner.streamer.Streamer; import fr.rakambda.channelpointsminer.miner.tests.ParallelizableTest; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -11,7 +18,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import java.time.Instant; +import java.util.Optional; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -19,40 +30,62 @@ @ParallelizableTest @ExtendWith(MockitoExtension.class) class ClaimDropHandlerTest{ + private static final String DROP_INSTANCE_ID = "drop-instance-id"; + private static final String CHANNEL_ID = "channel-id"; + private static final String CHANNEL_USERNAME = "channel-username"; + private static final Instant NOW = Instant.parse("2022-05-15T12:02:14.000Z"); + @InjectMocks private ClaimDropHandler tested; @Mock - private SyncInventory syncInventory; + private IMiner miner; + @Mock + private IEventManager eventManager; + @Mock + private GQLApi gqlApi; @Mock private Topic topic; @Mock - private DropProgress dropProgress; + private Streamer streamer; + @Mock + private DropClaim dropClaim; + @Mock + private DropClaimData dropClaimData; + @Mock - private DropProgressData dropProgressData; + private GQLResponse dropsPageClaimDropRewardsDataGQLResponse; @BeforeEach void setUp(){ - lenient().when(dropProgress.getData()).thenReturn(dropProgressData); + lenient().when(dropClaim.getData()).thenReturn(dropClaimData); + lenient().when(dropClaimData.getChannelId()).thenReturn(CHANNEL_ID); + lenient().when(dropClaimData.getDropInstanceId()).thenReturn(DROP_INSTANCE_ID); + + lenient().when(miner.getGqlApi()).thenReturn(gqlApi); + lenient().when(miner.getStreamerById(CHANNEL_ID)).thenReturn(Optional.of(streamer)); + lenient().when(streamer.getUsername()).thenReturn(CHANNEL_USERNAME); + + lenient().when(dropsPageClaimDropRewardsDataGQLResponse.isError()).thenReturn(false); + lenient().when(gqlApi.dropsPageClaimDropRewards(DROP_INSTANCE_ID)).thenReturn(Optional.of(dropsPageClaimDropRewardsDataGQLResponse)); } @Test - void dropNotProgressedEnough(){ - when(dropProgressData.getCurrentProgressMin()).thenReturn(1); - when(dropProgressData.getRequiredProgressMin()).thenReturn(2); - - tested.onDropProgress(topic, dropProgress); - - verify(syncInventory, never()).run(); + void dropClaim(){ + try(var timeFactory = mockStatic(TimeFactory.class)){ + timeFactory.when(TimeFactory::now).thenReturn(NOW); + tested.onDropClaim(topic, dropClaim); + + verify(eventManager).onEvent(new DropClaimedChannelEvent(CHANNEL_ID, CHANNEL_USERNAME, streamer, NOW)); + } } @Test - void dropProgressedEnough(){ - when(dropProgressData.getCurrentProgressMin()).thenReturn(2); - when(dropProgressData.getRequiredProgressMin()).thenReturn(2); + void dropClaimError(){ + when(dropsPageClaimDropRewardsDataGQLResponse.isError()).thenReturn(true); - tested.onDropProgress(topic, dropProgress); + tested.onDropClaim(topic, dropClaim); - verify(syncInventory).run(); + verify(eventManager, never()).onEvent(any(DropClaimedChannelEvent.class)); } } \ No newline at end of file diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java index 15b6a68b..04e8e64f 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java @@ -24,6 +24,7 @@ import fr.rakambda.channelpointsminer.miner.event.impl.ClaimMomentEvent; import fr.rakambda.channelpointsminer.miner.event.impl.ClaimedMomentEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimEvent; +import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedEvent; import fr.rakambda.channelpointsminer.miner.event.impl.EventCreatedEvent; import fr.rakambda.channelpointsminer.miner.event.impl.LoginRequiredEvent; @@ -630,6 +631,22 @@ void onDropClaimed(){ .build()); } + @Test + void onDropClaimedChannel(){ + var event = new DropClaimedChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW); + event.setMiner(miner); + var webhook = tested.createEmbedMessage(event, discordEventConfiguration); + + assertThat(webhook).isEqualTo(Webhook.builder() + .embeds(List.of(Embed.builder() + .author(author) + .footer(footer) + .color(CYAN.getRGB()) + .description("[username] \uD83C\uDF81 : Drop claimed on channel streamer-name") + .build())) + .build()); + } + @Test void onLoginRequired(){ var event = new LoginRequiredEvent(NOW, "message"); diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java index 0e826ec4..ef2e3e7a 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java @@ -20,6 +20,7 @@ import fr.rakambda.channelpointsminer.miner.event.impl.ClaimMomentEvent; import fr.rakambda.channelpointsminer.miner.event.impl.ClaimedMomentEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimEvent; +import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedEvent; import fr.rakambda.channelpointsminer.miner.event.impl.EventCreatedEvent; import fr.rakambda.channelpointsminer.miner.event.impl.LoginRequiredEvent; @@ -462,6 +463,17 @@ void onDropClaimed(){ .build()); } + @Test + void onDropClaimedChannel(){ + var event = new DropClaimedChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW); + event.setMiner(miner); + var webhook = tested.createSimpleMessage(event, discordEventConfiguration); + + assertThat(webhook).isEqualTo(Webhook.builder() + .content("[%s] 🎁 : Drop claimed on channel %s".formatted(USERNAME, STREAMER_USERNAME)) + .build()); + } + @Test void onLoginRequired(){ var event = new LoginRequiredEvent(NOW, "message"); diff --git a/miner/src/test/resources/api/ws/dropClaim.json b/miner/src/test/resources/api/ws/dropClaim.json new file mode 100644 index 00000000..6048379e --- /dev/null +++ b/miner/src/test/resources/api/ws/dropClaim.json @@ -0,0 +1,7 @@ +{ + "type" : "MESSAGE", + "data" : { + "topic" : "user-drop-events.123456789", + "message" : "{\"type\":\"drop-claim\",\"data\":{\"drop_instance_id\":\"drop-instance-id\",\"drop_id\":\"drop-id\",\"channel_id\":\"123456789\"}}" + } +} \ No newline at end of file diff --git a/miner/src/test/resources/api/ws/userDropEvent.json b/miner/src/test/resources/api/ws/dropProgress.json similarity index 100% rename from miner/src/test/resources/api/ws/userDropEvent.json rename to miner/src/test/resources/api/ws/dropProgress.json From 23dabbd8c7705f16dd87be17a429cff94ad859d2 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud Date: Mon, 14 Aug 2023 21:52:57 +0200 Subject: [PATCH 3/5] feat: event on drop progress --- .../miner/event/EventVariableKey.java | 1 + .../event/impl/DropProgressChannelEvent.java | 60 +++++++++++++++++++ .../miner/handler/ClaimDropHandler.java | 11 +++- .../miner/handler/ClaimDropHandlerTest.java | 48 +++++++++++++++ .../DiscordMessageBuilderEmbedTest.java | 18 ++++++ .../DiscordMessageBuilderMessageTest.java | 12 ++++ 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/EventVariableKey.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/EventVariableKey.java index e8034441..e133e152 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/EventVariableKey.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/EventVariableKey.java @@ -10,6 +10,7 @@ public class EventVariableKey{ public static final String COLOR = "color"; public static final String COMMIT = "commit"; public static final String DROP_NAME = "drop_name"; + public static final String DROP_PROGRESS = "drop_progress"; public static final String MESSAGE = "message"; public static final String EMOJI = "emoji"; public static final String POINTS = "points"; diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java new file mode 100644 index 00000000..3d1992b2 --- /dev/null +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java @@ -0,0 +1,60 @@ +package fr.rakambda.channelpointsminer.miner.event.impl; + +import fr.rakambda.channelpointsminer.miner.event.AbstractLoggableStreamerEvent; +import fr.rakambda.channelpointsminer.miner.event.EventVariableKey; +import fr.rakambda.channelpointsminer.miner.streamer.Streamer; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.time.Instant; +import java.util.Map; + +@EqualsAndHashCode(callSuper = true) +@ToString +public class DropProgressChannelEvent extends AbstractLoggableStreamerEvent{ + private final String progress; + + public DropProgressChannelEvent(@NotNull String streamerId, @Nullable String streamerUsername, @Nullable Streamer streamer, @NotNull Instant instant, float progress){ + super(streamerId, streamerUsername, streamer, instant); + this.progress = "%.2f".formatted(progress); + } + + @Override + @NotNull + public String getConsoleLogFormat(){ + return "Drop progress on channel {streamer} : {drop_progress}%"; + } + + @Override + @NotNull + public String getDefaultFormat(){ + return "[{username}] {emoji} : Drop progress on channel {streamer} : {drop_progress}%"; + } + + @Override + public String lookup(String key){ + if(EventVariableKey.DROP_PROGRESS.equals(key)){ + return progress; + } + return super.lookup(key); + } + + @Override + @NotNull + public Map getEmbedFields(){ + return Map.of("Progress", EventVariableKey.DROP_PROGRESS); + } + + @Override + @NotNull + protected String getColor(){ + return COLOR_INFO; + } + + @Override + @NotNull + protected String getEmoji(){ + return "🎁"; + } +} diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java index 67a369d8..96ddcbe1 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java @@ -4,6 +4,7 @@ import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; import fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.Topic; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; +import fr.rakambda.channelpointsminer.miner.event.impl.DropProgressChannelEvent; import fr.rakambda.channelpointsminer.miner.event.manager.IEventManager; import fr.rakambda.channelpointsminer.miner.factory.TimeFactory; import fr.rakambda.channelpointsminer.miner.log.LogContext; @@ -23,7 +24,15 @@ public class ClaimDropHandler extends PubSubMessageHandlerAdapter{ @Override public void onDropProgress(@NotNull Topic topic, @NotNull DropProgress message){ - super.onDropProgress(topic, message); + var channelId = message.getData().getChannelId(); + var streamer = miner.getStreamerById(channelId).orElse(null); + var username = Objects.isNull(streamer) ? null : streamer.getUsername(); + + try(var ignored = LogContext.with(miner).withStreamer(streamer)){ + var progress = message.getData().getCurrentProgressMin() / ((float) message.getData().getRequiredProgressMin()); + var event = new DropProgressChannelEvent(channelId, username, streamer, TimeFactory.now(), progress); + eventManager.onEvent(event); + } } @Override diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java index 4e06a5f5..8a5bc159 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java @@ -4,9 +4,12 @@ import fr.rakambda.channelpointsminer.miner.api.gql.gql.data.GQLResponse; import fr.rakambda.channelpointsminer.miner.api.gql.gql.data.dropspageclaimdroprewards.DropsPageClaimDropRewardsData; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropClaim; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.DropProgress; import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropclaim.DropClaimData; +import fr.rakambda.channelpointsminer.miner.api.ws.data.message.dropprogress.DropProgressData; import fr.rakambda.channelpointsminer.miner.api.ws.data.request.topic.Topic; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; +import fr.rakambda.channelpointsminer.miner.event.impl.DropProgressChannelEvent; import fr.rakambda.channelpointsminer.miner.event.manager.IEventManager; import fr.rakambda.channelpointsminer.miner.factory.TimeFactory; import fr.rakambda.channelpointsminer.miner.miner.IMiner; @@ -34,6 +37,8 @@ class ClaimDropHandlerTest{ private static final String CHANNEL_ID = "channel-id"; private static final String CHANNEL_USERNAME = "channel-username"; private static final Instant NOW = Instant.parse("2022-05-15T12:02:14.000Z"); + private static final int CURRENT_PROGRESS = 1; + private static final int REQUIRED_PROGRESS = 4; @InjectMocks private ClaimDropHandler tested; @@ -52,6 +57,10 @@ class ClaimDropHandlerTest{ private DropClaim dropClaim; @Mock private DropClaimData dropClaimData; + @Mock + private DropProgress dropProgress; + @Mock + private DropProgressData dropProgressData; @Mock private GQLResponse dropsPageClaimDropRewardsDataGQLResponse; @@ -61,6 +70,10 @@ void setUp(){ lenient().when(dropClaim.getData()).thenReturn(dropClaimData); lenient().when(dropClaimData.getChannelId()).thenReturn(CHANNEL_ID); lenient().when(dropClaimData.getDropInstanceId()).thenReturn(DROP_INSTANCE_ID); + lenient().when(dropProgress.getData()).thenReturn(dropProgressData); + lenient().when(dropProgressData.getChannelId()).thenReturn(CHANNEL_ID); + lenient().when(dropProgressData.getCurrentProgressMin()).thenReturn(CURRENT_PROGRESS); + lenient().when(dropProgressData.getRequiredProgressMin()).thenReturn(REQUIRED_PROGRESS); lenient().when(miner.getGqlApi()).thenReturn(gqlApi); lenient().when(miner.getStreamerById(CHANNEL_ID)).thenReturn(Optional.of(streamer)); @@ -80,6 +93,18 @@ void dropClaim(){ } } + @Test + void dropClaimUnknownStreamer(){ + try(var timeFactory = mockStatic(TimeFactory.class)){ + timeFactory.when(TimeFactory::now).thenReturn(NOW); + lenient().when(miner.getStreamerById(CHANNEL_ID)).thenReturn(Optional.empty()); + + tested.onDropClaim(topic, dropClaim); + + verify(eventManager).onEvent(new DropClaimedChannelEvent(CHANNEL_ID, null, null, NOW)); + } + } + @Test void dropClaimError(){ when(dropsPageClaimDropRewardsDataGQLResponse.isError()).thenReturn(true); @@ -88,4 +113,27 @@ void dropClaimError(){ verify(eventManager, never()).onEvent(any(DropClaimedChannelEvent.class)); } + + @Test + void dropProgress(){ + try(var timeFactory = mockStatic(TimeFactory.class)){ + timeFactory.when(TimeFactory::now).thenReturn(NOW); + + tested.onDropProgress(topic, dropProgress); + + verify(eventManager).onEvent(new DropProgressChannelEvent(CHANNEL_ID, CHANNEL_USERNAME, streamer, NOW, 0.25f)); + } + } + + @Test + void dropProgressUnknownStreamer(){ + try(var timeFactory = mockStatic(TimeFactory.class)){ + timeFactory.when(TimeFactory::now).thenReturn(NOW); + lenient().when(miner.getStreamerById(CHANNEL_ID)).thenReturn(Optional.empty()); + + tested.onDropProgress(topic, dropProgress); + + verify(eventManager).onEvent(new DropProgressChannelEvent(CHANNEL_ID, null, null, NOW, 0.25f)); + } + } } \ No newline at end of file diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java index 04e8e64f..1e1df264 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java @@ -26,6 +26,7 @@ import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedEvent; +import fr.rakambda.channelpointsminer.miner.event.impl.DropProgressChannelEvent; import fr.rakambda.channelpointsminer.miner.event.impl.EventCreatedEvent; import fr.rakambda.channelpointsminer.miner.event.impl.LoginRequiredEvent; import fr.rakambda.channelpointsminer.miner.event.impl.MinerStartedEvent; @@ -647,6 +648,23 @@ void onDropClaimedChannel(){ .build()); } + @Test + void onDropProgressChannel(){ + var event = new DropProgressChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW, 0.2f); + event.setMiner(miner); + var webhook = tested.createEmbedMessage(event, discordEventConfiguration); + + assertThat(webhook).isEqualTo(Webhook.builder() + .embeds(List.of(Embed.builder() + .author(author) + .footer(footer) + .color(CYAN.getRGB()) + .description("[username] \uD83C\uDF81 : Drop progress on channel streamer-name : 0.20%") + .field(Field.builder().name("Progress").value("0.20").build()) + .build())) + .build()); + } + @Test void onLoginRequired(){ var event = new LoginRequiredEvent(NOW, "message"); diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java index ef2e3e7a..9c72e7db 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java @@ -22,6 +22,7 @@ import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedChannelEvent; import fr.rakambda.channelpointsminer.miner.event.impl.DropClaimedEvent; +import fr.rakambda.channelpointsminer.miner.event.impl.DropProgressChannelEvent; import fr.rakambda.channelpointsminer.miner.event.impl.EventCreatedEvent; import fr.rakambda.channelpointsminer.miner.event.impl.LoginRequiredEvent; import fr.rakambda.channelpointsminer.miner.event.impl.MinerStartedEvent; @@ -474,6 +475,17 @@ void onDropClaimedChannel(){ .build()); } + @Test + void onDropProgressChannel(){ + var event = new DropProgressChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW, 0.256f); + event.setMiner(miner); + var webhook = tested.createSimpleMessage(event, discordEventConfiguration); + + assertThat(webhook).isEqualTo(Webhook.builder() + .content("[%s] 🎁 : Drop progress on channel %s : %s%%".formatted(USERNAME, STREAMER_USERNAME, "0.26")) + .build()); + } + @Test void onLoginRequired(){ var event = new LoginRequiredEvent(NOW, "message"); From 284c5cd278bc16253a9f3e6847947f6c695ce847 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud Date: Mon, 14 Aug 2023 22:00:07 +0200 Subject: [PATCH 4/5] fix: use actual percentage --- .../miner/event/impl/DropProgressChannelEvent.java | 4 ++-- .../channelpointsminer/miner/handler/ClaimDropHandler.java | 2 +- .../miner/handler/ClaimDropHandlerTest.java | 4 ++-- .../miner/log/discord/DiscordMessageBuilderEmbedTest.java | 6 +++--- .../miner/log/discord/DiscordMessageBuilderMessageTest.java | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java index 3d1992b2..7af0ac5c 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/impl/DropProgressChannelEvent.java @@ -15,9 +15,9 @@ public class DropProgressChannelEvent extends AbstractLoggableStreamerEvent{ private final String progress; - public DropProgressChannelEvent(@NotNull String streamerId, @Nullable String streamerUsername, @Nullable Streamer streamer, @NotNull Instant instant, float progress){ + public DropProgressChannelEvent(@NotNull String streamerId, @Nullable String streamerUsername, @Nullable Streamer streamer, @NotNull Instant instant, int progress){ super(streamerId, streamerUsername, streamer, instant); - this.progress = "%.2f".formatted(progress); + this.progress = Integer.toString(progress); } @Override diff --git a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java index 96ddcbe1..d74639bc 100644 --- a/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java +++ b/miner/src/main/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandler.java @@ -29,7 +29,7 @@ public void onDropProgress(@NotNull Topic topic, @NotNull DropProgress message){ var username = Objects.isNull(streamer) ? null : streamer.getUsername(); try(var ignored = LogContext.with(miner).withStreamer(streamer)){ - var progress = message.getData().getCurrentProgressMin() / ((float) message.getData().getRequiredProgressMin()); + var progress = 100 * message.getData().getCurrentProgressMin() / message.getData().getRequiredProgressMin(); var event = new DropProgressChannelEvent(channelId, username, streamer, TimeFactory.now(), progress); eventManager.onEvent(event); } diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java index 8a5bc159..2a84fefb 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/handler/ClaimDropHandlerTest.java @@ -121,7 +121,7 @@ void dropProgress(){ tested.onDropProgress(topic, dropProgress); - verify(eventManager).onEvent(new DropProgressChannelEvent(CHANNEL_ID, CHANNEL_USERNAME, streamer, NOW, 0.25f)); + verify(eventManager).onEvent(new DropProgressChannelEvent(CHANNEL_ID, CHANNEL_USERNAME, streamer, NOW, 25)); } } @@ -133,7 +133,7 @@ void dropProgressUnknownStreamer(){ tested.onDropProgress(topic, dropProgress); - verify(eventManager).onEvent(new DropProgressChannelEvent(CHANNEL_ID, null, null, NOW, 0.25f)); + verify(eventManager).onEvent(new DropProgressChannelEvent(CHANNEL_ID, null, null, NOW, 25)); } } } \ No newline at end of file diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java index 1e1df264..b46a5581 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderEmbedTest.java @@ -650,7 +650,7 @@ void onDropClaimedChannel(){ @Test void onDropProgressChannel(){ - var event = new DropProgressChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW, 0.2f); + var event = new DropProgressChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW, 20); event.setMiner(miner); var webhook = tested.createEmbedMessage(event, discordEventConfiguration); @@ -659,8 +659,8 @@ void onDropProgressChannel(){ .author(author) .footer(footer) .color(CYAN.getRGB()) - .description("[username] \uD83C\uDF81 : Drop progress on channel streamer-name : 0.20%") - .field(Field.builder().name("Progress").value("0.20").build()) + .description("[username] \uD83C\uDF81 : Drop progress on channel streamer-name : 20%") + .field(Field.builder().name("Progress").value("20").build()) .build())) .build()); } diff --git a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java index 9c72e7db..1f023828 100644 --- a/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java +++ b/miner/src/test/java/fr/rakambda/channelpointsminer/miner/log/discord/DiscordMessageBuilderMessageTest.java @@ -477,12 +477,12 @@ void onDropClaimedChannel(){ @Test void onDropProgressChannel(){ - var event = new DropProgressChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW, 0.256f); + var event = new DropProgressChannelEvent(STREAMER_ID, STREAMER_USERNAME, streamer, NOW, 26); event.setMiner(miner); var webhook = tested.createSimpleMessage(event, discordEventConfiguration); assertThat(webhook).isEqualTo(Webhook.builder() - .content("[%s] 🎁 : Drop progress on channel %s : %s%%".formatted(USERNAME, STREAMER_USERNAME, "0.26")) + .content("[%s] 🎁 : Drop progress on channel %s : %s%%".formatted(USERNAME, STREAMER_USERNAME, "26")) .build()); } From 49bc4a608f3a57fd9a5141ee02efb659aa420e62 Mon Sep 17 00:00:00 2001 From: Thomas Couchoud Date: Mon, 14 Aug 2023 22:02:10 +0200 Subject: [PATCH 5/5] doc: fix doc link --- miner/docs/modules/ROOT/pages/configuration/discord.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/docs/modules/ROOT/pages/configuration/discord.adoc b/miner/docs/modules/ROOT/pages/configuration/discord.adoc index f62b7c6d..06d02150 100644 --- a/miner/docs/modules/ROOT/pages/configuration/discord.adoc +++ b/miner/docs/modules/ROOT/pages/configuration/discord.adoc @@ -22,7 +22,7 @@ include::example$discord-filtering.json[] Format works the same way as <> except that additional parameters are provided. Placeholders are expected to be between braces, example: `\{placeholder_name}`. -Available values can be seen in link:https://github.com/Rakambda/ChannelPointsMiner/blob/filter/develop/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/EventVariableKey.java[EventVariableKey]. +Available values can be seen in link:https://github.com/Rakambda/ChannelPointsMiner/blob/develop/miner/src/main/java/fr/rakambda/channelpointsminer/miner/event/EventVariableKey.java[EventVariableKey]. NOTE: If you want to override the format of one event, you'll have to do the all as it'll also act as a filter.