def request_language_setup(update: Update, context: CallbackContext) -> None: setup_state, _ = get_user_states(str(update.effective_chat.id)) if setup_state is not SetupState.LANGUAGE_NOT_SET: logger.error('request_language_setup being called when state is not LANGUAGE_NOT_SET') return None if update.callback_query is None or update.callback_query.data not in _callback_query_options: reply_markup = InlineKeyboardMarkup(_lang_inline_keyboard_buttons) send_markup_msg(update, strings()["language:choose"], reply_markup) else: query = update.callback_query lang = Language(query.data.split("#")[1]) if lang is Language.ENGLISH: set_strings(force_lang=Language.ENGLISH) else: set_strings(force_lang=Language.PORTUGUESE) set_language(chat_id=str(update.callback_query.message.chat.id), lang=lang, next_state=SetupState.TIMEZONE_NOT_SET) try: set_timezone(str(update.effective_chat.id), int(os.environ.get('TIMEZONE_SECONDS_OFFSET', None)), SetupState.NONE) send_markup_msg(update, strings()['setup:complete'], options_kbd(strings())) except Exception as e: logger.warn(e) logger.warn('TIMEZONE_SECONDS_OFFSET not present or in wrong format.\n' 'Switching to fallback mode with google maps api.') edit_message(update, strings()['language:set']) send_message(update, strings()['timezone:request']) raise DispatcherHandlerStop
def request_clock_out_description(update: Update, context: CallbackContext): chat_id = update.effective_chat.id try: user_info = get_user_info(chat_id) msg_id = int(user_info['msg_reply_id']) clock_out_date = user_info['clock_out_date'] except KeyError: return if not update.effective_message.reply_to_message or update.effective_message.reply_to_message.message_id != msg_id: try: update.effective_message.bot.delete_message(chat_id=chat_id, message_id=msg_id) except BadRequest: logger.warn( 'The message to be replied has been deleted by the user, but we\'ll try again' ) msg = send_markup_msg(update, strings()['feedback:exit:insist_reply'], ForceReply(), True) set_clock_out_msg_reply_id(chat_id, msg.message_id) raise DispatcherHandlerStop else: replied_entry_description(chat_id, update.effective_message.text, clock_out_date) send_markup_msg(update, strings()['feedback:exit:acknowledgment'], options_kbd(strings())) remove_msg_reply_id(chat_id) raise DispatcherHandlerStop
def start(update: Update, context: CallbackContext): _, chat_state = get_user_states(str(update.effective_message.chat_id)) if chat_state in _sensitive_states: logger.debug(f"Not letting user perform a restart because they're in {chat_state} state") send_markup_msg(update, strings()['start:prohibit'], options_kbd(strings())) else: create_or_reset_user(str(update.message.chat_id)) request_language_setup(update, context) raise DispatcherHandlerStop
def ensure_valid_clocked_in(update: Update, context: CallbackContext): # if the chat_state is clocked_in but there are no records today, this means that # the user forgot to clock out the other day. chat_id = update.effective_chat.id if len(today_entries(chat_id)) == 0: user_info = get_user_info(chat_id) clocked_in_date = datetime.fromisoformat(user_info['clocked_in_date']) # we are in an inconsistent state # check if there's any msg waiting for a reply # if not, create one try: msg_id = int(user_info['msg_reply_id']) if not update.effective_message.reply_to_message \ or update.effective_message.reply_to_message.message_id != msg_id: try: update.effective_message.bot.delete_message(chat_id=chat_id, message_id=msg_id) except BadRequest: logger.warn('The message to be replied has been deleted by the user, but we\'ll try again') generate_reply(update, context, clocked_in_date.isoformat()) raise DispatcherHandlerStop else: msg = update.effective_message.text time = msg.split('\n')[0] if len(time) != 5: # hh:mm raise ValueError('It should be in format hh:mm') hour, minute = int(time.split(':')[0]), int(time.split(':')[1]) clock_out_date = clocked_in_date.replace(hour=hour, minute=minute, microsecond=clocked_in_date.microsecond + 1).isoformat() description = msg.split('\n')[0].strip() if len(description) == 0: raise ValueError('Description should not be empty') if clock_out_date < clocked_in_date.isoformat(): raise ValueError('Clock out date must be greater than the clock in date') create_full_entry(chat_id, clock_out_date, description) remove_msg_reply_id(chat_id) send_markup_msg(update, strings()['request:acknowledgment'], options_kbd(strings())) raise DispatcherHandlerStop except DispatcherHandlerStop: raise DispatcherHandlerStop except Exception as e: logger.error(e) generate_reply(update, context, clocked_in_date.isoformat()) raise DispatcherHandlerStop finally: raise DispatcherHandlerStop
def request_timezone_setup(update: Update, context: CallbackContext) -> None: state, _ = get_user_states(str(update.effective_chat.id)) if state is not SetupState.TIMEZONE_NOT_SET: logger.error( 'request_timezone_setup being called when state is not TIMEZONE_NOT_SET' ) return if update.message is not None: try: tz = get_address_and_timezone_by_name(update.message.text) is_negative = "-" if tz['offset'] < 0 else "" pretty_offset = is_negative + str( timedelta(seconds=abs(tz['offset'])))[:-3] _inline_keyboard_buttons = [[ InlineKeyboardButton( strings()['button:yes'], callback_data=f"timezone#yes#{pretty_offset}"), InlineKeyboardButton(strings()['button:no'], callback_data='timezone#no'), ]] reply_markup = InlineKeyboardMarkup(_inline_keyboard_buttons) tz_msg = f"regx*x{tz['timezone_id']}regx*x \nregx*xUTC{pretty_offset}regx*x\n" full_msg = strings()["timezone:location:confirm"].format(tz_msg) send_markup_msg(update, full_msg, reply_markup, True) except LocationNotFoundException: send_message(update, strings()['timezone:location:not_found']) else: query = update.callback_query if not any(cb_option in query.data for cb_option in _callback_query_options): send_message(update, strings()['timezone:request']) else: if 'timezone#no' in query.data: send_message(update, strings()['timezone:request']) else: pretty_offset = query.data.split('#')[-1] hours, minutes = map(int, pretty_offset.split(':')) minutes = minutes if hours >= 0 else minutes * (-1) offset = (hours * 60 * 60) + (minutes * 60) set_timezone(str(update.effective_chat.id), offset, SetupState.NONE) send_markup_msg(update, strings()['setup:complete'], options_kbd(strings())) raise DispatcherHandlerStop
def edit_choice_selector(update: Update, context: CallbackContext): kbd = [ [InlineKeyboardButton(strings()['button:today'], callback_data='edit#today')], [InlineKeyboardButton(strings()['button:yesterday'], callback_data='edit#yesterday')], [InlineKeyboardButton(strings()['button:other'], callback_data='edit#other')], ] try: edit_message(update, strings()['edit:choose:message'], True, InlineKeyboardMarkup(kbd)) except BadRequest as e: send_markup_msg( update, strings()['edit:choose:message'], InlineKeyboardMarkup(kbd), True, ) raise DispatcherHandlerStop
def clockin(update: Update, context: CallbackContext) -> None: chat_id = str(update.message.chat_id) _date = clock_in(chat_id) entries = today_entries(chat_id) if len(entries) % 2 == 0: msg = send_markup_msg(update, strings()['feedback:exit'], ForceReply(), True) set_unreported_clock_out(chat_id, msg.message_id, _date) raise DispatcherHandlerStop else: send_message(update, strings()['feedback:entrance']) raise DispatcherHandlerStop
def choose_report(update: Update, context: CallbackContext) -> None: class InlineKeyboardOptions(Enum): TODAY = strings()['button:today'] YESTERDAY = strings()['button:yesterday'] WEEK = strings()['button:report:week'] MONTH = strings()['button:report:month'] LAST_WEEK = strings()['button:report:last_week'] LAST_MONTH = strings()['button:report:last_month'] inline_keyboard_buttons = [ [ InlineKeyboardButton(InlineKeyboardOptions.TODAY.value, callback_data="report#today") ], [ InlineKeyboardButton(InlineKeyboardOptions.YESTERDAY.value, callback_data="report#yesterday") ], [ InlineKeyboardButton(InlineKeyboardOptions.WEEK.value, callback_data="report#week") ], [ InlineKeyboardButton(InlineKeyboardOptions.MONTH.value, callback_data="report#month") ], [ InlineKeyboardButton(InlineKeyboardOptions.LAST_WEEK.value, callback_data="report#last_week") ], [ InlineKeyboardButton(InlineKeyboardOptions.LAST_MONTH.value, callback_data="report#last_month") ], ] reply_markup = InlineKeyboardMarkup(inline_keyboard_buttons) send_markup_msg(update, strings()["report:period:choose"], reply_markup) raise DispatcherHandlerStop
def generate_reply(update: Update, context: CallbackContext, clocked_in_date: str) -> None: chat_id = update.effective_chat.id that_day_entries = _that_day_entries(chat_id, clocked_in_date) compiled_entries = [ strings()['request:warning'], datetime.fromisoformat(clocked_in_date).strftime('%d/%m/%Y') + '\n', compile_entries(that_day_entries) + '\n', strings()['request:missing_entry'] ] msg = send_markup_msg(update, "\n".join(compiled_entries), ForceReply(), True) set_msg_reply_id(chat_id, msg.message_id)
def delete(update: Update, context: CallbackContext): _del_inline_keyboard_buttons = [ [ InlineKeyboardButton(strings()['button:yes'], callback_data='delete#yes') ], [ InlineKeyboardButton(strings()['button:no'], callback_data='delete#no') ], ] _callback_query_options = ['delete#yes', 'delete#no'] chat_id = update.effective_chat.id if update.callback_query is None or update.callback_query.data not in _callback_query_options: reply_markup = InlineKeyboardMarkup(_del_inline_keyboard_buttons) send_markup_msg(update, strings()["delete:confirm"], reply_markup, True) raise DispatcherHandlerStop else: if update.callback_query.data == 'delete#yes': deleted_user_entries = db.delete_user_entries(chat_id) deleted_user_info = db.delete_user_info(chat_id) if not deleted_user_entries and not deleted_user_info: update.effective_message.delete() send_markup_msg( update, strings()['delete:nothing'], ReplyKeyboardMarkup([['/start']], resize_keyboard=True, one_time_keyboard=True)) else: update.effective_message.delete() send_markup_msg( update, strings()['delete:success'], ReplyKeyboardMarkup([['/start']], resize_keyboard=True, one_time_keyboard=True)) raise DispatcherHandlerStop else: edit_message(update, strings()['delete:cancelled']) raise DispatcherHandlerStop
def default(update: Update, context: CallbackContext): send_markup_msg(update, strings()['default'], options_kbd(strings())) raise DispatcherHandlerStop
def help_message(update: Update, context: CallbackContext): send_markup_msg(update, strings()['help'], help_inline_kdb(), True) raise DispatcherHandlerStop
def options(update: Update, context: CallbackContext): send_markup_msg(update, strings()['options:choose'], options_inline_kdb()) raise DispatcherHandlerStop