Corrected NameChangedEvent assumptions and log output to match Owncast webhook payloads.
CI / Formatting (push) Successful in 6s
CI / Linting (push) Successful in 5s
CI / Tests (Python 3.12) (push) Successful in 2m47s
CI / Tests (Python 3.13) (push) Successful in 2m50s
CI / Tests (Python 3.14) (push) Successful in 2m42s
CI / Type Checking (push) Successful in 9s
CI / Spelling (push) Successful in 5s

This commit is contained in:
2026-04-26 18:18:03 -04:00
parent d998429a76
commit 9b6151e7e6
5 changed files with 34 additions and 10 deletions
+1 -1
Submodule docs updated: c8be588c4d...b4e73c6f16
+13 -3
View File
@@ -224,7 +224,8 @@ class NameChangedEvent:
def from_dict(cls, data: dict[str, Any]) -> NameChangedEvent: def from_dict(cls, data: dict[str, Any]) -> NameChangedEvent:
"""Create a NameChangedEvent from webhook JSON data. """Create a NameChangedEvent from webhook JSON data.
Note: The user object contains the OLD display name in user.display_name. In Owncast webhook payloads, ``user.display_name`` already
reflects the new display name.
:param data: The event data from the webhook payload. :param data: The event data from the webhook payload.
:return: A populated NameChangedEvent instance. :return: A populated NameChangedEvent instance.
@@ -444,9 +445,18 @@ def log_event(event_type: EventType, event: Event) -> None:
case UserPartedEvent(user=user): case UserPartedEvent(user=user):
logger.info("[%s] %s parted.", event_type, user.display_name) logger.info("[%s] %s parted.", event_type, user.display_name)
case NameChangedEvent(user=user, new_name=new_name): case NameChangedEvent(user=user, new_name=new_name):
logger.info( # Owncast does not send the old name explicitly, so use the most
"[%s] %s changed name to %s.", event_type, user.display_name, new_name # recent distinct entry from the user's name history when present.
old_name = next(
(
name
for name in reversed(user.previous_names)
if name and name != new_name
),
None,
) )
subject = old_name if old_name is not None else "Display name"
logger.info("[%s] %s changed to %s.", event_type, subject, new_name)
case StreamStartedEvent(stream_title=title): case StreamStartedEvent(stream_title=title):
logger.info('[%s] Stream started: "%s"', event_type, title) logger.info('[%s] Stream started: "%s"', event_type, title)
case StreamStoppedEvent(): case StreamStoppedEvent():
+3 -2
View File
@@ -174,7 +174,8 @@ def make_name_changed_event(
) -> NameChangedEvent: ) -> NameChangedEvent:
"""Build a NameChangedEvent with sensible test defaults. """Build a NameChangedEvent with sensible test defaults.
:param user: The user changing names; defaults to ``make_user()``. :param user: The user changing names; defaults to a user whose
``display_name`` matches ``new_name``.
:param client_id: Client connection identifier. :param client_id: Client connection identifier.
:param new_name: The new display name. :param new_name: The new display name.
:param event_id: Unique event identifier. :param event_id: Unique event identifier.
@@ -182,7 +183,7 @@ def make_name_changed_event(
:return: A populated NameChangedEvent instance. :return: A populated NameChangedEvent instance.
""" """
return NameChangedEvent( return NameChangedEvent(
user=user if user is not None else make_user(), user=user if user is not None else make_user(display_name=new_name),
client_id=client_id, client_id=client_id,
new_name=new_name, new_name=new_name,
event_id=event_id, event_id=event_id,
+16 -4
View File
@@ -357,14 +357,14 @@ class TestNameChangedEvent:
def test_full_payload(self) -> None: def test_full_payload(self) -> None:
"""All fields are populated from a realistic NAME_CHANGE eventData dict.""" """All fields are populated from a realistic NAME_CHANGE eventData dict."""
data: dict[str, Any] = { data: dict[str, Any] = {
"user": _user_dict(display_name="OldName"), "user": _user_dict(display_name="NewName"),
"clientId": 5, "clientId": 5,
"newName": "NewName", "newName": "NewName",
"id": "evt-002", "id": "evt-002",
"timestamp": "2026-01-15T14:00:00Z", "timestamp": "2026-01-15T14:00:00Z",
} }
event = NameChangedEvent.from_dict(data) event = NameChangedEvent.from_dict(data)
assert event.user.display_name == "OldName" assert event.user.display_name == "NewName"
assert event.new_name == "NewName" assert event.new_name == "NewName"
assert event.client_id == 5 assert event.client_id == 5
assert event.event_id == "evt-002" assert event.event_id == "evt-002"
@@ -631,12 +631,24 @@ class TestLogEvent:
pytest.param( pytest.param(
EventType.NAME_CHANGE, EventType.NAME_CHANGE,
make_name_changed_event( make_name_changed_event(
user=make_user(display_name="OldName"), user=make_user(
display_name="NewName",
previous_names=["OldName", "NewName"],
),
new_name="NewName", new_name="NewName",
), ),
"[NAME_CHANGE] OldName changed name to NewName.", "[NAME_CHANGE] OldName changed to NewName.",
id="name-change", id="name-change",
), ),
pytest.param(
EventType.NAME_CHANGE,
make_name_changed_event(
user=make_user(display_name="NewName"),
new_name="NewName",
),
"[NAME_CHANGE] Display name changed to NewName.",
id="name-change-fallback",
),
pytest.param( pytest.param(
EventType.STREAM_STARTED, EventType.STREAM_STARTED,
make_stream_started_event(stream_title="Playing Minecraft"), make_stream_started_event(stream_title="Playing Minecraft"),
+1
View File
@@ -226,6 +226,7 @@ class TestMakeNameChangedEvent:
event = make_name_changed_event() event = make_name_changed_event()
assert isinstance(event, NameChangedEvent) assert isinstance(event, NameChangedEvent)
assert isinstance(event.user, User) assert isinstance(event.user, User)
assert event.user.display_name == "NewName"
assert event.client_id == 0 assert event.client_id == 0
assert event.new_name == "NewName" assert event.new_name == "NewName"
assert event.event_id == "test-name-change-id" assert event.event_id == "test-name-change-id"