dimples 1.5.1__tar.gz → 1.5.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.
Files changed (177) hide show
  1. {dimples-1.5.1 → dimples-1.5.2}/PKG-INFO +1 -1
  2. {dimples-1.5.1 → dimples-1.5.2}/dimples/__init__.py +0 -2
  3. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/__init__.py +4 -0
  4. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/facebook.py +11 -7
  5. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/messenger.py +2 -1
  6. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/packer.py +3 -3
  7. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/processor.py +3 -4
  8. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/__init__.py +0 -2
  9. dimples-1.5.2/dimples/common/archivist.py +223 -0
  10. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/checker.py +5 -4
  11. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/compat/__init__.py +6 -2
  12. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/compat/compatible.py +36 -28
  13. dimples-1.5.2/dimples/common/compat/compressor.py +68 -0
  14. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/compat/loader.py +15 -15
  15. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/facebook.py +39 -148
  16. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/messenger.py +14 -44
  17. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/packer.py +28 -4
  18. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/__init__.py +0 -4
  19. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/ans.py +1 -1
  20. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/handshake.py +1 -1
  21. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/login.py +1 -1
  22. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/report.py +2 -1
  23. {dimples-1.5.1 → dimples-1.5.2}/dimples/edge/shared.py +4 -3
  24. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/delegate.py +2 -1
  25. {dimples-1.5.1 → dimples-1.5.2}/dimples/register/shared.py +2 -2
  26. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/messenger.py +4 -1
  27. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/processor.py +4 -1
  28. {dimples-1.5.1 → dimples-1.5.2}/dimples/station/shared.py +4 -3
  29. {dimples-1.5.1 → dimples-1.5.2}/dimples/utils/thanos.py +4 -1
  30. {dimples-1.5.1 → dimples-1.5.2}/dimples.egg-info/PKG-INFO +1 -1
  31. {dimples-1.5.1 → dimples-1.5.2}/dimples.egg-info/SOURCES.txt +1 -1
  32. {dimples-1.5.1 → dimples-1.5.2}/dimples.egg-info/requires.txt +2 -3
  33. {dimples-1.5.1 → dimples-1.5.2}/setup.py +6 -6
  34. dimples-1.5.1/dimples/common/archivist.py +0 -106
  35. dimples-1.5.1/dimples/common/protocol/group.py +0 -96
  36. {dimples-1.5.1 → dimples-1.5.2}/LICENSE +0 -0
  37. {dimples-1.5.1 → dimples-1.5.2}/README.md +0 -0
  38. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/checker.py +0 -0
  39. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/checkpoint.py +0 -0
  40. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/__init__.py +0 -0
  41. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/commands.py +0 -0
  42. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/creator.py +0 -0
  43. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/customized.py +0 -0
  44. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/group.py +0 -0
  45. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/grp_expel.py +0 -0
  46. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/grp_invite.py +0 -0
  47. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/grp_join.py +0 -0
  48. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/grp_query.py +0 -0
  49. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/grp_quit.py +0 -0
  50. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/grp_reset.py +0 -0
  51. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/grp_resign.py +0 -0
  52. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/cpu/handshake.py +0 -0
  53. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/network/__init__.py +0 -0
  54. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/network/session.py +0 -0
  55. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/network/state.py +0 -0
  56. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/network/transition.py +0 -0
  57. {dimples-1.5.1 → dimples-1.5.2}/dimples/client/terminal.py +0 -0
  58. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/anonymous.py +0 -0
  59. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/ans.py +0 -0
  60. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/compat/address.py +0 -0
  61. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/compat/entity.py +0 -0
  62. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/compat/meta.py +0 -0
  63. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/compat/network.py +0 -0
  64. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/dbi/__init__.py +0 -0
  65. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/dbi/account.py +0 -0
  66. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/dbi/message.py +0 -0
  67. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/dbi/session.py +0 -0
  68. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/processer.py +0 -0
  69. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/block.py +0 -0
  70. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/customized.py +0 -0
  71. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/mute.py +0 -0
  72. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/password.py +0 -0
  73. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/roles.py +0 -0
  74. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/utils.py +0 -0
  75. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/protocol/version.py +0 -0
  76. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/queue.py +0 -0
  77. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/register.py +0 -0
  78. {dimples-1.5.1 → dimples-1.5.2}/dimples/common/session.py +0 -0
  79. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/__init__.py +0 -0
  80. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/flexible.py +0 -0
  81. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/gate.py +0 -0
  82. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/gatekeeper.py +0 -0
  83. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/mars.py +0 -0
  84. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/mtp.py +0 -0
  85. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/protocol/__init__.py +0 -0
  86. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/protocol/mars.py +0 -0
  87. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/protocol/ws.py +0 -0
  88. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/queue.py +0 -0
  89. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/seeker.py +0 -0
  90. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/session.py +0 -0
  91. {dimples-1.5.1 → dimples-1.5.2}/dimples/conn/ws.py +0 -0
  92. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/__init__.py +0 -0
  93. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/account.py +0 -0
  94. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/__init__.py +0 -0
  95. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/base.py +0 -0
  96. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/document.py +0 -0
  97. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/group.py +0 -0
  98. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/group_history.py +0 -0
  99. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/group_keys.py +0 -0
  100. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/login.py +0 -0
  101. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/meta.py +0 -0
  102. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/private.py +0 -0
  103. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/station.py +0 -0
  104. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/dos/user.py +0 -0
  105. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/message.py +0 -0
  106. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/__init__.py +0 -0
  107. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/base.py +0 -0
  108. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/document.py +0 -0
  109. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/group.py +0 -0
  110. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/grp_history.py +0 -0
  111. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/grp_keys.py +0 -0
  112. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/login.py +0 -0
  113. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/message.py +0 -0
  114. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/meta.py +0 -0
  115. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/station.py +0 -0
  116. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/redis/user.py +0 -0
  117. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/session.py +0 -0
  118. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_base.py +0 -0
  119. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_cipherkey.py +0 -0
  120. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_document.py +0 -0
  121. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_group.py +0 -0
  122. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_group_history.py +0 -0
  123. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_group_keys.py +0 -0
  124. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_login.py +0 -0
  125. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_message.py +0 -0
  126. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_meta.py +0 -0
  127. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_private.py +0 -0
  128. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_station.py +0 -0
  129. {dimples-1.5.1 → dimples-1.5.2}/dimples/database/t_user.py +0 -0
  130. {dimples-1.5.1 → dimples-1.5.2}/dimples/edge/__init__.py +0 -0
  131. {dimples-1.5.1 → dimples-1.5.2}/dimples/edge/messenger.py +0 -0
  132. {dimples-1.5.1 → dimples-1.5.2}/dimples/edge/octopus.py +0 -0
  133. {dimples-1.5.1 → dimples-1.5.2}/dimples/edge/start.py +0 -0
  134. {dimples-1.5.1 → dimples-1.5.2}/dimples/emitter.py +0 -0
  135. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/__init__.py +0 -0
  136. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/admin.py +0 -0
  137. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/builder.py +0 -0
  138. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/emitter.py +0 -0
  139. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/helper.py +0 -0
  140. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/manager.py +0 -0
  141. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/packer.py +0 -0
  142. {dimples-1.5.1 → dimples-1.5.2}/dimples/group/shared.py +0 -0
  143. {dimples-1.5.1 → dimples-1.5.2}/dimples/register/__init__.py +0 -0
  144. {dimples-1.5.1 → dimples-1.5.2}/dimples/register/base.py +0 -0
  145. {dimples-1.5.1 → dimples-1.5.2}/dimples/register/ext.py +0 -0
  146. {dimples-1.5.1 → dimples-1.5.2}/dimples/register/run.py +0 -0
  147. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/__init__.py +0 -0
  148. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/checker.py +0 -0
  149. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/cpu/__init__.py +0 -0
  150. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/cpu/ans.py +0 -0
  151. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/cpu/creator.py +0 -0
  152. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/cpu/document.py +0 -0
  153. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/cpu/handshake.py +0 -0
  154. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/cpu/login.py +0 -0
  155. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/cpu/report.py +0 -0
  156. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/deliver.py +0 -0
  157. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/dis_roamer.py +0 -0
  158. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/dispatcher.py +0 -0
  159. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/facebook.py +0 -0
  160. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/packer.py +0 -0
  161. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/push.py +0 -0
  162. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/session.py +0 -0
  163. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/session_center.py +0 -0
  164. {dimples-1.5.1 → dimples-1.5.2}/dimples/server/trace.py +0 -0
  165. {dimples-1.5.1 → dimples-1.5.2}/dimples/station/__init__.py +0 -0
  166. {dimples-1.5.1 → dimples-1.5.2}/dimples/station/handler.py +0 -0
  167. {dimples-1.5.1 → dimples-1.5.2}/dimples/station/start.py +0 -0
  168. {dimples-1.5.1 → dimples-1.5.2}/dimples/utils/__init__.py +0 -0
  169. {dimples-1.5.1 → dimples-1.5.2}/dimples/utils/cache.py +0 -0
  170. {dimples-1.5.1 → dimples-1.5.2}/dimples/utils/checker.py +0 -0
  171. {dimples-1.5.1 → dimples-1.5.2}/dimples/utils/config.py +0 -0
  172. {dimples-1.5.1 → dimples-1.5.2}/dimples/utils/http.py +0 -0
  173. {dimples-1.5.1 → dimples-1.5.2}/dimples/utils/log.py +0 -0
  174. {dimples-1.5.1 → dimples-1.5.2}/dimples.egg-info/dependency_links.txt +0 -0
  175. {dimples-1.5.1 → dimples-1.5.2}/dimples.egg-info/entry_points.txt +0 -0
  176. {dimples-1.5.1 → dimples-1.5.2}/dimples.egg-info/top_level.txt +0 -0
  177. {dimples-1.5.1 → dimples-1.5.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 1.5.1
3
+ Version: 1.5.2
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -348,8 +348,6 @@ __all__ = [
348
348
 
349
349
  'ReportCommand',
350
350
 
351
- 'GroupKeyCommand',
352
-
353
351
  'CustomizedContent', 'AppCustomizedContent',
354
352
 
355
353
  #
@@ -35,7 +35,9 @@ from .network import SessionState
35
35
  from .checkpoint import Checkpoint
36
36
  from .checker import ClientChecker
37
37
 
38
+ from .facebook import ClientArchivist
38
39
  from .facebook import ClientFacebook
40
+
39
41
  from .messenger import ClientMessenger
40
42
  from .packer import ClientMessagePacker
41
43
  from .processor import ClientMessageProcessor
@@ -58,7 +60,9 @@ __all__ = [
58
60
  'Checkpoint',
59
61
  'ClientChecker',
60
62
 
63
+ 'ClientArchivist',
61
64
  'ClientFacebook',
65
+
62
66
  'ClientMessenger',
63
67
  'ClientMessagePacker',
64
68
  'ClientMessageProcessor',
@@ -39,17 +39,16 @@ from dimsdk import Group
39
39
  from ..utils import Runner
40
40
  from ..common import BroadcastUtils
41
41
  from ..common import CommonFacebook
42
+ from ..common import CommonArchivist
42
43
  from ..group import SharedGroupManager
43
44
 
44
45
 
45
- class ClientFacebook(CommonFacebook):
46
+ class ClientArchivist(CommonArchivist):
46
47
 
47
48
  # Override
48
- async def get_group(self, identifier: ID) -> Optional[Group]:
49
- group = await super().get_group(identifier=identifier)
50
- if group is not None:
51
- group.data_source = SharedGroupManager()
52
- return group
49
+ def cache_group(self, group: Group):
50
+ group.data_source = SharedGroupManager()
51
+ super().cache_group(group=group)
53
52
 
54
53
  # Override
55
54
  async def save_document(self, document: Document) -> bool:
@@ -61,9 +60,14 @@ class ClientFacebook(CommonFacebook):
61
60
  group = document.identifier
62
61
  assert group.is_group, 'group ID error: %s' % group
63
62
  admins = ID.convert(array=array)
64
- ok = await self.save_administrators(administrators=admins, group=group)
63
+ db = self.database
64
+ ok = await db.save_administrators(administrators=admins, group=group)
65
65
  return ok
66
66
 
67
+
68
+ class ClientFacebook(CommonFacebook):
69
+ """ Client Facebook with Address Name Service """
70
+
67
71
  #
68
72
  # Group DataSource
69
73
  #
@@ -167,6 +167,7 @@ class ClientMessenger(CommonMessenger):
167
167
 
168
168
  async def _update_visa(self) -> Optional[Visa]:
169
169
  facebook = self.facebook
170
+ archivist = facebook.archivist
170
171
  user = await facebook.current_user
171
172
  assert user is not None, 'current user not found'
172
173
  # 1. get sign key for current user
@@ -189,7 +190,7 @@ class ClientMessenger(CommonMessenger):
189
190
  })
190
191
  if visa.sign(private_key=pri_key) is None:
191
192
  self.error(msg='failed to sign visa: %s, private key: %s' % (visa, pri_key))
192
- elif await facebook.save_document(document=visa):
193
+ elif await archivist.save_document(document=visa):
193
194
  self.info(msg='visa updated: %s' % visa)
194
195
  return visa
195
196
  else:
@@ -73,7 +73,7 @@ class ClientMessagePacker(CommonMessagePacker):
73
73
  'message': 'group not ready',
74
74
  'group': str(receiver),
75
75
  }
