def start_state(bot, update, context):
    chat = update.effective_chat
    chat_id = chat.id
    user = user_service.create_or_get_user(chat)

    if is_callback(update):
        deserialized = deserialize_data(update.callback_query.data)
        action = deserialized[CallbackData.ACTION]

        if action is Action.SELECTED_LANG:
            lang = deserialized[CallbackData.DATA]
            context[CONTEXT_LANG] = Language(lang)

    lang = context[CONTEXT_LANG]
    welcome_text = concat_username(
        emoji_rocket + '*', user,
        ', ' + message_source[lang]['state.start_state.welcome'])

    num_all, num_upcoming, num_completed = task_service.find_stats_for_user(
        chat_id)
    bot_ver = 19  # because why not? :)
    completed_percent = 0 if 0 == num_all else int(num_completed / num_all *
                                                   100)
    num_completed = f'{num_completed} ({completed_percent} %)'
    num_upcoming = f'{num_upcoming} ({0 if 0 == num_all else (100 - completed_percent)} %)'
    welcome_text = welcome_text.format(num_upcoming, num_completed, num_all,
                                       bot_ver)

    bot.send_message(chat_id=chat_id,
                     text=emojize(welcome_text, use_aliases=True),
                     parse_mode=ParseMode.MARKDOWN,
                     reply_markup=kb.StartStateKb(lang).build())
def notification_callback(bot, job):
    payload = job.context  # passed here via run_once(.. context=...)

    if payload:
        # chat id can be not int. e.g. '@username' is chat_id too
        chat_id, task_id = map(payload.get, (PAYLOAD_CHAT_ID, PAYLOAD_TASK_ID))
        chat_id = int(chat_id)

        task = task_service.find_task_by_id_and_user_id(task_id, chat_id)
        if task.is_task_enabled() is False or task.is_task_completed():
            log.info(
                f'Notification for task {task_id} in chat ({chat_id}) is disabled. Skipping'
            )
            return

        lang = g.automata.get_context(chat_id)[CONTEXT_LANG]

        user = user_service.find_user_by_id(chat_id)

        reminder = message_source[lang]['state.edit_date.reminder'].format(
            task.get_description(), readable_datetime(task.get_create_date()))
        reply_text = concat_username(emoji_mortal_reminder + '*', user,
                                     ', ' + reminder)

        markup = kb.ViewTaskKb(task_id, lang).build()

        bot.send_message(chat_id=chat_id,
                         text=emojize(reply_text, use_aliases=True),
                         parse_mode=ParseMode.MARKDOWN,
                         reply_markup=markup)

    else:
        log.error('No payload found in notification callback')
def all_tasks_state(bot, update, context):
    chat = update.effective_chat
    chat_id = chat.id
    lang = context[CONTEXT_LANG]
    user = user_service.create_or_get_user(chat)
    user_id = chat_id

    action = None
    if is_callback(update):
        deserialized = deserialize_data(update.callback_query.data)
        action = deserialized[CallbackData.ACTION]

    tasks = []
    text = None
    if not is_callback(update) or action is Action.LIST_ALL:
        tasks = task_service.find_tasks_by_user_id(user_id)
        text = concat_username(
            emoji_all + '*', user,
            ', ' + message_source[lang]['state.all_tasks.tasks.all'])

    elif action is Action.LIST_UPCOMING:
        tasks = task_service.find_upcoming_tasks_by_user_id(user_id)
        text = concat_username(
            emoji_upcoming + '*', user,
            ', ' + message_source[lang]['state.all_tasks.tasks.upcoming'])

    elif action is Action.LIST_COMPLETED:
        tasks = task_service.find_completed_tasks_by_user_id(user_id)
        text = concat_username(
            emoji_completed + '*', user,
            ', ' + message_source[lang]['state.all_tasks.tasks.completed'])

    if 0 == len(tasks):
        text_no_tasks = message_source[lang]['state.all_tasks.no_tasks_yet']
        notes = message_source[lang]['state.all_tasks.notes.no_tasks_yet']
        text = concat_username(emoji_search + '*', user,
                               ', ' + text_no_tasks + notes)

    else:
        text += message_source[lang]['state.all_tasks.notes']

    tasks = sorted(tasks, key=lambda t: t.get_create_date())
    bot.send_message(chat_id=chat_id,
                     text=emojize(text, use_aliases=True),
                     parse_mode=ParseMode.MARKDOWN,
                     reply_markup=kb.TasksAsButtons(tasks, lang).build())
def select_lang_state(bot, update, context):
    chat = update.effective_chat
    chat_id = chat.id

    user = user_service.create_or_get_user(chat)

    action = None
    if is_callback(update):
        deserialized = deserialize_data(update.callback_query.data)
        action = deserialized[CallbackData.ACTION]

    # find out what are we doing now? selecting lang or just showing available langs
    text = None
    if action is Action.SELECTED_LANG:
        deserialized = deserialize_data(update.callback_query.data)
        lang_string = deserialized[CallbackData.DATA]
        lang = Language(lang_string)
        context[CONTEXT_LANG] = lang

        text = message_source[lang]['btn.select_lang.' + lang_string +
                                    '.result']
        buttons = kb.StartStateKb(lang).build()

    elif action is Action.VIEW_LANG:
        # It's callback. nevertheless just show available langs
        lang = context[CONTEXT_LANG]
        text = concat_username(
            emoji_globe + '*', user,
            ', ' + message_source[lang]['state.select_lang'])
        buttons = kb.SelectLangKb(lang).build()

    else:
        # It's not callback. just show available langs
        lang = context[CONTEXT_LANG]
        text = concat_username(
            emoji_globe + '*', user,
            ', ' + message_source[lang]['state.select_lang'])
        buttons = kb.SelectLangKb(lang).build()

    bot.send_message(chat_id=chat_id,
                     text=emojize(text, use_aliases=True),
                     parse_mode=ParseMode.MARKDOWN,
                     reply_markup=buttons)