Esempio n. 1
0
 def setUp(self):
     """Create/connect to development database"""
     self.SQL_SCRIPT = Path(DB_SQL_SCRIPT).read_text()  # read a PosixPath file as str
     # db connection & creation
     self.db = DBHelper(filename="test.db")
     print("connecting to db... done.")
     self.db.setup()
Esempio n. 2
0
def send_schedule(db: DBHelper):
    # Order  =     0           1          2            3         4          5          6
    weekdays = ("monday", "tuesday", "wednesday", "thursday", "friday",
                "saturday", "sunday")
    study_days = (5, 6, 0, 1, 2)
    today = datetime.today().weekday()  # What is today?

    if today in study_days:
        schedule = db.get_schedule_of(weekdays[today])  # get schedule of today
        # ================== formating the message to send
        msg_schedule_part = ""
        for idx, entry in enumerate(schedule):
            msg_schedule_part += str(
                idx + 1) + '. ' + entry[1] + ' at ' + entry[0] + '\n'
        msg = "Good morning, \n" \
              "today is {0} and the schedule is: \n\n" \
              "{1}".format(weekdays[today].title(), msg_schedule_part)
        users = db.get_users()  # get list of all users
        for user in users:
            if user.active:
                log.info(f"Sending today's schedule to: {user}")
                send_message(user.chat_id, msg)  # send today's schedule
                time.sleep(
                    0.5)  # sleep for .5 second before sending to the next user
Esempio n. 3
0
def handle_updates(updates: list, db: DBHelper):
    """Handles incoming updates to the bot"""
    global current_command  # use current_command var from global scope
    for update in updates:  # loop through updates
        # db.add_message((id: int, update_id: int, user_id: int, chat_id: int, date: int(unix_timestamp), text: str))

        # TODO: common message and user data from the same update

        # Skip edited messages
        if not update.get("message"):
            continue

        # getting message data
        msg_id = update.get("message").get("message_id")  # message id
        msg_update_id = update.get("update_id")  # update id of this message
        msg_user_id = update.get("message").get("from").get("id")  # sending user
        msg_chat_id = update.get("message").get("chat").get("id")  # chat id of the message
        msg_date = update.get("message").get("date")  # message date
        msg_text = update.get("message").get("text", "")  # message text

        log.info("collecting message data... done")
        # Create Message object from incoming data
        msg = Message(msg_id, msg_update_id, msg_user_id, msg_chat_id, msg_date, msg_text)
        log.info("creating message object from collected data... done")
        if not db.get_message(msg.id):  # if message doesn't exist already
            db.add_message(msg)
            log.info("New message saved.")

        # db.add_user((id: int, is_bot: int, is_admin: int, first_name: str, last_name: str,
        # username: str, language_code: str, active: int(0|1), created: int(unix_timestamp),
        # updated: int(unix_timestamp), last_command: str))
        user_id = update.get("message").get("from").get("id")
        user_is_bot = update.get("message").get("from").get("is_bot")
        user_is_admin = False
        user_first_name = update.get("message").get("from").get("first_name")
        user_last_name = update.get("message").get("from").get("last_name")
        user_username = update.get("message").get("from").get("username")
        user_language_code = update.get("message").get("from").get("language_code", "en")
        user_active = True
        user_created = time.time()
        user_updated = time.time()
        user_last_command = None
        user_chat_id = update.get("message").get("chat").get("id")
        log.info("collecting user data... done")
        # if user doesn't exist, add him/her to db
        if not db.get_user(user_id):
            user = User(user_id, user_is_bot, user_is_admin, user_first_name, user_last_name, user_username,
                        user_language_code, user_active, user_created, user_updated, user_last_command, user_chat_id)
            db.add_user(user)
            log.info("New user saved.")

        log.info("Old user..")
        # Create user object from saved data
        user = db.get_user(user_id)
        if not user.chat_id:
            log.info("User does't have a chat_id yet!")
            db.set_user_chat_id(user.id, time.time(), user_chat_id)
            log.info("Updated user's chat_id")
            # get user again after updating chat_id
            user = db.get_user(user_id)
        log.info("creating user object from collected data... done")

        user_last_command = user.last_command
        text = None  # msg text
        chat = msg_chat_id  # chat id
        log.debug("user: "******" sent a message - chat_id: " + str(msg_chat_id))
        if msg_text:  # handle text messages only
            text = msg_text.strip()  # extract msg text
            if text and chat:  # make sure we have txt msg and chat_id
                log.info('text message and chat_id are  extracted.')
                if text.startswith("/"):  # if command
                    if is_available_command(text):  # if command is available
                        current_command = text  # set current command
                        log.info('command: "' + current_command + '" is available.')
                        db.set_user_last_command(user.id, time.time(), current_command)  # update user's last command
                        if command_takes_input(current_command):  # if command operates on inputs
                            hint_message = get_hint_message(current_command)  # get command hint message
                            send_message(chat, hint_message)  # send a help message to receive inputs later
                            log.info('sending hint message to user... done')
                        else:  # if command is available and does not operate on inputs
                            log.info('command: "' + current_command + '" has no argument.')
                            # execute command directly
                            if current_command == "/stop":
                                get_command_handler(current_command)(db, user_id, time.time(), False)
                                current_command = None
                            elif current_command == "/start":
                                get_command_handler(current_command)(db, user_id, time.time(), True)
                                current_command = None
                            else:
                                send_message(chat, get_command_handler(current_command)())
                                # then unset current_command, commands_without_args execute once!
                                current_command = None
                                log.info("updating user current command.. one time cmd")
                                db.set_user_last_command(user.id, time.time(), current_command)

                    else:  # if command is not available
                        log.info('Undefined Command')
                        send_message(chat, "Use a defined command.")
                else:  # if sent message does not start with a slash
                    log.info("working on user's last command.. " + str(user.last_command))
                    last_command = user.last_command
                    if command_takes_input(last_command):  # should be an argument if current_command is set
                        log.info('received command arguments from user...')
                        send_message(chat, get_command_handler(last_command)(text))
                        log.info('sending message to user... done')
                    elif current_command == "/start" or current_command == "/stop":
                        continue  # skip
                    else:
                        log.info('Undefined Command.')
                        send_message(chat, "Use a defined command.")
        else:  # if no text message
            log.debug("A non text message is sent by user: "******" - chat id: " + str(chat))
            send_message(chat, "I handle text messages only!")
