feat: fun/space commands + meme subreddit arg

- !catfact/!dogfact (feature/animals/command/AnimalFactCommand)
- !joke (jokeapi.dev), !chuck (chucknorris.io), !inspire (zenquotes.io) — feature/fun
- !apod (nasa DEMO_KEY), !iss (wheretheiss.at), !space (open-notify.org), !xkcd — feature/space
- !meme [subreddit] — optional meme-api.com/gimme/{subreddit}, 404 -> not found
This commit is contained in:
loki5512344 2026-08-19 14:36:08 +02:00
parent b3470a0f3d
commit bf14e03b21
Signed by: boba
GPG key ID: 253067914055423B
15 changed files with 871 additions and 5 deletions

View file

@ -19,6 +19,7 @@ import creator.loki.feature.config.ConfigModule;
import creator.loki.feature.counter.CounterModule; import creator.loki.feature.counter.CounterModule;
import creator.loki.feature.economy.EconomyModule; import creator.loki.feature.economy.EconomyModule;
import creator.loki.feature.entertainment.EntertainmentModule; import creator.loki.feature.entertainment.EntertainmentModule;
import creator.loki.feature.fun.FunModule;
import creator.loki.feature.image.ImageModule; import creator.loki.feature.image.ImageModule;
import creator.loki.feature.level.LevelModule; import creator.loki.feature.level.LevelModule;
import creator.loki.feature.metrics.HealthHttpServer; import creator.loki.feature.metrics.HealthHttpServer;
@ -30,6 +31,7 @@ import creator.loki.feature.profile.ProfileModule;
import creator.loki.feature.radio.RadioModule; import creator.loki.feature.radio.RadioModule;
import creator.loki.feature.reactroles.ReactionRolesModule; import creator.loki.feature.reactroles.ReactionRolesModule;
import creator.loki.feature.role.RoleModule; import creator.loki.feature.role.RoleModule;
import creator.loki.feature.space.SpaceModule;
import creator.loki.feature.ticket.TicketModule; import creator.loki.feature.ticket.TicketModule;
import creator.loki.feature.tiktok.TikTokModule; import creator.loki.feature.tiktok.TikTokModule;
import creator.loki.feature.utility.UtilityModule; import creator.loki.feature.utility.UtilityModule;
@ -67,6 +69,8 @@ public class BotModule extends AbstractModule {
install(new TikTokModule()); install(new TikTokModule());
install(new EntertainmentModule()); install(new EntertainmentModule());
install(new AnimalModule()); install(new AnimalModule());
install(new FunModule());
install(new SpaceModule());
install(new TicketModule()); install(new TicketModule());
install(new UtilityModule()); install(new UtilityModule());
install(new ReactionRolesModule()); install(new ReactionRolesModule());

View file

@ -3,6 +3,7 @@ package creator.loki.feature.animals;
import com.google.inject.Inject; import com.google.inject.Inject;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.core.registry.CommandRegistry; import creator.loki.core.registry.CommandRegistry;
import creator.loki.feature.animals.command.AnimalFactCommand;
import creator.loki.feature.animals.command.AnimalImageCommand; import creator.loki.feature.animals.command.AnimalImageCommand;
import creator.loki.feature.animals.command.ZooPersonCommand; import creator.loki.feature.animals.command.ZooPersonCommand;
import creator.loki.feature.entertainment.service.RatingService; import creator.loki.feature.entertainment.service.RatingService;
@ -25,5 +26,8 @@ public class AnimalCommandRegistrar implements CommandRegistrar {
for (AnimalImageCommand.Flavor flavor : AnimalImageCommand.Flavor.values()) { for (AnimalImageCommand.Flavor flavor : AnimalImageCommand.Flavor.values()) {
registry.register(new AnimalImageCommand(flavor, animalApi)); registry.register(new AnimalImageCommand(flavor, animalApi));
} }
for (AnimalFactCommand.Flavor flavor : AnimalFactCommand.Flavor.values()) {
registry.register(new AnimalFactCommand(flavor));
}
} }
} }

View file

