forked from sass/tipibot
Add startup scheme
This commit is contained in:
15
bot.py
15
bot.py
@@ -383,6 +383,21 @@ async def on_ready():
|
|||||||
log.info("Logged in as %s (ID: %s)", bot.user, bot.user.id)
|
log.info("Logged in as %s (ID: %s)", bot.user, bot.user.id)
|
||||||
economy.set_house(bot.user.id)
|
economy.set_house(bot.user.id)
|
||||||
|
|
||||||
|
# PocketBase silently drops writes to fields missing from the collection
|
||||||
|
# schema, so surface any drift loudly instead of letting features no-op.
|
||||||
|
try:
|
||||||
|
missing = await economy.missing_schema_fields()
|
||||||
|
if missing:
|
||||||
|
log.error(
|
||||||
|
"PocketBase collection '%s' is missing %d schema field(s): %s "
|
||||||
|
"- writes to them are silently dropped! Run scripts/sync_pb_schema.py.",
|
||||||
|
config.PB_ECONOMY_COLLECTION, len(missing), ", ".join(missing),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
log.info("PocketBase schema check passed (%s)", config.PB_ECONOMY_COLLECTION)
|
||||||
|
except Exception as e:
|
||||||
|
log.warning("Could not verify PocketBase schema: %s", e)
|
||||||
|
|
||||||
_apply_profile_command_filters()
|
_apply_profile_command_filters()
|
||||||
|
|
||||||
# Pull sheet data into cache
|
# Pull sheet data into cache
|
||||||
|
|||||||
@@ -573,6 +573,15 @@ async def do_spam_jail(user_id: int) -> None:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Public helpers
|
# Public helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
async def missing_schema_fields() -> list[str]:
|
||||||
|
"""Compare the live PocketBase collection schema against every field the
|
||||||
|
bot persists. PocketBase silently drops writes to undeclared fields, so
|
||||||
|
any name returned here means broken features without error messages."""
|
||||||
|
live = await pb_client.get_collection_fields()
|
||||||
|
expected = set(_default_user()) | {"user_id"}
|
||||||
|
return sorted(expected - live)
|
||||||
|
|
||||||
|
|
||||||
async def get_all_users_raw() -> dict[str, "UserData"]:
|
async def get_all_users_raw() -> dict[str, "UserData"]:
|
||||||
"""Return a snapshot of all user records."""
|
"""Return a snapshot of all user records."""
|
||||||
records = await pb_client.list_all_records()
|
records = await pb_client.list_all_records()
|
||||||
|
|||||||
@@ -147,6 +147,12 @@ async def update_record(record_id: str, data: dict[str, Any]) -> dict[str, Any]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_collection_fields() -> set[str]:
|
||||||
|
"""Return the field names defined on the economy collection's schema."""
|
||||||
|
data = await _request("GET", f"{PB_URL}/api/collections/{ECONOMY_COLLECTION}")
|
||||||
|
return {f["name"] for f in data.get("fields", [])}
|
||||||
|
|
||||||
|
|
||||||
async def count_records() -> int:
|
async def count_records() -> int:
|
||||||
"""Return the total number of records in the collection (single cheap request)."""
|
"""Return the total number of records in the collection (single cheap request)."""
|
||||||
data = await _request(
|
data = await _request(
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ class FakePocketBase:
|
|||||||
async def count_records(self) -> int:
|
async def count_records(self) -> int:
|
||||||
return len(self.records)
|
return len(self.records)
|
||||||
|
|
||||||
|
async def get_collection_fields(self) -> set[str]:
|
||||||
|
if self.schema_fields is None:
|
||||||
|
return set(economy._default_user()) | {"user_id"}
|
||||||
|
return set(self.schema_fields)
|
||||||
|
|
||||||
# -- test helpers -------------------------------------------------------
|
# -- test helpers -------------------------------------------------------
|
||||||
def record_for(self, user_id: int) -> dict:
|
def record_for(self, user_id: int) -> dict:
|
||||||
for record in self.records.values():
|
for record in self.records.values():
|
||||||
@@ -83,7 +88,7 @@ class FakePocketBase:
|
|||||||
|
|
||||||
def _install(monkeypatch, fake: FakePocketBase) -> FakePocketBase:
|
def _install(monkeypatch, fake: FakePocketBase) -> FakePocketBase:
|
||||||
for name in ("get_record", "create_record", "update_record",
|
for name in ("get_record", "create_record", "update_record",
|
||||||
"list_all_records", "count_records"):
|
"list_all_records", "count_records", "get_collection_fields"):
|
||||||
monkeypatch.setattr(pb_client, name, getattr(fake, name))
|
monkeypatch.setattr(pb_client, name, getattr(fake, name))
|
||||||
monkeypatch.setattr(economy, "HOUSE_ID", None)
|
monkeypatch.setattr(economy, "HOUSE_ID", None)
|
||||||
monkeypatch.setattr(economy, "_house_pb_id", None)
|
monkeypatch.setattr(economy, "_house_pb_id", None)
|
||||||
|
|||||||
@@ -100,6 +100,12 @@ class TestClaim:
|
|||||||
|
|
||||||
|
|
||||||
class TestSchemaDetection:
|
class TestSchemaDetection:
|
||||||
|
def test_missing_fields_reported(self, fake_pb_without_quest_fields):
|
||||||
|
assert run(economy.missing_schema_fields()) == ["quest_daily", "quest_weekly"]
|
||||||
|
|
||||||
|
def test_healthy_schema_reports_nothing(self, fake_pb):
|
||||||
|
assert run(economy.missing_schema_fields()) == []
|
||||||
|
|
||||||
def test_missing_quest_fields_warn_and_zero_progress(
|
def test_missing_quest_fields_warn_and_zero_progress(
|
||||||
self, fake_pb_without_quest_fields, caplog
|
self, fake_pb_without_quest_fields, caplog
|
||||||
):
|
):
|
||||||
|
|||||||
Reference in New Issue
Block a user