Advertisement
RavNProgramer

Python Telegram BOT

Jul 20th, 2018
465
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.13 KB | None | 0 0
  1. import json
  2. import requests
  3. import time
  4. import datetime
  5. from dbhelper import DBHelper
  6. import urllib
  7. from io import BytesIO
  8.  
  9. # info to load at start
  10. # token is not in the source code for security
  11. TOKEN  = open('token.txt', 'r').read().split('\n')[0].strip()
  12. # URL to interact with the API
  13. URL = "https://api.telegram.org/bot{}/".format(TOKEN)
  14. # handler to the database
  15. db = DBHelper()
  16. # caching the number of questions
  17. # 1 personal 2 food 3 activity
  18. nq_caterogy = {1:3, 2:2, 3:2}
  19.  
  20.  
  21. ###############
  22. #
  23. #   ERROR LOG
  24. #
  25. ################
  26. def log_entry(entry):
  27.     # get actual time
  28.     now = datetime.datetime.now()
  29.     dateerror = now.strftime("%Y-%m-%d %H:%M")
  30.     # open the log file in append mode
  31.     with open('error.log', 'a') as log:
  32.         log.write('\n\n'+dateerror+'\n')
  33.         log.write(entry+'\n\n')
  34.  
  35.  
  36. #################
  37. #
  38. #   BASIC TOOLS
  39. #
  40. ################
  41.  
  42. def build_keyboard(items):
  43.     # contruir un teclado para una vez
  44.     keyboard = [[item] for item in items]
  45.     reply_markup = {"keyboard":keyboard, "one_time_keyboard": True}
  46.     return json.dumps(reply_markup)
  47.  
  48.  
  49. # funcion auxiliar de nivel más bajo para recibir mensajes
  50. def get_url(url):
  51.     # funcion para recibir lo que se le pasa al bot
  52.     response = requests.get(url)
  53.     content = response.content.decode("utf8")
  54.     return content
  55.  
  56.  
  57. def get_json_from_url(url):
  58.     # obteniene lo que se le pasa al bot con la funcion auxiliar
  59.     content = get_url(url)
  60.     # transforma de JSON a diccionario
  61.     js = json.loads(content)
  62.     return js
  63.  
  64.  
  65. def get_updates(offset=None):
  66.     # peticion para obtener las novedades
  67.     url = URL + "getUpdates"
  68.     # offset es el numero del ultimo mensaje recibido
  69.     # el objetivo es no volver a pedirlo todo
  70.     if offset:
  71.         url += "?offset={}".format(offset)
  72.     # llamada a la funcion auxiliar
  73.     js = get_json_from_url(url)
  74.     return js
  75.  
  76.  
  77. def get_last_update_id(updates):
  78.     # el orden de llegada de los mensajes al bot produce un id creciente
  79.     # devolvemos el maximo para saber por donde nos hemos quedado
  80.     update_ids = []
  81.     return max([int(el['update_id']) for el in updates['result']])
  82.  
  83.  
  84. #################
  85. #
  86. #   TELEGRAM API MACROS
  87. #
  88. #################
  89.  
  90. def send_message(text, chat_id, reply_markup=None):
  91.     text = urllib.parse.quote_plus(text)
  92.     url = URL + "sendMessage?text={}&chat_id={}&parse_mode=Markdown".format(text, chat_id)
  93.     # reply_markup is for a special keyboard
  94.     if reply_markup:
  95.         url += "&reply_markup={}".format(reply_markup)
  96.     get_url(url)
  97.  
  98. def send_photo(localpath, chat_id):
  99.     # give the name of a file in 'img' folder
  100.     # send that image to the user
  101.     url = URL + "sendPhoto"
  102.     files = {'photo': open(localpath, 'rb')}
  103.     data = {'chat_id' : chat_id}
  104.     r = requests.post(url, files=files, data=data)
  105.  
  106. def send_sticker(sticker_id, chat_id):
  107.     # send an sticker
  108.     url = URL + "sendSticker"
  109.     # atributtes
  110.     data = {'chat_id' : chat_id, 'sticker': sticker_id}
  111.     # it returns the same file if success, can be stored in a variable
  112.     requests.post(url, data=data)
  113.  
  114.  
  115. def forward(chat_from, msg_id, chat_id):
  116.     # forward a messege
  117.     url = URL + "forwardMessage"
  118.     # atributtes
  119.     data = {'chat_id' : chat_id, 'from_chat_id': chat_from, 'message_id':msg_id}
  120.     requests.post(url, data=data)
  121.  
  122. def send_GIF(localpath, chat_id):
  123.     # sent a short video as a GIF
  124.     url = URL + "sendVideo"
  125.     # atributtes
  126.     files = {'video': open('img/'+localpath, 'rb')}
  127.     data = {'chat_id' : chat_id}
  128.     requests.post(url, files=files, data=data)
  129.  
  130. ###############################
  131. #
  132. #   BOT SPECIFIC METHODS
  133. #
  134. ###############################
  135.  
  136. def checknumber(str):
  137.     try:
  138.         float(str)
  139.         return True
  140.     except ValueError:
  141.         return False
  142.  
  143.  
  144. ##############################
  145. #
  146. #   MAIN FUNCTIONS
  147. #
  148. #############################
  149.  
  150.  
  151. def filter_update(update):
  152.     if 'edited_message' in update:
  153.         # check if text
  154.             if 'text' in update['edited_message']:
  155.                 # update the answer
  156.                 process_edit(update)
  157.                 return False, update['edited_message']['chat']['id'], update['edited_message']['message_id']
  158.             else:
  159.                 # returning none if it's an update without text
  160.                 return None, update["message"]["chat"]["id"]
  161.     else:
  162.         if 'text' in update['message']:
  163.             return update["message"]["text"].strip(), update["message"]["chat"]["id"], update['message']['message_id']
  164.         else:
  165.             # return none if it's a message withpout text
  166.             return None, update["message"]["chat"]["id"], update['message']['message_id']
  167.  
  168.  
  169. def process_edit(update):
  170.     text = update["edited_message"]["text"]
  171.     message_id = update['edited_message']['message_id']
  172.     db.update_response_edited(message_id, text)
  173.  
  174.  
  175. def numeric_keyboard():
  176.     keyboard = [['7', '8', '9'], ['4', '5', '6'], ['1', '2', '3'], ['0']]
  177.     reply_markup = {"keyboard":keyboard, "one_time_keyboard": False}
  178.     return json.dumps(reply_markup)
  179.  
  180.  
  181. def main_menu_keyboard():
  182.     keyboard = [['/personal', '/food'], ['/activity', '/social'], ['/risk', '/tip']]
  183.     reply_markup = {"keyboard":keyboard, "one_time_keyboard": True}
  184.     return json.dumps(reply_markup)
  185.  
  186.  
  187. def handle_updates(updates):
  188.     for update in updates["result"]:
  189.         # controlar si hay texto
  190.         # funcion auxiliar que trata eltipo de mensaje
  191.         text, chat, message_id = filter_update(update)
  192.  
  193.         if text == False:
  194.             continue
  195.         elif text == None:
  196.             send_message('This kind of message is not supported yet', chat)
  197.             continue
  198.  
  199.         # get the actual phase and question
  200.         #items = db.get_items(chat)  ##
  201.         if text == '/start':
  202.             # wellcome message
  203.             hello = 'CAADAgADBQYAAhhC7giZXSreX-e4UgI'
  204.             send_sticker(hello, chat)
  205.             send_message(open('text/en/welcome.txt', 'r').read(), chat)
  206.             # insert user into the db, check collisions
  207.             if db.check_start(chat):
  208.                 # sanity check
  209.                 try:
  210.                     db.register_user(chat)
  211.                 except:
  212.                     pass
  213.             else:
  214.                 db.change_phase(newphase = 0, id_user=chat)
  215.             # show keyboard for the menu
  216.             send_message('Choose your option!', chat,main_menu_keyboard())
  217.  
  218.         # Check if the user have done the start command
  219.         elif db.check_user(chat):
  220.             send_message('An error ocurred, pls use /start to restart the bot!', chat)
  221.             continue
  222.  
  223.         elif text == '/personal':
  224.             # set to phase and question 1
  225.             db.change_phase(newphase= 1, id_user=chat)
  226.             # welcome to personal
  227.             # TODO
  228.             # throw first question
  229.             q1 = db.get_question(1, 1)
  230.             send_message(q1, chat, numeric_keyboard())
  231.  
  232.         elif text == '/food':
  233.             # set to phase and question 1
  234.             db.change_phase(newphase= 2, id_user=chat)
  235.             # welcome to personal
  236.             # TODO
  237.             # throw first question
  238.             q1 = db.get_question(2, 1)
  239.             send_message(q1, chat, numeric_keyboard())
  240.  
  241.         elif text == '/activity':
  242.             # set to phase and question 1
  243.             db.change_phase(newphase= 3, id_user=chat)
  244.             # welcome to personal
  245.             # TODO
  246.             # throw first question
  247.             q1 = db.get_question(3, 1)
  248.             send_message(q1, chat, numeric_keyboard())
  249.  
  250.         elif text == '/tip':
  251.             completed = db.check_completed(chat)
  252.             # put phase to 0
  253.             db.change_phase(newphase = 0, id_user=chat)
  254.             # check if completed all questionaries
  255.             if completed[0] and completed[1] and completed[2]:
  256.                 # give a personall advice
  257.                 send_message('This will be a "custom" tip depending on the user results', chat)
  258.             # otherwise
  259.             else:
  260.                 # give a general advice
  261.                 send_message('User did not complete all the surveys. Generic tip.', chat)
  262.  
  263.             send_message('Choose your option!', chat, main_menu_keyboard())
  264.  
  265.         elif text == '/risk':
  266.             completed = db.check_completed(chat)
  267.             # put phase to 0
  268.             db.change_phase(newphase = 0, id_user=chat)
  269.             # check if completed all questionaries
  270.             if completed[0] and completed[1] and completed[2]:
  271.                 # give a personall advice
  272.                 send_message('Im not able to calculate your obesity risk yet, but look at this cute avocado!', chat)
  273.                 send_photo('img/avo.jpg', chat)
  274.             # otherwise
  275.             else:
  276.                 # give a general advice
  277.                 send_message('User did not complete all the surveys.', chat)
  278.  
  279.             send_message('Choose your option!', chat, main_menu_keyboard())
  280.         else:
  281.             # recata a que responde
  282.             status = db.get_phase_question(chat)
  283.             if (status[0] == 0):
  284.                 send_message('Choose your option!', chat, main_menu_keyboard())
  285.                 continue
  286.             # Si no es una respuesta valida
  287.             if not checknumber(text):
  288.                 send_message('Sorry but your answer have to be a number!', chat)
  289.                 continue
  290.             # comprueba si el usuario ya ha respondido a la pregunta
  291.             # check response
  292.             if db.check_answer(chat, phase=status[0], question=status[1]):
  293.                 # guarda la respuesta del usuario
  294.                 db.add_answer(id_user=chat, phase=status[0], question=status[1], message_id=message_id, answer=text)
  295.             else:
  296.                 db.update_response(id_user=chat, phase=status[0], question=status[1], message_id=message_id, answer=text)
  297.  
  298.             # comprueba que no es la ultima pregunta
  299.             if nq_caterogy[status[0]] > status[1]:
  300.                 # no lo es
  301.                 # pasa de estado
  302.                 db.next_question(chat)
  303.                 # siguiente pregunta
  304.                 q = db.get_question(status[0], status[1]+1)
  305.                 send_message(q, chat)
  306.             else:
  307.                 # si lo es, actualiza estatus y "vuelve" al menu principal
  308.                 db.completed_survey(chat, status[0])
  309.                 send_message('Thanks for completing the survey!', chat)
  310.                 send_message('Choose your option!', chat, main_menu_keyboard())
  311.  
  312.  
  313. def main():
  314.     # variable para controlar el numero de mensajes
  315.     last_update_id = None
  316.     db.setup()
  317.     # bucle infinito
  318.     while True:
  319.         # obten los mensajes no vistos
  320.         updates = get_updates(last_update_id)
  321.         # si hay algun mensaje do work
  322.         try:
  323.             if len(updates["result"]) > 0:
  324.                 last_update_id = get_last_update_id(updates) + 1
  325.                 handle_updates(updates)
  326.             # hay que dejar descansar los servidores de telegram
  327.             time.sleep(0.5)
  328.         except:
  329.             print('Error ocurred, watch log!')
  330.             log_entry(str(updates))
  331.  
  332.  
  333. if __name__ == '__main__':
  334.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement