new_command_distributor into master
| @ -0,0 +1,48 @@ | |||
| package de.yannicpunktdee.yoshibot.command.commands; | |||
| import de.yannicpunktdee.yoshibot.command.YoshiCommand; | |||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||
| import de.yannicpunktdee.yoshibot.utils.SauceProvider; | |||
| import net.dv8tion.jda.api.entities.MessageEmbed; | |||
| import java.util.Arrays; | |||
| import java.util.List; | |||
| public class SauceCommand extends YoshiCommand { | |||
| /** | |||
| * Erzeugt ein neues Kommando, führt es aber noch nicht aus. Es wird ermittelt, ob die Argumentenkombination valide | |||
| * ist und das isOk-Flag gesetzt. Im Fehlerfall wird eine Fehleremeldung spezifiziert. | |||
| * | |||
| * @param context Der Kontext mit dem das Kommando aufgerufen wurde. | |||
| */ | |||
| public SauceCommand(YoshiCommandContext context) { | |||
| super(context); | |||
| } | |||
| /** | |||
| * {@inheritDoc} | |||
| */ | |||
| @Override | |||
| public boolean execute() { | |||
| if (!super.execute()) return false; | |||
| if (!context.getEvent().getTextChannel().isNSFW()) { | |||
| sendErrorMessage("Dieser Kanal is nix gut, weil vong nsfw her. Geh woanders hin du kek"); | |||
| return true; | |||
| } | |||
| List<String> arguments = Arrays.asList(context.getArgument("tags").split(" ")); | |||
| try { | |||
| arguments.stream().map(Integer::parseInt).map(this::byIndex).forEach(this::sendCustomMessage); | |||
| } catch (Exception e) { | |||
| sendCustomMessage(byTags(arguments)); | |||
| } | |||
| return true; | |||
| } | |||
| private MessageEmbed byIndex(int index) { | |||
| return SauceProvider.getSauce(index); | |||
| } | |||
| private MessageEmbed byTags(List<String> tags){ | |||
| return SauceProvider.getRandomSauce(String.join(" ", tags)); | |||
| } | |||
| } | |||
| @ -1,7 +0,0 @@ | |||
| package de.yannicpunktdee.yoshibot.utils; | |||
| public interface Provider { | |||
| void onStop(); | |||
| } | |||
| @ -0,0 +1,156 @@ | |||
| package de.yannicpunktdee.yoshibot.utils; | |||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||
| import net.dv8tion.jda.api.EmbedBuilder; | |||
| import net.dv8tion.jda.api.entities.MessageEmbed; | |||
| import net.dv8tion.jda.api.entities.TextChannel; | |||
| import org.json.JSONArray; | |||
| import org.json.JSONObject; | |||
| import java.util.*; | |||
| import java.util.concurrent.Executors; | |||
| import java.util.concurrent.ScheduledExecutorService; | |||
| import java.util.concurrent.TimeUnit; | |||
| import java.util.stream.Collectors; | |||
| import java.util.stream.StreamSupport; | |||
| public class SauceProvider { | |||
| private static final String BASE_URL = "https://r34-json.herokuapp.com/"; | |||
| private int lastKnownSauce = -1; | |||
| private boolean isSauceInit = false; | |||
| private static MessageEmbed notFoundEmbed = null; | |||
| public SauceProvider(int timer) { | |||
| lastKnownSauce = this.getNewestIndex(); | |||
| ScheduledExecutorService sauceScheduler = Executors.newScheduledThreadPool(1); | |||
| sauceScheduler.scheduleAtFixedRate(this::provideSauce, 0, timer, TimeUnit.SECONDS); | |||
| new Thread(this::initSauceProviding).start(); | |||
| } | |||
| public SauceProvider(int timer, int lastKnownSauce) { | |||
| this(timer); | |||
| this.lastKnownSauce = lastKnownSauce; | |||
| } | |||
| public static MessageEmbed getSauce(int index) { | |||
| String url = BASE_URL + "posts?id=" + index; | |||
| JSONObject base = getParsedSauceData(url); | |||
| if (base.getInt("count") == 0) { | |||
| return getNotFoundEmbed(); | |||
| } | |||
| JSONObject post = base.getJSONArray("posts").getJSONObject(0); | |||
| return makeStringFromJson(post); | |||
| } | |||
| public static MessageEmbed getRandomSauce(String tags) { | |||
| tags = tagsForRest(tags); | |||
| tags += "+" + String.join("+", Resources.getFilteredTags()); | |||
| Random rand = YoshiBot.getInstance().getRandom(); | |||
| String url = BASE_URL + "posts?tags=" + String.join("+", tags); | |||
| Logger.logInfo("Soße angefordert für tags " + tags); | |||
| JSONObject baseObj = getParsedSauceData(url); | |||
| int amount = baseObj.getInt("count"); | |||
| if (amount == 0) { | |||
| return getNotFoundEmbed(); | |||
| } | |||
| int selectedIndex = rand.nextInt(amount); | |||
| int page = (selectedIndex / 100) % 100; | |||
| int pageIndex = selectedIndex % 100; | |||
| String pagedUrl = url + "&pid=" + page; | |||
| JSONObject post = Objects.requireNonNull(getParsedSauceData(pagedUrl)).getJSONArray("posts") | |||
| .getJSONObject(pageIndex); | |||
| return makeStringFromJson(post); | |||
| } | |||
| private static MessageEmbed makeStringFromJson(JSONObject post) { | |||
| EmbedBuilder eb = new EmbedBuilder(); | |||
| eb.setTitle("Soße").setDescription("URL: " + post.getString("file_url").substring(42)); | |||
| eb.addField("ID", post.getString("id"), false); | |||
| eb.addField("Tags", "`" + post.getJSONArray("tags").join("` `") + "`", false); | |||
| eb.setImage(post.getString("file_url").substring(42)); | |||
| return eb.build(); | |||
| } | |||
| private void provideSauce() { | |||
| if (!isSauceInit) return; | |||
| for (Map.Entry<String, List<String>> feed : Resources.getFeedDetails().entrySet()) { | |||
| String url = BASE_URL + "posts?tags=" + String.join("+", feed.getValue()) | |||
| + "+" + String.join("+", Resources.getFilteredTags()); | |||
| JSONArray posts = getParsedSauceData(url).getJSONArray("posts"); | |||
| assert posts != null; | |||
| List<JSONObject> postsInternal = | |||
| StreamSupport.stream(posts.spliterator(), false) | |||
| .map(obj -> (JSONObject) obj) | |||
| .filter(obj -> obj.getInt("id") > lastKnownSauce) | |||
| .collect(Collectors.toList()); | |||
| Collections.reverse(postsInternal); | |||
| YoshiBot yoshiBot = YoshiBot.getInstance(); | |||
| for (JSONObject post : postsInternal) { | |||
| List<TextChannel> channels = yoshiBot.jda.getTextChannelsByName(feed.getKey(), true); | |||
| if (channels.size() == 0) { | |||
| Logger.logError("Kein Kanal mit dem Namen " + feed.getKey() + "gefunden"); | |||
| break; | |||
| } else if (!channels.get(0).isNSFW()) { | |||
| Logger.logError("Kanal " + feed.getKey() + " ist nicht als NSFW markiert!"); | |||
| break; | |||
| } | |||
| channels.get(0).sendMessage(makeStringFromJson(post)).queue(); | |||
| } | |||
| if (postsInternal.size() > 0) { | |||
| Logger.logInfo(String.format("Found %d posts for feed '%s'", postsInternal.size(), feed.getKey())); | |||
| } | |||
| } | |||
| lastKnownSauce = this.getNewestIndex(); | |||
| } | |||
| private void initSauceProviding() { | |||
| YoshiBot yoshiBot = YoshiBot.getInstance(); | |||
| try { | |||
| yoshiBot.jda.awaitReady(); | |||
| } catch (InterruptedException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| for (Map.Entry<String, List<String>> entry : Resources.getFeedDetails().entrySet()) { | |||
| List<TextChannel> channels = yoshiBot.jda.getTextChannelsByName(entry.getKey(), true); | |||
| if (channels.size() > 0) { | |||
| this.isSauceInit = true; | |||
| this.provideSauce(); | |||
| } else { | |||
| Logger.logError("Konnte keine Kanaäle finden für die Soße"); | |||
| } | |||
| } | |||
| } | |||
| private int getNewestIndex() { | |||
| JSONObject result = getParsedSauceData("https://r34-json.herokuapp.com/posts?limit=1&q=index"); | |||
| int id = result.getJSONArray("posts").getJSONObject(0).getInt("id"); | |||
| Logger.logDebug("Neuste Soßen-ID: " + id); | |||
| return id; | |||
| } | |||
| private static JSONObject getParsedSauceData(String url) { | |||
| String raw = RestHelper.getFromURL(url); | |||
| return new JSONObject(raw); | |||
| } | |||
| private static String tagsForRest(String tags) { | |||
| tags = tags.replace(" ", "+"); | |||
| return tags; | |||
| } | |||
| private static MessageEmbed getNotFoundEmbed() { | |||
| if (SauceProvider.notFoundEmbed == null) { | |||
| EmbedBuilder eb = new EmbedBuilder(); | |||
| eb.setTitle("Could not find any posts matching the filter!"); | |||
| SauceProvider.notFoundEmbed = eb.build(); | |||
| } | |||
| return SauceProvider.notFoundEmbed; | |||
| } | |||
| } | |||
| @ -1,136 +0,0 @@ | |||
| package de.yannicpunktdee.yoshibot.utils; | |||
| import lombok.Getter; | |||
| import net.dv8tion.jda.api.EmbedBuilder; | |||
| import org.json.JSONObject; | |||
| import java.io.BufferedReader; | |||
| import java.io.IOException; | |||
| import java.io.InputStreamReader; | |||
| import java.time.LocalDateTime; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.concurrent.Executors; | |||
| import java.util.concurrent.ScheduledExecutorService; | |||
| import java.util.concurrent.TimeUnit; | |||
| import java.util.stream.Collectors; | |||
| import java.util.stream.StreamSupport; | |||
| public class StatusProvider implements Provider { | |||
| private static final ScheduledExecutorService statusScheduler = Executors.newScheduledThreadPool(1); | |||
| private int lastPlayersOnline = -1; | |||
| @Getter | |||
| private final String ip; | |||
| private final String desc; | |||
| private LocalDateTime timestampLastPlayerOnline; | |||
| private final ProcessBuilder mcstatus = new ProcessBuilder(); | |||
| public StatusProvider(String desc, int secondsPerTime, String ip, LocalDateTime timestamp) { | |||
| this.timestampLastPlayerOnline = timestamp; | |||
| this.desc = desc; | |||
| this.ip = ip; | |||
| this.mcstatus.command(Resources.getPath_to_mcstatus(), ip, "json"); | |||
| statusScheduler.scheduleAtFixedRate(this::updateStatusMessage, 0, secondsPerTime, | |||
| TimeUnit.SECONDS); | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| public void updateStatusMessage() { | |||
| EmbedBuilder eb = new EmbedBuilder(); | |||
| eb.setTitle(desc); | |||
| eb.addField("IP", ip, false); | |||
| try { | |||
| Map<String, Object> serverInfo = getPlayersOnline(); | |||
| if ((boolean) serverInfo.get("online")) { | |||
| if ((boolean) serverInfo.get("Server starting")) { | |||
| eb.addField("Server still Starting", "True", false); | |||
| } else { | |||
| int newPlayersOnline = (int) serverInfo.get("playerCount"); | |||
| eb.addField("Version", (String) serverInfo.get("version"), true); | |||
| eb.addField("MOTD", (String) serverInfo.get("motd"), true); | |||
| eb.addField("Spieler online", newPlayersOnline + " / " + serverInfo.get("playerMax"), false); | |||
| if (newPlayersOnline == 0) { | |||
| if (lastPlayersOnline > newPlayersOnline) { | |||
| timestampLastPlayerOnline = LocalDateTime.now(); | |||
| StatusProviderFactory.updateTimestamp(this, timestampLastPlayerOnline); | |||
| } | |||
| eb.addField("Zuletzt gesehen", | |||
| StatusProviderFactory.TIME_FORMATTER.format(timestampLastPlayerOnline), false); | |||
| } else { | |||
| eb.addField("Spieler:", String.join(", ", (List<String>) serverInfo.get("playerNames")), false); | |||
| } | |||
| lastPlayersOnline = newPlayersOnline; | |||
| } | |||
| } else { | |||
| eb.addField("Offline", "", false); | |||
| } | |||
| StatusProviderFactory.updateStatus(this, eb.build()); | |||
| } catch (IOException e) { | |||
| Logger.logError(e.toString()); | |||
| } | |||
| } | |||
| private Map<String, Object> getPlayersOnline() throws IOException { | |||
| Process process = mcstatus.start(); | |||
| try { | |||
| process.waitFor(); | |||
| } catch (InterruptedException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| if (process.exitValue() != 0) { | |||
| Logger.logError("MCStatus on IP " + ip + " exited with errorcode " + process.exitValue()); | |||
| } | |||
| String output = new BufferedReader(new InputStreamReader(process.getInputStream())).lines() | |||
| .collect(Collectors.joining()); | |||
| Map<String, Object> result = new HashMap<>(); | |||
| if (output.startsWith("The server did not respond to the query protocol.")) { | |||
| result.put("online", false); | |||
| return result; | |||
| } | |||
| JSONObject obj = new JSONObject(output); | |||
| result.put("online", obj.getBoolean("online")); | |||
| if (!obj.getBoolean("online")) { | |||
| return result; | |||
| } | |||
| for (String key : new String[]{"player_count", "player_max", "players", "motd", "version"}) { | |||
| if (!obj.has(key)) { | |||
| result.put("Server starting", true); | |||
| return result; | |||
| } | |||
| } | |||
| result.put("Server starting", false); | |||
| result.put("playerCount", obj.getInt("player_count")); | |||
| result.put("playerMax", obj.getInt("player_max")); | |||
| result.put("playerNames", StreamSupport.stream(obj.getJSONArray("players").spliterator(), false) | |||
| .map(jsonobj -> ((JSONObject) jsonobj).getString("name")).sorted().collect(Collectors.toList())); | |||
| result.put("motd", obj.getString("motd")); | |||
| result.put("version", obj.getString("version")); | |||
| return result; | |||
| } | |||
| @Override | |||
| public void onStop() { | |||
| StatusProviderFactory.updateTimestamp(this, timestampLastPlayerOnline); | |||
| } | |||
| } | |||
| @ -1,98 +0,0 @@ | |||
| package de.yannicpunktdee.yoshibot.utils; | |||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||
| import lombok.NonNull; | |||
| import lombok.SneakyThrows; | |||
| import net.dv8tion.jda.api.MessageBuilder; | |||
| import net.dv8tion.jda.api.entities.Message; | |||
| import net.dv8tion.jda.api.entities.MessageEmbed; | |||
| import net.dv8tion.jda.api.entities.TextChannel; | |||
| import net.dv8tion.jda.api.exceptions.ErrorResponseException; | |||
| import org.json.JSONArray; | |||
| import org.json.JSONObject; | |||
| import java.io.BufferedWriter; | |||
| import java.io.File; | |||
| import java.io.FileWriter; | |||
| import java.nio.file.Files; | |||
| import java.time.LocalDateTime; | |||
| import java.time.format.DateTimeFormatter; | |||
| import java.util.*; | |||
| public abstract class StatusProviderFactory { | |||
| public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss E, dd.MM.yyyy", | |||
| Locale.GERMANY); | |||
| private static final Map<StatusProvider, Long> providers = new HashMap<>(); | |||
| private static final Map<StatusProvider, JSONObject> providerJSON = new HashMap<>(); | |||
| private static JSONObject rootJSON; | |||
| private static File configFile; | |||
| @NonNull | |||
| private static final TextChannel statusChannel = | |||
| Objects.requireNonNull(YoshiBot.getInstance().getGuild().getTextChannelById(Resources.getStatus_channel())); | |||
| @SneakyThrows | |||
| public static void createStatusProviders(String configFilePath, Set<Provider> allProviders) { | |||
| configFile = new File(configFilePath); | |||
| rootJSON = new JSONObject(String.join("\n", Files.readAllLines(configFile.toPath()))); | |||
| final JSONArray servers = rootJSON.getJSONArray("servers"); | |||
| for (int i = 0; i < servers.length(); i++) { | |||
| final JSONObject server = servers.getJSONObject(i); | |||
| final String ip = server.getString("ip"); | |||
| final String name = server.getString("name"); | |||
| if (!server.has("timestamp")) { | |||
| server.put("timestamp", TIME_FORMATTER.format(LocalDateTime.now())); | |||
| } | |||
| final LocalDateTime timestamp = LocalDateTime.parse(server.getString("timestamp"), TIME_FORMATTER); | |||
| if (!server.has("message")) { | |||
| server.put("message", -1); | |||
| } | |||
| final long msg = ensureMessageId(server.getLong("message")); | |||
| server.put("message", msg); | |||
| StatusProvider provider = new StatusProvider(name, Resources.getStatusUpdate(), ip, timestamp); | |||
| providers.put(provider, msg); | |||
| providerJSON.put(provider, servers.getJSONObject(i)); | |||
| } | |||
| allProviders.addAll(providers.keySet()); | |||
| writeJSON(); | |||
| } | |||
| public static void updateStatus(StatusProvider provider, MessageEmbed msg) { | |||
| statusChannel.editMessageById(providers.get(provider), msg).queue(); | |||
| } | |||
| public static void updateTimestamp(StatusProvider provider, LocalDateTime timestamp) { | |||
| providerJSON.get(provider).put("timestamp", TIME_FORMATTER.format(timestamp)); | |||
| writeJSON(); | |||
| } | |||
| @SneakyThrows | |||
| private static void writeJSON() { | |||
| BufferedWriter bw = new BufferedWriter(new FileWriter(configFile)); | |||
| bw.write(rootJSON.toString(2)); | |||
| bw.flush(); | |||
| } | |||
| private static long createStatusMessage() { | |||
| Logger.logInfo("Creating new Status Message!"); | |||
| return StatusProviderFactory.statusChannel.sendMessage( | |||
| new MessageBuilder() | |||
| .append("ServerInformation") | |||
| .build()) | |||
| .complete().getIdLong(); | |||
| } | |||
| private static long ensureMessageId(long messageId) { | |||
| try { | |||
| Message msg = StatusProviderFactory.statusChannel.retrieveMessageById(messageId).complete(); | |||
| return msg.getIdLong(); | |||
| } catch (ErrorResponseException e) { | |||
| return createStatusMessage(); | |||
| } | |||
| } | |||
| } | |||
| @ -1,2 +0,0 @@ | |||
| 255*.txt | |||
| mcstatus.sh | |||
| @ -1,14 +1,4 @@ | |||
| #Sun Mar 13 19:43:48 CET 2022 | |||
| status_message_vanilla=890007862456238120 | |||
| status_channel=889880296168755231 | |||
| guild_id=801554100814741524 | |||
| # audio_source_directory=C:/Users/linky/workspace/Yoshi_Bot_Audio/ | |||
| restrict_commands_to_channel=bot-muell schrein-auf-den-bot | |||
| status_message_eighteen_vanilla=952638126327726150 | |||
| status_message_selina=952638127451820042 | |||
| mc_server=85.214.148.23 | |||
| greetings_and_byebyes_on=true | |||
| guild_id=801554100814741524 | |||
| status_message_modded=952638124197040139 | |||
| status_message_kreativ=952638123324616735 | |||
| status_update=30 | |||
| path_to_mcstatus=/home/paul/.local/bin/mcstatus | |||
| path_to_status_json=/home/paul/gits/YoshiBot/rsc/mcservers.json | |||
| @ -1,7 +1,6 @@ | |||
| Haare waschen | |||
| Kleine Menschen (Honey) verprügeln | |||
| Kleine Menschen (Vanessa) verprügeln | |||
| Im Kosovo Hexen verbennen | |||
| Magnesiumcarbonat schnupfen | |||
| Offene Wunden mit Sekundenkleber verschließen | |||
| Sich 'nen saftigen Knackarsch reinzimmern | |||
| Spielt Rasputin bei Just Dance | |||
| Sich 'nen saftigen Knackarsch reinzimmern | |||
| @ -1,3 +0,0 @@ | |||
| %s hat jetzt fertig. | |||
| %s ist wieder aufnahmebereit. | |||
| %s hat sich wieder erholt. | |||
| @ -1,4 +0,0 @@ | |||
| %s ist dann mal abwesend. | |||
| %s hat jetzt erstmal nix mehr zu sagen. | |||
| %s ist verschwunden. | |||
| %s kommt bald wieder.... vielleicht. | |||
| @ -1,20 +0,0 @@ | |||
| {"servers": [ | |||
| { | |||
| "ip": "85.214.148.23:25568", | |||
| "name": "MCMuffing™®㋏ Inc.", | |||
| "message": 960983446350594049, | |||
| "timestamp": "13:43:35 Fr., 15.04.2022" | |||
| }, | |||
| { | |||
| "ip": "85.214.148.23:25566", | |||
| "name": "Enigmatica 6: Expert 1.0.0", | |||
| "message": 960983448410013737, | |||
| "timestamp": "21:25:08 Di., 05.04.2022" | |||
| }, | |||
| { | |||
| "ip": "85.214.148.23:25570", | |||
| "name": "Medieval Minecraft 1.16.5 v52", | |||
| "message": 960983449961922600, | |||
| "timestamp": "11:30:09 So., 10.04.2022" | |||
| } | |||
| ]} | |||
| @ -0,0 +1,52 @@ | |||
| { | |||
| "tags_general_filter": [ | |||
| "-anthro", | |||
| "-nonconsensual", | |||
| "-rape", | |||
| "-vore", | |||
| "-scat", | |||
| "-yiff", | |||
| "-snuff", | |||
| "-crossdressing", | |||
| "-mind_break", | |||
| "-overweight", | |||
| "-hyper", | |||
| "-udders" | |||
| ], | |||
| "feeds": [ | |||
| { | |||
| "channel": "snek-feed", | |||
| "tags": [ | |||
| "lamia" | |||
| ] | |||
| }, | |||
| { | |||
| "channel": "auto-feed", | |||
| "tags": [ | |||
| [ | |||
| "fire_emblem", | |||
| "sword_art_online", | |||
| "monster_musume_no_iru_nichijou", | |||
| "samus_aran", | |||
| "metroid", | |||
| "palutena", | |||
| "xenoblade_chronicles", | |||
| "xenoblade_chronicles_2", | |||
| "nintendo", | |||
| "star_wars", | |||
| "nier:_automata", | |||
| "monster_girl", | |||
| "tate_no_yuusha_no_nariagari", | |||
| "zero_two_(darling_in_the_franxx)", | |||
| "re:zero_kara_hajimeru_isekai_seikatsu", | |||
| "fate_(series)", | |||
| "darling_in_the_franxx", | |||
| "dungeon_ni_deai_wo_motomeru_no_wa_machigatteiru_darou_ka", | |||
| "touhou", | |||
| "elf" | |||
| ], | |||
| "breasts" | |||
| ] | |||
| } | |||
| ] | |||
| } | |||