refactor: BotModule SRP, BlackjackService, FeatureTables, DatabaseMigration
- BotModule 149→63: @Provides вынесены в DatabaseModule - BlackjackService 172→152, FeatureTables 193→147, DatabaseMigration 193→149 - ShutdownManager (118) уже ≤150
This commit is contained in:
parent
0d916fbd96
commit
f6b037921b
11 changed files with 435 additions and 503 deletions
|
|
@ -1,11 +1,9 @@
|
|||
package creator.loki.database;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -15,7 +13,6 @@ import creator.loki.util.LoggerDecorator;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/** Миграция данных из JSON в SQLite */
|
||||
public class DatabaseMigration {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DatabaseMigration.class);
|
||||
private final Database database;
|
||||
|
|
@ -25,80 +22,51 @@ public class DatabaseMigration {
|
|||
this.database = database;
|
||||
}
|
||||
|
||||
/** Мигрировать данные из JSON */
|
||||
public void migrateFromJson(String jsonPath) {
|
||||
LoggerDecorator.database(logger, "Начало миграции данных из JSON: " + jsonPath);
|
||||
|
||||
if (!new java.io.File(jsonPath).exists()) {
|
||||
logger.info("No legacy data file found at {}, skipping migration", jsonPath);
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStreamReader reader =
|
||||
new InputStreamReader(new FileInputStream(jsonPath), StandardCharsets.UTF_8)) {
|
||||
Type type = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
Map<String, Object> data = gson.fromJson(reader, type);
|
||||
|
||||
if (data == null) {
|
||||
LoggerDecorator.warn(logger, "JSON файл пустой или не существует");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMigrated()) {
|
||||
LoggerDecorator.database(logger, "Данные уже мигрированы, пропускаем");
|
||||
return;
|
||||
}
|
||||
|
||||
migrateUserStats(data);
|
||||
migrateReputation(data);
|
||||
migrateShipPairs(data);
|
||||
migrateCustomRoles(data);
|
||||
markMigrated();
|
||||
|
||||
LoggerDecorator.success(logger, "Миграция завершена успешно");
|
||||
|
||||
} catch (Exception e) {
|
||||
LoggerDecorator.error(logger, "Ошибка миграции данных", e);
|
||||
var data = parseJson(jsonPath);
|
||||
if (data == null) return;
|
||||
try (var chk = database.prepare("SELECT value FROM config WHERE key = 'migrated'")) {
|
||||
var rs = chk.executeQuery();
|
||||
if (rs.next() && "true".equals(rs.getString("value"))) return;
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMigrated() {
|
||||
try (PreparedStatement stmt =
|
||||
database.prepare("SELECT value FROM config WHERE key = 'migrated'")) {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
return rs.next() && "true".equals(rs.getString("value"));
|
||||
} catch (SQLException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void markMigrated() {
|
||||
try (PreparedStatement stmt =
|
||||
database.prepare(
|
||||
"INSERT OR REPLACE INTO config (key, value) VALUES ('migrated', 'true')")) {
|
||||
stmt.executeUpdate();
|
||||
migrateUserStats(data);
|
||||
migrateReputation(data);
|
||||
migrateShipPairs(data);
|
||||
migrateCustomRoles(data);
|
||||
var sql = "INSERT OR REPLACE INTO config (key, value) VALUES ('migrated', 'true')";
|
||||
try (var p = database.prepare(sql)) {
|
||||
p.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LoggerDecorator.error(logger, "Ошибка сохранения статуса миграции", e);
|
||||
}
|
||||
LoggerDecorator.success(logger, "Миграция завершена успешно");
|
||||
}
|
||||
|
||||
private Map<String, Object> parseJson(String path) {
|
||||
try (var reader = new InputStreamReader(new FileInputStream(path), UTF_8)) {
|
||||
var type = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
return (Map<String, Object>) gson.fromJson(reader, type);
|
||||
} catch (Exception e) {
|
||||
LoggerDecorator.error(logger, "Ошибка миграции данных", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void migrateUserStats(Map<String, Object> data) {
|
||||
Map<String, Object> userStats = (Map<String, Object>) data.get("userStats");
|
||||
var userStats = (Map<String, Object>) data.get("userStats");
|
||||
if (userStats == null) return;
|
||||
|
||||
LoggerDecorator.database(logger, "Миграция userStats: " + userStats.size() + " записей");
|
||||
|
||||
String sql =
|
||||
"""
|
||||
INSERT OR REPLACE INTO user_stats
|
||||
(user_id, level, total_xp, message_count, voice_minutes, last_message_time, last_voice_join)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)""";
|
||||
|
||||
try (PreparedStatement stmt = database.prepare(sql)) {
|
||||
for (Map.Entry<String, Object> entry : userStats.entrySet()) {
|
||||
Map<String, Object> stats = (Map<String, Object>) entry.getValue();
|
||||
|
||||
try (var stmt =
|
||||
database.prepare(
|
||||
"INSERT OR REPLACE INTO user_stats (user_id, level, total_xp, message_count, voice_minutes, last_message_time, last_voice_join) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
|
||||
for (var entry : userStats.entrySet()) {
|
||||
var stats = (Map<String, Object>) entry.getValue();
|
||||
stmt.setString(1, (String) stats.get("userId"));
|
||||
stmt.setInt(2, ((Number) stats.get("level")).intValue());
|
||||
stmt.setInt(3, ((Number) stats.get("totalXP")).intValue());
|
||||
|
|
@ -106,7 +74,6 @@ public class DatabaseMigration {
|
|||
stmt.setLong(5, ((Number) stats.get("voiceMinutes")).longValue());
|
||||
stmt.setLong(6, ((Number) stats.get("lastMessageTime")).longValue());
|
||||
stmt.setLong(7, ((Number) stats.get("lastVoiceJoin")).longValue());
|
||||
|
||||
stmt.executeUpdate();
|
||||
}
|
||||
LoggerDecorator.success(logger, "userStats мигрированы");
|
||||
|
|
@ -115,19 +82,16 @@ public class DatabaseMigration {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void migrateReputation(Map<String, Object> data) {
|
||||
Map<String, Object> reputation = (Map<String, Object>) data.get("reputation");
|
||||
var reputation = (Map<String, Object>) data.get("reputation");
|
||||
if (reputation == null) return;
|
||||
|
||||
LoggerDecorator.database(logger, "Миграция reputation: " + reputation.size() + " записей");
|
||||
|
||||
try (PreparedStatement stmt =
|
||||
try (var a =
|
||||
database.prepare("INSERT OR REPLACE INTO reputation (user_id, reputation) VALUES (?, ?)")) {
|
||||
for (Map.Entry<String, Object> entry : reputation.entrySet()) {
|
||||
stmt.setString(1, entry.getKey());
|
||||
stmt.setDouble(2, ((Number) entry.getValue()).doubleValue());
|
||||
stmt.executeUpdate();
|
||||
for (var entry : reputation.entrySet()) {
|
||||
a.setString(1, entry.getKey());
|
||||
a.setDouble(2, ((Number) entry.getValue()).doubleValue());
|
||||
a.executeUpdate();
|
||||
}
|
||||
LoggerDecorator.success(logger, "reputation мигрирована");
|
||||
} catch (SQLException e) {
|
||||
|
|
@ -135,23 +99,20 @@ public class DatabaseMigration {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void migrateShipPairs(Map<String, Object> data) {
|
||||
Map<String, Object> shipPairs = (Map<String, Object>) data.get("shipPairs");
|
||||
var shipPairs = (Map<String, Object>) data.get("shipPairs");
|
||||
if (shipPairs == null) return;
|
||||
|
||||
LoggerDecorator.database(logger, "Миграция shipPairs: " + shipPairs.size() + " записей");
|
||||
|
||||
try (PreparedStatement stmt =
|
||||
try (var b =
|
||||
database.prepare(
|
||||
"INSERT OR REPLACE INTO ship_pairs (user1_id, user2_id, compatibility) VALUES (?, ?, ?)")) {
|
||||
for (Map.Entry<String, Object> entry : shipPairs.entrySet()) {
|
||||
String[] users = entry.getKey().split("-");
|
||||
for (var entry : shipPairs.entrySet()) {
|
||||
var users = entry.getKey().split("-");
|
||||
if (users.length == 2) {
|
||||
stmt.setString(1, users[0]);
|
||||
stmt.setString(2, users[1]);
|
||||
stmt.setDouble(3, ((Number) entry.getValue()).doubleValue());
|
||||
stmt.executeUpdate();
|
||||
b.setString(1, users[0]);
|
||||
b.setString(2, users[1]);
|
||||
b.setDouble(3, ((Number) entry.getValue()).doubleValue());
|
||||
b.executeUpdate();
|
||||
}
|
||||
}
|
||||
LoggerDecorator.success(logger, "shipPairs мигрированы");
|
||||
|
|
@ -160,30 +121,22 @@ public class DatabaseMigration {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void migrateCustomRoles(Map<String, Object> data) {
|
||||
Map<String, Object> customRoles = (Map<String, Object>) data.get("custom_roles");
|
||||
var customRoles = (Map<String, Object>) data.get("custom_roles");
|
||||
if (customRoles == null) return;
|
||||
|
||||
LoggerDecorator.database(logger, "Миграция custom_roles");
|
||||
|
||||
try (PreparedStatement stmt =
|
||||
try (var c =
|
||||
database.prepare(
|
||||
"INSERT OR REPLACE INTO custom_roles (guild_id, user_id, role_id) VALUES (?, ?, ?)")) {
|
||||
for (Map.Entry<String, Object> guildEntry : customRoles.entrySet()) {
|
||||
String guildId = guildEntry.getKey();
|
||||
Map<String, Object> users = (Map<String, Object>) guildEntry.getValue();
|
||||
|
||||
for (Map.Entry<String, Object> userEntry : users.entrySet()) {
|
||||
String userId = userEntry.getKey();
|
||||
Object rolesObj = userEntry.getValue();
|
||||
|
||||
if (rolesObj instanceof java.util.List<?> rolesList) {
|
||||
for (Object roleId : rolesList) {
|
||||
stmt.setString(1, guildId);
|
||||
stmt.setString(2, userId);
|
||||
stmt.setString(3, roleId.toString());
|
||||
stmt.executeUpdate();
|
||||
for (var guildEntry : customRoles.entrySet()) {
|
||||
var users = (Map<String, Object>) guildEntry.getValue();
|
||||
for (var userEntry : users.entrySet()) {
|
||||
if (userEntry.getValue() instanceof java.util.List<?> rolesList) {
|
||||
for (var roleId : rolesList) {
|
||||
c.setString(1, guildEntry.getKey());
|
||||
c.setString(2, userEntry.getKey());
|
||||
c.setString(3, roleId.toString());
|
||||
c.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
54
src/main/java/creator/loki/database/DatabaseModule.java
Normal file
54
src/main/java/creator/loki/database/DatabaseModule.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package creator.loki.database;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import creator.loki.config.BotConfig;
|
||||
import creator.loki.feature.config.repository.ConfigRepository;
|
||||
import creator.loki.feature.config.repository.GuildConfigRepository;
|
||||
import creator.loki.feature.config.repository.PrefixedGuildConfigRepository;
|
||||
import creator.loki.feature.music.repository.QueueRepository;
|
||||
import creator.loki.feature.rating.repository.RatingsRepository;
|
||||
import creator.loki.feature.reputation.repository.ReputationRepository;
|
||||
import creator.loki.feature.ship.repository.ShipPairRepository;
|
||||
|
||||
public class DatabaseModule extends AbstractModule {
|
||||
@Provides @Singleton
|
||||
Database provideDatabase(BotConfig config) {
|
||||
Database database = new Database(config.getDbPath());
|
||||
database.connect();
|
||||
new DatabaseSchema(database).createTables();
|
||||
new DatabaseMigration(database).migrateFromJson("data/bot_data.json");
|
||||
return database;
|
||||
}
|
||||
|
||||
@Provides @Singleton
|
||||
ConfigRepository provideConfigRepository(Database database) {
|
||||
return new ConfigRepository(database);
|
||||
}
|
||||
|
||||
@Provides @Singleton
|
||||
GuildConfigRepository provideGuildConfigRepository(ConfigRepository cr) {
|
||||
return new PrefixedGuildConfigRepository(cr);
|
||||
}
|
||||
|
||||
@Provides @Singleton
|
||||
RatingsRepository provideRatingsRepository(Database database) {
|
||||
return new RatingsRepository(database);
|
||||
}
|
||||
|
||||
@Provides @Singleton
|
||||
ReputationRepository provideReputationRepository(Database database) {
|
||||
return new ReputationRepository(database);
|
||||
}
|
||||
|
||||
@Provides @Singleton
|
||||
ShipPairRepository provideShipPairRepository(Database database) {
|
||||
return new ShipPairRepository(database);
|
||||
}
|
||||
|
||||
@Provides @Singleton
|
||||
QueueRepository provideQueueRepository(Database database) {
|
||||
return new QueueRepository(database);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,191 +3,145 @@ package creator.loki.database.schema;
|
|||
import creator.loki.database.Database;
|
||||
|
||||
public final class FeatureTables {
|
||||
private static final String ECONOMY =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS economy (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
cash INTEGER NOT NULL DEFAULT 0,
|
||||
bank INTEGER NOT NULL DEFAULT 0,
|
||||
total_earned INTEGER NOT NULL DEFAULT 0,
|
||||
total_spent INTEGER NOT NULL DEFAULT 0,
|
||||
last_daily TEXT,
|
||||
daily_streak INTEGER DEFAULT 0,
|
||||
fishing_luck INTEGER DEFAULT 0,
|
||||
efficiency INTEGER DEFAULT 0,
|
||||
last_fish TEXT,
|
||||
job TEXT,
|
||||
last_work TEXT
|
||||
)""";
|
||||
private static final String MUSIC_QUEUE =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS music_queue (
|
||||
guild_id TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
track_encoded TEXT NOT NULL,
|
||||
track_title TEXT,
|
||||
track_author TEXT,
|
||||
track_uri TEXT,
|
||||
track_duration BIGINT,
|
||||
PRIMARY KEY (guild_id, position)
|
||||
)""";
|
||||
private static final String WARNINGS =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS warnings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
moderator_id TEXT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
)""";
|
||||
private static final String MODERATION_CASES =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS moderation_cases (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
target_id TEXT NOT NULL,
|
||||
moderator_id TEXT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
duration_sec INTEGER,
|
||||
until_ms INTEGER
|
||||
)""";
|
||||
private static final String ACTIVE_MUTES =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS active_mutes (
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
ends_at_ms INTEGER NOT NULL,
|
||||
case_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (guild_id, user_id)
|
||||
)""";
|
||||
private static final String CUSTOM_ROLES =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS custom_roles (
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
role_id TEXT NOT NULL,
|
||||
PRIMARY KEY (guild_id, user_id, role_id)
|
||||
)""";
|
||||
private static final String VOICE_CHANNELS =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS voice_channels (
|
||||
channel_id TEXT PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
name TEXT,
|
||||
user_limit INTEGER DEFAULT 0,
|
||||
bitrate INTEGER DEFAULT 64000,
|
||||
locked INTEGER DEFAULT 0,
|
||||
text_channel_id TEXT
|
||||
)""";
|
||||
private static final String VOICE_PRESETS =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS voice_presets (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
user_limit INTEGER DEFAULT 0,
|
||||
bitrate INTEGER DEFAULT 64000,
|
||||
region TEXT DEFAULT 'auto',
|
||||
locked INTEGER DEFAULT 0,
|
||||
allowed_role_id TEXT,
|
||||
denied_role_id TEXT
|
||||
)""";
|
||||
private static final String COUNTER_STATS =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS counter_stats (
|
||||
date TEXT PRIMARY KEY,
|
||||
joined INTEGER DEFAULT 0,
|
||||
left_count INTEGER DEFAULT 0,
|
||||
difference INTEGER DEFAULT 0
|
||||
)""";
|
||||
private static final String RATINGS =
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS ratings (
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
rating_type TEXT NOT NULL,
|
||||
value INTEGER NOT NULL,
|
||||
PRIMARY KEY (guild_id, user_id, rating_type)
|
||||
)""";
|
||||
|
||||
public static void create(Database database) {
|
||||
createEconomy(database);
|
||||
createMusicQueue(database);
|
||||
createWarnings(database);
|
||||
createModerationCases(database);
|
||||
createActiveMutes(database);
|
||||
createCustomRoles(database);
|
||||
createVoiceChannels(database);
|
||||
createVoicePresets(database);
|
||||
createCounterStats(database);
|
||||
createRatings(database);
|
||||
database.execute(ECONOMY);
|
||||
database.execute(MUSIC_QUEUE);
|
||||
database.execute(WARNINGS);
|
||||
database.execute(MODERATION_CASES);
|
||||
database.execute(ACTIVE_MUTES);
|
||||
database.execute(CUSTOM_ROLES);
|
||||
database.execute(VOICE_CHANNELS);
|
||||
database.execute(VOICE_PRESETS);
|
||||
database.execute(COUNTER_STATS);
|
||||
database.execute(RATINGS);
|
||||
createIndexes(database);
|
||||
}
|
||||
|
||||
private static void createEconomy(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS economy (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
cash INTEGER NOT NULL DEFAULT 0,
|
||||
bank INTEGER NOT NULL DEFAULT 0,
|
||||
total_earned INTEGER NOT NULL DEFAULT 0,
|
||||
total_spent INTEGER NOT NULL DEFAULT 0,
|
||||
last_daily TEXT,
|
||||
daily_streak INTEGER DEFAULT 0,
|
||||
fishing_luck INTEGER DEFAULT 0,
|
||||
efficiency INTEGER DEFAULT 0,
|
||||
last_fish TEXT,
|
||||
job TEXT,
|
||||
last_work TEXT
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createMusicQueue(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS music_queue (
|
||||
guild_id TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
track_encoded TEXT NOT NULL,
|
||||
track_title TEXT,
|
||||
track_author TEXT,
|
||||
track_uri TEXT,
|
||||
track_duration BIGINT,
|
||||
PRIMARY KEY (guild_id, position)
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createWarnings(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS warnings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
moderator_id TEXT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createModerationCases(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS moderation_cases (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
target_id TEXT NOT NULL,
|
||||
moderator_id TEXT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
duration_sec INTEGER,
|
||||
until_ms INTEGER
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createActiveMutes(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS active_mutes (
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
ends_at_ms INTEGER NOT NULL,
|
||||
case_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (guild_id, user_id)
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createCustomRoles(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS custom_roles (
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
role_id TEXT NOT NULL,
|
||||
PRIMARY KEY (guild_id, user_id, role_id)
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createVoiceChannels(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS voice_channels (
|
||||
channel_id TEXT PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
name TEXT,
|
||||
user_limit INTEGER DEFAULT 0,
|
||||
bitrate INTEGER DEFAULT 64000,
|
||||
locked INTEGER DEFAULT 0,
|
||||
text_channel_id TEXT
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createVoicePresets(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS voice_presets (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
user_limit INTEGER DEFAULT 0,
|
||||
bitrate INTEGER DEFAULT 64000,
|
||||
region TEXT DEFAULT 'auto',
|
||||
locked INTEGER DEFAULT 0,
|
||||
allowed_role_id TEXT,
|
||||
denied_role_id TEXT
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createCounterStats(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS counter_stats (
|
||||
date TEXT PRIMARY KEY,
|
||||
joined INTEGER DEFAULT 0,
|
||||
left_count INTEGER DEFAULT 0,
|
||||
difference INTEGER DEFAULT 0
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createRatings(Database database) {
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS ratings (
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
rating_type TEXT NOT NULL,
|
||||
value INTEGER NOT NULL,
|
||||
PRIMARY KEY (guild_id, user_id, rating_type)
|
||||
)
|
||||
""");
|
||||
}
|
||||
|
||||
private static void createIndexes(Database database) {
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_ratings_guild_type ON ratings(guild_id, rating_type)");
|
||||
database.execute("CREATE INDEX IF NOT EXISTS idx_ratings_value ON ratings(value DESC)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_voice_channels_owner ON voice_channels(owner_id)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_voice_channels_created ON voice_channels(created_at)");
|
||||
database.execute("CREATE INDEX IF NOT EXISTS idx_custom_roles_guild ON custom_roles(guild_id)");
|
||||
database.execute("CREATE INDEX IF NOT EXISTS idx_custom_roles_user ON custom_roles(user_id)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_counter_stats_date ON counter_stats(date DESC)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_warnings_guild_user ON warnings(guild_id, user_id)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_warnings_guild_time ON warnings(guild_id, created_at DESC)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_mod_cases_guild_id ON moderation_cases(guild_id, id DESC)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_active_mutes_ends ON active_mutes(ends_at_ms)");
|
||||
database.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_economy_balance ON economy((cash + bank) DESC)");
|
||||
for (var sql :
|
||||
new String[] {
|
||||
"CREATE INDEX IF NOT EXISTS idx_ratings_guild_type ON ratings(guild_id, rating_type)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_ratings_value ON ratings(value DESC)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_voice_channels_owner ON voice_channels(owner_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_voice_channels_created ON voice_channels(created_at)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_custom_roles_guild ON custom_roles(guild_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_custom_roles_user ON custom_roles(user_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_counter_stats_date ON counter_stats(date DESC)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_warnings_guild_user ON warnings(guild_id, user_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_warnings_guild_time ON warnings(guild_id, created_at DESC)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_mod_cases_guild_id ON moderation_cases(guild_id, id DESC)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_active_mutes_ends ON active_mutes(ends_at_ms)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_economy_balance ON economy((cash + bank) DESC)"
|
||||
}) database.execute(sql);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,7 @@ import creator.loki.core.registry.CommandRegistry;
|
|||
import creator.loki.core.registry.PrefixCommandRegistrar;
|
||||
import creator.loki.core.security.CooldownManager;
|
||||
import creator.loki.core.security.RateLimiter;
|
||||
import creator.loki.database.Database;
|
||||
import creator.loki.database.DatabaseMigration;
|
||||
import creator.loki.database.DatabaseSchema;
|
||||
import creator.loki.feature.config.repository.ConfigRepository;
|
||||
import creator.loki.feature.config.repository.GuildConfigRepository;
|
||||
import creator.loki.feature.config.repository.PrefixedGuildConfigRepository;
|
||||
import creator.loki.database.DatabaseModule;
|
||||
import creator.loki.feature.counter.CounterModule;
|
||||
import creator.loki.feature.economy.EconomyCommandRegistrar;
|
||||
import creator.loki.feature.economy.EconomyModule;
|
||||
|
|
@ -34,20 +29,15 @@ import creator.loki.feature.moderation.ModerationCommandRegistrar;
|
|||
import creator.loki.feature.moderation.ModerationModule;
|
||||
import creator.loki.feature.music.MusicCommandRegistrar;
|
||||
import creator.loki.feature.music.MusicModule;
|
||||
import creator.loki.feature.music.repository.QueueRepository;
|
||||
import creator.loki.feature.profile.ProfileModule;
|
||||
import creator.loki.feature.radio.RadioCommandRegistrar;
|
||||
import creator.loki.feature.radio.RadioModule;
|
||||
import creator.loki.feature.rating.repository.RatingsRepository;
|
||||
import creator.loki.feature.reputation.repository.ReputationRepository;
|
||||
import creator.loki.feature.role.RoleModule;
|
||||
import creator.loki.feature.ship.repository.ShipPairRepository;
|
||||
import creator.loki.feature.utility.UtilityCommandRegistrar;
|
||||
import creator.loki.feature.voice.VoiceModule;
|
||||
import creator.loki.feature.welcome.WelcomeModule;
|
||||
|
||||
public class BotModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(CommandRegistry.class).in(Singleton.class);
|
||||
|
|
@ -60,6 +50,7 @@ public class BotModule extends AbstractModule {
|
|||
bind(ShutdownManager.class).in(Singleton.class);
|
||||
bind(PrefixCommandRegistrar.class).in(Singleton.class);
|
||||
|
||||
install(new DatabaseModule());
|
||||
install(new VoiceModule());
|
||||
install(new MusicModule());
|
||||
install(new LevelModule());
|
||||
|
|
@ -83,67 +74,12 @@ public class BotModule extends AbstractModule {
|
|||
commandBinder.addBinding().to(RadioCommandRegistrar.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
BotConfig provideBotConfig() {
|
||||
return ConfigLoader.load();
|
||||
}
|
||||
@Provides @Singleton
|
||||
BotConfig provideBotConfig() { return ConfigLoader.load(); }
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Provides @Singleton
|
||||
EventDispatcher provideEventDispatcher(
|
||||
BotConfig config,
|
||||
CommandRegistry registry,
|
||||
RateLimiter rateLimiter,
|
||||
MetricsCollector metrics) {
|
||||
BotConfig config, CommandRegistry registry, RateLimiter rateLimiter, MetricsCollector metrics) {
|
||||
return new EventDispatcher(config, registry, rateLimiter, metrics);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
Database provideDatabase(BotConfig config) {
|
||||
Database database = new Database(config.getDbPath());
|
||||
database.connect();
|
||||
DatabaseSchema schema = new DatabaseSchema(database);
|
||||
schema.createTables();
|
||||
DatabaseMigration migration = new DatabaseMigration(database);
|
||||
migration.migrateFromJson("data/bot_data.json");
|
||||
return database;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ConfigRepository provideConfigRepository(Database database) {
|
||||
return new ConfigRepository(database);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
GuildConfigRepository provideGuildConfigRepository(ConfigRepository configRepository) {
|
||||
return new PrefixedGuildConfigRepository(configRepository);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
RatingsRepository provideRatingsRepository(Database database) {
|
||||
return new RatingsRepository(database);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ReputationRepository provideReputationRepository(Database database) {
|
||||
return new ReputationRepository(database);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ShipPairRepository provideShipPairRepository(Database database) {
|
||||
return new ShipPairRepository(database);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
QueueRepository provideQueueRepository(Database database) {
|
||||
return new QueueRepository(database);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import java.util.List;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import creator.loki.feature.economy.domain.EconomyAccount;
|
||||
import creator.loki.feature.economy.repository.EconomyRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -30,109 +29,72 @@ public class BlackjackService {
|
|||
|
||||
public BlackjackState startGame(String userId, long bet) {
|
||||
if (bet <= 0 || activeGames.containsKey(userId)) return null;
|
||||
EconomyAccount account = repository.getAccount(userId);
|
||||
var account = repository.getAccount(userId);
|
||||
if (account.getCash() < bet) return null;
|
||||
|
||||
repository.save(account.withCash(account.getCash() - bet));
|
||||
|
||||
List<String> playerCards = new ArrayList<>(List.of(drawCard(), drawCard()));
|
||||
List<String> dealerCards = new ArrayList<>(List.of(drawCard(), drawCard()));
|
||||
var playerCards = new ArrayList<>(List.of(drawCard(), drawCard()));
|
||||
var dealerCards = new ArrayList<>(List.of(drawCard(), drawCard()));
|
||||
|
||||
int playerSum = calculateSum(playerCards);
|
||||
int dealerSum = calculateSum(dealerCards);
|
||||
boolean playerBj = playerSum == 21;
|
||||
boolean dealerBj = dealerSum == 21;
|
||||
var playerSum = calculateSum(playerCards);
|
||||
var dealerSum = calculateSum(dealerCards);
|
||||
var playerBj = playerSum == 21;
|
||||
var dealerBj = dealerSum == 21;
|
||||
|
||||
if (playerBj || dealerBj) {
|
||||
long payout;
|
||||
if (playerBj && !dealerBj) {
|
||||
payout = (long) (bet * 2.5);
|
||||
} else {
|
||||
payout = 0;
|
||||
}
|
||||
repository.save(
|
||||
repository.getAccount(userId).withCash(repository.getAccount(userId).getCash() + payout));
|
||||
return new BlackjackState(
|
||||
playerCards.toArray(String[]::new),
|
||||
dealerCards.toArray(String[]::new),
|
||||
playerSum,
|
||||
dealerSum,
|
||||
bet,
|
||||
true,
|
||||
payout);
|
||||
var payout = playerBj && !dealerBj ? (long) (bet * 2.5) : 0;
|
||||
creditUser(userId, payout);
|
||||
return buildState(playerCards, dealerCards, playerSum, dealerSum, bet, true, payout);
|
||||
}
|
||||
|
||||
ActiveBlackjack game = new ActiveBlackjack(playerCards, dealerCards, bet, userId);
|
||||
activeGames.put(userId, game);
|
||||
return new BlackjackState(
|
||||
playerCards.toArray(String[]::new),
|
||||
dealerCards.toArray(String[]::new),
|
||||
playerSum,
|
||||
dealerSum,
|
||||
bet,
|
||||
false,
|
||||
0);
|
||||
activeGames.put(userId, new ActiveBlackjack(playerCards, dealerCards, bet, userId));
|
||||
return buildState(playerCards, dealerCards, playerSum, dealerSum, bet, false, 0);
|
||||
}
|
||||
|
||||
public BlackjackState hit(String userId) {
|
||||
ActiveBlackjack game = activeGames.get(userId);
|
||||
var game = activeGames.get(userId);
|
||||
if (game == null) return null;
|
||||
|
||||
game.playerCards().add(drawCard());
|
||||
int sum = calculateSum(game.playerCards());
|
||||
var sum = calculateSum(game.playerCards());
|
||||
|
||||
if (sum > 21) {
|
||||
activeGames.remove(userId);
|
||||
return new BlackjackState(
|
||||
game.playerCards().toArray(String[]::new),
|
||||
game.dealerCards().toArray(String[]::new),
|
||||
sum,
|
||||
calculateSum(game.dealerCards()),
|
||||
game.bet(),
|
||||
true,
|
||||
0);
|
||||
}
|
||||
var over = sum > 21;
|
||||
if (over) activeGames.remove(userId);
|
||||
|
||||
return new BlackjackState(
|
||||
game.playerCards().toArray(String[]::new),
|
||||
game.dealerCards().toArray(String[]::new),
|
||||
return buildState(
|
||||
game.playerCards(),
|
||||
game.dealerCards(),
|
||||
sum,
|
||||
calculateSum(game.dealerCards()),
|
||||
game.bet(),
|
||||
false,
|
||||
over,
|
||||
0);
|
||||
}
|
||||
|
||||
public BlackjackState stand(String userId) {
|
||||
ActiveBlackjack game = activeGames.remove(userId);
|
||||
var game = activeGames.remove(userId);
|
||||
if (game == null) return null;
|
||||
|
||||
List<String> dealerCards = new ArrayList<>(game.dealerCards());
|
||||
var dealerCards = new ArrayList<>(game.dealerCards());
|
||||
while (calculateSum(dealerCards) < 16) {
|
||||
dealerCards.add(drawCard());
|
||||
}
|
||||
|
||||
int playerSum = calculateSum(game.playerCards());
|
||||
int dealerSum = calculateSum(dealerCards);
|
||||
long payout;
|
||||
var playerSum = calculateSum(game.playerCards());
|
||||
var dealerSum = calculateSum(dealerCards);
|
||||
var payout = dealerSum > 21 || playerSum > dealerSum ? game.bet() * 2 : 0;
|
||||
|
||||
if (dealerSum > 21 || playerSum > dealerSum) {
|
||||
payout = game.bet() * 2;
|
||||
} else {
|
||||
payout = 0;
|
||||
}
|
||||
creditUser(userId, payout);
|
||||
|
||||
EconomyAccount account = repository.getAccount(userId);
|
||||
repository.save(account.withCash(account.getCash() + payout));
|
||||
return buildState(
|
||||
game.playerCards(), dealerCards, playerSum, dealerSum, game.bet(), true, payout);
|
||||
}
|
||||
|
||||
return new BlackjackState(
|
||||
game.playerCards().toArray(String[]::new),
|
||||
dealerCards.toArray(String[]::new),
|
||||
playerSum,
|
||||
dealerSum,
|
||||
game.bet(),
|
||||
true,
|
||||
payout);
|
||||
private void creditUser(String userId, long amount) {
|
||||
if (amount <= 0) return;
|
||||
var account = repository.getAccount(userId);
|
||||
repository.save(account.withCash(account.getCash() + amount));
|
||||
}
|
||||
|
||||
private String drawCard() {
|
||||
|
|
@ -140,9 +102,9 @@ public class BlackjackService {
|
|||
}
|
||||
|
||||
private int calculateSum(List<String> cards) {
|
||||
int sum = 0;
|
||||
int aces = 0;
|
||||
for (String c : cards) {
|
||||
var sum = 0;
|
||||
var aces = 0;
|
||||
for (var c : cards) {
|
||||
switch (c) {
|
||||
case "J", "Q", "K" -> sum += 10;
|
||||
case "A" -> {
|
||||
|
|
@ -159,6 +121,24 @@ public class BlackjackService {
|
|||
return sum;
|
||||
}
|
||||
|
||||
private BlackjackState buildState(
|
||||
List<String> playerCards,
|
||||
List<String> dealerCards,
|
||||
int playerSum,
|
||||
int dealerSum,
|
||||
long bet,
|
||||
boolean isOver,
|
||||
long payout) {
|
||||
return new BlackjackState(
|
||||
playerCards.toArray(String[]::new),
|
||||
dealerCards.toArray(String[]::new),
|
||||
playerSum,
|
||||
dealerSum,
|
||||
bet,
|
||||
isOver,
|
||||
payout);
|
||||
}
|
||||
|
||||
public record BlackjackState(
|
||||
String[] playerCards,
|
||||
String[] dealerCards,
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ public class MessageListener extends ListenerAdapter {
|
|||
|
||||
@Inject
|
||||
public MessageListener(
|
||||
EventDispatcher dispatcher, LevelService levelService,
|
||||
EconomyService economyService, LevelUpNotifier levelUpNotifier) {
|
||||
EventDispatcher dispatcher,
|
||||
LevelService levelService,
|
||||
EconomyService economyService,
|
||||
LevelUpNotifier levelUpNotifier) {
|
||||
this.dispatcher = dispatcher;
|
||||
this.levelService = levelService;
|
||||
this.economyService = economyService;
|
||||
|
|
@ -39,8 +41,7 @@ public class MessageListener extends ListenerAdapter {
|
|||
LevelUpResult levelUp = levelService.addMessageXP(userId);
|
||||
if (levelUp != null && event.getMember() != null) {
|
||||
levelUpNotifier.sendLevelUpNotification(
|
||||
event.getMember(), levelUp.getOldLevel(), levelUp.getNewLevel(),
|
||||
event.getChannel());
|
||||
event.getMember(), levelUp.getOldLevel(), levelUp.getNewLevel(), event.getChannel());
|
||||
}
|
||||
int lcAmount = ThreadLocalRandom.current().nextInt(1, 4);
|
||||
economyService.earnCash(userId, lcAmount);
|
||||
|
|
|
|||
|
|
@ -32,38 +32,53 @@ public class ResetLevelsButtonHandler extends ListenerAdapter {
|
|||
String authorId = buttonId.substring(PREFIX_CONFIRM.length());
|
||||
if (authorId.isEmpty()) return;
|
||||
if (!event.getUser().getId().equals(authorId)) {
|
||||
event.reply("❌ Только инициатор команды может подтвердить действие!").setEphemeral(true).queue();
|
||||
event
|
||||
.reply("❌ Только инициатор команды может подтвердить действие!")
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
event.deferReply().queue();
|
||||
try {
|
||||
resetCommand.executeReset(event.getGuild().getId(), event.getUser().getId());
|
||||
event.getMessage().delete().queue();
|
||||
event.getHook().sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.withTitle("✅ Сброс уровней выполнен")
|
||||
.withDescription("Уровни и XP всех пользователей успешно сброшены!\n\n"
|
||||
+ "**Сохранено:**\n• Время в голосовых каналах\n"
|
||||
+ "• Количество сообщений\n• Репутация\n\n"
|
||||
+ "**Сброшено:**\n• Уровень → 0\n• XP → 0\n\n"
|
||||
+ "Теперь используется новая система уровней!")
|
||||
.withColor(Color.GREEN)
|
||||
.withFooter("Инициатор: " + event.getUser().getName(), event.getUser().getAvatarUrl())
|
||||
.build()).queue();
|
||||
event
|
||||
.getHook()
|
||||
.sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.withTitle("✅ Сброс уровней выполнен")
|
||||
.withDescription(
|
||||
"Уровни и XP всех пользователей успешно сброшены!\n\n"
|
||||
+ "**Сохранено:**\n• Время в голосовых каналах\n"
|
||||
+ "• Количество сообщений\n• Репутация\n\n"
|
||||
+ "**Сброшено:**\n• Уровень → 0\n• XP → 0\n\n"
|
||||
+ "Теперь используется новая система уровней!")
|
||||
.withColor(Color.GREEN)
|
||||
.withFooter(
|
||||
"Инициатор: " + event.getUser().getName(), event.getUser().getAvatarUrl())
|
||||
.build())
|
||||
.queue();
|
||||
LoggerDecorator.bot(logger, "Сброс уровней подтверждён: " + event.getUser().getName());
|
||||
} catch (Exception e) {
|
||||
LoggerDecorator.error(logger, "Ошибка при выполнении сброса", e);
|
||||
event.getHook().sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.withTitle("❌ Ошибка")
|
||||
.withDescription("Произошла ошибка при сбросе уровней: " + e.getMessage())
|
||||
.withColor(Color.RED).build()).queue();
|
||||
event
|
||||
.getHook()
|
||||
.sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.withTitle("❌ Ошибка")
|
||||
.withDescription("Произошла ошибка при сбросе уровней: " + e.getMessage())
|
||||
.withColor(Color.RED)
|
||||
.build())
|
||||
.queue();
|
||||
}
|
||||
} else if (buttonId.startsWith(PREFIX_CANCEL)) {
|
||||
String authorId = buttonId.substring(PREFIX_CANCEL.length());
|
||||
if (authorId.isEmpty()) return;
|
||||
if (!event.getUser().getId().equals(authorId)) {
|
||||
event.reply("❌ Только инициатор команды может отменить действие!").setEphemeral(true).queue();
|
||||
event
|
||||
.reply("❌ Только инициатор команды может отменить действие!")
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
event.getMessage().delete().queue();
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ public class VoiceTracker extends ListenerAdapter {
|
|||
private final Object lock = new Object();
|
||||
|
||||
@Inject
|
||||
public VoiceTracker(LevelService levelService, EconomyService economyService,
|
||||
LevelUpNotifier levelUpNotifier) {
|
||||
public VoiceTracker(
|
||||
LevelService levelService, EconomyService economyService, LevelUpNotifier levelUpNotifier) {
|
||||
this.levelService = levelService;
|
||||
this.economyService = economyService;
|
||||
this.levelUpNotifier = levelUpNotifier;
|
||||
|
|
@ -47,7 +47,9 @@ public class VoiceTracker extends ListenerAdapter {
|
|||
voiceJoinTimes.put(userId, System.currentTimeMillis());
|
||||
} else if (event.getChannelLeft() != null && event.getChannelJoined() == null) {
|
||||
Long joinTime;
|
||||
synchronized (lock) { joinTime = voiceJoinTimes.remove(userId); }
|
||||
synchronized (lock) {
|
||||
joinTime = voiceJoinTimes.remove(userId);
|
||||
}
|
||||
if (joinTime != null) {
|
||||
long minutes = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis() - joinTime);
|
||||
if (minutes > 0) {
|
||||
|
|
@ -55,13 +57,17 @@ public class VoiceTracker extends ListenerAdapter {
|
|||
economyService.earnCash(userId, minutes / 2);
|
||||
if (lr != null && event.getMember() != null) {
|
||||
levelUpNotifier.sendLevelUpNotification(
|
||||
event.getMember(), lr.getOldLevel(), lr.getNewLevel(),
|
||||
event.getMember(),
|
||||
lr.getOldLevel(),
|
||||
lr.getNewLevel(),
|
||||
event.getGuild().getSystemChannel());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (event.getChannelJoined() != null && event.getChannelLeft() != null) {
|
||||
synchronized (lock) { voiceJoinTimes.put(userId, System.currentTimeMillis()); }
|
||||
synchronized (lock) {
|
||||
voiceJoinTimes.put(userId, System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +88,11 @@ public class VoiceTracker extends ListenerAdapter {
|
|||
|
||||
public void shutdown() {
|
||||
scheduler.shutdown();
|
||||
try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) scheduler.shutdownNow(); }
|
||||
catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); }
|
||||
try {
|
||||
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) scheduler.shutdownNow();
|
||||
} catch (InterruptedException e) {
|
||||
scheduler.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package creator.loki.feature.level.service;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import creator.loki.feature.level.domain.UserStats;
|
||||
import creator.loki.feature.level.repository.UserStatsRepository;
|
||||
|
||||
public class MessageCooldownService {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ import creator.loki.command.Command;
|
|||
import creator.loki.command.CommandContext;
|
||||
import creator.loki.embed.EmbedFactory;
|
||||
import creator.loki.feature.config.repository.ConfigRepository;
|
||||
import creator.loki.feature.moderation.util.ModerationHelper;
|
||||
import creator.loki.feature.moderation.MuteRoleService;
|
||||
import creator.loki.feature.moderation.MuteScheduler;
|
||||
import creator.loki.feature.moderation.repository.ActiveMuteRepository;
|
||||
import creator.loki.feature.moderation.repository.ModerationCaseRepository;
|
||||
import creator.loki.feature.moderation.util.ModerationHelper;
|
||||
import creator.loki.util.MuteDurationParser;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
|
|
|
|||
|
|
@ -34,16 +34,22 @@ public class RadioManager {
|
|||
this.queueRepository = queueRepository;
|
||||
}
|
||||
|
||||
public void setLavalinkManager(LavalinkManager lm) { this.lavalinkManager = lm; }
|
||||
public void setLavalinkManager(LavalinkManager lm) {
|
||||
this.lavalinkManager = lm;
|
||||
}
|
||||
|
||||
public void setRadioJda(JDA radioJda) { this.radioJda = radioJda; }
|
||||
public void setRadioJda(JDA radioJda) {
|
||||
this.radioJda = radioJda;
|
||||
}
|
||||
|
||||
public void setRadio(long guildId, String streamUrl) {
|
||||
radioStates.compute(guildId, (id, state) -> {
|
||||
if (state == null) state = new GuildRadioState();
|
||||
state.streamUrl = streamUrl;
|
||||
return state;
|
||||
});
|
||||
radioStates.compute(
|
||||
guildId,
|
||||
(id, state) -> {
|
||||
if (state == null) state = new GuildRadioState();
|
||||
state.streamUrl = streamUrl;
|
||||
return state;
|
||||
});
|
||||
LoggerDecorator.music(logger, "Radio URL set for guild " + guildId + ": " + streamUrl);
|
||||
}
|
||||
|
||||
|
|
@ -65,12 +71,14 @@ public class RadioManager {
|
|||
if (guild == null) return;
|
||||
guild.getJDA().getDirectAudioController().connect(guild.getVoiceChannelById(channelId));
|
||||
var self = guild.getMember(guild.getJDA().getSelfUser());
|
||||
if (!VoiceHelper.waitForCondition(() -> self.getVoiceState().inAudioChannel(), VOICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
|
||||
if (!VoiceHelper.waitForCondition(
|
||||
() -> self.getVoiceState().inAudioChannel(), VOICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
|
||||
LoggerDecorator.warn(logger, "Radio: voice connection timeout for guild " + guildId);
|
||||
return;
|
||||
}
|
||||
var link = lavalinkManager.getOrCreateLink(guildId);
|
||||
if (!VoiceHelper.waitForCondition(() -> link.getCachedPlayer() != null, VOICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
|
||||
if (!VoiceHelper.waitForCondition(
|
||||
() -> link.getCachedPlayer() != null, VOICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
|
||||
LoggerDecorator.warn(logger, "Radio: Lavalink player timeout for guild " + guildId);
|
||||
return;
|
||||
}
|
||||
|
|
@ -78,43 +86,60 @@ public class RadioManager {
|
|||
int vol = finalState.volume;
|
||||
var url = finalState.streamUrl;
|
||||
finalState.musicManager = mm;
|
||||
lavalinkManager.getOrCreateLink(guildId).loadItem(url)
|
||||
.subscribe(new AbstractAudioLoadResultHandler() {
|
||||
@Override
|
||||
public void ontrackLoaded(TrackLoaded r) {
|
||||
if (vol != 100) link.createOrUpdatePlayer().setVolume(vol).subscribe();
|
||||
mm.getScheduler().enqueue(r.getTrack());
|
||||
LoggerDecorator.music(logger, "Radio started in guild " + guildId);
|
||||
}
|
||||
@Override
|
||||
public void onPlaylistLoaded(dev.arbjerg.lavalink.client.player.PlaylistLoaded r) {
|
||||
if (!r.getTracks().isEmpty()) {
|
||||
if (vol != 100) link.createOrUpdatePlayer().setVolume(vol).subscribe();
|
||||
mm.getScheduler().enqueue(r.getTracks().get(0));
|
||||
LoggerDecorator.music(logger, "Radio started in guild " + guildId);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onSearchResultLoaded(dev.arbjerg.lavalink.client.player.SearchResult r) {
|
||||
if (!r.getTracks().isEmpty()) {
|
||||
if (vol != 100) link.createOrUpdatePlayer().setVolume(vol).subscribe();
|
||||
mm.getScheduler().enqueue(r.getTracks().get(0));
|
||||
LoggerDecorator.music(logger, "Radio started in guild " + guildId);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void noMatches() { LoggerDecorator.warn(logger, "No radio stream: " + url); }
|
||||
@Override
|
||||
public void loadFailed(dev.arbjerg.lavalink.client.player.LoadFailed e) {
|
||||
LoggerDecorator.warn(logger, "Failed to load radio: " + (e.getException().getMessage() != null ? e.getException().getMessage() : "unknown"));
|
||||
}
|
||||
});
|
||||
lavalinkManager
|
||||
.getOrCreateLink(guildId)
|
||||
.loadItem(url)
|
||||
.subscribe(
|
||||
new AbstractAudioLoadResultHandler() {
|
||||
@Override
|
||||
public void ontrackLoaded(TrackLoaded r) {
|
||||
if (vol != 100) link.createOrUpdatePlayer().setVolume(vol).subscribe();
|
||||
mm.getScheduler().enqueue(r.getTrack());
|
||||
LoggerDecorator.music(logger, "Radio started in guild " + guildId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaylistLoaded(dev.arbjerg.lavalink.client.player.PlaylistLoaded r) {
|
||||
if (!r.getTracks().isEmpty()) {
|
||||
if (vol != 100) link.createOrUpdatePlayer().setVolume(vol).subscribe();
|
||||
mm.getScheduler().enqueue(r.getTracks().get(0));
|
||||
LoggerDecorator.music(logger, "Radio started in guild " + guildId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSearchResultLoaded(dev.arbjerg.lavalink.client.player.SearchResult r) {
|
||||
if (!r.getTracks().isEmpty()) {
|
||||
if (vol != 100) link.createOrUpdatePlayer().setVolume(vol).subscribe();
|
||||
mm.getScheduler().enqueue(r.getTracks().get(0));
|
||||
LoggerDecorator.music(logger, "Radio started in guild " + guildId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
LoggerDecorator.warn(logger, "No radio stream: " + url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(dev.arbjerg.lavalink.client.player.LoadFailed e) {
|
||||
LoggerDecorator.warn(
|
||||
logger,
|
||||
"Failed to load radio: "
|
||||
+ (e.getException().getMessage() != null
|
||||
? e.getException().getMessage()
|
||||
: "unknown"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stopRadio(long guildId) {
|
||||
GuildRadioState state = radioStates.remove(guildId);
|
||||
if (state == null) return;
|
||||
if (state.musicManager != null) { state.musicManager.stop(); state.musicManager.destroy(); }
|
||||
if (state.musicManager != null) {
|
||||
state.musicManager.stop();
|
||||
state.musicManager.destroy();
|
||||
}
|
||||
if (lavalinkManager != null) {
|
||||
Link link = lavalinkManager.getClient().getLinkIfCached(guildId);
|
||||
if (link != null) link.destroy().subscribe();
|
||||
|
|
@ -131,9 +156,13 @@ public class RadioManager {
|
|||
return state != null && state.streamUrl != null;
|
||||
}
|
||||
|
||||
public boolean hasRadioBot() { return radioJda != null; }
|
||||
public boolean hasRadioBot() {
|
||||
return radioJda != null;
|
||||
}
|
||||
|
||||
public JDA getRadioJda() { return radioJda; }
|
||||
public JDA getRadioJda() {
|
||||
return radioJda;
|
||||
}
|
||||
|
||||
public int getVolume(long guildId) {
|
||||
GuildRadioState state = radioStates.get(guildId);
|
||||
|
|
@ -147,7 +176,8 @@ public class RadioManager {
|
|||
if (lavalinkManager != null) {
|
||||
var link = lavalinkManager.getClient().getLinkIfCached(guildId);
|
||||
if (link != null && link.getCachedPlayer() != null) {
|
||||
link.createOrUpdatePlayer().setVolume(state.volume)
|
||||
link.createOrUpdatePlayer()
|
||||
.setVolume(state.volume)
|
||||
.subscribe(null, err -> logger.warn("Lavalink: {}", err.getMessage()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue