├── .gitignore ├── Backend ├── Modules │ ├── Economy │ │ ├── balance.py │ │ ├── coinflip.py │ │ ├── daily.py │ │ ├── dice.py │ │ ├── leaderboard.py │ │ ├── mafia.py │ │ ├── roulette.py │ │ ├── slots.py │ │ ├── steal.py │ │ ├── store.py │ │ ├── stripper.py │ │ └── work.py │ ├── Moderation │ │ ├── ban.py │ │ └── kick.py │ ├── __init__.py │ ├── afk.py │ ├── ai.py │ ├── avatar.py │ ├── calculator.py │ ├── choose.py │ ├── dictionary.py │ ├── ecoCore.py │ ├── github.py │ ├── help.py │ ├── lyrics.py │ ├── music.py │ ├── perms.py │ ├── petpet.py │ ├── polls.py │ ├── qotd.py │ ├── quote.py │ ├── reactions.py │ ├── reverse.py │ ├── setup.py │ ├── snipe.py │ ├── spotify.py │ ├── statistics.py │ ├── stealer.py │ ├── test.py │ ├── timezone.py │ └── tweet.py ├── __init__.py ├── bot.py ├── cogs.py ├── embed.py ├── events.py ├── groups.py ├── logger.py ├── permissions.py ├── send.py └── utils.py ├── LICENSE.md ├── README.md ├── TODO ├── assets └── quote │ ├── black.png │ ├── default_pfp.png │ ├── font.otf │ └── white.png ├── main.py ├── modules.json ├── settings.example.json ├── setup ├── create_data.py ├── full-setup.bat ├── full-setup.exe ├── requirements.txt └── setup.py ├── start.bat └── start.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | Pipfile 29 | Pipfile.lock 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # poetry 100 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 101 | # This is especially recommended for binary packages to ensure reproducibility, and is more 102 | # commonly ignored for libraries. 103 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 104 | #poetry.lock 105 | 106 | # pdm 107 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 108 | #pdm.lock 109 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 110 | # in version control. 111 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 112 | .pdm.toml 113 | .pdm-python 114 | .pdm-build/ 115 | 116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 117 | __pypackages__/ 118 | 119 | # Celery stuff 120 | celerybeat-schedule 121 | celerybeat.pid 122 | 123 | # SageMath parsed files 124 | *.sage.py 125 | 126 | # Environments 127 | .env 128 | .venv 129 | env/ 130 | venv/ 131 | ENV/ 132 | env.bak/ 133 | venv.bak/ 134 | 135 | # Spyder project settings 136 | .spyderproject 137 | .spyproject 138 | 139 | # Rope project settings 140 | .ropeproject 141 | 142 | # mkdocs documentation 143 | /site 144 | 145 | # mypy 146 | .mypy_cache/ 147 | .dmypy.json 148 | dmypy.json 149 | 150 | # Pyre type checker 151 | .pyre/ 152 | 153 | # pytype static type analyzer 154 | .pytype/ 155 | 156 | # Cython debug symbols 157 | cython_debug/ 158 | 159 | # PyCharm 160 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 161 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 162 | # and can be added to the global gitignore or merged into this file. For a more nuclear 163 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 164 | #.idea/ 165 | 166 | # Project files 167 | users.json 168 | messages.json 169 | gitignore 170 | test.py 171 | /audio 172 | /data/* 173 | .vscode/settings.json 174 | .VSCodeCounter/* 175 | settings.json 176 | examplesettings.json 177 | -------------------------------------------------------------------------------- /Backend/Modules/Economy/balance.py: -------------------------------------------------------------------------------- 1 | from Backend.Modules.ecoCore import Economy as EcoCore 2 | from discord.ext import commands 3 | import discord 4 | from Backend.send import send 5 | class Balance(commands.Cog): 6 | def __init__(self, bot): 7 | self.bot = bot 8 | self.db = EcoCore(bot) 9 | 10 | @commands.command(description="Check your balance", aliases=["bal"]) 11 | @commands.cooldown(1, 3, commands.BucketType.user) 12 | async def balance_cmd(self, ctx, user: discord.Member = None): 13 | if user is None: 14 | user = ctx.author 15 | else: 16 | user = await commands.UserConverter().convert(ctx, user) 17 | try: 18 | self.db.user_exists(user.id) 19 | balance = self.db.get_balance(user.id) 20 | print(f"Retrieved balance for {user} (ID: {user.id}): ${balance}") 21 | await send(self.bot, ctx, title=f"{user.display_name}'s Balance", content=f"{user.mention}'s balance is **${balance:,}**", color=0x2ECC71) 22 | except Exception as e: 23 | print(f"Error in balance command: {e}") 24 | await send(self.bot, ctx, title="Error", content="An error occurred while checking the balance.", color=0xFF0000) 25 | 26 | async def setup(bot): 27 | balance_cog = Balance(bot) 28 | balance_cmd = balance_cog.balance_cmd 29 | balance_cmd.name = "balance" 30 | bot.eco.add_command(balance_cmd) 31 | await bot.add_cog(balance_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/coinflip.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import random 3 | from discord.ext import commands 4 | from Backend.Modules.ecoCore import Economy as EcoCore 5 | from Backend.send import send 6 | choices = ["heads", "tails"] 7 | 8 | class Coinflip(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | self.db = EcoCore(bot) 12 | 13 | @commands.command(description="Flip a coin", aliases=["cf"]) 14 | async def coinflip_cmd(self, ctx, args): 15 | try: 16 | if self.db.get_balance(ctx.author.id) < int(args): 17 | await send(self.bot, ctx, title="Error", content="You don't have enough money to bet that amount.", color=0xFF0000) 18 | return 19 | 20 | if not args: 21 | await send(self.bot, ctx, title="Error", content="Please provide a bet amount.", color=0xFF0000) 22 | return 23 | parts = args.split(' ') 24 | if len(parts) < 1: 25 | await send(self.bot, ctx, title="Error", content="Please specify a bet amount! Usage: >eco coinflip [heads/tails]", color=0xFF0000) 26 | return 27 | 28 | bet = int(parts[0]) 29 | choice = parts[1].lower() if len(parts) > 1 else random.choice(choices) 30 | 31 | if choice not in choices: 32 | await send(self.bot, ctx, title="Error", content="Invalid choice! Please use 'heads' or 'tails'", color=0xFF0000) 33 | return 34 | 35 | correct = random.choice(choices) 36 | if choice == correct: 37 | self.db.add_balance(ctx.author.id, bet) 38 | await send(self.bot, ctx, title="You won!", content=f"You won the coinflip! You won **${bet}**", color=0x2ECC71) 39 | else: 40 | self.db.remove_balance(ctx.author.id, bet) 41 | await send(self.bot, ctx, title="You lost!", content=f"You lost **${bet}**! The correct choice was {correct}", color=0xE74C3C) 42 | 43 | except ValueError: 44 | await send(self.bot, ctx, title="Error", content="Invalid bet amount! Please use a number.", color=0xFF0000) 45 | except Exception as e: 46 | await send(self.bot, ctx, title="Error", content=f"Error: {str(e)}", color=0xFF0000) 47 | 48 | async def setup(bot): 49 | coinflip_cog = Coinflip(bot) 50 | coinflip_cmd = coinflip_cog.coinflip_cmd 51 | coinflip_cmd.name = "coinflip" 52 | bot.eco.add_command(coinflip_cmd) 53 | await bot.add_cog(coinflip_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/daily.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.Modules.ecoCore import Economy as EcoCore 4 | from datetime import datetime 5 | from Backend.send import send 6 | class Daily(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | self.db = EcoCore(bot) 10 | 11 | @commands.command(description="Claim your daily reward") 12 | @commands.cooldown(1, 86400, commands.BucketType.user) 13 | async def daily_cmd(self, ctx): 14 | try: 15 | data = self.db.get_cooldown(ctx.author.id, "last_daily") 16 | if data: 17 | time_diff = datetime.now().timestamp() - data 18 | if time_diff < 86400: 19 | remaining = 86400 - time_diff 20 | hours = int(remaining // 3600) 21 | minutes = int((remaining % 3600) // 60) 22 | await send(self.bot, ctx, title="Cooldown", content=f"You can claim your daily reward again in {hours} hours and {minutes} minutes!", color=0xE74C3C) 23 | return 24 | total = int(0.07 * self.db.get_balance(ctx.author.id)) 25 | self.db.add_balance(ctx.author.id, total) 26 | await self.db.daily(ctx.author.id) 27 | await send(self.bot, ctx, title="Daily Reward" ,content=f"{ctx.author.mention}, you have claimed your daily reward of **${total}**! Come back in 24 hours to claim another one!", color=0x2ECC71) 28 | 29 | except commands.CommandOnCooldown as e: 30 | hours = int(e.retry_after // 3600) 31 | minutes = int((e.retry_after % 3600) // 60) 32 | await send(self.bot, ctx, title="Cooldown", content=f"You can claim your daily reward again in {hours} hours and {minutes} minutes!", color=0xE74C3C) 33 | except Exception as e: 34 | print(f"Error in daily command: {e}") 35 | await send(self.bot, ctx, title="Error", content="An error occurred while processing the daily command.", color=0xFF0000) 36 | 37 | async def setup(bot): 38 | daily_cog = Daily(bot) 39 | daily_cmd = daily_cog.daily_cmd 40 | daily_cmd.name = "daily" 41 | bot.eco.add_command(daily_cmd) 42 | await bot.add_cog(daily_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/dice.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import random 3 | from discord.ext import commands 4 | from Backend.Modules.ecoCore import Economy as EcoCore 5 | from Backend.send import send 6 | class Dice(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | self.db = EcoCore(bot) 10 | 11 | @commands.command(description="Roll dice") 12 | async def dice_cmd(self, ctx, args): 13 | try: 14 | if self.db.get_balance(ctx.author.id) < int(args): 15 | await send(self.bot, ctx, title="Error", content="You don't have enough money to bet that amount.", color=0xFF0000) 16 | return 17 | 18 | if not args: 19 | await send(self.bot, ctx, title="Error", content= "Please provide a bet amount.", color=0xFF0000) 20 | return 21 | parts = args.split() 22 | if len(parts) < 2: 23 | await send(self.bot, ctx, title="Error", content="Usage: >eco dice \nGuess a number between 2-12", color=0xFF0000) 24 | return 25 | 26 | bet = int(parts[0]) 27 | guess = int(parts[1]) 28 | 29 | if guess < 2 or guess > 12: 30 | await send(self.bot, ctx, title="Error", content="Please choose a number between 2 and 12!", color=0xFF0000) 31 | return 32 | 33 | dice1 = random.randint(1, 6) 34 | dice2 = random.randint(1, 6) 35 | total = dice1 + dice2 36 | 37 | if total == guess: 38 | winnings = bet * (6 if guess in [2, 12] else 4 if guess in [3, 11] else 2) 39 | self.db.add_balance(ctx.author.id, winnings) 40 | await send(self.bot, ctx, title="You won!", content=f"🎲 Rolled: {dice1} + {dice2} = {total}\nYou won **${winnings}**!", color=0x2ECC71) 41 | else: 42 | self.db.remove_balance(ctx.author.id, bet) 43 | await send(self.bot, ctx, title="You lost!", content=f"🎲 Rolled: {dice1} + {dice2} = {total}\nYou lost **${bet}**!", color=0xE74C3C) 44 | 45 | except ValueError: 46 | await send(self.bot, ctx, title="Error", content="Invalid bet amount! Please use a number.", color=0xFF0000) 47 | except Exception as e: 48 | await send(self.bot, ctx, title="Error", content=f"Error: {str(e)}", color=0xFF0000) 49 | 50 | async def setup(bot): 51 | dice_cog = Dice(bot) 52 | dice_cmd = dice_cog.dice_cmd 53 | dice_cmd.name = "dice" 54 | bot.eco.add_command(dice_cmd) 55 | await bot.add_cog(dice_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/leaderboard.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.Modules.ecoCore import Economy as EcoCore 4 | 5 | class Leaderboard(commands.Cog): 6 | def __init__(self, bot): 7 | self.bot = bot 8 | self.db = EcoCore(bot) 9 | 10 | @commands.command(description="View the leaderboard") 11 | async def leaderboard(self, ctx): 12 | data = self.db.get_leaderboard() 13 | await ctx.send(data) 14 | 15 | async def setup(bot): 16 | 17 | await bot.add_cog(Leaderboard(bot)) -------------------------------------------------------------------------------- /Backend/Modules/Economy/mafia.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.Modules.ecoCore import Economy as EcoCore 4 | import random 5 | from datetime import datetime 6 | from Backend.send import send 7 | 8 | mafia_phrases = ["has helped with a drug deal", "has helped with a robbery", "has helped with a kidnapping", "has helped with a hit", "has helped with a murder", "has helped with a extortion", "has helped with a blackmail", "has helped with a prostitution ring", "has helped with a human trafficking ring", "has helped with a arms dealing", "has helped with a arms smuggling", "has helped with a arms trafficking", "has helped with a arms manufacturing", "has helped with a arms dealing", "has helped with a arms smuggling", "has helped with a arms trafficking", "has helped with a arms manufacturing"] 9 | 10 | class Mafia(commands.Cog): 11 | def __init__(self, bot): 12 | self.bot = bot 13 | self.db = EcoCore(bot) 14 | 15 | @commands.command(description="Work with the mafia") 16 | @commands.cooldown(1, 300, commands.BucketType.user) 17 | async def mafia_cmd(self, ctx): 18 | try: 19 | data = self.db.get_cooldown(ctx.author.id, "last_mafia") 20 | if data: 21 | time_diff = datetime.now().timestamp() - data 22 | if time_diff < 300: 23 | remaining = 300 - time_diff 24 | minutes = int(remaining // 60) 25 | seconds = int(remaining % 60) 26 | await send(self.bot, ctx, title="Cooldown", content=f"You need to wait {minutes} minutes and {seconds} seconds before doing another mafia job!", color=0xE74C3C) 27 | return 28 | 29 | caught = random.randint(1, 100) 30 | if caught <= 10: 31 | self.db.remove_balance(ctx.author.id, 1000) 32 | await send(self.bot, ctx, title="Failure", content=f"{ctx.author.mention} was caught by the police and was fined $1000!", color=0xE74C3C) 33 | else: 34 | money = random.randint(1000, 5000) 35 | self.db.add_balance(ctx.author.id, money) 36 | await send(self.bot, ctx, title="Success", content=f"{ctx.author.mention} {random.choice(mafia_phrases)} and earned **${money}**!", color=0x2ECC71) 37 | 38 | data = self.db.read_eco() 39 | for entry in data["users"]: 40 | if entry["user"] == str(ctx.author.id): 41 | entry["last_mafia"] = datetime.now().timestamp() 42 | self.db.write_eco(data) 43 | return 44 | 45 | except Exception as e: 46 | print(f"Error in mafia command: {e}") 47 | await send(self.bot, ctx, title="Error", content="An error occurred while processing the mafia command.", color=0xFF0000) 48 | 49 | async def setup(bot): 50 | mafia_cog = Mafia(bot) 51 | mafia_cmd = mafia_cog.mafia_cmd 52 | mafia_cmd.name = "mafia" 53 | bot.eco.add_command(mafia_cmd) 54 | await bot.add_cog(mafia_cog) 55 | -------------------------------------------------------------------------------- /Backend/Modules/Economy/roulette.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import random 3 | from discord.ext import commands 4 | from Backend.Modules.ecoCore import Economy as EcoCore 5 | from Backend.send import send 6 | 7 | class Roulette(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | self.db = EcoCore(bot) 11 | 12 | @commands.command(description="Play roulette", aliases=["rl"]) 13 | async def roulette_cmd(self, ctx, args): 14 | try: 15 | if self.db.get_balance(ctx.author.id) < int(args): 16 | await send(self.bot, ctx, title="Error", content="You don't have enough money to bet that amount.", color=0xFF0000) 17 | return 18 | 19 | if not args: 20 | await send(self.bot, ctx, title="Error", content="Please provide a bet amount.", color=0xFF0000) 21 | return 22 | parts = args.split() 23 | if len(parts) < 2: 24 | await send(self.bot, ctx, title="Error", content="Usage: >eco roulette \nChoices: red/black/green or number (0-36)", color=0xFF0000) 25 | return 26 | 27 | bet = int(parts[0]) 28 | choice = parts[1].lower() 29 | 30 | result = random.randint(0, 36) 31 | 32 | red = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36] 33 | black = [2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35] 34 | 35 | if choice.isdigit(): 36 | if int(choice) == result: 37 | winnings = bet * 35 38 | self.db.add_balance(ctx.author.id, winnings) 39 | await send(self.bot, ctx, title="You won!", content=f"🎲 The ball landed on {result}! You won **${winnings}**!", color=0x2ECC71) 40 | return 41 | elif choice == "red" and result in red: 42 | self.db.add_balance(ctx.author.id, bet) 43 | await send(self.bot, ctx, title="You Won!", content=f"🎲 The ball landed on {result} (red)! You won **${bet}**!", color=0x2ECC71) 44 | return 45 | elif choice == "black" and result in black: 46 | self.db.add_balance(ctx.author.id, bet) 47 | await send(self.bot, ctx, title="You Won!", content=f"🎲 The ball landed on {result} (black)! You won **${bet}**!", color=0x2ECC71) 48 | return 49 | elif choice == "green" and result == 0: 50 | winnings = bet * 35 51 | self.db.add_balance(ctx.author.id, winnings) 52 | await send(self.bot, ctx, title="You Won!", content=f"🎲 The ball landed on 0 (green)! You won **${winnings}**!", color=0x2ECC71) 53 | return 54 | 55 | self.db.remove_balance(ctx.author.id, bet) 56 | color = "red" if result in red else "black" if result in black else "green" 57 | await send(self.bot, ctx, title="You lost!", content=f"🎲 The ball landed on {result} ({color})! You lost **${bet}**!", color=0xE74C3C) 58 | 59 | except ValueError: 60 | await send(self.bot, ctx, title="Error", content="Invalid bet amount! Please use a number.", color=0xFF0000) 61 | except Exception as e: 62 | await send(self.bot, ctx, title="Error", content=f"Error: {str(e)}", color=0xFF0000) 63 | 64 | async def setup(bot): 65 | roulette_cog = Roulette(bot) 66 | roulette_cmd = roulette_cog.roulette_cmd 67 | roulette_cmd.name = "roulette" 68 | bot.eco.add_command(roulette_cmd) 69 | await bot.add_cog(roulette_cog) 70 | -------------------------------------------------------------------------------- /Backend/Modules/Economy/slots.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import random 3 | from discord.ext import commands 4 | from Backend.Modules.ecoCore import Economy as EcoCore 5 | from Backend.send import send 6 | 7 | class Slots(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | self.db = EcoCore(bot) 11 | self.symbols = ["🍒", "🍊", "🍋", "🍇", "💎", "7️⃣"] 12 | self.payouts = { 13 | "🍒": 2, 14 | "🍊": 2, 15 | "🍋": 3, 16 | "🍇": 4, 17 | "💎": 5, 18 | "7️⃣": 10 19 | } 20 | 21 | @commands.command(description="Play slots") 22 | async def slots_cmd(self, ctx, args): 23 | try: 24 | if self.db.get_balance(ctx.author.id) < int(args): 25 | await send(self.bot, ctx, title="Error", content="You don't have enough money to bet that amount.", color=0xFF0000) 26 | return 27 | 28 | if not args: 29 | await send(self.bot, ctx, title="Error", content="Please provide a bet amount.", color=0xFF0000) 30 | return 31 | bet = int(args) 32 | 33 | results = [random.choice(self.symbols) for _ in range(3)] 34 | 35 | if all(x == results[0] for x in results): 36 | winnings = bet * self.payouts[results[0]] 37 | self.db.add_balance(ctx.author.id, winnings) 38 | await send(self.bot, ctx, title="You won!", content=f"🎰 [{' | '.join(results)}]\nJACKPOT! You won **${winnings}**!", color=0x2ECC71) 39 | else: 40 | self.db.remove_balance(ctx.author.id, bet) 41 | await send(self.bot, ctx, title="You lost!", content=f"🎰 [{' | '.join(results)}]\nYou lost **${bet}**!", color=0xE74C3C) 42 | 43 | except ValueError: 44 | await send(self.bot, ctx, title="Error", content="Invalid bet amount! Please use a number.", color=0xFF0000) 45 | except Exception as e: 46 | await send(self.bot, ctx, title="Error", content=f"Error: {str(e)}", color=0xFF0000) 47 | 48 | async def setup(bot): 49 | slots_cog = Slots(bot) 50 | slots_cmd = slots_cog.slots_cmd 51 | slots_cmd.name = "slots" 52 | bot.eco.add_command(slots_cmd) 53 | await bot.add_cog(slots_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/steal.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.Modules.ecoCore import Economy as EcoCore 4 | import random 5 | from Backend.send import send 6 | class Steal(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | self.db = EcoCore(bot) 10 | 11 | @commands.command(description="Steal money from another user") 12 | @commands.cooldown(1, 420, commands.BucketType.user) 13 | async def steal_cmd(self, ctx, target: discord.Member): 14 | print(f"Steal command called with target: {target}") 15 | try: 16 | if target == ctx.author: 17 | await send(self.bot, ctx, title="Error", content="You can't steal from yourself!", color=0xFF0000) 18 | return 19 | 20 | target_balance = self.db.get_balance(target.id) 21 | if target_balance < 100: 22 | await send(self.bot, ctx, title="Haha Pooron!", content=f"{ctx.author.mention}, that user is too poor to steal from!", color=0xE74C3C) 23 | return 24 | 25 | steal_amount = min(random.randint(50, 200), target_balance) 26 | self.db.steal(ctx.author.id, target.id, steal_amount) 27 | await send(self.bot, ctx, title="Success!", content=f"{ctx.author.mention} has stolen **${steal_amount}** from {target.mention}!", color=0x2ECC71) 28 | 29 | except Exception as e: 30 | print(f"Error in steal command: {e}") 31 | await send(self.bot, ctx, title="Error", content="An error occurred while processing the steal command.", color=0xFF0000) 32 | 33 | 34 | async def setup(bot): 35 | steal_cog = Steal(bot) 36 | steal_cmd = steal_cog.steal_cmd 37 | steal_cmd.name = "steal" 38 | bot.eco.add_command(steal_cmd) 39 | await bot.add_cog(steal_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/store.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.Modules.ecoCore import Economy as EcoCore 4 | from Backend.send import send 5 | 6 | class Store(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | self.db = EcoCore(bot) 10 | 11 | @commands.command(description="Shop for items") 12 | async def shop(self, ctx): 13 | await send(self.bot, ctx, title="Shop", content="Coming soon!", color=0xFF0000) 14 | 15 | async def setup(bot): 16 | store_cog = Store(bot) 17 | store_cmd = store_cog.shop 18 | store_cmd.name = "shop" 19 | bot.eco.add_command(store_cmd) 20 | await bot.add_cog(store_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/stripper.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.Modules.ecoCore import Economy as EcoCore 4 | import random 5 | from datetime import datetime 6 | from Backend.send import send 7 | 8 | performance = [1, 2, 3] 9 | 10 | class Stripper(commands.Cog): 11 | def __init__(self, bot): 12 | self.bot = bot 13 | self.db = EcoCore(bot) 14 | 15 | @commands.command(description="Work as a stripper") 16 | @commands.cooldown(1, 300, commands.BucketType.user) 17 | async def stripper_cmd(self, ctx): 18 | print("Stripper command called") 19 | try: 20 | data = self.db.get_cooldown(ctx.author.id, "last_stripper") 21 | if data: 22 | time_diff = datetime.now().timestamp() - data 23 | if time_diff < 300: 24 | remaining = 300 - time_diff 25 | minutes = int(remaining // 60) 26 | seconds = int(remaining % 60) 27 | await send(self.bot, ctx, title="Cooldown", content=f"You need to wait {minutes} minutes and {seconds} seconds before stripping again!", color=0xE74C3C) 28 | return 29 | 30 | performance_multiplier = random.choice(performance) 31 | if performance_multiplier == 1: 32 | self.db.remove_balance(ctx.author.id, 100) 33 | await send(self.bot, ctx, title="Poor Performance :c", content=f"{ctx.author.mention} worked as a stripper, but didn't perform well and lost $100!", color=0xE74C3C) 34 | elif performance_multiplier == 2: 35 | money = random.randint(100, 300) 36 | self.db.add_balance(ctx.author.id, money) 37 | await send(self.bot, ctx, title="Alrightttt", content=f"{ctx.author.mention} worked as a stripper, and performed alright, earning **${money}**!", color=0xFEE75C) 38 | else: 39 | money = random.randint(300, 500) 40 | self.db.add_balance(ctx.author.id, money) 41 | await send(self.bot, ctx, title="You did great!", content=f"{ctx.author.mention} has worked as a stripper and did excellently, earning **${money}**!", color=0x2ECC71) 42 | 43 | data = self.db.read_eco() 44 | for entry in data["users"]: 45 | if entry["user"] == str(ctx.author.id): 46 | entry["last_stripper"] = datetime.now().timestamp() 47 | self.db.write_eco(data) 48 | return 49 | 50 | except Exception as e: 51 | print(f"Error in stripper command: {e}") 52 | await send(self.bot, ctx, title="Error", content="An error occurred while processing the stripper command.", color=0xFF0000) 53 | 54 | async def setup(bot): 55 | stripper_cog = Stripper(bot) 56 | stripper_cmd = stripper_cog.stripper_cmd 57 | bot.eco.add_command(stripper_cmd) 58 | await bot.add_cog(stripper_cog) -------------------------------------------------------------------------------- /Backend/Modules/Economy/work.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.Modules.ecoCore import Economy as EcoCore 4 | import random 5 | from datetime import datetime 6 | from Backend.send import send 7 | 8 | jobs = ["Amazon", "Walmart", "Target", "Costco", "Home Depot", "Lowe's", "Kroger", "CVS", "Walgreens", "Taco Bell", "McDonald's", "Burger King", "Starbucks", "Subway", "Pizza Hut", "Taco Bell", "McDonald's", "Burger King", "Starbucks", "Subway", "Pizza Hut"] 9 | 10 | class Work(commands.Cog): 11 | def __init__(self, bot): 12 | self.bot = bot 13 | self.db = EcoCore(bot) 14 | 15 | @commands.command(description="Work for money") 16 | @commands.cooldown(1, 420, commands.BucketType.user) 17 | async def work_cmd(self, ctx): 18 | print("Work command called") 19 | try: 20 | data = self.db.get_cooldown(ctx.author.id, "last_work") 21 | if data: 22 | time_diff = datetime.now().timestamp() - data 23 | if time_diff < 420: 24 | remaining = 420 - time_diff 25 | minutes = int(remaining // 60) 26 | seconds = int(remaining % 60) 27 | await send(self.bot, ctx, title="Cooldown", content=f"You need to wait {minutes} minutes and {seconds} seconds before working again!", color=0xE74C3C) 28 | return 29 | 30 | earned = random.randint(100, 500) 31 | self.db.add_balance(ctx.author.id, earned) 32 | self.db.work(ctx.author.id) 33 | job = random.choice(jobs) 34 | hours = random.randint(1, 8) 35 | await send(self.bot, ctx, title="Success", content=f"{ctx.author.mention} has worked at {job} for {hours} hours and earned **${earned}**!", color=0x2ECC71) 36 | 37 | except Exception as e: 38 | print(f"Error in work command: {e}") 39 | await send(self.bot, ctx, title="Error", content="An error occurred while processing the work command.", color=0xFF0000) 40 | 41 | async def setup(bot): 42 | work_cog = Work(bot) 43 | work_cmd = work_cog.work_cmd 44 | work_cmd.name = "work" 45 | bot.eco.add_command(work_cmd) 46 | await bot.add_cog(work_cog) -------------------------------------------------------------------------------- /Backend/Modules/Moderation/ban.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import discord.ext.commands as commands 3 | from Backend.utils import check_permissions, write_punishment, read_punishment 4 | class Ban(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | @commands.command(description="Ban a user from the server") 8 | async def ban(self, ctx, user: discord.Member, *, reason=None): 9 | if not check_permissions(ctx.author): 10 | return 11 | punishment_data = read_punishment() 12 | punishment_data["users"].append({ 13 | "id": str(user.user_id), 14 | "Reason": reason, 15 | "Type": "Ban" 16 | }) 17 | await user.kick(reason=reason) 18 | write_punishment(punishment_data) 19 | await ctx.send(f"Kicked {user.mention}") 20 | async def setup(bot): 21 | ban = Ban(bot) 22 | ban_cmd = ban.ban_cmd 23 | ban_cmd.name = "kick" 24 | bot.moderation.add_command(ban_cmd) 25 | await bot.add_cog(Ban) -------------------------------------------------------------------------------- /Backend/Modules/Moderation/kick.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import discord.ext.commands as commands 3 | from Backend.utils import check_permissions, write_punishment, read_punishment 4 | class Kick(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | @commands.command(description="Kick a user from the server") 8 | async def kick(self, ctx, user: discord.Member, *, reason=None): 9 | if not check_permissions(ctx.author): 10 | return 11 | punishment_data = read_punishment() 12 | punishment_data["users"].append({ 13 | "id": str(user.user_id), 14 | "Reason": reason, 15 | "Type": "Kick" 16 | }) 17 | await user.kick(reason=reason) 18 | write_punishment(punishment_data) 19 | await ctx.send(f"Kicked {user.mention}") 20 | async def setup(bot): 21 | Kick = Kick(bot) 22 | kick_cmd = Kick.kick_cmd 23 | kick_cmd.name = "kick" 24 | bot.moderation.add_command(kick_cmd) 25 | await bot.add_cog(Kick) -------------------------------------------------------------------------------- /Backend/Modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnarkyDeveloper/Selfbot/bbca365cffcc0cf2b00ee7e87fbf7044995a6fb0/Backend/Modules/__init__.py -------------------------------------------------------------------------------- /Backend/Modules/afk.py: -------------------------------------------------------------------------------- 1 | import json 2 | from discord.ext import commands 3 | import os 4 | import time 5 | from Backend.send import send 6 | 7 | path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 8 | 9 | try: 10 | if not os.path.exists(f'{path}/data/users/afk.json'): 11 | os.makedirs(f'{path}/data/users', exist_ok=True) 12 | with open(f'{path}/data/users/afk.json', 'w') as f: 13 | json.dump({}, f) 14 | except FileNotFoundError: 15 | os.makedirs(f'{path}/data/users', exist_ok=True) 16 | with open(f'{path}/data/users/afk.json', 'w') as f: 17 | json.dump({}, f) 18 | 19 | class Afk(commands.Cog): 20 | def __init__(self, bot): 21 | self.bot = bot 22 | 23 | @commands.command(name='afk', aliases=['brb'], description='Go AFK') 24 | async def afk(self, ctx, reason=None): 25 | reason = reason or "AFK" 26 | 27 | with open(f'{path}/data/users/afk.json', 'r') as f: 28 | afk_data = json.load(f) 29 | 30 | if not ctx.guild.id: 31 | guild = 'DM' 32 | else: 33 | guild = ctx.guild.id 34 | if str(guild) not in afk_data: 35 | afk_data[str(guild)] = {} 36 | afk_data[str(guild)][str(ctx.author.id)] = { 37 | 'reason': reason, 38 | 'timestamp': time.time() * 1000 39 | } 40 | 41 | with open(f'{path}/data/users/afk.json', 'w') as f: 42 | json.dump(afk_data, f) 43 | await send(self.bot, ctx, title='AFK', content=f'{ctx.author.mention} is now AFK: {reason}', color=0x2ECC71) 44 | 45 | @commands.Cog.listener() 46 | async def on_message(self, message): 47 | if message.author.bot: 48 | return 49 | ctx = await self.bot.get_context(message) 50 | 51 | with open(f'{path}/data/users/afk.json', 'r') as f: 52 | afk_data = json.load(f) 53 | 54 | if str(message.author.id) in afk_data.get(str(message.guild.id), {}): 55 | if not message.content.lower().startswith('>afk') and not message.content.lower().startswith('>brb'): 56 | afk_time = round((time.time() * 1000 - afk_data[str(message.guild.id)][str(message.author.id)]['timestamp']) / 1000) 57 | del afk_data[str(message.guild.id)][str(message.author.id)] 58 | minutes, seconds = divmod(afk_time, 60) 59 | hours, minutes = divmod(minutes, 60) 60 | try: 61 | deletable = await send(self.bot, ctx, title='AFK', content=f"Welcome back {message.author.mention}! You were AFK for {hours} hours, {minutes} minutes, and {seconds} seconds.") 62 | except Exception as e: 63 | print(f"Error: {e}") 64 | with open(f'{path}/data/users/afk.json', 'w') as f: 65 | json.dump(afk_data, f) 66 | 67 | await deletable.delete(delay=15) 68 | if message.mentions: 69 | for user in message.mentions: 70 | if str(user.id) in afk_data.get(str(message.guild.id), {}): 71 | reason = afk_data[str(message.guild.id)][str(user.id)]['reason'] 72 | await send(self.bot, ctx, title='AFK', content=f"{user.name} is AFK with reason: {reason}") 73 | 74 | async def setup(bot): 75 | await bot.add_cog(Afk(bot)) 76 | -------------------------------------------------------------------------------- /Backend/Modules/ai.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | import asyncio 4 | import concurrent.futures 5 | from diffusers import StableDiffusionPipeline 6 | import os 7 | import torch 8 | from torch.amp import autocast 9 | from transformers import AutoModelForCausalLM, AutoTokenizer 10 | from Backend.send import send 11 | 12 | torch.backends.cudnn.benchmark = True 13 | torch.backends.cudnn.enabled = True 14 | 15 | class AI(commands.Cog): 16 | def __init__(self, bot): 17 | self.bot = bot 18 | self.message_history = {} 19 | torch.cuda.empty_cache() 20 | 21 | self.offload_folder = './offload' 22 | 23 | if not os.path.exists(self.offload_folder): 24 | os.makedirs(self.offload_folder) 25 | 26 | self.model, self.tokenizer = self.initialize_model_tokenizer() 27 | 28 | def initialize_model_tokenizer(self): 29 | model_name = "openlm-research/open_llama_3b" 30 | 31 | try: 32 | tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False) 33 | tokenizer.pad_token = tokenizer.eos_token 34 | 35 | model = AutoModelForCausalLM.from_pretrained( 36 | model_name, 37 | torch_dtype=torch.float16, 38 | device_map="auto", 39 | offload_folder=self.offload_folder 40 | ) 41 | return model, tokenizer 42 | 43 | except Exception as e: 44 | raise ValueError(f"Error initializing model and tokenizer: {e}") 45 | 46 | def generate_response(self, user_id, question): 47 | if user_id not in self.message_history: 48 | self.message_history[user_id] = [] 49 | 50 | self.message_history[user_id].append({"role": "user", "content": question}) 51 | if len(self.message_history[user_id]) > 5: 52 | self.message_history[user_id] = self.message_history[user_id][-5:] 53 | 54 | messages = "\n".join(f"{msg['role']}: {msg['content']}" for msg in self.message_history[user_id]) 55 | 56 | inputs = self.tokenizer(messages, return_tensors="pt", padding=True, truncation=True).to(self.model.device) 57 | outputs = self.model.generate( 58 | **inputs, max_length=512, do_sample=True, top_p=0.95, temperature=0.7, pad_token_id=self.tokenizer.eos_token_id 59 | ) 60 | response = self.tokenizer.decode(outputs[0], skip_special_tokens=True) 61 | self.message_history[user_id].append({"role": "assistant", "content": response}) 62 | return response 63 | 64 | async def send_response(self, ctx, question, response): 65 | if len(response) > 4096: 66 | chunks = [response[i:i+4096] for i in range(0, len(response), 4096)] 67 | for chunk in chunks: 68 | await send(self.bot, ctx, title=f'{question}', content=chunk, color=0x2ECC71) 69 | else: 70 | await send(self.bot, ctx, title=f'{question}', content=response, color=0x2ECC71) 71 | 72 | @commands.command(description="Ask an AI a question") 73 | async def ask(self, ctx, question): 74 | user_id = ctx.author.id 75 | loading_message = await send(self.bot, ctx, title='Generating response', content="Please wait while the bot generates a response", color=0x2ECC71) 76 | 77 | try: 78 | with concurrent.futures.ThreadPoolExecutor() as pool: 79 | async with ctx.typing(): 80 | response = await asyncio.get_event_loop().run_in_executor( 81 | pool, 82 | lambda: self.generate_response(user_id, question) 83 | ) 84 | print(response) 85 | await self.send_response(ctx, question, response) 86 | 87 | except Exception as e: 88 | await send(self.bot, ctx, title='Error', content=f"An error occurred generating the response: {str(e)}", color=0xFF0000) 89 | 90 | finally: 91 | await loading_message.delete() 92 | 93 | @commands.command(description="Generate an image", aliases=["img", "imagine"]) 94 | async def generate(self, ctx, prompt): 95 | loading_message = await send(self.bot, ctx, title='Generating image', content="Generating image... This may take a while. You'll be notified when it's done.") 96 | 97 | try: 98 | with concurrent.futures.ThreadPoolExecutor() as pool: 99 | try: 100 | with autocast('cuda'): 101 | image = await asyncio.get_event_loop().run_in_executor( 102 | pool, 103 | lambda: self.pipeline(prompt, num_inference_steps=15).images[0] 104 | ) 105 | except torch.cuda.OutOfMemoryError: 106 | print("Not enough GPU memory. Trying CPU fallback.") 107 | self.pipeline.to('cpu') 108 | 109 | await loading_message.delete() 110 | image_path = f'{ctx.author.id}.png' 111 | image.save(image_path) 112 | await send(self.bot, ctx, title=f'Image generated!', content=f'{prompt} | Generated by {ctx.author.mention}', image=discord.File(image_path)) 113 | os.remove(image_path) 114 | torch.cuda.empty_cache() 115 | 116 | except Exception as e: 117 | await send(self.bot, ctx, title='Error', content=f"An error occurred: {str(e)}", color=0xFF0000) 118 | finally: 119 | await loading_message.delete() 120 | 121 | async def setup(bot): 122 | await bot.add_cog(AI(bot)) -------------------------------------------------------------------------------- /Backend/Modules/avatar.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.send import send 4 | class Avatar(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | @commands.command(description="Get the avatar of a user", aliases=['av']) 8 | async def avatar(self, ctx, user: discord.Member = None): 9 | if not user: 10 | user = ctx.author 11 | else: 12 | user = await commands.UserConverter().convert(ctx, user) 13 | await send(self.bot, ctx, title=f'{user.display_name}\'s Avatar', image=user.avatar.url) 14 | 15 | @commands.command(name="sv", description="Get the avatar of the server", aliases=["as", "sav"]) 16 | async def sv(self, ctx, user: discord.Member = None): 17 | if not user: 18 | user = ctx.author 19 | else: 20 | user = await commands.UserConverter().convert(ctx, user) 21 | if user.display_avatar != user.avatar: 22 | guild_avatar = user.display_avatar 23 | if guild_avatar: 24 | await send(self.bot, ctx, title="Server Avatar", image=user.guild_avatar.url) 25 | else: 26 | await send(self.bot, ctx, title=f'Error', content="The server doesn't have a server specific avatar!", color=0xFF0000) 27 | @commands.command(name="banner", description="Get the banner of a user") 28 | async def banner(self, ctx, user: discord.Member = None): 29 | if not user: 30 | user = str(ctx.author.id) 31 | user = await commands.UserConverter().convert(ctx, user) 32 | if user.banner: 33 | await send(self.bot, ctx, title=f'{user.display_name}\'s Banner', image=user.banner.url) 34 | else: 35 | await send(self.bot, ctx, title=f'Error', content="The user doesn't have a banner!", color=0xFF0000) 36 | 37 | async def setup(bot): 38 | await bot.add_cog(Avatar(bot)) 39 | -------------------------------------------------------------------------------- /Backend/Modules/calculator.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | import sympy 4 | from sympy.abc import x, y, z 5 | from Backend.send import send 6 | class Calculator(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.command(name='calc', description='Calculate mathematical expressions') 11 | async def calculate(self, ctx, expression): 12 | try: 13 | expression = expression.replace('^', '**') 14 | 15 | safe_dict = {"x": x, "y": y, "z": z} 16 | result = sympy.sympify(expression, locals=safe_dict) 17 | 18 | simplified = sympy.simplify(result) 19 | 20 | try: 21 | numeric_result = float(simplified.evalf()) 22 | if numeric_result.is_integer(): 23 | await send(self.bot, ctx, title="Result", content=f"{int(numeric_result)}", color=0x2ECC71) 24 | else: 25 | await send(self.bot, ctx, title="Result", content=f"{numeric_result}", color=0x2ECC71) 26 | except: 27 | await send(self.bot, ctx, title="Result", content=f"{simplified}", color=0x2ECC71) 28 | 29 | except sympy.SympifyError: 30 | await send(self.bot, ctx, title="Calculator Error", content="Error: Invalid mathematical expression.", color=0xff0000) 31 | except Exception as e: 32 | await send(self.bot, ctx, title="Calculator Error", content=f"Error: {str(e)}", color=0xff0000) 33 | 34 | 35 | async def setup(bot): 36 | await bot.add_cog(Calculator(bot)) -------------------------------------------------------------------------------- /Backend/Modules/choose.py: -------------------------------------------------------------------------------- 1 | import random 2 | from discord.ext import commands 3 | from Backend.send import send 4 | class Choose(commands.Cog): 5 | def __init__(self, bot): 6 | 7 | self.bot = bot 8 | 9 | @commands.command(description='For when you wanna settle the score some other way') 10 | async def choose(self, ctx, choices: str): 11 | choice_list = choices.split() 12 | await send(self.bot, ctx, title='Choice!', content=random.choice(choice_list)) 13 | async def setup(bot): 14 | await bot.add_cog(Choose(bot)) -------------------------------------------------------------------------------- /Backend/Modules/dictionary.py: -------------------------------------------------------------------------------- 1 | import re 2 | from discord.ext import commands 3 | from PyMultiDictionary import MultiDictionary 4 | dictionary = MultiDictionary() 5 | from Backend.send import send 6 | class Define(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.command(description='Define a word', aliases=['def']) 11 | async def define(self, ctx, word=None): 12 | if ctx.message.reference: 13 | ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 14 | word = word or ref_message.content.strip() 15 | elif word is None: 16 | await send(self.bot, ctx, title='Error', content="Please reply to a message or provide a word.", color=0xFF0000) 17 | return 18 | 19 | try: 20 | definition = dictionary.meaning('en', word) 21 | except Exception as e: 22 | await send(self.bot, ctx, title='Error', content=f"An error occurred while fetching the definition: {e}", color=0xFF0000) 23 | return 24 | 25 | if not definition or not definition[1]: 26 | await send(self.bot, ctx, title='Error', content="Definition not found.", color=0xFF0000) 27 | else: 28 | word_type = '/'.join(c for c in definition[0] if c.isalpha() or c.isspace()) 29 | await send(self.bot, ctx, title=word.title(), content=f'**Type:** {word_type}\n\n**Definition:** {definition[1]}', color=0x2ECC71) 30 | 31 | async def setup(bot): 32 | await bot.add_cog(Define(bot)) -------------------------------------------------------------------------------- /Backend/Modules/ecoCore.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from datetime import datetime 4 | from contextlib import contextmanager 5 | import asyncio 6 | 7 | def read_eco(): 8 | try: 9 | with open('data/economy/eco.json', 'r', encoding='utf-8') as file: 10 | return json.load(file) 11 | except FileNotFoundError: 12 | default_data = {"users": []} 13 | write_eco(default_data) 14 | return default_data 15 | 16 | def write_eco(data): 17 | os.makedirs('data/economy', exist_ok=True) 18 | with open('data/economy/eco.json', 'w', encoding='utf-8') as file: 19 | json.dump(data, file, indent=4, ensure_ascii=False) 20 | 21 | class Economy: 22 | def __init__(self, bot): 23 | self.bot = bot 24 | self.lock = asyncio.Lock() 25 | self.setup() 26 | 27 | def setup(self): 28 | os.makedirs("data/economy", exist_ok=True) 29 | # Initialize file if it doesn't exist 30 | if not os.path.exists("data/economy/eco.json"): 31 | write_eco({"users": []}) 32 | 33 | def get_cooldown(self, user_id, cooldown_type): 34 | data = read_eco() 35 | for entry in data["users"]: 36 | if entry["user"] == str(user_id): 37 | return entry.get(cooldown_type, 0) # Returns 0 if cooldown doesn't exist 38 | return 0 39 | 40 | def get_balance(self, user_id): 41 | data = read_eco() 42 | for entry in data["users"]: 43 | if entry["user"] == str(user_id): 44 | return entry["balance"] 45 | return 0 46 | 47 | def user_exists(self, user_id): 48 | data = read_eco() 49 | for entry in data["users"]: 50 | if entry["user"] == str(user_id): 51 | return True 52 | 53 | user_entry = { 54 | "user": str(user_id), 55 | "balance": 0, 56 | "last_daily": 0, 57 | "last_work": 0, 58 | "last_steal": 0, 59 | "last_stripper": 0, 60 | "last_mafia": 0 61 | } 62 | data["users"].append(user_entry) 63 | write_eco(data) 64 | return False 65 | 66 | def add_balance(self, user_id, amount): 67 | data = read_eco() 68 | for entry in data["users"]: 69 | if entry["user"] == str(user_id): 70 | entry["balance"] += amount 71 | write_eco(data) 72 | return 73 | self.user_exists(user_id) 74 | self.add_balance(user_id, amount) 75 | def remove_balance(self, user_id, amount): 76 | data = read_eco() 77 | for entry in data["users"]: 78 | if entry["user"] == str(user_id): 79 | entry["balance"] = max(0, entry["balance"] - amount) # Prevent negative balance 80 | write_eco(data) 81 | return 82 | self.user_exists(user_id) 83 | self.remove_balance(user_id, amount) 84 | 85 | async def daily(self, user_id): 86 | data = read_eco() 87 | for entry in data["users"]: 88 | if entry["user"] == str(user_id): 89 | entry["last_daily"] = datetime.now().timestamp() 90 | write_eco(data) 91 | return 92 | self.user_exists(user_id) 93 | await self.daily(user_id) 94 | 95 | def work(self, user_id): 96 | data = read_eco() 97 | for entry in data["users"]: 98 | if entry["user"] == str(user_id): 99 | entry["last_work"] = datetime.now().timestamp() 100 | write_eco(data) 101 | return 102 | self.user_exists(user_id) 103 | self.work(user_id) 104 | 105 | def steal(self, thief_id, target_id, amount): 106 | data = read_eco() 107 | thief_found = False 108 | target_found = False 109 | 110 | for entry in data["users"]: 111 | if entry["user"] == str(thief_id): 112 | entry["last_steal"] = datetime.now().timestamp() 113 | entry["balance"] += amount 114 | thief_found = True 115 | elif entry["user"] == str(target_id): 116 | entry["balance"] = max(0, entry["balance"] - amount) 117 | target_found = True 118 | 119 | if thief_found and target_found: 120 | write_eco(data) 121 | return 122 | 123 | if not thief_found: 124 | self.user_exists(thief_id) 125 | if not target_found: 126 | self.user_exists(target_id) 127 | self.steal(thief_id, target_id, amount) 128 | 129 | def leaderboard(self, limit=10): 130 | data = read_eco() 131 | # Sort users by balance in descending order 132 | sorted_users = sorted(data["users"], key=lambda x: x["balance"], reverse=True) 133 | return sorted_users[:limit] 134 | 135 | def read_eco(self): 136 | return read_eco() 137 | 138 | def write_eco(self, data): 139 | write_eco(data) 140 | 141 | async def setup(bot): 142 | eco_cog = Economy(bot) 143 | eco_cmd = eco_cog.eco_cmd 144 | eco_cmd.name = "eco" 145 | bot.eco.add_command(eco_cmd) 146 | await bot.add_cog(eco_cog) -------------------------------------------------------------------------------- /Backend/Modules/github.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | import httpx 4 | import json 5 | from Backend.send import send 6 | class github(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | @commands.command(Description="Get information on a github repository", aliases=["gh"]) 10 | async def github(self, ctx, user: str = None, repo: str = None): 11 | if not user: 12 | await ctx.send(f'Not in user/repo format! Example: `>github snarkydeveloper/selfbot`') 13 | if not repo: 14 | user = user.split('/') 15 | repo = user[1] 16 | user = user[0] 17 | response = httpx.get(f"https://api.github.com/repos/{user}/{repo}") 18 | if str(response.status_code) != '200': 19 | await ctx.send(f'Repository not found!') 20 | return 21 | response = json.loads(response.text) 22 | repo_name = response['name'] 23 | repo_description = response['description'] if response['description']=='null' else 'No description' 24 | repo_url = response['html_url'] 25 | repo_owner= response['owner']['login'] 26 | repo_owner_url = response['owner']['html_url'] 27 | repo_stars = response['stargazers_count'] 28 | repo_top_language = response['language'] 29 | full_repo_name = response['full_name'] 30 | await send(self.bot, ctx, title=f'{repo_name}', content=f'[{full_repo_name}](<{repo_url}>)\nDescription | {repo_description}\nOwner | [{repo_owner}](<{repo_owner_url}>)\nStars | {repo_stars} :star: \nTop Language | {repo_top_language}') 31 | async def setup(bot): 32 | await bot.add_cog(github(bot)) -------------------------------------------------------------------------------- /Backend/Modules/help.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.send import send 4 | class Help(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | 8 | @commands.command(name='help', description="Shows this message") 9 | async def help_command(self, ctx, page = 1): 10 | try: 11 | page = int(page) 12 | commands_list = [f"`{command.name}`: {command.description}" for command in self.bot.commands] 13 | end = page * 10 14 | start = end - 10 15 | page_commands = commands_list[start:end] 16 | response = "Available commands:\n" + "\n".join(page_commands) 17 | await send(self.bot, ctx, title=f'Help Page {page}', content=response) 18 | except Exception as e: 19 | print(f"Error in help command: {e}") 20 | 21 | async def setup(bot): 22 | await bot.add_cog(Help(bot)) 23 | 24 | -------------------------------------------------------------------------------- /Backend/Modules/lyrics.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | import json 4 | from urllib.parse import quote_plus 5 | import lyrical 6 | from Backend.send import send 7 | def split_string_into_list(input_string, max_length): 8 | lyrics = [] 9 | while len(input_string) > max_length: 10 | split_index = input_string.rfind('\n', 0, max_length) 11 | if split_index == -1: 12 | split_index = max_length 13 | lyrics.append(input_string[:split_index].strip()) 14 | input_string = input_string[split_index:].lstrip('\n') 15 | lyrics.append(input_string.strip()) 16 | return lyrics 17 | 18 | class Lyrics(commands.Cog): 19 | def __init__(self, bot): 20 | self.bot = bot 21 | 22 | @commands.command(description="Get lyrics for a song") 23 | async def lyrics(self, ctx, query: str): 24 | if not query: 25 | await ctx.send("Please provide a song name.") 26 | return 27 | lyrics_data = await lyrical.Lyrics.lyrics(query) 28 | if not lyrics_data: 29 | await send(self.bot, ctx, title='Error', content="No lyrics found.", color=0xff0000) 30 | return 31 | else: 32 | lyrics_data = json.loads(lyrics_data) 33 | title = lyrics_data['title'] 34 | artist = lyrics_data['artists'] 35 | lyrics = lyrics_data['lyrics'] 36 | try: 37 | await send(self.bot, ctx, title=f'Lyrics of {title.strip()} by {artist.strip()}', content=lyrics) 38 | except: 39 | send(self.bot, ctx, content='Issue retrieving lyrics', title='Error', color=0xff0000) 40 | 41 | async def setup(bot): 42 | await bot.add_cog(Lyrics(bot)) 43 | -------------------------------------------------------------------------------- /Backend/Modules/music.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import asyncio 3 | import os 4 | import yt_dlp 5 | import glob 6 | from discord.ext import commands 7 | from ytmusicapi import YTMusic 8 | from Backend.send import send 9 | from typing import Optional, Tuple 10 | 11 | class Music(commands.Cog): 12 | def __init__(self, bot): 13 | self.bot = bot 14 | self.ytmusic = YTMusic() 15 | self.queues = {} 16 | self.loops = {} 17 | self.current_tracks = {} 18 | self.now_playing_messages = {} 19 | self.audio_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'audio') 20 | os.makedirs(self.audio_dir, exist_ok=True) 21 | self.volume = 100 22 | 23 | async def search_yt(self, query: str) -> Tuple[Optional[dict], Optional[str]]: 24 | try: 25 | search_results = self.ytmusic.search(query, filter="songs") 26 | if not search_results: 27 | return None, "No results found." 28 | 29 | video_id = search_results[0]['videoId'] 30 | ydl_opts = { 31 | 'quiet': True, 32 | 'no_warnings': True, 33 | 'extract_flat': True 34 | } 35 | 36 | with yt_dlp.YoutubeDL(ydl_opts) as ydl: 37 | info = ydl.extract_info(f"https://www.youtube.com/watch?v={video_id}", download=False) 38 | return info, None 39 | 40 | except Exception as e: 41 | return None, f"Search error: {str(e)}" 42 | 43 | async def download_song(self, query: str, ctx) -> Tuple[Optional[dict], Optional[str], Optional[str]]: 44 | try: 45 | info, error = await self.search_yt(query) 46 | if error: 47 | return None, error, None 48 | 49 | safe_title = f"{info['title']}-{info['uploader']}".replace(' ', '_').encode("ascii", errors="ignore").decode() 50 | file_path = os.path.join(self.audio_dir, f"{safe_title}.mp3") 51 | 52 | if not os.path.exists(file_path): 53 | ydl_opts = { 54 | 'format': 'bestaudio/best', 55 | 'postprocessors': [{ 56 | 'key': 'FFmpegExtractAudio', 57 | 'preferredcodec': 'mp3', 58 | 'preferredquality': '192', 59 | }], 60 | 'outtmpl': file_path[:-4], 61 | 'no_warnings': True, 62 | 'extract_audio': True, 63 | 'audio_format': 'mp3', 64 | 'prefer_ffmpeg': True 65 | } 66 | 67 | async with ctx.typing(): 68 | with yt_dlp.YoutubeDL(ydl_opts) as ydl: 69 | ydl.download([info['webpage_url']]) 70 | 71 | await self._clean_old_files() #change max_files to whatever 72 | return info, None, file_path 73 | 74 | except Exception as e: 75 | return None, f"Download error: {str(e)}", None 76 | 77 | def _get_queue_id(self, ctx) -> str: 78 | return str(ctx.guild.id if ctx.guild else ctx.author.id) 79 | 80 | def _get_queue(self, ctx) -> list: 81 | queue_id = self._get_queue_id(ctx) 82 | if queue_id not in self.queues: 83 | self.queues[queue_id] = [] 84 | return self.queues[queue_id] 85 | 86 | async def _clean_old_files(self, max_files=50): 87 | try: 88 | audio_files = glob.glob(os.path.join(self.audio_dir, '*.mp3')) 89 | while len(audio_files) > max_files: 90 | oldest_file = min(audio_files, key=os.path.getctime) 91 | try: 92 | os.remove(oldest_file) 93 | audio_files = glob.glob(os.path.join(self.audio_dir, '*.mp3')) 94 | except OSError: 95 | break 96 | except Exception: 97 | pass 98 | 99 | async def connect_to_voice(self, ctx) -> Optional[discord.VoiceClient]: 100 | if not ctx.author.voice: 101 | await send(self.bot, ctx, title="Error", content="Join a voice channel first!", color=0xFF0000) 102 | return None 103 | 104 | target_channel = ctx.author.voice.channel 105 | target_guild = ctx.guild if ctx.guild else target_channel.guild 106 | 107 | for vc in self.bot.voice_clients: 108 | if vc.guild == target_guild: 109 | await vc.disconnect(force=True) 110 | 111 | try: 112 | return await target_channel.connect() 113 | except Exception as e: 114 | await send(self.bot, ctx, title="Error", content=f"Failed to connect: {str(e)}", color=0xFF0000) 115 | return None 116 | 117 | @commands.command(description="Play music") 118 | async def play(self, ctx, query: str): 119 | queue = self._get_queue(ctx) 120 | voice_client = discord.utils.get(self.bot.voice_clients, guild=ctx.guild if ctx.guild else None) 121 | 122 | if not voice_client: 123 | voice_client = await self.connect_to_voice(ctx) 124 | if not voice_client: 125 | return 126 | 127 | await ctx.message.add_reaction('🔎') 128 | info, error, file_path = await self.download_song(query, ctx) 129 | 130 | if error: 131 | await send(self.bot, ctx, title="Error", content=error, color=0xFF0000) 132 | await ctx.message.remove_reaction('🔎', self.bot.user) 133 | if not queue: 134 | await voice_client.disconnect() 135 | return 136 | 137 | artist = info.get('uploader', 'Unknown') 138 | if artist.endswith("- Topic"): 139 | artist = artist[:-8].strip() 140 | queue_entry = (file_path, info['title'], artist) 141 | queue.append(queue_entry) 142 | await ctx.message.remove_reaction('🔎', self.bot.user) 143 | if not voice_client.is_playing(): 144 | await self._play_next(ctx, voice_client) 145 | else: 146 | await send(self.bot, ctx, title="Added to Queue", 147 | content=f"🎵 Added {info['title']} by {artist}", color=0x2ECC71) 148 | 149 | async def _play_next(self, ctx, voice_client: discord.VoiceClient): 150 | if not voice_client or not voice_client.is_connected(): 151 | return 152 | 153 | queue = self._get_queue(ctx) 154 | queue_id = self._get_queue_id(ctx) 155 | if queue_id in self.now_playing_messages: 156 | try: 157 | await self.now_playing_messages[queue_id].delete() 158 | except: 159 | pass 160 | if self.loops.get(queue_id, False) and self.current_tracks.get(queue_id): 161 | title, author = self.current_tracks[queue_id] 162 | safe_title = f"{title}-{author}".replace(' ', '_').encode("ascii", errors="ignore").decode() 163 | file_path = os.path.join(self.audio_dir, f"{safe_title}.mp3") 164 | queue.append((file_path, title, author)) 165 | 166 | if not queue: 167 | if not self.loops.get(queue_id, False): 168 | await voice_client.disconnect() 169 | self.current_tracks[queue_id] = None 170 | return 171 | 172 | file_path, title, author = queue[0] 173 | if self.loops.get(queue_id, False): 174 | queue.append(queue.pop(0)) 175 | else: 176 | queue.pop(0) 177 | 178 | self.current_tracks[queue_id] = (title, author) 179 | 180 | try: 181 | audio_source = discord.FFmpegPCMAudio( 182 | file_path, 183 | options=f'-vn -b:a 128k -bufsize 64k -ar 48000 -filter:a "volume={self.volume/100}"' 184 | ) 185 | 186 | voice_client.play( 187 | audio_source, 188 | after=lambda e: asyncio.run_coroutine_threadsafe( 189 | self._handle_playback_error(e, ctx, voice_client), 190 | self.bot.loop 191 | ) 192 | ) 193 | message = await send(self.bot, ctx, title="Now Playing", content=f"🎵 Playing {title} by {author}", color=0x2ECC71) 194 | self.now_playing_messages[queue_id] = message 195 | 196 | except Exception as e: 197 | await send(self.bot, ctx, title="Error", 198 | content=f"Playback error: {str(e)}", color=0xFF0000) 199 | await self._play_next(ctx, voice_client) 200 | 201 | async def _handle_playback_error(self, error, ctx, voice_client): 202 | if error: 203 | await send(self.bot, ctx, title="Error", 204 | content=f"Playback error: {str(error)}", color=0xFF0000) 205 | await self._play_next(ctx, voice_client) 206 | 207 | @commands.command(description="Set volume (0-200)") 208 | async def volume(self, ctx, volume: int): 209 | volume = int(volume) 210 | if not 0 <= volume <= 200: 211 | await send(self.bot, ctx, title="Error", 212 | content="Volume must be between 0 and 200", color=0xFF0000) 213 | return 214 | 215 | self.volume = volume 216 | await send(self.bot, ctx, title="Volume", 217 | content=f"Volume set to {volume}%", color=0x2ECC71) 218 | 219 | @commands.command(description="Show current queue") 220 | async def queue(self, ctx): 221 | queue = self._get_queue(ctx) 222 | 223 | if not queue: 224 | await send(self.bot, ctx, title="Queue Empty", 225 | content="📝 No tracks in queue", color=0xFF0000) 226 | return 227 | 228 | queue_text = "\n".join(f"{i+1}. {title} by {author}" 229 | for i, (_, title, author) in enumerate(queue)) 230 | await send(self.bot, ctx, title="Current Queue", 231 | content=f"📝 {queue_text}", color=0x2ECC71) 232 | 233 | @commands.command(description="Toggle loop mode") 234 | async def loop(self, ctx): 235 | queue_id = self._get_queue_id(ctx) 236 | self.loops[queue_id] = not self.loops.get(queue_id, False) 237 | status = "enabled" if self.loops[queue_id] else "disabled" 238 | await send(self.bot, ctx, title="Loop", 239 | content=f"🔄 Loop mode {status}", color=0x2ECC71) 240 | @commands.command(name="now", description="Show current track") 241 | async def now_playing(self, ctx): 242 | queue_id = self._get_queue_id(ctx) 243 | current = self.current_tracks.get(queue_id) 244 | 245 | if not current: 246 | await send(self.bot, ctx, title="Not Playing", 247 | content="Nothing is playing right now", color=0xFF0000) 248 | return 249 | 250 | title, author = current 251 | await send(self.bot, ctx, title="Now Playing", 252 | content=f"🎵 {title} by {author}", color=0x2ECC71) 253 | @commands.command(description="Skip current track") 254 | async def skip(self, ctx): 255 | voice_client = discord.utils.get(self.bot.voice_clients, guild=ctx.guild if ctx.guild else None) 256 | 257 | if voice_client and voice_client.is_playing(): 258 | voice_client.stop() 259 | await ctx.message.add_reaction('⏭️') 260 | else: 261 | await send(self.bot, ctx, title="Cannot Skip", content="Nothing is playing!", color=0xFF0000) 262 | 263 | @commands.command(description="Stop playback and clear queue") 264 | async def stop(self, ctx): 265 | queue_id = self._get_queue_id(ctx) 266 | voice_client = discord.utils.get(self.bot.voice_clients, guild=ctx.guild if ctx.guild else None) 267 | 268 | if voice_client and voice_client.is_connected(): 269 | self.queues[queue_id] = [] 270 | self.current_tracks[queue_id] = None 271 | voice_client.stop() 272 | await voice_client.disconnect() 273 | await send(self.bot, ctx, title="Stopped", content="⏹️ Playback stopped and queue cleared", color=0x2ECC71) 274 | else: 275 | await send(self.bot, ctx, title="Cannot Stop", content="Not connected to a voice channel!", color=0xFF0000) 276 | 277 | async def setup(bot): 278 | await bot.add_cog(Music(bot)) -------------------------------------------------------------------------------- /Backend/Modules/perms.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | from Backend.utils import read_users, write_users, read_settings 3 | from Backend.send import send 4 | class perms(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | self.owner_id = int(read_settings()["main"]["owner_id"]) 8 | 9 | @commands.command(description='Add a user to the allowed list') 10 | async def adduser(self, ctx, user_mention: str): 11 | if int(ctx.author.id) == int(self.bot.user.id) or int(ctx.author.id) == self.owner_id: 12 | user = await commands.UserConverter().convert(ctx, user_mention) 13 | users_data = read_users() 14 | if user_mention in [u["id"] for u in users_data["users"]]: 15 | await send(self.bot, ctx, title='Error', content=f'User already whitelisted!', color=0xff0000) 16 | return 17 | users_data["users"].append({ 18 | "id": str(user.id), 19 | "name": f"{user.name}" 20 | }) 21 | write_users(users_data) 22 | await send(self.bot, ctx, title='Success', content=f'Added user with ID: {user.id}', color=0x2ECC71) 23 | else: 24 | await send(self.bot, ctx, title='Error', content="You are not allowed to use this command", color=0xff0000) 25 | 26 | @commands.command(description='Removes a user from allowed list') 27 | async def removeuser(self, ctx, user_mention: str): 28 | if int(ctx.author.id) == int(self.bot.user.id) or int(ctx.author.id) == self.owner_id: 29 | user_id = user_mention.strip('<@!>') 30 | users_data = read_users() 31 | if user_mention in [u["id"] for u in users_data["users"]]: 32 | users_data["users"] = [u for u in users_data["users"] if u["id"] != str(user_id)] 33 | write_users(users_data) 34 | await send(self.bot, ctx, title='Success', content=f'Removed user with ID: {user_id}', color=0xE74C3C) 35 | else: 36 | await send(self.bot, ctx, title='Error', content=f'User not whitelisted!', color=0xff0000) 37 | else: 38 | await send(self.bot, ctx, title='Error', content="You are not allowed to use this command", color=0xff0000) 39 | return 40 | @commands.command(description='Whitelists entire server') 41 | async def addserver(self, ctx): 42 | if int(ctx.author.id) == int(self.bot.user.id) or int(ctx.author.id) == self.owner_id: 43 | users_data = read_users() 44 | if "guilds" not in users_data: 45 | users_data["guilds"] = [] 46 | users_data["guilds"].append({ 47 | "id": ctx.guild.id, 48 | "name": ctx.guild.name 49 | }) 50 | write_users(users_data) 51 | await send(self.bot, ctx, title='Success', content=f'Added server with ID: {ctx.guild.id}', color=0x2ECC71) 52 | else: 53 | await send(self.bot, ctx, title='Error', content="You are not allowed to use this command", color=0xff0000) 54 | @commands.command(description='Removes a server from allowed list') 55 | async def removeserver(self, ctx): 56 | if int(ctx.author.id) == int(self.bot.user.id) or int(ctx.author.id) == self.owner_id: 57 | users_data = read_users() 58 | if "guilds" in users_data: 59 | users_data["guilds"] = [g for g in users_data["guilds"] if g["id"] != ctx.guild.id] 60 | write_users(users_data) 61 | await send(self.bot, ctx, title='Success', content=f'Removed server with ID: {ctx.guild.id}', color=0xE74C3C) 62 | else: 63 | await send(self.bot, ctx, title='Error', content=f'Server not whitelisted!', color=0xff0000) 64 | else: 65 | await send(self.bot, ctx, title='Error', content="You are not allowed to use this command", color=0xff0000) 66 | 67 | async def setup(bot): 68 | await bot.add_cog(perms(bot)) 69 | 70 | -------------------------------------------------------------------------------- /Backend/Modules/petpet.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import discord 3 | from discord.ext import commands 4 | import os 5 | from Backend.send import send 6 | class PetPet(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.command(description='PetPet a user!') 11 | async def petpet(self, ctx, user: discord.User = None): 12 | if not user: 13 | user = ctx.author 14 | user = await commands.UserConverter().convert(ctx, user) 15 | response = httpx.get('https://memeado.vercel.app/api/petpet', params={'image': user.avatar.url}) 16 | 17 | if response.status_code == 200: 18 | with open(f'petpet_{user.id}.gif', 'wb') as f: 19 | f.write(response.content) 20 | try: 21 | await send(self.bot, ctx, title=f'Heh that tickles-', image=f'petpet_{user.id}.gif') 22 | finally: 23 | os.remove(f'petpet_{user.id}.gif') 24 | else: 25 | await send(self.bot, ctx, title='Error', content='Failed to generate petpet image.', color=0xFF0000) 26 | 27 | async def setup(bot): 28 | await bot.add_cog(PetPet(bot)) 29 | -------------------------------------------------------------------------------- /Backend/Modules/polls.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.send import send 4 | class Polls(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | self.reactions = [u"1️⃣", u"2️⃣", u"3️⃣", u"4️⃣", u"5️⃣", u"6️⃣", u"7️⃣", u"8️⃣", u"9️⃣", u"🔟"] 8 | 9 | @commands.command(description='Create a poll!', usage='poll : , , ...') 10 | async def poll(self, ctx, content: str): 11 | if ':' not in content: 12 | await ctx.send("Please provide a question followed by options, separated by a colon.") 13 | return 14 | 15 | question, options_str = content.split(":", 1) 16 | question = question.strip() and question.capitalize() 17 | options = [opt.strip() for opt in options_str.split(",")] 18 | 19 | if len(options) < 2: 20 | await send(self.bot, ctx, title='Error', content="Please provide at least two options.", color=0xFF0000) 21 | return 22 | if len(options) > 10: 23 | await send(self.bot, ctx, title='Error', content="You can only have up to 10 options.", color=0xFF0000) 24 | return 25 | 26 | options_str = "\n".join([f"{i}. `{options[i]}`" for i in range(len(options))]) 27 | 28 | poll = await send(self.bot, ctx, title=question, content=options_str) 29 | 30 | for i in range(len(options)): 31 | await poll.add_reaction(self.reactions[i]) 32 | 33 | async def setup(bot): 34 | await bot.add_cog(Polls(bot)) 35 | -------------------------------------------------------------------------------- /Backend/Modules/qotd.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | import httpx 3 | from Backend.send import send 4 | class qotd(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | 8 | @commands.command(description="Get the quote of today") 9 | async def qotd(self, ctx): 10 | quote = httpx.get("https://zenquotes.io/api/today") 11 | await send(self.bot, ctx, title="Quote of the Day", content=f'{quote.json()[0]["q"]}\n\u2014 {quote.json()[0]["a"]}', color=0xFEE75C) 12 | @commands.command(description="Get a random quote", aliases=["rq"]) 13 | async def randomquote(self, ctx): 14 | quote = httpx.get("https://zenquotes.io/api/random") 15 | await send(self.bot, ctx, title="Random Quote", content=f'{quote.json()[0]["q"]}\n\u2014 {quote.json()[0]["a"]}', color=0xFEE75C) 16 | async def setup(bot): 17 | await bot.add_cog(qotd(bot)) -------------------------------------------------------------------------------- /Backend/Modules/quote.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from PIL import Image, ImageFont, ImageDraw 4 | import os 5 | from io import BytesIO 6 | from Backend.send import send 7 | path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'assets', 'quote') 8 | class Quote(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | @commands.command(name="quote", description="Quote a message", aliases=["q"]) 12 | async def quote(self, ctx, message_id: int = None, mode: str = "black"): 13 | if message_id is None: 14 | if ctx.message.reference: 15 | message_id = ctx.message.reference.message_id 16 | else: 17 | await send(self.bot, ctx, color=0xFF0000, title="Error", content="Please provide a message ID or reply to a message") 18 | return 19 | 20 | try: 21 | async with ctx.typing(): 22 | message = await ctx.fetch_message(int(message_id)) 23 | author_name = message.author.name 24 | image = Image.open(f"{path}/{mode}.png") 25 | font = ImageFont.truetype(f"{path}/font.otf", 36) 26 | draw = ImageDraw.Draw(image) 27 | 28 | img_width, img_height = image.size 29 | 30 | def wrap_text(text, font, max_width): 31 | lines = [] 32 | words = text.split() 33 | current_line = [] 34 | 35 | for word in words: 36 | test_line = current_line + [word] 37 | line_width = draw.textlength(' '.join(test_line), font=font) 38 | 39 | if line_width <= max_width: 40 | current_line.append(word) 41 | else: 42 | if current_line: 43 | lines.append(' '.join(current_line)) 44 | current_line = [word] 45 | 46 | if current_line: 47 | lines.append(' '.join(current_line)) 48 | return lines 49 | 50 | def get_optimal_font_size(text, max_width, max_height, min_size=12, start_size=36): 51 | test_size = start_size 52 | while test_size > min_size: 53 | font = ImageFont.truetype(f"{path}/font.otf", test_size) 54 | lines = wrap_text(text, font, max_width) 55 | total_height = len(lines) * (font.size * 1.2) 56 | max_line_width = max(draw.textlength(line, font=font) for line in lines) 57 | 58 | if (total_height <= max_height and 59 | max_line_width <= max_width): 60 | return font, lines 61 | 62 | test_size -= 2 63 | 64 | font = ImageFont.truetype(f"{path}/font.otf", min_size) 65 | return font, wrap_text(text, font, max_width) 66 | 67 | if message.author.avatar: 68 | avatar_data = await message.author.avatar.read() 69 | avatar_image = Image.open(BytesIO(avatar_data)) 70 | else: 71 | avatar_image = Image.open(f"{path}/default_pfp.png") 72 | 73 | image = Image.open(f"{path}/{mode}.png").convert('RGBA') 74 | img_width, img_height = image.size 75 | 76 | avatar_width = int(img_height * 1.05) 77 | avatar_height = img_height 78 | avatar_image = avatar_image.resize((avatar_width, avatar_height), Image.Resampling.LANCZOS) 79 | 80 | mask = Image.new('L', (avatar_width, avatar_height), 255) 81 | mask_draw = ImageDraw.Draw(mask) 82 | 83 | for y in range(avatar_height): 84 | for x in range(avatar_width): 85 | distance = (x + y) / 2 86 | opacity = 255 - int(255 * (distance / (avatar_width + avatar_height) * 1.5)) 87 | opacity = max(0, min(255, opacity)) 88 | mask_draw.point((x, y), fill=opacity) 89 | 90 | avatar_x = 0 91 | avatar_y = 0 92 | image.paste(avatar_image, (avatar_x, avatar_y), mask) 93 | 94 | draw = ImageDraw.Draw(image) 95 | min_x_position = avatar_width + 40 96 | max_x_position = img_width - 40 97 | text_area_width = max_x_position - min_x_position 98 | text_area_height = img_height * 0.7 99 | 100 | content = message.content.encode("ascii", errors="ignore").decode() 101 | for user in message.mentions: 102 | content = content.replace(f"<@{user.id}>", f"@{user.display_name}") 103 | content = content.replace(f"<@!{user.id}>", f"@{user.display_name}") 104 | 105 | font, lines = get_optimal_font_size( 106 | content, 107 | text_area_width, 108 | text_area_height 109 | ) 110 | 111 | line_height = font.size * 1.2 112 | total_height = len(lines) * line_height 113 | start_y = (img_height - total_height) / 2 - line_height 114 | 115 | for i, line in enumerate(lines): 116 | y_position = start_y + (i * line_height) 117 | draw.text((min_x_position, y_position), line, fill="white", font=font) 118 | 119 | author_text = f"- {message.author.display_name}" 120 | tag_text = f"@{message.author.name}" 121 | 122 | author_y = start_y + (len(lines) * line_height) + line_height 123 | tag_y = author_y + font.size * 1.2 124 | 125 | author_font = ImageFont.truetype(f"{path}/font.otf", int(font.size * 0.8)) 126 | small_font = ImageFont.truetype(f"{path}/font.otf", int(font.size * 0.6)) 127 | 128 | author_width = draw.textlength(author_text, font=author_font) 129 | tag_width = draw.textlength(tag_text, font=small_font) 130 | 131 | if min_x_position + max(author_width, tag_width) > max_x_position: 132 | author_font = ImageFont.truetype(f"{path}/font.otf", int(font.size * 0.6)) 133 | small_font = ImageFont.truetype(f"{path}/font.otf", int(font.size * 0.4)) 134 | 135 | draw.text((min_x_position, author_y), author_text, fill="white", font=author_font) 136 | draw.text((min_x_position, tag_y), tag_text, fill="white", font=small_font) 137 | 138 | image.save(f"{path}/output.png") 139 | 140 | try: 141 | await send(self.bot, ctx, title=f"Quote by {author_name}", image=f"{path}/output.png") 142 | finally: 143 | if os.path.exists(f"{path}/output.png"): 144 | os.remove(f"{path}/output.png") 145 | except discord.NotFound: 146 | await send(self.bot, ctx, title="Error", content="Message not found", color=0xFF0000) 147 | except Exception as e: 148 | await send(self.bot, ctx, title="Error", content=f"Error: {str(e)}", color=0xFF0000) 149 | 150 | async def setup(bot): 151 | await bot.add_cog(Quote(bot)) -------------------------------------------------------------------------------- /Backend/Modules/reactions.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import httpx 3 | from discord.ext import commands 4 | import random 5 | import json 6 | from Backend.send import send 7 | class Reactions(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | self.sources = ['https://nekos.life/api/v2/img', 'https://api.waifu.pics/sfw', 'https://purrbot.site/api/img/sfw'] 11 | 12 | async def get_reaction(self, reaction, source=None): 13 | try: 14 | if source: 15 | url = source 16 | else: 17 | url = random.choice(self.sources) 18 | if url != 'https://purrbot.site/api/img/sfw': 19 | response = json.loads(httpx.get(f'{url}/{reaction}').text) 20 | return response['url'] 21 | else: 22 | response = json.loads(httpx.get(f'{url}/{reaction}/gif').text) 23 | return response['link'] 24 | except: 25 | reaction = await self.get_reaction(reaction) 26 | return reaction 27 | 28 | @commands.command(description='Kiss a user!') 29 | async def kiss(self, ctx, user: discord.Member = None): 30 | reaction = await self.get_reaction('kiss') 31 | if user: 32 | user = await commands.UserConverter().convert(ctx, user) 33 | if ctx.message.reference: 34 | ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 35 | user = ref_message.author 36 | if not user or user == ctx.author: 37 | await send(self.bot, ctx, title=f'{ctx.author.display_name} kisses... themselves?',color=0xFEE75C, content=f'{ctx.author.mention} kisses themselves! How strange!', image=reaction) 38 | else: 39 | await send(self.bot, ctx, color=0xFEE75C, title=f'{ctx.author.display_name} kisses {user.display_name}!', content=f'{ctx.author.mention} kisses {user.mention}, How cute!', image=reaction) 40 | 41 | @commands.command(description='Hug a user!') 42 | async def hug(self, ctx, user: discord.Member = None): 43 | if user: 44 | user = await commands.UserConverter().convert(ctx, user) 45 | if ctx.message.reference: 46 | ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 47 | user = ref_message.author 48 | reaction = await self.get_reaction('hug') 49 | if not user or user == ctx.author: 50 | await send(self.bot, ctx, title=f'{ctx.author.display_name} hugs... themselves?', color=0xFEE75C, content=f'{ctx.author.mention} hugs themselves! How strange!', image=reaction) 51 | else: 52 | await send(self.bot, ctx, color=0xFEE75C, title=f'{ctx.author.display_name} hugs {user.display_name}!',content=f'{ctx.author.mention} hugs {user.mention}, How cute!', image=reaction) 53 | 54 | @commands.command(description='Tickle a user!') 55 | async def tickle(self, ctx, user: discord.Member = None): 56 | if user: 57 | user = await commands.UserConverter().convert(ctx, user) 58 | if ctx.message.reference: 59 | ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 60 | user = ref_message.author 61 | if not user or user == ctx.author: 62 | await send(self.bot, ctx, color=0xFF0000, title=f'Error', content=f'{ctx.author.mention} You can\'t tickle yourself!') 63 | else: 64 | reaction = await self.get_reaction('tickle') 65 | await send(self.bot, ctx, color=0xFEE75C, title=f'Hehe that tickles',content=f'{ctx.author.mention} tickles {user.mention}!', image=reaction) 66 | 67 | @commands.command(description='Bite a user!') 68 | async def bite(self, ctx, user: discord.Member = None): 69 | if user: 70 | user = await commands.UserConverter().convert(ctx, user) 71 | if ctx.message.reference: 72 | ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 73 | user = ref_message.author 74 | if not user or user == ctx.author: 75 | await send(self.bot, ctx, color=0xFF0000, title='Error', content=f'{ctx.author.mention} You can\'t bite yourself!') 76 | else: 77 | reaction = await self.get_reaction('bite') 78 | await send(self.bot, ctx, color=0xFEE75C, title=f'Someone is feeling feisty',content=f'{ctx.author.mention} bit {user.mention}!', image=reaction) 79 | 80 | @commands.command(description='Slap a user!') 81 | async def slap(self, ctx, user: discord.Member = None): 82 | if user: 83 | user = await commands.UserConverter().convert(ctx, user) 84 | if ctx.message.reference: 85 | ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 86 | user = ref_message.author 87 | if not user or user == ctx.author: 88 | await send(self.bot, ctx, color=0xFF0000, title='Error', content=f'{ctx.author.mention} You can\'t slap yourself!') 89 | else: 90 | reaction = await self.get_reaction('slap') 91 | await send(self.bot, ctx, color=0xFEE75C, title=f'Someone\'s angry', content=f'{ctx.author.mention} slapped {user.mention}!', image=reaction) 92 | 93 | @commands.command(description='Blush!') 94 | async def blush(self, ctx, user: discord.Member = None): 95 | if user: 96 | user = await commands.UserConverter().convert(ctx, user) 97 | if ctx.message.reference: 98 | ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 99 | user = ref_message.author 100 | reaction = await self.get_reaction('blush', source='https://purrbot.site/api/img/sfw') 101 | if user: 102 | await send(self.bot, ctx, color=0xFEE75C, title=f'Someone\'s blushing!', content=f'{ctx.author.mention} has a crush on {user.mention} (probably) :3', image=reaction) 103 | else: 104 | await send(self.bot, ctx, color=0xFEE75C, title='Someone\'s blushing!', content=f'{ctx.author.mention} is blushing!', image=reaction) 105 | 106 | async def setup(bot): 107 | await bot.add_cog(Reactions(bot)) 108 | 109 | 110 | -------------------------------------------------------------------------------- /Backend/Modules/reverse.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | import sys 4 | import httpx 5 | from bs4 import BeautifulSoup 6 | import json 7 | import re 8 | from urllib.parse import quote 9 | from Backend.send import send 10 | results = [] 11 | sys.stdout.reconfigure(encoding='utf-8') 12 | def reverse_search(image_url): 13 | search_url = f"https://yandex.com/images/search?rpt=imageview&url={quote(image_url)}" 14 | headers = { 15 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.60 Safari/537.36' 16 | } 17 | response = httpx.get(search_url, headers=headers) 18 | print(search_url) 19 | if response.status_code != 200: 20 | print("Failed to retrieve results") 21 | return None 22 | 23 | soup = BeautifulSoup(response.text, 'html.parser') 24 | 25 | root_div = soup.find('div', id=lambda x: x and x.startswith('CbirSites_simple')) 26 | if not root_div: 27 | print("No sites section found") 28 | return None 29 | 30 | data_state = root_div.get('data-state') 31 | if not data_state: 32 | print("No data-state found in the section") 33 | return None 34 | 35 | try: 36 | data = json.loads(data_state) 37 | sites = data.get('sites', []) 38 | return [{'title': site['title'], 'url': site['url']} for site in sites] 39 | except json.JSONDecodeError: 40 | print("Failed to parse JSON") 41 | return None 42 | 43 | def is_english_text(text): 44 | return bool(re.match(r'^[\x00-\x7F]*$', text)) 45 | 46 | class Reverse(commands.Cog): 47 | def __init__(self, bot): 48 | self.bot = bot 49 | 50 | @commands.command(description="Reverse image search using Yandex", aliases=["rev", "yandex"]) 51 | async def reverse(self, ctx, image_url: str = None): 52 | if not image_url: 53 | if ctx.message.reference: 54 | message = await ctx.fetch_message(ctx.message.reference.message_id) 55 | if message.attachments: 56 | image_url = message.attachments[0].url 57 | else: 58 | await ctx.send("Please provide an image or reply to an image.") 59 | return 60 | else: 61 | await ctx.send("Please provide an image or reply to an image.") 62 | return 63 | 64 | sites = reverse_search(image_url) 65 | if sites: 66 | message = "" 67 | for idx, site in enumerate(sites, start=1): 68 | if is_english_text(site['title']) and is_english_text(site['url']): 69 | message += f"{site['title']}: {site['url']}\n" 70 | await ctx.send(f'# Results:\n{message}') 71 | else: 72 | await ctx.send("No matching sites found.") 73 | 74 | 75 | async def setup(bot): 76 | await bot.add_cog(Reverse(bot)) 77 | -------------------------------------------------------------------------------- /Backend/Modules/setup.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from Backend.bot import bot 4 | import json 5 | import os 6 | from Backend.utils import is_owner 7 | path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 8 | class Setup(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | @commands.command(hidden=True) 13 | async def setup(self, ctx): 14 | if ctx.author.id != ctx.guild.owner.id: 15 | return await ctx.send("Only the server owner can run this command.") 16 | if not is_owner(ctx.author.id): 17 | return await ctx.send("You are not allowed to run this command.") 18 | channel = ctx.channel 19 | if not channel.permissions_for(ctx.guild.me).manage_webhooks: 20 | await ctx.send("I don't have permission to manage webhooks in this channel.") 21 | return 22 | try: 23 | webhook = await channel.create_webhook(name=f"{ctx.author.global_name}") 24 | await ctx.send(f"Webhook created successfully!") 25 | try: 26 | webhook_file_path = f'{path}/data/webhook.json' 27 | if os.path.exists(webhook_file_path): 28 | with open(webhook_file_path, 'r') as f: 29 | data = json.load(f) 30 | else: 31 | data = {} 32 | data.update({'webhook_url': webhook.url}) 33 | with open(webhook_file_path, 'w') as f: 34 | json.dump(data, f, indent=4) 35 | except Exception as e: 36 | await ctx.send(f'Error writing to webhook file: {e}') 37 | return 38 | await ctx.send('Please start the bot again to apply the changes.') 39 | await bot.close() 40 | except discord.Forbidden: 41 | await ctx.send("I couldn't create a webhook due to permission issues.") 42 | return 43 | except discord.HTTPException as e: 44 | await ctx.send(f"An error occurred while creating the webhook: {e}") 45 | return 46 | async def setup(bot): 47 | await bot.add_cog(Setup(bot)) 48 | -------------------------------------------------------------------------------- /Backend/Modules/snipe.py: -------------------------------------------------------------------------------- 1 | import random 2 | from discord.ext import commands 3 | from Backend.utils import read_messages, write_messages 4 | from Backend.send import send 5 | from Backend.utils import is_owner 6 | class Snipe(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.command(description='Snipe a deleted message. Optional: specify position (e.g. >s 2)', aliases=['s']) 11 | async def snipe(self, ctx, position=1): 12 | try: 13 | position = int(position) 14 | except ValueError: 15 | await send(self.bot, ctx, title='Error', content="Please provide a valid number for position", color=0xff0000) 16 | return 17 | 18 | messages_data = read_messages() 19 | if len(messages_data["messages"]) > 0: 20 | current_location = ctx.guild.id if ctx.guild else 'DM' 21 | 22 | location_messages = [ 23 | msg for msg in messages_data["messages"] 24 | if (msg.get("server") == current_location) and msg["type"] == "delete" 25 | ] 26 | 27 | if location_messages: 28 | if 1 <= position <= len(location_messages): 29 | msg = location_messages[-position] 30 | response = f"{msg['message']}" 31 | await send(self.bot, ctx, title=f'Message Deleted by {msg["user"]}', content=response, color=0xFEE75C) 32 | else: 33 | await send(self.bot, ctx, title='Error', content=f"Invalid position. Available range: 1-{len(location_messages)}", color=0xff0000) 34 | else: 35 | await send(self.bot, ctx, title='Error', content="No deleted messages to snipe here", color=0xff0000) 36 | else: 37 | await send(self.bot, ctx, title='Error', content="No messages to snipe", color=0xff0000) 38 | 39 | @commands.command(description='Snipe an edited message. Optional: specify position (e.g. >es 2)', aliases=['es']) 40 | async def editsnipe(self, ctx, position=1): 41 | try: 42 | position = int(position) 43 | except ValueError: 44 | await send(self.bot, ctx, title='Error', content="Please provide a valid number for position", color=0xff0000) 45 | return 46 | messages_data = read_messages() 47 | if len(messages_data["messages"]) > 0: 48 | current_location = ctx.guild.id if ctx.guild else 'DM' 49 | 50 | location_messages = [ 51 | msg for msg in messages_data["messages"] 52 | if (msg.get("server") == current_location) and msg["type"] == "edit" 53 | ] 54 | 55 | if location_messages: 56 | if 1 <= position <= len(location_messages): 57 | msg = location_messages[-position] 58 | response = f"\n**Before:** {msg['message_before']}\n**After:** {msg['message_after']}\n[Jump to message]({msg['message_link']})" 59 | await send(self.bot, ctx, title=f'Message Edited by {msg["user"]}', content=response, color=0xFEE75C) 60 | else: 61 | await send(self.bot, ctx, title='Error', content=f"Invalid position. Available range: 1-{len(location_messages)}", color=0xff0000) 62 | else: 63 | await send(self.bot, ctx, title='Error', content="No edited messages to snipe here", color=0xff0000) 64 | else: 65 | await send(self.bot, ctx, title='Error', content="No messages to snipe", color=0xff0000) 66 | 67 | @commands.command(description='Clear the snipe cache', aliases=['cs']) 68 | async def clearsnipe(self, ctx): 69 | if not is_owner(ctx.author.id): 70 | await send(self.bot, ctx, title='Error', content="You are not allowed to use this command", color=0xff0000) 71 | return 72 | messages_data = read_messages() 73 | messages_data["messages"] = [] 74 | write_messages(messages_data) 75 | await send(self.bot, ctx, title='Success', content="Snipe cache cleared", color=0x2ECC71) 76 | 77 | async def setup(bot): 78 | await bot.add_cog(Snipe(bot)) -------------------------------------------------------------------------------- /Backend/Modules/spotify.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | import httpx 4 | import flask 5 | from flask import request 6 | from Backend.utils import read_settings 7 | 8 | class Spotify(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | @commands.command() 12 | async def spotify(self, ctx): 13 | url = read_settings()["Spotify"].get("redirect_uri") 14 | httpx.get(url) 15 | 16 | 17 | 18 | def setup(bot): 19 | bot.add_cog(Spotify(bot)) -------------------------------------------------------------------------------- /Backend/Modules/statistics.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | import discord 3 | from main import start_time 4 | import time 5 | from Backend.utils import read_users 6 | from Backend.send import send 7 | class Statistics(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | 11 | @commands.command(description="Check the bot's uptime", aliases=['uptime', 'stats','info']) 12 | async def statistics(self, ctx): 13 | commands = len(self.bot.commands) 14 | users=read_users()['users'] 15 | servers = read_users()['guilds'] 16 | uptime_seconds = time.time() - start_time 17 | days = int(uptime_seconds // (24 * 3600)) 18 | hours = int((uptime_seconds % (24 * 3600)) // 3600) 19 | minutes = int((uptime_seconds % 3600) // 60) 20 | seconds = int(uptime_seconds % 60) 21 | def uptime(days, hours, minutes, seconds): 22 | parts = [] 23 | if days: 24 | parts.append(f"{days} days") 25 | if hours: 26 | parts.append(f"{hours} hours") 27 | if minutes: 28 | parts.append(f"{minutes} minutes") 29 | if seconds: 30 | parts.append(f"{seconds} seconds") 31 | return ", ".join(parts) 32 | await send(self.bot, ctx, title="Statistics", content=f"Uptime: {uptime(days, hours, minutes, seconds)}\nTotal Users: {len(users)}\nServers: {len(servers)}\nTotal Commands: {commands}\nProject: github.com/SnarkyDeveloper/Selfbot (Please star ⭐)") 33 | @commands.command(description="Pong!") 34 | async def ping(self, ctx): 35 | latency = round(self.bot.latency * 1000, 2) 36 | if latency < 150: 37 | await send(self.bot, ctx, title='Pong!', content=f'Latency: {latency} ms', color=0x2ECC71) 38 | elif latency < 500: 39 | await send(self.bot, ctx, title='Pong!', content=f'Latency: {latency} ms', color=0xFEE75C) 40 | else: 41 | await send(self.bot, ctx, title='Pong!', content=f'Latency: {latency} ms', color=0xE74C3C) 42 | 43 | async def setup(bot): 44 | await bot.add_cog(Statistics(bot)) -------------------------------------------------------------------------------- /Backend/Modules/stealer.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.ext.commands import has_permissions 4 | import aiohttp 5 | import os 6 | from Backend.send import send 7 | 8 | class Stealer(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | async def download_asset(self, url, file_path): 13 | async with aiohttp.ClientSession() as session: 14 | async with session.get(url) as response: 15 | if response.status == 200: 16 | with open(file_path, 'wb') as file: 17 | file.write(await response.read()) 18 | return True 19 | return False 20 | 21 | @commands.command(description="Steal emojis and stickers") 22 | @has_permissions(manage_emojis=True) 23 | async def steal(self, ctx, *args): 24 | try: 25 | stolen_assets = [] 26 | emojis_to_steal = [] 27 | added_emojis = [] 28 | existing_emoji_names = {emoji.name for emoji in ctx.guild.emojis} 29 | existing_sticker_names = {sticker.name for sticker in ctx.guild.stickers} 30 | if ctx.message.reference: 31 | message = await ctx.channel.fetch_message(ctx.message.reference.message_id) 32 | else: 33 | message = ctx.message 34 | 35 | for sticker in message.stickers: 36 | if sticker.name in existing_sticker_names: 37 | stolen_assets.append(f"❌ Emoji: `{emoji.name}` already exists") 38 | continue 39 | file_extension = 'gif' if sticker.format == discord.StickerFormatType.gif else 'png' 40 | file_path = f"{sticker.id}.{file_extension}" 41 | 42 | if await self.download_asset(sticker.url, file_path): 43 | with open(file_path, 'rb') as image_file: 44 | try: 45 | added = await ctx.guild.create_sticker( 46 | name=sticker.name, 47 | description=f"Stolen by {ctx.author}", 48 | emoji="👍", 49 | file=discord.File(image_file), 50 | reason=f'Stolen by {ctx.author}' 51 | ) 52 | stolen_assets.append(f"✅ Sticker: `{added.name}`") 53 | except Exception as e: 54 | print(f"Error creating sticker {sticker.name}: {e}") 55 | os.remove(file_path) 56 | 57 | for word in message.content.split(): 58 | if word.startswith("<:") or word.startswith(" 1 else '' 23 | if args: 24 | await command(ctx, args) 25 | else: 26 | await command(ctx) 27 | except Exception as e: 28 | print(f"Error executing command: {e}") 29 | except Exception as e: 30 | print(f"Error in command processing: {e}") 31 | async def on_message(self, message): 32 | await self.parse(message) 33 | status_text = read_settings()["main"]["status"] 34 | bot = CustomBot( 35 | command_prefix=read_settings()["main"]["prefix"], 36 | description=description, 37 | self_bot=True, 38 | status=discord.Status.dnd, 39 | activity = discord.Activity( 40 | type=discord.ActivityType.streaming, 41 | name=status_text,), 42 | help_command=None, 43 | ) -------------------------------------------------------------------------------- /Backend/cogs.py: -------------------------------------------------------------------------------- 1 | import json 2 | from Backend.bot import bot 3 | from Backend.utils import read_settings 4 | with open('modules.json') as f: 5 | modules = json.load(f) 6 | 7 | loaded = [] 8 | 9 | async def setup_cogs(): 10 | if read_settings().get('main').get('first_run') == True: 11 | await bot.load_extension('Backend.Modules.setup') 12 | return 13 | for module, enabled in modules['loaded'].items(): 14 | if enabled: 15 | if module == 'economy': 16 | print("Cogs loaded, loading economy module...") 17 | economy_modules = [ 18 | 'work', 'daily', 'steal', 'balance', 'store', 'stripper', 19 | 'mafia', 'coinflip', 'roulette', 'slots', 'dice' 20 | ] 21 | for econ_module in economy_modules: 22 | await bot.load_extension(f'Backend.Modules.Economy.{econ_module}') 23 | loaded.append(f'Eco{econ_module.capitalize()}') 24 | else: 25 | await bot.load_extension(f'Backend.Modules.{module}') 26 | loaded.append(module.capitalize()) 27 | 28 | print("--------------------------------") 29 | print(f"Cogs loaded: {', '.join(loaded)}") 30 | print("--------------------------------") -------------------------------------------------------------------------------- /Backend/embed.py: -------------------------------------------------------------------------------- 1 | import discordwebhook 2 | import json 3 | from datetime import datetime 4 | import os 5 | import httpx 6 | import asyncio 7 | import aiofiles 8 | import discord 9 | 10 | path = os.path.dirname(os.path.dirname(__file__)) 11 | if not os.path.exists(f'{path}/data/webhook.json'): 12 | with open(f'{path}/data/webhook.json', 'w') as f: 13 | json.dump({'webhook_url': None}, f) 14 | with open(f'{path}/data/webhook.json') as f: 15 | try: 16 | data = json.load(f) 17 | webhook_url = data['webhook_url'] 18 | except: 19 | webhook_url = None 20 | 21 | class CreateEmbed: 22 | def __init__(self): 23 | self.webhook_url = webhook_url 24 | async def send_file(self, file, webhook): 25 | if not os.path.exists(file): 26 | print("File does not exist") 27 | return None 28 | 29 | async with aiofiles.open(file, 'rb') as f: 30 | file_content = await f.read() 31 | 32 | files = { 33 | 'file': (os.path.basename(file), file_content) 34 | } 35 | 36 | async with httpx.AsyncClient() as client: 37 | response = await client.post(webhook, files=files) 38 | 39 | if response.status_code == 200: 40 | response_data = response.json() 41 | return response_data['attachments'][0]['url'] 42 | else: 43 | print(f'Failed to send file: {response.status_code}, {response.text}') 44 | return None 45 | 46 | async def embed(self, ctx, title, content, color=0x000000, image=None, video=None): 47 | if not self.webhook_url: 48 | raise ValueError("Webhook URL is not provided") 49 | if image and not image.startswith("http") or video and not video.startswith("http"): 50 | image = await self.send_file(file=f'{image}', webhook=self.webhook_url) 51 | webhook = discordwebhook.Webhook(url=self.webhook_url) 52 | try: 53 | embed = discordwebhook.Embed(title=title, description=content, color=color) 54 | if image: 55 | embed.set_image(url=image) 56 | if video: 57 | embed.set_video(url=video) 58 | embed.set_footer(text=f'Bot created by SnarkyDev, Command ran at {datetime.now().strftime("%m/%d, %I:%M %p")}') 59 | embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.display_avatar.url) 60 | webhook = await webhook.send_async( 61 | embed=embed, 62 | ) 63 | return webhook 64 | except Exception as e: 65 | raise e 66 | async def fetch_embed(self, message): 67 | webhook = discordwebhook.Webhook(url=webhook_url) 68 | webhook = await webhook.fetch_async(message) 69 | return webhook 70 | async def delete_embed(self, message): 71 | webhook = discordwebhook.Webhook(url=webhook_url) 72 | webhook = await webhook.delete_async(message) 73 | return webhook 74 | -------------------------------------------------------------------------------- /Backend/events.py: -------------------------------------------------------------------------------- 1 | from Backend.bot import bot 2 | def setup_events(): 3 | @bot.event 4 | async def on_command_error(ctx, error): 5 | print(f"Error executing command: {error}") 6 | @bot.event 7 | async def on_ready(): 8 | print(f"Logged in as {bot.user}") 9 | print("Bot is ready") 10 | print("--------------------------------") -------------------------------------------------------------------------------- /Backend/groups.py: -------------------------------------------------------------------------------- 1 | from Backend.bot import bot 2 | from Backend.send import send 3 | @bot.group(invoke_without_command=True, description="Economy commands") 4 | async def eco(ctx, *args): 5 | """Economy commands""" 6 | 7 | if not args: 8 | await send(bot, ctx, title="Available commands: Economy", content="Work, Daily, Balance, Steal, Shop, Coinflip, Mafia, Stripper", color=0x2ECC71) 9 | return 10 | command_parts = args[0].split(maxsplit=1) 11 | command_name = command_parts[0].lower() 12 | print(f"Parsed command name: {command_name}") 13 | if command_name == "bal": 14 | command_name = "balance" 15 | elif command_name == "stripper": 16 | command_name = "stripper_cmd" 17 | 18 | command = eco.get_command(command_name) 19 | if command: 20 | print(f"Executing command: {command.name}") 21 | 22 | for arg in args: 23 | if '<@' in arg: 24 | user_id = int(''.join(filter(str.isdigit, arg))) 25 | member = ctx.guild.get_member(user_id) 26 | print(f"Found mention, processing member: {member}") 27 | await ctx.invoke(command, member) 28 | return 29 | 30 | if len(command_parts) > 1 or len(args) > 1: 31 | params = command_parts[1] if len(command_parts) > 1 else args[1] 32 | if command_name == "coinflip": 33 | params = int(params) 34 | await ctx.invoke(command, params) 35 | else: 36 | await ctx.invoke(command) 37 | 38 | @bot.group(invoke_without_command=True) 39 | async def moderation(ctx, *args): 40 | """Moderation commands""" 41 | 42 | if not args: 43 | await send(bot, ctx, title="Available commands: Economy", content=" Kick, Ban, Mute, Role, Anti-Raid", color=0x2ECC71) 44 | return 45 | command_parts = args[0].split(maxsplit=1) 46 | command_name = command_parts[0].lower() 47 | print(f"Parsed command name: {command_name}") 48 | 49 | command = moderation.get_command(command_name) 50 | if command: 51 | print(f"Executing command: {command.name}") 52 | 53 | for arg in args: 54 | if '<@' in arg: 55 | user_id = int(''.join(filter(str.isdigit, arg))) 56 | member = ctx.guild.get_member(user_id) 57 | print(f"Found mention, processing member: {member}") 58 | await ctx.invoke(command, member) 59 | return 60 | 61 | if len(command_parts) > 1 or len(args) > 1: 62 | params = command_parts[1] if len(command_parts) > 1 else args[1] 63 | await ctx.invoke(command, params) 64 | else: 65 | await ctx.invoke(command) -------------------------------------------------------------------------------- /Backend/logger.py: -------------------------------------------------------------------------------- 1 | import json 2 | from Backend.bot import bot 3 | import os 4 | path = os.path.dirname(os.path.dirname(__file__)) 5 | def read_messages(): 6 | try: 7 | with open(f'{path}/data/messages/messages.json', 'r', encoding='utf-8') as file: 8 | return json.load(file) 9 | except FileNotFoundError: 10 | return {"messages": []} 11 | 12 | def write_messages(data): 13 | try: 14 | with open(f'{path}/data/messages/messages.json', 'w', encoding='utf-8') as file: 15 | json.dump(data, file, indent=4, ensure_ascii=False) 16 | except IOError as e: 17 | print(f"Error writing to messages file: {e}") 18 | 19 | @bot.event 20 | async def on_message_delete(message): 21 | if message.author.bot: 22 | return 23 | user = message.author 24 | if not message.content: 25 | return 26 | messages_data = read_messages() 27 | messages_data["messages"].append({ 28 | "user": str(user), 29 | "message": message.content, 30 | "type": "delete", 31 | "server": message.guild.id if message.guild else 'DM' 32 | }) 33 | write_messages(messages_data) 34 | 35 | @bot.event 36 | async def on_message_edit(before, after): 37 | if before.author.bot: 38 | return 39 | messages_data = read_messages() 40 | messages_data["messages"].append({ 41 | "user": str(before.author), 42 | "message_before": before.content, 43 | "message_after": after.content, 44 | "type": "edit", 45 | "server": before.guild.id if before.guild else 'DM', 46 | "message_link": f"https://discord.com/channels/{before.guild.id if before.guild else '@me'}/{before.channel.id}/{before.id}" 47 | }) 48 | write_messages(messages_data) 49 | await bot.parse(after) 50 | 51 | def logger(): 52 | bot.add_listener(on_message_delete, "on_message_delete") 53 | bot.add_listener(on_message_edit, "on_message_edit") -------------------------------------------------------------------------------- /Backend/permissions.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | path = os.path.dirname(os.path.dirname(__file__)) 5 | 6 | def read_users(): 7 | try: 8 | with open(f'{path}/data/users/users.json', 'r') as f: 9 | return json.load(f) 10 | except FileNotFoundError: 11 | default_users = {"users": [], "guilds": []} 12 | with open(f'{path}/data/users/users.json', 'w') as f: 13 | json.dump(default_users, f, indent=4) 14 | return default_users 15 | def check_permissions(bot, message): 16 | if not os.path.exists('users.json'): 17 | initial_users = { 18 | "users": [ 19 | { 20 | "id": str(bot.user.id), 21 | "name": str(bot.user) 22 | } 23 | ], 24 | "guilds": [] 25 | } 26 | with open('users.json', 'w') as f: 27 | json.dump(initial_users, f, indent=4) 28 | 29 | user_id = message.author.id 30 | users_data = read_users() 31 | if str(user_id) in [str(user["id"]) for user in users_data["users"]]: 32 | return True 33 | 34 | if message.guild: 35 | if str(message.guild.id) in [str(guild["id"]) for guild in users_data.get("guilds", [])]: 36 | return True 37 | 38 | return False 39 | 40 | -------------------------------------------------------------------------------- /Backend/send.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from Backend.embed import CreateEmbed 3 | import asyncio 4 | channel_cache = None 5 | 6 | create_embed = CreateEmbed() 7 | async def send(bot, ctx, title, content=None, color=None, image=None, video=None): 8 | global channel_cache 9 | try: 10 | async with ctx.typing(): 11 | try: 12 | webhook = await create_embed.embed(ctx, title=title, content=content, color=color, image=image, video=video) 13 | except Exception as e: 14 | webhook = await create_embed.embed(ctx, title='Error', content=f"An error occurred: {e}", color=0xFF0000) 15 | if channel_cache is None: 16 | channel_cache = bot.get_channel(int(webhook.channel_id)) 17 | message = await channel_cache.fetch_message(int(webhook.id)) 18 | if image or video: 19 | await asyncio.sleep(2) 20 | message = await message.forward(ctx.channel) 21 | return message 22 | except discord.Forbidden: 23 | await ctx.send("I don't have permission to embed links in this channel.") 24 | except discord.HTTPException: 25 | print("An error occurred while sending the message.") 26 | except Exception as e: 27 | await ctx.send(f"An error occurred: {e}") -------------------------------------------------------------------------------- /Backend/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | from discord.ext import commands 3 | import os 4 | path = os.path.dirname(os.path.dirname(__file__)) 5 | if os.path.exists(f'{path}/data/users/users.json'): 6 | pass 7 | else: 8 | with open(f'{path}/data/users/users.json', 'w') as file: 9 | json.dump({"users": []}, file, indent=4) 10 | if os.path.exists(f'{path}/data/punishments/punishments.json'): 11 | pass 12 | else: 13 | with open(f'{path}/data/punishments/punishments.json', 'w') as file: 14 | json.dump({"punishments": []}, file, indent=4) 15 | 16 | def read_users(): 17 | try: 18 | with open(f'{path}/data/users/users.json', 'r') as file: 19 | return json.load(file) 20 | except FileNotFoundError: 21 | return {"users": []} 22 | except IOError as e: 23 | print(f"Error reading users file: {e}") 24 | 25 | def write_users(data): 26 | with open(f'{path}/data/users/users.json', 'w') as file: 27 | json.dump(data, file, indent=4) 28 | 29 | def read_messages(): 30 | try: 31 | with open(f'{path}/data/messages/messages.json', 'r', encoding='utf-8') as file: 32 | return json.load(file) 33 | except FileNotFoundError: 34 | return {"messages": []} 35 | 36 | def write_messages(data): 37 | with open(f'{path}/data/messages/messages.json', 'w', encoding='utf-8') as file: 38 | json.dump(data, file, indent=4, ensure_ascii=False) 39 | 40 | def write_punishments(data): 41 | with open(f'{path}/data/punishments/punishments.json', 'w') as file: 42 | json.dump(data, file, indent=4) 43 | def read_punishments(): 44 | try: 45 | with open(f'{path}/data/punishments/punishments.json', 'r') as file: 46 | return json.load(file) 47 | except FileNotFoundError: 48 | return {"punishments": []} 49 | 50 | def read_settings(): 51 | try: 52 | with open(f'{path}/settings.json', 'r') as file: 53 | return json.load(file) 54 | except FileNotFoundError: 55 | return {"settings": []} 56 | def is_owner(id): 57 | data = read_settings() 58 | if data.get('owner_id') is None: 59 | return False 60 | elif data.get('owner_id') != id: 61 | return False 62 | return True -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Attribution-NonCommercial 4.0 International 2 | 3 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. 4 | 5 | ## Section 1 – Definitions 6 | 7 | - **Adapted Material** means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. 8 | - **Adapter's License** means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. 9 | - **Copyright and Similar Rights** means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. 10 | - **Effective Technological Measures** means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. 11 | - **Exceptions and Limitations** means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. 12 | - **Licensed Material** means the artistic or literary work, database, or other material to which the Licensor applied this Public License. 13 | - **Licensed Rights** means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. 14 | - **Licensor** means the individual(s) or entity(ies) granting rights under this Public License. 15 | - **NonCommercial** means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. 16 | - **Share** means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. 17 | - **Sui Generis Database Rights** means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. 18 | - **You** means the individual or entity exercising the Licensed Rights under this Public License. *Your* has a corresponding meaning. 19 | 20 | ## Section 2 – Scope 21 | 22 | ### License grant 23 | 24 | Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: 25 | 26 | - Reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and 27 | - Produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 28 | 29 | ### Exceptions and Limitations 30 | 31 | For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 32 | 33 | ### Term 34 | 35 | The term of this Public License is specified in Section 6(a). 36 | 37 | ### Media and formats; technical modifications allowed 38 | 39 | The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 40 | 41 | ### Downstream recipients 42 | 43 | - **Offer from the Licensor – Licensed Material**: Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. 44 | - **No downstream restrictions**: You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 45 | - **No endorsement**: Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). 46 | 47 | ### Other rights 48 | 49 | - **Moral rights**: Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 50 | - **Patent and trademark rights**: Patent and trademark rights are not licensed under this Public License. 51 | - **Royalties**: To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. 52 | 53 | ## Section 3 – License Conditions 54 | 55 | Your exercise of the Licensed Rights is expressly made subject to the following conditions. 56 | 57 | ### Attribution 58 | 59 | If You Share the Licensed Material (including in modified form), You must: 60 | 61 | - Retain the following if it is supplied by the Licensor with the Licensed Material: 62 | - Identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); 63 | - A copyright notice; 64 | - A notice that refers to this Public License; 65 | - A notice that refers to the disclaimer of warranties; 66 | - A URI or hyperlink to the Licensed Material to the extent reasonably practicable; 67 | - Indicate if You modified the Licensed Material and retain an indication of any previous modifications; and 68 | - Indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 69 | 70 | You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 71 | 72 | If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 73 | 74 | If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. 75 | 76 | ## Section 4 – Sui Generis Database Rights 77 | 78 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: 79 | 80 | - For the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; 81 | - If You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and 82 | - You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. 83 | 84 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. 85 | 86 | ## Section 5 – Disclaimer of Warranties and Limitation of Liability 87 | 88 | - Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. 89 | - To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. 90 | - The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. 91 | 92 | ## Section 6 – Term and Termination 93 | 94 | - This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. 95 | - Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 96 | - Automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 97 | - Upon express reinstatement by the Licensor. 98 | - For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. 99 | - For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. 100 | - Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 101 | 102 | ## Section 7 – Other Terms and Conditions 103 | 104 | - The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. 105 | - Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. 106 | 107 | ## Section 8 – Interpretation 108 | 109 | - For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. 110 | - To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. 111 | - No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. 112 | - Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snarky's Selfbot 2 | 3 | ![Project LOC](https://tokei.rs/b1/github/SnarkyDeveloper/Selfbot) ![GitHub Repo stars](https://img.shields.io/github/stars/SnarkyDeveloper/Selfbot?style=flat) 4 | 5 | This is my selfbot i made as a side project, it is probably going to stay maintained for a while. 6 | 7 | ## FAQ 8 | 9 | #### Can my discord account be ban for this? 10 | 11 | Yes, automating user accounts is against discord TOS, a normal bot rewrite is soon 12 | 13 | #### Where can I contact you? 14 | 15 | @SnarkyDev on discord 16 | 17 | 18 |
19 |

