Browse Source

Strip Komet of all of it's useful stuff and leave all the cringe and memes.

main
Nichole Mattera 4 months ago
parent
commit
84b10dc7cf
30 changed files with 314 additions and 4492 deletions
  1. +75
    -136
      Robocop.py
  2. +19
    -74
      cogs/admin.py
  3. +31
    -26
      cogs/basic.py
  4. +53
    -42
      cogs/common.py
  5. +0
    -196
      cogs/err.py
  6. +0
    -43
      cogs/invites.py
  7. +29
    -45
      cogs/links.py
  8. +7
    -23
      cogs/lists.py
  9. +0
    -263
      cogs/lists_verification.py
  10. +0
    -90
      cogs/lockdown.py
  11. +0
    -353
      cogs/logs.py
  12. +76
    -50
      cogs/meme.py
  13. +0
    -551
      cogs/mod.py
  14. +0
    -143
      cogs/mod_mail.py
  15. +0
    -32
      cogs/mod_note.py
  16. +0
    -110
      cogs/mod_reacts.py
  17. +0
    -160
      cogs/mod_stats.py
  18. +0
    -137
      cogs/mod_timed.py
  19. +0
    -221
      cogs/mod_userlog.py
  20. +0
    -46
      cogs/mod_watch.py
  21. +0
    -157
      cogs/pin.py
  22. +0
    -68
      cogs/remind.py
  23. +0
    -153
      cogs/robocronp.py
  24. +1
    -0
      cogs/uwu.py
  25. +19
    -101
      config_template.py
  26. +4
    -6
      helpers/checks.py
  27. +0
    -1115
      helpers/errcodes.py
  28. +0
    -42
      helpers/restrictions.py
  29. +0
    -37
      helpers/robocronp.py
  30. +0
    -72
      helpers/userlogs.py

+ 75
- 136
Robocop.py View File

