Beispiel #1
0
    def __init__(self, botToken, admins, password, db, nodeList, rewardList):

        # Currently only used for markdown
        self.messenger = "discord"
        self.running = False

        self.client = discord.Client()
        self.client.on_ready = self.on_ready
        self.client.on_message = self.on_message
        # Create a bot instance for async messaging
        self.token = botToken
        # Set the database of the pools/users/nodes
        self.database = db
        # Store and setup the nodeslist
        self.nodeList = nodeList
        self.nodeList.networkCB = self.networkCB
        self.nodeList.nodeChangeCB = self.nodeUpdateCB
        self.nodeList.adminCB = self.adminCB
        # Store and setup the nodereward list
        self.rewardList = rewardList
        self.rewardList.rewardCB = self.rewardCB
        self.rewardList.errorCB = self.rewardListErrorCB
        # Create the WebExplorer
        self.explorer = WebExplorer(self.balancesCB)
        self.balanceChecks = {}
        # Store the admin password
        self.password = password
        # Store the admin user
        self.admins = admins
        # Semphore to lock the balance check list.
        self.balanceSem = threading.Lock()
        # Reward aberration
        self.aberration = 0
Beispiel #2
0
    def __init__(self, botToken, admin, password, db, nodeList):

        # Currently only used for markdown
        self.messenger = "telegram"

        # Create a bot instance for async messaging
        self.bot = telegram.Bot(token=botToken)
        # Create the updater instance for configuration
        self.updater = Updater(token=botToken)
        # Set the database of the pools/users/nodes
        self.database = db
        # Store and setup the nodeslist
        self.nodeList = nodeList
        self.nodeList.networkCB = self.networkCB
        self.nodeList.nodeChangeCB = self.nodeUpdateCB
        self.nodeList.adminCB = self.adminCB
        # Create the WebExplorer
        self.explorer = WebExplorer(self.balancesCB)
        self.balanceChecks = {}
        # Store the admins id
        self.admin = admin
        # Store the admin password
        self.password = password
        # Create the message queue
        self.messageQueue = MessagingMachine(self.bot, db)
        # Semphore to lock the balance check list.
        self.balanceSem = threading.Lock()

        # Get the dispather to add the needed handlers
        dp = self.updater.dispatcher

        #### Setup node related handler ####
        dp.add_handler(CommandHandler('add', self.nodeAdd, pass_args=True))
        dp.add_handler(CommandHandler('update', self.nodeUpdate, pass_args=True))
        dp.add_handler(CommandHandler('remove', self.nodeRemove, pass_args=True))
        dp.add_handler(CommandHandler('detail', self.detail))
        dp.add_handler(CommandHandler('nodes', self.nodes))
        dp.add_handler(CommandHandler('balance', self.balance))
        dp.add_handler(CommandHandler('lookup', self.lookup, pass_args=True))

        #### Setup user related handler ####
        dp.add_handler(CommandHandler('username', self.username, pass_args=True))
        dp.add_handler(CommandHandler('me', self.me))
        dp.add_handler(CommandHandler('status', self.status, pass_args=True))
        dp.add_handler(CommandHandler('reward', self.reward, pass_args=True))
        dp.add_handler(CommandHandler('timeout', self.timeout, pass_args=True))
        dp.add_handler(CommandHandler('network', self.network, pass_args=True))

        #### Setup common handler ####
        dp.add_handler(CommandHandler('start', self.started))
        dp.add_handler(CommandHandler('help', self.help))
        dp.add_handler(CommandHandler('info', self.info))

        #### Setup admin handler, Not public ####
        dp.add_handler(CommandHandler('broadcast', self.broadcast, pass_args=True))
        dp.add_handler(CommandHandler('stats', self.stats, pass_args=True))
        dp.add_handler(CommandHandler('loglevel', self.loglevel, pass_args=True))
        dp.add_handler(CommandHandler('settings', self.settings, pass_args=True))

        dp.add_handler(MessageHandler(Filters.command, self.unknown))
        dp.add_error_handler(self.error)

        self.sendMessage(self.admin, "*Bot Started*")
