Skip to content

Commit

Permalink
Merge pull request #259 from 15sawyer/main
Browse files Browse the repository at this point in the history
Record chat members predictions + 'mostTrusted' outcome picker.
  • Loading branch information
Rakambda authored Aug 29, 2022
2 parents 78cb011 + 2050696 commit 4e4c638
Show file tree
Hide file tree
Showing 128 changed files with 2,290 additions and 508 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
/out
/streamers
/target
bin/

*.iml
/analytics.db
/config.json
/log4j2.xml
26 changes: 23 additions & 3 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Other forms of point, like those provided by bots through IRC (Streamlabs, etc.)
* Follow raids.
* Participate and claim link:https://www.twitch.tv/drops/campaigns[campaigns].
* Join IRC chat.
* Participate in predictions.
* Participate in predictions and record peoples predictions.
Limits:

Expand Down Expand Up @@ -224,9 +224,10 @@ Analytics settings define a way to collect data on your twitch account as time p
This includes:

* Balance evolution
* Predictions made & results
* Your own Predictions made & results
* Predictions from other chat participants and their return-on-investment (only approximate as the bet amount is mostly anonymous).

These are store in an external database which allows any external software to access it and process it they wanted way.
These are stored in an external database which allows any external software to access it and process it the wanted way.

Several database types are supported and listed below.
Each database (logical database for MariaDB, different file for H2/SQLite) will however represent one mined account.
Expand All @@ -243,6 +244,10 @@ You'll therefore have to adjust the settings for each mined account to not point
|database
|<<analytics_database_settings,Analytics database settings>>.
|

|recordChatsPredictions
|If set to true, other peoples predictions will be recorded. This is done from two sources, from the top-predictors list and from chat messages (via their badge). Reading from chat, requires the joinIrc setting to be set to true.
|false
|===

==== Analytics database [[analytics_database_settings]]
Expand Down Expand Up @@ -270,6 +275,12 @@ NOTE: Can be omitted if no account

NOTE: Can be omitted if no password
|

|maxPoolSize
|Maximum number of connections to the database.

NOTE: For SQLite connection, value 1 will be forced.
|10
|===

=== Streamer settings [[streamer_settings]]
Expand Down Expand Up @@ -472,6 +483,15 @@ This is the same as "the outcome with higher odds".
|Outcome with the person that placed the biggest prediction overall.
|

|mostTrusted
|Choose the outcome that's backed by other users with the highest average return-on-investment. *Requires at least some data gathered beforehand via the 'recordChatsPredictions' setting!* E.g. only users with at least 5 made predictions are taken into account by default.
|Take the return-on-investment from people who already placed a prediction, calculate the average per outcome, then pick the highest.
|* minTotalBetsPlacedByUser: only user with at least this number of bets are considered in the calculation. Default is 5.

* minTotalBetsPlacedOnPrediction: if not enough bets were placed, skip this prediction. Default is 10.

* minTotalBetsPlacedOnOutcome: if not enough bets were placed on the chosen outcome, skip this prediction. Default is 5.