@ -0,0 +1,131 @@
package creator.loki.feature.animals.command;
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class AnimalFactCommand implements Command {
public enum Flavor {
CAT("catfact", "Случайный факт о коте", "🐱", "https://catfact.ninja/fact"),
DOG("dogfact", "Случайный факт о собаке", "🐶", "https://dog-api.kinduff.com/api/facts");
private final String name;
private final String description;
private final String emoji;
private final String apiUrl;
Flavor(final String name, final String description, final String emoji, final String apiUrl) {
this.name = name;
this.description = description;
this.emoji = emoji;
this.apiUrl = apiUrl;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getEmoji() {
return emoji;
}
public String getApiUrl() {
return apiUrl;
}
}
private static final Color EMBED_COLOR = new Color(88, 101, 242);
private final Flavor flavor;
private final OkHttpClient client;
public AnimalFactCommand(final Flavor flavor) {
this.flavor = flavor;
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return flavor.getName();
}
@Override
public String getDescription() {
return flavor.getDescription();
}
@Override
public String getUsage() {
return flavor.getName();
}
@Override
public String getCategory() {
return CommandCategories.ANIMALS;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🔍 Ищу факт...")
.queue(
loadingMsg -> {
try {
String fact = fetchFact();
loadingMsg
.editMessageEmbeds(
EmbedFactory.create()
.withTitle(flavor.getEmoji() + " Факт")
.withDescription(fact)
.withColor(EMBED_COLOR)
.build())
.queue();
} catch (IOException | IllegalStateException e) {
loadingMsg
.editMessageEmbeds(
EmbedTemplates.error("Ошибка", "Не удалось получить факт, попробуй позже."))
.queue();
}
});
}
private String fetchFact() throws IOException {
Request request = new Request.Builder().url(flavor.getApiUrl()).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("API вернул " + response.code());
}
JsonObject json = JsonParser.parseString(response.body().string()).getAsJsonObject();
if (flavor == Flavor.CAT) {
return json.get("fact").getAsString();
}
JsonArray facts = json.getAsJsonArray("facts");
if (facts == null || facts.isEmpty()) {
throw new IOException("API вернул пустой список фактов");
}
return facts.get(0).getAsString();
}
}
}

View file

