From: Jordan Duabe <1442368+j4ckofalltrades@users.noreply.github.com>
Date: Sat, 27 Feb 2021 14:32:22 +0530
Subject: Initial Twitch Chat action implemenation
---
Initial Twitch Chat action implemenation
Includes server-side action for configuring connection to Twitch Chat
and client-side action for sending message(s) to a channel.
---
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.iml
+.idea/
+
+target/
Binary files /dev/null and b/PreBuiltPlugins/Java-Twirk-0.6.3.jar differ
Binary files /dev/null and b/PreBuiltPlugins/twitch-chat-connect.jar differ
Binary files /dev/null and b/PreBuiltPlugins/twitch-send-channel-msg.jar differ
--- 'a/README.md'
+++ b/README.md
@@ -17,6 +17,25 @@ Set of trusted, pre-bundled actions and
- Text Block
- Media File
- Media Key
+- Twitch
+
+## Actions Help Guide
+
+### Twitch Chat Integration
+
+The first step is to acquire an OAuth token from https://twitchapps.com/tmi/, the generated OAuth token should look something like `oauth:xxxxx`.
+
+In the Stream-Pi Server's Plugin page enter your Twitch username, and the generated token then click on `Save Twitch chat credentials` button. You should then be able to use the pre-bundled Twitch chat actions.
+
+### Supported actions
+
+- Send channel message
+
+### Running locally
+
+Copy the `twitch-chat-connect`, `twitch-send-channel-msg`, and `Java-Twirk` jar files from the `PreBuiltPlugins` directory to your Stream-Pi server plugins' directory.
+
+---
## Quick Start
--- 'a/build.sh'
+++ b/build.sh
@@ -1,83 +1,72 @@
FOLD=../PreBuiltPlugins
+pushd . || exit
cd hotkeyaction && mvn clean package
-
mv target/hotkeyaction-1.0.0.jar $FOLD/hotkeyaction.jar
cd ../mediakeyaction && mvn clean package
-
mv target/mediakeyaction-1.0.0.jar $FOLD/mediakeyaction.jar
cd ../playaudioclipaction && mvn clean package
-
mv target/playaudioclipaction-1.0.0.jar $FOLD/playaudioclipaction.jar
-cd ../runcommandaction && mvn clean package
-
+cd ../runcommandaction && mvn clean package
mv target/runcommandaction-1.0.0.jar $FOLD/runcommandaction.jar
cd ../textblockaction && mvn clean package
-
mv target/textblockaction-1.0.0.jar $FOLD/textblockaction.jar
-cd ../twitteraction && mvn clean package
-
+cd ../twitteraction && mvn clean package
mv target/twitteraction-1.0.0.jar $FOLD/twitteraction.jar
cd ../websiteaction && mvn clean package
-
mv target/websiteaction-1.0.0.jar $FOLD/websiteaction.jar
+popd || exit
-
-# OBS Suite
-
-cd ../obssuite/mother && mvn clean install package
-
+## OBS Suite
+pushd . || exit
+cd obssuite/mother && mvn clean install package
mv target/obssuite_motheraction-1.0.0.jar ../$FOLD/obssuite_motheraction.jar
-cd ../setcurrentprofile && mvn clean package
-
+cd ../setcurrentprofile && mvn clean package
mv target/obssuite_setcurrentprofileaction-1.0.0.jar ../$FOLD/obssuite_setcurrentprofileaction.jar
-cd ../setcurrentscene && mvn clean package
-
+cd ../setcurrentscene && mvn clean package
mv target/obssuite_setcurrentsceneaction-1.0.0.jar ../$FOLD/obssuite_setcurrentsceneaction.jar
-cd ../setcurrenttransition && mvn clean package
-
+cd ../setcurrenttransition && mvn clean package
mv target/obssuite_setcurrenttransitionaction-1.0.0.jar ../$FOLD/obssuite_setcurrenttransitionaction.jar
-cd ../setmute && mvn clean package
-
+cd ../setmute && mvn clean package
mv target/obssuite_setmuteaction-1.0.0.jar ../$FOLD/obssuite_setmuteaction.jar
-cd ../setcurrentprofile && mvn clean package
-
+cd ../setcurrentprofile && mvn clean package
mv target/obssuite_setcurrentprofileaction-1.0.0.jar ../$FOLD/obssuite_setcurrentprofileaction.jar
-cd ../setpreviewscene && mvn clean package
-
+cd ../setpreviewscene && mvn clean package
mv target/obssuite_setpreviewsceneaction-1.0.0.jar ../$FOLD/obssuite_setpreviewsceneaction.jar
cd ../setrecording && mvn clean package
-
mv target/obssuite_setrecordingaction-1.0.0.jar ../$FOLD/obssuite_setrecordingaction.jar
-cd ../setreplaybuffer && mvn clean package
-
+cd ../setreplaybuffer && mvn clean package
mv target/obssuite_setreplaybufferaction-1.0.0.jar ../$FOLD/obssuite_setreplaybufferaction.jar
-cd ../setstreaming && mvn clean package
-
+cd ../setstreaming && mvn clean package
mv target/obssuite_setstreamingaction-1.0.0.jar ../$FOLD/obssuite_setstreamingaction.jar
-cd ../setstudiomode && mvn clean package
-
+cd ../setstudiomode && mvn clean package
mv target/obssuite_setstudiomodeaction-1.0.0.jar ../$FOLD/obssuite_setstudiomodeaction.jar
cd ../setvolume && mvn clean package
-
mv target/obssuite_setvolumeaction-1.0.0.jar ../$FOLD/obssuite_setvolumeaction.jar
+popd || exit
-
-
+# Twitch Chat
+pushd . || exit
+cd twitch/twitch-chat-connect && mvn clean package
+mv target/twitch-chat-connect-1.0.0.jar ../$FOLD/twitch-chat-connect.jar
+
+cd ../send-channel-msg && mvn clean package
+mv target/twitch-send-channel-msg-1.0.0.jar ../$FOLD/twitch-send-channel-msg.jar
+popd || exit
--- /dev/null
+++ b/twitch/send-channel-msg/pom.xml
@@ -0,0 +1,81 @@
+
+
+ 4.0.0
+
+ com.stream-pi
+ twitch-send-channel-msg
+ 1.0.0
+
+
+ 11
+ 11
+ 1.0.0
+ 1.0.0
+ 1.0.0
+ 0.6.3
+
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.0
+
+
+ test-jar
+ package
+
+ test-jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 11
+
+
+
+
+
+
+
+ com.stream-pi
+ util
+ ${streamPiUtilVersion}
+
+
+
+ com.stream-pi
+ action-api
+ ${streamPiActionApiVersion}
+
+
+
+ com.github.gikkman
+ Java-Twirk
+ ${javaTwirkVersion}
+
+
+
+ com.stream-pi
+ twitch-chat-connect
+ ${streamPiTwitchChatConnectVersion}
+
+
+
+
\ No newline at end of file
--- /dev/null
+++ b/twitch/send-channel-msg/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.sendchannelmsgaction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.normalaction.NormalAction with sendchannelmsg.SendChannelMessageAction;
+}
\ No newline at end of file
--- /dev/null
+++ b/twitch/send-channel-msg/src/main/java/sendchannelmsg/SendChannelMessageAction.java
@@ -0,0 +1,117 @@
+package sendchannelmsg;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.util.alert.StreamPiAlert;
+import com.stream_pi.util.alert.StreamPiAlertType;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class SendChannelMessageAction extends NormalAction
+{
+
+ private static final String TWITCH_CHANNEL_NAME_KEY = "twitch_channel_name";
+ private static final String TWITCH_CHANNEL_MSG_KEY = "twitch_channel_msg";
+
+ private Twirk twirk;
+
+ public SendChannelMessageAction()
+ {
+ setName("Send Channel Message");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property channelName = new Property(TWITCH_CHANNEL_NAME_KEY, Type.STRING);
+ channelName.setDisplayName("Channel Name");
+ channelName.setDefaultValueStr("channel_name");
+ channelName.setCanBeBlank(false);
+
+ Property channelMessage = new Property(TWITCH_CHANNEL_MSG_KEY, Type.STRING);
+ channelMessage.setDisplayName("Message");
+ channelMessage.setDefaultValueStr("channel_msg");
+ channelMessage.setCanBeBlank(false);
+
+ addClientProperties(channelName, channelMessage);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ if (!isChatCredentialsInitialized(credentials))
+ {
+ new StreamPiAlert(
+ "Twitch Chat plugin configuration uninitialized",
+ "Please check that the Twitch Chat plugin configuration is correct.",
+ StreamPiAlertType.ERROR
+ ).show();
+ return;
+ }
+
+ final String channel = getClientProperties().getSingleProperty(TWITCH_CHANNEL_NAME_KEY).getStringValue();
+ final String message = getClientProperties().getSingleProperty(TWITCH_CHANNEL_MSG_KEY).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(
+ getClientProperties().getSingleProperty(TWITCH_CHANNEL_NAME_KEY).getStringValue(),
+ credentials.getNickname(),
+ credentials.getOauthToken())
+ .build();
+ twirk.connect();
+ twirk.channelMessage(message);
+ } catch (Exception ex)
+ {
+ new StreamPiAlert(
+ "Failed to send channel message",
+ String.format("Could not send message '%s' to '%s' channel, please try again.",
+ channel, message),
+ StreamPiAlertType.ERROR
+ ).show();
+ }
+ }
+
+ private boolean isChatCredentialsInitialized(TwitchChatCredentials.ChatCredentials credentials)
+ {
+ if (credentials == null)
+ {
+ return false;
+ }
+
+ final String twitchNickname = credentials.getNickname();
+ boolean isNicknameInitialized = twitchNickname != null &&
+ !twitchNickname.isEmpty() &&
+ !twitchNickname.equalsIgnoreCase("twitch_nickname");
+
+ final String twitchChatOauthToken = credentials.getOauthToken();
+ boolean isTokenInitialized = twitchChatOauthToken != null &&
+ !twitchChatOauthToken.isEmpty() &&
+ !twitchChatOauthToken.equalsIgnoreCase("twitch_oauth_token");
+
+ return isNicknameInitialized && isTokenInitialized;
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ twirk.close();
+ }
+}
+
+
--- /dev/null
+++ b/twitch/twitch-chat-connect/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+ com.stream-pi
+ twitch-chat-connect
+ 1.0.0
+
+
+ 11
+ 11
+ 1.0.0
+ 1.0.0
+ 0.6.3
+
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.0
+
+
+ test-jar
+ package
+
+ test-jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 11
+
+
+
+
+
+
+
+ com.github.gikkman
+ Java-Twirk
+ ${javaTwirkVersion}
+
+
+
+ com.stream-pi
+ util
+ ${streamPiUtilVersion}
+
+
+
+ com.stream-pi
+ action-api
+ ${streamPiActionApiVersion}
+
+
+
+
\ No newline at end of file
--- /dev/null
+++ b/twitch/twitch-chat-connect/src/main/java/connect/TwitchChatConnectAction.java
@@ -0,0 +1,96 @@
+package connect;
+
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.util.alert.StreamPiAlert;
+import com.stream_pi.util.alert.StreamPiAlertType;
+import com.stream_pi.util.exception.MinorException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+import javafx.scene.control.Button;
+
+public class TwitchChatConnectAction extends NormalAction
+{
+
+ private static final String TWITCH_ACCESS_TOKEN_KEY = "twitch_access_token";
+ private static final String TWITCH_NICKNAME_KEY = "twitch_nickname";
+
+ private final Button saveCredentialsBtn;
+
+ public TwitchChatConnectAction()
+ {
+ setName("Twitch Chat Plugin");
+ setCategory("Twitch Chat");
+ setVisibilityInPluginsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/Stream-Pi/essentialactions");
+
+ saveCredentialsBtn = new Button("Save Twitch chat credentials");
+ setButtonBar(saveCredentialsBtn);
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property twitchNicknameProp = new Property(TWITCH_NICKNAME_KEY, Type.STRING);
+ twitchNicknameProp.setDisplayName("Twitch Username");
+ twitchNicknameProp.setDefaultValueStr("twitch_nickname");
+ twitchNicknameProp.setCanBeBlank(false);
+
+ Property twitchAccessTokenProp = new Property(TWITCH_ACCESS_TOKEN_KEY, Type.STRING);
+ twitchAccessTokenProp.setDisplayName("Access Token");
+ twitchAccessTokenProp.setDefaultValueStr("twitch_oauth_token");
+ twitchAccessTokenProp.setCanBeBlank(false);
+
+ addServerProperties(twitchNicknameProp, twitchAccessTokenProp);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+ saveCredentialsBtn.setOnAction(action ->
+ {
+ try
+ {
+ persistCredentials();
+
+ new StreamPiAlert(
+ "Twitch chat credentials saved",
+ "Chat credentials been saved, you can now start using Twitch chat integration actions.",
+ StreamPiAlertType.INFORMATION)
+ .show();
+ } catch (Exception e)
+ {
+ new StreamPiAlert(
+ "Failed to save chat credentials",
+ "An error has occurred while saving chat credentials, please try again.",
+ StreamPiAlertType.WARNING)
+ .show();
+ }
+ });
+
+ persistCredentials();
+ }
+
+ private void persistCredentials() throws MinorException
+ {
+ final String token = getServerProperties().getSingleProperty(TWITCH_ACCESS_TOKEN_KEY).getStringValue();
+ final String nickname = getServerProperties().getSingleProperty(TWITCH_NICKNAME_KEY).getStringValue();
+ TwitchChatCredentials.setCredentials(
+ new TwitchChatCredentials.ChatCredentials()
+ .setOauthToken(token)
+ .setNickname(nickname));
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ }
+}
--- /dev/null
+++ b/twitch/twitch-chat-connect/src/main/java/connect/chat/TwitchChatCredentials.java
@@ -0,0 +1,45 @@
+package connect.chat;
+
+public final class TwitchChatCredentials
+{
+
+ private static ChatCredentials credentials;
+
+ public static ChatCredentials getCredentials()
+ {
+ return credentials;
+ }
+
+ public static void setCredentials(ChatCredentials chatCredentials)
+ {
+ credentials = chatCredentials;
+ }
+
+ public static class ChatCredentials
+ {
+ String nickname;
+ String oauthToken;
+
+ public String getNickname()
+ {
+ return nickname;
+ }
+
+ public ChatCredentials setNickname(String nickname)
+ {
+ this.nickname = nickname;
+ return this;
+ }
+
+ public String getOauthToken()
+ {
+ return oauthToken;
+ }
+
+ public ChatCredentials setOauthToken(String oauthToken)
+ {
+ this.oauthToken = oauthToken;
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/twitch/twitch-chat-connect/src/main/java/module-info.java
@@ -0,0 +1,13 @@
+import connect.TwitchChatConnectAction;
+
+module com.stream_pi.twitchconnectaction {
+
+ requires com.stream_pi.action_api;
+ requires com.stream_pi.util;
+
+ requires Java.Twirk;
+
+ exports connect.chat;
+
+ provides com.stream_pi.action_api.normalaction.NormalAction with TwitchChatConnectAction;
+}
\ No newline at end of file