示例#1
0
 def __load_records(self) -> Dict[str, ID]:
     path = self.__path()
     self.info('Loading ANS records from: %s' % path)
     dictionary = {}
     text = self.read_text(path=path)
     if text is not None:
         lines = text.splitlines()
         for record in lines:
             pair = record.split('\t')
             if len(pair) != 2:
                 self.error('invalid record: %s' % record)
                 continue
             k = pair[0]
             v = pair[1]
             dictionary[k] = ID.parse(identifier=v)
     #
     #  Reserved names
     #
     dictionary['all'] = EVERYONE
     dictionary[EVERYONE.name] = EVERYONE
     dictionary[ANYONE.name] = ANYONE
     dictionary['owner'] = ANYONE
     dictionary['founder'] = ID.parse(
         identifier='moky@4DnqXWdTV8wuZgfqSCX9GjE2kNq7HJrUgQ'
     )  # 'Albert Moky'
     return dictionary
示例#2
0
 def __init__(self):
     super().__init__()
     # caches
     self.__ids = {}
     self.__private_keys = {}
     self.__metas = {}
     self.__profiles = {}
     self.__users = {}
     # load built-in users
     self.__load_user(identifier=ID.parse(self.HULK))
     self.__load_user(identifier=ID.parse(self.MOKI))
示例#3
0
 def __init__(self, messenger: ClientMessenger):
     super().__init__()
     # delegate for send message
     self.messenger = messenger
     # group
     gid = ID.parse(identifier=group_naruto)
     self.__group = messenger.facebook.group(gid)
示例#4
0
def upload_profile() -> Response:
    try:
        form = request.form.to_dict()
        identifier = form['ID']
        meta = form['meta']
        profile = form['profile']
        identifier = ID.parse(identifier=identifier)
        # save meta
        if meta is None:
            meta = g_facebook.meta(identifier=identifier)
            if meta is None:
                raise LookupError('meta not found: %s' % identifier)
        else:
            meta = Meta.parse(meta=json.loads(meta))
            if not g_facebook.save_meta(meta=meta, identifier=identifier):
                raise ValueError('meta not acceptable: %s' % identifier)
        # save profile
        if profile is None:
            raise ValueError('profile empty: %s' % identifier)
        else:
            profile = Document.parse(document=json.loads(profile))
            if not g_facebook.save_document(document=profile):
                raise ValueError('profile not acceptable: %s' % identifier)
        # OK
        return user(identifier)
    except Exception as error:
        res = {
            'code': 500,
            'name': 'Internal Server Error',
            'message': '%s' % error
        }
        xml = render_template('error.xml', result=res)
    return respond_xml(xml)
示例#5
0
 def scan_ids(self) -> List[ID]:
     ids = []
     directory = os.path.join(self.root, 'public')
     # get all files in messages directory and sort by filename
     files = os.listdir(directory)
     for filename in files:
         path = os.path.join(directory, filename, 'meta.js')
         if not os.path.exists(path):
             self.warning('meta file not exists: %s' % path)
             continue
         address = ID.parse(identifier=filename)
         if address is None:
             self.error('ID/address error: %s' % filename)
             continue
         meta = self.meta(identifier=address)
         if meta is None:
             self.error('meta error: %s' % address)
         else:
             identifier = meta.generate_identifier(network=address.type)
             self.debug('loaded meta for %s from %s: %s' %
                        (identifier, path, meta))
             # the ID contains 'username' now
             if identifier != address:
                 # switch cache key
                 # self.__caches.pop(address)
                 self.__caches[identifier] = meta
             ids.append(identifier)
     self.debug('Scanned %d ID(s) from %s' % (len(ids), directory))
     return ids
示例#6
0
 def do_profile(self, name: str):
     if self.client is None:
         self.info('login first')
         return
     facebook = g_messenger.facebook
     user = facebook.current_user
     profile = None
     if name is None:
         identifier = user.identifier
     elif name.startswith('{') and name.endswith('}'):
         identifier = user.identifier
         profile = json_decode(data=utf8_encode(string=name))
     else:
         identifier = ID.parse(identifier=name)
         if identifier is None:
             self.info('I don\'t understand.')
             return
     if profile:
         private_key = facebook.private_key_for_signature(identifier=identifier)
         assert private_key is not None, 'failed to get private key for client: %s' % self.client
         # create new profile and set all properties
         tai = Document.create(doc_type=Document.VISA, identifier=identifier)
         for key in profile:
             tai.set_property(key, profile.get(key))
         tai.sign(private_key=private_key)
         cmd = DocumentCommand.response(identifier=identifier, document=tai)
     else:
         cmd = DocumentCommand.query(identifier=identifier)
     self.client.send_command(cmd=cmd)