@ -39,7 +39,7 @@ public class MemeCommand implements Command {
@Override @Override
public String getUsage() { public String getUsage() {
return "meme"; return "meme [субреддит]";
} }
@Override @Override
@ -55,7 +55,8 @@ public class MemeCommand implements Command {
.queue( .queue(
loadingMsg -> { loadingMsg -> {
try { try {
JsonObject json = fetchMeme(); String sub = context.getArgs().isEmpty() ? null : context.getArgs().get(0);
JsonObject json = fetchMeme(sub);
String memeUrl = json.get("url").getAsString(); String memeUrl = json.get("url").getAsString();
String title = json.get("title").getAsString(); String title = json.get("title").getAsString();
String subreddit = json.get("subreddit").getAsString(); String subreddit = json.get("subreddit").getAsString();
@ -76,11 +77,13 @@ public class MemeCommand implements Command {
}); });
} }
private JsonObject fetchMeme() throws IOException { private JsonObject fetchMeme(final String subreddit) throws IOException {
Request request = new Request.Builder().url(MEME_API).get().build(); String url = subreddit == null ? MEME_API : MEME_API + "/" + subreddit;
Request request = new Request.Builder().url(url).get().build();
try (Response response = client.newCall(request).execute()) { try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) { if (!response.isSuccessful() || response.body() == null) {
throw new IOException("meme-api вернул " + response.code()); throw new IOException(
subreddit == null ? "meme-api вернул " + response.code() : "сабреддит не найден");
} }
return JsonParser.parseString(response.body().string()).getAsJsonObject(); return JsonParser.parseString(response.body().string()).getAsJsonObject();
} }

View file

@ -0,0 +1,16 @@
package creator.loki.feature.fun;
import creator.loki.core.registry.CommandRegistrar;
import creator.loki.core.registry.CommandRegistry;
import creator.loki.feature.fun.command.ChuckCommand;
import creator.loki.feature.fun.command.InspireCommand;
import creator.loki.feature.fun.command.JokeCommand;
public class FunCommandRegistrar implements CommandRegistrar {
@Override
public void register(final CommandRegistry registry) {
registry.register(new JokeCommand());
registry.register(new ChuckCommand());
registry.register(new InspireCommand());
}
}

View file

@ -0,0 +1,14 @@
package creator.loki.feature.fun;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import creator.loki.core.registry.CommandRegistrar;
public class FunModule extends AbstractModule {
@Override
protected void configure() {
Multibinder.newSetBinder(binder(), CommandRegistrar.class)
.addBinding()
.to(FunCommandRegistrar.class);
}
}

View file

@ -0,0 +1,84 @@
package creator.loki.feature.fun.command;
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class ChuckCommand implements Command {
private static final String CHUCK_API = "https://api.chucknorris.io/jokes/random";
private static final Color EMBED_COLOR = new Color(189, 8, 28);
private final OkHttpClient client;
public ChuckCommand() {
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return "chuck";
}
@Override
public String getDescription() {
return "Случайная шутка про Чака Норриса";
}
@Override
public String getCategory() {
return CommandCategories.ENTERTAINMENT;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🔍 Ищу шутку про Чака Норриса...")
.queue(
loadingMsg -> {
try {
JsonObject json = fetchJoke();
loadingMsg
.editMessageEmbeds(
EmbedFactory.create()
.withTitle("🥋 Чак Норрис")
.withDescription(json.get("value").getAsString())
.withColor(EMBED_COLOR)
.withThumbnail("https://api.chucknorris.io/img/avatar/chuck-norris.png")
.build())
.queue();
} catch (IOException | IllegalStateException e) {
loadingMsg
.editMessageEmbeds(
EmbedTemplates.error(
"Ошибка", "Не удалось загрузить шутку, попробуй позже."))
.queue();
}
});
}
private JsonObject fetchJoke() throws IOException {
Request request = new Request.Builder().url(CHUCK_API).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("Chuck Norris API вернул " + response.code());
}
return JsonParser.parseString(response.body().string()).getAsJsonObject();
}
}
}

View file

@ -0,0 +1,89 @@
package creator.loki.feature.fun.command;
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class InspireCommand implements Command {
private static final String INSPIRE_API = "https://zenquotes.io/api/random";
private static final Color EMBED_COLOR = new Color(39, 174, 96);
private final OkHttpClient client;
public InspireCommand() {
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return "inspire";
}
@Override
public String getDescription() {
return "Случайная вдохновляющая цитата";
}
@Override
public String getCategory() {
return CommandCategories.ENTERTAINMENT;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🔍 Ищу цитату...")
.queue(
loadingMsg -> {
try {
JsonObject quote = fetchQuote();
loadingMsg
.editMessageEmbeds(
EmbedFactory.create()
.withTitle("✨ Цитата")
.withDescription("«" + quote.get("q").getAsString() + "»")
.withColor(EMBED_COLOR)
.withFooter("— " + quote.get("a").getAsString())
.build())
.queue();
} catch (IOException | IllegalStateException e) {
loadingMsg
.editMessageEmbeds(
EmbedTemplates.error(
"Ошибка", "Не удалось загрузить цитату, попробуй позже."))
.queue();
}
});
}
private JsonObject fetchQuote() throws IOException {
Request request = new Request.Builder().url(INSPIRE_API).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("ZenQuotes вернул " + response.code());
}
JsonArray array = JsonParser.parseString(response.body().string()).getAsJsonArray();
if (array.isEmpty()) {
throw new IOException("ZenQuotes вернул пустой список");
}
return array.get(0).getAsJsonObject();
}
}
}

View file

@ -0,0 +1,90 @@
package creator.loki.feature.fun.command;
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class JokeCommand implements Command {
private static final String JOKE_API = "https://v2.jokeapi.dev/joke/Any";
private static final Color EMBED_COLOR = new Color(255, 193, 7);
private final OkHttpClient client;
public JokeCommand() {
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return "joke";
}
@Override
public String getDescription() {
return "Случайная шутка";
}
@Override
public String getCategory() {
return CommandCategories.ENTERTAINMENT;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🔍 Ищу шутку...")
.queue(
loadingMsg -> {
try {
JsonObject json = fetchJoke();
String text =
json.get("type").getAsString().equals("twopart")
? json.get("setup").getAsString()
+ "\n\n"
+ json.get("delivery").getAsString()
: json.get("joke").getAsString();
loadingMsg
.editMessageEmbeds(
EmbedFactory.create()
.withTitle("🤣 Шутка")
.withDescription(text)
.withColor(EMBED_COLOR)
.withFooter("Категория: " + json.get("category").getAsString())
.build())
.queue();
} catch (IOException | IllegalStateException e) {
loadingMsg
.editMessageEmbeds(
EmbedTemplates.error(
"Ошибка", "Не удалось загрузить шутку, попробуй позже."))
.queue();
}
});
}
private JsonObject fetchJoke() throws IOException {
Request request = new Request.Builder().url(JOKE_API).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("JokeAPI вернул " + response.code());
}
return JsonParser.parseString(response.body().string()).getAsJsonObject();
}
}
}

View file

