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.
Files changed (282) hide show
  1. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/CHANGELOG.md +19 -0
  2. {telepost_bot-2.78.1/telepost_bot.egg-info → telepost_bot-2.80.0}/PKG-INFO +1 -1
  3. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/callback_handlers.py +16 -1
  4. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/command_handlers.py +9 -3
  5. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/search_handlers.py +58 -0
  6. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/build_info.py +1 -1
  7. {telepost_bot-2.78.1 → telepost_bot-2.80.0/telepost_bot.egg-info}/PKG-INFO +1 -1
  8. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/SOURCES.txt +3 -2
  9. telepost_bot-2.80.0/tests/test_search_disabled_hint.py +247 -0
  10. telepost_bot-2.80.0/webapp/dist/assets/index-BxUwEgTT.css +1 -0
  11. telepost_bot-2.80.0/webapp/dist/assets/index-CM9sCW78.js +111 -0
  12. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/webapp/dist/index.html +2 -2
  13. telepost_bot-2.78.1/webapp/dist/assets/index-C-xxEN9W.js +0 -110
  14. telepost_bot-2.78.1/webapp/dist/assets/index-DJgL81We.css +0 -1
  15. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/LICENSE +0 -0
  16. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/MANIFEST.in +0 -0
  17. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/README.en.md +0 -0
  18. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/README.md +0 -0
  19. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/__init__.py +0 -0
  20. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/fly-telepost-policy.example.json +0 -0
  21. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/review_policy.md +0 -0
  22. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config/settings.py +0 -0
  23. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/config.ini.example +0 -0
  24. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/database/__init__.py +0 -0
  25. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/database/db_manager.py +0 -0
  26. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/__init__.py +0 -0
  27. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/api_commands.py +0 -0
  28. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/automation.py +0 -0
  29. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/botconfig.py +0 -0
  30. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/channel_listener.py +0 -0
  31. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/conversation.py +0 -0
  32. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/error_handler.py +0 -0
  33. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/index_handlers.py +0 -0
  34. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/mode_selection.py +0 -0
  35. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/moderation.py +0 -0
  36. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/preview_handlers.py +0 -0
  37. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/publish.py +0 -0
  38. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/reaction_stats.py +0 -0
  39. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/recovery.py +0 -0
  40. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/review.py +0 -0
  41. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/schedule_status.py +0 -0
  42. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/stats_handlers.py +0 -0
  43. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/handlers/upload.py +0 -0
  44. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/main.py +0 -0
  45. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/mcp_server/__init__.py +0 -0
  46. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/mcp_server/review_client.py +0 -0
  47. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/mcp_server/server.py +0 -0
  48. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/models/__init__.py +0 -0
  49. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/models/state.py +0 -0
  50. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/__init__.py +0 -0
  51. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/SOURCES.sha256 +0 -0
  52. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/__init__.py +0 -0
  53. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/error-mapping.json +0 -0
  54. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/ackresult.candidate_search.json +0 -0
  55. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/capabilities.pixivflow.json +0 -0
  56. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/event.job.expired.json +0 -0
  57. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/event.job.succeeded.json +0 -0
  58. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/eventpage.candidate_search.json +0 -0
  59. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.cancelled.candidate_search.json +0 -0
  60. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.failed.no_candidate.json +0 -0
  61. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.queued.json +0 -0
  62. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.running.json +0 -0
  63. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/job.succeeded.candidate_search.json +0 -0
  64. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/jobpage.candidate_search.json +0 -0
  65. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/task.candidate_search.json +0 -0
  66. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/fixtures/task.candidate_search.target_only.json +0 -0
  67. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/protocol/v1/protocol.schema.json +0 -0
  68. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/pyproject.toml +0 -0
  69. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/run.py +0 -0
  70. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/services/__init__.py +0 -0
  71. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/services/review_service.py +0 -0
  72. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/setup.cfg +0 -0
  73. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/__init__.py +0 -0
  74. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/api/__init__.py +0 -0
  75. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/__init__.py +0 -0
  76. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/admin_ops.py +0 -0
  77. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/automation.py +0 -0
  78. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/delivery_planner.py +0 -0
  79. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/editorial.py +0 -0
  80. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/hot.py +0 -0
  81. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/novel_fallback_card.py +0 -0
  82. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/novel_preview.py +0 -0
  83. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/pixivflow_jobs.py +0 -0
  84. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/post_media.py +0 -0
  85. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/posts.py +0 -0
  86. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/publication.py +0 -0
  87. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/reaction_backfill.py +0 -0
  88. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/recovery.py +0 -0
  89. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/refetch.py +0 -0
  90. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/rerender.py +0 -0
  91. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/resubmit.py +0 -0
  92. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/review_queue.py +0 -0
  93. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/submitter_notify.py +0 -0
  94. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/application/telepress_provider.py +0 -0
  95. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/__init__.py +0 -0
  96. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/automation.py +0 -0
  97. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/delivery.py +0 -0
  98. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/editorial.py +0 -0
  99. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/errors.py +0 -0
  100. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/hot.py +0 -0
  101. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/media.py +0 -0
  102. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/navigation.py +0 -0
  103. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/novel_preview.py +0 -0
  104. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/packing.py +0 -0
  105. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/presentation.py +0 -0
  106. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/presentation_policy.py +0 -0
  107. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/refetch_state.py +0 -0
  108. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/review.py +0 -0
  109. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/domain/submission.py +0 -0
  110. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/__init__.py +0 -0
  111. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/auth.py +0 -0
  112. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/rbac.py +0 -0
  113. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/role_bindings.py +0 -0
  114. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/miniapp/session.py +0 -0
  115. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/__init__.py +0 -0
  116. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/audit.py +0 -0
  117. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/cli.py +0 -0
  118. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/doctor.py +0 -0
  119. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/errors.py +0 -0
  120. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/observability/redact.py +0 -0
  121. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/__init__.py +0 -0
  122. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/__init__.py +0 -0
  123. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/automation.py +0 -0
  124. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/columns.py +0 -0
  125. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/editorial.py +0 -0
  126. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/ledger.py +0 -0
  127. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/media_assets.py +0 -0
  128. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/moderation.py +0 -0
  129. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/novel_preview.py +0 -0
  130. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/posts.py +0 -0
  131. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/protocol_events.py +0 -0
  132. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/recovery.py +0 -0
  133. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/refetch.py +0 -0
  134. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/reviews.py +0 -0
  135. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/storage/sqlite/submitter_notifications.py +0 -0
  136. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/__init__.py +0 -0
  137. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/capture.py +0 -0
  138. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/__init__.py +0 -0
  139. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/discussion.py +0 -0
  140. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/executor.py +0 -0
  141. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/gateway.py +0 -0
  142. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/legacy_runner.py +0 -0
  143. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/planner.py +0 -0
  144. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/preparation.py +0 -0
  145. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/registry.py +0 -0
  146. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/delivery/sender.py +0 -0
  147. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/review_keyboard.py +0 -0
  148. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost/telegram/review_stager.py +0 -0
  149. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/dependency_links.txt +0 -0
  150. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/entry_points.txt +0 -0
  151. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/requires.txt +0 -0
  152. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/telepost_bot.egg-info/top_level.txt +0 -0
  153. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_admin_api.py +0 -0
  154. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_anonymous.py +0 -0
  155. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_api_commands.py +0 -0
  156. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_api_server.py +0 -0
  157. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_api_version.py +0 -0
  158. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_architecture_application.py +0 -0
  159. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_architecture_core.py +0 -0
  160. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_automation.py +0 -0
  161. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_backfill_attribution.py +0 -0
  162. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_blacklist.py +0 -0
  163. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_bot_token_logging.py +0 -0
  164. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_botconfig.py +0 -0
  165. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_boundary.py +0 -0
  166. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_callback_contract.py +0 -0
  167. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_caption_mentions.py +0 -0
  168. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_compatibility.py +0 -0
  169. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_conversation_flow.py +0 -0
  170. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_data_class_cleanup.py +0 -0
  171. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_database.py +0 -0
  172. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_database_operations.py +0 -0
  173. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_delivery_planner.py +0 -0
  174. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_disposition.py +0 -0
  175. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_doctor.py +0 -0
  176. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_editorial.py +0 -0
  177. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_file_validator.py +0 -0
  178. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_frozen_support.py +0 -0
  179. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_handlers.py +0 -0
  180. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_heat_calculator.py +0 -0
  181. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_helper_functions.py +0 -0
  182. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_hot_domain.py +0 -0
  183. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_identity_provenance.py +0 -0
  184. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_integration.py +0 -0
  185. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_legacy_delivery_port_navigation.py +0 -0
  186. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_logic_flow.py +0 -0
  187. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_logical_submissions.py +0 -0
  188. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_maintenance_jobs.py +0 -0
  189. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_manager_notifications.py +0 -0
  190. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_decision_audit.py +0 -0
  191. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_delivery_pipeline.py +0 -0
  192. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_domain.py +0 -0
  193. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_preparation.py +0 -0
  194. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_media_proxy_delivery.py +0 -0
  195. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_menu_shortcut_single_reply.py +0 -0
  196. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_messages.py +0 -0
  197. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_api.py +0 -0
  198. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_auth.py +0 -0
  199. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_content.py +0 -0
  200. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_entries.py +0 -0
  201. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_history.py +0 -0
  202. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_miniapp_rbac.py +0 -0
  203. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_mixed_media_integrity.py +0 -0
  204. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_moderation_and_admin_ux.py +0 -0
  205. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_navigation.py +0 -0
  206. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_no_owner_probe.py +0 -0
  207. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_novel_cover_preview.py +0 -0
  208. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_novel_delivery_semantics.py +0 -0
  209. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_novel_preview.py +0 -0
  210. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_observability.py +0 -0
  211. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_observability_lifecycle.py +0 -0
  212. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_online_reading_root_button.py +0 -0
  213. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_packing_ssot.py +0 -0
  214. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_polling_server.py +0 -0
  215. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_preview_pagination.py +0 -0
  216. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_production_handler_graph.py +0 -0
  217. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_protocol_contract.py +0 -0
  218. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publication_presentation.py +0 -0
  219. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publish_idempotency.py +0 -0
  220. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publish_regressions.py +0 -0
  221. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_publish_reply.py +0 -0
  222. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_reaction_backfill.py +0 -0
  223. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_reaction_stats.py +0 -0
  224. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_recovery.py +0 -0
  225. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch.py +0 -0
  226. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_card_state.py +0 -0
  227. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_events.py +0 -0
  228. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_job_lifecycle.py +0 -0
  229. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_reason_normalization.py +0 -0
  230. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_refetch_replacement.py +0 -0
  231. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_remote_fetch_fallback.py +0 -0
  232. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_rerender.py +0 -0
  233. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_resubmit.py +0 -0
  234. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review.py +0 -0
  235. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review_consistency.py +0 -0
  236. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review_http_mcp.py +0 -0
  237. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_review_service.py +0 -0
  238. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_robustness.py +0 -0
  239. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_run_dispatcher.py +0 -0
  240. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_run_mode.py +0 -0
  241. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_run_supervisor.py +0 -0
  242. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_schedule_outcomes.py +0 -0
  243. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_search_recovery.py +0 -0
  244. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_security.py +0 -0
  245. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_shutdown.py +0 -0
  246. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_streaming_uploads.py +0 -0
  247. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_submission_timeout_idempotency.py +0 -0
  248. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_telegram_golden_messages.py +0 -0
  249. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_telegram_media_cache.py +0 -0
  250. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_upload.py +0 -0
  251. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_user_friendly_messages.py +0 -0
  252. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_ready.py +0 -0
  253. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_router.py +0 -0
  254. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_secret.py +0 -0
  255. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_secret_logging.py +0 -0
  256. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_webhook_static.py +0 -0
  257. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/tests/test_work_id_wire.py +0 -0
  258. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/ui/__init__.py +0 -0
  259. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/ui/keyboards.py +0 -0
  260. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/ui/messages.py +0 -0
  261. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/__init__.py +0 -0
  262. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/api_server.py +0 -0
  263. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/api_tokens.py +0 -0
  264. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/blacklist.py +0 -0
  265. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/cache.py +0 -0
  266. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/config_wizard.py +0 -0
  267. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/database.py +0 -0
  268. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/file_validator.py +0 -0
  269. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/heat_calculator.py +0 -0
  270. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/helper_functions.py +0 -0
  271. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/index_manager.py +0 -0
  272. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/logging_config.py +0 -0
  273. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/maintenance_jobs.py +0 -0
  274. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/polling_server.py +0 -0
  275. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/run_mode.py +0 -0
  276. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/runtime_policy.py +0 -0
  277. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/schedule_watchdog.py +0 -0
  278. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/search_engine.py +0 -0
  279. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/submission.py +0 -0
  280. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/utils/webhook_server.py +0 -0
  281. {telepost_bot-2.78.1 → telepost_bot-2.80.0}/webapp/__init__.py +0 -0
  282. {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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: telepost-bot
3
- Version: 2.78.1
3
+ Version: 2.80.0
4
4
  Summary: Telegram submission, review, and publication bot with a Mini App, HTTP API, and multi-bot supervisor.
5
5
  Author: redtidev1918
6
6
  License-Expression: MIT
@@ -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 search_posts_by_tag
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
- await update.message.reply_text(
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.78.1" # x-release-please-version
11
+ RELEASE_VERSION = "2.80.0" # x-release-please-version
12
12
  RELEASE_COMMIT = "dev"
13
13
  BUILD_DATE = "dev"
14
14
  SERVICE = "telepost"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: telepost-bot
3
- Version: 2.78.1
3
+ Version: 2.80.0
4
4
  Summary: Telegram submission, review, and publication bot with a Mini App, HTTP API, and multi-bot supervisor.
5
5
  Author: redtidev1918
6
6
  License-Expression: MIT
@@ -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-C-xxEN9W.js
277
- webapp/dist/assets/index-DJgL81We.css
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