dimples 0.5.5__tar.gz → 0.5.7__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 (143) hide show
  1. {dimples-0.5.5 → dimples-0.5.7}/PKG-INFO +1 -1
  2. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/network/state.py +4 -0
  3. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/dbi/session.py +4 -0
  4. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/session.py +2 -0
  5. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/protocol/mars.py +4 -0
  6. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/session.py +10 -0
  7. {dimples-0.5.5 → dimples-0.5.7}/dimples/edge/octopus.py +48 -19
  8. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/archivist.py +7 -3
  9. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/dispatcher.py +92 -16
  10. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/processor.py +9 -41
  11. {dimples-0.5.5 → dimples-0.5.7}/dimples/utils/config.py +2 -0
  12. {dimples-0.5.5 → dimples-0.5.7}/dimples.egg-info/PKG-INFO +1 -1
  13. {dimples-0.5.5 → dimples-0.5.7}/setup.py +1 -1
  14. {dimples-0.5.5 → dimples-0.5.7}/README.md +0 -0
  15. {dimples-0.5.5 → dimples-0.5.7}/dimples/__init__.py +0 -0
  16. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/__init__.py +0 -0
  17. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/archivist.py +0 -0
  18. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/checkpoint.py +0 -0
  19. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/__init__.py +0 -0
  20. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/commands.py +0 -0
  21. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/creator.py +0 -0
  22. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/group.py +0 -0
  23. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/grp_expel.py +0 -0
  24. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/grp_invite.py +0 -0
  25. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/grp_join.py +0 -0
  26. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/grp_query.py +0 -0
  27. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/grp_quit.py +0 -0
  28. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/grp_reset.py +0 -0
  29. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/grp_resign.py +0 -0
  30. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/cpu/handshake.py +0 -0
  31. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/facebook.py +0 -0
  32. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/messenger.py +0 -0
  33. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/network/__init__.py +0 -0
  34. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/network/session.py +0 -0
  35. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/network/transition.py +0 -0
  36. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/packer.py +0 -0
  37. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/processor.py +0 -0
  38. {dimples-0.5.5 → dimples-0.5.7}/dimples/client/terminal.py +0 -0
  39. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/__init__.py +0 -0
  40. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/anonymous.py +0 -0
  41. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/ans.py +0 -0
  42. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/archivist.py +0 -0
  43. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/compat/__init__.py +0 -0
  44. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/compat/btc.py +0 -0
  45. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/compat/compatible.py +0 -0
  46. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/compat/entity.py +0 -0
  47. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/compat/meta.py +0 -0
  48. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/compat/network.py +0 -0
  49. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/dbi/__init__.py +0 -0
  50. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/dbi/account.py +0 -0
  51. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/dbi/message.py +0 -0
  52. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/facebook.py +0 -0
  53. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/messenger.py +0 -0
  54. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/packer.py +0 -0
  55. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/processer.py +0 -0
  56. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/protocol/__init__.py +0 -0
  57. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/protocol/ans.py +0 -0
  58. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/protocol/block.py +0 -0
  59. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/protocol/handshake.py +0 -0
  60. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/protocol/login.py +0 -0
  61. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/protocol/mute.py +0 -0
  62. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/protocol/report.py +0 -0
  63. {dimples-0.5.5 → dimples-0.5.7}/dimples/common/register.py +0 -0
  64. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/__init__.py +0 -0
  65. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/gate.py +0 -0
  66. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/gatekeeper.py +0 -0
  67. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/mars.py +0 -0
  68. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/mtp.py +0 -0
  69. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/protocol/__init__.py +0 -0
  70. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/protocol/ws.py +0 -0
  71. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/queue.py +0 -0
  72. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/seeker.py +0 -0
  73. {dimples-0.5.5 → dimples-0.5.7}/dimples/conn/ws.py +0 -0
  74. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/__init__.py +0 -0
  75. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/account.py +0 -0
  76. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/__init__.py +0 -0
  77. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/base.py +0 -0
  78. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/document.py +0 -0
  79. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/group.py +0 -0
  80. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/group_history.py +0 -0
  81. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/group_keys.py +0 -0
  82. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/login.py +0 -0
  83. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/meta.py +0 -0
  84. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/private.py +0 -0
  85. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/station.py +0 -0
  86. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/dos/user.py +0 -0
  87. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/message.py +0 -0
  88. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/session.py +0 -0
  89. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_cipherkey.py +0 -0
  90. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_document.py +0 -0
  91. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_group.py +0 -0
  92. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_group_history.py +0 -0
  93. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_group_keys.py +0 -0
  94. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_login.py +0 -0
  95. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_message.py +0 -0
  96. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_meta.py +0 -0
  97. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_private.py +0 -0
  98. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_station.py +0 -0
  99. {dimples-0.5.5 → dimples-0.5.7}/dimples/database/t_user.py +0 -0
  100. {dimples-0.5.5 → dimples-0.5.7}/dimples/edge/__init__.py +0 -0
  101. {dimples-0.5.5 → dimples-0.5.7}/dimples/edge/shared.py +0 -0
  102. {dimples-0.5.5 → dimples-0.5.7}/dimples/edge/start.py +0 -0
  103. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/__init__.py +0 -0
  104. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/admin.py +0 -0
  105. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/builder.py +0 -0
  106. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/delegate.py +0 -0
  107. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/emitter.py +0 -0
  108. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/helper.py +0 -0
  109. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/manager.py +0 -0
  110. {dimples-0.5.5 → dimples-0.5.7}/dimples/group/packer.py +0 -0
  111. {dimples-0.5.5 → dimples-0.5.7}/dimples/register/__init__.py +0 -0
  112. {dimples-0.5.5 → dimples-0.5.7}/dimples/register/base.py +0 -0
  113. {dimples-0.5.5 → dimples-0.5.7}/dimples/register/ext.py +0 -0
  114. {dimples-0.5.5 → dimples-0.5.7}/dimples/register/run.py +0 -0
  115. {dimples-0.5.5 → dimples-0.5.7}/dimples/register/shared.py +0 -0
  116. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/__init__.py +0 -0
  117. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/cpu/__init__.py +0 -0
  118. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/cpu/ans.py +0 -0
  119. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/cpu/document.py +0 -0
  120. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/cpu/handshake.py +0 -0
  121. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/cpu/login.py +0 -0
  122. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/cpu/report.py +0 -0
  123. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/messenger.py +0 -0
  124. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/packer.py +0 -0
  125. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/push.py +0 -0
  126. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/session.py +0 -0
  127. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/session_center.py +0 -0
  128. {dimples-0.5.5 → dimples-0.5.7}/dimples/server/trace.py +0 -0
  129. {dimples-0.5.5 → dimples-0.5.7}/dimples/station/__init__.py +0 -0
  130. {dimples-0.5.5 → dimples-0.5.7}/dimples/station/handler.py +0 -0
  131. {dimples-0.5.5 → dimples-0.5.7}/dimples/station/shared.py +0 -0
  132. {dimples-0.5.5 → dimples-0.5.7}/dimples/station/start.py +0 -0
  133. {dimples-0.5.5 → dimples-0.5.7}/dimples/utils/__init__.py +0 -0
  134. {dimples-0.5.5 → dimples-0.5.7}/dimples/utils/cache.py +0 -0
  135. {dimples-0.5.5 → dimples-0.5.7}/dimples/utils/dos.py +0 -0
  136. {dimples-0.5.5 → dimples-0.5.7}/dimples/utils/log.py +0 -0
  137. {dimples-0.5.5 → dimples-0.5.7}/dimples/utils/singleton.py +0 -0
  138. {dimples-0.5.5 → dimples-0.5.7}/dimples.egg-info/SOURCES.txt +0 -0
  139. {dimples-0.5.5 → dimples-0.5.7}/dimples.egg-info/dependency_links.txt +0 -0
  140. {dimples-0.5.5 → dimples-0.5.7}/dimples.egg-info/entry_points.txt +0 -0
  141. {dimples-0.5.5 → dimples-0.5.7}/dimples.egg-info/requires.txt +0 -0
  142. {dimples-0.5.5 → dimples-0.5.7}/dimples.egg-info/top_level.txt +0 -0
  143. {dimples-0.5.5 → dimples-0.5.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 0.5.5
3
+ Version: 0.5.7
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -136,12 +136,15 @@ class SessionState(BaseState[StateMachine, StateTransition]):
136
136
  def enter_time(self) -> float:
137
137
  return self.__time
138
138
 
139
+ # Override
139
140
  def __str__(self) -> str:
140
141
  return self.__name
141
142
 
143
+ # Override
142
144
  def __repr__(self) -> str:
143
145
  return self.__name
144
146
 
147
+ # Override
145
148
  def __eq__(self, other) -> bool:
146
149
  if isinstance(other, SessionState):
147
150
  if self is other:
@@ -156,6 +159,7 @@ class SessionState(BaseState[StateMachine, StateTransition]):
156
159
  else:
157
160
  return False
158
161
 
162
+ # Override
159
163
  def __ne__(self, other) -> bool:
160
164
  if isinstance(other, SessionState):
161
165
  if self is other:
@@ -63,10 +63,12 @@ class ProviderInfo:
63
63
  self.identifier = identifier
64
64
  self.chosen = chosen
65
65
 
66
+ # Override
66
67
  def __str__(self) -> str:
67
68
  clazz = self.__class__.__name__
68
69
  return '<%s ID="%s" chosen=%d />' % (clazz, self.identifier, self.chosen)
69
70
 
71
+ # Override
70
72
  def __repr__(self) -> str:
71
73
  clazz = self.__class__.__name__
72
74
  return '<%s ID="%s" chosen=%d />' % (clazz, self.identifier, self.chosen)
@@ -94,11 +96,13 @@ class StationInfo:
94
96
  self.provider = provider
95
97
  self.chosen = chosen
96
98
 
99
+ # Override
97
100
  def __str__(self) -> str:
98
101
  clazz = self.__class__.__name__
99
102
  return '<%s host="%s" port=%d ID="%s" SP="%s" chosen=%d />' % (clazz, self.host, self.port, self.identifier,
100
103
  self.provider, self.chosen)
101
104
 
105
+ # Override
102
106
  def __repr__(self) -> str:
103
107
  clazz = self.__class__.__name__
104
108
  return '<%s host="%s" port=%d ID="%s" SP="%s" chosen=%d />' % (clazz, self.host, self.port, self.identifier,
@@ -114,10 +114,12 @@ class Session(Transmitter, ABC):
114
114
  """ Update active flag and return True on changed """
115
115
  raise NotImplemented
116
116
 
117
+ # Override
117
118
  def __str__(self) -> str:
118
119
  clazz = self.__class__.__name__
119
120
  return '<%s:%s %s|%s active=%s />' % (clazz, self.key, self.remote_address, self.identifier, self.active)
120
121
 
122
+ # Override
121
123
  def __repr__(self) -> str:
122
124
  clazz = self.__class__.__name__
123
125
  return '<%s:%s %s|%s active=%s />' % (clazz, self.key, self.remote_address, self.identifier, self.active)
@@ -94,10 +94,12 @@ class NetMsgHead:
94
94
  self.__options = options
95
95
  self.__body_length = body_len
96
96
 
97
+ # Override
97
98
  def __str__(self) -> str:
98
99
  cname = self.__class__.__name__
99
100
  return '<%s: %d| cmd=%d, seq=%d, body_len=%d />' % (cname, self.version, self.cmd, self.seq, self.body_length)
100
101
 
102
+ # Override
101
103
  def __repr__(self) -> str:
102
104
  cname = self.__class__.__name__
103
105
  return '<%s: %d| cmd=%d, seq=%d, body_len=%d />' % (cname, self.version, self.cmd, self.seq, self.body_length)
@@ -198,11 +200,13 @@ class NetMsg:
198
200
  self.__head = head
199
201
  self.__body = body
200
202
 
203
+ # Override
201
204
  def __str__(self) -> str:
202
205
  mod = self.__module__
203
206
  cname = self.__class__.__name__
204
207
  return '<%s body_len=%d>%s</%s module="%s">' % (cname, self.body_length, self.head, cname, mod)
205
208
 
209
+ # Override
206
210
  def __repr__(self) -> str:
207
211
  mod = self.__module__
208
212
  cname = self.__class__.__name__
@@ -50,6 +50,16 @@ class BaseSession(GateKeeper, Session, ABC):
50
50
  self.__identifier: Optional[ID] = None
51
51
  self.__messenger: Optional[weakref.ReferenceType] = None
52
52
 
53
+ # Override
54
+ def __str__(self) -> str:
55
+ cname = self.__class__.__name__
56
+ return '<%s id="%s" remote="%s" key="%s" />' % (cname, self.identifier, self.remote_address, self.key)
57
+
58
+ # Override
59
+ def __repr__(self):
60
+ cname = self.__class__.__name__
61
+ return '<%s id="%s" remote="%s" key="%s" />' % (cname, self.identifier, self.remote_address, self.key)
62
+
53
63
  @property # Override
54
64
  def database(self) -> SessionDBI:
55
65
  return self.__database
@@ -190,35 +190,64 @@ class Octopus(Runner, Logging):
190
190
 
191
191
  def income_message(self, msg: ReliableMessage, priority: int = 0) -> List[ReliableMessage]:
192
192
  """ redirect message from remote station """
193
+ sender = msg.sender
194
+ receiver = msg.receiver
193
195
  sig = get_msg_sig(msg=msg)
194
196
  messenger = self.inner_messenger
195
197
  if messenger.send_reliable_message(msg=msg, priority=priority):
196
- self.info(msg='redirected msg (%s) for receiver (%s)' % (sig, msg.receiver))
198
+ self.info(msg='redirected msg (%s): %s -> %s' % (sig, sender, receiver))
197
199
  else:
198
- self.error(msg='failed to redirect msg (%s) for receiver (%s)' % (sig, msg.receiver))
200
+ self.error(msg='failed to redirect msg (%s): %s -> %s' % (sig, sender, receiver))
199
201
  # no need to respond receipt for station
200
202
  return []
201
203
 
202
204
  def outgo_message(self, msg: ReliableMessage, priority: int = 0) -> List[ReliableMessage]:
203
205
  """ redirect message to remote station """
204
- target = ID.parse(identifier=msg.get('neighbor'))
205
- if target is None:
206
- # target station not found
207
- self.info(msg='cannot get target station for receiver (%s)' % msg.receiver)
208
- return []
209
- messenger = self.get_outer_messenger(identifier=target)
210
- if messenger is None:
211
- # target station not my neighbor
212
- self.info(msg='receiver (%s) is targeted to (%s), but not my neighbor' % (msg.receiver, target))
213
- return []
214
- msg.pop('neighbor', None)
215
- if messenger.send_reliable_message(msg=msg, priority=priority):
216
- sig = get_msg_sig(msg=msg)
217
- self.info(msg='redirected msg (%s) to target (%s) for receiver (%s)' % (sig, target, msg.receiver))
218
- # no need to respond receipt for station
219
- return []
206
+ receiver = msg.receiver
207
+ # get neighbor stations
208
+ neighbor = ID.parse(identifier=msg.get('neighbor'))
209
+ if neighbor is not None:
210
+ neighbors = set()
211
+ neighbors.add(neighbor)
212
+ msg.pop('neighbor', None)
213
+ else:
214
+ with self.__outer_lock:
215
+ neighbors = set(self.__outer_map.keys())
216
+ #
217
+ # 0. check recipients
218
+ #
219
+ new_recipients = set()
220
+ old_recipients = msg.get('recipients')
221
+ old_recipients = [] if old_recipients is None else ID.convert(old_recipients)
222
+ for item in neighbors:
223
+ if item not in old_recipients:
224
+ new_recipients.add(item)
225
+ all_recipients = []
226
+ for item in old_recipients:
227
+ all_recipients.append(item)
228
+ # avoid the new recipients redirect it to same targets
229
+ self.info(msg='append new recipients: %s, %s => %s' % (receiver, new_recipients, all_recipients))
230
+ for item in new_recipients:
231
+ all_recipients.append(item)
232
+ msg['recipients'] = ID.revert(all_recipients)
233
+ #
234
+ # 1. send to the new recipients (neighbor stations)
235
+ #
220
236
  sig = get_msg_sig(msg=msg)
221
- self.error(msg='failed to redirect msg (%s) to target (%s) for receiver (%s)' % (sig, target, msg.receiver))
237
+ failed_neighbors = []
238
+ for target in new_recipients:
239
+ messenger = self.get_outer_messenger(identifier=target)
240
+ if messenger is None:
241
+ # target station not my neighbor
242
+ self.warning(msg='not my neighbor: %s (%s)' % (target, receiver))
243
+ failed_neighbors.append(target)
244
+ elif messenger.send_reliable_message(msg=msg, priority=priority):
245
+ self.info(msg='redirected msg (%s) to neighbor: %s (%s)' % (sig, target, receiver))
246
+ else:
247
+ self.error(msg='failed to send to neighbor: %s (%s)' % (target, receiver))
248
+ failed_neighbors.append(target)
249
+ if len(failed_neighbors) > 0:
250
+ self.error(msg='failed to redirect msg (%s) for receiver (%s): %s' % (sig, receiver, failed_neighbors))
222
251
  return []
223
252
 
224
253
 
@@ -44,7 +44,6 @@ from ..common import CommonArchivist
44
44
  from ..common import CommonFacebook, CommonMessenger
45
45
  from ..common import StationInfo
46
46
 
47
- from .dispatcher import Dispatcher
48
47
  from .session_center import SessionCenter
49
48
 
50
49
 
@@ -62,6 +61,11 @@ def get_messenger(archivist: CommonArchivist):
62
61
  return messenger
63
62
 
64
63
 
64
+ def get_dispatcher():
65
+ from .dispatcher import Dispatcher
66
+ return Dispatcher()
67
+
68
+
65
69
  class ServerArchivist(CommonArchivist, ABC):
66
70
 
67
71
  # each respond will be expired after 10 minutes
@@ -95,7 +99,7 @@ class ServerArchivist(CommonArchivist, ABC):
95
99
  @property
96
100
  def all_stations(self) -> List[StationInfo]:
97
101
  """ get stations from database """
98
- dispatcher = Dispatcher()
102
+ dispatcher = get_dispatcher()
99
103
  db = dispatcher.sdb
100
104
  # TODO: get chosen provider
101
105
  providers = db.all_providers()
@@ -136,7 +140,7 @@ class ServerArchivist(CommonArchivist, ABC):
136
140
  s_msg = messenger.encrypt_message(msg=i_msg)
137
141
  r_msg = messenger.sign_message(msg=s_msg)
138
142
  # dispatch
139
- dispatcher = Dispatcher()
143
+ dispatcher = get_dispatcher()
140
144
  neighbors = self.all_neighbors
141
145
  # avoid the new recipients redirect it to same targets
142
146
  r_msg['recipients'] = ID.revert(neighbors)
@@ -32,13 +32,14 @@
32
32
 
33
33
  import threading
34
34
  from abc import ABC, abstractmethod
35
- from typing import Optional, List
35
+ from typing import Optional, Set, List
36
36
 
37
- from dimsdk import EntityType, ID
37
+ from dimsdk import EntityType, ID, EVERYONE
38
+ from dimsdk import Station
38
39
  from dimsdk import Content, ReceiptCommand
39
40
  from dimsdk import ReliableMessage
40
41
 
41
- from ..utils import Singleton, Logging, Runner, Daemon
42
+ from ..utils import Singleton, Log, Logging, Runner, Daemon
42
43
  from ..common import CommonFacebook
43
44
  from ..common import MessageDBI, SessionDBI
44
45
  from ..common import ReliableMessageDBI
@@ -46,6 +47,7 @@ from ..common import LoginCommand
46
47
 
47
48
  from .session_center import SessionCenter
48
49
  from .push import PushCenter
50
+ from .archivist import ServerArchivist
49
51
 
50
52
 
51
53
  class MessageDeliver(ABC):
@@ -64,7 +66,7 @@ class MessageDeliver(ABC):
64
66
 
65
67
 
66
68
  @Singleton
67
- class Dispatcher(MessageDeliver):
69
+ class Dispatcher(MessageDeliver, Logging):
68
70
 
69
71
  def __init__(self):
70
72
  super().__init__()
@@ -147,7 +149,11 @@ class Dispatcher(MessageDeliver):
147
149
  def deliver_message(self, msg: ReliableMessage, receiver: ID) -> List[Content]:
148
150
  """ Deliver message to destination """
149
151
  worker = self.deliver_worker
150
- if receiver.type == EntityType.STATION:
152
+ if receiver.is_group:
153
+ # broadcast message to neighbor stations
154
+ # e.g.: 'stations@everywhere', 'everyone@everywhere'
155
+ return self.__deliver_group_message(msg=msg, receiver=receiver)
156
+ elif receiver.type == EntityType.STATION:
151
157
  # message to other stations
152
158
  # station won't roam to other station, so just push for it directly
153
159
  responses = worker.redirect_message(msg=msg, neighbor=receiver)
@@ -181,6 +187,66 @@ class Dispatcher(MessageDeliver):
181
187
  # message delivered
182
188
  return responses
183
189
 
190
+ def __deliver_group_message(self, msg: ReliableMessage, receiver: ID) -> List[Content]:
191
+ if receiver == Station.EVERY or receiver == EVERYONE:
192
+ # broadcast message to neighbor stations
193
+ # e.g.: 'stations@everywhere', 'everyone@everywhere'
194
+ archivist = self.facebook.archivist
195
+ assert isinstance(archivist, ServerArchivist)
196
+ candidates = archivist.all_neighbors
197
+ if len(candidates) == 0:
198
+ self.warning(msg='failed to get neighbors: %s' % receiver)
199
+ return []
200
+ self.info(msg='forward to neighbor stations: %s -> %s' % (receiver, candidates))
201
+ return self.__broadcast_message(msg=msg, receiver=receiver, neighbors=candidates)
202
+ else:
203
+ self.warning(msg='unknown group: %s' % receiver)
204
+ text = 'Group message not allow for this station'
205
+ res = ReceiptCommand.create(text=text, envelope=msg.envelope)
206
+ return [res]
207
+
208
+ def __broadcast_message(self, msg: ReliableMessage, receiver: ID, neighbors: Set[ID]) -> List[Content]:
209
+ #
210
+ # 0. check recipients
211
+ #
212
+ new_recipients = set()
213
+ old_recipients = msg.get('recipients')
214
+ old_recipients = [] if old_recipients is None else ID.convert(old_recipients)
215
+ for item in neighbors:
216
+ if item not in old_recipients:
217
+ new_recipients.add(item)
218
+ all_recipients = []
219
+ for item in old_recipients:
220
+ all_recipients.append(item)
221
+ # avoid the new recipients redirect it to same targets
222
+ self.info(msg='append new recipients: %s, %s => %s' % (receiver, new_recipients, all_recipients))
223
+ for item in new_recipients:
224
+ all_recipients.append(item)
225
+ msg['recipients'] = ID.revert(all_recipients)
226
+ #
227
+ # 1. push to neighbor stations directly
228
+ #
229
+ indirect_neighbors = set()
230
+ for target in new_recipients:
231
+ if session_push(msg=msg, receiver=target) == 0:
232
+ indirect_neighbors.add(target)
233
+ if len(indirect_neighbors) > 0:
234
+ for item in indirect_neighbors:
235
+ all_recipients.remove(item)
236
+ msg['recipients'] = ID.revert(all_recipients)
237
+ #
238
+ # 2. push to other neighbor stations via station bridge
239
+ #
240
+ worker = self.deliver_worker
241
+ worker.redirect_message(msg=msg, neighbor=None)
242
+ #
243
+ # OK
244
+ #
245
+ text = 'Message forwarded.'
246
+ cmd = ReceiptCommand.create(text=text, envelope=msg.envelope)
247
+ cmd['recipients'] = ID.revert(new_recipients)
248
+ return [cmd]
249
+
184
250
  def __save_reliable_message(self, msg: ReliableMessage, receiver: ID) -> bool:
185
251
  if receiver.type == EntityType.STATION or msg.sender.type == EntityType.STATION:
186
252
  # no need to save station message
@@ -305,7 +371,7 @@ class DeliverWorker(Logging):
305
371
  # 3. redirect message to roaming station
306
372
  return self.redirect_message(msg=msg, neighbor=roaming)
307
373
 
308
- def redirect_message(self, msg: ReliableMessage, neighbor: ID) -> Optional[List[Content]]:
374
+ def redirect_message(self, msg: ReliableMessage, neighbor: Optional[ID]) -> Optional[List[Content]]:
309
375
  """
310
376
  Redirect message to neighbor station
311
377
 
@@ -314,7 +380,7 @@ class DeliverWorker(Logging):
314
380
  :return: responses
315
381
  """
316
382
  """ Redirect message to neighbor station """
317
- assert neighbor.type == EntityType.STATION, 'neighbor station ID error: %s' % neighbor
383
+ assert neighbor is None or neighbor.type == EntityType.STATION, 'neighbor station ID error: %s' % neighbor
318
384
  self.info(msg='redirect message %s => %s to neighbor station: %s' % (msg.sender, msg.receiver, neighbor))
319
385
  # 0. check current station
320
386
  current = self.facebook.current_user.identifier
@@ -325,7 +391,7 @@ class DeliverWorker(Logging):
325
391
  # return None to tell the push center to push notification for it.
326
392
  return None
327
393
  # 1. try to push message to neighbor station directly
328
- if session_push(msg=msg, receiver=neighbor) > 0:
394
+ if neighbor is not None and session_push(msg=msg, receiver=neighbor) > 0:
329
395
  text = 'Message redirected.'
330
396
  cmd = ReceiptCommand.create(text=text, envelope=msg.envelope)
331
397
  cmd['neighbor'] = str(neighbor)
@@ -334,9 +400,10 @@ class DeliverWorker(Logging):
334
400
  return bridge_message(msg=msg, neighbor=neighbor, bridge=current)
335
401
 
336
402
 
337
- def bridge_message(msg: ReliableMessage, neighbor: ID, bridge: ID) -> Optional[List[Content]]:
403
+ def bridge_message(msg: ReliableMessage, neighbor: Optional[ID], bridge: ID) -> List[Content]:
338
404
  """
339
405
  Redirect message to neighbor station via the station bridge
406
+ if neighbor is None, try to broadcast
340
407
 
341
408
  :param msg: network message
342
409
  :param neighbor: roaming station
@@ -348,16 +415,25 @@ def bridge_message(msg: ReliableMessage, neighbor: ID, bridge: ID) -> Optional[L
348
415
  # be changed to another value before pushing to the bridge.
349
416
  # clone = msg.copy_dictionary()
350
417
  # msg = ReliableMessage.parse(msg=clone)
351
- msg['neighbor'] = str(neighbor)
352
- if session_push(msg=msg, receiver=bridge) > 0:
353
- text = 'Message redirected.'
354
- cmd = ReceiptCommand.create(text=text, envelope=msg.envelope)
355
- cmd['neighbor'] = str(neighbor)
356
- return [cmd]
418
+ if neighbor is None:
419
+ # broadcast to all neighbor stations
420
+ # except that ones already in msg['recipients']
421
+ session_push(msg=msg, receiver=bridge)
422
+ # no need to respond receipt for this broadcast message
423
+ return []
357
424
  else:
425
+ assert neighbor != bridge, 'cannot bridge cycled message: %s' % neighbor
426
+ msg['neighbor'] = str(neighbor)
427
+ # push to the bridge
428
+ if session_push(msg=msg, receiver=bridge) == 0:
358
429
  # station bridge not found
359
- # return an empty array to avoid calling push center
430
+ Log.warning(msg='failed to push message to bridge: %s, drop message: %s -> %s'
431
+ % (bridge, msg.sender, msg.receiver))
360
432
  return []
433
+ text = 'Message redirected via station bridge.'
434
+ cmd = ReceiptCommand.create(text=text, envelope=msg.envelope)
435
+ cmd['neighbor'] = str(neighbor)
436
+ return [cmd]
361
437
 
362
438
 
363
439
  def session_push(msg: ReliableMessage, receiver: ID) -> int:
@@ -56,7 +56,6 @@ from .cpu import DocumentCommandProcessor
56
56
 
57
57
  from .packer import FilterManager
58
58
  from .dispatcher import Dispatcher
59
- from .archivist import ServerArchivist
60
59
 
61
60
 
62
61
  class ServerMessageProcessor(CommonMessageProcessor):
@@ -195,49 +194,18 @@ class ServerMessageProcessor(CommonMessageProcessor):
195
194
  if bot is None:
196
195
  self.warning(msg='failed to get receiver: %s' % receiver)
197
196
  return False
198
- candidates = set()
199
- candidates.add(bot)
200
- self.info(msg='forward to station bot: %s -> %s' % (name, bot))
201
- elif receiver == Station.EVERY or receiver == EVERYONE:
202
- # broadcast message to neighbor stations
203
- # e.g.: 'stations@everywhere', 'everyone@everywhere'
204
- archivist = self.facebook.archivist
205
- assert isinstance(archivist, ServerArchivist)
206
- candidates = archivist.all_neighbors
207
- if len(candidates) == 0:
208
- self.warning(msg='failed to get neighbors: %s' % receiver)
197
+ elif bot == sender:
198
+ self.warning(msg='skip cycled message: %s -> %s' % (sender, receiver))
209
199
  return False
210
- self.info(msg='forward to neighbor stations: %s -> %s' % (receiver, candidates))
211
- else:
212
- self.warning(msg='unknown receiver: %s' % receiver)
213
- return False
214
- # check recipients
215
- new_recipients = candidates.copy()
216
- old_recipients = msg.get('recipients')
217
- if old_recipients is None:
218
- all_recipients = []
219
- else:
220
- all_recipients = ID.convert(old_recipients)
221
- # check duplicated
222
- self.info(msg='discard recipients: %s, new recipients: %s' % (old_recipients, new_recipients))
223
- for item in all_recipients:
224
- new_recipients.discard(item)
225
- if len(new_recipients) == 0:
226
- self.info(msg='new recipients empty: %s => %s' % (receiver, candidates))
200
+ elif bot == station:
201
+ self.warning(msg='skip current station: %s -> %s' % (sender, receiver))
227
202
  return False
228
- self.info(msg='append new recipients: %s, %s => %s' % (receiver, new_recipients, all_recipients))
229
- for item in new_recipients:
230
- all_recipients.append(item)
231
- # avoid the new recipients redirect it to same targets
232
- msg['recipients'] = ID.revert(all_recipients)
233
- # dispatch
234
- dispatcher = Dispatcher()
235
- for target in new_recipients:
236
- if target == sender or target == station:
237
- self.info(msg='skip cycled message: %s -> %s, %s' % (sender, receiver, target))
238
203
  else:
239
- dispatcher.deliver_message(msg=msg, receiver=target)
240
- return True
204
+ self.info(msg='forward to bot: %s -> %s' % (name, bot))
205
+ receiver = bot
206
+ # deliver by dispatcher
207
+ dispatcher = Dispatcher()
208
+ dispatcher.deliver_message(msg=msg, receiver=receiver)
241
209
 
242
210
  def _split_group_message(self, msg: ReliableMessage, station: ID):
243
211
  """ redirect group message to assistant """
@@ -42,10 +42,12 @@ class Node(Dictionary):
42
42
  if port > 0:
43
43
  self['port'] = port
44
44
 
45
+ # Override
45
46
  def __str__(self) -> str:
46
47
  clazz = self.__class__.__name__
47
48
  return '<%s host="%s" port=%d name="%s" />' % (clazz, self.host, self.port, self.name)
48
49
 
50
+ # Override
49
51
  def __repr__(self) -> str:
50
52
  clazz = self.__class__.__name__
51
53
  return '<%s host="%s" port=%d name="%s" />' % (clazz, self.host, self.port, self.name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 0.5.5
3
+ Version: 0.5.7
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -14,7 +14,7 @@ import io
14
14
 
15
15
  from setuptools import setup, find_packages
16
16
 
17
- __version__ = '0.5.5'
17
+ __version__ = '0.5.7'
18
18
  __author__ = 'Albert Moky'
19
19
  __contact__ = 'albert.moky@gmail.com'
20
20
 
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