Skip to content

Commit

Permalink
Add send_to_android option and fix location dialog closing
Browse files Browse the repository at this point in the history
  • Loading branch information
coskundeniz committed Oct 25, 2024
1 parent 6ae629f commit 722d0ef
Show file tree
Hide file tree
Showing 14 changed files with 881 additions and 485 deletions.
47 changes: 45 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This command-line tool clicks ads for a certain query on Google search using [un
* 2captcha integration
* Telegram notification
* Generate daily click report
* Open found links on Android device
* Hooks for extending the tool with custom behavior

<br>
Expand Down Expand Up @@ -119,7 +120,8 @@ The followings are the default values in the config file.
"running_interval_end": "00:00",
"2captcha_apikey": "",
"hooks_enabled": false,
"telegram_enabled": false
"telegram_enabled": false,
"send_to_android": false
}
}
```
Expand Down Expand Up @@ -217,6 +219,47 @@ The followings are the default values in the config file.

* There is a limit of 2048 characters. If the length of the message exceeds this, it will be truncated.

* **send_to_android**: Send links to open on connected Android mobile device. Used with `run_ad_clicker.py` and `run_in_loop.py`.

* Note that mobile device must be connected to the same wireless network or directly via USB cable for one of these usages.

* If you have less devices(n) than browsers you run, first n browsers will be assigned to n devices.

* ADB setup steps
1. Open your phone’s **Settings**.
2. Scroll down to **About phone**.
3. Find **Build number** (usually under Software Information).
4. Tap Build number 7 times. You should see a message like "You are now a developer!"
5. Go back to Settings, and you will now see a **Developer options** menu.
6. Open **Developer options** and enable **USB Debugging**.

7. Install ADB on your computer
* `sudo apt install adb`

* See [here](https://www.xda-developers.com/install-adb-windows-macos-linux/) or [here](https://www.howtogeek.com/125769/how-to-install-and-use-abd-the-android-debug-bridge-utility/) for Windows.

8. Verify installation
* `adb version`

9. Connect your phone to your computer via USB.
* When prompted on your device, select Allow USB Debugging.

10. Check if ADB recognizes your device by running `adb devices`.
* If you see your device’s ID, the connection is successful.

* You can also use ADB wirelessly without a USB cable.

1. Connect your phone to your computer via USB and enable USB Debugging as explained before.
2. Run the following command.
* `adb tcpip 5555`

3. Disconnect the USB cable and find the phone’s IP address (Settings > About phone > Status).
4. Connect ADB to your phone wirelessly.
* `adb connect <phone_ip>:5555`

* Shopping ads will still be opened on browser.

<video src="assets/send_to_android_recording.mp4"></video>

<br>

Expand Down Expand Up @@ -287,7 +330,7 @@ Apply the following steps for once to enable Telegram notifications.

[https://coskundeniz.github.io/ad_clicker](https://coskundeniz.github.io/ad_clicker)

If you benefit from this tool, please consider donating using the sponsor links([patreon](https://patreon.com/pythondoctor), [ko-fi](https://ko-fi.com/coskundeniz)) or the following crypto addresses.
If you benefit from this tool, please give a star and consider donating using the sponsor links([patreon](https://patreon.com/pythondoctor), [ko-fi](https://ko-fi.com/coskundeniz)) or the following crypto addresses.

* ETH: 0x461c1B3bd9c3E2d949C56670C088465Bf3457F4B
* USDT: 0x1a4f06937100Dc704031386667D731Bea0670aaf
7 changes: 6 additions & 1 deletion ad_clicker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
from proxy import get_proxies
from search_controller import SearchController
from utils import (
create_webdriver,
get_random_user_agent_string,
get_domains,
take_screenshot,
generate_click_report,
)
from webdriver import create_webdriver


if config.behavior.telegram_enabled:
from telegram_notifier import notify_matching_ads, start_bot
Expand Down Expand Up @@ -54,6 +55,7 @@ def get_arg_parser() -> ArgumentParser:
arg_parser.add_argument(
"--check_nowsecure", action="store_true", help="Check nowsecure.nl for undetection"
)
arg_parser.add_argument("-d", "--device_id", help="Android device ID for assigning to browser")

return arg_parser

Expand Down Expand Up @@ -162,6 +164,9 @@ def main():
if args.id:
search_controller.set_browser_id(args.id)

if args.device_id:
search_controller.assign_android_device(args.device_id)

ads, non_ad_links, shopping_ads = search_controller.search_for_ads(non_ad_domains=domains)

if config.behavior.hooks_enabled:
Expand Down
145 changes: 145 additions & 0 deletions adb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import subprocess

from logger import logger


class ADBController:

def __init__(self):

self.devices = []

def get_connected_devices(self) -> None:
"""Retrieve the list of connected devices"""

try:
command = ["adb", "devices"]
result = subprocess.run(command, capture_output=True, text=True)

if result.returncode != 0:
raise Exception(f"Failed to list devices: {result.stderr}")

# parse the output to get device ids
for line in result.stdout.splitlines()[1:]:
if line:
device_id = line.split()[0]
self.devices.append(device_id)

if not self.devices:
raise SystemExit("No device was found! Please connect at least 1 device.")

for device in self.devices:
logger.debug(f"Android device: {device}")

except Exception as exp:
raise Exception(f"An error occurred while running: '{' '.join(command)}'") from exp

@staticmethod
def open_url(url: str, device_id: str) -> None:
"""Open URL on the phone
:type url: str
:param url: URL to open on connected device
:type device_id: str
:param device_id: Connected device's ID
"""

try:
command = [
"adb",
"-s",
device_id,
"shell",
"am",
"start",
"-a",
"android.intent.action.VIEW",
"-d",
url,
"com.android.chrome",
]
result = subprocess.run(command, capture_output=True, text=True)

if result.returncode == 0:
logger.info(f"URL[{url}] was successfully opened on device[{device_id}]")
else:
logger.error(f"Error opening URL: {result.stderr}")

except Exception as exp:
raise Exception(f"An error occurred while running: '{' '.join(command)}'") from exp

@staticmethod
def send_keyevent(keycode: int) -> None:
"""Send the key event via adb shell
:type keycode: int
:param keycode: Integer value for the key to be send
"""

try:
command = ["adb", "shell", "input", "keyevent", str(keycode)]
result = subprocess.run(command, capture_output=True, text=True)

if result.returncode == 0:
logger.debug(f"Key event[{keycode}] was successfully sent.")
else:
logger.error(f"Couldn't send key event: {result.stderr}")

except Exception as exp:
raise Exception(f"An error occurred while running: '{' '.join(command)}'") from exp

@staticmethod
def send_swipe(x1: int, y1: int, x2: int, y2: int, duration: int) -> None:
"""Swipe the screen via adb shell
:type x1: int
:param x1: Starting x coordinate
:type y1: int
:param y1: Starting y coordinate
:type x2: int
:param x2: Ending x coordinate
:type y2: int
:param y2: Ending y coordinate
:type duration: int
:param duration: Duration of the swipe in milliseconds
"""

try:
command = [
"adb",
"shell",
"input",
"swipe",
str(x1),
str(y1),
str(x2),
str(y2),
str(duration),
]
result = subprocess.run(command, capture_output=True, text=True)

if result.returncode != 0:
logger.error(f"Error during swipe: {result.stderr}")

except Exception as exp:
raise Exception(f"An error occurred while running: '{' '.join(command)}'") from exp

@staticmethod
def close_browser() -> None:
"""Close the browser via adb shell"""

try:
BACK_KEYCODE = 4
command = ["adb", "shell", "input", "keyevent", str(BACK_KEYCODE)]
result = subprocess.run(command, capture_output=True, text=True)

if result.returncode == 0:
logger.debug(f"Back key event was successfully sent.")
else:
logger.error(f"Couldn't send key event: {result.stderr}")

except Exception as exp:
raise Exception(f"An error occurred while running: '{' '.join(command)}'") from exp


adb_controller = ADBController()
Binary file modified assets/ad_clicker_gui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/send_to_android_recording.mp4
Binary file not shown.
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"running_interval_end": "00:00",
"2captcha_apikey": "",
"hooks_enabled": false,
"telegram_enabled": false
"telegram_enabled": false,
"send_to_android": false
}
}
2 changes: 2 additions & 0 deletions config_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class BehaviorParams:
twocaptcha_apikey: Optional[str] = ""
hooks_enabled: Optional[bool] = False
telegram_enabled: Optional[bool] = False
send_to_android: Optional[bool] = False


class ConfigReader:
Expand Down Expand Up @@ -115,6 +116,7 @@ def read_parameters(self) -> None:
twocaptcha_apikey=config["behavior"]["2captcha_apikey"],
hooks_enabled=config["behavior"]["hooks_enabled"],
telegram_enabled=config["behavior"]["telegram_enabled"],
send_to_android=config["behavior"]["send_to_android"],
)


Expand Down
4 changes: 4 additions & 0 deletions gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@ def __init__(self, master) -> None:
self._telegram_enabled_value = self._add_checkbox(
row=9, column=4, label="Telegram enabled", enabled=config.behavior.telegram_enabled
)
self._send_to_android_value = self._add_checkbox(
row=9, column=5, label="Send to Android", enabled=config.behavior.send_to_android
)

def get_behavior_config(self) -> dict[str, str]:
"""Get behavior config values
Expand Down Expand Up @@ -368,6 +371,7 @@ def get_behavior_config(self) -> dict[str, str]:
"2captcha_apikey": self._2captcha_apikey_input.get("1.0", "end-1c"),
"hooks_enabled": self._hooks_enabled_value.get(),
"telegram_enabled": self._telegram_enabled_value.get(),
"send_to_android": self._send_to_android_value.get(),
}

def _add_input_field(
Expand Down
42 changes: 39 additions & 3 deletions run_ad_clicker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
from itertools import cycle
from pathlib import Path
from time import sleep
from typing import Optional

from adb import adb_controller
from logger import logger
from config_reader import config
from proxy import get_proxies
from utils import get_queries


def start_tool(browser_id: int, query: str, proxy: str, start_timeout: float) -> None:
def start_tool(
browser_id: int, query: str, proxy: str, start_timeout: float, device_id: Optional[str] = None
) -> None:
"""Start the tool
:type browser_id: int
Expand All @@ -23,13 +27,18 @@ def start_tool(browser_id: int, query: str, proxy: str, start_timeout: float) ->
:param proxy: Proxy to use in ip:port or user:pass@host:port format
:type start_timeout: float
:param start_timeout: Start timeout to avoid race condition in driver patching
:type device_id: str
:param device_id: Android device ID to assign
"""

sleep(start_timeout)

command = ["python", "ad_clicker.py"]
command.extend(["-q", query, "-p", proxy, "--id", str(browser_id)])

if device_id:
command.extend(["-d", device_id])

subprocess.run(command)


Expand Down Expand Up @@ -61,13 +70,28 @@ def main() -> None:
else:
raise SystemExit("Missing proxy_file parameter!")

if config.behavior.send_to_android:
adb_controller.get_connected_devices()
devices = adb_controller.devices
random.shuffle(devices)
device_ids = devices + [None] * (MAX_WORKERS - len(devices))
else:
device_ids = [None] * MAX_WORKERS

logger.info(f"Running with {MAX_WORKERS} browser{'s' if MAX_WORKERS > 1 else ''}...")

# 1st way - different query on each browser (default)
if config.behavior.multiprocess_style == 1:
with ProcessPoolExecutor(max_workers=MAX_WORKERS) as executor:
futures = [
executor.submit(start_tool, i, next(query), next(proxy), start_timeout=i * 0.5)
executor.submit(
start_tool,
i,
next(query),
next(proxy),
start_timeout=i * 0.5,
device_id=device_ids[i - 1],
)
for i in range(1, MAX_WORKERS + 1)
]

Expand All @@ -81,9 +105,21 @@ def main() -> None:
random.shuffle(proxies)
proxy = cycle(proxies)

if config.behavior.send_to_android:
devices = adb_controller.devices
random.shuffle(devices)
device_ids = devices + [None] * (MAX_WORKERS - len(devices))

with ProcessPoolExecutor(max_workers=MAX_WORKERS) as executor:
futures = [
executor.submit(start_tool, i, query, next(proxy), start_timeout=i * 0.5)
executor.submit(
start_tool,
i,
query,
next(proxy),
start_timeout=i * 0.5,
device_id=device_ids[i - 1],
)
for i in range(1, MAX_WORKERS + 1)
]

Expand Down
1 change: 1 addition & 0 deletions run_in_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def main() -> None:
main()
except Exception as exp:
logger.error("Exception occurred. See the details in the log file.")

message = str(exp).split("\n")[0]
logger.debug(f"Exception: {message}")
details = traceback.format_tb(exp.__traceback__)
Expand Down
Loading

0 comments on commit 722d0ef

Please sign in to comment.