dimp 2.3.0__tar.gz → 2.3.1__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 (50) hide show
  1. {dimp-2.3.0 → dimp-2.3.1}/PKG-INFO +1 -1
  2. {dimp-2.3.0 → dimp-2.3.1}/dimp/__init__.py +2 -1
  3. {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/keys.py +4 -3
  4. {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/pnf.py +1 -3
  5. {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/ted.py +17 -13
  6. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/contents.py +17 -15
  7. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/group_admins.py +40 -4
  8. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/money.py +9 -1
  9. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/quote.py +5 -1
  10. {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/document.py +1 -12
  11. {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/meta.py +0 -1
  12. {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/instant.py +5 -5
  13. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/commands.py +1 -1
  14. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/groups.py +20 -3
  15. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/quote.py +1 -1
  16. {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/PKG-INFO +1 -1
  17. dimp-2.3.1/dimp.egg-info/requires.txt +2 -0
  18. {dimp-2.3.0 → dimp-2.3.1}/setup.py +3 -3
  19. dimp-2.3.0/dimp.egg-info/requires.txt +0 -2
  20. {dimp-2.3.0 → dimp-2.3.1}/LICENSE +0 -0
  21. {dimp-2.3.0 → dimp-2.3.1}/README.md +0 -0
  22. {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/__init__.py +0 -0
  23. {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/algorithms.py +0 -0
  24. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/__init__.py +0 -0
  25. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/base.py +0 -0
  26. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/commands.py +0 -0
  27. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/files.py +0 -0
  28. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/groups.py +0 -0
  29. {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/receipt.py +0 -0
  30. {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/__init__.py +0 -0
  31. {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/docs.py +0 -0
  32. {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/__init__.py +0 -0
  33. {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/base.py +0 -0
  34. {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/envelope.py +0 -0
  35. {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/reliable.py +0 -0
  36. {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/secure.py +0 -0
  37. {dimp-2.3.0 → dimp-2.3.1}/dimp/plugins/__init__.py +0 -0
  38. {dimp-2.3.0 → dimp-2.3.1}/dimp/plugins/cmd.py +0 -0
  39. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/__init__.py +0 -0
  40. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/contents.py +0 -0
  41. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/docs.py +0 -0
  42. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/files.py +0 -0
  43. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/helpers.py +0 -0
  44. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/money.py +0 -0
  45. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/receipt.py +0 -0
  46. {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/types.py +0 -0
  47. {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/SOURCES.txt +0 -0
  48. {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/dependency_links.txt +0 -0
  49. {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/top_level.txt +0 -0
  50. {dimp-2.3.0 → dimp-2.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimp
3
- Version: 2.3.0
3
+ Version: 2.3.1
4
4
  Summary: Decentralized Instant Messaging Protocol
5
5
  Home-page: https://github.com/dimchat/core-py
6
6
  Author: Albert Moky
@@ -52,7 +52,8 @@ __all__ = [
52
52
 
53
53
  'URI', 'DateTime',
54
54
 
55
- 'Converter', 'Copier',
55
+ 'Converter', 'DataConverter', 'BaseConverter',
56
+ 'Copier',
56
57
  'Wrapper', 'Stringer', 'Mapper',
57
58
  'ConstantString', # 'String',
58
59
  'Dictionary',
@@ -29,7 +29,7 @@
29
29
  # ==============================================================================
30
30
 
31
31
  from abc import ABC
32
- from typing import Optional, Any, Dict
32
+ from typing import Any, Dict
33
33
 
34
34
  from mkm.types import Dictionary
35
35
  from mkm.crypto import CryptographyKey, EncryptKey, DecryptKey, SignKey, VerifyKey
@@ -58,9 +58,10 @@ class BaseKey(Dictionary, CryptographyKey, ABC):
58
58
  #
59
59
 
60
60
  @classmethod
61
- def get_key_algorithm(cls, key: Dict[str, Any]) -> Optional[str]:
61
+ def get_key_algorithm(cls, key: Dict[str, Any]) -> str:
62
62
  ext = SharedCryptoExtensions()
63
- return ext.helper.get_key_algorithm(key=key, default='')
63
+ algorithm = ext.helper.get_key_algorithm(key=key, default=None)
64
+ return '' if algorithm is None else algorithm
64
65
 
65
66
  @classmethod
66
67
  def match_encrypt_key(cls, encrypt_key: EncryptKey, decrypt_key: DecryptKey) -> bool:
@@ -35,8 +35,6 @@ from mkm.types import URI
35
35
  from mkm.types import Dictionary
36
36
  from mkm.crypto import DecryptKey, SymmetricKey
37
37
 
38
- from .algorithms import EncodeAlgorithms
39
-
40
38
 
41
39
  class BaseFileWrapper(Dictionary):
42
40
  """
@@ -92,7 +90,7 @@ class BaseFileWrapper(Dictionary):
92
90
  ted = None
93
91
  self.pop('data', None)
94
92
  else:
95
- ted = TransportableData.create(data=binary, algorithm=EncodeAlgorithms.DEFAULT)
93
+ ted = TransportableData.create(data=binary)
96
94
  self['data'] = ted.object
97
95
  self.__attachment = ted
98
96
 
@@ -69,11 +69,11 @@ class BaseDataWrapper(Dictionary):
69
69
 
70
70
  # Override
71
71
  def __str__(self) -> str:
72
- text = self.get_str(key='data', default='')
73
- if len(text) == 0:
74
- return text
75
- alg = self.get_str(key='algorithm', default='')
76
- if alg == EncodeAlgorithms.DEFAULT:
72
+ text = self.get_str(key='data', default=None)
73
+ if text is None: # or len(text) == 0:
74
+ return ''
75
+ alg = self.get_str(key='algorithm', default=None)
76
+ if alg is None or alg == EncodeAlgorithms.DEFAULT:
77
77
  alg = ''
78
78
  if len(alg) == 0:
79
79
  # 0. "{BASE64_ENCODE}"
@@ -85,9 +85,9 @@ class BaseDataWrapper(Dictionary):
85
85
  def encode(self, mime_type: str) -> str:
86
86
  """ toString(mimeType) """
87
87
  # get encoded data
88
- text = self.get_str(key='data', default='')
89
- if len(text) == 0:
90
- return text
88
+ text = self.get_str(key='data', default=None)
89
+ if text is None: # or len(text) == 0:
90
+ return ''
91
91
  alg = self.algorithm
92
92
  # 2. "data:image/png;base64,{BASE64_ENCODE}"
93
93
  return 'data:%s;%s,%s' % (mime_type, alg, text)
@@ -98,8 +98,8 @@ class BaseDataWrapper(Dictionary):
98
98
 
99
99
  @property
100
100
  def algorithm(self) -> str:
101
- alg = self.get_str(key='algorithm', default='')
102
- if len(alg) == 0:
101
+ alg = self.get_str(key='algorithm', default=None)
102
+ if alg is None or len(alg) == 0:
103
103
  alg = EncodeAlgorithms.DEFAULT
104
104
  return alg
105
105
 
@@ -118,8 +118,11 @@ class BaseDataWrapper(Dictionary):
118
118
  def data(self) -> Optional[bytes]:
119
119
  binary = self.__data
120
120
  if binary is None:
121
- text = self.get_str(key='data', default='')
122
- if len(text) > 0:
121
+ text = self.get_str(key='data', default=None)
122
+ if text is None or len(text) == 0:
123
+ assert False, 'TED data empty: %s' % self.dictionary
124
+ # return None
125
+ else:
123
126
  alg = self.algorithm
124
127
  if alg == EncodeAlgorithms.BASE_64:
125
128
  binary = base64_decode(string=text)
@@ -145,6 +148,7 @@ class BaseDataWrapper(Dictionary):
145
148
  elif alg == EncodeAlgorithms.HEX:
146
149
  text = hex_encode(data=binary)
147
150
  else:
148
- assert False, 'data algorithm not support: %s' % alg
151
+ raise ArithmeticError('data algorithm not support: %s' % alg)
152
+ # assert False, 'data algorithm not support: %s' % alg
149
153
  self['data'] = text
150
154
  self.__data = binary
@@ -106,13 +106,15 @@ class ListContent(BaseContent, ArrayContent):
106
106
 
107
107
  @property # Override
108
108
  def contents(self) -> List[Content]:
109
- if self.__list is None:
109
+ array = self.__list
110
+ if array is None:
110
111
  array = self.get('contents')
111
112
  if array is None:
112
- self.__list = []
113
+ array = []
113
114
  else:
114
- self.__list = Content.convert(array=array)
115
- return self.__list
115
+ array = Content.convert(array=array)
116
+ self.__list = array
117
+ return array
116
118
 
117
119
 
118
120
  class SecretContent(BaseContent, ForwardContent):
@@ -161,19 +163,19 @@ class SecretContent(BaseContent, ForwardContent):
161
163
 
162
164
  @property # Override
163
165
  def secrets(self) -> List[ReliableMessage]:
164
- if self.__secrets is None:
165
- messages = self.get('secrets')
166
- if messages is None:
166
+ messages = self.__secrets
167
+ if messages is None:
168
+ info = self.get('secrets')
169
+ if isinstance(info, List):
170
+ # get from 'secrets'
171
+ messages = ReliableMessage.convert(array=info)
172
+ else:
173
+ assert info is None, 'secret messages error: %s' % info
167
174
  # get from 'forward'
168
175
  msg = self.forward
169
- if msg is None:
170
- self.__secrets = []
171
- else:
172
- self.__secrets = [msg]
173
- else:
174
- # get from 'secrets'
175
- self.__secrets = ReliableMessage.convert(array=messages)
176
- return self.__secrets
176
+ messages = [] if msg is None else [msg]
177
+ self.__secrets = messages
178
+ return messages
177
179
 
178
180
 
179
181
  class WebPageContent(BaseContent, PageContent):
@@ -62,14 +62,32 @@ class HireGroupCommand(BaseGroupCommand, HireCommand):
62
62
  @property # Override
63
63
  def administrators(self) -> Optional[List[ID]]:
64
64
  users = self.get('administrators')
65
- if users is not None:
65
+ if isinstance(users, List):
66
+ # convert all items to ID objects
66
67
  return ID.convert(array=users)
68
+ assert users is None, 'ID list error: %s' % users
69
+
70
+ @administrators.setter # Override
71
+ def administrators(self, users: List[ID]):
72
+ if users is None:
73
+ self.pop('administrators', None)
74
+ else:
75
+ self['administrators'] = ID.revert(identifiers=users)
67
76
 
68
77
  @property # Override
69
78
  def assistants(self) -> Optional[List[ID]]:
70
79
  bots = self.get('assistants')
71
- if bots is not None:
80
+ if isinstance(bots, List):
81
+ # convert all items to ID objects
72
82
  return ID.convert(array=bots)
83
+ assert bots is None, 'ID list error: %s' % bots
84
+
85
+ @assistants.setter # Override
86
+ def assistants(self, bots: List[ID]):
87
+ if bots is None:
88
+ self.pop('assistants', None)
89
+ else:
90
+ self['assistants'] = ID.revert(identifiers=bots)
73
91
 
74
92
 
75
93
  class FireGroupCommand(BaseGroupCommand, FireCommand):
@@ -89,14 +107,32 @@ class FireGroupCommand(BaseGroupCommand, FireCommand):
89
107
  @property # Override
90
108
  def administrators(self) -> Optional[List[ID]]:
91
109
  users = self.get('administrators')
92
- if users is not None:
110
+ if isinstance(users, List):
111
+ # convert all items to ID objects
93
112
  return ID.convert(array=users)
113
+ assert users is None, 'ID list error: %s' % users
114
+
115
+ @administrators.setter # Override
116
+ def administrators(self, users: List[ID]):
117
+ if users is None:
118
+ self.pop('administrators', None)
119
+ else:
120
+ self['administrators'] = ID.revert(identifiers=users)
94
121
 
95
122
  @property # Override
96
123
  def assistants(self) -> Optional[List[ID]]:
97
124
  bots = self.get('assistants')
98
- if bots is not None:
125
+ if isinstance(bots, List):
126
+ # convert all items to ID objects
99
127
  return ID.convert(array=bots)
128
+ assert bots is None, 'ID list error: %s' % bots
129
+
130
+ @assistants.setter # Override
131
+ def assistants(self, bots: List[ID]):
132
+ if bots is None:
133
+ self.pop('assistants', None)
134
+ else:
135
+ self['assistants'] = ID.revert(identifiers=bots)
100
136
 
101
137
 
102
138
  class ResignGroupCommand(BaseGroupCommand, ResignCommand):
@@ -30,6 +30,7 @@
30
30
 
31
31
  from typing import Optional, Any, Dict
32
32
 
33
+ from mkm.types import Converter
33
34
  from mkm import ID
34
35
 
35
36
  from ..protocol import ContentType
@@ -77,7 +78,14 @@ class BaseMoneyContent(BaseContent, MoneyContent):
77
78
 
78
79
  @property # Override
79
80
  def amount(self) -> float:
80
- return self.get_float(key='amount', default=0.0)
81
+ # return self.get_float(key='amount', default=0.0)
82
+ value = self.get('amount')
83
+ if value is None:
84
+ return 0.0
85
+ elif isinstance(value, int) or isinstance(value, float):
86
+ return value
87
+ else:
88
+ return Converter.get_float(value=value, default=0.0)
81
89
 
82
90
  @amount.setter # Override
83
91
  def amount(self, value: float):
@@ -120,6 +120,7 @@ class CombineForwardContent(BaseContent, CombineContent):
120
120
  assert not (title is None or messages is None), 'params error: %s, %s' % (title, messages)
121
121
  msg_type = ContentType.COMBINE_FORWARD
122
122
  super().__init__(None, msg_type)
123
+ self['title'] = title
123
124
  self['messages'] = InstantMessage.revert(messages=messages)
124
125
  else:
125
126
  # 2. content info from network
@@ -136,6 +137,9 @@ class CombineForwardContent(BaseContent, CombineContent):
136
137
  def messages(self) -> List[InstantMessage]:
137
138
  if self.__messages is None:
138
139
  array = self.get('messages')
139
- if array is not None:
140
+ if array is None:
141
+ self.__messages = []
142
+ else:
143
+ assert isinstance(array, List), 'combined messages error: %s' % array
140
144
  self.__messages = InstantMessage.convert(array=array)
141
145
  return self.__messages
@@ -37,8 +37,6 @@ from mkm.format import TransportableData
37
37
  from mkm.format import json_encode, json_decode, utf8_encode
38
38
  from mkm import ID, Document
39
39
 
40
- from ..crypto import EncodeAlgorithms
41
-
42
40
 
43
41
  """
44
42
  Base Documents
@@ -102,15 +100,6 @@ class BaseDocument(Dictionary, Document):
102
100
  self.__properties = properties
103
101
  self.__status = status # 1 for valid, -1 for invalid
104
102
 
105
- # @property # Override
106
- # def type(self) -> Optional[str]:
107
- # doc_type = self.get_property(name='type') # deprecated
108
- # if doc_type is None:
109
- # ext = SharedAccountExtensions()
110
- # doc_type = ext.helper.get_document_type(document=self.dictionary, default=None)
111
- # # doc_type = self.get_str(key='type', default=None)
112
- # return doc_type
113
-
114
103
  @property # Override
115
104
  def identifier(self) -> ID:
116
105
  if self.__identifier is None:
@@ -210,7 +199,7 @@ class BaseDocument(Dictionary, Document):
210
199
  if len(signature) == 0:
211
200
  # assert False, 'should not happen'
212
201
  return None
213
- ted = TransportableData.create(data=signature, algorithm=EncodeAlgorithms.DEFAULT)
202
+ ted = TransportableData.create(data=signature)
214
203
  # 3. update 'data' & 'signature' fields
215
204
  self['data'] = data # JsON string
216
205
  self['signature'] = ted.object # BASE-64
@@ -59,7 +59,6 @@ from mkm import Meta
59
59
  """
60
60
 
61
61
 
62
- # noinspection PyAbstractClass
63
62
  class BaseMeta(Dictionary, Meta, ABC):
64
63
 
65
64
  def __init__(self, meta: Dict[str, Any] = None,
@@ -86,11 +86,11 @@ class PlainMessage(BaseMessage, InstantMessage):
86
86
 
87
87
  @property # Override
88
88
  def time(self) -> Optional[DateTime]:
89
- value = self.content.time
90
- if value is not None:
91
- return value
92
- # return super().time
93
- return self.envelope.time
89
+ when = self.content.time
90
+ if when is None:
91
+ # when = super().time
92
+ when = self.envelope.time
93
+ return when
94
94
 
95
95
  @property # Override
96
96
  def group(self) -> Optional[ID]:
@@ -220,7 +220,7 @@ class DocumentCommand(MetaCommand, ABC):
220
220
  #
221
221
 
222
222
  @classmethod
223
- def query(cls, identifier: ID, last_time: str = None): # -> DocumentCommand:
223
+ def query(cls, identifier: ID, last_time: DateTime = None): # -> DocumentCommand:
224
224
  """
225
225
  1. Query Entity Document
226
226
  2. Query Entity Document for updating with last time
@@ -210,7 +210,6 @@ class QuitCommand(GroupCommand, ABC):
210
210
  pass
211
211
 
212
212
 
213
- # noinspection PyAbstractClass
214
213
  class QueryCommand(GroupCommand, ABC):
215
214
  """
216
215
  NOTICE:
@@ -236,7 +235,6 @@ class ResetCommand(GroupCommand, ABC):
236
235
  """
237
236
 
238
237
 
239
- # noinspection PyAbstractClass
240
238
  class HireCommand(GroupCommand, ABC):
241
239
 
242
240
  @property
@@ -244,13 +242,22 @@ class HireCommand(GroupCommand, ABC):
244
242
  def administrators(self) -> Optional[List[ID]]:
245
243
  raise NotImplemented
246
244
 
245
+ @administrators.setter
246
+ @abstractmethod
247
+ def administrators(self, users: List[ID]):
248
+ raise NotImplemented
249
+
247
250
  @property
248
251
  @abstractmethod
249
252
  def assistants(self) -> Optional[List[ID]]:
250
253
  raise NotImplemented
251
254
 
255
+ @assistants.setter
256
+ @abstractmethod
257
+ def assistants(self, bots: List[ID]):
258
+ raise NotImplemented
259
+
252
260
 
253
- # noinspection PyAbstractClass
254
261
  class FireCommand(GroupCommand, ABC):
255
262
 
256
263
  @property
@@ -258,11 +265,21 @@ class FireCommand(GroupCommand, ABC):
258
265
  def administrators(self) -> Optional[List[ID]]:
259
266
  raise NotImplemented
260
267
 
268
+ @administrators.setter
269
+ @abstractmethod
270
+ def administrators(self, users: List[ID]):
271
+ raise NotImplemented
272
+
261
273
  @property
262
274
  @abstractmethod
263
275
  def assistants(self) -> Optional[List[ID]]:
264
276
  raise NotImplemented
265
277
 
278
+ @assistants.setter
279
+ @abstractmethod
280
+ def assistants(self, bots: List[ID]):
281
+ raise NotImplemented
282
+
266
283
 
267
284
  # noinspection PyAbstractClass
268
285
  class ResignCommand(GroupCommand, ABC):
@@ -114,7 +114,7 @@ class CombineContent(Content, ABC):
114
114
  ~~~~~~~~~~~~~~~~~~~~~~~
115
115
 
116
116
  data format: {
117
- type : i2s(0xFF),
117
+ type : i2s(0xCF),
118
118
  sn : 456,
119
119
 
120
120
  title : "...", // chat title
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimp
3
- Version: 2.3.0
3
+ Version: 2.3.1
4
4
  Summary: Decentralized Instant Messaging Protocol
5
5
  Home-page: https://github.com/dimchat/core-py
6
6
  Author: Albert Moky
@@ -0,0 +1,2 @@
1
+ dkd>=2.3.1
2
+ mkm>=2.3.1
@@ -14,7 +14,7 @@ import io
14
14
 
15
15
  from setuptools import setup, find_packages
16
16
 
17
- __version__ = '2.3.0'
17
+ __version__ = '2.3.1'
18
18
  __author__ = 'Albert Moky'
19
19
  __contact__ = 'albert.moky@gmail.com'
20
20
 
@@ -38,7 +38,7 @@ setup(
38
38
  'Operating System :: OS Independent',
39
39
  ],
40
40
  install_requires=[
41
- 'dkd>=2.3.0',
42
- 'mkm>=2.3.0',
41
+ 'dkd>=2.3.1',
42
+ 'mkm>=2.3.1',
43
43
  ]
44
44
  )
@@ -1,2 +0,0 @@
1
- dkd>=2.3.0
2
- mkm>=2.3.0
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