diff --git a/app/controllers/reservation.py b/app/controllers/reservation.py index f6fd4380d91e01142c7c8da183fc70d98f79c026..b9c6d8080d73ec3d13345eab989cbd749fca31ad 100644 --- a/app/controllers/reservation.py +++ b/app/controllers/reservation.py @@ -20,14 +20,16 @@ class ReservationController(): if not user.is_confirmed: raise UserNotConfirmedError - record = Reservation() - record.user = user - record.time_start = datetime.now() - record.time_end = datetime.now() + current_app.config.get('RESERVATION_DURATION', timedelta(hours=1)) + reservation = Reservation() + reservation.user = user + reservation.time_start = datetime.now() + reservation.time_end = datetime.now() + current_app.config.get('RESERVATION_DURATION', timedelta(hours=1)) - db.session.add(record) + db.session.add(reservation) db.session.commit() + return reservation + @classmethod def has_active_reservation(cls, user): diff --git a/app/controllers/user.py b/app/controllers/user.py index 8f3ca6e316719f11bc2b2cb6a7766236dde701a6..49653b607c14fa1355dc978f2fc2615187220de4 100644 --- a/app/controllers/user.py +++ b/app/controllers/user.py @@ -29,7 +29,7 @@ class UserController(): debug=False) or \ User.query.filter(User.email == email).count(): errors.append('email') - if telegram_id is not None and User.query.filter(User.telegram_id == telegram_id): + if telegram_id is not None and User.query.filter(User.telegram_id == telegram_id).count() > 0: errors.append('telegram_id') if telegram_id is None and (password is None or len(password) == 0): errors.append('password') @@ -62,7 +62,19 @@ class UserController(): db.session.add(user) db.session.commit() - cls._send_confirm_email(user) + cls.send_confirm_email(user) + + + @staticmethod + def update(user, name=None, email=None, telegram_chat_id=None): + if name: + user.name = name + if email: + user.email = email + user.is_confirmed = False + if telegram_chat_id: + user.telegram_chat_id = telegram_chat_id + db.session.commit() @staticmethod @@ -75,6 +87,11 @@ class UserController(): return User.query.filter(User.email == email).first() + @staticmethod + def get_by_telegram_user_id(telegram_user_id): + return User.query.filter(User.telegram_id == telegram_user_id).first() + + @classmethod def verify_confirm_email(cls, email, token): user = cls.get_by_email(email) @@ -139,19 +156,35 @@ class UserController(): RecordController.terminate(record) + @classmethod + def unlink_telegram_user_id(cls, telegram_user_id): + user = cls.get_by_telegram_user_id(telegram_user_id) + + if user: + user.telegram_id = None + user.telegram_chat_id = None + db.session.commit() + + @staticmethod - def send_password_reset_email(user): + def get_password_reset_link(user): user.generate_new_token() db.session.commit() + return url_for('login.password_reset', token=user.token, email=user.email, _external=True) + + + @classmethod + def send_password_reset_email(cls, user): + password_reset_link = cls.get_password_reset_link(user) msg = Message(subject='[Bastli Bouncer] Reset Password', - body=render_template('email/password_reset.txt', user=user), + body=render_template('email/password_reset.txt', user=user, password_reset_link=password_reset_link), recipients=[user.email]) - print(url_for('login.password_reset', token=user.token, email=user.email, _external=True)) + print(password_reset_link) mail.send(msg) @staticmethod - def _send_confirm_email(user): + def send_confirm_email(user): msg = Message( subject='[Bastli Bouncer] Confirm Email Address', body=render_template('email/confirm.txt', user=user), diff --git a/app/templates/email/password_reset.txt b/app/templates/email/password_reset.txt index 7edb49afb0e271a1e42a2c1fe6c902d8724a7551..ed817249c56d32dd252aa4643be758674839aa0e 100644 --- a/app/templates/email/password_reset.txt +++ b/app/templates/email/password_reset.txt @@ -5,6 +5,6 @@ If that was not you, this message can be ignored. Please follow the instructions at the link below. -{{ url_for('login.password_reset', token=user.token, email=user.email, _external=True) }} +{{ password_reset_link }} The link will expire after some time. diff --git a/bot/bot.py b/bot/bot.py index e9fd8f2982977598a92743975ab984cd8544ff0d..869627c7feb5fa8eee9a25e39b4913f54f4b68fa 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -1,11 +1,9 @@ from telegram.ext import Updater, CommandHandler, MessageHandler, Filters from .commands.help import help_command, start_command -from .commands.free_workplaces import freeworkplaces_command - -# def echo(update, context): -# """Echo the user message.""" -# update.message.reply_text(update.message.text) +from .commands.workplace import freeworkplaces_command, reserve_command, cancel_command, enter_command, leave_command +from .commands.user import status_command, resetpassword_command, resendconfirmationemail_command +from .commands.register import register_conv_handler def create_bot(token): @@ -14,12 +12,16 @@ def create_bot(token): # Get the dispatcher to register handlers dp = updater.dispatcher - # on different commands - answer in Telegram - dp.add_handler(CommandHandler("start", start_command)) - dp.add_handler(CommandHandler("help", help_command)) - dp.add_handler(CommandHandler("freeworkplaces", freeworkplaces_command)) - - # on noncommand i.e message - echo the message on Telegram - # dp.add_handler(MessageHandler(Filters.text, echo)) + dp.add_handler(CommandHandler('start', start_command)) + dp.add_handler(CommandHandler('help', help_command)) + dp.add_handler(CommandHandler('freeworkplaces', freeworkplaces_command)) + dp.add_handler(CommandHandler('resetpassword', resetpassword_command)) + dp.add_handler(CommandHandler('resendconfirmationemail', resendconfirmationemail_command)) + dp.add_handler(CommandHandler('status', status_command)) + dp.add_handler(CommandHandler('reserve', reserve_command)) + dp.add_handler(CommandHandler('cancel', cancel_command)) + dp.add_handler(CommandHandler('enter', enter_command)) + dp.add_handler(CommandHandler('leave', leave_command)) + dp.add_handler(register_conv_handler) return updater diff --git a/bot/commands/free_workplaces.py b/bot/commands/free_workplaces.py deleted file mode 100644 index 9613dc7e9443665b68c47107e78a22f6179a0936..0000000000000000000000000000000000000000 --- a/bot/commands/free_workplaces.py +++ /dev/null @@ -1,10 +0,0 @@ -from telegram import ParseMode -from app.controllers import FreeWorkplacesController -from app import app_context - -def freeworkplaces_command(update, context): - """Send a message when the command /freeworkplaces is issued.""" - with app_context(): - free_workplaces = FreeWorkplacesController.get_free_workplaces() - response = '*' + str(free_workplaces) + '* workplaces are currently available.' - update.message.reply_text(response, parse_mode=ParseMode.MARKDOWN) diff --git a/bot/commands/help.py b/bot/commands/help.py index 8870bf52a4e5d292fa6e6fe620e5aa4bf27d4106..4759475f3046bf31c888f8ba5022887ab5492ecc 100644 --- a/bot/commands/help.py +++ b/bot/commands/help.py @@ -1,21 +1,24 @@ -from telegram import ParseMode +from telegram import ParseMode, ReplyKeyboardRemove def start_command(update, context): """Send a message when the command /start is issued.""" - start_text = 'Hi!\n\n' + name = update.message.from_user.first_name + start_text = 'Hi ' + name + '!\n\n' start_text += 'I\'m the Bastli bouncer bot.\n' start_text += 'My job is to ensure that the workshop doesn\'t get crowded and to maintain the user list.\n\n' start_text += 'Please follow the rules below:\n\n' start_text += '*Rule 1*\n' - start_text += 'Register yourself with /register [email] [name]\n\n' + start_text += 'Register yourself with /register.\n\n' start_text += '*Rule 2*\n' - start_text += 'Check for free workplaces and reserve one before you come here.\n\n' + start_text += 'Check for free workplaces and /reserve one before you come here.\n\n' start_text += '*Rule 3*\n' - start_text += 'Send /enter when you enter the workshop\n\n' + start_text += 'Send /enter when you enter the workshop.\n\n' start_text += '*Rule 4*\n' - start_text += 'Send /leave when you leave the workshop\n\n\n' + start_text += 'Send /leave when you leave the workshop.\n\n\n' start_text += 'See /help for more information about the commands.' - update.message.reply_text(start_text, parse_mode=ParseMode.MARKDOWN) + update.message.reply_text(start_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) def help_command(update, context): @@ -23,8 +26,8 @@ def help_command(update, context): help_text = '*Bastli Bouncer bot usage:*\n\n' help_text += '/freeworkplaces\n' help_text += ' _Get available workplaces._\n' - help_text += '/register [email] [name]\n' - help_text += ' _Create an account._\n' + help_text += '/register\n' + help_text += ' _Initiate account creation._\n' help_text += '/resetpassword\n' help_text += ' _Request password reset._\n' help_text += '/status\n' @@ -37,4 +40,6 @@ def help_command(update, context): help_text += ' _Request for entry._\n' help_text += '/leave\n' help_text += ' _Free the workspace._\n' - update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) + update.message.reply_text(help_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) diff --git a/bot/commands/register.py b/bot/commands/register.py new file mode 100644 index 0000000000000000000000000000000000000000..2e9315bf0022f8d0ae8ebd949132fbcd4d69f147 --- /dev/null +++ b/bot/commands/register.py @@ -0,0 +1,343 @@ +from telegram import ParseMode, ReplyKeyboardMarkup, ReplyKeyboardRemove +from telegram.ext import CommandHandler, MessageHandler, Filters, ConversationHandler +from validate_email import validate_email +from app.controllers import FreeWorkplacesController, UserController +from app import app_context + +# Conversation states +REGISTER_TYPE, EMAIL, NAME, CONFIRM, PASSWORD = range(5) + + +def register_command(update, context): + try: + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + + if user: + response_text = 'Your telegram user is already linked to an existing account (' + user.email + ').\n\n' + + if user.has_password: + response_text += ( + 'You can update the account information or ' + 'unlink your telegram user from the account.\n') + reply_keyboard = [['Update', 'Unlink', 'Cancel']] + else: + response_text += ( + 'You can update the account information instead.\n' + 'To unlink your telegram user from the account, ' + 'you must set a password first.') + reply_keyboard = [['Update', 'Set Password', 'Cancel']] + + update.message.reply_text(response_text, + reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=False)) + return REGISTER_TYPE + else: + update.message.reply_text( + 'I will guide you through the interactive registration process now.\n' + 'Send /cancel to abort the registration at any time.\n\n' + 'What is your email address?') + return EMAIL + except: + return abort_error(update, context) + + +def register_type(update, context): + try: + reply = update.message.text + with app_context(): + if reply == 'Update': + context.user_data['register_update'] = True + + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + + response_text = ( + 'Send /cancel to abort the update process at any time. ' + 'The data will not be updated until the end of the process.\n\n' + 'The stored email address is *' + user.email + '*.\n' + 'Send me the new email address or /skip if it is still up to date.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return EMAIL + elif reply == 'Unlink': + UserController.unlink_telegram_user_id(update.message.from_user.id) + response_text = ( + 'Your are free! Now, let\'s continue with the registration.\n' + 'Send /cancel to abort the registration at any time.\n\n' + 'What is your email address?') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return EMAIL + elif reply == 'Set Password': + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user: + password_reset_link = UserController.get_password_reset_link(user) + print(password_reset_link) + response_text = ( + '[Click here](' + password_reset_link + ') to set a new password.\n\n' + 'Send /register afterwards to start the registration process again.') + else: + response_text = ( + 'Ooops, there\'s something wrong.\n' + 'I could not find any information about your account anymore.' + 'Send /register to start the registration process again.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + context.user_data.clear() + return ConversationHandler.END + elif reply == 'Cancel': + return cancel(update, context) + else: + update.message.reply_text('I did not understand your answer.\n' + 'Please choose an option from the reply keyboard.') + return REGISTER_TYPE + except: + return abort_error(update, context) + + +def email_skip(update, context): + try: + if 'register_update' in context.user_data: + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + response_text = ( + 'Great! I know you as *' + user.name + '*.\n' + 'Send me another name or /skip if you don\'t want to change it.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return NAME + else: + response_text = ( + 'You prankster, you! There is no way to skip this step!\n' + 'Tell me your email address or send /cancel to abort the registration.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return EMAIL + except: + return abort_error(update, context) + +def email(update, context): + try: + email = update.message.text + + with app_context(): + if not validate_email(email_address=email, \ + check_regex=True, \ + check_mx=False, \ + use_blacklist=True, \ + debug=False): + response_text = ( + 'Ooops, that doesn\'t seem to be a valid email address. Please try again.\n' + '_Send /cancel to abort the registration process._') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return EMAIL + elif UserController.get_by_email(email) is not None: + response_text = ( + 'Ooops, your email address is already taken. Please take another one.\n' + '_Send /cancel to abort the registration process._') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return EMAIL + + context.user_data['register_email'] = email + + if 'register_update' in context.user_data: + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + response_text = ( + 'Great! I know you as *' + user.name + '*.\n' + 'Send me another name or /skip if you don\'t want to change it.') + else: + response_text = 'Great! What\'s your name?' + + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return NAME + except: + return abort_error(update, context) + + +def name_skip(update, context): + try: + if 'register_update' in context.user_data: + return send_confirm_message(update, context) + else: + response_text = ( + 'You prankster, you! There is no way to skip this step!\n' + 'Tell me your name or send /cancel to abort the registration.') + update.message.reply_text(response_text, + reply_markup=ReplyKeyboardRemove()) + return NAME + except: + return abort_error(update, context) + +def name(update, context): + try: + name = update.message.text + + with app_context(): + if len(name) < 3: + response_text = ( + 'Ooops, that name seems to be a little too short! Please give me your full name.\n' + '_Send /cancel to abort the registration process._') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + return NAME + + context.user_data['register_name'] = name + + return send_confirm_message(update, context) + except: + return abort_error(update, context) + + +def send_confirm_message(update, context): + update_text = None + + if 'register_update' in context.user_data: + if 'register_name' in context.user_data: + update_text = 'I will change the name to "' + context.user_data['register_name'] + '"' + if 'register_email' in context.user_data: + if update_text: + update_text += ' and the associated email address to "' + context.user_data['register_email'] + '"' + else: + update_text = 'I will change the email address to "' + context.user_data['register_email'] + '"' + + if not update_text: + response_text = ( + 'Great! There is nothing to update!\n' + 'Thank you for reviewing the account information.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + context.user_data.clear() + return ConversationHandler.END + else: + update_text += '.' + else: + update_text = ( + 'I will create a new account with the name "' + context.user_data['register_name'] + '" ' + 'and the email address "' + context.user_data['register_email'] + '".') + + response_text = ( + 'Great!\n\n' + update_text + '\n\n' + 'Is that okay?') + reply_keyboard = [['Yes', 'No']] + update.message.reply_text(response_text, + reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=False)) + return CONFIRM + + +def confirm(update, context): + try: + reply = update.message.text + if reply == 'Yes': + telegram_chat_id = update.message.chat_id + telegram_user_id = update.message.from_user.id + email = context.user_data['register_email'] if 'register_email' in context.user_data else None + name = context.user_data['register_name'] if 'register_name' in context.user_data else None + + with app_context(): + if 'register_update' in context.user_data: + user = UserController.get_by_telegram_user_id(telegram_user_id) + + if user: + UserController.update(user, name, email, telegram_chat_id) + response_text = 'Successfully updated the account information.' + if 'register_email' in context.user_data: + response_text += ( + '\n\nYour account has been locked ' + 'until you confirm the new email address.\n' + 'Please check your inbox.') + update.message.reply_text(response_text, reply_markup=ReplyKeyboardRemove()) + context.user_data.clear() + return ConversationHandler.END + else: + return abort_error(update, context) + else: + UserController.create(name, email, telegram_user_id, telegram_chat_id) + + response_text = ( + 'Successfully created your account.\n\n' + 'Do you want to set a password for your account?') + reply_keyboard = [['Yes', 'No']] + update.message.reply_text(response_text, + reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=False)) + return PASSWORD + else: + if 'register_update' in context.user_data: + response_text = 'I did not change any account information. Aborted.' + else: + response_text = ( + 'I did not create a new account.\n\n' + 'Restart the registration process with /register.') + + update.message.reply_text(response_text, reply_markup=ReplyKeyboardRemove()) + context.user_data.clear() + return ConversationHandler.END + except: + return abort_error(update, context) + + +def password(update, context): + try: + reply = update.message.text + if reply.startswith('/cancel'): + return + if reply == 'Yes': + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + password_reset_link = UserController.get_password_reset_link(user) + response_text = ( + '[Click here](' + password_reset_link + ') to set a password.\n\n' + '_You have reached the end of the registration process._') + else: + response_text = 'You have reached the end of the registration process.' + + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + context.user_data.clear() + return ConversationHandler.END + except: + return abort_error(update, context) + + +def cancel(update, context): + update.message.reply_text('The registration process has been cancelled.', + reply_markup=ReplyKeyboardRemove()) + context.user_data.clear() + return ConversationHandler.END + + +def abort_error(update, context): + update.message.reply_text(( + 'Ooops, something went horribly wrong on my side.\n' + 'The registration had to be aborted.\n' + 'Restart the registration with /register.'), + reply_markup=ReplyKeyboardRemove()) + context.user_data.clear() + return ConversationHandler.END + +register_conv_handler = ConversationHandler( + entry_points=[CommandHandler('register', register_command)], + states={ + # REGISTER_TYPE: [MessageHandler(Filters.regex('^(Update|Set Password|Unlink|Cancel)$'), gender)], + REGISTER_TYPE: [MessageHandler(Filters.regex('^(?!/cancel).*$'), register_type)], + EMAIL: [CommandHandler('skip', email_skip), + MessageHandler(Filters.regex('^(?!/cancel).*$'), email)], + NAME: [CommandHandler('skip', name_skip), + MessageHandler(Filters.regex('^(?!/cancel).*$'), name)], + CONFIRM: [MessageHandler(Filters.regex('^(?!/cancel).*$'), confirm)], + PASSWORD: [MessageHandler(Filters.regex('^(?!/cancel).*$'), password)] + }, + fallbacks=[CommandHandler('cancel', cancel)] +) diff --git a/bot/commands/user.py b/bot/commands/user.py new file mode 100644 index 0000000000000000000000000000000000000000..96c8bb2cf27cca9c9629ad511875555b7158fa41 --- /dev/null +++ b/bot/commands/user.py @@ -0,0 +1,75 @@ +from telegram import ParseMode, ReplyKeyboardRemove +from app.controllers import UserController, ReservationController, RecordController +from app import app_context + +def resetpassword_command(update, context): + """Send a message when the command /resetpassword is issued.""" + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user: + password_reset_link = UserController.get_password_reset_link(user) + response_text = '[Click here](' + password_reset_link + ') to set a new password.' + else: + response_text = ( + 'You telegram user is not associated with an account!\n' + 'Please /register first.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + + +def resendconfirmationemail_command(update, context): + """Send a message when the command /resendconfirmationemail is issued.""" + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user: + if user.is_confirmed: + response_text = 'Your email address is already confirmed!' + else: + UserController.send_confirm_email(user) + response_text = ( + 'An email with instructions have been sent to ' + user.email + '.\n' + 'Please check your inbox.') + else: + response_text = ( + 'You telegram user is not associated with an account!\n' + 'Please /register first.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + + +def status_command(update, context): + """Send a message when the command /status is issued.""" + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user: + response_text = ( + 'Account of ' + user.name + ' (' + user.email + ')\n\n') + if not user.is_confirmed: + response_text += ( + 'Your account is locked until the email address is confirmed!\n\n' + 'Request another confirmation email with /resendconfirmationemail.') + else: + reservation = ReservationController.get_active_reservation(user) + record = RecordController.get_active_record(user) + + if record: + response_text += ( + 'You have reported to be at the workshop.\n' + 'Do you want to make the workplace free for others and /leave?') + elif reservation: + response_text += ( + 'You have reserved a workplace until ' + reservation.time_end.strftime("%H:%M") + '\n' + '/cancel reservation or /enter workshop.') + else: + response_text += ( + 'It\'s really boring here!\n\n' + 'Do you want to /reserve a workplace or /enter the workshop?') + else: + response_text = ( + 'You telegram user is not associated with an account!\n' + 'Please /register first.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) \ No newline at end of file diff --git a/bot/commands/workplace.py b/bot/commands/workplace.py new file mode 100644 index 0000000000000000000000000000000000000000..62400fb8c62d02b3c8c4590bd57b8266169e173e --- /dev/null +++ b/bot/commands/workplace.py @@ -0,0 +1,129 @@ +from telegram import ParseMode, ReplyKeyboardRemove +from app.controllers import FreeWorkplacesController, UserController, ReservationController, RecordController +from app.exceptions import NoFreeWorkplaceError, ActiveReservationExistsError, ActiveRecordExistsError, NoActiveRecordError +from app import app_context + + +def freeworkplaces_command(update, context): + """Send a message when the command /freeworkplaces is issued.""" + with app_context(): + free_workplaces = FreeWorkplacesController.get_free_workplaces() + response_text = '*' + str(free_workplaces) + '* workplaces are currently available.' + if 'email' in context.user_data: + response_text += '\n\nYour email address is ' + context.user_data['email'] + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + + +def reserve_command(update, context): + """Send a message when the command /reserve is issued.""" + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user and user.is_confirmed: + try: + reservation = UserController.reserve(user) + response_text = ( + 'You have a reserved workplace until ' + reservation.time_end.strftime("%H:%M") + '\n' + '/cancel reservation or /enter workshop.') + except NoFreeWorkplaceError: + response_text = ( + 'There are no workplaces available at the moment. ' + 'Please try again later.') + except ActiveReservationExistsError: + reservation = ReservationController.get_active_reservation(user) + response_text = ( + 'You already have a reservation valid until ' + reservation.time_end.strftime("%H:%M") + '\n' + '/cancel reservation or /enter workshop.') + except ActiveRecordExistsError: + record = RecordController.get_active_record(user) + response_text = ( + 'You already reported to be at the workshop!\n' + 'Send /leave if you are not there anymore.') + elif user and not user.is_confirmed: + response_text = ( + 'Your email address ' + user.email + ' is not confirmed yet!\n' + 'Please confirm your email address first. ' + 'If you didn\'t get an email, /resendconfirmationemail.') + else: + response_text = ( + 'Your telegram user is not linked to an account!\n' + 'Please /register first and confirm your email address.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + + +def cancel_command(update, context): + """Send a message when the command /cancel is issued.""" + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user: + reservation = ReservationController.get_active_reservation(user) + + if reservation: + ReservationController.cancel(reservation) + response_text = 'Your reservation has been cancelled.' + else: + response_text = 'You do not have a reservation!' + else: + response_text = ( + 'Your telegram user is not linked to an account!\n' + 'Please /register first and confirm your email address.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + + +def enter_command(update, context): + """Send a message when the command /enter is issued.""" + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user and user.is_confirmed: + try: + UserController.start_record(user) + + response_text = 'You may enter the workshop now.' + except NoFreeWorkplaceError: + response_text = ( + 'There are no workplaces available at the moment. ' + 'Please try again later.') + except ActiveRecordExistsError: + response_text = 'You already reported to be at the workshop!' + elif user and not user.is_confirmed: + response_text = ( + 'Your email address ' + user.email + ' is not confirmed yet!\n' + 'Please confirm your email address first. ' + 'If you didn\'t get an email, /resendconfirmationemail.') + else: + response_text = ( + 'Your telegram user is not linked to an account!\n' + 'Please /register first and confirm your email address.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove()) + + +def leave_command(update, context): + """Send a message when the command /leave is issued.""" + with app_context(): + user = UserController.get_by_telegram_user_id(update.message.from_user.id) + if user: + try: + UserController.terminate_record(user) + + response_text = 'Thank you for the visit. Stay healthy.' + except NoActiveRecordError: + response_text = ( + 'You did not report to be at the workshop!\n' + 'If that was a mistake, send an email to ' + 'info@bastli.ethz.ch with the time when you were here.') + except ActiveRecordExistsError: + response_text = 'You already reported to be at the workshop!' + else: + response_text = ( + 'Your telegram user is not linked to an account!\n' + 'Please /register first and confirm your email address.') + update.message.reply_text(response_text, + parse_mode=ParseMode.MARKDOWN, + reply_markup=ReplyKeyboardRemove())