Other

ТЗ Colombino Chat

ColombinoChat — ТЗ

Плагин управления чатами/PM/упоминаниями для сети Colombino (Paper + Velocity). Цель — единая система чатов с MiniMessage, PlaceholderAPI, антиспамом, логированием и сетевым хранением данных.


0) Техническая информация


1) Гайдлайны

1.1 YAML

1.2 Git / коммиты

1.3 Стек библиотек


2) Термины


3) Архитектура и зоны ответственности

3.1 Velocity (прокси)

Отвечает за:

3.2 Paper (бекенд)

Отвечает за:


4) Хранение данных (обязательное)

4.1 Общее правило

Все данные, требующие хранения и/или синхронизации в сети, должны храниться в PostgreSQL.

4.2 Что хранить в БД (минимум)

4.3 Идентификаторы

4.4 БД-слой


5) Уровни чата (Scopes)

5.1 Обязательные scopes

  1. Network Global — межсерверный (Velocity)

  2. Server Global — внутри Paper

  3. World — внутри текущего мира

  4. Plot — внутри текущего плота (PlotSquared)

5.2 Поведение Plot scope

Если игрок не находится на плоту:


6) Переключение чатов и триггеры

6.1 Активный чат (state)

6.2 Команды переключения

Минимальный набор:

Алиасы настраиваемые (пример): /gc, /sc, /wc, /pc.

Команды:

6.3 Триггеры в сообщениях (send-only / switch-and-send)

Примеры:

Требования:

Допускается, что изменение алиасов команд/тегов может не поддерживать hot-reload.

6.4 Приоритет обработки

  1. Команда (если используется команда отправки)

  2. Триггер/префикс в сообщении

  3. Активный scope игрока


7) Форматирование сообщений

7.1 MiniMessage в конфигурации

Во всех текстовых полях с форматированием / hover / click — MiniMessage.

7.2 Плейсхолдеры (встроенные)

Дополнительно:

7.3 PlaceholderAPI


8) MiniMessage в сообщениях игроков и ссылки

8.1 MM в сообщении игрока

8.2 Автокликабельные ссылки (linkify)

Требования:


9) Система упоминаний (Mentions)

9.1 Упоминания

9.2 Звук и кулдаун

9.3 Оффлайн-упоминания (Notifications)

Если игрок упомянут, но оффлайн — создать уведомление:

Команды:

Требования:

9.4 Автокомплит


10) Cross-server PM (Личные сообщения)

Команды:

Требования:

Опционально:


11) Валидация сообщений (Regex)

Конфиг (пример):

validation:
  enabled: true
  regex: "^[!\"@№;%:?*()_=#$^&\\/\\-—+`~;'<>\u005B\u005D{}|,.a-zA-Zа-яА-ЯёЁҐґЄєЇї0-9\\s]+$"

Требования:

Permission bypass:


12) Фильтрация однотипных сообщений (Levenshtein Similarity)

Цель: блокировать спам “почти одинаковыми” сообщениями.

Настройки:

Определение:

Нормализация (настраиваемо):

Permission bypass:


13) Логирование

13.1 Требование по формату и именованию

Формат и ротация логов должны соответствовать формату логов сервера. Хороший пример — дата в имени файла в ISO-формате (YYYY-MM-DD).

13.2 Что логировать

13.3 Где логировать

13.4 Минимальная структура

Должны быть минимум два потока:

Пример (точное имя/папка — по стандарту сервера):


14) Permissions (минимум)

Чаты:

PM:

Сообщения:

Bypass:

Mentions (опционально):

SocialSpy (опционально):


15) Hot Reload

Команда:

Требования:


16) Acceptance Criteria (готовность)

Опциональные интеграции / отложено

A) Реплейсер строк (Text Replacer) — опционально

Фича может быть добавлена позже при необходимости.

Пример конфига:

replacer:
  enabled: true
  mappings:
    ":doge:": "☺"
    ":lol:": "☻"

B) Discord Bridge — отложено

Discord bridge исключён из текущего объёма работ, т.к. требует интеграции с системой верификации и наличия у неё API.

Вернуться к задаче после утверждения:

 

Метки на плотах — ТЗ (идея)

Проект Plane: PLOTMARK (из CREATIVE-20). Созвон 20.08.2026 + фидбэк в Discord «взаимодействие с плотами.» Документ фиксирует продуктовую идею, не финальный контракт реализации.

Staff ставит на плоту заметную метку в точке правки с текстом. Игрок кликает → Dialog с запиской → закрывает метку по правилам режима. Автор получает уведомление. Менять блоки на чужом плоту ради подсказки не нужно.


1) Зачем