@ -1,34 +1,20 @@
import os
import asyncio
import sys
import logging
import logging.handlers
import traceback
import aiohttp
import config
import discord
from discord.ext import commands
script_name = os.path.basename(__file__).split('.')[0]
log_file_name = f"{script_name}.log"
# Limit of discord (non-nitro) is 8MB (not MiB)
max_file_size = 1000 * 1000 * 8
backup_count = 3
file_handler = logging.handlers.RotatingFileHandler(
filename=log_file_name, maxBytes=max_file_size, backupCount=backup_count)
stdout_handler = logging.StreamHandler(sys.stdout)
log_format = logging.Formatter(
'[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s')
file_handler.setFormatter(log_format)
"[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s"
)
stdout_handler.setFormatter(log_format)
log = logging.getLogger('discord')
log = logging.getLogger("discord")
log.setLevel(logging.INFO)
log.addHandler(file_handler)
log.addHandler(stdout_handler)
@ -38,87 +24,49 @@ def get_prefix(bot, message):
return commands.when_mentioned_or(*prefixes)(bot, message)
wanted_jsons = ["data/restrictions.json",
"data/robocronptab.json",
"data/userlog.json",
"data/invites.json"]
initial_extensions = ['cogs.common',
'cogs.admin',
'cogs.mod',
'cogs.mod_mail',
'cogs.mod_note',
'cogs.mod_reacts',
'cogs.mod_stats',
'cogs.mod_userlog',
'cogs.mod_timed',
'cogs.mod_watch',
'cogs.basic',
'cogs.logs',
'cogs.lockdown',
'cogs.links',
'cogs.lists',
'cogs.lists_verification',
'cogs.remind',
'cogs.robocronp',
'cogs.meme',
'cogs.uwu']
bot = commands.Bot(command_prefix=get_prefix,
description=config.bot_description, pm_help=True)
initial_extensions = [
"cogs.common",
"cogs.admin",
"cogs.basic",
"cogs.links",
"cogs.lists",
"cogs.meme",
"cogs.uwu",
]
bot = commands.Bot(
command_prefix=get_prefix, description=config.bot_description, pm_help=True
)
bot.log = log
bot.loop = asyncio.get_event_loop()
bot.config = config
bot.script_name = script_name
bot.wanted_jsons = wanted_jsons
if __name__ == '__main__':
if __name__ == "__main__":
for extension in initial_extensions:
try:
bot.load_extension(extension)
except Exception as e:
log.error(f'Failed to load extension {extension}.')
log.error(f"Failed to load extension {extension}.")
log.error(traceback.print_exc())
@bot.event
async def on_ready():
aioh = {"User-Agent": f"{script_name}/1.0'"}
bot.aiosession = aiohttp.ClientSession(headers=aioh)
bot.app_info = await bot.application_info()
bot.botlog_channel = bot.get_channel(config.botlog_channel)
log.info(f'\nLogged in as: {bot.user.name} - '
f'{bot.user.id}\ndpy version: {discord.__version__}\n')
log.info(
f"\nLogged in as: {bot.user.name} - "
f"{bot.user.id}\ndpy version: {discord.__version__}\n"
)
game_name = f"{config.prefixes[0]}help"
# Send "Robocop has started! x has y members!"
guild = bot.botlog_channel.guild
msg = f"{bot.user.name} has started! "\
f"{guild.name} has {guild.member_count} members!"
data_files = [discord.File(fpath) for fpath in wanted_jsons]
await bot.botlog_channel.send(msg, files=data_files)
msg = f"{bot.user.name} has started! "
await bot.botlog_channel.send(msg)
activity = discord.Activity(name=game_name,
type=discord.ActivityType.listening)
activity = discord.Activity(name=game_name, type=discord.ActivityType.listening)
await bot.change_presence(activity=activity)
@bot.event
async def on_command(ctx):
log_text = f"{ctx.message.author} ({ctx.message.author.id}): "\
f"\"{ctx.message.content}\" "
if ctx.guild: # was too long for tertiary if
log_text += f"on \"{ctx.channel.name}\" ({ctx.channel.id}) "\
f"at \"{ctx.guild.name}\" ({ctx.guild.id})"
else:
log_text += f"on DMs ({ctx.channel.id})"
log.info(log_text)
@bot.event
async def on_error(event_method, *args, **kwargs):
log.error(f"Error on {event_method}: {sys.exc_info()}")
@ -128,9 +76,11 @@ async def on_error(event_method, *args, **kwargs):
async def on_command_error(ctx, error):
error_text = str(error)
err_msg = f"Error with \"{ctx.message.content}\" from "\
f"\"{ctx.message.author} ({ctx.message.author.id}) "\
f"of type {type(error)}: {error_text}"
err_msg = (
f'Error with "{ctx.message.content}" from '
f"{ctx.message.author} ({ctx.message.author.id}) "
f"of type {type(error)}: {error_text}"
)
log.error(err_msg)
@ -141,71 +91,60 @@ async def on_command_error(ctx, error):
if isinstance(error, commands.NoPrivateMessage):
return await ctx.send("This command doesn't work on DMs.")
elif isinstance(error, commands.MissingPermissions):
roles_needed = '\n- '.join(error.missing_perms)
return await ctx.send(f"{ctx.author.mention}: You don't have the right"
" permissions to run this command. You need: "
f"```- {roles_needed}```")
roles_needed = "\n- ".join(error.missing_perms)
return await ctx.send(
f"{ctx.author.mention}: You don't have the right"
" permissions to run this command. You need: "
f"```- {roles_needed}```"
)
elif isinstance(error, commands.BotMissingPermissions):
roles_needed = '\n-'.join(error.missing_perms)
return await ctx.send(f"{ctx.author.mention}: Bot doesn't have "
"the right permissions to run this command. "
"Please add the following roles: "
f"```- {roles_needed}```")
roles_needed = "\n-".join(error.missing_perms)
return await ctx.send(
f"{ctx.author.mention}: Bot doesn't have "
"the right permissions to run this command. "
"Please add the following roles: "
f"```- {roles_needed}```"
)
elif isinstance(error, commands.CommandOnCooldown):
return await ctx.send(f"{ctx.author.mention}: You're being "
"ratelimited. Try in "
f"{error.retry_after:.1f} seconds.")
return await ctx.send(
f"{ctx.author.mention}: You're being "
"ratelimited. Try in "
f"{error.retry_after:.1f} seconds."
)
elif isinstance(error, commands.CheckFailure):
return await ctx.send(f"{ctx.author.mention}: Check failed. "
"You might not have the right permissions "
"to run this command, or you may not be able "
"to run this command in the current channel.")
elif isinstance(error, commands.CommandInvokeError) and\
("Cannot send messages to this user" in error_text):
return await ctx.send(f"{ctx.author.mention}: I can't DM you.\n"
"You might have me blocked or have DMs "
f"blocked globally or for {ctx.guild.name}.\n"
"Please resolve that, then "
"run the command again.")
return await ctx.send(
f"{ctx.author.mention}: Check failed. "
"You might not have the right permissions "
"to run this command, or you may not be able "
"to run this command in the current channel."
)
elif isinstance(error, commands.CommandInvokeError) and (
"Cannot send messages to this user" in error_text
):
return await ctx.send(
f"{ctx.author.mention}: I can't DM you.\n"
"You might have me blocked or have DMs "
f"blocked globally or for {ctx.guild.name}.\n"
"Please resolve that, then "
"run the command again."
)
elif isinstance(error, commands.CommandNotFound):
# Nothing to do when command is not found.
return
help_text = f"Usage of this command is: ```{ctx.prefix}"\
f"{ctx.command.signature}```\nPlease see `{ctx.prefix}help "\
f"{ctx.command.name}` for more info about this command."
help_text = (
f"Usage of this command is: ```{ctx.prefix}"
f"{ctx.command.signature}```\nPlease see `{ctx.prefix}help "
f"{ctx.command.name}` for more info about this command."
)
if isinstance(error, commands.BadArgument):
return await ctx.send(f"{ctx.author.mention}: You gave incorrect "
f"arguments. {help_text}")
return await ctx.send(
f"{ctx.author.mention}: You gave incorrect " f"arguments. {help_text}"
)
elif isinstance(error, commands.MissingRequiredArgument):
return await ctx.send(f"{ctx.author.mention}: You gave incomplete "
f"arguments. {help_text}")
@bot.event
async def on_message(message):
if message.author.bot:
return
if (message.guild) and (message.guild.id not in config.guild_whitelist):
return
# Ignore messages in newcomers channel, unless it's potentially
# an allowed command
welcome_allowed = ["reset", "kick", "ban", "warn"]
if message.channel.id == config.welcome_channel and\
not any(cmd in message.content for cmd in welcome_allowed):
return
ctx = await bot.get_context(message)
await bot.invoke(ctx)
if not os.path.exists("data"):
os.makedirs("data")
return await ctx.send(
f"{ctx.author.mention}: You gave incomplete " f"arguments. {help_text}"
)
for wanted_json in wanted_jsons:
if not os.path.exists(wanted_json):
with open(wanted_json, "w") as f:
f.write("{}")
bot.run(config.token, bot=True, reconnect=True)

+ 19
- 74
cogs/admin.py View File

