Пример #1
0
 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)
Пример #2
0
 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)
Пример #3
0
    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)
Пример #4
0
    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)
Пример #5
0
 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)
Пример #6
0
 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)
Пример #7
0
 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))
Пример #8
0
 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!')
Пример #9
0
 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)
Пример #10
0
 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
Пример #11
0
 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)
Пример #12
0
    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)
Пример #13
0
 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)
Пример #14
0
    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)
Пример #15
0
    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)
Пример #16
0
    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)
Пример #17
0
    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)
Пример #18
0
 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)
Пример #19
0
    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)
Пример #20
0
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
Пример #21
0
    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