test_message_cleanup.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. from __future__ import annotations
  2. import asyncio
  3. from types import SimpleNamespace
  4. import pytest
  5. class FakeMessage:
  6. def __init__(self, chat_id: int, message_id: int, *, text: str = "") -> None:
  7. self.chat = SimpleNamespace(
  8. id=chat_id,
  9. type=SimpleNamespace(value="supergroup" if chat_id < 0 else "private"),
  10. )
  11. self.id = message_id
  12. self.text = text
  13. self.caption = None
  14. self.delete_calls: list[bool] = []
  15. async def delete(self, *, revoke: bool = True) -> None:
  16. self.delete_calls.append(revoke)
  17. class FakeClient:
  18. def __init__(self) -> None:
  19. self.next_message_id = 100
  20. async def send_message(
  21. self,
  22. chat_id: int,
  23. _text: str,
  24. *,
  25. reply_to_message_id: int | None = None,
  26. ) -> FakeMessage:
  27. self.next_message_id += 1
  28. return FakeMessage(chat_id, self.next_message_id)
  29. @pytest.fixture
  30. def cleanup(app_modules):
  31. module = app_modules.load("wbb.services.message_cleanup")
  32. module._scheduled_messages.clear()
  33. module._active_interactions.clear()
  34. yield module
  35. module._scheduled_messages.clear()
  36. module._active_interactions.clear()
  37. async def test_group_cleanup_revokes_once_for_everyone(cleanup, monkeypatch):
  38. real_sleep = asyncio.sleep
  39. async def immediate_sleep(_delay: int) -> None:
  40. return None
  41. monkeypatch.setattr(cleanup.asyncio, "sleep", immediate_sleep)
  42. message = FakeMessage(-1001, 10, text="/points")
  43. cleanup.schedule_group_cleanup(message)
  44. cleanup.schedule_group_cleanup(message)
  45. await real_sleep(0)
  46. await real_sleep(0)
  47. assert message.delete_calls == [True]
  48. assert (-1001, 10) not in cleanup._scheduled_messages
  49. async def test_private_messages_are_not_scheduled(cleanup):
  50. message = FakeMessage(1001, 11, text="/points")
  51. cleanup.schedule_group_cleanup(message)
  52. assert not cleanup.is_group_cleanup_scheduled(message)
  53. assert message.delete_calls == []
  54. async def test_command_context_tracks_only_responses_in_the_same_group(
  55. cleanup, monkeypatch
  56. ):
  57. scheduled: list[FakeMessage] = []
  58. monkeypatch.setattr(
  59. cleanup,
  60. "schedule_group_cleanup",
  61. lambda *messages, **_kwargs: scheduled.extend(messages),
  62. )
  63. client = FakeClient()
  64. cleanup.install_group_interaction_tracking(client)
  65. command = FakeMessage(-1001, 12, text="/points")
  66. assert cleanup.begin_group_message_interaction(command)
  67. group_response = await client.send_message(
  68. -1001, "积分余额", reply_to_message_id=command.id
  69. )
  70. await client.send_message(-1001, "抽奖主消息")
  71. await client.send_message(-1002, "其他群通知", reply_to_message_id=command.id)
  72. await client.send_message(1001, "私聊通知", reply_to_message_id=command.id)
  73. assert scheduled == [command, group_response]
  74. async def test_plain_message_clears_previous_command_context(cleanup, monkeypatch):
  75. scheduled: list[FakeMessage] = []
  76. monkeypatch.setattr(
  77. cleanup,
  78. "schedule_group_cleanup",
  79. lambda *messages, **_kwargs: scheduled.extend(messages),
  80. )
  81. client = FakeClient()
  82. cleanup.install_group_interaction_tracking(client)
  83. command = FakeMessage(-1001, 13, text="/points")
  84. plain_message = FakeMessage(-1001, 14, text="大家好")
  85. assert cleanup.begin_group_message_interaction(command)
  86. assert not cleanup.begin_group_message_interaction(plain_message)
  87. await client.send_message(
  88. -1001, "普通自动回复", reply_to_message_id=plain_message.id
  89. )
  90. assert scheduled == [command]
  91. async def test_checkin_button_is_treated_as_group_interaction(cleanup, monkeypatch):
  92. scheduled: list[FakeMessage] = []
  93. monkeypatch.setattr(
  94. cleanup,
  95. "schedule_group_cleanup",
  96. lambda *messages, **_kwargs: scheduled.extend(messages),
  97. )
  98. message = FakeMessage(-1001, 15, text="签到")
  99. assert cleanup.begin_group_message_interaction(message, menu_texts={"签到"})
  100. assert scheduled == [message]
  101. async def test_callback_only_continues_an_already_ephemeral_menu(
  102. cleanup, monkeypatch
  103. ):
  104. scheduled: list[FakeMessage] = []
  105. monkeypatch.setattr(
  106. cleanup,
  107. "schedule_group_cleanup",
  108. lambda *messages, **_kwargs: scheduled.extend(messages),
  109. )
  110. client = FakeClient()
  111. cleanup.install_group_interaction_tracking(client)
  112. persistent_menu = FakeMessage(-1001, 16)
  113. query = SimpleNamespace(message=persistent_menu)
  114. assert not cleanup.begin_group_callback_interaction(query)
  115. await client.send_message(
  116. -1001, "不应自动删除", reply_to_message_id=persistent_menu.id
  117. )
  118. cleanup._scheduled_messages.add((-1001, 16))
  119. assert cleanup.begin_group_callback_interaction(query)
  120. callback_response = await client.send_message(
  121. -1001, "临时菜单回复", reply_to_message_id=persistent_menu.id
  122. )
  123. assert scheduled == [callback_response]