iranzo/stampython

View on GitHub
stampy/stampy.py

Summary

Maintainability
F
6 days
Test Coverage
#!/usr/bin/env python
# encoding: utf-8
#
# Description: Bot for controlling karma on Telegram
# Author: Pablo Iranzo Gomez (Pablo.Iranzo@gmail.com)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
# GNU General Public License for more details.

import argparse
import datetime
import json
import logging
import random
import sqlite3 as lite
import string
import sys
import time
import traceback
import urllib
from time import sleep

import dateutil.parser
import pytz
import requests

import plugin.config
import plugin.forward
import plugins
from i18n import _
from i18n import _L
from i18n import chlang

logger = logging.getLogger("stampy")
logger.setLevel(logging.DEBUG)

plugs = []
plugtriggers = {}

global language
global loglanguage
loglanguage = 'en'
language = 'en'

description = _('Stampy is a script for controlling Karma via Telegram.org bot api')

# Option parsing
p = argparse.ArgumentParser("stampy.py [arguments]", description=description)
p.add_argument("-t", "--token", dest="token",
               help=_("API token for bot access to messages"), default=False)
p.add_argument("-b", "--database", dest="database",
               help=_("database file for storing karma"),
               default="stampy.db")
p.add_argument('-v', "--verbosity", dest="verbosity",
               help=_("Set verbosity level for messages while running/logging"),
               default=0, choices=["info", "debug", "warn", "critical"])
p.add_argument('-u', "--url", dest="url",
               help=_("Define URL for accessing bot API"),
               default="https://api.telegram.org/bot")
p.add_argument('-o', '--owner', dest='owner',
               help=_("Define owner username"),
               default="iranzo")
p.add_argument('-d', '--daemon', dest='daemon', help=_("Run as daemon"),
               default=False, action="store_true")

options, unknown = p.parse_known_args()


# Implement switch from http://code.activestate.com/recipes/410692/
class Switch(object):
    """
    Defines a class that can be used easily as traditional switch commands
    """

    def __init__(self, value):
        self.value = value
        self.fall = False

    def __iter__(self):
        """Return the match method once, then stop"""
        yield self.match
        raise StopIteration

    def match(self, *args):
        """Indicate whether or not to enter a case suite"""
        if self.fall or not args:
            return True
        elif self.value in args:  # changed for v1.5, see below
            self.fall = True
            return True
        else:
            return False


def createorupdatedb():
    """
    Create database if it doesn't exist or upgrade it to head
    :return:
    """

    logger = logging.getLogger(__name__)

    import alembic.config
    alembicArgs = [
        '-x', 'database=%s' % options.database, '--raiseerr',
        'upgrade', 'head',
    ]

    logger.debug(msg=_L("Using alembic to upgrade/create database to expected revision"))

    alembic.config.main(argv=alembicArgs)

    return


# Function definition
def dbsql(sql=False):
    """
    Performs SQL operation on database
    :param sql: sql command to execute
    :return:
    """
    logger = logging.getLogger(__name__)

    # Initialize database access
    con = False
    try:
        con = lite.connect(options.database, timeout=60)
        cur = con.cursor()
        cur.execute("SELECT key,value FROM config WHERE key='token';")
        cur.fetchone()

    except lite.Error, e:
        logger.debug(msg="Error %s:" % e.args[0])
        print _("Error accessing database, creating...")
        createorupdatedb()
        con = lite.connect(options.database, timeout=1)
        cur = con.cursor()

    # Database initialized

    worked = False
    attempt = 0
    while attempt < 60:
        if sql:
            attempt = attempt + 1
            try:
                cur.execute(sql)
                con.commit()
                worked = True
                attempt = 60
            except:
                exc_info = sys.exc_info()
                traceback.print_exception(*exc_info)
                worked = False
                sleep(random.randint(0, 10))
        else:
            attempt = 60

        if not worked:
            logger.critical(msg=_L("Error # %s on SQL execution: %s") % (attempt, sql))

    return cur


