├── version.txt ├── run.bat ├── Install Requirements.bat ├── requirements.txt ├── README.md └── main.py /version.txt: -------------------------------------------------------------------------------- 1 | 1.2.0 -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | py main.py 2 | PAUSE 3 | -------------------------------------------------------------------------------- /Install Requirements.bat: -------------------------------------------------------------------------------- 1 | pip install -r requirements.txt 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | colorama==0.4.4 2 | PyAutoGUI==0.9.53 3 | keyboard==0.13.5 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snapchat Snapscore Booster PLUS v1.2 2 | 3 | Boost your Snapchat Snapscore with ease using this automation script. 4 | 5 | # FOR EVERYONE WHO DON'T KNOW HOW TO USE IT [JUST TEXT ME ON REDDIT](https://www.reddit.com/user/OMGVictor/), FOR A COFFEE AND A BAGEL I'LL DO IT FOR YOU :) 6 | 7 | [BuyMeACoffee](https://buymeacoffee.com/snapscore) 8 | --- 9 | ## ✨ Features 10 | 11 | * Automates sending snaps to a selected Snapchat shortcut 12 | * Adjustable delays between actions 13 | * Mouse-movement pause control 14 | * Resume, restart or quit from paused mode 15 | * Snap counter included 16 | * **Configurable Settings:** Use `config.ini` to set default values for shortcut users, delays, and target snaps. 17 | * **Detailed Logging:** All actions and important events are logged to `snapscore_booster.log` for easy troubleshooting. 18 | * **Countdown Timers:** Visual countdowns are displayed during delays for better user feedback. 19 | * **Target Snap Count:** Set a specific number of snaps to send, and the script will automatically stop once reached. 20 | 21 | --- 22 | ## Script controls 23 | - Press **CTRL+Q** → Pause (global hotkey, works anytime) 24 | - Press **F** while paused → Resume 25 | - Press **R** while paused → Restart calibration 26 | - Press **Q** while paused → Quit (works only when paused) 27 | - Press **CTRL+C** → Force quit (works anytime by closing console or sending interrupt signal) 28 | 29 | ## 🚀 Quick Start Options 30 | 31 | Choose the setup method that suits you best: 32 | 33 | --- 34 | 35 | ### 🌐 Option 1: Snapchat Web or Microsoft Store App (Simple & Accessible) 36 | 37 | 1. **Download this script** 38 | Clone the repo or [download it as ZIP](https://github.com/FlazeIGuess/SnapscoreBoosterPlus/releases) and extract it. 39 | 40 | 2. **Install Python 3.9** 41 | [Download for Windows](https://www.python.org/ftp/python/3.9.2/python-3.9.2-amd64.exe) 42 | During setup, make sure to check **"Add Python to PATH"**. 43 | 44 | 3. **Install Python dependencies** 45 | Double-click `Install Requirements.bat` or run `pip install -r requirements.txt` inside the script folder. 46 | 47 | 4. **Choose your Snapchat platform**: 48 | - **Snapchat Web**: Go to [web.snapchat.com](https://web.snapchat.com/), log in, and leave the window open. 49 | - **Microsoft Store App**: Install Snapchat from the [Microsoft Store](https://apps.microsoft.com/store/detail/snapchat/9NL4J1B0Q61N). 50 | 51 | 5. **Run the script** 52 | Launch `run.bat` or run `python main.py`. 53 | 54 | 6. **Calibrate mouse positions** 55 | Hover the cursor over the required buttons in the Snapchat interface (Camera, Snap, Send To, etc.) and press **F** to save each position. 56 | 57 | 7. **Follow the prompts** 58 | You will be asked if you want to load settings from `config.ini` or enter them manually. Then, enter the number of recipients, timing preferences, and target snaps. Begin boosting your Snapscore. 59 | > ⚠️ Calibration may need to be adjusted manually due to UI differences. 60 | Recommendation: 61 | - Because Snapchat web doesn't have an extra Chat site just use the first and second calibration on the shutter button, everything else should be the same as Android. 62 | 63 | 64 | These alternatives are easier to set up than emulators and work well for simple Snapscore automation. 65 | 66 | --- 67 | 68 | ### 📱 Option 2: Android Device via USB Debugging (Recommended) or Emulator (Not Recommended) 69 | 70 | 1. **Download this script** 71 | Clone the repository or [download it as ZIP](https://github.com/FlazeIGuess/Snapchat-Snapscore-Booster-PLUS/archive/refs/heads/main.zip) and extract it. 72 | 73 | 2. **Install Python 3.9** 74 | [Download here](https://www.python.org/ftp/python/3.9.2/python-3.9.2-amd64.exe) and check **"Add Python to PATH"** during setup. 75 | 76 | 3. **Install dependencies** 77 | Double-click `Install Requirements.bat` or run `pip install -r requirements.txt` in the script folder. 78 | 79 | 4. **Enable USB debugging on your Android phone** (if using a real device) 80 | - Go to *Settings → About phone → Tap Build number 7 times* 81 | - Then enable *Developer options → USB debugging* 82 | [Video guide](https://www.youtube.com/watch?v=G_Xw3336xLQ) 83 | 84 | 5. **Install ADB + scrcpy** 85 | - ADB: `choco install adb` (Windows) 86 | - scrcpy: `choco install scrcpy` (Windows), `brew install scrcpy` (macOS), or `sudo apt install scrcpy` (Linux) 87 | 88 | 6. **Connect your phone via USB** and allow debugging when prompted (if using a real device). 89 | 90 | 7. **Start scrcpy** to mirror your phone's screen (if using Android). 91 | 92 | 8. **Run the script** 93 | Double-click `run.bat` or run `python main.py` in the script directory. 94 | 95 | 9. **Follow the on-screen setup** 96 | - Choose your platform (Android for this option). 97 | - Stay on the *Chats* screen in Snapchat 98 | - Hover the mouse over each button (Camera [Android only], Snap, Send To, Shortcut, Select All, Send) and press **F** to record positions 99 | - Enter recipient count, click delay (ms), and rest delay (ms), and target snaps. 100 | 101 | --- 102 | ## 🖱️ Mouse Position Management 103 | 104 | To ensure the script interacts correctly with your Snapchat interface, you need to set up mouse positions. You have several options at the start of the script: 105 | 106 | * **0: Calibrate new positions:** Follow the on-screen prompts to move your mouse to each required button (Camera [Android only], Take Picture, Send To, Shortcut, Select All, Send Snap) and press **F** to record its coordinates. These positions will be automatically saved to `config.ini` for future use. 107 | * **1: Load saved positions from `config.ini`:** If you have previously calibrated and saved your positions, you can load them directly from `config.ini`. This skips the manual calibration step. 108 | * **2: Show markers for saved positions:** This option is helpful to verify if your saved positions are still accurate after resizing or moving your Snapchat window. The script will temporarily move your mouse cursor to each saved position, allowing you to visually confirm they align with the correct buttons. After showing the markers, you can choose to continue with the loaded positions or recalibrate if needed. 109 | * **3: Skip (continue without positions):** This will proceed without loading or calibrating positions. Use this only if you are confident your setup does not require precise mouse control or if you plan to manually handle mouse movements during the script's operation. 110 | 111 | --- 112 | 113 | ### ⚠️ Important Notice: Emulator Use is Unstable 114 | 115 | Using Snapchat inside Android emulators like BlueStacks, NoxPlayer, or LDPlayer is **highly unreliable**. Snapchat actively detects and restricts virtual devices. Expect: 116 | - Login errors 117 | - Black screens 118 | - App crashes 119 | 120 | For best results, use a real Android phone. 121 | 122 | --- 123 | 124 | ## �� Helpful Downloads 125 | 126 | | Purpose | Link | 127 | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | 128 | | Python 3.9.2 (Windows x64) | [Download](https://www.python.org/ftp/python/3.9.2/python-3.9.2-amd64.exe) | 129 | | This Script (ZIP) | [Download](https://github.com/FlazeIGuess/Snapchat-Snapscore-Booster-PLUS/archive/refs/heads/main.zip) | 130 | | ADB platform tools | [Google Platform Tools](https://developer.android.com/tools/releases/platform-tools) | 131 | | scrcpy | [GitHub - Genymobile/scrcpy](https://github.com/Genymobile/scrcpy) | 132 | | USB debugging tutorial | [YouTube Guide](https://www.youtube.com/watch?v=G_Xw3336xLQ) | 133 | 134 | --- 135 | 136 | ## ⚠️ Disclaimer 137 | 138 | This script is provided **for educational purposes only**. 139 | Use it at your own risk. The developer assumes **no responsibility** for bans, data loss, or other consequences. 140 | 141 | --- 142 | 143 | ## 👨‍💻 Development 144 | 145 | Created by **@FlazeIGuess** with support from **@useragents** on GitHub. 146 | Feel free to open issues or submit pull requests to improve the project. 147 | 148 | ## 🧪 Option 3: Android Emulator (Experimental / Not Recommended) 149 | 150 | 1. **Download this script** (see Option 2) 151 | 152 | 2. **Install Python 3.9** and dependencies 153 | Use `Install Requirements.bat` or run `pip install -r requirements.txt`. 154 | 155 | 3. **Install an Android emulator** 156 | e.g. [NoxPlayer](https://www.bignox.com/) or [LDPlayer](https://www.ldplayer.net/) 157 | 158 | 4. **Install and log in to Snapchat** inside the emulator 159 | 160 | 5. **Create a Snapchat Shortcut** with the recipients you want to automate 161 | 162 | 6. **Run the script** 163 | Launch `run.bat` or run `python main.py` 164 | 165 | 7. **Follow the same calibration process** as described in Option 2 166 | 167 | > ⚠️ Use at your own risk – emulator compatibility may break without notice. 168 | 169 | --- 170 | 171 | ## ⚙️ Configuration (`config.ini`) 172 | 173 | The script can load settings from `config.ini` to avoid manual input each time. A default `config.ini` is provided: 174 | 175 | ```ini 176 | [Settings] 177 | shortcut_users = 10 ; Default number of users in your Snapchat shortcut 178 | delay_ms = 1300 ; Default delay between clicks in milliseconds 179 | post_snap_delay_ms = 4000 ; Default delay after sending a batch of snaps in milliseconds (0 for no delay, or leave blank to use same as click delay) 180 | target_snaps = 0 ; Target number of snaps to send (0 for no limit) 181 | ``` 182 | 183 | **To use `config.ini`:** 184 | 1. Ensure `config.ini` is in the same directory as `main.py`. 185 | 2. When running the script, select option `1` to load settings from `config.ini`. 186 | 3. You can modify these values directly in the file. 187 | 188 | --- 189 | ## 📝 Logging 190 | 191 | The script generates a log file named `snapscore_booster.log` in the same directory. This file records: 192 | * Script start and end times 193 | * Important actions and events 194 | * Errors and warnings 195 | 196 | This log file is useful for debugging and tracking the script's activity over time. 197 | 198 | --- 199 | ## ✨ Enhanced User Experience 200 | 201 | * **Dynamic Snap Count:** The total number of snaps sent is now displayed dynamically in a visually appealing ASCII art format, updating on the same line without spamming the console. 202 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | try: 2 | from colorama import Fore 3 | import ctypes, pyautogui, keyboard, os, time, platform 4 | from datetime import datetime 5 | import sys 6 | import configparser 7 | import logging 8 | import tkinter as tk 9 | import threading 10 | except ImportError: 11 | input("Error while importing modules. Please install the modules in requirements.txt") 12 | 13 | 14 | ascii_text = """ 15 | 16 | _______ __ _ _______ _______ _______ ___ __ __ _______ 17 | | || | | || _ || | | || | | | | || | 18 | | _____|| |_| || |_| || _ | | _ || | | | | || _____| 19 | | |_____ | || || |_| | | |_| || | | |_| || |_____ 20 | |_____ || _ || || ___| | ___|| |___ | ||_____ | 21 | _____| || | | || _ || | | | | || | _____| | 22 | |_______||_| |__||__| |__||___| |___| |_______||_______||_______| v1.2 23 | 24 | by @FlazeIGuess with help of @useragents on Github 25 | """ 26 | 27 | 28 | active_marker_windows = [] 29 | 30 | class PositionMarker: 31 | def __init__(self, master, x, y, marker_id, name): 32 | self.master = master 33 | self.x = x 34 | self.y = y 35 | self.marker_id = marker_id 36 | self.name = name 37 | 38 | self.window = tk.Toplevel(master) 39 | self.window.attributes('-topmost', True) 40 | self.window.attributes("-transparentcolor", "red") 41 | self.window.overrideredirect(True) 42 | self.window.geometry(f'100x100+{x-50}+{y-50}') 43 | self.window.config(bg='red') 44 | self.window.lift() 45 | 46 | 47 | self.canvas = tk.Canvas(self.window, width=100, height=100, bg='red', highlightthickness=0) 48 | self.canvas.pack() 49 | 50 | 51 | 52 | self.canvas.create_line(10, 10, 90, 90, fill='white', width=2) 53 | self.canvas.create_line(10, 90, 90, 10, fill='white', width=2) 54 | 55 | active_marker_windows.append(self.window) 56 | 57 | def destroy(self): 58 | self.window.destroy() 59 | if self.window in active_marker_windows: 60 | active_marker_windows.remove(self.window) 61 | 62 | def onLinux(): 63 | if platform.system() == "Linux": 64 | return True 65 | else: 66 | return False 67 | 68 | class snapchat: 69 | 70 | ASCII_NUMBERS = { 71 | '0': [" _ ", "| |", "|_|"], 72 | '1': [" ", " |", " |"], 73 | '2': [" _ ", " _|", "|_ "], 74 | '3': [" _ ", " _|", " _|"], 75 | '4': [" ", "|_|", " |"], 76 | '5': [" _ ", "|_ ", " _|"], 77 | '6': [" _ ", "|_ ", "|_|"], 78 | '7': [" _ ", " |", " |"], 79 | '8': [" _ ", "|_|", "|_|"], 80 | '9': [" _ ", "|_|", " _|"] 81 | } 82 | 83 | @staticmethod 84 | def _get_ascii_number_lines(number): 85 | s_number = str(number) 86 | lines = ["", "", ""] 87 | for digit in s_number: 88 | if digit in snapchat.ASCII_NUMBERS: 89 | for i in range(3): 90 | lines[i] += snapchat.ASCII_NUMBERS[digit][i] + " " 91 | return lines 92 | 93 | def __init__(self): 94 | self.sent_snaps = 0 95 | self.delay = 1.3 96 | self.post_snap_delay = 4 97 | self.paused = False 98 | self.last_mouse_pos = pyautogui.position() 99 | self._should_quit = False 100 | self._should_reset = False 101 | self.platform_mode = None 102 | self._current_status_message = "" 103 | self.config = configparser.ConfigParser() 104 | 105 | 106 | self.switch_to_camera = pyautogui.Point(x=0, y=0) 107 | self.take_picture = pyautogui.Point(x=0, y=0) 108 | self.send_to = pyautogui.Point(x=0, y=0) 109 | self.shortcut = pyautogui.Point(x=0, y=0) 110 | self.select_all = pyautogui.Point(x=0, y=0) 111 | self.send_snap_button = pyautogui.Point(x=0, y=0) 112 | 113 | self.last_mouse_pos = pyautogui.position() 114 | 115 | 116 | self.marker_root = tk.Tk() 117 | self.marker_root.withdraw() 118 | self._tk_thread = threading.Thread(target=self.marker_root.mainloop, daemon=True) 119 | self._tk_thread.start() 120 | 121 | self._setup_logging() 122 | 123 | def _setup_logging(self): 124 | log_file = 'snapscore_booster.log' 125 | logging.basicConfig( 126 | level=logging.INFO, 127 | format='%(asctime)s - %(levelname)s - %(message)s', 128 | handlers=[ 129 | logging.FileHandler(log_file), 130 | ] 131 | ) 132 | self.logger = logging.getLogger(__name__) 133 | self.logger.info("Snapscore Booster PLUS started.") 134 | 135 | def _load_config(self): 136 | config_file = 'config.ini' 137 | try: 138 | 139 | 140 | self.delay = int(self.config.get('Settings', 'delay_ms')) / 1000.0 141 | self.post_snap_delay = int(self.config.get('Settings', 'post_snap_delay_ms')) / 1000.0 142 | self._config_shortcut_users = int(self.config.get('Settings', 'shortcut_users')) 143 | self._config_target_snaps = int(self.config.get('Settings', 'target_snaps')) 144 | self.print_console("Settings loaded from config.ini", status="Info") 145 | return True 146 | except (configparser.Error, ValueError) as e: 147 | self.print_console(f"Error reading config file: {e}. Please check config.ini format.", status="Error") 148 | return False 149 | 150 | def _save_config(self): 151 | config_file = 'config.ini' 152 | try: 153 | with open(config_file, 'w') as configfile: 154 | self.config.write(configfile) 155 | self.print_console("Settings saved to config.ini", status="Info") 156 | except IOError as e: 157 | self.print_console(f"Error saving config file: {e}", status="Error") 158 | 159 | def _load_positions(self): 160 | position_names = ["switch_to_camera", "take_picture", "send_to", "shortcut", "select_all", "send_snap_button"] 161 | loaded_successfully = True 162 | try: 163 | if not self.config.has_section('Positions'): 164 | self.print_console(f"DEBUG: Sections found by configparser: {self.config.sections()}", status="DEBUG") 165 | self.print_console("No [Positions] section found in config.ini. Please calibrate positions.", status="Warning") 166 | return False 167 | 168 | for name in position_names: 169 | x = int(self.config.get('Positions', f'{name}_x')) 170 | y = int(self.config.get('Positions', f'{name}_y')) 171 | setattr(self, name, pyautogui.Point(x=x, y=y)) 172 | self.print_console("Mouse positions loaded from config.ini", status="Info") 173 | return True 174 | except (configparser.Error, ValueError) as e: 175 | self.print_console(f"Error loading mouse positions: {e}. Please recalibrate.", status="Error") 176 | return False 177 | 178 | def _save_positions(self): 179 | if not self.config.has_section('Positions'): 180 | self.config.add_section('Positions') 181 | 182 | positions_to_save = { 183 | "switch_to_camera": self.switch_to_camera, 184 | "take_picture": self.take_picture, 185 | "send_to": self.send_to, 186 | "shortcut": self.shortcut, 187 | "select_all": self.select_all, 188 | "send_snap_button": self.send_snap_button, 189 | } 190 | 191 | for name, pos_obj in positions_to_save.items(): 192 | if pos_obj is not None: 193 | self.config.set('Positions', f'{name}_x', str(pos_obj.x)) 194 | self.config.set('Positions', f'{name}_y', str(pos_obj.y)) 195 | else: 196 | 197 | self.config.set('Positions', f'{name}_x', '0') 198 | self.config.set('Positions', f'{name}_y', '0') 199 | 200 | self._save_config() 201 | self.print_console("Mouse positions saved to config.ini", status="Info") 202 | 203 | def _destroy_all_markers(self): 204 | global active_marker_windows 205 | for window in list(active_marker_windows): 206 | try: 207 | window.destroy() 208 | except tk.TclError: 209 | pass 210 | active_marker_windows = [] 211 | self.print_console("All markers destroyed.", status="Info") 212 | 213 | def _show_position_markers(self): 214 | self.print_console("Displaying saved mouse position markers. Please observe your screen.", status="Info") 215 | 216 | 217 | 218 | 219 | if not hasattr(self, 'marker_root') or not self.marker_root.winfo_exists(): 220 | self.marker_root = tk.Tk() 221 | self.marker_root.withdraw() 222 | 223 | position_list = [ 224 | ("Camera Button", self.switch_to_camera), 225 | ("Take Picture Button", self.take_picture), 226 | ("Send To Button", self.send_to), 227 | ("Shortcut Button", self.shortcut), 228 | ("Select All in Shortcut", self.select_all), 229 | ("Send Snap Button", self.send_snap_button), 230 | ] 231 | 232 | marker_count = 0 233 | for name, pos in position_list: 234 | if pos is not None and (pos.x != 0 or pos.y != 0): 235 | marker_count += 1 236 | marker = PositionMarker(self.marker_root, pos.x, pos.y, marker_count, name) 237 | elif name == "Camera Button" and self.platform_mode == "web": 238 | self.print_console(f"Skipping marker for '{name}' (not needed for Web platform).", status="Marker") 239 | else: 240 | self.print_console(f"Position '{name}' not set (0,0). Skipping marker.", status="Warning") 241 | 242 | if marker_count > 0: 243 | self.print_console("Markers are displayed. Press F to destroy markers and continue.", status="Marker") 244 | 245 | 246 | while not keyboard.is_pressed("f"): 247 | self.marker_root.update_idletasks() 248 | self.marker_root.update() 249 | time.sleep(0.05) 250 | self._destroy_all_markers() 251 | 252 | while keyboard.is_pressed("f"): 253 | time.sleep(0.05) 254 | else: 255 | self.print_console("No valid positions found to display markers.", status="Warning") 256 | 257 | def get_positions(self): 258 | self.print_console("Make sure your snapchat is open and you are on the chats page.", status="Setup") 259 | if self.platform_mode == "android": 260 | self.print_console("Move your mouse to the camera button, then press F", status="Setup") 261 | while True: 262 | if keyboard.is_pressed("f"): 263 | self.switch_to_camera = pyautogui.position() 264 | break 265 | time.sleep(0.5) 266 | else: 267 | self.switch_to_camera = None 268 | self.print_console("Move your mouse to the take picture button, then press F", status="Setup") 269 | while True: 270 | if keyboard.is_pressed("f"): 271 | self.take_picture = pyautogui.position() 272 | break 273 | time.sleep(0.5) 274 | 275 | self.print_console("Move your mouse to the Send To button, then press F", status="Setup") 276 | while True: 277 | if keyboard.is_pressed("f"): 278 | self.send_to = pyautogui.position() 279 | break 280 | time.sleep(0.5) 281 | self.print_console("Move your mouse to your shortcut, then press F", status="Setup") 282 | while True: 283 | if keyboard.is_pressed("f"): 284 | self.shortcut = pyautogui.position() 285 | break 286 | time.sleep(0.5) 287 | self.print_console("Move your mouse to select all in shortcut, then press F", status="Setup") 288 | while True: 289 | if keyboard.is_pressed("f"): 290 | self.select_all = pyautogui.position() 291 | break 292 | time.sleep(0.5) 293 | self.print_console("Move your mouse to send snap button, then press F", status="Setup") 294 | while True: 295 | if keyboard.is_pressed("f"): 296 | self.send_snap_button = pyautogui.position() 297 | break 298 | 299 | self._save_positions() 300 | self.print_console("All positions calibrated and saved.", status="Setup") 301 | 302 | def send_snap(self, shortcut_users): 303 | self.sent_snaps += shortcut_users 304 | 305 | self.update_title(shortcut_users) 306 | self._refresh_console_display() 307 | 308 | 309 | if self.platform_mode == "android": 310 | pyautogui.moveTo(self.switch_to_camera) 311 | if not self.interruptible_sleep(self.delay): return False 312 | pyautogui.click() 313 | 314 | pyautogui.moveTo(self.take_picture) 315 | if not self.interruptible_sleep(self.delay): return False 316 | pyautogui.click() 317 | 318 | pyautogui.moveTo(self.send_to) 319 | if not self.interruptible_sleep(self.delay): return False 320 | pyautogui.click() 321 | 322 | pyautogui.moveTo(self.shortcut) 323 | if not self.interruptible_sleep(self.delay): return False 324 | pyautogui.click() 325 | 326 | pyautogui.moveTo(self.select_all) 327 | if not self.interruptible_sleep(self.delay): return False 328 | pyautogui.click() 329 | 330 | pyautogui.moveTo(self.send_snap_button) 331 | if not self.interruptible_sleep(self.delay): return False 332 | pyautogui.click() 333 | 334 | return True 335 | 336 | def update_title(self, shortcut_users): 337 | now = time.time() 338 | elapsed = str(now - self.started_time).split(".")[0] 339 | sent_snaps = self.sent_snaps 340 | if onLinux() == False: 341 | ctypes.windll.kernel32.SetConsoleTitleW(f"Snapchat Score PLUS v1.2| Sent Snaps: {sent_snaps} | Elapsed: {elapsed}s | Developed by @FlazeIGuess with help of @useragents on Github") 342 | 343 | def print_console(self, arg, status = "Console"): 344 | log_message = f"[{status}] {arg}" 345 | if status == "Error": 346 | self.logger.error(log_message) 347 | elif status == "Warning": 348 | self.logger.warning(log_message) 349 | elif status == "Info" or status == "Console" or status == "Setup" or status == "Settings" or status == "Delay Selection" or status == "Post-Snap Delay Selection" or status == "Platform Selection" or status == "Status": 350 | self.logger.info(log_message) 351 | 352 | 353 | 354 | 355 | 356 | 357 | if status != "Progress": 358 | lines = arg.split('\n') 359 | 360 | indentation_len = 7 + len(f"[{status}] ") 361 | 362 | print(f"\n {Fore.WHITE}[{Fore.RED}{status}{Fore.WHITE}] {lines[0]}") 363 | 364 | for i in range(1, len(lines)): 365 | 366 | print(f"{ ' ' * indentation_len}{lines[i]}") 367 | 368 | def _refresh_console_display(self): 369 | if onLinux() == False: 370 | os.system("cls") 371 | else: 372 | os.system("clear") 373 | 374 | print(Fore.RED + ascii_text) 375 | 376 | 377 | print(f"\n {Fore.WHITE}[{Fore.RED}Info{Fore.WHITE}] You can pause the script at any time by pressing CTRL+Q (even if this window is not focused).") 378 | 379 | 380 | ascii_count_lines = snapchat._get_ascii_number_lines(self.sent_snaps) 381 | print(f"\n {Fore.WHITE}[{Fore.RED}Progress{Fore.WHITE}] Sent Snaps:") 382 | for line in ascii_count_lines: 383 | print(f" {Fore.WHITE} {line}") 384 | 385 | 386 | if self._current_status_message: 387 | self.print_console(self._current_status_message, status="Status") 388 | 389 | sys.stdout.flush() 390 | 391 | def main(self): 392 | if onLinux() == False: 393 | os.system("cls") 394 | ctypes.windll.kernel32.SetConsoleTitleW("Snapchat Score PLUS v1.2| Developed by @FlazeIGuess with help of @useragents on Github") 395 | else: 396 | os.system("clear") 397 | 398 | print(Fore.RED + ascii_text) 399 | 400 | 401 | config_file = 'config.ini' 402 | if os.path.exists(config_file): 403 | self.config.read(config_file) 404 | self.print_console(f"Successfully read config file '{config_file}'.", status="Info") 405 | else: 406 | self.print_console(f"Config file '{config_file}' not found. Will use default/manual settings.", status="Warning") 407 | 408 | while True: 409 | try: 410 | self.print_console("Select your Snapchat platform:", status="Platform Selection") 411 | self.print_console("0: Snapchat Web/Microsoft Store App", status="Platform Selection") 412 | self.print_console("1: Android Device via USB Debugging (scrcpy) |Recommended| or Emulator |Not Recommended|", status="Platform Selection") 413 | platform_choice = int(input(f"\n {Fore.WHITE}[{Fore.RED}Platform Selection{Fore.WHITE}] Enter choice (0 or 1): ")) 414 | if platform_choice not in [0, 1]: 415 | raise ValueError 416 | self.platform_mode = "web" if platform_choice == 0 else "android" 417 | break 418 | except ValueError: 419 | self.print_console("Invalid input. Please enter 0 for Web/Microsoft Store App or 1 for Android.", status="Platform Selection") 420 | 421 | if onLinux() == False: 422 | os.system("cls") 423 | else: 424 | os.system("clear") 425 | print(Fore.RED + ascii_text) 426 | 427 | 428 | if self._has_saved_positions(): 429 | while True: 430 | try: 431 | self.print_console("Mouse Positions Setup:", status="Setup") 432 | self.print_console("0: Calibrate new positions", status="Setup") 433 | self.print_console("1: Load saved positions from config.ini", status="Setup") 434 | self.print_console("2: Show markers for saved positions", status="Setup") 435 | position_choice = int(input(f"\n {Fore.WHITE}[{Fore.RED}Setup{Fore.WHITE}] Enter choice (0, 1 or 2): ")) 436 | if position_choice not in [0, 1, 2]: 437 | raise ValueError 438 | break 439 | except ValueError: 440 | self.print_console("Invalid input. Please enter 0, 1 or 2.", status="Setup") 441 | else: 442 | self.print_console("No saved positions found. You must calibrate new positions.", status="Setup") 443 | self.print_console("0: Calibrate new positions", status="Setup") 444 | position_choice = 0 445 | 446 | if position_choice == 0: 447 | self.get_positions() 448 | elif position_choice == 1: 449 | if not self._load_positions(): 450 | self.print_console("Failed to load positions. Please calibrate new positions.", status="Warning") 451 | self.get_positions() 452 | elif position_choice == 2: 453 | if self._load_positions(): 454 | self._show_position_markers() 455 | time.sleep(0.5) 456 | 457 | while True: 458 | self.print_console("Markers shown. Do you want to: ", status="Setup") 459 | self.print_console("0: Continue with loaded positions", status="Setup") 460 | self.print_console("1: Recalibrate positions", status="Setup") 461 | marker_action_choice = int(input(f"\n {Fore.WHITE}[{Fore.RED}Setup{Fore.WHITE}] Enter choice (0 or 1): ")) 462 | if marker_action_choice == 0: 463 | break 464 | elif marker_action_choice == 1: 465 | self.get_positions() 466 | break 467 | else: 468 | self.print_console("Invalid input. Please enter 0 or 1.", status="Error") 469 | else: 470 | self.print_console("No positions loaded to show markers. Please calibrate or load first.", status="Warning") 471 | self.get_positions() 472 | 473 | if onLinux() == False: 474 | os.system("cls") 475 | else: 476 | os.system("clear") 477 | print(Fore.RED + ascii_text) 478 | 479 | 480 | while True: 481 | try: 482 | self.print_console("Do you want to load settings from config.ini?", status="Settings") 483 | self.print_console("0: No, enter manually", status="Settings") 484 | self.print_console("1: Yes, use config.ini", status="Settings") 485 | self.print_console("2: Exit", status="Settings") 486 | config_choice = int(input(f"\n {Fore.WHITE}[{Fore.RED}Settings{Fore.WHITE}] Enter choice (0, 1 or 2): ")) 487 | if config_choice not in [0, 1, 2]: 488 | raise ValueError 489 | break 490 | except ValueError: 491 | self.print_console("Invalid input. Please enter 0, 1 or 2.", status="Settings") 492 | 493 | if config_choice == 2: 494 | self.print_console("Exiting script.", status="Console") 495 | self._should_quit = True 496 | return 497 | 498 | use_config = (config_choice == 1) 499 | config_loaded_successfully = False 500 | 501 | 502 | if not self.config.has_section('Settings'): 503 | self.config.add_section('Settings') 504 | 505 | if use_config: 506 | config_loaded_successfully = self._load_config() 507 | if not config_loaded_successfully: 508 | self.print_console("Falling back to manual input due to config file error.", status="Warning") 509 | use_config = False 510 | 511 | if not use_config: 512 | while True: 513 | try: 514 | shortcut_users_input = input(f"\n {Fore.WHITE}[{Fore.RED}Console{Fore.WHITE}] How many people are in this shortcut ({self._config_shortcut_users if use_config and config_loaded_successfully else 'e.g. 100'}): ") 515 | if shortcut_users_input.strip() == "" and use_config and config_loaded_successfully: 516 | shortcut_users = self._config_shortcut_users 517 | else: 518 | shortcut_users = int(shortcut_users_input) 519 | break 520 | except ValueError: 521 | print(f"\n {Fore.WHITE}[{Fore.RED}Console{Fore.WHITE}] There was an error with that input, please try again :) ") 522 | 523 | 524 | self.config.set('Settings', 'shortcut_users', str(shortcut_users)) 525 | 526 | if onLinux() == False: 527 | os.system("cls") 528 | else: 529 | os.system("clear") 530 | print(Fore.RED + ascii_text) 531 | 532 | while True: 533 | try: 534 | self.print_console("This delay sets the time between each click action.", status="Delay Explanation") 535 | print(f"\n") 536 | self.print_console("Fast PC/Smartphone -> 400 / 500", status="Delay Selection") 537 | self.print_console("Mid PC/Smartphone -> 800 / 1000", status="Delay Selection") 538 | self.print_console("Slow PC/Smartphone -> 1300 / 1500", status="Delay Selection") 539 | print(f"\n") 540 | self.print_console("This delay sets the time between each click action.", status="Delay Explanation") 541 | self.print_console(f"Enter delay in milliseconds (e.g., 1300 for 1.3 seconds) ({int(self.delay * 1000) if use_config and config_loaded_successfully else '1300'}):", status="Delay Selection") 542 | delay_input = input(f"\n {Fore.WHITE}[{Fore.RED}Delay Selection{Fore.WHITE}] Input: ") 543 | if delay_input.strip() == "" and use_config and config_loaded_successfully: 544 | delay_ms = int(self.delay * 1000) 545 | else: 546 | delay_ms = int(delay_input) 547 | if delay_ms < 400: 548 | self.print_console("Delays under 400ms may not be accurately recognized by the system.", status="Warning") 549 | self.delay = delay_ms / 1000.0 550 | break 551 | except ValueError: 552 | print(f"\n {Fore.WHITE}[{Fore.RED}Delay Selection{Fore.WHITE}] Invalid input. Please enter a valid number for delay.") 553 | 554 | 555 | self.config.set('Settings', 'delay_ms', str(int(self.delay * 1000))) 556 | 557 | if onLinux() == False: 558 | os.system("cls") 559 | else: 560 | os.system("clear") 561 | print(Fore.RED + ascii_text) 562 | while True: 563 | try: 564 | 565 | self.print_console("This delay sets the time to wait after sending a batch of snaps.", status="Post-Snap Delay Explanation") 566 | 567 | print(f"\n") 568 | self.print_console(f"Enter delay after sending snaps in milliseconds (e.g., 4000 for 4 seconds) ({int(self.post_snap_delay * 1000) if use_config and config_loaded_successfully else '4000'}) or leave blank:", status="Post-Snap Delay Selection") 569 | self.print_console("Leave blank to use the same delay as between clicks.", status="Post-Snap Delay Info") 570 | post_snap_delay_input = input(f"\n {Fore.WHITE}[{Fore.RED}Post-Snap Delay Selection{Fore.WHITE}] Input: ") 571 | if post_snap_delay_input.strip() == "" and use_config and config_loaded_successfully: 572 | self.post_snap_delay = self.post_snap_delay 573 | elif post_snap_delay_input.strip() == "": 574 | self.post_snap_delay = self.delay 575 | else: 576 | post_snap_delay_ms = int(post_snap_delay_input) 577 | if post_snap_delay_ms < 0: 578 | raise ValueError 579 | self.post_snap_delay = post_snap_delay_ms / 1000.0 580 | break 581 | except ValueError: 582 | print(f"\n {Fore.WHITE}[{Fore.RED}Post-Snap Delay Selection{Fore.WHITE}] Invalid input. Please enter a non-negative number or leave blank.") 583 | 584 | 585 | self.config.set('Settings', 'post_snap_delay_ms', str(int(self.post_snap_delay * 1000))) 586 | 587 | if onLinux() == False: 588 | os.system("cls") 589 | else: 590 | os.system("clear") 591 | print(Fore.RED + ascii_text) 592 | while True: 593 | try: 594 | self.print_console(f"Enter target number of snaps to send (0 for no limit) ({self._config_target_snaps if use_config and config_loaded_successfully else '0'}):", status="Target Snaps") 595 | target_snaps_input = input(f"\n {Fore.WHITE}[{Fore.RED}Target Snaps{Fore.WHITE}] Input: ") 596 | if target_snaps_input.strip() == "": 597 | self.target_snaps = 0 598 | else: 599 | self.target_snaps = int(target_snaps_input) 600 | if self.target_snaps < 0: 601 | raise ValueError 602 | break 603 | except ValueError: 604 | print(f"\n {Fore.WHITE}[{Fore.RED}Target Snaps{Fore.WHITE}] Invalid input. Please enter a non-negative number.") 605 | 606 | 607 | self.config.set('Settings', 'target_snaps', str(self.target_snaps)) 608 | 609 | self._save_config() 610 | 611 | else: 612 | shortcut_users = self._config_shortcut_users 613 | self.target_snaps = self._config_target_snaps 614 | 615 | self.print_console("Please navigate to your chats and ensure Snapchat is open. Press F when you're ready.", status="Console") 616 | 617 | while True: 618 | if keyboard.is_pressed("f"): 619 | break 620 | time.sleep(0.5) 621 | if onLinux() == False: 622 | os.system("cls") 623 | else: 624 | os.system("clear") 625 | print(Fore.RED + ascii_text) 626 | self.print_console("Sending snaps...") 627 | self.started_time = time.time() 628 | self.last_mouse_pos = pyautogui.position() 629 | self._current_status_message = "Sending snaps..." 630 | self._refresh_console_display() 631 | while True: 632 | if self._should_quit or self._should_reset: 633 | break 634 | 635 | current_mouse_pos = pyautogui.position() 636 | if self.last_mouse_pos is not None: 637 | 638 | move_threshold = 5 639 | if not self.paused and (abs(current_mouse_pos.x - self.last_mouse_pos.x) > move_threshold or abs(current_mouse_pos.y - self.last_mouse_pos.y) > move_threshold): 640 | self.paused = True 641 | self._current_status_message = "Mouse moved. Script paused. Press F to resume, R to reset, or Q to quit." 642 | self._refresh_console_display() 643 | 644 | while self.paused: 645 | time.sleep(0.1) 646 | self.last_mouse_pos = current_mouse_pos 647 | 648 | 649 | if not self.paused: 650 | 651 | 652 | if not self.send_snap(shortcut_users): 653 | continue 654 | 655 | if self.target_snaps > 0 and self.sent_snaps >= self.target_snaps: 656 | self.print_console(f"Target of {self.target_snaps} snaps reached. Script stopping.", status="Info") 657 | self._should_quit = True 658 | break 659 | 660 | if not self.interruptible_sleep(self.post_snap_delay): 661 | continue 662 | self.last_mouse_pos = pyautogui.position() 663 | else: 664 | 665 | time.sleep(0.1) 666 | 667 | 668 | self.print_console(f"Finished sending {self.sent_snaps} snaps.") 669 | 670 | def interruptible_sleep(self, duration): 671 | start_time = time.time() 672 | while time.time() - start_time < duration: 673 | 674 | if self._should_quit or self._should_reset or self.paused: 675 | return False 676 | time.sleep(0.05) 677 | 678 | return True 679 | 680 | def global_pause(self): 681 | if not self.paused: 682 | self.paused = True 683 | self._current_status_message = "Global pause hotkey (CTRL+Q) detected. Script paused. Press F to resume, R to reset, or Q to quit." 684 | self._refresh_console_display() 685 | 686 | def resume_script(self): 687 | if self.paused: 688 | time.sleep(0.1) 689 | self.paused = False 690 | self._current_status_message = "Sending snaps..." 691 | self._refresh_console_display() 692 | self.last_mouse_pos = pyautogui.position() 693 | 694 | while keyboard.is_pressed("f"): 695 | time.sleep(0.05) 696 | 697 | def reset_script(self): 698 | if self.paused: 699 | time.sleep(0.1) 700 | self.paused = False 701 | self._should_reset = True 702 | self._current_status_message = "Resetting script..." 703 | self._refresh_console_display() 704 | 705 | def quit_script(self): 706 | if self.paused: 707 | time.sleep(0.1) 708 | self.paused = False 709 | self._should_quit = True 710 | self._current_status_message = "Quitting script..." 711 | self._refresh_console_display() 712 | self._destroy_all_markers() 713 | 714 | def _has_saved_positions(self): 715 | if not self.config.has_section('Positions'): 716 | return False 717 | 718 | position_names = ["switch_to_camera", "take_picture", "send_to", "shortcut", "select_all", "send_snap_button"] 719 | for name in position_names: 720 | try: 721 | x = int(self.config.get('Positions', f'{name}_x')) 722 | y = int(self.config.get('Positions', f'{name}_y')) 723 | if x != 0 or y != 0: 724 | return True 725 | except (configparser.NoOptionError, ValueError): 726 | continue 727 | return False 728 | 729 | obj = snapchat() 730 | 731 | 732 | keyboard.add_hotkey('ctrl+q', obj.global_pause) 733 | keyboard.add_hotkey('f', obj.resume_script) 734 | keyboard.add_hotkey('r', obj.reset_script) 735 | keyboard.add_hotkey('q', obj.quit_script) 736 | 737 | while True: 738 | obj.main() 739 | if not obj._should_reset: 740 | break 741 | 742 | 743 | if obj._should_quit: 744 | obj._destroy_all_markers() 745 | obj.marker_root.quit() 746 | sys.exit() 747 | --------------------------------------------------------------------------------