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
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