@ -0,0 +1,18 @@
package creator.loki.feature.space;
import creator.loki.core.registry.CommandRegistrar;
import creator.loki.core.registry.CommandRegistry;
import creator.loki.feature.space.command.ApodCommand;
import creator.loki.feature.space.command.IssCommand;
import creator.loki.feature.space.command.SpaceCommand;
import creator.loki.feature.space.command.XkcdCommand;
public class SpaceCommandRegistrar implements CommandRegistrar {
@Override
public void register(final CommandRegistry registry) {
registry.register(new ApodCommand());
registry.register(new IssCommand());
registry.register(new SpaceCommand());
registry.register(new XkcdCommand());
}
}

View file

@ -0,0 +1,14 @@
package creator.loki.feature.space;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import creator.loki.core.registry.CommandRegistrar;
public class SpaceModule extends AbstractModule {
@Override
protected void configure() {
Multibinder.newSetBinder(binder(), CommandRegistrar.class)
.addBinding()
.to(SpaceCommandRegistrar.class);
}
}

View file

@ -0,0 +1,98 @@
package creator.loki.feature.space.command;
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class ApodCommand implements Command {
private static final String APOD_API = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY";
private static final int DESCRIPTION_LIMIT = 200;
private static final Color EMBED_COLOR = new Color(25, 25, 112);
private final OkHttpClient client;
public ApodCommand() {
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return "apod";
}
@Override
public String getDescription() {
return "Астрономическая картинка дня (NASA APOD)";
}
@Override
public String getCategory() {
return CommandCategories.INFORMATION;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🔍 Загружаю астрономическую картинку дня...")
.queue(
loadingMsg -> {
try {
JsonObject json = fetchApod();
String title = json.get("title").getAsString();
String image =
json.has("hdurl")
? json.get("hdurl").getAsString()
: json.get("url").getAsString();
String explanation = truncate(json.get("explanation").getAsString());
String date = json.get("date").getAsString();
loadingMsg
.editMessageEmbeds(
EmbedFactory.create()
.withTitle("🌌 " + title)
.withImage(image)
.withDescription(explanation)
.withColor(EMBED_COLOR)
.withFooter("NASA APOD · " + date)
.build())
.queue();
} catch (IOException | IllegalStateException e) {
loadingMsg
.editMessage("❌ Не удалось загрузить картинку дня: " + e.getMessage())
.queue();
}
});
}
private JsonObject fetchApod() throws IOException {
Request request = new Request.Builder().url(APOD_API).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("NASA APOD вернул " + response.code());
}
return JsonParser.parseString(response.body().string()).getAsJsonObject();
}
}
private static String truncate(final String text) {
if (text.length() <= DESCRIPTION_LIMIT) {
return text;
}
return text.substring(0, DESCRIPTION_LIMIT).trim() + "...";
}
}

View file

@ -0,0 +1,107 @@
package creator.loki.feature.space.command;
import java.awt.Color;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class IssCommand implements Command {
private static final String ISS_API = "https://api.wheretheiss.at/v1/satellites/25544";
private static final Color EMBED_COLOR = new Color(52, 152, 219);
private static final DateTimeFormatter TIME_FORMAT =
DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss").withLocale(Locale.ROOT);
private final OkHttpClient client;
public IssCommand() {
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return "iss";
}
@Override
public String getDescription() {
return "Текущее положение МКС на орбите";
}
@Override
public String getCategory() {
return CommandCategories.INFORMATION;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🛰️ Получаю данные о МКС...")
.queue(
loadingMsg -> {
try {
JsonObject json = fetchIss();
String latitude = formatCoordinate(json.get("latitude").getAsDouble(), "N", "S");
String longitude = formatCoordinate(json.get("longitude").getAsDouble(), "E", "W");
String altitude =
String.format(Locale.ROOT, "%.0f км", json.get("altitude").getAsDouble());
String velocity =
String.format(Locale.ROOT, "%.0f км/ч", json.get("velocity").getAsDouble());
String time =
TIME_FORMAT.format(
Instant.ofEpochSecond(json.get("timestamp").getAsLong())
.atZone(ZoneOffset.UTC));
loadingMsg
.editMessageEmbeds(
EmbedFactory.create()
.withTitle("🛰️ Международная космическая станция")
.addField("Широта", latitude, true)
.addField("Долгота", longitude, true)
.addField("Высота", altitude, true)
.addField("Скорость", velocity, true)
.withColor(EMBED_COLOR)
.withFooter("Время (UTC): " + time)
.build())
.queue();
} catch (IOException | IllegalStateException e) {
loadingMsg
.editMessage("❌ Не удалось получить данные о МКС: " + e.getMessage())
.queue();
}
});
}
private JsonObject fetchIss() throws IOException {
Request request = new Request.Builder().url(ISS_API).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("Where The ISS At вернул " + response.code());
}
return JsonParser.parseString(response.body().string()).getAsJsonObject();
}
}
private static String formatCoordinate(
final double value, final String positive, final String negative) {
char sign = value >= 0 ? positive.charAt(0) : negative.charAt(0);
return String.format(Locale.ROOT, "%.4f° %s", Math.abs(value), sign);
}
}