Beispiel #3
0
class SmartNodeBotTelegram(object):

    def __init__(self, botToken, admin, password, db, nodeList):

        # Currently only used for markdown
        self.messenger = "telegram"

        # Create a bot instance for async messaging
        self.bot = telegram.Bot(token=botToken)
        # Create the updater instance for configuration
        self.updater = Updater(token=botToken)
        # Set the database of the pools/users/nodes
        self.database = db
        # Store and setup the nodeslist
        self.nodeList = nodeList
        self.nodeList.networkCB = self.networkCB
        self.nodeList.nodeChangeCB = self.nodeUpdateCB
        self.nodeList.adminCB = self.adminCB
        # Create the WebExplorer
        self.explorer = WebExplorer(self.balancesCB)
        self.balanceChecks = {}
        # Store the admins id
        self.admin = admin
        # Store the admin password
        self.password = password
        # Create the message queue
        self.messageQueue = MessagingMachine(self.bot, db)
        # Semphore to lock the balance check list.
        self.balanceSem = threading.Lock()

        # Get the dispather to add the needed handlers
        dp = self.updater.dispatcher

        #### Setup node related handler ####
        dp.add_handler(CommandHandler('add', self.nodeAdd, pass_args=True))
        dp.add_handler(CommandHandler('update', self.nodeUpdate, pass_args=True))
        dp.add_handler(CommandHandler('remove', self.nodeRemove, pass_args=True))
        dp.add_handler(CommandHandler('detail', self.detail))
        dp.add_handler(CommandHandler('nodes', self.nodes))
        dp.add_handler(CommandHandler('balance', self.balance))
        dp.add_handler(CommandHandler('lookup', self.lookup, pass_args=True))

        #### Setup user related handler ####
        dp.add_handler(CommandHandler('username', self.username, pass_args=True))
        dp.add_handler(CommandHandler('me', self.me))
        dp.add_handler(CommandHandler('status', self.status, pass_args=True))
        dp.add_handler(CommandHandler('reward', self.reward, pass_args=True))
        dp.add_handler(CommandHandler('timeout', self.timeout, pass_args=True))
        dp.add_handler(CommandHandler('network', self.network, pass_args=True))

        #### Setup common handler ####
        dp.add_handler(CommandHandler('start', self.started))
        dp.add_handler(CommandHandler('help', self.help))
        dp.add_handler(CommandHandler('info', self.info))

        #### Setup admin handler, Not public ####
        dp.add_handler(CommandHandler('broadcast', self.broadcast, pass_args=True))
        dp.add_handler(CommandHandler('stats', self.stats, pass_args=True))
        dp.add_handler(CommandHandler('loglevel', self.loglevel, pass_args=True))
        dp.add_handler(CommandHandler('settings', self.settings, pass_args=True))

        dp.add_handler(MessageHandler(Filters.command, self.unknown))
        dp.add_error_handler(self.error)

        self.sendMessage(self.admin, "*Bot Started*")

    ######
    # Starts the bot and block until the programm will be stopped.
    ######
    def start(self):
        logger.info("Start!")
        self.updater.start_polling()
        self.updater.idle()

    def isGroup(self, update):

        if update.message.chat_id != update.message.from_user.id:
            logger.warning("not allowed group action")
            response = messages.notAvailableInGroups(self.messenger)
            self.sendMessage(update.message.chat_id, response )
            return True

        return False

    ######
    # Add a message to the queue
    ######
    def sendMessage(self, chatId, text, split = '\n'):
        self.messageQueue.addMessage(chatId, text, split)


    def adminCheck(self, chatId, password):
        logger.warning("adminCheck - {} == {}, {} == {}".format(self.admin, chatId, self.password, password))
        return int(self.admin) == int(chatId) and self.password == password


    ############################################################
    #                 Node handler calls                       #
    ############################################################

    def nodeAdd(self, bot, update, args):

        if not self.isGroup(update):
            response = node.nodeAdd(self, update, args)
            self.sendMessage(update.message.chat_id, response)

    def nodeUpdate(self, bot, update, args):

        if not self.isGroup(update):
            response = node.nodeUpdate(self, update, args)
            self.sendMessage(update.message.chat_id, response)

    def nodeRemove(self, bot, update, args):

        if not self.isGroup(update):
            response = node.nodeRemove(self, update, args)
            self.sendMessage(update.message.chat_id, response)

    def detail(self, bot, update):

        if not self.isGroup(update):
            response = node.detail(self, update)
            self.sendMessage(update.message.chat_id, response,'\n\n')

    def nodes(self, bot, update):

        if not self.isGroup(update):
            response = node.nodes(self, update)
            self.sendMessage(update.message.chat_id, response,'\n\n')

    def balance(self, bot, update):

        if not self.isGroup(update):

            failed = None
            nodes = []

            dbUser = self.database.getUser(update.message.chat_id)
            userNodes = self.database.getAllNodes(update.message.chat_id)

            if dbUser == None or userNodes == None or len(userNodes) == 0:

                response = messages.markdown("<u><b>Balances<b><u>\n\n",self.messenger)
                response +=  messages.nodesRequired(self.messenger)

                self.sendMessage(update.message.chat_id, response)
                return

            collaterals = list(map(lambda x: x['collateral'],userNodes))
            nodes = self.nodeList.getNodes(collaterals)
            check = self.explorer.balances(nodes)

            # Needed cause the balanceChecks dict also gets modified from other
            # threads.
            self.balanceSem.acquire()

            if check:
                self.balanceChecks[check] = update.message.chat_id
            else:
                logger.info("Balance check failed instant.")
                failed = uuid.uuid4()
                self.balanceChecks[failed] = update.message.chat_id

            # Needed cause the balanceChecks dict also gets modified from other
            # threads.
            self.balanceSem.release()

            if failed:
                self.balancesCB(failed,None)

    def lookup(self, bot, update, args):

        response = node.lookup(self, update, args)
        self.sendMessage(update.message.chat_id, response)

    ############################################################
    #                 User handler calls                     #
    ############################################################

    def username(self, bot, update, args):

        if not self.isGroup(update):
            response = user.username( self, update, args)
            self.sendMessage(update.message.chat_id, response)

    def me(self, bot, update):

        if not self.isGroup(update):
            response = user.me( self, update )
            self.sendMessage(update.message.chat_id, response)

    def status(self, bot, update, args):

        if not self.isGroup(update):
            response = user.status( self, update, args )
            self.sendMessage(update.message.chat_id, response)

    def reward(self, bot, update, args):

        if not self.isGroup(update):
            response = user.reward( self, update, args )
            self.sendMessage(update.message.chat_id, response)

    def timeout(self, bot, update, args):

        if not self.isGroup(update):
            response = user.timeout( self, update, args )
            self.sendMessage(update.message.chat_id, response)

    def network(self, bot, update, args):

        if not self.isGroup(update):
            response = user.network( self, update, args )
            self.sendMessage(update.message.chat_id, response)

    ############################################################
    #                 Common handler calls                     #
    ############################################################

    def started(self, bot, update):

        self.sendMessage(update.message.chat_id, '**Welcome**\n\n' + messages.help(self.messenger))

    def help(self, bot, update):

        self.sendMessage(update.message.chat_id, messages.help(self.messenger))

    def info(self, bot, update):

        response = common.info( self, update )
        self.sendMessage(update.message.chat_id, response)

    def broadcast(self, bot, update, args):

        if len(args) >= 2 and\
           self.adminCheck(update.message.chat_id, args[0]):

            logger.warning("broadcast - access granted")

            response = " ".join(args[1:])

            for dbUser in self.database.getUsers():
                self.sendMessage(dbUser['id'], response)
        else:
            response = common.unknown(self, update)
            self.sendMessage(update.message.chat_id, response)

    def stats(self, bot, update, args):

        if len(args) == 1 and\
           self.adminCheck(update.message.chat_id, args[0]):

            logger.warning("stats - access granted")

            response = common.stats(self)

            self.sendMessage(self.admin, response)
        else:
            response = common.unknown(self)
            self.sendMessage(update.message.chat_id, response)

    def loglevel(self, bot, update, args):

        if len(args) >= 2 and\
           self.adminCheck(update.message.chat_id, args[0]):

            logger.warning("loglevel - access granted")

            response = "*Loglevel*"

            self.sendMessage(self.admin, response)
        else:
            response = common.unknown(self)
            self.sendMessage(update.message.chat_id, response)

    def settings(self, bot, update, args):

        if len(args) == 1 and\
           self.adminCheck(update.message.chat_id, args[0]):

            logger.warning("settings - access granted")

            response = "*Settings*"

            self.sendMessage(self.admin, response)
        else:
            response = common.unknown(self)
            self.sendMessage(update.message.chat_id, response)

    def unknown(self, bot, update):

        response = common.unknown(self)
        self.sendMessage(update.message.chat_id, response)

    def error(self, bot, update, error):

        common.error(self, update, error)


    ############################################################
    #                        Callbacks                         #
    ############################################################


    ######
    # Callback which get called when there is a new releases in the smartcash repo.
    #
    # Called by: Nothing yet, SmartGitHubUpdates later.
    #
    ######
    def updateCheckCallback(self, tag):

        for dbUser in self.database.getUsers():
            self.sendMessage(dbUser['id'], ("*Node update available*\n\n"
                                         "https://github.com/SmartCash/smartcash/releases/tag/{}").format(tag))

    ######
    # Callback for evaluating if someone in the database had an upcomming event
    # and send messages to all chats with activated notifications
    #
    # Called by: SmartNodeList
    #
    ######
    def nodeUpdateCB(self, update, n):

        for dbUser in self.database.getUsers():

            userNode = self.database.getNodes(str(n.collateral), dbUser['id'])

            if not userNode:
                continue

            logger.info("nodeUpdateCB {}".format(n.payee))

            for response in node.nodeUpdated(self, update, dbUser, userNode, n):
                self.sendMessage(dbUser['id'], response)

    ######
    # Callback for evaluating if someone has enabled network notifications
    # and send messages to all relevant chats
    ######
    def networkCB(self, collaterals, added):

        response = common.networkUpdate(self, collaterals, added)

        for dbUser in self.database.getUsers('where network_n=1'):

            self.sendMessage(dbUser['id'], response)

        if added:
            # If the callback is related to new nodes no need for
            # the continue here.
            return

        # Remove the nodes also from the user database
        for collateral in collaterals:

            # Before chec if a node from anyone got removed and let him know about it.
            for userNode in self.database.getNodes(collateral):

                response = messages.nodeRemovedNotification(self.messenger, userNode['name'])

                self.sendMessage(userNode['user_id'], response)

            # Remove all entries containing this node in the db
            self.database.deleteNodesWithId(collateral)


    ######
    # Callback which gets called from the SmartNodeList when a balance request triggered by any user
    # is done. It sends the result to the related user.
    #
    # Called by: SmartExplorer
    #
    ######
    def balancesCB(self, check, results):

        self.balanceSem.acquire()

        if not check in self.balanceChecks:
            logger.error("Ivalid balance check received {} - count {}".format(check,len(results)))
            self.balanceSem.release()
            return

        userId = self.balanceChecks[check]
        self.balanceChecks.pop(check)

        self.balanceSem.release()

        response = node.balances(self, userId, results)

        self.sendMessage(userId, response)

    ######
    # Push the message to the admin
    #
    # Called by: SmartNodeList
    #
    ######
    def adminCB(self, message):
        self.sendMessage(self.admin, message)
