Skip to content

Commit 8c16e44

Browse files
authored
Merge pull request #176 from denis1011101/feature/game-chat-fixes-and-weekly-reset
Feature/game chat fixes and weekly reset
2 parents ab7a25e + 6041232 commit 8c16e44

4 files changed

Lines changed: 107 additions & 10 deletions

File tree

app/models/game.rb

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,27 @@ def coaches
6161
[ coach, second_coach ].compact
6262
end
6363

64-
# Чат игры живёт до конца дня игры. У повторяющейся игры одной даты нет,
65-
# поэтому там режим просто протухает через сутки, и его включают заново —
66-
# угадывать, какое занятие человек имел в виду, мы не беремся.
67-
CHAT_MAX_TTL = 24.hours
68-
64+
# Чат живёт ровно столько же, сколько состав: до еженедельного сброса участий —
65+
# ночь с пятницы на субботу, 4 утра (ResetParticipationsJob в recurring.yml,
66+
# там же чистка прошедших разовых игр). Раньше у серии режим протухал через
67+
# сутки, и человек оставался без чата посреди недели, хотя состав, которому он
68+
# писал, никуда не делся.
69+
CHAT_RESET_WDAY = 6
70+
CHAT_RESET_HOUR = 4
71+
72+
def self.next_weekly_reset_at(from = Time.current)
73+
from = from.in_time_zone
74+
reset = from.beginning_of_day.change(hour: CHAT_RESET_HOUR)
75+
reset += 1.day while reset <= from || reset.wday != CHAT_RESET_WDAY
76+
reset
77+
end
78+
79+
# Считаем не «ближайшую субботу вообще», а ту, что действительно разберёт этот
80+
# состав: сброс сносит участия уже отыгранного вхождения, а чистка — только
81+
# прошедшую разовую игру. Игру, назначенную после ближайшей субботы, эта ночь
82+
# не касается, и гасить её чат в 4 утра не за что.
6983
def chat_open_until
70-
return CHAT_MAX_TTL.from_now if recurring? || date.blank?
71-
72-
closes_at = date.end_of_day
84+
closes_at = Game.next_weekly_reset_at(chat_window_anchor)
7385
closes_at > Time.current ? closes_at : nil
7486
end
7587

@@ -472,6 +484,13 @@ def next_time
472484
time
473485
end
474486

487+
# День, после которого состав этой игры пойдёт под нож: у серии — ближайшее
488+
# вхождение, у разовой — её дата.
489+
def chat_window_anchor
490+
occurrence = recurring? ? (next_date || date) : date
491+
occurrence ? occurrence.end_of_day : Time.current
492+
end
493+
475494
def previous_occurrence_before_next_date
476495
return nil unless recurring? && next_date.present?
477496

config/recurring.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ production:
2929
reset_participations:
3030
class: ResetParticipationsJob
3131
schedule: every saturday at 4am
32+
# В ту же ночь, что и сброс: до неё состав и чат прошедшей игры живы, после —
33+
# нет. Раньше обе задачи ходили из /etc/cron.d/getcourt по пятницам; кроновые
34+
# строки с 2026-09-02 закомментированы.
35+
cleanup_past_one_off_games:
36+
class: CleanupPastOneOffGamesJob
37+
schedule: every saturday at 4:15am
38+
cleanup_past_tournaments:
39+
class: CleanupPastTournamentsJob
40+
schedule: every saturday at 4:30am
3241
reset_inactive_player_statistics:
3342
class: ResetInactivePlayerStatisticsJob
3443
schedule: at 4:30am every day

