commit 0a7b382cdb3f1d7fae5f20acdaa4c0c262909c19 Author: Loki Date: Fri Dec 19 12:07:40 2025 +0100 Initial project setup with Gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d51f87b --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Gradle +.gradle/ +build/ +!gradle/wrapper/gradle-wrapper.jar + +# IDE +.idea/ +*.iml +*.ipr +*.iws +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Java +*.class +*.jar +*.war +*.ear +hs_err_pid* + +# Logs +*.log +logs/ + +# Database +*.db +*.sqlite + +# Plugin data +plugins/lorep/ diff --git a/.kiro/specs/lorep-report-plugin/design.md b/.kiro/specs/lorep-report-plugin/design.md new file mode 100644 index 0000000..8b04999 --- /dev/null +++ b/.kiro/specs/lorep-report-plugin/design.md @@ -0,0 +1,319 @@ +# Design Document: lorep Report Plugin + +## Overview + +Плагин lorep — система репортов для Paper 1.21, написанная на Java 21. Использует Gradle для сборки, SQLite для хранения данных и интегрируется с Discord через webhooks. + +**Технологический стек:** +- Java 21 +- Paper API 1.21 +- Gradle (Kotlin DSL) +- SQLite (встроенная БД) или PostgreSQL (внешняя БД) +- HikariCP (connection pool для PostgreSQL) +- OkHttp (HTTP клиент для webhooks) + +**Git репозиторий:** https://git.lokili.xyz/loki/lorep.git + +## Architecture + +```mermaid +graph TB + subgraph Plugin + Main[LorepPlugin] + CMD[CommandManager] + GUI[GuiManager] + DB[DatabaseManager] + WH[WebhookService] + CFG[ConfigManager] + end + + subgraph External + Discord[Discord Webhook] + SQLite[(SQLite DB)] + end + + Main --> CMD + Main --> GUI + Main --> DB + Main --> WH + Main --> CFG + + CMD --> DB + CMD --> WH + GUI --> DB + WH --> Discord + DB --> SQLite + CFG --> Main +``` + +### Структура проекта + +``` +lorep/ +├── .gitignore +├── build.gradle.kts +├── settings.gradle.kts +├── src/ +│ └── main/ +│ ├── java/ +│ │ └── dev/ +│ │ └── loki/ +│ │ └── lorep/ +│ │ ├── LorepPlugin.java +│ │ ├── command/ +│ │ │ ├── ReportCommand.java +│ │ │ ├── ReportGuiCommand.java +│ │ │ └── ReportStatsCommand.java +│ │ ├── database/ +│ │ │ ├── DatabaseManager.java +│ │ │ ├── SQLiteDatabaseManager.java +│ │ │ ├── PostgreSQLDatabaseManager.java +│ │ │ └── Report.java +│ │ ├── gui/ +│ │ │ ├── ReportGui.java +│ │ │ └── GuiClickListener.java +│ │ ├── webhook/ +│ │ │ └── DiscordWebhook.java +│ │ ├── config/ +│ │ │ └── ConfigManager.java +│ │ └── util/ +│ │ ├── TimeUtil.java +│ │ └── MessageUtil.java +│ └── resources/ +│ ├── plugin.yml +│ └── config.yml +└── src/ + └── test/ + └── java/ + └── dev/ + └── loki/ + └── lorep/ + ├── database/ + │ └── DatabaseManagerTest.java + └── util/ + └── TimeUtilTest.java +``` + +## Components and Interfaces + +### 1. LorepPlugin (Main Class) + +```java +public final class LorepPlugin extends JavaPlugin { + private DatabaseManager databaseManager; + private ConfigManager configManager; + private DiscordWebhook discordWebhook; + + @Override + public void onEnable(); + + @Override + public void onDisable(); + + // Getters for managers +} +``` + +### 2. Report (Data Model) + +```java +public record Report( + int id, + UUID reporterUuid, + String reporterName, + UUID targetUuid, + String targetName, + String reason, + Instant createdAt +) {} +``` + +### 3. DatabaseManager + +```java +public interface DatabaseManager { + void initialize(); + void close(); + + void saveReport(Report report); + List getReportsForTarget(UUID targetUuid); + List getAllReports(); + int getReportCount(UUID targetUuid); + boolean hasReported(UUID reporterUuid, UUID targetUuid); + List getReportsPaginated(int page, int pageSize); +} + +// Реализации: +public class SQLiteDatabaseManager implements DatabaseManager { } +public class PostgreSQLDatabaseManager implements DatabaseManager { } +``` + +### 4. DiscordWebhook + +```java +public class DiscordWebhook { + public DiscordWebhook(String webhookUrl); + + public void sendReport(Report report); +} +``` + +### 5. ConfigManager + +```java +public class ConfigManager { + public String getWebhookUrl(); + public String getDatabasePath(); + public String getMessage(String key); +} +``` + +### 6. ReportGui + +```java +public class ReportGui { + public void open(Player player, int page); + public ItemStack createReportItem(Report report, int totalReports); +} +``` + +## Data Models + +### Report Entity + +| Field | Type | Description | +|-------|------|-------------| +| id | INTEGER | Primary key, auto-increment | +| reporter_uuid | TEXT | UUID репортера | +| reporter_name | TEXT | Имя репортера | +| target_uuid | TEXT | UUID цели | +| target_name | TEXT | Имя цели | +| reason | TEXT | Причина репорта | +| created_at | INTEGER | Unix timestamp создания | + +### SQL Schema + +```sql +CREATE TABLE IF NOT EXISTS reports ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + reporter_uuid TEXT NOT NULL, + reporter_name TEXT NOT NULL, + target_uuid TEXT NOT NULL, + target_name TEXT NOT NULL, + reason TEXT NOT NULL, + created_at INTEGER NOT NULL +); + +CREATE INDEX idx_target_uuid ON reports(target_uuid); +CREATE INDEX idx_reporter_target ON reports(reporter_uuid, target_uuid); +``` + +### Configuration (config.yml) + +```yaml +# Discord Webhook URL +webhook-url: "" + +# Database settings +database: + # Type: sqlite or postgresql + type: "sqlite" + + # SQLite settings + sqlite: + file: "reports.db" + + # PostgreSQL settings + postgresql: + host: "localhost" + port: 5432 + database: "lorep" + username: "lorep" + password: "password" + pool-size: 10 + +# Messages +messages: + report-sent: "&aРепорт успешно отправлен!" + already-reported: "&cВы уже отправляли репорт на этого игрока!" + self-report: "&cВы не можете отправить репорт на себя!" + player-not-found: "&cИгрок не найден!" + no-permission: "&cУ вас нет прав на эту команду!" + usage: "&eИспользование: /report <ник> <причина>" +``` + +## Correctness Properties + +*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* + +### Property 1: Report Creation Integrity +*For any* valid reporter UUID, target UUID, and reason string, creating a report and then querying reports for that target SHALL return a list containing the created report with matching fields. +**Validates: Requirements 1.1, 2.1** + +### Property 2: Duplicate Report Prevention +*For any* reporter-target pair, if a report already exists from that reporter to that target, attempting to create another report SHALL be rejected. +**Validates: Requirements 1.2** + +### Property 3: Report Persistence Round-Trip +*For any* set of reports saved to the database, reloading the database SHALL return the same reports with identical data. +**Validates: Requirements 2.2** + +### Property 4: Report Ordering +*For any* collection of reports with different timestamps, querying all reports SHALL return them sorted by creation timestamp in descending order (newest first). +**Validates: Requirements 2.3** + +### Property 5: Webhook Payload Completeness +*For any* report sent to the webhook, the payload SHALL contain reporter name, target name, reason, and formatted timestamp. +**Validates: Requirements 3.1** + +### Property 6: Pagination Correctness +*For any* list of N reports and page size P, requesting page K SHALL return at most P reports starting from index K*P, and the total page count SHALL equal ceil(N/P). +**Validates: Requirements 4.4** + +### Property 7: Report Count Accuracy +*For any* target UUID, the report count returned by getReportCount SHALL equal the actual number of reports in the database for that target. +**Validates: Requirements 5.1** + +### Property 8: Permission Enforcement +*For any* command execution, if the player lacks the required permission, the command SHALL be denied and return false. +**Validates: Requirements 6.1, 6.2, 6.3** + +### Property 9: Configuration Parsing +*For any* valid config.yml file, parsing SHALL correctly extract webhook URL and database path values. +**Validates: Requirements 7.1, 7.3, 7.4** + +## Error Handling + +| Scenario | Handling | +|----------|----------| +| Database connection failure | Log error, disable plugin gracefully | +| Webhook request failure | Log warning, continue operation | +| Invalid player name | Return error message to player | +| Missing config values | Use default values | +| SQL exceptions | Log error, return empty result | + +## Testing Strategy + +### Property-Based Testing Library +- **JQwik** — библиотека для property-based тестирования в Java +- Минимум 100 итераций на каждый property test + +### Unit Tests +- DatabaseManager: CRUD операции, pagination +- TimeUtil: форматирование времени +- ConfigManager: парсинг конфигурации + +### Property-Based Tests +Каждый property test должен быть аннотирован: +```java +// **Feature: lorep-report-plugin, Property 1: Report Creation Integrity** +@Property(tries = 100) +void reportCreationIntegrity(@ForAll UUID reporter, @ForAll UUID target, @ForAll String reason) { + // test implementation +} +``` + +### Integration Tests +- Полный цикл создания репорта +- GUI открытие и навигация +- Webhook отправка (с mock сервером) diff --git a/.kiro/specs/lorep-report-plugin/requirements.md b/.kiro/specs/lorep-report-plugin/requirements.md new file mode 100644 index 0000000..5c00d74 --- /dev/null +++ b/.kiro/specs/lorep-report-plugin/requirements.md @@ -0,0 +1,93 @@ +# Requirements Document + +## Introduction + +Плагин "lorep" для Paper 1.21 — система репортов игроков с интеграцией Discord webhook и базой данных. Позволяет игрокам отправлять жалобы на других игроков, а администраторам — управлять и просматривать репорты через GUI и команды. + +## Glossary + +- **Report_System**: Основная система обработки репортов плагина lorep +- **Player**: Обычный игрок сервера с базовыми правами +- **Admin**: Администратор сервера с расширенными правами +- **Reporter**: Игрок, отправляющий репорт +- **Target**: Игрок, на которого отправлен репорт +- **Webhook**: Discord webhook для отправки уведомлений +- **GUI**: Графический интерфейс в виде инвентаря Minecraft +- **Cooldown**: Ограничение на повторную отправку репорта на того же игрока + +## Requirements + +### Requirement 1: Отправка репортов игроками + +**User Story:** As a Player, I want to report other players for rule violations, so that administrators can review and take action. + +#### Acceptance Criteria + +1. WHEN a Player executes `/report [nickname] [reason]` command THEN the Report_System SHALL create a new report record with reporter name, target name, reason, and timestamp +2. WHEN a Player attempts to report a Target they have already reported THEN the Report_System SHALL reject the report and display a cooldown message +3. WHEN a Player attempts to report themselves THEN the Report_System SHALL reject the report and display an error message +4. WHEN a Player attempts to report a non-existent player THEN the Report_System SHALL reject the report and display a player not found message +5. WHEN a report is successfully created THEN the Report_System SHALL send a notification to the configured Discord webhook + +### Requirement 2: Хранение данных репортов + +**User Story:** As a system administrator, I want reports to be stored persistently, so that report history is preserved across server restarts. + +#### Acceptance Criteria + +1. WHEN a new report is created THEN the Report_System SHALL store the report in the database with reporter UUID, target UUID, reason, and creation timestamp +2. WHEN the server starts THEN the Report_System SHALL load existing reports from the database +3. WHEN querying reports THEN the Report_System SHALL return reports sorted by creation timestamp in descending order + +### Requirement 3: Discord интеграция + +**User Story:** As an Admin, I want to receive Discord notifications about new reports, so that I can respond quickly to player issues. + +#### Acceptance Criteria + +1. WHEN a new report is created THEN the Report_System SHALL send an embed message to the Discord webhook containing reporter name, target name, reason, and timestamp +2. WHEN the Discord webhook URL is not configured THEN the Report_System SHALL log a warning and continue without sending notifications +3. WHEN the Discord webhook request fails THEN the Report_System SHALL log the error and continue normal operation + +### Requirement 4: GUI для администраторов + +**User Story:** As an Admin, I want to view all reports in a graphical interface, so that I can easily browse and manage player reports. + +#### Acceptance Criteria + +1. WHEN an Admin executes `/report gui` command THEN the Report_System SHALL open an inventory GUI displaying report entries +2. WHEN displaying a report entry THEN the Report_System SHALL show a player head with the Target's skin +3. WHEN an Admin hovers over a report entry THEN the Report_System SHALL display target nickname, reporter nickname, reason, time since report creation, and total report count for the target +4. WHEN there are more reports than fit on one page THEN the Report_System SHALL provide pagination controls + +### Requirement 5: Статистика репортов + +**User Story:** As an Admin, I want to view report statistics for specific players, so that I can identify problematic players. + +#### Acceptance Criteria + +1. WHEN an Admin executes `/report stats [nickname]` command THEN the Report_System SHALL display total report count for the specified player +2. WHEN displaying stats THEN the Report_System SHALL show the player's last online timestamp +3. WHEN displaying stats THEN the Report_System SHALL list recent reports with reasons and timestamps +4. WHEN the specified player has no reports THEN the Report_System SHALL display a message indicating zero reports + +### Requirement 6: Система прав + +**User Story:** As a server owner, I want granular permission control, so that I can configure who can use which features. + +#### Acceptance Criteria + +1. WHEN a Player without `lorep.report` permission attempts to use `/report` command THEN the Report_System SHALL deny access and display a no permission message +2. WHEN a user without `lorep.admin` permission attempts to use `/report gui` command THEN the Report_System SHALL deny access and display a no permission message +3. WHEN a user without `lorep.admin` permission attempts to use `/report stats` command THEN the Report_System SHALL deny access and display a no permission message + +### Requirement 7: Конфигурация плагина + +**User Story:** As a server owner, I want to configure plugin settings, so that I can customize the plugin behavior for my server. + +#### Acceptance Criteria + +1. WHEN the plugin loads THEN the Report_System SHALL read configuration from config.yml file +2. WHEN config.yml does not exist THEN the Report_System SHALL create a default configuration file +3. WHEN configuration includes database settings THEN the Report_System SHALL use those settings for database connection +4. WHEN configuration includes webhook URL THEN the Report_System SHALL use that URL for Discord notifications diff --git a/.kiro/specs/lorep-report-plugin/tasks.md b/.kiro/specs/lorep-report-plugin/tasks.md new file mode 100644 index 0000000..770176e --- /dev/null +++ b/.kiro/specs/lorep-report-plugin/tasks.md @@ -0,0 +1,121 @@ +# Implementation Plan + +- [-] 1. Настройка проекта и Git + + + - [x] 1.1 Создать структуру Gradle проекта + + - Создать build.gradle.kts с зависимостями Paper API, HikariCP, OkHttp + - Создать settings.gradle.kts + - Настроить Java 21 и shadowJar для сборки + - _Requirements: 7.1_ + + - [ ] 1.2 Настроить Git и .gitignore + - Создать .gitignore для Java/Gradle проекта + - Инициализировать git репозиторий + - Добавить remote https://git.lokili.xyz/loki/lorep.git + - Сделать initial commit и push + - _Requirements: N/A_ + - [ ] 1.3 Создать plugin.yml и основной класс + - Создать plugin.yml с командами и правами + - Создать LorepPlugin.java с onEnable/onDisable + - _Requirements: 6.1, 6.2, 6.3_ + +- [ ] 2. Реализация конфигурации + - [ ] 2.1 Создать ConfigManager и config.yml + - Создать default config.yml с настройками БД и webhook + - Реализовать ConfigManager для чтения конфигурации + - _Requirements: 7.1, 7.2, 7.3, 7.4_ + - [ ] 2.2 Написать property test для парсинга конфигурации + - **Property 9: Configuration Parsing** + - **Validates: Requirements 7.1, 7.3, 7.4** + +- [ ] 3. Реализация базы данных + - [ ] 3.1 Создать модель Report и интерфейс DatabaseManager + - Создать Report record + - Создать интерфейс DatabaseManager + - _Requirements: 2.1_ + - [ ] 3.2 Реализовать SQLiteDatabaseManager + - Реализовать все методы интерфейса для SQLite + - Создать таблицу и индексы + - _Requirements: 2.1, 2.2, 2.3_ + - [ ] 3.3 Реализовать PostgreSQLDatabaseManager + - Реализовать все методы с HikariCP connection pool + - Создать таблицу и индексы для PostgreSQL + - _Requirements: 2.1, 2.2, 2.3_ + - [ ] 3.4 Написать property tests для DatabaseManager + - **Property 1: Report Creation Integrity** + - **Property 2: Duplicate Report Prevention** + - **Property 3: Report Persistence Round-Trip** + - **Property 4: Report Ordering** + - **Property 7: Report Count Accuracy** + - **Validates: Requirements 1.1, 1.2, 2.1, 2.2, 2.3, 5.1** + +- [ ] 4. Checkpoint - Проверка тестов БД + - Ensure all tests pass, ask the user if questions arise. + +- [ ] 5. Реализация Discord Webhook + - [ ] 5.1 Создать DiscordWebhook сервис + - Реализовать отправку embed сообщений через OkHttp + - Обработать ошибки и отсутствие URL + - _Requirements: 3.1, 3.2, 3.3_ + - [ ] 5.2 Написать property test для webhook payload + - **Property 5: Webhook Payload Completeness** + - **Validates: Requirements 3.1** + +- [ ] 6. Реализация команд + - [ ] 6.1 Создать ReportCommand (/report [ник] [причина]) + - Валидация аргументов и прав + - Проверка на самого себя и существование игрока + - Проверка на дубликат репорта + - Сохранение в БД и отправка в webhook + - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 6.1_ + - [ ] 6.2 Создать ReportStatsCommand (/report stats [ник]) + - Показ количества репортов + - Показ последнего онлайна + - Список последних репортов + - _Requirements: 5.1, 5.2, 5.3, 5.4, 6.3_ + - [ ] 6.3 Написать property test для проверки прав + - **Property 8: Permission Enforcement** + - **Validates: Requirements 6.1, 6.2, 6.3** + +- [ ] 7. Реализация GUI + - [ ] 7.1 Создать ReportGui с пагинацией + - Создать инвентарь 54 слота + - Отображение голов игроков с их скинами + - Lore с информацией о репорте + - Кнопки навигации по страницам + - _Requirements: 4.1, 4.2, 4.3, 4.4_ + - [ ] 7.2 Создать GuiClickListener + - Обработка кликов по страницам + - Отмена выноса предметов + - _Requirements: 4.4_ + - [ ] 7.3 Создать ReportGuiCommand (/report gui) + - Проверка прав и открытие GUI + - _Requirements: 4.1, 6.2_ + - [ ] 7.4 Написать property test для пагинации + - **Property 6: Pagination Correctness** + - **Validates: Requirements 4.4** + +- [ ] 8. Утилиты и сообщения + - [ ] 8.1 Создать TimeUtil для форматирования времени + - Форматирование "X минут/часов/дней назад" + - _Requirements: 4.3, 5.2_ + - [ ] 8.2 Создать MessageUtil для цветных сообщений + - Парсинг цветовых кодов + - Отправка сообщений игрокам + - _Requirements: 1.2, 1.3, 1.4, 5.4, 6.1, 6.2, 6.3_ + +- [ ] 9. Интеграция и финализация + - [ ] 9.1 Интегрировать все компоненты в LorepPlugin + - Инициализация всех менеджеров в onEnable + - Регистрация команд и листенеров + - Корректное закрытие в onDisable + - _Requirements: 2.2, 7.1_ + - [ ] 9.2 Финальный push в Git + - Commit всех изменений + - Push в репозиторий + - _Requirements: N/A_ + +- [ ] 10. Final Checkpoint - Финальная проверка + - Ensure all tests pass, ask the user if questions arise. diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..04d1552 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,53 @@ +plugins { + java + id("com.github.johnrengelman.shadow") version "8.1.1" +} + +group = "dev.loki" +version = "1.0.0" + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") + + implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("com.zaxxer:HikariCP:5.1.0") + implementation("org.postgresql:postgresql:42.7.4") + + testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") + testImplementation("net.jqwik:jqwik:1.8.4") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +tasks { + shadowJar { + archiveClassifier.set("") + relocate("okhttp3", "dev.loki.lorep.libs.okhttp3") + relocate("okio", "dev.loki.lorep.libs.okio") + relocate("com.zaxxer.hikari", "dev.loki.lorep.libs.hikari") + } + + build { + dependsOn(shadowJar) + } + + test { + useJUnitPlatform() + } + + processResources { + filesMatching("plugin.yml") { + expand("version" to version) + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..0d3e129 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "lorep"