diff --git a/.gitignore b/.gitignore index 4788b4b..87d4436 100755 --- a/.gitignore +++ b/.gitignore @@ -110,4 +110,4 @@ buildNumber.properties .flattened-pom.xml # Common working directory -run/ +run/ \ No newline at end of file diff --git a/README.md b/README.md index ccc13b6..b4f8d40 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,58 @@ # FroggyMonitorReward Плагин для награды игроков за голоса и отзывы на FroggyMonitor -Плагин запускает сайт, -на который будут приходить запросы от -FroggyMonitor на поощрение игрока, -плагин их читает и выполняет действие из конфига - [Скачать](https://github.com/MeexReay/FroggyMonitorReward/releases/latest) ## Конфиг ```yml -site_host: localhost # IP адрес для сайта -site_port: 8080 # Порт для сайта -site_backlog: 0 # Максимальное кол-во подключений одновременно +bind_host: 0.0.0.0 # Локальный IP адрес сервера (обычно такой же как и в server.properties) +bind_port: 8080 # Свободный порт для сайта (потребуется открыть его на хостинге) + +external_host: example.com # Внешний IP адрес / домен сервера secret_token: "ваш_секретный_токен" # Секретный токен с FroggyMonitor -comment_page: "/api/comment" # Страница для награды за отзыв -vote_page: "/api/vote" # Страница для награды за голос - -# Что указать в FroggyMonitor? -# В URL для поощрения за отзыв: -# http://{ip_сервера}:{site_port}{comment_page} -# -> http://example.com:8080/api/comment -# В URL для поощрения за голос: -# http://{ip_сервера}:{site_port}{vote_page} -# -> http://example.com:8080/api/vote -# Также возможно понадобится открыть порт на хосте - vote: # Награда за голос - vault: 10 # Выдать валюту (необяз.) - item: "diamond 10" # Выдать предмет (необяз.) (забрать предмет нельзя) - message: "Спасибо за голос!" # Отправить сообщение (необяз.) - commands: # Исполнить команды (необяз.) + vault: 10 # Выдать валюту + item: "diamond 10" # Выдать предмет (забрать предмет нельзя) + message: "Спасибо за голос!" # Отправить сообщение + commands: # Исполнить команды - "/title {player_name} subtitle на FroggyMonitor" - "/title {player_name} title Спасибо за отзыв!" + # Каждый параметр наград не обязателен -add_comment: # Награда за добавление отзыва +add_comment: # Награда за удаление отзыва vault: 10 message: "Спасибо за отзыв!" del_comment: # Награда за удаление отзыва vault: -10 # Снять валюту + +enable_logs: true # Включить логи плагина (true/false); true - вкл; false - выкл + +message_formatting: "ampersand" # Изменить тип форматирования сообщений + # Типы форматирования: + # ampersand: &cСообщение + # section: §cСообщение + # minimessage: Сообщение + # json: {"text": "Сообщение", "color": "red"} ``` + +## Как это работает + +```mermaid +sequenceDiagram + participant FroggyMonitor + participant Сервер + participant Сайт + participant Плагин + participant Vault + Сервер-->>Плагин: Запуск плагина + Плагин-->>Сайт: Запуск сайта + FroggyMonitor->>Сайт: Игрок проголосовал за сервер + Сайт->>Плагин: Голос игрока + Плагин->>Vault: Выдать валюту + Плагин->>Сервер: Выдать предмет + Плагин->>Сервер: Отправить сообщение + Плагин->>Сервер: Выполнить команды +``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index 77f0beb..31af942 100755 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ru.froggymonitor rewardplugin - 1.2 + 1.3 jar FroggyMonitorReward @@ -100,5 +100,25 @@ 2.11.5 provided + + net.kyori + adventure-text-serializer-legacy + 4.16.0 + + + net.kyori + adventure-text-serializer-gson + 4.16.0 + + + net.kyori + adventure-text-minimessage + 4.16.0 + + + net.kyori + adventure-api + 4.16.0 + diff --git a/src/main/java/ru.froggymonitor/rewardplugin/Main.java b/src/main/java/ru.froggymonitor/rewardplugin/Main.java index 8fb43f9..dd67752 100755 --- a/src/main/java/ru.froggymonitor/rewardplugin/Main.java +++ b/src/main/java/ru.froggymonitor/rewardplugin/Main.java @@ -2,26 +2,22 @@ package ru.froggymonitor.rewardplugin; import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.MemorySection; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; import java.io.IOException; -import java.nio.file.Path; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Files; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public final class Main extends JavaPlugin implements Listener { public static Main me; @@ -45,13 +41,22 @@ public final class Main extends JavaPlugin implements Listener { public String secret_token; - public String comment_page; - public String vote_page; + public String external_host; + + public boolean enable_logs; + public MessageFormatting message_formatting; public Reward vote_reward; public Reward add_comment_reward; public Reward del_comment_reward; + public String vote_page; + public String comment_page; + + public Map cache; + + public File cache_file; + @Override public void onEnable() { if (!setupEconomy()) { @@ -70,19 +75,31 @@ public final class Main extends JavaPlugin implements Listener { conf = new UnrealConfig(this, "config.yml"); + vote_page = "/api/vote"; + comment_page = "/api/comment"; + secret_token = (String) conf.get("secret_token"); + external_host = (String) conf.get("external_host"); - comment_page = (String) conf.get("comment_page"); - vote_page = (String) conf.get("vote_page"); + enable_logs = (Boolean) conf.get("enable_logs"); + message_formatting = MessageFormatting.getFormatting((String) conf.get("message_formatting")); - vote_reward = new Reward((Map) conf.get("vote")); - add_comment_reward = new Reward((Map) conf.get("add_comment")); - del_comment_reward = new Reward((Map) conf.get("del_comment")); + vote_reward = new Reward("vote", (Map) conf.get("vote")); + add_comment_reward = new Reward("add_comment", (Map) conf.get("add_comment")); + del_comment_reward = new Reward("del_comment", (Map) conf.get("del_comment")); + + httpClient = HttpClient.newHttpClient(); + + sendRewardUrls(); + + cache_file = new File(getDataFolder().getPath(), "cache"); + + loadCache(); site = new SitePart( - (String) conf.get("site_host"), - ((Number) conf.get("site_port")).intValue(), - ((Number) conf.get("site_backlog")).intValue()); + (String) conf.get("bind_host"), + ((Number) conf.get("bind_port")).intValue(), + 0); site.start(); @@ -92,6 +109,59 @@ public final class Main extends JavaPlugin implements Listener { @Override public void onDisable() { site.stop(); + saveCache(); + } + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + Player p = e.getPlayer(); + String n = p.getName(); + + if (cache.containsKey(n)) { + switch (cache.get(n)) { + case ("vote") -> vote_reward.later(p); + case ("add_comment") -> add_comment_reward.later(p); + case ("del_comment") -> del_comment_reward.later(p); + } + + Main.me.cache.remove(n); + } + } + + public void saveCache() { + if (cache.isEmpty()) { + cache_file.delete(); + } else { + try { + if (!cache_file.exists()) + cache_file.createNewFile(); + + StringBuilder text = new StringBuilder(); + for (Map.Entry e : cache.entrySet()) + text.append(e.getKey()).append("=").append(e.getValue()).append("\n"); + + Files.writeString(cache_file.toPath(), text.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public void loadCache() { + cache = new HashMap<>(); + + if (cache_file.exists()) { + try { + String text = Files.readString(cache_file.toPath()); + + for (String s : text.split("\n")) { + String[] ss = s.split("="); + cache.put(ss[0], ss[1]); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } } public static OfflinePlayer getOfflinePlayer(String name) { @@ -102,4 +172,25 @@ public final class Main extends JavaPlugin implements Listener { } return null; } + + public HttpClient httpClient; + + public void sendRewardUrls() { + String start_url = "http://"+external_host+":"+site.port; + + String body = "{\"secret_token\": \""+secret_token+"\", "+ + "\"vote_url\": \""+start_url+vote_page+"\", "+ + "\"comment_url\": \""+start_url+comment_page+"\"}"; + + try { + httpClient.send(HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(body)) + .uri(URI.create("https://froggymonitor.ru/api/set_reward_urls")) + .build(), HttpResponse.BodyHandlers.ofString()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/ru.froggymonitor/rewardplugin/MessageFormatting.java b/src/main/java/ru.froggymonitor/rewardplugin/MessageFormatting.java new file mode 100644 index 0000000..1bfd5a5 --- /dev/null +++ b/src/main/java/ru.froggymonitor/rewardplugin/MessageFormatting.java @@ -0,0 +1,43 @@ +package ru.froggymonitor.rewardplugin; + +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.json.JSONComponentSerializer; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.ComponentSerializer; + +public enum MessageFormatting { + AMPERSAND("ampersand", (str) -> LegacyComponentSerializer.legacyAmpersand().deserialize(str)), + SECTION("section", (str) -> LegacyComponentSerializer.legacySection().deserialize(str)), + MINIMESSAGE("minimessage", (str) -> (TextComponent) MiniMessage.miniMessage().deserialize(str)), + JSON("json", (str) -> (TextComponent) JSONComponentSerializer.json().deserialize(str)); + + public interface MessageFormat { + TextComponent format(String input); + } + + private MessageFormat format_func; + private String name; + MessageFormatting(String name, MessageFormat format) { + this.name = name; + this.format_func = format; + } + + public String getName() { + return name; + } + + public BaseComponent[] format(String str) { + return ComponentSerializer.parse(JSONComponentSerializer.json().serialize(format_func.format(str))); + } + + public static MessageFormatting getFormatting(String name) { + for (MessageFormatting f : values()) { + if (f.getName().startsWith(name.toLowerCase())) { + return f; + } + } + return SECTION; + } +} diff --git a/src/main/java/ru.froggymonitor/rewardplugin/Reward.java b/src/main/java/ru.froggymonitor/rewardplugin/Reward.java index a9af00d..ef44d6b 100644 --- a/src/main/java/ru.froggymonitor/rewardplugin/Reward.java +++ b/src/main/java/ru.froggymonitor/rewardplugin/Reward.java @@ -14,10 +14,12 @@ import java.util.Map; import static org.bukkit.Bukkit.getServer; public class Reward { + public String name; public Map data; - public Reward(Map data) { + public Reward(String name, Map data) { this.data = data; + this.name = name; } public void execute(String nickname) { @@ -30,8 +32,9 @@ public class Reward { player.getInventory().addItem(item); } if (data.containsKey("message")) { - player.sendMessage(PlaceholderAPI.setPlaceholders(player, (String) data.get("message"))); + player.spigot().sendMessage(Main.me.message_formatting.format(PlaceholderAPI.setPlaceholders(player, (String) data.get("message")))); } + Main.me.cache.put(nickname, name); } OfflinePlayer offlinePlayer = player != null ? player : Main.getOfflinePlayer(nickname); @@ -49,4 +52,15 @@ public class Reward { } } } + + public void later(Player player) { + if (data.containsKey("item")) { + String[] ss = ((String)data.get("item")).split(" "); + ItemStack item = new ItemStack(Material.valueOf(ss[0].toUpperCase()), ss.length == 1 ? 1 : Integer.parseInt(ss[1])); + player.getInventory().addItem(item); + } + if (data.containsKey("message")) { + player.spigot().sendMessage(Main.me.message_formatting.format(PlaceholderAPI.setPlaceholders(player, (String) data.get("message")))); + } + } } diff --git a/src/main/java/ru.froggymonitor/rewardplugin/SitePart.java b/src/main/java/ru.froggymonitor/rewardplugin/SitePart.java index 4f32615..4ddbd6d 100755 --- a/src/main/java/ru.froggymonitor/rewardplugin/SitePart.java +++ b/src/main/java/ru.froggymonitor/rewardplugin/SitePart.java @@ -3,21 +3,23 @@ package ru.froggymonitor.rewardplugin; import com.google.common.base.Charsets; import com.google.common.hash.Hashing; import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; import java.util.Map; public class SitePart extends FormDataHandler { public HttpServer server; + public String host; + public int port; + public SitePart(String host, int port, int backlog) { + this.host = host; + this.port = port; + try { server = HttpServer.create(new InetSocketAddress(host,port),backlog); server.createContext("/",this); @@ -29,6 +31,8 @@ public class SitePart extends FormDataHandler { public void start() { server.start(); + + if (Main.me.enable_logs) Main.me.getLogger().info("Site started at "+host+":"+port); } public void stop() { @@ -47,8 +51,6 @@ public class SitePart extends FormDataHandler { String method = e.getRequestMethod(); String path = e.getRequestURI().getPath(); -// System.out.println(response+" "+status_code+" "+path+" "+method); - if (method.equals("GET")) { if (path.equals(Main.me.vote_page)) { if (params.containsKey("nickname") && @@ -62,6 +64,8 @@ public class SitePart extends FormDataHandler { Main.me.vote_reward.execute(nickname); response = "ok"; status_code = 200; + + if (Main.me.enable_logs) Main.me.getLogger().info("Reward \"vote\" gave to player "+nickname); } } } else if (path.equals(Main.me.comment_page)) { @@ -82,11 +86,15 @@ public class SitePart extends FormDataHandler { response = "ok"; status_code = 200; + + if (Main.me.enable_logs) Main.me.getLogger().info("Reward \"add_comment\" gave to player "+nickname); } else if (type.equals("delete")) { Main.me.del_comment_reward.execute(nickname); response = "ok"; status_code = 200; + + if (Main.me.enable_logs) Main.me.getLogger().info("Reward \"del_comment\" gave to player "+nickname); } } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d076c4f..8b2b054 100755 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,28 +1,31 @@ -site_host: localhost # IP адрес для сайта -site_port: 8080 # Порт для сайта -site_backlog: 0 # Максимальное кол-во подключений одновременно +bind_host: 0.0.0.0 # Локальный IP адрес сервера (обычно такой же как и в server.properties) +bind_port: 8080 # Свободный порт для сайта (потребуется открыть его на хостинге) + +external_host: example.com # Внешний IP адрес / домен сервера secret_token: "ваш_секретный_токен" # Секретный токен с FroggyMonitor -comment_page: "/api/comment" # Страница для награды за отзыв -vote_page: "/api/vote" # Страница для награды за голос - -# Что указать в FroggyMonitor? -# В URL для поощрения за отзыв: http://{ip_сервера}:{site_port}{comment_page} -> http://example.com:8080/api/comment -# В URL для поощрения за голос: http://{ip_сервера}:{site_port}{vote_page} -> http://example.com:8080/api/vote -# Также возможно понадобится открыть порт на хосте - vote: # Награда за голос - vault: 10 # Выдать валюту (необяз.) - item: "diamond 10" # Выдать предмет (необяз.) (забрать предмет нельзя) - message: "Спасибо за голос!" # Отправить сообщение (необяз.) - commands: # Исполнить команды (необяз.) + vault: 10 # Выдать валюту + item: "diamond 10" # Выдать предмет (забрать предмет нельзя) + message: "Спасибо за голос!" # Отправить сообщение + commands: # Исполнить команды - "/title {player_name} subtitle на FroggyMonitor" - "/title {player_name} title Спасибо за отзыв!" + # Каждый параметр наград не обязателен add_comment: # Награда за удаление отзыва vault: 10 message: "Спасибо за отзыв!" del_comment: # Награда за удаление отзыва - vault: -10 # Снять валюту \ No newline at end of file + vault: -10 # Снять валюту + +enable_logs: true # Включить логи плагина (true/false); true - вкл; false - выкл + +message_formatting: "ampersand" # Изменить тип форматирования сообщений + # Типы форматирования: + # ampersand: &cСообщение + # section: §cСообщение + # minimessage: Сообщение + # json: {"text": "Сообщение", "color": "red"}