dimples 0.4.0__tar.gz → 0.4.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 (143) hide show
  1. {dimples-0.4.0 → dimples-0.4.1}/PKG-INFO +1 -1
  2. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/terminal.py +5 -0
  3. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/protocol/ws.py +1 -1
  4. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/__init__.py +0 -4
  5. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/archivist.py +73 -11
  6. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/processor.py +44 -13
  7. {dimples-0.4.0 → dimples-0.4.1}/dimples/station/shared.py +0 -9
  8. {dimples-0.4.0 → dimples-0.4.1}/dimples.egg-info/PKG-INFO +1 -1
  9. {dimples-0.4.0 → dimples-0.4.1}/dimples.egg-info/SOURCES.txt +0 -1
  10. {dimples-0.4.0 → dimples-0.4.1}/setup.py +1 -1
  11. dimples-0.4.0/dimples/server/broadcast.py +0 -219
  12. {dimples-0.4.0 → dimples-0.4.1}/README.md +0 -0
  13. {dimples-0.4.0 → dimples-0.4.1}/dimples/__init__.py +0 -0
  14. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/__init__.py +0 -0
  15. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/archivist.py +0 -0
  16. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/checkpoint.py +0 -0
  17. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/__init__.py +0 -0
  18. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/commands.py +0 -0
  19. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/creator.py +0 -0
  20. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/group.py +0 -0
  21. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/grp_expel.py +0 -0
  22. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/grp_invite.py +0 -0
  23. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/grp_join.py +0 -0
  24. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/grp_query.py +0 -0
  25. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/grp_quit.py +0 -0
  26. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/grp_reset.py +0 -0
  27. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/grp_resign.py +0 -0
  28. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/cpu/handshake.py +0 -0
  29. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/facebook.py +0 -0
  30. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/messenger.py +0 -0
  31. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/network/__init__.py +0 -0
  32. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/network/session.py +0 -0
  33. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/network/state.py +0 -0
  34. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/network/transition.py +0 -0
  35. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/packer.py +0 -0
  36. {dimples-0.4.0 → dimples-0.4.1}/dimples/client/processor.py +0 -0
  37. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/__init__.py +0 -0
  38. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/anonymous.py +0 -0
  39. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/ans.py +0 -0
  40. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/archivist.py +0 -0
  41. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/compat/__init__.py +0 -0
  42. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/compat/btc.py +0 -0
  43. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/compat/compatible.py +0 -0
  44. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/compat/entity.py +0 -0
  45. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/compat/meta.py +0 -0
  46. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/compat/network.py +0 -0
  47. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/dbi/__init__.py +0 -0
  48. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/dbi/account.py +0 -0
  49. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/dbi/message.py +0 -0
  50. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/dbi/session.py +0 -0
  51. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/facebook.py +0 -0
  52. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/messenger.py +0 -0
  53. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/packer.py +0 -0
  54. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/protocol/__init__.py +0 -0
  55. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/protocol/ans.py +0 -0
  56. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/protocol/block.py +0 -0
  57. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/protocol/handshake.py +0 -0
  58. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/protocol/login.py +0 -0
  59. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/protocol/mute.py +0 -0
  60. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/protocol/report.py +0 -0
  61. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/register.py +0 -0
  62. {dimples-0.4.0 → dimples-0.4.1}/dimples/common/session.py +0 -0
  63. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/__init__.py +0 -0
  64. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/gate.py +0 -0
  65. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/gatekeeper.py +0 -0
  66. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/mars.py +0 -0
  67. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/mtp.py +0 -0
  68. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/protocol/__init__.py +0 -0
  69. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/protocol/mars.py +0 -0
  70. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/queue.py +0 -0
  71. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/seeker.py +0 -0
  72. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/session.py +0 -0
  73. {dimples-0.4.0 → dimples-0.4.1}/dimples/conn/ws.py +0 -0
  74. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/__init__.py +0 -0
  75. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/account.py +0 -0
  76. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/__init__.py +0 -0
  77. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/base.py +0 -0
  78. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/document.py +0 -0
  79. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/group.py +0 -0
  80. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/group_history.py +0 -0
  81. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/group_keys.py +0 -0
  82. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/login.py +0 -0
  83. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/meta.py +0 -0
  84. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/private.py +0 -0
  85. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/station.py +0 -0
  86. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/dos/user.py +0 -0
  87. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/message.py +0 -0
  88. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/session.py +0 -0
  89. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_cipherkey.py +0 -0
  90. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_document.py +0 -0
  91. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_group.py +0 -0
  92. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_group_history.py +0 -0
  93. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_group_keys.py +0 -0
  94. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_login.py +0 -0
  95. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_message.py +0 -0
  96. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_meta.py +0 -0
  97. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_private.py +0 -0
  98. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_station.py +0 -0
  99. {dimples-0.4.0 → dimples-0.4.1}/dimples/database/t_user.py +0 -0
  100. {dimples-0.4.0 → dimples-0.4.1}/dimples/edge/__init__.py +0 -0
  101. {dimples-0.4.0 → dimples-0.4.1}/dimples/edge/octopus.py +0 -0
  102. {dimples-0.4.0 → dimples-0.4.1}/dimples/edge/shared.py +0 -0
  103. {dimples-0.4.0 → dimples-0.4.1}/dimples/edge/start.py +0 -0
  104. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/__init__.py +0 -0
  105. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/admin.py +0 -0
  106. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/builder.py +0 -0
  107. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/delegate.py +0 -0
  108. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/emitter.py +0 -0
  109. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/helper.py +0 -0
  110. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/manager.py +0 -0
  111. {dimples-0.4.0 → dimples-0.4.1}/dimples/group/packer.py +0 -0
  112. {dimples-0.4.0 → dimples-0.4.1}/dimples/register/__init__.py +0 -0
  113. {dimples-0.4.0 → dimples-0.4.1}/dimples/register/base.py +0 -0
  114. {dimples-0.4.0 → dimples-0.4.1}/dimples/register/ext.py +0 -0
  115. {dimples-0.4.0 → dimples-0.4.1}/dimples/register/run.py +0 -0
  116. {dimples-0.4.0 → dimples-0.4.1}/dimples/register/shared.py +0 -0
  117. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/cpu/__init__.py +0 -0
  118. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/cpu/ans.py +0 -0
  119. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/cpu/document.py +0 -0
  120. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/cpu/handshake.py +0 -0
  121. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/cpu/login.py +0 -0
  122. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/cpu/report.py +0 -0
  123. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/dispatcher.py +0 -0
  124. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/messenger.py +0 -0
  125. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/packer.py +0 -0
  126. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/push.py +0 -0
  127. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/session.py +0 -0
  128. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/session_center.py +0 -0
  129. {dimples-0.4.0 → dimples-0.4.1}/dimples/server/trace.py +0 -0
  130. {dimples-0.4.0 → dimples-0.4.1}/dimples/station/__init__.py +0 -0
  131. {dimples-0.4.0 → dimples-0.4.1}/dimples/station/handler.py +0 -0
  132. {dimples-0.4.0 → dimples-0.4.1}/dimples/station/start.py +0 -0
  133. {dimples-0.4.0 → dimples-0.4.1}/dimples/utils/__init__.py +0 -0
  134. {dimples-0.4.0 → dimples-0.4.1}/dimples/utils/cache.py +0 -0
  135. {dimples-0.4.0 → dimples-0.4.1}/dimples/utils/config.py +0 -0
  136. {dimples-0.4.0 → dimples-0.4.1}/dimples/utils/dos.py +0 -0
  137. {dimples-0.4.0 → dimples-0.4.1}/dimples/utils/log.py +0 -0
  138. {dimples-0.4.0 → dimples-0.4.1}/dimples/utils/singleton.py +0 -0
  139. {dimples-0.4.0 → dimples-0.4.1}/dimples.egg-info/dependency_links.txt +0 -0
  140. {dimples-0.4.0 → dimples-0.4.1}/dimples.egg-info/entry_points.txt +0 -0
  141. {dimples-0.4.0 → dimples-0.4.1}/dimples.egg-info/requires.txt +0 -0
  142. {dimples-0.4.0 → dimples-0.4.1}/dimples.egg-info/top_level.txt +0 -0
  143. {dimples-0.4.0 → dimples-0.4.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -149,6 +149,11 @@ class Terminal(Runner, StateDelegate, Logging):
149
149
  # broadcast current meta & visa document to all stations
150
150
  messenger = self.messenger
151
151
  messenger.handshake_success()
152
+ session = messenger.session
153
+ usr_id = session.identifier
154
+ if usr_id is not None and usr_id.type != EntityType.STATION:
155
+ # send login command to everyone to provide more information.
156
+ messenger.broadcast_login(sender=usr_id, user_agent=self.user_agent)
152
157
  # update last online time
153
158
  self.__last_time = time.time()
154
159
 
@@ -76,7 +76,7 @@ class WebSocket:
76
76
  text = utf8_decode(data=stream).lower()
77
77
  pos1 = text.find('sec-websocket-key:')
78
78
  if pos1 > 0:
79
- pos1 += len('sec-WebSocket-key:')
79
+ pos1 += len('sec-websocket-key:')
80
80
  pos2 = text.find('\r\n', pos1)
81
81
  if pos2 > 0:
82
82
  return stream[pos1:pos2].strip()
@@ -32,8 +32,6 @@
32
32
  from .session import ServerSession
33
33
  from .session_center import SessionCenter # SessionPool
34
34
 
35
- from .broadcast import BroadcastRecipientManager
36
-
37
35
  from .push import BadgeKeeper, PushService
38
36
  from .push import PushCenter
39
37
 
@@ -56,8 +54,6 @@ __all__ = [
56
54
  # Session
57
55
  'ServerSession', 'SessionCenter', # 'SessionPool',
58
56
 
59
- 'BroadcastRecipientManager',
60
-
61
57
  # Push Notification
62
58
  'BadgeKeeper', 'PushService',
63
59
  'PushCenter',
@@ -28,11 +28,13 @@
28
28
  # SOFTWARE.
29
29
  # ==============================================================================
30
30
 
31
+ import threading
31
32
  from abc import ABC
32
- from typing import Dict, List
33
+ from typing import Dict, List, Set
33
34
 
35
+ from dimsdk import DateTime
34
36
  from dimsdk import FrequencyChecker
35
- from dimsdk import ID, Document
37
+ from dimsdk import EntityType, ID, Document
36
38
  from dimsdk import Envelope, InstantMessage
37
39
  from dimsdk import Command, MetaCommand, DocumentCommand
38
40
  from dimsdk import Station
@@ -40,8 +42,10 @@ from dimsdk import Station
40
42
  from ..common import AccountDBI
41
43
  from ..common import CommonArchivist
42
44
  from ..common import CommonFacebook, CommonMessenger
45
+ from ..common import StationInfo
43
46
 
44
- from .broadcast import broadcast_reliable_message
47
+ from .dispatcher import Dispatcher
48
+ from .session_center import SessionCenter
45
49
 
46
50
 
47
51
  def get_facebook(archivist: CommonArchivist):
@@ -67,21 +71,79 @@ class ServerArchivist(CommonArchivist, ABC):
67
71
  super().__init__(database=database)
68
72
  self.__document_responses = FrequencyChecker(expires=self.RESPOND_EXPIRES)
69
73
  self.__last_active_members: Dict[ID, ID] = {} # group => member
70
-
71
- def _broadcast_command(self, content: Command, receiver: ID) -> bool:
74
+ # neighbor stations
75
+ self.__neighbors = set()
76
+ self.__lock = threading.Lock()
77
+ self.__expires = 0
78
+
79
+ @property
80
+ def active_stations(self) -> Set[ID]:
81
+ """ get neighbor stations connected to current station """
82
+ now = DateTime.now()
83
+ with self.__lock:
84
+ if self.__expires < now.timestamp:
85
+ neighbors = set()
86
+ center = SessionCenter()
87
+ all_users = center.all_users()
88
+ for item in all_users:
89
+ if item.type == EntityType.STATION:
90
+ neighbors.add(item)
91
+ self.__neighbors = neighbors
92
+ self.__expires = now.timestamp + 128
93
+ return self.__neighbors
94
+
95
+ @property
96
+ def all_stations(self) -> List[StationInfo]:
97
+ """ get stations from database """
98
+ dispatcher = Dispatcher()
99
+ db = dispatcher.sdb
100
+ # TODO: get chosen provider
101
+ providers = db.all_providers()
102
+ assert len(providers) > 0, 'service provider not found'
103
+ gsp = providers[0].identifier
104
+ return db.all_stations(provider=gsp)
105
+
106
+ @property
107
+ def all_neighbors(self) -> Set[ID]:
108
+ """ get all stations """
109
+ neighbors = set()
110
+ # get stations from chosen provider
111
+ chosen_stations = self.all_stations
112
+ for item in chosen_stations:
113
+ sid = item.identifier
114
+ if sid is None or sid.is_broadcast:
115
+ continue
116
+ neighbors.add(sid)
117
+ # get neighbor station from session server
118
+ proactive_neighbors = self.active_stations
119
+ for sid in proactive_neighbors:
120
+ if sid is None or sid.is_broadcast:
121
+ self.error(msg='neighbor station ID error: %s' % sid)
122
+ continue
123
+ neighbors.add(sid)
124
+ return neighbors
125
+
126
+ def _broadcast_command(self, command: Command) -> bool:
72
127
  facebook = get_facebook(archivist=self)
73
128
  messenger = get_messenger(archivist=self)
74
129
  if facebook is None or messenger is None:
75
130
  self.error(msg='twins not ready yet: %s, %s' % (facebook, messenger))
76
131
  return False
77
132
  sid = facebook.current_user.identifier
78
- env = Envelope.create(sender=sid, receiver=receiver)
79
- i_msg = InstantMessage.create(head=env, body=content)
133
+ env = Envelope.create(sender=sid, receiver=Station.EVERY)
134
+ i_msg = InstantMessage.create(head=env, body=command)
80
135
  # pack & deliver message
81
136
  s_msg = messenger.encrypt_message(msg=i_msg)
82
137
  r_msg = messenger.sign_message(msg=s_msg)
83
- cnt = broadcast_reliable_message(msg=r_msg, station=sid)
84
- return cnt > 0
138
+ # dispatch
139
+ dispatcher = Dispatcher()
140
+ neighbors = self.all_neighbors
141
+ for receiver in neighbors:
142
+ if receiver == sid:
143
+ self.debug(msg='skip cycled message: %s -> %s' % (sid, receiver))
144
+ continue
145
+ dispatcher.deliver_message(msg=r_msg, receiver=receiver)
146
+ return len(neighbors) > 0
85
147
 
86
148
  # protected
87
149
  def is_documents_respond_expired(self, identifier: ID, force: bool) -> bool:
@@ -98,7 +160,7 @@ class ServerArchivist(CommonArchivist, ABC):
98
160
  return False
99
161
  self.info(msg='querying meta for: %s' % identifier)
100
162
  command = MetaCommand.query(identifier=identifier)
101
- return self._broadcast_command(content=command, receiver=Station.EVERY)
163
+ return self._broadcast_command(command=command)
102
164
 
103
165
  # Override
104
166
  def query_documents(self, identifier: ID, documents: List[Document]) -> bool:
@@ -109,7 +171,7 @@ class ServerArchivist(CommonArchivist, ABC):
109
171
  last_time = self.get_last_document_time(identifier=identifier, documents=documents)
110
172
  self.info(msg='querying document for: %s, last time: %s' % (identifier, last_time))
111
173
  command = DocumentCommand.query(identifier=identifier, last_time=last_time)
112
- return self._broadcast_command(content=command, receiver=Station.EVERY)
174
+ return self._broadcast_command(command=command)
113
175
 
114
176
  # Override
115
177
  def query_members(self, group: ID, members: List[ID]) -> bool:
@@ -31,7 +31,7 @@
31
31
  import time
32
32
  from typing import Optional, Union, List, Dict
33
33
 
34
- from dimsdk import EntityType, ID, ANYONE
34
+ from dimsdk import EntityType, ID, ANYONE, EVERYONE
35
35
  from dimsdk import Station
36
36
  from dimsdk import InstantMessage, ReliableMessage
37
37
  from dimsdk import Envelope
@@ -58,7 +58,7 @@ from .cpu import DocumentCommandProcessor
58
58
 
59
59
  from .packer import FilterManager
60
60
  from .dispatcher import Dispatcher
61
- from .broadcast import broadcast_reliable_message
61
+ from .archivist import ServerArchivist
62
62
 
63
63
 
64
64
  class ServerMessageProcessor(MessageProcessor, Logging):
@@ -95,8 +95,7 @@ class ServerMessageProcessor(MessageProcessor, Logging):
95
95
  messenger = self.messenger
96
96
  session = messenger.session
97
97
  current = self.facebook.current_user
98
- sid = current.identifier
99
- sender = msg.sender
98
+ station = current.identifier
100
99
  receiver = msg.receiver
101
100
  # 0. verify message
102
101
  s_msg = messenger.verify_message(msg=msg)
@@ -104,7 +103,7 @@ class ServerMessageProcessor(MessageProcessor, Logging):
104
103
  # TODO: suspend and waiting for sender's meta if not exists
105
104
  return []
106
105
  # 1. check receiver
107
- if receiver == sid:
106
+ if receiver == station:
108
107
  # message to this station
109
108
  # maybe a meta command, document command, etc ...
110
109
  pass
@@ -131,16 +130,22 @@ class ServerMessageProcessor(MessageProcessor, Logging):
131
130
  # we can trust this message an no need to verify it;
132
131
  # else if sender is a neighbor station,
133
132
  # we can trust it too;
134
- if receiver.is_broadcast:
135
- # broadcast message (to neighbor stations, or station bots)
136
- # e.g.: 'stations@everywhere',
137
- # 'archivist@anywhere', 'announcer@anywhere', 'monitor@anywhere'
138
- broadcast_reliable_message(msg=msg, station=sid)
139
- # if receiver.is_group:
140
- # broadcast message to multiple destinations,
133
+ if receiver == Station.EVERY or receiver == EVERYONE:
134
+ # broadcast message (to neighbor stations)
135
+ # e.g.: 'stations@everywhere'
136
+ self._broadcast_message(msg=msg, station=station)
137
+ # if receiver == 'everyone@everywhere':
138
+ # broadcast message to all destinations,
141
139
  # current station is it's receiver too.
140
+ elif receiver.is_broadcast:
141
+ # broadcast message (to station bots)
142
+ # e.g.: 'archivist@anywhere', 'announcer@anywhere', 'monitor@anywhere'
143
+ self._broadcast_message(msg=msg, station=station)
144
+ return []
142
145
  elif receiver.is_group:
143
- self.error(msg='group message should not send to station: %s -> %s' % (sender, receiver))
146
+ # encrypted group messages should be sent to the group assistant,
147
+ # the station will never process these messages.
148
+ self._split_group_message(msg=msg, station=station)
144
149
  return []
145
150
  else:
146
151
  # this message is not for current station,
@@ -180,6 +185,32 @@ class ServerMessageProcessor(MessageProcessor, Logging):
180
185
  else:
181
186
  return [r_msg]
182
187
 
188
+ def _broadcast_message(self, msg: ReliableMessage, station: ID):
189
+ """ broadcast message to neighbor stations """
190
+ archivist = self.facebook.archivist
191
+ assert isinstance(archivist, ServerArchivist)
192
+ neighbors = archivist.all_neighbors
193
+ sender = msg.sender
194
+ self.info(msg='broadcast message %s -> %s (%s): from station %s to %s'
195
+ % (sender, msg.receiver, msg.group, station, neighbors))
196
+ # dispatch
197
+ dispatcher = Dispatcher()
198
+ for receiver in neighbors:
199
+ if receiver == sender:
200
+ self.debug(msg='skip cycled message: %s -> %s' % (sender, receiver))
201
+ continue
202
+ elif receiver == station:
203
+ self.debug(msg='skip current station: %s -> %s' % (sender, receiver))
204
+ continue
205
+ dispatcher.deliver_message(msg=msg, receiver=receiver)
206
+ return len(neighbors) > 0
207
+
208
+ def _split_group_message(self, msg: ReliableMessage, station: ID):
209
+ """ redirect group message to assistant """
210
+ sender = msg.sender
211
+ receiver = msg.receiver
212
+ self.error(msg='group message should not send to station: %s, %s -> %s' % (station, sender, receiver))
213
+
183
214
  def _deliver_message(self, msg: ReliableMessage) -> List[ReliableMessage]:
184
215
  messenger = self.messenger
185
216
  current = self.facebook.current_user
@@ -42,7 +42,6 @@ from ..server import ServerSession
42
42
  from ..server import ServerMessenger
43
43
  from ..server import ServerMessagePacker
44
44
  from ..server import ServerMessageProcessor
45
- from ..server import BroadcastRecipientManager
46
45
  from ..server import Dispatcher
47
46
 
48
47
 
@@ -189,12 +188,4 @@ def create_ans(config: Config) -> AddressNameServer:
189
188
  ans_records = config.ans_records
190
189
  if ans_records is not None:
191
190
  ans.fix(records=ans_records)
192
- # set bots to receive message for 'everyone@everywhere'
193
- bots = set()
194
- se = ans.identifier(name='archivist') # Search Engine
195
- if se is not None:
196
- bots.add(se)
197
- if len(bots) > 0:
198
- manager = BroadcastRecipientManager()
199
- manager.station_bots = bots
200
191
  return ans
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -114,7 +114,6 @@ dimples/register/run.py
114
114
  dimples/register/shared.py
115
115
  dimples/server/__init__.py
116
116
  dimples/server/archivist.py
117
- dimples/server/broadcast.py
118
117
  dimples/server/dispatcher.py
119
118
  dimples/server/messenger.py
120
119
  dimples/server/packer.py
@@ -14,7 +14,7 @@ import io
14
14
 
15
15
  from setuptools import setup, find_packages
16
16
 
17
- __version__ = '0.4.0'
17
+ __version__ = '0.4.1'
18
18
  __author__ = 'Albert Moky'
19
19
  __contact__ = 'albert.moky@gmail.com'
20
20
 
@@ -1,219 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # DIM-SDK : Decentralized Instant Messaging Software Development Kit
4
- #
5
- # ==============================================================================
6
- # MIT License
7
- #
8
- # Copyright (c) 2023 Albert Moky
9
- #
10
- # Permission is hereby granted, free of charge, to any person obtaining a copy
11
- # of this software and associated documentation files (the "Software"), to deal
12
- # in the Software without restriction, including without limitation the rights
13
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- # copies of the Software, and to permit persons to whom the Software is
15
- # furnished to do so, subject to the following conditions:
16
- #
17
- # The above copyright notice and this permission notice shall be included in all
18
- # copies or substantial portions of the Software.
19
- #
20
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- # SOFTWARE.
27
- # ==============================================================================
28
-
29
- """
30
- Broadcast Recipient Manager
31
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~
32
- """
33
-
34
- import threading
35
- from typing import Set, List
36
-
37
- from dimsdk import DateTime
38
- from dimsdk import EntityType, ID, EVERYONE
39
- from dimsdk import Station
40
- from dimsdk import ReliableMessage
41
-
42
- from ..utils import Singleton, Log, Logging
43
- from ..common import StationInfo
44
-
45
- from .cpu import AnsCommandProcessor
46
- from .trace import TraceNode, TraceList, TraceManager
47
- from .dispatcher import Dispatcher
48
- from .session_center import SessionCenter
49
-
50
-
51
- @Singleton
52
- class BroadcastRecipientManager(Logging):
53
-
54
- def __init__(self):
55
- super().__init__()
56
- self.__lock = threading.Lock()
57
- self.__expires = 0
58
- self.__neighbors = set()
59
- self.__bots = set()
60
-
61
- @property
62
- def station_bots(self) -> Set[ID]:
63
- """ get station bots """
64
- return self.__bots
65
-
66
- @station_bots.setter
67
- def station_bots(self, bots: Set[ID]):
68
- """ set station bots to receive message for 'everyone@everywhere' """
69
- assert isinstance(bots, Set), 'bots error: %s' % bots
70
- self.__bots = bots
71
-
72
- @property
73
- def proactive_neighbors(self) -> Set[ID]:
74
- """ get neighbor stations connected to current station """
75
- now = DateTime.now()
76
- with self.__lock:
77
- if self.__expires < now.timestamp:
78
- neighbors = set()
79
- center = SessionCenter()
80
- all_users = center.all_users()
81
- for item in all_users:
82
- if item.type == EntityType.STATION:
83
- neighbors.add(item)
84
- self.__neighbors = neighbors
85
- self.__expires = now.timestamp + 128
86
- return self.__neighbors
87
-
88
- @property
89
- def all_stations(self) -> List[StationInfo]:
90
- """ get stations from database """
91
- dispatcher = Dispatcher()
92
- db = dispatcher.sdb
93
- # TODO: get chosen provider
94
- providers = db.all_providers()
95
- assert len(providers) > 0, 'service provider not found'
96
- gsp = providers[0].identifier
97
- return db.all_stations(provider=gsp)
98
-
99
- @property
100
- def all_neighbors(self) -> Set[ID]:
101
- """ get all stations """
102
- neighbors = set()
103
- # get stations from chosen provider
104
- stations = self.all_stations
105
- for item in stations:
106
- sid = item.identifier
107
- if sid is None or sid.is_broadcast:
108
- continue
109
- neighbors.add(sid)
110
- # get neighbor station from session server
111
- proactive_neighbors = self.proactive_neighbors
112
- for sid in proactive_neighbors:
113
- if sid is None or sid.is_broadcast:
114
- assert False, 'neighbor station ID error: %s' % sid
115
- # continue
116
- neighbors.add(sid)
117
- return neighbors
118
-
119
- def get_recipients(self, msg: ReliableMessage, receiver: ID, station: ID) -> Set[ID]:
120
- """ get nodes passed through, includes current node which is just added before """
121
- recipients = set()
122
- tm = TraceManager()
123
- traces = tm.get_traces(msg=msg)
124
- # if this message is sending to 'stations@everywhere' or 'everyone@everywhere'
125
- # get all neighbor stations to broadcast, but
126
- # traced nodes should be ignored to avoid cycled delivering
127
- if receiver == Station.EVERY or receiver == EVERYONE:
128
- self.info(msg='forward to neighbors: %s' % receiver)
129
- # get neighbor stations
130
- neighbors = self.all_neighbors
131
- for sid in neighbors:
132
- if traces.search(node=sid) >= 0 or sid == station:
133
- self.warning(msg='skip duplicated station: %s' % sid)
134
- continue
135
- recipients.add(sid)
136
- # get station bots
137
- if receiver == EVERYONE:
138
- # include station bots as 'everyone@everywhere'
139
- bots = self.station_bots
140
- for bid in bots:
141
- if traces.search(node=bid) >= 0:
142
- self.warning(msg='skip duplicated bot: %s' % bid)
143
- continue
144
- recipients.add(bid)
145
- elif receiver.is_user:
146
- # 'archivist@anywhere', 'announcer@anywhere', 'monitor@anywhere'
147
- name = receiver.name
148
- if name is not None:
149
- assert name != 'station' and name != 'anyone', 'receiver error: %s' % receiver
150
- bot = AnsCommandProcessor.ans_id(name=name)
151
- self.info(msg='forward to bot: %s -> %s' % (name, bot))
152
- if bot is not None and traces.search(node=bot) < 0:
153
- recipients.add(bot)
154
- self.info(msg='recipients: %s -> %s' % (receiver, recipients))
155
- return recipients
156
-
157
-
158
- def broadcast_reliable_message(msg: ReliableMessage, station: ID):
159
- receiver = msg.receiver
160
- # get other recipients for broadcast message
161
- manager = BroadcastRecipientManager()
162
- recipients = manager.get_recipients(msg=msg, receiver=receiver, station=station)
163
- if len(recipients) == 0:
164
- Log.warning('other recipients not found: %s' % receiver)
165
- return 0
166
- sender = msg.sender
167
- # dispatch
168
- dispatcher = Dispatcher()
169
- for target in recipients:
170
- assert not target.is_broadcast, 'recipient error: %s, %s' % (target, receiver)
171
- deliver_message(msg=msg, receiver=target, recipients=recipients, station=station, dispatcher=dispatcher)
172
- # TODO: after deliver to connected neighbors, the dispatcher will continue
173
- # delivering via station bridge, should we mark 'sent_neighbors' in
174
- # only one message to the bridge, let the bridge to separate for other
175
- # neighbors which not connect to this station directly?
176
- # set trace nodes
177
- tm = TraceManager()
178
- tm.set_nodes(msg=msg, nodes=recipients)
179
- # OK
180
- Log.info(msg='Broadcast message delivered: %s => %s' % (sender, recipients))
181
- return len(recipients)
182
-
183
-
184
- def deliver_message(msg: ReliableMessage, receiver: ID, recipients: Set[ID], station: ID, dispatcher: Dispatcher):
185
- if receiver == station:
186
- Log.warning(msg='skip current node: %s, %s' % (receiver, recipients))
187
- return None
188
- elif receiver == msg.sender:
189
- Log.warning(msg='skip sender: %s, %s' % (receiver, recipients))
190
- return None
191
- assert isinstance(recipients, set), 'recipients error: %s' % recipients
192
- tm = TraceManager()
193
- traces = tm.get_traces(msg=msg)
194
- if traces.search(node=receiver) >= 0:
195
- Log.warning(msg='skip cycled message: %s, %s' % (msg.sender, receiver))
196
- return None
197
- # clone
198
- msg = ReliableMessage.parse(msg=msg.dictionary)
199
- traces = copy_traces(traces=traces)
200
- # add these recipients into traces, exclude current receiver
201
- recipients = recipients.copy()
202
- recipients.discard(receiver) # exclude receiver
203
- recipients.add(station) # include current station
204
- for mta in recipients:
205
- node = TraceNode.create(identifier=mta)
206
- traces.insert(node=node)
207
- msg['traces'] = TraceNode.revert(nodes=traces.nodes)
208
- # deliver message with traces
209
- return dispatcher.deliver_message(msg=msg, receiver=receiver)
210
-
211
-
212
- def copy_traces(traces: TraceList) -> TraceList:
213
- when = traces.time
214
- if when is None:
215
- when = DateTime.now()
216
- else:
217
- when = DateTime.parse(value=when)
218
- nodes = traces.nodes
219
- return TraceList(msg_time=when, traces=nodes.copy())
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