@ -2,22 +2,18 @@ import discord
from discord.ext import commands
from discord.ext.commands import Cog
import traceback
import inspect
import re
import config
from helpers.checks import check_if_bot_manager
from helpers.userlogs import set_userlog
class Admin(Cog):
def __init__(self, bot):
self.bot = bot
self.last_eval_result = None
self.previous_eval_code = None
@commands.guild_only()
@commands.check(check_if_bot_manager)
@commands.command(name='exit', aliases=["quit", "bye", "stop", "kill", "restart", "die"])
@commands.command(
name="exit", aliases=["quit", "bye", "stop", "kill", "restart", "die"]
)
async def _exit(self, ctx):
"""Shuts down the bot, bot manager only."""
await ctx.send(":wave: Goodbye!")
@ -25,53 +21,15 @@ class Admin(Cog):
@commands.guild_only()
@commands.check(check_if_bot_manager)
@commands.command()
async def fetchlog(self, ctx):
"""Returns log"""
await ctx.send("Here's the current log file:",
file=discord.File(f"{self.bot.script_name}.log"))
@commands.guild_only()
@commands.check(check_if_bot_manager)
@commands.command()
async def fetchdata(self, ctx):
"""Returns data files"""
data_files = [discord.File(fpath) for fpath in self.bot.wanted_jsons]
await ctx.send("Here you go:", files=data_files)
@commands.guild_only()
@commands.check(check_if_bot_manager)
@commands.command(name='eval')
@commands.command(name="eval")
async def _eval(self, ctx):
await ctx.send("Fuck off. This doesn't belong in production code!")
async def cog_load_actions(self, cog_name):
if cog_name == "verification":
verif_channel = self.bot.get_channel(config.welcome_channel)
await self.bot.do_resetalgo(verif_channel, "cog load")
@commands.guild_only()
@commands.check(check_if_bot_manager)
@commands.command()
async def pull(self, ctx, auto=False):
"""Does a git pull, bot manager only."""
tmp = await ctx.send('Pulling...')
git_output = await self.bot.async_call_shell("git pull")
await tmp.edit(content=f"Pull complete. Output: ```{git_output}```")
if auto:
cogs_to_reload = re.findall(r'cogs/([a-z_]*).py[ ]*\|', git_output)
for cog in cogs_to_reload:
try:
self.bot.unload_extension("cogs." + cog)
self.bot.load_extension("cogs." + cog)
self.bot.log.info(f'Reloaded ext {cog}')
await ctx.send(f':white_check_mark: `{cog}` '
'successfully reloaded.')
await self.cog_load_actions(cog)
except:
await ctx.send(f':x: Cog reloading failed, traceback: '
f'```\n{traceback.format_exc()}\n```')
return
await ctx.send("Fuck off. This doesn't belong in production code! Bother Nichole instead.")
@commands.guild_only()
@commands.check(check_if_bot_manager)
@ -80,13 +38,14 @@ class Admin(Cog):
"""Loads a cog, bot manager only."""
try:
self.bot.load_extension("cogs." + ext)
await self.cog_load_actions(ext)
except:
await ctx.send(f':x: Cog loading failed, traceback: '
f'```\n{traceback.format_exc()}\n```')
await ctx.send(
f":x: Cog loading failed, traceback: "
f"```\n{traceback.format_exc()}\n```"
)
return
self.bot.log.info(f'Loaded ext {ext}')
await ctx.send(f':white_check_mark: `{ext}` successfully loaded.')
self.bot.log.info(f"Loaded ext {ext}")
await ctx.send(f":white_check_mark: `{ext}` successfully loaded.")
@commands.guild_only()
@commands.check(check_if_bot_manager)
@ -94,8 +53,8 @@ class Admin(Cog):
async def unload(self, ctx, ext: str):
"""Unloads a cog, bot manager only."""
self.bot.unload_extension("cogs." + ext)
self.bot.log.info(f'Unloaded ext {ext}')
await ctx.send(f':white_check_mark: `{ext}` successfully unloaded.')
self.bot.log.info(f"Unloaded ext {ext}")
await ctx.send(f":white_check_mark: `{ext}` successfully unloaded.")
@commands.check(check_if_bot_manager)
@commands.command()
@ -111,28 +70,14 @@ class Admin(Cog):
self.bot.load_extension("cogs." + ext)
await self.cog_load_actions(ext)
except:
await ctx.send(f':x: Cog reloading failed, traceback: '
f'```\n{traceback.format_exc()}\n```')
await ctx.send(
f":x: Cog reloading failed, traceback: "
f"```\n{traceback.format_exc()}\n```"
)
return
self.bot.log.info(f'Reloaded ext {ext}')
await ctx.send(f':white_check_mark: `{ext}` successfully reloaded.')
@commands.guild_only()
@commands.check(check_if_bot_manager)
@commands.command()
async def restoreuserlog(self, ctx, message_id: int):
"""Restores the user log from a saved backup."""
message = await self.bot.botlog_channel.fetch_message(message_id)
if message.author.id != self.bot.user.id or len(message.attachments) == 0:
await ctx.send("Incorrect Message ID.")
self.bot.log.info(f"Reloaded ext {ext}")
await ctx.send(f":white_check_mark: `{ext}` successfully reloaded.")
for attachment in message.attachments:
if attachment.filename == "userlog.json":
logs = (await attachment.read()).decode("utf-8")
set_userlog(logs)
return
await ctx.send("Incorrect Message ID.")
def setup(bot):
bot.add_cog(Admin(bot))

+ 31
- 26
cogs/basic.py View File

@ -5,6 +5,7 @@ from discord.ext import commands
from discord.ext.commands import Cog
from helpers.checks import check_if_verified
class Basic(Cog):
def __init__(self, bot):
self.bot = bot
@ -17,64 +18,68 @@ class Basic(Cog):
await ctx.send(f"Hello {ctx.author.mention}!")
@commands.check(check_if_verified)
@commands.command(aliases=['aboutkosmos'])
@commands.command(aliases=["aboutkosmos"])
async def about(self, ctx):
"""Shows what kosmos is and what it includes"""
await ctx.send("Kosmos is a CFW bundle that comes with Atmosphere, Hekate, and some homebrew. You can see all the homebrew that is included here: https://github.com/AtlasNX/Kosmos#featuring")
await ctx.send(
"Kosmos is a CFW bundle that comes with Atmosphere, Hekate, and some homebrew. You can see all the homebrew that is included here: https://github.com/AtlasNX/Kosmos#featuring"
)
@commands.check(check_if_verified)
@commands.command(aliases=["fat32"])
async def exfat(self, ctx):
"""Displays a helpful message on why not to use exFAT"""
embed = discord.Embed(title="GUIFormat",
url="http://www.ridgecrop.demon.co.uk/guiformat.exe",
description="A useful tool for formatting SD cards over 32GB as FAT32 on Windows.")
message_text=("The exFAT drivers built into the Switch has been known "
"to corrupt SD cards and homebrew only makes this worse. "
"Backup everything on your SD card as soon as possible "
"and format it to FAT32. On Windows, if your SD card is "
"over 32GB then it will not let you select FAT32 from "
"the built-in format tool, however you can use a tool "
"like GUIFormat to format it.")
await ctx.send(content=message_text,
embed=embed)
embed = discord.Embed(
title="GUIFormat",
url="http://www.ridgecrop.demon.co.uk/guiformat.exe",
description="A useful tool for formatting SD cards over 32GB as FAT32 on Windows.",
)
message_text = (
"The exFAT drivers built into the Switch has been known "
"to corrupt SD cards and homebrew only makes this worse. "
"Backup everything on your SD card as soon as possible "
"and format it to FAT32. On Windows, if your SD card is "
"over 32GB then it will not let you select FAT32 from "
"the built-in format tool, however you can use a tool "
"like GUIFormat to format it."
)
await ctx.send(content=message_text, embed=embed)
@commands.guild_only()
@commands.check(check_if_verified)
@commands.command()
async def membercount(self, ctx):
"""Prints the member count of the server."""
await ctx.send(f"{ctx.guild.name} has "
f"{ctx.guild.member_count} members!")
await ctx.send(f"{ctx.guild.name} has " f"{ctx.guild.member_count} members!")
@commands.check(check_if_verified)
@commands.command(aliases=["robocopng", "robocop-ng", "komet", "komet-cl"])
async def robocop(self, ctx):
"""Shows a quick embed with bot info."""
embed = discord.Embed(title="Komet",
url=config.source_url,
description=config.embed_desc)
embed = discord.Embed(
title="Komet", url=config.source_url, description=config.embed_desc
)
embed.set_thumbnail(url=self.bot.user.avatar_url)
await ctx.send(embed=embed)
@commands.check(check_if_verified)
@commands.command(aliases=['p', "ddos"])
@commands.command(aliases=["p", "ddos"])
async def ping(self, ctx):
"""Shows ping values to discord.
RTT = Round-trip time, time taken to send a message to discord
GW = Gateway Ping"""
before = time.monotonic()
tmp = await ctx.send('Calculating ping...')
tmp = await ctx.send("Calculating ping...")
after = time.monotonic()
rtt_ms = (after - before) * 1000
gw_ms = self.bot.latency * 1000
message_text = f":ping_pong:\n"\
f"rtt: `{rtt_ms:.1f}ms`\n"\
f"gw: `{gw_ms:.1f}ms`"
message_text = (
f":ping_pong:\n" f"rtt: `{rtt_ms:.1f}ms`\n" f"gw: `{gw_ms:.1f}ms`"
)
self.bot.log.info(message_text)
await tmp.edit(content=message_text)


