mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-11 13:07:51 +01:00
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
58 lines
2.0 KiB
Python
58 lines
2.0 KiB
Python
# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence.
|
|
"""Which messages count toward the daily tally, and which chats we listen to."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from signal_bot.config import Config
|
|
from signal_bot.models import IncomingMessage
|
|
|
|
|
|
def watched_group(message: IncomingMessage, config: Config) -> str | None:
|
|
"""Return the canonical id of the watched group a message came from.
|
|
|
|
A linked Signal device receives every direct conversation and every group
|
|
on the account. This function is the entire boundary between "a bot in
|
|
its groups" and "a bot reading all of your chats", so it is deliberately
|
|
written as an explicit group-id lookup rather than a falsy check: a
|
|
direct message has no group id at all and must never reach a handler.
|
|
|
|
It returns the id rather than a bool so that the gate and the reply
|
|
router are the same decision. Answering "may this run?" and "where does
|
|
the answer go?" separately is how a bot ends up replying to the wrong
|
|
group.
|
|
|
|
Args:
|
|
message: The parsed inbound message.
|
|
config: Resolved runtime configuration.
|
|
|
|
Returns:
|
|
The canonical group id, or None for a direct message or any group
|
|
the bot is not scoped to.
|
|
"""
|
|
if message.group_id is None:
|
|
return None
|
|
return config.resolve_group(message.group_id)
|
|
|
|
|
|
def should_count(message: IncomingMessage) -> bool:
|
|
"""True when a message should add to its sender's tally.
|
|
|
|
Reactions and stickers are not messages for tally purposes, and a frame
|
|
with no text body carries nothing to count.
|
|
"""
|
|
if message.is_reaction or message.has_sticker:
|
|
return False
|
|
return bool(message.text)
|
|
|
|
|
|
def format_tally(rows: list[tuple[str, int]]) -> str:
|
|
"""Render the daily report, or a placeholder when nobody spoke."""
|
|
if not rows:
|
|
return "Brak wiadomosci dzisiaj."
|
|
width = max(len(name) for name, _ in rows)
|
|
lines = [f"{name.ljust(width)} {count}" for name, count in rows]
|
|
return "\n".join(lines)
|