def main(url):
    character = {}
    client = DDPClient('ws://dicecloud.com/websocket', auto_reconnect=False)
    client.is_connected = False
    client.connect()

    def connected():
        client.is_connected = True

    client.on('connected', connected)
    while not client.is_connected:
        time.sleep(1)
    client.subscribe('singleCharacter', [url])

    def update_character(collection, _id, fields):
        if character.get(collection) is None:
            character[collection] = []
        fields['id'] = _id
        character.get(collection).append(fields)

    client.on('added', update_character)
    time.sleep(10)
    client.close()
    character['id'] = url
    return character
class DDPCiaoClient:
    def __init__(self, ddp_params, ciao_queue):
        # validate params - START
        missing_params = []
        required_params = ["host", "port"]
        for p in required_params:
            if not p in ddp_params:
                missing_params.append(p)

        if len(missing_params) > 0:
            raise RuntimeError("DDP configuration error, missing: %s" % ",".join(missing_params))

            # validate params - END

            # reference to Queue for exchanging data with CiaoCore
        self.ciao_queue = ciao_queue

        # local instance of DDP Client
        self.handle = DDPClient("ws://" + ddp_params["host"] + ":" + str(ddp_params["port"]) + "/websocket")
        self.handle.on("connected", self.on_connect)

        self.host = ddp_params["host"]
        self.port = ddp_params["port"]

        self.logger = logging.getLogger("ddp.client")

    def on_connect():
        self.logger.info("Connected to DDP Server %s" % self.host)

    """
	TODO from METEOR to SKETCH
	def on_message(self, client, userdata, msg):
		self.logger.debug("Got new message. Topic: %s Message: %s" % (str(msg.topic), str(msg.payload)))
		entry = {
			"data" : [str(msg.topic), str(msg.payload)]
		}
		self.ciao_queue.put(entry)
	"""

    def connect(self):
        try:
            self.handle.connect()
            return True
        except:
            return False

    def disconnect(self):
        self.handle.close()

    def callback_function(a, b):
        self.logger.debug("Callback Function. Param1: %s Param2: %s" % (str(a), str(b)))

    def publish(self, function, parameter):
        self.logger.debug("Call function: %s Parameter: %s" % (function, str(parameter)))
        try:
            self.handle.call(function, json.loads(parameter), None)
        except Exception, e:
            self.logger.error("Failed handle call: " + str(e))
