def results(bot, update): """Fetch Unit Test results from the Aldel MIS. Core function. Fetch Test Reports from Aldel's MIS. Runs ``ResultsSpider`` for registered users and passes it their ``Student_ID`` (PID) & ``Password``. ResultsSpider creates a image file of the format: ``<Student_ID>_tests.png`` File is deleted after being sent to the user. If the file is unavailable, error message is sent to the user. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update """ # Get chatID and user details based on chatID chatID = update.message.chat_id user_info = get_user_info(chatID) Student_ID = user_info['PID'] password = user_info['password'] #Run ResultsSpider if not rate_limited(bot, chatID, "results"): bot.send_chat_action(chat_id=chatID, action='upload_photo') scrape_results(Student_ID, password, chatID)
def input_target(bot, update): """If the user reply is a int/float and between 1-99, stores the figure as the new attendance target. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update :return: ConversationHandler.END :rtype: int """ try: target_figure = float(update.message.text) except ValueError: bot.sendMessage(chat_id=update.message.chat_id, text="You must send a number between 1-99.") return if not 1 <= target_figure <= 99: bot.sendMessage(chat_id=update.message.chat_id, text="You must send a number between 1-99.") return db_session.query(Misc).filter( Misc.chatID == update.message.chat_id).update( {'attendance_target': target_figure}) db_session.commit() messageContent = "Your attendance target has been set to {}%.".format( target_figure) username = get_user_info(update.message.chat_id)['PID'] logger.info("Set attendance target for {} to {}%".format( username, target_figure)) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent) return ConversationHandler.END
def subscription(bot, update): """Sends text detailing the subscription model""" chat_id = update.message.chat_id bot.sendMessage(chat_id=chat_id, text=SUBSCRIPTION_MSG, parse_mode='markdown', disable_web_page_preview=True) mp.track(get_user_info(chat_id)['PID'], 'Checked Subscription')
def error_callback(bot, update, error): """Simple error handling function. Handles PTB lib errors""" user = get_user_info(update.message.chat_id) username = update.message.chat_id if user is None else user['PID'] try: raise error except Unauthorized: # remove update.message.chat_id from conversation list mp.track(username, 'Error', {'type': 'Unauthorized'}) logger.warning( "TelegramError: Unauthorized user. User probably blocked the bot.") except BadRequest as br: # handle malformed requests mp.track(username, 'Error', { 'type': 'BadRequest', 'text': update.message.text, 'error': str(br) }) logger.warning("TelegramError: {} | Text: {} | From: {}".format( str(br), update.message.text, update.message.from_user)) except TimedOut as time_out: # handle slow connection problems mp.track( username, 'Error', { 'type': 'TimedOut', 'text': update.message.text, 'error': str(time_out) }) logger.warning("TelegramError: {} | Text: {} | From: {}".format( str(time_out), update.message.text, update.message.from_user)) except NetworkError as ne: # handle other connection problems mp.track(username, 'Error', { 'type': 'NetworkError', 'text': update.message.text, 'error': str(ne) }) logger.warning("TelegramError: {} | Text: {} | From: {}".format( str(ne), update.message.text, update.message.from_user)) except ChatMigrated as cm: # the chat_id of a group has changed, use e.new_chat_id instead mp.track(username, 'Error', {'type': 'ChatMigrated'}) logger.warning("TelegramError: {} | Text: {} | From: {}".format( str(cm), update.message.text, update.message.from_user)) except TelegramError as e: # handle all other telegram related errors mp.track(username, 'Error', { 'type': 'TelegramError', 'text': update.message.text, 'error': str(e) }) logger.warning("TelegramError: {} | Text: {} | From: {}".format( str(e), update.message.text, update.message.from_user))
def attendance_target(bot, update): """Like :func:`until_eighty`, but with user specified target attendance percentage which is stored in the ``Misc`` table. If target isn't set, asks users whether they'd like to and passes control to :py:func:`select_yn` :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update :return: SELECT_YN :rtype: int """ bot.send_chat_action(chat_id=update.message.chat_id, action='typing') student_misc = Misc.query.filter( Misc.chatID == update.message.chat_id).first() if student_misc is None: new_misc_record = Misc(chatID=update.message.chat_id) db_session.add(new_misc_record) db_session.commit() username = get_user_info(update.message.chat_id)['PID'] logger.info("Created new Misc record for {}".format(username)) student_misc = Misc.query.filter( Misc.chatID == update.message.chat_id).first() target = student_misc.attendance_target if target is None: messageContent = textwrap.dedent(""" You have not set a target yet. Would you like to set it now? You can change it anytime using /edit_target """) keyboard = [['Yes'], ['No']] reply_markup = ReplyKeyboardMarkup(keyboard) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent, reply_markup=reply_markup) return SELECT_YN no_of_lectures = int(until_x(update.message.chat_id, target)) if no_of_lectures < 0: messageContent = "Your attendance is already over {}%. Maybe set it higher? Use /edit_target to change it.".format( target) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent) return ConversationHandler.END messageContent = "You need to attend {} lectures to meet your target of {}%".format( no_of_lectures, target) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent) return ConversationHandler.END
def delete(bot, update): """Delete a user's credentials if they wish to stop using the bot or update them.""" chatID = update.message.chat_id username = get_user_info(chatID)['PID'] logger.info("Deleting user credentials for {}!".format(username)) Chat.query.filter(Chat.chatID == chatID).delete( ) # Delete the user's record referenced by their ChatID Misc.query.filter(Misc.chatID == chatID).delete() db_session.commit() messageContent = "Your credentials have been deleted, {}\nHope to see you back soon!".format( username[3:-4].title()) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent) mp.track(username, 'User Left') mp.people_set(username, {'active': False})
def update_target(bot, update): """Takes the sent figure and sets it as new attendance target. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update :return: ConversationHandler.END :rtype: int """ user_reply = update.message.text if user_reply == '/cancel': bot.sendMessage(chat_id=update.message.chat_id, text="As you wish! The operation is cancelled!") return ConversationHandler.END try: new_target = int(user_reply) except ValueError: bot.sendMessage(chat_id=update.message.chat_id, text="You must send a number between 1-99.") return if not 1 <= new_target <= 99: bot.sendMessage(chat_id=update.message.chat_id, text="You must send a number between 1-99.") return old_target = get_misc_record(update.message.chat_id).attendance_target db_session.query(Misc).filter( Misc.chatID == update.message.chat_id).update( {'attendance_target': new_target}) db_session.commit() username = get_user_info(update.message.chat_id)['PID'] logger.info("Modified attendance target for {} to {}%".format( username, new_target)) new_target_message = "Your attendance target has been updated to {}%!".format( new_target) bot.sendMessage(chat_id=update.message.chat_id, text=new_target_message) mp.track(username, 'Edit Attendance Target', { 'new_target': new_target, 'old_target': old_target }) return ConversationHandler.END
def itinerary(bot, update, args): """Core function. Fetch detailed attendance reports from Aldel's MIS (Parent's Portal). Runs ``ItinerarySpider`` for registered users and passes it their ``Student_ID`` (PID) & ``Password``. ``AttendanceSpider`` creates a image file of the format: ``<Student_ID>_itinerary.png`` If ``args`` are present, full report is sent in the form of a document. Otherwise, it is cropped to the past 7 days using :py:func:`misbot.mis_utils.crop_image` and this function stores the resultant image as: ``<Student_ID>_itinerary_cropped.png`` and returns True. File is deleted after sent to the user. If the file is unavailable, error message is sent to the user. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update :param args: User supplied arguments :type args: tuple """ chatID = update.message.chat_id #If registered, but DOB is absent from the DB if Chat.query.filter(and_(Chat.chatID == chatID, Chat.DOB == None)).first(): bot.sendMessage( chat_id=update.message.chat_id, text="📋 Your DOB is missing! Please use /register to start.") return user_info = get_user_info(chatID) Student_ID = user_info['PID'] DOB = user_info['DOB'] if not rate_limited(bot, chatID, "itinerary"): if args: bot.send_chat_action(chat_id=update.message.chat_id, action='upload_document') scrape_itinerary(Student_ID, DOB, chatID, uncropped=True) return else: bot.send_chat_action(chat_id=update.message.chat_id, action='upload_photo') scrape_itinerary(Student_ID, DOB, chatID) return
def wrapped(bot, update, *args, **kwargs): chatID = update.message.chat_id if not str(chatID) == os.environ['ADMIN_CHAT_ID']: messageContent = "You are not authorized to use this command. This incident has been reported." bot.sendMessage(chat_id=chatID, text=messageContent) user = get_user_info(chatID) if user: mp.track(user['PID'], 'Admin Function Access Attempt', { 'pid': user['PID'], 'link': update.message.from_user.link, }) logger.warning("Unauthorized Access attempt by {}".format( user['PID'])) else: mp.track(user['PID'], 'Admin Function Access Attempt', { 'chat_id': chatID, 'link': update.message.from_user.link, }) logger.warning( "Unauthorized Access attempt by {}".format(chatID)) return return func(bot, update, *args, **kwargs)
def attendance(bot, update): """Core function. Fetch attendance figures from Aldel's MIS. Runs AttendanceSpider for registered users and passes it their ``Student_ID`` (PID), ``Password``, & ``ChatID`` (necessary for ``AttendancePipeline``) ``AttendanceSpider`` creates a image file of the format: ``<Student_ID>_attendance.png`` File is deleted after being sent to the user. If the file is unavailable, error message is sent to the user. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update """ # Get chatID and user details based on chatID chatID = update.message.chat_id user_info = get_user_info(chatID) Student_ID = user_info['PID'] password = user_info['password'] if not rate_limited(bot, chatID, "attendance"): bot.send_chat_action(chat_id=chatID, action='upload_photo') scrape_attendance(Student_ID, password, chatID)
def profile(bot, update): """Fetch profile info from the Aldel MIS. Core function. Runs ``ProfileSpider`` for registered users and passes it their ``Student_ID`` (PID) & ``Password``. ProfileSpider creates a image file of the format: ``<Student_ID>_profile.png`` File is deleted after being sent to the user. If the file is unavailable, error message is sent to the user. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update """ # Get chatID and user details based on chatID chatID = update.message.chat_id user_info = get_user_info(chatID) Student_ID = user_info['PID'] password = user_info['password'] #Run ProfileSpider if not rate_limited(bot, chatID, "profile"): bot.send_chat_action(chat_id=chatID, action='upload_document') scrape_profile(Student_ID, password, chatID)