Beispiel #1
0
async def create_user_session(user):
    user_data = mysql_connect.fetchone(
        "SELECT language_code, learning_language, first_name, last_name "
        "FROM users WHERE user_id = %s", (user, ))
    if user_data is None:
        logger.info("{} has now session in db")
        await bot.send_message(user,
                               "You should /start the bot before learning")
        return

    if user_data[0] is None:
        user_data = ('english', user_data[1])
        await bot.send_message(
            user, 'Please, run /settings to specify your language')

    if user_data[1] is None:
        await bot.send_message(
            user,
            'Please, run /setlanguage to specify the language you want to learn'
        )
        user_data = (user_data[0], 'english')

    s = UserSession(user, user_data[2], user_data[3], user_data[0])
    s.subscribed = mysql_connect.check_subscribed(user)
    s.set_active_language(user_data[1])
    logger.info("{} session is ready, subscription status is {}", user,
                s.subscribed)
    sessions[user] = s
    return s
Beispiel #2
0
async def create_user_session(user):
    user_data = mysql_connect.fetchone("SELECT language_code, learning_language, first_name, last_name "
                                       "FROM users WHERE user_id = %s",
                                       (user,))
    if user_data is None:
        logger.info("{} has no session in db", user)
        await onboarding.onboarding_start(user)
        return
    else:
        if user_data[0] is None:
            user_data = ('english', user_data[1], user_data[2], user_data[3])
            await bot.send_message(user, 'Please, run /settings to specify your language')

        elif user_data[1] is None:
            await bot.send_message(user, 'Please, run /setlanguage to specify the language you want to learn')
            user_data = (user_data[0], 'english', user_data[2], user_data[3])
    logger.info("{} has data {}", user, user_data)
    s = UserSession(user, user_data[2],
                    user_data[3],
                    user_data[0])
    s.subscribed = mysql_connect.check_subscribed(user)
    s.set_active_language(user_data[1])
    logger.info("{} session is ready, subscription status is {}", user, s.subscribed)
    sessions[user] = s
    await bot.send_message(user, "OK, now you can /addwords to get exercises.\n"
                                 "Or you can add many words with /wordlist command.\n"
                                 "Use /addtext to work with texts\n"
                                 "Then type /learn to start training.\n\n"
                                 "/subscribe to activate *premium features* "
                                 "(voice recognition, automatic translations and text-to-speech)\n\n"
                                 "Use /help if you need help")
    return s
async def repeat_ticket(
    callback_query: types.CallbackQuery,
    state: FSMContext,
) -> None:
    """ Duplicate ticket """
    callback_id: str = callback_query.id
    user_id: int = callback_query.from_user.id
    ticket_id: int = int(callback_query.data.split(":")[1])

    user_session = UserSession(user_id)
    await user_session.create(state)
    ticket: Dict = user_session.get_one_ticket(ticket_id)
    title: str = ticket.get("name", str(None))
    description: str = ticket.get("content", str(None))
    priority_int: int = ticket.get("priority", str(None))
    try:
        ticket_id = user_session.create_ticket(
            title=title, description=description, urgency=priority_int
        )
    except StupidError as err:
        message_text: str = f"Произошла неизвестная науке ошибка {err}"
    else:
        message_text = f'Заявка "{title}" принята. Номер заявки - {ticket_id}'
    logging.info(message_text)
    await bot.send_message(user_id, message_text, reply_markup=select_command)
    ddd = await bot.answer_callback_query(callback_id)
    logging.info("callback done %s", ddd)
async def process_to_select_priority(
    user_id: int, priority: str, state: FSMContext
) -> None:
    """Create new ticket with provided title, description and priority

    Args:
        user_id (int): telegram user id that issued command
        description (str): title for new ticket
    """
    user_session = UserSession(user_id)
    await user_session.create(state)
    title = await user_session.pop_field(key="title")
    description = await user_session.pop_field(key="description")
    logging.info("title = _%s_ description = _%s_", title, description)
    if len(title) == 0 or len(description) == 0:
        logging.warning(
            "process_to_select_priority(): title = _%s_ description = _%s_",
            title,
            description,
        )
        await Form.logged_in.set()
        await bot.send_message(
            user_id,
            "Заявка не принята. Програма выдала ошибку",
            reply_markup=select_command,
        )
        return
    try:
        priority_int: int = urgency_to_int(priority)
    except KeyError:
        logging.warning(
            "process_to_select_priority(): title = _%s_ description = _%s_ priority = _%s",
            title,
            description,
            priority,
        )
        await Form.logged_in.set()
        await bot.send_message(
            user_id,
            "Заявка не принята. Неправильно выбран приоритет",
            reply_markup=select_command,
        )
        return

    try:
        ticket_id = user_session.create_ticket(
            title=title, description=description, urgency=priority_int
        )
    except StupidError as err:
        message_text: str = f"Произошла неизвестная науке ошибка {err}"
    else:
        # message_text = f'Заявка "{title}" принята. Номер заявки - [{ticket_id}]({GLPI_TICKET_URL}{ticket_id})'
        message_text = f'Заявка "{title}" принята. Номер заявки - <a href="{GLPI_TICKET_URL}{ticket_id}">{ticket_id}</a>'
    logging.info(message_text)
    await bot.send_message(user_id, message_text, reply_markup=select_command)
    await Form.logged_in.set()
