Other
ТЗ Colombino Chat
ColombinoChat — ТЗ
Плагин управления чатами/PM/упоминаниями для сети Colombino (Paper + Velocity). Цель — единая система чатов с MiniMessage, PlaceholderAPI, антиспамом, логированием и сетевым хранением данных.
0) Техническая информация
-
Название плагина:
ColombinoChat -
Поддерживаемые версии Paper:
1.20.4+ -
Платформа:
Paper + Velocity -
Сборка:
Uber-Jar(общий артефакт) или один репозиторий с двумя модулями:-
colombinochat-paper -
colombinochat-velocity
-
1) Гайдлайны
1.1 YAML
-
Формат путей:
example-path-to-something -
Все пользовательские строки (уведомления/ошибки/подсказки) — в
messages.ymlи поддерживают MiniMessage.
1.2 Git / коммиты
-
Conventional Commits по Angular (без scope): https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit-message-header
-
Пример:
feat: add mentions cooldown
1.3 Стек библиотек
-
CommandAPI не использовать.
-
Выбор библиотек (конфиг/БД/миграции) согласовать до старта, но без избыточных зависимостей.
2) Термины
-
Scope (уровень чата): область видимости сообщения.
-
Network Global: межсерверный чат по сети Velocity.
-
Server Global: чат внутри одного Paper-сервера.
-
World: чат внутри одного мира (World) на Paper.
-
Plot: чат внутри плота PlotSquared (использовать встроенный Plot chat PlotSquared или интеграцию).
3) Архитектура и зоны ответственности
3.1 Velocity (прокси)
Отвечает за:
-
Network Global (межсерверный чат)
-
Cross-server PM (
/pm,/reply) -
Синхронизированный PM ignore (персистентно)
-
Хранение/синхронизацию данных через сетевую БД PostgreSQL
-
Логи: Network Global, PM, blocked
3.2 Paper (бекенд)
Отвечает за:
-
Server/World/Plot отображение (рендер в чат)
-
Форматирование (Adventure/MiniMessage) и PAPI
-
Валидацию/антиспам
-
Mentions + звук + кулдауны
-
Логи Server/World/Plot и blocked
4) Хранение данных (обязательное)
4.1 Общее правило
Все данные, требующие хранения и/или синхронизации в сети, должны храниться в PostgreSQL.
4.2 Что хранить в БД (минимум)
-
UUID игрока → active-scope / active-channel (если включено сохранение)
-
PM ignore-листы (UUID → UUID)
-
Оффлайн-упоминания (notifications) с TTL и лимитами
4.3 Идентификаторы
-
Везде использовать UUID (ник — только для отображения).
4.4 БД-слой
-
Пул соединений (например HikariCP) — допускается/рекомендуется.
-
Миграции (Flyway/Liquibase или собственный механизм) — согласовать до начала реализации.
5) Уровни чата (Scopes)
5.1 Обязательные scopes
-
Network Global — межсерверный (Velocity)
-
Server Global — внутри Paper
-
World — внутри текущего мира
-
Plot — внутри текущего плота (PlotSquared)
5.2 Поведение Plot scope
Если игрок не находится на плоту:
-
A) блокировать отправку + уведомление отправителю
или -
B) fallback на
WorldилиServer(настраивается)
6) Переключение чатов и триггеры
6.1 Активный чат (state)
-
У игрока есть
active-scope(илиactive-channel-id). -
Настройки:
-
дефолтный scope при входе
-
сохранять ли active-scope в БД (при релоге)
-
синхронизировать ли active-scope в сети (при смене сервера в Velocity)
-
6.2 Команды переключения
Минимальный набор:
-
/chat network -
/chat server -
/chat world -
/chat plot
Алиасы настраиваемые (пример): /gc, /sc, /wc, /pc.
Команды:
-
переключают активный scope
-
возвращают подтверждение (MM)
6.3 Триггеры в сообщениях (send-only / switch-and-send)
Примеры:
-
@network <сообщение> -
@server <сообщение> -
@world <сообщение> -
@plot <сообщение>
Требования:
-
возможность задавать любые алиасы тегов/префиксов для каждого scope
-
режимы:
-
send-only(без смены active-scope) -
switch-and-send(сменить active-scope и отправить)
-
Допускается, что изменение алиасов команд/тегов может не поддерживать hot-reload.
6.4 Приоритет обработки
-
Команда (если используется команда отправки)
-
Триггер/префикс в сообщении
-
Активный scope игрока
7) Форматирование сообщений
7.1 MiniMessage в конфигурации
Во всех текстовых полях с форматированием / hover / click — MiniMessage.
7.2 Плейсхолдеры (встроенные)
-
<username>— ник отправителя -
<message>— сообщение -
<timestamp>— Unix timestamp (host time)
Дополнительно:
-
(Network)
<source_server_id> -
(Network)
<source_server_name>(маппинг ID→DisplayName на стороне Paper) -
(World)
<world> -
(Plot)
<plot_id>(если доступно)
7.3 PlaceholderAPI
-
Поддержка PAPI плейсхолдеров в местах, где это имеет смысл
-
Парсинг PAPI относительно игрока-отправителя
8) MiniMessage в сообщениях игроков и ссылки
8.1 MM в сообщении игрока
-
MM парсится только при permission:
-
colombinochat.message.minimessage
-
-
Без permission — plain text (без MM парсинга)
8.2 Автокликабельные ссылки (linkify)
-
Навешивать ClickEvent средствами Adventure.
-
Парсинг ссылок только при permission:
-
colombinochat.message.links
-
Требования:
-
при отсутствии
http(s)://добавлятьhttps://(настраиваемо) -
не ограничивать современные TLD (не использовать 2–4 символа)
-
опционально: whitelist/blacklist доменов
9) Система упоминаний (Mentions)
9.1 Упоминания
-
Тег по никнейму (например
@Nickname), паттерн настраиваем -
Подсветка упоминания задаётся отдельной настройкой (MiniMessage)
9.2 Звук и кулдаун
-
При упоминании игроку воспроизводится звук (настраиваемо)
-
Кулдаун на получателя (ms), чтобы исключить спам звуком
9.3 Оффлайн-упоминания (Notifications)
Если игрок упомянут, но оффлайн — создать уведомление:
-
доставить при следующем входе
-
при входе показать "Пока тебя не было: N упоминаний" и/или подсказку команды
Команды:
-
/mentions— список уведомлений -
/mentions clear— очистить
Требования:
-
Персистентно хранить в PostgreSQL
-
TTL (например 7 дней) и лимит на игрока (например 50)
9.4 Автокомплит
-
Комплит никнеймов онлайн игроков сети (Velocity) — по возможности
10) Cross-server PM (Личные сообщения)
Команды:
-
/pm <игрок> <сообщение>— отправить PM -
/reply <сообщение>(/r) — ответ последнему собеседнику -
/pmignore <игрок>— добавить в игнор -
/pmignore remove <игрок>— убрать из игнора -
/pmignore list— список игнорируемых
Требования:
-
Автокомплит никнеймов онлайн игроков сети (Velocity)
-
Ignore хранится персистентно в PostgreSQL
-
Отдельные форматы для входящих/исходящих PM (MiniMessage)
Опционально:
-
звуки на отправку/получение PM (настраиваемо на Paper)
-
SocialSpy:
-
/socialspy(toggle) -
permission
colombinochat.socialspy
-
11) Валидация сообщений (Regex)
Конфиг (пример):
validation:
enabled: true
regex: "^[!\"@№;%:?*()_=#$^&\\/\\-—+`~;'<>\u005B\u005D{}|,.a-zA-Zа-яА-ЯёЁҐґЄєЇї0-9\\s]+$"
Требования:
-
Валидация применяется к:
-
server/world/plot
-
network (до отправки на прокси)
-
pm
-
-
При блокировке — уведомление отправителю (MM)
Permission bypass:
-
colombinochat.validation.bypass
12) Фильтрация однотипных сообщений (Levenshtein Similarity)
Цель: блокировать спам “почти одинаковыми” сообщениями.
Настройки:
-
similarity.enabled -
similarity.threshold-percent(0..100) -
similarity.window-seconds(например 60) -
similarity.max-message-length(лимит на расчёт)
Определение:
-
сравнивать текущее сообщение с последним сообщением игрока в пределах
window-seconds -
similarity = 1 - (levenshtein / max(len1, len2)) -
блокировать, если
similarity*100 >= threshold-percent
Нормализация (настраиваемо):
-
lower-case
-
trim
-
collapse spaces
-
optional: remove punctuation
Permission bypass:
-
colombinochat.similarity.bypass
13) Логирование
13.1 Требование по формату и именованию
Формат и ротация логов должны соответствовать формату логов сервера. Хороший пример — дата в имени файла в ISO-формате (YYYY-MM-DD).
13.2 Что логировать
-
Все сообщения чатов
-
Все PM
-
Сообщения, заблокированные:
-
validation
-
similarity
-
permissions/прочие фильтры
-
13.3 Где логировать
-
Server/World/Plot — на Paper
-
Network Global — на Velocity
-
PM — на Velocity
13.4 Минимальная структура
Должны быть минимум два потока:
-
разрешённые сообщения
-
заблокированные сообщения
Пример (точное имя/папка — по стандарту сервера):
-
logs/colombinochat/2026-02-22-chat.log -
logs/colombinochat/2026-02-22-chat-blocked.log
14) Permissions (минимум)
Чаты:
-
colombinochat.chat.network -
colombinochat.chat.server -
colombinochat.chat.world -
colombinochat.chat.plot
PM:
-
colombinochat.pm.send -
colombinochat.pm.reply -
colombinochat.pm.ignore
Сообщения:
-
colombinochat.message.minimessage -
colombinochat.message.links
Bypass:
-
colombinochat.validation.bypass -
colombinochat.similarity.bypass
Mentions (опционально):
-
colombinochat.mentions.bypass
15) Hot Reload
Команда:
-
/colombinochat reload
Требования:
-
reload перезагружает: форматы, messages, validation, similarity, mentions
-
предусмотреть отдельную подкоманду для работы с БД:
-
/colombinochat reload db— переподключение/проверка соединения, обновление кэшей (если используются)
-
-
алиасы команд/регистрация новых команд может требовать рестарт (допускается)
16) Acceptance Criteria (готовность)
-
Работают 4 scope: network/server/world/plot (форматы + триггеры)
-
Активный scope переключается и сохраняется согласно настройкам (если включено)
-
PM/Reply/Ignore работают по сети Velocity и используют PostgreSQL
-
Mentions работают со звуком и кулдауном; оффлайн-упоминания доставляются при входе и хранятся в PostgreSQL
-
Validation и Similarity корректно блокируют сообщения + уведомляют отправителя
-
Linkify работает только при permission
-
Логи пишутся на корректной стороне (paper/velocity), имена/даты соответствуют формату логов сервера, есть blocked лог
Опциональные интеграции / отложено
A) Реплейсер строк (Text Replacer) — опционально
Фича может быть добавлена позже при необходимости.
Пример конфига:
replacer:
enabled: true
mappings:
":doge:": "☺"
":lol:": "☻"
B) Discord Bridge — отложено
Discord bridge исключён из текущего объёма работ, т.к. требует интеграции с системой верификации и наличия у неё API.
Вернуться к задаче после утверждения:
-
модели верификации
-
API/протокола взаимодействия бота и прокси
-
требований по безопасности и анти-эхо
Метки на плотах — ТЗ (идея)
Проект Plane: PLOTMARK (из CREATIVE-20). Созвон 20.08.2026 + фидбэк в Discord «взаимодействие с плотами.» Документ фиксирует продуктовую идею, не финальный контракт реализации.
Staff ставит на плоту заметную метку в точке правки с текстом. Игрок кликает → Dialog с запиской → закрывает метку по правилам режима. Автор получает уведомление. Менять блоки на чужом плоту ради подсказки не нужно.
1) Зачем
Подсказать «где и что поправить», в том числе когда игрок оффлайн, без правок его постройки. Задача staff — довести игрока до самостоятельной правки, а не править за него.
2) Режимы и права
| Режим | Кто ставит | Поведение игрока |
|---|---|---|
| Примечание | Helper и Mentor | Может убрать сам, без подтверждения автора |
| Критическая | Только Mentor (+ Senior Mentor) | Нельзя просто отклонить; принять как выполненную или оспорить |
Лимиты на плот одновременно: Helper — до 3 примечаний; Mentor — до 10 меток любого режима.
Создание — по LuckPerms. Наказаний за «принял и не сделал» нет.
На созвоне обсуждали также «предложение / предупреждение / обязательная». Рабочая модель из staff-фидбэка — два режима выше; при необходимости «предупреждение» можно добавить позже как третий.
3) Сценарии
Создание: команда на точке плота → Dialog (текст + режим) → метка появляется.
Игрок: клик по метке → Dialog с запиской → принять / убрать / оспорить (по режиму).
Уведомления: автору — о действии игрока (онлайн или при входе); игроку при входе — если появились новые метки.
Списки: staff — «мои / открытые» + телепорт к метке; игрок — меню по своим плотам (PlotSquared API).
4) Визуал (исследовать)
Заметный символ «!» (голограмма / головы / atlas sprites), подпись «нажмите».
Разный вид для примечания и критической.
Референс: map decorations atlas sprites.
5) Техрамка
Paper (Creative): PlotSquared API, Dialog API, LuckPerms, Adventure/MiniMessage.
Опционально FancyHolograms / display entities — по прототипу.
БД, точные permission-ноды и имена команд — в детальном ТЗ после MVP-решения.
6) Вне скоупа
Права хелпера строить/ломать на чужом плоту без согласия.
«Ветки» правок staff с merge/reject блоками.
Глобальная панель «все игроки с недоделками» (достаточно списка меток + TP).
Colombino Analytics — ТЗ
Проект Plane: пока CREATIVE (отдельный проект аналитики не заведён). Источник: обсуждение 25.08.2026 (retention, ключевые действия, история плотов, Plan). Документ — контракт v1, не экран в Plan.
Плагин-шина ключевых действий игроков. Другие плагины вызывают API, события пишутся в свою PostgreSQL. Plan (stats.binomc.net), DataExtension и отчёт воронки в v1 нет.
0) Техническая информация
-
Название:
ColombinoAnalytics -
Платформа: Paper (Hub, Creative, ивентовые) + Velocity Master
-
Сборка: один репозиторий, три модуля:
-
analytics-api— тонкий jar,compileOnlyу других плагинов -
analytics-paper -
analytics-velocity
-
-
БД: отдельная database PostgreSQL (тот же хост, что Plane/Bookstack, не их схемы и не БД Plan)
-
Soft-depend (Paper): PlotSquared, BinoRankup, PlotMark — нет плагина, нет слушателя, API живёт
1) Зачем
Три цели на одном логе. Creative — UGC: активация не «зашёл», а взял слот. Ивентовые режимы (Build Battle и др.) в логе не отдельные продукты, а значение server у захода.
| Цель | Что даёт лог | Когда на экране |
|---|---|---|
| Шина | API для других плагинов | v1 |
| Модерация | История владения плотом | данные в v1, UI позже |
| Воронка новичка | Где обрыв до активации и ранга | данные в v1, отчёт позже |
| Staff | Ревью, staff-rate, марки PlotMark | данные в v1, UI позже |
2) Готово когда (v1)
-
Jar
analytics-api: другой плагин вызываетtrack, строка появляется в Postgres. Если analytics не установлен — вызов no-op, не падает. -
Velocity Master пишет заходы с каноническим именем сервера.
-
Paper Creative пишет плоты, заявки ранга, staff-ревью/рейтинг, марки PlotMark.
-
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) Воронка новичка (как смотрит аналитик)
-
Зашёл в сеть → заход с
server=hub -
Попал на Creative → заход с
server=creative -
Взял плот →
plot.claimed← активация -
Подал
/done→rank.request_submitted(/rankup— только меню, не этот шаг) -
Заявка закрылась →
rank.request_status_changed(APPROVED/REJECTED/WITHDRAWN/DELETED) -
Получил ранг →
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
-
Выдаёт
session_idна время пребывания в сети. -
Пишет два типа заходов (§6.1). First: кэш + уникальный индекс (гонка → conflict ignore).
-
После коннекта/switch на Paper — plugin message с
session_idи канономserver.
8.2 Paper
-
Не считает first/session.
track()дописывает текущиеsession_idиserver. -
Нет сообщения с прокси — событие пишем,
session_id = null. Фактplot.claimedважнее склейки с сессией. -
Состояние: карта UUID → сессия до quit. Тот же
session_idпри каждом switch. -
Гонка: событие раньше plugin message — допустимо,
session_idnull.
Shadow: в v1 не ставим. Иначе два источника сессий. Игрок через Shadow = как без прокси.
Не в v1: Redis, слушатели плотов на прокси.
9) API
boolean track(UUID player, String eventType, Map<String, Object> extra)
-
Нет paper/velocity плагина на сервере → no-op,
false. -
Тип не из каталога или нет обязательных ключей extra →
false, warn в лог, в очередь не кладём. -
Успешный enqueue →
true(ещё не значит «уже в Postgres»).
Игровой поток только 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)
-
Контракт extra: полный набор — ok; дырявый — reject.
-
Дедуп
first_joinedиsession_joined. -
rank.rating_submitted: Helper/Mentor/Senior Mentor — пишем; игрок без роли — нет. -
rank.review_submittedбезvoter_role— нет. -
Запрос-заготовка воронки: цепочка типов по uuid (не UI).
12) Следующие этапы (не v1)
-
DataExtension Plan: счётчики, first/last, урезанная история (лимиты Plan: 4 колонки, 50 символов).
-
Отчёт воронки / retention по когортам (SQL или отдельный сервис).
-
Закрытие дыры «взял плот, не строит».
-
Velocity Shadow, если станет основным входом.
-
TTL/партиции, когда будет ясен объём.
13) Сознательно отвергнутое
-
Хранение в БД Plan через Query API.
-
DataExtension как «запись в БД» — он не пишет лог.
-
build_battle.joined— дубль захода на ивентовыйserver. -
PlayerEnterPlotEvent— ходьба, не ключ. -
Каждый Paper join без дедупа по network-сессии.
-
PostgreSQL через Plan API — не поддерживается (
SQLITE/MYSQLonly).