Esempio n. 4
0
        try:
            log.info("getting updates...")
            updates = get_updates(updates_offset)  # get new updates after last handled one
            if "result" in updates:  # to prevent KeyError exception
                if len(updates["result"]) > 0:  # make sure updates list is longer than 0
                    updates_offset = last_update_id(updates) + 1  # to remove handled updates
                    handle_updates(updates["result"], db)  # handle new (unhandled) updates
                else:
                    log.info('no updates to be handled')
            time.sleep(0.5)

            # if it's 8 in the morning
            #   # What is today?
            #   # get schedule of today
            #   # get list of all users
            #   # for user in all_users
            #   #   # send today's schedule
            #   #   # sleep for .5 second

        except KeyboardInterrupt:  # exit on Ctrl-C
            log.info("\nquiting...")
            exit(0)


if __name__ == "__main__":
    # Setting DB
    db = DBHelper()
    db.setup()
    log.info("Running bot...")
    main(db)
Esempio n. 5
0
def main(db: DBHelper):
    """The entry point"""
    updates_offset = None  # track last_update_id to use it as offset
    while True:  # infinitely listen to new updates (as long as the script is running)
        try:
            # =============================== Handling Schedule ==============================================
            # Order  =     0           1          2            3         4          5          6
            weekdays = ("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday")
            study_days = (5, 6, 0, 1, 2)
            today = datetime.today().weekday()  # What is today?
            now_in_egypt = str(format(datetime.utcnow() + timedelta(hours=2), "%H:%M:%S"))  # Cairo time = UTC+2
            now_in_egypt = dtime(*[int(x) for x in now_in_egypt.split(":")])  # to datetime.time object
            before_eight = dtime(7, 59, 45)  # get before 8:00AM with 5 seconds
            after_eight = dtime(8, 0, 15)  # get after 8:00AM with 5 seconds

            # if it's in range (07:59:55 |08:00| 08:00:05) in the morning
            if time_in_range(before_eight, after_eight, now_in_egypt):
                if today in study_days:
                    schedule = db.get_schedule_of(weekdays[today])  # get schedule of today
                    # ================== formating the message to send
                    msg_schedule_part = ""
                    for idx, entry in enumerate(schedule):
                        msg_schedule_part += str(idx+1) + '. ' + entry[1] + ' at ' + entry[0] + '\n'
                    msg = "Good morning, \n" \
                          "today is {0} and the schedule is: \n\n" \
                          "{1}".format(weekdays[today].title(), msg_schedule_part)
                    users = db.get_users()  # get list of all users
                    for user in users:
                        if user.active:
                            log.info(f"Sending today's schedule to: {user}")
                            send_message(user.chat_id, msg)  # send today's schedule
                            time.sleep(0.5)  # sleep for .5 second before sending to the next user

            # =============================== Handling Announcements =========================================
            # last_check: nonlocal var will be used to check for future announcement each 2 hours
            # for less resources consumption

            # get future announcements (where done column is)
            # if ann.done != "once" nor "twice" and ann.cancelled != true
            # send announcement to each active user
            # set ann.done = "once"
            # if ann.done="once" check timedelta
            # if timedelta < 24hrs
            # send the another announcement reminder, and mark ann.done="twice"
            # else: pass

            anns = db.get_announcements()
            for ann in anns:
                if ann.done == "":
                    users = db.get_users()  # get list of all users
                    for user in users:
                        if user.active:
                            log.info(f"Sending announcement: {ann} schedule to: {user}")
                            send_message(user.chat_id, ann.description)
                            ann.done = "once"
                            db.update_announcement(ann.id)
                            time.sleep(0.5)  # sleep for .5 second before sending to the next user
                elif ann.done == "once":
                    # if ann.time - current_time = 1 day
                    "YYYY-MM-DD HH:MM"
                    if ann.time - timedelta:
                pass

            # =============================== Handling incoming messages =====================================
            log.info("getting updates...")
            updates = get_updates(updates_offset)  # get new updates after last handled one
            if "result" in updates:  # to prevent KeyError exception
                if len(updates["result"]) > 0:  # make sure updates list is longer than 0
                    updates_offset = last_update_id(updates) + 1  # to remove handled updates
                    handle_updates(updates["result"], db)  # handle new (unhandled) updates
                else:
                    log.info('no updates to be handled')

            time.sleep(0.5)  # delay the loop a .5 second
        except KeyboardInterrupt:  # exit on Ctrl-C
            log.info("\nquiting...")
            exit(0)