Features

20 | 21 | 22 | - [✅] Music Bot 23 | 24 | * Play 25 | * Pause 26 | * Resume 27 | * Stop 28 | * Loop 29 | * Queue 30 | 31 | - [✅] Moderation 32 | 33 | * Kick 34 | * Ban 35 | 36 | - [✅] Fun Commands 37 | * Reactions | Kiss, Hug, Slap, Bite, Tickle 38 | * Quote 39 | * Fake tweet 40 | * PetPet 41 | * Lyrics 42 | 43 | - [✅] Utility 44 | * Avatar 45 | * AI Chat and Image Generation 46 | * Full permissions system 47 | * Message snipe 48 | * Bot Stats 49 | 50 | - [✅] Economy 51 | * Balance 52 | * Work 53 | * Daily 54 | * Steal 55 | * Stripper 56 | * Mafia 57 | * Gambling 58 | * Roulette 59 | * Coinflip 60 | * Dice 61 | - [✅] Misc 62 | * Reverse Image Searching [❌] 63 | * QOTD 64 | * Polls 65 | * Github info 66 | - [❌] Spotify and music integration 67 | * Control Currently playing music 68 | * Get current song 69 |
70 | 71 | ## Coming Up 72 | 73 | - Spotify Integration for playback controls (Will require seperate flask server) 74 | 75 | ## Environment Variables 76 | 77 | To run this project, you will need to add the following environment variables to your .env file 78 | 79 | `token=your_token` (This exact format) 80 | 81 | ## Run Locally 82 | 83 | Clone the project 84 | 85 | ```bash 86 | git clone https://github.com/SnarkyDeveloper/Selfbot 87 | ``` 88 | 89 | Go to the project directory 90 | 91 | ```bash 92 | cd Selfbot 93 | ``` 94 | 95 | Create your venv 96 | 97 | ```python 98 | python -m venv venv 99 | #(Ensure its called venv or start script won't work) 100 | ``` 101 | 102 | Install dependencies 103 | 104 | ```bash 105 | pip install -r requirements.txt 106 | ``` 107 | 108 | Configure your settings 109 | 110 | ```text 111 | Just add your desired settings in settings.json 112 | ``` 113 | 114 | Rename settings.example.json to settings.json and add your desired settings 115 | 116 | Start the bot 117 | 118 | ```bash 119 | # Either python main.py 120 | # or 121 | .\start.bat 122 | .\start.sh 123 | ``` 124 | 125 | ### Setup 126 | 127 | Use a server of your own or create one and run prefixsetup, (I. E. !setup) in the channel you want your webhook messages sent. 128 | 129 | ## Contributing 130 | 131 | Contributions are always welcome! 132 | 133 | Just open a pull request! 134 | 135 | ## Authors 136 | 137 | - [@SnarkyDev](https://github.com/SnarkyDev) 138 | - Looking for more! Please DM me on discord if you want to help! 139 | 140 | ## License 141 | 142 | [MIT](https://choosealicense.com/licenses/mit/) 143 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | Rewrite cog loading entirely ✅ 4 | Add embed support ✅ 5 | 6 | ## Moderation 7 | 8 | Ban 9 | Kick 10 | Anti-Raid 11 | Roles 12 | 13 | ## Backup 14 | 15 | Save Channels 16 | Save Roles 17 | -------------------------------------------------------------------------------- /assets/quote/black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnarkyDeveloper/Selfbot/bbca365cffcc0cf2b00ee7e87fbf7044995a6fb0/assets/quote/black.png -------------------------------------------------------------------------------- /assets/quote/default_pfp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnarkyDeveloper/Selfbot/bbca365cffcc0cf2b00ee7e87fbf7044995a6fb0/assets/quote/default_pfp.png -------------------------------------------------------------------------------- /assets/quote/font.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnarkyDeveloper/Selfbot/bbca365cffcc0cf2b00ee7e87fbf7044995a6fb0/assets/quote/font.otf -------------------------------------------------------------------------------- /assets/quote/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnarkyDeveloper/Selfbot/bbca365cffcc0cf2b00ee7e87fbf7044995a6fb0/assets/quote/white.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import time, dotenv, os, asyncio, json 2 | if not os.path.exists("data"): 3 | os.system('python create_data.py') 4 | from Backend.bot import bot 5 | from Backend.groups import * 6 | from Backend.cogs import setup_cogs 7 | from Backend.events import setup_events 8 | from Backend.logger import logger 9 | dotenv.load_dotenv() 10 | token = os.getenv('token') 11 | setup_events() 12 | start_time = time.time() 13 | async def setup_groups(): 14 | bot.eco = eco 15 | async def main(): 16 | try: 17 | await setup_groups() 18 | print("Starting setup_cogs...") 19 | await setup_cogs() 20 | print("Starting bot...") 21 | await bot.start(token) 22 | except Exception as e: 23 | print(f"Error in main: {str(e)}") 24 | print(f"Error type: {type(e)}") 25 | if hasattr(e, 'args'): 26 | print(f"Error args: {e.args}") 27 | finally: 28 | await bot.close() 29 | def handle_exception(loop, context): 30 | exc = context.get('exception') 31 | if isinstance(exc, (asyncio.CancelledError, KeyboardInterrupt)) or 'KeyboardInterrupt' in str(context): 32 | loop.stop() 33 | return 34 | loop.default_exception_handler(context) 35 | 36 | if __name__ == "__main__": 37 | loop = asyncio.new_event_loop() 38 | asyncio.set_event_loop(loop) 39 | loop.set_exception_handler(handle_exception) 40 | try: 41 | loop.run_until_complete(main()) 42 | except json.JSONDecodeError as e: 43 | print(f"Error parsing settings.json: {str(e)}") 44 | except KeyboardInterrupt: 45 | pass 46 | finally: 47 | loop.close() -------------------------------------------------------------------------------- /modules.json: -------------------------------------------------------------------------------- 1 | { 2 | "loaded": { 3 | "calculator": true, 4 | "choose": true, 5 | "perms": true, 6 | "snipe": true, 7 | "music": true, 8 | "avatar": true, 9 | "quote": true, 10 | "ai": false, 11 | "reverse": true, 12 | "dictionary": true, 13 | "afk": true, 14 | "qotd": true, 15 | "lyrics": true, 16 | "statistics": true, 17 | "polls": true, 18 | "github": true, 19 | "reactions": true, 20 | "tweet": true, 21 | "petpet": true, 22 | "stealer": true, 23 | "help": true, 24 | "economy": true, 25 | "timezone": true 26 | } 27 | } -------------------------------------------------------------------------------- /settings.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "main": { 4 | "prefix": "!", 5 | "appearance": "discord.Status.dnd", 6 | "status": "Bot online!", 7 | "owner_id": "", 8 | "first_run": true 9 | }, 10 | "spotify": { 11 | "redirect_uri": "" 12 | }, 13 | "notes": { 14 | "note1": "Make sure to put the token in .env encoded in base64", 15 | "note2": "Ensure the redirect_uri ends with /code in the panel but omit it here" 16 | } 17 | } -------------------------------------------------------------------------------- /setup/create_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import sys 4 | path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | args = sys.argv 6 | if sys.argv: 7 | id = args[1] 8 | name = args[2] 9 | def get_run(): 10 | with open(f'{path}/settings.json', 'r') as f: 11 | data = json.load(f) 12 | f.close() 13 | return data.get('main').get('first_run') 14 | if get_run() == True: 15 | def create_users(): 16 | try: 17 | os.makedirs(f'{path}/data/users', exist_ok=True) 18 | with open(f'{path}/data/users/users.json', 'w') as f: 19 | data = { 20 | "users": [ 21 | { 22 | "id": id, 23 | "name": f'{name}#0', 24 | } 25 | ] 26 | } 27 | json.dump(data, f, indent=4) 28 | f.close() 29 | with open(f'{path}/data/users/afk.json', 'w') as f: 30 | f.write('{}') 31 | f.close() 32 | except Exception as e: 33 | print(f'Error creating users file: {e}') 34 | finally: 35 | input('Users file created. Hit enter to continue...') 36 | def create_punishments(): 37 | try: 38 | os.makedirs(f'{path}/data/punishments', exist_ok=True) 39 | with open(f'{path}/data/punishments/punishments.json', 'w') as f: 40 | data = { 41 | "punishments": [ 42 | 43 | ] 44 | } 45 | json.dump(data, f, indent=4) 46 | f.close() 47 | except Exception as e: 48 | print(f'Error creating punishments file: {e}') 49 | 50 | def create_messages(): 51 | try: 52 | os.makedirs(f'{path}/data/messages', exist_ok=True) 53 | with open(f'{path}/data/messages/messages.json', 'w') as f: 54 | data = { 55 | "messages": [ 56 | 57 | ] 58 | } 59 | json.dump(data, f, indent=4) 60 | f.close() 61 | except Exception as e: 62 | print(f'Error creating messages file: {e}') 63 | def create_economy(): 64 | try: 65 | os.makedirs(f'{path}/data/economy', exist_ok=True) 66 | with open(f'{path}/data/economy/eco.json', 'w') as f: 67 | data = { 68 | "users": [ 69 | ] 70 | } 71 | json.dump(data, f, indent=4) 72 | f.close() 73 | except Exception as e: 74 | print(f'Error creating economy file: {e}') 75 | def create_webhook(): 76 | try: 77 | with open(f'{path}/data/webhook.json', 'w') as f: 78 | data = { 79 | "webhook_url": "" 80 | } 81 | json.dump(data, f, indent=4) 82 | f.close() 83 | except Exception as e: 84 | print(f'Error creating webhook file: {e}') 85 | def change_run(): 86 | try: 87 | with open(f'{path}/settings.json', 'r') as f: 88 | data = json.load(f) 89 | data['main']['first_run'] = False 90 | with open(f'{path}/settings.json', 'w') as f: 91 | json.dump(data, f, indent=4) 92 | except Exception as e: 93 | print(f'Error creating run file: {e}') 94 | if __name__ == '__main__' and get_run() == True: 95 | try: 96 | create_punishments() 97 | create_messages() 98 | create_economy() 99 | create_webhook() 100 | change_run() 101 | create_users() 102 | print('Data files created.') 103 | except Exception as e: 104 | print(f'Error creating data files: {e}') 105 | else: 106 | print('Skipping data creation. Files already exist. Change first_run to true in settings.json to recreate data files.') -------------------------------------------------------------------------------- /setup/full-setup.bat: -------------------------------------------------------------------------------- 1 | git clone https://github.com/SnarkyDeveloper/Selfbot 2 | cd Selfbot 3 | ./start.bat -------------------------------------------------------------------------------- /setup/full-setup.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnarkyDeveloper/Selfbot/bbca365cffcc0cf2b00ee7e87fbf7044995a6fb0/setup/full-setup.exe -------------------------------------------------------------------------------- /setup/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohappyeyeballs==2.4.4 2 | aiohttp==3.11.9 3 | aiosignal==1.3.1 4 | annotated-types==0.7.0 5 | anyio==4.6.2.post1 6 | async-timeout==5.0.1 7 | attrs==24.2.0 8 | beautifulsoup4==4.12.3 9 | certifi==2024.8.30 10 | charset-normalizer==3.4.0 11 | colorama==0.4.6 12 | discord-protos==0.0.2 13 | discord.py-self @ git+https://github.com/dolfies/discord.py-self.git 14 | exceptiongroup==1.2.2 15 | filelock==3.16.1 16 | frozenlist==1.5.0 17 | fsspec==2024.10.0 18 | h11==0.14.0 19 | httpcore==1.0.7 20 | httpx==0.27.0 21 | idna==3.10 22 | importlib_metadata==8.5.0 23 | Jinja2==3.1.4 24 | MarkupSafe==3.0.2 25 | mpmath==1.3.0 26 | multidict==6.1.0 27 | networkx==3.4.2 28 | numpy==2.1.3 29 | packaging==24.2 30 | pillow==11.0.0 31 | propcache==0.2.1 32 | protobuf==5.29.0 33 | psutil==6.1.0 34 | pydantic==2.10.2 35 | pydantic_core==2.27.1 36 | python-dotenv==1.0.1 37 | pytube==15.0.0 38 | PyYAML==6.0.2 39 | regex==2024.11.6 40 | requests==2.32.3 41 | safetensors==0.4.5 42 | sniffio==1.3.1 43 | soupsieve==2.6 44 | sympy==1.13.1 45 | tqdm==4.67.1 46 | typing_extensions==4.12.2 47 | urllib3==2.2.3 48 | yarl==1.18.3 49 | yt-dlp==2024.11.18 50 | zipp==3.21.0 51 | PyMultiDictionary == 1.2.5 52 | PyNaCl==1.5.0 53 | lyrical==1.1.1 54 | discordwebhook.py==1.1.2 55 | ytmusicapi==1.9.1 56 | aiofiles==24.1.0 -------------------------------------------------------------------------------- /setup/setup.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os, sys 3 | import subprocess 4 | import ctypes 5 | from pathlib import Path 6 | 7 | path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 8 | print(path) 9 | base_dir = Path(__file__).parent.absolute() 10 | create_data_path = os.path.join(base_dir, 'setup', 'create_data.py') 11 | class AdminStateUnknownError(Exception): 12 | pass 13 | 14 | if os.path.exists(f'{path}/settings.example.json') and not os.path.exists(f'{path}/settings.json'): 15 | os.rename(f'{path}/settings.example.json', f'{path}/settings.json') 16 | 17 | def read_settings(): 18 | with open(f'{path}/settings.json', 'r') as f: 19 | data = json.load(f) 20 | return data 21 | 22 | def is_user_admin(): 23 | try: 24 | return os.getuid() == 0 25 | except AttributeError: 26 | pass 27 | try: 28 | return ctypes.windll.shell32.IsUserAnAdmin() == 1 29 | except AttributeError: 30 | raise AdminStateUnknownError 31 | 32 | def create_venv(): 33 | venv_path = os.path.join(path, 'venv') 34 | python_executable = 'python' 35 | 36 | try: 37 | subprocess.run([python_executable, '-m', 'venv', str(venv_path)], check=True) 38 | print('Venv created successfully') 39 | return True 40 | except subprocess.CalledProcessError: 41 | try: 42 | subprocess.run(['py', '-m', 'venv', str(venv_path)], check=True) 43 | print('Venv created successfully using py command') 44 | return True 45 | except subprocess.CalledProcessError: 46 | print('Failed to create venv with both python and py commands') 47 | return False 48 | 49 | args = sys.argv 50 | if '--no-setup' in args: 51 | print('Skipping setup...') 52 | exit() 53 | if '--setup' in args: 54 | print('Running setup...') 55 | subprocess.run(['python', 'setup.py']) 56 | exit() 57 | 58 | if not is_user_admin(): 59 | if os.name == 'nt': 60 | subprocess.run(['powershell', '-Command', 'Start-Process', 'python', '-ArgumentList', './setup.py', '-Verb', 'RunAs']) 61 | 62 | new_requirements = [] 63 | settings = read_settings() 64 | 65 | class setup_ai: 66 | def __init__(self): 67 | self.requirements = new_requirements 68 | self.reqs = ['diffusers==0.31.0', 'accelerate==1.1.1', 'huggingface-hub==0.26.3', 'tokenizers==0.20.3', 'transformers==4.46.3', 'torch==2.5.1', 'torchaudio==2.5.1', 'torchvision==0.20.1', 'sentencepiece==0.2.0'] 69 | 70 | def add_reqs(self): 71 | self.requirements += self.reqs 72 | requirements_path = base_dir / 'requirements.txt' 73 | with open(requirements_path, 'r+') as f: 74 | for line in f: 75 | new_requirements.append(line.strip()) 76 | 77 | modules_path = base_dir.parent / 'modules.json' 78 | with open(modules_path, 'r+') as m: 79 | data = json.load(m) 80 | data['ai'] = True 81 | m.seek(0) 82 | json.dump(data, m, indent=4) 83 | m.truncate() 84 | 85 | if settings.get("main").get("first_run") == True: 86 | print("First run detected. Running setup...") 87 | print(settings["main"].get("first_run")) 88 | venv_path = os.path.join(path, 'venv') 89 | if os.path.exists(venv_path): 90 | print('Venv already exists.') 91 | else: 92 | print('Creating venv...') 93 | create_venv() 94 | 95 | print('Creating data directory...') 96 | os.makedirs(path+'data', exist_ok=True) 97 | 98 | ai = input('Do you want AI features enabled? Not recommended for low end computers/servers. Must have NVIDIA GPU (y/N):').lower() 99 | if ai != 'y': 100 | ai = 'n' 101 | if ai == 'y': 102 | setup_ai().add_reqs() 103 | print('AI features enabled.') 104 | 105 | requirements_path = base_dir / 'requirements.txt' 106 | if requirements_path.exists(): 107 | subprocess.run([sys.executable, '-m', 'pip', 'install', '-r', str(requirements_path)]) 108 | 109 | id = input('Enter your user ID: ') 110 | name = input('Enter your username: ') 111 | subprocess.run([sys.executable, os.path.join(base_dir, 'create_data.py'), id, name.lower()]) 112 | print('Data files created.') 113 | print('Setup complete.') 114 | print('Starting bot...') 115 | print('Ensure to run >setup after the bot starts to configure the bot.') 116 | 117 | subprocess.run([sys.executable, 'main.py']) 118 | -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | set args=%* 2 | if "%args%"=="" ( 3 | set args=--no-args 4 | ) 5 | 6 | @echo off 7 | if "%args%"=="--no-args" ( 8 | call .\venv\Scripts\activate.bat 9 | python "./setup/setup.py" 10 | ) 11 | if "%args%"=="--no-pip" ( 12 | echo skipping... 13 | ) 14 | timeout /t 2 /nobreak > nul 15 | 16 | call .\venv\Scripts\activate.bat 17 | python main.py -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source ./venv/bin/activate 3 | ARGS=$@ 4 | if [ -z "$ARGS" ] 5 | then 6 | ARGS="--no-args" 7 | fi 8 | 9 | if [ "$ARGS" == "--no-args" ] 10 | then 11 | source ./venv/bin/activate 12 | python ./setup/setup.py 13 | fi 14 | 15 | if [ "$ARGS" == "--no-pip" ] 16 | then 17 | echo "skipping..." 18 | fi 19 | 20 | sleep 2 21 | 22 | source ./venv/bin/activate 23 | python ./main.py 24 | 25 | # Obama 26 | # - Typera, 2024 --------------------------------------------------------------------------------