Esempio n. 3
0
class MeteorClient(EventEmitter):
    def __init__(self,
                 url,
                 auto_reconnect=True,
                 auto_reconnect_timeout=0.5,
                 debug=False):
        EventEmitter.__init__(self)
        self.collection_data = CollectionData()
        self.ddp_client = DDPClient(
            url,
            auto_reconnect=auto_reconnect,
            auto_reconnect_timeout=auto_reconnect_timeout,
            debug=debug)
        self.ddp_client.on('connected', self.connected)
        self.ddp_client.on('socket_closed', self.closed)
        self.ddp_client.on('failed', self.failed)
        self.ddp_client.on('added', self.added)
        self.ddp_client.on('changed', self.changed)
        self.ddp_client.on('removed', self.removed)
        self.ddp_client.on('reconnected', self._reconnected)
        self.connected = False
        self.subscriptions = {}
        self._login_data = None
        self._login_token = None

    def connect(self):
        """Connect to the meteor server"""
        self.ddp_client.connect()

    def close(self):
        """Close connection with meteor server"""
        self.ddp_client.close()

    def _reconnected(self):
        """Reconnect

        Currently we get a new session every time so we have
        to clear all the data an resubscribe"""

        if self._login_data or self._login_token:

            def reconnect_login_callback(error, result):
                if error:
                    if self._login_token:
                        self._login_token = None
                        self._login(self._login_data,
                                    callback=reconnect_login_callback)
                        return
                    else:
                        raise MeteorClientException(
                            'Failed to re-authenticate during reconnect')

                self.connected = True
                self._resubscribe()

            if self._login_token:
                self._resume(self._login_token,
                             callback=reconnect_login_callback)
            else:
                self._login(self._login_data,
                            callback=reconnect_login_callback)
        else:
            self._resubscribe()

    def _resubscribe(self):
        self.collection_data.data = {}
        cur_subs = self.subscriptions.items()
        self.subscriptions = {}
        for name, value in cur_subs:
            self.subscribe(name, value['params'])
        self.emit('reconnected')

    #
    # Account Management
    #

    def login(self, user, password, token=None, callback=None):
        """Login with a username and password

        Arguments:
        user - username or email address
        password - the password for the account

        Keyword Arguments:
        token - meteor resume token
        callback - callback function containing error as first argument and login data"""
        # TODO: keep the tokenExpires around so we know the next time
        #       we need to authenticate

        # hash the password
        hashed = hashlib.sha256(password).hexdigest()
        # handle username or email address
        if '@' in user:
            user_object = {'email': user}
        else:
            user_object = {'username': user}
        password_object = {'algorithm': 'sha-256', 'digest': hashed}

        self._login_token = token
        self._login_data = {'user': user_object, 'password': password_object}

        if token:
            self._resume(token, callback=callback)
        else:
            self._login(self._login_data, callback=callback)

    def _resume(self, token, callback=None):
        login_data = {'resume': token}
        self._login(login_data, callback=callback)

    def _login(self, login_data, callback=None):
        self.emit('logging_in')

        def logged_in(error, data):
            if error:
                if self._login_token:
                    self._login_token = None
                    self._login(self._login_data, callback=callback)
                    return
                if callback:
                    callback(error, None)
                return

            self._login_token = data['token']

            if callback:
                callback(None, data)
            self.emit('logged_in', data)

        self.ddp_client.call('login', [login_data], callback=logged_in)

    def logout(self, callback=None):
        """Logout a user

        Keyword Arguments:
        callback - callback function called when the user has been logged out"""
        self.ddp_client.call('logout', [], callback=callback)
        self.emit('logged_out')

    #
    # Meteor Method Call
    #

    def call(self, method, params, callback=None):
        """Call a remote method

        Arguments:
        method - remote method name
        params - remote method parameters

        Keyword Arguments:
        callback - callback function containing return data"""
        self._wait_for_connect()
        self.ddp_client.call(method, params, callback=callback)

    #
    # Subscription Management
    #

    def subscribe(self, name, params=[], callback=None):
        """Subscribe to a collection
        Arguments:
        name - the name of the publication
        params - the subscription parameters

        Keyword Arguments:
        callback - a function callback that returns an error (if exists)"""
        self._wait_for_connect()

        def subscribed(error, sub_id):
            if error:
                self._remove_sub_by_id(sub_id)
                if callback:
                    callback(error.get('reason'))
                return
            if callback:
                callback(None)
            self.emit('subscribed', name)

        if name in self.subscriptions:
            raise MeteorClientException('Already subcribed to {}'.format(name))

        sub_id = self.ddp_client.subscribe(name, params, subscribed)
        self.subscriptions[name] = {'id': sub_id, 'params': params}

    def unsubscribe(self, name):
        """Unsubscribe from a collection

        Arguments:
        name - the name of the publication"""
        self._wait_for_connect()
        if name not in self.subscriptions:
            raise MeteorClientException('No subscription for {}'.format(name))
        self.ddp_client.unsubscribe(self.subscriptions[name]['id'])
        del self.subscriptions[name]
        self.emit('unsubscribed', name)

    #
    # Collection Management
    #

    def find(self, collection, selector={}):
        """Find data in a collection

        Arguments:
        collection - collection to search

        Keyword Arguments:
        selector - the query (default returns all items in a collection)"""
        results = []
        for _id, doc in self.collection_data.data.get(collection, {}).items():
            doc.update({'_id': _id})
            if selector == {}:
                results.append(doc)
            for key, value in selector.items():
                if key in doc and doc[key] == value:
                    results.append(doc)
        return results

    def find_one(self, collection, selector={}):
        """Return one item from a collection

        Arguments:
        collection - collection to search

        Keyword Arguments:
        selector - the query (default returns first item found)"""
        for _id, doc in self.collection_data.data.get(collection, {}).items():
            doc.update({'_id': _id})
            if selector == {}:
                return doc
            for key, value in selector.items():
                if key in doc and doc[key] == value:
                    return doc
        return None

    def insert(self, collection, doc, callback=None):
        """Insert an item into a collection

        Arguments:
        collection - the collection to be modified
        doc - The document to insert. May not yet have an _id attribute,
        in which case Meteor will generate one for you.

        Keyword Arguments:
        callback - Optional. If present, called with an error object as the first argument and,
        if no error, the _id as the second."""
        self.call("/" + collection + "/insert", [doc], callback=callback)

    def update(self, collection, selector, modifier, callback=None):
        """Insert an item into a collection

        Arguments:
        collection - the collection to be modified
        selector - specifies which documents to modify
        modifier - Specifies how to modify the documents

        Keyword Arguments:
        callback - Optional. If present, called with an error object as the first argument and,
        if no error, the number of affected documents as the second."""
        self.call("/" + collection + "/update", [selector, modifier],
                  callback=callback)

    def remove(self, collection, selector, callback=None):
        """Remove an item from a collection

        Arguments:
        collection - the collection to be modified
        selector - Specifies which documents to remove

        Keyword Arguments:
        callback - Optional. If present, called with an error object as its argument."""
        self.call("/" + collection + "/remove", [selector], callback=callback)

    #
    # Event Handlers
    #

    def connected(self):
        self.connected = True
        self.emit('connected')

    def closed(self, code, reason):
        self.connected = False
        self.emit('closed', code, reason)

    def failed(self, data):
        self.emit('failed', str(data))

    def added(self, collection, id, fields):
        self.collection_data.add_data(collection, id, fields)
        self.emit('added', collection, id, fields)

    def changed(self, collection, id, fields, cleared):
        self.collection_data.change_data(collection, id, fields, cleared)
        self.emit('changed', collection, id, fields, cleared)

    def removed(self, collection, id):
        self.collection_data.remove_data(collection, id)
        self.emit('removed', collection, id)

    #
    # Helper functions
    #

    def _time_from_start(self, start):
        now = datetime.datetime.now()
        return now - start

    def _remove_sub_by_id(self, sub_id):
        for name, cur_sub_id in self.subscriptions.items():
            if cur_sub_id == sub_id:
                del self.subscriptions[name]

    def _wait_for_connect(self):
        start = datetime.datetime.now()
        while not self.connected and self._time_from_start(start).seconds < 5:
            time.sleep(0.1)

        if not self.connected:
            raise MeteorClientException(
                'Could not subscribe because a connection has not been established'
            )
