def start(update, context): """ Shows welcome message and asks for language """ try: # Try to answer query if the user is registering again update.callback_query.answer() except Exception: pass patient_id = update._effective_user.id # Check that user is not registered try: patient = Patient.objects.get(identifier=patient_id) logger.info( f'User {patient.username} tried to register again.') context.bot.send_message(chat_id=patient_id, text=messages[patient.language]['already_exists']) return ConversationHandler.END except Patient.DoesNotExist: # The user should not exist in DB context.user_data['patient'] = Patient(name=update._effective_user.first_name, identifier=str(patient_id), username=update._effective_user.username) logger.info(f'User {update._effective_user.username} started a new conversation') context.bot.send_message(chat_id=patient_id, text=f'Hi {update._effective_user.first_name}. Welcome to HOW-R-U psychologist bot.\n' f'Hola {update._effective_user.first_name}. Bienvenido al bot psicólogo HOW-R-U') context.bot.send_message(chat_id=patient_id, text=f'Please select a language:\nElija un idioma por favor:', reply_markup=keyboards.language_keyboard) return LANGUAGE
def cancel(update, context): """ Cancels current action and shows config menu """ logger.info( f'User {update.message.from_user.username} cancelled current operation.') return config_menu(update, context)
def finish(update, context): """ Saves patient in DB, assigns him/her to data_analyst, creates PendingQuestion entries for assigned_to_all questions and finally creates the user's PendingQuestionJob """ patient = context.user_data['patient'] patient.save() # Add patient to data analysts and assigned_to_all questions try: data_analysts = Doctor.objects.filter(is_analyst=True) for doctor in data_analysts: patient.assigned_doctors.add(doctor) patient.save() assigned_to_all = doctor.assigned_questions.filter(assigned_to_all=True) for question in assigned_to_all: pending_question = PendingQuestion(doctor=doctor, question=question, patient=patient, answering=False) pending_question.save() logger.info("Patient %s assigned to data_analysts", patient.username) except Exception: logger.exception("Exception while adding patient %s to data_analysts.", patient.username) update.message.reply_text(messages[patient.language]['registration_ok']) logger.info(f'Creating pending_questions job for user {update.message.from_user.username}') PendingQuestionJob(context, patient) return ConversationHandler.END
def job_callback(self, context): """ Prompts PendingQuestions to the user. """ pending_questions = self._get_pending_questions() for task in pending_questions: if not self.is_question_answered(task): question = task.question task.answering = True task.save() context.bot.send_message( chat_id=self.patient.identifier, text=question.text, reply_markup=keyboards.get_custom_keyboard( question.response_set.all())) while not self.is_question_answered(task): time.sleep(0.5) message = messages[self.patient.language]['finish_answering'] if self.answered_questions_today() else \ messages[self.patient.language]['no_questions'] logger.info(f'User {self.patient.username} answered all the questions') context.bot.send_message(chat_id=self.patient.identifier, text=message, reply_markup=ReplyKeyboardRemove()) time.sleep(0.1) if was_configurator_running(self.patient.identifier, context): logger.info( f'Reopening configurator for user {self.patient.username} id {self.patient.identifier}' ) context.bot.send_message( chat_id=self.patient.identifier, text=messages[self.patient.language]['select_config'], reply_markup=keyboards.config_keyboard[self.patient.language])
def answer_question(update, context): """ Prompts user's question by querying PendingQuestion DB. Creates an AsweredQuestion object. """ user = update.message.from_user response = update.message.text # Get question that is being answered from DB: try: question_task = _get_pending_question_task(str(user.id)) except PendingQuestion.DoesNotExist: logger.info( f'User {user.username} id {user.id} wrote {response} while there was no question to answer' ) update.message.reply_text( "Unrecognized command\nComando no reconocido", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END logger.info( f'User {user.username} id {user.id} answered "{response}" to question "{question_task.question}"' ) response_object = question_task.question.response_set.get(text=response) # Create answered question entry answered_question = AnsweredQuestion(patient_id=user.id, doctor=question_task.doctor, answer_date=datetime.now( pytz.timezone('Europe/Madrid')), response=response_object, question=question_task.question) answered_question.save() # Set answering to false question_task.answering = False question_task.save() return ConversationHandler.END
def back(update, context): """ Cancels current action and shows config menu """ update.callback_query.answer() username = context.user_data["patient"].username logger.info(f'User {username} cancelled current operation.') return config_menu(update, context)
def ask_delete_user(update, context): """ Asks for confirmation to completely delete the user from the system. """ patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} wants to delete his account.') update.message.reply_text(messages[patient.language]['delete_user'], reply_markup=keyboards.delete_user_keyboard[patient.language]) return PROCESS_DELETE_USER
def _exit(update, context): """ Exits from the configurator """ patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} close the configurator.') update.message.reply_text(messages[patient.language]['exit_configurator'], reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END
def ask_change_name(update, context): """ Sends old name to the user and asks for the new one """ patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} asked to change name') update.message.reply_text(messages[patient.language]['current_name'] + patient.name) update.message.reply_text(messages[patient.language]['change_name'], reply_markup=ReplyKeyboardRemove()) return PROCESS_NAME
def schedule(update, context): """ Processes schedule and calls finish() method """ patient_schedule = update.message.text patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} chose schedule {patient_schedule}') patient.schedule = patient_schedule return finish(update, context)
def ask_change_gender(update, context): """ Sends old gender to the user and asks for the new one """ patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} asked to change gender') update.message.reply_text(messages[patient.language]['current_gender'] + patient.gender) update.message.reply_text(messages[patient.language]['change_gender'], reply_markup=keyboards.gender_keyboard[patient.language]) return PROCESS_GENDER
def process_language(update, context): """ Saves the new language """ patient = context.user_data['patient'] patient.language = Flag.unflag(update.message.text) patient.save(update_fields=['language']) logger.info(f'User {update.message.from_user.username} changed language to {patient.language}') update.message.reply_text(messages[patient.language]['language_updated']) return config_menu(update, context)
def get_utc_result(value, timezone='Europe/Madrid'): # Get full datetime dt = datetime.today() tmp = datetime.strptime(value, '%H:%M') dt = dt.replace(hour=tmp.hour, minute=tmp.minute, second=0) # Add timezone timezone = pytz.timezone(timezone) result = timezone.localize(dt) logger.info("el result %s", result) return result
def process_delete_user(update, context): """ Deletes the user from the system. """ patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} deleted his account.') patient.delete() update.message.reply_text(messages[patient.language]['deleted_user'], reply_markup=keyboards.start_keyboard) return ConversationHandler.END
def ask_change_language(update, context): """ Sends old language to the user and asks for the new one """ patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} asked to change language') update.message.reply_text(messages[patient.language]['current_language'] + patient.language) update.message.reply_text(messages[patient.language]['change_language'], reply_markup=keyboards.language_keyboard) return PROCESS_LANGUAGE
def process_gender(update, context): """ Saves the new gender """ patient = context.user_data['patient'] gender = update.message.text patient.gender = gender patient.save(update_fields=['_gender']) logger.info(f'User {update.message.from_user.username} changed gender to {gender}') update.message.reply_text(messages[patient.language]['gender_updated']) return config_menu(update, context)
def timezone(update, context): """ Gets the patient's timezone by analyzing the sent location """ patient = context.user_data['patient'] patient_timezone = Patient.get_timezone_from_location(update.message.location) logger.info(f'User {patient.username} choose timezone {patient_timezone}') patient.timezone = patient_timezone context.bot.send_message(chat_id=patient.identifier, text=messages[patient.language]['choose_schedule'], reply_markup=ReplyKeyboardRemove()) return SCHEDULE
def _exit(update, context): """ Exits from the configurator """ patient = context.user_data['patient'] logger.info(f'User {patient.username} close the configurator.') update.callback_query.answer() update.callback_query.edit_message_text( messages[patient.language]['exit_configurator']) return ConversationHandler.END
def default_timezone(update, context): """ Sets default timezone to a patient (Europe/Madrid) """ update.callback_query.answer() patient = context.user_data['patient'] logger.info(f'Set default timezone (Europe/Madrid) for user {patient.username}') patient.timezone = "Europe/Madrid" context.bot.send_message(chat_id=patient.identifier, text=messages[patient.language]['choose_schedule'], reply_markup=ReplyKeyboardRemove()) return SCHEDULE
def ask_change_schedule(update, context): """ Sends old schedule to the user and asks for the new one """ patient = context.user_data['patient'] logger.info(f'User {update.message.from_user.username} asked to change schedule') schedule_dt = UTCTime.to_locale(patient.schedule) schedule = schedule_dt.strftime("%H:%M") update.message.reply_text(messages[patient.language]['current_schedule'] + schedule) update.message.reply_text(messages[patient.language]['change_schedule'], reply_markup=ReplyKeyboardRemove()) return PROCESS_SCHEDULE
def skip_picture(update, context): """ Sets default picture and asks for schedule """ update.callback_query.answer() patient = context.user_data['patient'] logger.info(f'User {patient.username} did not send a picture, using default') patient.picture = '/projects/how-r-u/chatbot/pics/default_profile_picture.png' context.bot.send_message(chat_id=patient.identifier, text=messages[patient.language]['choose_timezone'], reply_markup=keyboards.send_location_keyboard[patient.language]) return TIMEZONE
def process_name(update, context): """ Saves the new name """ patient = context.user_data['patient'] old_name = patient.name name = update.message.text patient.name = name patient.save(update_fields=['name']) logger.info(f'User {update.message.from_user.username} old name {old_name} changed name to {name}') update.message.reply_text(messages[patient.language]['name_updated']) return config_menu(update, context)
def ask_delete_user(update, context): """ Asks for confirmation to completely delete the user from the system. """ update.callback_query.answer() patient = context.user_data['patient'] logger.info(f'User {patient.username} wants to delete his account.') context.bot.send_message( chat_id=patient.identifier, text=messages[patient.language]['delete_user'], reply_markup=keyboards.delete_user_keyboard[patient.language]) return PROCESS_DELETE_USER
def process_name(update, context): """ Saves the new name """ patient = context.user_data['patient'] old_name = patient.name name = update.message.text patient.name = name patient.save(update_fields=['name']) logger.info( f'User {patient.name} old name {old_name} changed name to {name}') context.bot.send_message(chat_id=patient.identifier, text=messages[patient.language]['name_updated']) return config_menu(update, context)
def main(): logger.info("Started HOW-R-U psychologist") # Initialize bot updater = Updater(token=bot_config.TOKEN, use_context=True) dispatcher = updater.dispatcher handlers = [start_handler, config_handler, question_handler] # Add handlers to dispatcher for handler in handlers: dispatcher.add_handler(handler) # Add error callback dispatcher.add_error_handler(error_callback) # Start bot service updater.start_polling() updater.idle()
def language(update, context): """ Processes language and asks for gender """ update.callback_query.answer() patient_language = update.callback_query.data patient = context.user_data['patient'] logger.info(f'User {patient.username} chose language {patient_language}') context.user_data['patient'].language = patient_language context.bot.send_message(chat_id=patient.identifier, text=messages[patient.language]['choose_gender'], reply_markup=keyboards.gender_keyboard[patient.language]) return GENDER
def process_profile_pic(update, context): """ Saves the new profile picture """ patient = context.user_data['patient'] photo_file = update.message.photo[-1].get_file() pic_name = f'/opt/chatbot/chatbot/pics/{update.message.from_user.id}.jpg' photo_file.download(pic_name) patient.picture = pic_name patient.save() logger.info(f'User {update.message.from_user.username} changed profile picture') update.message.reply_text(messages[patient.language]['picture_updated'], reply_markup=ReplyKeyboardRemove()) return config_menu(update, context)
def gender(update, context): """ Processes gender and asks for picture """ update.callback_query.answer() patient = context.user_data['patient'] patient_gender = update.callback_query.data logger.info( f'User {patient.username} chose gender {patient_gender}') patient.gender = patient_gender context.bot.send_message(chat_id=patient.identifier, text=messages[patient.language]['choose_pic'], reply_markup=keyboards.skip_keyboard[patient.language]) return PICTURE
def picture(update, context): """ Processes picture and asks for schedule """ patient = context.user_data['patient'] photo_file = update.message.photo[-1].get_file() pic_name = f'/projects/how-r-u/chatbot/pics/{update.message.from_user.id}.jpg' photo_file.download(pic_name) patient.picture = pic_name os.remove(pic_name) logger.info(f'User {update.message.from_user.username} sent picture {pic_name}') context.bot.send_message(chat_id=patient.identifier, text=messages[patient.language]['choose_timezone'], reply_markup=keyboards.send_location_keyboard[patient.language]) return TIMEZONE
def process_wrong_schedule(update, context): """ Informs that the patient introduced a wrong schedule """ patient = context.user_data['patient'] text = update.message.text logger.info( f'User {patient.username} introduced an invalid schedule ({text})') context.bot.send_message( chat_id=patient.identifier, text=messages[patient.language]['invalid_schedule'].format(text), reply_markup=keyboards.back_keyboard[patient.language]) return PROCESS_SCHEDULE