示例#7
0
def create_server(identifier: str, host: str, port: int = 9394) -> Station:
    """ Create Local Server """
    identifier = ID.parse(identifier=identifier)
    server = Station(identifier=identifier, host=host, port=port)
    g_facebook.cache_user(user=server)
    Log.info('local station created: %s' % server)
    return server
示例#8
0
 def assistants(self, identifier: ID) -> Optional[List[ID]]:
     array = super().assistants(identifier=identifier)
     if array is not None and len(array) > 0:
         return array
     # get from ANS
     robot = ID.parse(identifier='assistant')
     if robot is not None:
         return [robot]
示例#9
0
 def do_login(self, name: str):
     sender = ID.parse(identifier=name)
     if sender is None:
         self.info('unknown user: %s' % name)
     else:
         self.info('login as %s' % sender)
         self.login(identifier=sender)
         facebook = g_messenger.facebook
         self.prompt = Console.prompt + facebook.name(identifier=sender) + '$ '
示例#10
0
 def __roaming(self, cmd: LoginCommand, sender: ID) -> Optional[ID]:
     # check time expires
     old = self.database.login_command(identifier=sender)
     if old is not None:
         if cmd.time < old.time:
             return None
     # get station ID
     assert cmd.station is not None, 'login command error: %s' % cmd
     sid = ID.parse(identifier=cmd.station.get('ID'))
     if sid == g_station.identifier:
         return None
     return sid
示例#11
0
def load_station(identifier: Union[ID, str], facebook: CommonFacebook) -> Station:
    """ Load station info from 'etc' directory

        :param identifier - station ID
        :param facebook - social network data source
        :return station with info from 'dims/etc/{address}/*'
    """
    identifier = ID.parse(identifier=identifier)
    # check meta
    meta = facebook.meta(identifier=identifier)
    if meta is None:
        # load from 'etc' directory
        meta = Meta.parse(meta=load_station_info(identifier=identifier, filename='meta.js'))
        if meta is None:
            raise LookupError('failed to get meta for station: %s' % identifier)
        elif not facebook.save_meta(meta=meta, identifier=identifier):
            raise ValueError('meta error: %s' % meta)
    # check private key
    private_key = facebook.private_key_for_signature(identifier=identifier)
    if private_key is None:
        # load from 'etc' directory
        private_key = PrivateKey.parse(key=load_station_info(identifier=identifier, filename='secret.js'))
        if private_key is None:
            pass
        elif not facebook.save_private_key(key=private_key, identifier=identifier):
            raise AssertionError('failed to save private key for ID: %s, %s' % (identifier, private_key))
    # check profile
    profile = load_station_info(identifier=identifier, filename='profile.js')
    if profile is None:
        raise LookupError('failed to get profile for station: %s' % identifier)
    Log.info('station profile: %s' % profile)
    name = profile.get('name')
    host = profile.get('host')
    port = profile.get('port')
    # create station
    if private_key is None:
        # remote station
        station = Station(identifier=identifier, host=host, port=port)
    else:
        # create profile
        profile = Document.create(doc_type=Document.PROFILE, identifier=identifier)
        profile.set_property('name', name)
        profile.set_property('host', host)
        profile.set_property('port', port)
        profile.sign(private_key=private_key)
        if not facebook.save_document(document=profile):
            raise AssertionError('failed to save profile: %s' % profile)
        # local station
        station = Station(identifier=identifier, host=host, port=port)
    facebook.cache_user(user=station)
    Log.info('station loaded: %s' % station)
    return station
示例#12
0
    def update_keys(self, key_map: dict) -> bool:
        """
        Update cipher key table into memory cache

        :param key_map: cipher keys(with direction) from local storage
        :return:        False on nothing changed
        """
        changed = False
        for _from in key_map:
            sender = ID.parse(identifier=_from)
            table = key_map.get(_from)
            assert isinstance(
                table, dict), 'sender table error: %s, %s' % (_from, table)
            for _to in table:
                receiver = ID.parse(identifier=_to)
                pw = table.get(_to)
                key = SymmetricKey.parse(key=pw)
                # TODO: check whether exists an old key
                changed = True
                # cache key with direction
                self.__cache_cipher_key(key, sender, receiver)
        return changed
