Initial commit v2 (token free)

This commit is contained in:
Xargana 2025-05-07 17:44:50 +03:00
parent 93235081ea
commit 0cd926b9a7
19 changed files with 1458 additions and 0 deletions

1
DOTENV Normal file
View file

@ -0,0 +1 @@
TOKEN=discord user token

402
app.py Normal file
View file

@ -0,0 +1,402 @@
import random
import discord
import asyncio
import traceback
import random
import datetime
import os
import importlib.util
import ast
import websockets
import json
import difflib
import io
import gzip
import re
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
time_regex = re.compile(r'(\d+)([smhd])') # Matches 4m2s, 1h30m, etc.
def parse_time(time_str):
units = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
total_seconds = sum(int(amount) * units[unit] for amount, unit in time_regex.findall(time_str))
return total_seconds if total_seconds > 0 else None
COMMANDS_DIR = "commands"
os.makedirs(COMMANDS_DIR, exist_ok=True)
def load_commands():
commands = {}
for filename in os.listdir(COMMANDS_DIR):
if filename.endswith(".py"):
cmd_name = filename[:-3]
cmd_path = os.path.join(COMMANDS_DIR, filename)
spec = importlib.util.spec_from_file_location(cmd_name, cmd_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
if hasattr(module, "run") and callable(module.run):
commands[cmd_name] = module.run
return commands
# Add a tracking file to save channel IDs
TRACKED_CHANNELS_FILE = "tracked_channels.json"
def load_tracked_channels():
if os.path.exists(TRACKED_CHANNELS_FILE):
with open(TRACKED_CHANNELS_FILE, 'r') as f:
return json.load(f)
return []
def save_tracked_channels(tracked_channels):
with open(TRACKED_CHANNELS_FILE, 'w') as f:
json.dump(tracked_channels, f)
async def handle_blacklist(message):
if message.author in [696800726084747314]:
await message.reply("no")
class Selfbot(discord.Client):
def __init__(self):
super().__init__()
self.default_status = None
self.loaded_commands = load_commands()
self.tracked_channels = load_tracked_channels()
self.last_status = {}
self.AFK_STATUS = False # Static variable to track AFK status
self.AFK_NOTIFIED_USERS = [] # Static list to store users notified of AFK status
self.horsin = []
async def on_ready(self):
print(f"Logged in as {self.user}")
async def handle_afk_dm(self,message):
if self.AFK_STATUS and message.author.id not in self.AFK_NOTIFIED_USERS:
await message.reply(
"Heya, I'm not at my computer right now, if you're requesting something please follow <https://nohello.club>. I'll let you know when I'm back :) \n\n-# This action was automated."
)
self.AFK_NOTIFIED_USERS.append(message.author.id)
async def on_message(self, message):
if message.author.id == 1169111190824308768 and "<@1236667927944761396>" in message.content:
await message.reply("shut the fuck up")
if message.content.startswith(".remindme "):
await handle_blacklist(message)
parts = message.content.split(" ", 2)
if len(parts) < 3:
await message.reply("Usage: `.remindme <time> <message>`", silent=True)
else:
duration = parse_time(parts[1])
if duration is None:
await message.reply("Invalid time format. Example: `4m2s`, `1h30m`.", silent=True)
else:
reminder_text = parts[2]
await message.reply(f"Reminder set for {parts[1]}.", silent=True)
async def reminder_task():
await asyncio.sleep(duration)
await message.reply(f"{message.author.mention} Reminder: {reminder_text.replace("@", "at")}")
asyncio.create_task(reminder_task())
elif message.content.startswith(".rps "):
await handle_blacklist(message)
parts = message.content.split(" ",2)
if len(parts) != 2:
await message.reply("Usage: `.rps <item`",silent=True)
item
item = parts[1]
if item.lower() == "dick":
await message.reply("Scissors beats dick any day :3",silent=True) # little easter egg
return
if item == "<@696800726084747314>":
await message.reply("Head so thick that i would try rock but the rock would break")
return
choice = random.choice([1,2,3])
rps_map = dict(zip((1,2,3),[i for i in "rps"]))
rps_map_reverse = dict(zip([i for i in "rps"],(1,2,3))) # FIXME: if you cant see the issue you're blind
shortmaps = {word[0]: word for word in ["rock", "paper", "scissors"]}
beat_map = {1:3,2:1,3:2}
iid = 0
for k,v in rps_map_reverse.items():
if item.lower().startswith(k):
iid = v
break
if iid == 0:
await message.reply("Invalid choice!",silent=True)
return
if choice == iid:
await message.reply(f"Huh we chose the same thing. Try again!",silent=True)
return
if beat_map[iid] == choice:
await message.reply(f"Welp, ggs, I won. I chose `{shortmaps[rps_map[choice]]}`",silent=True)
else:
await message.reply(f"Oop, you lost. Try again later! I chose `{shortmaps[rps_map[choice]]}`",silent=True)
# Handle DM if in AFK mode
if isinstance(message.channel, discord.DMChannel) and message.author != self.user:
await self.handle_afk_dm(message)
if message.channel.id in self.horsin:
await message.add_reaction("🐴")
if message.author.id == 1341423498618208257:
await message.add_reaction("🪣")
if message.author != self.user:
if message.author.id == 1351739454141763626:
try:
await message.delete()
except:
pass
return
if message.content.startswith(".horse"):
if message.channel.id in self.horsin:
self.horsin.remove(message.channel.id)
await message.reply("no longer horsin around D:")
else:
self.horsin.append(message.channel.id)
await message.reply(":D")
if message.content.startswith(".afk"):
if not self.AFK_STATUS:
self.AFK_STATUS = True
await message.reply("k")
else:
await message.reply("You are already in AFK mode.", silent=True)
# UNAFK Command
elif message.content.startswith(".unafk"):
if self.AFK_STATUS:
self.AFK_STATUS = False
for i in self.AFK_NOTIFIED_USERS:
try:
user = await self.fetch_user(i) # Fetch user by ID
if user:
dm_channel = await user.create_dm() # Create DM channel
await dm_channel.send("Hey, I'm back, human me will take over now!")
except Exception as e:
raise RuntimeWarning from e
self.AFK_NOTIFIED_USERS.clear() # Clear the AFK notified users list
await message.reply("should work")
else:
await message.reply("You are not in AFK mode.", silent=True)
if message.content.startswith(".fmt "):
try:
formated = eval(f"f'{message.content[5:]}'", globals(),locals())
print(formated)
await asyncio.wait_for(message.edit(formated), timeout=5)
except Exception as e:
flashdel = await message.channel.send(f"Error: {e}",silent=True)
await message.delete()
await flashdel.delete()
raise RuntimeWarning from e
elif message.content.startswith(".eval "):
try:
formatted = message.content[6:]
print(repr(formatted))
exec_scope = {
"msg": message,
"asyncio": asyncio,
"random": random,
**self.loaded_commands, # Inject loaded commands
"out": lambda content: message.reply(content, silent=True),
}
# Parse the code to detect non-async function calls
tree = ast.parse(formatted)
for node in ast.walk(tree):
if isinstance(node, ast.Call) and not isinstance(node.func, ast.Attribute):
# Check if the function is a coroutine
func_name = node.func.id
if func_name in exec_scope and asyncio.iscoroutinefunction(exec_scope[func_name]):
# Replace the call with an await expression
formatted = formatted.replace(f"{func_name}(", f"await {func_name}(")
exec(f"async def __eval():\n {formatted.replace(chr(10), chr(10) + ' ')}", exec_scope)
result = await exec_scope["__eval"]()
except Exception:
await message.edit(content=traceback.format_exc())
finally:
await message.delete()
elif message.content.startswith(".addcmd "):
try:
parts = message.content.split(" ", 2)
if len(parts) < 3:
await message.reply("Usage: .addcmd <name> <code>",silent=True)
return
cmd_name, code = parts[1], parts[2]
cmd_path = os.path.join(COMMANDS_DIR, f"{cmd_name}.py")
with open(cmd_path, "w") as f:
f.write("async def run(msg):\n")
for line in code.split("\n"):
f.write(f" {line}\n")
self.loaded_commands = load_commands()
await message.reply(f"Command {cmd_name} saved.",silent=True)
except Exception as e:
await message.reply(f"Error: {e}",silent=True)
elif message.content.startswith(".delcmd "):
cmd_name = message.content.split(" ", 1)[1]
cmd_path = os.path.join(COMMANDS_DIR, f"{cmd_name}.py")
if os.path.exists(cmd_path):
os.remove(cmd_path)
self.loaded_commands = load_commands()
await message.reply(f"Command {cmd_name} deleted.",silent=True)
else:
await message.reply(f"Command {cmd_name} not found.",silent=True)
elif message.content.startswith(".listcmds"):
cmds = list(self.loaded_commands.keys())
await message.reply("Saved commands:\n" + ", ".join(cmds) if cmds else "No saved commands.",silent=True)
elif message.content.startswith(".delrecent "):
try:
minutes = float(message.content.split(" ", 1)[1])
cutoff_time = datetime.datetime.now(datetime.UTC) - datetime.timedelta(minutes=minutes)
deleted = 0
async for msg in message.channel.history(limit=100):
if msg.author == self.user and msg.created_at >= cutoff_time:
await msg.delete()
deleted += 1
await message.channel.send(f"Deleted {deleted} messages.", delete_after=0,silent=True)
except Exception as e:
await message.channel.send(f"Error: {e}", delete_after=0,silent=True)
elif message.content.startswith(".trackmessages"):
channel_id = message.channel.id
if channel_id not in self.tracked_channels:
self.tracked_channels.append(channel_id)
save_tracked_channels(self.tracked_channels)
await message.reply(f"Tracking messages in this channel {message.channel.name}.",silent=True)
else:
await message.reply("This channel is already being tracked.",silent=True)
elif message.content.startswith(".untrackmessages"):
channel_id = message.channel.id
if channel_id in self.tracked_channels:
self.tracked_channels.remove(channel_id)
save_tracked_channels(self.tracked_channels)
await message.reply(f"Stopped tracking messages in {message.channel.name}.",silent=True)
else:
await message.reply("This channel is not being tracked.",silent=True)
elif message.content.startswith(".savechannel"):
try:
messages = []
async for msg in message.channel.history(limit=None):
msg_dict = {
"id": msg.id,
"content": msg.content,
"created_at": msg.created_at.isoformat() if msg.created_at else None,
"edited_at": msg.edited_at.isoformat() if msg.edited_at else None,
"author": {
"id": msg.author.id,
"name": msg.author.name,
"discriminator": msg.author.discriminator,
"bot": msg.author.bot
} if msg.author else None,
"channel_id": msg.channel.id,
"attachments": [
{"filename": a.filename, "url": a.url} for a in msg.attachments
] if msg.attachments else [],
"embeds": [embed.to_dict() for embed in msg.embeds] if msg.embeds else [],
"reactions": [
{"emoji": str(r.emoji), "count": r.count} for r in msg.reactions
] if msg.reactions else [],
"mentions": [user.id for user in msg.mentions] if msg.mentions else [],
"role_mentions": [role.id for role in msg.role_mentions] if msg.role_mentions else [],
"pinned": msg.pinned,
"tts": msg.tts,
"type": str(msg.type),
"reference": {
"message_id": msg.reference.message_id,
"channel_id": msg.reference.channel_id,
"guild_id": msg.reference.guild_id
} if msg.reference else None,
}
messages.append(msg_dict)
filename = f"{message.channel.id}.json.gz"
filepath = os.path.join("downloads", filename) # Save to a "downloads" folder
os.makedirs("downloads", exist_ok=True)
with gzip.open(filepath, "wt", encoding="utf-8") as f:
json.dump(messages, f, ensure_ascii=False, indent=4)
await message.edit(content=f"Saved messages to `{filepath}`")
except Exception as e:
await message.edit(content=f"Error: {e}")
elif message.content.startswith(".repeat29"):
await message.reply("oop ok")
while 1:
await message.channel.send("you asked dad")
await asyncio.sleep(29*60)
@staticmethod
async def process_log_whitelist(message):
if message.author in [627566973869359104,]:
return
async def on_message_delete(self, message):
await self.process_log_whitelist(message)
if message.channel.id in self.tracked_channels:
member = message.author
if member != self.user:
await message.channel.send(f"<@{member.id}> deleted {message.content}",silent=True)
async def on_message_edit(self, before, after):
await self.process_log_whitelist(before)
if before.channel.id in self.tracked_channels:
member = after.author
if member == self.user: return
if before.content == after.content: return
diff = difflib.unified_diff(before.content.splitlines(), after.content.splitlines())
diff_result = '\n'.join(diff)
# Use BytesIO to create an in-memory file-like object with the full diff (no line removal)
with io.BytesIO(diff_result.encode('utf-8')) as diff_file:
diff_file.seek(0) # Ensure we're at the start of the BytesIO buffer
# Send the file to the channel
await after.channel.send(
f"<@{member.id}> edited a message",
file=discord.File(diff_file, "cutie.diff"),silent=True
)
async def on_presence_update(self, before, after):
if after.id == 627566973869359104:
old_status = self.last_status.get(after.id, discord.Status.offline)
self.last_status[after.id] = after.status
if old_status in [discord.Status.offline] and after.status == discord.Status.online:
channel = self.get_channel(1302691935152246851)
if channel:
...
#await channel.send(f"[BOT] Welcome back, {after.mention}!",silent=True)
client = Selfbot()
# get token from environment variable
TOKEN = os.getenv("TOKEN")
if not TOKEN:
raise ValueError("No TOKEN found in environment variables. Please add it to your .env file.")
client.run(TOKEN)

1
bot/__init__.py Normal file
View file

@ -0,0 +1 @@
# Empty init to make directory a package

1
bot/commands/__init__.py Normal file
View file

@ -0,0 +1 @@
# Empty init to make directory a package

View file

@ -0,0 +1,65 @@
import os
from config import COMMANDS_DIR
from utils.storage import save_tracked_channels
class AdminCommands:
def __init__(self, bot):
self.bot = bot
async def cmd_addcmd(self, message):
"""Add a custom command"""
try:
parts = message.content.split(" ", 2)
if len(parts) < 3:
await message.reply("Usage: .addcmd <name> <code>", silent=True)
return
cmd_name, code = parts[1], parts[2]
cmd_path = os.path.join(COMMANDS_DIR, f"{cmd_name}.py")
with open(cmd_path, "w") as f:
f.write("async def run(msg):\n")
for line in code.split("\n"):
f.write(f" {line}\n")
self.bot.reload_commands()
await message.reply(f"Command {cmd_name} saved.", silent=True)
except Exception as e:
await message.reply(f"Error: {e}", silent=True)
async def cmd_delcmd(self, message):
"""Delete a custom command"""
cmd_name = message.content.split(" ", 1)[1]
cmd_path = os.path.join(COMMANDS_DIR, f"{cmd_name}.py")
if os.path.exists(cmd_path):
os.remove(cmd_path)
self.bot.reload_commands()
await message.reply(f"Command {cmd_name} deleted.", silent=True)
else:
await message.reply(f"Command {cmd_name} not found.", silent=True)
async def cmd_listcmds(self, message):
"""List all custom commands"""
cmds = list(self.bot.loaded_commands.keys())
await message.reply("Saved commands:\n" + ", ".join(cmds) if cmds else "No saved commands.", silent=True)
async def cmd_trackmessages(self, message):
"""Start tracking messages in the current channel"""
channel_id = message.channel.id
if channel_id not in self.bot.tracked_channels:
self.bot.tracked_channels.append(channel_id)
save_tracked_channels(self.bot.tracked_channels)
await message.reply(f"Tracking messages in this channel {message.channel.name}.", silent=True)
else:
await message.reply("This channel is already being tracked.", silent=True)
async def cmd_untrackmessages(self, message):
"""Stop tracking messages in the current channel"""
channel_id = message.channel.id
if channel_id in self.bot.tracked_channels:
self.bot.tracked_channels.remove(channel_id)
save_tracked_channels(self.bot.tracked_channels)
await message.reply(f"Stopped tracking messages in {message.channel.name}.", silent=True)
else:
await message.reply("This channel is not being tracked.", silent=True)

View file

@ -0,0 +1,38 @@
class AfkCommands:
def __init__(self, bot):
self.bot = bot
async def handle_afk_dm(self, message):
"""Handle DMs when in AFK mode"""
if self.bot.AFK_STATUS and message.author.id not in self.bot.AFK_NOTIFIED_USERS:
await message.reply(
"Heya, I'm not at my computer right now, if you're requesting something please follow <https://nohello.club>. I'll let you know when I'm back :) \n\n-# This action was automated."
)
self.bot.AFK_NOTIFIED_USERS.append(message.author.id)
async def cmd_afk(self, message):
"""Enable AFK mode"""
if not self.bot.AFK_STATUS:
self.bot.AFK_STATUS = True
await message.reply("k")
else:
await message.reply("You are already in AFK mode.", silent=True)
async def cmd_unafk(self, message):
"""Disable AFK mode"""
if self.bot.AFK_STATUS:
self.bot.AFK_STATUS = False
for i in self.bot.AFK_NOTIFIED_USERS:
try:
user = await self.bot.fetch_user(i)
if user:
dm_channel = await user.create_dm()
await dm_channel.send("Hey, I'm back, human me will take over now!")
except Exception as e:
raise RuntimeWarning from e
self.bot.AFK_NOTIFIED_USERS.clear()
await message.reply("should work")
else:
await message.reply("You are not in AFK mode.", silent=True)

View file

@ -0,0 +1,65 @@
import random
import asyncio
class FunCommands:
def __init__(self, bot):
self.bot = bot
async def cmd_horse(self, message):
"""Toggle horse reactions in a channel"""
if message.channel.id in self.bot.horsin:
self.bot.horsin.remove(message.channel.id)
await message.reply("no longer horsin around D:")
else:
self.bot.horsin.append(message.channel.id)
await message.reply(":D")
async def cmd_rps(self, message):
"""Play rock, paper, scissors"""
parts = message.content.split(" ", 1)
if len(parts) != 2:
await message.reply("Usage: `.rps <item>`", silent=True)
return
item = parts[1]
# Easter eggs
if item.lower() == "dick":
await message.reply("Scissors beats dick any day :3", silent=True)
return
if item == "<@696800726084747314>":
await message.reply("Head so thick that i would try rock but the rock would break")
return
# The main game
choice = random.choice([1, 2, 3]) # 1=rock, 2=paper, 3=scissors
rps_map = {1: 'r', 2: 'p', 3: 's'}
rps_map_reverse = {'r': 1, 'p': 2, 's': 3} # Fixed the reversed mapping
shortmaps = {'r': 'rock', 'p': 'paper', 's': 'scissors'}
beat_map = {1: 3, 2: 1, 3: 2} # What beats what: rock>scissors, paper>rock, scissors>paper
# Determine user choice
iid = 0
for k, v in rps_map_reverse.items():
if item.lower().startswith(k):
iid = v
break
if iid == 0:
await message.reply("Invalid choice!", silent=True)
return
if choice == iid:
await message.reply(f"Huh we chose the same thing. Try again!", silent=True)
elif beat_map[iid] == choice:
await message.reply(f"Welp, ggs, I won. I chose `{shortmaps[rps_map[choice]]}`", silent=True)
else:
await message.reply(f"Oop, you lost. Try again later! I chose `{shortmaps[rps_map[choice]]}`", silent=True)
async def cmd_repeat(self, message):
"""Repeat a message every 29 minutes"""
await message.reply("oop ok")
while True:
await message.channel.send("you asked dad")
await asyncio.sleep(29 * 60) # 29 minutes

View file

@ -0,0 +1,430 @@
import asyncio
import time
import inspect
import traceback
from datetime import datetime
class TestCommands:
def __init__(self, bot):
self.bot = bot
self.tests = {
"afk": self.test_afk,
"remindme": self.test_remindme,
"commands": self.test_commands,
"storage": self.test_storage,
"health": self.test_health,
"all": self.test_all,
}
async def cmd_test(self, message):
"""Run self-tests on the bot"""
parts = message.content.split(" ", 1)
test_name = parts[1] if len(parts) > 1 else "all"
if test_name not in self.tests:
await message.channel.send(
f"Unknown test '{test_name}'. Available tests: {', '.join(self.tests.keys())}"
)
return
# Create a status message without silent flag for better visibility
status_msg = await message.channel.send(f"🔄 Running test: {test_name}...")
start_time = time.time()
try:
if test_name == "all":
# For "all" tests, update status more frequently
await status_msg.edit(content="🔄 Initializing test suite...")
results = await self.test_all(status_msg)
else:
test_func = self.tests[test_name]
results = await test_func(status_msg)
elapsed = time.time() - start_time
# Format results into a nice report
report = self._format_test_report(results, elapsed)
# Make sure report isn't too long for Discord
if len(report) > 2000:
report = report[:1997] + "..."
# Update status with results
await status_msg.edit(content=report)
except Exception as e:
error_msg = f"❌ Test failed with error:\n\n{traceback.format_exc()[:1500]}\n"
print(f"Test error: {str(e)}")
await status_msg.edit(content=error_msg)
def _format_test_report(self, results, elapsed):
"""Format test results into a readable report"""
if isinstance(results, dict):
# We have multiple test suites
total_passed = sum(r['passed'] for r in results.values())
total_failed = sum(r['failed'] for r in results.values())
total_tests = total_passed + total_failed
report = f"# Self-Test Report ({elapsed:.2f}s)\n\n"
report += f"✅ **{total_passed}/{total_tests}** tests passed\n"
if total_failed > 0:
report += f"❌ **{total_failed}** tests failed\n\n"
else:
report += "\n"
# Add individual test suite results
for suite_name, suite_result in results.items():
passed = suite_result['passed']
failed = suite_result['failed']
total = passed + failed
status = "" if failed == 0 else "⚠️"
report += f"{status} **{suite_name}**: {passed}/{total} passed\n"
# Add failure details if any
if failed > 0 and 'failures' in suite_result:
report += "\n"
for failure in suite_result['failures']:
report += f"{failure}\n"
report += "\n"
else:
# Single test suite
passed = results['passed']
failed = results['failed']
total = passed + failed
report = f"# Test Results ({elapsed:.2f}s)\n\n"
report += f"✅ **{passed}/{total}** tests passed\n"
if failed > 0:
report += f"❌ **{failed}** tests failed\n\n"
report += "\n"
for failure in results.get('failures', []):
report += f"{failure}\n"
report += "\n"
return report
async def test_all(self, status_msg):
"""Run all available tests"""
results = {}
test_funcs = [v for k, v in self.tests.items() if k != "all"]
for i, test_func in enumerate(test_funcs):
test_name = test_func.__name__.replace('test_', '')
try:
# Update status before each test
await status_msg.edit(content=f"🔄 Running tests ({i+1}/{len(test_funcs)}): {test_name}...")
results[test_name] = await test_func(status_msg)
# Quick status after each test
passed = results[test_name]['passed']
failed = results[test_name]['failed']
await status_msg.edit(content=f"🔄 Test {test_name}: ✅{passed}{failed} | Continuing tests...")
except Exception as e:
results[test_name] = {'passed': 0, 'failed': 1, 'failures': [f"Exception: {str(e)}"]}
await status_msg.edit(content=f"⚠️ Error in test {test_name}, continuing with next test...")
return results
async def test_afk(self, status_msg):
"""Test AFK functionality"""
results = {'passed': 0, 'failed': 0, 'failures': []}
# Save original state
original_afk = self.bot.AFK_STATUS
original_notified = self.bot.AFK_NOTIFIED_USERS.copy()
try:
# Test 1: Enable AFK
self.bot.AFK_STATUS = False
await self.bot.afk_commands.cmd_afk(status_msg)
if self.bot.AFK_STATUS:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Failed to enable AFK mode")
# Test 2: Disable AFK
await self.bot.afk_commands.cmd_unafk(status_msg)
if not self.bot.AFK_STATUS:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Failed to disable AFK mode")
# Test 3: AFK notification list clears
if len(self.bot.AFK_NOTIFIED_USERS) == 0:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("AFK notified users list not cleared")
finally:
# Restore original state
self.bot.AFK_STATUS = original_afk
self.bot.AFK_NOTIFIED_USERS = original_notified
return results
async def test_remindme(self, status_msg):
"""Test reminder functionality with a very short reminder"""
results = {'passed': 0, 'failed': 0, 'failures': []}
# Create a test reminder message
test_content = ".remindme 1s Test reminder"
mock_message = MockMessage(
author=self.bot.user,
content=test_content,
channel=status_msg.channel
)
# Set up a flag to verify the reminder was triggered
reminder_triggered = False
original_reply = mock_message.reply
async def mock_reply(content, **kwargs):
nonlocal reminder_triggered
if "Reminder:" in content:
reminder_triggered = True
return await original_reply(content, **kwargs)
mock_message.reply = mock_reply
try:
# Test reminder setup
await self.bot.utility_commands.cmd_remindme(mock_message)
# Wait for the reminder to trigger (slightly more than 1s)
await asyncio.sleep(1.5)
if reminder_triggered:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Reminder did not trigger")
except Exception as e:
results['failed'] += 1
results['failures'].append(f"Reminder test error: {str(e)}")
return results
async def test_commands(self, status_msg):
"""Test custom command functionality"""
results = {'passed': 0, 'failed': 0, 'failures': []}
# Test command name
test_cmd_name = "__test_cmd__"
test_cmd_path = f"commands/{test_cmd_name}.py"
try:
# Test 1: Add a command
add_cmd_msg = MockMessage(
author=self.bot.user,
content=f".addcmd {test_cmd_name} return await msg.reply('test success', silent=True)",
channel=status_msg.channel
)
await self.bot.admin_commands.cmd_addcmd(add_cmd_msg)
if test_cmd_name in self.bot.loaded_commands:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Failed to add test command")
# Test 2: Execute the command
if test_cmd_name in self.bot.loaded_commands:
try:
test_msg = MockMessage(
author=self.bot.user,
content="test content",
channel=status_msg.channel
)
reply_received = False
original_reply = test_msg.reply
async def test_reply(content, **kwargs):
nonlocal reply_received
if content == "test success":
reply_received = True
return await original_reply(content, **kwargs)
test_msg.reply = test_reply
await self.bot.loaded_commands[test_cmd_name](test_msg)
if reply_received:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Command execution failed")
except Exception as e:
results['failed'] += 1
results['failures'].append(f"Command execution error: {str(e)}")
# Test 3: Delete the command
del_cmd_msg = MockMessage(
author=self.bot.user,
content=f".delcmd {test_cmd_name}",
channel=status_msg.channel
)
await self.bot.admin_commands.cmd_delcmd(del_cmd_msg)
if test_cmd_name not in self.bot.loaded_commands:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Failed to delete test command")
except Exception as e:
results['failed'] += 1
results['failures'].append(f"Command test error: {str(e)}")
# Clean up any leftovers
import os
if os.path.exists(test_cmd_path):
try:
os.remove(test_cmd_path)
self.bot.reload_commands()
except:
pass
return results
async def test_storage(self, status_msg):
"""Test channel tracking functionality"""
results = {'passed': 0, 'failed': 0, 'failures': []}
# Save original state
original_tracked = self.bot.tracked_channels.copy()
try:
# Test channel ID that likely doesn't exist
test_channel_id = 1349095349905788968
# Make sure it's not in the tracked channels
if test_channel_id in self.bot.tracked_channels:
self.bot.tracked_channels.remove(test_channel_id)
# Test 1: Add a tracked channel
self.bot.tracked_channels.append(test_channel_id)
from utils.storage import save_tracked_channels
save_tracked_channels(self.bot.tracked_channels)
# Test 2: Load tracked channels
from utils.storage import load_tracked_channels
loaded_channels = load_tracked_channels()
if test_channel_id in loaded_channels:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Failed to save/load tracked channel")
# Test 3: Remove tracked channel
if test_channel_id in self.bot.tracked_channels:
self.bot.tracked_channels.remove(test_channel_id)
save_tracked_channels(self.bot.tracked_channels)
loaded_channels = load_tracked_channels()
if test_channel_id not in loaded_channels:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Failed to remove tracked channel")
finally:
# Restore original state
self.bot.tracked_channels = original_tracked
from utils.storage import save_tracked_channels
save_tracked_channels(self.bot.tracked_channels)
return results
async def test_health(self, status_msg):
"""Test basic bot health and connectivity"""
results = {'passed': 0, 'failed': 0, 'failures': []}
# Test 1: Check if the bot is logged in
if self.bot.user is not None:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append("Bot is not logged in")
# Test 2: Check discord API connection by getting client latency
try:
latency = self.bot.latency
if isinstance(latency, float):
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append(f"Invalid latency value: {latency}")
except Exception as e:
results['failed'] += 1
results['failures'].append(f"Failed to get latency: {str(e)}")
# Test 3: Test message sending/editing (core functionality)
try:
test_msg = await status_msg.channel.send(
"Test message - will be deleted", silent=False
)
await test_msg.edit(content="Test message edited - will be deleted")
await test_msg.delete()
results['passed'] += 1
except Exception as e:
results['failed'] += 1
results['failures'].append(f"Message operations failed: {str(e)}")
return results
def _assert(self, results, condition, message):
"""Helper method for assertions in tests"""
if condition:
results['passed'] += 1
else:
results['failed'] += 1
results['failures'].append(message)
return condition
class MockMessage:
"""A mock message class for testing"""
def __init__(self, author, content, channel):
self.author = author
self.content = content
self.channel = channel
self.id = int(time.time() * 1000)
self.created_at = datetime.now()
async def reply(self, content, **kwargs):
"""Mock reply method"""
return await self.channel.send(f"Reply to {self.id}: {content}", **kwargs)
async def edit(self, **kwargs):
"""Mock edit method"""
self.content = kwargs.get('content', self.content)
return self
async def cmd_test_debug(self, message):
"""Run a simple debug test to verify command functionality"""
try:
# Send a simple message that should always work
debug_msg = await message.channel.send("🔍 Debug test initiated...")
# Wait a moment
await asyncio.sleep(1)
# Edit the message
await debug_msg.edit(content="✅ Debug test successful - message editing works!")
except Exception as e:
# If this fails, there's a fundamental issue
await message.channel.send(f"❌ Debug test failed: {str(e)}")

View file

@ -0,0 +1,142 @@
import asyncio
import traceback
import datetime
import os
import ast
import gzip
import json
from utils.time_parser import parse_time
from config import DOWNLOADS_DIR
class UtilityCommands:
def __init__(self, bot):
self.bot = bot
async def cmd_remindme(self, message):
"""Set a reminder"""
parts = message.content.split(" ", 2)
if len(parts) < 3:
await message.reply("Usage: `.remindme <time> <message>`", silent=True)
else:
duration = parse_time(parts[1])
if duration is None:
await message.reply("Invalid time format. Example: `4m2s`, `1h30m`.", silent=True)
else:
reminder_text = parts[2]
await message.reply(f"Reminder set for {parts[1]}.", silent=True)
async def reminder_task():
await asyncio.sleep(duration)
await message.reply(f"{message.author.mention} Reminder: {reminder_text.replace('@', 'at')}")
asyncio.create_task(reminder_task())
async def cmd_fmt(self, message):
"""Format a string using f-string"""
try:
formatted = eval(f"f'{message.content[5:]}'", globals(), locals())
print(formatted)
await asyncio.wait_for(message.edit(formatted), timeout=5)
except Exception as e:
flashdel = await message.channel.send(f"Error: {e}", silent=True)
await message.delete()
await flashdel.delete()
raise RuntimeWarning from e
async def cmd_eval(self, message):
"""Evaluate Python code"""
try:
formatted = message.content[6:]
print(repr(formatted))
exec_scope = {
"msg": message,
"asyncio": asyncio,
"random": __import__("random"),
**self.bot.loaded_commands,
"out": lambda content: message.reply(content, silent=True),
}
# Parse the code to detect non-async function calls
tree = ast.parse(formatted)
for node in ast.walk(tree):
if isinstance(node, ast.Call) and not isinstance(node.func, ast.Attribute):
# Check if the function is a coroutine
func_name = node.func.id
if func_name in exec_scope and asyncio.iscoroutinefunction(exec_scope[func_name]):
# Replace the call with an await expression
formatted = formatted.replace(f"{func_name}(", f"await {func_name}(")
exec(f"async def __eval():\n {formatted.replace(chr(10), chr(10) + ' ')}", exec_scope)
await exec_scope["__eval"]()
except Exception:
await message.edit(content=traceback.format_exc())
finally:
await message.delete()
async def cmd_delrecent(self, message):
"""Delete recent messages"""
try:
minutes = float(message.content.split(" ", 1)[1])
cutoff_time = datetime.datetime.now(datetime.UTC) - datetime.timedelta(minutes=minutes)
deleted = 0
async for msg in message.channel.history(limit=100):
if msg.author == self.bot.user and msg.created_at >= cutoff_time:
await msg.delete()
deleted += 1
await message.channel.send(f"Deleted {deleted} messages.", delete_after=0, silent=True)
except Exception as e:
await message.channel.send(f"Error: {e}", delete_after=0, silent=True)
async def cmd_savechannel(self, message):
"""Save all messages in a channel to a file"""
try:
messages = []
async for msg in message.channel.history(limit=None):
msg_dict = {
"id": msg.id,
"content": msg.content,
"created_at": msg.created_at.isoformat() if msg.created_at else None,
"edited_at": msg.edited_at.isoformat() if msg.edited_at else None,
"author": {
"id": msg.author.id,
"name": msg.author.name,
"discriminator": msg.author.discriminator,
"bot": msg.author.bot
} if msg.author else None,
"channel_id": msg.channel.id,
"attachments": [
{"filename": a.filename, "url": a.url} for a in msg.attachments
] if msg.attachments else [],
"embeds": [embed.to_dict() for embed in msg.embeds] if msg.embeds else [],
"reactions": [
{"emoji": str(r.emoji), "count": r.count} for r in msg.reactions
] if msg.reactions else [],
"mentions": [user.id for user in msg.mentions] if msg.mentions else [],
"role_mentions": [role.id for role in msg.role_mentions] if msg.role_mentions else [],
"pinned": msg.pinned,
"tts": msg.tts,
"type": str(msg.type),
"reference": {
"message_id": msg.reference.message_id,
"channel_id": msg.reference.channel_id,
"guild_id": msg.reference.guild_id
} if msg.reference else None,
}
messages.append(msg_dict)
filename = f"{message.channel.id}.json.gz"
os.makedirs(DOWNLOADS_DIR, exist_ok=True)
filepath = os.path.join(DOWNLOADS_DIR, filename)
with gzip.open(filepath, "wt", encoding="utf-8") as f:
json.dump(messages, f, ensure_ascii=False, indent=4)
await message.edit(content=f"Saved messages to `{filepath}`")
except Exception as e:
await message.edit(content=f"Error: {e}")

1
bot/handlers/__init__.py Normal file
View file

@ -0,0 +1 @@
# Empty init to make directory a package

View file

@ -0,0 +1,109 @@
import discord
import asyncio
from config import BLACKLISTED_USERS, BUCKET_REACT_USERS, AUTO_DELETE_USERS, SPECIAL_RESPONSES
from bot.commands.afk_commands import AfkCommands
from bot.commands.utility_commands import UtilityCommands
from bot.commands.fun_commands import FunCommands
from bot.commands.admin_commands import AdminCommands
from bot.commands.test_commands import TestCommands
class MessageHandler:
def __init__(self, bot):
self.bot = bot
# Initialize command handlers
self.afk_commands = AfkCommands(bot)
self.utility_commands = UtilityCommands(bot)
self.fun_commands = FunCommands(bot)
self.admin_commands = AdminCommands(bot)
self.test_commands = TestCommands(bot)
# Attach command handlers to the bot for easier access from tests
bot.afk_commands = self.afk_commands
bot.utility_commands = self.utility_commands
bot.fun_commands = self.fun_commands
bot.admin_commands = self.admin_commands
bot.test_commands = self.test_commands
async def handle_message(self, message):
# Handle special responses
for user_id, data in SPECIAL_RESPONSES.items():
if message.author.id == user_id and data["trigger"] in message.content:
await message.reply(data["response"])
# Handle automatic reactions
if message.channel.id in self.bot.horsin:
await message.add_reaction("🐴")
if message.author.id in BUCKET_REACT_USERS:
await message.add_reaction("🪣")
# Handle auto-delete for specific users
if message.author.id in AUTO_DELETE_USERS:
try:
await message.delete()
except:
pass
# Handle DM if in AFK mode
if isinstance(message.channel, discord.DMChannel) and message.author != self.bot.user:
await self.afk_commands.handle_afk_dm(message)
# Don't process further if the message is not from the bot user
if message.author != self.bot.user:
return
# Handle commands
await self.handle_commands(message)
async def handle_blacklist(self, message):
"""Handle blacklisted users"""
if message.author.id in BLACKLISTED_USERS:
await message.reply("no")
return True
return False
async def handle_commands(self, message):
"""Handle commands issued by the bot user"""
content = message.content
# AFK Commands
if content.startswith(".afk"):
await self.afk_commands.cmd_afk(message)
elif content.startswith(".unafk"):
await self.afk_commands.cmd_unafk(message)
# Fun Commands
elif content.startswith(".horse"):
await self.fun_commands.cmd_horse(message)
elif content.startswith(".rps "):
if not await self.handle_blacklist(message):
await self.fun_commands.cmd_rps(message)
elif content.startswith(".repeat29"):
await self.fun_commands.cmd_repeat(message)
# Utility Commands
elif content.startswith(".remindme "):
if not await self.handle_blacklist(message):
await self.utility_commands.cmd_remindme(message)
elif content.startswith(".fmt "):
await self.utility_commands.cmd_fmt(message)
elif content.startswith(".eval "):
await self.utility_commands.cmd_eval(message)
elif content.startswith(".delrecent "):
await self.utility_commands.cmd_delrecent(message)
elif content.startswith(".savechannel"):
await self.utility_commands.cmd_savechannel(message)
# Admin Commands
elif content.startswith(".addcmd "):
await self.admin_commands.cmd_addcmd(message)
elif content.startswith(".delcmd "):
await self.admin_commands.cmd_delcmd(message)
elif content.startswith(".listcmds"):
await self.admin_commands.cmd_listcmds(message)
elif content.startswith(".trackmessages"):
await self.admin_commands.cmd_trackmessages(message)
elif content.startswith(".untrackmessages"):
await self.admin_commands.cmd_untrackmessages(message)
elif content.startswith(".test"):
await self.test_commands.cmd_test(message)

View file

@ -0,0 +1,19 @@
import discord
from config import WELCOME_BACK_CHANNEL_ID
class PresenceHandler:
def __init__(self, bot):
self.bot = bot
async def handle_presence_update(self, before, after):
"""Handle user presence updates"""
if after.id == 627566973869359104:
old_status = self.bot.last_status.get(after.id, discord.Status.offline)
self.bot.last_status[after.id] = after.status
if old_status == discord.Status.offline and after.status == discord.Status.online:
channel = self.bot.get_channel(WELCOME_BACK_CHANNEL_ID)
if channel:
# Commented out for now as in original code
# await channel.send(f"[BOT] Welcome back, {after.mention}!", silent=True)
pass

View file

@ -0,0 +1,51 @@
import discord
import io
import difflib
class TrackingHandler:
def __init__(self, bot):
self.bot = bot
async def process_log_whitelist(self, message):
"""Process whitelist for logging"""
if message.author.id == 627566973869359104:
return True
return False
async def handle_message_delete(self, message):
"""Handle when messages are deleted"""
if await self.process_log_whitelist(message):
return
if message.channel.id in self.bot.tracked_channels:
member = message.author
if member != self.bot.user:
await message.channel.send(
f"<@{member.id}> deleted {message.content}",
silent=True
)
async def handle_message_edit(self, before, after):
"""Handle when messages are edited"""
if await self.process_log_whitelist(before):
return
if before.channel.id in self.bot.tracked_channels:
member = after.author
if member == self.bot.user:
return
if before.content == after.content:
return
diff = difflib.unified_diff(before.content.splitlines(), after.content.splitlines())
diff_result = '\n'.join(diff)
with io.BytesIO(diff_result.encode('utf-8')) as diff_file:
diff_file.seek(0)
await after.channel.send(
f"<@{member.id}> edited a message",
file=discord.File(diff_file, "cutie.diff"),
silent=True
)

41
bot/selfbot.py Normal file
View file

@ -0,0 +1,41 @@
import discord
from utils.storage import load_commands, load_tracked_channels
from bot.handlers.message_handler import MessageHandler
from bot.handlers.tracking_handler import TrackingHandler
from bot.handlers.presence_handler import PresenceHandler
class Selfbot(discord.Client):
def __init__(self):
super().__init__()
# State variables
self.default_status = None
self.loaded_commands = load_commands()
self.tracked_channels = load_tracked_channels()
self.last_status = {}
self.AFK_STATUS = False
self.AFK_NOTIFIED_USERS = []
self.horsin = []
# Initialize handlers
self.message_handler = MessageHandler(self)
self.tracking_handler = TrackingHandler(self)
self.presence_handler = PresenceHandler(self)
async def on_ready(self):
print(f"Logged in as {self.user}")
async def on_message(self, message):
await self.message_handler.handle_message(message)
async def on_message_delete(self, message):
await self.tracking_handler.handle_message_delete(message)
async def on_message_edit(self, before, after):
await self.tracking_handler.handle_message_edit(before, after)
async def on_presence_update(self, before, after):
await self.presence_handler.handle_presence_update(before, after)
def reload_commands(self):
"""Reload user-defined commands"""
self.loaded_commands = load_commands()

19
config.py Normal file
View file

@ -0,0 +1,19 @@
import os
# Directories
COMMANDS_DIR = "commands"
DOWNLOADS_DIR = "downloads"
# Files
TRACKED_CHANNELS_FILE = "tracked_channels.json"
# User IDs for special handling
BLACKLISTED_USERS = []
BUCKET_REACT_USERS = []
AUTO_DELETE_USERS = []
SPECIAL_RESPONSES = {
1236667927944761396: {"trigger": "<@1168626335942455326>", "response": "shut the fuck up"}
}
# Channels
WELCOME_BACK_CHANNEL_ID = 0

19
main.py Normal file
View file

@ -0,0 +1,19 @@
import os
from dotenv import load_dotenv
from bot.selfbot import Selfbot
def main():
# Load environment variables from .env file
load_dotenv()
# Get token from environment variable
TOKEN = os.getenv("TOKEN")
if not TOKEN:
raise ValueError("No TOKEN found in environment variables. Please add it to your .env file.")
# Initialize and run the bot
bot = Selfbot()
bot.run(TOKEN)
if __name__ == "__main__":
main()

1
utils/__init__.py Normal file
View file

@ -0,0 +1 @@
# Empty init to make directory a package

33
utils/storage.py Normal file
View file

@ -0,0 +1,33 @@
import os
import json
from config import COMMANDS_DIR, TRACKED_CHANNELS_FILE
import importlib.util
def load_tracked_channels():
"""Load tracked channel IDs from file"""
if os.path.exists(TRACKED_CHANNELS_FILE):
with open(TRACKED_CHANNELS_FILE, 'r') as f:
return json.load(f)
return []
def save_tracked_channels(tracked_channels):
"""Save tracked channel IDs to file"""
with open(TRACKED_CHANNELS_FILE, 'w') as f:
json.dump(tracked_channels, f)
def load_commands():
"""Load user-defined commands from the commands directory"""
os.makedirs(COMMANDS_DIR, exist_ok=True)
commands = {}
for filename in os.listdir(COMMANDS_DIR):
if filename.endswith(".py"):
cmd_name = filename[:-3]
cmd_path = os.path.join(COMMANDS_DIR, filename)
spec = importlib.util.spec_from_file_location(cmd_name, cmd_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
if hasattr(module, "run") and callable(module.run):
commands[cmd_name] = module.run
return commands

20
utils/time_parser.py Normal file
View file

@ -0,0 +1,20 @@
import re
time_regex = re.compile(r'(\d+)([smhd])') # Matches 4m2s, 1h30m, etc.
def parse_time(time_str):
"""
Parse time strings like "4m2s", "1h30m" into seconds.
Args:
time_str: String in format like "4m2s", "1h30m"
Returns:
Integer of total seconds or None if invalid
"""
units = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
try:
total_seconds = sum(int(amount) * units[unit] for amount, unit in time_regex.findall(time_str))
return total_seconds if total_seconds > 0 else None
except:
return None