|
|
@@ -308,6 +308,13 @@ _PRESENCE_LABELS = {
|
|
|
PresenceStatus.OFFLINE: "offline",
|
|
|
}
|
|
|
|
|
|
+# Proto enum name (``PRESENCE_STATUS_ONLINE``) → chattolib member, built once
|
|
|
+# from the enum's own values. The realtime wire carries the presence status as
|
|
|
+# a plain int (protobuf runtime without native enum wrappers), so every
|
|
|
+# presence change needs this lookup — a linear scan per event would be O(n) on
|
|
|
+# a hot path for a five-member enum.
|
|
|
+_PRESENCE_BY_PROTO_NAME = {member.value: member for member in PresenceStatus}
|
|
|
+
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
class _Mentions:
|
|
|
@@ -2668,23 +2675,18 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
and (user := self._user_cache.get(changed_user_id)) is not None
|
|
|
):
|
|
|
# The realtime payload carries the proto enum as a plain int
|
|
|
- # (or proto enum wrapper); chattolib's own bot.py coerces the
|
|
|
- # same field the same way — read the wire shape, never
|
|
|
- # ``.name`` directly, which raised AttributeError on every
|
|
|
- # presence change and dropped inbound messages inside the
|
|
|
- # reconnect gaps that followed.
|
|
|
+ # (protobuf runtime without native enum wrappers); chattolib's
|
|
|
+ # own bot.py coerces the same field the same way. Read the
|
|
|
+ # wire shape, never ``.name`` directly, which raised
|
|
|
+ # AttributeError on every presence change and dropped inbound
|
|
|
+ # messages inside the reconnect gaps that followed.
|
|
|
raw_status = event.payload.status
|
|
|
if isinstance(raw_status, PresenceStatus):
|
|
|
status = raw_status
|
|
|
else:
|
|
|
proto_name = ProtoPresenceStatus.Name(int(raw_status))
|
|
|
- status = next(
|
|
|
- (
|
|
|
- member
|
|
|
- for member in PresenceStatus
|
|
|
- if member.value == proto_name
|
|
|
- ),
|
|
|
- PresenceStatus.UNSPECIFIED,
|
|
|
+ status = _PRESENCE_BY_PROTO_NAME.get(
|
|
|
+ proto_name, PresenceStatus.UNSPECIFIED
|
|
|
)
|
|
|
user.presence_status = status
|
|
|
logger.debug(
|