Simplified title-change notification handling.
This commit is contained in:
@@ -134,12 +134,11 @@ def _classify_notification(
|
|||||||
observation: StreamStateObservation,
|
observation: StreamStateObservation,
|
||||||
*,
|
*,
|
||||||
offline_duration_seconds: int | None = None,
|
offline_duration_seconds: int | None = None,
|
||||||
went_offline_after_last_notification: bool = False,
|
|
||||||
) -> _NotificationKind:
|
) -> _NotificationKind:
|
||||||
"""Classify the live/title notification policy for a transition.
|
"""Classify the live/title notification policy for a transition.
|
||||||
|
|
||||||
The caller supplies timing context because it comes from the monitor's
|
The caller supplies offline duration because it comes from the monitor's
|
||||||
monotonic offline cache and notification-service cache, not the transition.
|
monotonic offline cache, not the transition.
|
||||||
"""
|
"""
|
||||||
match transition.kind:
|
match transition.kind:
|
||||||
case _StreamTransitionKind.WENT_LIVE:
|
case _StreamTransitionKind.WENT_LIVE:
|
||||||
@@ -153,8 +152,6 @@ def _classify_notification(
|
|||||||
return _NotificationKind.LIVE
|
return _NotificationKind.LIVE
|
||||||
|
|
||||||
case _StreamTransitionKind.TITLE_CHANGED:
|
case _StreamTransitionKind.TITLE_CHANGED:
|
||||||
if went_offline_after_last_notification:
|
|
||||||
return _NotificationKind.LIVE
|
|
||||||
return _NotificationKind.TITLE_CHANGE
|
return _NotificationKind.TITLE_CHANGE
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
@@ -314,7 +311,6 @@ class StreamMonitor:
|
|||||||
# Notification policy may need runtime timing context in addition to
|
# Notification policy may need runtime timing context in addition to
|
||||||
# the stored state and latest observation.
|
# the stored state and latest observation.
|
||||||
offline_duration_seconds: int | None = None
|
offline_duration_seconds: int | None = None
|
||||||
went_offline_after_last_notification = False
|
|
||||||
|
|
||||||
match transition.kind:
|
match transition.kind:
|
||||||
case _StreamTransitionKind.FIRST_OBSERVATION:
|
case _StreamTransitionKind.FIRST_OBSERVATION:
|
||||||
@@ -343,11 +339,6 @@ class StreamMonitor:
|
|||||||
)
|
)
|
||||||
case _StreamTransitionKind.TITLE_CHANGED:
|
case _StreamTransitionKind.TITLE_CHANGED:
|
||||||
self.log.info("[%s] Stream title was changed!", domain)
|
self.log.info("[%s] Stream title was changed!", domain)
|
||||||
# If we saw an offline event after the last notification, send
|
|
||||||
# a go-live notice instead of only a title-change notice.
|
|
||||||
went_offline_after_last_notification = self.offline_timer_cache[
|
|
||||||
domain
|
|
||||||
] > self.notification_service.get_last_notification_time(domain)
|
|
||||||
case _StreamTransitionKind.WENT_OFFLINE:
|
case _StreamTransitionKind.WENT_OFFLINE:
|
||||||
self.offline_timer_cache[domain] = time.monotonic()
|
self.offline_timer_cache[domain] = time.monotonic()
|
||||||
self.log.info("[%s] Stream is now offline.", domain)
|
self.log.info("[%s] Stream is now offline.", domain)
|
||||||
@@ -359,7 +350,6 @@ class StreamMonitor:
|
|||||||
old_state,
|
old_state,
|
||||||
observation,
|
observation,
|
||||||
offline_duration_seconds=offline_duration_seconds,
|
offline_duration_seconds=offline_duration_seconds,
|
||||||
went_offline_after_last_notification=went_offline_after_last_notification,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
stream_config: StreamConfigObservation | None = None
|
stream_config: StreamConfigObservation | None = None
|
||||||
|
|||||||
@@ -457,32 +457,6 @@ class TestClassifyNotification:
|
|||||||
|
|
||||||
assert notification_kind is _NotificationKind.TITLE_CHANGE
|
assert notification_kind is _NotificationKind.TITLE_CHANGE
|
||||||
|
|
||||||
def test_went_offline_after_last_notification_notifies_live(self) -> None:
|
|
||||||
"""Notify live when stream went offline after the last notification."""
|
|
||||||
old_state = StreamState(
|
|
||||||
domain="example.com",
|
|
||||||
title="Old",
|
|
||||||
online=True,
|
|
||||||
status_since="2026-01-01T10:00:00+00:00",
|
|
||||||
)
|
|
||||||
observation = _stream_observation(
|
|
||||||
domain="example.com",
|
|
||||||
title="New",
|
|
||||||
online=True,
|
|
||||||
observed_at="2026-01-01T12:00:00+00:00",
|
|
||||||
)
|
|
||||||
transition = _classify_transition(old_state, observation)
|
|
||||||
|
|
||||||
notification_kind = _classify_notification(
|
|
||||||
transition,
|
|
||||||
old_state,
|
|
||||||
observation,
|
|
||||||
went_offline_after_last_notification=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert notification_kind is _NotificationKind.LIVE
|
|
||||||
|
|
||||||
|
|
||||||
class TestUpdateAllStreams:
|
class TestUpdateAllStreams:
|
||||||
"""Parallel stream update orchestration."""
|
"""Parallel stream update orchestration."""
|
||||||
|
|
||||||
@@ -959,12 +933,12 @@ class TestUpdateStreamTitleChange:
|
|||||||
)
|
)
|
||||||
assert owncast.config_call_count == 1
|
assert owncast.config_call_count == 1
|
||||||
|
|
||||||
async def test_sends_go_live_when_last_notification_before_offline(
|
async def test_sends_title_change_after_offline_gap(
|
||||||
self,
|
self,
|
||||||
stream_repo: StreamRepository,
|
stream_repo: StreamRepository,
|
||||||
subscription_repo: SubscriptionRepository,
|
subscription_repo: SubscriptionRepository,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Send go-live instead of title-change after an offline gap."""
|
"""Send title-change when an already-online stream changes title."""
|
||||||
owncast = _StubOwncastClient(
|
owncast = _StubOwncastClient(
|
||||||
stream_state=_stream_observation(
|
stream_state=_stream_observation(
|
||||||
domain="title.com",
|
domain="title.com",
|
||||||
@@ -991,8 +965,8 @@ class TestUpdateStreamTitleChange:
|
|||||||
status_since="2026-01-01T12:00:00+00:00",
|
status_since="2026-01-01T12:00:00+00:00",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Offline timer is MORE recent than last notification,
|
# Offline timer is more recent than the last notification, but title
|
||||||
# and both are old enough to pass rate limiting
|
# changes remain title changes once the stream is already online.
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
monitor.offline_timer_cache["title.com"] = (
|
monitor.offline_timer_cache["title.com"] = (
|
||||||
now - _SECONDS_BETWEEN_NOTIFICATIONS - 100
|
now - _SECONDS_BETWEEN_NOTIFICATIONS - 100
|
||||||
@@ -1005,7 +979,7 @@ class TestUpdateStreamTitleChange:
|
|||||||
assert result is True
|
assert result is True
|
||||||
assert len(client.sent_messages) == 1
|
assert len(client.sent_messages) == 1
|
||||||
assert client.sent_messages[0].content.body == (
|
assert client.sent_messages[0].content.body == (
|
||||||
"🎥 Title Stream is now live!\n"
|
"📝 Title Stream has changed its stream title!\n"
|
||||||
"Stream Title: Updated Title\n"
|
"Stream Title: Updated Title\n"
|
||||||
"\n"
|
"\n"
|
||||||
"To tune in, visit: https://title.com/"
|
"To tune in, visit: https://title.com/"
|
||||||
|
|||||||
Reference in New Issue
Block a user