class Fibot(object): """ This object contains information and methods to manage the bot, and interact with its users. Attributes: name(:obj:`str`): Unique identifier for the bot bot_token(:obj:`str`): Token to access the bot chats(:class:`Fibot.Chat`): Object that represents the chats oauth(:class:`Fibot.api.Oauth`): Object that does the oauth communication necessary nlg(:class:`Fibot.NLP.nlg.NLG_unit`): Object that interacts with non FIB messages ~ query_answer(:class:`Fibot.NLP.nlg.Query_answer_unit`): Object that responds to FIB-related queries translator(:class:`Fibot.NLP.language.Translator`): Object that eases the translation of the messages messages(:obj:`dict`): Object that contains the Fibot configuration messages state_machine(:obj:`dict`): Object that simplifies the state machine management """ def __init__(self, name='Fibot'): self.name = name self.bot_token = getenv('FibotTOKEN') self.chats = Chats() self.oauth = Oauth() self.nlg = NLG_unit() self.qa = Query_answer_unit() self.translator = Translator() self.messages = {} self.state_machine = { 'MessageHandler': '0', 'Authorise': '1', 'Wait_authorisation': '2', 'Erase_user': '******', 'Push_notification': '4', } """ Loads the following components: chats: Loads the chats information from persistence nlu: Loads the trained model nlg: Loads the trained model """ def load_components(self): self.chats.load() print("Chats loaded") self.nlg.load() print("NLG model loaded") self.qa.load(train=False) print("Query answering model loaded") with open('./Data/messages.json', 'r') as fp: self.messages = json.load(fp) print("Preset messages loaded") """ Sends an action to a chat (using ChatAction helper) """ def send_chat_action(self, chat_id, action=ChatAction.TYPING): params = {'chat_id': chat_id, 'action': action} base_url = 'https://api.telegram.org/bot{}/sendChatAction'.format( self.bot_token) response = requests.get(base_url, params=params) """ Sends a message to the chat with chat_id with content text """ def send_message(self, chat_id, message, typing=False, reply_to=None): ini = time() if isinstance(message, list): for item in message: self.send_message(chat_id, item, typing, reply_to) else: if typing: self.send_chat_action(chat_id) print("chat action sent in {}".format((time() - ini))) user_language = self.chats.get_chat(chat_id)['language'] if user_language != 'English': urls = re.findall( 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', message) if urls: message = message.replace(urls[0], "{}") message = self.translator.translate(message, to=user_language) if urls: message = message.format(urls[0]) params = {'chat_id': chat_id, 'text': message} if reply_to: params['reply_to_message_id'] = reply_to base_url = 'https://api.telegram.org/bot{}/sendMessage'.format( self.bot_token) response = requests.get(base_url, params=params) print("message sent in {}".format((time() - ini))) """ Parameters: chat_id (:obj:`str`): chat_id of the user to send the message to preset (:obj:`str`): the preset of the message to send param (:obj:`str` or None): the parameter of the messages This function sends a preset message to the user with user id. See /Data/messages.json to see the preset messages. """ def send_preset_message(self, chat_id, preset, param=None): print("sending {}".format(preset)) if param: message = self.messages[preset].format(param) else: message = self.messages[preset] self.send_message(chat_id, message, typing=True) """ Parameters: chat_id (:obj:`str`): chat_id of the user that sent the messages message (:obj:`str`): text the user sent This function receives a message from a user and decides which mechanism is responsible for responding the message. """ def process_income_message(self, chat_id, message, message_id=None, debug=False): print("Processing income message...") user_language = self.chats.get_chat(chat_id)['language'] if user_language != 'English': message = self.translator.translate(message, to='English', _from=user_language) ini = time() response = self.qa.get_response(message, sender_id=chat_id) print("Getting response time is {}".format((time() - ini))) print(response) if message_id: self.send_message(chat_id, response, typing=True, reply_to=message_id) else: self.send_message(chat_id, response, typing=True)
from Fibot.chats import Chats from Fibot.api.api_raco import API_raco from Fibot.Data.data_types.exam import Exam_schedule from Fibot.Data.data_types.practical_work import Practical_schedule from pprint import pprint c = Chats() c.load() a = API_raco() a_t = c.get_chat('349611162')['access_token'] user_lang = c.get_chat('349611162')['language'] exams = list(a.get_exams_user(a_t)) e_e = Exam_schedule(exams, user_lang) pprint(list(e_e.get_closest_exams(range=50))) pracs = list(a.get_practiques(a_t)) p_e = Practical_schedule(pracs, user_lang) pprint(list(p_e.get_closest_pracs(range=50)))
class Refresh_token_thread(object): """This class enables multithreading capabilities by using an extra thread to scan looking for chats with expired tokens to refresh. Attributes: oauth(:class:`Fibot.api.oauth.Oauth`): Object that manages the oauth processes. chats(:class:`Fibot.chats.Chats`): Chat records of users. delay(:obj:`int`): Amount of seconds between scans. queue(:obj:`list`): Lists of chat_id's of the people with tokens to be refreshed. thread(:class:`threading.Timer`): Thread that does the scanning. polling(:obj:`bool`): Object that indicates if polling has to be done. """ def __init__(self, delay, thread_logging = True): self.oauth = Oauth() self.chats = Chats() self.delay = delay self.thread_logging = thread_logging self.queue = [] self.polling = True self.thread = None """ Updates the internal representation of the chats with the last dumped values. """ def update_chats(self): self.chats.load() self.queue = self.chats.get_expired_chats() """ This function defines the new timer and starts it (effectively allows the scanning) """ def run(self, initial_offset = 0): if self.polling: self.thread = Timer(self.delay - initial_offset, self.poll) self.thread.start() """ Does a scan over all users with expired tokens, and then returns to the activation function """ def poll(self): if self.thread_logging: print("\n") if self.thread_logging: log("R_Thread: Refrescando tokens\n") self.update_chats() for chat in self.queue: if self.thread_logging: log("R_Thread: Refrescando token de {}\n".format(self.chats.get_chat(chat)['name'])) refresh_token = self.chats.get_chat(chat)['refresh_token'] callback = self.oauth.refresh_token(refresh_token) if callback: self.chats.update_chat(chat, data = callback, full_data = False) if callback and self.thread_logging: log("R_Thread: Token refrescado correctamente!\n") self.queue = [] self.run() """ Allows polling """ def stop_polling(self): self.polling = False """ Forbids polling """ def start_polling(self): self.polling = True self.run()
class Fibot(object): """ This object contains information and methods to manage the bot, and interact with its users. Attributes: local (:obj:`bool`): Indicates if it runs from Telegram or Locally name (:obj:`str`): Unique identifier for the bot bot_token (:obj:`str`): Token to access the bot chats (:class:`Fibot.Chat`): Object that represents the chats oauth (:class:`Fibot.api.Oauth`): Object that does the oauth communication necessary qa (:class:`Fibot.NLP.nlg.Query_answer_unit`): Object that responds to FIB-related queries message_handler (:class:`Fibot.message_handler.Message_handler`): Object that handles messages delay (:obj:`int`): Cantidad de segundos entre escaneos en los threads notification_thread (:class:`Fibot.multithreading.threads.Notification_thread`): Object that enables a thread to scan for notifications. refresh_token_thread(:class:`Fibot.multithreading.threads.Refresh_token_thread`): Object that enables a thread to scan for tokens to refresh. messages (:obj:`dict`): Object that contains the Fibot configuration messages state_machine (:obj:`dict`): Object that simplifies the state machine management """ def __init__(self, name='Fibot', local=False, debug=True): self.local = local self.debug = debug self.name = name self.bot_token = getenv('FibotTOKEN') self.chats = Chats() self.oauth = Oauth() self.qa = Query_answer_unit() self.message_handler = None self.delay = 60 self.refresh_token_thread = None self.notification_thread = None self.messages = {} self.state_machine = {'MessageHandler': '0', 'Wait_authorisation': '1'} def log(self, text): print(colored("LOG: {}".format(text), 'cyan')) """ Loads the following components: chats: Loads the chats information from persistence message_handler: Enables it to send messages to users notification_thread: Starts its activation and defines the polling interval refresh_token_thread: Starts its activation and defines the polling interval (and the offset) nlu: Loads the trained models qa: Loads the trained models messages: Loads the preset messages to memory """ def load_components(self, thread_logging=True): self.chats.load() self.log("Base de datos de usuarios cargados") if self.local: self.message_handler = Local_Message_handler(self.chats) else: self.message_handler = Message_handler(self.chats) self.refresh_token_thread = Refresh_token_thread( self.delay, thread_logging=thread_logging) self.refresh_token_thread.run(initial_offset=30) self.notification_thread = Notification_thread( self.message_handler, self.delay, thread_logging=thread_logging) self.notification_thread.run() self.log("Threads creados") self.qa.load() with open('./Data/messages.json', 'r') as fp: self.messages = json.load(fp) self.log("Mensajes predefinidos cargados") return """ Parameters: chat_id (:obj:`int`): chat id of the user to send the message to action (:obj:`str`): defines the action to send the user (default is typing) This function sends an action to the chat with chat_id (using ChatAction helper) """ def send_chat_action(self, chat_id, action=ChatAction.TYPING): self.message_handler.send_chat_action(chat_id, message, typing, reply_to) """ Parameters: chat_id (:obj:`int`): chat id of the user to send the message to message (:obj:`str`): content of the message to be sent typing (:obj:`bool`): value that defines whether to send typing action or not reply_to (:obj:`int` or None): If defined, it is the message_id of the message that will be replied to, else no message will be replied. parse_mode (:obj:`str`): The parse mode to use (normally Markdown or None) This function sends a message to the chat with chat_id with content text, and depending on the rest of the parameters it might do extra functionality. """ def send_message(self, chat_id, message, typing=False, reply_to=None, parse_mode='Markdown'): self.message_handler.send_message(chat_id, message, typing, reply_to, parse_mode) """ Parameters: chat_id (:obj:`str`): chat_id of the user to send the message to preset (:obj:`str`): the preset of the message to send param (:obj:`str` or None): the parameter of the messages This function sends a preset message to the user with user id. See /Data/messages.json to see the preset messages. """ def send_preset_message(self, chat_id, preset, param=None): user_lang = self.chats.get_chat(chat_id)['language'] if param: message = self.messages[user_lang][preset].format(param) else: message = self.messages[user_lang][preset] if 'set_lang' in message: self.send_message(chat_id, message, typing=True, parse_mode=None) else: self.send_message(chat_id, message, typing=True) """ Parameters: chat_id (:obj:`str`): chat_id of the user that sent the messages message (:obj:`str`): text the user sent message_id (:obj:`int`): message_id of the message to reply to This function receives a message from a user and decides which mechanism is responsible for responding the message. """ def process_income_message(self, chat_id, message, message_id=None): user_language = self.chats.get_chat(chat_id)['language'] now = time() response = self.qa.get_response(message, sender_id=chat_id, language=user_language, debug=self.debug) response = [i['text'] for i in response] self.send_message(chat_id, response, typing=True, reply_to=message_id, parse_mode=None)