Esempio n. 6
0
class DBHelperTest(unittest.TestCase):

    def setUp(self):
        """Create/connect to development database"""
        self.SQL_SCRIPT = Path(DB_SQL_SCRIPT).read_text()  # read a PosixPath file as str
        # db connection & creation
        self.db = DBHelper(filename="test.db")
        print("connecting to db... done.")
        self.db.setup()

    def tearDown(self):
        """Drop DB tables, close connection and remove the db later"""
        self.db.destroy()

    def test_add_message(self):
        # inserting the message
        msg = Message(1, 2, 3, 4, 5, "message 1")
        self.assertTrue(self.db.add_message(msg))
        # making sure it was inserted right
        sql = "SELECT * FROM Message"
        got_msg = Message(*self.db.cur.execute(sql).fetchone())
        self.assertEqual(msg.id, got_msg.id)
        self.assertEqual(msg.update_id, got_msg.update_id)
        self.assertEqual(msg.user_id, got_msg.user_id)
        self.assertEqual(msg.chat_id, got_msg.chat_id)
        self.assertEqual(msg.date, got_msg.date)
        self.assertEqual(msg.text, got_msg.text)
        print("testing add_message... done.")

    def test_get_message(self):
        # inserting the message
        sql = "INSERT INTO Message VALUES (?, ?, ?, ?, ?, ?)"
        params = (1, 2, 3, 4, 5, "message")
        self.db.cur.execute(sql, params)
        # making sure we get it right
        msg = self.db.get_message(1)
        self.assertTrue(isinstance(msg, Message))
        self.assertEqual(msg.id, params[0])
        self.assertEqual(msg.update_id, params[1])
        self.assertEqual(msg.user_id, params[2])
        self.assertEqual(msg.chat_id, params[3])
        self.assertEqual(msg.date, params[4])
        self.assertEqual(msg.text, params[5])
        not_msg = self.db.get_message(2)
        self.assertFalse(not_msg)
        print("testing get_message... done.")

    def test_add_user(self):
        # insert new user
        user = User(70437390, False, True, "Ahmed", "Shahwan", "ash753", "en", True, 1555512911.45624,
                    1556303495.79887, "/calculate", 332324)
        self.assertTrue(self.db.add_user(user))
        # testing if it's inserted correctly
        sql = "SELECT * FROM User"
        got_user = User(*self.db.cur.execute(sql).fetchone())
        self.assertEqual(user.id, got_user.id)
        self.assertEqual(user.is_bot, got_user.is_bot)
        self.assertEqual(user.is_admin, got_user.is_admin)
        self.assertEqual(user.first_name, got_user.first_name)
        self.assertEqual(user.last_name, got_user.last_name)
        self.assertEqual(user.username, got_user.username)
        self.assertEqual(user.language_code, got_user.language_code)
        self.assertEqual(user.active, got_user.active)
        self.assertEqual(user.created, got_user.created)
        self.assertEqual(user.updated, got_user.updated)
        self.assertEqual(user.last_command, got_user.last_command)
        self.assertEqual(user.chat_id, got_user.chat_id)

    def test_get_user(self):
        # inserting user to db using sql
        sql = "INSERT INTO User VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
        params = (70437390, False, True, "Ahmed", "Shahwan", "ash753", "en", True, 1555512911.45624,
                  1556303495.79887, "/calculate", 2345)
        self.db.cur.execute(sql, params)
        # user exists
        user = self.db.get_user(params[0])
        self.assertTrue(isinstance(user, User))
        self.assertEqual(user.id, params[0])
        self.assertEqual(user.is_bot, params[1])
        self.assertEqual(user.is_admin, params[2])
        self.assertEqual(user.first_name, params[3])
        self.assertEqual(user.last_name, params[4])
        self.assertEqual(user.username, params[5])
        self.assertEqual(user.language_code, params[6])
        self.assertEqual(user.active, params[7])
        self.assertEqual(user.created, params[8])
        self.assertEqual(user.updated, params[9])
        self.assertEqual(user.last_command, params[10])
        self.assertEqual(user.chat_id, params[11])
        # user doesn't exist
        not_user = self.db.get_user(111)
        self.assertTrue(not_user is None)

    def test_get_users(self):
        # add users using sql, then get them using the function
        # inserting users using sql
        sql = "INSERT INTO User VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
        user1 = (43739, False, False, "Ahme", "Shahwa", "ash75", "en", True, 51291, 30349, "/calculate", 33)
        user2 = (4373, False, True, "Ahm", "Shahw", "ash7", "en", False, 5129, 3034, "/translate", 555)
        user3 = (437, False, False, "Ah", "Shah", "ash", "en", True, 512, 303, "/ocr_url", 556)
        self.db.cur.execute(sql, user1)
        self.db.cur.execute(sql, user2)
        self.db.cur.execute(sql, user3)

        got_users = self.db.get_users()
        self.assertTrue(isinstance(got_users, list))
        self.assertTrue(len(got_users) == 3)
        self.assertTrue(isinstance(got_users[0], User))
        self.assertTrue(isinstance(got_users[1], User))
        self.assertTrue(isinstance(got_users[2], User))

    def test_set_user_last_command(self):
        # create a user in db with tested functions
        user = User(7043739, False, False, "Ahme", "Shahwa", "ash75", "en", True, 51291, 30349, "/calculate", 5554)
        self.db.add_user(user)
        # alter user's last command
        self.assertTrue(self.db.set_user_last_command(user.id, time.time(), "/translate"))
        # test alteration success
        got_user = self.db.get_user(user.id)
        self.assertEqual("/translate", got_user.last_command)

    def test_set_user_status(self):
        # create a user
        user = User(7043739, False, False, "Ahme", "Shahwa", "ash75", "en", True, 51291, 30349, "/calculate", 5556)
        self.db.add_user(user)
        # alter user's status
        self.assertTrue(self.db.set_user_status(user.id, time.time(), False))
        # test alteration success
        got_user = self.db.get_user(user.id)
        self.assertEqual(False, got_user.active)

    def test_set_user_chat_id(self):
        # create a user without caht_id
        user = User(7043739, False, False, "Ahme", "Shahwa", "ash75", "en", True, 51291, 30349, "/calculate", None)
        self.db.add_user(user)
        # alter user's chat_id
        new_chat_id = 3456
        self.db.set_user_chat_id(user.id, time.time(), new_chat_id)
        # test alternation
        got_user = self.db.get_user(user.id)
        self.assertEqual(got_user.chat_id, new_chat_id)

    def test_get_schedule(self):
        # get entries
        entries_list = self.db.get_schedule()
        self.assertTrue(isinstance(entries_list, list))
        self.assertTrue(len(entries_list) == 14)
        self.assertTrue(isinstance(entries_list[0], ScheduleEntry))
        self.assertTrue(entries_list[0].id, 1)
        self.assertTrue(isinstance(entries_list[1], ScheduleEntry))
        self.assertTrue(entries_list[1].id, 2)
        self.assertTrue(isinstance(entries_list[2], ScheduleEntry))
        self.assertTrue(entries_list[2].id, 3)
        print(entries_list[0], entries_list[1], entries_list[2])

    def test_get_schedule_of(self):
        schedule = self.db.get_schedule_of("saturday")
        self.assertTrue(isinstance(schedule, list))
        self.assertEqual(len(schedule), 3)
        self.assertTrue(isinstance(schedule[0], tuple))
        self.assertTrue(isinstance(schedule[1], tuple))
        self.assertTrue(isinstance(schedule[2], tuple))
        print("=++Schedule++= ", schedule)

    def test_add_announcement(self):
        # add announcement
        ann = Announcement("08:30", "DSP Assignment 10 should be delivered tomorrow", "once")
        ann1 = Announcement("10:10", "Another test announcement", "twice")
        self.assertTrue(self.db.add_announcement(ann))
        self.assertTrue(self.db.add_announcement(ann1))

        # get announcement to test
        sql = "SELECT * FROM Announcement"
        result = self.db.cur.execute(sql)
        got_ann = result.fetchone()
        got_ann1 = result.fetchone()

        self.assertEqual(1, got_ann[0])
        self.assertEqual(ann.time, got_ann[1])
        self.assertEqual(ann.description, got_ann[2])
        self.assertEqual(ann.done, got_ann[3])

        self.assertEqual(2, got_ann1[0])
        self.assertEqual(ann1.time, got_ann1[1])
        self.assertEqual(ann1.description, got_ann1[2])
        self.assertEqual(ann1.done, got_ann1[3])

    def test_get_announcements(self):
        # add some announcements
        ann1 = Announcement("21:30", "DSP Assignment 10 should be delivered tomorrow", "")
        ann2 = Announcement("10:30", "Communication Lecture is cancelled", "once")
        ann3 = Announcement("09:30", "Dr Tamer is not coming again", "twice")

        self.db.add_announcement(ann1)
        self.db.add_announcement(ann2)
        self.db.add_announcement(ann3)

        # get what've been added and test it
        anns = self.db.get_announcements()
        self.assertTrue(isinstance(anns, list))
        self.assertTrue(isinstance(anns[0], Announcement))
        self.assertTrue(isinstance(anns[1], Announcement))
        self.assertTrue(isinstance(anns[2], Announcement))

    def test_update_announcement(self):
        # add an announcment
        self.db.add_announcement(Announcement("21:30", "DSP Assignment 10 should be delivered tomorrow", ""))
        ann = self.db.get_announcements()[0]  # get announcment before update to use its id
        self.db.update_announcement(ann.id, "once")  # update
        ann_updated = self.db.get_announcements()[0]  # get announcment after update
        self.assertEqual(ann_updated.done,  "once")  # test updated value