Move Presence class to its own python file
It's not a ModelItem
This commit is contained in:
parent
692c78f398
commit
1b5a09c052
|
@ -18,9 +18,10 @@ from .matrix_client import MatrixClient
|
||||||
from .media_cache import MediaCache
|
from .media_cache import MediaCache
|
||||||
from .models import SyncId
|
from .models import SyncId
|
||||||
from .models.filters import FieldSubstringFilter
|
from .models.filters import FieldSubstringFilter
|
||||||
from .models.items import Account, Presence
|
from .models.items import Account
|
||||||
from .models.model import Model
|
from .models.model import Model
|
||||||
from .models.model_store import ModelStore
|
from .models.model_store import ModelStore
|
||||||
|
from .presence import Presence
|
||||||
from .user_files import Accounts, History, Theme, UISettings, UIState
|
from .user_files import Accounts, History, Theme, UISettings, UIState
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
|
|
|
@ -42,10 +42,11 @@ from .errors import (
|
||||||
from .html_markdown import HTML_PROCESSOR as HTML
|
from .html_markdown import HTML_PROCESSOR as HTML
|
||||||
from .media_cache import Media, Thumbnail
|
from .media_cache import Media, Thumbnail
|
||||||
from .models.items import (
|
from .models.items import (
|
||||||
ZERO_DATE, Account, Event, Member, Presence, Room, Upload, UploadStatus,
|
ZERO_DATE, Account, Event, Member, Room, Upload, UploadStatus,
|
||||||
)
|
)
|
||||||
from .models.model_store import ModelStore
|
from .models.model_store import ModelStore
|
||||||
from .nio_callbacks import NioCallbacks
|
from .nio_callbacks import NioCallbacks
|
||||||
|
from .presence import Presence
|
||||||
from .pyotherside_events import AlertRequested, LoopException
|
from .pyotherside_events import AlertRequested, LoopException
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
|
@ -10,8 +10,10 @@ from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import lxml # nosec
|
import lxml # nosec
|
||||||
|
|
||||||
import nio
|
import nio
|
||||||
|
|
||||||
|
from ..presence import Presence
|
||||||
from ..utils import AutoStrEnum, auto
|
from ..utils import AutoStrEnum, auto
|
||||||
from .model_item import ModelItem
|
from .model_item import ModelItem
|
||||||
|
|
||||||
|
@ -19,12 +21,6 @@ OptionalExceptionType = Union[Type[None], Type[Exception]]
|
||||||
|
|
||||||
ZERO_DATE = datetime.fromtimestamp(0)
|
ZERO_DATE = datetime.fromtimestamp(0)
|
||||||
|
|
||||||
PRESENCE_ORDER: Dict[str, int] = {
|
|
||||||
"online": 0,
|
|
||||||
"unavailable": 1,
|
|
||||||
"offline": 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TypeSpecifier(AutoStrEnum):
|
class TypeSpecifier(AutoStrEnum):
|
||||||
"""Enum providing clarification of purpose for some matrix events."""
|
"""Enum providing clarification of purpose for some matrix events."""
|
||||||
|
@ -34,104 +30,6 @@ class TypeSpecifier(AutoStrEnum):
|
||||||
MembershipChange = auto()
|
MembershipChange = auto()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Presence:
|
|
||||||
"""Represents a single matrix user's presence fields.
|
|
||||||
|
|
||||||
These objects are stored in `Backend.presences`, indexed by user ID.
|
|
||||||
It must only be instanced when receiving a `PresenceEvent` or
|
|
||||||
registering an `Account` model item.
|
|
||||||
|
|
||||||
When receiving a `PresenceEvent`, we get or create a `Presence` object in
|
|
||||||
`Backend.presences` for the targeted user. If the user is registered in any
|
|
||||||
room, add its `Member` model item to `members`. Finally, update every
|
|
||||||
`Member` presence fields inside `members`.
|
|
||||||
|
|
||||||
When a room member is registered, we try to find a `Presence` in
|
|
||||||
`Backend.presences` for that user ID. If found, the `Member` item is added
|
|
||||||
to `members`.
|
|
||||||
|
|
||||||
When an Account model is registered, we create a `Presence` in
|
|
||||||
`Backend.presences` for the accountu's user ID whether the server supports
|
|
||||||
presence or not (we cannot know yet at this point),
|
|
||||||
and assign that `Account` to the `Presence.account` field.
|
|
||||||
|
|
||||||
Special attributes:
|
|
||||||
members: A `{room_id: Member}` dict for storing room members related to
|
|
||||||
this `Presence`. As each room has its own `Member`s objects, we
|
|
||||||
have to keep track of their presence fields. `Member`s are indexed
|
|
||||||
by room ID.
|
|
||||||
|
|
||||||
account: `Account` related to this `Presence`, if any. Should be
|
|
||||||
assigned when client starts (`MatrixClient._start()`) and
|
|
||||||
cleared when client stops (`MatrixClient._start()`).
|
|
||||||
"""
|
|
||||||
|
|
||||||
class State(AutoStrEnum):
|
|
||||||
offline = auto() # can mean offline, invisible or unknwon
|
|
||||||
unavailable = auto()
|
|
||||||
online = auto()
|
|
||||||
invisible = auto()
|
|
||||||
|
|
||||||
echo_unavailable = auto()
|
|
||||||
echo_online = auto()
|
|
||||||
echo_invisible = auto()
|
|
||||||
|
|
||||||
def __lt__(self, other: "Presence.State") -> bool:
|
|
||||||
return PRESENCE_ORDER[self.value] < PRESENCE_ORDER[other.value]
|
|
||||||
|
|
||||||
|
|
||||||
presence: State = State.offline
|
|
||||||
currently_active: bool = False
|
|
||||||
last_active_at: datetime = ZERO_DATE
|
|
||||||
status_msg: str = ""
|
|
||||||
|
|
||||||
members: Dict[str, "Member"] = field(default_factory=dict)
|
|
||||||
account: Optional["Account"] = None
|
|
||||||
|
|
||||||
def update_members(self) -> None:
|
|
||||||
"""Update presence fields of every `M̀ember` in `members`.
|
|
||||||
|
|
||||||
Currently it is only called when receiving a `PresenceEvent` and when
|
|
||||||
registering room members.
|
|
||||||
"""
|
|
||||||
|
|
||||||
for member in self.members.values():
|
|
||||||
member.set_fields(
|
|
||||||
presence = self.presence,
|
|
||||||
status_msg = self.status_msg,
|
|
||||||
last_active_at = self.last_active_at,
|
|
||||||
currently_active = self.currently_active,
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_account(self) -> None:
|
|
||||||
"""Update presence fields of `Account` related to this `Presence`."""
|
|
||||||
|
|
||||||
# Do not update if account is changing to invisible.
|
|
||||||
# When setting presence to invisible, the server will give us a
|
|
||||||
# presence event telling us we are offline, but we do not want to set
|
|
||||||
# account presence to offline.
|
|
||||||
if (
|
|
||||||
not self.account or
|
|
||||||
self.presence == self.State.offline and
|
|
||||||
self.account.presence != self.State.echo_invisible
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
fields: Dict[str, Any] = {}
|
|
||||||
|
|
||||||
if self.account.presence == self.State.echo_invisible:
|
|
||||||
fields["presence"] = self.State.invisible
|
|
||||||
else:
|
|
||||||
fields["presence"] = self.presence
|
|
||||||
fields["status_msg"] = self.status_msg
|
|
||||||
|
|
||||||
fields["last_active_at"] = self.last_active_at
|
|
||||||
fields["currently_active"] = self.currently_active
|
|
||||||
|
|
||||||
self.account.set_fields(**fields)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Account(ModelItem):
|
class Account(ModelItem):
|
||||||
"""A logged in matrix account."""
|
"""A logged in matrix account."""
|
||||||
|
|
|
@ -12,7 +12,8 @@ from urllib.parse import quote
|
||||||
import nio
|
import nio
|
||||||
|
|
||||||
from .html_markdown import HTML_PROCESSOR
|
from .html_markdown import HTML_PROCESSOR
|
||||||
from .models.items import Presence, TypeSpecifier
|
from .models.items import TypeSpecifier
|
||||||
|
from .presence import Presence
|
||||||
from .pyotherside_events import DevicesUpdated
|
from .pyotherside_events import DevicesUpdated
|
||||||
from .utils import classes_defined_in, plain2html
|
from .utils import classes_defined_in, plain2html
|
||||||
|
|
||||||
|
|
116
src/backend/presence.py
Normal file
116
src/backend/presence.py
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||||
|
|
||||||
|
from .utils import AutoStrEnum, auto
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .models.items import Account, Member
|
||||||
|
|
||||||
|
ORDER: Dict[str, int] = {
|
||||||
|
"online": 0,
|
||||||
|
"unavailable": 1,
|
||||||
|
"offline": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Presence:
|
||||||
|
"""Represents a single matrix user's presence fields.
|
||||||
|
|
||||||
|
These objects are stored in `Backend.presences`, indexed by user ID.
|
||||||
|
It must only be instanced when receiving a `PresenceEvent` or
|
||||||
|
registering an `Account` model item.
|
||||||
|
|
||||||
|
When receiving a `PresenceEvent`, we get or create a `Presence` object in
|
||||||
|
`Backend.presences` for the targeted user. If the user is registered in any
|
||||||
|
room, add its `Member` model item to `members`. Finally, update every
|
||||||
|
`Member` presence fields inside `members`.
|
||||||
|
|
||||||
|
When a room member is registered, we try to find a `Presence` in
|
||||||
|
`Backend.presences` for that user ID. If found, the `Member` item is added
|
||||||
|
to `members`.
|
||||||
|
|
||||||
|
When an Account model is registered, we create a `Presence` in
|
||||||
|
`Backend.presences` for the accountu's user ID whether the server supports
|
||||||
|
presence or not (we cannot know yet at this point),
|
||||||
|
and assign that `Account` to the `Presence.account` field.
|
||||||
|
|
||||||
|
Special attributes:
|
||||||
|
members: A `{room_id: Member}` dict for storing room members related to
|
||||||
|
this `Presence`. As each room has its own `Member`s objects, we
|
||||||
|
have to keep track of their presence fields. `Member`s are indexed
|
||||||
|
by room ID.
|
||||||
|
|
||||||
|
account: `Account` related to this `Presence`, if any. Should be
|
||||||
|
assigned when client starts (`MatrixClient._start()`) and
|
||||||
|
cleared when client stops (`MatrixClient._start()`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class State(AutoStrEnum):
|
||||||
|
offline = auto() # can mean offline, invisible or unknwon
|
||||||
|
unavailable = auto()
|
||||||
|
online = auto()
|
||||||
|
invisible = auto()
|
||||||
|
|
||||||
|
echo_unavailable = auto()
|
||||||
|
echo_online = auto()
|
||||||
|
echo_invisible = auto()
|
||||||
|
|
||||||
|
def __lt__(self, other: "Presence.State") -> bool:
|
||||||
|
return ORDER[self.value] < ORDER[other.value]
|
||||||
|
|
||||||
|
|
||||||
|
presence: State = State.offline
|
||||||
|
currently_active: bool = False
|
||||||
|
last_active_at: datetime = datetime.fromtimestamp(0)
|
||||||
|
status_msg: str = ""
|
||||||
|
|
||||||
|
members: Dict[str, "Member"] = field(default_factory=dict)
|
||||||
|
account: Optional["Account"] = None
|
||||||
|
|
||||||
|
|
||||||
|
def update_members(self) -> None:
|
||||||
|
"""Update presence fields of every `M̀ember` in `members`.
|
||||||
|
|
||||||
|
Currently it is only called when receiving a `PresenceEvent` and when
|
||||||
|
registering room members.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for member in self.members.values():
|
||||||
|
member.set_fields(
|
||||||
|
presence = self.presence,
|
||||||
|
status_msg = self.status_msg,
|
||||||
|
last_active_at = self.last_active_at,
|
||||||
|
currently_active = self.currently_active,
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_account(self) -> None:
|
||||||
|
"""Update presence fields of `Account` related to this `Presence`."""
|
||||||
|
|
||||||
|
# Do not update if account is changing to invisible.
|
||||||
|
# When setting presence to invisible, the server will give us a
|
||||||
|
# presence event telling us we are offline, but we do not want to set
|
||||||
|
# account presence to offline.
|
||||||
|
if (
|
||||||
|
not self.account or
|
||||||
|
self.presence == self.State.offline and
|
||||||
|
self.account.presence != self.State.echo_invisible
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
fields: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
if self.account.presence == self.State.echo_invisible:
|
||||||
|
fields["presence"] = self.State.invisible
|
||||||
|
else:
|
||||||
|
fields["presence"] = self.presence
|
||||||
|
fields["status_msg"] = self.status_msg
|
||||||
|
|
||||||
|
fields["last_active_at"] = self.last_active_at
|
||||||
|
fields["currently_active"] = self.currently_active
|
||||||
|
|
||||||
|
self.account.set_fields(**fields)
|
Loading…
Reference in New Issue
Block a user