def __split_group_message(self, msg: ReliableMessage, members: List[ID]) -> Optional[Content]: """ Split group message for each member """ messages = msg.split(members=members) success_list = [] failed_list = [] for item in messages: if item.delegate is None: item.delegate = msg.delegate if self.__forward_group_message(msg=item): success_list.append(item.receiver) else: failed_list.append(item.receiver) response = ReceiptCommand(message='Group message delivering') if len(success_list) > 0: response['success'] = ID.revert(success_list) if len(failed_list) > 0: response['failed'] = ID.revert(failed_list) # failed to get keys for this members, # query from sender by invite members sender = msg.sender group = msg.receiver cmd = GroupCommand.invite(group=group, members=failed_list) self.send_content(sender=None, receiver=sender, content=cmd) return response
def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]: assert isinstance(cmd, InviteCommand) or isinstance(cmd, ResetCommand), 'group command error: %s' % cmd facebook = self.facebook from ..facebook import Facebook assert isinstance(facebook, Facebook), 'entity delegate error: %s' % facebook # 0. check group group = cmd.group owner = facebook.owner(identifier=group) members = facebook.members(identifier=group) if owner is None or members is None or len(members) == 0: # FIXME: group profile lost? # FIXME: how to avoid strangers impersonating group members? return self.__temporary_save(cmd=cmd, sender=msg.sender) # 1. check permission sender = msg.sender if sender != owner: # not the owner? check assistants assistants = facebook.assistants(identifier=group) if assistants is None or sender not in assistants: text = '%s is not the owner/assistant of group %s, cannot reset members' % (sender, group) raise AssertionError(text) # 2. resetting members new_members = self.members(cmd=cmd) if new_members is None or len(new_members) == 0: raise ValueError('group command error: %s' % cmd) # 2.1. check owner if owner not in new_members: raise AssertionError('cannot expel owner (%s) of group: %s' % (owner, group)) # 2.2. build expelled-list remove_list = [] for item in members: if item not in new_members: # removing member found remove_list.append(item) # 2.3. build invited-list add_list = [] for item in new_members: if item not in members: # adding member found add_list.append(item) # 2.4. do reset if len(add_list) > 0 or len(remove_list) > 0: if facebook.save_members(members=new_members, identifier=group): if len(add_list) > 0: cmd['added'] = ID.revert(add_list) if len(remove_list) > 0: cmd['removed'] = ID.revert(remove_list) # 3. response (no need to response this group command) return None
def __broadcast_message(self, msg: ReliableMessage) -> Optional[Content]: """ Deliver message to everyone@everywhere, including all neighbours """ self.debug('broadcasting message from: %s' % msg.sender) if self.__traced(msg=msg, station=self.station): self.error('ignore traced msg: %s in %s' % (self.station, msg.get('traces'))) return None session_server = self.session_server # push to all neighbors connected th current station neighbors = self.__neighbors.copy() sent_neighbors = [] success = 0 for sid in neighbors: if sid == self.station: continue sessions = session_server.active_sessions(identifier=sid) if len(sessions) == 0: self.warning('remote station (%s) not connected, try later.' % sid) continue if self.__push_message(msg=msg, receiver=sid, sessions=sessions): sent_neighbors.append(sid) success += 1 # push to the bridge (octopus) of current station sid = self.station sessions = session_server.active_sessions(identifier=sid) if len(sessions) > 0: # tell the bridge ignore this neighbor stations sent_neighbors.append(sid) msg['sent_neighbors'] = ID.revert(sent_neighbors) self.__push_message(msg=msg, receiver=sid, sessions=sessions) # FIXME: what about the failures # response text = 'Message broadcast to %d/%d stations' % (success, len(neighbors)) res = TextContent(text=text) res.group = msg.group return res
def save_contacts(self, contacts: List[ID], user: ID) -> bool: assert contacts is not None, 'contacts cannot be empty' # store into memory cache self.__contacts[user] = contacts # store into local storage path = self.__contacts_path(identifier=user) self.info('Saving contacts into: %s' % path) contacts = ID.revert(members=contacts) text = '\n'.join(contacts) return self.write_text(text=text, path=path)
def save_members(self, members: List[ID], group: ID) -> bool: assert len( members) > 0, 'group members should not be empty: %s' % group # 1. store into memory cache self.__members[group] = members # 2. store into local storage path = self.__members_path(identifier=group) self.info('Saving members into: %s' % path) members = ID.revert(members=members) text = '\n'.join(members) return self.write_text(text=text, path=path)
def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]: assert isinstance(cmd, InviteCommand), 'group command error: %s' % cmd facebook = self.facebook from ..facebook import Facebook assert isinstance(facebook, Facebook), 'entity delegate error: %s' % facebook # 0. check group group = cmd.group owner = facebook.owner(identifier=group) members = facebook.members(identifier=group) if owner is None or members is None or len(members) == 0: # NOTICE: group membership lost? # reset group members return self.__reset(cmd=cmd, msg=msg) # 1. check permission sender = msg.sender if sender not in members: # not a member? check assistants assistants = facebook.assistants(identifier=group) if assistants is None or sender not in assistants: raise AssertionError('only member/assistant can invite: %s' % msg) # 2. inviting members invite_list = self.members(cmd=cmd) if invite_list is None or len(invite_list) == 0: raise ValueError('invite command error: %s' % cmd) # 2.1. check for reset if sender == owner and owner in invite_list: # NOTICE: owner invites owner? # it means this should be a 'reset' command return self.__reset(cmd=cmd, msg=msg) # 2.2. build invited-list add_list = [] for item in invite_list: if item in members: continue # new member found add_list.append(item) members.append(item) # 2.3. do invite if len(add_list) > 0: if facebook.save_members(members=members, identifier=group): cmd['added'] = ID.revert(add_list) # 3. response (no need to response this group command) return None
def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]: assert isinstance(cmd, ExpelCommand), 'group command error: %s' % cmd facebook = self.facebook from ..facebook import Facebook assert isinstance(facebook, Facebook), 'entity delegate error: %s' % facebook # 0. check group group = cmd.group owner = facebook.owner(identifier=group) members = facebook.members(identifier=group) if owner is None or members is None or len(members) == 0: raise LookupError('Group not ready: %s' % group) # 1. check permission sender = msg.sender if sender != owner: # not the owner? check assistants assistants = facebook.assistants(identifier=group) if assistants is None or sender not in assistants: text = '%s is not the owner/assistant of group %s, cannot expel member' % ( sender, group) raise AssertionError(text) # 2. expelling members expel_list = self.members(cmd=cmd) if expel_list is None or len(expel_list) == 0: raise ValueError('expel command error: %s' % cmd) # 2.1. check owner if owner in expel_list: raise AssertionError('cannot expel owner %s of group %s' % (owner, group)) # 2.2. build removed-list remove_list = [] for item in expel_list: if item not in members: continue # expelled member found remove_list.append(item) members.remove(item) # 2.3. do expel if len(remove_list) > 0: if facebook.save_members(members=members, identifier=group): cmd['removed'] = ID.revert(remove_list) # 3. response (no need to response this group command) return None
def __traced(self, msg: ReliableMessage, station: ID) -> bool: traces = msg.get('traces') if traces is None: # broadcast message starts from here traces = [station] else: for node in traces: if isinstance(node, str): if station == node: return True elif isinstance(node, dict): if station == node.get('ID'): return True else: self.error('traces node error: %s' % node) # broadcast message go through here traces.append(station) msg['traces'] = ID.revert(members=traces) return False
def users(self, value: List[ID]): if value is None: self.pop('users', None) else: self['users'] = ID.revert(members=value) self.__users = value
def mute_list(self, value: List[ID]): if value is None: self.pop('list', None) else: self['list'] = ID.revert(members=value)