def sendmessage(chat_id=0, text="", reply_to_message_id=False,
                disable_web_page_preview=True, parse_mode=False,
                extra=False):
    """
    Sends a message to a chat
    :param chat_id: chat_id to receive the message
    :param text: message text
    :param reply_to_message_id: message_id to reply
    :param disable_web_page_preview: do not expand links to include preview
    :param parse_mode: use specific format (markdown, html)
    :param extra: extra parameters to send
                 (for future functions like keyboard_markup)
    :return:
    """

    logger = logging.getLogger(__name__)
    url = "%s%s/sendMessage" % (plugin.config.config(key="url"),
                                plugin.config.config(key='token'))
    lines = text.split("\n")
    maxlines = 15
    if len(lines) > maxlines:
        # message might be too big for single message (max 4K)
        if "```" in text:
            markdown = True
        else:
            markdown = False

        texto = string.join(lines[0:maxlines], "\n")
        if markdown:
            texto = "%s```" % texto

        # Send first batch
        sendmessage(chat_id=chat_id, text=texto,
                    reply_to_message_id=reply_to_message_id,
                    disable_web_page_preview=disable_web_page_preview,
                    parse_mode=parse_mode, extra=extra)
        # Send remaining batch
        texto = string.join(lines[maxlines:], "\n")
        if markdown:
            texto = "```%s" % texto
        sendmessage(chat_id=chat_id, text=texto, reply_to_message_id=False,
                    disable_web_page_preview=disable_web_page_preview,
                    parse_mode=parse_mode, extra=extra)
        return

    overridegid = plugin.config.config(key='overridegid', gid=0,
                                       default=False)
    if overridegid:
        chat_id = overridegid

    message = "%s?chat_id=%s&text=%s" % (
              url, chat_id, urllib.quote_plus(text.encode('utf-8')))
    if reply_to_message_id:
        message += "&reply_to_message_id=%s" % reply_to_message_id
    if disable_web_page_preview:
        message += "&disable_web_page_preview=1"
    else:
        message += "&disable_web_page_preview=0"
    if parse_mode:
        message += "&parse_mode=%s" % parse_mode
    if extra:
        message += "&%s" % extra

    code = False
    attempt = 0
    while not code:
        # It this is executed as per unit testing, skip sending message
        UTdisable = not plugin.config.config(key='unittest', default=False)
        Silent = not plugin.config.gconfig(key='silent', default=False, gid=geteffectivegid(gid=chat_id))
        if UTdisable and Silent:
            result = json.load(urllib.urlopen(message))
            code = result['ok']
        else:
            code = True
            result = ""

        if attempt > 0:
            logger.error(msg=_L("ERROR (%s) sending message: Code: %s : Text: %s") % (attempt, code, result))

        attempt += 1
        sleep(1)
        if not code:
            error = result['description']
            if 'entity starting at byte offset' in error:
                # Trim the byte offset to make this error more generic
                error = error[:103]

            for case in Switch(error):
                if case(u"Bad Request: message text is empty"):
                    # Message is empty, no need to resend
                    attempt = 61
                    break

                if case(u"Forbidden: bot can't initiate conversation with a user"):
                    # Bot hasn't been authorized by user, cancelling
                    attempt = 61
                    break

                if case(u"Bad Request: reply message not found"):
                    # Original reply has been deleted
                    attempt = 61
                    break

                if case(u"Forbidden: bot was blocked by the user"):
                    # User blocked the bot
                    attempt = 61
                    break

                if case(u"Bad Request: can't parse entities in message text: Can't find end of the entity starting at byte offset"):
                    attempt = 61
                    break

                if case(u'Forbidden: bot was kicked from the supergroup chat'):
                    attempt = 61
                    break

                if case(u'Bad Request: chat not found'):
                    attempt = 61
                    break

                if case(u'Bad Request: reply message not found'):
                    attempt = 61
                    break

        # exit after 60 retries with 1 second delay each
        if attempt > 60:
            logger.error(msg=_L("PERM ERROR sending message: Code: %s : Text: %s") % (code, result))
            code = True

    try:
        sent = {"message": result['result']}
    except:
        sent = False

    if sent:
        # Check if there's something to forward and do it
        plugin.forward.forwardmessage(sent)

    logger.debug(msg=_L("Sending message: Code: %s : Text: %s") % (code, text))
    return code


