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 parent_login(bot, update, user_data): """ user_data dict contains ``Student_ID`` key from :py:func:`credentials`. Extracts DOB from ``update.message.text`` and checks validity using :py:func:`misbot.mis_utils.check_parent_login` before adding it to database. Finally, sends a message to the user requesting them to start using ``/attendance`` or ``/itinerary`` commands. """ DOB = update.message.text Student_ID = user_data['Student_ID'] chatID = update.message.chat_id if not check_parent_login(Student_ID, DOB): messageContent = textwrap.dedent(""" Looks like your Date of Birth details are incorrect! Give it one more shot. Send DOB in the below format: `DD/MM/YYYY` """) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent, parse_mode='markdown') return new_user = Student_ID[3:-4].title() db_session.query(Chat).filter(Chat.chatID == chatID).update({'DOB': DOB}) db_session.commit() logger.info("New Registration! Username: {}".format((Student_ID))) messageContent = "Welcome {}!\nStart by checking your /attendance or /itinerary".format(new_user) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent, parse_mode='markdown') return ConversationHandler.END
def confirm_otp(bot, update, user_data): reply_markup = ReplyKeyboardRemove() admin_response = update.message.text if admin_response == "Confirm": pass elif admin_response == "Abort": bot.sendMessage(chat_id=update.message.chat_id, text="Operation Aborted!", reply_markup=reply_markup) return ConversationHandler.END else: bot.sendMessage(chat_id=update.message.chat_id, text="Choose one of the two options.") return misc_record = get_misc_record(user_data['user_record'].chatID) misc_record.premium_user = True misc_record.premium_tier = user_data['tier'] misc_record.premium_till = datetime.now() + timedelta( days=user_data['validity_days']) db_session.commit() date_beautified = misc_record.premium_till.strftime("%B %d, %Y") message_content = "Your premium subscription is active and valid till: {}!".format( date_beautified) bot.sendMessage(chat_id=user_data['user_record'].chatID, text=message_content) admin_message = "{} has been elevated to premium!\n Valid till: {}".format( user_data['username'], date_beautified) bot.sendMessage(chat_id=update.message.chat_id, text=admin_message, reply_markup=reply_markup) return ConversationHandler.END
def confirm_revert(bot, update, user_data): """If Yes, revert the specified message for all users and send a summary of the operation to admin. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param update: Telegram Update object :type update: telegram.update.Update :param user_data: Conversation data :type user_data: dict """ reply_markup = ReplyKeyboardRemove() if update.message.text == "Yes": try: notification_message = PushMessage.query.filter( PushMessage.uuid == user_data['uuid']).first().text except AttributeError: bot.sendMessage(chat_id=update.message.chat_id, text="Unknown UUID. Try again.", reply_markup=reply_markup) return ConversationHandler.END notifications = PushNotification.query.filter(and_(PushNotification.message_uuid == user_data['uuid'],\ PushNotification.sent == True)) user_list = [notification.chatID for notification in notifications] message_ids = [ notification.message_id for notification in notifications ] time_taken = delete_threaded(message_ids, user_list) notification_message_short = textwrap.shorten(notification_message, width=20, placeholder='...') stats_message = textwrap.dedent(""" Deleted the notification: ``` {} ``` {} messages deleted in {:.2f}secs. """.format(notification_message_short, len(message_ids), time_taken)) bot.sendMessage(chat_id=update.message.chat_id, text=stats_message, parse_mode='markdown', reply_markup=reply_markup) db_session.query(PushMessage).filter( PushMessage.uuid == user_data['uuid']).update({'deleted': True}) db_session.commit() return ConversationHandler.END elif update.message.text == "No" or update.message.text == "/cancel": bot.sendMessage(chat_id=update.message.chat_id, text="Revert Aborted!", reply_markup=reply_markup) return ConversationHandler.END return
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 user_details = db_session.query(Chat).filter(Chat.chatID == chatID).first() #Pull user's username from the DB username = user_details.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 db_session.commit() #Save changes 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)
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 rate_limited(bot, chat_id, command): """Checks if user has made a request in the past 5 minutes. :param bot: Telegram Bot object :type bot: telegram.bot.Bot :param chat_id: 9-Digit unique user ID :type chat_id: str :param command: Telegram command :type command: str :return: True if user has made a request in past 5 mins, else False :rtype: bool """ rate_limit = RateLimit.query.filter( and_(RateLimit.chatID == chat_id, RateLimit.command == command)).first() if rate_limit is None: new_rate_limit_record = RateLimit(chatID=chat_id, status='new', command=command, count=0) db_session.add(new_rate_limit_record) db_session.commit() rate_limit = RateLimit.query.filter( and_(RateLimit.chatID == chat_id, RateLimit.command == command)).first() if abs(datetime.now() - rate_limit.requested_at) < timedelta(minutes=5): if rate_limit.count < 1: RateLimit.query.filter(and_(RateLimit.chatID == chat_id, RateLimit.command == command))\ .update({'count': rate_limit.count + 1}) db_session.commit() return False elif rate_limit.count < 2: RateLimit.query.filter(and_(RateLimit.chatID == chat_id, RateLimit.command == command))\ .update({'count': rate_limit.count + 1}) db_session.commit() message_content = "You've already used this command in the past 5 minutes. Please wait 5 minutes before sending another request." bot.send_message(chat_id=chat_id, text=message_content) return True elif rate_limit.count in range(2, 1000): RateLimit.query.filter(and_(RateLimit.chatID == chat_id, RateLimit.command == command))\ .update({'count': rate_limit.count + 1}) db_session.commit() bot.send_animation(chat_id=chat_id, animation=random.choice(list_of_gifs)) return True else: RateLimit.query.filter(and_(RateLimit.chatID == chat_id, RateLimit.command == command))\ .update({'count': 1, 'requested_at': datetime.now()}) db_session.commit() return False
def clean_attendance_records(): """Delete all lectures and practical records from the DB. To be used at the beginning of a new semester so that ``/bunk`` command doesn't display lectures of previous semester(s). :return: Number of records deleted from Lecture and Practical tables. :rtype: tuple """ lecture_records = db_session.query(Lecture).delete() practical_records = db_session.query(Practical).delete() db_session.commit() return lecture_records, practical_records
def get_misc_record(chat_id): """Returns Misc record for a user :param chat_id: 9-Digit unique user ID :type chat_id: str """ misc_record = Misc.query.filter(Misc.chatID == chat_id).first() if misc_record is None: new_misc_record = Misc(chatID=chat_id) db_session.add(new_misc_record) db_session.commit() misc_record = Misc.query.filter(Misc.chatID == chat_id).first() return misc_record
def credentials(bot, update, user_data): """ Store user credentials in a database. Takes student info (PID & password) from ``update.message.text`` and splits it into Student_ID & Password and checks if they are correct with :py:func:`misbot.mis_utils.check_login` and stores them in the ``Chat`` table. Finally, sends message asking users to enter DOB and gives control to :func:`parent_login` after storing ``Student_ID`` (PID) in user_data dict. """ chat_id = update.message.chat_id # If message contains less or more than 2 arguments, send message and stop. try: Student_ID, password = update.message.text.split() except ValueError: messageContent = textwrap.dedent(""" Oops, you made a mistake! You must send the Student_ID and password in a single line, separated by a space. This is what valid login credentials look like: `123name4567 password` """) bot.send_chat_action(chat_id=chat_id, action='typing') bot.sendMessage(chat_id=update.message.chat_id, text=messageContent, parse_mode='markdown') return if not check_login(Student_ID, password): messageContent = textwrap.dedent(""" Looks like your credentials are incorrect! Give it one more shot. This is what valid login credentials look like: `123name4567 password` """) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent, parse_mode='markdown') return # Create an object of Class <Chat> and store Student_ID, password, and Telegram # User ID, Add it to the database, commit it to the database. userChat = Chat(PID=Student_ID, password=password, chatID=chat_id) db_session.add(userChat) db_session.commit() messageContent = textwrap.dedent(""" Now enter your Date of Birth (DOB) in the following format: `DD/MM/YYYY` """) update.message.reply_text(messageContent, parse_mode='markdown') user_data['Student_ID'] = Student_ID return PARENT_LGN
def credentials(bot, update): """Store user credentials in a database.""" user = update.message.from_user chatID = update.message.chat_id #If message contains less or more than 2 arguments, send message and stop. try: Student_ID, passwd = update.message.text.split() except ValueError: messageContent = textwrap.dedent(""" Oops, you made a mistake! You must send the Student_ID and password in a single line, separated by a space. This is what valid login credentials look like: `123name4567 password` """) bot.send_chat_action(chat_id=update.message.chat_id, action='typing') bot.sendMessage(chat_id=update.message.chat_id, text=messageContent, parse_mode='markdown') return if Chat.query.filter(Chat.chatID == chatID).first(): bot.sendMessage(chat_id=update.message.chat_id, text="Already Registered!") return ConversationHandler.END if check_login(Student_ID, passwd) == False: messageContent = textwrap.dedent(""" Looks like your credentials are incorrect! Give it one more shot. This is what valid login credentials look like: `123name4567 password` """) bot.sendMessage(chat_id=update.message.chat_id, text=messageContent, parse_mode='markdown') return logger.info("New Registration! Username: %s" % (Student_ID)) # Create an object of Class <Chat> and store Student_ID, password, and Telegeram # User ID, Add it to the database, commit it to the database. userChat = Chat(PID=Student_ID, password=passwd, chatID=chatID) db_session.add(userChat) db_session.commit() new_user = Student_ID[3:-4].title() update.message.reply_text( "Welcome {}!\nStart by checking your /attendance".format(new_user)) 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 delete(bot, update): """Delete a user's credentials if they wish to stop using the bot.""" chatID = update.message.chat_id if not Chat.query.filter(Chat.chatID == chatID).first(): bot.sendMessage(chat_id=update.message.chat_id, text="Unregistered!") return user_details = db_session.query(Chat).filter( Chat.chatID == chatID).first() #Pull user's username from the DB username = user_details.PID logger.info("Deleting user credentials for %s!" % (username)) Chat.query.filter(Chat.chatID == chatID).delete( ) #Delete the user's record referenced by their ChatID db_session.commit() #Save changes bot.sendMessage(chat_id=update.message.chat_id, text="Your credentials have been deleted, %s\nHope to see you back soon." \ % (username[3:-4].title()))
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 process_item(self, item, spider): #Initiate DB init_db() if not Attendance.query.filter( Attendance.chatID == spider.chatID).first(): record = Attendance( total_lec_attended=item['total_lec_attended'], total_lec_conducted=item['total_lec_conducted'], chatID=spider.chatID) db_session.add(record) db_session.commit() else: db_session.query(Attendance).filter(Attendance.chatID == spider.chatID).\ update({'total_lec_attended': item['total_lec_attended'], 'total_lec_conducted':item['total_lec_conducted']}) db_session.commit() return item
def process_item(self, item, spider): if not isinstance(item, Practicals): return item #Do nothing for Lectures Items (NOT Practicals) if not Practical.query.filter( and_(Practical.chatID == spider.chatID, Practical.name == item['subject'])).first(): record = Practical(name=item['subject'], chatID=spider.chatID, attended=item['attended'], conducted=item['conducted']) db_session.add(record) db_session.commit() else: db_session.query(Practical).filter(and_(Practical.chatID == spider.chatID, Practical.name == item['subject'])).\ update({'attended': item['attended'], 'conducted':item['conducted']}) db_session.commit() return item
def command_func(bot, update, *args, **kwargs): chat_id = update.message.chat_id misc_record = get_misc_record(chat_id) if misc_record.premium_user is False: messageContent = "You must upgrade to premium to use this feature. See /subscription" bot.sendMessage(chat_id=chat_id, text=messageContent) return if misc_record.premium_tier < tier: messageContent = "This feature is not included in your subscription plan." bot.sendMessage(chat_id=chat_id, text=messageContent) return if datetime.now() > misc_record.premium_till: misc_record.premium_user = False db_session.commit() messageContent = "Your /premium subscription has expired! Kindly renew your subscription." bot.sendMessage(chat_id=chat_id, text=messageContent) return return func(bot, update, *args, **kwargs)
def extend_input_days(bot, update, user_data): try: user_data['extension_period'] = int(update.message.text) except ValueError: bot.sendMessage(chat_id=update.message.chat_id, text="Please send a number.") return user_record = Chat.query.filter( Chat.PID == user_data['username'])[user_data['index']] misc_record = get_misc_record(user_record.chatID) misc_record.premium_till += timedelta(days=user_data['extension_period']) db_session.commit() date_beautified = misc_record.premium_till.strftime("%B %d, %Y") message_content = "Your subscription has been extended till {}. Cheers!".format( date_beautified) bot.sendMessage(chat_id=user_record.chatID, text=message_content) admin_message = "Subscription for {} extended by {} day(s) ({})".format( user_record.PID, user_data['extension_period'], date_beautified) bot.sendMessage(chat_id=update.message.chat_id, text=admin_message) return ConversationHandler.END
def __parse_html__(self, html): rows = BeautifulSoup(html) \ .find('table', {'class': 'tablesorter'}) \ .find('tbody') \ .find_all('tr') endpoint = rows[0].find_all('a')[0]['href'] profile_html = BeautifulSoup(self.__get_profile__(endpoint)) profile_data = [td.text for td in profile_html.find_all('table', {'id': 'general-info'})[0].find_all('td')] for row in rows: column_data = [ele.text.strip() for ele in row.find_all('td')] try: athlete = Athlete( name=column_data[0], country=column_data[1], div_rank=column_data[2], gender_rank=column_data[3], overall_rank=column_data[4], swim_time=column_data[5], bike_time=column_data[6], run_time=column_data[7], finish_time=column_data[8], points=column_data[9], bib_id=profile_data[2], age=profile_data[6], state=profile_data[8], profession=profile_data[12] ) db_session.add(athlete) db_session.commit() yield athlete except ValueError as e: self.invalid_athlete_count += 1 print('Invalid atheletes: {0}'.format(str(self.invalid_athlete_count))) continue