async def process_to_explain_decline(
    user_id: int, text: str, state: FSMContext
) -> None:
    """ Process declitation explain and decline ticket """
    user_session = UserSession(user_id)
    await user_session.create(state)
    ticket_id: int = int(await user_session.pop_field(key="ticket_id"))
    result = user_session.refuse_ticket_solition(ticket_id, text)
    logging.info("user_session.refuse_ticket_solition = %s", result)
    await Form.logged_in.set()
    await bot.send_message(user_id, "Принято", reply_markup=select_command)
async def refuse_solution(
    callback_query: types.CallbackQuery,
    state: FSMContext,
) -> None:
    """ Start a process of refusal """
    callback_id: str = callback_query.id
    user_id: int = callback_query.from_user.id
    ticket_id: int = int(callback_query.data.split(":")[1])
    message_id: int = callback_query.message.message_id
    message_text: str = callback_query.message.text
    user_session = UserSession(user_id)
    await user_session.create(state)
    await user_session.add_field("ticket_id", str(ticket_id))

    await Form.to_explain_decline.set()
    await bot.send_message(
        user_id, "Заполните причину отклонения", reply_markup=select_cancel_create
    )
    ddd = await bot.answer_callback_query(callback_id)
    logging.info("callback done %s", ddd)
    await bot.edit_message_text(
        message_text,
        chat_id=user_id,
        message_id=message_id,
        reply_markup=None,
    )
async def list_all_tickets(user_id: int, state: FSMContext) -> None:
    """/list command handler

    Args:
        user_id (int): telegram user id that issued command
    """
    user_session = UserSession(user_id=user_id)
    await user_session.create(state=state)
    try:
        list_tickets: Dict[int, Dict] = user_session.get_all_my_tickets(
            open_only=True, full_info=True
        )
    except glpi_api.GLPIError as err:
        logging.error("Ошибка %s", err)
        # TODO Catch error properly
        return

    if len(list_tickets) == 0:
        logging.info(
            "User ID %d issued /tickets command and there are no tickets", user_id
        )
        await bot.send_message(user_id, "Список заявок пуст", reply_markup=select_command)
        return
    logging.info("list_tickets = %s", list_tickets)
    logging.info("list_tickets = %s", list(enumerate(list_tickets)))
    for ticket_id in list_tickets:
        current_ticket = list_tickets[ticket_id]
        try:
            logging.info("current_ticket = %s", current_ticket)
            await bot.send_message(
                chat_id=user_id,
                # text=show_ticket(user_session.get_one_ticket(current_ticket)),
                text=show_ticket(current_ticket),
                reply_markup=select_command,
            )
        except glpi_api.GLPIError as err:
            logging.error("Ошибка %s", err)
            # TODO Catch error properly
            return
async def approve_solution(
    callback_query: types.CallbackQuery,
    state: FSMContext,
) -> None:
    """ Approve solution """
    callback_id: str = callback_query.id
    message_id: int = callback_query.message.message_id
    message_text: str = callback_query.message.text
    user_id: int = callback_query.from_user.id
    ticket_id: int = int(callback_query.data.split(":")[1])
    user_session = UserSession(user_id)
    await user_session.create(state)
    user_session.approve_ticket_solution(ticket_id)
    # ddd= await bot.answer_callback_query(callback_id,text="TEXT",show_alert=True)
    # logging.info("callback done %s", ddd)
    await bot.edit_message_text(
        message_text,
        chat_id=user_id,
        message_id=message_id,
        reply_markup=keyboard.select_repeat_ticket(ticket_id),
    )
    ddd = await bot.answer_callback_query(callback_id)
    logging.info("callback done %s", ddd)
async def process_to_enter_description(
    user_id: int, description: str, state: FSMContext
) -> None:
    """Create new ticket with provided title and description

    Args:
        user_id (int): telegram user id that issued command
        description (str): title for new ticket
    """
    user_session = UserSession(user_id)
    await user_session.create(state)
    await user_session.add_field("description", description)

    await Form.to_select_priority.set()
    await bot.send_message(
        user_id, "Выберите приоритет", reply_markup=select_urgent_keyboard
    )
async def process_to_enter_title(user_id: int, title: str, state: FSMContext) -> None:
    """Create new ticket with provided title

    Args:
        user_id (int): telegram user id that issued command
        title (str): title for new ticket
    """
    user_session = UserSession(user_id)
    await user_session.create(state=state)
    await user_session.add_field(key="title", data=title)

    await Form.to_enter_description.set()
    await bot.send_message(
        user_id,
        f'Тема вашей заявки "{title}". Введите описание возникшей проблемы',
        reply_markup=select_cancel_create,
    )
async def start_message(user_id: int, state: FSMContext) -> None:
    """/start command handler

    Args:
        user_id (int): telegram user id that issued /start command
        state (FSMContext): state for specific user
    """
    user_session = UserSession(user_id=user_id)
    await user_session.create(state=state)
    if user_session.is_logged_in:
        logging.info("User %d already logged in", user_id)
        await Form.logged_in.set()
        await bot.send_message(
            user_id,
            "Вы ужe залогинены. Если хотите разлогинится, то введите /logout. Что бы увидеть все команды, введите /help",
            reply_markup=select_command,
        )
        return
    logging.info("User ID %d is not logged in", user_id)
    await onboarding.onboarding_start(user_id)
async def run_check(dbhelper: DBHelper) -> None:
    """Check for every user if it has updates"""
    # TODO add error catch
    for user_id in dbhelper.all_user():
        logging.info("checker.run_check: user_id = %s", user_id)
        user_session: UserSession = UserSession(user_id)
        await user_session.create(dbhelper=dbhelper)
        if not user_session.is_logged_in or user_session.glpi_id is None:
            # TODO notify user if he is suddenly unlogged (due password change or else)
            continue
        old_tickets: typing.Dict[int, typing.Dict] = dbhelper.all_tickets_glpi(
            user_session.glpi_id)
        try:
            new_tickets: typing.Dict[
                int,
                typing.Dict] = user_session.get_all_my_tickets(open_only=False,
                                                               full_info=False)
        except GLPIError as err:
            # logging.info(err.__dict__)
            error_text = str(err)
            logging.info("error_text = %s", error_text)
            if "Incorrect username or password" in error_text:
                await generic.logout(
                    user_id,
                    FSMContext(storage=dbhelper, chat=user_id, user=user_id))
                continue
            else:
                raise

        logging.debug("checker.run_check: old_tickets = %d %s",
                      len(old_tickets), old_tickets)
        logging.debug("checker.run_check: new_tickets = %d %s",
                      len(new_tickets), new_tickets)
        messages, have_changes = check_diff(old_tickets,
                                            new_tickets,
                                            user_session=user_session)
        if have_changes:
            dbhelper.write_tickets_glpi(glpi_id=user_session.glpi_id,
                                        data=new_tickets)
            logging.info("checker.run_check: messages = %s", messages)
            await process_messages(user_id, *messages)
