diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..b96cada --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: python main.py \ No newline at end of file diff --git a/README.md b/README.md index 07e4f04..0629a2d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,13 @@


+ + + + + + Support + @@ -22,10 +29,47 @@ 🤝 The major features of the bot are Moderation, Fun, Utility, Games, etc. - based commands.
🍁 The bot is user-friendly too, making comfortable for everyone to get familiar with discord bots and stuff.

+ +> Make Sure to ⭐ the Repo if You liked it -- It Helps!
-
+## 📋 Features + +⚡️ Open-Source\ +⚡️ Contains Cogs for Easy Management of Code\ +⚡ Open to Contributions\ +⚡ Direct Deployable to Heroku\ +⚡ Actively Maintained\ +⚡ Contains Constant New Features of Discord\ +⚡️ Easy to modify + +## 🚀 Contributing + +### Step 1: Clone The Repo 💡 + +Fork the repository and then clone it locally by doing - + +```bash +git clone https://github.com/TheKaushikGoswami/Olympus.git +``` + +### Step 2: Build Your Code 🔨 + +Start your magic by modifying the code and making changes of your own. Then push the commit using - + +```bash +git add . +git commit -m "" +git push YOUR_REPO_URL develop +``` + +### Step 3: Create a new pull request 🔃 + +After cloning & setting up the local project you can push the changes to your github fork and make a pull request. ## Authors ✒️ -- [@TheKaushikGoswami](https://github.com/TheKaushikGoswami) \ No newline at end of file +- [@TheKaushikGoswami](https://github.com/TheKaushikGoswami) +------ + +Made with :heart: in India \ No newline at end of file diff --git a/cogs/activities.py b/cogs/activities.py new file mode 100644 index 0000000..716244b --- /dev/null +++ b/cogs/activities.py @@ -0,0 +1,37 @@ +from discord import slash_command, Option +from discord.ext import commands +from discord.ext.commands.errors import MissingPermissions, BotMissingPermissions + +class Activities(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @slash_command() + @commands.has_permissions(start_embedded_activities=True) + async def activity(self, ctx, activity: Option(str, "Choose the Activity You wanna play in the Voice Channel", choices=["Watch Together", "Poker Night", "Chess in the Park", "Betrayal.io", "Fishington.io", "Letter Tile", "Word Snack", "Doodle Crew", "SpellCast", "Awkword", "Checkers in the Park"], required=True)): + """🚀 Let's Have Some Fun Activities Together""" + + try: + activities = {"Watch Together":'youtube', "Poker Night":'poker', "Chess in the Park":'chess', "Betrayal.io":'betrayal', "Fishington.io":'fishington', "Letter Tile":'letter-tile', "Word Snack": 'word-snack', "Doodle Crew":'doodle-crew', "SpellCast":'spellcast', "Awkword":'awkword', "Checkers in the Park":'checkers'} + + final_activity = activities.get(activity) + if ctx.author.voice == None: + await ctx.respond(f"**<:Cross:902943066724388926> You Need To Join a Voice Channel in order to Start An Activity!**", ephemeral=True) + else: + link = await self.bot.togetherControl.create_link(ctx.author.voice.channel.id, final_activity) + await ctx.respond(f"Click the Blue Link!\n{link}") + except Exception as e: + print(e) + + @activity.error + async def activity_error(self, ctx, error): + if isinstance(error, ConnectionError): + await ctx.respond(f"**<:Cross:902943066724388926> Oops! There was an issue connecting to Discord API!**") + elif isinstance(error, MissingPermissions): + await ctx.respond(f"**<:Cross:902943066724388926> You need `Start Activities` permission to be able to use this command.**") + elif isinstance(error, BotMissingPermissions): + await ctx.respond(f"**<:Cross:902943066724388926> I don't have enough permissions to Launch the Activity**") + +def setup(bot): + bot.add_cog(Activities(bot)) + print("Activities Cog is Loaded\n------") \ No newline at end of file diff --git a/cogs/api_cmd.py b/cogs/api_cmd.py index 2e41fb3..cafe9d0 100644 --- a/cogs/api_cmd.py +++ b/cogs/api_cmd.py @@ -1,4 +1,3 @@ -from datetime import datetime import discord from discord.ext import commands import io @@ -6,7 +5,6 @@ from discord.commands.commands import Option, slash_command from discord.ext import commands import aiohttp -from discord.ext.commands.errors import MissingPermissions import requests class Api(commands.Cog): diff --git a/cogs/fun.py b/cogs/fun.py index b757e1e..1f55cc4 100644 --- a/cogs/fun.py +++ b/cogs/fun.py @@ -562,7 +562,6 @@ async def snap(self, ctx, member: Option(discord.Member, "Choose the Member", re # setup COMMAND - def setup(bot): bot.add_cog(Fun(bot)) print("Fun Cog is Loaded\n------") diff --git a/cogs/lavalink.py b/cogs/lavalink.py deleted file mode 100644 index c16aa78..0000000 --- a/cogs/lavalink.py +++ /dev/null @@ -1,239 +0,0 @@ -import re -import discord -import lavalink -from discord.ext import commands - -url_rx = re.compile(r'https?://(?:www\.)?.+') - - -class LavalinkVoiceClient(discord.VoiceClient): - """ - This is the preferred way to handle external voice sending - This client will be created via a cls in the connect method of the channel - see the following documentation: - https://discordpy.readthedocs.io/en/latest/api.html#voiceprotocol - """ - - def __init__(self, client: discord.Client, channel: discord.abc.Connectable): - self.client = client - self.channel = channel - # ensure there exists a client already - if hasattr(self.client, 'lavalink'): - self.lavalink = self.client.lavalink - else: - self.client.lavalink = lavalink.Client(client.user.id) - self.client.lavalink.add_node( - 'localhost', - 2333, - 'youshallnotpass', - 'us', - 'default-node') - self.lavalink = self.client.lavalink - - async def on_voice_server_update(self, data): - # the data needs to be transformed before being handed down to - # voice_update_handler - lavalink_data = { - 't': 'VOICE_SERVER_UPDATE', - 'd': data - } - await self.lavalink.voice_update_handler(lavalink_data) - - async def on_voice_state_update(self, data): - # the data needs to be transformed before being handed down to - # voice_update_handler - lavalink_data = { - 't': 'VOICE_STATE_UPDATE', - 'd': data - } - await self.lavalink.voice_update_handler(lavalink_data) - - async def connect(self, *, timeout: float, reconnect: bool) -> None: - """ - Connect the bot to the voice channel and create a player_manager - if it doesn't exist yet. - """ - # ensure there is a player_manager when creating a new voice_client - self.lavalink.player_manager.create(guild_id=self.channel.guild.id) - await self.channel.guild.change_voice_state(channel=self.channel) - - async def disconnect(self, *, force: bool) -> None: - """ - Handles the disconnect. - Cleans up running player and leaves the voice client. - """ - player = self.lavalink.player_manager.get(self.channel.guild.id) - - # no need to disconnect if we are not connected - if not force and not player.is_connected: - return - - # None means disconnect - await self.channel.guild.change_voice_state(channel=None) - - # update the channel_id of the player to None - # this must be done because the on_voice_state_update that - # would set channel_id to None doesn't get dispatched after the - # disconnect - player.channel_id = None - self.cleanup() - - -class Music(commands.Cog): - def __init__(self, bot): - self.bot = bot - - if not hasattr(bot, 'lavalink'): # This ensures the client isn't overwritten during cog reloads. - bot.lavalink = lavalink.Client(bot.user.id) - bot.lavalink.add_node('127.0.0.1', 2333, 'youshallnotpass', 'eu', 'default-node') # Host, Port, Password, Region, Name - - lavalink.add_event_hook(self.track_hook) - - def cog_unload(self): - """ Cog unload handler. This removes any event hooks that were registered. """ - self.bot.lavalink._event_hooks.clear() - - async def cog_before_invoke(self, ctx): - """ Command before-invoke handler. """ - guild_check = ctx.guild is not None - # This is essentially the same as `@commands.guild_only()` - # except it saves us repeating ourselves (and also a few lines). - - if guild_check: - await self.ensure_voice(ctx) - # Ensure that the bot and command author share a mutual voicechannel. - - return guild_check - - async def cog_command_error(self, ctx, error): - if isinstance(error, commands.CommandInvokeError): - await ctx.send(error.original) - # The above handles errors thrown in this cog and shows them to the user. - # This shouldn't be a problem as the only errors thrown in this cog are from `ensure_voice` - # which contain a reason string, such as "Join a voicechannel" etc. You can modify the above - # if you want to do things differently. - - async def ensure_voice(self, ctx): - """ This check ensures that the bot and command author are in the same voicechannel. """ - player = self.bot.lavalink.player_manager.create(ctx.guild.id, endpoint=str(ctx.guild.region)) - # Create returns a player if one exists, otherwise creates. - # This line is important because it ensures that a player always exists for a guild. - - # Most people might consider this a waste of resources for guilds that aren't playing, but this is - # the easiest and simplest way of ensuring players are created. - - # These are commands that require the bot to join a voicechannel (i.e. initiating playback). - # Commands such as volume/skip etc don't require the bot to be in a voicechannel so don't need listing here. - should_connect = ctx.command.name in ('play',) - - if not ctx.author.voice or not ctx.author.voice.channel: - # Our cog_command_error handler catches this and sends it to the voicechannel. - # Exceptions allow us to "short-circuit" command invocation via checks so the - # execution state of the command goes no further. - raise commands.CommandInvokeError('Join a voicechannel first.') - - if not player.is_connected: - if not should_connect: - raise commands.CommandInvokeError('Not connected.') - - permissions = ctx.author.voice.channel.permissions_for(ctx.me) - - if not permissions.connect or not permissions.speak: # Check user limit too? - raise commands.CommandInvokeError('I need the `CONNECT` and `SPEAK` permissions.') - - player.store('channel', ctx.channel.id) - await ctx.author.voice.channel.connect(cls=LavalinkVoiceClient) - else: - if int(player.channel_id) != ctx.author.voice.channel.id: - raise commands.CommandInvokeError('You need to be in my voicechannel.') - - async def track_hook(self, event): - if isinstance(event, lavalink.events.QueueEndEvent): - # When this track_hook receives a "QueueEndEvent" from lavalink.py - # it indicates that there are no tracks left in the player's queue. - # To save on resources, we can tell the bot to disconnect from the voicechannel. - guild_id = int(event.player.guild_id) - guild = self.bot.get_guild(guild_id) - await guild.voice_client.disconnect(force=True) - - @commands.command(aliases=['p']) - async def play(self, ctx, *, query: str): - """ Searches and plays a song from a given query. """ - # Get the player for this guild from cache. - player = self.bot.lavalink.player_manager.get(ctx.guild.id) - # Remove leading and trailing <>. <> may be used to suppress embedding links in Discord. - query = query.strip('<>') - - # Check if the user input might be a URL. If it isn't, we can Lavalink do a YouTube search for it instead. - # SoundCloud searching is possible by prefixing "scsearch:" instead. - if not url_rx.match(query): - query = f'ytsearch:{query}' - - # Get the results for the query from Lavalink. - results = await player.node.get_tracks(query) - - # Results could be None if Lavalink returns an invalid response (non-JSON/non-200 (OK)). - # ALternatively, resullts['tracks'] could be an empty array if the query yielded no tracks. - if not results or not results['tracks']: - return await ctx.send('Nothing found!') - - embed = discord.Embed(color=discord.Color.blurple()) - - # Valid loadTypes are: - # TRACK_LOADED - single video/direct URL) - # PLAYLIST_LOADED - direct URL to playlist) - # SEARCH_RESULT - query prefixed with either ytsearch: or scsearch:. - # NO_MATCHES - query yielded no results - # LOAD_FAILED - most likely, the video encountered an exception during loading. - if results['loadType'] == 'PLAYLIST_LOADED': - tracks = results['tracks'] - - for track in tracks: - # Add all of the tracks from the playlist to the queue. - player.add(requester=ctx.author.id, track=track) - - embed.title = 'Playlist Enqueued!' - embed.description = f'{results["playlistInfo"]["name"]} - {len(tracks)} tracks' - else: - track = results['tracks'][0] - embed.title = 'Track Enqueued' - embed.description = f'[{track["info"]["title"]}]({track["info"]["uri"]})' - - # You can attach additional information to audiotracks through kwargs, however this involves - # constructing the AudioTrack class yourself. - track = lavalink.models.AudioTrack(track, ctx.author.id, recommended=True) - player.add(requester=ctx.author.id, track=track) - - await ctx.send(embed=embed) - - # We don't want to call .play() if the player is playing as that will effectively skip - # the current track. - if not player.is_playing: - await player.play() - - @commands.command(aliases=['dc']) - async def disconnect(self, ctx): - """ Disconnects the player from the voice channel and clears its queue. """ - player = self.bot.lavalink.player_manager.get(ctx.guild.id) - - if not player.is_connected: - # We can't disconnect, if we're not connected. - return await ctx.send('Not connected.') - - if not ctx.author.voice or (player.is_connected and ctx.author.voice.channel.id != int(player.channel_id)): - # Abuse prevention. Users not in voice channels, or not in the same voice channel as the bot - # may not disconnect the bot. - return await ctx.send('You\'re not in my voicechannel!') - - # Clear the queue to ensure old tracks don't start playing - # when someone else queues something. - player.queue.clear() - # Stop the current track so Lavalink consumes less resources. - await player.stop() - # Disconnect from the voice channel. - await ctx.voice_client.disconnect(force=True) - await ctx.send('*⃣ | Disconnected.') - - -def setup(bot): - bot.add_cog(Music(bot)) \ No newline at end of file diff --git a/cogs/mod.py b/cogs/mod.py index d574950..ccceda5 100644 --- a/cogs/mod.py +++ b/cogs/mod.py @@ -1,10 +1,10 @@ import discord -from discord.commands.commands import Option, message_command +from discord.commands.commands import Option from discord.commands.errors import ApplicationCommandInvokeError -from discord.errors import Forbidden, HTTPException, NotFound +from discord.errors import NotFound from discord.ext import commands from discord.commands import slash_command -from discord.ext.commands.errors import MissingPermissions, NotOwner +from discord.ext.commands.errors import MissingPermissions import asyncio class Mod(commands.Cog): diff --git a/cogs/olympus.py b/cogs/olympus.py index ef4d759..4a05bdf 100644 --- a/cogs/olympus.py +++ b/cogs/olympus.py @@ -1,12 +1,9 @@ import discord, datetime, time -from discord.commands.commands import command, slash_command +from discord.commands.commands import slash_command from discord.ext import commands -import sys import datetime -import random import platform import time -from discord.ext.commands import bot import discord.utils start_time = time.time() diff --git a/requirements.txt b/requirements.txt index 8e9824b..73dacb8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ ago == 0.0.93 aiohttp == 3.7.4.post0 disputils == 0.2.0 requests == 2.25.1 +discord-together # For Installing py-cord use: git+https://github.com/Pycord-Development/pycord