View file

@ -0,0 +1,103 @@
package creator.loki.feature.space.command;
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class SpaceCommand implements Command {
private static final String ASTROS_API = "http://api.open-notify.org/astros.json";
private static final int MAX_NAMES = 40;
private static final int FIELD_SIZE = 10;
private static final Color EMBED_COLOR = new Color(70, 70, 130);
private final OkHttpClient client;
public SpaceCommand() {
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return "space";
}
@Override
public String getDescription() {
return "Кто сейчас находится в космосе";
}
@Override
public String getCategory() {
return CommandCategories.INFORMATION;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🚀 Узнаю, кто сейчас в космосе...")
.queue(
loadingMsg -> {
try {
JsonObject json = fetchAstros();
int number = json.get("number").getAsInt();
JsonArray people = json.getAsJsonArray("people");
EmbedFactory embed =
EmbedFactory.create()
.withTitle("🚀 Люди в космосе")
.withDescription("Сейчас на орбите: " + number)
.withColor(EMBED_COLOR);
int shown = Math.min(people.size(), MAX_NAMES);
for (int start = 0; start < shown; start += FIELD_SIZE) {
int end = Math.min(start + FIELD_SIZE, shown);
StringBuilder fieldValue = new StringBuilder();
for (int i = start; i < end; i++) {
JsonObject person = people.get(i).getAsJsonObject();
fieldValue
.append(person.get("name").getAsString())
.append(" (")
.append(person.get("craft").getAsString())
.append(")\n");
}
embed.addField(
"Экипаж " + (start / FIELD_SIZE + 1), fieldValue.toString(), false);
}
if (people.size() > shown) {
embed.addField(
"…", "И ещё " + (people.size() - shown) + " человек на орбите", false);
}
loadingMsg.editMessageEmbeds(embed.build()).queue();
} catch (IOException | IllegalStateException e) {
loadingMsg.editMessage("❌ Не удалось получить данные: " + e.getMessage()).queue();
}
});
}
private JsonObject fetchAstros() throws IOException {
Request request = new Request.Builder().url(ASTROS_API).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("Open Notify вернул " + response.code());
}
return JsonParser.parseString(response.body().string()).getAsJsonObject();
}
}
}

View file

@ -0,0 +1,91 @@
package creator.loki.feature.space.command;
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class XkcdCommand implements Command {
private static final String XKCD_API = "https://xkcd.com/info.0.json";
private static final Color EMBED_COLOR = new Color(150, 150, 150);
private final OkHttpClient client;
public XkcdCommand() {
this.client =
new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
}
@Override
public String getName() {
return "xkcd";
}
@Override
public String getDescription() {
return "Последний комикс xkcd";
}
@Override
public String getCategory() {
return CommandCategories.INFORMATION;
}
@Override
public void execute(final CommandContext context) {
context
.getChannel()
.sendMessage("🧐 Загружаю свежий xkcd...")
.queue(
loadingMsg -> {
try {
JsonObject json = fetchXkcd();
int num = json.get("num").getAsInt();
String title = json.get("title").getAsString();
String image = json.get("img").getAsString();
String alt = json.get("alt").getAsString();
String date =
json.get("day").getAsString()
+ "."
+ json.get("month").getAsString()
+ "."
+ json.get("year").getAsString();
loadingMsg
.editMessageEmbeds(
EmbedFactory.create()
.withTitle("📐 " + title)
.withImage(image)
.withDescription(alt)
.withColor(EMBED_COLOR)
.withFooter("#" + num + " · " + date)
.build())
.queue();
} catch (IOException | IllegalStateException e) {
loadingMsg.editMessage("❌ Не удалось загрузить xkcd: " + e.getMessage()).queue();
}
});
}
private JsonObject fetchXkcd() throws IOException {
Request request = new Request.Builder().url(XKCD_API).get().build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("xkcd вернул " + response.code());
}
return JsonParser.parseString(response.body().string()).getAsJsonObject();
}
}
}