diff --git a/README.md b/README.md index ca85ef8..b734981 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,10 @@ -# Tesla Inventory Auto-Buyer Bot πŸ‡ΉπŸ‡· +# Tesla Inventory Bot (No Docker) -This is an automated Telegram bot that monitors [Tesla TΓΌrkiye's inventory page](https://www.tesla.com/tr_TR/inventory/new/my?arrangeby=plh&zip=&range=0) and sends real-time alerts when new cars appear. It includes price, VIN, color info, and an inline "HEMEN SΔ°PARİŞ VER" button. +This Telegram bot monitors Tesla TΓΌrkiye inventory and notifies you of new cars. -## πŸ“¦ Features +## Setup -- πŸš— Detects all new inventory vehicles (Model Y) -- πŸ’¬ Sends Telegram messages (with order button) -- πŸ–ΌοΈ Includes exterior/interior color & price -- 🧠 Avoids duplicates using VIN tracking -- πŸ“… Cronjob support (run every X minutes) -- 🐳 Docker-based installation -- πŸ›  Menu-based installer script +1. Run `install.sh`. +2. Follow prompts to install Python, pip, and configure Telegram. +3. Use menu options to run manually or set up a cronjob. ---- - -## πŸš€ Installation (Recommended via `install.sh`) - -> This method clones the repo, builds Docker, prompts for Telegram info, and lets you configure a cronjob. - -### 1. Download and run: - -```bash -git clone https://git.bitmaster.cc/BitMaster/tesla-bot.git -cd tesla-bot -chmod +x install.sh -./install.sh -``` - -βœ… curl One-liner -```bash -bash <(curl -sSL https://git.bitmaster.cc/BitMaster/tesla-bot/raw/branch/main/install.sh) -``` - -βœ… wget One-liner -```bash -bash <(wget -qO- https://git.bitmaster.cc/BitMaster/tesla-bot/raw/branch/main/install.sh) -``` \ No newline at end of file diff --git a/bot.py b/bot.py index 9be07b2..89cc1bf 100644 --- a/bot.py +++ b/bot.py @@ -7,111 +7,88 @@ import os import requests TELEGRAM_TOKEN = "your_token_here" -CHAT_ID = "your_chat_id_here" -TESLA_URL = "https://www.tesla.com/tr_TR/inventory/new/my?arrangeby=plh&zip=&range=0" -SEEN_VINS_FILE = "seen_vins.txt" +CHAT_ID = "your_chat_id_here" +TESLA_URL = "https://www.tesla.com/tr_TR/inventory/new/my?arrangeby=plh&zip=&range=0" +SEEN_VINS_FILE= "seen_vins.txt" -def detect_chat_id(token): - try: - resp = requests.get(f"https://api.telegram.org/bot{token}/getUpdates") - data = resp.json() - if not data.get("ok"): - return None - for update in reversed(data.get("result", [])): - chat = update.get("message", {}).get("chat", {}) - chat_id = chat.get("id") - if chat_id: - return chat_id - except Exception as e: - print(f"Chat ID detection failed: {e}") - return None - -scraper = cloudscraper.create_scraper(browser={'browser': 'chrome', 'platform': 'windows'}, delay=5) +scraper = cloudscraper.create_scraper( + browser={'browser': 'chrome', 'platform': 'windows'}, + delay=5 +) HEADERS = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/114.0.0.0 Safari/537.36", + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/114.0.0.0 Safari/537.36" + ), "Accept-Language": "tr-TR,tr;q=0.9", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9", "Referer": "https://www.tesla.com/" } -def notify_telegram_with_button(message, button_text, button_url): +def notify_telegram_with_button(msg, btn_text, btn_url): payload = { "chat_id": CHAT_ID, - "text": message, + "text": msg, "parse_mode": "HTML", "reply_markup": json.dumps({ - "inline_keyboard": [[{ - "text": button_text, - "url": button_url - }]] + "inline_keyboard": [[{"text": btn_text, "url": btn_url}]] }) } - requests.post(f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage", data=payload) + requests.post( + f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage", + data=payload, + headers=HEADERS, + timeout=30 + ) def load_seen_vins(): if not os.path.exists(SEEN_VINS_FILE): return set() - with open(SEEN_VINS_FILE, "r") as f: - return set(line.strip() for line in f.readlines()) + return set(open(SEEN_VINS_FILE).read().splitlines()) def save_seen_vins(vins): with open(SEEN_VINS_FILE, "w") as f: - f.writelines(f"{vin}\n" for vin in vins) + f.write("\n".join(vins)) def get_page(url): time.sleep(random.uniform(10, 60)) try: - return scraper.get(url, headers=HEADERS, timeout=30) + return scraper.get(url, headers=HEADERS, timeout=60) except Exception as e: print("Request failed:", e) + notify_telegram_with_button(f"⚠️ Failed to fetch Tesla page: {e}", "Retry", TESLA_URL) return None def check_inventory(): resp = get_page(TESLA_URL) - if resp is None: - notify_telegram_with_button("⚠️ Bot failed to get Tesla page (no response).", "Retry", TESLA_URL) - return - - if resp.status_code in (403, 429) or "captcha" in resp.text.lower(): - notify_telegram_with_button(f"⚠️ Tesla bot likely detected. Status: {resp.status_code}", "Open Tesla", TESLA_URL) + if not resp or resp.status_code in (403,429) or "captcha" in resp.text.lower(): + notify_telegram_with_button(f"⚠️ Bot likely detected. Status: {getattr(resp,'status_code','N/A')}", "Open Tesla", TESLA_URL) return try: - page_text = resp.text - json_data_match = re.search(r'\"results\"\s*:\s*(\[\{.*?\}\])', page_text, re.DOTALL) - if not json_data_match: - raise Exception("Could not find inventory JSON") - - cars = json.loads(json_data_match.group(1)) - seen_vins = load_seen_vins() - new_seen = seen_vins.copy() - + m = re.search(r'"results"\s*:\s*(\[\{.*?\}\])', resp.text, re.DOTALL) + cars = json.loads(m.group(1)) if m else [] + seen = load_seen_vins() for car in cars: vin = car.get("vin") - if not vin or vin in seen_vins: - continue - - price = car.get("price", "N/A") - ext_color = car.get("paint", {}).get("value", "N/A") - int_color = car.get("interior", {}).get("value", "N/A") - order_link = f"https://www.tesla.com/tr_TR/my/order/{vin}" - - message = ( - f"🚨 YENΔ° ARAΓ‡ GELDΔ°!\n" - f"πŸ’° Fiyat: {price:,} TL\n" - f"🎨 Dış Renk: {ext_color.upper()}\n" - f"πŸͺ‘ Δ°Γ§ Renk: {int_color.upper()}\n" - f"πŸ”‘ VIN: {vin}" - ) - - notify_telegram_with_button(message, "πŸš— HEMEN SΔ°PARİŞ VER", order_link) - new_seen.add(vin) - - save_seen_vins(new_seen) - + if not vin or vin in seen: continue + price = car.get("price","N/A") + ext = car.get("paint",{}).get("value","N/A").upper() + inn = car.get("interior",{}).get("value","N/A").upper() + link= f"https://www.tesla.com/tr_TR/my/order/{vin}" + msg = (f"🚨 NEW CAR!\n" + f"πŸ’° Price: {price:,} TL\n" + f"🎨 Exterior: {ext}\n" + f"πŸͺ‘ Interior: {inn}\n" + f"πŸ”‘ VIN: {vin}") + notify_telegram_with_button(msg,"πŸš— ORDER NOW",link) + seen.add(vin) + save_seen_vins(seen) except Exception as e: - print("Error parsing inventory:", e) - notify_telegram_with_button("❌ Bot error during parsing.", "Retry", TESLA_URL) + print("Parsing error:",e) + notify_telegram_with_button("❌ Parsing error occurred.","Retry",TESLA_URL) -check_inventory() +if __name__=="__main__": + check_inventory() diff --git a/install.sh b/install.sh index 5a9c823..d2ad181 100644 --- a/install.sh +++ b/install.sh @@ -29,7 +29,6 @@ function check_requirements() { PYTHON_OK=true else echo -e "${RED}Missing${NC}" - PYTHON_OK=false fi echo -n "πŸ“¦ pip3: " @@ -38,20 +37,18 @@ function check_requirements() { PIP_OK=true else echo -e "${RED}Missing${NC}" - PIP_OK=false fi echo -n "πŸ“ Project folder '$CLONE_DIR': " if [ -d "$CLONE_DIR" ]; then - echo -e "${YELLOW}Exists${NC}" + echo -e "${GREEN}Exists${NC}" else echo -e "${RED}Not present${NC}" fi if [ "$PYTHON_OK" = false ]; then - echo -e "${YELLOW}❓ Python3 is required. Do you want to install it now? [Y/n]${NC}" - read -rp "> " confirm - confirm="${confirm,,}" + echo -e "${YELLOW}❓ Python3 is required. Install now? [Y/n]${NC}" + read -rp "> " confirm; confirm="${confirm,,}" if [[ "$confirm" =~ ^(y|yes| )?$ ]]; then sudo apt update && sudo apt install -y python3 else @@ -60,9 +57,8 @@ function check_requirements() { fi if [ "$PIP_OK" = false ]; then - echo -e "${YELLOW}❓ pip3 is required. Do you want to install it now? [Y/n]${NC}" - read -rp "> " confirm - confirm="${confirm,,}" + echo -e "${YELLOW}❓ pip3 is required. Install now? [Y/n]${NC}" + read -rp "> " confirm; confirm="${confirm,,}" if [[ "$confirm" =~ ^(y|yes| )?$ ]]; then sudo apt update && sudo apt install -y python3-pip else @@ -74,8 +70,8 @@ function check_requirements() { function clone_and_setup() { echo -e "${GREEN}πŸ“₯ Cloning repo...${NC}" rm -rf "$CLONE_DIR" - git clone "$REPO_URL" "$CLONE_DIR" - cd "$CLONE_DIR" || exit 1 + git clone "$REPO_URL" "$CLONE_DIR" || { echo -e "${RED}❌ git clone failed${NC}"; return; } + cd "$CLONE_DIR" echo -e "${GREEN}πŸ“¦ Installing Python dependencies...${NC}" pip3 install -r requirements.txt @@ -83,33 +79,37 @@ function clone_and_setup() { echo -e "${YELLOW}πŸ“¨ Enter your Telegram bot token:${NC}" read -rp "πŸ”‘ TELEGRAM_TOKEN: " token - echo -e "${YELLOW}πŸ€– Auto detecting the Telegram chat-ID...${NC}" - auto_chat_id=$(curl -s "https://api.telegram.org/bot$token/getUpdates" | grep -oE '"id":[0-9-]+' | head -n1 | cut -d: -f2) + echo -e "${YELLOW}πŸ€– Auto-detecting your Telegram chat ID...${NC}" + auto_chat_id=$(python3 - < /dev/null - echo -e "${GREEN}βœ… Test message sent. Check your Telegram!${NC}" + curl -s -X POST "https://api.telegram.org/bot$token/sendMessage" -d chat_id="$chat_id" -d text="βœ… Tesla bot installed and ready!" -d parse_mode="HTML" + echo -e "${GREEN}βœ… Test message sent!${NC}" cd .. read -rp "$(echo -e "${GREEN}πŸ” Press Enter to return to the main menu...${NC}")" @@ -117,12 +117,12 @@ function clone_and_setup() { function run_bot_now() { if [ ! -f "$CLONE_DIR/bot.py" ]; then - echo -e "${RED}❌ Bot not installed. Please run option 1 first.${NC}" + echo -e "${RED}❌ Bot not installed. Run option 1 first.${NC}" else echo -e "${YELLOW}βš™οΈ Running bot.py...${NC}" python3 "$CLONE_DIR/bot.py" fi - read -rp "$(echo -e "${GREEN}πŸ” Press Enter to return to the main menu...${NC}")" + read -rp "$(echo -e "${GREEN}πŸ” Press Enter to return...${NC}")" } function setup_cronjob() { @@ -130,43 +130,37 @@ function setup_cronjob() { if [ ! -d "$CLONE_DIR" ]; then echo -e "${RED}❌ Bot not installed. Run option 1 first.${NC}" else - echo -e "${YELLOW}⏱ How often should the bot run? (in minutes, 1-60)${NC}" - read -rp "Interval: " interval + read -rp "⏱ Run interval in minutes (1–60): " interval if ! [[ "$interval" =~ ^[0-9]+$ ]] || [ "$interval" -lt 1 ] || [ "$interval" -gt 60 ]; then - echo -e "${RED}❌ Invalid input. Must be between 1 and 60.${NC}" + echo -e "${RED}❌ Invalid interval.${NC}" else - (crontab -l 2>/dev/null | grep -v "$CRON_NAME"; echo "*/$interval * * * * cd $(pwd)/$CLONE_DIR && python3 bot.py >> cron.log 2>&1") | crontab - - echo -e "${GREEN}βœ… Cronjob installed to run every $interval minute(s).${NC}" + (crontab -l 2>/dev/null | grep -v "$CRON_NAME"; echo "*/$interval * * * * cd $(pwd)/$CLONE_DIR && python3 bot.py >> cron.log 2>&1") | crontab - + echo -e "${GREEN}βœ… Cronjob set for every $interval min.${NC}" fi fi - read -rp "$(echo -e "${GREEN}πŸ” Press Enter to return to the main menu...${NC}")" + read -rp "$(echo -e "${GREEN}πŸ” Press Enter to return...${NC}")" } function remove_cronjob() { - echo -e "${YELLOW}Removing bot cronjob...${NC}" - crontab -l 2>/dev/null | grep -v "$CRON_NAME" | crontab - + crontab -l | grep -v "$CRON_NAME" | crontab - echo -e "${GREEN}βœ… Cronjob removed.${NC}" - read -rp "$(echo -e "${GREEN}πŸ” Press Enter to return to the main menu...${NC}")" + read -rp "$(echo -e "${GREEN}πŸ” Press Enter to return...${NC}")" } while true; do - clear - banner - check_requirements + clear; banner; check_requirements echo -e "${YELLOW}1. Install bot and configure" echo -e "2. Run bot manually now" - echo -e "3. Setup new cronjob (every X min)" + echo -e "3. Setup new cronjob" echo -e "4. Remove existing cronjob" echo -e "5. Exit${NC}" - echo "" read -rp "$(echo -e "${YELLOW}πŸ”— Choose [1-5]: ${NC}")" choice - case $choice in 1) clone_and_setup ;; - 2) run_bot_now ;; - 3) setup_cronjob ;; - 4) remove_cronjob ;; + 2) run_bot_now ;; + 3) setup_cronjob ;; + 4) remove_cronjob ;; 5) echo -e "${RED}❌ Exiting...${NC}"; exit 0 ;; *) echo -e "${RED}❌ Invalid option.${NC}"; sleep 1 ;; esac -done \ No newline at end of file +done diff --git a/requirements.txt b/requirements.txt index 1b44c54..f7b8e8f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ requests - cloudscraper