2025-01-20 19:21:02 +00:00
|
|
|
import aiohttp
|
|
|
|
import random
|
|
|
|
import string
|
|
|
|
import time
|
|
|
|
import socket
|
|
|
|
import threading
|
|
|
|
import asyncio
|
|
|
|
from datetime import datetime
|
|
|
|
import json
|
|
|
|
import signal
|
|
|
|
import sys
|
|
|
|
|
|
|
|
# IRC connection settings
|
|
|
|
SERVER = "irc.supernets.org"
|
|
|
|
PORT = 6667
|
2025-01-22 01:42:52 +00:00
|
|
|
CHANNEL = "#"
|
|
|
|
OUTPUT_CHANNEL = "#"
|
|
|
|
OLD_CHANNEL = "#"
|
|
|
|
NICKNAME = ""
|
|
|
|
NICKSERV_PASSWORD = ""
|
2025-01-20 19:21:02 +00:00
|
|
|
|
|
|
|
# Counters and metrics
|
|
|
|
valid_invite_count = 0
|
|
|
|
scanned_invite_count = 0
|
|
|
|
error_count = 0
|
|
|
|
rate_limited_count = 0
|
|
|
|
scan_state_file = "scan_state.json"
|
|
|
|
user_agents = [
|
2025-01-22 01:42:52 +00:00
|
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
|
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36",
|
|
|
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"
|
2025-01-20 19:21:02 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
# Load scan state if exists
|
|
|
|
def load_scan_state():
|
|
|
|
global valid_invite_count, scanned_invite_count, error_count
|
|
|
|
try:
|
|
|
|
with open(scan_state_file, 'r') as f:
|
|
|
|
state = json.load(f)
|
|
|
|
valid_invite_count = state.get('valid_invite_count', 0)
|
|
|
|
scanned_invite_count = state.get('scanned_invite_count', 0)
|
|
|
|
error_count = state.get('error_count', 0)
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Save scan state to file
|
|
|
|
def save_scan_state():
|
|
|
|
state = {
|
|
|
|
'valid_invite_count': valid_invite_count,
|
|
|
|
'scanned_invite_count': scanned_invite_count,
|
|
|
|
'error_count': error_count
|
|
|
|
}
|
|
|
|
with open(scan_state_file, 'w', encoding='utf-8') as f:
|
|
|
|
json.dump(state, f, ensure_ascii=False)
|
|
|
|
|
|
|
|
# Generate a random user-agent
|
|
|
|
def get_random_user_agent():
|
|
|
|
return random.choice(user_agents)
|
|
|
|
|
|
|
|
# Generate a random Discord invite code
|
|
|
|
def generate_random_code():
|
|
|
|
length = random.randint(2, 10)
|
|
|
|
characters = string.ascii_letters + string.digits
|
|
|
|
return ''.join(random.choice(characters) for _ in range(length))
|
|
|
|
|
2025-01-22 01:42:52 +00:00
|
|
|
# Keep IRC connection alive by responding to PING messages
|
2025-01-20 19:21:02 +00:00
|
|
|
def keep_connection_alive(irc_socket):
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
data = irc_socket.recv(2048).decode("utf-8", errors="replace")
|
|
|
|
for line in data.split("\r\n"):
|
|
|
|
if line.startswith("PING"):
|
|
|
|
pong_response = line.split()[1]
|
|
|
|
irc_socket.sendall(f"PONG {pong_response}\r\n".encode("utf-8"))
|
|
|
|
print(f"Sent PONG {pong_response}")
|
|
|
|
except socket.error as e:
|
|
|
|
print(f"Connection error in keep_connection_alive: {e}")
|
|
|
|
|
2025-01-22 01:42:52 +00:00
|
|
|
# Handle initial IRC connection and commands
|
2025-01-20 19:21:02 +00:00
|
|
|
def handle_irc_connection(irc_socket):
|
|
|
|
irc_socket.sendall(f"NICK {NICKNAME}\r\n".encode("utf-8"))
|
|
|
|
irc_socket.sendall(f"USER {NICKNAME} 0 * :{NICKNAME}\r\n".encode("utf-8"))
|
|
|
|
threading.Thread(target=keep_connection_alive, args=(irc_socket,), daemon=True).start()
|
|
|
|
print("Waiting 5 seconds before identifying with NickServ...")
|
|
|
|
time.sleep(5)
|
|
|
|
irc_socket.sendall(f"PRIVMSG NickServ :IDENTIFY {NICKSERV_PASSWORD}\r\n".encode("utf-8"))
|
|
|
|
print("Identified with NickServ.")
|
2025-01-22 01:42:52 +00:00
|
|
|
print("Waiting 5 seconds before leaving old channel...")
|
2025-01-20 19:21:02 +00:00
|
|
|
time.sleep(5)
|
|
|
|
irc_socket.sendall(f"PART {OLD_CHANNEL}\r\n".encode("utf-8"))
|
|
|
|
print(f"Left channel {OLD_CHANNEL}")
|
|
|
|
irc_socket.sendall(f"JOIN {CHANNEL}\r\n".encode("utf-8"))
|
|
|
|
print(f"Joined channel {CHANNEL}")
|
|
|
|
irc_socket.sendall(f"PRIVMSG {CHANNEL} :Starting invite scanner...\r\n".encode("utf-8"))
|
2025-01-22 01:42:52 +00:00
|
|
|
irc_socket.sendall(f"JOIN {OUTPUT_CHANNEL}\r\n".encode("utf-8"))
|
|
|
|
print(f"Joined output channel {OUTPUT_CHANNEL}")
|
|
|
|
irc_socket.sendall(f"PRIVMSG {OUTPUT_CHANNEL} :Starting invite scanner...\r\n".encode("utf-8"))
|
|
|
|
|
|
|
|
async def check_invite_http(url, log_file, irc_socket):
|
|
|
|
global valid_invite_count, scanned_invite_count, rate_limited_count
|
|
|
|
scanned_invite_count += 1
|
|
|
|
headers = {'User-Agent': get_random_user_agent()}
|
|
|
|
api_url = url.replace("discord.com/invite", "discord.com/api/invites") + "?with_counts=true"
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
|
try:
|
|
|
|
async with session.get(api_url, headers=headers, timeout=10) as response:
|
|
|
|
if response.status == 429:
|
|
|
|
rate_limited_count += 1
|
|
|
|
print("Rate limit encountered. Slowing down.")
|
|
|
|
irc_socket.sendall(f"!!!RATE LIMIT HIT!!! - 5 MINUTE NAP.\r\n".encode("utf-8"))
|
|
|
|
await asyncio.sleep(300)
|
|
|
|
return
|
|
|
|
if response.status == 200:
|
|
|
|
data = await response.json()
|
|
|
|
guild_name = data.get("guild", {}).get("name", "Unknown Server")
|
|
|
|
guild_id = data.get("guild", {}).get("id", "Unknown")
|
|
|
|
description = data.get("guild", {}).get("description", "No description")
|
|
|
|
verification_level = data.get("guild", {}).get("verification_level", "Unknown")
|
|
|
|
nsfw_level = data.get("guild", {}).get("nsfw_level", "Unknown")
|
|
|
|
nsfw = data.get("guild", {}).get("nsfw", False)
|
|
|
|
premium_count = data.get("guild", {}).get("premium_subscription_count", 0)
|
|
|
|
presence_count = data.get("approximate_presence_count", 0)
|
|
|
|
member_count = data.get("approximate_member_count", 0)
|
|
|
|
|
|
|
|
log_entry = (f"Valid invite: {url}\n"
|
|
|
|
f"Guild Name: {guild_name}\n"
|
|
|
|
f"Guild ID: {guild_id}\n"
|
|
|
|
f"Description: {description}\n"
|
|
|
|
f"Verification Level: {verification_level}\n"
|
|
|
|
f"NSFW Level: {nsfw_level}\n"
|
|
|
|
f"NSFW: {nsfw}\n"
|
|
|
|
f"Premium Subscribers: {premium_count}\n"
|
|
|
|
f"Active Members: {presence_count}\n"
|
|
|
|
f"Total Members: {member_count}\n")
|
|
|
|
log_file.write(log_entry + "\n")
|
|
|
|
log_file.flush()
|
|
|
|
valid_invite_count += 1
|
|
|
|
|
|
|
|
irc_socket.sendall(f"PRIVMSG {OUTPUT_CHANNEL} :Valid invite found: {url} - Server name: {guild_name} - Description: {description} - Verification Level: {verification_level} - Members: {presence_count}/{member_count} - Premium subscribers: Premium Subscribers: {premium_count} - Verification Level: {verification_level} - NSFW: {nsfw} - NSFW Level: {nsfw_level}\r\n".encode("utf-8"))
|
|
|
|
else:
|
|
|
|
print(f"Invalid invite: {url}")
|
|
|
|
except Exception as e:
|
|
|
|
global error_count
|
|
|
|
error_count += 1
|
|
|
|
print(f"Error checking invite: {url} - {e}")
|
2025-01-20 19:21:02 +00:00
|
|
|
|
|
|
|
async def report_valid_invites(irc_socket):
|
|
|
|
global valid_invite_count, scanned_invite_count, error_count, rate_limited_count
|
|
|
|
while True:
|
|
|
|
await asyncio.sleep(600) # 10 minutes
|
|
|
|
irc_socket.sendall(f"PRIVMSG {CHANNEL} :Valid invites found: {valid_invite_count}, Links scanned: {scanned_invite_count}, Errors: {error_count}, Rate limits hit: {rate_limited_count}\r\n".encode("utf-8"))
|
2025-01-22 01:42:52 +00:00
|
|
|
irc_socket.sendall(f"PRIVMSG {OUTPUT_CHANNEL} :Valid invites found: {valid_invite_count}, Links scanned: {scanned_invite_count}, Errors: {error_count}, Rate limits hit: {rate_limited_count}\r\n".encode("utf-8"))
|
2025-01-20 19:21:02 +00:00
|
|
|
|
2025-01-22 01:42:52 +00:00
|
|
|
async def shutdown_handler(irc_socket):
|
2025-01-20 19:21:02 +00:00
|
|
|
print("Shutting down gracefully...")
|
|
|
|
irc_socket.sendall(f"PRIVMSG {CHANNEL} :Stopping invite scanner.\r\n".encode("utf-8"))
|
2025-01-22 01:42:52 +00:00
|
|
|
irc_socket.sendall(f"PRIVMSG {OUTPUT_CHANNEL} :Stopping invite scanner.\r\n".encode("utf-8"))
|
2025-01-20 19:21:02 +00:00
|
|
|
irc_socket.close()
|
|
|
|
save_scan_state()
|
|
|
|
sys.exit()
|
|
|
|
|
2025-01-22 01:42:52 +00:00
|
|
|
async def run_scanner(log_file, irc_socket):
|
2025-01-20 19:21:02 +00:00
|
|
|
try:
|
2025-01-22 01:42:52 +00:00
|
|
|
while True:
|
2025-01-20 19:21:02 +00:00
|
|
|
random_code = generate_random_code()
|
|
|
|
invite_url = f"https://discord.com/invite/{random_code}"
|
2025-01-22 01:42:52 +00:00
|
|
|
await check_invite_http(invite_url, log_file, irc_socket)
|
|
|
|
await asyncio.sleep(random.uniform(5, 10))
|
2025-01-20 19:21:02 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
|
|
|
|
|
|
|
async def main():
|
|
|
|
log_filename = "valid_invites.txt"
|
|
|
|
with open(log_filename, 'a', encoding='utf-8') as log_file:
|
|
|
|
irc_socket = socket.create_connection((SERVER, PORT))
|
|
|
|
handle_irc_connection(irc_socket)
|
|
|
|
|
2025-01-22 01:42:52 +00:00
|
|
|
signal.signal(signal.SIGINT, lambda *args: sys.exit())
|
|
|
|
signal.signal(signal.SIGTERM, lambda *args: sys.exit())
|
2025-01-20 19:21:02 +00:00
|
|
|
|
|
|
|
asyncio.create_task(report_valid_invites(irc_socket))
|
2025-01-22 01:42:52 +00:00
|
|
|
await run_scanner(log_file, irc_socket)
|
2025-01-20 19:21:02 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
load_scan_state()
|
|
|
|
try:
|
|
|
|
asyncio.run(main())
|
|
|
|
except (KeyboardInterrupt, SystemExit):
|
|
|
|
print("Program terminated.")
|