Esempio n. 4
0
class MeteorClient(EventEmitter):
    def __init__(self, url, auto_reconnect=True, auto_reconnect_timeout=0.5, debug=False, headers=None):
        EventEmitter.__init__(self)
        self.collection_data = CollectionData()
        self.ddp_client = DDPClient(url, auto_reconnect=auto_reconnect,
                                    auto_reconnect_timeout=auto_reconnect_timeout, debug=debug, headers=headers)
        self.ddp_client.on('connected', self.connected)
        self.ddp_client.on('socket_closed', self.closed)
        self.ddp_client.on('failed', self.failed)
        self.ddp_client.on('added', self.added)
        self.ddp_client.on('changed', self.changed)
        self.ddp_client.on('removed', self.removed)
        self.ddp_client.on('reconnected', self._reconnected)
        self.connected = False
        self.subscriptions = {}
        self._login_data = None
        self._login_token = None

    def connect(self):
        """Connect to the meteor server"""
        self.ddp_client.connect()

    def close(self):
        """Close connection with meteor server"""
        self.ddp_client.close()

    def _reconnected(self):
        """Reconnect

        Currently we get a new session every time so we have
        to clear all the data an resubscribe"""

        if self._login_data or self._login_token:

            def reconnect_login_callback(error, result):
                if error:
                    if self._login_token:
                        self._login_token = None
                        self._login(self._login_data,
                                    callback=reconnect_login_callback)
                        return
                    else:
                        raise MeteorClientException(
                            'Failed to re-authenticate during reconnect')

                self.connected = True
                self._resubscribe()

            if self._login_token:
                self._resume(self._login_token,
                             callback=reconnect_login_callback)
            else:
                self._login(self._login_data,
                            callback=reconnect_login_callback)
        else:
            self._resubscribe()

    def _resubscribe(self):
        self.collection_data.data = {}
        cur_subs = self.subscriptions.items()
        self.subscriptions = {}
        for name, value in cur_subs:
            self.subscribe(name, value['params'])
        self.emit('reconnected')
    #
    # Account Management
    #

    def login(self, user, password, token=None, callback=None):
        """Login with a username and password

        Arguments:
        user - username or email address
        password - the password for the account

        Keyword Arguments:
        token - meteor resume token
        callback - callback function containing error as first argument and login data"""
        # TODO: keep the tokenExpires around so we know the next time
        #       we need to authenticate

        # hash the password
        hashed = hashlib.sha256(password).hexdigest()
        # handle username or email address
        if '@' in user:
            user_object = {
                'email': user
            }
        else:
            user_object = {
                'username': user
            }
        password_object = {
            'algorithm': 'sha-256',
            'digest': hashed
        }

        self._login_token = token
        self._login_data = {'user': user_object, 'password': password_object}

        if token:
            self._resume(token, callback=callback)
        else:
            self._login(self._login_data, callback=callback)

    def _resume(self, token, callback=None):
        login_data = {'resume': token}
        self._login(login_data, callback=callback)

    def _login(self, login_data, callback=None):
        self.emit('logging_in')

        def logged_in(error, data):
            if error:
                if self._login_token:
                    self._login_token = None
                    self._login(self._login_data, callback=callback)
                    return
                if callback:
                    callback(error, None)
                return

            self._login_token = data['token']

            if callback:
                callback(None, data)
            self.emit('logged_in', data)

        self.ddp_client.call('login', [login_data], callback=logged_in)

    def logout(self, callback=None):
        """Logout a user

        Keyword Arguments:
        callback - callback function called when the user has been logged out"""
        self.ddp_client.call('logout', [], callback=callback)
        self.emit('logged_out')

    #
    # Meteor Method Call
    #

    def call(self, method, params, callback=None):
        """Call a remote method

        Arguments:
        method - remote method name
        params - remote method parameters

        Keyword Arguments:
        callback - callback function containing return data"""
        self._wait_for_connect()
        self.ddp_client.call(method, params, callback=callback)

    #
    # Subscription Management
    #

    def subscribe(self, name, params=[], callback=None):
        """Subscribe to a collection
        Arguments:
        name - the name of the publication
        params - the subscription parameters

        Keyword Arguments:
        callback - a function callback that returns an error (if exists)"""
        self._wait_for_connect()

        def subscribed(error, sub_id):
            if error:
                self._remove_sub_by_id(sub_id)
                if callback:
                    callback(error.get('reason'))
                return
            if callback:
                callback(None)
            self.emit('subscribed', name)

        if name in self.subscriptions:
            raise MeteorClientException('Already subcribed to {}'.format(name))

        sub_id = self.ddp_client.subscribe(name, params, subscribed)
        self.subscriptions[name] = {
            'id': sub_id,
            'params': params
        }

    def unsubscribe(self, name):
        """Unsubscribe from a collection

        Arguments:
        name - the name of the publication"""
        self._wait_for_connect()
        if name not in self.subscriptions:
            raise MeteorClientException('No subscription for {}'.format(name))
        self.ddp_client.unsubscribe(self.subscriptions[name]['id'])
        del self.subscriptions[name]
        self.emit('unsubscribed', name)

    #
    # Collection Management
    #

    def find(self, collection, selector={}):
        """Find data in a collection

        Arguments:
        collection - collection to search

        Keyword Arguments:
        selector - the query (default returns all items in a collection)"""
        results = []
        for _id, doc in self.collection_data.data.get(collection, {}).items():
            doc.update({'_id': _id})
            if selector == {}:
                results.append(doc)
            for key, value in selector.items():
                if key in doc and doc[key] == value:
                    results.append(doc)
        return results

    def find_one(self, collection, selector={}):
        """Return one item from a collection

        Arguments:
        collection - collection to search

        Keyword Arguments:
        selector - the query (default returns first item found)"""
        for _id, doc in self.collection_data.data.get(collection, {}).items():
            doc.update({'_id': _id})
            if selector == {}:
                return doc
            for key, value in selector.items():
                if key in doc and doc[key] == value:
                    return doc
        return None

    def insert(self, collection, doc, callback=None):
        """Insert an item into a collection

        Arguments:
        collection - the collection to be modified
        doc - The document to insert. May not yet have an _id attribute,
        in which case Meteor will generate one for you.

        Keyword Arguments:
        callback - Optional. If present, called with an error object as the first argument and,
        if no error, the _id as the second."""
        self.call("/" + collection + "/insert", [doc], callback=callback)

    def update(self, collection, selector, modifier, callback=None):
        """Insert an item into a collection

        Arguments:
        collection - the collection to be modified
        selector - specifies which documents to modify
        modifier - Specifies how to modify the documents

        Keyword Arguments:
        callback - Optional. If present, called with an error object as the first argument and,
        if no error, the number of affected documents as the second."""
        self.call("/" + collection + "/update", [selector, modifier], callback=callback)

    def remove(self, collection, selector, callback=None):
        """Remove an item from a collection

        Arguments:
        collection - the collection to be modified
        selector - Specifies which documents to remove

        Keyword Arguments:
        callback - Optional. If present, called with an error object as its argument."""
        self.call("/" + collection + "/remove", [selector], callback=callback)

    #
    # Event Handlers
    #

    def connected(self):
        self.connected = True
        self.emit('connected')

    def closed(self, code, reason):
        self.connected = False
        self.emit('closed', code, reason)

    def failed(self, data):
        self.emit('failed', str(data))

    def added(self, collection, id, fields):
        self.collection_data.add_data(collection, id, fields)
        self.emit('added', collection, id, fields)

    def changed(self, collection, id, fields, cleared):
        self.collection_data.change_data(collection, id, fields, cleared)
        self.emit('changed', collection, id, fields, cleared)

    def removed(self, collection, id):
        self.collection_data.remove_data(collection, id)
        self.emit('removed', collection, id)

    #
    # Helper functions
    #

    def _time_from_start(self, start):
        now = datetime.datetime.now()
        return now - start

    def _remove_sub_by_id(self, sub_id):
        for name, cur_sub_id in self.subscriptions.items():
            if cur_sub_id == sub_id:
                del self.subscriptions[name]

    def _wait_for_connect(self):
        start = datetime.datetime.now()
        while not self.connected and self._time_from_start(start).seconds < 5:
            time.sleep(0.1)

        if not self.connected:
            raise MeteorClientException('Could not subscribe because a connection has not been established')