76
- self.messenger.suspend_instant_message(msg=msg, error=error)
76
+ self.suspend_instant_message(msg=msg, error=error)
77
77
  return False
78
78
  #
79
79
  # check group members' visa key
@@ -92,7 +92,7 @@ class ClientMessagePacker(CommonMessagePacker):
92
92
  'group': str(receiver),
93
93
  'members': ID.revert(identifiers=waiting),
94
94
  }
95
- self.messenger.suspend_instant_message(msg=msg, error=error)
95
+ self.suspend_instant_message(msg=msg, error=error)
96
96
  # perhaps some members have already disappeared,
97
97
  # although the packer will query document when the member's visa key is not found,
98
98
  # but the station will never respond with the right document,
@@ -129,7 +129,7 @@ class ClientMessagePacker(CommonMessagePacker):
129
129
  'message': 'group not ready',
130
130
  'group': str(receiver),
131
131
  }
132
- self.messenger.suspend_reliable_message(msg=msg, error=error) # msg['error'] = error
132
+ self.suspend_reliable_message(msg=msg, error=error) # msg['error'] = error
133
133
  return False
134
134
 
135
135
  # Override
@@ -114,11 +114,10 @@ class ClientMessageProcessor(CommonMessageProcessor):
114
114
  return responses
115
115
  sender = r_msg.sender
