skip to Main Content

Abstract.

I have two python files. The first one reads some inputs (texts) and the second one stores some functions. The problem here is the method I’m using to look for the function that I need. Now I’m using a If / Elif method to compare the input and the functions but this method needs to compare all the functions stored in the second file with the input and I was thinking if “this is the best way to do it?”.

Full explanation.

I’m trying to build a Telegram chat bot, just to practice some Python. Before anything I drew a mental map to know what I want this scripts will do. Because of that mental map, I came with the idea of spliting the code in different files in order organize everything better and to do the debugging process a little bit easier. So, I did that. I store the credentials (for the api’s) in one file, the mainframe in other file and the functions in other file. I import in the files into the mainframe with From Credentials import * and From Comands import *. When any text come to the bot it first checks how it starts, if it starts with “/” then it stores whatever comes after the slash in a variable and send it as a parameter to the functions file. When is there it starts to look for the exact command required and execute it. And it works but I guessing if there is a better way to do it. I hope I explained well the problem and you can help me. Here’s the extract of code that I’m talking.

mainframe.py

from Comands import *
from credentials import *

...

if text.startswith("/"):
            comando = text[1:]
            print(comando)
            Comands.search(comando)
elif text in items:
            db.delete_item(text, chat)
            items = db.get_items(chat)
            keyboard = build_keyboard(items)
            send_message("Select an item to delete", chat, keyboard)
else:
            db.add_item(text, chat)
            items = db.get_items(chat)
            message = "n".join(items)
            send_message(message, chat)
...

Comands.py

from message_dictionary import *
from mainframe import *

def search(comando):
    if comando == "start":
        def start():
            keyboard = build_keyboard(acuerdo)
            send_message(mensaje["start"], chat, keyboard)
    elif comando == "done":
        def done():
            keyboard = build_kerboard(items)
            send_message("Select an item to delete", chat, keyboard)

2

