├── .github └── workflows │ └── stale.yml ├── .gitignore ├── README.md ├── app.py ├── assets ├── dark-logo.png ├── light-logo.png └── logo.ico ├── bot.py ├── requirements.txt └── utils.py /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close stale issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' # Runs every day at midnight 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Close stale issues and pull requests 13 | uses: actions/stale@v9 14 | with: 15 | stale-issue-message: > 16 | This issue has been marked as stale due to inactivity. 17 | If you believe this issue is still relevant, please comment to keep it open. 18 | Otherwise, it will be closed in 7 days. 19 | stale-pr-message: > 20 | This pull request has been marked as stale due to inactivity. 21 | Please update the PR if it is still active, or it will be closed in 7 days. 22 | days-before-stale: 14 23 | days-before-close: 7 24 | stale-issue-label: 'stale' 25 | stale-pr-label: 'stale' 26 | exempt-issue-labels: 'type: bug, type: docs, type: feature, type: improvement, type: question' 27 | exempt-pr-labels: 'type: bug, type: docs, type: feature, type: improvement, type: question' 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | cpatcha.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Logo 3 |

4 | 5 | ## Table of Contents 6 | - [Description](#description) 7 | - [Preview](#preview) 8 | - [Features](#features) 9 | - [Watch the Installation Video](#watch-the-installation-video-outdated) 10 | - [Prerequisites](#prerequisites) 11 | - [Installation](#installation) 12 | - [Usage](#usage) 13 | - [Contributing](#contributing) 14 | - [Disclaimer](#disclaimer) 15 | - [Acknowledgements](#acknowledgements) 16 | - [Issues](#issues) 17 | 18 | # TIKTOD V3 19 | 20 | ## Description 21 | TIKTOD V3 is a bot application designed to automate interactions on Zefoy website, such as increasing views, hearts, followers, and shares on a specified video. The bot uses technologies like Selenium for web automation and OCR (Optical Character Recognition) for solving captchas. 22 | 23 | ## Preview 24 | Here is a screenshot of the TIKTOD V3 application: 25 | 26 |

27 | TIKTOD V3 Screenshot 28 |

