-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tsl): add Twitch Chat Message event
- Added new TSL Event: Twitch Chat Message - Fixed the multi-trigger on WebSocketTracer for good
- Loading branch information
Showing
14 changed files
with
318 additions
and
24 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/main/java/net/programmer/igoodie/twitchspawn/easteregg/Developers.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package net.programmer.igoodie.twitchspawn.easteregg; | ||
|
||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
public class Developers { | ||
|
||
public static final Set<String> TWITCH_NICKS = new HashSet<>( | ||
Arrays.asList("iGoodiex", "TheDiaval", "Girloflegend")); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
src/main/java/net/programmer/igoodie/twitchspawn/tracer/chat/TwitchChatTracer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package net.programmer.igoodie.twitchspawn.tracer.chat; | ||
|
||
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.tracer.Platform; | ||
import net.programmer.igoodie.twitchspawn.tracer.TraceManager; | ||
import net.programmer.igoodie.twitchspawn.tracer.WebSocketTracer; | ||
import net.programmer.igoodie.twitchspawn.tracer.model.TwitchChatMessage; | ||
import net.programmer.igoodie.twitchspawn.tslanguage.EventArguments; | ||
import net.programmer.igoodie.twitchspawn.util.CooldownBucket; | ||
import okhttp3.Response; | ||
import okhttp3.WebSocket; | ||
import okhttp3.WebSocketListener; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.stream.Stream; | ||
|
||
public class TwitchChatTracer extends WebSocketTracer { | ||
|
||
// Streamer Nickname -> CooldownBucket | ||
public Map<String, CooldownBucket> cooldownBuckets; | ||
|
||
public TwitchChatTracer(TraceManager manager) { | ||
super(Platform.TWITCH_CHAT_IRC, manager); | ||
this.cooldownBuckets = new HashMap<>(); | ||
} | ||
|
||
@Override | ||
public void start() { | ||
for (CredentialsConfig.Streamer streamer : ConfigManager.CREDENTIALS.streamers) { | ||
WebSocketListener socket = createSocket(streamer); | ||
this.sockets.add(startClient(socket)); | ||
this.cooldownBuckets.put(streamer.twitchNick, new CooldownBucket()); | ||
} | ||
} | ||
|
||
@Override | ||
public void stop() { | ||
for (WebSocket socket : this.sockets) { | ||
if (!socket.close(1000, null)) { | ||
socket.cancel(); | ||
} | ||
} | ||
this.cooldownBuckets.clear(); | ||
} | ||
|
||
/* --------------------------------------------------- */ | ||
|
||
@Override | ||
protected void onOpen(CredentialsConfig.Streamer streamer, WebSocket socket, Response response) { | ||
TwitchSpawn.LOGGER.info("Attempting to connect Twitch Chat of {}", streamer.twitchNick); | ||
|
||
socket.send("PASS " + streamer.tokenChat); | ||
socket.send("NICK " + streamer.twitchNick.toLowerCase()); | ||
socket.send("JOIN #" + streamer.twitchNick.toLowerCase()); | ||
socket.send("CAP REQ :twitch.tv/tags"); | ||
socket.send("PRIVMSG #" + streamer.twitchNick.toLowerCase() | ||
+ " :TwitchSpawn now connected to the chat! Hey folks!"); | ||
|
||
// https://twitchapps.com/tmi/ | ||
} | ||
|
||
@Override | ||
protected void onClosing(CredentialsConfig.Streamer streamer, WebSocket socket, int code, String reason) { | ||
TwitchSpawn.LOGGER.info("Disconnected from {}'s Twitch Chat connection. (intentional)", streamer.minecraftNick); | ||
} | ||
|
||
@Override | ||
protected void onMessage(CredentialsConfig.Streamer streamer, WebSocket socket, String text) { | ||
Stream.of(text.split("\r?\n")).map(String::trim).forEach(message -> { | ||
if (message.equals("PING :tmi.twitch.tv")) { | ||
socket.send("PONG :tmi.twitch.tv"); | ||
|
||
} else if (TwitchChatMessage.matches(message)) { | ||
TwitchChatMessage twitchChatMessage = new TwitchChatMessage(message); | ||
onChatMessage(streamer, twitchChatMessage); | ||
|
||
} else if (message.contains(":tmi.twitch.tv NOTICE")) { | ||
if (message.contains("Improperly formatted auth")) { | ||
// Intentionally left empty/malformed. | ||
TwitchSpawn.LOGGER.info("Disconnected from {}'s Twitch Chat connection. (no token)", streamer.minecraftNick); | ||
socket.cancel(); | ||
|
||
} else if (message.contains("Login authentication failed")) { | ||
// Uh oh invalid token? | ||
TwitchSpawn.LOGGER.warn("Disconnected from {}'s Twitch Chat connection. (unauthorized)", streamer.minecraftNick); | ||
manager.stop(null, streamer.twitchNick + " unauthorized by the Twitch Chat server."); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
protected void onChatMessage(CredentialsConfig.Streamer streamer, TwitchChatMessage twitchChatMessage) { | ||
CooldownBucket cooldownBucket = cooldownBuckets.get(streamer.twitchNick); | ||
|
||
if (cooldownBucket.canConsume(twitchChatMessage.username)) { | ||
EventArguments eventArguments = new EventArguments("chat", "twitch"); | ||
eventArguments.streamerNickname = streamer.minecraftNick; | ||
eventArguments.actorNickname = twitchChatMessage.username; | ||
eventArguments.message = twitchChatMessage.message; | ||
eventArguments.subscriptionMonths = twitchChatMessage.subscriptionMonths; | ||
// TODO: Add badges too | ||
|
||
ConfigManager.RULESET_COLLECTION.handleEvent(eventArguments, cooldownBucket); | ||
|
||
} else { | ||
TwitchSpawn.LOGGER.info("Still has {} seconds global cooldown.", cooldownBucket.getGlobalCooldown()); | ||
} | ||
} | ||
|
||
public static void main(String[] args) { | ||
new TwitchChatTracer(new TraceManager()).start(); | ||
} | ||
|
||
} |
72 changes: 72 additions & 0 deletions
72
src/main/java/net/programmer/igoodie/twitchspawn/tracer/model/TwitchChatMessage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package net.programmer.igoodie.twitchspawn.tracer.model; | ||
|
||
import net.programmer.igoodie.twitchspawn.easteregg.Developers; | ||
|
||
import java.util.*; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Stream; | ||
|
||
public class TwitchChatMessage { | ||
|
||
public static final Pattern TWITCH_CHAT_PATTERN = Pattern.compile("^@(?<tags>.*?) (:(?<user>.*?)!.*?\\.tmi\\.twitch\\.tv) PRIVMSG #(?<channel>.*?) :(?<msg>.*)$"); | ||
|
||
private String raw; | ||
|
||
public String username; | ||
public String message; | ||
public Set<String> badges; // admin, bits, broadcaster, global_mod, moderator, subscriber, staff, turbo, vip, glhf-pledge | ||
public int subscriptionMonths; | ||
public boolean isDeveloper; | ||
|
||
public TwitchChatMessage(String raw) { | ||
this.raw = raw; | ||
this.badges = new HashSet<>(); | ||
|
||
Matcher matcher = TWITCH_CHAT_PATTERN.matcher(raw); | ||
|
||
if (matcher.matches()) { | ||
Map<String, String> tags = parseTags(matcher.group("tags")); | ||
|
||
String displayName = tags.getOrDefault("display-name", ""); | ||
this.username = displayName.isEmpty() ? matcher.group("user") : displayName; | ||
|
||
Stream.of(tags.getOrDefault("badges", "").split(",")).forEach(badgeRaw -> { | ||
if (badgeRaw.isEmpty()) return; | ||
String[] parts = badgeRaw.split("/", 2); | ||
String badgeName = parts[0]; | ||
String badgeVersion = parts[1]; | ||
badges.add(badgeName); | ||
}); | ||
|
||
Stream.of(tags.getOrDefault("badge-info", "").split(",")).forEach(infoRaw -> { | ||
if (infoRaw.isEmpty()) return; | ||
String[] parts = infoRaw.split("/", 2); | ||
String infoName = parts[0]; | ||
String infoValue = parts[1]; | ||
if (infoName.equals("subscriber")) | ||
subscriptionMonths = Integer.parseInt(infoValue); | ||
}); | ||
|
||
this.isDeveloper = Developers.TWITCH_NICKS.contains(this.username); | ||
|
||
this.message = matcher.group("msg"); | ||
} | ||
} | ||
|
||
public static Map<String, String> parseTags(String tagsRaw) { | ||
Map<String, String> tags = new HashMap<>(); | ||
|
||
for (String tagPairRaw : tagsRaw.split(";")) { | ||
String[] tagPair = tagPairRaw.split("=", 2); | ||
tags.put(tagPair[0], tagPair[1]); | ||
} | ||
|
||
return tags; | ||
} | ||
|
||
public static boolean matches(String raw) { | ||
return TWITCH_CHAT_PATTERN.matcher(raw).matches(); | ||
} | ||
|
||
} |
5 changes: 4 additions & 1 deletion
5
...wn/tracer/StreamElementsSocketTracer.java → ...er/socket/StreamElementsSocketTracer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 4 additions & 1 deletion
5
...hspawn/tracer/StreamlabsSocketTracer.java → ...tracer/socket/StreamlabsSocketTracer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.