Files
FunguyBot/plugins/proxy.py
T

227 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
This plugin provides a command to get random SOCKS5 proxies.
"""
# plugins/proxy.py
import os
import logging
import random
import requests
import socket
import time
from datetime import datetime, timedelta
import concurrent.futures
import simplematrixbotlib as botlib
import sqlite3
# Constants
SOCKS5_LIST_URL = 'https://raw.githubusercontent.com/TheSpeedX/SOCKS-List/master/socks5.txt'
# SOCKS5_LIST_URL = 'https://raw.githubusercontent.com/proxifly/free-proxy-list/main/proxies/protocols/socks5/data.txt'
MAX_TRIES = 64
PROXY_LIST_FILENAME = 'socks5.txt'
PROXY_LIST_EXPIRATION = timedelta(hours=8)
MAX_THREADS = 128
PROXIES_DB_FILE = 'proxies.db'
MAX_PROXIES_IN_DB = 10
# Setup verbose logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def test_proxy(proxy):
"""
Test a SOCKS5 proxy and return the outcome.
Args:
proxy (str): The SOCKS5 proxy address in the format 'ip:port'.
Returns:
tuple: (bool: success, str: proxy, int: latency)
"""
try:
ip, port = proxy.split(':')
logging.info(f"Testing SOCKS5 proxy: {ip}:{port}")
start_time = time.time()
with socket.create_connection((ip, int(port)), timeout=12) as client:
client.sendall(b'\x05\x01\x00')
response = client.recv(2)
if response == b'\x05\x00':
latency = int(round((time.time() - start_time) * 1000, 0))
return True, proxy, latency
else:
return False, proxy, None
except Exception as e:
return False, proxy, None
async def download_proxy_list():
"""
Download the SOCKS5 proxy list file if it doesn't already exist or if it's expired.
Returns:
bool: True if the proxy list is downloaded or up-to-date, False otherwise.
"""
try:
if not os.path.exists(PROXY_LIST_FILENAME) or \
datetime.now() - datetime.fromtimestamp(os.path.getctime(PROXY_LIST_FILENAME)) > PROXY_LIST_EXPIRATION:
logging.info("Downloading SOCKS5 proxy list")
response = requests.get(SOCKS5_LIST_URL, timeout=5)
with open(PROXY_LIST_FILENAME, 'w') as f:
f.write(response.text)
logging.info("Proxy list downloaded successfully")
return True
else:
logging.info("Proxy list already exists and is up-to-date")
return True
except Exception as e:
logging.error(f"Error downloading proxy list: {e}")
return False
def check_db_for_proxy():
"""
Check the proxies database for a working proxy.
If found, test the proxy and remove it from the database if it doesn't work.
Returns:
str or None: The working proxy if found, None otherwise.
"""
try:
with sqlite3.connect(PROXIES_DB_FILE) as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS proxies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
proxy TEXT,
latency INTEGER,
status TEXT
)
""")
cursor.execute("SELECT proxy, latency FROM proxies WHERE status = 'working' AND latency < 3000 ORDER BY RANDOM() LIMIT 1")
result = cursor.fetchone()
if result:
proxy, latency = result
success, _, _ = test_proxy(proxy)
if success:
return proxy, latency
else:
cursor.execute("DELETE FROM proxies WHERE proxy = ?", (proxy,))
conn.commit()
logging.info(f"Removed non-working proxy from the database: {proxy}")
return None, None
else:
return None, None
except Exception as e:
logging.error(f"Error checking proxies database: {e}")
return None, None
def save_proxy_to_db(proxy, latency):
"""
Save a working proxy to the proxies database.
Args:
proxy (str): The working proxy to be saved.
latency (int): Latency of the proxy.
"""
try:
with sqlite3.connect(PROXIES_DB_FILE) as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS proxies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
proxy TEXT,
latency INTEGER,
status TEXT
)
""")
cursor.execute("INSERT INTO proxies (proxy, latency, status) VALUES (?, ?, 'working')", (proxy, latency))
conn.commit()
except Exception as e:
logging.error(f"Error saving proxy to database: {e}")
async def handle_command(room, message, bot, prefix, config):
"""
Function to handle the !proxy command.
Args:
room (Room): The Matrix room where the command was invoked.
message (RoomMessage): The message object containing the command.
Returns:
None
"""
match = botlib.MessageMatch(room, message, bot, prefix)
if match.is_not_from_this_bot() and match.prefix() and match.command("proxy"):
logging.info("Received !proxy command")
# Check database for a working proxy
working_proxy, latency = check_db_for_proxy()
if working_proxy:
await bot.api.send_markdown_message(room.room_id,
f"✅ Using cached working SOCKS5 Proxy: **{working_proxy}** - Latency: **{latency} ms**")
logging.info(f"Using cached working SOCKS5 proxy {working_proxy}")
return
# Download proxy list if needed
else:
if not await download_proxy_list():
await bot.api.send_markdown_message(room.room_id, "Error downloading proxy list")
logging.error("Error downloading proxy list")
return
try:
# Read proxies from file
with open(PROXY_LIST_FILENAME, 'r') as f:
socks5_proxies = [line.replace("socks5://", "") for line in f.read().splitlines()]
random.shuffle(socks5_proxies)
# Test proxies concurrently
tested_proxies = 0
with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
futures = []
for proxy in socks5_proxies[:MAX_TRIES]:
futures.append(executor.submit(test_proxy, proxy))
for future in concurrent.futures.as_completed(futures):
success, proxy, latency = future.result()
if success:
await bot.api.send_markdown_message(room.room_id,
f"✅ Anonymous SOCKS5 Proxy: **{proxy}** - Latency: **{latency} ms**")
logging.info(f"Sent SOCKS5 proxy {proxy} to the room")
save_proxy_to_db(proxy, latency) # Save working proxy to the database
tested_proxies += 1
if tested_proxies >= MAX_PROXIES_IN_DB:
break # Stop testing proxies once MAX_PROXIES_IN_DB are saved to the database
# Check database for a working proxy after testing
working_proxy, latency = check_db_for_proxy()
if working_proxy:
await bot.api.send_markdown_message(room.room_id,
f"✅ Using cached working SOCKS5 Proxy: **{working_proxy}** - Latency: **{latency} ms**")
logging.info(f"Using cached working SOCKS5 proxy {working_proxy}")
else:
# If no working proxy found after testing
await bot.api.send_markdown_message(room.room_id, "❌ No working anonymous SOCKS5 proxy found")
logging.info("No working anonymous SOCKS5 proxy found")
except Exception as e:
logging.error(f"Error handling !proxy command: {e}")
await bot.api.send_markdown_message(room.room_id, "❌ Error handling !proxy command")
# ---------------------------------------------------------------------------
# Plugin Metadata
# ---------------------------------------------------------------------------
__version__ = "1.0.0"
__author__ = "Funguy Bot"
__description__ = "Working SOCKS5 proxy finder"
__help__ = """
<details>
<summary><strong>!proxy</strong> Random working SOCKS5 proxy</summary>
<p>Fetches, tests, and returns a random working SOCKS5 proxy with latency. Caches good proxies in SQLite.</p>
</details>
"""