def check_diff(
    old_ticket_dict: typing.Dict[int, typing.Dict],
    new_ticket_dict: typing.Dict[int, typing.Dict],
    user_session: UserSession,
) -> typing.Tuple[typing.Tuple[typing.Dict, typing.Dict, typing.Dict], bool]:
    """ Read old data, compare it with the new data, rewrite new data, return diff """
    logging.info("len(old_ticket_dict) = %s", len(old_ticket_dict))
    logging.info("len(new_ticket_dict) = %s", len(new_ticket_dict))
    logging.info("old_ticket_dict = %s", old_ticket_dict)
    logging.info("new_ticket_dict = %s", new_ticket_dict)
    # old_ticket_dict: typing.Dict[
    #     int, typing.Dict[str, typing.Union[None, int, str]]
    # ] = {ticket["id"]: ticket for ticket in old_tickets_list}
    # new_ticket_dict: typing.Dict[
    #     int, typing.Dict[str, typing.Union[None, int, str]]
    # ] = {ticket["id"]: ticket for ticket in new_tickets_list}

    messages: typing.Dict[int, str] = dict()
    have_changes: bool = False
    proposed_solutions = dict()
    closed_tickets = dict()
    for ticket_id in old_ticket_dict.keys() | new_ticket_dict.keys():
        logging.info("got ticket_id %s", ticket_id)
        logging.info(
            "in old_ticket_dict: %s, in new_ticket_dict: %s",
            ticket_id in old_ticket_dict,
            ticket_id in new_ticket_dict,
        )
        if ticket_id in old_ticket_dict and ticket_id in new_ticket_dict:
            for elem in old_ticket_dict[ticket_id]:
                # logging.info(f"old_ticket_dict[ticket_id] = {old_ticket_dict[ticket_id]}")
                # logging.info(f"type = {type(old_ticket_dict[ticket_id])} elem = {type(elem)} {elem}")
                if old_ticket_dict[ticket_id].get(
                        elem, None) != new_ticket_dict[ticket_id].get(
                            elem, None):
                    have_changes = True
            old_status = old_ticket_dict[ticket_id].get(STATUS, None)
            new_status = new_ticket_dict[ticket_id].get(STATUS, None)
            if old_status != new_status:
                name: str = (
                    '"' + str(new_ticket_dict[ticket_id].get("name", None)) +
                    '"')
                date_mod: str = str(new_ticket_dict[ticket_id].get(
                    "date_mod", None))
                # messages[ticket_id] = f"Status: old = {old_status} new = {new_status}"
                if new_status == 1:  # Новый
                    messages[ticket_id] = (
                        f"Ваша заявка с номером <a href=\"{GLPI_TICKET_URL}{ticket_id}\">{ticket_id} {name}</a> пересоздана."
                        + f" Дата и время назначения: {date_mod}")
                elif new_status == 2:  # В работе (назначена)
                    messages[ticket_id] = (
                        f"Ваша заявка с номером <a href=\"{GLPI_TICKET_URL}{ticket_id}\">{ticket_id} {name}</a> назначена."
                        + f" Дата и время назначения: {date_mod}")
                elif new_status == 3:  # В работе (запланирована)
                    messages[ticket_id] = (
                        f"Ваша заявка с номером <a href=\"{GLPI_TICKET_URL}{ticket_id}\">{ticket_id} {name}</a> запланирована."
                        + f" Дата и время изменения: {date_mod}")
                elif new_status == 4:  # Ожидает ответа от заявителя
                    messages[ticket_id] = (
                        f"Ваша заявка с номером <a href=\"{GLPI_TICKET_URL}{ticket_id}\">{ticket_id} {name}</a>"
                        +
                        f" ожидает ответа от заявителя. Дата и время изменения: {date_mod}"
                    )
                elif new_status == 5:  # Решена
                    solution: str = user_session.get_last_solution(ticket_id)
                    messages[ticket_id] = (
                        f"По Вашей заявке с номером <a href=\"{GLPI_TICKET_URL}{ticket_id}\">{ticket_id} {name}</a>"
                        +
                        f" предложено решение: {solution}.\nДата и время изменения: {date_mod}"
                    )
                    proposed_solutions[ticket_id] = messages[ticket_id]
                    # TODO Add buttons
                elif new_status == 6:  # Закрыто
                    messages[ticket_id] = ""
                    closed_tickets[ticket_id] = messages[ticket_id]
                    # TODO Add button
                else:
                    messages[
                        ticket_id] = f"Status: old = {old_status} new = {new_status}"
                    logging.error("UNKNOWN STATUS: old = %s new = %s",
                                  old_status, new_status)
                    logging.error("old_ticket = %s",
                                  old_ticket_dict[ticket_id])
                    logging.error("new_ticket = %s",
                                  new_ticket_dict[ticket_id])
        elif ticket_id in old_ticket_dict and ticket_id not in new_ticket_dict:
            logging.info("Deleted ticket: %s", old_ticket_dict[ticket_id])
            # TODO Think about it
            messages[
                ticket_id] = f"Ваша заявка с номером {ticket_id} \"{old_ticket_dict[ticket_id].get('name',None)}\" удалена."
            have_changes = True
        elif ticket_id not in old_ticket_dict and ticket_id in new_ticket_dict:
            logging.info("New ticket %s", new_ticket_dict[ticket_id])
            # TODO Think about it
            # messages[ticket_id] = "NEW"
            have_changes = True
        elif ticket_id not in old_ticket_dict and ticket_id in new_ticket_dict:
            logging.error(
                "Impossible: ticket_id not in old_ticket_dict and ticket_id in new_ticket_dict"
            )
            logging.error("ticket_id = %s", ticket_id)
            logging.error("old_ticket_dict = %s", old_ticket_dict)
            logging.error("new_ticket_dict = %s", new_ticket_dict)
            raise StupidError("Never ever can I get here")
        else:
            logging.error("Impossible: ELSE")
            logging.error("ticket_id = %s", ticket_id)
            logging.error("old_ticket_dict = %s", old_ticket_dict)
            logging.error("new_ticket_dict = %s", new_ticket_dict)
            raise StupidError("Never ever can I get here")

        continue
    logging.info("messages = %s", messages)
    return (messages, proposed_solutions, closed_tickets), have_changes