示例#13
0
def user(address: str) -> Response:
    try:
        if address.find('@') < 0:
            address = Address.parse(address=address)
        identifier = ID.parse(identifier=address)
        info = g_worker.user_info(identifier=identifier)
        if info is None:
            messages = []
        else:
            identifier = ID.parse(identifier=info.get('ID'))
            messages = g_worker.messages(identifier=identifier,
                                         start=0,
                                         count=20)
        xml = render_template('user.xml', user=info, messages=messages)
    except Exception as error:
        res = {
            'code': 500,
            'name': 'Internal Server Error',
            'message': '%s' % error
        }
        xml = render_template('error.xml', result=res)
    return respond_xml(xml)
示例#14
0
 def do_call(self, name: str):
     if self.client is None:
         self.info('login first')
         return
     receiver = ID.parse(identifier=name)
     if receiver is None:
         self.info('unknown user: %s' % name)
     else:
         facebook = g_messenger.facebook
         meta = facebook.meta(identifier=receiver)
         self.info('talking with %s now, meta=%s' % (receiver, meta))
         # switch receiver
         self.receiver = receiver
示例#15
0
 def send(self, identifier: str) -> int:
     identifier = ID.parse(identifier=identifier)
     device = Device(identifier)
     tokens = device.tokens
     if tokens is None:
         print('Device token not found, failed to push message: %s' % self.__payload.alert)
         return 0
     count = 0
     for token in tokens:
         self.__client.send_notification(token_hex=token, notification=self.__payload)
         count += 1
     print('Message has been sent to %d device(s)' % count)
     return count
示例#16
0
 def __roaming(self, cmd: LoginCommand, sender: ID) -> Optional[ID]:
     # check time expires
     old = self.database.login_command(identifier=sender)
     if old is not None:
         if cmd.time < old.time:
             return None
     station = self.messenger.get_context(key='station')
     assert station is not None, 'current station not in the context'
     # get station ID
     assert cmd.station is not None, 'login command error: %s' % cmd
     sid = ID.parse(identifier=cmd.station.get('ID'))
     if sid == station.identifier:
         return None
     return sid
示例#17
0
def load_freshmen() -> List[ID]:
    freshmen = []
    text = Storage.read_text(path=freshmen_file)
    if text is None:
        return freshmen
    array = text.splitlines()
    for item in array:
        identifier = ID.parse(identifier=item)
        if identifier is None:
            Log.error('ID error: %s' % item)
        elif identifier.type == NetworkType.MAIN:
            freshmen.append(identifier)
        else:
            # Log.error('Not a user ID: %s' % identifier)
            pass
    return freshmen
示例#18
0
 def users(self) -> list:
     array = []
     anyone = ANYONE
     id_list = self.__recommended_users(address=anyone.address)
     for item in id_list:
         identifier = item.strip()
         if len(identifier) == 0:
             # skip empty lines
             continue
         identifier = ID.parse(identifier=identifier)
         info = self.user_info(identifier=identifier)
         if info is None:
             # user info not found
             continue
         array.append(info)
     return array
示例#19
0
 def __roaming(self, receiver: ID) -> Optional[ID]:
     login = self.database.login_command(identifier=receiver)
     if login is None:
         return None
     station = login.station
     if station is None:
         return None
     # check time expires
     now = time.time()
     login_time = login.time
     if login_time is None:
         self.error('%s login time not set: %s' % (receiver, login))
         return None
     if (now - login_time) > (3600 * 24 * 7):
         t_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(login_time))
         self.debug('%s login expired: [%s] %s' % (receiver, t_str, login))
         return None
     sid = ID.parse(identifier=station.get('ID'))
     if sid == self.station:
         return None
     return sid
示例#20
0
def load_user(identifier: str, facebook: CommonFacebook) -> User:
    identifier = ID.parse(identifier=identifier)
    # check meta
    try:
        meta = facebook.meta(identifier=identifier)
    except AssertionError:
        meta = None
    if meta is None:
        # load from 'etc' directory
        meta = Meta.parse(meta=load_robot_info(identifier=identifier, filename='meta.js'))
        if meta is None:
            raise LookupError('failed to get meta for robot: %s' % identifier)
        elif not facebook.save_meta(meta=meta, identifier=identifier):
            raise ValueError('meta error: %s' % meta)
    # check private key
    private_key = facebook.private_key_for_signature(identifier=identifier)
    if private_key is None:
        # load from 'etc' directory
        private_key = PrivateKey.parse(key=load_robot_info(identifier=identifier, filename='secret.js'))
        if private_key is None:
            raise AssertionError('private key not found for ID: %s' % identifier)
        elif not facebook.save_private_key(key=private_key, identifier=identifier):
            raise AssertionError('failed to save private key for ID: %s, %s' % (identifier, private_key))
    # check profile
    profile = load_robot_info(identifier=identifier, filename='profile.js')
    if profile is None:
        raise LookupError('failed to get profile for robot: %s' % identifier)
    Log.info('robot profile: %s' % profile)
    name = profile.get('name')
    avatar = profile.get('avatar')
    # create profile
    profile = Document.create(doc_type=Document.VISA, identifier=identifier)
    profile.set_property('name', name)
    profile.set_property('avatar', avatar)
    profile.sign(private_key=private_key)
    if not facebook.save_document(document=profile):
        raise AssertionError('failed to save profile: %s' % profile)
    # create local user
    return facebook.user(identifier=identifier)
