mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-11 18:34:07 +01:00
The tally becomes a message history. Ordinal 9 lands on 0.1.0 by arithmetic rather than by choice, which is a pleasant coincidence for a release this size. What changes for the group, in the four bullets they will read: photos and videos now count (a caption-less photo was worth zero, so everyone's numbers jumped), people who said nothing appear at 0, nine new commands, and the bot remembers history from 30 August 2026. What changes underneath: an append-only per-message log holding metadata and never a body; days that are calendar days in Europe/Warsaw rather than "since the last post"; records, crown streaks and weekday averages; weekly and monthly summaries; and reports that survive an outage instead of being lost. Exercised in testbot before this bump, per DOCS-changelog.md: all nine commands ran clean, and kuhy confirmed the rendering on a real phone -- the one check that could not be done from here, since Signal's proportional font is why the columns are separated by a dash rather than padded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015d48t4qsQLBJnDwDeo2Nom
125 lines
3.5 KiB
TOML
125 lines
3.5 KiB
TOML
[project]
|
|
name = "signal-bot"
|
|
version = "0.1.0"
|
|
description = "A Signal group bot: image commands and daily message tallies."
|
|
readme = "README.md"
|
|
license = "MIT"
|
|
license-files = ["LICENSE"]
|
|
requires-python = ">=3.14"
|
|
|
|
# Exact pins, newest stable, per the dependency-freshness rule. A caret range
|
|
# is "newest-compatible" by definition and gives the freshness gate nothing to
|
|
# compare against.
|
|
dependencies = [
|
|
"fastapi==0.141.1",
|
|
"httpx==0.28.1",
|
|
"python-dotenv==1.2.3",
|
|
"PyYAML==6.0.3",
|
|
"uvicorn==0.52.4",
|
|
"websockets==17.1",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"mypy==2.3.1",
|
|
"pre-commit==4.6.2",
|
|
"pytest==9.1.1",
|
|
"pytest-asyncio==1.4.0",
|
|
"pytest-cov==7.1.0",
|
|
"ruff==0.16.5",
|
|
"types-PyYAML==6.0.12.20260815",
|
|
]
|
|
|
|
[project.scripts]
|
|
signal-bot = "signal_bot.__main__:main"
|
|
|
|
[build-system]
|
|
requires = ["setuptools==84.0.0"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[tool.setuptools]
|
|
packages = ["signal_bot", "fake_signal"]
|
|
|
|
# ============================================================================
|
|
# RUFF
|
|
# ============================================================================
|
|
[tool.ruff]
|
|
target-version = "py314"
|
|
line-length = 100
|
|
|
|
[tool.ruff.lint]
|
|
select = ["ALL"]
|
|
ignore = [
|
|
"D203", # conflicts with D211, we use D211
|
|
"D213", # conflicts with D212, we use D212
|
|
"COM812", # formatter owns trailing commas
|
|
"ISC001", # formatter may create implicit string concatenation
|
|
]
|
|
fixable = ["ALL"]
|
|
|
|
# Verified empirically, not copied: with this block removed, ruff reports
|
|
# exactly these four rules across tests/ and nothing else. SLF001 was in
|
|
# the original list and fired zero times, so it is gone.
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/**/*.py" = [
|
|
"ARG", # fixture and mock params exist for their side effect
|
|
"D", # test names are the documentation
|
|
"PLR2004", # `assert count == 2` beats a named constant
|
|
"S101", # assert IS what tests do
|
|
]
|
|
|
|
[tool.ruff.lint.pylint]
|
|
# The test envelope builder takes 7 keyword-only fields, one per envelope
|
|
# attribute. Splitting it into a dataclass would only move the count.
|
|
max-args = 7
|
|
|
|
[tool.ruff.lint.pydocstyle]
|
|
convention = "google"
|
|
|
|
[tool.ruff.lint.flake8-quotes]
|
|
docstring-quotes = "double"
|
|
inline-quotes = "double"
|
|
|
|
[tool.ruff.lint.flake8-tidy-imports]
|
|
ban-relative-imports = "all"
|
|
|
|
[tool.ruff.format]
|
|
quote-style = "double"
|
|
docstring-code-format = true
|
|
|
|
# ============================================================================
|
|
# MYPY
|
|
# ============================================================================
|
|
[tool.mypy]
|
|
python_version = "3.14"
|
|
strict = true
|
|
warn_unreachable = true
|
|
disallow_any_explicit = false
|
|
|
|
# ============================================================================
|
|
# PYTEST / COVERAGE
|
|
# ============================================================================
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
asyncio_mode = "auto"
|
|
asyncio_default_fixture_loop_scope = "function"
|
|
addopts = [
|
|
"--cov=signal_bot",
|
|
"--cov=fake_signal",
|
|
"--cov-branch",
|
|
"--cov-report=term-missing",
|
|
"--cov-fail-under=100",
|
|
"--strict-markers",
|
|
]
|
|
|
|
[tool.coverage.run]
|
|
branch = true
|
|
# No omits. Every line of both packages is covered by tests.
|
|
|
|
[tool.coverage.report]
|
|
# No "pragma: no cover" escape hatch: nothing in this repo uses one, and
|
|
# leaving the pattern configured is an invitation to hide code from the
|
|
# 100% gate later. TYPE_CHECKING bodies are genuinely never executed.
|
|
exclude_lines = ["if TYPE_CHECKING:"]
|
|
show_missing = true
|