+ 53
- 42
cogs/common.py View File

@ -7,6 +7,7 @@ import math
import parsedatetime
from discord.ext.commands import Cog
class Common(Cog):
def __init__(self, bot):
self.bot = bot
@ -30,9 +31,14 @@ class Common(Cog):
res_timestamp = math.floor(time.mktime(time_struct))
return res_timestamp
def get_relative_timestamp(self, time_from=None, time_to=None,
humanized=False, include_from=False,
include_to=False):
def get_relative_timestamp(
self,
time_from=None,
time_to=None,
humanized=False,
include_from=False,
include_to=False,
):
# Setting default value to utcnow() makes it show time from cog load
# which is not what we want
if not time_from:
@ -42,17 +48,19 @@ class Common(Cog):
if humanized:
humanized_string = humanize.naturaltime(time_from - time_to)
if include_from and include_to:
str_with_from_and_to = f"{humanized_string} "\
f"({str(time_from).split('.')[0]} "\
f"- {str(time_to).split('.')[0]})"
str_with_from_and_to = (
f"{humanized_string} "
f"({str(time_from).split('.')[0]} "
f"- {str(time_to).split('.')[0]})"
)
return str_with_from_and_to
elif include_from:
str_with_from = f"{humanized_string} "\
f"({str(time_from).split('.')[0]})"
str_with_from = (
f"{humanized_string} " f"({str(time_from).split('.')[0]})"
)
return str_with_from
elif include_to:
str_with_to = f"{humanized_string} "\
f"({str(time_to).split('.')[0]})"
str_with_to = f"{humanized_string} " f"({str(time_to).split('.')[0]})"
return str_with_to
return humanized_string
else:
@ -60,8 +68,7 @@ class Common(Cog):
epoch_from = (time_from - epoch).total_seconds()
epoch_to = (time_to - epoch).total_seconds()
second_diff = epoch_to - epoch_from
result_string = str(datetime.timedelta(
seconds=second_diff)).split('.')[0]
result_string = str(datetime.timedelta(seconds=second_diff)).split(".")[0]
return result_string
async def aioget(self, url):
@ -72,11 +79,12 @@ class Common(Cog):
self.bot.log.info(f"Data from {url}: {text_data}")
return text_data
else:
self.bot.log.error(f"HTTP Error {data.status} "
"while getting {url}")
self.bot.log.error(f"HTTP Error {data.status} " "while getting {url}")
except:
self.bot.log.error(f"Error while getting {url} "
f"on aiogetbytes: {traceback.format_exc()}")
self.bot.log.error(
f"Error while getting {url} "
f"on aiogetbytes: {traceback.format_exc()}"
)
async def aiogetbytes(self, url):
try:
@ -86,11 +94,12 @@ class Common(Cog):
self.bot.log.debug(f"Data from {url}: {byte_data}")
return byte_data
else:
self.bot.log.error(f"HTTP Error {data.status} "
"while getting {url}")
self.bot.log.error(f"HTTP Error {data.status} " "while getting {url}")
except:
self.bot.log.error(f"Error while getting {url} "
f"on aiogetbytes: {traceback.format_exc()}")
self.bot.log.error(
f"Error while getting {url} "
f"on aiogetbytes: {traceback.format_exc()}"
)
async def aiojson(self, url):
try:
@ -98,18 +107,19 @@ class Common(Cog):
if data.status == 200:
text_data = await data.text()
self.bot.log.info(f"Data from {url}: {text_data}")
content_type = data.headers['Content-Type']
content_type = data.headers["Content-Type"]
return await data.json(content_type=content_type)
else:
self.bot.log.error(f"HTTP Error {data.status} "
"while getting {url}")
self.bot.log.error(f"HTTP Error {data.status} " "while getting {url}")
except:
self.bot.log.error(f"Error while getting {url} "
f"on aiogetbytes: {traceback.format_exc()}")
self.bot.log.error(
f"Error while getting {url} "
f"on aiogetbytes: {traceback.format_exc()}"
)
def hex_to_int(self, color_hex: str):
"""Turns a given hex color into an integer"""
return int("0x" + color_hex.strip('#'), 16)
return int("0x" + color_hex.strip("#"), 16)
def escape_message(self, text: str):
"""Escapes unfun stuff from messages"""
@ -129,10 +139,12 @@ class Common(Cog):
"""Slices a message into multiple messages"""
if len(text) > size * self.max_split_length:
haste_url = await self.haste(text)
return [f"Message is too long ({len(text)} > "
f"{size * self.max_split_length} "
f"({size} * {self.max_split_length}))"
f", go to haste: <{haste_url}>"]
return [
f"Message is too long ({len(text)} > "
f"{size * self.max_split_length} "
f"({size} * {self.max_split_length}))"
f", go to haste: <{haste_url}>"
]
reply_list = []
size_wo_fix = size - len(prefix) - len(suffix)
while len(text) > size_wo_fix:
@ -141,28 +153,28 @@ class Common(Cog):
reply_list.append(f"{prefix}{text}{suffix}")
return reply_list
async def haste(self, text, instance='https://mystb.in/'):
response = await self.bot.aiosession.post(f"{instance}documents",
data=text)
async def haste(self, text, instance="https://mystb.in/"):
response = await self.bot.aiosession.post(f"{instance}documents", data=text)
if response.status == 200:
result_json = await response.json()
return f"{instance}{result_json['key']}"
else:
return f"Error {response.status}: {response.text}"
async def async_call_shell(self, shell_command: str,
inc_stdout=True, inc_stderr=True):
async def async_call_shell(
self, shell_command: str, inc_stdout=True, inc_stderr=True
):
pipe = asyncio.subprocess.PIPE
proc = await asyncio.create_subprocess_shell(str(shell_command),
stdout=pipe,
stderr=pipe)
proc = await asyncio.create_subprocess_shell(
str(shell_command), stdout=pipe, stderr=pipe
)
if not (inc_stdout or inc_stderr):
return "??? you set both stdout and stderr to False????"
proc_result = await proc.communicate()
stdout_str = proc_result[0].decode('utf-8').strip()
stderr_str = proc_result[1].decode('utf-8').strip()
stdout_str = proc_result[0].decode("utf-8").strip()
stderr_str = proc_result[1].decode("utf-8").strip()
if inc_stdout and not inc_stderr:
return stdout_str
@ -170,8 +182,7 @@ class Common(Cog):
return stderr_str
if stdout_str and stderr_str:
return f"stdout:\n\n{stdout_str}\n\n"\
f"======\n\nstderr:\n\n{stderr_str}"
return f"stdout:\n\n{stdout_str}\n\n" f"======\n\nstderr:\n\n{stderr_str}"
elif stdout_str:
return f"stdout:\n\n{stdout_str}"
elif stderr_str:


+ 0
- 196
cogs/err.py View File

@ -1,196 +0,0 @@
import re
import discord
from discord.ext import commands
from discord.ext.commands import Cog
from helpers.checks import check_if_verified
from helpers.errcodes import *
class Err(Cog):
"""Everything related to Nintendo 3DS, Wii U and Switch error codes"""
def __init__(self, bot):
self.bot = bot
self.dds_re = re.compile(r'0\d{2}\-\d{4}')
self.wiiu_re = re.compile(r'1\d{2}\-\d{4}')
self.switch_re = re.compile(r'2\d{3}\-\d{4}')
self.no_err_desc = "It seems like your error code is unknown. "\
"You should report relevant details to "\
"<@141532589725974528> (tomGER#7462) "\
"so it can be added to the bot."
self.rickroll = "https://www.youtube.com/watch?v=4uj896lr3-E"
@commands.check(check_if_verified)
@commands.command(aliases=["3dserr", "3err", "dserr"])
async def dderr(self, ctx, err: str):
"""Searches for 3DS error codes!
Usage: .ddserr/.3err/.dserr/.3dserr <Error Code>"""
if self.dds_re.match(err): # 3DS - dds -> Drei DS -> Three DS
if err in dds_errcodes:
err_description = dds_errcodes[err]
else:
err_description = self.no_err_desc
# Make a nice Embed out of it
embed = discord.Embed(title=err,
url=self.rickroll,
description=err_description)
embed.set_footer(text="Console: 3DS")
# Send message, crazy
await ctx.send(embed=embed)
# These are not similar to the other errors apparently ... ?
elif err.startswith("0x"):
derr = err[2:]
derr = derr.strip()
rc = int(derr, 16)
desc = rc & 0x3FF
mod = (rc >> 10) & 0xFF
summ = (rc >> 21) & 0x3F
level = (rc >> 27) & 0x1F
embed = discord.Embed(title=f"0x{rc:X}")
embed.add_field(name="Module", value=dds_modules.get(mod, mod))
embed.add_field(name="Description",
value=dds_descriptions.get(desc, desc))
embed.add_field(name="Summary", value=dds_summaries.get(summ, summ))
embed.add_field(name="Level", value=dds_levels.get(level, level))
embed.set_footer(text="Console: 3DS")
await ctx.send(embed=embed)
return
else:
await ctx.send("Unknown Format - This is either "
"no error code or you made some mistake!")
@commands.check(check_if_verified)
@commands.command(aliases=["wiiuserr", "uerr", "wuerr", "mochaerr"])
async def wiiuerr(self, ctx, err: str):
"""Searches for Wii U error codes!
Usage: .wiiuserr/.uerr/.wuerr/.mochaerr <Error Code>"""
if self.wiiu_re.match(err): # Wii U
module = err[2:3] # Is that even true, idk just guessing
desc = err[5:8]
if err in wii_u_errors:
err_description = wii_u_errors[err]
else:
err_description = self.no_err_desc
# Make a nice Embed out of it
embed = discord.Embed(title=err,
url=self.rickroll,
description=err_description)
embed.set_footer(text="Console: Wii U")
embed.add_field(name="Module", value=module, inline=True)
embed.add_field(name="Description", value=desc, inline=True)
# Send message, crazy
await ctx.send(embed=embed)
else:
await ctx.send("Unknown Format - This is either "
"no error code or you made some mistake!")
@commands.check(check_if_verified)
@commands.command(aliases=["nxerr", "serr"])
async def err(self, ctx, err: str):
"""Searches for Switch error codes!
Usage: .serr/.nxerr/.err <Error Code>"""
if self.switch_re.match(err) or err.startswith("0x"): # Switch
if err.startswith("0x"):
err = err[2:]
errcode = int(err, 16)
module = errcode & 0x1FF
desc = (errcode >> 9) & 0x3FFF
else:
module = int(err[0:4]) - 2000
desc = int(err[5:9])
errcode = (desc << 9) + module
str_errcode = f'{(module + 2000):04}-{desc:04}'
# Searching for Modules in list
if module in switch_modules:
err_module = switch_modules[module]
else:
err_module = "Unknown"
# Set initial value unconditionally
err_description = self.no_err_desc
# Searching for error codes related to the Switch
# (doesn't include special cases)
if errcode in switch_known_errcodes:
err_description = switch_known_errcodes[errcode]
elif errcode in switch_support_page:
err_description = switch_support_page[errcode]
elif module in switch_known_errcode_ranges:
for errcode_range in switch_known_errcode_ranges[module]:
if desc >= errcode_range[0] and desc <= errcode_range[1]:
err_description = errcode_range[2]
# Make a nice Embed out of it
embed = discord.Embed(title=f"{str_errcode} / {hex(errcode)}",
url=self.rickroll,
description=err_description)
embed.add_field(name="Module",
value=f"{err_module} ({module})",
inline=True)
embed.add_field(name="Description", value=desc, inline=True)
if "ban" in err_description:
embed.set_footer("F to you | Console: Switch")
else:
embed.set_footer(text="Console: Switch")
await ctx.send(embed=embed)
# Special case handling because Nintendo feels like
# its required to break their format lol
elif err in switch_game_err:
game, desc = switch_game_err[err].split(":")
embed = discord.Embed(title=err,
url=self.rickroll,
description=desc)
embed.set_footer(text="Console: Switch")
embed.add_field(name="Game", value=game, inline=True)
await ctx.send(embed=embed)
else:
await ctx.send("Unknown Format - This is either "
"no error code or you made some mistake!")
@commands.check(check_if_verified)
@commands.command(aliases=["e2h"])
async def err2hex(self, ctx, err: str):
"""Converts Nintendo Switch errors to hex
Usage: .err2hex <Error Code>"""
if self.switch_re.match(err):
module = int(err[0:4]) - 2000
desc = int(err[5:9])
errcode = (desc << 9) + module
await ctx.send(hex(errcode))
else:
await ctx.send("This doesn't follow the typical"
" Nintendo Switch 2XXX-XXXX format!")
@commands.check(check_if_verified)
@commands.command(aliases=["h2e"])
async def hex2err(self, ctx, err: str):
"""Converts Nintendo Switch errors to hex
Usage: .hex2err <Hex>"""
if err.startswith("0x"):
err = err[2:]
err = int(err, 16)
module = err & 0x1FF
desc = (err >> 9) & 0x3FFF
errcode = f'{(module + 2000):04}-{desc:04}'
await ctx.send(errcode)
else:
await ctx.send("This doesn't look like typical hex!")
def setup(bot):
bot.add_cog(Err(bot))