def deletemessage(chat_id=0, message_id=False):
    """
    Deletes a message from a chat
    :param chat_id: chat_id to delete the message
    :param message_id: message to delete
    :return:
    """

    logger = logging.getLogger(__name__)
    url = "%s%s/deleteMessage" % (plugin.config.config(key="url"), plugin.config.config(key='token'))
    message = "%s?chat_id=%s&message_id=%s" % (url, chat_id, message_id)

    code = False
    attempt = 0
    while not code:
        # It this is executed as per unit testing, skip deleting message
        UTdisable = not plugin.config.config(key='unittest', default=False)
        Silent = not plugin.config.gconfig(key='silent', default=False, gid=geteffectivegid(gid=chat_id))
        if UTdisable and Silent:
            result = json.load(urllib.urlopen(message))
            code = result['ok']
        else:
            code = True
            result = ""

        if attempt > 0:
            logger.error(msg=_L("ERROR (%s) deleting message: Code: %s : Text: %s") % (attempt, code, result))

        attempt += 1
        sleep(1)
        if not code:
            error = result['description']
            if 'entity starting at byte offset' in error:
                # Trim the byte offset to make this error more generic
                error = error[:103]

            for case in Switch(error):
                if case(u"Bad Request: message can't be deleted"):
                    # Message can't be deleted
                    attempt = 61
                    break
                if case(u'Bad Request: message to delete not found'):
                    # Message was already removed
                    attempt = 61
                    break

        # exit after 60 retries with 1 second delay each
        if attempt > 60:
            logger.error(msg=_L("PERM ERROR deleting message: Code: %s : Text: %s") % (code, result))
            code = True

    logger.debug(msg=_L("Deleted message: Code: %s : Text: %s") % (code, message_id))
    return code


def getupdates(offset=0, limit=100):
    """
    Gets updates (new messages from server)
    :param offset: last update id
    :param limit: maximum number of messages to gather
    :return: returns the items obtained
    """

    logger = logging.getLogger(__name__)
    url = "%s%s/getUpdates" % (plugin.config.config(key='url'),
                               plugin.config.config(key='token'))
    message = "%s?" % url
    if offset != 0:
        message += "offset=%s&" % offset
    message += "limit=%s" % limit
    try:
        result = json.load(urllib.urlopen(message))['result']
    except:
        result = []
    for item in result:
        logger.info(msg=_L("Getting updates and returning: %s") % item)
        yield item


def getme():
    """
    Gets bot user
    :return: returns the items obtained
    """

    logger = logging.getLogger(__name__)

    if not plugin.config.config(key='myself', default=False):

        url = "%s%s/getMe" % (plugin.config.config(key='url'),
                              plugin.config.config(key='token'))
        message = "%s" % url
        try:
            result = json.load(urllib.urlopen(message))['result']['username']
        except:
            result = 'stampy'

        plugin.config.setconfig(key='myself', value=result)

    else:
        result = plugin.config.config(key='myself')

    logger.info(msg=_L("Getting bot details and returning: %s") % result)
    return result


def clearupdates(offset):
    """
    Marks updates as already processed so they are removed by API
    :param offset:
    :return:
    """

    logger = logging.getLogger(__name__)
    url = "%s%s/getUpdates" % (plugin.config.config(key='url'), plugin.config.config(key='token'))
    message = "%s?" % url
    message += "offset=%s&" % offset
    try:
        result = json.load(urllib.urlopen(message))
    except:
        result = False
    logger.info(msg=_L("Clearing messages at %s") % offset)
    return result


def sendsticker(chat_id=0, sticker="", text="", reply_to_message_id=""):
    """
    Sends a sticker to chat_id as a reply to a message received
    :param chat_id: ID of the chat
    :param sticker: Sticker identification
    :param text: Additional text
    :param reply_to_message_id:
    :return:
    """

    logger = logging.getLogger(__name__)
    url = "%s%s/sendSticker" % (plugin.config.config(key='url'), plugin.config.config(key='token'))
    message = "%s?chat_id=%s" % (url, chat_id)
    message = "%s&sticker=%s" % (message, sticker)
    if reply_to_message_id:
        message += "&reply_to_message_id=%s" % reply_to_message_id
    logger.debug(msg=_L("Sending sticker: %s") % text)

    # It this is executed as per unit testing, skip sending message
    UTdisable = not plugin.config.config(key='unittest', default=False)
    Silent = not plugin.config.gconfig(key='silent', default=False, gid=geteffectivegid(gid=chat_id))
    if UTdisable and Silent:
        sent = {"message": json.load(urllib.urlopen(message))['result']}
    else:
        sent = False

    # Check if there's something to forward and do it
    plugin.forward.forwardmessage(sent)

    return


