diff --git a/.replit b/.replit deleted file mode 100644 index 6ab0ace..0000000 --- a/.replit +++ /dev/null @@ -1,3 +0,0 @@ -run="python3 bot.py" -language="python3" -onBoot="pip3 install -r requirements.txt" \ No newline at end of file diff --git a/bot.py b/bot.py index 65dd1d3..4200212 100644 --- a/bot.py +++ b/bot.py @@ -1,46 +1,47 @@ -import discord -import time -import aiohttp -from discord.ext import commands -import os -import json import asyncio -from mcstatus import MinecraftServer -import traceback +import itertools +import json +import os import pathlib +import time +import traceback + +import aiohttp +import discord +import mcstatus +from discord.ext import commands class MinearchyBot(commands.Bot): session: aiohttp.ClientSession + suggestions_channel: discord.TextChannel log_webhook: discord.Webhook up_ts: float embed_color = 0x3500FF - def __init__(self, token: str, webhook_url: str, /) -> None: + def __init__( + self, *, token: str, webhook_url: str, suggestions_channel_id: int + ) -> None: ip = "play.landsofminearchy.com" - self.mc_server = MinecraftServer.lookup(ip) + self.mc_server = mcstatus.JavaServer.lookup(ip) self.mc_server.ip = ip self.mc_server.bedrock_ip = "bedrock.landsofminearchy.com" self.token = token self.webhook_url = webhook_url - - intents = discord.Intents( - guilds=True, - members=True, - messages=True, - message_content=True, - ) - stuff_to_cache = discord.MemberCacheFlags.from_intents(intents) - + self.suggestions_channel_id = suggestions_channel_id super().__init__( command_prefix="=", owner_ids=set([512640455834337290]), - intents=intents, + intents=discord.Intents( + guilds=True, + members=True, + messages=True, + message_content=True, + ), case_insensitive=True, allowed_mentions=discord.AllowedMentions.none(), - member_cache_flags=stuff_to_cache, max_messages=1000, strip_after_prefix=True, help_attrs=dict( @@ -51,13 +52,17 @@ class MinearchyBot(commands.Bot): async def on_ready(self) -> None: self.up_ts = time.time() + self.suggestions_channel = self.get_channel(self.suggestions_channel_id) print(f"\nConnected to Discord!\nUser: {self.user}\nID: {self.user.id}") await self.log_webhook.send("Bot is now online!") async def load_extensions(self) -> None: - for fn in map( - lambda file_path: str(file_path).replace("/", ".")[:-3], - pathlib.Path("./cogs").rglob("*.py"), + for fn in itertools.chain( + map( + lambda file_path: str(file_path).replace("/", ".")[:-3], + pathlib.Path("./cogs").rglob("*.py"), + ), + ["jishaku"], ): try: await self.load_extension(fn) @@ -87,11 +92,10 @@ with open("./config.json") as f: for key in ["BOT_TOKEN", "WEBHOOK_URL"]: config.setdefault(key, os.getenv(key)) -bot = MinearchyBot(config["BOT_TOKEN"], config["WEBHOOK_URL"]) - -if os.getenv("USING_REPLIT"): - import webserver - - webserver.keep_alive() +bot = MinearchyBot( + token=config["BOT_TOKEN"], + webhook_url=config["WEBHOOK_URL"], + suggestions_channel_id=config["SUGGESTIONS_CHANNEL_ID"], +) bot.run() diff --git a/cogs/error_handler.py b/cogs/error_handler.py index 5d0941c..6fedd2c 100644 --- a/cogs/error_handler.py +++ b/cogs/error_handler.py @@ -1,22 +1,22 @@ from __future__ import annotations -from typing import TYPE_CHECKING -from discord.ext import commands -import discord -import sys import traceback +from typing import TYPE_CHECKING + +import discord +from discord.ext import commands if TYPE_CHECKING: from bot import MinearchyBot class ErrorHandler(commands.Cog): - def __init__(self, bot: MinearchyBot, /) -> None: + def __init__(self, bot: MinearchyBot) -> None: self.bot = bot @commands.Cog.listener() async def on_command_error( - self, ctx: commands.Context, error: commands.CommandError, / + self, ctx: commands.Context, error: commands.CommandError ) -> None: if hasattr(ctx.command, "on_error"): return @@ -42,18 +42,17 @@ class ErrorHandler(commands.Cog): elif isinstance(error, commands.MissingPermissions): await ctx.reply("You can't use this command!") + elif isinstance(error, commands.MissingRequiredArgument): + await ctx.reply(f"Missing a required argument: `{error.param.name}`") + elif isinstance(error, commands.ChannelNotFound): await ctx.reply("Invalid channel.") else: - trace = traceback.format_exception( - type(error), error, error.__traceback__ - ) - print(f"Ignoring exception in command {ctx.command}:\n{trace}") - await self.bot.log_webhook.send( - f"<@512640455834337290>```{trace}```" - ) + trace = traceback.format_exception(type(error), error, error.__traceback__) + print(f"Ignoring exception in command {ctx.command}:\n{''.join(trace)}") + await self.bot.log_webhook.send(f"<@512640455834337290>```{trace}```") -async def setup(bot: MinearchyBot, /) -> None: +async def setup(bot: MinearchyBot) -> None: await bot.add_cog(ErrorHandler(bot)) diff --git a/cogs/mc_server.py b/cogs/mc_server.py index a94d439..7471aab 100644 --- a/cogs/mc_server.py +++ b/cogs/mc_server.py @@ -1,8 +1,9 @@ from __future__ import annotations from typing import TYPE_CHECKING -from discord.ext import commands + import discord +from discord.ext import commands if TYPE_CHECKING: from bot import MinearchyBot @@ -13,7 +14,7 @@ class MinecraftServer( name="Minecraft Server", description="Utilites for the Minecraft server.", ): - def __init__(self, bot: MinearchyBot, /) -> None: + def __init__(self, bot: MinearchyBot) -> None: self.bot = bot @commands.group( @@ -21,15 +22,13 @@ class MinecraftServer( brief="Sends the server IP.", help="Sends the server IP.", ) - async def ip(self, ctx: commands.Context, /) -> None: + async def ip(self, ctx: commands.Context) -> None: await ctx.reply( f"Java edition IP: `{self.bot.mc_server.ip}`\nBedrock edition IP: `{self.bot.mc_server.bedrock_ip}` (Port: 19132)\nNote: Minecraft 1.18+ is required to join." ) - @ip.command( - brief="Sends the Java edition IP.", help="Sends the Java edition IP." - ) - async def java(self, ctx: commands.Context, /) -> None: + @ip.command(brief="Sends the Java edition IP.", help="Sends the Java edition IP.") + async def java(self, ctx: commands.Context) -> None: await ctx.reply( f"The IP to connect on Minecraft Java edition is `{self.bot.mc_server.ip}`\nNote: Minecraft 1.18+ is required to join." ) @@ -38,7 +37,7 @@ class MinecraftServer( brief="Sends the Bedrock edition IP.", help="Sends the Bedrock edition IP.", ) - async def bedrock(self, ctx: commands.Context, /) -> None: + async def bedrock(self, ctx: commands.Context) -> None: await ctx.reply( f"The IP to connect on Minecraft Bedrock edition is `{self.bot.mc_server.bedrock_ip}` (Port: 19132)\nNote: Minecraft 1.18+ is required to join." ) @@ -47,7 +46,7 @@ class MinecraftServer( brief="Shows information about the Minecraft server.", help="Shows the total player count, the Minecraft server IP and the server latency.", ) - async def status(self, ctx: commands.Context, /) -> None: + async def status(self, ctx: commands.Context) -> None: server = self.bot.mc_server status = server.status() await ctx.reply( @@ -58,7 +57,7 @@ class MinecraftServer( @commands.command( brief="Sends the link to the wiki.", help="Sends the link to the wiki." ) - async def wiki(self, ctx: commands.Context, /) -> None: + async def wiki(self, ctx: commands.Context) -> None: view = discord.ui.View() view.add_item( discord.ui.Button( @@ -72,7 +71,7 @@ class MinecraftServer( brief="Sends the link to the store.", help="Sends the link to the store.", ) - async def store(self, ctx: commands.Context, /) -> None: + async def store(self, ctx: commands.Context) -> None: view = discord.ui.View() view.add_item( discord.ui.Button( diff --git a/cogs/misc.py b/cogs/misc.py index 6f3c6e0..bc16419 100644 --- a/cogs/misc.py +++ b/cogs/misc.py @@ -1,15 +1,15 @@ from __future__ import annotations -from typing import TYPE_CHECKING -from discord.ext import commands import datetime -from collections import defaultdict, deque -import discord import inspect -from discord.utils import escape_markdown as es_md import platform - import time +from collections import defaultdict, deque +from typing import TYPE_CHECKING + +import discord +from discord.ext import commands +from discord.utils import escape_markdown as es_md if TYPE_CHECKING: from bot import MinearchyBot @@ -20,7 +20,7 @@ class Miscellanious( name="Miscellanious", description="Various utilities.", ): - def __init__(self, bot: MinearchyBot, /) -> None: + def __init__(self, bot: MinearchyBot) -> None: self.bot = bot self.bot.help_command.cog = self self.sniped = defaultdict(deque) @@ -30,7 +30,7 @@ class Miscellanious( self.bot.help_command.hidden = True @commands.command(brief="Sends the bots ping.", help="Sends the bots ping.") - async def ping(self, ctx: commands.Context, /) -> None: + async def ping(self, ctx: commands.Context) -> None: ts = time.time() message = await ctx.reply("Pong!") ts = time.time() - ts @@ -39,7 +39,7 @@ class Miscellanious( @commands.command( brief="Sends info about the bot.", help="Sends info about the bot." ) - async def info(self, ctx: commands.Context, /) -> None: + async def info(self, ctx: commands.Context) -> None: embed = discord.Embed(title="Bot Info", color=self.bot.embed_color) embed.add_field( name="Python Version", value=f"```v{platform.python_version()}```" @@ -51,21 +51,19 @@ class Miscellanious( await ctx.reply(embed=embed) @commands.command(brief="Hello!", help="Hello!") - async def hello(self, ctx: commands.Context, /) -> None: - await ctx.reply( - f"Hi {es_md(ctx.author.name)}, yes the bot is running :)" - ) + async def hello(self, ctx: commands.Context) -> None: + await ctx.reply(f"Hi {es_md(ctx.author.name)}, yes the bot is running :)") @commands.command( aliases=["server_count", "server-count"], brief="Sends how many servers the bot is in.", help="Sends how many servers the bot is in.", ) - async def count(self, ctx: commands.Context, /) -> None: + async def count(self, ctx: commands.Context) -> None: await ctx.reply(f"Currently in `{len(self.bot.guilds)}` servers.") @commands.Cog.listener() - async def on_message_delete(self, message: discord.Message, /) -> None: + async def on_message_delete(self, message: discord.Message) -> None: if not message.guild: return @@ -84,7 +82,7 @@ class Miscellanious( manage_messages=True ) # needs to be able to delete messages to run the command async def snipe( - self, ctx: commands.Context, channel: discord.TextChannel = None, / + self, ctx: commands.Context, channel: discord.TextChannel = None ) -> None: if channel is None: channel = ctx.channel diff --git a/cogs/suggestions.py b/cogs/suggestions.py new file mode 100644 index 0000000..35ea52c --- /dev/null +++ b/cogs/suggestions.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import discord +from discord.ext import commands + +if TYPE_CHECKING: + from bot import MinearchyBot + + +class Suggestions(commands.Cog): + def __init__(self, bot: MinearchyBot) -> None: + self.bot = bot + + @commands.command(brief="Make a suggestion.", help="Make a suggestion.") + async def suggest(self, ctx: commands.Context, *, suggestion: str) -> None: + embed = ( + discord.Embed( + title=f"Suggestion from {ctx.author}", + description=suggestion, + color=self.bot.embed_color, + ) + .set_thumbnail(url=ctx.author.display_avatar.url) + .set_footer(text="=suggest ") + ) + + message = await self.bot.suggestions_channel.send(embed=embed) + + await message.add_reaction("✅") + await message.add_reaction("❌") + + await ctx.reply( + f"Suggestion submitted!\nYou can view it at {self.bot.suggestions_channel.mention}" + ) + + +async def setup(bot: MinearchyBot) -> None: + await bot.add_cog(Suggestions(bot)) diff --git a/config.example.json b/config.example.json index a412796..9af60e1 100644 --- a/config.example.json +++ b/config.example.json @@ -3,5 +3,6 @@ "JAVA_IP": "play.landsofminearchy.com", "BOT_PREFIX": "=", "BOT_TOKEN": "XXX(ENV works too)", - "WEBHOOK_URL": "XXX(ENV works too)" + "WEBHOOK_URL": "XXX(ENV works too)", + "SUGGESTIONS_CHANNEL_ID": 955972394885984276 } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8d8812c..f62b521 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ discord.py @ git+https://github.com/Rapptz/discord.py +jishaku==2.5.0 mcstatus \ No newline at end of file diff --git a/webserver.py b/webserver.py deleted file mode 100644 index 4b179a1..0000000 --- a/webserver.py +++ /dev/null @@ -1,17 +0,0 @@ -from flask import Flask -from threading import Thread - -app = Flask(__name__) - - -@app.route("/") -def root() -> str: - return "Online!" - - -def run() -> None: - app.run(host="0.0.0.0", port=8080) - - -def keep_alive() -> None: - Thread(target=run).start()