+ 0
- 43
cogs/invites.py View File

@ -1,43 +0,0 @@
from discord.ext import commands
from discord.ext.commands import Cog
from helpers.checks import check_if_collaborator
import config
import json
class Invites(Cog):
def __init__(self, bot):
self.bot = bot
@commands.command()
@commands.guild_only()
@commands.check(check_if_collaborator)
async def invite(self, ctx):
welcome_channel = self.bot.get_channel(config.welcome_channel)
author = ctx.message.author
reason = f"Created by {str(author)} ({author.id})"
invite = await welcome_channel.create_invite(max_age = 0,
max_uses = 1, temporary = True, unique = True, reason = reason)
with open("data/invites.json", "r") as f:
invites = json.load(f)
invites[invite.id] = {
"uses": 0,
"url": invite.url,
"max_uses": 1,
"code": invite.code
}
with open("data/invites.json", "w") as f:
f.write(json.dumps(invites))
await ctx.message.add_reaction("๐Ÿ†—")
try:
await ctx.author.send(f"Created single-use invite {invite.url}")
except discord.errors.Forbidden:
await ctx.send(f"{ctx.author.mention} I could not send you the \
invite. Send me a DM so I can reply to you.")
def setup(bot):
bot.add_cog(Invites(bot))

+ 29
- 45
cogs/links.py View File

