Skip to content

Commit

Permalink
feat(socket): implement StreamElements support
Browse files Browse the repository at this point in the history
  • Loading branch information
iGoodie committed Aug 19, 2019
1 parent bdb13bc commit a08dbb1
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 81 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Expand Up @@ -5,7 +5,7 @@ org.gradle.daemon=false

mod_id=twitchspawn
mod_group=net.programmer.igoodie
mod_version=1.1.3
mod_version=1.1.4

minecraft_version=1.14.3
forge_version=27.0.50
Expand Down
Expand Up @@ -184,7 +184,7 @@ public static int simulateModule(CommandContext<CommandSource> context) {
return 0;
}

TSLEventPair eventPair = TSLEventKeyword.toPair(eventName);
TSLEventPair eventPair = TSLEventKeyword.toPairs(eventName).iterator().next();

if (eventPair == null) {
context.getSource().sendFeedback(new TranslationTextComponent(
Expand All @@ -193,7 +193,7 @@ public static int simulateModule(CommandContext<CommandSource> context) {
}

boolean random = nbt.getBoolean("random");
EventArguments simulatedEvent = new EventArguments(eventPair.getEventType(), eventPair.getEventFor());
EventArguments simulatedEvent = new EventArguments(eventPair.getEventType(), eventPair.getEventAccount());
simulatedEvent.streamerNickname = context.getSource().getName();

if (random) {
Expand Down
@@ -0,0 +1,115 @@
package net.programmer.igoodie.twitchspawn.tracer;

import io.socket.client.Socket;
import net.programmer.igoodie.twitchspawn.TwitchSpawn;
import net.programmer.igoodie.twitchspawn.configuration.ConfigManager;
import net.programmer.igoodie.twitchspawn.configuration.CredentialsConfig;
import net.programmer.igoodie.twitchspawn.tslanguage.EventArguments;
import net.programmer.igoodie.twitchspawn.tslanguage.event.TSLEventPair;
import net.programmer.igoodie.twitchspawn.tslanguage.keyword.TSLEventKeyword;
import net.programmer.igoodie.twitchspawn.util.JSONUtils;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

public class StreamElementsSocketTracer extends SocketIOTracer {

public Set<Socket> authorized;

public StreamElementsSocketTracer(TraceManager manager) {
super(Platform.STREAMELEMENTS, manager);
this.authorized = new HashSet<>();
}

@Override
public void start() {
TwitchSpawn.LOGGER.info("Starting StreamElements Tracer...");

// Create socket for every credential with StreamElements platform
ConfigManager.CREDENTIALS.streamers.stream()
.filter(streamer -> streamer.platform.equals(Platform.STREAMELEMENTS))
.forEach(this::createSocket);

this.sockets.forEach(Socket::connect);
}

@Override
public void stop() {
TwitchSpawn.LOGGER.info("Stopping StreamElements Tracer...");

this.sockets.forEach(Socket::disconnect);

this.sockets.clear();
}

@Override
protected void onConnect(Socket socket, CredentialsConfig.Streamer streamer, Object... args) {
try {
JSONObject authArguments = new JSONObject();
authArguments.put("method", "jwt");
authArguments.put("token", streamer.token);
socket.emit("authenticate", authArguments);

} catch (JSONException ignored) {} // Must be impossible

socket.on("authenticated", foo -> {
TwitchSpawn.LOGGER.info("Connected to StreamElements Socket API with {}'s token successfully!", streamer.twitchNick);
authorized.add(socket);
});

// TODO: refactor dis, duh :V
new Timer().schedule(new TimerTask() {
@Override
public void run() {
if (manager.isRunning() && !authorized.contains(socket)) {
TwitchSpawn.LOGGER.info("Disconnected from {}'s StreamElements Socket connection. (unauthorized)", streamer.minecraftNick);
manager.stop(null, streamer.twitchNick + " unauthorized by the socket server");
}
}
}, 5000);
}

@Override
protected void onDisconnect(Socket socket, CredentialsConfig.Streamer streamer, Object... args) {
TwitchSpawn.LOGGER.info("Disconnected from {}'s StreamElements Socket connection. (intentional)", streamer.minecraftNick);
}

@Override
protected void onLiveEvent(Socket socket, CredentialsConfig.Streamer streamer, Object... args) {
JSONObject event = (JSONObject) args[0];

if (!event.has("data") || event.optJSONArray("data") == null)
return; // Contains no data (in expected format), stop here

String eventType = JSONUtils.extractFrom(event, "type", String.class, null);
String eventAccount = JSONUtils.extractFrom(event, "provider", String.class, "streamelements");

JSONObject data = JSONUtils.extractFrom(event, "data", JSONObject.class, new JSONObject());

TwitchSpawn.LOGGER.info("Received StreamElements package {} -> {}",
new TSLEventPair(eventType, eventAccount), data);

// Unregistered event alias
if (TSLEventKeyword.ofPair(eventType, eventAccount) == null)
return; // Stop here, do not handle

// Refine incoming data into EventArguments model
EventArguments eventArguments = new EventArguments(eventType, eventAccount);
eventArguments.streamerNickname = streamer.minecraftNick;
eventArguments.actorNickname = JSONUtils.extractFrom(data, "username", String.class, null);
eventArguments.message = JSONUtils.extractFrom(data, "message", String.class, null);
eventArguments.donationAmount = JSONUtils.extractNumberFrom(data, "amount", 0.0).doubleValue();
eventArguments.donationCurrency = JSONUtils.extractFrom(data, "currency", String.class, null);
eventArguments.subscriptionMonths = JSONUtils.extractNumberFrom(data, "amount", 0).intValue();
// eventArguments.raiderCount = JSONUtils.extractNumberFrom(message, "raiders", 0).intValue(); // Raids aren't supported (?)
eventArguments.viewerCount = JSONUtils.extractNumberFrom(data, "amount ", 0).intValue();

// Pass the model to the handler
ConfigManager.RULESET_COLLECTION.handleEvent(eventArguments);
}

}
Expand Up @@ -28,7 +28,11 @@ public StreamlabsSocketTracer(TraceManager manager) {
public void start() {
TwitchSpawn.LOGGER.info("Starting Streamlabs Tracer...");

ConfigManager.CREDENTIALS.streamers.forEach(this::createSocket);
// Create socket for every credential with Streamlabs platform
ConfigManager.CREDENTIALS.streamers.stream()
.filter(streamer -> streamer.platform.equals(Platform.STREAMLABS))
.forEach(this::createSocket);

this.sockets.forEach(Socket::connect);
}

Expand All @@ -37,17 +41,13 @@ public void stop() {
TwitchSpawn.LOGGER.info("Stopping Streamlabs Tracer...");

this.sockets.forEach(Socket::disconnect);
this.sockets.clear();
}

@Override
protected void checkCredentials(CredentialsConfig.Streamer streamer) {
super.checkCredentials(streamer); // TODO
this.sockets.clear();
}

@Override
protected IO.Options generateOptions(CredentialsConfig.Streamer streamer) {
IO.Options options = super.generateOptions(streamer);
IO.Options options = super.generateOptions(streamer);
options.query = "token=" + streamer.token;
return options;
}
Expand All @@ -63,7 +63,9 @@ protected void onDisconnect(Socket socket, CredentialsConfig.Streamer streamer,
TwitchSpawn.LOGGER.info("Disconnected from {}'s Streamlabs Socket connection. ({})",
streamer.minecraftNick, authorized.contains(socket) ? "intentional" : "unauthorized");

if(!authorized.contains(socket)) {
authorized.remove(socket);

if (manager.isRunning() && !authorized.contains(socket)) {
manager.stop(null, streamer.twitchNick + " unauthorized by the socket server");
}
}
Expand All @@ -72,24 +74,25 @@ protected void onDisconnect(Socket socket, CredentialsConfig.Streamer streamer,
protected void onLiveEvent(Socket socket, CredentialsConfig.Streamer streamer, Object... args) {
JSONObject event = (JSONObject) args[0];

if(!event.has("message") || event.optJSONArray("message") == null)
if (!event.has("message") || event.optJSONArray("message") == null)
return; // Contains no message (in expected format), stop here

String responseType = JSONUtils.extractFrom(event, "type", String.class, null);
String responseFor = JSONUtils.extractFrom(event, "for", String.class, "streamlabs");
String eventType = JSONUtils.extractFrom(event, "type", String.class, null);
String eventFor = JSONUtils.extractFrom(event, "for", String.class, "streamlabs");
String eventAccount = eventFor.replace("_account", "");

JSONArray messages = JSONUtils.extractFrom(event, "message", JSONArray.class, null);
JSONArray messages = JSONUtils.extractFrom(event, "message", JSONArray.class, new JSONArray());

JSONUtils.forEach(messages, message -> {
TwitchSpawn.LOGGER.info("Received streamlabs package {} -> {}",
new TSLEventPair(responseType, responseFor), message);
TwitchSpawn.LOGGER.info("Received Streamlabs package {} -> {}",
new TSLEventPair(eventType, eventFor), message);

// Unregistered event alias
if (TSLEventKeyword.ofPair(responseType, responseFor) == null)
if (TSLEventKeyword.ofPair(eventType, eventAccount) == null)
return; // Stop here, do not handle

// Refine incoming data into EventArguments model
EventArguments eventArguments = new EventArguments(responseType, responseFor);
EventArguments eventArguments = new EventArguments(eventType, eventAccount);
eventArguments.streamerNickname = streamer.minecraftNick;
eventArguments.actorNickname = JSONUtils.extractFrom(message, "name", String.class, null);
eventArguments.message = JSONUtils.extractFrom(message, "message", String.class, null);
Expand Down
Expand Up @@ -4,34 +4,49 @@
import net.minecraft.util.text.TranslationTextComponent;
import net.programmer.igoodie.twitchspawn.TwitchSpawn;

import java.util.LinkedList;
import java.util.List;

public class TraceManager {

private boolean running;
private StreamlabsSocketTracer streamlabsSocketTracer;
private List<SocketIOTracer> socketIOTracers;

public TraceManager() {
this.streamlabsSocketTracer = new StreamlabsSocketTracer(this);
this.socketIOTracers = new LinkedList<>();
this.socketIOTracers.add(new StreamlabsSocketTracer(this));
this.socketIOTracers.add(new StreamElementsSocketTracer(this));
}

public boolean isRunning() {
return running;
synchronized (this) {
return running;
}
}

public void start() {
if (isRunning()) throw new IllegalStateException("Tracer is already started");

TwitchSpawn.LOGGER.info("Starting all the tracers...");

// Start tracers
streamlabsSocketTracer.start();
socketIOTracers.forEach(SocketIOTracer::start);

running = true;

TwitchSpawn.SERVER.getPlayerList().sendMessage(
new TranslationTextComponent("commands.twitchspawn.start.success"), true);
}

public void stop(CommandSource source, String reason) {
if (!isRunning()) throw new IllegalStateException("Tracer is already stopped");

TwitchSpawn.LOGGER.info("Stopping all the tracers...");

// Stop tracers
streamlabsSocketTracer.stop();
socketIOTracers.forEach(SocketIOTracer::stop);

running = false;

if (TwitchSpawn.SERVER != null) {
TwitchSpawn.SERVER.getPlayerList().sendMessage(
Expand Down
@@ -1,16 +1,11 @@
package net.programmer.igoodie.twitchspawn.tslanguage;

import com.google.common.base.Defaults;
import net.programmer.igoodie.twitchspawn.tslanguage.event.TSLEvent;
import net.programmer.igoodie.twitchspawn.tslanguage.event.TSLEventPair;
import net.programmer.igoodie.twitchspawn.tslanguage.keyword.TSLEventKeyword;
import net.programmer.igoodie.twitchspawn.tslanguage.predicate.TSLPredicate;

import java.awt.*;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

public class EventArguments {

Expand All @@ -26,8 +21,8 @@ public static EventArguments createRandom(String streamerNickname) {
/* ------------------------------ */

public final String eventType;
public final String eventFor;
public final String eventAlias;
public final String eventAccount;
public final String eventName;

public String streamerNickname;
public String actorNickname;
Expand All @@ -41,14 +36,14 @@ public static EventArguments createRandom(String streamerNickname) {
public int viewerCount;
public int raiderCount;

public EventArguments(String eventType, String eventFor) {
public EventArguments(String eventType, String eventAccount) {
this.eventType = eventType;
this.eventFor = eventFor;
this.eventAlias = TSLEventKeyword.ofPair(eventType, eventFor);
this.eventAccount = eventAccount;
this.eventName = TSLEventKeyword.ofPair(eventType, eventAccount);
}

public EventArguments(TSLEventPair eventPair) {
this(eventPair.getEventType(), eventPair.getEventFor());
this(eventPair.getEventType(), eventPair.getEventAccount());
}

public void randomize() {
Expand Down
Expand Up @@ -37,7 +37,7 @@ public void handleEvent(EventArguments args) {
TwitchSpawn.LOGGER.info("Handling (for {}) arguments {}", args.streamerNickname, args);

// Fetch event pair and keyword
TSLEventPair eventPair = new TSLEventPair(args.eventType, args.eventFor);
TSLEventPair eventPair = new TSLEventPair(args.eventType, args.eventAccount);
String eventKeyword = TSLEventKeyword.ofPair(eventPair);

// Event pair is not known by TSL
Expand Down
Expand Up @@ -13,9 +13,7 @@
import net.programmer.igoodie.twitchspawn.tslanguage.EventArguments;
import net.programmer.igoodie.twitchspawn.tslanguage.TSLFlowNode;
import net.programmer.igoodie.twitchspawn.tslanguage.keyword.TSLActionKeyword;
import net.programmer.igoodie.twitchspawn.tslanguage.parser.TSLParser;
import net.programmer.igoodie.twitchspawn.tslanguage.parser.TSLRuleTokenizer;
import net.programmer.igoodie.twitchspawn.tslanguage.parser.TSLSyntaxError;
import net.programmer.igoodie.twitchspawn.util.MessageEvaluator;

import java.text.SimpleDateFormat;
Expand Down Expand Up @@ -119,7 +117,7 @@ public boolean process(EventArguments args) {
// If not silent, notify player
if (!silent) {
// Fetch title and format it
String title = ConfigManager.TITLES.getTextComponentRaw(args.eventAlias);
String title = ConfigManager.TITLES.getTextComponentRaw(args.eventName);
title = MessageEvaluator.replaceExpressions(title, expression -> {
// TODO: Implement better expression evaluator
if (expression.equals("actor"))
Expand Down
Expand Up @@ -3,24 +3,24 @@
public class TSLEventPair {

private final String eventType;
private final String eventFor;
private final String eventAccount;

public TSLEventPair(String eventType, String eventFor) {
public TSLEventPair(String eventType, String eventAccount) {
this.eventType = eventType;
this.eventFor = eventFor;
this.eventAccount = eventAccount;
}

public String getEventType() {
return eventType;
}

public String getEventFor() {
return eventFor;
public String getEventAccount() {
return eventAccount;
}

@Override
public int hashCode() {
return eventType.hashCode() ^ eventFor.hashCode();
return eventType.hashCode() ^ eventAccount.hashCode();
}

@Override
Expand All @@ -30,12 +30,12 @@ public boolean equals(Object o) {

TSLEventPair other = (TSLEventPair) o;

return this.eventType.equals(other.eventType)
&& this.eventFor.equals(other.eventFor);
return this.eventType.equalsIgnoreCase(other.eventType)
&& this.eventAccount.equalsIgnoreCase(other.eventAccount);
}

@Override
public String toString() {
return String.format("(%s, %s)", eventType, eventFor);
return String.format("(%s, %s)", eventType, eventAccount);
}
}

0 comments on commit a08dbb1

Please sign in to comment.