def sendimage(chat_id=0, image="", text="", reply_to_message_id=""):
    """
    Sends an image to chat_id as a reply to a message received
    :param chat_id: ID of the chat
    :param image: image URI
    :param text: Additional text or caption
    :param reply_to_message_id:
    :return:
    """

    logger = logging.getLogger(__name__)

    if not image:
        return False

    url = "%s%s/sendPhoto" % (plugin.config.config(key='url'), plugin.config.config(key='token'))
    payload = {'chat_id': chat_id}

    if reply_to_message_id:
        payload['reply_to_message_id'] = reply_to_message_id
    if text:
        payload['caption'] = text.encode('utf-8')

    logger.debug(msg=_L("Sending image: %s") % text)

    # Download image first to later send it
    rawimage = requests.get(image, stream=True)
    sent = False

    if rawimage.status_code == 200:
        rawimage.raw.decode_content = True

        # Send image
        files = {'photo': rawimage.raw}

        # It this is executed as per unit testing, skip sending message
        UTdisable = not plugin.config.config(key='unittest', default=False)
        Silent = not plugin.config.gconfig(key='silent', default=False, gid=geteffectivegid(gid=chat_id))

        if UTdisable and Silent:
            output = requests.post(url, files=files, data=payload)
            sent = {"message": json.loads(output.text)['result']}
        else:
            sent = False
            logger.debug(msg=_L("Failure sending image: %s") % image)
    else:
        logger.debug(msg=_L("Failure downloading image: %s") % image)

    # Check if there's something to forward and do it
    plugin.forward.forwardmessage(sent)

    return sent


def replace_all(text, dictionary):
    """
    Replaces text with the dict
    :param text: Text to process
    :param dictionary:  The dictionary of replacements
    :return: the modified text
    """

    for i, j in dictionary.iteritems():
        text = text.replace(i, j)
    return text


def getmsgdetail(message):
    """
    Gets message details and returns them as dict
    :param message: message to get details from
    :return: message details as dict
    """

    dictionary = {
        "'": ""
    }

    try:
        update_id = message['update_id']
    except:
        update_id = ""

    type = ""

    try:
        # Regular message
        chat_id = message['message']['chat']['id']
        type = "message"
    except:
        try:
            # Message in a channel
            chat_id = message['channel_post']['chat']['id']
            type = "channel_post"
        except:
            try:
                chat_id = message['edited_message']['chat']['id']
                type = 'edited_message'
            except:
                chat_id = ""

    # Define dictionary for text replacements

    try:
        chat_type = message[type]['chat']['type']
    except:
        chat_type = ""

    try:
        chat_name = replace_all(message[type]['chat']['title'], dictionary)
    except:
        chat_name = ""

    try:
        text = message[type]['text']
    except:
        text = ""

    try:
        replyto = message[type]['reply_to_message']['from']['username']
    except:
        replyto = False

    if replyto:
        try:
            replytotext = message[type]['reply_to_message']['text']
        except:
            replytotext = False
    else:
        replytotext = False

    try:
        message_id = int(message[type]['message_id'])

    except:
        message_id = ""

    try:
        date = int(float(message[type]['date']))
        datefor = datetime.datetime.fromtimestamp(float(date)).strftime('%Y-%m-%d %H:%M:%S')
    except:
        date = ""
        datefor = ""

    try:
        who_gn = replace_all(message[type]['from']['first_name'], dictionary)
        who_id = message[type]['from']['id']
        error = False
    except:
        error = True
        who_id = ""
        who_gn = ""

    try:
        who_ln = replace_all(message[type]['from']['last_name'], dictionary)
    except:
        who_ln = ""

    # Some user might not have username defined so this
    # was failing and message was ignored
    try:
        who_un = message[type]['from']['username']
    except:
        who_un = ""

    name = "%s %s (@%s)" % (who_gn, who_ln, who_un)
    while "  " in name:
        name = name.replace("  ", " ")

    # args = ('name', 'chat_id', 'chat_name', 'date', 'datefor', 'error', 'message_id',
    #         'text', 'update_id', 'who_gn', 'who_id', 'who_ln', 'who_un')
    # vals = dict((k, v) for (k, v) in locals().iteritems() if k in args)

    vals = {"name": name, "chat_id": chat_id, "chat_name": chat_name, "date": date, "datefor": datefor, "error": error,
            "message_id": message_id, "text": text, "update_id": update_id, "who_gn": who_gn, "who_id": who_id,
            "who_ln": who_ln, "who_un": who_un, "type": type, "replyto": replyto, "replytotext": replytotext, "chat_type": chat_type}

    return vals


