chore: add changelog and documentation updates
All checks were successful
CI / lint-and-test (push) Successful in 17s
All checks were successful
CI / lint-and-test (push) Successful in 17s
- Created a new `CHANGELOG.md` file to document all notable changes to the project, adhering to the Keep a Changelog format. - Updated `CONTRIBUTING.md` to include instructions for building and previewing documentation using MkDocs. - Added `mkdocs.yml` configuration for documentation generation, including navigation structure and theme settings. - Enhanced various documentation files, including API reference, architecture overview, configuration reference, and runbook, to provide comprehensive guidance for users and developers. - Included new sections in the README for changelog and documentation links, improving accessibility to project information.
This commit is contained in:
@@ -6,6 +6,11 @@ from . import commands, errors, group_duty_pin, import_duty_schedule
|
||||
|
||||
|
||||
def register_handlers(app: Application) -> None:
|
||||
"""Register all Telegram handlers (commands, import, group pin, error handler) on the application.
|
||||
|
||||
Args:
|
||||
app: python-telegram-bot Application instance.
|
||||
"""
|
||||
app.add_handler(commands.start_handler)
|
||||
app.add_handler(commands.help_handler)
|
||||
app.add_handler(commands.set_phone_handler)
|
||||
|
||||
@@ -17,6 +17,7 @@ from duty_teller.utils.user import build_full_name
|
||||
|
||||
|
||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Handle /start: register user in DB and send greeting."""
|
||||
if not update.message:
|
||||
return
|
||||
user = update.effective_user
|
||||
@@ -47,6 +48,7 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
|
||||
|
||||
async def set_phone(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Handle /set_phone [number]: set or clear phone (private chat only)."""
|
||||
if not update.message or not update.effective_user:
|
||||
return
|
||||
lang = get_lang(update.effective_user)
|
||||
@@ -87,7 +89,7 @@ async def set_phone(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
|
||||
|
||||
async def calendar_link(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Send personal calendar subscription URL (private chat only, access check)."""
|
||||
"""Handle /calendar_link: send personal ICS URL (private chat only; user must be in allowlist)."""
|
||||
if not update.message or not update.effective_user:
|
||||
return
|
||||
lang = get_lang(update.effective_user)
|
||||
@@ -136,6 +138,7 @@ async def calendar_link(update: Update, context: ContextTypes.DEFAULT_TYPE) -> N
|
||||
|
||||
|
||||
async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Handle /help: send list of commands (admins see import_duty_schedule)."""
|
||||
if not update.message or not update.effective_user:
|
||||
return
|
||||
lang = get_lang(update.effective_user)
|
||||
|
||||
@@ -14,6 +14,12 @@ logger = logging.getLogger(__name__)
|
||||
async def error_handler(
|
||||
update: Update | None, context: ContextTypes.DEFAULT_TYPE
|
||||
) -> None:
|
||||
"""Global error handler: log exception and reply with generic message if possible.
|
||||
|
||||
Args:
|
||||
update: Update that caused the error (may be None).
|
||||
context: Callback context.
|
||||
"""
|
||||
logger.exception("Exception while handling an update")
|
||||
if isinstance(update, Update) and update.effective_message:
|
||||
user = getattr(update, "effective_user", None)
|
||||
|
||||
@@ -91,6 +91,7 @@ async def _schedule_next_update(
|
||||
|
||||
|
||||
async def update_group_pin(context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Job callback: refresh pinned duty message and schedule next update at shift end."""
|
||||
chat_id = context.job.data.get("chat_id")
|
||||
if chat_id is None:
|
||||
return
|
||||
@@ -117,6 +118,7 @@ async def update_group_pin(context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
async def my_chat_member_handler(
|
||||
update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
) -> None:
|
||||
"""Handle bot added to or removed from group: send/pin duty message or delete pin record."""
|
||||
if not update.my_chat_member or not update.effective_user:
|
||||
return
|
||||
old = update.my_chat_member.old_chat_member
|
||||
@@ -185,6 +187,7 @@ def _get_all_pin_chat_ids_sync() -> list[int]:
|
||||
|
||||
|
||||
async def restore_group_pin_jobs(application) -> None:
|
||||
"""Restore scheduled pin-update jobs for all chats that have a pinned message (on startup)."""
|
||||
loop = asyncio.get_running_loop()
|
||||
chat_ids = await loop.run_in_executor(None, _get_all_pin_chat_ids_sync)
|
||||
for chat_id in chat_ids:
|
||||
@@ -194,6 +197,7 @@ async def restore_group_pin_jobs(application) -> None:
|
||||
|
||||
|
||||
async def pin_duty_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Handle /pin_duty: pin the current duty message in the group (reply to bot's message)."""
|
||||
if not update.message or not update.effective_chat or not update.effective_user:
|
||||
return
|
||||
chat = update.effective_chat
|
||||
|
||||
@@ -19,6 +19,7 @@ from duty_teller.utils.handover import parse_handover_time
|
||||
async def import_duty_schedule_cmd(
|
||||
update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
) -> None:
|
||||
"""Handle /import_duty_schedule: start two-step import (admin only); asks for handover time."""
|
||||
if not update.message or not update.effective_user:
|
||||
return
|
||||
lang = get_lang(update.effective_user)
|
||||
@@ -32,6 +33,7 @@ async def import_duty_schedule_cmd(
|
||||
async def handle_handover_time_text(
|
||||
update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
) -> None:
|
||||
"""Handle text message when awaiting handover time (e.g. 09:00 Europe/Moscow)."""
|
||||
if not update.message or not update.effective_user or not update.message.text:
|
||||
return
|
||||
if not context.user_data.get("awaiting_handover_time"):
|
||||
@@ -54,6 +56,7 @@ async def handle_handover_time_text(
|
||||
async def handle_duty_schedule_document(
|
||||
update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
) -> None:
|
||||
"""Handle uploaded JSON file: parse duty-schedule and run import."""
|
||||
if not update.message or not update.message.document or not update.effective_user:
|
||||
return
|
||||
if not context.user_data.get("awaiting_duty_schedule_file"):
|
||||
|
||||
Reference in New Issue
Block a user