.
diff --git a/README.md b/README.md
index e3616aa..1f1299b 100644
--- a/README.md
+++ b/README.md
@@ -1,128 +1,70 @@
-
+# lorep
-# LoReport
+Плагин системы репортов для Paper 1.21
-Modern player report system with Discord integration, GUI and database support for Paper servers.
+## Возможности
-
-
-
-
-
+- `/report [ник] [причина]` — отправить репорт на игрока
+- `/report gui` — GUI со списком репортов (головы игроков с информацией)
+- `/report stats [ник]` — статистика репортов на игрока
+- Discord webhook уведомления о новых репортах
+- Поддержка SQLite и PostgreSQL
-[English](#english) | [Русский](#russian)
+## Права
-
+| Право | Описание | По умолчанию |
+|-------|----------|--------------|
+| `lorep.report` | Отправка репортов | true |
+| `lorep.admin` | GUI и статистика | op |
----
+## Установка
-
+1. Скачай `lorep-1.0.0.jar` из [Releases](https://git.lokili.xyz/loki/lorep/releases)
+2. Положи в папку `plugins/`
+3. Перезапусти сервер
+4. Настрой `plugins/lorep/config.yml`
-## English
+## Конфигурация
-### Overview
+```yaml
+# Discord Webhook URL
+webhook-url: "https://discord.com/api/webhooks/..."
-LoReport is a modern and performant player report system for Minecraft servers. It features an intuitive GUI, Discord webhook integration, statistics, and supports both SQLite and PostgreSQL databases.
+# База данных
+database:
+ type: "sqlite" # sqlite или postgresql
+
+ sqlite:
+ file: "reports.db"
+
+ postgresql:
+ host: "localhost"
+ port: 5432
+ database: "lorep"
+ username: "lorep"
+ password: "password"
+ pool-size: 10
-### Features
+# Сообщения
+messages:
+ report-sent: "&aРепорт успешно отправлен!"
+ already-reported: "&cВы уже отправляли репорт на этого игрока!"
+ # ...
+```
-| Feature | Description |
-|---------|-------------|
-| Report system | `/report ` with cooldown and limits |
-| GUI interface | Browse and manage reports in an inventory menu |
-| Player heads | Visual player identification in GUI |
-| Pagination | Navigate large report lists |
-| Statistics | Per-player report history and stats |
-| Discord integration | Instant webhook notifications with embeds |
-| SQLite | Zero-config database for small servers |
-| PostgreSQL | Scalable database for large servers |
-| Async database | All DB operations off the main thread |
-| Localization | Fully customizable messages |
-| SQL injection protection | All queries use PreparedStatement |
-| PlaceholderAPI | Report stats in other plugins |
+## Сборка
-### Commands
+```bash
+./gradlew shadowJar
+```
-| Command | Permission | Description |
-|---------|------------|-------------|
-| `/report ` | `lorep.report` | Report a player |
-| `/report gui` | `lorep.admin` | Open reports GUI |
-| `/report stats ` | `lorep.admin` | View player report stats |
-| `/rep` | `lorep.report` | Alias for /report |
+Jar будет в `build/libs/lorep-1.0.0.jar`
-### Dependencies
+## Требования
-- Required: Paper 1.19.2+, Java 21+
-- Optional: PlaceholderAPI
-- Database: SQLite 3.47+ (default) or PostgreSQL 12+
+- Paper 1.21+
+- Java 21
-### Installation
+## Автор
-1. Drop the jar into `plugins/`
-2. Restart the server
-3. Configure Discord webhook in `plugins/Loreport/config.yml`:
- ```yaml
- webhook-url: "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
- ```
-
----
-
-
-
-## Русский
-
-### Обзор
-
-LoReport - современная и производительная система репортов для Minecraft серверов. Включает интуитивный GUI, Discord webhook интеграцию, статистику и поддержку SQLite и PostgreSQL.
-
-### Возможности
-
-| Возможность | Описание |
-|-------------|----------|
-| Система репортов | `/report <игрок> <причина>` с кулдауном и лимитами |
-| GUI интерфейс | Просмотр и управление репортами в меню |
-| Головы игроков | Визуальная идентификация в GUI |
-| Пагинация | Навигация по большим спискам |
-| Статистика | История репортов по каждому игроку |
-| Discord интеграция | Мгновенные уведомления через webhook с embed |
-| SQLite | Встроенная БД без настройки |
-| PostgreSQL | Масштабируемая БД для крупных серверов |
-| Асинхронная БД | Все запросы вне основного потока |
-| Локализация | Полностью настраиваемые сообщения |
-| Защита от SQL-иньекций | Все запросы через PreparedStatement |
-| PlaceholderAPI | Статистика репортов в других плагинах |
-
-### Команды
-
-| Команда | Право | Описание |
-|---------|-------|----------|
-| `/report <игрок> <причина>` | `lorep.report` | Отправить репорт |
-| `/report gui` | `lorep.admin` | Открыть GUI репортов |
-| `/report stats <игрок>` | `lorep.admin` | Статистика репортов игрока |
-
-### Зависимости
-
-- Обязательные: Paper 1.19.2+, Java 21+
-- Опциональные: PlaceholderAPI
-- База данных: SQLite 3.47+ (по умолчанию) или PostgreSQL 12+
-
-### Установка
-
-1. Положите jar в папку `plugins/`
-2. Перезапустите сервер
-3. Настройте Discord webhook в `plugins/Loreport/config.yml`:
- ```yaml
- webhook-url: "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
- ```
-
----
-
-### Links
-
-- [Releases](../../releases)
-- [Issues](../../issues)
-- [License](LICENSE)
-
-### License
-
-GNU General Public License v3.0
\ No newline at end of file
+loki
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 41db6ad..0000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Политика безопасности
-
-## Поддерживаемые версии
-
-Мы активно поддерживаем следующие версии плагина:
-
-| Версия | Поддержка |
-| ------ | ------------------ |
-| 1.1.x | :white_check_mark: |
-| 1.0.x | :x: |
-
-## Сообщение об уязвимости
-
-Если вы обнаружили уязвимость в безопасности, пожалуйста, **не создавайте публичный issue**.
-
-Вместо этого:
-
-1. Отправьте email на **loki@lokili.xyz** с темой "Security Vulnerability in Lorep"
-2. Опишите уязвимость максимально подробно:
- - Тип уязвимости
- - Шаги для воспроизведения
- - Потенциальное влияние
- - Предлагаемое решение (если есть)
-
-### Что ожидать
-
-- **Подтверждение получения** в течение 48 часов
-- **Оценка уязвимости** в течение 7 дней
-- **Исправление** в зависимости от серьезности:
- - Критические: 1-3 дня
- - Высокие: 7-14 дней
- - Средние: 14-30 дней
- - Низкие: следующий релиз
-
-### Известные области безопасности
-
-Плагин обрабатывает:
-- Подключения к базам данных (PostgreSQL, SQLite)
-- Пользовательский ввод в командах
-- Конфигурационные файлы
-- Данные игроков
-
-Мы особенно ценим отчеты о:
-- SQL инъекциях
-- Утечках данных игроков
-- Проблемах с правами доступа
-- Уязвимостях в зависимостях
-
-## Благодарности
-
-Мы благодарим всех, кто ответственно сообщает об уязвимостях и помогает сделать Lorep безопаснее.
diff --git a/build.gradle.kts b/build.gradle.kts
index c78b0c6..69039bf 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,11 +1,10 @@
plugins {
java
id("com.gradleup.shadow") version "9.0.0-beta4"
- checkstyle
}
group = "dev.loki"
-version = "1.1.0"
+version = "1.0.0"
java {
toolchain {
@@ -19,30 +18,26 @@ repositories {
}
dependencies {
- // Paper API для 1.19.4+
- compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
-
- implementation("com.google.code.gson:gson:2.11.0")
+ compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
+
implementation("com.squareup.okhttp3:okhttp:4.12.0")
- implementation("com.zaxxer:HikariCP:6.2.1")
+ implementation("com.zaxxer:HikariCP:5.1.0")
implementation("org.postgresql:postgresql:42.7.4")
-
- testImplementation("org.junit.jupiter:junit-jupiter:5.11.4")
- testImplementation("net.jqwik:jqwik:1.9.2")
- testImplementation("org.yaml:snakeyaml:2.3")
- testImplementation("org.xerial:sqlite-jdbc:3.47.2.0")
- testCompileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
+
+ testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
+ testImplementation("net.jqwik:jqwik:1.8.4")
+ testImplementation("org.yaml:snakeyaml:2.2")
+ testImplementation("org.xerial:sqlite-jdbc:3.45.1.0")
+ testCompileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks {
shadowJar {
archiveClassifier.set("")
- archiveBaseName.set("Loreport")
relocate("okhttp3", "dev.loki.lorep.libs.okhttp3")
relocate("okio", "dev.loki.lorep.libs.okio")
relocate("com.zaxxer.hikari", "dev.loki.lorep.libs.hikari")
- // Don't relocate PostgreSQL to avoid driver loading issues
}
build {
@@ -58,29 +53,4 @@ tasks {
expand("version" to version)
}
}
-
- compileJava {
- options.encoding = "UTF-8"
- options.release.set(21)
- }
-
- compileTestJava {
- options.encoding = "UTF-8"
- options.release.set(21)
- }
-}
-
-checkstyle {
- toolVersion = "10.21.4"
- configFile = file("config/checkstyle/checkstyle.xml")
- isIgnoreFailures = false
- maxErrors = 0
- maxWarnings = 0
-}
-
-tasks.withType().configureEach {
- reports {
- xml.required.set(true)
- html.required.set(true)
- }
}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
deleted file mode 100644
index a3a9089..0000000
--- a/config/checkstyle/checkstyle.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/docs/api.md b/docs/api.md
deleted file mode 100644
index af93c50..0000000
--- a/docs/api.md
+++ /dev/null
@@ -1,293 +0,0 @@
-# API для разработчиков
-
-## Подключение к проекту
-
-### Maven
-```xml
-
- codeberg
- https://codeberg.org/api/packages/loki5512344/maven
-
-
-
- dev.loki
- loreport
- 1.0.0
- provided
-
-```
-
-### Gradle
-```kotlin
-repositories {
- maven("https://codeberg.org/api/packages/loki5512344/maven")
-}
-
-dependencies {
- compileOnly("dev.loki:loreport:1.0.0")
-}
-```
-
-## Основные классы
-
-### LorepPlugin
-Главный класс плагина, предоставляет доступ к основным компонентам.
-
-```java
-LorepPlugin plugin = LorepPlugin.getInstance();
-DatabaseManager db = plugin.getDatabaseManager();
-ConfigManager config = plugin.getConfigManager();
-```
-
-### DatabaseManager
-Интерфейс для работы с базой данных репортов.
-
-```java
-public interface DatabaseManager {
- void addReport(Report report);
- List getReports(String playerName);
- List getAllReports();
- int getReportCount(String playerName);
- boolean hasReported(String reporter, String reported);
- void initialize();
- void close();
-}
-```
-
-### Report
-Класс представляющий репорт.
-
-```java
-public class Report {
- private final String reporter;
- private final String reported;
- private final String reason;
- private final long timestamp;
-
- // Конструкторы и геттеры
-}
-```
-
-## События (Events)
-
-### ReportSubmitEvent
-Вызывается при отправке нового репорта.
-
-```java
-@EventHandler
-public void onReportSubmit(ReportSubmitEvent event) {
- Player reporter = event.getReporter();
- String reported = event.getReported();
- String reason = event.getReason();
-
- // Ваша логика
-
- // Отменить репорт
- event.setCancelled(true);
-}
-```
-
-### ReportViewEvent
-Вызывается при просмотре репортов через GUI.
-
-```java
-@EventHandler
-public void onReportView(ReportViewEvent event) {
- Player viewer = event.getViewer();
- List reports = event.getReports();
-
- // Ваша логика
-}
-```
-
-## Примеры использования
-
-### Добавление репорта программно
-
-```java
-public void addCustomReport(String reporter, String reported, String reason) {
- LorepPlugin plugin = LorepPlugin.getInstance();
- DatabaseManager db = plugin.getDatabaseManager();
-
- Report report = new Report(reporter, reported, reason, System.currentTimeMillis());
- db.addReport(report);
-
- // Отправить в Discord если настроен
- DiscordWebhook webhook = plugin.getDiscordWebhook();
- if (webhook != null) {
- webhook.sendReport(report);
- }
-}
-```
-
-### Получение статистики игрока
-
-```java
-public void showPlayerStats(Player admin, String targetPlayer) {
- LorepPlugin plugin = LorepPlugin.getInstance();
- DatabaseManager db = plugin.getDatabaseManager();
-
- List reports = db.getReports(targetPlayer);
- int count = reports.size();
-
- admin.sendMessage("У игрока " + targetPlayer + " " + count + " репортов");
-
- for (Report report : reports) {
- admin.sendMessage("- " + report.getReason() + " (от " + report.getReporter() + ")");
- }
-}
-```
-
-### Интеграция с системой наказаний
-
-```java
-@EventHandler
-public void onReportSubmit(ReportSubmitEvent event) {
- String reported = event.getReported();
-
- // Получаем количество репортов
- DatabaseManager db = LorepPlugin.getInstance().getDatabaseManager();
- int reportCount = db.getReportCount(reported);
-
- // Автоматические действия
- if (reportCount >= 5) {
- // Временный бан на 1 час
- Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
- "tempban " + reported + " 1h Множественные репорты");
- } else if (reportCount >= 3) {
- // Предупреждение
- Player player = Bukkit.getPlayer(reported);
- if (player != null) {
- player.sendMessage("§cВнимание! На вас поступают жалобы от других игроков.");
- }
- }
-}
-```
-
-### Кастомные команды
-
-```java
-public class CustomReportCommand implements CommandExecutor {
-
- @Override
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- if (!(sender instanceof Player)) return false;
-
- Player player = (Player) sender;
- LorepPlugin plugin = LorepPlugin.getInstance();
-
- if (args.length < 2) {
- player.sendMessage("Использование: /customreport <игрок> <причина>");
- return true;
- }
-
- String reported = args[0];
- String reason = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
-
- // Создаем репорт с дополнительной информацией
- Report report = new Report(
- player.getName(),
- reported,
- "[CUSTOM] " + reason,
- System.currentTimeMillis()
- );
-
- plugin.getDatabaseManager().addReport(report);
- player.sendMessage("§aКастомный репорт отправлен!");
-
- return true;
- }
-}
-```
-
-## Хуки для других плагинов
-
-### PlaceholderAPI
-
-```java
-public class LorepPlaceholders extends PlaceholderExpansion {
-
- @Override
- public String getIdentifier() {
- return "loreport";
- }
-
- @Override
- public String getAuthor() {
- return "loki5512344";
- }
-
- @Override
- public String getVersion() {
- return "1.0.0";
- }
-
- @Override
- public String onPlaceholderRequest(Player player, String params) {
- if (player == null) return "";
-
- DatabaseManager db = LorepPlugin.getInstance().getDatabaseManager();
-
- switch (params) {
- case "reports_count":
- return String.valueOf(db.getReportCount(player.getName()));
- case "reports_total":
- return String.valueOf(db.getAllReports().size());
- default:
- return null;
- }
- }
-}
-```
-
-### DiscordSRV интеграция
-
-```java
-@EventHandler
-public void onReportSubmit(ReportSubmitEvent event) {
- // Отправляем в Discord через DiscordSRV
- String message = String.format(
- "🚨 **Новый репорт!**\n" +
- "**Отправитель:** %s\n" +
- "**Нарушитель:** %s\n" +
- "**Причина:** %s",
- event.getReporter().getName(),
- event.getReported(),
- event.getReason()
- );
-
- DiscordSRV.getPlugin().getMainTextChannel().sendMessage(message).queue();
-}
-```
-
-## Лучшие практики
-
-1. **Всегда проверяйте доступность плагина:**
-```java
-if (!Bukkit.getPluginManager().isPluginEnabled("Loreport")) {
- // Плагин не загружен
- return;
-}
-```
-
-2. **Используйте асинхронные операции для БД:**
-```java
-Bukkit.getScheduler().runTaskAsynchronously(yourPlugin, () -> {
- // Работа с базой данных
- List reports = db.getReports(playerName);
-
- Bukkit.getScheduler().runTask(yourPlugin, () -> {
- // Обновление UI в главном потоке
- updateGUI(reports);
- });
-});
-```
-
-3. **Обрабатывайте исключения:**
-```java
-try {
- db.addReport(report);
-} catch (Exception e) {
- getLogger().severe("Ошибка при добавлении репорта: " + e.getMessage());
-}
-```
\ No newline at end of file
diff --git a/docs/commands.md b/docs/commands.md
deleted file mode 100644
index bed816d..0000000
--- a/docs/commands.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Команды и права
-
-## Команды
-
-### `/report <игрок> <причина>`
-Отправить репорт на игрока.
-
-**Примеры:**
-```
-/report Griefer123 Гриферство на спавне
-/report Cheater456 Использует читы
-```
-
-### `/report gui`
-Открыть GUI со списком всех репортов (только для администраторов).
-
-### `/report stats <игрок>`
-Показать статистику репортов на конкретного игрока (только для администраторов).
-
-**Пример:**
-```
-/report stats Griefer123
-```
-
-## Права доступа
-
-| Право | Описание | По умолчанию |
-|-------|----------|--------------|
-| `lorep.report` | Позволяет отправлять репорты | `true` |
-| `lorep.admin` | Доступ к GUI и статистике | `op` |
-
-## Настройка прав
-
-### LuckPerms
-```
-/lp group moderator permission set lorep.admin true
-/lp group default permission set lorep.report true
-```
-
-### PermissionsEx
-```
-/pex group moderator add lorep.admin
-/pex group default add lorep.report
-```
-
-### GroupManager
-```yaml
-groups:
- moderator:
- permissions:
- - lorep.admin
- default:
- permissions:
- - lorep.report
-```
\ No newline at end of file
diff --git a/docs/configuration.md b/docs/configuration.md
deleted file mode 100644
index b9721d1..0000000
--- a/docs/configuration.md
+++ /dev/null
@@ -1,129 +0,0 @@
-# Конфигурация
-
-## Основной файл конфигурации
-
-Файл `config.yml` находится в папке `plugins/Loreport/`.
-
-```yaml
-# Discord Webhook URL для уведомлений
-webhook-url: "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
-
-# Настройки базы данных
-database:
- # Тип БД: sqlite или postgresql
- type: "sqlite"
-
- # Настройки SQLite
- sqlite:
- file: "reports.db"
-
- # Настройки PostgreSQL
- postgresql:
- host: "localhost"
- port: 5432
- database: "loreport"
- username: "loreport"
- password: "your_password"
- pool-size: 10
-
-# Настройки GUI
-gui:
- # Количество репортов на странице
- reports-per-page: 45
- # Автообновление GUI (в тиках, 20 тиков = 1 секунда)
- auto-refresh: 100
-
-# Ограничения
-limits:
- # Максимальная длина причины репорта
- max-reason-length: 100
- # Кулдаун между репортами (в секундах)
- report-cooldown: 30
-
-# Сообщения
-messages:
- report-sent: "&aРепорт успешно отправлен!"
- already-reported: "&cВы уже отправляли репорт на этого игрока!"
- self-report: "&cВы не можете отправить репорт на себя!"
- player-not-found: "&cИгрок не найден!"
- no-permission: "&cУ вас нет прав на эту команду!"
- usage: "&eИспользование: /report <ник> <причина>"
- no-reports: "&eУ этого игрока нет репортов."
- stats-header: "&6=== Статистика репортов: %player% ==="
- stats-count: "&eВсего репортов: &f%count%"
- stats-last-online: "&eПоследний онлайн: &f%time%"
- stats-report-entry: "&7- %reason% &8(%time% назад)"
- gui-title: "Репорты - Страница %page%"
- cooldown: "&cПодождите %time% секунд перед следующим репортом!"
- reason-too-long: "&cПричина слишком длинная! Максимум %max% символов."
-```
-
-## Discord Webhook
-
-### Создание Webhook
-
-1. Откройте настройки канала в Discord
-2. Перейдите в "Интеграции" → "Вебхуки"
-3. Нажмите "Создать вебхук"
-4. Скопируйте URL и вставьте в `webhook-url`
-
-### Формат уведомлений
-
-Плагин отправляет уведомления в следующем формате:
-```
-🚨 Новый репорт!
-Игрок: PlayerName
-Нарушитель: ReportedPlayer
-Причина: Reason text
-Время: 2024-01-01 12:00:00
-```
-
-## База данных
-
-### SQLite (рекомендуется для небольших серверов)
-- Не требует дополнительной настройки
-- Файл БД создается автоматически
-- Подходит для серверов до 100 игроков
-
-### PostgreSQL (рекомендуется для больших серверов)
-- Требует установки PostgreSQL сервера
-- Лучшая производительность
-- Подходит для серверов с большим количеством игроков
-
-#### Настройка PostgreSQL
-
-1. Установите PostgreSQL
-2. Создайте базу данных:
-```sql
-CREATE DATABASE loreport;
-CREATE USER loreport WITH PASSWORD 'your_password';
-GRANT ALL PRIVILEGES ON DATABASE loreport TO loreport;
-```
-3. Обновите конфигурацию плагина
-
-## Цветовые коды
-
-Плагин поддерживает стандартные цветовые коды Minecraft:
-- `&0` - черный
-- `&1` - темно-синий
-- `&2` - темно-зеленый
-- `&3` - темно-бирюзовый
-- `&4` - темно-красный
-- `&5` - темно-фиолетовый
-- `&6` - золотой
-- `&7` - серый
-- `&8` - темно-серый
-- `&9` - синий
-- `&a` - зеленый
-- `&b` - бирюзовый
-- `&c` - красный
-- `&d` - светло-фиолетовый
-- `&e` - желтый
-- `&f` - белый
-
-А также форматирование:
-- `&l` - жирный
-- `&m` - зачеркнутый
-- `&n` - подчеркнутый
-- `&o` - курсив
-- `&r` - сброс форматирования
\ No newline at end of file
diff --git a/docs/faq.md b/docs/faq.md
deleted file mode 100644
index f3af802..0000000
--- a/docs/faq.md
+++ /dev/null
@@ -1,108 +0,0 @@
-# Часто задаваемые вопросы (FAQ)
-
-## Установка и настройка
-
-### Q: Плагин не загружается, что делать?
-A: Проверьте:
-- Версию Java (требуется 17+)
-- Версию сервера (Paper 1.19.2+)
-- Логи сервера на наличие ошибок
-- Правильность названия файла плагина
-
-### Q: Как настроить Discord уведомления?
-A:
-1. Создайте webhook в настройках Discord канала
-2. Скопируйте URL webhook
-3. Вставьте его в `config.yml` в поле `webhook-url`
-4. Перезагрузите плагин командой `/reload confirm`
-
-### Q: Какую базу данных выбрать?
-A:
-- **SQLite** - для серверов до 100 игроков, не требует настройки
-- **PostgreSQL** - для больших серверов, лучшая производительность
-
-## Использование
-
-### Q: Игроки не могут отправлять репорты
-A: Проверьте права доступа:
-- Убедитесь что у игроков есть право `lorep.report`
-- Проверьте настройки плагина прав (LuckPerms, PermissionsEx и т.д.)
-
-### Q: GUI не открывается
-A: Проверьте:
-- Есть ли у вас право `lorep.admin`
-- Нет ли ошибок в консоли
-- Правильно ли настроена база данных
-
-### Q: Как удалить репорт?
-A: В текущей версии удаление репортов через игру не поддерживается. Можно удалить напрямую из базы данных или ждать следующего обновления.
-
-### Q: Можно ли изменить сообщения плагина?
-A: Да, все сообщения настраиваются в секции `messages` файла `config.yml`.
-
-## Производительность
-
-### Q: Плагин тормозит сервер
-A:
-- Переключитесь на PostgreSQL если используете SQLite
-- Увеличьте `pool-size` в настройках PostgreSQL
-- Проверьте логи на ошибки подключения к БД
-
-### Q: Discord уведомления не приходят
-A: Проверьте:
-- Правильность URL webhook
-- Доступность Discord API с вашего сервера
-- Логи плагина на наличие ошибок
-
-## Совместимость
-
-### Q: Работает ли с Spigot?
-A: Нет, плагин требует Paper API. Используйте Paper, Purpur или Pufferfish.
-
-### Q: Поддерживается ли версия 1.18?
-A: Нет, минимальная поддерживаемая версия - 1.19.2.
-
-### Q: Работает ли с другими плагинами репортов?
-A: Плагин независим, но может конфликтовать с другими системами репортов. Рекомендуется использовать только один плагин репортов.
-
-## Разработка
-
-### Q: Есть ли API для других плагинов?
-A: Да, смотрите [документацию API](api.md).
-
-### Q: Как собрать плагин из исходников?
-A:
-```bash
-git clone https://codeberg.org/loki5512344/Loreport.git
-cd Loreport
-./gradlew shadowJar
-```
-Готовый jar будет в папке `build/libs/`.
-
-### Q: Как сообщить об ошибке?
-A: Создайте issue на [Codeberg](https://codeberg.org/loki5512344/Loreport/issues) с подробным описанием проблемы и логами.
-
-## Миграция
-
-### Q: Как перенести данные с другого плагина репортов?
-A: В текущей версии автоматическая миграция не поддерживается. Обратитесь к разработчику для помощи с миграцией.
-
-### Q: Как сделать бэкап репортов?
-A:
-- **SQLite**: скопируйте файл `plugins/Loreport/reports.db`
-- **PostgreSQL**: используйте `pg_dump` для создания бэкапа базы данных
-
-## Поддержка
-
-### Q: Где получить помощь?
-A:
-- [Issues](https://codeberg.org/loki5512344/Loreport/issues) - для багов
-- [Discussions](https://codeberg.org/loki5512344/Loreport/discussions) - для вопросов
-- Discord сервер разработчика (ссылка в профиле)
-
-### Q: Планируются ли новые функции?
-A: Да! Планируется:
-- Веб-панель для управления репортами
-- Система категорий репортов
-- Автоматические действия (бан, кик)
-- Интеграция с другими плагинами модерации
\ No newline at end of file
diff --git a/gradlew b/gradlew
old mode 100755
new mode 100644
diff --git a/modrinth/changelog.md b/modrinth/changelog.md
deleted file mode 100644
index 1fd37aa..0000000
--- a/modrinth/changelog.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Changelog
-
-## v1.1.0 - Улучшенная версия
-
-### ✨ Новые возможности
-- Поддержка Minecraft 1.19.2 - 1.21.4
-- Улучшенная конфигурация с новыми опциями
-- Добавлены алиасы команд: `/rep`, `/reports`
-- Система уведомлений администраторов в игре
-- Настраиваемые ограничения на количество репортов
-- Улучшенный GUI с настройками отображения
-
-### 🔧 Улучшения
-- Оптимизирована производительность базы данных
-- Улучшена совместимость с различными версиями Minecraft
-- Обновлены зависимости для лучшей стабильности
-- Добавлена поддержка Java 17+ (вместо Java 21)
-- Улучшена обработка ошибок
-
-### 🐛 Исправления
-- Исправлены проблемы с кодировкой сообщений
-- Улучшена стабильность Discord webhook
-- Исправлены мелкие баги в GUI
-
-### 📚 Документация
-- Полная документация в папке `docs/`
-- FAQ с ответами на частые вопросы
-- Руководство по API для разработчиков
-- Подробное описание конфигурации
-
----
-
-## v1.0.0 - Первый релиз
-
-### ✨ Основные возможности
-- Система репортов с командой `/report`
-- GUI интерфейс для администраторов
-- Статистика репортов по игрокам
-- Интеграция с Discord через webhook
-- Поддержка SQLite и PostgreSQL
-- Настраиваемые сообщения
-- Система прав доступа
\ No newline at end of file
diff --git a/modrinth/description.md b/modrinth/description.md
deleted file mode 100644
index 19c6208..0000000
--- a/modrinth/description.md
+++ /dev/null
@@ -1,108 +0,0 @@
-# 🚨 Loreport - Современная система репортов
-
-**Loreport** - это мощный и удобный плагин для Minecraft серверов, который предоставляет полноценную систему репортов с интеграцией Discord и красивым GUI интерфейсом.
-
-## ✨ Основные возможности
-
-### 📝 Простая система репортов
-- Интуитивно понятная команда `/report <игрок> <причина>`
-- Защита от спама репортов с настраиваемым кулдауном
-- Ограничение на количество репортов на одного игрока
-- Проверка длины причины репорта
-
-### 🎨 Красивый GUI интерфейс
-- Удобный интерфейс для просмотра всех репортов
-- Отображение голов игроков для лучшей визуализации
-- Пагинация для больших списков репортов
-- Автообновление интерфейса
-
-### 📊 Подробная статистика
-- Команда `/report stats <игрок>` для просмотра статистики
-- История всех репортов на игрока
-- Информация о времени последнего онлайна
-- Детальная информация о каждом репорте
-
-### 🔔 Discord интеграция
-- Автоматические уведомления в Discord через webhook
-- Красиво оформленные сообщения с полной информацией
-- Мгновенные уведомления администраторов о новых репортах
-
-### 🗄️ Гибкая база данных
-- **SQLite** - для небольших серверов, не требует настройки
-- **PostgreSQL** - для больших серверов с высокой нагрузкой
-- Автоматическое создание таблиц при первом запуске
-- Оптимизированные запросы для высокой производительности
-
-### 🌐 Полная настраиваемость
-- Все сообщения можно изменить в конфигурации
-- Поддержка цветовых кодов Minecraft
-- Гибкие настройки ограничений и кулдаунов
-- Настройка GUI и уведомлений
-
-## 🎯 Совместимость
-
-- **Minecraft версии**: 1.19.2 - 1.21.4
-- **Серверное ПО**: Paper, Purpur, Pufferfish
-- **Java**: 17+
-- **Операционные системы**: Windows, Linux, macOS
-
-## 🚀 Команды
-
-| Команда | Описание | Права |
-|---------|----------|-------|
-| `/report <игрок> <причина>` | Отправить репорт на игрока | `lorep.report` |
-| `/report gui` | Открыть GUI с репортами | `lorep.admin` |
-| `/report stats <игрок>` | Статистика репортов игрока | `lorep.admin` |
-
-**Алиасы**: `/rep`, `/reports`
-
-## 🔐 Права доступа
-
-- `lorep.report` - Отправка репортов (по умолчанию: все игроки)
-- `lorep.admin` - Доступ к GUI и статистике (по умолчанию: операторы)
-- `lorep.*` - Все права плагина
-
-## ⚙️ Быстрая настройка
-
-1. Скачайте плагин и поместите в папку `plugins/`
-2. Перезапустите сервер
-3. Настройте Discord webhook в `config.yml`:
-```yaml
-webhook-url: "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
-```
-4. При необходимости настройте базу данных PostgreSQL
-5. Готово! Игроки могут использовать `/report`
-
-## 📈 Производительность
-
-Плагин оптимизирован для работы на серверах любого размера:
-- Асинхронные операции с базой данных
-- Кэширование часто используемых данных
-- Минимальное влияние на производительность сервера
-- Поддержка пула соединений для PostgreSQL
-
-## 🛠️ Для разработчиков
-
-Loreport предоставляет API для интеграции с другими плагинами:
-- События для отслеживания новых репортов
-- Методы для программного добавления репортов
-- Интеграция с PlaceholderAPI
-- Подробная документация API
-
-## 🆘 Поддержка
-
-- **Документация**: Полная документация включена в плагин
-- **Issues**: [Codeberg Issues](https://codeberg.org/loki5512344/Loreport/issues)
-- **Обсуждения**: [Codeberg Discussions](https://codeberg.org/loki5512344/Loreport/discussions)
-
-## 📄 Лицензия
-
-Проект распространяется под лицензией **GPL-3.0**, что означает:
-- ✅ Свободное использование
-- ✅ Изменение исходного кода
-- ✅ Коммерческое использование
-- ✅ Распространение
-
----
-
-**Сделайте свой сервер безопаснее с Loreport! 🛡️**
\ No newline at end of file
diff --git a/modrinth/project-info.md b/modrinth/project-info.md
deleted file mode 100644
index b3700e9..0000000
--- a/modrinth/project-info.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Информация о проекте для Modrinth
-
-## Основная информация
-- **Название**: Loreport
-- **Краткое описание**: Современная система репортов для Minecraft серверов с Discord интеграцией
-- **Категории**: Management, Utility, Server Management
-- **Лицензия**: GPL-3.0
-- **Тип проекта**: Plugin (Server-side)
-
-## Поддерживаемые версии
-- **Minecraft**: 1.19.2, 1.19.3, 1.19.4, 1.20, 1.20.1, 1.20.2, 1.20.3, 1.20.4, 1.20.5, 1.20.6, 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4
-- **Загрузчики**: Paper, Purpur, Pufferfish
-- **Окружение**: Server
-
-## Теги
-- report
-- reports
-- moderation
-- discord
-- gui
-- database
-- admin
-- management
-- paper
-- plugin
-
-## Ссылки
-- **Исходный код**: https://codeberg.org/loki5512344/Loreport
-- **Issues**: https://codeberg.org/loki5512344/Loreport/issues
-- **Wiki**: https://codeberg.org/loki5512344/Loreport/wiki
-- **Discussions**: https://codeberg.org/loki5512344/Loreport/discussions
-
-## Зависимости
-Плагин не требует дополнительных зависимостей, все необходимые библиотеки включены в jar файл.
-
-## Рекомендуемые плагины
-- **LuckPerms** - для расширенного управления правами
-- **PlaceholderAPI** - для интеграции с другими плагинами
-- **DiscordSRV** - для дополнительной интеграции с Discord
-
-## Галерея изображений
-1. GUI интерфейс с репортами
-2. Пример Discord уведомления
-3. Статистика игрока
-4. Конфигурационный файл
-
-## Инструкции для загрузки
-1. Выберите версию плагина, совместимую с вашей версией Minecraft
-2. Скачайте файл `Loreport-x.x.x.jar`
-3. Поместите файл в папку `plugins/` вашего сервера
-4. Перезапустите сервер
-5. Настройте конфигурацию в `plugins/Loreport/config.yml`
-6. При необходимости настройте Discord webhook
\ No newline at end of file
diff --git a/src/main/java/dev/loki/lorep/command/ReportCommand.java b/src/main/java/dev/loki/lorep/command/ReportCommand.java
index 4cdf8f3..4f20bf7 100644
--- a/src/main/java/dev/loki/lorep/command/ReportCommand.java
+++ b/src/main/java/dev/loki/lorep/command/ReportCommand.java
@@ -16,189 +16,122 @@ import java.util.List;
import java.util.stream.Collectors;
public class ReportCommand implements CommandExecutor, TabCompleter {
-
+
private final LorepPlugin plugin;
-
+
public ReportCommand(LorepPlugin plugin) {
this.plugin = plugin;
}
-
+
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
- sender.sendMessage(plugin.getConfigManager().getMessage("console-only"));
+ sender.sendMessage("§cЭта команда только для игроков!");
return true;
}
-
+
if (args.length == 0) {
player.sendMessage(plugin.getConfigManager().getMessage("usage"));
return true;
}
-
+
+ // Handle subcommands for admins
if (args[0].equalsIgnoreCase("gui")) {
return handleGui(player);
}
-
+
if (args[0].equalsIgnoreCase("stats") && args.length >= 2) {
return handleStats(player, args[1]);
}
-
- if (args[0].equalsIgnoreCase("image") && args.length >= 3) {
- return handleReportImage(player, args[1], args[2]);
- }
-
+
+ // Regular report command
if (!player.hasPermission("lorep.report")) {
player.sendMessage(plugin.getConfigManager().getMessage("no-permission"));
return true;
}
-
+
if (args.length < 2) {
player.sendMessage(plugin.getConfigManager().getMessage("usage"));
return true;
}
-
+
String targetName = args[0];
- String reason;
- String imageUrl = null;
-
- if (args.length >= 3 && args[2].startsWith("http")) {
- reason = args[1];
- imageUrl = args[2];
- } else {
- reason = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
- }
-
- if (reason.trim().isEmpty()) {
- player.sendMessage(plugin.getConfigManager().getMessage("usage"));
- return true;
- }
-
- if (reason.length() > 500) {
- player.sendMessage(plugin.getConfigManager().getMessage("reason-too-long", "%max%", "500"));
- return true;
- }
-
- return handleReport(player, targetName, reason, imageUrl);
+ String reason = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
+
+ return handleReport(player, targetName, reason);
}
-
+
private boolean handleReport(Player reporter, String targetName, String reason) {
- return handleReport(reporter, targetName, reason, null);
- }
-
- private boolean handleReport(Player reporter, String targetName, String reason, String imageUrl) {
+ // Check if target exists
@SuppressWarnings("deprecation")
OfflinePlayer target = Bukkit.getOfflinePlayer(targetName);
-
+
if (!target.hasPlayedBefore() && !target.isOnline()) {
reporter.sendMessage(plugin.getConfigManager().getMessage("player-not-found"));
return true;
}
-
+
+ // Check self-report
if (target.getUniqueId().equals(reporter.getUniqueId())) {
reporter.sendMessage(plugin.getConfigManager().getMessage("self-report"));
return true;
}
-
+
+ // Check duplicate report
if (plugin.getDatabaseManager().hasReported(reporter.getUniqueId(), target.getUniqueId())) {
reporter.sendMessage(plugin.getConfigManager().getMessage("already-reported"));
return true;
}
-
+
+ // Create and save report
Report report = new Report(
reporter.getUniqueId(),
reporter.getName(),
target.getUniqueId(),
target.getName() != null ? target.getName() : targetName,
- reason,
- imageUrl
+ reason
);
-
+
plugin.getDatabaseManager().saveReport(report);
-
+
+ // Send to Discord webhook
if (plugin.getDiscordWebhook() != null) {
plugin.getDiscordWebhook().sendReport(report);
}
-
+
reporter.sendMessage(plugin.getConfigManager().getMessage("report-sent"));
return true;
}
- private boolean handleReportImage(Player player, String idStr, String imageUrl) {
- if (!player.hasPermission("lorep.admin")) {
- player.sendMessage(plugin.getConfigManager().getMessage("no-permission"));
- return true;
- }
-
- int reportId;
- try {
- reportId = Integer.parseInt(idStr);
- } catch (NumberFormatException e) {
- player.sendMessage(plugin.getConfigManager().getMessage("invalid-report-id"));
- return true;
- }
-
- Report report = plugin.getDatabaseManager().getReportById(reportId);
- if (report == null) {
- player.sendMessage(plugin.getConfigManager().getMessage("report-not-found"));
- return true;
- }
-
- plugin.getDatabaseManager().updateReportImage(reportId, imageUrl);
-
- Report updated = new Report(
- report.id(),
- report.reporterUuid(),
- report.reporterName(),
- report.targetUuid(),
- report.targetName(),
- report.reason(),
- imageUrl,
- report.status(),
- report.createdAt()
- );
-
- if (plugin.getDiscordWebhook() != null) {
- plugin.getDiscordWebhook().sendReport(updated);
- }
-
- player.sendMessage(plugin.getConfigManager().getMessage("report-image-updated", "%id%", String.valueOf(reportId)));
- return true;
- }
-
private boolean handleGui(Player player) {
if (!player.hasPermission("lorep.admin")) {
player.sendMessage(plugin.getConfigManager().getMessage("no-permission"));
return true;
}
-
+
new dev.loki.lorep.gui.ReportGui(plugin, 0).open(player);
return true;
}
-
+
private boolean handleStats(Player player, String targetName) {
new ReportStatsCommand(plugin).execute(player, targetName);
return true;
}
-
+
@Override
public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List completions = new ArrayList<>();
-
+
if (args.length == 1) {
+ // First argument: player names or subcommands
String input = args[0].toLowerCase();
-
+
if (sender.hasPermission("lorep.admin")) {
- if ("gui".startsWith(input)) {
- completions.add("gui");
- }
- if ("stats".startsWith(input)) {
- completions.add("stats");
- }
- if ("image".startsWith(input)) {
- completions.add("image");
- }
+ if ("gui".startsWith(input)) completions.add("gui");
+ if ("stats".startsWith(input)) completions.add("stats");
}
-
+
+ // Add online player names
completions.addAll(
Bukkit.getOnlinePlayers().stream()
.map(Player::getName)
@@ -206,6 +139,7 @@ public class ReportCommand implements CommandExecutor, TabCompleter {
.collect(Collectors.toList())
);
} else if (args.length == 2 && args[0].equalsIgnoreCase("stats")) {
+ // Second argument for stats: player names
String input = args[1].toLowerCase();
completions.addAll(
Bukkit.getOnlinePlayers().stream()
@@ -214,7 +148,7 @@ public class ReportCommand implements CommandExecutor, TabCompleter {
.collect(Collectors.toList())
);
}
-
+
return completions;
}
}
diff --git a/src/main/java/dev/loki/lorep/command/ReportStatsCommand.java b/src/main/java/dev/loki/lorep/command/ReportStatsCommand.java
index 545cb2a..876d37e 100644
--- a/src/main/java/dev/loki/lorep/command/ReportStatsCommand.java
+++ b/src/main/java/dev/loki/lorep/command/ReportStatsCommand.java
@@ -10,9 +10,7 @@ import org.bukkit.entity.Player;
import java.util.List;
public class ReportStatsCommand {
-
- private static final int MAX_RECENT_REPORTS = 5;
-
+
private final LorepPlugin plugin;
public ReportStatsCommand(LorepPlugin plugin) {
@@ -49,14 +47,15 @@ public class ReportStatsCommand {
player.sendMessage(plugin.getConfigManager().getMessage("stats-count",
"%count%", String.valueOf(reportCount)));
- String lastOnline = target.isOnline()
- ? plugin.getConfigManager().getMessage("online-status")
- : TimeUtil.formatTimeAgo(target.getLastPlayed());
+ // Last online
+ String lastOnline = target.isOnline() ? "Сейчас онлайн" :
+ TimeUtil.formatTimeAgo(target.getLastPlayed());
player.sendMessage(plugin.getConfigManager().getMessage("stats-last-online",
"%time%", lastOnline));
-
- player.sendMessage(plugin.getConfigManager().getMessage("stats-recent-header"));
- int shown = Math.min(MAX_RECENT_REPORTS, reports.size());
+
+ // Recent reports (max 5)
+ player.sendMessage("§7Последние репорты:");
+ int shown = Math.min(5, reports.size());
for (int i = 0; i < shown; i++) {
Report report = reports.get(i);
String timeAgo = TimeUtil.formatTimeAgo(report.createdAt().toEpochMilli());
@@ -64,10 +63,9 @@ public class ReportStatsCommand {
"%reason%", report.reason(),
"%time%", timeAgo));
}
-
- if (reports.size() > MAX_RECENT_REPORTS) {
- player.sendMessage(plugin.getConfigManager().getMessage("stats-more-reports",
- "%count%", String.valueOf(reports.size() - MAX_RECENT_REPORTS)));
+
+ if (reports.size() > 5) {
+ player.sendMessage("§7... и ещё " + (reports.size() - 5) + " репортов");
}
}
}
diff --git a/src/main/java/dev/loki/lorep/config/ConfigManager.java b/src/main/java/dev/loki/lorep/config/ConfigManager.java
index e9f2fd2..e47ad46 100644
--- a/src/main/java/dev/loki/lorep/config/ConfigManager.java
+++ b/src/main/java/dev/loki/lorep/config/ConfigManager.java
@@ -2,21 +2,12 @@ package dev.loki.lorep.config;
import dev.loki.lorep.LorepPlugin;
import org.bukkit.ChatColor;
-import org.bukkit.Material;
-import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
-import org.bukkit.configuration.file.YamlConfiguration;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
public class ConfigManager {
private final LorepPlugin plugin;
private FileConfiguration config;
- private FileConfiguration messages;
public ConfigManager(LorepPlugin plugin) {
this.plugin = plugin;
@@ -27,15 +18,6 @@ public class ConfigManager {
plugin.saveDefaultConfig();
plugin.reloadConfig();
this.config = plugin.getConfig();
- loadMessages();
- }
-
- private void loadMessages() {
- File messagesFile = new File(plugin.getDataFolder(), "messages.yml");
- if (!messagesFile.exists()) {
- plugin.saveResource("messages.yml", false);
- }
- this.messages = YamlConfiguration.loadConfiguration(messagesFile);
}
public String getWebhookUrl() {
@@ -75,61 +57,14 @@ public class ConfigManager {
}
public String getMessage(String key) {
- String message = messages.getString(key);
- if (message == null) {
- message = config.getString("messages." + key);
- }
- if (message == null) {
- message = "&cMessage not found: " + key;
- }
+ String message = config.getString("messages." + key, "&cMessage not found: " + key);
return ChatColor.translateAlternateColorCodes('&', message);
}
- public GuiItemConfig getGuiItem(String path) {
- ConfigurationSection section = config.getConfigurationSection("gui.items." + path);
- if (section == null) {
- return null;
- }
-
- Material material = Material.getMaterial(section.getString("material", "STONE").toUpperCase());
- if (material == null) {
- material = Material.STONE;
- }
-
- int customModelData = section.contains("custom-model-data") ? section.getInt("custom-model-data") : GuiItemConfig.NO_CUSTOM_MODEL;
-
- String displayName = section.contains("name") ? ChatColor.translateAlternateColorCodes('&', section.getString("name")) : null;
-
- List lore = section.contains("lore")
- ? section.getStringList("lore").stream()
- .map(line -> ChatColor.translateAlternateColorCodes('&', line))
- .collect(Collectors.toList())
- : Collections.emptyList();
-
- List slots = section.contains("slots") ? section.getIntegerList("slots") : Collections.emptyList();
-
- boolean glowing = section.getBoolean("glowing", false);
-
- return new GuiItemConfig(material, customModelData, displayName, lore, slots, glowing);
- }
-
- public List getItemSlots(String path) {
- GuiItemConfig config = getGuiItem(path);
- if (config != null && !config.slots().isEmpty()) {
- return config.slots();
- }
- return Collections.emptyList();
- }
-
public String getMessage(String key, String... replacements) {
String message = getMessage(key);
- if (replacements == null || replacements.length == 0) {
- return message;
- }
for (int i = 0; i < replacements.length - 1; i += 2) {
- if (replacements[i] != null && replacements[i + 1] != null) {
- message = message.replace(replacements[i], replacements[i + 1]);
- }
+ message = message.replace(replacements[i], replacements[i + 1]);
}
return message;
}
diff --git a/src/main/java/dev/loki/lorep/config/GuiItemConfig.java b/src/main/java/dev/loki/lorep/config/GuiItemConfig.java
deleted file mode 100644
index e92433c..0000000
--- a/src/main/java/dev/loki/lorep/config/GuiItemConfig.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package dev.loki.lorep.config;
-
-import org.bukkit.Material;
-import org.bukkit.enchantments.Enchantment;
-import org.bukkit.inventory.ItemFlag;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.meta.ItemMeta;
-
-import java.util.List;
-
-public record GuiItemConfig(
- Material material,
- int customModelData,
- String displayName,
- List lore,
- List slots,
- boolean glowing
-) {
- public static final int NO_CUSTOM_MODEL = -1;
-
- public ItemStack createItem() {
- ItemStack item = new ItemStack(material);
- ItemMeta meta = item.getItemMeta();
- if (meta != null) {
- if (customModelData != NO_CUSTOM_MODEL) {
- meta.setCustomModelData(customModelData);
- }
- if (displayName != null) {
- meta.setDisplayName(displayName);
- }
- if (lore != null && !lore.isEmpty()) {
- meta.setLore(lore);
- }
- if (glowing) {
- meta.addEnchant(Enchantment.DURABILITY, 1, true);
- meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
- }
- item.setItemMeta(meta);
- }
- return item;
- }
-}
diff --git a/src/main/java/dev/loki/lorep/database/DatabaseManager.java b/src/main/java/dev/loki/lorep/database/DatabaseManager.java
index 48de964..2ecfeda 100644
--- a/src/main/java/dev/loki/lorep/database/DatabaseManager.java
+++ b/src/main/java/dev/loki/lorep/database/DatabaseManager.java
@@ -20,12 +20,6 @@ public interface DatabaseManager {
boolean hasReported(UUID reporterUuid, UUID targetUuid);
List getReportsPaginated(int page, int pageSize);
-
+
int getTotalReportCount();
-
- void updateReportStatus(int reportId, String status);
-
- void updateReportImage(int reportId, String imageUrl);
-
- Report getReportById(int id);
}
diff --git a/src/main/java/dev/loki/lorep/database/PostgreSQLDatabaseManager.java b/src/main/java/dev/loki/lorep/database/PostgreSQLDatabaseManager.java
index 8f2314d..12b72ad 100644
--- a/src/main/java/dev/loki/lorep/database/PostgreSQLDatabaseManager.java
+++ b/src/main/java/dev/loki/lorep/database/PostgreSQLDatabaseManager.java
@@ -3,11 +3,7 @@ package dev.loki.lorep.database;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
+import java.sql.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -40,11 +36,7 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
@Override
public void initialize() {
try {
- // Explicitly load PostgreSQL driver
- Class.forName("org.postgresql.Driver");
-
HikariConfig config = new HikariConfig();
- config.setDriverClassName("org.postgresql.Driver");
config.setJdbcUrl("jdbc:postgresql://" + host + ":" + port + "/" + database);
config.setUsername(username);
config.setPassword(password);
@@ -57,8 +49,6 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
dataSource = new HikariDataSource(config);
createTables();
logger.info("PostgreSQL database initialized: " + host + ":" + port + "/" + database);
- } catch (ClassNotFoundException e) {
- logger.log(Level.SEVERE, "PostgreSQL driver not found", e);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to initialize PostgreSQL database", e);
}
@@ -73,8 +63,6 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
target_uuid TEXT NOT NULL,
target_name TEXT NOT NULL,
reason TEXT NOT NULL,
- image_url TEXT,
- status TEXT NOT NULL DEFAULT 'PENDING',
created_at BIGINT NOT NULL
)
""";
@@ -100,8 +88,7 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
@Override
public void saveReport(Report report) {
- String sql = "INSERT INTO reports (reporter_uuid, reporter_name, target_uuid, "
- + "target_name, reason, image_url, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+ String sql = "INSERT INTO reports (reporter_uuid, reporter_name, target_uuid, target_name, reason, created_at) VALUES (?, ?, ?, ?, ?, ?)";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
@@ -110,13 +97,7 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
stmt.setString(3, report.targetUuid().toString());
stmt.setString(4, report.targetName());
stmt.setString(5, report.reason());
- if (report.imageUrl() != null) {
- stmt.setString(6, report.imageUrl());
- } else {
- stmt.setNull(6, java.sql.Types.VARCHAR);
- }
- stmt.setString(7, report.status());
- stmt.setLong(8, report.createdAt().toEpochMilli());
+ stmt.setLong(6, report.createdAt().toEpochMilli());
stmt.executeUpdate();
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to save report", e);
@@ -127,19 +108,18 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
public List getReportsForTarget(UUID targetUuid) {
String sql = "SELECT * FROM reports WHERE target_uuid = ? ORDER BY created_at DESC";
List reports = new ArrayList<>();
-
+
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, targetUuid.toString());
- try (ResultSet rs = stmt.executeQuery()) {
- while (rs.next()) {
- reports.add(mapReport(rs));
- }
+ ResultSet rs = stmt.executeQuery();
+ while (rs.next()) {
+ reports.add(mapReport(rs));
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get reports for target", e);
}
-
+
return reports;
}
@@ -147,56 +127,54 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
public List getAllReports() {
String sql = "SELECT * FROM reports ORDER BY created_at DESC";
List reports = new ArrayList<>();
-
+
try (Connection conn = dataSource.getConnection();
- PreparedStatement stmt = conn.prepareStatement(sql);
- ResultSet rs = stmt.executeQuery()) {
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ ResultSet rs = stmt.executeQuery();
while (rs.next()) {
reports.add(mapReport(rs));
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get all reports", e);
}
-
+
return reports;
}
@Override
public int getReportCount(UUID targetUuid) {
String sql = "SELECT COUNT(*) FROM reports WHERE target_uuid = ?";
-
+
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, targetUuid.toString());
- try (ResultSet rs = stmt.executeQuery()) {
- if (rs.next()) {
- return rs.getInt(1);
- }
+ ResultSet rs = stmt.executeQuery();
+ if (rs.next()) {
+ return rs.getInt(1);
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get report count", e);
}
-
+
return 0;
}
@Override
public boolean hasReported(UUID reporterUuid, UUID targetUuid) {
String sql = "SELECT COUNT(*) FROM reports WHERE reporter_uuid = ? AND target_uuid = ?";
-
+
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, reporterUuid.toString());
stmt.setString(2, targetUuid.toString());
- try (ResultSet rs = stmt.executeQuery()) {
- if (rs.next()) {
- return rs.getInt(1) > 0;
- }
+ ResultSet rs = stmt.executeQuery();
+ if (rs.next()) {
+ return rs.getInt(1) > 0;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to check if reported", e);
}
-
+
return false;
}
@@ -204,45 +182,40 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
public List getReportsPaginated(int page, int pageSize) {
String sql = "SELECT * FROM reports ORDER BY created_at DESC LIMIT ? OFFSET ?";
List reports = new ArrayList<>();
-
+
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, pageSize);
stmt.setInt(2, page * pageSize);
- try (ResultSet rs = stmt.executeQuery()) {
- while (rs.next()) {
- reports.add(mapReport(rs));
- }
+ ResultSet rs = stmt.executeQuery();
+ while (rs.next()) {
+ reports.add(mapReport(rs));
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get paginated reports", e);
}
-
+
return reports;
}
@Override
public int getTotalReportCount() {
String sql = "SELECT COUNT(*) FROM reports";
-
+
try (Connection conn = dataSource.getConnection();
- PreparedStatement stmt = conn.prepareStatement(sql);
- ResultSet rs = stmt.executeQuery()) {
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+ ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return rs.getInt(1);
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get total report count", e);
}
-
+
return 0;
}
private Report mapReport(ResultSet rs) throws SQLException {
- String imageUrl = rs.getString("image_url");
- if (rs.wasNull()) {
- imageUrl = null;
- }
return new Report(
rs.getInt("id"),
UUID.fromString(rs.getString("reporter_uuid")),
@@ -250,60 +223,7 @@ public class PostgreSQLDatabaseManager implements DatabaseManager {
UUID.fromString(rs.getString("target_uuid")),
rs.getString("target_name"),
rs.getString("reason"),
- imageUrl,
- rs.getString("status"),
Instant.ofEpochMilli(rs.getLong("created_at"))
);
}
-
- @Override
- public void updateReportStatus(int reportId, String status) {
- String sql = "UPDATE reports SET status = ? WHERE id = ?";
-
- try (Connection conn = dataSource.getConnection();
- PreparedStatement stmt = conn.prepareStatement(sql)) {
- stmt.setString(1, status);
- stmt.setInt(2, reportId);
- stmt.executeUpdate();
- } catch (SQLException e) {
- logger.log(Level.SEVERE, "Failed to update report status", e);
- }
- }
-
- @Override
- public void updateReportImage(int reportId, String imageUrl) {
- String sql = "UPDATE reports SET image_url = ? WHERE id = ?";
-
- try (Connection conn = dataSource.getConnection();
- PreparedStatement stmt = conn.prepareStatement(sql)) {
- if (imageUrl != null) {
- stmt.setString(1, imageUrl);
- } else {
- stmt.setNull(1, java.sql.Types.VARCHAR);
- }
- stmt.setInt(2, reportId);
- stmt.executeUpdate();
- } catch (SQLException e) {
- logger.log(Level.SEVERE, "Failed to update report image", e);
- }
- }
-
- @Override
- public Report getReportById(int id) {
- String sql = "SELECT * FROM reports WHERE id = ?";
-
- try (Connection conn = dataSource.getConnection();
- PreparedStatement stmt = conn.prepareStatement(sql)) {
- stmt.setInt(1, id);
- try (ResultSet rs = stmt.executeQuery()) {
- if (rs.next()) {
- return mapReport(rs);
- }
- }
- } catch (SQLException e) {
- logger.log(Level.SEVERE, "Failed to get report by id", e);
- }
-
- return null;
- }
}
diff --git a/src/main/java/dev/loki/lorep/database/Report.java b/src/main/java/dev/loki/lorep/database/Report.java
index 643e281..e1ada60 100644
--- a/src/main/java/dev/loki/lorep/database/Report.java
+++ b/src/main/java/dev/loki/lorep/database/Report.java
@@ -10,15 +10,9 @@ public record Report(
UUID targetUuid,
String targetName,
String reason,
- String imageUrl,
- String status,
Instant createdAt
) {
public Report(UUID reporterUuid, String reporterName, UUID targetUuid, String targetName, String reason) {
- this(0, reporterUuid, reporterName, targetUuid, targetName, reason, null, "PENDING", Instant.now());
- }
-
- public Report(UUID reporterUuid, String reporterName, UUID targetUuid, String targetName, String reason, String imageUrl) {
- this(0, reporterUuid, reporterName, targetUuid, targetName, reason, imageUrl, "PENDING", Instant.now());
+ this(0, reporterUuid, reporterName, targetUuid, targetName, reason, Instant.now());
}
}
diff --git a/src/main/java/dev/loki/lorep/database/SQLiteDatabaseManager.java b/src/main/java/dev/loki/lorep/database/SQLiteDatabaseManager.java
index 02434f9..2f40280 100644
--- a/src/main/java/dev/loki/lorep/database/SQLiteDatabaseManager.java
+++ b/src/main/java/dev/loki/lorep/database/SQLiteDatabaseManager.java
@@ -1,12 +1,7 @@
package dev.loki.lorep.database;
import java.io.File;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
+import java.sql.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -54,8 +49,6 @@ public class SQLiteDatabaseManager implements DatabaseManager {
target_uuid TEXT NOT NULL,
target_name TEXT NOT NULL,
reason TEXT NOT NULL,
- image_url TEXT,
- status TEXT NOT NULL DEFAULT 'PENDING',
created_at INTEGER NOT NULL
)
""";
@@ -84,8 +77,7 @@ public class SQLiteDatabaseManager implements DatabaseManager {
@Override
public void saveReport(Report report) {
- String sql = "INSERT INTO reports (reporter_uuid, reporter_name, target_uuid, "
- + "target_name, reason, image_url, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+ String sql = "INSERT INTO reports (reporter_uuid, reporter_name, target_uuid, target_name, reason, created_at) VALUES (?, ?, ?, ?, ?, ?)";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, report.reporterUuid().toString());
@@ -93,13 +85,7 @@ public class SQLiteDatabaseManager implements DatabaseManager {
stmt.setString(3, report.targetUuid().toString());
stmt.setString(4, report.targetName());
stmt.setString(5, report.reason());
- if (report.imageUrl() != null) {
- stmt.setString(6, report.imageUrl());
- } else {
- stmt.setNull(6, java.sql.Types.VARCHAR);
- }
- stmt.setString(7, report.status());
- stmt.setLong(8, report.createdAt().toEpochMilli());
+ stmt.setLong(6, report.createdAt().toEpochMilli());
stmt.executeUpdate();
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to save report", e);
@@ -110,18 +96,17 @@ public class SQLiteDatabaseManager implements DatabaseManager {
public List getReportsForTarget(UUID targetUuid) {
String sql = "SELECT * FROM reports WHERE target_uuid = ? ORDER BY created_at DESC";
List reports = new ArrayList<>();
-
- try (PreparedStatement stmt = connection.prepareStatement(sql);) {
+
+ try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, targetUuid.toString());
- try (ResultSet rs = stmt.executeQuery()) {
- while (rs.next()) {
- reports.add(mapReport(rs));
- }
+ ResultSet rs = stmt.executeQuery();
+ while (rs.next()) {
+ reports.add(mapReport(rs));
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get reports for target", e);
}
-
+
return reports;
}
@@ -129,53 +114,51 @@ public class SQLiteDatabaseManager implements DatabaseManager {
public List getAllReports() {
String sql = "SELECT * FROM reports ORDER BY created_at DESC";
List reports = new ArrayList<>();
-
- try (PreparedStatement stmt = connection.prepareStatement(sql);
- ResultSet rs = stmt.executeQuery()) {
+
+ try (PreparedStatement stmt = connection.prepareStatement(sql)) {
+ ResultSet rs = stmt.executeQuery();
while (rs.next()) {
reports.add(mapReport(rs));
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get all reports", e);
}
-
+
return reports;
}
@Override
public int getReportCount(UUID targetUuid) {
String sql = "SELECT COUNT(*) FROM reports WHERE target_uuid = ?";
-
+
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, targetUuid.toString());
- try (ResultSet rs = stmt.executeQuery()) {
- if (rs.next()) {
- return rs.getInt(1);
- }
+ ResultSet rs = stmt.executeQuery();
+ if (rs.next()) {
+ return rs.getInt(1);
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get report count", e);
}
-
+
return 0;
}
@Override
public boolean hasReported(UUID reporterUuid, UUID targetUuid) {
String sql = "SELECT COUNT(*) FROM reports WHERE reporter_uuid = ? AND target_uuid = ?";
-
+
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, reporterUuid.toString());
stmt.setString(2, targetUuid.toString());
- try (ResultSet rs = stmt.executeQuery()) {
- if (rs.next()) {
- return rs.getInt(1) > 0;
- }
+ ResultSet rs = stmt.executeQuery();
+ if (rs.next()) {
+ return rs.getInt(1) > 0;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to check if reported", e);
}
-
+
return false;
}
@@ -183,43 +166,38 @@ public class SQLiteDatabaseManager implements DatabaseManager {
public List getReportsPaginated(int page, int pageSize) {
String sql = "SELECT * FROM reports ORDER BY created_at DESC LIMIT ? OFFSET ?";
List reports = new ArrayList<>();
-
+
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setInt(1, pageSize);
stmt.setInt(2, page * pageSize);
- try (ResultSet rs = stmt.executeQuery()) {
- while (rs.next()) {
- reports.add(mapReport(rs));
- }
+ ResultSet rs = stmt.executeQuery();
+ while (rs.next()) {
+ reports.add(mapReport(rs));
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get paginated reports", e);
}
-
+
return reports;
}
@Override
public int getTotalReportCount() {
String sql = "SELECT COUNT(*) FROM reports";
-
- try (PreparedStatement stmt = connection.prepareStatement(sql);
- ResultSet rs = stmt.executeQuery()) {
+
+ try (PreparedStatement stmt = connection.prepareStatement(sql)) {
+ ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return rs.getInt(1);
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to get total report count", e);
}
-
+
return 0;
}
private Report mapReport(ResultSet rs) throws SQLException {
- String imageUrl = rs.getString("image_url");
- if (rs.wasNull()) {
- imageUrl = null;
- }
return new Report(
rs.getInt("id"),
UUID.fromString(rs.getString("reporter_uuid")),
@@ -227,57 +205,7 @@ public class SQLiteDatabaseManager implements DatabaseManager {
UUID.fromString(rs.getString("target_uuid")),
rs.getString("target_name"),
rs.getString("reason"),
- imageUrl,
- rs.getString("status"),
Instant.ofEpochMilli(rs.getLong("created_at"))
);
}
-
- @Override
- public void updateReportStatus(int reportId, String status) {
- String sql = "UPDATE reports SET status = ? WHERE id = ?";
-
- try (PreparedStatement stmt = connection.prepareStatement(sql)) {
- stmt.setString(1, status);
- stmt.setInt(2, reportId);
- stmt.executeUpdate();
- } catch (SQLException e) {
- logger.log(Level.SEVERE, "Failed to update report status", e);
- }
- }
-
- @Override
- public void updateReportImage(int reportId, String imageUrl) {
- String sql = "UPDATE reports SET image_url = ? WHERE id = ?";
-
- try (PreparedStatement stmt = connection.prepareStatement(sql)) {
- if (imageUrl != null) {
- stmt.setString(1, imageUrl);
- } else {
- stmt.setNull(1, java.sql.Types.VARCHAR);
- }
- stmt.setInt(2, reportId);
- stmt.executeUpdate();
- } catch (SQLException e) {
- logger.log(Level.SEVERE, "Failed to update report image", e);
- }
- }
-
- @Override
- public Report getReportById(int id) {
- String sql = "SELECT * FROM reports WHERE id = ?";
-
- try (PreparedStatement stmt = connection.prepareStatement(sql)) {
- stmt.setInt(1, id);
- try (ResultSet rs = stmt.executeQuery()) {
- if (rs.next()) {
- return mapReport(rs);
- }
- }
- } catch (SQLException e) {
- logger.log(Level.SEVERE, "Failed to get report by id", e);
- }
-
- return null;
- }
}
diff --git a/src/main/java/dev/loki/lorep/gui/GuiClickListener.java b/src/main/java/dev/loki/lorep/gui/GuiClickListener.java
index e7d1873..be27115 100644
--- a/src/main/java/dev/loki/lorep/gui/GuiClickListener.java
+++ b/src/main/java/dev/loki/lorep/gui/GuiClickListener.java
@@ -1,9 +1,7 @@
package dev.loki.lorep.gui;
import dev.loki.lorep.LorepPlugin;
-import dev.loki.lorep.database.Report;
import dev.loki.lorep.util.PaginationUtil;
-import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -11,119 +9,47 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
public class GuiClickListener implements Listener {
-
+
private final LorepPlugin plugin;
-
+
public GuiClickListener(LorepPlugin plugin) {
this.plugin = plugin;
}
-
+
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getInventory().getHolder() instanceof ReportGui gui)) {
return;
}
-
+
event.setCancelled(true);
-
+
if (!(event.getWhoClicked() instanceof Player player)) {
return;
}
-
- String action = gui.slotActions.get(event.getRawSlot());
-
- if (action == null) {
- return;
- }
-
- switch (action) {
- case "prev_page" -> handlePrevPage(player, gui);
- case "next_page" -> handleNextPage(player, gui);
- case "close" -> handleClose(player, gui);
- case "teleport" -> handleTeleport(player, gui);
- case "screenshot" -> handleScreenshot(player, gui);
- default -> handleDefaultAction(player, gui, action);
- }
- }
-
- private void handlePrevPage(Player player, ReportGui gui) {
- if (gui.page > 0) {
- new ReportGui(plugin, gui.page - 1).open(player);
- }
- }
-
- private void handleNextPage(Player player, ReportGui gui) {
- int totalReports = plugin.getDatabaseManager().getTotalReportCount();
- int pageSize = plugin.getConfig().getInt("gui.reports-per-page", 45);
- int totalPages = PaginationUtil.calculateTotalPages(totalReports, pageSize);
- if (gui.page < totalPages - 1) {
- new ReportGui(plugin, gui.page + 1).open(player);
- }
- }
-
- private void handleClose(Player player, ReportGui gui) {
- if (gui.detailView) {
- new ReportGui(plugin, gui.page).open(player);
- } else {
- player.closeInventory();
- }
- }
-
- private void handleTeleport(Player player, ReportGui gui) {
- Report report = gui.currentReport;
- if (report == null) {
- return;
- }
- Player target = Bukkit.getPlayer(report.targetUuid());
- if (target != null && target.isOnline()) {
- player.teleport(target.getLocation());
- player.sendMessage(plugin.getConfigManager().getMessage("teleported-to", "%player%", report.targetName()));
- } else {
- player.sendMessage(plugin.getConfigManager().getMessage("player-offline", "%player%", report.targetName()));
- }
- }
-
- private void handleScreenshot(Player player, ReportGui gui) {
- Report report = gui.currentReport;
- if (report != null && report.imageUrl() != null && !report.imageUrl().isEmpty()) {
- player.sendMessage(plugin.getConfigManager().getMessage("screenshot-link", "%url%", report.imageUrl()));
- }
- }
-
- private void handleDefaultAction(Player player, ReportGui gui, String action) {
- if (action.startsWith("status_set:")) {
- handleStatusSet(player, gui, action.substring("status_set:".length()));
- } else if (action.startsWith("report_detail:")) {
- handleReportDetail(player, gui, action.substring("report_detail:".length()));
- }
- }
-
- private void handleStatusSet(Player player, ReportGui gui, String status) {
- Report report = gui.currentReport;
- if (report == null) {
- return;
- }
- plugin.getDatabaseManager().updateReportStatus(report.id(), status);
- Report updated = plugin.getDatabaseManager().getReportById(report.id());
- if (updated != null && plugin.getDiscordWebhook() != null) {
- plugin.getDiscordWebhook().sendStatusUpdate(updated);
- }
- if (updated != null) {
- new ReportGui(plugin, gui.page).openDetail(player, updated);
- }
- }
-
- private void handleReportDetail(Player player, ReportGui gui, String idStr) {
- try {
- int reportId = Integer.parseInt(idStr);
- Report report = plugin.getDatabaseManager().getReportById(reportId);
- if (report != null) {
- new ReportGui(plugin, gui.page).openDetail(player, report);
+
+ int slot = event.getRawSlot();
+
+ // Navigation buttons are in bottom row (slots 45-53)
+ switch (slot) {
+ case 45 -> { // Previous page
+ if (gui.getPage() > 0) {
+ new ReportGui(plugin, gui.getPage() - 1).open(player);
+ }
+ }
+ case 47 -> { // Close
+ player.closeInventory();
+ }
+ case 53 -> { // Next page
+ int totalReports = plugin.getDatabaseManager().getTotalReportCount();
+ int totalPages = PaginationUtil.calculateTotalPages(totalReports, ReportGui.PAGE_SIZE);
+ if (gui.getPage() < totalPages - 1) {
+ new ReportGui(plugin, gui.getPage() + 1).open(player);
+ }
}
- } catch (NumberFormatException ignored) {
}
}
-
+
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
if (event.getInventory().getHolder() instanceof ReportGui) {
diff --git a/src/main/java/dev/loki/lorep/gui/ReportGui.java b/src/main/java/dev/loki/lorep/gui/ReportGui.java
index 301c390..04589f3 100644
--- a/src/main/java/dev/loki/lorep/gui/ReportGui.java
+++ b/src/main/java/dev/loki/lorep/gui/ReportGui.java
@@ -1,356 +1,135 @@
package dev.loki.lorep.gui;
import dev.loki.lorep.LorepPlugin;
-import dev.loki.lorep.config.GuiItemConfig;
import dev.loki.lorep.database.Report;
-import dev.loki.lorep.util.PaginationUtil;
import dev.loki.lorep.util.TimeUtil;
import org.bukkit.Bukkit;
import org.bukkit.Material;
-import org.bukkit.OfflinePlayer;
-import org.bukkit.enchantments.Enchantment;
-import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
-import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
+import org.bukkit.entity.Player;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public class ReportGui implements InventoryHolder {
-
- public static final int INVENTORY_SIZE = 54;
-
- final int page;
- boolean detailView;
- Report currentReport;
- final Map slotActions = new HashMap<>();
-
+
+ public static final int PAGE_SIZE = 45; // 5 rows for reports
+ public static final int INVENTORY_SIZE = 54; // 6 rows total
+
private final LorepPlugin plugin;
+ private final int page;
private Inventory inventory;
-
+
public ReportGui(LorepPlugin plugin, int page) {
this.plugin = plugin;
this.page = page;
- this.detailView = false;
- this.currentReport = null;
}
-
+
public void open(Player player) {
- detailView = false;
- currentReport = null;
- slotActions.clear();
-
- String title = plugin.getConfigManager().getMessage("gui-title",
- "%page%", String.valueOf(page + 1));
+ String title = plugin.getConfigManager().getMessage("gui-title", "%page%", String.valueOf(page + 1));
this.inventory = Bukkit.createInventory(this, INVENTORY_SIZE, title);
-
- int pageSize = plugin.getConfig().getInt("gui.reports-per-page", 45);
- List reports = plugin.getDatabaseManager().getReportsPaginated(page, pageSize);
+
+ List reports = plugin.getDatabaseManager().getReportsPaginated(page, PAGE_SIZE);
int totalReports = plugin.getDatabaseManager().getTotalReportCount();
- int totalPages = PaginationUtil.calculateTotalPages(totalReports, pageSize);
-
- GuiItemConfig entryConfig = plugin.getConfigManager().getGuiItem("report-entry");
-
+
+ // Add report items
for (int i = 0; i < reports.size(); i++) {
Report report = reports.get(i);
int targetReportCount = plugin.getDatabaseManager().getReportCount(report.targetUuid());
-
- ItemStack item;
- if (entryConfig != null) {
- item = entryConfig.createItem();
- if (item.getItemMeta() instanceof SkullMeta skullMeta) {
- OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(report.targetUuid());
- skullMeta.setOwningPlayer(offlinePlayer);
- item.setItemMeta(skullMeta);
- }
- ItemMeta meta = item.getItemMeta();
- if (meta != null) {
- meta.setDisplayName(replacePlaceholders(meta.getDisplayName(), report, targetReportCount));
- if (meta.getLore() != null) {
- List lore = meta.getLore();
- List newLore = new ArrayList<>();
- for (String line : lore) {
- newLore.add(replacePlaceholders(line, report, targetReportCount));
- }
- meta.setLore(newLore);
- }
- item.setItemMeta(meta);
- }
- } else {
- item = createFallbackReportItem(report, targetReportCount);
- }
-
- List slots = entryConfig != null ? entryConfig.slots() : List.of();
- if (!slots.isEmpty() && i < slots.size()) {
- int slot = slots.get(i);
- inventory.setItem(slot, item);
- slotActions.put(slot, "report_detail:" + report.id());
- } else {
- inventory.setItem(i, item);
- slotActions.put(i, "report_detail:" + report.id());
- }
+ inventory.setItem(i, createReportItem(report, targetReportCount));
}
-
- addNavigation(totalReports, totalPages);
+
+ // Add navigation buttons
+ addNavigationButtons(totalReports);
+
player.openInventory(inventory);
}
-
- public void openDetail(Player player, Report report) {
- detailView = true;
- currentReport = report;
- slotActions.clear();
-
- String title = plugin.getConfigManager().getMessage("gui-title-detail", "%id%", String.valueOf(report.id()));
- this.inventory = Bukkit.createInventory(this, INVENTORY_SIZE, title);
-
- int targetReportCount = plugin.getDatabaseManager().getReportCount(report.targetUuid());
-
- GuiItemConfig entryConfig = plugin.getConfigManager().getGuiItem("report-entry");
- ItemStack item;
- if (entryConfig != null) {
- item = entryConfig.createItem();
- if (item.getItemMeta() instanceof SkullMeta skullMeta) {
- skullMeta.setOwningPlayer(Bukkit.getOfflinePlayer(report.targetUuid()));
- item.setItemMeta(skullMeta);
- }
- ItemMeta meta = item.getItemMeta();
- if (meta != null) {
- meta.setDisplayName(replacePlaceholders(meta.getDisplayName(), report, targetReportCount));
- if (meta.getLore() != null) {
- List lore = meta.getLore();
- List newLore = new ArrayList<>();
- for (String line : lore) {
- newLore.add(replacePlaceholders(line, report, targetReportCount));
- }
- meta.setLore(newLore);
- }
- item.setItemMeta(meta);
- }
- } else {
- item = createFallbackReportItem(report, targetReportCount);
- }
- inventory.setItem(22, item);
-
- placeDetailButton("teleport", "teleport");
- if (report.imageUrl() != null && !report.imageUrl().isEmpty()) {
- placeDetailButton("screenshot", "screenshot");
- }
-
- String currentStatus = report.status() != null ? report.status() : "PENDING";
- placeStatusButton("status-pending", "status_set:PENDING", "PENDING".equals(currentStatus));
- placeStatusButton("status-reviewing", "status_set:REVIEWING", "REVIEWING".equals(currentStatus));
- placeStatusButton("status-resolved", "status_set:RESOLVED", "RESOLVED".equals(currentStatus));
- placeStatusButton("status-dismissed", "status_set:DISMISSED", "DISMISSED".equals(currentStatus));
-
- GuiItemConfig closeConfig = plugin.getConfigManager().getGuiItem("close");
- if (closeConfig != null) {
- List slots = closeConfig.slots();
- ItemStack closeItem = closeConfig.createItem();
- if (slots.isEmpty()) {
- inventory.setItem(49, closeItem);
- slotActions.put(49, "close");
- } else {
- for (int slot : slots) {
- inventory.setItem(slot, closeItem);
- slotActions.put(slot, "close");
- }
- }
- }
-
- player.openInventory(inventory);
- }
-
- private void placeDetailButton(String configPath, String action) {
- GuiItemConfig config = plugin.getConfigManager().getGuiItem(configPath);
- if (config == null) {
- return;
- }
-
- List slots = config.slots();
- ItemStack button = config.createItem();
- if (slots.isEmpty()) {
- int slot = switch (action) {
- case "teleport" -> 20;
- case "screenshot" -> 24;
- default -> 22;
- };
- inventory.setItem(slot, button);
- slotActions.put(slot, action);
- } else {
- for (int slot : slots) {
- inventory.setItem(slot, button);
- slotActions.put(slot, action);
- }
- }
- }
-
- private void placeStatusButton(String configPath, String action, boolean active) {
- GuiItemConfig config = plugin.getConfigManager().getGuiItem(configPath);
- if (config == null) {
- return;
- }
-
- List slots = config.slots();
- ItemStack button = config.createItem();
- if (active) {
- button.addUnsafeEnchantment(Enchantment.ARROW_DAMAGE, 1);
- ItemMeta meta = button.getItemMeta();
- if (meta != null) {
- meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
- button.setItemMeta(meta);
- }
- }
- if (slots.isEmpty()) {
- int slot = switch (action) {
- case "status_set:PENDING" -> 29;
- case "status_set:REVIEWING" -> 30;
- case "status_set:RESOLVED" -> 31;
- case "status_set:DISMISSED" -> 32;
- default -> 29;
- };
- inventory.setItem(slot, button);
- slotActions.put(slot, action);
- } else {
- for (int slot : slots) {
- inventory.setItem(slot, button);
- slotActions.put(slot, action);
- }
- }
- }
-
- private void addNavigation(int totalReports, int totalPages) {
- if (page > 0) {
- GuiItemConfig prevConfig = plugin.getConfigManager().getGuiItem("prev-page");
- if (prevConfig != null) {
- List slots = prevConfig.slots();
- ItemStack item = prevConfig.createItem();
- if (slots.isEmpty()) {
- inventory.setItem(45, item);
- slotActions.put(45, "prev_page");
- } else {
- for (int slot : slots) {
- inventory.setItem(slot, item);
- slotActions.put(slot, "prev_page");
- }
- }
- }
- }
-
- GuiItemConfig pageInfoConfig = plugin.getConfigManager().getGuiItem("page-info");
- if (pageInfoConfig != null) {
- List slots = pageInfoConfig.slots();
- ItemStack item = pageInfoConfig.createItem();
- ItemMeta meta = item.getItemMeta();
- if (meta != null) {
- meta.setDisplayName(meta.getDisplayName()
- .replace("%page%", String.valueOf(page + 1))
- .replace("%total%", String.valueOf(Math.max(1, totalPages)))
- .replace("%total_reports%", String.valueOf(totalReports)));
- if (meta.getLore() != null) {
- List lore = meta.getLore();
- List newLore = new ArrayList<>();
- for (String line : lore) {
- newLore.add(line
- .replace("%page%", String.valueOf(page + 1))
- .replace("%total%", String.valueOf(Math.max(1, totalPages)))
- .replace("%total_reports%", String.valueOf(totalReports)));
- }
- meta.setLore(newLore);
- }
- item.setItemMeta(meta);
- }
- if (slots.isEmpty()) {
- inventory.setItem(49, item);
- } else {
- for (int slot : slots) {
- inventory.setItem(slot, item);
- }
- }
- }
-
- if (page < totalPages - 1) {
- GuiItemConfig nextConfig = plugin.getConfigManager().getGuiItem("next-page");
- if (nextConfig != null) {
- List slots = nextConfig.slots();
- ItemStack item = nextConfig.createItem();
- if (slots.isEmpty()) {
- inventory.setItem(53, item);
- slotActions.put(53, "next_page");
- } else {
- for (int slot : slots) {
- inventory.setItem(slot, item);
- slotActions.put(slot, "next_page");
- }
- }
- }
- }
-
- GuiItemConfig closeConfig = plugin.getConfigManager().getGuiItem("close");
- if (closeConfig != null) {
- List slots = closeConfig.slots();
- ItemStack item = closeConfig.createItem();
- if (slots.isEmpty()) {
- inventory.setItem(47, item);
- slotActions.put(47, "close");
- } else {
- for (int slot : slots) {
- inventory.setItem(slot, item);
- slotActions.put(slot, "close");
- }
- }
- }
- }
-
- private String replacePlaceholders(String text, Report report, int reportCount) {
- String statusDisplay = plugin.getConfigManager().getMessage("status-" + report.status().toLowerCase());
- return text
- .replace("%target_name%", report.targetName() != null ? report.targetName() : "")
- .replace("%reporter_name%", report.reporterName() != null ? report.reporterName() : "")
- .replace("%reason%", report.reason() != null ? report.reason() : "")
- .replace("%time%", TimeUtil.formatTimeAgo(report.createdAt()))
- .replace("%status%", statusDisplay)
- .replace("%report_count%", String.valueOf(reportCount));
- }
-
- private ItemStack createFallbackReportItem(Report report, int totalReports) {
+
+ private ItemStack createReportItem(Report report, int totalReports) {
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
SkullMeta meta = (SkullMeta) head.getItemMeta();
+
if (meta != null) {
+ // Set skull owner for skin
meta.setOwningPlayer(Bukkit.getOfflinePlayer(report.targetUuid()));
+
+ // Display name
meta.setDisplayName("§c" + report.targetName());
+
+ // Lore with report info
List lore = new ArrayList<>();
lore.add("§7");
- lore.add("§eBy: §f" + report.reporterName());
- lore.add("§eReason: §f" + report.reason());
- lore.add("§eTime: §f" + TimeUtil.formatTimeAgo(report.createdAt()));
+ lore.add("§eОтправитель: §f" + report.reporterName());
+ lore.add("§ePричина: §f" + report.reason());
+ lore.add("§eВремя: §f" + TimeUtil.formatTimeAgo(report.createdAt().toEpochMilli()));
lore.add("§7");
- lore.add("§eReports: §c" + totalReports);
+ lore.add("§eВсего репортов: §c" + totalReports);
+
meta.setLore(lore);
head.setItemMeta(meta);
}
+
return head;
}
+ private void addNavigationButtons(int totalReports) {
+ int totalPages = (int) Math.ceil((double) totalReports / PAGE_SIZE);
+
+ // Previous page button (slot 45)
+ if (page > 0) {
+ ItemStack prevButton = new ItemStack(Material.ARROW);
+ ItemMeta prevMeta = prevButton.getItemMeta();
+ if (prevMeta != null) {
+ prevMeta.setDisplayName("§a← Предыдущая страница");
+ prevButton.setItemMeta(prevMeta);
+ }
+ inventory.setItem(45, prevButton);
+ }
+
+ // Page info (slot 49)
+ ItemStack pageInfo = new ItemStack(Material.PAPER);
+ ItemMeta pageMeta = pageInfo.getItemMeta();
+ if (pageMeta != null) {
+ pageMeta.setDisplayName("§eСтраница " + (page + 1) + " из " + Math.max(1, totalPages));
+ List lore = new ArrayList<>();
+ lore.add("§7Всего репортов: " + totalReports);
+ pageMeta.setLore(lore);
+ pageInfo.setItemMeta(pageMeta);
+ }
+ inventory.setItem(49, pageInfo);
+
+ // Next page button (slot 53)
+ if (page < totalPages - 1) {
+ ItemStack nextButton = new ItemStack(Material.ARROW);
+ ItemMeta nextMeta = nextButton.getItemMeta();
+ if (nextMeta != null) {
+ nextMeta.setDisplayName("§aСледующая страница →");
+ nextButton.setItemMeta(nextMeta);
+ }
+ inventory.setItem(53, nextButton);
+ }
+
+ // Close button (slot 47)
+ ItemStack closeButton = new ItemStack(Material.BARRIER);
+ ItemMeta closeMeta = closeButton.getItemMeta();
+ if (closeMeta != null) {
+ closeMeta.setDisplayName("§cЗакрыть");
+ closeButton.setItemMeta(closeMeta);
+ }
+ inventory.setItem(47, closeButton);
+ }
+
public int getPage() {
return page;
}
-
- public boolean isDetailView() {
- return detailView;
- }
-
- public Report getCurrentReport() {
- return currentReport;
- }
-
+
@Override
public Inventory getInventory() {
return inventory;
}
+
}
diff --git a/src/main/java/dev/loki/lorep/util/MessageUtil.java b/src/main/java/dev/loki/lorep/util/MessageUtil.java
index 290097f..60411d0 100644
--- a/src/main/java/dev/loki/lorep/util/MessageUtil.java
+++ b/src/main/java/dev/loki/lorep/util/MessageUtil.java
@@ -5,9 +5,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class MessageUtil {
-
- private MessageUtil() {}
-
+
private static final String PREFIX = "§8[§clorep§8] §r";
public static String colorize(String message) {
diff --git a/src/main/java/dev/loki/lorep/util/PaginationUtil.java b/src/main/java/dev/loki/lorep/util/PaginationUtil.java
index c1a472d..95eb3eb 100644
--- a/src/main/java/dev/loki/lorep/util/PaginationUtil.java
+++ b/src/main/java/dev/loki/lorep/util/PaginationUtil.java
@@ -1,27 +1,13 @@
package dev.loki.lorep.util;
public class PaginationUtil {
-
- private PaginationUtil() {}
-
+
public static int calculateTotalPages(int totalItems, int pageSize) {
- if (pageSize <= 0) {
- throw new IllegalArgumentException("pageSize must be positive");
- }
- if (totalItems <= 0) {
- return 1;
- }
+ if (totalItems <= 0) return 1;
return (int) Math.ceil((double) totalItems / pageSize);
}
public static int calculateStartIndex(int page, int pageSize) {
- if (page < 0 || pageSize <= 0) {
- return 0;
- }
- // Check for overflow
- if (page > Integer.MAX_VALUE / pageSize) {
- return Integer.MAX_VALUE;
- }
return page * pageSize;
}
diff --git a/src/main/java/dev/loki/lorep/util/TimeUtil.java b/src/main/java/dev/loki/lorep/util/TimeUtil.java
index 989b3e5..00b9a12 100644
--- a/src/main/java/dev/loki/lorep/util/TimeUtil.java
+++ b/src/main/java/dev/loki/lorep/util/TimeUtil.java
@@ -4,70 +4,62 @@ import java.time.Duration;
import java.time.Instant;
public class TimeUtil {
-
- private TimeUtil() {}
-
- private static final long SECONDS_PER_MINUTE = 60;
- private static final long MINUTES_PER_HOUR = 60;
- private static final long HOURS_PER_DAY = 24;
- private static final long DAYS_PER_MONTH = 30;
- private static final long MONTHS_PER_YEAR = 12;
-
+
public static String formatTimeAgo(long epochMillis) {
return formatTimeAgo(Instant.ofEpochMilli(epochMillis));
}
-
+
public static String formatTimeAgo(Instant instant) {
Duration duration = Duration.between(instant, Instant.now());
-
+
long seconds = duration.getSeconds();
-
- if (seconds < SECONDS_PER_MINUTE) {
+
+ if (seconds < 60) {
return seconds + " сек. назад";
}
-
- long minutes = seconds / SECONDS_PER_MINUTE;
- if (minutes < MINUTES_PER_HOUR) {
+
+ long minutes = seconds / 60;
+ if (minutes < 60) {
return minutes + " мин. назад";
}
-
- long hours = minutes / MINUTES_PER_HOUR;
- if (hours < HOURS_PER_DAY) {
+
+ long hours = minutes / 60;
+ if (hours < 24) {
return hours + " ч. назад";
}
-
- long days = hours / HOURS_PER_DAY;
- if (days < DAYS_PER_MONTH) {
+
+ long days = hours / 24;
+ if (days < 30) {
return days + " дн. назад";
}
-
- long months = days / DAYS_PER_MONTH;
- if (months < MONTHS_PER_YEAR) {
+
+ long months = days / 30;
+ if (months < 12) {
return months + " мес. назад";
}
-
- long years = months / MONTHS_PER_YEAR;
+
+ long years = months / 12;
return years + " г. назад";
}
-
+
public static String formatDuration(Duration duration) {
long seconds = duration.getSeconds();
-
- if (seconds < SECONDS_PER_MINUTE) {
+
+ if (seconds < 60) {
return seconds + " секунд";
}
-
- long minutes = seconds / SECONDS_PER_MINUTE;
- if (minutes < MINUTES_PER_HOUR) {
+
+ long minutes = seconds / 60;
+ if (minutes < 60) {
return minutes + " минут";
}
-
- long hours = minutes / MINUTES_PER_HOUR;
- if (hours < HOURS_PER_DAY) {
+
+ long hours = minutes / 60;
+ if (hours < 24) {
return hours + " часов";
}
-
- long days = hours / HOURS_PER_DAY;
+
+ long days = hours / 24;
return days + " дней";
}
}
diff --git a/src/main/java/dev/loki/lorep/webhook/DiscordWebhook.java b/src/main/java/dev/loki/lorep/webhook/DiscordWebhook.java
index 95832a9..dbf32c3 100644
--- a/src/main/java/dev/loki/lorep/webhook/DiscordWebhook.java
+++ b/src/main/java/dev/loki/lorep/webhook/DiscordWebhook.java
@@ -1,16 +1,7 @@
package dev.loki.lorep.webhook;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
import dev.loki.lorep.database.Report;
-import okhttp3.Call;
-import okhttp3.Callback;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
+import okhttp3.*;
import java.io.IOException;
import java.time.ZoneId;
@@ -19,42 +10,42 @@ import java.util.logging.Level;
import java.util.logging.Logger;
public class DiscordWebhook {
-
+
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
private static final DateTimeFormatter FORMATTER = DateTimeFormatter
.ofPattern("dd.MM.yyyy HH:mm:ss")
.withZone(ZoneId.systemDefault());
-
+
private final String webhookUrl;
private final Logger logger;
private final OkHttpClient client;
-
+
public DiscordWebhook(String webhookUrl, Logger logger) {
this.webhookUrl = webhookUrl;
this.logger = logger;
this.client = new OkHttpClient();
}
-
+
public void sendReport(Report report) {
if (webhookUrl == null || webhookUrl.isEmpty()) {
logger.warning("Discord webhook URL is not configured");
return;
}
-
+
String json = buildPayload(report);
-
+
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(webhookUrl)
.post(body)
.build();
-
+
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
logger.log(Level.WARNING, "Failed to send Discord webhook", e);
}
-
+
@Override
public void onResponse(Call call, Response response) {
if (!response.isSuccessful()) {
@@ -64,168 +55,33 @@ public class DiscordWebhook {
}
});
}
-
- public void sendStatusUpdate(Report report) {
- if (webhookUrl == null || webhookUrl.isEmpty()) {
- logger.warning("Discord webhook URL is not configured");
- return;
- }
-
- String json = buildStatusPayload(report);
-
- RequestBody body = RequestBody.create(json, JSON);
- Request request = new Request.Builder()
- .url(webhookUrl)
- .post(body)
- .build();
-
- client.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- logger.log(Level.WARNING, "Failed to send status update webhook", e);
- }
-
- @Override
- public void onResponse(Call call, Response response) {
- if (!response.isSuccessful()) {
- logger.warning("Status update webhook returned: " + response.code());
- }
- response.close();
- }
- });
- }
-
+
public String buildPayload(Report report) {
- JsonObject embed = new JsonObject();
- embed.addProperty("title", "\uD83D\uDCE2 Новый репорт #" + report.id());
- embed.addProperty("color", getColorForStatus(report.status()));
-
- JsonObject thumbnail = new JsonObject();
- thumbnail.addProperty("url", "https://mc-heads.net/avatar/" + report.targetUuid());
- embed.add("thumbnail", thumbnail);
-
- JsonObject author = new JsonObject();
- author.addProperty("name", report.reporterName());
- author.addProperty("icon_url", "https://mc-heads.net/avatar/" + report.reporterUuid());
- embed.add("author", author);
-
- JsonArray fields = new JsonArray();
-
- JsonObject targetField = new JsonObject();
- targetField.addProperty("name", "\uD83D\uDC64 Нарушитель");
- targetField.addProperty("value", report.targetName());
- targetField.addProperty("inline", true);
- fields.add(targetField);
-
- JsonObject reporterField = new JsonObject();
- reporterField.addProperty("name", "\uD83D\uDCDD Отправитель");
- reporterField.addProperty("value", report.reporterName());
- reporterField.addProperty("inline", true);
- fields.add(reporterField);
-
- JsonObject reasonField = new JsonObject();
- reasonField.addProperty("name", "\uD83D\uDCCB Причина");
- reasonField.addProperty("value", report.reason());
- reasonField.addProperty("inline", false);
- fields.add(reasonField);
-
- JsonObject statusField = new JsonObject();
- statusField.addProperty("name", "\uD83C\uDFF7 Статус");
- statusField.addProperty("value", getStatusDisplay(report.status()));
- statusField.addProperty("inline", true);
- fields.add(statusField);
-
- JsonObject timeField = new JsonObject();
- timeField.addProperty("name", "\uD83D\uDD50 Время");
- timeField.addProperty("value", FORMATTER.format(report.createdAt()));
- timeField.addProperty("inline", true);
- fields.add(timeField);
-
- embed.add("fields", fields);
-
- if (report.imageUrl() != null && !report.imageUrl().isEmpty()) {
- JsonObject image = new JsonObject();
- image.addProperty("url", report.imageUrl());
- embed.add("image", image);
- }
-
- JsonObject footer = new JsonObject();
- footer.addProperty("text", "LoReport \u2022 " + FORMATTER.format(report.createdAt()));
- embed.add("footer", footer);
-
- embed.addProperty("timestamp", report.createdAt().toString());
-
- JsonObject root = new JsonObject();
- JsonArray embeds = new JsonArray();
- embeds.add(embed);
- root.add("embeds", embeds);
-
- return new GsonBuilder().create().toJson(root);
+ String timestamp = FORMATTER.format(report.createdAt());
+
+ return String.format("""
+ {
+ "embeds": [{
+ "title": "📢 Новый репорт",
+ "color": 16711680,
+ "fields": [
+ {"name": "👤 Нарушитель", "value": "%s", "inline": true},
+ {"name": "📝 Отправитель", "value": "%s", "inline": true},
+ {"name": "📋 Причина", "value": "%s", "inline": false},
+ {"name": "🕐 Время", "value": "%s", "inline": false}
+ ]
+ }]
+ }
+ """,
+ escapeJson(report.targetName()),
+ escapeJson(report.reporterName()),
+ escapeJson(report.reason()),
+ timestamp
+ );
}
-
- private String buildStatusPayload(Report report) {
- JsonObject embed = new JsonObject();
- embed.addProperty("title", "\uD83D\uDD04 Статус репорта #" + report.id() + " изменён");
- embed.addProperty("color", getColorForStatus(report.status()));
-
- JsonObject thumbnail = new JsonObject();
- thumbnail.addProperty("url", "https://mc-heads.net/avatar/" + report.targetUuid());
- embed.add("thumbnail", thumbnail);
-
- JsonArray fields = new JsonArray();
-
- JsonObject targetField = new JsonObject();
- targetField.addProperty("name", "\uD83D\uDC64 Нарушитель");
- targetField.addProperty("value", report.targetName());
- targetField.addProperty("inline", true);
- fields.add(targetField);
-
- JsonObject statusField = new JsonObject();
- statusField.addProperty("name", "\uD83C\uDFF7 Новый статус");
- statusField.addProperty("value", getStatusDisplay(report.status()));
- statusField.addProperty("inline", false);
- fields.add(statusField);
-
- embed.add("fields", fields);
-
- JsonObject footer = new JsonObject();
- footer.addProperty("text", "LoReport \u2022 " + FORMATTER.format(report.createdAt()));
- embed.add("footer", footer);
-
- embed.addProperty("timestamp", report.createdAt().toString());
-
- JsonObject root = new JsonObject();
- JsonArray embeds = new JsonArray();
- embeds.add(embed);
- root.add("embeds", embeds);
-
- return new GsonBuilder().create().toJson(root);
- }
-
- private int getColorForStatus(String status) {
- return switch (status != null ? status.toUpperCase() : "PENDING") {
- case "PENDING" -> 0xFF0000;
- case "REVIEWING" -> 0xFFAA00;
- case "RESOLVED" -> 0x00FF00;
- case "DISMISSED" -> 0x808080;
- default -> 0xFF0000;
- };
- }
-
- private String getStatusDisplay(String status) {
- return switch (status != null ? status.toUpperCase() : "PENDING") {
- case "PENDING" -> "\u23F3 Ожидает";
- case "REVIEWING" -> "\uD83D\uDD0D На рассмотрении";
- case "RESOLVED" -> "\u2705 Решён";
- case "DISMISSED" -> "\u274C Отклонён";
- default -> "\u2753 Неизвестно";
- };
- }
-
+
private String escapeJson(String text) {
- if (text == null) {
- return "";
- }
+ if (text == null) return "";
return text
.replace("\\", "\\\\")
.replace("\"", "\\\"")
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 2c4508a..9907501 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,9 +1,9 @@
-# Discord webhook URL for report notifications
+# Discord Webhook URL
webhook-url: ""
-# Database configuration
+# Database settings
database:
- # Database type: "sqlite" or "postgresql"
+ # Type: sqlite or postgresql
type: "sqlite"
# SQLite settings
@@ -14,150 +14,22 @@ database:
postgresql:
host: "localhost"
port: 5432
- database: "loreport"
- username: "loreport"
+ database: "lorep"
+ username: "lorep"
password: "password"
pool-size: 10
-# GUI settings
-gui:
- # Reports per page in GUI
- reports-per-page: 45
- # Auto-refresh interval in ticks (20 ticks = 1 second)
- auto-refresh: 100
- # Show player heads in GUI
- show-player-heads: true
- # GUI item definitions — customize materials, textures, and positions
- # Set custom-model-data to your resource pack model ID (-1 to disable)
- # Use slots to make an item span multiple slots (empty = default position)
- items:
- prev-page:
- material: ARROW
- custom-model-data: -1
- name: "&a← Previous page"
- lore: []
- slots: [45]
- glowing: false
- next-page:
- material: ARROW
- custom-model-data: -1
- name: "&aNext page →"
- lore: []
- slots: [53]
- glowing: false
- page-info:
- material: PAPER
- custom-model-data: -1
- name: "&ePage %page% of %total%"
- lore:
- - "&7Total reports: %total_reports%"
- slots: [49]
- glowing: false
- close:
- material: BARRIER
- custom-model-data: -1
- name: "&cClose"
- lore: []
- slots: [47]
- glowing: false
- report-entry:
- material: PLAYER_HEAD
- custom-model-data: -1
- name: "&c%target_name%"
- lore:
- - "&7"
- - "&eBy: &f%reporter_name%"
- - "&eReason: &f%reason%"
- - "&eTime: &f%time%"
- - "&eStatus: &f%status%"
- - "&7"
- - "&eReports: &c%report_count%"
- slots: []
- glowing: false
- status-pending:
- material: RED_STAINED_GLASS_PANE
- custom-model-data: -1
- name: "&cPending"
- lore: []
- slots: []
- glowing: false
- status-reviewing:
- material: YELLOW_STAINED_GLASS_PANE
- custom-model-data: -1
- name: "&eReviewing"
- lore: []
- slots: []
- glowing: true
- status-resolved:
- material: GREEN_STAINED_GLASS_PANE
- custom-model-data: -1
- name: "&aResolved"
- lore: []
- slots: []
- glowing: false
- status-dismissed:
- material: GRAY_STAINED_GLASS_PANE
- custom-model-data: -1
- name: "&7Dismissed"
- lore: []
- slots: []
- glowing: false
- teleport:
- material: ENDER_PEARL
- custom-model-data: -1
- name: "&bTeleport to player"
- lore: []
- slots: []
- glowing: false
- screenshot:
- material: MAP
- custom-model-data: -1
- name: "&aScreenshot"
- lore: []
- slots: []
- glowing: false
-
-# Limits
-limits:
- # Maximum reason length in characters
- max-reason-length: 100
- # Cooldown between reports (seconds)
- report-cooldown: 30
- # Max reports per player from the same reporter
- max-reports-per-player: 3
-
-# Notifications
-notifications:
- # Notify admins about new reports
- notify-admins: true
- # Admin notification sound
- admin-notification-sound: "BLOCK_NOTE_BLOCK_PLING"
- # Notify reporter when report is handled
- notify-reporter: true
-
-# Messages (override defaults from messages.yml)
+# Messages
messages:
- report-sent: "&aReport sent successfully!"
- already-reported: "&cYou have already reported this player!"
- self-report: "&cYou cannot report yourself!"
- player-not-found: "&cPlayer not found!"
- no-permission: "&cYou don't have permission!"
- usage: "&eUsage: /report [screenshot_url] | /report gui | /report stats | /report image "
- no-reports: "&eThis player has no reports."
- stats-header: "&6=== Report stats: %player% ==="
- stats-count: "&eTotal reports: &f%count%"
- stats-last-online: "&eLast online: &f%time%"
- stats-report-entry: "&7- %reason% &8(%time% ago)"
- gui-title: "Reports - Page %page%"
- cooldown: "&cPlease wait %time% seconds before your next report!"
- reason-too-long: "&cReason too long! Max %max% characters."
- max-reports-reached: "&cYou've reached the max reports for this player!"
- admin-notification: "&6[LoReport] &eNew report from &f%reporter% &eon &f%reported%: &7%reason%"
- report-status-changed: "&aReport #%id% status changed to: %status%"
- report-image-added: "&aScreenshot added to report #%id%"
- report-image-usage: "&eUsage: /report [screenshot_url]"
- report-image-url-usage: "&eUsage: /report image "
- status-pending: "&cPending"
- status-reviewing: "&eReviewing"
- status-resolved: "&aResolved"
- status-dismissed: "&7Dismissed"
+ report-sent: "&aРепорт успешно отправлен!"
+ already-reported: "&cВы уже отправляли репорт на этого игрока!"
+ self-report: "&cВы не можете отправить репорт на себя!"
+ player-not-found: "&cИгрок не найден!"
+ no-permission: "&cУ вас нет прав на эту команду!"
+ usage: "&eИспользование: /report <ник> <причина>"
+ no-reports: "&eУ этого игрока нет репортов."
+ stats-header: "&6=== Статистика репортов: %player% ==="
+ stats-count: "&eВсего репортов: &f%count%"
+ stats-last-online: "&eПоследний онлайн: &f%time%"
+ stats-report-entry: "&7- %reason% &8(%time% назад)"
+ gui-title: "Репорты - Страница %page%"
diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml
deleted file mode 100644
index 1d3d456..0000000
--- a/src/main/resources/messages.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-# LoReport messages
-# You can edit these to change plugin messages without recompiling
-# Color codes: &0-9, &a-f, &k-o, &r
-
-# General
-console-only: "&cThis command is for players only!"
-no-permission: "&cYou don't have permission to use this command!"
-usage: "&eUsage: /report [screenshot_url] | /report gui | /report stats | /report image "
-
-# Reporting
-report-sent: "&aReport sent successfully!"
-already-reported: "&cYou have already reported this player!"
-self-report: "&cYou cannot report yourself!"
-player-not-found: "&cPlayer not found!"
-reason-too-long: "&cReason is too long! Maximum %max% characters."
-max-reports-reached: "&cYou have reached the maximum number of reports for this player!"
-cooldown: "&cPlease wait %time% seconds before your next report!"
-invalid-report-id: "&cInvalid report ID."
-report-not-found: "&cReport with this ID not found."
-report-image-updated: "&aImage for report #%id% updated."
-
-# Admin notifications
-admin-notification: "&6[LoReport] &eNew report from &f%reporter% &eon &f%reported%: &7%reason%"
-
-# Report status
-report-status-changed: "&aReport #%id% status changed to: %status%"
-report-image-added: "&aScreenshot added to report #%id%"
-report-image-usage: "&eUsage: /report [screenshot_url]"
-report-image-url-usage: "&eUsage: /report image "
-status-pending: "&cPending"
-status-reviewing: "&eReviewing"
-status-resolved: "&aResolved"
-status-dismissed: "&7Dismissed"
-
-# GUI
-gui-title: "Reports - Page %page%"
-gui-title-detail: "&8Report #%id%"
-teleported-to: "&aTeleported to %player%"
-player-offline: "&cPlayer %player% is offline"
-screenshot-link: "&aScreenshot: &e%url%"
-
-# Stats
-online-status: "&aOnline now"
-no-reports: "&eThis player has no reports."
-stats-header: "&6=== Report stats: %player% ==="
-stats-count: "&eTotal reports: &f%count%"
-stats-last-online: "&eLast online: &f%time%"
-stats-report-entry: "&7- %reason% &8(%time% ago)"
-stats-recent-header: "&7Recent reports:"
-stats-more-reports: "&7... and %count% more reports"
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 0932beb..347666e 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,17 +1,15 @@
-name: Loreport
+name: lorep
version: ${version}
main: dev.loki.lorep.LorepPlugin
-api-version: '1.19'
-author: loki5512344
-description: Modern player report system with Discord integration and GUI
-website: https://codeberg.org/loki5512344/Loreport
+api-version: '1.21'
+author: loki
+description: Player report system with Discord integration
commands:
report:
description: Report a player or manage reports
- usage: /report [image_url] | /report gui | /report stats | /report image
+ usage: /report | /report gui | /report stats
permission: lorep.report
- aliases: [rep, reports]
permissions:
lorep.report:
@@ -20,9 +18,3 @@ permissions:
lorep.admin:
description: Allows access to admin commands (gui, stats)
default: op
- lorep.*:
- description: Gives access to all Loreport permissions
- default: false
- children:
- lorep.report: true
- lorep.admin: true
diff --git a/src/test/java/dev/loki/lorep/database/DatabaseManagerTest.java b/src/test/java/dev/loki/lorep/database/DatabaseManagerTest.java
index f637dfa..11ed531 100644
--- a/src/test/java/dev/loki/lorep/database/DatabaseManagerTest.java
+++ b/src/test/java/dev/loki/lorep/database/DatabaseManagerTest.java
@@ -65,7 +65,7 @@ public class DatabaseManagerTest {
Assume.that(!reporterUuid.equals(targetUuid));
Report report = new Report(
- reporterUuid, "Reporter", targetUuid, "Target", reason
+ 0, reporterUuid, "Reporter", targetUuid, "Target", reason, Instant.now()
);
dbManager.saveReport(report);
@@ -95,7 +95,7 @@ public class DatabaseManagerTest {
"Should not have reported before creating report");
Report report = new Report(
- reporterUuid, "Reporter", targetUuid, "Target", "Test reason"
+ 0, reporterUuid, "Reporter", targetUuid, "Target", "Test reason", Instant.now()
);
dbManager.saveReport(report);
@@ -121,7 +121,7 @@ public class DatabaseManagerTest {
String targetName = "Target_" + targetUuid.toString().substring(0, 8);
Instant createdAt = Instant.now();
- Report original = new Report(0, reporterUuid, reporterName, targetUuid, targetName, reason, null, "PENDING", createdAt);
+ Report original = new Report(0, reporterUuid, reporterName, targetUuid, targetName, reason, createdAt);
dbManager.saveReport(original);
// Close and reopen database
@@ -151,10 +151,9 @@ public class DatabaseManagerTest {
UUID targetUuid = UUID.randomUUID();
for (int i = 0; i < count; i++) {
- Instant reportTime = Instant.now().minusSeconds(count - i);
Report report = new Report(
0, UUID.randomUUID(), "Reporter" + i, targetUuid, "Target",
- "Reason " + i, null, "PENDING", reportTime
+ "Reason " + i, Instant.now().minusSeconds(count - i)
);
dbManager.saveReport(report);
}
@@ -181,8 +180,8 @@ public class DatabaseManagerTest {
for (int i = 0; i < count; i++) {
Report report = new Report(
- UUID.randomUUID(), "Reporter" + i, targetUuid, "Target",
- "Reason " + i
+ 0, UUID.randomUUID(), "Reporter" + i, targetUuid, "Target",
+ "Reason " + i, Instant.now()
);
dbManager.saveReport(report);
}
diff --git a/src/test/java/dev/loki/lorep/webhook/DiscordWebhookTest.java b/src/test/java/dev/loki/lorep/webhook/DiscordWebhookTest.java
index e787ab1..dc5d0d6 100644
--- a/src/test/java/dev/loki/lorep/webhook/DiscordWebhookTest.java
+++ b/src/test/java/dev/loki/lorep/webhook/DiscordWebhookTest.java
@@ -33,11 +33,13 @@ public class DiscordWebhookTest {
DiscordWebhook webhook = new DiscordWebhook("https://example.com/webhook", logger);
Report report = new Report(
+ 1,
UUID.randomUUID(),
reporterName,
UUID.randomUUID(),
targetName,
- reason
+ reason,
+ Instant.now()
);
String payload = webhook.buildPayload(report);
@@ -93,9 +95,9 @@ public class DiscordWebhookTest {
DiscordWebhook webhook = new DiscordWebhook("https://example.com/webhook", logger);
Report report = new Report(
- UUID.randomUUID(), "TestReporter",
+ 1, UUID.randomUUID(), "TestReporter",
UUID.randomUUID(), "TestTarget",
- "Test reason"
+ "Test reason", Instant.now()
);
String payload = webhook.buildPayload(report);