openmodule 16.0.0rc0__tar.gz → 16.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/PKG-INFO +1 -1
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/base.py +28 -2
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/server.py +2 -2
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/presence.py +1 -1
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/validation.py +1 -1
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/PKG-INFO +1 -1
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_model.py +23 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_rpc.py +4 -4
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_eventlog.py +3 -2
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_vehicle.py +1 -1
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/LICENSE +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/README.md +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/__init__.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/alert.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/config.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/connection_status.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/core.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/__init__.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/custom_types.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/database.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/env.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/migration.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/dispatcher.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/health.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/logging.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/messaging.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/__init__.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/access_service.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/alert.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/io.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/kv_store.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/presence.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/privacy.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/rpc.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/settings.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/signals.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/validation.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/vehicle.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/__init__.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/client.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/common.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/sentry.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/threading.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/__init__.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/access_service.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/charset.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/cleanup.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/csv_export.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/databox.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/db_helper.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/eventlog.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/io.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/kv_store.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/matching.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/misc_functions.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/package_reader.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/schedule.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/settings.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/signal_listener.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/translation.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/SOURCES.txt +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/dependency_links.txt +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/not-zip-safe +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/requires.txt +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/top_level.txt +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/setup.cfg +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/setup.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_alembic_migrations.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_alert.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_checks.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_config.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_connection_status.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_core.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_database.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_dispatcher.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_health.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_interrupt.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_io_listen.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_logging.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_messaging.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_mockrpcclient.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_sentry.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_test_alert.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_test_gate.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_test_zeromq.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_access_service.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_charset.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_cleanup.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_csv_export.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_databox.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_kv_store.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_kv_store_multiple.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_matching.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_misc_functions.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_package_reader.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_presence.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_schedule.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_settings.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_signal.py +0 -0
- {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_validation.py +0 -0
|
@@ -5,13 +5,13 @@ from datetime import datetime
|
|
|
5
5
|
from decimal import Decimal
|
|
6
6
|
from enum import StrEnum
|
|
7
7
|
from json.encoder import ESCAPE_ASCII
|
|
8
|
-
from typing import Annotated
|
|
8
|
+
from typing import Annotated, Optional, Any, Self, ClassVar
|
|
9
9
|
|
|
10
10
|
import orjson
|
|
11
11
|
from pydantic_core import PydanticUndefined
|
|
12
12
|
import zmq
|
|
13
13
|
from dateutil.tz import UTC
|
|
14
|
-
from pydantic import AfterValidator, ConfigDict, Field, BaseModel, RootModel
|
|
14
|
+
from pydantic import AfterValidator, ConfigDict, Field, BaseModel, PrivateAttr, RootModel, model_validator, field_validator
|
|
15
15
|
|
|
16
16
|
from openmodule import sentry
|
|
17
17
|
from openmodule.config import run_checks, settings
|
|
@@ -158,7 +158,11 @@ def datetime_to_timestamp(dt: datetime):
|
|
|
158
158
|
|
|
159
159
|
|
|
160
160
|
class ZMQMessage(OpenModuleModel):
|
|
161
|
+
_ALLOW_CUSTOM_TIMESTAMP: ClassVar[bool] = False # set to true for tools scripts, where you need to set the timestamp manually
|
|
162
|
+
|
|
161
163
|
timestamp: Datetime = Field(default_factory=lambda: utcnow())
|
|
164
|
+
_using_default_timestamp: bool = PrivateAttr()
|
|
165
|
+
|
|
162
166
|
name: str = Field(default_factory=lambda: settings.NAME)
|
|
163
167
|
type: str
|
|
164
168
|
baggage: str | None = None
|
|
@@ -179,6 +183,28 @@ class ZMQMessage(OpenModuleModel):
|
|
|
179
183
|
data["timestamp"] = datetime_to_timestamp(data["timestamp"])
|
|
180
184
|
return data
|
|
181
185
|
|
|
186
|
+
@classmethod
|
|
187
|
+
def model_validate(cls, obj: Any, *, context: Optional[Any] = None, **kwargs) -> Self:
|
|
188
|
+
context = {**(context or {}), "from_model_validate": True}
|
|
189
|
+
return super().model_validate(obj, context=context, **kwargs)
|
|
190
|
+
|
|
191
|
+
@model_validator(mode="before")
|
|
192
|
+
@classmethod
|
|
193
|
+
def check_default_timestamp(cls, data: dict):
|
|
194
|
+
cls._using_default_timestamp = "timestamp" not in data
|
|
195
|
+
return data
|
|
196
|
+
|
|
197
|
+
@field_validator("timestamp", mode="before")
|
|
198
|
+
@classmethod
|
|
199
|
+
def check_custom_timestamp(cls, v, info):
|
|
200
|
+
from_model_validate = (info.context or {}).get("from_model_validate", False) is True
|
|
201
|
+
using_default_timestamp = cls._using_default_timestamp is True
|
|
202
|
+
custom_constructor_timestamp_allowed = cls._ALLOW_CUSTOM_TIMESTAMP is True
|
|
203
|
+
|
|
204
|
+
if not using_default_timestamp and not custom_constructor_timestamp_allowed and not from_model_validate:
|
|
205
|
+
raise ValueError("timestamp must not be set manually")
|
|
206
|
+
return v
|
|
207
|
+
|
|
182
208
|
|
|
183
209
|
class Direction(StrEnum):
|
|
184
210
|
UNKNOWN = ""
|
|
@@ -185,7 +185,7 @@ class RPCServer(object):
|
|
|
185
185
|
handler: HandlerEntry = self.find_handler(channel, message.type)
|
|
186
186
|
if handler:
|
|
187
187
|
try:
|
|
188
|
-
request = handler.request_class(
|
|
188
|
+
request = handler.request_class.model_validate(message.request)
|
|
189
189
|
except ValidationError as e:
|
|
190
190
|
self.log.exception("exception in parsing request. request not processed and error returned")
|
|
191
191
|
return {"status": RPCServerError.validation_error, "exception": e.json()}
|
|
@@ -225,7 +225,7 @@ class RPCServer(object):
|
|
|
225
225
|
return
|
|
226
226
|
|
|
227
227
|
try:
|
|
228
|
-
message = RPCRequest(
|
|
228
|
+
message = RPCRequest.model_validate(message)
|
|
229
229
|
message_type = message.type
|
|
230
230
|
rpc_id = message.rpc_id
|
|
231
231
|
except ValidationError as e:
|
|
@@ -210,7 +210,7 @@ class PresenceListener:
|
|
|
210
210
|
present_vehicle = self.present_vehicles.get(message.gateway.gate)
|
|
211
211
|
if present_vehicle and present_vehicle.id == message.vehicle_id:
|
|
212
212
|
self.log.error(f"Got {message.type} without a leave. We generate a leave ourself.")
|
|
213
|
-
self._on_leave(PresenceLeaveMessage(
|
|
213
|
+
self._on_leave(PresenceLeaveMessage.model_validate(message.model_dump()))
|
|
214
214
|
|
|
215
215
|
def _execute_leave(self, vehicle: Vehicle, gateway: Gateway):
|
|
216
216
|
if vehicle.leave_time is None:
|
|
@@ -25,7 +25,7 @@ class Validation:
|
|
|
25
25
|
rpc_server.register_handler("validation_provider", "validate", request_class=ValidateRequest,
|
|
26
26
|
response_class=ValidateResponse, handler=self.rpc_validate_ticket)
|
|
27
27
|
|
|
28
|
-
def _validation_provider_filter(self, request,
|
|
28
|
+
def _validation_provider_filter(self, request, _message, _handler) -> bool:
|
|
29
29
|
validation_provider = request.name
|
|
30
30
|
if not validation_provider:
|
|
31
31
|
return False
|
|
@@ -16,6 +16,12 @@ class TestModel(OpenModuleModel):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class ModelTestCase(TestCase):
|
|
19
|
+
def setUp(self):
|
|
20
|
+
ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = True
|
|
21
|
+
|
|
22
|
+
def tearDown(self):
|
|
23
|
+
ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = False
|
|
24
|
+
|
|
19
25
|
def test_json_is_forbidden(self):
|
|
20
26
|
instance = TestModel(value="Hello World")
|
|
21
27
|
with self.assertRaises(AssertionError):
|
|
@@ -72,6 +78,12 @@ class ModelTestCase(TestCase):
|
|
|
72
78
|
|
|
73
79
|
|
|
74
80
|
class ZMQMessageTestCase(TestCase):
|
|
81
|
+
def setUp(self):
|
|
82
|
+
ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = True
|
|
83
|
+
|
|
84
|
+
def tearDown(self):
|
|
85
|
+
ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = False
|
|
86
|
+
|
|
75
87
|
def test_timestamp_to_datetime(self):
|
|
76
88
|
instance = ZMQMessage(timestamp=1607586354631, name="test", type="test")
|
|
77
89
|
self.assertEqual(instance.timestamp.isoformat(), "2020-12-10T07:45:54.631000")
|
|
@@ -136,3 +148,14 @@ class ZMQMessageTestCase(TestCase):
|
|
|
136
148
|
|
|
137
149
|
res1.pop("timestamp")
|
|
138
150
|
self.assertDictEqual(res1, {"name": "tä\nst", "type": "tö\tst"})
|
|
151
|
+
|
|
152
|
+
def test_allow_custom_timestamp(self):
|
|
153
|
+
ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = False
|
|
154
|
+
with self.assertRaises(ValueError):
|
|
155
|
+
ZMQMessage(timestamp=1607586354631, name="test", type="test")
|
|
156
|
+
instance = ZMQMessage(name="test", type="test")
|
|
157
|
+
instance.type="asdf"
|
|
158
|
+
with self.assertRaises(ValueError):
|
|
159
|
+
instance.timestamp = datetime.datetime(2020, 1, 1, 0, 0, 0, 0)
|
|
160
|
+
ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = True
|
|
161
|
+
instance.timestamp = datetime.datetime(2020, 1, 1, 0, 0, 0, 0)
|
|
@@ -148,7 +148,7 @@ class RPCServerTestCase(RPCServerTestMixin, OpenModuleCoreTestMixin):
|
|
|
148
148
|
self.assertTrue(self.called_types.get("test-type"))
|
|
149
149
|
|
|
150
150
|
def test_exception_in_filter_function(self):
|
|
151
|
-
def bad_filter(
|
|
151
|
+
def bad_filter(_request, _message, _handler) -> bool:
|
|
152
152
|
raise Exception("Error123")
|
|
153
153
|
|
|
154
154
|
self.server.register_handler("channel", "test-type", TestRPCRequest, TestRPCResponse, self.set_called)
|
|
@@ -160,7 +160,7 @@ class RPCServerTestCase(RPCServerTestMixin, OpenModuleCoreTestMixin):
|
|
|
160
160
|
self.assertEqual("Error123", str(e.exception))
|
|
161
161
|
|
|
162
162
|
def test_filter(self):
|
|
163
|
-
def allow_only_type_test(_request:
|
|
163
|
+
def allow_only_type_test(_request: None, message: RPCRequest, _handler: None) -> bool:
|
|
164
164
|
return message.type == "test-type"
|
|
165
165
|
|
|
166
166
|
self.server.register_handler("channel", "test-type", TestRPCRequest, TestRPCResponse, self.set_called)
|
|
@@ -188,7 +188,7 @@ class RPCServerTestCase(RPCServerTestMixin, OpenModuleCoreTestMixin):
|
|
|
188
188
|
self.assertNotIn("test-type2", self.called_types)
|
|
189
189
|
|
|
190
190
|
def test_filter_channel_type(self):
|
|
191
|
-
def f(
|
|
191
|
+
def f(_request: None, _message: None, _handler: None):
|
|
192
192
|
return False
|
|
193
193
|
|
|
194
194
|
def check_ok():
|
|
@@ -511,7 +511,7 @@ class RpcClientTest(RPCServerTestMixin, OpenModuleCoreTestMixin):
|
|
|
511
511
|
return TestRPCResponse2(some_payload="abc")
|
|
512
512
|
|
|
513
513
|
@staticmethod
|
|
514
|
-
def broken_filter(
|
|
514
|
+
def broken_filter(_request: None, _message: None, _handler: None) -> bool:
|
|
515
515
|
assert False
|
|
516
516
|
|
|
517
517
|
@staticmethod
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
|
+
from openmodule.models.base import ZMQMessage
|
|
3
4
|
from openmodule.utils.eventlog import send_event, EventInfo, MessageKwarg, EventlogMessage, AnonymizationType, \
|
|
4
5
|
PlateFormatData
|
|
5
6
|
from openmodule_test.core import OpenModuleCoreTestMixin
|
|
@@ -19,7 +20,7 @@ class TestEvents(OpenModuleCoreTestMixin):
|
|
|
19
20
|
self.assertIn("INFO:Eventlog:Sending event at gate1: LPR plate='G ARIVO 1' country='A': "
|
|
20
21
|
"customer Test User, medium QR1, price 1", cm.output)
|
|
21
22
|
|
|
22
|
-
msg = EventlogMessage(
|
|
23
|
+
msg = EventlogMessage.model_validate(self.zmq_client.wait_for_message_on_topic("eventlog"))
|
|
23
24
|
self.assertEqual(msg.event.infos.type, "test_1")
|
|
24
25
|
self.assertEqual(msg.event.infos.timestamp, datetime.min)
|
|
25
26
|
self.assertEqual(msg.event.infos.gate, "gate1")
|
|
@@ -47,5 +48,5 @@ class TestEvents(OpenModuleCoreTestMixin):
|
|
|
47
48
|
with self.assertLogs("Eventlog") as cm:
|
|
48
49
|
send_event(EventInfo.create("test_1"), "test")
|
|
49
50
|
self.assertIn("INFO:Eventlog:Sending event: test", cm.output)
|
|
50
|
-
msg = EventlogMessage(
|
|
51
|
+
msg = EventlogMessage.model_validate(self.zmq_client.wait_for_message_on_topic("eventlog"))
|
|
51
52
|
self.assertIsNotNone(msg.event.infos.timestamp)
|
|
@@ -66,6 +66,6 @@ class VehicleTestCase(TestCase):
|
|
|
66
66
|
"id":"ZH 614198","no_countrylogic_id":"ZH 614198","track_id":1638169339870027893,"type":"lpr"}},"name":
|
|
67
67
|
"forward-om_alpr_tracking_3","present-area-name":"einfahrt-garage","source":"einfahrt-garage",
|
|
68
68
|
"timestamp":1638169352.606454,"type":"forward","unsure":false,"vehicle_id":1638169340812374101}"""
|
|
69
|
-
parsed_message = PresenceForwardMessage(
|
|
69
|
+
parsed_message = PresenceForwardMessage.model_validate(orjson.loads(message))
|
|
70
70
|
except ValidationError as e:
|
|
71
71
|
assert False, "Parsing Presence Message failed"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|