# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """!good bot: a reaction on the compliment, and not a word in the group.""" from __future__ import annotations import datetime as dt import json from typing import TYPE_CHECKING import pytest from signal_bot.animals import ALIAS_FILE from signal_bot.context import CommandContext from signal_bot.decorators import cooldown from signal_bot.dispatcher import Dispatcher from signal_bot.good_bot_command import ( COMMAND_NAME, EMOJI, pick, register_good_bot_command, ) from signal_bot.help_command import GROUPS from signal_bot.models import parse_envelope from signal_bot.registry import CommandRegistry from signal_bot.registry_holder import RegistryHolder from tests.conftest import WATCHED_GROUP, envelope if TYPE_CHECKING: from signal_bot.config import Config from signal_bot.discovery import GroupDirectory from signal_bot.store import Store from signal_bot.transport_fake import FakeTransport ALIASES = {COMMAND_NAME: ["!good bot", "!dobry bot", "!goodbot"]} SENT_AT = 1719177917717 DRAWS = 200 NOW = dt.datetime(2026, 9, 4, 12, 0, tzinfo=dt.UTC) def _registry() -> CommandRegistry: registry = CommandRegistry() register_good_bot_command(registry, aliases=ALIASES) return registry async def _run( store: Store, transport: FakeTransport, config: Config, text: str, *, timestamp: int ) -> None: """Type one line at the bot through the real resolver and handler.""" message = parse_envelope(envelope(text=text, timestamp=timestamp)) assert message is not None resolved = _registry().resolve_with_args(text, WATCHED_GROUP) assert resolved is not None ctx = CommandContext( message=message, group_id=WATCHED_GROUP, transport=transport, store=store, config=config, command_name=resolved.command.name, now=1_000.0, day="2026-09-04", args=resolved.args, ) await resolved.command.handler(ctx) @pytest.mark.parametrize( "text", ["!good bot", "!Good Bot", "!dobry bot", "!goodbot", " !good bot "] ) async def test_the_compliment_gets_an_emoji_on_itself_and_no_reply( store: Store, transport: FakeTransport, config: Config, text: str ) -> None: await _run(store, transport, config, text, timestamp=SENT_AT) (reaction,) = transport.reactions assert reaction.emoji in EMOJI # On the asker's message, in the asker's group: what a quote would name. assert (reaction.recipient, reaction.target_author) == (WATCHED_GROUP, "uuid-alice") assert reaction.timestamp == SENT_AT assert transport.sent == [] async def test_a_message_that_cannot_be_pointed_at_gets_the_emoji_as_text( store: Store, transport: FakeTransport, config: Config ) -> None: """The visible degradation, the same one reply() makes when it cannot quote.""" await _run(store, transport, config, "!good bot", timestamp=0) assert transport.reactions == [] (sent,) = transport.sent assert sent.text in EMOJI assert sent.quote is None def test_only_the_two_words_together_are_the_command() -> None: """The bare first word is nothing, and so is a compliment with a tail.""" registry = _registry() assert registry.resolve_with_args("!good", WATCHED_GROUP) is None assert registry.resolve_with_args("!good bot!!!", WATCHED_GROUP) is None assert registry.resolve_with_args("!good bot thanks", WATCHED_GROUP) is None assert registry.resolve_with_args("!goodbot", WATCHED_GROUP) is not None def test_every_emoji_in_the_set_comes_up() -> None: """A draw over seven; two hundred draws that miss one would be a broken draw.""" assert {pick() for _ in range(DRAWS)} == set(EMOJI) assert pick(("x",)) == "x" def test_the_shipped_aliases_are_command_words_and_the_first_is_the_two_word_one() -> None: """_repo_vocabulary() subtracts these from the personal-data gate. The first alias is what !pomoc prints, and "!good bot" is the spelling the group asked for; the one-word ones exist for a double space. """ document = json.loads(ALIAS_FILE.read_text(encoding="utf-8")) triggers = document[COMMAND_NAME] assert triggers[0] == "!good bot" for trigger in triggers: assert trigger.startswith("!") assert trigger[1:].replace(" ", "").replace("_", "").isalpha() def test_help_files_it_under_its_own_heading() -> None: assert any(COMMAND_NAME in names for _, names in GROUPS) async def test_through_the_dispatcher_it_is_a_command_with_a_cooldown( config: Config, directory: GroupDirectory, store: Store, transport: FakeTransport ) -> None: """Served like any other command: recorded, timed, and rate-limited.""" registry = CommandRegistry() register_good_bot_command(registry, aliases=ALIASES, decorate=cooldown(10, "wait {seconds}")) dispatcher = Dispatcher( config=config, directory=directory, holder=RegistryHolder(lambda: registry), store=store, transport=transport, clock=lambda: NOW, ) first = await dispatcher.handle(envelope(text="!good bot")) second = await dispatcher.handle(envelope(text="!good bot")) assert first is not None assert (first.command, first.outcome) == (COMMAND_NAME, "ok") assert first.send_ms >= 0 assert second is not None assert second.outcome == "refused_cooldown" assert len(transport.reactions) == 1 assert transport.texts[0].startswith("wait ")