Answers


  1. Chosen as BEST ANSWER

    Here it is.

    mainframe.py (it is actually called Prubeas.py)

    #PYTHON 3.6.2
    #LGsus
    
    import json
    import requests
    import time
    import urllib
    from dbhelper import DBHelper
    from credentials import *
    from message_dictionary import *
    import Comands
    
    db = DBHelper()
    
    #DECLARAR ID DEL BOT Y URL DE TELEGRAM
    URL = "https://api.telegram.org/bot{}/".format(telegram_token)
    
    #CONSULTAR ESTADO
    def get_url(url):
        response = requests.get(url)
        content = response.content.decode("utf8")
        return content
    
    #CAMBIAR DE JSON A PYTHON (PARSE)
    def get_json_from_url(url):
        content = get_url(url)
        js = json.loads(content)
        return js
    
    #SOLICITAR LISTA DE MENSAJES
    def get_updates(offset=None):
        url = URL + "getUpdates?timeout=100"
        if offset:
            url += "&offset={}".format(offset)
        js = get_json_from_url(url)
        return js
    
    #DETERMINAR MENSAJES NO  LEÍDOS
    def get_last_update_id(updates):
        update_ids = []
        for update in updates["result"]:
            update_ids.append(int(update["update_id"]))
        return max(update_ids)
    
    #RESPONDER A TODOS LOS NO LEIDOS
    def handle_updates(updates):
        for update in updates["result"]:
            text = update["message"]["text"]
            chat = update["message"]["chat"]["id"]
            items = db.get_items(chat)
            if text.startswith("/"):
                comando = text[1:]
                print(comando)
                Comands.search(comando)
                #fin = text.find(" ")
                #print(fin)
    
                #if text == "/done":
                #    keyboard = build_keyboard(items)
                #    send_message("select an item to delete", chat, keyboard)
                #elif text == "/start":
                #    keyboard = build_keyboard(acuerdo)
                #    send_message(mensajes["start"], chat, keyboard)
                #elif text.startswith("/"):
                #    continue
            elif text in items:
                db.delete_item(text, chat)
                items = db.get_items(chat)
                keyboard = build_keyboard(items)
                send_message("Select an item to delete", chat, keyboard)
            else:
                db.add_item(text, chat)
                items = db.get_items(chat)
                message = "n".join(items)
                send_message(message, chat)
    
    #SOLICITAR ULTIMO MENSAJE Y ID DEL CHAT
    def get_last_chat_id_and_text(updates):
        global Texto
        global full_last
        num_updates = len(updates["result"])
        lista = updates["result"]
        data = json.dumps(lista)
        last_update = num_updates - 1
        full_last = updates["result"][last_update]
        Texto = "text" in full_last["message"]
        if Texto == True:
            text = updates["result"][last_update]["message"]["text"]
        else:
            text = "Entrada invalida"
        chat_id = updates["result"][last_update]["message"]["chat"]["id"]
        return (text, chat_id)
    
    #CREAR EL TECLADO
    def build_keyboard(items):
        keyboard = [[item] for item in items]
        reply_markup = {"keyboard":keyboard, "one_time_keyboard":True}
        return json.dumps(reply_markup)
    
    #ENVIAR MENSAJE
    def send_message(text, chat_id, reply_markup=None):
        text = text.encode(encoding='utf-8')
        text = urllib.parse.quote_plus(text)
        url = URL + "sendMessage?text={}&chat_id={}&parse_mode=Markdown".format(text, chat_id)
        if reply_markup:
            url += "&reply_markup={}".format(reply_markup)
        get_url(url)
        print (text)
    
    text, chat = get_last_chat_id_and_text(get_updates())
    send_message(text, chat)
    
    ##EJECUTAR
    def main():
        db.setup()
        last_update_id = None
        while True:
            updates = get_updates(last_update_id)
            if len(updates["result"]) > 0:
                last_update_id = get_last_update_id(updates) + 1
                handle_updates(updates)
            time.sleep(0.5)
    
    #CONDICION PARA EJECUTAR
    if __name__ == '__main__':
        main()
    
    
    
    
    
    #import ccxt
    
    #b = ccxt.bitso({
    #    'apiKey': "XXXXXX",
    #    'secret': "XXXXXX",
    #    'verbose': False,
    #    })
    #
    #print(b.fetchBalance())
    

    Whats it commented at the end of this code is for an API i'll try to use later, once I done with the telegram api.

    Comands.py

    #Lista de comandos
    
    from message_dictionary import *
    from Prubebas import *
    
    def search(comando):
        if comando == "start":
    
        #def start():
            keyboard = build_keyboard(acuerdo)
            send_message(mensaje["start"], chat, keyboard)
    
        def done():
            keyboard = build_kerboard(items)
            send_message("Select an item to delete", chat, keyboard)
    

    I apologies for mixing the languages. I really appreciate your help, thanks.


  2. First, I’ll start with 2 recommendations:

    • Don’t use stars in imports (it is hard to tell where functions are declared later)
    • Try to avoid cycle imports (import Command in mainframe.py and import mainframe in command.py)

    mainframe.py

    import command
    
    comando_str = 'str_command'
    
    # at this stage we want to call command.str_command(), but we have a string, not a function
    if hasattr(command, comando_str):  # let's check if such command exist in `command` module
        comando_function = getattr(command, comando_str)  # now let's get a reference to that function
    
        # we can now call comando_function()!
        # but maybe you have some parameters to call (like passing in `keyboard`)
        arg_count = comando_function.__code__.co_argcount  # total number of arguments to the function
        # co_varnames has all the variables that function would use, but arguments come firs
        arg_names = comando_function.__code__.co_varnames[:arg_count]  
    
        if arg_count >= 1 and arg_names[0] == 'keyboard':
             comando_function(keyboard)
        elif arg_count == 0:  # function require no arguments
             comando_function()
        else:
            raise Exception('Function %s could not be called', comando_str)
    else:
        raise Exception('Function command.%s is not declared', comando_str)
    

    command.py

    import message_dictionary as md  # no *!
    
    def str_command(keyboard):
        md.send_message(mensaje["start"], chat, keyboard)  # now I know that send_message is in md (message_dictionary)
    
    def start():
        keyboard = build_keyboard(acuerdo)  
        send_message(mensaje["start"], chat, keyboard)  
    

    some notes about your comand.py:

    def search(comando):  # ok
        if comando == "start":  # ok
            # here you define function `start`
            # yet, you have not called `start` function 
            # so, when you exit `search` function, this function is gone!
            def start():
                keyboard = build_keyboard(acuerdo)  # where is acuerdo is declared?
                send_message(mensaje["start"], chat, keyboard)  # where mensaje and chat are declared?
        elif comando == "done":
            # same problem as with `start` function above
            def done():
                keyboard = build_kerboard(items)
                send_message("Select an item to delete", chat, keyboard)
    

    modified comand.py to return callable function (to address questions in comments):

    def search(comando): 
        if comando == "start":
            def start(): 
                keyboard = build_keyboard(acuerdo)  
                send_message(mensaje["start"], chat, keyboard)  
            return start
        elif comando == "done":
            # same problem as with `start` function above
            def done():
                keyboard = build_kerboard(items)
                send_message("Select an item to delete", chat, keyboard)
            return done
    

    modified fragment mainframe.py to use returned values:

    if text.startswith("/"):
        comando = text[1:]
        print(comando)  # look at the logging as a fancy replacing for print
        call_me = Comands.search(comando)
        if call_me:  # checking that something is returned (not None)
            call_me()  # example of calling it
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search