Beispiel #4
0
class SmartNodeBotDiscord(object):
    def __init__(self, botToken, admins, password, db, nodeList, rewardList):

        # Currently only used for markdown
        self.messenger = "discord"
        self.running = False

        self.client = discord.Client()
        self.client.on_ready = self.on_ready
        self.client.on_message = self.on_message
        # Create a bot instance for async messaging
        self.token = botToken
        # Set the database of the pools/users/nodes
        self.database = db
        # Store and setup the nodeslist
        self.nodeList = nodeList
        self.nodeList.networkCB = self.networkCB
        self.nodeList.nodeChangeCB = self.nodeUpdateCB
        self.nodeList.adminCB = self.adminCB
        # Store and setup the nodereward list
        self.rewardList = rewardList
        self.rewardList.rewardCB = self.rewardCB
        self.rewardList.errorCB = self.rewardListErrorCB
        # Create the WebExplorer
        self.explorer = WebExplorer(self.balancesCB)
        self.balanceChecks = {}
        # Store the admin password
        self.password = password
        # Store the admin user
        self.admins = admins
        # Semphore to lock the balance check list.
        self.balanceSem = threading.Lock()
        # Reward aberration
        self.aberration = 0

    def runClient(self):

        loop = asyncio.get_event_loop()

        while True:

            try:
                loop.run_until_complete(self.client.start(self.token))
            except KeyboardInterrupt:
                logger.warning("Terminate!")
                self.stop()
                return
            except Exception as e:
                logger.error("Bot crashed?! ", e)

            self.rewardList.pause()

            asyncio.run_coroutine_threadsafe(self.client.close(), loop=loop)

            time.sleep(10)

            self.client = discord.Client()

            self.client.on_ready = self.on_ready
            self.client.on_message = self.on_message

    ######
    # Starts the bot and block until the programm gets stopped.
    ######
    def start(self):
        logger.info("Start!")
        self.runClient()

    def stop(self):

        asyncio.run_coroutine_threadsafe(self.client.close(),
                                         loop=self.client.loop)

        self.rewardList.stop()
        self.nodeList.stop()

    ######
    # Send a message :text to a specific user :user
    ######
    async def sendMessage(self, user, text, split='\n'):

        logger.info("sendMessage - Chat: {}, Text: {}".format(user, text))

        parts = messages.splitMessage(text, split, 2000)

        try:
            for part in parts:
                await self.client.send_message(user, part)
        except discord.errors.Forbidden:
            logging.error('sendMessage user blocked the bot')

            # Remove the user and the assigned nodes.
            self.database.deleteNodesForUser(user.id)
            self.database.deleteUser(user.id)

        except discord.errors.HTTPException as e:
            logging.error('HTTPException', exc_info=e)
        except Exception as e:
            logging.error('sendMessage', exc_info=e)
        else:
            logger.info("sendMessage - OK!")

    async def on_ready(self):

        logger.info('Logged in as')
        logger.info(self.client.user.name)
        logger.info(self.client.user.id)
        logger.info('------')

        if not self.running:
            self.running = True

            # Advise the admin about the start.
            self.adminCB("**Bot started**")

            with self.nodeList as nodeList:

                await self.client.change_presence(game=discord.Game(
                    name='our {} SmartNodes'.format(nodeList.count()), type=3))

                # # Update the sources where the blocks are assigned to the nodelist
                # for node in nodeList.nodes.values():
                #
                #     if node.lastPaidBlock <= 0:
                #         continue
                #
                #     reward = self.rewardList.getReward(node.lastPaidBlock)
                #
                #     if not reward:
                #
                #         reward = SNReward(block=node.lastPaidBlock,
                #                           payee = node.payee,
                #                           txtime=node.lastPaidTime,
                #                           source=1)
                #
                #         self.rewardList.addReward(reward)
                #         continue
                #
                #
                #     if reward.source == 1:
                #         continue
                #
                #     reward = SNReward(block=node.lastPaidBlock,
                #                       payee = node.payee,
                #                       txtime=node.lastPaidTime,
                #                       source=1)
                #
                #     self.rewardList.updateSource(reward)

                # start = int(time.time() - 43200) # 12 hours of collecting
                # total = self.rewardList.getRewardCount(start = start)
                # nList = self.rewardList.getRewardCount(start = start, source=1)
                # if nList and total:
                #     self.aberration = 1 - ( nList / total)

                # Start the rewardlist updates
                self.rewardList.start()
                # Start the nodelist updates
                nodeList.start()

        else:
            logger.info("Reconnect")
            # Advise the admin about the reconnect
            self.adminCB("**Bot reconnected**")

            self.rewardList.resume()

    async def on_resumed(self):
        logger.info("Resumed")

    ######
    # Discord api coroutine which gets called when a new message has been
    # received in one of the channels or in a private chat with the bot.
    ######
    async def on_message(self, message):

        if message.author == self.client.user:
            # Just jump out if its the bots message.
            return

        # split the new messages by spaces
        parts = message.content.split()

        command = None
        args = None

        # If the first mention in the message is the bot itself
        # and there is a possible command in the message
        if len(message.mentions) == 1 and message.mentions[0] == self.client.user\
            and len(parts) > 1:
            command = parts[1]
            args = parts[2:]
        # If there are multiple mentions send each one (excluded the bot itself)
        # the help message.
        # Like: hey @dustinface and @whoever check out the @SmartNodeMonitorBot
        # The above would send @dustinface and @whoever the help message of the bot.
        elif len(
                message.mentions) > 1 and self.client.user in message.mentions:

            for mention in message.mentions:
                if not mention == self.client.user:

                    # Check if the user is already in the databse
                    result = common.checkUser(self, mention)

                    if result['response']:
                        await self.sendMessage(mention, result['response'])

                    if result['added']:
                        continue

                    await self.sendMessage(mention,
                                           messages.help(self.messenger))

            return
        # If there are no mentions and we are in a private chat
        elif len(message.mentions) == 0 and not isinstance(
                message.author, discord.Member):
            command = parts[0]
            args = parts[1:]
        # If we got mentioned but no command is available in the message just send the help
        elif len(message.mentions) and message.mentions[0] == self.client.user and\
              len(parts) == 1:
            command = 'help'
        # No message of which the bot needs to know about.
        else:
            logger.debug("on_message - jump out {}".format(self.client.user))
            return

        # If we got here call the command handler to see if there is any action required now.
        await self.commandHandler(message, command.lower(), args)

    ######
    # Handles incomming splitted messages. Check if there are commands which require
    # any action. If so it calls the related methods and sends the response to
    # the author of the command message.
    ######
    async def commandHandler(self, message, command, args):

        logger.info("commandHandler - {}, command: {}, args: {}".format(
            message.author, command, args))

        # Check if the user is already in the databse
        result = common.checkUser(self, message)

        if result['response']:
            await self.sendMessage(message.author, result['response'])

        if result['added'] and not isinstance(message.author, discord.Member):
            return

        # per default assume the message gets back from where it came
        receiver = message.author

        ####
        # List of available commands
        # Public = 0
        # DM-Only = 1
        # Admin only = 2
        ####
        commands = {
            # Common commands
            'help': 0,
            'info': 0,
            'faq': 0,
            # User commmands
            'me': 1,
            'status': 1,
            'reward': 1,
            'network': 1,  #'timeout':1,
            # Node commands
            'add': 1,
            'update': 1,
            'remove': 1,
            'nodes': 1,
            'detail': 1,
            'history': 1,
            'balance': 1,
            'lookup': 1,
            'top': 1,
            # Admin commands
            'stats': 2,
            'broadcast': 2,
            'payouts': 2
        }

        choices = fuzzy.extract(command, commands.keys(), limit=2)

        if choices[0][1] == choices[1][1] or choices[0][1] < 60:
            logger.debug('Invalid fuzzy result {}'.format(choices))
            command = 'unknown'
        else:
            command = choices[0][0]

        # If the command is DM only
        if command in commands and commands[command] == 1:

            if isinstance(message.author, discord.Member):
                await self.client.send_message(message.channel,\
                message.author.mention + ', the command `{}` is only available in private chat with me!'.format(command))
                await self.client.send_message(
                    message.author,
                    messages.markdown('<b>Try it here!<b>\n', self.messenger))
                return

        else:
            receiver = message.channel

        # If the command is admin only
        if command in commands and commands[command] == 2:

            # Admin command got fired in a public chat
            if isinstance(message.author, discord.Member):
                # Just send the unknown command message and jump out
                await self.sendMessage(receiver,
                                       (message.author.mention + ", " +
                                        commandhandler.unknown(self)))
                logger.info("Admin only, public")
                return

            def tryAdmin(message, args):

                if message.author.id in self.admins:

                    if self.password:
                        if len(args) >= 1 and args[0] == self.password:
                            return True
                    else:
                        return True

                return False

            # Admin command got fired from an unauthorized user
            if tryAdmin(message, args):
                receiver = message.author
            else:
                logger.info("Admin only, other")
                # Just send the unknown command message and jump out
                await self.sendMessage(
                    receiver,
                    (message.author.mention + ", " + common.unknown(self)))
                return

        ### Common command handler ###
        if command == 'info':
            response = common.info(self, message)
            await self.sendMessage(receiver, response)
        elif command == 'faq':
            response = faq.parse(self, args)
            await self.sendMessage(receiver, response)
        ### Node command handler ###
        elif command == 'add':
            response = node.nodeAdd(self, message, args)
            await self.sendMessage(receiver, response)
        elif command == 'update':
            response = node.nodeUpdate(self, message, args)
            await self.sendMessage(receiver, response)
        elif command == 'remove':
            response = node.nodeRemove(self, message, args)
            await self.sendMessage(receiver, response)
        elif command == 'nodes':
            response = node.nodes(self, message)
            await self.sendMessage(receiver, response)
        elif command == 'detail':
            response = node.detail(self, message)
            await self.sendMessage(receiver, response)
        elif command == 'history':
            response = node.history(self, message)
            await self.sendMessage(receiver, response)
        elif command == 'top':
            response = node.top(self, message, args)
            await self.sendMessage(receiver, response)
        elif command == 'balance':

            failed = None
            nodes = []

            dbUser = self.database.getUser(message.author.id)
            userNodes = self.database.getAllNodes(message.author.id)

            # If there is no nodes added yet send an error and return
            if dbUser == None or userNodes == None or len(userNodes) == 0:

                response = messages.markdown("<u><b>Balances<b><u>\n\n",
                                             self.messenger)
                response += messages.nodesRequired(self.messenger)

                await self.sendMessage(message.author, response)
                return

            collaterals = list(map(lambda x: x['collateral'], userNodes))
            nodes = self.nodeList.getNodes(collaterals)
            check = self.explorer.balances(nodes)

            # Needed cause the balanceChecks dict also gets modified from other
            # threads.
            self.balanceSem.acquire()

            if check:
                self.balanceChecks[check] = message.author.id
            else:
                logger.info("Balance check failed instant.")
                failed = uuid.uuid4()
                self.balanceChecks[failed] = message.author.id

            # Needed cause the balanceChecks dict also gets modified from other
            # threads.
            self.balanceSem.release()

            if failed:
                self.balancesCB(failed, None)

        elif command == 'lookup':
            response = messages.markdown(node.lookup(self, message, args),
                                         self.messenger)
            await self.sendMessage(receiver, response)
        ### User command handler ###
        elif command == 'me':
            response = user.me(self, message)
            await self.sendMessage(receiver, response)
        elif command == 'status':
            response = user.status(self, message, args)
            await self.sendMessage(receiver, response)
        elif command == 'reward':
            response = user.reward(self, message, args)
            await self.sendMessage(receiver, response)
        elif command == 'timeout':
            response = user.timeout(self, message, args)
            await self.sendMessage(receiver, response)
        elif command == 'network':
            response = user.network(self, message, args)
            await self.sendMessage(receiver, response)

        ### Admin command handler ###
        elif command == 'stats':
            response = common.stats(self)
            await self.sendMessage(receiver, response)
        elif command == 'payouts':
            response = common.payouts(self, args[1:])
            await self.sendMessage(receiver, response)
        elif command == 'broadcast':

            response = " ".join(args[1:])

            for dbUser in self.database.getUsers():

                member = self.findMember(dbUser['id'])

                if member:
                    await self.sendMessage(member, response)

        # Help message
        elif command == 'help':
            await self.sendMessage(receiver, messages.help(self.messenger))

        # Could not match any command. Send the unknwon command message.
        else:
            await self.sendMessage(
                receiver,
                (message.author.mention + ", " + common.unknown(self)))

    ######
    # Unfortunately there is no better way to send messages to a user if you have
    # only their userId. Therefor this method searched the discord user object
    # in the global member list and returns is.
    ######
    def findMember(self, userId):

        for member in self.client.get_all_members():
            if int(member.id) == int(userId):
                return member

        logger.info(
            "Could not find the userId in the list?! {}".format(userId))

        return None

    ############################################################
    #                        Callbacks                         #
    ############################################################

    ######
    # Callback which get called when there is a new releases in the smartcash repo.
    #
    # Called by: Nothing yet, SmartGitHubUpdates later.
    #
    ######
    def updateCheckCallback(self, tag):

        for user in self.database.getUsers():
            self.sendMessage(
                user['id'],
                ("*Node update available*\n\n"
                 "https://github.com/SmartCash/smartcash/releases/tag/{}"
                 ).format(tag))

    ######
    # Callback for evaluating if someone in the database had an upcomming event
    # and send messages to all chats with activated notifications
    #
    # Called by: SmartNodeList
    #
    ######
    def nodeUpdateCB(self, update, n):

        responses = node.handleNodeUpdate(self, update, n)

        for userId, messages in responses.items():

            member = self.findMember(userId)

            if member:

                for message in messages:
                    asyncio.run_coroutine_threadsafe(self.sendMessage(
                        member, message),
                                                     loop=self.client.loop)

    ######
    # Callback for evaluating if someone in the database has won the reward
    # and send messages to all chats with activated notifications
    #
    # Called by: SNRewardList from python-smartcash
    #
    ######
    def rewardCB(self, reward, distance):

        responses = node.handleReward(self, reward, distance)

        for userId, messages in responses.items():

            member = self.findMember(userId)

            if member:

                for message in messages:
                    asyncio.run_coroutine_threadsafe(self.sendMessage(
                        member, message),
                                                     loop=self.client.loop)

        # start = int(time.time() - 43200) # 12 hours of collecting
        # total = self.rewardList.getRewardCount(start = start)
        # nList = self.rewardList.getRewardCount(start = start, source=1)
        # if nList and total:
        #     self.aberration = 1 - ( nList / total)

    ######
    # Callback for SNRewardList errors
    #
    # Called by: SNRewardList from python-smartcash
    #
    ######
    def rewardListErrorCB(self, error):
        self.adminCB(str(error))

    #####
    # Callback for evaluating if someone has enabled network notifications
    # and send messages to all relevant chats
    #
    # Called by: SmartNodeList
    #
    ######
    def networkCB(self, collaterals, added):

        nodeCount = self.nodeList.count()
        asyncio.run_coroutine_threadsafe(
            self.client.change_presence(game=discord.Game(
                name='our {} SmartNodes'.format(nodeCount), type=3)),
            loop=self.client.loop)

        response = common.networkUpdate(self, collaterals, added)

        # Handle the network update notifications.
        for dbUser in self.database.getUsers('where network_n=1'):

            member = self.findMember(dbUser['id'])

            if member:
                asyncio.run_coroutine_threadsafe(self.sendMessage(
                    member, response),
                                                 loop=self.client.loop)

        if added:
            # If the callback is related to new nodes no need for
            # the continue here.
            return

        # Remove the nodes also from the user database
        for collateral in collaterals:

            # Before chec if a node from anyone got removed and let him know about it.
            for userNode in self.database.getNodes(collateral):

                member = self.findMember(userNode['user_id'])

                if member:
                    response = messages.nodeRemovedNotification(
                        self.messenger, userNode['name'])
                    asyncio.run_coroutine_threadsafe(self.sendMessage(
                        member, response),
                                                     loop=self.client.loop)

            # Remove all entries containing this node in the db
            self.database.deleteNodesWithId(collateral)

    ######
    # Callback which gets called from the SmartNodeList when a balance request triggered by any user
    # is done. It sends the result to the related user.
    #
    # Called by: SmartExplorer
    #
    ######
    def balancesCB(self, check, results):

        # Needed cause the balanceChecks dict also gets modified from other
        # threads.
        self.balanceSem.acquire()

        if not check in self.balanceChecks:
            logger.error("Ivalid balance check received {} - count {}".format(
                check, len(results)))
            self.balanceSem.release()
            return

        userId = self.balanceChecks[check]
        self.balanceChecks.pop(check)

        # Needed cause the balanceChecks dict also gets modified from other
        # threads.
        self.balanceSem.release()

        response = node.balances(self, userId, results)

        member = self.findMember(userId)

        if member:
            asyncio.run_coroutine_threadsafe(self.sendMessage(
                member, response),
                                             loop=self.client.loop)

    ######
    # Push the message to the admin
    #
    # Called by: SmartNodeList
    #
    ######
    def adminCB(self, message):

        if not len(self.admins):
            return

        admin = self.findMember(self.admins[0])

        if admin:
            asyncio.run_coroutine_threadsafe(self.sendMessage(admin, message),
                                             loop=self.client.loop)
        else:
            logger.warning("adminCB - Could not find admin.")