├── .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 |
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 |
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 |
--------------------------------------------------------------------------------