def process(messages):
    """
    This function processes the updates in the Updates URL at Telegram
    for finding commands, karma changes, config, etc
    """

    logger = logging.getLogger(__name__)

    # check if Log level has changed
    loglevel()

    # Main code for processing the karma updates
    date = 0
    lastupdateid = 0
    count = 0

    # Process each message available in URL and search for karma operators
    for message in messages:
        # Count messages in each batch
        count += 1

        # Forward message if defined
        plugin.forward.forwardmessage(message)

        # Call plugins to process message
        global plugs
        global plugtriggers

        msgdetail = getmsgdetail(message)
        botname = getme()

        # Write the line for debug
        messageline = _L("TEXT: %s : %s : %s") % (msgdetail["chat_name"], msgdetail["name"], msgdetail["text"])
        logger.debug(msg=messageline)

        # Process group configuration for language
        chat_id = msgdetail['chat_id']
        chlang(lang=plugin.config.gconfig(key='language', gid=chat_id))

        try:
            command = msgdetail["text"].split()[0].lower().replace('@%s' % botname, '')
            texto = msgdetail["text"].lower()
            date = msgdetail["datefor"]
        except:
            command = ""
            texto = ""
            date = 0

        for i in plugs:
            name = i.__name__.split(".")[-1]

            runplugin = False
            for trigger in plugtriggers[name]:
                if "*" in trigger:
                    runplugin = True
                    break
                elif trigger[0] == "^":
                    if command == trigger[1:]:
                        runplugin = True
                        break
                elif trigger in texto:
                    runplugin = True
                    break

            code = False
            if runplugin:
                logger.debug(msg=_L("Processing plugin: %s") % name)
                code = i.run(message=message)

            if code:
                # Plugin has changed triggers, reload
                plugtriggers[name] = i.init()
                logger.debug(msg=_L("New triggers for %s: %s") % (name, plugtriggers[name]))

        # Update last message id to later clear it from the server
        if msgdetail["update_id"] > lastupdateid:
            lastupdateid = msgdetail["update_id"]

    if date != 0:
        logger.info(msg=_L("Last processed message at: %s") % date)
    if lastupdateid != 0:
        logger.debug(msg=_L("Last processed update_id : %s") % lastupdateid)
    if count != 0:
        logger.info(msg=_L("Number of messages in this batch: %s") % count)

    # clear updates (marking messages as read)
    if lastupdateid != 0:
        clearupdates(offset=lastupdateid + 1)


def utize(date):
    """
    Converts date to UTC tz
    :param date: date to convert
    :return:
    """

    tz = pytz.timezone('GMT')

    try:
        code = date.astimezone(tz)

    except:
        try:
            code = date.replace(tzinfo=tz)
        except:
            code = date

    return code


def processcron():
    """
    This function processes plugins with cron features
    """

    logger = logging.getLogger(__name__)

    # Call plugins to process message
    global plugs
    global plugtriggers

    for i in plugs:
        name = i.__name__.split(".")[-1]
        if shouldrun(name=name):
            logger.debug(msg=_L("Processing plugin cron: %s") % name)
            i.cron()