116
116
  receiver = r_msg.receiver
117
- user = await self.facebook.select_user(receiver=receiver)
118
- if user is None:
117
+ me = await self.facebook.select_local_user(receiver=receiver)
118
+ if me is None:
119
119
  # assert False, 'receiver error: %s' % receiver
120
120
  return responses
121
- receiver = user.identifier
122
121
  messenger = self.messenger
123
122
  # check responses
124
123
  from_bots = sender.type == EntityType.STATION or sender.type == EntityType.BOT
@@ -137,6 +136,6 @@ class ClientMessageProcessor(CommonMessageProcessor):
137
136
  self.info(msg='drop text to %s, origin time=[%s], text=%s' % (sender, r_msg.time, res.text))
138
137
  continue
139
138
  # normal response
140
- await messenger.send_content(sender=receiver, receiver=sender, content=res, priority=1)
139
+ await messenger.send_content(sender=me, receiver=sender, content=res, priority=1)
141
140
  # DON'T respond to station directly
142
141
  return []
@@ -67,8 +67,6 @@ __all__ = [
67
67
 
68
68
  'ReportCommand',
69
69
 
70
- 'GroupKeyCommand',
71
-
72
70
  'CustomizedContent', 'AppCustomizedContent',
73
71
 
74
72
  #
@@ -0,0 +1,223 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # DIM-SDK : Decentralized Instant Messaging Software Development Kit
4
+ #
5
+ # Written in 2023 by Moky <albert.moky@gmail.com>
6
+ #
7
+ # ==============================================================================
8
+ # MIT License
9
+ #
10
+ # Copyright (c) 2023 Albert Moky
11
+ #
12
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ # of this software and associated documentation files (the "Software"), to deal
14
+ # in the Software without restriction, including without limitation the rights
15
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ # copies of the Software, and to permit persons to whom the Software is
17
+ # furnished to do so, subject to the following conditions:
18
+ #
19
+ # The above copyright notice and this permission notice shall be included in all
20
+ # copies or substantial portions of the Software.
21
+ #
22
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ # SOFTWARE.
29
+ # ==============================================================================
30
+
31
+ import weakref
32
+ from typing import Optional, List
33
+
34
+ from dimsdk import DateTime
35
+ from dimsdk import VerifyKey, EncryptKey
36
+ from dimsdk import ID, Meta, Document
37
+ from dimsdk import MetaUtils, DocumentUtils
38
+ from dimsdk import User, Group
39
+ from dimsdk import Facebook
40
+ from dimsdk import Archivist
41
+ from dimsdk import Barrack
42
+
43
+ from ..utils import Logging
44
+ from ..utils import MemoryCache, ThanosCache
45
+
46
+ from .dbi import AccountDBI
47
+
48
+
49
+ class CommonArchivist(Barrack, Archivist, Logging):
50
+
51
+ def __init__(self, facebook: Facebook, database: AccountDBI):
52
+ super().__init__()
53
+ self.__facebook = weakref.ref(facebook)
54
+ self.__database = database
55
+ # memory caches
56
+ self.__user_cache = self._create_user_cache()
57
+ self.__group_cache = self._create_group_cache()
58
+
59
+ @property
60
+ def facebook(self) -> Optional[Facebook]:
61
+ return self.__facebook()
62
+
63
+ @property
64
+ def database(self) -> AccountDBI:
65
+ return self.__database
66
+
67
+ # noinspection PyMethodMayBeStatic
68
+ def _create_user_cache(self) -> MemoryCache[ID, User]:
69
+ return ThanosCache()
70
+
71
+ # noinspection PyMethodMayBeStatic
72
+ def _create_group_cache(self) -> MemoryCache[ID, Group]:
73
+ return ThanosCache()
74
+
75
+ def reduce_memory(self):
76
+ """
77
+ Call it when received 'UIApplicationDidReceiveMemoryWarningNotification',
78
+ this will remove 50% of cached objects
79
+
80
+ :return: number of survivors
81
+ """
82
+ cnt1 = self.__user_cache.reduce_memory()
83
+ cnt2 = self.__group_cache.reduce_memory()
84
+ return cnt1 + cnt2
85
+
86
+ #
87
+ # Barrack
88
+ #
89
+
90
+ # Override
91
+ def cache_user(self, user: User):
92
+ if user.data_source is None:
93
+ user.data_source = self.facebook
94
+ self.__user_cache.put(key=user.identifier, value=user)
95
+
96
+ # Override
97
+ def cache_group(self, group: Group):
98
+ if group.data_source is None:
99
+ group.data_source = self.facebook
100
+ self.__group_cache.put(key=group.identifier, value=group)
101
+
102
+ # Override
103
+ def get_user(self, identifier: ID):
104
+ return self.__user_cache.get(key=identifier)
105
+
106
+ # Override
107
+ def get_group(self, identifier: ID):
108
+ return self.__group_cache.get(key=identifier)
109
+
110
+ #
111
+ # Archivist
112
+ #
113
+
114
+ # Override
115
+ async def save_meta(self, meta: Meta, identifier: ID) -> bool:
116
+ #
117
+ # 1. check valid
118
+ #
119
+ if not self.check_meta(meta=meta, identifier=identifier):
120
+ self.warning(msg='meta not valid: %s' % identifier)
121
+ return False
122
+ #
123
+ # 2. check duplicated
124
+ #
125
+ old = await self.facebook.get_meta(identifier=identifier)
126
+ if old is not None:
127
+ self.debug(msg='meta duplicated: %s' % identifier)
128
+ return True
129
+ #
130
+ # 3. save into database
131
+ #
132
+ db = self.database
133
+ return await db.save_meta(meta=meta, identifier=identifier)
134
+
135
+ # protected
136
+ def check_meta(self, meta: Meta, identifier: ID) -> bool:
137
+ if meta.valid:
138
+ return MetaUtils.match_identifier(identifier=identifier, meta=meta)
139
+ else:
140
+ self.warning(msg='meta error: %s -> %s' % (meta, identifier))
141
+
142
+ # Override
143
+ async def save_document(self, document: Document) -> bool:
144
+ #
145
+ # 1. check valid
146
+ #
147
+ valid = await self.check_document(document=document)
148
+ if not valid:
149
+ self.warning(msg='meta not valid: %s' % document.identifier)
150
+ return False
151
+ #
152
+ # 2. check expired
153
+ #
154
+ expired = await self.is_document_expired(document=document)
155
+ if expired:
156
+ self.info(msg='drop expired document: %s' % document)
157
+ return False
158
+ #
159
+ # 3. save into database
160
+ #
161
+ db = self.database
162
+ return await db.save_document(document=document)
163
+
164
+ # protected
165
+ async def check_document(self, document: Document) -> bool:
166
+ identifier = document.identifier
167
+ doc_time = document.time
168
+ # check document time
169
+ if doc_time is None:
170
+ self.warning(msg='document without time: %s' % identifier)
171
+ else:
172
+ # calibrate the clock
173
+ # make sure the document time is not in the far future
174
+ near_future = DateTime.current_timestamp() + 30 * 60
175
+ if doc_time > near_future:
176
+ self.error(msg='document time error: %s, %s' % (doc_time, identifier))
177
+ return False
178
+ # check valid
179
+ return await self.verify_document(document=document)
180
+
181
+ # protected
182
+ async def verify_document(self, document: Document) -> bool:
183
+ if document.valid:
184
+ return True
185
+ else:
186
+ identifier = document.identifier
187
+ meta = await self.facebook.get_meta(identifier=identifier)
188
+ if meta is None:
189
+ self.warning(msg='failed to get meta: %s' % identifier)
190
+ else:
191
+ return document.verify(public_key=meta.public_key)
192
+
193
+ # protected
194
+ async def is_document_expired(self, document: Document) -> bool:
195
+ identifier = document.identifier
196
+ doc_type = DocumentUtils.get_document_type(document=document)
197
+ if doc_type is None:
198
+ doc_type = '*'
199
+ # check old documents with type
200
+ docs = await self.facebook.get_documents(identifier=identifier)
201
+ if docs is None or len(docs) == 0:
202
+ return False
203
+ old = DocumentUtils.last_document(documents=docs, doc_type=doc_type)
204
+ return old is not None and DocumentUtils.is_expired(this_doc=document, old_doc=old)
205
+
206
+ # Override
207
+ async def get_meta_key(self, identifier: ID) -> Optional[VerifyKey]:
208
+ meta = await self.facebook.get_meta(identifier=identifier)
209
+ if meta is not None:
210
+ return meta.public_key
211
+
212
+ # Override
213
+ async def get_visa_key(self, identifier: ID) -> Optional[EncryptKey]:
214
+ docs = await self.facebook.get_documents(identifier=identifier)
215
+ if docs is None or len(docs) == 0:
216
+ return None
217
+ visa = DocumentUtils.last_visa(documents=docs)
218
+ if visa is not None:
219
+ return visa.public_key
220
+
221
+ # Override
222
+ async def local_users(self) -> List[ID]:
223
+ return await self.database.get_local_users()
@@ -34,12 +34,13 @@ from typing import Optional, List, Dict
34
34
  from dimsdk import DateTime
35
35
  from dimsdk import ID, Meta, Document, Visa
36
36
 
37
+ from ..utils import Logging
37
38
  from ..utils import FrequencyChecker, RecentTimeChecker
38
39
 
39
40
  from .dbi import AccountDBI
40
41
 
41
42
 
42
- class EntityChecker(ABC):
43
+ class EntityChecker(Logging, ABC):
43
44
 
44
45
  # each query will be expired after 10 minutes
45
46
  QUERY_EXPIRES = 10 * 60
@@ -174,7 +175,7 @@ class EntityChecker(ABC):
174
175
  doc_time = doc.time
175
176
  if doc_time is None:
176
177
  # assert False, 'document error: %s' % doc
177
- continue
178
+ self.warning(msg='document time error: %s' % doc)
178
179
  elif last_time is None or last_time < doc_time:
179
180
  last_time = doc_time
180
181
  # OK
@@ -221,8 +222,8 @@ class EntityChecker(ABC):
221
222
  for cmd, _ in array:
222
223
  his_time = cmd.time
223
224
  if his_time is None:
224
- assert False, 'group command error: %s' % cmd
225
- pass
225
+ # assert False, 'group command error: %s' % cmd
226
+ self.warning(msg='group command time error: %s' % cmd)
226
227
  elif last_time is None or last_time < his_time:
227
228
  last_time = his_time
228
229
  # OK
@@ -38,9 +38,11 @@ from .address import UnknownAddress
38
38
 
39
39
  from .meta import CompatibleMetaFactory
40
40
 
41
- from .loader import CommonLoader
41
+ from .loader import CommonExtensionLoader
42
42
  from .loader import CommonPluginLoader
43
+ from .loader import LibraryLoader
43
44
 
45
+ from .compressor import CompatibleCompressor # , CompatibleShortener
44
46
  from .compatible import Compatible, CompatibleIncoming, CompatibleOutgoing
45
47
 
46
48
 
@@ -56,9 +58,11 @@ __all__ = [
56
58
 
57
59
  'CompatibleMetaFactory',
58
60
 
59
- 'CommonLoader',
61
+ 'CommonExtensionLoader',
60
62
  'CommonPluginLoader',
63
+ 'LibraryLoader',
61
64
 
65
+ 'CompatibleCompressor', # 'CompatibleShortener',
62
66
  'Compatible', 'CompatibleIncoming', 'CompatibleOutgoing',
63
67
 
64
68
  ]
@@ -1,9 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
- #
3
- # Ming-Ke-Ming : Decentralized User Identity Authentication
4
- #
5
- # Written in 2022 by Moky <albert.moky@gmail.com>
6
- #
7
2
  # ==============================================================================
8
3
  # MIT License
9
4
  #
@@ -57,7 +52,7 @@ class Compatible:
57
52
  def fix_meta_attachment(cls, msg: ReliableMessage):
58
53
  meta = msg.get('meta')
59
54
  if meta is not None:
60
- return _fix_meta_version(meta=meta)
55
+ _fix_meta_version(meta=meta)
61
56
 
62
57
  @classmethod
63
58
  def fix_meta_version(cls, meta: Dict[str, Any]):
@@ -67,7 +62,7 @@ class Compatible:
67
62
  def fix_visa_attachment(cls, msg: ReliableMessage):
68
63
  visa = msg.get('visa')
69
64
  if visa is not None:
70
- return _fix_doc_id(document=visa)
65
+ _fix_doc_id(document=visa)
71
66
 
72
67
  @classmethod
73
68
  def fix_document_id(cls, document: Dict[str, Any]):
@@ -76,32 +71,40 @@ class Compatible:
76
71
 
77
72
  def _fix_cmd(content: Dict[str, Any]):
78
73
  cmd = content.get('command')
79
- if cmd is not None:
80
- # command to cmd
81
- if 'cmd' not in content:
82
- content['cmd'] = cmd
83
- else:
84
- # cmd to command
74
+ if cmd is None:
75
+ # 'command' not exists, copy the value from 'cmd'
85
76
  cmd = content.get('cmd')
86
77
  if cmd is not None:
87
78
  content['command'] = cmd
79
+ else:
80
+ assert False, 'command error: %s' % content
81
+ elif 'cmd' in content:
82
+ # these two values must be equal
83
+ assert content.get('cmd') == cmd, 'command error: %s' % content
84
+ else:
85
+ # copy value from 'command' to 'cmd'
86
+ content['cmd'] = cmd
88
87
 
89
88
 
90
89
  def _fix_did(content: Dict[str, Any]):
91
90
  did = content.get('did')
92
- if did is not None:
93
- # did to ID
94
- if 'ID' not in content:
95
- content['ID'] = did
96
- else:
97
- # ID to did
91
+ if did is None:
92
+ # 'did' not exists, copy the value from 'ID'
98
93
  did = content.get('ID')
99
94
  if did is not None:
100
95
  content['did'] = did
96
+ # else:
97
+ # assert False, 'did not exists: %s' % content
98
+ elif 'ID' in content:
99
+ # these two values must be equal
100
+ assert content.get('ID') == did, 'did error: %s' % content
101
+ else:
102
+ # copy value from 'did' to 'ID'
103
+ content['ID'] = did
101
104
 
102
105
 
103
106
  def _fix_doc_id(document: Dict[str, Any]):
104
- # 'ID' <-> 'did
107
+ # 'ID' <-> 'did'
105
108
  _fix_did(document)
106
109
  return document
107
110
 
@@ -133,7 +136,14 @@ def _fix_file_content(content: Dict[str, Any]):
133
136
  pwd = content.get('password')
134
137
  if pwd is not None:
135
138
  content['key'] = pwd
136
- return content
139
+
140
+
141
+ _file_types = [
142
+ ContentType.FILE, 'file',
143
+ ContentType.IMAGE, 'image',
144
+ ContentType.AUDIO, 'audio',
145
+ ContentType.VIDEO, 'video',
146
+ ]
137
147
 
138
148
 
139
149
  # TODO: remove after all server/client upgraded
@@ -145,10 +155,7 @@ class CompatibleIncoming:
145
155
  msg_type = content.get('type')
146
156
  msg_type = Converter.get_str(value=msg_type, default='')
147
157
 
148
- if msg_type == ContentType.FILE or msg_type == 'file' or \
149
- msg_type == ContentType.IMAGE or msg_type == 'image' or \
150
- msg_type == ContentType.AUDIO or msg_type == 'audio' or \
151
- msg_type == ContentType.VIDEO or msg_type == 'video':
158
+ if msg_type in _file_types:
152
159
  # 1. 'key' <-> 'password'
153
160
  _fix_file_content(content=content)
154
161
  return
@@ -174,12 +181,13 @@ class CompatibleIncoming:
174
181
 
175
182
  if cmd == LoginCommand.LOGIN:
176
183
  # 2. 'ID' <-> 'did'
177
- _fix_doc_id(content)
184
+ _fix_did(content=content)
178
185
  return
179
186
 
180
187
  if cmd == Command.DOCUMENTS or cmd == 'document':
181
- # 2. 'cmd: 'document' -> 'documents'
188
+ # 2. cmd: 'document' -> 'documents'
182
189
  cls._fix_docs(content=content)
190
+
183
191
  if cmd == Command.META or cmd == Command.DOCUMENTS or cmd == 'document':
184
192
  # 3. 'ID' <-> 'did'
185
193
  _fix_did(content=content)
@@ -197,7 +205,7 @@ class CompatibleIncoming:
197
205
  # 'document' -> 'documents'
198
206
  doc = content.get('document')
199
207
  if doc is not None:
200
- content['documents'] = [_fix_did(doc)]
208
+ content['documents'] = [_fix_doc_id(document=doc)]
201
209
  content.pop('document', None)
202
210
 
203
211
 
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+ # ==============================================================================
3
+ # MIT License
4
+ #
5
+ # Copyright (c) 2025 Albert Moky
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ # ==============================================================================
25
+
26
+ from typing import Optional, Dict
27
+
28
+ from dimsdk import MessageCompressor
29
+ from dimsdk import MessageShortener
30
+
31
+ from .compatible import CompatibleIncoming
32
+
33
+
34
+ class CompatibleCompressor(MessageCompressor):
35
+
36
+ # Override
37
+ def _create_shortener(self) -> MessageShortener:
38
+ return CompatibleShortener()
39
+
40
+ # Override
41
+ def compress_content(self, content: Dict, key: Dict) -> bytes:
42
+ # CompatibleOutgoing.fix_content(content=content);
43
+ return super().compress_content(content=content, key=key)
44
+
45
+ # Override
46
+ def extract_content(self, data: bytes, key: Dict) -> Optional[Dict]:
47
+ content = super().extract_content(data=data, key=key)
48
+ if content is not None:
49
+ CompatibleIncoming.fix_content(content=content)
50
+ return content
51
+
52
+
53
+ class CompatibleShortener(MessageShortener):
54
+
55
+ # Override
56
+ def compress_content(self, content: Dict) -> Dict:
57
+ # DON'T COMPRESS NOW
58
+ return content
59
+
60
+ # Override
61
+ def compress_symmetric_key(self, key: Dict) -> Dict:
62
+ # DON'T COMPRESS NOW
63
+ return key
64
+
65
+ # Override
66
+ def compress_reliable_message(self, msg: Dict) -> Dict:
67
+ # DON'T COMPRESS NOW
68
+ return msg