vcon 0.2__tar.gz → 0.3.0__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.
Files changed (39) hide show
  1. vcon-0.2/LICENSE → vcon-0.3.0/LICENSE.txt +2 -2
  2. vcon-0.3.0/PKG-INFO +136 -0
  3. vcon-0.3.0/README.md +112 -0
  4. vcon-0.3.0/pyproject.toml +34 -0
  5. vcon-0.3.0/src/vcon/__init__.py +3 -0
  6. vcon-0.3.0/src/vcon/civic_address.py +90 -0
  7. vcon-0.3.0/src/vcon/dialog.py +277 -0
  8. vcon-0.3.0/src/vcon/party.py +87 -0
  9. vcon-0.3.0/src/vcon/vcon.py +480 -0
  10. vcon-0.2/PKG-INFO +0 -10
  11. vcon-0.2/README.md +0 -71
  12. vcon-0.2/setup.cfg +0 -4
  13. vcon-0.2/setup.py +0 -76
  14. vcon-0.2/tests/test_dates.py +0 -59
  15. vcon-0.2/tests/test_external_content.py +0 -231
  16. vcon-0.2/tests/test_filter_plugins.py +0 -95
  17. vcon-0.2/tests/test_jwe.py +0 -244
  18. vcon-0.2/tests/test_migrate.py +0 -25
  19. vcon-0.2/tests/test_minimalist.py +0 -266
  20. vcon-0.2/tests/test_party_search.py +0 -43
  21. vcon-0.2/tests/test_redis_mgr.py +0 -39
  22. vcon-0.2/tests/test_signature.py +0 -325
  23. vcon-0.2/tests/test_uuid.py +0 -57
  24. vcon-0.2/tests/test_vcon_cli.py +0 -183
  25. vcon-0.2/tests/test_whisper_plugin.py +0 -135
  26. vcon-0.2/vcon/__init__.py +0 -1354
  27. vcon-0.2/vcon/bin/vcon +0 -10
  28. vcon-0.2/vcon/cli.py +0 -337
  29. vcon-0.2/vcon/filter_plugins/__init__.py +0 -268
  30. vcon-0.2/vcon/filter_plugins/impl/whisper.py +0 -145
  31. vcon-0.2/vcon/filter_plugins/whisper.py +0 -9
  32. vcon-0.2/vcon/security.py +0 -373
  33. vcon-0.2/vcon/utils.py +0 -81
  34. vcon-0.2/vcon.egg-info/PKG-INFO +0 -10
  35. vcon-0.2/vcon.egg-info/SOURCES.txt +0 -29
  36. vcon-0.2/vcon.egg-info/dependency_links.txt +0 -1
  37. vcon-0.2/vcon.egg-info/not-zip-safe +0 -1
  38. vcon-0.2/vcon.egg-info/requires.txt +0 -17
  39. vcon-0.2/vcon.egg-info/top_level.txt +0 -1
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 vcon-dev
3
+ Copyright (c) [2024] [StrolidInc]
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
vcon-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.1
2
+ Name: vcon
3
+ Version: 0.3.0
4
+ Summary: The vCon library
5
+ License: MIT
6
+ Author: pair
7
+ Author-email: dev_team@strolid.com
8
+ Requires-Python: >=3.8,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Dist: authlib (>=1.3.2,<2.0.0)
17
+ Requires-Dist: pydash (>=8.0.3,<9.0.0)
18
+ Requires-Dist: pytest (>=8.3.2,<9.0.0)
19
+ Requires-Dist: pytest-cov (>=5.0.0,<6.0.0)
20
+ Requires-Dist: pytest-mock (>=3.14.0,<4.0.0)
21
+ Requires-Dist: requests (>=2.32.3,<3.0.0)
22
+ Requires-Dist: uuid6 (>=2024.7.10,<2025.0.0)
23
+ Description-Content-Type: text/markdown
24
+
25
+ # vCon Python Library
26
+
27
+ This Python library provides a convenient way to work with vCon (Virtualized Conversation) objects. vCon is a standard for representing conversation data, including metadata, dialog, analysis, and attachments.
28
+
29
+ ## Features
30
+
31
+ - Create and manipulate vCon objects
32
+ - Add parties, dialogs, attachments, and analysis to vCons
33
+ - Sign and verify vCons using JWS (JSON Web Signature)
34
+ - Generate UUID8 identifiers
35
+ - Pack and unpack dialogs
36
+ - Add and retrieve tags
37
+
38
+ ## Installation
39
+
40
+ To install the vCon library, use pip:
41
+
42
+ ```
43
+ pip install vcon
44
+ ```
45
+
46
+ ## Usage
47
+ from vcon import Vcon
48
+ from vcon.party import Party
49
+
50
+ # Create a new vcon
51
+ vcon = Vcon.build_new()
52
+
53
+ # Add a party to the vcon
54
+ party = Party(mailto="example@example.com")
55
+ vcon.add_party(party)
56
+
57
+ # Add a dialog to the vcon
58
+ dialog = {
59
+ "id": "dialog1",
60
+ "type": "text",
61
+ "start": "2024-05-03T20:13:48.414984",
62
+ "parties": [0],
63
+ "body": "Hello, world!",
64
+ "encoding": "none"
65
+ }
66
+ vcon.add_dialog(dialog)
67
+
68
+ # Add an attachment to the vcon
69
+ attachment = {
70
+ "type": "tags",
71
+ "body": ["tag1:value1", "tag2:value2"],
72
+ "encoding": "json"
73
+ }
74
+ vcon.add_attachment(attachment)
75
+
76
+ # Add analysis to the vcon
77
+ analysis = {
78
+ "type": "transcript",
79
+ "dialog": 0,
80
+ "vendor": "openai",
81
+ "body": {"key": "value"},
82
+ "encoding": "json"
83
+ }
84
+ vcon.add_analysis(analysis)
85
+
86
+ # Serialize the vcon to JSON
87
+ json_string = vcon.to_json()
88
+ print(json_string)
89
+
90
+ # Create a new vcon from the JSON string
91
+ new_vcon = Vcon.build_from_json(json_string)
92
+
93
+ # Print the new vcon's UUID
94
+ print(new_vcon.uuid)
95
+
96
+
97
+ ## API Reference
98
+
99
+ For detailed information about the available methods and their usage, please refer to the docstrings in the source code.
100
+
101
+ ## Contributing
102
+
103
+ Contributions to the vCon library are welcome! Please submit pull requests or open issues on the GitHub repository.
104
+
105
+ ## License
106
+
107
+ This project is licensed under the MIT License:
108
+
109
+ MIT License
110
+
111
+ Copyright (c) 2023 Thomas McCarthy-Howe
112
+
113
+ Permission is hereby granted, free of charge, to any person obtaining a copy
114
+ of this software and associated documentation files (the "Software"), to deal
115
+ in the Software without restriction, including without limitation the rights
116
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
117
+ copies of the Software, and to permit persons to whom the Software is
118
+ furnished to do so, subject to the following conditions:
119
+
120
+ The above copyright notice and this permission notice shall be included in all
121
+ copies or substantial portions of the Software.
122
+
123
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
124
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
125
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
126
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
127
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
128
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
129
+ SOFTWARE.
130
+
131
+ ## Contact
132
+
133
+ For questions or support, please contact:
134
+
135
+ Thomas McCarthy-Howe
136
+ Email: ghostofbasho@gmail.com
vcon-0.3.0/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # vCon Python Library
2
+
3
+ This Python library provides a convenient way to work with vCon (Virtualized Conversation) objects. vCon is a standard for representing conversation data, including metadata, dialog, analysis, and attachments.
4
+
5
+ ## Features
6
+
7
+ - Create and manipulate vCon objects
8
+ - Add parties, dialogs, attachments, and analysis to vCons
9
+ - Sign and verify vCons using JWS (JSON Web Signature)
10
+ - Generate UUID8 identifiers
11
+ - Pack and unpack dialogs
12
+ - Add and retrieve tags
13
+
14
+ ## Installation
15
+
16
+ To install the vCon library, use pip:
17
+
18
+ ```
19
+ pip install vcon
20
+ ```
21
+
22
+ ## Usage
23
+ from vcon import Vcon
24
+ from vcon.party import Party
25
+
26
+ # Create a new vcon
27
+ vcon = Vcon.build_new()
28
+
29
+ # Add a party to the vcon
30
+ party = Party(mailto="example@example.com")
31
+ vcon.add_party(party)
32
+
33
+ # Add a dialog to the vcon
34
+ dialog = {
35
+ "id": "dialog1",
36
+ "type": "text",
37
+ "start": "2024-05-03T20:13:48.414984",
38
+ "parties": [0],
39
+ "body": "Hello, world!",
40
+ "encoding": "none"
41
+ }
42
+ vcon.add_dialog(dialog)
43
+
44
+ # Add an attachment to the vcon
45
+ attachment = {
46
+ "type": "tags",
47
+ "body": ["tag1:value1", "tag2:value2"],
48
+ "encoding": "json"
49
+ }
50
+ vcon.add_attachment(attachment)
51
+
52
+ # Add analysis to the vcon
53
+ analysis = {
54
+ "type": "transcript",
55
+ "dialog": 0,
56
+ "vendor": "openai",
57
+ "body": {"key": "value"},
58
+ "encoding": "json"
59
+ }
60
+ vcon.add_analysis(analysis)
61
+
62
+ # Serialize the vcon to JSON
63
+ json_string = vcon.to_json()
64
+ print(json_string)
65
+
66
+ # Create a new vcon from the JSON string
67
+ new_vcon = Vcon.build_from_json(json_string)
68
+
69
+ # Print the new vcon's UUID
70
+ print(new_vcon.uuid)
71
+
72
+
73
+ ## API Reference
74
+
75
+ For detailed information about the available methods and their usage, please refer to the docstrings in the source code.
76
+
77
+ ## Contributing
78
+
79
+ Contributions to the vCon library are welcome! Please submit pull requests or open issues on the GitHub repository.
80
+
81
+ ## License
82
+
83
+ This project is licensed under the MIT License:
84
+
85
+ MIT License
86
+
87
+ Copyright (c) 2023 Thomas McCarthy-Howe
88
+
89
+ Permission is hereby granted, free of charge, to any person obtaining a copy
90
+ of this software and associated documentation files (the "Software"), to deal
91
+ in the Software without restriction, including without limitation the rights
92
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
93
+ copies of the Software, and to permit persons to whom the Software is
94
+ furnished to do so, subject to the following conditions:
95
+
96
+ The above copyright notice and this permission notice shall be included in all
97
+ copies or substantial portions of the Software.
98
+
99
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
100
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
101
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
102
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
103
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
104
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
105
+ SOFTWARE.
106
+
107
+ ## Contact
108
+
109
+ For questions or support, please contact:
110
+
111
+ Thomas McCarthy-Howe
112
+ Email: ghostofbasho@gmail.com
@@ -0,0 +1,34 @@
1
+ [tool.poetry]
2
+ name = "vcon"
3
+ version = "0.3.0"
4
+ description = "The vCon library"
5
+ authors = ["pair <dev_team@strolid.com>"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ packages = [
9
+ { include = "vcon", from = "src" }
10
+ ]
11
+
12
+ [[tool.poetry.source]]
13
+ name = "testpypi"
14
+ url = "https://test.pypi.org/legacy/"
15
+ priority = "primary"
16
+
17
+
18
+ [[tool.poetry.source]]
19
+ name = "PyPI"
20
+ priority = "primary"
21
+
22
+ [tool.poetry.dependencies]
23
+ python = "^3.8"
24
+ authlib = "^1.3.2"
25
+ pytest = "^8.3.2"
26
+ uuid6 = "^2024.7.10"
27
+ requests = "^2.32.3"
28
+ pydash = "^8.0.3"
29
+ pytest-cov = "^5.0.0"
30
+ pytest-mock = "^3.14.0"
31
+
32
+ [build-system]
33
+ requires = ["poetry-core"]
34
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,3 @@
1
+ from .vcon import Vcon
2
+
3
+ __all__ = ["Vcon"]
@@ -0,0 +1,90 @@
1
+ from typing import Optional
2
+ from typing import Optional
3
+
4
+ class CivicAddress:
5
+ def __init__(self,
6
+ country: Optional[str] = None,
7
+ a1: Optional[str] = None,
8
+ a2: Optional[str] = None,
9
+ a3: Optional[str] = None,
10
+ a4: Optional[str] = None,
11
+ a5: Optional[str] = None,
12
+ a6: Optional[str] = None,
13
+ prd: Optional[str] = None,
14
+ pod: Optional[str] = None,
15
+ sts: Optional[str] = None,
16
+ hno: Optional[str] = None,
17
+ hns: Optional[str] = None,
18
+ lmk: Optional[str] = None,
19
+ loc: Optional[str] = None,
20
+ flr: Optional[str] = None,
21
+ nam: Optional[str] = None,
22
+ pc: Optional[str] = None):
23
+ """
24
+ Initialize a new CivicAddress object.
25
+
26
+ :param country: Country code (ISO 3166-1 alpha-2)
27
+ :type country: str
28
+ :param a1: Administrative area 1 (e.g. state or province)
29
+ :type a1: str
30
+ :param a2: Administrative area 2 (e.g. county or municipality)
31
+ :type a2: str
32
+ :param a3: Administrative area 3 (e.g. city or town)
33
+ :type a3: str
34
+ :param a4: Administrative area 4 (e.g. neighborhood or district)
35
+ :type a4: str
36
+ :param a5: Administrative area 5 (e.g. postal code)
37
+ :type a5: str
38
+ :param a6: Administrative area 6 (e.g. building or floor)
39
+ :type a6: str
40
+ :param prd: Premier (e.g. department or suite number)
41
+ :type prd: str
42
+ :param pod: Post office box identifier
43
+ :type pod: str
44
+ :param sts: Street name
45
+ :type sts: str
46
+ :param hno: House number
47
+ :type hno: str
48
+ :param hns: House name
49
+ :type hns: str
50
+ :param lmk: Landmark name
51
+ :type lmk: str
52
+ :param loc: Location name
53
+ :type loc: str
54
+ :param flr: Floor
55
+ :type flr: str
56
+ :param nam: Name of the location
57
+ :type nam: str
58
+ :param pc: Postal code
59
+ :type pc: str
60
+ """
61
+ self.country = country
62
+ self.a1 = a1
63
+ self.a2 = a2
64
+ self.a3 = a3
65
+ self.a4 = a4
66
+ self.a5 = a5
67
+ self.a6 = a6
68
+ self.prd = prd
69
+ self.pod = pod
70
+ self.sts = sts
71
+ self.hno = hno
72
+ self.hns = hns
73
+ self.lmk = lmk
74
+ self.loc = loc
75
+ self.flr = flr
76
+ self.nam = nam
77
+ self.pc = pc
78
+
79
+ def to_dict(self) -> dict[str, Optional[str]]:
80
+ """
81
+ Convert the CivicAddress object to a dictionary.
82
+
83
+ The dictionary keys are the attribute names of the object, and the
84
+ values are the attribute values.
85
+
86
+ :return: A dictionary of the object's attributes
87
+ :rtype: dict[str, Optional[str]]
88
+ """
89
+ return {attr: getattr(self, attr) for attr in dir(self) if not attr.startswith("_") and getattr(self, attr) is not None and not callable(getattr(self, attr))}
90
+
@@ -0,0 +1,277 @@
1
+ import requests
2
+ import hashlib
3
+ import base64
4
+ from datetime import datetime
5
+ from typing import Optional, List
6
+ from .party import PartyHistory
7
+
8
+
9
+ MIME_TYPES = [
10
+ "text/plain",
11
+ "audio/x-wav",
12
+ "audio/x-mp3",
13
+ "audio/x-mp4",
14
+ "audio/ogg",
15
+ "video/x-mp4",
16
+ "video/ogg",
17
+ "multipart/mixed",
18
+ "message/external-body",
19
+ ]
20
+
21
+
22
+ class Dialog:
23
+ def __init__(self,
24
+ type: str,
25
+ start: datetime,
26
+ parties: List[int],
27
+ originator: Optional[int] = None,
28
+ mimetype: Optional[str] = None,
29
+ filename: Optional[str] = None,
30
+ body: Optional[str] = None,
31
+ encoding: Optional[str] = None,
32
+ url: Optional[str] = None,
33
+ alg: Optional[str] = None,
34
+ signature: Optional[str] = None,
35
+ disposition: Optional[str] = None,
36
+ party_history: Optional[List[PartyHistory]] = None,
37
+ transferee: Optional[int] = None,
38
+ transferor: Optional[int] = None,
39
+ transfer_target: Optional[int] = None,
40
+ original: Optional[int] = None,
41
+ consultation: Optional[int] = None,
42
+ target_dialog: Optional[int] = None,
43
+ campaign: Optional[str] = None,
44
+ interaction: Optional[str] = None,
45
+ skill: Optional[str] = None,
46
+ duration: Optional[float] = None) -> None:
47
+ """
48
+ Initialize a Dialog object.
49
+
50
+ :param type: the type of the dialog (e.g. "text", "audio", etc.)
51
+ :type type: str
52
+ :param start: the start time of the dialog
53
+ :type start: datetime
54
+ :param parties: the parties involved in the dialog
55
+ :type parties: List[int]
56
+ :param originator: the party that originated the dialog
57
+ :type originator: int or None
58
+ :param mimetype: the MIME type of the dialog body
59
+ :type mimetype: str or None
60
+ :param filename: the filename of the dialog body
61
+ :type filename: str or None
62
+ :param body: the body of the dialog
63
+ :type body: str or None
64
+ :param encoding: the encoding of the dialog body
65
+ :type encoding: str or None
66
+ :param url: the URL of the dialog
67
+ :type url: str or None
68
+ :param alg: the algorithm used to sign the dialog
69
+ :type alg: str or None
70
+ :param signature: the signature of the dialog
71
+ :type signature: str or None
72
+ :param disposition: the disposition of the dialog
73
+ :type disposition: str or None
74
+ :param party_history: the history of parties involved in the dialog
75
+ :type party_history: List[PartyHistory] or None
76
+ :param transferee: the party that the dialog was transferred to
77
+ :type transferee: int or None
78
+ :param transferor: the party that transferred the dialog
79
+ :type transferor: int or None
80
+ :param transfer_target: the target of the transfer
81
+ :type transfer_target: int or None
82
+ :param original: the original dialog
83
+ :type original: int or None
84
+ :param consultation: the consultation dialog
85
+ :type consultation: int or None
86
+ :param target_dialog: the target dialog
87
+ :type target_dialog: int or None
88
+ :param campaign: the campaign that the dialog is associated with
89
+ :type campaign: str or None
90
+ :param interaction: the interaction that the dialog is associated with
91
+ :type interaction: str or None
92
+ :param skill: the skill that the dialog is associated with
93
+ :type skill: str or None
94
+ :param duration: the duration of the dialog
95
+ :type duration: float or None
96
+ """
97
+ self.type = type
98
+ self.start = start
99
+ self.parties = parties
100
+ self.originator = originator
101
+ self.mimetype = mimetype
102
+ self.filename = filename
103
+ self.body = body
104
+ self.encoding = encoding
105
+ self.url = url
106
+ self.alg = alg
107
+ self.signature = signature
108
+ self.disposition = disposition
109
+ self.party_history = party_history
110
+ self.transferee = transferee
111
+ self.transferor = transferor
112
+ self.transfer_target = transfer_target
113
+ self.original = original
114
+ self.consultation = consultation
115
+ self.target_dialog = target_dialog
116
+ self.campaign = campaign
117
+ self.interaction = interaction
118
+ self.skill = skill
119
+ self.duration = duration
120
+
121
+ def to_dict(self):
122
+ """
123
+ Returns a dictionary representation of the Dialog object.
124
+
125
+ :return: a dictionary containing the Dialog object's attributes
126
+ :rtype: dict
127
+ """
128
+ dialog_dict = {
129
+ "type": self.type,
130
+ "start": self.start.isoformat(),
131
+ "duration": self.duration,
132
+ "parties": self.parties,
133
+ "originator": self.originator,
134
+ "mimetype": self.mimetype,
135
+ "filename": self.filename,
136
+ "body": self.body,
137
+ "encoding": self.encoding,
138
+ "url": self.url,
139
+ "alg": self.alg,
140
+ "signature": self.signature,
141
+ "disposition": self.disposition,
142
+ "party_history": (
143
+ [party_history.to_dict() for party_history in self.party_history]
144
+ if self.party_history
145
+ else None
146
+ ),
147
+ "transferee": self.transferee,
148
+ "transferor": self.transferor,
149
+ "transfer_target": self.transfer_target,
150
+ "original": self.original,
151
+ "consultation": self.consultation,
152
+ "target_dialog": self.target_dialog,
153
+ "campaign": self.campaign,
154
+ "interaction": self.interaction,
155
+ "skill": self.skill
156
+ }
157
+ return {k: v for k, v in dialog_dict.items() if v is not None}
158
+
159
+ def add_external_data(self, url: str, filename: str, mimetype: str) -> None:
160
+ """
161
+ Add external data to the dialog.
162
+
163
+ :param url: the URL of the external data
164
+ :type url: str
165
+ :return: None
166
+ :rtype: None
167
+ """
168
+ response = requests.get(url)
169
+ if response.status_code == 200:
170
+ self.body = response.text
171
+ self.mimetype = response.headers["Content-Type"]
172
+ else:
173
+ raise Exception(f"Failed to fetch external data: {response.status_code}")
174
+
175
+ # Overide the filename if provided, otherwise use the filename from the URL
176
+ if filename:
177
+ self.filename = filename
178
+ else:
179
+ self.filename = url.split("/")[-1]
180
+
181
+ # Overide the mimetype if provided, otherwise use the mimetype from the URL
182
+ if mimetype:
183
+ self.mimetype = mimetype
184
+
185
+ # Calculate the SHA-256 hash of the body as the signature
186
+ self.alg = "sha256"
187
+ self.encoding = "base64url"
188
+ self.signature = base64.urlsafe_b64encode(hashlib.sha256(self.body.encode()).digest()).decode()
189
+
190
+ def add_inline_data(self, body: str, filename: str, mimetype: str) -> None:
191
+ """
192
+ Add inline data to the dialog.
193
+
194
+ :param body: the body of the inline data
195
+ :type body: str
196
+ :param filename: the filename of the inline data
197
+ :type filename: str
198
+ :param mimetype: the mimetype of the inline data
199
+ :type mimetype: str
200
+ :return: None
201
+ :rtype: None
202
+ """
203
+ self.body = body
204
+ self.mimetype = mimetype
205
+ self.filename = filename
206
+ self.alg = "sha256"
207
+ self.encoding = "base64url"
208
+ self.signature = base64.urlsafe_b64encode(
209
+ hashlib.sha256(self.body.encode()).digest()).decode()
210
+
211
+ # Check if the dialog is an external data dialog
212
+ def is_external_data(self) -> bool:
213
+ return self.url is not None
214
+
215
+ # Check if the dialog is an inline data dialog
216
+ def is_inline_data(self) -> bool:
217
+ return self.body is not None
218
+
219
+
220
+ # Check if the dialog is a text dialog
221
+ def is_text(self) -> bool:
222
+ return self.mimetype == "text/plain"
223
+
224
+
225
+ # Check if the dialog is an audio dialog
226
+ def is_audio(self) -> bool:
227
+ return self.mimetype in ["audio/x-wav", "audio/x-mp3", "audio/x-mp4", "audio/ogg"]
228
+
229
+
230
+ # Check if the dialog is a video dialog
231
+ def is_video(self) -> bool:
232
+ return self.mimetype in ["video/x-mp4", "video/ogg"]
233
+
234
+ # Check if the dialog is an email dialog
235
+ def is_email(self) -> bool:
236
+ return self.mimetype == "message/rfc822"
237
+
238
+ # Check to see if it's an external data dialog, that the contents are valid by
239
+ # checking the hash of the body against the signature
240
+ def is_external_data_changed(self) -> bool:
241
+ if not self.is_external_data():
242
+ return False
243
+ try:
244
+ body_hash = base64.urlsafe_b64decode(self.signature.encode())
245
+ return hashlib.sha256(self.body.encode()).digest() != body_hash
246
+ except Exception as e:
247
+ print(e)
248
+ return True
249
+
250
+ # Convert the dialog from an external data dialog to an inline data dialog
251
+ # by reading the contents from the URL then adding the contents to the body
252
+ def to_inline_data(self) -> None:
253
+ # Read the contents from the URL
254
+ response = requests.get(self.url)
255
+ if response.status_code == 200:
256
+ self.body = response.text
257
+ self.mimetype = response.headers["Content-Type"]
258
+ else:
259
+ raise Exception(f"Failed to fetch external data: {response.status_code}")
260
+
261
+ # Calculate the SHA-256 hash of the body as the signature
262
+ self.alg = "sha256"
263
+ self.encoding = "base64url"
264
+ self.signature = base64.urlsafe_b64encode(hashlib.sha256(self.body.encode()).digest()).decode()
265
+
266
+ # Overide the filename if provided, otherwise use the filename from the URL
267
+ if self.filename:
268
+ self.filename = self.filename
269
+ else:
270
+ self.filename = self.url.split("/")[-1]
271
+
272
+ # Overide the mimetype if provided, otherwise use the mimetype from the URL
273
+ if self.mimetype:
274
+ self.mimetype = self.mimetype
275
+
276
+ # Add the body to the dialog
277
+ self.add_inline_data(self.body, self.filename, self.mimetype)