Files
FunguyBot/plugins/proxy.py
T

148 lines
6.0 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.
"""
import os
import logging
import random
import aiohttp
import socket
import time
from datetime import datetime, timedelta
import concurrent.futures
import asyncio
import simplematrixbotlib as botlib
import sqlite3
from plugins.common import is_public_destination, html_escape
SOCKS5_LIST_URL = 'https://raw.githubusercontent.com/TheSpeedX/SOCKS-List/master/socks5.txt'
MAX_TRIES = 64
PROXY_LIST_FILENAME = 'socks5.txt'
PROXY_LIST_EXPIRATION = timedelta(hours=8)
MAX_THREADS = 64 # lowered to avoid resource exhaustion
PROXIES_DB_FILE = 'proxies.db'
MAX_PROXIES_IN_DB = 10
def test_proxy(proxy):
"""Test a SOCKS5 proxy and return (success, proxy, latency)."""
try:
ip, port = proxy.split(':')
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))
return True, proxy, latency
else:
return False, proxy, None
except Exception:
return False, proxy, None
async def download_proxy_list():
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")
async with aiohttp.ClientSession() as session:
async with session.get(SOCKS5_LIST_URL, timeout=20) as response:
response.raise_for_status()
text = await response.text()
with open(PROXY_LIST_FILENAME, 'w') as f:
f.write(text)
logging.info("Proxy list downloaded")
return True
else:
return True
except Exception as e:
logging.error(f"Error downloading proxy list: {e}")
return False
def check_db_for_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("SELECT proxy, latency FROM proxies WHERE status='working' AND latency<3000 ORDER BY RANDOM() LIMIT 1")
row = cursor.fetchone()
if row:
proxy, latency = row
success, _, _ = test_proxy(proxy)
if success:
return proxy, latency
else:
cursor.execute("DELETE FROM proxies WHERE proxy=?", (proxy,))
conn.commit()
return None, None
except Exception as e:
logging.error(f"DB error: {e}")
return None, None
def save_proxy_to_db(proxy, latency):
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: {e}")
async def handle_command(room, message, bot, prefix, config):
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")
working_proxy, latency = check_db_for_proxy()
if working_proxy:
safe_proxy = html_escape(working_proxy)
await bot.api.send_markdown_message(room.room_id,
f"✅ Using cached working SOCKS5 Proxy: **{safe_proxy}** - Latency: **{latency} ms**")
return
if not await download_proxy_list():
await bot.api.send_markdown_message(room.room_id, "Error downloading proxy list")
return
try:
with open(PROXY_LIST_FILENAME, 'r') as f:
socks5_proxies = [line.replace("socks5://", "") for line in f.read().splitlines()]
socks5_proxies = [p for p in socks5_proxies if is_public_destination(p.split(':')[0])]
random.shuffle(socks5_proxies)
loop = asyncio.get_running_loop()
tested = 0
with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
futures = [loop.run_in_executor(executor, test_proxy, proxy) for proxy in socks5_proxies[:MAX_TRIES]]
for future in asyncio.as_completed(futures):
success, proxy, latency = await future
if success:
safe_proxy = html_escape(proxy)
await bot.api.send_markdown_message(room.room_id,
f"✅ Anonymous SOCKS5 Proxy: **{safe_proxy}** - Latency: **{latency} ms**")
save_proxy_to_db(proxy, latency)
tested += 1
if tested >= MAX_PROXIES_IN_DB:
break
if tested == 0:
await bot.api.send_markdown_message(room.room_id, "❌ 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")
__version__ = "1.0.2"
__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.</p>
</details>
"""