示例#21
0
 def __login_station(self, identifier: ID) -> Optional[Station]:
     login = self.database.login_command(identifier=identifier)
     if login is None:
         self.error('login info not found: %s' % identifier)
         return None
     station = login.station
     if station is None:
         self.error('login station not found: %s -> %s' %
                    (identifier, login))
         return None
     sid = station.get('ID')
     if sid is None:
         self.error('login station error: %s -> %s' % (identifier, login))
         return None
     facebook = self.facebook
     sid = ID.parse(identifier=sid)
     assert sid.type == NetworkType.STATION, 'station ID error: %s' % station
     if sid == self.station:
         self.debug('login station is current station: %s -> %s' %
                    (identifier, sid))
         return None
     # anything else?
     return facebook.user(identifier=sid)
示例#22
0
def profile(address: str) -> Response:
    path = request.path
    try:
        address = ID.parse(identifier=address)
        info = g_facebook.document(identifier=address)
        if info is None:
            res = {
                'code': 404,
                'name': 'Not Found',
                'message': 'profile not found: %s' % address
            }
        else:
            res = info
    except Exception as error:
        res = {
            'code': 500,
            'name': 'Internal Server Error',
            'message': '%s' % error
        }
    js = json.dumps(res)
    if path.endswith('.js'):
        # JS callback
        js = 'dim.js.respond(%s,{"path":"%s"});' % (js, path)
    return respond_js(js)
示例#23
0
 def __init__(self, identifier: str):
     self.__identifier = ID.parse(identifier=identifier)
示例#24
0
def load_naruto():
    gid = ID.parse(identifier=group_naruto)
    Log.info('naruto group: %s' % gid)
    meta = Meta.parse(meta=load_robot_info(gid, 'meta.js'))
    g_facebook.save_meta(identifier=gid, meta=meta)
示例#25
0
    def __process_group_message(
            self, msg: ReliableMessage) -> Optional[ReliableMessage]:
        """
        Separate group message and forward them one by one

            if members not found,
                drop this message and query group info from sender;
            if 'keys' found in group message,
                update them to cache;
                remove non-exists member from 'keys
            split group message with members, forward them

        :param msg:
        :return: ReliableMessage as result
        """
        s_msg = self.verify_message(msg=msg)
        if s_msg is None:
            # signature error?
            return None
        sender = msg.sender
        receiver = msg.receiver
        if not self.facebook.exists_member(member=sender, group=receiver):
            if not self.facebook.is_owner(member=sender, group=receiver):
                # not allow, kick it out
                cmd = GroupCommand.expel(group=receiver, member=sender)
                sender = self.facebook.current_user.identifier
                receiver = msg.sender
                env = Envelope.create(sender=sender, receiver=receiver)
                i_msg = InstantMessage.create(head=env, body=cmd)
                s_msg = self.encrypt_message(msg=i_msg)
                if s_msg is None:
                    self.error('failed to encrypt message: %s' % i_msg)
                    self.suspend_message(msg=i_msg)
                    return None
                return self.sign_message(msg=s_msg)
        members = self.facebook.members(receiver)
        if members is None or len(members) == 0:
            # members not found for this group,
            # query it from sender
            res = GroupCommand.query(group=receiver)
        else:
            # check 'keys'
            keys = msg.get('keys')
            if keys is not None:
                # remove non-exists members from 'keys'
                expel_list = []
                for item in keys:
                    if item == 'digest':
                        self.info('key digest: %s' % keys[item])
                        continue
                    m = ID.parse(identifier=item)
                    if m not in members:
                        self.info('remove non-exists member: %s' % item)
                        expel_list.append(m)
                if len(expel_list) > 0:
                    # send 'expel' command to the sender
                    cmd = GroupCommand.expel(group=receiver,
                                             members=expel_list)
                    g_messenger.send_content(sender=None,
                                             receiver=sender,
                                             content=cmd)
                # update key map
                self.__key_cache.update_keys(keys=keys,
                                             sender=sender,
                                             group=receiver)
            # split and forward group message,
            # respond receipt with success or failed list
            res = self.__split_group_message(msg=msg, members=members)
        # pack response
        if res is not None:
            sender = self.facebook.current_user.identifier
            receiver = msg.sender
            env = Envelope.create(sender=sender, receiver=receiver)
            i_msg = InstantMessage.create(head=env, body=res)
            s_msg = self.encrypt_message(msg=i_msg)
            if s_msg is None:
                self.error('failed to encrypt message: %s' % i_msg)
                self.suspend_message(msg=i_msg)
                return None
            return self.sign_message(msg=s_msg)