test/models/game_test.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,4 +404,55 @@ class GameTest < ActiveSupport::TestCase
404404
ensure
405405
game&.destroy
406406
end
407+
408+
test "chat stays open until the weekly reset, not for a fixed day" do
409+
game = Game.create!(court: courts(:one), user: users(:one), date: Date.new(2026, 9, 3), kind: "game")
410+
411+
# Среда: игра в четверг, разберёт её состав ближайшая суббота.
412+
travel_to Time.zone.local(2026, 9, 2, 21, 0) do
413+
assert_equal Time.zone.local(2026, 9, 5, 4, 0), Game.next_weekly_reset_at
414+
assert_equal Time.zone.local(2026, 9, 5, 4, 0), game.chat_open_until
415+
assert game.chat_open?
416+
end
417+
ensure
418+
game&.destroy
419+
end
420+
421+
# Ближайшая суббота ничего не делает с составом игры, назначенной после неё:
422+
# чистка сносит только прошедшие разовые игры, сброс — только отыгранные серии.
423+
test "a game scheduled beyond the coming reset keeps its chat until its own" do
424+
one_off = Game.create!(court: courts(:one), user: users(:one), date: Date.new(2026, 9, 20), kind: "game")
425+
# Серия с ближайшим вхождением в воскресенье — уже за субботней границей.
426+
series = Game.create!(court: courts(:one), user: users(:one), date: Date.new(2026, 9, 6), recurring: true, kind: "game")
427+
428+
travel_to Time.zone.local(2026, 9, 2, 21, 0) do
429+
assert_equal Time.zone.local(2026, 9, 26, 4, 0), one_off.chat_open_until
430+
assert_equal Time.zone.local(2026, 9, 12, 4, 0), series.chat_open_until
431+
assert one_off.chat_open?
432+
assert series.chat_open?
433+
end
434+
ensure
435+
[ one_off, series ].compact.each(&:destroy)
436+
end
437+
438+
test "chat closes once the cleanup that removes the game has passed" do
439+
game = Game.create!(court: courts(:one), user: users(:one), date: Date.new(2026, 8, 25), kind: "game")
440+
441+
travel_to Time.zone.local(2026, 9, 2, 21, 0) do
442+
assert_nil game.chat_open_until
443+
assert_not game.chat_open?
444+
end
445+
ensure
446+
game&.destroy
447+
end
448+
449+
test "the chat window rolls over to the next week right at the reset" do
450+
travel_to Time.zone.local(2026, 9, 5, 3, 59) do
451+
assert_equal Time.zone.local(2026, 9, 5, 4, 0), Game.next_weekly_reset_at
452+
end
453+
454+
travel_to Time.zone.local(2026, 9, 5, 4, 0) do
455+
assert_equal Time.zone.local(2026, 9, 12, 4, 0), Game.next_weekly_reset_at
456+
end
457+
end
407458
end

test/telegram/chat/relay_test.rb

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,29 @@ class Telegram::Chat::RelayTest < ActiveSupport::TestCase
6363
end
6464
end
6565

66-
test "a finished game stops accepting messages" do
66+
# Отыгранная игра живёт в базе до еженедельной чистки, и состав вместе с ней:
67+
# обсудить её после матча — обычное дело, поэтому чат закрывается не концом
68+
# дня игры, а сбросом.
69+
test "a played game keeps its chat until the weekly reset" do
70+
enqueued = nil
71+
6772
in_chat_mode do
6873
@game.update_columns(date: Date.current - 2, recurring: false)
6974

70-
stub_singleton(Telegram::RelayChatMessageJob, :perform_later, ->(*) { flunk "игра прошла" }) do
75+
stub_singleton(Telegram::RelayChatMessageJob, :perform_later, ->(*args) { enqueued = args }) do
76+
assert Telegram::Chat::Relay.handle_message(tg_message("привет"))
77+
end
78+
assert_equal @game.id, Telegram::Chat::Session.game_id(@player.telegram_chat_id)
79+
end
80+
81+
assert_equal [ @game.id, @player.id, "привет" ], enqueued
82+
end
83+
84+
test "a deleted game closes the chat mode" do
85+
in_chat_mode do
86+
@game.destroy
87+
88+
stub_singleton(Telegram::RelayChatMessageJob, :perform_later, ->(*) { flunk "игры больше нет" }) do
7189
assert_not Telegram::Chat::Relay.handle_message(tg_message("привет"))
7290
end
7391
assert_nil Telegram::Chat::Session.game_id(@player.telegram_chat_id)

0 commit comments

Comments
 (0)