def process_electives(message): check = controller.check_electives_course(message.text) if check: controller.set_electives_user(message.from_user.id, message.text) bot.send_message(message.chat.id, permanent.MESSAGE_SUCCESS, reply_markup=main_markup) else: bot.send_message(message.chat.id, permanent.MESSAGE_UNKNOWN_LESSON, reply_markup=main_markup)
def weekday_select_handler(message): """ Handler for schedule request of specific weekday """ log(permanent.MODULE_NAME, message) # check user is configured if not controller.get_user(message.chat.id).is_configured: bot.send_message(message.chat.id, permanent.MESSAGE_USER_NOT_CONFIGURED, reply_markup=main_markup) return # remove star symbol if needed weekday = message.text[:2] # force check message is weekday due to some bug if weekday not in permanent.TEXT_DAYS_OF_WEEK: return # get list of lessons for specified user and day schedule = controller.get_day_lessons( message.from_user.id, day=permanent.TEXT_DAYS_OF_WEEK.index(weekday)) elective_shedule = get_day_elective_lessons( message.from_user.id, day=permanent.TEXT_DAYS_OF_WEEK.index(weekday)) # convert lessons to understandable string output reply = "Core Courses:\n" reply += permanent.MESSAGE_FREE_DAY if not schedule else \ permanent.HEADER_SEPARATOR.join(str(lesson) for lesson in schedule) reply += '\nElectives:\n' reply += (MESSAGE_FREE_DAY_ELECTIVE if not elective_shedule else permanent.HEADER_SEPARATOR.join( str(lesson) for lesson in elective_shedule)) bot.send_message(message.chat.id, reply, reply_markup=main_markup)
def schedule_command_handler(message): """ Register module's commands """ log(permanent.MODULE_NAME, message) if '/friend' in message.text: # both '/friend' and '/friend @alias' are supported if message.text == "/friend": msg = bot.send_message(message.chat.id, permanent.REQUEST_ALIAS) bot.register_next_step_handler(msg, process_friend_request_step) elif len(message.text) > 8: alias = message.text[8:].strip() send_friend_schedule(message, alias) elif message.text == '/configure_schedule': # Register user if he is not registered if not controller.get_user(message.from_user.id): controller.register_user(message.from_user.id, message.from_user.username) # set configured to false and remove his groups controller.set_user_configured(message.from_user.id, False) options = telebot.types.ReplyKeyboardMarkup(True, False) # add buttons to choose course options.add(*list(permanent.REGISTERED_COURSES.keys())) msg = bot.send_message(message.chat.id, permanent.REQUEST_COURSE, reply_markup=options) bot.register_next_step_handler(msg, process_course_step)
def process_course_step(message): """ Get user's course and request course group """ log(permanent.MODULE_NAME, message) if not message.text: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return course = message.text # check course is in registered courses if course not in permanent.REGISTERED_COURSES.keys(): bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return controller.append_user_group(message.from_user.id, course) options = telebot.types.ReplyKeyboardMarkup(True, False) # add buttons of groups in selected course options.add(*list(permanent.REGISTERED_COURSES[course])) msg = bot.send_message(message.chat.id, permanent.REQUEST_GROUP, reply_markup=options) bot.register_next_step_handler(msg, process_group_step)
def process_request_string(message): """ Get and save new user's favorite string """ # log message log(permanent.MODULE_NAME, message) # save new string to db controller.set_string(message.from_user.id, message.text) # notify user of changes bot.send_message(message.chat.id, permanent.MESSAGE_STRING_SAVED)
def process_friend_request_step(message): """ Get friend's alias after /friend command to show his current schedule """ log(permanent.MODULE_NAME, message) if not message.text: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return send_friend_schedule(message, message.text)
def admin(message): """ Register module's commands """ log(permanent.MODULE_NAME, message) # only admins from list are allowed to call admin commands if message.from_user.id not in permanent.ADMIN_LIST: bot.send_message(message.chat.id, permanent.MESSAGE_UNAUTHORIZED) return elif message.text == '/helpa': # send admin commands help bot.send_message(message.chat.id, ' '.join(admin_commands))
def test_process_electives(self): message = self.elective check = elective_controller.check_electives_course(message) self.assertTrue(check) elective_controller.set_electives_user('test_user_id', message) ret_msg = bot.send_message(chat_id, elective_permanent.MESSAGE_SUCCESS, reply_markup=main_markup) self.assertEqual(ret_msg.text, 'The course has been successfully added!')
def process_english_step(message): """ Save user`s english group to database """ log(permanent.MODULE_NAME, message) if not message.text or len( message.text ) != 8 or message.text[:6] not in permanent.REGISTERED_COURSES["B19"]: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return controller.append_user_group(message.from_user.id, message.text) controller.set_user_configured(message.from_user.id, True) bot.send_message(message.chat.id, permanent.MESSAGE_SETTINGS_SAVED, reply_markup=main_markup)
def remind_time(): """ Function is called by reminder module in certain amount of minutes before each lesson Iterates over users and sends reminders if needed """ # get relevant reminders for current moment for remind in controller.get_relevant_reminders(): user_id, lesson = remind[0], remind[1] try: bot.send_message(user_id, permanent.HEADER_REMIND + str(lesson), reply_markup=main_markup) except Exception as exception: # 403 Forbidden means user blocked the bot (what a silly!) if hasattr(exception, 'result') and exception.result.status_code == 403: controller.delete_user(user_id) continue
def schedule_command_handler(message): """ Register module's commands """ # log message log(permanent.MODULE_NAME, message) # get user's favorite string from database favorite_string = controller.get_string(message.from_user.id) # set default string if no such found if not favorite_string: favorite_string = permanent.TEXT_DEFAULT_STRING controller.register_user(message.from_user.id, favorite_string) if message.text == '/set_favorite_string': # send string request msg = bot.send_message(message.chat.id, permanent.REQUEST_FAVORITE_STRING) # register new single-use message handler bot.register_next_step_handler(msg, process_request_string) elif message.text == '/get_favorite_string': bot.send_message(message.chat.id, favorite_string)
def send_current_schedule(to_chat_id, about_user_id): """ Send current and next lesson about specified user to given chat Function could be called from /friend command or NOW button press :param to_chat_id: int :param about_user_id: int """ current_lesson = controller.get_current_lesson(about_user_id) next_lesson = controller.get_next_lesson(about_user_id) # add headers if needed reply = permanent.HEADER_NOW + current_lesson.get_str_current( ) if current_lesson else "" if next_lesson: reply += permanent.HEADER_NEXT + next_lesson.get_str_future() else: reply += permanent.HEADER_NO_NEXT_LESSONS bot.send_message(to_chat_id, reply, reply_markup=main_markup)
def remind_command_handler(message): """ Register commands to change module settings """ log(permanent.MODULE_NAME, message) if message.text == '/configure_remind': markup = telebot.types.ReplyKeyboardMarkup(True, False) markup.add(permanent.MESSAGE_YES, permanent.MESSAGE_NO) msg = bot.send_message(message.chat.id, permanent.REQUEST_REMINDERS, reply_markup=markup) bot.register_next_step_handler(msg, process_reminders_step)
def process_group_step(message): """ Save user`s group choice to database """ log(permanent.MODULE_NAME, message) # check msg has text if not message.text: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return if message.text[:3] in permanent.REGISTERED_COURSES.keys(): user_course = message.text[:3] elif message.text[:4] in permanent.REGISTERED_COURSES.keys(): user_course = message.text[:4] else: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return if message.text not in permanent.REGISTERED_COURSES[user_course]: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return course_BS = message.text if user_course == 'B19': # B19 need special configuration for english group options = telebot.types.ReplyKeyboardMarkup(True, False) # add buttons for english group select options.add(*[ f"{course_BS}-{group}" for group in permanent.B19_ENGLISH_GROUPS ]) msg = bot.send_message(message.chat.id, permanent.REQUEST_ENGLISH, reply_markup=options) bot.register_next_step_handler(msg, process_english_step) else: controller.append_user_group(message.from_user.id, message.text) controller.set_user_configured(message.from_user.id, True) bot.send_message(message.chat.id, permanent.MESSAGE_SETTINGS_SAVED, reply_markup=main_markup)
def send_friend_schedule(message, alias): """ Send friend schedule by request """ # check alias was sent if not message.text: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return # remove tabs, spaces and new line symbols alias = alias.strip() # remove '@' at beginning if alias[0] == '@': alias = alias[1:] friend = controller.get_user_by_alias(alias) # check such friend exists if not friend or not friend.is_configured: bot.send_message(message.chat.id, permanent.MESSAGE_FRIEND_NOT_FOUND, reply_markup=main_markup) return send_current_schedule(message.chat.id, friend.id)
def add_course_handler(message): """ Register module's commands """ log(permanent.MODULE_NAME, message) if message.text == '/add_course': lessons = controller.get_electives_course() options = telebot.types.ReplyKeyboardMarkup(True, False) for lesson in lessons: line_list = telebot.types.KeyboardButton(lesson.subject) options.row(line_list) reply = str("What course you want to add?") msg = bot.send_message(message.chat.id, reply, reply_markup=options) bot.register_next_step_handler(msg, process_electives)
def test_add_course(self): """ Register module's commands """ message = self.test_message if message == '/add_course': lessons = elective_controller.get_electives_course() options = telebot.types.ReplyKeyboardMarkup(True, False) for lesson in lessons: line_list = telebot.types.KeyboardButton(lesson.subject) options.row(line_list) reply = str("What course you want to add?") ret_msg = bot.send_message(chat_id, reply, reply_markup=options) self.assertEqual(ret_msg.text, reply)
def process_reminders_step(message): """ Save user if he wants reminders or delete him from db otherwise """ log(permanent.MODULE_NAME, message) if not message.text: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return user_id = message.from_user.id if message.text == permanent.MESSAGE_YES: controller.register_user(user_id) elif message.text == permanent.MESSAGE_NO: controller.delete_user(user_id) else: bot.send_message(message.chat.id, permanent.MESSAGE_ERROR, reply_markup=main_markup) return bot.send_message(message.chat.id, permanent.MESSAGE_SETTINGS_SAVED, reply_markup=main_markup)
def main_buttons_handler(message): """ Handler for processing three main buttons requests """ log(permanent.MODULE_NAME, message) # check user if configured user = controller.get_user(message.chat.id) if not user or not user.is_configured: bot.send_message(message.chat.id, permanent.MESSAGE_USER_NOT_CONFIGURED, reply_markup=main_markup) return # update alias if it was changed controller.set_user_alias(message.from_user.id, message.from_user.username) if message.text == permanent.TEXT_BUTTON_NOW: send_current_schedule(message.chat.id, message.from_user.id) elif message.text == permanent.TEXT_BUTTON_DAY: markup = telebot.types.ReplyKeyboardMarkup(True) buttons = list() day_of_week = datetime.today().weekday() # make list of weekdays and add star for today for i, day in enumerate(permanent.TEXT_DAYS_OF_WEEK): buttons.append( telebot.types.KeyboardButton( day if day_of_week != i else day + "⭐")) markup.add(*buttons) bot.send_message(message.chat.id, permanent.REQUEST_WEEKDAY, reply_markup=markup) elif message.text == permanent.TEXT_BUTTON_WEEK: bot.send_message(message.chat.id, permanent.MESSAGE_FULL_WEEK, reply_markup=main_markup)
def electives_parse(compare_with_prev): wb = load_workbook( f'{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_NAME}') ws = wb[wb.sheetnames[0]] # open workbook from backup wb_old, ws_old = None, None if compare_with_prev: wb_old = load_workbook( f'{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_BACKUP_1}') ws_old = wb_old[wb_old.sheetnames[0]] # delete and add new course name controller.delete_all_electives_lessons() controller.delete_all_electives_lesson_info() get_all_electives_course_name(ws) # iterate over each cell col = 5 while col <= permanent.ELECTIVES_SCHEDULE_LAST_COLUMN: course_name = get_value(ws, 1, col) row = 2 while row <= permanent.ELECTIVES_SCHEDULE_LAST_ROW: date = get_value(ws, row, 1) cell_new = electives_parse_cell(ws, row, col) if not cell_new[0]: row += 1 continue subject, teacher, room = cell_new[0], cell_new[1], cell_new[2] time = get_value(ws, row, 4) time_splitted = time.split('-') start_time, end_time = time_splitted[0], time_splitted[1] if compare_with_prev: # compare new cell with old one cell_old = electives_parse_cell(ws_old, row, col) if cell_new != cell_old: users = controller.get_electives_user(subject) for user in users: if not cell_old[0]: # schedule added bot.send_message( user.user_id, f"- There is an additional schedule in {date.strftime('%b %d %Y')}, {str(start_time)}-{str(end_time)}:\n" f" {subject}, {teacher}, {room}\n") else: # schedule changed subject_old, teacher_old, room_old = cell_old[ 0], cell_old[1], cell_old[2] bot.send_message( user.user_id, f"- There is an additional schedule in {date.strftime('%b %d %Y')}, {str(start_time)}-{str(end_time)}:\n" f"Was {subject_old}, {teacher_old}, {room_old}\n" f"Now {subject}, {teacher}, {room}\n") controller.insert_electives_lesson_info(subject, teacher, date, start_time, end_time, room) row += 1 col += 1
def test_update_schedule(self): """ Download xlsx schedule from link and parse all lessons Stores two previous versions of databases and xlsx files """ # specific Exception for download error class ScheduleDownloadError(Exception): pass try: # move previous backups shutil.move(f"{DATABASE_FOLDER}/{permanent.DATABASE_BACKUP_1}", f"{DATABASE_FOLDER}/{permanent.DATABASE_BACKUP_2}") shutil.move(f"{DATABASE_FOLDER}/{permanent.SCHEDULE_BACKUP_1}", f"{DATABASE_FOLDER}/{permanent.SCHEDULE_BACKUP_2}") # add backup for electives schedule shutil.move( f"{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_BACKUP_1}", f"{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_BACKUP_2}") except FileNotFoundError: pass compare_with_prev = True # compare with previous version of database if such is found try: # make new backup shutil.copy(f"{DATABASE_FOLDER}/{DATABASE_NAME}", f"{DATABASE_FOLDER}/{permanent.DATABASE_BACKUP_1}") shutil.move(f"{DATABASE_FOLDER}/{permanent.SCHEDULE_NAME}", f"{DATABASE_FOLDER}/{permanent.SCHEDULE_BACKUP_1}") # add backup for electives schedule shutil.move( f"{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_NAME}", f"{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_BACKUP_1}") except FileNotFoundError: compare_with_prev = False # download new schedule from google sheet new_schedule = requests.get(permanent.SCHEDULE_DOWNLOAD_LINK) with open(f'{DATABASE_FOLDER}/{permanent.SCHEDULE_NAME}', 'wb') as f: f.write(new_schedule.content) # download new schedule from google sheet electives_schedule = requests.get( permanent.ELECTIVES_SCHEDULE_DOWNLOAD_LINK) with open(f'{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_NAME}', 'wb') as f: f.write(electives_schedule.content) try: # check download is ok schedule_size = shutil.os.path.getsize( f'{DATABASE_FOLDER}/{permanent.SCHEDULE_NAME}') if schedule_size < permanent.SCHEDULE_MIN_SIZE_BYTES: raise ScheduleDownloadError # check electives download electives_schedule_size = shutil.os.path.getsize( f'{DATABASE_FOLDER}/{permanent.ELECTIVES_SCHEDULE_NAME}') if electives_schedule_size < permanent.SCHEDULE_MIN_SIZE_BYTES: raise ScheduleDownloadError except (FileNotFoundError, ScheduleDownloadError): # send error notification to admins for admin in SUPERADMIN_LIST: bot.send_message(admin, permanent.MESSAGE_ERROR_NOTIFY) return # Electives parse function electives_parse(compare_with_prev) # delete all lessons because new ones will be parsed controller.delete_all_lessons() # open workbook wb = load_workbook(f'{DATABASE_FOLDER}/{permanent.SCHEDULE_NAME}') ws = wb[wb.sheetnames[0]] # open workbook from backup wb_old, ws_old = None, None if compare_with_prev: wb_old = load_workbook( f'{DATABASE_FOLDER}/{permanent.SCHEDULE_BACKUP_1}') ws_old = wb_old[wb_old.sheetnames[0]] # iterate over each cell col = 2 all_course_groups = reduce( concat, [REGISTERED_COURSES[x] for x in REGISTERED_COURSES]) while col <= permanent.SCHEDULE_LAST_COLUMN: course_group = get_value(ws, 2, col) if course_group not in all_course_groups: for admin in SUPERADMIN_LIST: bot.send_message( admin, f"{permanent.MESSAGE_ERROR_UNKNOWN_GROUP}: {course_group}" ) col += 1 continue if course_group[:3] == "B19": course_group = course_group[:6] + ('-a' if col % 2 == 0 else '-b') cur_weekday = 0 row = 4 while row <= permanent.SCHEDULE_LAST_ROW: first_col_value = get_value(ws, row, 1) # time or weekday if isinstance( first_col_value, str) and first_col_value.upper() in permanent.WEEKDAYS: cur_weekday += 1 row += 1 continue cell_new = parse_cell(ws, row, col) if not cell_new[0]: row += 3 continue if cell_new[0] == -1: # send error notification to admins for admin in SUPERADMIN_LIST: bot.send_message( admin, f"{permanent.MESSAGE_ERROR_PARSE_SYNTAX} row={row} col={col}" ) row += 3 continue subject, teacher, room = cell_new[0], cell_new[1], cell_new[2] # extract time time_splitted = first_col_value.split('-') start_time, end_time = time_splitted[0], time_splitted[1] if compare_with_prev: # compare new cell with old one cell_old = parse_cell(ws_old, row, col) if cell_new != cell_old: subject_old, teacher_old, room_old = cell_old[ 0], cell_old[1], cell_old[2] for admin in SUPERADMIN_LIST: # send changes to admin bot.send_message( admin, f"{course_group} {first_col_value} changed:\n" f"Was {subject_old}, {teacher_old}, {room_old}\n" f"Now {subject}, {teacher}, {room}\n") # insert new lesson to database controller.insert_lesson(course_group, subject, teacher, cur_weekday, start_time, end_time, room) row += 3 col += 1