def shouldrun(name):
    """
    Checks name on database to see if it should run or not and updates as executed
    :param name: Name to check on database
    :return: Bool
    """

    sql = "SELECT name,lastchecked,interval from cron where name='%s'" % name
    cur = dbsql(sql)

    date = utize(datetime.datetime.now())

    # Formatted date to write back in database
    datefor = date.strftime('%Y/%m/%d %H:%M:%S')

    # Convert to epoch to properly compare
    dateforts = time.mktime(date.timetuple())

    code = False

    for row in cur:
        (name, lastchecked, interval) = row

        try:
            # Parse date or if in error, use past
            datelast = utize(dateutil.parser.parse(lastchecked))

        except:
            datelast = utize(datetime.datetime(year=1981, month=1, day=24))

        # Get time since last check on the feed (epoch)
        datelastts = time.mktime(datelast.timetuple())
        timediff = int((dateforts - datelastts) / 60)

        # Check if interval is defined or set default
        interval = int(interval)

        if interval == 0:
            interval = 1440

        # If more time has passed since last check than the interval for
        # checks, run the check

        if timediff < interval:
            code = False
        else:
            code = True

    # Update db with results
    if code:
        sql = "UPDATE cron SET lastchecked='%s' where name='%s'" % (datefor, name)
        logger.debug(msg=_L("Updating last checked as per %s") % sql)
        dbsql(sql=sql)
    return code


def cronme(name=False, interval=5):
    """
    Adds an entry in cron database for job name and interval in mins
    :param name: name of job
    :param interval: mins between executions
    :return:
    """

    if name:
        sql = "DELETE from cron WHERE name='%s'" % name
        dbsql(sql=sql)
        sql = "INSERT INTO cron(name,interval) VALUES('%s', '%s')" % (name, interval)
        dbsql(sql=sql)


def getitems(var):
    """
    Returns list of items even if provided args are lists of lists
    :param var: list or value to pass
    :return: unique list of values
    """

    logger = logging.getLogger(__name__)

    result = []
    if not isinstance(var, list):
        result.append(var)
    else:
        for elem in var:
            result.extend(getitems(elem))

    # Do cleanup of duplicates
    final = []
    for elem in result:
        if elem not in final:
            final.append(elem)

    # As we call recursively, don't log calls for just one ID
    if len(final) > 1:
        logger.debug(msg=_L("Final deduplicated list: %s") % final)
    return final


def is_owner(message):
    """
    Check if user of message is owner
    :param message: message to check
    :return:  True on owner
    """

    logger = logging.getLogger(__name__)
    code = False
    msgdetail = getmsgdetail(message)
    for each in plugin.config.config(key='owner').split(" "):
        if each == msgdetail["who_un"]:
            code = True
    return code


def is_owner_or_admin(message, strict=False):
    """
    Check if user is owner or admin for group
    :param strict: Defines if we target the actual gid, not effective
    :param message: message to check
    :return: True on owner or admin
    """

    logger = logging.getLogger(__name__)
    admin = False
    owner = False
    msgdetail = getmsgdetail(message)
    if strict:
        chat_id = msgdetail["chat_id"]
    else:
        chat_id = geteffectivegid(msgdetail["chat_id"])

    # if we're on a user private chat, return admin true
    if chat_id > 0:
        admin = True
        logger.debug(msg=_L("We're admin of private chats"))
    else:
        # Check if we're owner
        owner = is_owner(message)
        if not owner:
            logger.debug(msg=_L("We're not owner of public chats"))
            # Check if we are admin of chat
            for each in plugin.config.config(key='admin', default="", gid=chat_id).split(" "):
                if each == msgdetail["who_un"]:
                    admin = True
                    logger.debug(msg=_L("We're admin of public chat"))

            # If we're not admin and admin is empty, consider ourselves admin
            if not admin:
                if plugin.config.config(key='admin', gid=chat_id, default="") == "":
                    logger.debug(msg=_L("We're admin because no admin listed on public chat"))
                    admin = True

    return owner or admin


def geteffectivegid(gid):
    """
    Gets effective gid based on settings
    :param gid: gid of group
    :return: gid to use
    """

    if plugin.config.gconfig(key='isolated', default=False, gid=gid):
        # Isolated is defined for either channel or general bot config.
        # need to check now if group is linked and if so, return that gid,
        # and if not, return groupid

        link = plugin.config.gconfig(key='link', default=False, gid=gid)
        if link:
            # This chat_id has 'link' defined against master, effective gid
            # should be that one
            return int(link)
        else:
            return gid
    else:
        # Non isolation configured, returning '0' as gid to use
        return 0