@ -4,6 +4,7 @@ from discord.ext import commands
from discord.ext.commands import Cog
from helpers.checks import check_if_verified
class Links(Cog):
"""
Commands for easily linking to projects.
@ -28,65 +29,45 @@ class Links(Cog):
@commands.command(hidden=True, aliases=["bootloader"])
async def hekate(self, ctx):
"""Link to the Hekate repo"""
await ctx.send("https://github.com/CTCaer/hekate")
await ctx.send("https://github.com/CTCaer/hekate")
@commands.check(check_if_verified)
@commands.command(hidden=True, aliases=["xyproblem"])
async def xy(self, ctx):
"""Link to the "What is the XY problem?" post from SE"""
await ctx.send("<https://meta.stackexchange.com/q/66377/285481>\n\n"
"TL;DR: It's asking about your attempted solution "
"rather than your actual problem.\n"
"It's perfectly okay to want to learn about a "
"solution, but please be clear about your intentions "
"if you're not actually trying to solve a problem.")
await ctx.send(
"<https://meta.stackexchange.com/q/66377/285481>\n\n"
"TL;DR: It's asking about your attempted solution "
"rather than your actual problem.\n"
"It's perfectly okay to want to learn about a "
"solution, but please be clear about your intentions "
"if you're not actually trying to solve a problem."
)
@commands.check(check_if_verified)
@commands.command(hidden=True, aliases=["guides", "link"])
async def guide(self, ctx):
"""Link to the guide(s)"""
message_text=("**Generic starter guides:**\n"
"AtlasNX's Guide: "
"<https://switch.homebrew.guide>\n"
"\n"
"**Specific guides:**\n"
"Manually Updating/Downgrading (with HOS): "
"<https://switch.homebrew.guide/usingcfw/manualupgrade>\n"
"Manually Repairing/Downgrading (without HOS): "
"<https://switch.homebrew.guide/usingcfw/manualchoiupgrade>\n"
"How to get started developing Homebrew: "
"<https://switch.homebrew.guide/homebrew_dev/introduction>\n"
"\n")
try:
support_faq_channel = self.bot.get_channel(config.support_faq_channel)
if support_faq_channel is None:
message_text += "Check out #support-faq for additional help."
else:
message_text += f"Check out {support_faq_channel.mention} for additional help."
except AttributeError:
message_text += "Check out #support-faq for additional help."
await ctx.send(message_text)
@commands.check(check_if_verified)
@commands.command(hidden=True, aliases=["patron"])
async def patreon(self, ctx):
"""Link to the patreon"""
await ctx.send("https://patreon.teamatlasnx.com")
@commands.check(check_if_verified)
@commands.command(hidden=True, aliases=["coffee"])
async def kofi(self, ctx):
"""Link to Ko-fi"""
await ctx.send("https://kofi.teamatlasnx.com")
await ctx.send(
"**Generic starter guides:**\n"
"SDSetup Guide: "
"<https://switch.homebrew.guide>\n"
"\n"
"**Specific guides:**\n"
"Manually Updating/Downgrading (with HOS): "
"<https://switch.homebrew.guide/usingcfw/manualupgrade>\n"
"Manually Repairing/Downgrading (without HOS): "
"<https://switch.homebrew.guide/usingcfw/manualchoiupgrade>\n"
"How to get started developing Homebrew: "
"<https://switch.homebrew.guide/homebrew_dev/introduction>"
)
@commands.check(check_if_verified)
@commands.command(hidden=True, aliases=["sdfiles"])
async def kosmos(self, ctx):
"""Link to the latest Kosmos release"""
await ctx.send("https://github.com/AtlasNX/Kosmos/releases/latest")
await ctx.send("https://github.com/Team-Neptune/DeepSea/releases/latest")
@commands.check(check_if_verified)
@commands.command(hidden=True, aliases=["sd"])
@ -98,8 +79,11 @@ class Links(Cog):
@commands.command()
async def source(self, ctx):
"""Gives link to source code."""
await ctx.send(f"You can find my source at {config.source_url}. "
"Serious PRs and issues welcome!")
await ctx.send(
f"You can find my source at {config.source_url}. "
"Serious PRs and issues welcome!"
)
def setup(bot):
bot.add_cog(Links(bot))

+ 7
- 23
cogs/lists.py View File

@ -209,22 +209,6 @@ class Lists(Cog):
mod_cog, ctx=ctx, target=target, reason=f"Rule {number} - {reason}"
)
@commands.guild_only()
@commands.check(check_if_verified)
@commands.command(aliases=["faq"])
async def support(self, ctx, number: int):
"""Link to a specific list item in #support-faq"""
channel = ctx.guild.get_channel(config.support_faq_channel)
await self.link_list_item(ctx, channel, number)
@commands.guild_only()
@commands.check(check_if_verified)
@commands.command(aliases=["es", "fs", "acid", "piracy"])
async def patches(self, ctx):
"""Link to the list item in #support-faq about patches"""
channel = ctx.guild.get_channel(config.support_faq_channel)
await self.link_list_item(ctx, channel, 1)
# Listeners
@Cog.listener()
@ -324,7 +308,7 @@ class Lists(Cog):
await message.delete()
return
log_channel = self.bot.get_channel(config.log_channel)
botlog_channel = self.bot.get_channel(config.botlog_channel)
channel = message.channel
content = message.content
user = message.author
@ -363,7 +347,7 @@ class Lists(Cog):
for reaction in reactions:
await reaction.remove(user)
await log_channel.send(
await botlog_channel.send(
self.create_log_message("๐Ÿ’ฌ", "List item added:", user, channel)
)
return
@ -377,14 +361,14 @@ class Lists(Cog):
await targeted_message.edit(content=content)
await targeted_reaction.remove(user)
await log_channel.send(
await botlog_channel.send(
self.create_log_message("๐Ÿ“", "List item edited:", user, channel)
)
elif self.is_delete(targeted_reaction):
await targeted_message.delete()
await log_channel.send(
await botlog_channel.send(
self.create_log_message(
"โŒ", "List item deleted:", user, channel, content
)
@ -403,7 +387,7 @@ class Lists(Cog):
for message in messages:
await self.send_cached_message(channel, message)
await log_channel.send(
await botlog_channel.send(
self.create_log_message(
"โ™ป", "List item recycled:", user, channel, content
)
@ -430,7 +414,7 @@ class Lists(Cog):
for message in messages:
await self.send_cached_message(channel, message)
await log_channel.send(
await botlog_channel.send(
self.create_log_message("๐Ÿ’ฌ", "List item added:", user, channel)
)
@ -457,7 +441,7 @@ class Lists(Cog):
for message in messages:
await self.send_cached_message(channel, message)
await log_channel.send(
await botlog_channel.send(
self.create_log_message("๐Ÿ’ฌ", "List item added:", user, channel)
)


+ 0
- 263
cogs/lists_verification.py View File

@ -1,263 +0,0 @@
import asyncio
import config
import discord
from discord.ext import commands
from discord.ext.commands import Cog
from helpers.checks import check_if_staff
import io
import os
import random
import re
class ListsVerification(Cog):
def __init__(self, bot):
self.bot = bot
self.verification = ""
if os.path.exists("data/verification.txt"):
with open("data/verification.txt", "r") as f:
self.verification = f.read()
bot.loop.create_task(self.daily())
def generate_verification_phrase(self):
random_words = [
"colony",
"carriage",
"be",
"employee",
"empirical",
"flourish",
"moral",
"troop",
"waterfall",
"reduction",
"fraction",
"goalkeeper",
"conscious",
"acceptable",
"advertising",
"visual",
"spin",
"margin",
"greeting",
"continuation",
"sandwich",
"upset",
"stake",
"safe",
"rally",
"reservoir",
"effort",
"integration",
"extent",
"expression",
"echo",
"prove",
"precedent",
"inhibition",
"expect",
"theft",
"distinct",
"part",
"revolution",
"player",
"fragrant",
"waste",
"value",
"profession",
"quote",
"room",
"master",
"utter",
"aloof",
"quantity",
]
self.verification = (
random.choice(random_words)
+ "_"
+ random.choice(random_words)
+ "_"
+ random.choice(random_words)
)
with open("data/verification.txt", "w") as f:
f.write(self.verification)
async def reset_verification_channel(self, rules_channel, verification_channel):
self.generate_verification_phrase()
# Get all the rules from the rules channel.
rules_messages = []
number_of_rules = 0
async for message in rules_channel.history(limit=None, oldest_first=True):
if len(message.content.strip()) != 0:
number_of_rules += 1
rules_messages.append(message)
if number_of_rules == 0:
return
# Randomly choose which rule to inject the hidden message in.
random_rule = 0
if number_of_rules != 1:
if number_of_rules < 2:
random_rule = random.randint(0, number_of_rules - 1)
else:
# Don't include the first or last rule.
random_rule = random.randint(1, number_of_rules - 2)
# Delete all messages from the welcome channel.
await verification_channel.purge(limit=None)
# Put all rules in the welcome channel.
i = 0
for message in rules_messages:
content = message.content
if content.strip():
i += 1
if i == random_rule:
# Find all of the sentences in the random rule.
matches = list(re.finditer(r"[.|!|?][\W]*\s*", content))
# Randomly choose where to put the random message in our random rule.
random_sentence = 0
if len(matches) != 1:
random_sentence = random.randint(0, len(matches) - 1)
# Insert our verification text.
pos = matches[random_sentence].end()
content = (
content[:pos]
+ f' When you have finished reading all of the rules, send a message in this channel that includes "{self.verification}", and the bot will automatically grant you access to the other channels. '
+ content[pos:]
)
message_file = None
if len(message.attachments) != 0:
# Lists will only reupload a single image per message.
attachment = next(
(
a
for a in message.attachments
if os.path.splitext(a.filename)[1] in [".png", ".jpg", ".jpeg"]
),
None,
)
if attachment is not None:
message_file = discord.File(
io.BytesIO(await attachment.read()),
filename=attachment.filename,
)
await verification_channel.send(content=content, file=message_file)
# Commands
@commands.guild_only()
@commands.check(check_if_staff)
@commands.command()
async def reset(self, ctx):
"""Resets the verification channel with the latest rules"""
rules_channel_id = getattr(config, "rules_channel", 0)
verification_channel_id = getattr(config, "verification_channel", 0)
rules_channel = None
if rules_channel_id != 0:
rules_channel = self.bot.get_channel(rules_channel_id)
verification_channel = None
if verification_channel_id != 0:
verification_channel = self.bot.get_channel(verification_channel_id)
if rules_channel is not None and verification_channel is not None:
await self.reset_verification_channel(rules_channel, verification_channel)
@commands.guild_only()
@commands.check(check_if_staff)
@commands.command()
async def verifyall(self, ctx):
"""Gives everyone the verification role"""
verified_role = ctx.guild.get_role(config.verified_role)
for member in ctx.guild.members:
if verified_role not in member.roles:
await member.add_roles(verified_role)
await ctx.send("All members verified.")
@commands.guild_only()
@commands.check(check_if_staff)
@commands.command()
async def verifiedcount(self, ctx):
"""Prints the number of verified members"""
verified_role = ctx.guild.get_role(config.verified_role)
await ctx.send(
f"{ctx.guild.name} has " f"{len(verified_role.members)} verified members!"
)
# Listeners
@Cog.listener()
async def on_message(self, message):
await self.bot.wait_until_ready()
if not hasattr(config, "verification_channel"):
return
# We only care about messages in Rules, and Support FAQ
if message.channel.id != config.verification_channel:
return
# We don't care about messages from bots.
if message.author.bot:
return
await message.delete()
# We only care if the message contained the verification phrase.
if self.verification not in message.content:
return
# Grant user the verified role.
verified_role = message.guild.get_role(config.verified_role)
await message.author.add_roles(verified_role)
# Tasks
async def daily(self):
await self.bot.wait_until_ready()
if (
not hasattr(config, "log_channel")
or not hasattr(config, "rules_channel")
or not hasattr(config, "verification_channel")
):
return
log_channel = self.bot.get_channel(config.log_channel)
rules_channel = self.bot.get_channel(config.rules_channel)
verification_channel = self.bot.get_channel(config.verification_channel)
# Make sure the bot is open.
while not self.bot.is_closed():
# Reset the verification channel
try:
await self.reset_verification_channel(
rules_channel, verification_channel
)
except:
await log_channel.send(
"Verification reset has errored: ```" f"{traceback.format_exc()}```"
)
# Wait 1 day
await asyncio.sleep(86400)
def setup(bot):
bot.add_cog(ListsVerification(bot))

+ 0
- 90
cogs/lockdown.py View File

@ -1,90 +0,0 @@
from discord.ext import commands
from discord.ext.commands import Cog
import config
import discord
from helpers.checks import check_if_staff
class Lockdown(Cog):
def __init__(self, bot):
self.bot = bot
async def set_sendmessage(self, channel: discord.TextChannel,
role, allow_send, issuer):
try:
roleobj = channel.guild.get_role(role)
overrides = channel.overwrites_for(roleobj)
overrides.send_messages = allow_send
await channel.set_permissions(roleobj,
overwrite=overrides,
reason=str(issuer))
except:
pass
async def unlock_for_staff(self, channel: discord.TextChannel, issuer):
for role in config.staff_role_ids:
await self.set_sendmessage(channel, role, True, issuer)
@commands.guild_only()
@commands.check(check_if_staff)
@commands.command()
async def lock(self, ctx, channel: discord.TextChannel = None,
soft: bool = False):
"""Prevents people from speaking in a channel, staff only.
Defaults to current channel."""
if not channel:
channel = ctx.channel
log_channel = self.bot.get_channel(config.log_channel)
roles = config.lockdown_configs["default"]["roles"]
for key, lockdown_conf in config.lockdown_configs.items():
if channel.id in lockdown_conf["channels"]:
roles = lockdown_conf["roles"]
for role in roles:
await self.set_sendmessage(channel, role, False, ctx.author)
await self.unlock_for_staff(channel, ctx.author)
public_msg = "๐Ÿ”’ Channel locked down. "
if not soft:
public_msg += "Only staff members may speak. "\
"Do not bring the topic to other channels or risk "\
"disciplinary actions."
await ctx.send(public_msg)
safe_name = await commands.clean_content().convert(ctx, str(ctx.author))
msg = f"๐Ÿ”’ **Lockdown**: {ctx.channel.mention} by {ctx.author.mention} "\
f"| {safe_name}"
await log_channel.send(msg)