0
votes

It's probably my fault, but I can't make the "run_repeating" method works. I took a look at the examples from the official Github repo and find that timerbot.py. I tried to follow the same idea in my code, but I failed. I mean, the timerbot works, but I'm doing something wrong I can't figure out that is not working in my code. And I know, timerbot uses "run_once" instead of "run_repeating" but the documentation indicates a pretty similar syntax for both.

By the way, my bot is supposed to be a price tracker for Amazon products using Selenium (that part is ok). The bot get the url, then tracks the product until the price get cheaper and notify the user with a message.

This is basically the code (the part that matters):

from telegram import Update
from telegram.ext import Updater, CommandHandler, CallbackContext, MessageHandler, Filters, ConversationHandler
from selenium import webdriver, common
import time, re

#Variables
token = open('note_token_telegram', 'r').read()
driver_path = open('note_driver','r').read()
bin_path = open('note_bin','r').read()
  
#Track
def track(update: Update, context: CallbackContext) -> None:
    data = context.user_data
    url = context.args[0]
    chat_id = update.message.chat_id 
    if re.match(r'(https:\/\/www\.amazon\.).*', url):
        print('track running')
        #Scrape 
        driver_options = webdriver.FirefoxOptions()
        driver_options.add_argument('--headless')
        driver_options.binary_location = bin_path.strip()
        driver = webdriver.Firefox(executable_path=driver_path.strip(), options=driver_options)
        driver.get(url)
        try:
            try:
                price = driver.find_element_by_css_selector('#priceblock_ourprice')
            except common.exceptions.NoSuchElementException:
                try:
                    price = driver.find_element_by_css_selector('#priceblock_saleprice')
                except:
                    price = driver.find_element_by_css_selector('#priceblock_dealprice')
            name =  driver.find_element_by_css_selector('#productTitle')
        except:
            update.message.reply_text('Are you sure this is an Amazon product page? '+url)
            driver.close()
        data[name.text] = {'url': url, 'price' : price.text}
        print(data.items())
        driver.close()
        print(chat_id)
        context.job_queue.run_repeating(notification, 30, context=chat_id, name=str(chat_id))
        update.message.reply_text("Good! We are now tracking one more Product!")
def notification(update, context):
#The Script
    job = context.job
    print('notification running')
    print(type(job.context))
    print(job.context)
    print(context.user_data.items())
    #Scrape 
    driver_options = webdriver.FirefoxOptions()
    driver_options.add_argument('--headless')
    driver_options.binary_location = bin_path.strip()
    driver = webdriver.Firefox(executable_path=driver_path.strip(), options=driver_options)
    for i in context.user_data.items():
        driver.get(i[1]['url'])
        try:
            try:
                new_price = driver.find_element_by_css_selector('#priceblock_ourprice')
            except common.exceptions.NoSuchElementException:
                try:
                    new_price = driver.find_element_by_css_selector('#priceblock_saleprice')
                except:
                    new_price = driver.find_element_by_css_selector('#priceblock_dealprice')
        except:
            context.bot.send_message(job.context, text='Are you sure this is an Amazon product page?'+i['url'])
                        
            #Notification
                        
            if '-' in new_price.text and float(new_price.text[1:4]) < float(i[1]['price']):
                context.bot.send_message(job.context, text=f"It's on your budget! \n{i[0]} \n {price.text} \n {i[1]['url']}")
                driver.close()
            elif float(new_price.text[1:9]) < float(i[1]['price']):
                context.bot.send_message(job.context, text=f"It's on your budget! \n{i[0]} \n {price.text} \n {i[1]['url']}")
                driver.close()
            else:
                driver.close()

#Main Function
def main():
    updater = Updater(token.strip())
    #Commands Instantiation
    updater.dispatcher.add_handler(CommandHandler('track', track))
    updater.dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, help))
   

    #Main loop methods
    updater.start_polling()
    updater.idle()
    
if __name__ == '__main__':
    main()

And this is the error message I am receiveing:

raise ValueError('The following arguments have not been supplied: %s' %
ValueError: The following arguments have not been supplied: context

It's indicating a value error, but I have paste the context and all that stuff in the right place at the notification function, don't I? I just follow the same principles of timerbot.py (at least I tried...).

1

1 Answers

2
votes

You have an extra input argument for your notification function:

def notification(update, context):

should be:

def notification(context):

Another thing to keep in mind, you don't currently have a way to remove jobs, so any product you want to check will continue infinitely until you restart your bot. You can add a "last" parameter to your run_repeating(), or add a command that will let you remove jobs (you should give them more unique names if this is the way you want to handle it).

UPDATE:

To pass data to the repeating job, try this:

def track(update: Update, context: CallbackContext) -> None:
    data = context.user_data
    ...
    data["url"] = url
    data["price"] = price.text
    data["chat_id"] = update.message.chat_id
    ...
    context.job_queue.run_repeating(notification, 3, context=context.user_data, name=str(chat_id))


def notification(context):
    job = context.job
    chat_id = job.context["chat_id"]
    url = job.context["url"]
    price = job.context["price"]
    ...

As far as naming jobs for removal, product names sounds good, or even the product id which is guaranteed to be unique (I think...).