From ce1d86300944c073d7abe3a2a483da67d1158eb5 Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Thu, 6 Aug 2026 17:47:27 +0200 Subject: [PATCH] feat: TikTok temp downloads + Lavalink timeout fix + ModerationLogger null check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TikTok: videos >8MB saved to temp files, download link via /d/{key} (24h TTL) - TempFileManager: persistent JSON metadata, lazy cleanup - HealthHttpServer: added /d/{key} endpoint for file downloads - BotConfig: added externalUrl field (EXTERNAL_URL env var) - LavalinkManager: httpTimeout 30s → 60s - BotInitializer: stagger delays between bot Lavalink connections - ModerationLogger: null check for log_channel_id config - TikTokCommand: empty title fallback (Title may not be empty fix) --- .../java/creator/loki/config/BotConfig.java | 9 +- .../creator/loki/config/ConfigLoader.java | 6 +- .../creator/loki/core/BotInitializer.java | 9 ++ .../feature/metrics/HealthHttpServer.java | 66 ++++++++- .../moderation/log/ModerationLogger.java | 63 ++++---- .../feature/music/core/LavalinkManager.java | 2 +- .../creator/loki/feature/tiktok/TempFile.java | 7 + .../loki/feature/tiktok/TempFileManager.java | 136 ++++++++++++++++++ .../loki/feature/tiktok/TikTokCommand.java | 87 +++++++---- .../loki/feature/tiktok/TikTokModule.java | 20 ++- .../creator/loki/config/BotConfigTest.java | 5 +- 11 files changed, 334 insertions(+), 76 deletions(-) create mode 100644 src/main/java/creator/loki/feature/tiktok/TempFile.java create mode 100644 src/main/java/creator/loki/feature/tiktok/TempFileManager.java diff --git a/src/main/java/creator/loki/config/BotConfig.java b/src/main/java/creator/loki/config/BotConfig.java index ef37a09..41548e6 100644 --- a/src/main/java/creator/loki/config/BotConfig.java +++ b/src/main/java/creator/loki/config/BotConfig.java @@ -18,6 +18,7 @@ public class BotConfig { private final int aiDossierUpdateHours; private final String aiDebateChannelId; private final LavalinkConfig lavalinkConfig; + private final String externalUrl; public BotConfig( String mainToken, @@ -36,7 +37,8 @@ public class BotConfig { int aiDossierMsgs, int aiDossierUpdateHours, String aiDebateChannelId, - LavalinkConfig lavalinkConfig) { + LavalinkConfig lavalinkConfig, + String externalUrl) { this.mainToken = mainToken; this.musicToken = musicToken; this.radioToken = radioToken; @@ -54,6 +56,7 @@ public class BotConfig { this.aiDossierUpdateHours = aiDossierUpdateHours; this.aiDebateChannelId = aiDebateChannelId; this.lavalinkConfig = lavalinkConfig; + this.externalUrl = externalUrl; } public String getMainToken() { @@ -127,4 +130,8 @@ public class BotConfig { public LavalinkConfig getLavalinkConfig() { return lavalinkConfig; } + + public String getExternalUrl() { + return externalUrl; + } } diff --git a/src/main/java/creator/loki/config/ConfigLoader.java b/src/main/java/creator/loki/config/ConfigLoader.java index ce59b33..7469efd 100644 --- a/src/main/java/creator/loki/config/ConfigLoader.java +++ b/src/main/java/creator/loki/config/ConfigLoader.java @@ -86,6 +86,9 @@ public final class ConfigLoader { String aiDebateChannelId = dotenv.get("AI_DEBATE_CHANNEL_ID"); LavalinkConfig lavalinkConfig = getLavalinkConfig(); + + String externalUrl = getEnv(dotenv, "EXTERNAL_URL", "http://127.0.0.1:8712"); + LoggerDecorator.success(logger, "Конфигурация загружена успешно"); return new BotConfig( mainToken, @@ -104,6 +107,7 @@ public final class ConfigLoader { aiDossierMsgs, aiDossierUpdateHours, aiDebateChannelId, - lavalinkConfig); + lavalinkConfig, + externalUrl); } } diff --git a/src/main/java/creator/loki/core/BotInitializer.java b/src/main/java/creator/loki/core/BotInitializer.java index ad4fc8d..b9e2982 100644 --- a/src/main/java/creator/loki/core/BotInitializer.java +++ b/src/main/java/creator/loki/core/BotInitializer.java @@ -77,6 +77,7 @@ public class BotInitializer { JDA musicJda = createMusicJDA(); JDA radioJda = null; if (config.getRadioToken() != null && !config.getRadioToken().isBlank()) { + sleep(3000); LavalinkManager radioLavalink = LavalinkManagerFactory.create(config, queueRepository, config.getRadioToken()); radioJda = createRadioJDA(radioLavalink); @@ -144,4 +145,12 @@ public class BotInitializer { } return JdaFactory.awaitJda(builder.build()); } + + private static void sleep(long ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } } diff --git a/src/main/java/creator/loki/feature/metrics/HealthHttpServer.java b/src/main/java/creator/loki/feature/metrics/HealthHttpServer.java index e8dd41a..fae3744 100644 --- a/src/main/java/creator/loki/feature/metrics/HealthHttpServer.java +++ b/src/main/java/creator/loki/feature/metrics/HealthHttpServer.java @@ -1,9 +1,12 @@ package creator.loki.feature.metrics; +import java.io.InputStream; import java.io.OutputStream; import java.net.BindException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.Executors; import com.google.inject.Inject; @@ -12,26 +15,27 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpServer; import creator.loki.config.BotConfig; import creator.loki.core.Lifecycle; +import creator.loki.feature.tiktok.TempFile; +import creator.loki.feature.tiktok.TempFileManager; import creator.loki.util.LoggerDecorator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - * Минимальный HTTP {@code GET /health} для оркестраторов (Docker/K8s). Порт из {@link - * BotConfig#getHealthHttpPort()} (0 = выкл). - */ @Singleton public class HealthHttpServer implements Lifecycle { private static final Logger logger = LoggerFactory.getLogger(HealthHttpServer.class); private final BotConfig config; private final MetricsCollector metrics; + private final TempFileManager tempFileManager; private HttpServer server; @Inject - public HealthHttpServer(BotConfig config, MetricsCollector metrics) { + public HealthHttpServer( + BotConfig config, MetricsCollector metrics, TempFileManager tempFileManager) { this.config = config; this.metrics = metrics; + this.tempFileManager = tempFileManager; } public synchronized void start() { @@ -46,6 +50,7 @@ public class HealthHttpServer implements Lifecycle { try { server = bindHealthServer(port); server.createContext("/health", this::handleHealth); + server.createContext("/d/", this::handleDownload); server.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); server.start(); int bound = server.getAddress().getPort(); @@ -67,7 +72,6 @@ public class HealthHttpServer implements Lifecycle { } } - /** Пробуем заданный порт; при {@link BindException} - системный свободный порт (0). */ private static HttpServer bindHealthServer(final int preferredPort) throws Exception { try { return HttpServer.create(new InetSocketAddress(preferredPort), 0); @@ -107,6 +111,56 @@ public class HealthHttpServer implements Lifecycle { } } + private void handleDownload(final HttpExchange exchange) { + try { + if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) { + exchange.sendResponseHeaders(405, -1); + return; + } + String path = exchange.getRequestURI().getPath(); + String key = path.substring("/d/".length()); + if (key.isEmpty()) { + exchange.sendResponseHeaders(400, -1); + return; + } + TempFile entry = tempFileManager.getEntry(key); + if (entry == null) { + String body = "File not found or expired"; + byte[] bytes = body.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8"); + exchange.sendResponseHeaders(404, bytes.length); + try (final OutputStream os = exchange.getResponseBody()) { + os.write(bytes); + } + return; + } + Path filePath = tempFileManager.getFilePath(entry); + if (!Files.exists(filePath)) { + tempFileManager.getEntry(key); + exchange.sendResponseHeaders(404, -1); + return; + } + long fileSize = Files.size(filePath); + exchange.getResponseHeaders().add("Content-Type", "video/mp4"); + exchange + .getResponseHeaders() + .add("Content-Disposition", "attachment; filename=\"" + entry.filename() + "\""); + exchange.sendResponseHeaders(200, fileSize); + try (InputStream is = Files.newInputStream(filePath); + OutputStream os = exchange.getResponseBody()) { + is.transferTo(os); + } + } catch (Exception e) { + LoggerDecorator.error(logger, "Ошибка ответа /d/", e); + try { + exchange.sendResponseHeaders(500, -1); + } catch (Exception ignored) { + } + } finally { + exchange.close(); + } + } + @Override public synchronized void shutdown() { if (server != null) { diff --git a/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java b/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java index c671be0..4827121 100644 --- a/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java +++ b/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java @@ -35,7 +35,9 @@ public class ModerationLogger extends ListenerAdapter { private void send(final Guild g, final EmbedBuilder eb) { if (eb == null) return; - var ch = g.getTextChannelById(configRepo.get("moderation.log_channel_id")); + String channelId = configRepo.get("moderation.log_channel_id"); + if (channelId == null || channelId.isBlank()) return; + var ch = g.getTextChannelById(channelId); if (ch == null) return; ch.sendMessageEmbeds(eb.setTimestamp(OffsetDateTime.now()).build()).queue(); } @@ -46,24 +48,32 @@ public class ModerationLogger extends ListenerAdapter { public void onMessageDelete(MessageDeleteEvent e) { var content = messageCache.getContent(e.getMessageId()); var g = e.getGuild(); - g.retrieveAuditLogs().type(ActionType.MESSAGE_DELETE).limit(5).queue( - logs -> { - var entry = logs.stream() - .filter(log -> e.getMessageId().equals(log.getTargetId())) - .findFirst().orElse(null); - send(g, LogEmbeds.messageDelete(entry, e.getChannel().getAsMention(), content)); - }); + g.retrieveAuditLogs() + .type(ActionType.MESSAGE_DELETE) + .limit(5) + .queue( + logs -> { + var entry = + logs.stream() + .filter(log -> e.getMessageId().equals(log.getTargetId())) + .findFirst() + .orElse(null); + send(g, LogEmbeds.messageDelete(entry, e.getChannel().getAsMention(), content)); + }); } @Override public void onMessageBulkDelete(MessageBulkDeleteEvent e) { var g = e.getGuild(); var size = e.getMessageIds().size(); - g.retrieveAuditLogs().type(ActionType.MESSAGE_BULK_DELETE).limit(3).queue( - logs -> { - var entry = logs.stream().findFirst().orElse(null); - send(g, LogEmbeds.messageBulkDelete(entry, e.getChannel().getAsMention(), size)); - }); + g.retrieveAuditLogs() + .type(ActionType.MESSAGE_BULK_DELETE) + .limit(3) + .queue( + logs -> { + var entry = logs.stream().findFirst().orElse(null); + send(g, LogEmbeds.messageBulkDelete(entry, e.getChannel().getAsMention(), size)); + }); } // ── Ban / Unban ──────────────────────────────────────────────────────────── @@ -77,10 +87,7 @@ public class ModerationLogger extends ListenerAdapter { var entry = findEntry(l, ev.getUser().getId()); if (entry == null) return null; return LogEmbeds.ban( - ev.getUser().getAsMention(), - mod(entry), - entry.getReason(), - ev.getUser().getAvatarUrl()); + ev.getUser().getAsMention(), mod(entry), entry.getReason(), ev.getUser().getAvatarUrl()); } @Override @@ -91,10 +98,7 @@ public class ModerationLogger extends ListenerAdapter { private EmbedBuilder unban(GuildUnbanEvent ev, List l) { var entry = findEntry(l, ev.getUser().getId()); if (entry == null) return null; - return LogEmbeds.unban( - ev.getUser().getAsMention(), - mod(entry), - ev.getUser().getAvatarUrl()); + return LogEmbeds.unban(ev.getUser().getAsMention(), mod(entry), ev.getUser().getAvatarUrl()); } // ── Kick ────────────────────────────────────────────────────────────────── @@ -108,10 +112,7 @@ public class ModerationLogger extends ListenerAdapter { var entry = findEntry(l, ev.getUser().getId()); if (entry == null) return null; return LogEmbeds.kick( - ev.getUser().getAsMention(), - mod(entry), - entry.getReason(), - ev.getUser().getAvatarUrl()); + ev.getUser().getAsMention(), mod(entry), entry.getReason(), ev.getUser().getAvatarUrl()); } // ── Timeout ──────────────────────────────────────────────────────────────── @@ -122,7 +123,8 @@ public class ModerationLogger extends ListenerAdapter { handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> timeout(e, muted, l)); } - private EmbedBuilder timeout(GuildMemberUpdateTimeOutEvent e, boolean muted, List l) { + private EmbedBuilder timeout( + GuildMemberUpdateTimeOutEvent e, boolean muted, List l) { var entry = findEntry(l, e.getUser().getId()); if (entry == null) return null; return LogEmbeds.timeout( @@ -157,7 +159,8 @@ public class ModerationLogger extends ListenerAdapter { handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> voiceDeafen(e, deaf, l)); } - private EmbedBuilder voiceDeafen(GuildVoiceGuildDeafenEvent e, boolean deaf, List l) { + private EmbedBuilder voiceDeafen( + GuildVoiceGuildDeafenEvent e, boolean deaf, List l) { var member = e.getMember(); var entry = findEntry(l, member.getId(), "deaf"); return LogEmbeds.voiceDeafen( @@ -169,9 +172,9 @@ public class ModerationLogger extends ListenerAdapter { // ── Helpers ──────────────────────────────────────────────────────────────── - private void handle(Guild g, ActionType t, int limit, Function, EmbedBuilder> fn) { - g.retrieveAuditLogs().type(t).limit(limit).queue( - logs -> send(g, fn.apply(logs))); + private void handle( + Guild g, ActionType t, int limit, Function, EmbedBuilder> fn) { + g.retrieveAuditLogs().type(t).limit(limit).queue(logs -> send(g, fn.apply(logs))); } private static AuditLogEntry findEntry(List l, String targetId, String... key) { diff --git a/src/main/java/creator/loki/feature/music/core/LavalinkManager.java b/src/main/java/creator/loki/feature/music/core/LavalinkManager.java index 2438650..5e8b355 100644 --- a/src/main/java/creator/loki/feature/music/core/LavalinkManager.java +++ b/src/main/java/creator/loki/feature/music/core/LavalinkManager.java @@ -55,7 +55,7 @@ public class LavalinkManager { .setName("main") .setServerUri(lavalinkConfig.getWsUri()) .setPassword(lavalinkConfig.getPassword()) - .setHttpTimeout(30000L) + .setHttpTimeout(60000L) .build()); registerListeners(); diff --git a/src/main/java/creator/loki/feature/tiktok/TempFile.java b/src/main/java/creator/loki/feature/tiktok/TempFile.java new file mode 100644 index 0000000..f5329b2 --- /dev/null +++ b/src/main/java/creator/loki/feature/tiktok/TempFile.java @@ -0,0 +1,7 @@ +package creator.loki.feature.tiktok; + +public record TempFile(String key, String filename, long expiresAt) { + public boolean isExpired() { + return System.currentTimeMillis() > expiresAt; + } +} diff --git a/src/main/java/creator/loki/feature/tiktok/TempFileManager.java b/src/main/java/creator/loki/feature/tiktok/TempFileManager.java new file mode 100644 index 0000000..60eaf7e --- /dev/null +++ b/src/main/java/creator/loki/feature/tiktok/TempFileManager.java @@ -0,0 +1,136 @@ +package creator.loki.feature.tiktok; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import creator.loki.util.LoggerDecorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TempFileManager { + private static final Logger logger = LoggerFactory.getLogger(TempFileManager.class); + private static final long TTL_MS = 24 * 60 * 60 * 1000L; + private static final String KEY_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789"; + private static final int KEY_LENGTH = 15; + private static final SecureRandom RANDOM = new SecureRandom(); + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + private final Path cacheDir; + private final ConcurrentHashMap entries = new ConcurrentHashMap<>(); + + public TempFileManager(String dataDir) { + this.cacheDir = Path.of(dataDir).resolve("tiktok-cache"); + init(); + } + + private void init() { + try { + Files.createDirectories(cacheDir); + loadMeta(); + cleanup(); + LoggerDecorator.database(logger, "TempFileManager: " + entries.size() + " активных ссылок"); + } catch (IOException e) { + LoggerDecorator.error(logger, "TempFileManager init error", e); + } + } + + public synchronized String save(byte[] data, String originalName) throws IOException { + cleanup(); + String key = generateKey(); + String filename = key + ".mp4"; + Files.write(cacheDir.resolve(filename), data); + TempFile entry = new TempFile(key, filename, System.currentTimeMillis() + TTL_MS); + entries.put(key, entry); + saveMeta(); + LoggerDecorator.database( + logger, + "TempFileManager: saved " + filename + " (" + (data.length / 1024 / 1024) + "MB)"); + return key; + } + + public TempFile getEntry(String key) { + TempFile entry = entries.get(key); + if (entry == null) { + return null; + } + if (entry.isExpired()) { + deleteFile(entry); + entries.remove(key); + saveMeta(); + return null; + } + Path filePath = cacheDir.resolve(entry.filename()); + if (!Files.exists(filePath)) { + entries.remove(key); + saveMeta(); + return null; + } + return entry; + } + + public Path getFilePath(TempFile entry) { + return cacheDir.resolve(entry.filename()); + } + + public void cleanup() { + entries.entrySet().removeIf(e -> { + if (e.getValue().isExpired()) { + deleteFile(e.getValue()); + return true; + } + return false; + }); + } + + private void deleteFile(TempFile entry) { + try { + Path file = cacheDir.resolve(entry.filename()); + Files.deleteIfExists(file); + } catch (IOException e) { + LoggerDecorator.warn(logger, "TempFileManager: failed to delete " + entry.filename()); + } + } + + private String generateKey() { + StringBuilder sb = new StringBuilder(KEY_LENGTH); + for (int i = 0; i < KEY_LENGTH; i++) { + sb.append(KEY_CHARS.charAt(RANDOM.nextInt(KEY_CHARS.length()))); + } + return sb.toString(); + } + + private void loadMeta() { + Path metaFile = cacheDir.resolve("cache-meta.json"); + if (!Files.exists(metaFile)) { + return; + } + try { + String json = Files.readString(metaFile, StandardCharsets.UTF_8); + Map loaded = + GSON.fromJson(json, new TypeToken>() {}.getType()); + if (loaded != null) { + entries.putAll(loaded); + } + } catch (Exception e) { + LoggerDecorator.warn(logger, "TempFileManager: failed to load meta, starting fresh"); + } + } + + private synchronized void saveMeta() { + Path metaFile = cacheDir.resolve("cache-meta.json"); + try { + String json = GSON.toJson(entries); + Files.writeString(metaFile, json, StandardCharsets.UTF_8); + } catch (IOException e) { + LoggerDecorator.warn(logger, "TempFileManager: failed to save meta"); + } + } +} diff --git a/src/main/java/creator/loki/feature/tiktok/TikTokCommand.java b/src/main/java/creator/loki/feature/tiktok/TikTokCommand.java index 28b9325..73a92a8 100644 --- a/src/main/java/creator/loki/feature/tiktok/TikTokCommand.java +++ b/src/main/java/creator/loki/feature/tiktok/TikTokCommand.java @@ -25,10 +25,15 @@ public class TikTokCommand implements Command { private final TikTokApi tikTokApi; private final OkHttpClient downloadClient; + private final TempFileManager tempFileManager; + private final String externalUrl; @Inject - public TikTokCommand(TikTokApi tikTokApi) { + public TikTokCommand( + TikTokApi tikTokApi, TempFileManager tempFileManager, String externalUrl) { this.tikTokApi = tikTokApi; + this.tempFileManager = tempFileManager; + this.externalUrl = externalUrl; this.downloadClient = new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) @@ -88,37 +93,57 @@ public class TikTokCommand implements Command { byte[] videoBytes = downloadVideo(result.getVideoUrl()); - if (videoBytes.length > MAX_FILE_SIZE) { - loadingMsg - .editMessageEmbeds( - EmbedTemplates.error("Ошибка", "Видео слишком большое (> 8MB)")) - .queue(); - return; - } - String durationStr = formatDuration(result.getDuration()); - MessageEmbed embed = - EmbedFactory.create() - .withTitle(truncate(result.getTitle(), 256)) - .withAuthor("@" + result.getAuthor()) - .withColor(new Color(0, 0, 0)) - .addField("Длительность", durationStr, true) - .withThumbnail(result.getCover()) - .build(); + if (videoBytes.length <= MAX_FILE_SIZE) { + MessageEmbed embed = + EmbedFactory.create() + .withTitle(truncate(result.getTitle(), 256)) + .withAuthor("@" + result.getAuthor()) + .withColor(new Color(0, 0, 0)) + .addField("Длительность", durationStr, true) + .withThumbnail(result.getCover()) + .build(); - context - .getChannel() - .sendFiles(FileUpload.fromData(videoBytes, "tiktok.mp4")) - .setEmbeds(embed) - .queue( - success -> loadingMsg.delete().queue(), - failure -> - loadingMsg - .editMessageEmbeds( - EmbedTemplates.error( - "Ошибка", "Не удалось отправить видео")) - .queue()); + context + .getChannel() + .sendFiles(FileUpload.fromData(videoBytes, "tiktok.mp4")) + .setEmbeds(embed) + .queue( + success -> loadingMsg.delete().queue(), + failure -> + loadingMsg + .editMessageEmbeds( + EmbedTemplates.error( + "Ошибка", "Не удалось отправить видео")) + .queue()); + } else { + String key = tempFileManager.save(videoBytes, "tiktok.mp4"); + String downloadUrl = externalUrl + "/d/" + key; + double sizeMb = videoBytes.length / (1024.0 * 1024.0); + + MessageEmbed embed = + EmbedFactory.create() + .withTitle(truncate(result.getTitle(), 256)) + .withAuthor("@" + result.getAuthor()) + .withColor(new Color(0, 0, 0)) + .addField("Длительность", durationStr, true) + .addField( + "Размер", + String.format("%.1f MB", sizeMb), + true) + .addField( + "Скачать", + "[Нажми чтобы скачать](" + downloadUrl + ")", + false) + .withFooter("Ссылка действительна 24 часа") + .withThumbnail(result.getCover()) + .build(); + + loadingMsg + .editMessageEmbeds(embed) + .queue(); + } } catch (TikTokApi.TikTokApiException e) { loadingMsg @@ -158,8 +183,8 @@ public class TikTokCommand implements Command { } private String truncate(String str, final int maxLen) { - if (str == null) { - return ""; + if (str == null || str.isBlank()) { + return "TikTok Video"; } return str.length() > maxLen ? str.substring(0, maxLen - 3) + "..." : str; } diff --git a/src/main/java/creator/loki/feature/tiktok/TikTokModule.java b/src/main/java/creator/loki/feature/tiktok/TikTokModule.java index b95ebee..6ed985f 100644 --- a/src/main/java/creator/loki/feature/tiktok/TikTokModule.java +++ b/src/main/java/creator/loki/feature/tiktok/TikTokModule.java @@ -1,17 +1,29 @@ package creator.loki.feature.tiktok; +import java.nio.file.Path; + import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; +import creator.loki.config.BotConfig; public class TikTokModule extends AbstractModule { @Override - protected void configure() { - bind(TikTokApi.class).in(Singleton.class); + protected void configure() {} + + @Provides + @Singleton + TempFileManager provideTempFileManager(final BotConfig config) { + String dbPath = config.getDbPath(); + String dataDir = Path.of(dbPath).getParent() != null + ? Path.of(dbPath).getParent().toString() + : "data"; + return new TempFileManager(dataDir); } @Provides - TikTokCommand provideTikTokCommand(final TikTokApi tikTokApi) { - return new TikTokCommand(tikTokApi); + TikTokCommand provideTikTokCommand( + final TikTokApi tikTokApi, final TempFileManager tempFileManager, final BotConfig config) { + return new TikTokCommand(tikTokApi, tempFileManager, config.getExternalUrl()); } } diff --git a/src/test/java/creator/loki/config/BotConfigTest.java b/src/test/java/creator/loki/config/BotConfigTest.java index 3a62e69..1057af9 100644 --- a/src/test/java/creator/loki/config/BotConfigTest.java +++ b/src/test/java/creator/loki/config/BotConfigTest.java @@ -27,7 +27,8 @@ class BotConfigTest { 30, 24, null, - lavalink); + lavalink, + "http://127.0.0.1:8712"); assertEquals("token123", config.getMainToken()); assertEquals("token123", config.getToken()); assertNull(config.getMusicToken()); @@ -46,7 +47,7 @@ class BotConfigTest { BotConfig config = new BotConfig( null, null, null, null, null, 0, null, null, null, null, 10, 300, 3000, 30, 24, null, - null); + null, null); assertNull(config.getMainToken()); assertNull(config.getMusicToken()); assertNull(config.getRadioToken());