telepost-bot 2.78.1__tar.gz → 2.80.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/CHANGELOG.md +19 -0
- {telepost_bot-2.78.1/telepost_bot.egg-info → telepost_bot-2.80.0}/PKG-INFO +1 -1
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/callback_handlers.py +16 -1
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/command_handlers.py +9 -3
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/search_handlers.py +58 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/build_info.py +1 -1
- {telepost_bot-2.78.1 → telepost_bot-2.80.0/telepost_bot.egg-info}/PKG-INFO +1 -1
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/SOURCES.txt +3 -2
- telepost_bot-2.80.0/tests/test_search_disabled_hint.py +247 -0
- telepost_bot-2.80.0/webapp/dist/assets/index-BxUwEgTT.css +1 -0
- telepost_bot-2.80.0/webapp/dist/assets/index-CM9sCW78.js +111 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/webapp/dist/index.html +2 -2
- telepost_bot-2.78.1/webapp/dist/assets/index-C-xxEN9W.js +0 -110
- telepost_bot-2.78.1/webapp/dist/assets/index-DJgL81We.css +0 -1
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/LICENSE +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/MANIFEST.in +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/README.en.md +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/README.md +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/fly-telepost-policy.example.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/review_policy.md +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/settings.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config.ini.example +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/database/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/database/db_manager.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/api_commands.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/automation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/botconfig.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/channel_listener.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/conversation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/error_handler.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/index_handlers.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/mode_selection.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/moderation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/preview_handlers.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/publish.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/reaction_stats.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/recovery.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/review.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/schedule_status.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/stats_handlers.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/upload.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/main.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/mcp_server/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/mcp_server/review_client.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/mcp_server/server.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/models/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/models/state.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/SOURCES.sha256 +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/error-mapping.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/ackresult.candidate_search.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/capabilities.pixivflow.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/event.job.expired.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/event.job.succeeded.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/eventpage.candidate_search.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.cancelled.candidate_search.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.failed.no_candidate.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.queued.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.running.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.succeeded.candidate_search.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/jobpage.candidate_search.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/task.candidate_search.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/task.candidate_search.target_only.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/protocol.schema.json +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/pyproject.toml +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/run.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/services/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/services/review_service.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/setup.cfg +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/api/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/admin_ops.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/automation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/delivery_planner.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/editorial.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/hot.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/novel_fallback_card.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/novel_preview.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/pixivflow_jobs.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/post_media.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/posts.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/publication.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/reaction_backfill.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/recovery.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/refetch.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/rerender.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/resubmit.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/review_queue.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/submitter_notify.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/telepress_provider.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/automation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/delivery.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/editorial.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/errors.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/hot.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/media.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/navigation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/novel_preview.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/packing.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/presentation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/presentation_policy.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/refetch_state.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/review.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/submission.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/auth.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/rbac.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/role_bindings.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/session.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/audit.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/cli.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/doctor.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/errors.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/redact.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/automation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/columns.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/editorial.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/ledger.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/media_assets.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/moderation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/novel_preview.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/posts.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/protocol_events.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/recovery.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/refetch.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/reviews.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/submitter_notifications.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/capture.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/discussion.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/executor.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/gateway.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/legacy_runner.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/planner.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/preparation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/registry.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/sender.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/review_keyboard.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/review_stager.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/dependency_links.txt +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/entry_points.txt +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/requires.txt +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/top_level.txt +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_admin_api.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_anonymous.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_api_commands.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_api_server.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_api_version.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_architecture_application.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_architecture_core.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_automation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_backfill_attribution.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_blacklist.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_bot_token_logging.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_botconfig.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_boundary.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_callback_contract.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_caption_mentions.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_compatibility.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_conversation_flow.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_data_class_cleanup.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_database.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_database_operations.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_delivery_planner.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_disposition.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_doctor.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_editorial.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_file_validator.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_frozen_support.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_handlers.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_heat_calculator.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_helper_functions.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_hot_domain.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_identity_provenance.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_integration.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_legacy_delivery_port_navigation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_logic_flow.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_logical_submissions.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_maintenance_jobs.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_manager_notifications.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_decision_audit.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_delivery_pipeline.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_domain.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_preparation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_proxy_delivery.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_menu_shortcut_single_reply.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_messages.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_api.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_auth.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_content.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_entries.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_history.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_rbac.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_mixed_media_integrity.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_moderation_and_admin_ux.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_navigation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_no_owner_probe.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_novel_cover_preview.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_novel_delivery_semantics.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_novel_preview.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_observability.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_observability_lifecycle.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_online_reading_root_button.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_packing_ssot.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_polling_server.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_preview_pagination.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_production_handler_graph.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_protocol_contract.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publication_presentation.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publish_idempotency.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publish_regressions.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publish_reply.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_reaction_backfill.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_reaction_stats.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_recovery.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_card_state.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_events.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_job_lifecycle.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_reason_normalization.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_replacement.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_remote_fetch_fallback.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_rerender.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_resubmit.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review_consistency.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review_http_mcp.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review_service.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_robustness.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_run_dispatcher.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_run_mode.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_run_supervisor.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_schedule_outcomes.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_search_recovery.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_security.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_shutdown.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_streaming_uploads.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_submission_timeout_idempotency.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_telegram_golden_messages.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_telegram_media_cache.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_upload.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_user_friendly_messages.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_ready.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_router.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_secret.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_secret_logging.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_static.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_work_id_wire.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/ui/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/ui/keyboards.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/ui/messages.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/api_server.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/api_tokens.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/blacklist.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/cache.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/config_wizard.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/database.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/file_validator.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/heat_calculator.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/helper_functions.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/index_manager.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/logging_config.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/maintenance_jobs.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/polling_server.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/run_mode.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/runtime_policy.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/schedule_watchdog.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/search_engine.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/submission.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/webhook_server.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/webapp/__init__.py +0 -0
- {telepost_bot-2.78.1 → telepost_bot-2.80.0}/webapp/dist/assets/2d82b92e720462f8dd3b-Bh4Fahcw.svg +0 -0
|
@@ -8,6 +8,25 @@
|
|
|
8
8
|
> 发布说明面向普通用户:写「对用户有什么改变」,不写内部实现细节(模块名、函数名、配置键);
|
|
9
9
|
> 技术细节放进代码注释或 `docs/`。每个 Release 的正文由本文件对应版本段自动生成。
|
|
10
10
|
|
|
11
|
+
## [2.80.0](https://github.com/redtidev1918/TelePost/compare/v2.79.0...v2.80.0) (2026-10-04)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* **webapp:** add a mobile presentation kit and design tokens ([b1aab32](https://github.com/redtidev1918/TelePost/commit/b1aab325043ad5830bd0e7022980654c53633ddd))
|
|
17
|
+
* **webapp:** improve submission experience ([308c3d5](https://github.com/redtidev1918/TelePost/commit/308c3d5e872114898a7458b58b6346c1ffdb6267))
|
|
18
|
+
* **webapp:** make home a dashboard, hot a content browser, mine a history ([8edd729](https://github.com/redtidev1918/TelePost/commit/8edd72903030eafa412cfdd62df3102aa271c21e))
|
|
19
|
+
* **webapp:** redesign mobile mini app navigation ([7a5d141](https://github.com/redtidev1918/TelePost/commit/7a5d141f081f262f62e616fcc56b4eeb968ea3a7))
|
|
20
|
+
* **webapp:** redesign the Mini App for mobile (navigation, browsing, submit, review) ([46d6238](https://github.com/redtidev1918/TelePost/commit/46d6238173a7103f30afc25d0428e0dadf50c758))
|
|
21
|
+
* **webapp:** simplify reviewer workspace and review actions ([ba2d8aa](https://github.com/redtidev1918/TelePost/commit/ba2d8aa85a8772270d8c6841c26b6240a4195f9a))
|
|
22
|
+
|
|
23
|
+
## [2.79.0](https://github.com/redtidev1918/TelePost/compare/v2.78.1...v2.79.0) (2026-10-03)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
* **search:** tell users when search is disabled on this bot ([896a8e2](https://github.com/redtidev1918/TelePost/commit/896a8e281af8904c6c750a5b18675227699e845a))
|
|
29
|
+
|
|
11
30
|
## [2.78.1](https://github.com/redtidev1918/TelePost/compare/v2.78.0...v2.78.1) (2026-10-03)
|
|
12
31
|
|
|
13
32
|
|
|
@@ -11,7 +11,9 @@ from ui.messages import MessageFormatter
|
|
|
11
11
|
from database.db_manager import get_db
|
|
12
12
|
from utils.blacklist import remove_from_blacklist, is_owner
|
|
13
13
|
from handlers.stats_handlers import get_hot_posts
|
|
14
|
-
from handlers.search_handlers import
|
|
14
|
+
from handlers.search_handlers import (
|
|
15
|
+
reply_search_disabled, search_enabled, search_posts_by_tag,
|
|
16
|
+
)
|
|
15
17
|
|
|
16
18
|
logger = logging.getLogger(__name__)
|
|
17
19
|
|
|
@@ -104,6 +106,9 @@ async def handle_callback_query(update: Update, context: CallbackContext):
|
|
|
104
106
|
# 搜索-时间筛选
|
|
105
107
|
elif data.startswith("time_"):
|
|
106
108
|
# 记录时间筛选并提示输入关键词
|
|
109
|
+
if not search_enabled():
|
|
110
|
+
await reply_search_disabled(update, edit=True)
|
|
111
|
+
return
|
|
107
112
|
mapping = {
|
|
108
113
|
'time_day': 'day',
|
|
109
114
|
'time_week': 'week',
|
|
@@ -216,6 +221,11 @@ async def handle_search_action(update: Update, context: CallbackContext):
|
|
|
216
221
|
query = update.callback_query
|
|
217
222
|
await _safe_answer(query)
|
|
218
223
|
action = query.data.replace("search_", "")
|
|
224
|
+
|
|
225
|
+
# search_myposts 走数据库,不依赖搜索引擎,不受 SEARCH_ENABLED 影响。
|
|
226
|
+
if action != "myposts" and not search_enabled():
|
|
227
|
+
await reply_search_disabled(update, edit=True)
|
|
228
|
+
return
|
|
219
229
|
|
|
220
230
|
if action == "fulltext":
|
|
221
231
|
await query.edit_message_text(
|
|
@@ -247,6 +257,11 @@ async def handle_search_action(update: Update, context: CallbackContext):
|
|
|
247
257
|
async def handle_tag_search(update: Update, context: CallbackContext):
|
|
248
258
|
"""处理标签搜索"""
|
|
249
259
|
query = update.callback_query
|
|
260
|
+
|
|
261
|
+
if not search_enabled():
|
|
262
|
+
await reply_search_disabled(update, edit=True)
|
|
263
|
+
return
|
|
264
|
+
|
|
250
265
|
tag = query.data.replace("tag_search_", "")
|
|
251
266
|
|
|
252
267
|
await _safe_answer(query, f"正在搜索标签: {tag}")
|
|
@@ -142,10 +142,16 @@ async def handle_menu_shortcuts(update: Update, context: CallbackContext) -> Non
|
|
|
142
142
|
handled = True
|
|
143
143
|
# 搜索
|
|
144
144
|
elif text.endswith("搜索"):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
reply_markup=Keyboards.search_options()
|
|
145
|
+
from handlers.search_handlers import (
|
|
146
|
+
reply_search_disabled, search_enabled,
|
|
148
147
|
)
|
|
148
|
+
if not search_enabled():
|
|
149
|
+
await reply_search_disabled(update)
|
|
150
|
+
else:
|
|
151
|
+
await update.message.reply_text(
|
|
152
|
+
"🔍 请输入搜索关键词,或点击下方选项:",
|
|
153
|
+
reply_markup=Keyboards.search_options()
|
|
154
|
+
)
|
|
149
155
|
handled = True
|
|
150
156
|
# 帮助
|
|
151
157
|
elif text.endswith("帮助"):
|
|
@@ -21,12 +21,53 @@ logger = logging.getLogger(__name__)
|
|
|
21
21
|
# 简单缓存:标签云 60s
|
|
22
22
|
_tag_cloud_cache = TTLCache(default_ttl=60, max_size=16)
|
|
23
23
|
|
|
24
|
+
# 搜索未启用时的统一提示(与 Mini App 未启用的文案风格保持一致)。
|
|
25
|
+
# 只拦依赖搜索引擎的入口;「我的投稿 / 标签云 / 热门内容」走数据库,不受
|
|
26
|
+
# SEARCH_ENABLED 影响,因此提示里把它们作为仍然可用的替代路径。
|
|
27
|
+
SEARCH_DISABLED_MESSAGE = (
|
|
28
|
+
"ℹ️ 搜索功能在当前机器人尚未启用。\n\n"
|
|
29
|
+
"你仍然可以:\n"
|
|
30
|
+
"• 📋 我的投稿 —— 查看自己发布过的内容\n"
|
|
31
|
+
"• 🏷️ 标签云 —— 按标签浏览\n"
|
|
32
|
+
"• 🔥 热门内容 —— 看看最近的热门\n\n"
|
|
33
|
+
"如需使用搜索,请联系频道管理员开启后再试。"
|
|
34
|
+
)
|
|
35
|
+
|
|
24
36
|
|
|
25
37
|
def is_owner(user_id: int) -> bool:
|
|
26
38
|
"""检查用户是否是 OWNER"""
|
|
27
39
|
return OWNER_ID and user_id == OWNER_ID
|
|
28
40
|
|
|
29
41
|
|
|
42
|
+
def search_enabled() -> bool:
|
|
43
|
+
"""当前 bot 是否启用搜索。
|
|
44
|
+
|
|
45
|
+
多 bot 部署下 SEARCH_ENABLED 可被 ``BOT{n}_SEARCH_ENABLED`` 逐 bot 覆盖,
|
|
46
|
+
所以必须从 config.settings 现读,不能在模块导入时缓存。
|
|
47
|
+
"""
|
|
48
|
+
from config.settings import SEARCH_ENABLED
|
|
49
|
+
return bool(SEARCH_ENABLED)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def reply_search_disabled(update: Update, *, edit: bool = False) -> None:
|
|
53
|
+
"""回复「搜索未启用」提示,命令 / 菜单 / 回调三种入口通用。"""
|
|
54
|
+
# 回调应答由调用方负责(每个 callback query 只能应答一次)。
|
|
55
|
+
query = getattr(update, "callback_query", None)
|
|
56
|
+
if query is not None:
|
|
57
|
+
try:
|
|
58
|
+
if edit:
|
|
59
|
+
await query.edit_message_text(SEARCH_DISABLED_MESSAGE)
|
|
60
|
+
else:
|
|
61
|
+
await query.message.reply_text(SEARCH_DISABLED_MESSAGE)
|
|
62
|
+
return
|
|
63
|
+
except Exception:
|
|
64
|
+
logger.warning("搜索未启用提示(回调)发送失败", exc_info=True)
|
|
65
|
+
return
|
|
66
|
+
message = getattr(update, "message", None) or getattr(update, "effective_message", None)
|
|
67
|
+
if message is not None:
|
|
68
|
+
await message.reply_text(SEARCH_DISABLED_MESSAGE)
|
|
69
|
+
|
|
70
|
+
|
|
30
71
|
async def search_posts(update: Update, context: CallbackContext):
|
|
31
72
|
"""
|
|
32
73
|
搜索已发布的帖子 - 使用全文搜索引擎(支持中文分词)
|
|
@@ -49,6 +90,11 @@ async def search_posts(update: Update, context: CallbackContext):
|
|
|
49
90
|
update: Telegram 更新对象
|
|
50
91
|
context: 回调上下文
|
|
51
92
|
"""
|
|
93
|
+
# 搜索未启用时不再进入帮助/检索流程,直接给出明确说明。
|
|
94
|
+
if not search_enabled():
|
|
95
|
+
await reply_search_disabled(update)
|
|
96
|
+
return
|
|
97
|
+
|
|
52
98
|
try:
|
|
53
99
|
# 解析参数
|
|
54
100
|
if not context.args:
|
|
@@ -320,6 +366,14 @@ async def handle_search_input(update: Update, context: CallbackContext):
|
|
|
320
366
|
mode = context.user_data.get('search_mode')
|
|
321
367
|
if not mode:
|
|
322
368
|
return # 未处于搜索输入模式,交给其他处理器
|
|
369
|
+
|
|
370
|
+
# 开关可能在用户进入搜索模式之后才被关掉(例如 bot 重启换配置),此时
|
|
371
|
+
# 明确提示并退出搜索模式,避免用户一直停在「等待输入关键词」的状态。
|
|
372
|
+
if not search_enabled():
|
|
373
|
+
context.user_data['search_mode'] = None
|
|
374
|
+
await reply_search_disabled(update)
|
|
375
|
+
return
|
|
376
|
+
|
|
323
377
|
text = (update.message.text or '').strip()
|
|
324
378
|
if not text:
|
|
325
379
|
await update.message.reply_text("❌ 请输入搜索关键词")
|
|
@@ -350,6 +404,10 @@ async def search_posts_by_tag(update: Update, context: CallbackContext, tag: str
|
|
|
350
404
|
context: 回调上下文
|
|
351
405
|
tag: 要搜索的标签
|
|
352
406
|
"""
|
|
407
|
+
if not search_enabled():
|
|
408
|
+
await reply_search_disabled(update)
|
|
409
|
+
return
|
|
410
|
+
|
|
353
411
|
# 如果没有提供标签,从context.args获取
|
|
354
412
|
if tag is None:
|
|
355
413
|
if not context.args:
|
|
@@ -8,7 +8,7 @@ to the entry points; when present it overrides these committed defaults.
|
|
|
8
8
|
# Bumped automatically by release-please (extra-files in
|
|
9
9
|
# release-please-config.json). Release bundles and images override it at build
|
|
10
10
|
# time with the exact version/commit; this constant is the local-dev fallback.
|
|
11
|
-
RELEASE_VERSION = "2.
|
|
11
|
+
RELEASE_VERSION = "2.80.0" # x-release-please-version
|
|
12
12
|
RELEASE_COMMIT = "dev"
|
|
13
13
|
BUILD_DATE = "dev"
|
|
14
14
|
SERVICE = "telepost"
|
|
@@ -232,6 +232,7 @@ tests/test_run_dispatcher.py
|
|
|
232
232
|
tests/test_run_mode.py
|
|
233
233
|
tests/test_run_supervisor.py
|
|
234
234
|
tests/test_schedule_outcomes.py
|
|
235
|
+
tests/test_search_disabled_hint.py
|
|
235
236
|
tests/test_search_recovery.py
|
|
236
237
|
tests/test_security.py
|
|
237
238
|
tests/test_shutdown.py
|
|
@@ -273,5 +274,5 @@ utils/webhook_server.py
|
|
|
273
274
|
webapp/__init__.py
|
|
274
275
|
webapp/dist/index.html
|
|
275
276
|
webapp/dist/assets/2d82b92e720462f8dd3b-Bh4Fahcw.svg
|
|
276
|
-
webapp/dist/assets/index-
|
|
277
|
-
webapp/dist/assets/index-
|
|
277
|
+
webapp/dist/assets/index-BxUwEgTT.css
|
|
278
|
+
webapp/dist/assets/index-CM9sCW78.js
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""搜索关闭时,所有搜索入口都要给出「尚未启用」提示,而不是静默或报错。
|
|
2
|
+
|
|
3
|
+
背景:``SEARCH_ENABLED=false``(多 bot 下是 ``BOT{n}_SEARCH_ENABLED=false``)
|
|
4
|
+
时搜索引擎根本不会初始化,但 ``/search`` 和底部菜单「🔍 搜索」此前仍会照常
|
|
5
|
+
进入流程:命令会打到 ``get_search_engine()``,菜单则会弹出一组点了也没用的
|
|
6
|
+
搜索选项键盘。用户看到的是卡住或空结果,而不是「这个功能没开」。
|
|
7
|
+
|
|
8
|
+
本文件锁死两件事:
|
|
9
|
+
1. 关闭时,命令 / 菜单 / 内联搜索按钮都只回一条「尚未启用」说明;
|
|
10
|
+
2. 不牵连不依赖搜索引擎的入口——「我的投稿 / 标签云 / 热门内容」照旧可用,
|
|
11
|
+
且搜索输入模式不会被无限期留在 user_data 里。
|
|
12
|
+
"""
|
|
13
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
from telegram import Chat, Message, MessageEntity, Update, User
|
|
17
|
+
from telegram.ext import Application, ApplicationHandlerStop, MessageHandler, filters
|
|
18
|
+
|
|
19
|
+
from handlers import command_handlers
|
|
20
|
+
from handlers.callback_handlers import handle_search_action, handle_tag_search
|
|
21
|
+
from handlers.search_handlers import (
|
|
22
|
+
SEARCH_DISABLED_MESSAGE,
|
|
23
|
+
search_posts,
|
|
24
|
+
handle_search_input,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _message_update(text, user_id=42):
|
|
29
|
+
user = User(id=user_id, is_bot=False, first_name="T", username="tester")
|
|
30
|
+
chat = Chat(id=user_id, type="private")
|
|
31
|
+
kw = dict(message_id=1, date=0, chat=chat, from_user=user, text=text)
|
|
32
|
+
if text.startswith("/"):
|
|
33
|
+
kw["entities"] = (
|
|
34
|
+
MessageEntity(type="bot_command", offset=0, length=len(text.split()[0])),
|
|
35
|
+
)
|
|
36
|
+
return Update(update_id=1, message=Message(**kw))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _command_update(args=None):
|
|
40
|
+
"""/search 命令:message 用 AsyncMock,避免真的走 Bot API。"""
|
|
41
|
+
update = MagicMock()
|
|
42
|
+
update.callback_query = None
|
|
43
|
+
update.message = AsyncMock()
|
|
44
|
+
update.message.text = "/search" + (" " + " ".join(args) if args else "")
|
|
45
|
+
update.message.chat.type = "private"
|
|
46
|
+
update.channel_post = None
|
|
47
|
+
update.edited_channel_post = None
|
|
48
|
+
update.effective_user = MagicMock(id=42)
|
|
49
|
+
return update
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _callback_update(data):
|
|
53
|
+
query = AsyncMock()
|
|
54
|
+
query.data = data
|
|
55
|
+
query.message = AsyncMock()
|
|
56
|
+
update = MagicMock()
|
|
57
|
+
update.callback_query = query
|
|
58
|
+
update.message = None
|
|
59
|
+
return update, query
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class _RecordingBot:
|
|
63
|
+
"""记录出站文本的最小 Bot 替身(与菜单回归测试同一套路)。"""
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self.outbox = []
|
|
67
|
+
self._mid = 1000
|
|
68
|
+
self.id = 777
|
|
69
|
+
self.username = "testbot"
|
|
70
|
+
self.name = "testbot"
|
|
71
|
+
|
|
72
|
+
async def initialize(self):
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
async def shutdown(self):
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
async def send_message(self, chat_id, text, **kw):
|
|
79
|
+
self.outbox.append(text)
|
|
80
|
+
return MagicMock(message_id=self._mid)
|
|
81
|
+
|
|
82
|
+
async def edit_message_text(self, *a, **kw):
|
|
83
|
+
return MagicMock(message_id=self._mid)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _menu_app(bot):
|
|
87
|
+
app = Application.builder().bot(bot).build()
|
|
88
|
+
app.add_handler(
|
|
89
|
+
MessageHandler(filters.TEXT & ~filters.COMMAND, command_handlers.handle_menu_shortcuts),
|
|
90
|
+
group=998,
|
|
91
|
+
)
|
|
92
|
+
return app
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.mark.asyncio
|
|
96
|
+
@pytest.mark.parametrize("args", [None, ["python"]])
|
|
97
|
+
async def test_search_command_when_disabled(monkeypatch, args):
|
|
98
|
+
"""/search(带不带关键词都一样)只回一条未启用提示,且不碰搜索引擎。"""
|
|
99
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", False)
|
|
100
|
+
engine_calls = []
|
|
101
|
+
monkeypatch.setattr(
|
|
102
|
+
"handlers.search_handlers.get_search_engine",
|
|
103
|
+
lambda *a, **kw: engine_calls.append(1),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
update = _command_update(args)
|
|
107
|
+
await search_posts(update, MagicMock(args=args or []))
|
|
108
|
+
|
|
109
|
+
update.message.reply_text.assert_awaited_once()
|
|
110
|
+
assert update.message.reply_text.await_args.args[0] == SEARCH_DISABLED_MESSAGE
|
|
111
|
+
assert "尚未启用" in update.message.reply_text.await_args.args[0]
|
|
112
|
+
assert engine_calls == [], "关闭时不该再去取搜索引擎实例"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.asyncio
|
|
116
|
+
async def test_search_command_when_enabled_still_answers_help(monkeypatch):
|
|
117
|
+
"""开启时保持原行为:无参数仍给搜索帮助,不是未启用提示。"""
|
|
118
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", True)
|
|
119
|
+
|
|
120
|
+
update = _command_update(None)
|
|
121
|
+
await search_posts(update, MagicMock(args=[]))
|
|
122
|
+
|
|
123
|
+
sent = update.message.reply_text.await_args.args[0]
|
|
124
|
+
assert "尚未启用" not in sent
|
|
125
|
+
assert "搜索帮助" in sent
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@pytest.mark.asyncio
|
|
129
|
+
async def test_menu_search_button_when_disabled(monkeypatch):
|
|
130
|
+
"""底部菜单「🔍 搜索」关闭时只回一条未启用提示(不落到 catch_all 兜底)。"""
|
|
131
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", False)
|
|
132
|
+
bot = _RecordingBot()
|
|
133
|
+
app = _menu_app(bot)
|
|
134
|
+
await app.initialize()
|
|
135
|
+
try:
|
|
136
|
+
update = _message_update("🔍 搜索")
|
|
137
|
+
update.set_bot(bot)
|
|
138
|
+
update.message.set_bot(bot)
|
|
139
|
+
await app.process_update(update)
|
|
140
|
+
finally:
|
|
141
|
+
await app.shutdown()
|
|
142
|
+
|
|
143
|
+
assert len(bot.outbox) == 1, f"应只回一条,实际 {bot.outbox}"
|
|
144
|
+
assert "尚未启用" in bot.outbox[0]
|
|
145
|
+
assert "我没看懂" not in bot.outbox[0]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@pytest.mark.asyncio
|
|
149
|
+
async def test_menu_search_button_when_enabled(monkeypatch):
|
|
150
|
+
"""开启时菜单照旧引导输入关键词。"""
|
|
151
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", True)
|
|
152
|
+
bot = _RecordingBot()
|
|
153
|
+
app = _menu_app(bot)
|
|
154
|
+
await app.initialize()
|
|
155
|
+
try:
|
|
156
|
+
update = _message_update("🔍 搜索")
|
|
157
|
+
update.set_bot(bot)
|
|
158
|
+
update.message.set_bot(bot)
|
|
159
|
+
await app.process_update(update)
|
|
160
|
+
finally:
|
|
161
|
+
await app.shutdown()
|
|
162
|
+
|
|
163
|
+
assert len(bot.outbox) == 1
|
|
164
|
+
assert "请输入搜索关键词" in bot.outbox[0]
|
|
165
|
+
assert "尚未启用" not in bot.outbox[0]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@pytest.mark.asyncio
|
|
169
|
+
async def test_menu_search_button_raises_handler_stop_when_disabled(monkeypatch):
|
|
170
|
+
"""提示之后仍然要中断后续 group,否则用户会额外收到兜底文案。"""
|
|
171
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", False)
|
|
172
|
+
bot = _RecordingBot()
|
|
173
|
+
update = _message_update("🔍 搜索")
|
|
174
|
+
update.set_bot(bot)
|
|
175
|
+
update.message.set_bot(bot)
|
|
176
|
+
context = MagicMock()
|
|
177
|
+
context.user_data = {}
|
|
178
|
+
|
|
179
|
+
with pytest.raises(ApplicationHandlerStop):
|
|
180
|
+
await command_handlers.handle_menu_shortcuts(update, context)
|
|
181
|
+
|
|
182
|
+
assert bot.outbox == [SEARCH_DISABLED_MESSAGE]
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@pytest.mark.asyncio
|
|
186
|
+
@pytest.mark.parametrize("data", ["search_fulltext", "search_tag", "search_time"])
|
|
187
|
+
async def test_inline_search_actions_when_disabled(monkeypatch, data):
|
|
188
|
+
"""内联搜索按钮关闭时原地改写为未启用提示。"""
|
|
189
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", False)
|
|
190
|
+
update, query = _callback_update(data)
|
|
191
|
+
|
|
192
|
+
await handle_search_action(update, MagicMock(user_data={}))
|
|
193
|
+
|
|
194
|
+
query.edit_message_text.assert_awaited_once()
|
|
195
|
+
assert query.edit_message_text.await_args.args[0] == SEARCH_DISABLED_MESSAGE
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@pytest.mark.asyncio
|
|
199
|
+
async def test_inline_myposts_unaffected_when_disabled(monkeypatch):
|
|
200
|
+
"""「我的投稿」走数据库,搜索关闭时也必须照常可用。"""
|
|
201
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", False)
|
|
202
|
+
called = {}
|
|
203
|
+
|
|
204
|
+
async def _fake_myposts(update, context):
|
|
205
|
+
called["ok"] = True
|
|
206
|
+
|
|
207
|
+
# handle_search_action 内部是函数级 import,patch 源头即可。
|
|
208
|
+
monkeypatch.setattr("handlers.search_handlers.get_my_posts", _fake_myposts)
|
|
209
|
+
|
|
210
|
+
update, query = _callback_update("search_myposts")
|
|
211
|
+
await handle_search_action(update, MagicMock(user_data={}))
|
|
212
|
+
|
|
213
|
+
assert called.get("ok"), "我的投稿不应被搜索开关拦下"
|
|
214
|
+
query.edit_message_text.assert_not_awaited()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@pytest.mark.asyncio
|
|
218
|
+
async def test_tag_search_callback_when_disabled(monkeypatch):
|
|
219
|
+
"""标签搜索回调关闭时给提示,不去取搜索引擎。"""
|
|
220
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", False)
|
|
221
|
+
update, query = _callback_update("tag_search_python")
|
|
222
|
+
|
|
223
|
+
await handle_tag_search(update, MagicMock(user_data={}))
|
|
224
|
+
|
|
225
|
+
query.edit_message_text.assert_awaited_once()
|
|
226
|
+
assert query.edit_message_text.await_args.args[0] == SEARCH_DISABLED_MESSAGE
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@pytest.mark.asyncio
|
|
230
|
+
async def test_search_input_exits_mode_when_disabled(monkeypatch):
|
|
231
|
+
"""开关在用户进入搜索模式后才关闭时:提示并退出搜索模式,避免卡住。"""
|
|
232
|
+
monkeypatch.setattr("config.settings.SEARCH_ENABLED", False)
|
|
233
|
+
update = MagicMock()
|
|
234
|
+
update.callback_query = None
|
|
235
|
+
update.message = AsyncMock()
|
|
236
|
+
update.message.text = "python"
|
|
237
|
+
update.message.chat.type = "private"
|
|
238
|
+
update.channel_post = None
|
|
239
|
+
update.edited_channel_post = None
|
|
240
|
+
context = MagicMock()
|
|
241
|
+
context.user_data = {"search_mode": "fulltext"}
|
|
242
|
+
|
|
243
|
+
await handle_search_input(update, context)
|
|
244
|
+
|
|
245
|
+
assert context.user_data["search_mode"] is None, "必须退出搜索输入模式"
|
|
246
|
+
update.message.reply_text.assert_awaited_once()
|
|
247
|
+
assert update.message.reply_text.await_args.args[0] == SEARCH_DISABLED_MESSAGE
|