from telegram import Update, Bot from telegram.ext import Updater, CommandHandler, CallbackContext from bs4 import BeautifulSoup import requests import time import difflib import threading # Parameters TOKEN = 'your-bot-token' # Telegram bot token CHAT_ID = 'your-chat-id' # Telegram chat id MAX_TRACKED_SITES = 5 # Maximum number of sites a user can track MIN_TRACKING_INTERVAL = 30 # Minimum tracking interval (minutes) active_threads = {} # Dictionary to store tracked websites and threads def start(update: Update, context: CallbackContext) -> None: context.bot.send_message(chat_id=update.effective_chat.id, text="Hello! You can track websites using the /track command. Example: /track www.google.com 60") def track_website_changes(update: Update, context: CallbackContext, url, interval) -> None: old_content = fetch_website_content(url) while True: time.sleep(interval * 60) # convert minutes to seconds new_content = fetch_website_content(url) if url not in active_threads[update.effective_chat.id]: # If cancel command is received, stop this thread return if new_content != old_content: # Check if the content matches with the previous fetch changes = difflib.ndiff(old_content.splitlines(keepends=True), new_content.splitlines(keepends=True)) changes_report = list(changes) message = '\n'.join(changes_report) # Messages longer than 4096 characters are not accepted by Telegram. if len(message) > 4096: message = message[:4095] context.bot.send_message(chat_id=update.effective_chat.id, text=message) old_content = new_content def fetch_website_content(url): response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') return soup.prettify() def track(update: Update, context: CallbackContext): url = context.args[0] interval = int(context.args[1]) if interval < MIN_TRACKING_INTERVAL: context.bot.send_message(chat_id=update.effective_chat.id, text=f"The tracking interval must be at least {MIN_TRACKING_INTERVAL} minutes.") return if update.effective_chat.id not in active_threads: active_threads[update.effective_chat.id] = {} if url in active_threads[update.effective_chat.id]: # If the website is already being tracked, don't start a new thread context.bot.send_message(chat_id=update.effective_chat.id, text="This website is already being tracked.") return if len(active_threads[update.effective_chat.id]) >= MAX_TRACKED_SITES: context.bot.send_message(chat_id=update.effective_chat.id, text=f"You can track up to {MAX_TRACKED_SITES} websites.") return thread = threading.Thread(target=track_website_changes, args=(update, context, url, interval)) active_threads[update.effective_chat.id][url] = thread thread.start() def list_tracked_websites(update: Update, context: CallbackContext): if update.effective_chat.id not in active_threads or not active_threads[update.effective_chat.id]: context.bot.send_message(chat_id=update.effective_chat.id, text="No websites are being tracked.") return message = "Tracked websites:\n" + "\n".join(active_threads[update.effective_chat.id].keys()) context.bot.send_message(chat_id=update.effective_chat.id, text=message) def cancel_tracking(update: Update, context: CallbackContext): url = context.args[0] if update.effective_chat.id not in active_threads or url not in active_threads[update.effective_chat.id]: context.bot.send_message(chat_id=update.effective_chat.id, text="This website is not being tracked.") return del active_threads[update.effective_chat.id][url] # Remove the tracked website and thread from the dictionary context.bot.send_message(chat_id=update.effective_chat.id, text="Tracking for the website has been canceled.") def help_message(update: Update, context: CallbackContext): help_text = """ /track www.example.com 60: Tracks the website at www.example.com with an interval of 60 minutes. Sends a notification when a change is detected. The tracking interval must be at least 30 minutes. /list: Returns a list of actively tracked websites. /cancel www.example.com: Stops tracking the website at www.example.com. """ context.bot.send_message(chat_id=update.effective_chat.id, text=help_text) updater = Updater(token=TOKEN, use_context=True) dispatcher = updater.dispatcher start_handler = CommandHandler('start', start) track_handler = CommandHandler('track', track) list_handler = CommandHandler('list', list_tracked_websites) cancel_handler = CommandHandler('cancel', cancel_tracking) help_handler = CommandHandler('help', help_message) dispatcher.add_handler(start_handler) dispatcher.add_handler(track_handler) dispatcher.add_handler(list_handler) dispatcher.add_handler(cancel_handler) dispatcher.add_handler(help_handler) updater.start_polling()