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.
- {dimp-2.3.0 → dimp-2.3.1}/PKG-INFO +1 -1
- {dimp-2.3.0 → dimp-2.3.1}/dimp/__init__.py +2 -1
- {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/keys.py +4 -3
- {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/pnf.py +1 -3
- {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/ted.py +17 -13
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/contents.py +17 -15
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/group_admins.py +40 -4
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/money.py +9 -1
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/quote.py +5 -1
- {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/document.py +1 -12
- {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/meta.py +0 -1
- {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/instant.py +5 -5
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/commands.py +1 -1
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/groups.py +20 -3
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/quote.py +1 -1
- {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/PKG-INFO +1 -1
- dimp-2.3.1/dimp.egg-info/requires.txt +2 -0
- {dimp-2.3.0 → dimp-2.3.1}/setup.py +3 -3
- dimp-2.3.0/dimp.egg-info/requires.txt +0 -2
- {dimp-2.3.0 → dimp-2.3.1}/LICENSE +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/README.md +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/__init__.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/crypto/algorithms.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/__init__.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/base.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/commands.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/files.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/groups.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/dkd/receipt.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/__init__.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/mkm/docs.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/__init__.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/base.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/envelope.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/reliable.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/msg/secure.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/plugins/__init__.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/plugins/cmd.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/__init__.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/contents.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/docs.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/files.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/helpers.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/money.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/receipt.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp/protocol/types.py +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/SOURCES.txt +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/dependency_links.txt +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/dimp.egg-info/top_level.txt +0 -0
- {dimp-2.3.0 → dimp-2.3.1}/setup.cfg +0 -0
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
# ==============================================================================
|
|
30
30
|
|
|
31
31
|
from abc import ABC
|
|
32
|
-
from typing import
|
|
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]) ->
|
|
61
|
+
def get_key_algorithm(cls, key: Dict[str, Any]) -> str:
|
|
62
62
|
ext = SharedCryptoExtensions()
|
|
63
|
-
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
array = self.__list
|
|
110
|
+
if array is None:
|
|
110
111
|
array = self.get('contents')
|
|
111
112
|
if array is None:
|
|
112
|
-
|
|
113
|
+
array = []
|
|
113
114
|
else:
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
@@ -86,11 +86,11 @@ class PlainMessage(BaseMessage, InstantMessage):
|
|
|
86
86
|
|
|
87
87
|
@property # Override
|
|
88
88
|
def time(self) -> Optional[DateTime]:
|
|
89
|
-
|
|
90
|
-
if
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return
|
|
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:
|
|
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):
|
|
@@ -14,7 +14,7 @@ import io
|
|
|
14
14
|
|
|
15
15
|
from setuptools import setup, find_packages
|
|
16
16
|
|
|
17
|
-
__version__ = '2.3.
|
|
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.
|
|
42
|
-
'mkm>=2.3.
|
|
41
|
+
'dkd>=2.3.1',
|
|
42
|
+
'mkm>=2.3.1',
|
|
43
43
|
]
|
|
44
44
|
)
|
|
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
|