Esempio n. 5
0
class Hoduino():
    """
        A listener for the arduino
    """
    latest_hash = None

    def __init__(self, ddp_server_url, debug=False):
        self.shutting_down = False
        self.server_url = ddp_server_url
        self.debug = debug

        self.board_interface = HoduinoBoardInterface(port=ARDUINO_PORT)

        # Setup connection to the DDP server
        self.ddp_connect()

    def ddp_connect(self):
        print '++ DDP connection attempt...'
        self.ddp_client = DDPClient(self.server_url)
        self.ddp_client.debug = self.debug

        # Some events to handle from DDP
        self.ddp_client.on('added', self.added)
        self.ddp_client.on('connected', self.connected)
        self.ddp_client.on('socket_closed', self.closed)
        self.ddp_client.on('failed', self.failed)

        # Connect to DDP server
        self.ddp_client.connect()

    def exit(self):
        self.shutting_down = True

        # Close connect to Arduino
        if self.board_interface:
            self.board_interface.close_arduino()

        # Close connection to 
        self.ddp_client.close()

    def connected(self):
        print '++ DDP Connected'
        self.ddp_connected = True

        # Subscribe to messages stream. Limit messages to 1 as we 
        # only want the latest message.
        sub_id = self.ddp_client.subscribe('messages', [{'limit': 1}])

    def closed(self, code, reason):
        print '++ DDP connection closed {0} {1}'.format(code, reason)
        self.ddp_connected = False

        if not self.shutting_down:
            while not self.ddp_connected:
                self.ddp_connect()
                time.sleep(30)

    def failed(self, data):
        print '++ DDP failed - data: {0}'.format(str(data))

    def added(self, collection, id, fields):
        print '++ DDP added {0} {1}'.format(len(collection), id)
        if id <> self.latest_hash:
            print '++ New messages!'
            self.board_interface.donation_reaction()
            self.latest_hash = id
        else:
            print '++ No new messages'