29 | 30 | ## Features 31 | - User-friendly interface using `customtkinter`. 32 | - Added feature to auto-detect available modes on the website. 33 | - Automatic captcha solving using OCR with `pytesseract`. 34 | - Light mode and dark mode support. 35 | - Detailed stats. 36 | 37 | ## Watch the Installation Video (outdated) 38 | If you are unsure how to install the application, please watch this [installation video](https://youtu.be/50gvfn1zg-w) for a step-by-step guide, or for a demo of the bot. 39 | 40 | ## Prerequisites 41 | - Google Chrome (version 89 or later) must be installed on your system. You can download it from [here](https://www.google.com/chrome/). 42 | - Ensure Tesseract OCR is installed on your system. You can download it from [here](https://github.com/tesseract-ocr/tesseract/releases/latest). 43 | Additionally, make sure to add Tesseract to your system PATH. Follow this [tutorial](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/) for instructions on how to add it to the PATH on Windows 10. 44 | - Python 3.7 or higher must be installed on your system. You can download it from [here](https://www.python.org/downloads/). 45 | 46 | > **Note:** If you plan to use the executable version, you do not need to install Python. Ensure that Python (if you plan to use the source code) and Tesseract OCR are added to your system's PATH. 47 | 48 | 49 | ## Installation 50 | 51 | 1. Download the latest release zip or executable from the [releases page](https://github.com/kangoka/tiktodv3/releases). 52 | 2. If you downloaded the zip file, extract it to a directory of your choice. 53 | 3. Navigate to the extracted directory or the directory containing the executable. 54 | 55 | ## Usage 56 | 57 | ### Option 1: Using Source Code 58 | 59 | 1. Install the required packages: 60 | ```sh 61 | pip install -r requirements.txt 62 | ``` 63 | 2. Run the application: 64 | ```sh 65 | python app.py 66 | ``` 67 | 68 | ### Option 2: Using Executable 69 | 70 | 1. Run the executable file directly. 71 | 72 | 2. Enter the TikTok video URL in the provided input field. 73 | 3. Click the "Setup" button to initialize the bot. 74 | 4. Select the desired mode (Views, Hearts, Followers, Shares) from the sidebar. 75 | 5. Click the "Start" button to begin the automation process. 76 | 6. To stop the application or change the mode, click the "Stop" button. 77 | 78 | 79 | ## Contributing 80 | Contributions are welcome! Please fork the repository and submit a pull request with your changes. 81 | 82 | ## Disclaimer 83 | 84 | This project is intended for educational purposes only. The use of this bot to manipulate TikTok metrics may violate TikTok's terms of service and could result in legal consequences. Use it responsibly, ethically, and at your own risk. 85 | 86 | ## Acknowledgements 87 | 88 | Thanks to Zefoy for providing free services and previous contributors for their valuable input and support. 89 | 90 | ## Issues 91 | 92 | If you encounter any issues while using TIKTOD V3, please open an issue on the [GitHub repository](https://github.com/kangoka/tiktodv3/issues) with detailed information about the issue, including: 93 | - Steps to reproduce the issue. 94 | - Any error messages or logs. 95 | - Your operating system and Python version. -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import customtkinter as ctk 3 | import threading 4 | import time 5 | from PIL import Image # Import the Image class 6 | from bot import Bot 7 | from utils import log_message, resource_path 8 | 9 | class App(ctk.CTk): 10 | def __init__(self): 11 | super().__init__() 12 | 13 | self.title("TIKTOD V3") 14 | self.geometry("800x600") 15 | self.iconbitmap(resource_path("assets/logo.ico")) 16 | 17 | # Configure grid layout 18 | self.grid_columnconfigure(1, weight=1) 19 | self.grid_rowconfigure(0, weight=1) 20 | 21 | # Create sidebar frame with widgets 22 | self.sidebar_frame = ctk.CTkFrame(self, width=200, corner_radius=0, fg_color="gray20") 23 | self.sidebar_frame.grid(row=0, column=0, rowspan=5, sticky="nsew") 24 | self.sidebar_frame.grid_rowconfigure(9, weight=1) 25 | 26 | custom_font = ctk.CTkFont(family="Helvetica", size=14, weight="bold") 27 | 28 | self.logo_image_dark = ctk.CTkImage(light_image=Image.open(resource_path("assets/dark-logo.png")), size=(100, 100)) 29 | self.logo_image_light = ctk.CTkImage(light_image=Image.open(resource_path("assets/light-logo.png")), size=(100, 100)) 30 | self.logo_image_label = ctk.CTkLabel(self.sidebar_frame, image=self.logo_image_dark, text="") 31 | self.logo_image_label.grid(row=0, column=0, padx=20, pady=(20, 20)) 32 | 33 | self.link_label = ctk.CTkLabel(self.sidebar_frame, text="TikTok video URL:", font=custom_font, anchor="w") # Added anchor="w" 34 | self.link_label.grid(row=1, column=0, padx=20, pady=1, sticky="w") # Added sticky="w" 35 | self.link_entry = ctk.CTkEntry(self.sidebar_frame, width=180, font=custom_font) 36 | self.link_entry.grid(row=2, column=0, padx=20, pady=1) 37 | 38 | self.amount_label = ctk.CTkLabel(self.sidebar_frame, text="Amount:", font=custom_font, anchor="w") # New label 39 | self.amount_label.grid(row=3, column=0, padx=20, pady=1, sticky="w") # New label grid 40 | self.amount_entry = ctk.CTkEntry(self.sidebar_frame, width=180, font=custom_font) # New entry 41 | self.amount_entry.grid(row=4, column=0, padx=20, pady=1) # New entry grid 42 | 43 | self.start_button = ctk.CTkButton(self.sidebar_frame, text="Setup", command=lambda: threading.Thread(target=self.setup_bot).start(), font=custom_font) 44 | self.start_button.grid(row=6, column=0, padx=20, pady=20) # Adjusted row to move the button lower 45 | 46 | # Create main frame with tab view for log and stats 47 | self.main_frame = ctk.CTkFrame(self, corner_radius=0) 48 | self.main_frame.grid(row=0, column=1, sticky="nsew") 49 | self.main_frame.grid_rowconfigure(1, weight=1) 50 | self.main_frame.grid_columnconfigure(0, weight=1) 51 | 52 | self.tab_view = ctk.CTkTabview(self.main_frame) 53 | self.tab_view.grid(row=0, column=0, padx=20, pady=10, sticky="nsew") 54 | 55 | self.log_tab = self.tab_view.add("Log") 56 | self.log_text = ctk.CTkTextbox(self.log_tab, height=300, width=600, font=custom_font) 57 | self.log_text.pack(padx=20, pady=10, fill="both", expand=True) 58 | 59 | self.stats_tab = self.tab_view.add("Stats") 60 | self.stats_tab.grid_rowconfigure(0, weight=1) 61 | self.stats_tab.grid_columnconfigure(0, weight=1) 62 | 63 | self.stats_frame = ctk.CTkFrame(self.stats_tab) 64 | self.stats_frame.grid(row=0, column=0, padx=20, pady=10, sticky="nsew") 65 | 66 | self.stats_labels = { 67 | "views": ctk.CTkLabel(self.stats_frame, text="Views Sent: 0", font=custom_font), 68 | "hearts": ctk.CTkLabel(self.stats_frame, text="Hearts Sent: 0", font=custom_font), 69 | "followers": ctk.CTkLabel(self.stats_frame, text="Followers Sent: 0", font=custom_font), 70 | "shares": ctk.CTkLabel(self.stats_frame, text="Shares Sent: 0", font=custom_font), 71 | "favorites": ctk.CTkLabel(self.stats_frame, text="Favorites Sent: 0", font=custom_font), 72 | "elapsed_time": ctk.CTkLabel(self.stats_frame, text="Elapsed Time: 00:00:00", font=custom_font) 73 | } 74 | 75 | for i, label in enumerate(self.stats_labels.values()): 76 | label.grid(row=i, column=0, padx=20, pady=5, sticky="w") 77 | 78 | self.running = False # Add a flag to control the loop 79 | self.mode_var = tk.StringVar(value="Views") # Initialize mode_var 80 | self.bot = Bot(self, log_message) 81 | self.elapsed_time = 0 # Initialize elapsed_time 82 | self.views = 0 # Initialize views 83 | self.hearts = 0 # Initialize hearts 84 | self.followers = 0 # Initialize followers 85 | self.shares = 0 # Initialize shares 86 | self.favorites = 0 # Initialize favorites 87 | 88 | self.theme_switch_var = tk.StringVar(value="dark") 89 | self.theme_switch = ctk.CTkSwitch(self.sidebar_frame, text="Dark Mode", variable=self.theme_switch_var, onvalue="dark", offvalue="light", command=self.switch_theme, font=custom_font) 90 | self.theme_switch.grid(row=10, column=0, padx=20, pady=10, sticky="s") 91 | 92 | self.version_label = ctk.CTkLabel(self, text="Version 1.2.0", fg_color="transparent") 93 | self.version_label.grid(row=5, column=1, padx=20, pady=(10, 0), sticky="se") 94 | 95 | self.github_link = ctk.CTkLabel(self, text="https://github.com/kangoka/tiktodv3", fg_color="transparent", cursor="hand2") 96 | self.github_link.grid(row=6, column=1, padx=20, pady=(0, 10), sticky="se") 97 | self.github_link.bind("", lambda e: self.open_github()) 98 | 99 | def open_github(self): 100 | import webbrowser 101 | webbrowser.open("https://github.com/kangoka/tiktodv3") 102 | 103 | def switch_theme(self): 104 | if self.theme_switch_var.get() == "dark": 105 | ctk.set_appearance_mode("dark") 106 | self.sidebar_frame.configure(fg_color="gray20") 107 | self.logo_image_label.configure(image=self.logo_image_dark) 108 | else: 109 | ctk.set_appearance_mode("light") 110 | self.sidebar_frame.configure(fg_color="white") 111 | self.logo_image_label.configure(image=self.logo_image_light) 112 | 113 | def setup_bot(self): 114 | self.bot.setup_bot() 115 | 116 | def start_bot(self): 117 | auto = self.mode_var.get() 118 | vidUrl = self.link_entry.get() 119 | 120 | try: 121 | amount = int(self.amount_entry.get()) # Get the amount entered and ensure it is a number 122 | except ValueError: 123 | log_message(self, "Amount must be a number") 124 | return 125 | 126 | if auto in ["Views", "Hearts", "Followers", "Shares", "Favorites"]: 127 | if not self.running: 128 | self.start_time = time.time() - self.elapsed_time # Continue from the last elapsed time 129 | self.log_text.delete(1.0, tk.END) # Clear the log area 130 | log_message(self, "TIKTOD V3") 131 | log_message(self, "Log:") 132 | 133 | self.running = True # Set the flag to True 134 | self.bot.running = True # Ensure the bot's running flag is also set to True 135 | 136 | self.link_entry.configure(state="disabled") # Disable the URL entry 137 | self.amount_entry.configure(state="disabled") # Disable the amount entry 138 | self.mode_menu.configure(state="disabled") # Disable the option menu 139 | 140 | threading.Thread(target=self.update_stats_label).start() # Start the stats update thread 141 | 142 | threading.Thread(target=self.bot.loop, args=(vidUrl, auto, amount)).start() # Pass the amount to the bot loop 143 | 144 | self.start_button.configure(text="Stop", command=self.stop_bot) 145 | else: 146 | log_message(self, f"{auto} is not a valid option. Please pick Views, Hearts, Followers, Shares, or Favorites") 147 | 148 | def stop_bot(self): 149 | log_message(self, "Bot stopped") 150 | 151 | self.link_entry.configure(state="normal") # Enable the URL entry 152 | self.amount_entry.configure(state="normal") # Enable the amount entry 153 | self.mode_menu.configure(state="normal") # Enable the option menu 154 | 155 | self.running = False # Set the flag to False 156 | self.bot.running = False # Ensure the bot's running flag is also set to False 157 | self.elapsed_time = time.time() - self.start_time # Save the elapsed time 158 | 159 | self.start_button.configure(text="Start", command=self.start_bot) 160 | 161 | def update_stats_label(self): 162 | while self.running: 163 | time_elapsed = time.strftime('%H:%M:%S', time.gmtime(time.time() - self.start_time)) 164 | self.stats_labels["elapsed_time"].configure(text=f"Elapsed Time: {time_elapsed}") 165 | self.stats_labels["views"].configure(text=f"Views Sent: {self.views}") 166 | self.stats_labels["hearts"].configure(text=f"Hearts Sent: {self.hearts}") 167 | self.stats_labels["followers"].configure(text=f"Followers Sent: {self.followers}") 168 | self.stats_labels["shares"].configure(text=f"Shares Sent: {self.shares}") 169 | self.stats_labels["favorites"].configure(text=f"Favorites Sent: {self.favorites}") 170 | time.sleep(1) 171 | 172 | if __name__ == "__main__": 173 | app = App() 174 | app.mainloop() 175 | -------------------------------------------------------------------------------- /assets/dark-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangoka/tiktodv3/8365524ce587ec22b56eeb73d72fc33a04e530e6/assets/dark-logo.png -------------------------------------------------------------------------------- /assets/light-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangoka/tiktodv3/8365524ce587ec22b56eeb73d72fc33a04e530e6/assets/light-logo.png -------------------------------------------------------------------------------- /assets/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangoka/tiktodv3/8365524ce587ec22b56eeb73d72fc33a04e530e6/assets/logo.ico -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import chromedriver_autoinstaller 2 | from selenium import webdriver 3 | from selenium.webdriver.chrome.service import Service 4 | from selenium.webdriver.common.by import By 5 | from selenium.webdriver.chrome.options import Options 6 | from selenium.webdriver.support.ui import WebDriverWait 7 | from selenium.webdriver.support import expected_conditions as EC 8 | from PIL import Image 9 | import pytesseract 10 | import time 11 | import random 12 | import customtkinter as ctk 13 | import tkinter as tk 14 | import re 15 | from utils import log_message, resource_path 16 | 17 | class Bot: 18 | def __init__(self, app, log_callback): 19 | self.app = app 20 | self.log_callback = log_callback 21 | self.driver = None 22 | self.running = False 23 | 24 | def setup_bot(self): 25 | log_message(self.app, "Setting up the bot...") 26 | # Automatically install the correct version of ChromeDriver 27 | chromedriver_autoinstaller.install() 28 | 29 | chrome_options = Options() 30 | chrome_options.add_argument("--headless") # Enable headless mode 31 | chrome_options.add_argument("--disable-gpu") 32 | chrome_options.add_argument("--no-sandbox") 33 | chrome_options.add_argument("--disable-dev-shm-usage") 34 | chrome_options.add_argument("--disable-webgl") 35 | chrome_options.add_argument("--disable-software-rasterizer") 36 | chrome_options.add_argument("--log-level=3") # Suppress most logs 37 | chrome_options.add_argument("--disable-logging") # Disable logging 38 | 39 | self.driver = webdriver.Chrome(options=chrome_options) 40 | 41 | # Block requests to fundingchoicesmessages.google.com 42 | self.driver.execute_cdp_cmd( 43 | "Network.setBlockedURLs", 44 | {"urls": ["https://fundingchoicesmessages.google.com/*"]} 45 | ) 46 | self.driver.execute_cdp_cmd("Network.enable", {}) # Enable network interception 47 | 48 | self.get_captcha() 49 | 50 | # Create a frame for the mode selection 51 | self.app.mode_frame = ctk.CTkFrame(self.app.sidebar_frame, corner_radius=0) 52 | self.app.mode_frame.grid(row=5, column=0, padx=20, pady=10, sticky="nsew") 53 | 54 | self.app.mode_label = ctk.CTkLabel(self.app.mode_frame, text="Select Mode:") 55 | self.app.mode_label.grid(row=0, column=0, padx=20, pady=10) 56 | self.app.mode_var = tk.StringVar(value="----------") 57 | 58 | available_modes = [] 59 | buttons = { 60 | "Followers": '//button[@class="btn btn-primary rounded-0 t-followers-button"]', 61 | "Hearts": '//button[@class="btn btn-primary rounded-0 t-hearts-button"]', 62 | "Views": '//button[@class="btn btn-primary rounded-0 t-views-button"]', 63 | "Shares": '//button[@class="btn btn-primary rounded-0 t-shares-button"]', 64 | "Favorites": '//button[@class="btn btn-primary rounded-0 t-favorites-button"]', 65 | "Live Stream": '//button[@class="btn btn-primary rounded-0 t-livestream-button"]' 66 | } 67 | 68 | for text, xpath in buttons.items(): 69 | try: 70 | button = self.driver.find_element(By.XPATH, xpath) 71 | if not button.get_attribute("disabled"): 72 | available_modes.append(text) 73 | except Exception as e: 74 | log_message(self.app, f"Error finding button {text}: {e}") 75 | 76 | self.app.mode_menu = ctk.CTkOptionMenu(self.app.mode_frame, variable=self.app.mode_var, values=available_modes) 77 | self.app.mode_menu.grid(row=1, column=0, padx=20, pady=10) 78 | 79 | self.app.start_button.configure(text="Start", command=self.app.start_bot) 80 | 81 | def get_captcha(self): 82 | url = "http://zefoy.com" # Replace with the actual URL of the main page 83 | 84 | try: 85 | self.driver.get(url) 86 | # Wait for the page to load 87 | WebDriverWait(self.driver, 20).until(EC.presence_of_element_located((By.TAG_NAME, 'body'))) 88 | 89 | for attempt in range(3): 90 | try: 91 | # Wait for the captcha image to be present 92 | captcha_img_tag = WebDriverWait(self.driver, 10).until( 93 | EC.presence_of_element_located((By.XPATH, '//img[@class="img-thumbnail card-img-top border-0"]')) 94 | ) # Using an XPath selector 95 | 96 | if captcha_img_tag: 97 | log_message(self.app, "Captcha image found") 98 | # Take a screenshot of the captcha image element 99 | captcha_img_tag.screenshot('captcha.png') 100 | log_message(self.app, "Captcha saved as captcha.png") 101 | image = Image.open('captcha.png') 102 | captcha_text = self.read_captcha(image) 103 | log_message(self.app, f"Captcha text: {captcha_text}") 104 | 105 | # Find the input field and send the captcha text 106 | input_field = self.driver.find_element(By.XPATH, '//input[@class="form-control form-control-lg text-center rounded-0 remove-spaces"]') 107 | input_field.send_keys(captcha_text) 108 | log_message(self.app, "Captcha text entered") 109 | 110 | time.sleep(3) # Wait for 5 seconds before proceeding 111 | 112 | # Check if the specified element is present 113 | if self.driver.find_elements(By.XPATH, '/html/body/div[6]/div/div[2]/div/div/div[1]'): 114 | log_message(self.app, "Setup complete. Select mode and start the bot. Make sure you have entered the correct URL.") 115 | break 116 | else: 117 | log_message(self.app, "Captcha image not found on the main page") 118 | except Exception as e: 119 | log_message(self.app, f"Attempt {attempt + 1} failed: {e}") 120 | if attempt < 2: 121 | time.sleep(3) # Wait for 3 seconds before retrying 122 | else: 123 | log_message(self.app, "Max attempts reached. Exiting. Please restart the application.") 124 | return # Exit the function 125 | except Exception as e: 126 | log_message(self.app, f"Error during captcha solving: {e}") 127 | 128 | def read_captcha(self, image): 129 | config = r'--oem 3 --psm 6' 130 | return pytesseract.image_to_string(image, config=config) 131 | 132 | def parse_wait_time(self, text): 133 | match = re.search(r'(\d+) minute\(s\) (\d{1,2}) second\(s\)', text) 134 | if not match: 135 | match = re.search(r'(\d+) minute\(s\) (\d{1,2}) seconds', text) 136 | if match: 137 | minutes = int(match.group(1)) 138 | seconds = int(match.group(2)) 139 | return minutes * 60 + seconds + 2 140 | else: 141 | log_message(self.app, f"Failed to parse wait time from text: {text}") 142 | return 0 143 | 144 | def increment_mode_count(self, mode): 145 | if mode == "Views": 146 | self.app.views += 1000 147 | log_message(self.app, f"Views incremented by 1000") 148 | elif mode == "Hearts": 149 | increment = random.randint(11, 15) 150 | self.app.hearts += increment 151 | log_message(self.app, f"Hearts incremented by {increment}") 152 | elif mode == "Shares": 153 | increment = random.randint(70, 80) 154 | self.app.shares += increment 155 | log_message(self.app, f"Shares incremented by {increment}") 156 | elif mode == "Favorites": 157 | increment = random.randint(3, 6) 158 | self.app.favorites += increment 159 | log_message(self.app, f"Favorites incremented by {increment}") 160 | 161 | def loop(self, vidUrl, mode, amount): 162 | data = { 163 | "Followers": { 164 | "MainButton": '//button[@class="btn btn-primary rounded-0 t-followers-button"]', 165 | "Input": '/html/body/div[9]/div/form/div/input', 166 | "Send": '/html/body/div[9]/div/div/div[1]/div/form/button', 167 | "Search": '/html/body/div[9]/div/form/div/div/button', 168 | "TextBeforeSend": '/html/body/div[9]/div/div/span', 169 | "TextAfterSend": '/html/body/div[9]/div/div/span[1]' 170 | }, 171 | "Hearts": { 172 | "MainButton": '//button[@class="btn btn-primary rounded-0 t-hearts-button"]', 173 | "Input": '/html/body/div[8]/div/form/div/input', 174 | "Send": '/html/body/div[8]/div/div/div[1]/div/form/button', 175 | "Search": '/html/body/div[8]/div/form/div/div/button', 176 | "TextBeforeSend": '/html/body/div[8]/div/div/span', 177 | "TextAfterSend": '/html/body/div[8]/div/div/span[1]' 178 | }, 179 | "Views": { 180 | "MainButton": '//button[@class="btn btn-primary rounded-0 t-views-button"]', 181 | "Input": '/html/body/div[10]/div/form/div/input', 182 | "Send": '/html/body/div[10]/div/div/div[1]/div/form/button', 183 | "Search": '/html/body/div[10]/div/form/div/div/button', 184 | "TextBeforeSend": '/html/body/div[10]/div/div/span', 185 | "TextAfterSend": '/html/body/div[10]/div/div/span[1]' 186 | }, 187 | "Shares": { 188 | "MainButton": '//button[@class="btn btn-primary rounded-0 t-shares-button"]', 189 | "Input": '/html/body/div[11]/div/form/div/input', 190 | "Send": '/html/body/div[11]/div/div/div[1]/div/form/button', 191 | "Search": '/html/body/div[11]/div/form/div/div/button', 192 | "TextBeforeSend": '/html/body/div[11]/div/div/span', 193 | "TextAfterSend": '/html/body/div[11]/div/div/span[1]' 194 | }, 195 | "Favorites": { 196 | "MainButton": '//button[@class="btn btn-primary rounded-0 t-favorites-button"]', 197 | "Input": '/html/body/div[12]/div/form/div/input', 198 | "Send": '/html/body/div[12]/div/div/div[1]/div/form/button', 199 | "Search": '/html/body/div[12]/div/form/div/div/button', 200 | "TextBeforeSend": '/html/body/div[12]/div/div/span', 201 | "TextAfterSend": '/html/body/div[12]/div/div/span[1]' 202 | }, 203 | } 204 | 205 | while self.running: # Check the flag in the loop condition 206 | try: 207 | self.driver.refresh() 208 | time.sleep(2) 209 | self.driver.find_element(By.XPATH, data[mode]["MainButton"]).click() 210 | time.sleep(2) 211 | self.driver.find_element(By.XPATH, data[mode]["Input"]).send_keys(vidUrl) 212 | time.sleep(2) 213 | self.driver.find_element(By.XPATH, data[mode]["Search"]).click() 214 | time.sleep(6) 215 | 216 | # Check for delay after Search 217 | wait_text = self.driver.find_element(By.XPATH, data[mode]["TextBeforeSend"]).text 218 | if wait_text: 219 | wait_seconds = self.parse_wait_time(wait_text) 220 | if wait_seconds > 0: 221 | current_time = time.time() - self.app.start_time 222 | future_time = time.strftime('%H:%M:%S', time.gmtime(current_time + wait_seconds)) 223 | log_message(self.app, f"Wait {wait_seconds} seconds for your next submit (at {future_time} Elapsed Time)") 224 | time.sleep(wait_seconds) 225 | self.driver.refresh() 226 | continue # Skip the rest of the loop and start over 227 | 228 | self.driver.find_element(By.XPATH, data[mode]["Send"]).click() 229 | time.sleep(7) 230 | 231 | # Extract wait time after Send 232 | wait_text = self.driver.find_element(By.XPATH, data[mode]["TextAfterSend"]).text 233 | time.sleep(1) 234 | wait_seconds = self.parse_wait_time(wait_text) 235 | current_time = time.time() - self.app.start_time 236 | future_time = time.strftime('%H:%M:%S', time.gmtime(current_time + wait_seconds)) 237 | log_message(self.app, f"Wait {wait_seconds} seconds for your next submit (at {future_time} Elapsed Time)") 238 | 239 | # Increment counts based on mode 240 | self.increment_mode_count(mode) 241 | 242 | # Check if the amount limit is reached 243 | if (mode == "Views" and self.app.views >= amount) or \ 244 | (mode == "Hearts" and self.app.hearts >= amount) or \ 245 | (mode == "Followers" and self.app.followers >= amount) or \ 246 | (mode == "Shares" and self.app.shares >= amount) or \ 247 | (mode == "Favorites" and self.app.favorites >= amount): 248 | log_message(self.app, f"{mode} limit reached: {amount}") 249 | self.app.stop_bot() 250 | break 251 | 252 | time.sleep(wait_seconds) 253 | except Exception as e: 254 | log_message(self.app, f"Error in {mode} loop: {e}") 255 | self.driver.refresh() 256 | time.sleep(5) 257 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | customtkinter 2 | selenium 3 | Pillow 4 | pytesseract 5 | chromedriver-autoinstaller -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import os 3 | import sys 4 | 5 | def log_message(app, message): 6 | formatted_message = f"[{time.strftime('%H:%M:%S')}] {message}" 7 | app.log_text.insert("end", formatted_message + "\n") 8 | app.log_text.see("end") 9 | 10 | def resource_path(relative_path): 11 | if hasattr(sys, '_MEIPASS'): 12 | return os.path.join(sys._MEIPASS, relative_path) 13 | return os.path.join(os.path.abspath("."), relative_path) 14 | --------------------------------------------------------------------------------