mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-10-08 15:27:21 +00:00
The ACK fix I wrote for the radio-lock work is now upstream and released (meshcore_py#108, v2.3.13). Before it, send_msg_with_retry reported ACKed DMs as failures: the ACK subscription was registered only after send_msg returned, so an ACK queued right behind MSG_SENT was dispatched with no listener, and each attempt accepted only its own ACK code, so a late ACK answering an earlier attempt was ignored. The bot logged "no ACK received after retries" and skipped the delivery bookkeeping for messages the recipient had in fact received. Pin 2.3.14, which also fixes send_cmd's destination-type handling. Two fixes from re-reading the merged radio-lock change: A region-scoped channel message now restores global flood even when set_flood_scope raises. The restore only ran in the finally around the send, so a set that raised left the device pinned to that region and every later send went out under it. Drop the __setattr__ left behind when _SerializedCommands became _serialize_command_frames; it sat after the function's return, unreachable, and still referenced the proxy's _commands slot.
213 lines
7.5 KiB
TOML
213 lines
7.5 KiB
TOML
# pyproject.toml
|
|
[build-system]
|
|
requires = ["setuptools>=61.0", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "meshcore-bot"
|
|
version = "1.1.0"
|
|
description = "MeshCore Bot with commands for mesh testing, utilities, and service integration."
|
|
readme = "README.md"
|
|
# Table form rather than the SPDX string form: the string form needs
|
|
# setuptools>=77, above this project's declared build requirement.
|
|
license = { text = "MIT" }
|
|
authors = [{ name = "Adam Gessaman", email = "adam@gessaman.com" }]
|
|
requires-python = ">=3.10"
|
|
classifiers = [
|
|
"License :: OSI Approved :: MIT License",
|
|
"Programming Language :: Python :: 3",
|
|
"Operating System :: OS Independent",
|
|
]
|
|
dependencies = [
|
|
"pyserial>=3.5",
|
|
"bleak>=0.20.0",
|
|
"asyncio-mqtt>=0.11.0",
|
|
"configparser>=5.3.0",
|
|
"python-dateutil>=2.8.2",
|
|
"apscheduler>=3.10.0",
|
|
"colorlog>=6.7.0",
|
|
"requests>=2.31.0",
|
|
"urllib3>=2.0.0",
|
|
"pyephem>=4.1.4",
|
|
"geopy>=2.3.0",
|
|
"maidenhead>=1.4.0",
|
|
"pytz>=2023.3",
|
|
"aiohttp>=3.8.0",
|
|
# 2.3.14+ required for send_msg_with_retry's ACK handling (see requirements.txt)
|
|
"meshcore>=2.3.14",
|
|
"openmeteo-requests>=1.7.2",
|
|
"requests-cache>=1.1.1",
|
|
"retry-requests>=1.0.0",
|
|
"flask>=2.3.0",
|
|
"flask-socketio>=5.3.0",
|
|
"flask-compress>=1.14",
|
|
# Still required: `next_cmd` backs contact and channel operations that have no
|
|
# equivalent in the meshcore library yet (see requirements.txt).
|
|
"meshcore-cli>=1.4.0",
|
|
"feedparser>=6.0.10",
|
|
"paho-mqtt>=1.6.0",
|
|
"cryptography>=41.0.0",
|
|
"pynacl>=1.5.0",
|
|
"aiosqlite>=0.19.0",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
profanity = ["better-profanity>=0.7.0", "unidecode>=1.3.0"]
|
|
geo = ["pycountry>=23.12.0", "us>=2.0.0"]
|
|
lang = ["langdetect>=1.0.9"]
|
|
test = ["pytest>=7.0", "pytest-asyncio>=0.21", "pytest-mock>=3.10", "pytest-cov>=4.0", "pytest-timeout>=2.1.0", "types-requests>=2.31"]
|
|
docs = ["mkdocs-material>=9.0.0", "mkdocs-exclude>=1.0.0"]
|
|
|
|
[project.scripts]
|
|
meshcore-bot = "meshcore_bot:main"
|
|
meshcore-viewer = "modules.web_viewer.app:main"
|
|
|
|
[tool.setuptools]
|
|
py-modules = ["meshcore_bot"]
|
|
include-package-data = false
|
|
license-files = ["LICENSE"]
|
|
|
|
[tool.setuptools.packages.find]
|
|
where = ["."]
|
|
include = ["modules*", "translations*", "data*"]
|
|
exclude = ["tests*", "local*"]
|
|
|
|
[tool.setuptools.package-data]
|
|
modules = [
|
|
"web_viewer/templates/*.html",
|
|
"web_viewer/templates/**/*.html",
|
|
"web_viewer/static/*",
|
|
"web_viewer/static/*/*",
|
|
"web_viewer/static/*/*/*",
|
|
]
|
|
translations = ["*.json"]
|
|
"data.randomlines" = ["*.txt"]
|
|
|
|
[tool.ruff]
|
|
# Match CI/Makefile installs so lint rules do not drift on unpinned `pip install ruff`.
|
|
required-version = "==0.15.15"
|
|
line-length = 120
|
|
target-version = "py310"
|
|
exclude = [".venv", "build", "dist"]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "W", "I", "UP", "B", "C4", "SIM"]
|
|
ignore = [
|
|
"E501", # line too long (handled by formatter)
|
|
# Legacy-code tolerances — pervasive in pre-typed source, not worth a bulk churn:
|
|
"E701", # multiple-statements-on-one-line-colon
|
|
"E702", # multiple-statements-on-one-line-semicolon
|
|
"E711", # comparison-to-none (== None common in legacy)
|
|
"E712", # comparison-to-true (== True/False common in legacy)
|
|
"E722", # bare-except (used extensively in graceful-degradation patterns)
|
|
"E741", # ambiguous-variable-name (l, O, I in legacy loops)
|
|
"E402", # module-level import not at top (conditional imports pattern)
|
|
"F601", # multi-value-repeated-key-literal (legacy dict pattern)
|
|
"B007", # unused-loop-control-variable (rename to _ is a bulk churn)
|
|
"B023", # function-uses-loop-variable (legacy closures in packet capture)
|
|
"B904", # raise-without-from-in-except (legacy raise pattern)
|
|
"C408", # unnecessary-collection-call
|
|
"C414", # unnecessary-double-cast-or-process
|
|
# pyupgrade rules that would require a bulk typing rewrite — deferred to a code-quality PR:
|
|
"UP007", # non-pep604-annotation-union (Union[X, Y] -> X | Y)
|
|
"UP035", # deprecated-import (typing.List/Dict/... remain prevalent)
|
|
"UP041", # timeout-error-alias (socket.timeout -> TimeoutError)
|
|
"UP045", # non-pep604-annotation-optional (Optional[X] -> X | None)
|
|
# Simplification suggestions — stylistic, not enforced on legacy code:
|
|
"SIM102", # collapsible-if
|
|
"SIM103", # needless-bool
|
|
"SIM105", # suppressible-exception
|
|
"SIM108", # if-else-block-instead-of-if-exp (ternary preference is subjective)
|
|
"SIM109", # compare-with-tuple
|
|
"SIM110", # reimplemented-builtin
|
|
"SIM114", # if-with-same-arms
|
|
"SIM115", # open-file-with-context-handler
|
|
"SIM117", # multiple-with-statements
|
|
"SIM210", # if-expr-with-true-false
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/*" = ["S101"]
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# mypy — incremental strict mode
|
|
# ---------------------------------------------------------------------------
|
|
# Global baseline: safe non-breaking options.
|
|
# Per-module overrides below tighten settings for fully-typed modules.
|
|
[tool.mypy]
|
|
python_version = "3.11"
|
|
ignore_missing_imports = true
|
|
warn_unused_ignores = true
|
|
warn_return_any = false # too noisy until all modules are annotated
|
|
warn_unused_configs = true
|
|
no_implicit_optional = true
|
|
strict_optional = true
|
|
|
|
# New modules written with full type annotations get strict treatment.
|
|
[[tool.mypy.overrides]]
|
|
module = [
|
|
"modules.alert_format",
|
|
"modules.commands.schedule_command",
|
|
"modules.service_plugins.webhook_service",
|
|
"modules.service_plugins.base_service",
|
|
"modules.message_handler",
|
|
"modules.utils",
|
|
"modules.plugin_loader",
|
|
"modules.security_utils",
|
|
"modules.commands.base_command",
|
|
"modules.commands.rain_command",
|
|
]
|
|
disallow_untyped_defs = true
|
|
disallow_incomplete_defs = true
|
|
check_untyped_defs = true
|
|
no_implicit_optional = true
|
|
|
|
# Modules with known type errors not yet brought up to strict standard.
|
|
# Suppress all mypy errors for these modules until they are fully annotated.
|
|
[[tool.mypy.overrides]]
|
|
module = [
|
|
"modules.core",
|
|
"modules.command_manager",
|
|
"modules.feed_manager",
|
|
"modules.mesh_graph",
|
|
"modules.scheduler",
|
|
"modules.maintenance",
|
|
"modules.transmission_tracker",
|
|
"modules.commands.alert_command",
|
|
"modules.commands.announcements_command",
|
|
"modules.commands.aurora_command",
|
|
"modules.commands.channels_command",
|
|
"modules.commands.greeter_command",
|
|
"modules.commands.help_command",
|
|
"modules.commands.joke_command",
|
|
"modules.commands.multitest_command",
|
|
"modules.commands.prefix_command",
|
|
"modules.commands.wx_command",
|
|
"modules.commands.alternatives.wx_international",
|
|
"modules.commands.trace_command",
|
|
"modules.commands.solarforecast_command",
|
|
"modules.commands.roll_command",
|
|
"modules.commands.repeater_command",
|
|
"modules.commands.path_command",
|
|
"modules.commands.sports_command",
|
|
"modules.commands.aqi_command",
|
|
"modules.service_plugins.discord_bridge_service",
|
|
"modules.service_plugins.packet_capture_service",
|
|
"modules.service_plugins.weather_service",
|
|
"modules.web_viewer.app",
|
|
"modules.web_viewer.integration",
|
|
]
|
|
ignore_errors = true
|
|
|
|
# Pytest is configured in pytest.ini, not here. pytest.ini takes precedence over
|
|
# pyproject.toml, so a [tool.pytest.ini_options] block in this file would be dead
|
|
# config that silently drifts from the settings actually in effect.
|
|
|
|
[tool.coverage.run]
|
|
source = ["modules"]
|
|
omit = ["tests/*", ".venv/*"]
|
|
|
|
[tool.coverage.report]
|
|
fail_under = 50
|
|
show_missing = true
|