|smart
|Choose the outcome with the most users.
However, if the two most picked outcomes have a user count similar, choose the outcome with the least points (higher odds).
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface ITwitchChatClient extends AutoCloseable{

@Override
void close();

void addChatMessageListener(@NotNull ITwitchChatMessageListener listener);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fr.raksrinana.channelpointsminer.miner.api.chat;

import org.jetbrains.annotations.NotNull;

public interface ITwitchChatMessageListener{

void onChatMessage(@NotNull String streamer, @NotNull String actor, @NotNull String message);

void onChatMessage(@NotNull String streamer, @NotNull String actor, @NotNull String message, @NotNull String badges);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package fr.raksrinana.channelpointsminer.miner.api.chat;

import fr.raksrinana.channelpointsminer.miner.event.impl.ChatMessageEvent;
import fr.raksrinana.channelpointsminer.miner.factory.TimeFactory;
import fr.raksrinana.channelpointsminer.miner.miner.IMiner;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;

@RequiredArgsConstructor
public class TwitchChatEventProducer implements ITwitchChatMessageListener{
@NotNull
private final IMiner miner;

@Override
public void onChatMessage(@NotNull String streamer, @NotNull String actor, @NotNull String message){
onChatMessage(streamer, actor, message, "");
}

@Override
public void onChatMessage(@NotNull String streamer, @NotNull String actor, @NotNull String message, @NotNull String badges){
var event = new ChatMessageEvent(miner, TimeFactory.now(), streamer, actor, message, badges);
miner.onEvent(event);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package fr.raksrinana.channelpointsminer.miner.api.chat.irc;

import fr.raksrinana.channelpointsminer.miner.api.chat.ITwitchChatClient;
import fr.raksrinana.channelpointsminer.miner.api.chat.ITwitchChatMessageListener;
import fr.raksrinana.channelpointsminer.miner.api.passport.TwitchLogin;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kitteh.irc.client.library.Client;
import org.kitteh.irc.client.library.defaults.element.messagetag.DefaultMessageTagLabel;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -15,11 +19,18 @@
@Log4j2
public class TwitchIrcChatClient implements ITwitchChatClient{

private static final String TAGS_CAPABILITY = "twitch.tv/tags";
private static final String EMOTE_SETS_TAG_NAME = "emote-sets";

@NotNull
private final TwitchLogin twitchLogin;
private final boolean listenMessages;
private final Collection<ITwitchChatMessageListener> chatMessageListeners = new LinkedList<>();

@Nullable
private Client ircClient;
@Nullable
private TwitchIrcMessageHandler ircMessageHandler;

@Override
public void join(@NotNull String channel){
Expand All @@ -38,20 +49,10 @@ public void join(@NotNull String channel){
public void joinPending(){
}

private synchronized Client getIrcClient(){
if(Objects.isNull(ircClient)){
log.info("Creating new Twitch IRC client");

ircClient = TwitchIrcFactory.createIrcClient(twitchLogin);
ircClient.connect();
ircClient.setExceptionListener(e -> log.error("Error from irc", e));

ircClient.getEventManager().registerEventListener(TwitchIrcFactory.createIrcListener(twitchLogin.getUsername()));

log.info("IRC Client created");
}

return ircClient;
@Override
public void addChatMessageListener(@NotNull ITwitchChatMessageListener listener){
chatMessageListeners.add(listener);
Optional.ofNullable(ircMessageHandler).ifPresent(i -> i.addListener(listener));
}

@Override
Expand Down Expand Up @@ -79,4 +80,35 @@ public void ping(){
public void close(){
Optional.ofNullable(ircClient).ifPresent(Client::shutdown);
}

@NotNull
private synchronized Client getIrcClient(){
if(Objects.isNull(ircClient)){
log.info("Creating new Twitch IRC client");

ircClient = TwitchIrcFactory.createIrcClient(twitchLogin);
ircClient.connect();
ircClient.setExceptionListener(e -> log.error("Error from irc", e));

var eventManager = ircClient.getEventManager();
eventManager.registerEventListener(TwitchIrcFactory.createIrcConnectionHandler(twitchLogin.getUsername()));

if(listenMessages){
ircMessageHandler = TwitchIrcFactory.createIrcMessageHandler(twitchLogin.getUsername());
chatMessageListeners.forEach(ircMessageHandler::addListener);
eventManager.registerEventListener(ircMessageHandler);

var capabilityRequest = ircClient.commands().capabilityRequest();
capabilityRequest.enable(TAGS_CAPABILITY);
capabilityRequest.execute();

var tagManager = ircClient.getMessageTagManager();
tagManager.registerTagCreator(TAGS_CAPABILITY, EMOTE_SETS_TAG_NAME, DefaultMessageTagLabel.FUNCTION);
}

log.info("IRC Client created");
}

return ircClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

@RequiredArgsConstructor
@Log4j2
public class TwitchIrcEventListener{
public class TwitchIrcConnectionHandler{

@NotNull
private final String accountName;

@Handler
Expand All @@ -20,7 +22,7 @@ private void onClientConnectionEstablishedEvent(ClientNegotiationCompleteEvent e
log.info("IRC client connected");
}
}

@Handler
public void onClientConnectionCLoseEvent(ClientConnectionClosedEvent event){
try(var ignored = LogContext.with(accountName)){
Expand All @@ -33,7 +35,7 @@ public void onClientConnectionCLoseEvent(ClientConnectionClosedEvent event){
}
}
}

@Handler
public void onChannelJoinEvent(@NotNull RequestedChannelJoinCompleteEvent event){
try(var ignored = LogContext.with(accountName)){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TwitchIrcFactory{
private static final String TWITCH_IRC_HOST = "irc.chat.twitch.tv";
@NotNull
public static Client createIrcClient(@NotNull TwitchLogin twitchLogin){
return createIrcClient(twitchLogin.getUsername(), "oauth:%s".formatted(twitchLogin.getAccessToken()));
}
@NotNull
public static Client createIrcClient(@NotNull TwitchLogin twitchLogin){
return createIrcClient(twitchLogin.getUsername(), "oauth:%s".formatted(twitchLogin.getAccessToken()));
}
@NotNull
private static Client createIrcClient(@NotNull String username, @Nullable String password){
var client = Client.builder()
Expand All @@ -31,7 +31,12 @@ private static Client createIrcClient(@NotNull String username, @Nullable String
}

@NotNull
public static TwitchIrcEventListener createIrcListener(@NotNull String accountName){
return new TwitchIrcEventListener(accountName);
public static TwitchIrcConnectionHandler createIrcConnectionHandler(@NotNull String accountName){
return new TwitchIrcConnectionHandler(accountName);
}

@NotNull
public static TwitchIrcMessageHandler createIrcMessageHandler(@NotNull String accountName){
return new TwitchIrcMessageHandler(accountName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package fr.raksrinana.channelpointsminer.miner.api.chat.irc;

import fr.raksrinana.channelpointsminer.miner.api.chat.ITwitchChatMessageListener;
import fr.raksrinana.channelpointsminer.miner.log.LogContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.engio.mbassy.listener.Handler;
import org.jetbrains.annotations.NotNull;
import org.kitteh.irc.client.library.event.channel.ChannelMessageEvent;
import java.util.Collection;
import java.util.LinkedList;

@RequiredArgsConstructor
@Log4j2
public class TwitchIrcMessageHandler{

@NotNull
private final String accountName;

@NotNull
private final Collection<ITwitchChatMessageListener> listeners = new LinkedList<>();

@Handler
public void onMessageEvent(@NotNull ChannelMessageEvent event){
try(var ignored = LogContext.with(accountName)){
log.trace("Received Irc Chat Message");
var badges = event.getTag("badges");
if(badges.isPresent()){
listeners.forEach(l -> l.onChatMessage(
event.getChannel().getName().substring(1),
event.getActor().getMessagingName(),
event.getMessage(),
badges.get().getAsString()));
}
else{
listeners.forEach(l -> l.onChatMessage(
event.getChannel().getName().substring(1),
event.getActor().getMessagingName(),
event.getMessage()));
}
}
}

public void addListener(ITwitchChatMessageListener listener){
listeners.add(listener);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public interface ITwitchChatWebSocketListener{
public interface ITwitchChatWebSocketClosedListener{
void onWebSocketClosed(@NotNull TwitchChatWebSocketClient client, int code, @Nullable String reason, boolean remote);
}
Loading

0 comments on commit 4e4c638

Please sign in to comment.