示例#26
0
from etc.cfg_bots import group_naruto
from etc.cfg_bots import lingling_id, xiaoxiao_id, chatroom_id, tokentalkteam_id

from etc.cfg_loader import load_robot_info, load_station

g_released = True

Log.info("local storage directory: %s" % base_dir)
Storage.root = base_dir


"""
    Current Station
    ~~~~~~~~~~~~~~~
"""
station_id = ID.parse(identifier=station_id)

station_host = '127.0.0.1'
# station_host = '134.175.87.98'  # dimchat-gz
# station_host = '124.156.108.150'  # dimchat-hk
station_port = 9394

g_station = Server(identifier=station_id, host=station_host, port=station_port)

g_facebook = ClientFacebook()
g_facebook.cache_user(user=g_station)

# Address Name Service
g_ans = AddressNameServer()
g_ans.save('station', g_station.identifier)
g_ans.save('moki', ID.parse(identifier='moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk'))
示例#27
0
def channel(address: str) -> Response:
    path = request.path
    if path is None:
        ext = 'xml'
    elif path.endswith('.rss'):
        ext = 'rss'
    elif path.endswith('.js'):
        ext = 'js'
    elif path.endswith('.json'):
        ext = 'json'
    else:
        ext = 'xml'
    try:
        if address.find('@') < 0:
            address = Address.parse(address=address)
        identifier = ID.parse(identifier=address)
        info = g_worker.user_info(identifier=identifier)
        if info is None:
            res = {
                'code': 404,
                'name': 'Not Found',
                'message': '%s not found' % address
            }
            xml = render_template('error.xml', result=res)
        else:
            start = request.args.get('start')
            count = request.args.get('count')
            if start is None:
                start = 0
            else:
                start = int(start)
            if count is None:
                count = 20
            else:
                count = int(count)
            identifier = ID.parse(identifier=info.get('ID'))
            messages = g_worker.messages(identifier=identifier,
                                         start=start,
                                         count=count)
            if ext == 'rss':
                xml = render_template('channel.rss',
                                      user=info,
                                      messages=messages)
            else:
                xml = render_template('channel.xml',
                                      user=info,
                                      messages=messages)
    except Exception as error:
        res = {
            'code': 500,
            'name': 'Internal Server Error',
            'message': '%s' % error
        }
        xml = render_template('error.xml', result=res)
    if ext == 'js' or ext == 'json':
        info = xmltodict.parse(xml)
        if 'xml' in info:
            info = info['xml']
        elif 'result' in info:
            info = info['result']
        js = json.dumps(info)
        if ext == 'js':
            js = 'dwitter.js.respond(%s,{"path":"%s"});' % (js, path)
        return respond_js(js)
    else:
        return respond_xml(xml)
示例#28
0
 def identifier(self, identifier: str) -> Optional[ID]:
     try:
         return ID.parse(identifier=identifier)
     except ValueError:
         self.error('ID error: %s' % identifier)
示例#29
0
 def identifier(self) -> ID:
     return ID.parse(identifier=self.get('ID'))
示例#30
0
    identifier = ID.parse(identifier=identifier)
    server = Station(identifier=identifier, host=host, port=port)
    g_facebook.cache_user(user=server)
    Log.info('local station created: %s' % server)
    return server


"""
    Loading info
    ~~~~~~~~~~~~
"""

# load ANS reserved records
Log.info('-------- Loading ANS reserved records')
for key, value in ans_reserved_records.items():
    _id = ID.parse(identifier=value)
    assert _id is not None, 'ANS record error: %s, %s' % (key, value)
    Log.info('Name: %s -> ID: %s' % (key, _id))
    if key in ans_keywords:
        # remove reserved name temporary
        index = ans_keywords.index(key)
        ans_keywords.remove(key)
        g_ans.save(key, _id)
        ans_keywords.insert(index, key)
    else:
        # not reserved name, save it directly
        g_ans.save(key, _id)

# scan accounts
Log.info('-------- scanning accounts')
g_database.scan_ids()