def loglevel():
    """
    This functions stores or sets the proper log level based on the
    database configuration
    """

    logger = logging.getLogger(__name__)
    level = False

    for case in Switch(plugin.config.config(key="verbosity").lower()):
        # choices=["info", "debug", "warn", "critical"])
        if case('debug'):
            level = logging.DEBUG
            break
        if case('critical'):
            level = logging.CRITICAL
            break
        if case('warn'):
            level = logging.WARN
            break
        if case('info'):
            level = logging.INFO
            break
        if case():
            # Default to DEBUG log level
            level = logging.DEBUG

    # If logging level has changed, redefine in logger,
    # database and send message
    if logging.getLevelName(logger.level).lower() != plugin.config.config(key="verbosity"):
        logger.setLevel(level)
        logger.info(msg=_L("Logging level set to %s") % plugin.config.config(key="verbosity"))
        plugin.config.setconfig(key="verbosity",
                                value=logging.getLevelName(logger.level).lower())


def conflogging(target=None):
    """
    This function configures the logging handlers for console and file
    """

    if target is None:
        target = __name__

    logger = logging.getLogger(target)

    # Define logging settings
    if not plugin.config.config(key="verbosity"):
        if not options.verbosity:
            # If we don't have defined command line value and it's not stored,
            # use DEBUG
            plugin.config.setconfig(key="verbosity", value="DEBUG")
        else:
            plugin.config.setconfig(key="verbosity", value=options.verbosity)

    # create formatter
    formatter = logging.Formatter('%(asctime)s : %(name)s : %(funcName)s(%(lineno)d) : %(levelname)s : %(message)s')

    # create console handler and set level to debug
    console = logging.StreamHandler()
    console.setLevel(logging.DEBUG)
    console.setFormatter(formatter)
    logger.addHandler(console)

    # create file logger
    filename = '%s.log' % plugin.config.config(key='database').split(".")[0]

    file = logging.FileHandler(filename)
    file.setLevel(logging.DEBUG)
    file.setFormatter(formatter)
    logger.addHandler(file)

    return


def main():
    """
    Main code for the bot
    """

    # Main code
    logger = logging.getLogger(__name__)

    # Set database name in config
    if options.database:
        createorupdatedb()
        plugin.config.setconfig(key='database', value=options.database)

    # Configure logging
    conflogging(target="stampy")

    # Configuring alembic logger
    conflogging(target="alembic")

    logger.info(msg=_L("Started execution"))

    if not plugin.config.config(key='sleep'):
        plugin.config.setconfig(key='sleep', value=10)

    # Remove our name so it is retrieved on boot
    plugin.config.deleteconfig(key='myself')

    # Check if we've the token required to access or exit
    if not plugin.config.config(key='token'):
        if options.token:
            token = options.token
            plugin.config.setconfig(key='token', value=token)
        else:
            msg = _("Token required for operation, please check https://core.telegram.org/bots")
            logger.critical(msg)
            sys.exit(1)

    # Check if we've URL defined on DB or on cli and store
    if not plugin.config.config(key='url'):
        if options.url:
            plugin.config.setconfig(key='url', value=options.url)

    # Check if we've owner defined in DB or on cli and store
    if not plugin.config.config(key='owner'):
        if options.owner:
            plugin.config.setconfig(key='owner', value=options.owner)

    # Initialize modules
    global plugs
    global plugtriggers
    plugs, plugtriggers = plugins.initplugins()

    logger.debug(msg=_L("Plug triggers reported: %s") % plugtriggers)

    # Check operation mode and call process as required
    if options.daemon or plugin.config.config(key='daemon'):
        plugin.config.setconfig(key='daemon', value=True)
        logger.info(msg=_L("Running in daemon mode"))
        while plugin.config.config(key='daemon') == 'True':
            process(getupdates())
            processcron()
            sleep(int(plugin.config.config(key='sleep')))
    else:
        logger.info(msg=_L("Running in one-shoot mode"))
        process(getupdates())
        processcron()

    logger.info(msg=_L("Stopped execution"))
    logging.shutdown()
    sys.exit(0)


if __name__ == "__main__":
    # Update db if needed
    createorupdatedb()

    # Set name to the database being used to allow multibot execution
    if plugin.config.config(key="database"):
        __name__ = plugin.config.config(key="database").split(".")[0]
    else:
        plugin.config.setconfig(key="database", value='stampy')
        __name__ = "stampy.stampy"
    main()