01
Jak to działa
Każdy moduł to osobny folder w katalogu modules/ repozytorium bota. Bot ładuje moduły przy starcie, a strona czyta manifest i sama buduje stronę modułu oraz formularz ustawień.
Moduły są odizolowane: każdy ma własne komendy, zdarzenia, ustawienia i tłumaczenia. Moduł nie może czytać danych innych modułów bezpośrednio — tylko przez publiczne API bota.
02
Szybki start
Potrzebujesz Pythona 3.12+, uv i testowego serwera na Discordzie.
- Zrób fork repozytorium
mosaicbot-dev1/mosaic-bot-corei sklonuj je. - Zainstaluj zależności:
uv sync. - Wygeneruj szkielet modułu:
uv run mosaic create birthdays. - Uruchom bota lokalnie na testowym serwerze:
uv run mosaic dev.
git clone https://github.com/<you>/mosaic-bot-core.git
cd mosaic-bot-core
uv sync
uv run mosaic create birthdays
uv run mosaic dev --guild 12345678901234567803
Struktura modułu
Minimalny moduł to manifest i punkt wejścia. Reszta — według potrzeb.
modules/birthdays/
├── module.toml
├── __init__.py
├── commands.py
├── events.py
├── locales/
│ ├── uk.json
│ ├── en.json
│ └── pl.json
└── README.md- module.toml
- manifest: id, wersja, autor, uprawnienia i schemat ustawień
- __init__.py
- punkt wejścia: pakiet Pythona, który rejestruje komendy, zdarzenia i hooki
- commands.py
- komendy slash i grupy komend
- events.py
- obsługa zdarzeń Discorda i zadań cyklicznych
- locales/
- tłumaczenia modułu, co najmniej uk i en
- README.md
- opis i lista komend do katalogu
04
Manifest
Strona buduje formularz ustawień bezpośrednio z settings. Pola grupuje się przez section; dostępne typy: boolean, number, text, select, color, channel i channels, role i roles oraz pairs — reguły „liczba → wartość”, np. „poziom 10 → rola”.
[module]
id = "birthdays"
name = "Birthdays"
version = "1.2.0"
category = "community"
author = "olha-codes"
permissions = ["ManageRoles", "SendMessages"]
network = []
[[settings]]
section = "general"
key = "announceChannel"
type = "channel"
channel_type = "text"
[[settings]]
section = "general"
key = "birthdayRole"
type = "role"
[[settings]]
section = "general"
key = "timezone"
type = "select"
options = ["Europe/Kyiv", "Europe/Warsaw", "UTC"]
default = "Europe/Kyiv"
[[settings]]
section = "messages"
key = "birthdayMessage"
type = "text"
default = "Happy birthday, {user}!"
max_length = 30005
Przykład kodu
Moduł to pakiet Pythona z obiektem Module, który czyta manifest. Ustawienia są typowane ze schematu manifestu, a tłumaczenia dostępne przez ctx.t.
from mosaic import Context, Module
module = Module.from_manifest(__file__)
@module.on_enable
async def on_enable(ctx: Context) -> None:
ctx.log.info("birthdays enabled on %s", ctx.guild.name)
# Importing the files registers their commands and events on `module`
from . import commands, events # noqa: E402,F401Komendy to funkcje async z dekoratorem @birthday.command. Dane zapisuj wyłącznie przez ctx.store: tak bot izoluje moduły i czyści dane po usunięciu.
from datetime import date
from discord import app_commands
from mosaic import CommandContext
from . import module
birthday = module.group("birthday", description="Birthdays")
@birthday.command("set", description="Set your birthday")
@app_commands.describe(day="Day of the month", month="Month, 1-12")
async def set_birthday(
ctx: CommandContext,
day: app_commands.Range[int, 1, 31],
month: app_commands.Range[int, 1, 12],
) -> None:
try:
when = date(2000, month, day) # a leap year, so 29 February is valid
except ValueError:
await ctx.reply(ctx.t("errors.invalidDate"), ephemeral=True)
return
# ctx.settings comes from the dashboard, ctx.store keeps this module's data
await ctx.store.user(ctx.user.id).set("birthday", when.strftime("%m-%d"))
await ctx.reply(ctx.t("set.success", date=when.strftime("%d.%m")), ephemeral=True)06
Sprawdzanie lokalnie
Przed PR uruchom te same testy co CI: ruff, pytest i mosaic validate, który sprawdza manifest, uprawnienia i tłumaczenia.
uv run ruff check modules/birthdays
uv run pytest
uv run mosaic validate birthdays07
Zasady pull requestów
PR zostaje przyjęty, jeśli moduł spełnia te zasady:
- Jeden moduł — jeden PR. Tytuł w formacie
feat(module): birthdays. ruff,pytestimosaic validateprzechodzą bez błędów, a publiczne funkcje mają adnotacje typów.- Moduł prosi tylko o te uprawnienia Discorda, których naprawdę używa, i wyjaśnia je w README.
- Żadnych zewnętrznych zapytań bez pola
networkw manifeście. Dane osobowe — tylko niezbędne. - Tłumaczenia co najmniej po ukraińsku i angielsku.
- README z opisem, listą komend i zrzutem ekranu ustawień.
- Kod na licencji MIT, bez zapożyczonego kodu na niezgodnej licencji.
08
Review i wydanie
Opiekunowie przeglądają PR w ciągu siedmiu dni. Automatyczne testy startują od razu: build, testy, analiza bezpieczeństwa i walidacja manifestu.
Po scaleniu moduł pojawia się w katalogu z oznaczeniem „Społeczność” w najbliższym wydaniu, a Ty — na liście kontrybutorów.