Подсказать «где и что поправить», в том числе когда игрок оффлайн, без правок его постройки. Задача staff — довести игрока до самостоятельной правки, а не править за него.


2) Режимы и права

РежимКто ставитПоведение игрока
ПримечаниеHelper и MentorМожет убрать сам, без подтверждения автора
КритическаяТолько Mentor (+ Senior Mentor)Нельзя просто отклонить; принять как выполненную или оспорить

На созвоне обсуждали также «предложение / предупреждение / обязательная». Рабочая модель из staff-фидбэка — два режима выше; при необходимости «предупреждение» можно добавить позже как третий.


3) Сценарии

  1. Создание: команда на точке плота → Dialog (текст + режим) → метка появляется.

  2. Игрок: клик по метке → Dialog с запиской → принять / убрать / оспорить (по режиму).

  3. Уведомления: автору — о действии игрока (онлайн или при входе); игроку при входе — если появились новые метки.

  4. Списки: staff — «мои / открытые» + телепорт к метке; игрок — меню по своим плотам (PlotSquared API).


4) Визуал (исследовать)


5) Техрамка


6) Вне скоупа

```

Colombino Analytics — ТЗ

Проект Plane: пока CREATIVE (отдельный проект аналитики не заведён). Источник: обсуждение 25.08.2026 (retention, ключевые действия, история плотов, Plan). Документ — контракт v1, не экран в Plan.

Плагин-шина ключевых действий игроков. Другие плагины вызывают API, события пишутся в свою PostgreSQL. Plan (stats.binomc.net), DataExtension и отчёт воронки в v1 нет.


0) Техническая информация


1) Зачем

Три цели на одном логе. Creative — UGC: активация не «зашёл», а взял слот. Ивентовые режимы (Build Battle и др.) в логе не отдельные продукты, а значение server у захода.

Цель Что даёт лог Когда на экране
Шина API для других плагинов v1
Модерация История владения плотом данные в v1, UI позже
Воронка новичка Где обрыв до активации и ранга данные в v1, отчёт позже
Staff Ревью, staff-rate, марки PlotMark данные в v1, UI позже

2) Готово когда (v1)

  1. Jar analytics-api: другой плагин вызывает track, строка появляется в Postgres. Если analytics не установлен — вызов no-op, не падает.

  2. Velocity Master пишет заходы с каноническим именем сервера.

  3. Paper Creative пишет плоты, заявки ранга, staff-ревью/рейтинг, марки PlotMark.

  4. Paper стоит на Hub и ивентовых бэкендах (API и будущие типы; заходы в v1 пишет прокси).

Не в приёмке v1: DataExtension и страницы Plan, дашборд retention, BlockPlace, плагин на Shadow, TTL/чистка лога, Redis.


3) Термины

Термин Смысл
Network-сессия Пока игрок в сети (Velocity), не «пока на этом Paper»
Канон server Имя сервера как в Velocity: hub, creative, имя ивентового
Активация plot.claimed — взял слот, не заход на Creative
DataExtension API Plan для снимка на сайте. Лог не пишет. В v1 не используем
Query API Plan SQL в БД Plan. Типы движка: SQLite или MySQL. Postgres через Plan нет. В v1 не используем

4) Архитектура

Почему не таблица в БД Plan: DataExtension не пишет события (4 колонки на игрока, 50 символов в ячейке). Query API — SQLite/MySQL. Заходы со всех серверов не кладём в боевую БД Plan: патчи, /plan db clear и move не знают чужие таблицы; воронки будут конкурировать с вебом.

Поток: Velocity Master и Paper пишут в одну PostgreSQL player_events. Прокси шлёт на Paper plugin message канала colombino:analytics (session_id + канон server). Плот, ранг и марки прокси не слушает.


5) Воронка новичка (как смотрит аналитик)

  1. Зашёл в сеть → заход с server=hub

  2. Попал на Creative → заход с server=creative

  3. Взял плот → plot.claimedактивация

  4. Подал /donerank.request_submitted (/rankup — только меню, не этот шаг)

  5. Заявка закрылась → rank.request_status_changed (APPROVED / REJECTED / WITHDRAWN / DELETED)

  6. Получил ранг → rank.granted

Дыра между 3 и 4: PlotSquared не видит «начал строить». Без BlockPlace на своём плоте не отличить «взял и бросил» от «строит третий день». В v1 дыру помечаем, не закрываем.

«Поучаствовал в Build Battle» = заход на ивентовый сервер. Отдельного типа build_battle.joined нет.


6) Каталог событий v1 (закрыт)

Строка: player_uuid, event_type, occurred_at, server, session_id, extra (JSONB строго по типу). Неизвестный тип API отвергает. Свободных строк типов нет.

6.1 Заходы (пишет только Velocity)

Hub → Creative → Hub → Creative в одном визите: по одному session_joined на hub и на creative. Возврат на Creative в той же сессии не пишем.

Тип Смысл Extra
player.first_joined_server Первый раз в жизни на этом server server
player.session_joined_server Первый раз на этом server в network-сессии server

6.2 Плоты (PlotSquared, Creative)

Активация и история владения. PlayerEnterPlotEvent не пишем. Trusted/members в v1 нет. plot_id — идентификатор из PlotSquared API. width — ширина в блоках.

Тип Источник Extra
plot.claimed PlotClaimedNotifyEvent (wasAuto()method: auto | claim) plot_id, world, width, method
plot.unclaimed unclaim plot_id, world, width
plot.deleted delete plot_id, world, width
plot.cleared clear plot_id, world, width
plot.transferred PlotChangeOwnerEvent то же + from_uuid, to_uuid

6.3 Ранг — новичок (BinoRankup)

Слушаем Bukkit API плагина, хуки не выдумываем. categories — как отдаёт Rankup (JSON-массив строк). Имена классов сверить с репозиторием при реализации; смысл событий не менять.

Тип Слушаем Extra
rank.request_submitted RequestSubmitEvent request_id, from_rank, to_rank, plot_id, world, width, categories
rank.request_status_changed RequestStatusChangedEvent request_id, from_status, to_status
rank.granted PlayerRankGrantedEvent from_rank, to_rank, request_id

6.4 Ранг — staff (основная метрика команды)

Тип Слушаем Extra
rank.review_submitted ReviewSubmitEvent request_id, vote (APPROVE/REJECT), voter_role как в конфиге Rankup, plot_id, world
rank.rating_submitted RatingSubmitEvent только если у голосующего роль Helper / Mentor / Senior Mentor request_id, plot_id, world

Review без voter_role не пишем: вес голоса в Rankup от роли. Обычный игроковый /rate в лог не идёт (отсекаем на входе).

6.5 Марки (PlotMark, Creative)

См. Метки на плотах — ТЗ. PlotMark вызывает Analytics.track или отдаёт Bukkit-событие с тем же extra.

Тип Extra
staff.mark_placed mark_type: pointer / edit / rework; plot_id, world
staff.mark_resolved то же + resolved_by: player | staff

7) Хранение

Таблица player_events, append-only.

Колонка Тип Назначение
id bigserial PK
occurred_at timestamptz время события, не вставки
received_at timestamptz время записи в Postgres
player_uuid uuid игрок
event_type text только каталог §6
server text канон Velocity
session_id uuid nullable network-сессия; null если прокси не доставил
extra jsonb контракт типа

Индексы: (player_uuid, occurred_at DESC); (event_type, occurred_at); (server, event_type, occurred_at); expression (extra->>'plot_id'), (extra->>'request_id'). GIN по всему extra в v1 нет.

Уникальность: first_joined(player_uuid, event_type, server). session_joined(session_id, event_type, server) WHERE session_id IS NOT NULL (без WHERE в Postgres несколько NULL не дедупятся). Повтор — ON CONFLICT DO NOTHING. Плот/ранг/марки не дедупим.

TTL сырого лога в v1 нет. Политика хранения — отдельное решение по факту объёма.


8) Кто пишет что

8.1 Velocity Master

8.2 Paper

Shadow: в v1 не ставим. Иначе два источника сессий. Игрок через Shadow = как без прокси.

Не в v1: Redis, слушатели плотов на прокси.


9) API

boolean track(UUID player, String eventType, Map<String, Object> extra)

Игровой поток только enqueue. Фоновый writer — batch insert. Главный поток JDBC не ждёт.


10) Ошибки

Ситуация Поведение
Postgres недоступен при старте Плагин включается; track enqueue; периодический warn
Postgres лёг в рантайме Очередь в памяти, потолок 10 000. Overflow: drop + счётчик. Игрок не лагает
После подъёма БД Flush очереди
Failover на диск/SQLite Нет в v1 (два источника правды)
Битый plugin message Игнор
Неверный extra / тип Отказ до очереди

11) Тесты (CI без живого MC)


12) Следующие этапы (не v1)

  1. DataExtension Plan: счётчики, first/last, урезанная история (лимиты Plan: 4 колонки, 50 символов).

  2. Отчёт воронки / retention по когортам (SQL или отдельный сервис).

  3. Закрытие дыры «взял плот, не строит».

  4. Velocity Shadow, если станет основным входом.

  5. TTL/партиции, когда будет ясен объём.


13) Сознательно отвергнутое

```