done i think
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
97
pom.xml
97
pom.xml
@@ -0,0 +1,97 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||||
|
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.minster586</groupId>
|
||||||
|
<artifactId>tiktok-stream-plugin</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>TikTokStreamPlugin</name>
|
||||||
|
<description>Integrates TikTok Live events with Minecraft 1.19</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spigot API for Minecraft 1.19 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot-api</artifactId>
|
||||||
|
<version>1.19-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Java-WebSocket for TikTok stream events -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.java-websocket</groupId>
|
||||||
|
<artifactId>Java-WebSocket</artifactId>
|
||||||
|
<version>1.5.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Gson for JSON parsing -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.10.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SnakeYAML for YAML parsing -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
<version>2.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<!-- Spigot repo for 1.19 snapshot -->
|
||||||
|
<repository>
|
||||||
|
<id>spigot-repo</id>
|
||||||
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<!-- Compiler plugin -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>${java.version}</source>
|
||||||
|
<target>${java.version}</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Shade plugin to bundle dependencies -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals><goal>shade</goal></goals>
|
||||||
|
<configuration>
|
||||||
|
<relocations>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.java_websocket</pattern>
|
||||||
|
<shadedPattern>com.minster586.shaded.websocket</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
</relocations>
|
||||||
|
<minimizeJar>true</minimizeJar>
|
||||||
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
102
src/main/java/com/minster586/events/TikTokEventDispatcher.java
Normal file
102
src/main/java/com/minster586/events/TikTokEventDispatcher.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package com.minster586.tiktokstream.events;
|
||||||
|
|
||||||
|
import com.minster586.tiktokstream.TikTokStreamPlugin;
|
||||||
|
import com.minster586.tiktokstream.config.ConfigManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
|
import com.minster586.ui.NotificationManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TikTokEventDispatcher {
|
||||||
|
|
||||||
|
private final ConfigManager config;
|
||||||
|
|
||||||
|
public TikTokEventDispatcher() {
|
||||||
|
this.config = TikTokStreamPlugin.getInstance().getConfigManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleGift(String username, String giftName, int amount) {
|
||||||
|
if (!config.isGiftEnabled()) return;
|
||||||
|
|
||||||
|
List<String> locations = config.getGiftLocations();
|
||||||
|
String prefix = config.getPrefix();
|
||||||
|
|
||||||
|
String chatMessage = config.getGiftChatMessage()
|
||||||
|
.replace("%prefix%", prefix)
|
||||||
|
.replace("%username%", username)
|
||||||
|
.replace("%giftName%", giftName)
|
||||||
|
.replace("%amount%", String.valueOf(amount));
|
||||||
|
|
||||||
|
String actionBarMessage = config.getGiftActionBarMessage()
|
||||||
|
.replace("%prefix%", prefix)
|
||||||
|
.replace("%username%", username)
|
||||||
|
.replace("%giftName%", giftName)
|
||||||
|
.replace("%amount%", String.valueOf(amount));
|
||||||
|
|
||||||
|
if (locations.contains("chat")) {
|
||||||
|
Bukkit.broadcastMessage(chatMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locations.contains("action-bar")) {
|
||||||
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(actionBarMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toast notifications removed. Use NotificationManager for chat/action-bar only.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleJoin(String username) {
|
||||||
|
if (!config.isJoinEnabled()) return;
|
||||||
|
|
||||||
|
String location = config.getJoinLocation();
|
||||||
|
String message = config.getJoinActionBarMessage().replace("%username%", username);
|
||||||
|
|
||||||
|
switch (location) {
|
||||||
|
case "chat" -> Bukkit.broadcastMessage(config.getJoinChatMessage().replace("%username%", username));
|
||||||
|
case "action-bar" -> Bukkit.getOnlinePlayers().forEach(p -> p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(message)));
|
||||||
|
// Toast notifications removed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleChat(String username, String messageText) {
|
||||||
|
if (!config.isChatEnabled()) return;
|
||||||
|
|
||||||
|
String location = config.getChatLocation();
|
||||||
|
String formatted = config.getChatMessage()
|
||||||
|
.replace("%prefix%", config.getPrefix())
|
||||||
|
.replace("%username%", username)
|
||||||
|
.replace("%message%", messageText);
|
||||||
|
|
||||||
|
switch (location) {
|
||||||
|
case "chat" -> Bukkit.broadcastMessage(formatted);
|
||||||
|
case "action-bar" -> Bukkit.getOnlinePlayers().forEach(p -> p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(
|
||||||
|
config.getChatActionBarMessage()
|
||||||
|
.replace("%username%", username)
|
||||||
|
.replace("%message%", messageText)
|
||||||
|
)));
|
||||||
|
// Toast notifications removed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleFollow(String username) {
|
||||||
|
if (!config.isFollowEnabled()) return;
|
||||||
|
|
||||||
|
List<String> locations = config.getFollowLocations();
|
||||||
|
|
||||||
|
if (locations.contains("chat")) {
|
||||||
|
Bukkit.broadcastMessage(config.getFollowChatMessage().replace("%username%", username));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locations.contains("action-bar")) {
|
||||||
|
Bukkit.getOnlinePlayers().forEach(p -> p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(
|
||||||
|
config.getFollowActionBarMessage().replace("%username%", username)
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toast notifications removed. Use NotificationManager for chat/action-bar only.
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
package com.minster586.tiktokstream;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabCompleter;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TikTokLiveCommand implements CommandExecutor, TabCompleter {
|
||||||
|
private final TikTokStreamPlugin plugin;
|
||||||
|
private static final String PERMISSION = "tiktok.live";
|
||||||
|
|
||||||
|
public TikTokLiveCommand(TikTokStreamPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (!sender.hasPermission(PERMISSION)) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "You do not have permission to use this command.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (args.length != 1 || !(args[0].equalsIgnoreCase("enable") || args[0].equalsIgnoreCase("disable"))) {
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "Usage: /tiktok live <enable|disable>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
boolean enable = args[0].equalsIgnoreCase("enable");
|
||||||
|
plugin.setTiktokLiveEnabled(enable);
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "TikTok live notifications " + (enable ? "enabled" : "disabled") + ".");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||||
|
if (args.length == 1) {
|
||||||
|
return Arrays.asList("enable", "disable");
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
@@ -2,15 +2,18 @@ package com.minster586.tiktokstream;
|
|||||||
|
|
||||||
import com.minster586.tiktokstream.config.ConfigManager;
|
import com.minster586.tiktokstream.config.ConfigManager;
|
||||||
import com.minster586.tiktokstream.websocket.StreamerBotWebSocketClient;
|
import com.minster586.tiktokstream.websocket.StreamerBotWebSocketClient;
|
||||||
|
import com.minster586.tiktokstream.util.GiftMappingManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
public class TikTokStreamPlugin extends JavaPlugin {
|
|
||||||
|
|
||||||
|
public class TikTokStreamPlugin extends JavaPlugin {
|
||||||
private static TikTokStreamPlugin instance;
|
private static TikTokStreamPlugin instance;
|
||||||
private ConfigManager configManager;
|
private ConfigManager configManager;
|
||||||
private StreamerBotWebSocketClient webSocketClient;
|
private StreamerBotWebSocketClient webSocketClient;
|
||||||
|
private GiftMappingManager giftMappingManager;
|
||||||
|
private boolean tiktokLiveEnabled = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
@@ -20,12 +23,31 @@ public class TikTokStreamPlugin extends JavaPlugin {
|
|||||||
configManager = new ConfigManager(this);
|
configManager = new ConfigManager(this);
|
||||||
configManager.load();
|
configManager.load();
|
||||||
|
|
||||||
|
// Initialize GiftMappingManager
|
||||||
|
String giftMappingUrl = getConfig().getString("config.giftMappingUrl");
|
||||||
|
if (giftMappingUrl != null && !giftMappingUrl.isEmpty()) {
|
||||||
|
giftMappingManager = new GiftMappingManager(this, giftMappingUrl);
|
||||||
|
} else {
|
||||||
|
getLogger().warning("No giftMappingUrl found in config! Gift names will not be available.");
|
||||||
|
}
|
||||||
|
|
||||||
String websocketUrl = configManager.getWebSocketUrl();
|
String websocketUrl = configManager.getWebSocketUrl();
|
||||||
webSocketClient = new StreamerBotWebSocketClient(URI.create(websocketUrl));
|
webSocketClient = new StreamerBotWebSocketClient(URI.create(websocketUrl));
|
||||||
webSocketClient.connect();
|
webSocketClient.connect();
|
||||||
|
|
||||||
|
// Register /tiktok live command
|
||||||
|
getCommand("tiktok").setExecutor(new TikTokLiveCommand(this));
|
||||||
|
getCommand("tiktok").setTabCompleter(new TikTokLiveCommand(this));
|
||||||
|
|
||||||
getLogger().info("TikTokStreamPlugin enabled and connected to Streamer.bot.");
|
getLogger().info("TikTokStreamPlugin enabled and connected to Streamer.bot.");
|
||||||
}
|
}
|
||||||
|
public boolean isTiktokLiveEnabled() {
|
||||||
|
return tiktokLiveEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTiktokLiveEnabled(boolean enabled) {
|
||||||
|
this.tiktokLiveEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
@@ -42,4 +64,8 @@ public class TikTokStreamPlugin extends JavaPlugin {
|
|||||||
public ConfigManager getConfigManager() {
|
public ConfigManager getConfigManager() {
|
||||||
return configManager;
|
return configManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GiftMappingManager getGiftMappingManager() {
|
||||||
|
return giftMappingManager;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,150 @@
|
|||||||
|
package com.minster586.tiktokstream.config;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ConfigManager {
|
||||||
|
|
||||||
|
private final JavaPlugin plugin;
|
||||||
|
private FileConfiguration config;
|
||||||
|
|
||||||
|
public ConfigManager(JavaPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load() {
|
||||||
|
File configFile = new File(plugin.getDataFolder(), "config.yml");
|
||||||
|
|
||||||
|
if (!configFile.exists()) {
|
||||||
|
plugin.saveResource("config.yml", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
|
||||||
|
// Load defaults from resources/config.yml
|
||||||
|
InputStream defaultStream = plugin.getResource("config.yml");
|
||||||
|
if (defaultStream != null) {
|
||||||
|
YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defaultStream));
|
||||||
|
config.setDefaults(defaultConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔧 Accessors (no hardcoded defaults — rely on config.yml)
|
||||||
|
public String getPrefix() {
|
||||||
|
return config.getString("config.prefix");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTikTokUsername() {
|
||||||
|
return config.getString("config.tiktok_username");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGiftMappingUrl() {
|
||||||
|
return config.getString("config.giftMappingUrl");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getWebSocketUrl() {
|
||||||
|
return config.getString("config.websocket.url");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getWebSocketRefreshDelay() {
|
||||||
|
return config.getInt("config.websocket.refresh-delay");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJoinEnabled() {
|
||||||
|
return config.getBoolean("config.settings.join.joined-enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoinLocation() {
|
||||||
|
return config.getString("config.settings.join.loctions");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoinChatMessage() {
|
||||||
|
return config.getString("config.settings.join.chat-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoinActionBarMessage() {
|
||||||
|
return config.getString("config.settings.join.action-bar-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGiftEnabled() {
|
||||||
|
return config.getBoolean("config.settings.gift.gifts-enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getGiftLocations() {
|
||||||
|
return parseLocationList("config.settings.gift.loctions");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGiftChatMessage() {
|
||||||
|
return config.getString("config.settings.gift.chat-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGiftActionBarMessage() {
|
||||||
|
return config.getString("config.settings.gift.action-bar-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChatEnabled() {
|
||||||
|
return config.getBoolean("config.settings.chat.chat-enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChatLocation() {
|
||||||
|
return config.getString("config.settings.chat.loctions");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChatMessage() {
|
||||||
|
return config.getString("config.settings.chat.chat-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChatActionBarMessage() {
|
||||||
|
return config.getString("config.settings.chat.action-bar-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isFollowEnabled() {
|
||||||
|
return config.getBoolean("config.settings.follows.follow-enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getFollowLocations() {
|
||||||
|
return parseLocationList("config.settings.follows.loctions");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFollowChatMessage() {
|
||||||
|
return config.getString("config.settings.follows.chat-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFollowActionBarMessage() {
|
||||||
|
return config.getString("config.settings.follows.action-bar-message");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 🔧 Helpers
|
||||||
|
private List<String> parseLocationList(String path) {
|
||||||
|
String raw = config.getString(path);
|
||||||
|
if (raw == null) return Collections.emptyList();
|
||||||
|
return Arrays.stream(raw.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(s -> !s.isEmpty())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getNullableInt(String path) {
|
||||||
|
if (!config.contains(path) || config.get(path) == null) return null;
|
||||||
|
return config.getInt(path);
|
||||||
|
}
|
||||||
|
// Generic config accessors for plugin internals
|
||||||
|
public int getIntOrDefault(String path, int def) {
|
||||||
|
if (!config.contains(path) || config.get(path) == null) return def;
|
||||||
|
return config.getInt(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStringOrDefault(String path, String def) {
|
||||||
|
if (!config.contains(path) || config.get(path) == null) return def;
|
||||||
|
return config.getString(path);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,94 @@
|
|||||||
|
package com.minster586.tiktokstream.util;
|
||||||
|
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
public class GiftMappingManager {
|
||||||
|
private final JavaPlugin plugin;
|
||||||
|
private final File mappingFile;
|
||||||
|
private Map<Integer, String> giftMap = new HashMap<>();
|
||||||
|
|
||||||
|
public GiftMappingManager(JavaPlugin plugin, String mappingUrl) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.mappingFile = new File(plugin.getDataFolder(), "gift-mapping.yaml");
|
||||||
|
checkAndUpdateMapping(mappingUrl);
|
||||||
|
loadMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndUpdateMapping(String urlStr) {
|
||||||
|
try {
|
||||||
|
// Download remote file to temp
|
||||||
|
File tempFile = File.createTempFile("gift-mapping", ".yaml");
|
||||||
|
try (InputStream in = new URL(urlStr).openStream();
|
||||||
|
FileOutputStream out = new FileOutputStream(tempFile)) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Compare contents
|
||||||
|
boolean shouldReplace = !mappingFile.exists() || !filesEqual(mappingFile, tempFile);
|
||||||
|
if (shouldReplace) {
|
||||||
|
try (InputStream in = new FileInputStream(tempFile);
|
||||||
|
OutputStream out = new FileOutputStream(mappingFile)) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plugin.getLogger().info("Gift mapping updated from remote.");
|
||||||
|
} else {
|
||||||
|
plugin.getLogger().info("Gift mapping is up to date.");
|
||||||
|
}
|
||||||
|
tempFile.delete();
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().warning("Failed to check/update gift mapping: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filesEqual(File f1, File f2) throws IOException {
|
||||||
|
if (f1.length() != f2.length()) return false;
|
||||||
|
try (InputStream in1 = new FileInputStream(f1); InputStream in2 = new FileInputStream(f2)) {
|
||||||
|
int b1, b2;
|
||||||
|
do {
|
||||||
|
b1 = in1.read();
|
||||||
|
b2 = in2.read();
|
||||||
|
if (b1 != b2) return false;
|
||||||
|
} while (b1 != -1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadMapping is now handled by checkAndUpdateMapping
|
||||||
|
|
||||||
|
public void loadMapping() {
|
||||||
|
if (!mappingFile.exists()) return;
|
||||||
|
try (InputStream input = new FileInputStream(mappingFile)) {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
Map<String, Object> data = yaml.load(input);
|
||||||
|
giftMap.clear();
|
||||||
|
if (data != null) {
|
||||||
|
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||||
|
try {
|
||||||
|
int id = Integer.parseInt(entry.getKey());
|
||||||
|
giftMap.put(id, String.valueOf(entry.getValue()));
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().warning("Failed to load gift mapping: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGiftName(int id) {
|
||||||
|
return giftMap.getOrDefault(id, "Unknown Gift");
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
package com.minster586.tiktokstream.websocket;
|
||||||
|
|
||||||
|
|
||||||
|
import org.java_websocket.client.WebSocketClient;
|
||||||
|
import org.java_websocket.handshake.ServerHandshake;
|
||||||
|
import java.net.URI;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import com.minster586.tiktokstream.TikTokStreamPlugin;
|
||||||
|
import com.minster586.tiktokstream.config.ConfigManager;
|
||||||
|
|
||||||
|
public class StreamerBotWebSocketClient extends WebSocketClient {
|
||||||
|
private int retryCount = 0;
|
||||||
|
private final ConfigManager configManager;
|
||||||
|
private final int maxRetries;
|
||||||
|
private final String notifyPermission;
|
||||||
|
private final String notifyMessage;
|
||||||
|
|
||||||
|
public StreamerBotWebSocketClient(URI serverUri) {
|
||||||
|
super(serverUri);
|
||||||
|
this.configManager = TikTokStreamPlugin.getInstance().getConfigManager();
|
||||||
|
this.maxRetries = configManager != null ? configManager.getIntOrDefault("config.websocket.retry-count", 3) : 3;
|
||||||
|
this.notifyPermission = configManager != null ? configManager.getStringOrDefault("config.websocket.notify-permission", "tiktok.live") : "tiktok.live";
|
||||||
|
this.notifyMessage = configManager != null ? configManager.getStringOrDefault("config.websocket.notify-message", "§c[StreamerBot] Could not connect after %retries% attempts. Check your config or Streamer.bot status.") : "§c[StreamerBot] Could not connect after %retries% attempts. Check your config or Streamer.bot status.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen(ServerHandshake handshakedata) {
|
||||||
|
retryCount = 0; // Reset on successful connection
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(String message) {
|
||||||
|
// Handle incoming messages from Streamer.bot here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(int code, String reason, boolean remote) {
|
||||||
|
if (retryCount < maxRetries) {
|
||||||
|
retryCount++;
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000L * retryCount); // Exponential backoff
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
this.reconnect();
|
||||||
|
} else {
|
||||||
|
String msg = notifyMessage.replace("%retries%", String.valueOf(maxRetries));
|
||||||
|
notifyOps(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception ex) {
|
||||||
|
// Optionally log errors
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyOps(String message) {
|
||||||
|
Bukkit.getScheduler().runTask(Bukkit.getPluginManager().getPlugin("TikTokStreamPlugin"), () -> {
|
||||||
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
if (player.isOp() || player.hasPermission(notifyPermission)) {
|
||||||
|
player.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
54
src/main/java/com/minster586/ui/NotificationManager.java
Normal file
54
src/main/java/com/minster586/ui/NotificationManager.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package com.minster586.ui;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NotificationManager {
|
||||||
|
private static final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(NotificationManager.class);
|
||||||
|
|
||||||
|
public static void sendNotification(Player player, ConfigurationSection section, Map<String, String> placeholders) {
|
||||||
|
if (section == null) return;
|
||||||
|
|
||||||
|
String locationsRaw = section.getString("locations", "").toLowerCase();
|
||||||
|
String[] locations = locationsRaw.split(",");
|
||||||
|
|
||||||
|
for (String loc : locations) {
|
||||||
|
switch (loc.trim()) {
|
||||||
|
case "chat":
|
||||||
|
sendChat(player, section.getString("chat-message", ""), placeholders);
|
||||||
|
break;
|
||||||
|
case "action-bar":
|
||||||
|
sendActionBar(player, section.getString("action-bar-message", ""), placeholders);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Bukkit.getLogger().warning("Unknown notification location: " + loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendChat(Player player, String message, Map<String, String> placeholders) {
|
||||||
|
if (message == null || message.isEmpty()) return;
|
||||||
|
player.sendMessage(ChatColor.translateAlternateColorCodes('&', replacePlaceholders(message, placeholders)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendActionBar(Player player, String message, Map<String, String> placeholders) {
|
||||||
|
if (message == null || message.isEmpty()) return;
|
||||||
|
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(replacePlaceholders(message, placeholders)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String replacePlaceholders(String input, Map<String, String> placeholders) {
|
||||||
|
if (input == null) return "";
|
||||||
|
for (Map.Entry<String, String> entry : placeholders.entrySet()) {
|
||||||
|
input = input.replace("%" + entry.getKey() + "%", entry.getValue());
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
33
src/main/resources/config.yml
Normal file
33
src/main/resources/config.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
config:
|
||||||
|
prefix: [TikTok]
|
||||||
|
tiktok_username: Change-me #this is the username of your tiktok account with out the "@" symbol
|
||||||
|
giftMappingUrl: "https://yourdomain.com/gift-mapping.yaml" #you can change if need but might want to make sure it follows same scheam
|
||||||
|
|
||||||
|
websocket:
|
||||||
|
url: "ws://localhost:8080" # change IP if need
|
||||||
|
refresh-delay: 6 # this is in seconds
|
||||||
|
retry-count: 3 # Number of times to retry connecting to Streamer.bot
|
||||||
|
notify-permission: "tiktok.live" # Permission required to receive connection failure notifications
|
||||||
|
notify-message: "§c[StreamerBot] Could not connect after %retries% attempts. Check your config or Streamer.bot status."
|
||||||
|
|
||||||
|
settings:
|
||||||
|
join:
|
||||||
|
joined-enabled: true
|
||||||
|
loctions: action-bar #this can be chat, action-bar
|
||||||
|
chat-message: ""
|
||||||
|
action-bar-message: "%username% joined the stream!"
|
||||||
|
gift:
|
||||||
|
gifts-enabled: true
|
||||||
|
loctions: chat #this can be chat, action-bar
|
||||||
|
chat-message: "%prefix% - %username% sent a %giftName%!"
|
||||||
|
action-bar-message: ""
|
||||||
|
chat:
|
||||||
|
chat-enabled: true
|
||||||
|
loctions: chat #this can be chat, action-bar
|
||||||
|
chat-message: "%prefix% - [%username%] > %message%"
|
||||||
|
action-bar-message: ""
|
||||||
|
follows:
|
||||||
|
follow-enabled: true
|
||||||
|
loctions: chat #this can be chat, action-bar
|
||||||
|
chat-message: "%prefix% - [%username%] Followed"
|
||||||
|
action-bar-message: ""
|
16
src/main/resources/plugin.yml
Normal file
16
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: TikTokStreamPlugin
|
||||||
|
main: com.minster586.tiktokstream.TikTokStreamPlugin
|
||||||
|
version: 1.0.0
|
||||||
|
author: minster586
|
||||||
|
description: Integrates TikTok Live events with Minecraft 1.19
|
||||||
|
api-version: 1.19
|
||||||
|
|
||||||
|
commands:
|
||||||
|
tiktok:
|
||||||
|
description: TikTok live control command
|
||||||
|
usage: /tiktok live <enable|disable>
|
||||||
|
permission: tiktok.live
|
||||||
|
permissions:
|
||||||
|
tiktok.live:
|
||||||
|
description: Allows control of TikTok live notifications
|
||||||
|
default: op
|
Reference in New Issue
Block a user