diff --git a/.gitignore b/.gitignore index 3f3771c..63ae52e 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ Temporary Items #login details token clientid +clcreds \ No newline at end of file diff --git a/BMis.gif b/BMis.gif new file mode 100644 index 0000000..86af38c Binary files /dev/null and b/BMis.gif differ diff --git a/FEU_Seal.png b/FEU_Seal.png new file mode 100644 index 0000000..91dad2d Binary files /dev/null and b/FEU_Seal.png differ diff --git a/Letha_Seal_of_Approval.png b/Letha_Seal_of_Approval.png new file mode 100644 index 0000000..d931805 Binary files /dev/null and b/Letha_Seal_of_Approval.png differ diff --git a/Tikis_in_orbit.png b/Tikis_in_orbit.png new file mode 100644 index 0000000..29bbec7 Binary files /dev/null and b/Tikis_in_orbit.png differ diff --git a/__pycache__/feubotFormatter.cpython-35.pyc b/__pycache__/feubotFormatter.cpython-35.pyc deleted file mode 100644 index ec1e311..0000000 Binary files a/__pycache__/feubotFormatter.cpython-35.pyc and /dev/null differ diff --git a/__pycache__/helpful.cpython-310.pyc b/__pycache__/helpful.cpython-310.pyc new file mode 100644 index 0000000..d6d9647 Binary files /dev/null and b/__pycache__/helpful.cpython-310.pyc differ diff --git a/__pycache__/helpful.cpython-35.pyc b/__pycache__/helpful.cpython-35.pyc deleted file mode 100644 index ee7a4df..0000000 Binary files a/__pycache__/helpful.cpython-35.pyc and /dev/null differ diff --git a/__pycache__/helpful.cpython-39.pyc b/__pycache__/helpful.cpython-39.pyc new file mode 100644 index 0000000..6358d97 Binary files /dev/null and b/__pycache__/helpful.cpython-39.pyc differ diff --git a/__pycache__/memes.cpython-310.pyc b/__pycache__/memes.cpython-310.pyc new file mode 100644 index 0000000..3d9470f Binary files /dev/null and b/__pycache__/memes.cpython-310.pyc differ diff --git a/__pycache__/memes.cpython-35.pyc b/__pycache__/memes.cpython-35.pyc deleted file mode 100644 index 25aa17b..0000000 Binary files a/__pycache__/memes.cpython-35.pyc and /dev/null differ diff --git a/__pycache__/memes.cpython-39.pyc b/__pycache__/memes.cpython-39.pyc new file mode 100644 index 0000000..68f03a0 Binary files /dev/null and b/__pycache__/memes.cpython-39.pyc differ diff --git a/__pycache__/messageManager.cpython-310.pyc b/__pycache__/messageManager.cpython-310.pyc new file mode 100644 index 0000000..04418a9 Binary files /dev/null and b/__pycache__/messageManager.cpython-310.pyc differ diff --git a/__pycache__/mycog.cpython-35.pyc b/__pycache__/mycog.cpython-35.pyc deleted file mode 100644 index a0a21fc..0000000 Binary files a/__pycache__/mycog.cpython-35.pyc and /dev/null differ diff --git a/__pycache__/other.cpython-310.pyc b/__pycache__/other.cpython-310.pyc new file mode 100644 index 0000000..cd88b46 Binary files /dev/null and b/__pycache__/other.cpython-310.pyc differ diff --git a/__pycache__/other.cpython-39.pyc b/__pycache__/other.cpython-39.pyc new file mode 100644 index 0000000..68a950d Binary files /dev/null and b/__pycache__/other.cpython-39.pyc differ diff --git a/__pycache__/reactions.cpython-310.pyc b/__pycache__/reactions.cpython-310.pyc new file mode 100644 index 0000000..d569bc2 Binary files /dev/null and b/__pycache__/reactions.cpython-310.pyc differ diff --git a/__pycache__/reactions.cpython-35.pyc b/__pycache__/reactions.cpython-35.pyc deleted file mode 100644 index 78372e0..0000000 Binary files a/__pycache__/reactions.cpython-35.pyc and /dev/null differ diff --git a/__pycache__/reactions.cpython-39.pyc b/__pycache__/reactions.cpython-39.pyc new file mode 100644 index 0000000..04578eb Binary files /dev/null and b/__pycache__/reactions.cpython-39.pyc differ diff --git a/__pycache__/useful.cpython-35.pyc b/__pycache__/useful.cpython-35.pyc deleted file mode 100644 index c6c0977..0000000 Binary files a/__pycache__/useful.cpython-35.pyc and /dev/null differ diff --git a/approved.png b/approved.png new file mode 100644 index 0000000..1afd960 Binary files /dev/null and b/approved.png differ diff --git a/awful.jpg b/awful.jpg new file mode 100644 index 0000000..57f3901 Binary files /dev/null and b/awful.jpg differ diff --git a/commands.pickle b/commands.pickle new file mode 100644 index 0000000..e5749ae Binary files /dev/null and b/commands.pickle differ diff --git a/commands_backup.pickle b/commands_backup.pickle new file mode 100644 index 0000000..e5749ae Binary files /dev/null and b/commands_backup.pickle differ diff --git a/creepy.png b/creepy.png new file mode 100644 index 0000000..cfa21dc Binary files /dev/null and b/creepy.png differ diff --git a/disgusting/ephraim.png b/disgusting/ephraim.png new file mode 100644 index 0000000..46d30f9 Binary files /dev/null and b/disgusting/ephraim.png differ diff --git a/disgusting/jedah.png b/disgusting/jedah.png new file mode 100644 index 0000000..5ab728a Binary files /dev/null and b/disgusting/jedah.png differ diff --git a/disgusting/jeorge.jpg b/disgusting/jeorge.jpg new file mode 100644 index 0000000..cac3e92 Binary files /dev/null and b/disgusting/jeorge.jpg differ diff --git a/disgusting/loki.jpg b/disgusting/loki.jpg new file mode 100644 index 0000000..1cc633a Binary files /dev/null and b/disgusting/loki.jpg differ diff --git a/disgusting/shigure.png b/disgusting/shigure.png new file mode 100644 index 0000000..aa6cfc2 Binary files /dev/null and b/disgusting/shigure.png differ diff --git a/feubot.py b/feubot.py index 12f96d7..94cc424 100644 --- a/feubot.py +++ b/feubot.py @@ -5,37 +5,144 @@ import random import os from sys import argv +# from feubotFormatter import FeubotFormatter +from discord.ext.commands import DefaultHelpCommand + +import messageManager as msgManager + +async def setupBot(bot): + import helpful, memes, reactions, other#, undelete + await reactions.setup(bot) + await memes.setup(bot) + await helpful.setup(bot) + # undelete.setup(bot) + await other.setup(bot) + #TODO: Stuff like bot.other = bot.get_cog("Other") and such. Then initialize debug's "self" to be bot. + + bot.remove_command('debug') + #Reload this as part of reload due to use of other.developerCheck + @bot.command(pass_context=True, hidden = True, aliases = ['exec']) + @other.developerCheck + async def debug(ctx, *, arg): + # https://stackoverflow.com/questions/3906232/python-get-the-print-output-in-an-exec-statement + from io import StringIO + import sys + old_stdout = sys.stdout + redirected_output = sys.stdout = StringIO() + bot = ctx.bot + try: + exec(arg) + except SystemExit: + await bot.send("I tried to quit().") + finally: + sys.stdout = old_stdout + output = redirected_output.getvalue() + output = "No output." if not output else output + await bot.send(output) -import helpful, memes, reactions -from feubotFormatter import FeubotFormatter if __name__ == "__main__": + + intents = discord.Intents.all() + intents.members = True + if "--debug" in argv: - bot = commands.Bot(command_prefix=['##', 'feubeta '], description='this is feubot beta.', formatter = FeubotFormatter()) + bot = commands.Bot(command_prefix=['##', 'feubeta '], description='this is feubot beta.', intents=intents, help_command = DefaultHelpCommand(dm_help=True)) else: - bot = commands.Bot(command_prefix=['!', '>>', 'feubot '], description='this is feubot.', formatter = FeubotFormatter()) + bot = commands.Bot(command_prefix=['!', '>>', 'feubot '], description='this is feubot.', intents=intents, help_command = DefaultHelpCommand(dm_help=True)) @bot.event async def on_ready(): + await setupBot(bot) print('Logged in as') print(bot.user.name) print(bot.user.id) print('------') - await bot.change_presence(game=discord.Game(name="Reading the doc!")) + await bot.change_presence(activity=discord.Game(name="Reading the doc!")) + + @bot.event + async def on_raw_reaction_add(payload): + messageID = payload.message_id + reaction = payload.emoji.name + role_name = msgManager.find_role(str(messageID), reaction) + msg_properties = msgManager.get_properties(str(messageID)) + + guild = bot.get_guild(payload.guild_id) + member = payload.member + role = discord.utils.get(guild.roles, name=role_name) + + #Extra check for management uses + if member == guild.owner and "ignoreOwner" in msg_properties: + print("Ignoring owner") + return + + if role: + #If roles are exclusive, remove all other roles from the user + if "exclusiveRoles" in msg_properties: + reactRoles = msgManager.load_roles()[str(messageID)].values() + for x in member.roles: + if x.name in reactRoles: + await member.remove_roles(x) + + #Remove other reactions on the message from this user + message = await guild.get_channel(payload.channel_id).fetch_message(messageID) + for x in message.reactions: + if x.emoji != reaction: + await x.remove(member) + + await member.add_roles(role) + + @bot.event + async def on_raw_reaction_remove(payload): + messageID = payload.message_id + reaction = payload.emoji.name + role_name = msgManager.find_role(str(messageID), reaction) + msg_properties = msgManager.get_properties(str(messageID)) + + guild = bot.get_guild(payload.guild_id) + #Use guild.get_member since payload.member is None on ReactionRemove + member = guild.get_member(payload.user_id) + role = discord.utils.get(guild.roles, name=role_name) + + #Extra check for management uses + if member == guild.owner and "ignoreOwner" in msg_properties: + print("Ignoring owner") + return + + if role: + await member.remove_roles(role) + + @bot.add_listener + async def on_command_error(ctx, error): + if type(error) == commands.CheckFailure: + pass + elif type(error) == commands.CommandNotFound: + pass + else: + await ctx.message.channel.send(error) + + @bot.add_listener + async def on_message(message): + soldier_spellings = ['soldier', 'solider', 'soilder', 'solidier', 'soldeir'] + if message.channel.id == 746515160377851997: #prep_screen id + bot_member = message.guild.get_member(bot.user.id) + bot_role = discord.utils.get(message.guild.roles, name="Conscripter") + if bot_role in bot_member.roles: + if any(word in message.content.lower() for word in soldier_spellings): + role = discord.utils.get(message.guild.roles, name="Soldier") + await message.author.add_roles(role) @bot.command() - async def donate(): + async def donate(ctx): """you know it""" - await bot.say("https://donorbox.org/donate-to-circles") + await ctx.send("https://donate.tiltify.com/+fire-emblem-universe/fire-emblem-e3-2021") + await ctx.send("https://www.patreon.com/theFEUfund") + # await ctx.send("https://donorbox.org/donate-to-circles") token = os.environ.get('TOKEN', default=None) if token is None: token = open('./token').read().replace('\n','') - reactions.setup(bot) - memes.setup(bot) - helpful.setup(bot) + bot.reload = lambda: setupBot(bot) bot.run(token) - - diff --git a/feubotFormatter.py b/feubotFormatter.py index 521ebac..01db4fa 100644 --- a/feubotFormatter.py +++ b/feubotFormatter.py @@ -1,6 +1,6 @@ -from discord.ext.commands import HelpFormatter +from discord.ext.commands import HelpCommand -class FeubotFormatter(HelpFormatter): +class FeubotFormatter(HelpCommand): def filter_command_list(self): return sorted(super().filter_command_list(), key=lambda e: str.lower(e[0])) diff --git a/goofs/EN.png b/goofs/EN.png new file mode 100644 index 0000000..225d564 Binary files /dev/null and b/goofs/EN.png differ diff --git a/goofs/anecdote.png b/goofs/anecdote.png new file mode 100644 index 0000000..db7e90b Binary files /dev/null and b/goofs/anecdote.png differ diff --git a/goofs/artifact.png b/goofs/artifact.png new file mode 100644 index 0000000..bcad4b2 Binary files /dev/null and b/goofs/artifact.png differ diff --git a/goofs/beat.png b/goofs/beat.png new file mode 100644 index 0000000..1f99950 Binary files /dev/null and b/goofs/beat.png differ diff --git a/goofs/bondage.png b/goofs/bondage.png new file mode 100644 index 0000000..f096201 Binary files /dev/null and b/goofs/bondage.png differ diff --git a/goofs/candy.png b/goofs/candy.png new file mode 100644 index 0000000..c0b482a Binary files /dev/null and b/goofs/candy.png differ diff --git a/goofs/cute.png b/goofs/cute.png new file mode 100644 index 0000000..8ec04d8 Binary files /dev/null and b/goofs/cute.png differ diff --git a/goofs/dead.png b/goofs/dead.png new file mode 100644 index 0000000..bdb052c Binary files /dev/null and b/goofs/dead.png differ diff --git a/goofs/depression.png b/goofs/depression.png new file mode 100644 index 0000000..c21b8fa Binary files /dev/null and b/goofs/depression.png differ diff --git a/goofs/gheb.png b/goofs/gheb.png new file mode 100644 index 0000000..1d8d2a6 Binary files /dev/null and b/goofs/gheb.png differ diff --git a/goofs/goof.png b/goofs/goof.png new file mode 100644 index 0000000..1455dfc Binary files /dev/null and b/goofs/goof.png differ diff --git a/goofs/handcuffs.png b/goofs/handcuffs.png new file mode 100644 index 0000000..845e0d4 Binary files /dev/null and b/goofs/handcuffs.png differ diff --git a/goofs/keriku.png b/goofs/keriku.png new file mode 100644 index 0000000..7cb5837 Binary files /dev/null and b/goofs/keriku.png differ diff --git a/goofs/metagoof.png b/goofs/metagoof.png new file mode 100644 index 0000000..7dd2357 Binary files /dev/null and b/goofs/metagoof.png differ diff --git a/goofs/out.png b/goofs/out.png new file mode 100644 index 0000000..940ad91 Binary files /dev/null and b/goofs/out.png differ diff --git a/goofs/porn.png b/goofs/porn.png new file mode 100644 index 0000000..a30b5e9 Binary files /dev/null and b/goofs/porn.png differ diff --git a/goofs/prostitute.png b/goofs/prostitute.png new file mode 100644 index 0000000..b149e88 Binary files /dev/null and b/goofs/prostitute.png differ diff --git a/goofs/reeg.png b/goofs/reeg.png new file mode 100644 index 0000000..2e3e681 Binary files /dev/null and b/goofs/reeg.png differ diff --git a/goofs/sex.png b/goofs/sex.png new file mode 100644 index 0000000..171d80d Binary files /dev/null and b/goofs/sex.png differ diff --git a/goofs/stalkers.png b/goofs/stalkers.png new file mode 100644 index 0000000..7a4ef05 Binary files /dev/null and b/goofs/stalkers.png differ diff --git a/hard.png b/hard.png new file mode 100644 index 0000000..b5306a7 Binary files /dev/null and b/hard.png differ diff --git a/helpful.py b/helpful.py index cd7a9ee..d8e30a7 100644 --- a/helpful.py +++ b/helpful.py @@ -62,55 +62,85 @@ def create_embed(posts, threads, term): text="Truncated %d result(s)." % (len(posts)-numresults)) return result -class Helpful: +class Helpful(bot.Cog): """Actually Helpful commands""" def __init__(self, bot): self.bot = bot + # ctx = bot + # self.port = bot.listen('on_message')(self.port) + @bot.command() + async def mod(self, ctx, rule_num, *, link): + """!mod """ + FEU_id = 144670830150811649 + if ctx.message.guild is None or ctx.message.guild.id == FEU_id: + await ctx.message.author.send("Your request for moderation was successful.") + if ctx.message.guild is not None: + await ctx.message.delete + mod_channel = self.bot.get_channel(650911156277477377) + paladins = discord.utils.get(ctx.message.guild.roles, id=145992793796378624).mention + await ctx.mod_channel.send("%s, moderation request received by user %s: Rule %s, at <%s>." % (paladins, ctx.message.author.name, rule_num, link)) + else: + await ctx.send("Moderation features are for FEU only.") + + @bot.command() + async def howtomod(self, ctx): + """Gives information on how to use the !mod command.""" + await ctx.send("First, have Developer Mode enabled (Settings -> Appearance -> Developer Mode).") + await ctx.send("Then, click the `...` by the offending message, and click \"Copy Link\".") + await ctx.send("Then simple say !mod , where is the rule it violates, and is the pasted link to the message.") + await ctx.send("If you do not have Developer Mode, you may instead of a link, write a short description of where the infraction took place, and by who.") + await ctx.send("Note that after requesting moderation, the message requesting moderation will be removed.") @bot.command() - async def goldmine(self): + async def goldmine(self, ctx): """everything you ever wanted""" embed=discord.Embed(title="Unified FE Hacking Dropbox", url='https://www.dropbox.com/sh/xl73trcck2la799/AAAMdpNSGQWEzYkLEQEiEhGFa?dl=0', description="All the hacking resources you could ever need, in one place", color=0xefba01) # embed.set_thumbnail(url='http://i.imgur.com/Bg5NSga.png') - await self.bot.say(embed=embed) + await ctx.send(embed=embed) + + # @bot.command() # removed aliases=["repo"] + # async def repository(self, ctx): + # """graphics for you""" + # embed=discord.Embed(title="Emblem Anims", url='https://emblem-anims.herokuapp.com/', description="Get your animations here (credits missing on some, check just in case!)", color=0x4286f4) + # await ctx.send(embed=embed) @bot.command() - async def mugs(self): + async def mugs(self, ctx): """Link to image of all GBAFE mugs.""" - await self.bot.say("http://doc.feuniverse.us/static/resources/mugs.png") + await ctx.send("http://doc.feuniverse.us/static/resources/mugs.png") @bot.command() - async def hit(self, number, type="2RN"): + async def hit(self, ctx, number, type="2RN"): """Convert 2RN/fates hit to actual chance""" try: num = int(number) except ValueError: - await self.bot.say("Specify an integer 0-100") + await ctx.send("Specify an integer 0-100") return if (num < 0) or (num > 100): - await self.bot.say("Specify an integer 0-100") + await ctx.send("Specify an integer 0-100") return if type.upper()=="2RN": table = [0.00, 0.03, 0.10, 0.21, 0.36, 0.55, 0.78, 1.05, 1.36, 1.71, 2.10, 2.53, 3.00, 3.51, 4.06, 4.65, 5.28, 5.95, 6.66, 7.41, 8.20, 9.03, 9.90, 10.81, 11.76, 12.75, 13.78, 14.85, 15.96, 17.11, 18.30, 19.53, 20.80, 22.11, 23.46, 24.85, 26.28, 27.75, 29.26, 30.81, 32.40, 34.03, 35.70, 37.41, 39.16, 40.95, 42.78, 44.65, 46.56, 48.51, 50.50, 52.47, 54.40, 56.29, 58.14, 59.95, 61.72, 63.45, 65.14, 66.79, 68.40, 69.97, 71.50, 72.99, 74.44, 75.85, 77.22, 78.55, 79.84, 81.09, 82.30, 83.47, 84.60, 85.69, 86.74, 87.75, 88.72, 89.65, 90.54, 91.39, 92.20, 92.97, 93.70, 94.39, 95.04, 95.65, 96.22, 96.75, 97.24, 97.69, 98.10, 98.47, 98.80, 99.09, 99.34, 99.55, 99.72, 99.85, 99.94, 99.99, 100.00] elif type.upper()=="FATES": table = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50.5,51.83,53.17,54.5,55.83,57.17,58.5,59.83,61.17,62.5,63.83,65.17,66.5,67.83,69.17,70.5,71.83,73.17,74.5,75.83,77.17,78.5,79.83,81.17,82.5,83.83,85.12,86.35,87.53,88.66,89.73,90.75,91.72,92.63,93.49,94.3,95.05,95.75,96.4,96.99,97.53,98.02,98.45,98.83,99.16,99.43,99.65,99.82,99.93,99.99,100] else: - await self.bot.say("Valid types are 2RN, Fates") + await ctx.send("Valid types are 2RN, Fates") return - await self.bot.say(str(table[num])) + await ctx.send(str(table[num])) @bot.command() - async def roll(self, number, type="2RN"): + async def roll(self, ctx, number, type="2RN"): """rolls hit or miss (e.g. >>hit 50 1rn/2rn[default]/fates)""" try: num = int(number) except ValueError: - await self.bot.say("Specify an integer 0-100") + await ctx.send("Specify an integer 0-100") return if (num < 0) or (num > 100): - await self.bot.say("Specify an integer 0-100") + await ctx.send("Specify an integer 0-100") return if type.upper()=="1RN": rolled = random.randint(1,100) @@ -121,13 +151,21 @@ async def roll(self, number, type="2RN"): if rolled > 50: rolled = ((rolled*3) + random.randint(1,100))>>2 else: - await self.bot.say("Valid types are 1RN, 2RN, Fates") + await ctx.send("Valid types are 1RN, 2RN, Fates") return - if rolled <= num: await self.bot.say("HIT (%d)" % rolled) - else: await self.bot.say("MISS (%d)" % rolled) + if rolled <= num: await ctx.send("HIT (%d)" % rolled) + else: await ctx.send("MISS (%d)" % rolled) - @bot.command() - async def search(self, *, term): + @bot.command() # removed aliases = ['die'] + async def rollDie(self, ctx, n : int): + if n <= 0: + await ctx.send("Specify a positive integer.") + return + res = random.randrange(n) + 1 + await ctx.send(str(res)) + + @bot.command(aliases=['s']) + async def search(self, ctx, *, term): """search feu""" root = "http://feuniverse.us/search.json?q=%s" payload = urllib.parse.quote(term) @@ -143,22 +181,27 @@ async def search(self, *, term): # data = json.loads(js.read().decode()) posts = data["posts"] threads = data["topics"] - await self.bot.say(embed=create_embed(posts, threads, payload)) + await ctx.send(embed=create_embed(posts, threads, payload)) except urllib.error.URLError: - await self.bot.say("Error accessing FEU server, please try again later.") + await ctx.send("Error accessing FEU server, please try again later.") except KeyError: embedded=create_embed(posts, [], payload) try: - await self.bot.say(embed=embedded) + await ctx.send(embed=embedded) except discord.errors.HTTPException: print(embedded.title) - @bot.command(aliases=["UT2"]) - async def ut2(self): + @bot.command() #aliases=["UT2"] + async def ut2(self, ctx): """links ultimate tutorial v2""" embed=discord.Embed(title="Fire Emblem Hacking Ultimate Tutorial v2", url='https://tutorial.feuniverse.us/', description="How to do everything with Event Assembler buildfiles", color=0x40caf2) - await self.bot.say(embed=embed) + await ctx.send(embed=embed) + async def port(self, ctx, msg): + if str(msg.author.id) != 149576374984638464: return + if 'PORT' in msg.content.upper(): + pass + # await ctx.send(msg.channel, '```I think you mean MUG```') -def setup(bot): - bot.add_cog(Helpful(bot)) +async def setup(bot): + await bot.add_cog(Helpful(bot)) diff --git a/lel.png b/lel.png new file mode 100644 index 0000000..e3ac105 Binary files /dev/null and b/lel.png differ diff --git a/lel2.png b/lel2.png new file mode 100644 index 0000000..17b7806 Binary files /dev/null and b/lel2.png differ diff --git a/marisa.png b/marisa.png new file mode 100644 index 0000000..ff28557 Binary files /dev/null and b/marisa.png differ diff --git a/memes.py b/memes.py index e1b2cba..a947b74 100644 --- a/memes.py +++ b/memes.py @@ -2,51 +2,90 @@ from discord.ext import commands as bot import os, random, re, asyncio -class Memes: +class Memes(bot.Cog): """only the dankest""" def __init__(self, bot): self.bot = bot @bot.command() - async def reply(self): + async def reply(self, ctx): """r e p l y s o o n""" - await self.bot.say("reply :soon: :smile:") + await ctx.send("reply :soon: :smile:") @bot.command() - async def whattime(self): + async def whattime(self, ctx): """tells the time""" - await self.bot.say("`it's tiki time`") + await ctx.send("`it's tiki time`") # await asyncio.sleep(1) - await self.bot.upload("tiki.gif") + await ctx.send(file=discord.File("tiki.gif")) @bot.command() - async def writing(self): + async def orbit(self, ctx): + """my tiki's""" + await ctx.send(file=discord.File("Tikis_in_orbit.png")) + + @bot.command() + async def writing(self, ctx): """get it in writing. in blood.""" - await self.bot.upload("Pelleass_Blood_Pact.png") + await ctx.send(file=discord.File("Pelleass_Blood_Pact.png")) + + @bot.command(aliases=["wtf"]) + async def wtfdyjfsamylb(self, ctx): + """what the fuck did you just fucking say about me you little bitch""" + await ctx.send("""``` +What the fuck did you just fucking say about me, you little bitch? I’ll have you know I graduated top of my class in the FE University, and I’ve been involved in numerous secret raids on Serenes Forest, and I have over 300 confirmed hacks. I am trained in donating to hex and I’m the top debugger in the entire FE Shrine. You are nothing to me but just another breakpoint. I will wipe you the fuck out with precision the likes of which has never been seen before on an ARMv7TDMI, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of backups across the ROM and your link register is being traced right now so you better prepare for the screech, fleshling. The death screech that wipes out the pathetic little thing you call your reskin. You’re fucking dead, kid. I can be anywhere, anytime, and I can crash your rom in over seven hundred ways, and that’s just with FEditor. Not only am I extensively trained in Nightmare, but I have access to the entire arsenal of the Unified FE Hacking Doc and I will use it to its full extent to wipe your miserable map sprite off the face of Magvel, you little shit. If only you could have known what unholy retribution your little “clever” comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn’t, you didn’t, and now you’re paying the price, you goddamn idiot. I will shit Erin all over you and you will drown in it. You’re fucking dead, kiddo. +```""") + + @bot.command(aliases=["ma???a"]) + async def ma___a(self, ctx): + """what IS her name anyway""" + letters = [x for x in "abcdefghijklmnopqrstuvwyxz"] + consonants = [x for x in "bcdfghjklmnpqrstvwxz"] + vowels = [x for x in "aeiouy"] + infix = random.choice(consonants) + random.choice(letters) + random.choice(vowels) + await ctx.send("I think you mean Ma"+infix+"a!") + + @bot.command() + async def evil(self, ctx, *args): + """Sub-humans.""" + if len(args) > 0: + thing = ' '.join(args) + plural = thing[-1] == 's' #TODO: use inflect + formatString = '''```\n{1} {0} evil.\n{1} {0} the enemy.\n{1} must be eradicated.```''' + verb = "are" if plural else "is" + await ctx.send(formatString.format(verb, thing)) + else: + await ctx.send("You gotta tell me **what's** evil!") @bot.command() - async def arch(self): + async def arch(self, ctx): """do something with arch""" direction = random.choice([":arrow_down:", ":arrow_up:"]) - await self.bot.say(direction+" with <:arch_mini:230160993299202068>") + await ctx.send(direction+" with <:arch_mini:230160993299202068>") + + @bot.command() + async def colorz(self, ctx): + """do something with colorz""" + direction = random.choice([":arrow_down:", ":arrow_up:"]) + await ctx.send(direction+" with <:colorz:230159530158194688>") @bot.command() - async def style(self): + async def style(self, ctx): """if my style gets in your way""" img = random.choice(["styleRD.gif", "stylePoR.jpeg"]) - await self.bot.upload(img) + await ctx.send(file=discord.File(img)) @bot.command() - async def goofs(self): + async def goofs(self, ctx): """list goofs""" filenameslist = [os.path.splitext(f)[0] for f in os.listdir("./goofs")] - await self.bot.say("```"+"\n".join(map(str, filenameslist))+"```") + await ctx.send("```"+"\n".join(map(str, filenameslist))+"```") @bot.command() - async def goof(self,*args): + async def goof(self, ctx, *args): """show goof""" requested = args gooflist = {a.lower(): a for a in os.listdir("./goofs")} @@ -61,37 +100,40 @@ async def goof(self,*args): request_file = file_extension_or_not_pattern.sub(extension, request).lower() if request_file in gooflist: found = True - await self.bot.upload("./goofs/"+gooflist[request_file]) + await ctx.send(file=discord.File("./goofs/"+gooflist[request_file])) if not found: - await self.bot.say("Use >>goofs to see a list of accepted goofs.") + await ctx.send("Use >>goofs to see a list of accepted goofs.") else: - await self.bot.upload("./goofs/"+random.choice([a for a in gooflist.values()])) + await ctx.send(file=discord.File("./goofs/"+random.choice([a for a in gooflist.values()]))) @bot.command() - async def whois(self,*args): + async def whois(self, ctx, *args): """roy is our boy""" if len(args) > 0: lord = ' '.join(args) if (lord.lower() in ['circles','circleseverywhere']): - await self.bot.say(lord + " is my dad") + await ctx.send(lord + " is my dad") return elif (lord.lower() == 'feditor'): - await self.bot.say("`" + lord + " a shit`") + await ctx.send("`" + lord + " a shit`") return elif (lord.lower() == 'feubot'): - await self.bot.say("That's me, silly!") + await ctx.send("That's me, silly!") return elif (lord.lower() == 'ea'): - await self.bot.say("`" + lord + " is our bae`") + await ctx.send("`" + lord + " is our bae`") + return + elif (lord.lower() in ['bm', 'blackmage', 'black mage']): + await ctx.send(file=discord.File("BMis.gif")) return elif lord[0].lower() in 'bcdfghjklmnpqrstvwxz': blord = 'b'+lord[1:] else: blord = 'b'+lord - await self.bot.say(lord + " is our " + blord) + await ctx.send(lord + " is our " + blord) @bot.command() - async def createwaifu(self,*args): + async def createwaifu(self, ctx, *args): """:wink:""" heads = [ "<:zigludo:252132877678936064>", @@ -110,7 +152,7 @@ async def createwaifu(self,*args): "<:elise:235616193065517066>", "<:eliwood:232283812938121217>", "<:elbert:232283825974149120>", - "<:EAmoe:317182514559188994>", + "<:dootthunk:339479700147798016>", "<:doot:324593825815461889>", "<:donate:230166446146191362>", "<:doc:280527122802540544>", @@ -119,40 +161,40 @@ async def createwaifu(self,*args): "<:celica:272027128231362571>", "<:BBQ:230169373694885888>", "<:arch_mini:230160993299202068>", - "<:cam_thumb:307559627573428224>", + "<:camthumb:307559627573428224>", "<:dat:292422389197701121>", "<:thighs:294965155819683840>"] if len(args) > 0: head = ' '.join(args) else: head = random.choice(heads) - await self.bot.say(head + """ -<:personality:274564774979960832> + await ctx.send(head + """ +<:personality:902077101627289630> <:thighs:294965155819683840>""") @bot.command() - async def doot(self): + async def doot(self, ctx): """doot doot""" flip = random.choice([0,1]) if flip ==1: - await self.bot.say("""<:doot:324593825815461889> <:doot:324593825815461889> :trumpet: :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: <:doot:324593825815461889> <:doot:324593825815461889> <:doot:324593825815461889> + await ctx.send("""<:doot:324593825815461889> <:doot:324593825815461889> :trumpet: :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: <:doot:324593825815461889> <:doot:324593825815461889> <:doot:324593825815461889> <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: <:doot:324593825815461889> <:doot:324593825815461889> :trumpet: :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: :trumpet: <:doot:324593825815461889> :trumpet: :trumpet: :trumpet: <:doot:324593825815461889> :trumpet:""") else: - await self.bot.upload("./DOOT.png") + await ctx.send(file=discord.File("./DOOT.png")) @bot.command(aliases=["(lol"]) - async def lol(self): + async def lol(self, ctx): """(lol""" flip = random.choice([0,1]) - if flip==1: await self.bot.upload("./Water_lol.png") - else: await self.bot.upload("./Water_lol2.png") + if flip==1: await ctx.send(file=discord.File("./Water_lol.png")) + else: await ctx.send(file=discord.File("./Water_lol2.png")) @bot.command(aliases=["eventassembler", "everythingassembler"]) - async def ea(self): + async def ea(self, ctx): """EVERYTHING ASSEMBLER""" - everythingassemblerstring = """``` _____ _ _ _ -| __|_ _ ___ ___ _ _| |_| |_|_|___ ___ + everythingassemblerstring = """``` _____ _ _ _ +| __|_ _ ___ ___ _ _| |_| |_|_|___ ___ | __| | | -_| _| | | _| | | | . | |_____|\_/|___|_| |_ |_| |_|_|_|_|_|_ | |___| |___| @@ -160,32 +202,42 @@ async def ea(self): | _ |___ ___ ___ _____| |_| |___ ___ | |_ -|_ -| -_| | . | | -_| _| |__|__|___|___|___|_|_|_|___|_|___|_|```""" - await self.bot.say(everythingassemblerstring) + await ctx.send(everythingassemblerstring) @bot.command() - async def casual(self): + async def casual(self, ctx): """just play phoenix""" barflist = os.listdir("./casual") - await self.bot.upload("./casual/"+random.choice(barflist)) + await ctx.send(file=discord.File("./casual/"+random.choice(barflist))) @bot.command() - async def erin(self): + async def erin(self, ctx): """ERIN INTENSIFIES""" - await self.bot.upload("./erinyous.gif") + await ctx.send(file=discord.File("./erinyous.gif")) @bot.command() - async def fury(self): + async def slow(self, ctx): + """It's what I ain't.""" + await ctx.send(file=discord.File("./slow.png")) + + @bot.command() + async def fury(self, ctx): """2 FAST 2 FURYOUS""" - await self.bot.say("Don't you mean `>>erin`?") + await ctx.send("Don't you mean `>>erin`?") @bot.command(aliases=["SOA", 'SoA']) - async def soa(self): + async def soa(self, ctx): """there's your problem""" - await self.bot.upload("./SoA.png") + await ctx.send(file=discord.File("./SoA.png")) + + @bot.command() + async def hard(self, ctx): + """HARD""" + await ctx.send(file=discord.File("./hard.png")) #TODO: HUBBA TESTER @bot.command() - async def hubba(self, person1, person2): + async def hubba(self, ctx, person1, person2): """discover their true feelings""" best_responses = ("About to swoon","Always staring","Carries a torch for","Desires attention","Devoted","Drawn by destiny","Drools openly","Fatal attraction","First love","Has a sweet spot","Head over heels","Heart aflutter","Infatuated","Lives and dies for","Loins afire","Lost in pheromones","Lovey Dovey","Never wants to part","One-track mind","Possessive","Puts on a pedestal","Separation anxiety","Smitten","True love","True sacrificer","Uses pet names","Wants to snuggle","Way too attached","Would give anything","Would hold hands") good_responses = ("Admires lifestyle","Always entertained","Best friend","Budding friendship","Comrade","Confessor","Easy to joke with","Feels safe","Finds agreeable","Fun to train with","Good friend","Into the same foods","Like minded","Low maintenance","Not a dull moment","On the same page","Relaxed around","Same wavelength","Sees potential","Shopping buddy","Similar hobbies","Sparring partner","Thinks highly of","Total respect","Totally platonic","Trusts implicitly","Ultimate duo") @@ -221,7 +273,7 @@ async def hubba(self, person1, person2): msg = """{person1} `--{ltr}->` {person2} {person1} `<-{rtl}--` {person2}``` {reaction}```""".format(person1=person1,person2=person2,ltr=ltr,rtl=rtl,reaction=reaction) - await self.bot.say(msg) + await ctx.send(msg) -def setup(bot): - bot.add_cog(Memes(bot)) +async def setup(bot): + await bot.add_cog(Memes(bot)) diff --git a/messageManager.py b/messageManager.py new file mode 100644 index 0000000..0b2a2fa --- /dev/null +++ b/messageManager.py @@ -0,0 +1,132 @@ +import pickle +import cloudinary, urllib +import sys + +ROLE_FILE = "Roles.pickle" +MESSAGE_PROPERTIES_FILE = "MessageProperties.pickle" + +#Internal function for loading and returning the contents of the roles file +def load_roles(): + try: + web_copy = cloudinary.api.resource(ROLE_FILE, resource_type='raw')['url'] + response = urllib.request.urlopen(web_copy) + roleReact_db = pickle.load(response) + except Exception as e: + print(e) + roleReact_db = {} + + with open(ROLE_FILE, "wb") as file: + file.seek(0) + pickle.dump(roleReact_db, file) + + return roleReact_db + +def add_role(messageID, reaction, role): + roleReact_db = load_roles() + if not roleReact_db: + roleReact_db = {} + + if messageID not in roleReact_db: + temp = {messageID: {reaction: role}} + roleReact_db.update(temp) + + with open(ROLE_FILE, "wb") as file: + roleReact_db[messageID][reaction] = role + pickle.dump(roleReact_db, file) + + cloudinary.uploader.upload(ROLE_FILE, resource_type='raw', public_id=ROLE_FILE, invalidate=True) + +def delete_reaction_role(messageID, reaction): + roleReact_db = load_roles() + if not roleReact_db: + return("There are no role reactions set") + + if reaction == "ALL" and messageID in roleReact_db: + roleReact_db.pop(messageID) + + elif messageID in roleReact_db and reaction in roleReact_db[messageID]: + roleReact_db[messageID].pop(reaction) + + else: + return(f"{reaction} reaction for {messageID} is not in database") + + with open(ROLE_FILE, "wb") as file: + pickle.dump(roleReact_db, file) + + cloudinary.uploader.upload(ROLE_FILE, resource_type='raw', public_id=ROLE_FILE, invalidate=True) + return("Done") + +#Internal function for checking database for reaction based roles +def find_role(messageID, reaction): + roleReact_db = load_roles() + if messageID in roleReact_db: + if reaction in roleReact_db[messageID]: + return roleReact_db[messageID][reaction] + + else: + print(f"{reaction} not in {messageID}") + return None + else: + print(f"messageID {messageID} not found") + return None + +def load_properties(): + try: + web_copy = cloudinary.api.resource(MESSAGE_PROPERTIES_FILE, resource_type='raw')['url'] + response = urllib.request.urlopen(web_copy) + property_db = pickle.load(response) + except Exception as e: + print(e) + property_db = {} + + with open(MESSAGE_PROPERTIES_FILE, "wb") as file: + file.seek(0) + pickle.dump(property_db, file) + + return property_db + +def add_property(messageID, newProperty): + property_db = load_properties() + if not property_db: + property_db = {} + + if messageID not in property_db: + temp = {messageID: {newProperty: True}} + property_db.update(temp) + + #Write data + with open(MESSAGE_PROPERTIES_FILE, "wb") as file: + property_db[messageID][newProperty] = True + pickle.dump(property_db, file) + + cloudinary.uploader.upload(MESSAGE_PROPERTIES_FILE, resource_type='raw', public_id=MESSAGE_PROPERTIES_FILE, invalidate=True) + +def delete_property(messageID, newProperty): + property_db = load_properties() + if not property_db: + return("There are no message properties set") + + if newProperty == "ALL" and messageID in property_db: + property_db.pop(messageID) + + elif messageID in property_db and newProperty in property_db[messageID]: + property_db[messageID].pop(newProperty) + + else: + return(f"{newProperty} property for {messageID} is not in database") + + #Write data + with open(MESSAGE_PROPERTIES_FILE, "wb") as file: + pickle.dump(property_db, file) + + cloudinary.uploader.upload(MESSAGE_PROPERTIES_FILE, resource_type='raw', public_id=MESSAGE_PROPERTIES_FILE, invalidate=True) + return("Done") + +def get_properties(messageID): + property_db = load_properties() + if messageID in property_db: + return property_db[messageID] + + else: + print(f"messageID {messageID} not found") + return {} diff --git a/ok.png b/ok.png new file mode 100644 index 0000000..2fd2959 Binary files /dev/null and b/ok.png differ diff --git a/other.py b/other.py new file mode 100644 index 0000000..27fadf7 --- /dev/null +++ b/other.py @@ -0,0 +1,275 @@ +import discord +from discord.ext import commands +import os +from pickle import dump, load +import pickle +from functools import reduce +import cloudinary, cloudinary.uploader, cloudinary.api, urllib +import messageManager as msgManager + +developerIDs = (91393737950777344, 171863408822452224, 146075481534365697) +developerCheck = commands.check(lambda x: x.message.author.id in developerIDs) + +cl_name = os.environ.get('CLOUDNAME', default=None) +cl_key = os.environ.get('CLOUDKEY', default=None) +cl_secret = os.environ.get('CLOUDSECRET', default=None) + +#assume if name is not set then none are set +if cl_name is None: + cl_name, cl_key, cl_secret = open('./clcreds').read().replace('\n','').split(';') + +cloudinary.config( + cloud_name = cl_name, + api_key = cl_key, + api_secret = cl_secret +) + +class Other(commands.Cog): + """Commands added for convienience.""" + def __init__(self, bot): + self.bot = bot + try: + #use cloud if available, fall back on local (i.e. what is in the repo) + web_copy = cloudinary.api.resource("commands.pickle", resource_type='raw')['url'] + # try: + response = urllib.request.urlopen(web_copy) + print(response) + self.dynamicCommands = load(response) + if type(self.dynamicCommands) != dict: + raise Exception + print("Loaded pickle file from cloud") + # except urllib.error.URLError as e: + # print(e) + # if os.path.exists("commands.pickle"): + # with open('commands.pickle', 'rb') as f: + # self.dynamicCommands = load(f) + # if type(self.dynamicCommands) != dict: + # raise Exception + # else: + # self.dynamicCommands = dict() + except Exception as e: + print(e) + print("Attempting to load local backup") + try: + if os.path.exists("commands_backup.pickle"): + with open('commands_backup.pickle', 'rb') as f: + self.dynamicCommands = load(f) + if type(self.dynamicCommands) != dict: + raise Exception + print("Backup loaded.") + else: + print("Corrupted command pickle file! Loading nothing.\nException: %s" % e) + self.dynamicCommands = dict() + except Exception as e2: + print("Corrupted command pickle file and backup! Loading nothing.\nException: %s\n%s" % (e, e2)) + self.dynamicCommands = dict() + + for command in self.dynamicCommands: + # Add a layer of function abstraction to store a context-local variable + def makeCommand(): + localCommand = command # Store for when command changes. + @self.bot.command(name = localCommand, cog_name = "Other") + async def local(ctx): + await ctx.send(self.dynamicCommands[localCommand]) + # And call it. + try: + makeCommand() + except: + pass + + async def developerError(self, error, ctx): + if type(error) == commands.CheckFailure: + await ctx.send('You are not authorized to use that command.') + self.addCommand.error(developerError) + self.removeCommand.error(developerError) + self.save.error(developerError) + self.botEval.error(developerError) + + @commands.command(ignore_extra = False) + @developerCheck + async def addCommand(self, ctx, command_name, command_content): + """Admins only. Adds a new simple response command.""" + if str(command_name).casefold() in map(lambda x: str(x).casefold(), self.bot.commands): + await ctx.send("Command name conflicts with existing command.") + return + + self.dynamicCommands[command_name] = command_content + @self.bot.command(name = command_name, cog_name = "Other") + async def local(ctx): + await ctx.send(command_content) + await ctx.send("Added command \"%s\"." % command_name) + + @commands.command(ignore_extra = False) + @developerCheck + async def removeCommand(self, ctx, command_name): + """Admins only. Removes a previously defined simple response command.""" + if command_name not in self.dynamicCommands: + await ctx.send("Custom command does not exist.") + return + del self.dynamicCommands[command_name] + self.bot.remove_command(command_name) + await ctx.send("Command \"%s\" successfully deleted." % command_name) + + @commands.command() + @developerCheck + async def save(self, ctx): + """Admins only. Saves all of the current custom commands to be loaded when FEUbot is booted.""" + try: + with open('commands.pickle', 'wb') as f: + # Pickle the 'data' dictionary using the highest protocol available. + dump(self.dynamicCommands, f, pickle.HIGHEST_PROTOCOL) + f.close() + result = cloudinary.uploader.upload('commands.pickle', resource_type='raw', public_id='commands.pickle', invalidate=True) + + with open('commands_backup.pickle', 'wb') as f: + dump(self.dynamicCommands, f, pickle.HIGHEST_PROTOCOL) + f.close() + result = cloudinary.uploader.upload('commands_backup.pickle', resource_type='raw', public_id='commands_backup.pickle', invalidate=True) + except Exception as e: + await ctx.send("Error saving commands.\nException: %s" % e) + return + await ctx.send("Commands successfully saved.") + + @commands.command(name = 'eval') + @developerCheck + async def botEval(self, ctx, *, arg): + """Admins only. Evaluate a Python code segment. UNSAFE!!!""" + fix = lambda f: (lambda x: x(x))(lambda y: f(lambda args: y(y)(args))) + try: + res = eval(arg, __builtins__, { "fix" : fix , "reduce" : reduce }) + await ctx.send(str(res)) + except SystemExit: + await ctx.send("I tried to quit().") + + @commands.command() + @developerCheck + async def setReactionRole(self, ctx, messageID, role, reaction): + """Admins only. Sets a reaction role on a specified message""" + if messageID.isdigit(): + msgManager.add_role(str(messageID), reaction, role) + await ctx.send(f"{role} for {reaction} reaction set") + else: + await ctx.send("Invalid messageID") + + @commands.command() + @developerCheck + async def unsetReactionRole(self, ctx, messageID, reaction): + """ + Admins only. Removes a specified reaction role from a specified message. + Using "ALL" as the role argument will remove all reaction roles + """ + return_string = msgManager.delete_reaction_role(str(messageID), reaction) + await ctx.send(return_string) + + @commands.command() + async def listReactionRoles(self, ctx): + """Lists messages with role reactions set along with what roles and reactions are usable""" + return_string = "" + + reactRoles = msgManager.load_roles() + + if not reactRoles: + await ctx.send("No reaction roles set") + return + + #Loop through message dictionaries in reactRoles + for a, b in reactRoles.items(): + messageID_link = None + #loop through server channels to find matching message + for guild in self.bot.guilds: + for channel in guild.text_channels: + #Try to get message by ID + try: + messageID_link = await channel.fetch_message(a) + break + except (ValueError): + continue + except (discord.NotFound): + continue + except (discord.Forbidden): + continue + + #Display link to message if possible + if messageID_link: + return_string += f"<{messageID_link.jump_url}>:\n" + else: + return_string += f"{a}:\n" + + #Loop through reactions for the current message + for x, y in b.items(): + if (discord.utils.get(ctx.guild.roles, name=y)): + return_string += f" {x}: {y}\n" + else: + return_string += f" {x}: {y} | Does not exist\n" + + await ctx.send(return_string) + + @commands.command() + @developerCheck + async def setMessageProperty(self, ctx, messageID, newProperty): + """Adnims only. Sets a property to a message""" + if messageID.isdigit(): + msgManager.add_property(messageID, newProperty) + await ctx.send(f"{newProperty} property set") + else: + await ctx.send("Invalid messageID") + + @commands.command() + @developerCheck + async def unsetMessageProperty(self, ctx, messageID, newProperty): + """ + Adnins only. Removes a property from a message + Using "ALL" as the property argument will remove all properties for the message + """ + return_string = msgManager.delete_property(str(messageID), newProperty) + await ctx.send(return_string) + + @commands.command() + #@developerCheck + async def listMessageProperties(self, ctx): + return_string = "" + + properties = msgManager.load_properties() + + if not properties: + await ctx.send("No properties set") + return + + for a, b in properties.items(): + #loop through server channels to find matching message + for guild in self.bot.guilds: + for channel in guild.text_channels: + #Try to get message by ID + try: + messageID_link = await channel.fetch_message(a) + break + except (ValueError): + continue + except (discord.NotFound): + continue + except (discord.Forbidden): + continue + + #Display link to message if possible + if messageID_link: + return_string += f"<{messageID_link.jump_url}>:\n" + else: + return_string += f"{a}:\n" + + for x, y in b.items(): + return_string += f" {x}\n" + + await ctx.send(return_string) + + + # @commands.command() + # async def cltest(self, ctx): + # filename = "test2.txt" + # with open(filename, 'w+') as save_to: + # save_to.write("testing") + # save_to.close() + # result = cloudinary.uploader.upload(filename, resource_type='raw', public_id=filename[2:], invalidate=True) + # print("saved!") + +async def setup(bot): + await bot.add_cog(Other(bot)) diff --git a/reactions.py b/reactions.py index b94fccf..6560301 100644 --- a/reactions.py +++ b/reactions.py @@ -2,20 +2,20 @@ from discord.ext import commands as bot import os, random, re, asyncio -class Reactions: +class Reactions(bot.Cog): """memes but not""" def __init__(self, bot): self.bot = bot @bot.command() - async def ews(self): + async def ews(self, ctx): """disgusting list""" filenameslist = [os.path.splitext(f)[0] for f in os.listdir("./disgusting")] - await self.bot.say("```"+"\n".join(map(str, filenameslist))+"```") + await ctx.send("```"+"\n".join(map(str, filenameslist))+"```") @bot.command() - async def ew(self,*args): + async def ew(self, ctx,*args): """disgusting""" requested = args ewlist = {a.lower(): a for a in os.listdir("./disgusting")} @@ -30,67 +30,117 @@ async def ew(self,*args): request_file = file_extension_or_not_pattern.sub(extension, request).lower() if request_file in ewlist: found = True - await self.bot.upload("./disgusting/"+ewlist[request_file]) + await ctx.send(file=discord.File("./disgusting/"+ewlist[request_file])) if not found: - await self.bot.say("Use >>ews to see a list of accepted names.") + await ctx.send("Use >>ews to see a list of accepted names.") else: - await self.bot.upload("./disgusting/"+random.choice([a for a in ewlist.values()])) + await ctx.send(file=discord.File("./disgusting/"+random.choice([a for a in ewlist.values()]))) @bot.command() - async def fuckingincredible(self): + async def fuckingincredible(self, ctx, aliases=["incredible"]): """fuckingincredible.png""" - await self.bot.say("http://i.imgur.com/yt4hXhJ.png") + await ctx.send("http://i.imgur.com/yt4hXhJ.png") @bot.command() - async def crackers(self): + async def crackers(self, ctx): """jumping boat monkeys!""" - await self.bot.upload("./Holy_crackers.png") + await ctx.send(file=discord.File("./Holy_crackers.png")) @bot.command() - async def hector(self): + async def hector(self, ctx): """judges you""" - await self.bot.upload("./hectorpc.png") + await ctx.send(file=discord.File("./hectorpc.png")) @bot.command() - async def eliwood(self): + async def eliwood(self, ctx): """:(""" - await self.bot.upload("./eliwoodpc.jpg") + await ctx.send(file=discord.File("./eliwoodpc.jpg")) @bot.command() - async def lyn(self): + async def lyn(self, ctx): """>:(""" - await self.bot.upload("./lynpc.png") + await ctx.send(file=discord.File("./lynpc.png")) @bot.command() - async def spritans(self): + async def spritans(self, ctx): """REEE""" - await self.bot.say("muh") + await ctx.send("muh") await asyncio.sleep(1) - await self.bot.say("SPRITANS") + await ctx.send("SPRITANS") await asyncio.sleep(2) - await self.bot.say("***REEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE***") + await ctx.send("***REEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE***") + @bot.command() - async def reee(self): + async def reee(self, ctx): """REEEEEEEEEEEEEEEEEEE""" - msg = await self.bot.say("*REEE*") - await asyncio.sleep(0.5) - for i in range(1, random.randint(5,10)): + action = random.choice([1,2]) + if action==1: + msg = await ctx.send("*REEE*") + await asyncio.sleep(0.5) + for i in range(1, random.randint(5,10)): + await asyncio.sleep(0.25) + await msg.edit(content = "**REEE" + "E"*i + "**") await asyncio.sleep(0.25) - await self.bot.edit_message(msg, "**REEE" + "E"*i + "**") - await asyncio.sleep(0.25) - await self.bot.edit_message(msg, "***REEE" + "E"*(i+1) + "***") + await msg.edit(content = "***REEE" + "E"*(i+1) + "***") + else: + await ctx.send(file=discord.File("./reee.gif")) - @bot.command(aliases=["f", 'respects']) - async def F(self): + @bot.command() + async def F(self, ctx): """Press F to pay respects.""" - await self.bot.upload("./respects.jpeg") + await ctx.send(file=discord.File("./respects.jpeg")) @bot.command() - async def enough(self): + async def enough(self, ctx): """you wouldn't like me when i'm angry""" - await self.bot.upload("./enough.png") + await ctx.send(file=discord.File("./enough.png")) + + @bot.command() + async def creepy(self, ctx): + """stay away""" + await ctx.send(file=discord.File("./creepy.png")) + + @bot.command() + async def tethys(self, ctx): + """dancer think""" + await ctx.send(file=discord.File("./tethys.png")) + + @bot.command() + async def marisa(self, ctx): + """u srs""" + await ctx.send(file=discord.File("./marisa.png")) + @bot.command() + async def lel(self, ctx): + """lel""" + img=random.choice(["./lel.png","./lel2.png"]) + await ctx.send(file=discord.File(img)) + + @bot.command() + async def approve(self, ctx, pass_context=True, hidden=True): + pid = str(ctx.message.author.id) + if pid == '171863408822452224': + await ctx.send(file=discord.File('./approved.png')) + elif pid == '378954025145466880': + await ctx.send(file=discord.File('./Letha_Seal_of_Approval.png')) + else: + await ctx.send(file=discord.File('./FEU_Seal.png')) + + @bot.command() + async def ok(self, ctx, aliases=["OK"]): + """ok""" + await ctx.send(file=discord.File("./ok.png")) + + @bot.command() + async def uberthink(self, ctx): + """🤔""" + await ctx.send(file=discord.File("./uberthink.gif")) + + @bot.command() + async def awful(self, ctx, aliases=["awfuldisplay"]): + """for when someone posts cringe""" + await ctx.send(file=discord.File("./awful.jpg")) -def setup(bot): - bot.add_cog(Reactions(bot)) +async def setup(bot): + await bot.add_cog(Reactions(bot)) diff --git a/reee.gif b/reee.gif new file mode 100644 index 0000000..ae0facf Binary files /dev/null and b/reee.gif differ diff --git a/requirements.txt b/requirements.txt index 03f5e9a..538450e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -discord.py==0.16.8 +discord.py>=1.7.3 aiohttp websockets -chardet \ No newline at end of file +chardet +cloudinary \ No newline at end of file diff --git a/runtime.txt b/runtime.txt index 5f61493..396db58 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -python-3.5.2 \ No newline at end of file +python-3.9.6 \ No newline at end of file diff --git a/slow.png b/slow.png new file mode 100644 index 0000000..42a79cc Binary files /dev/null and b/slow.png differ diff --git a/tethys.png b/tethys.png new file mode 100644 index 0000000..4864ead Binary files /dev/null and b/tethys.png differ diff --git a/uberthink.gif b/uberthink.gif new file mode 100644 index 0000000..f349d0a Binary files /dev/null and b/uberthink.gif differ diff --git a/undelete.py b/undelete.py new file mode 100644 index 0000000..afc1129 --- /dev/null +++ b/undelete.py @@ -0,0 +1,96 @@ + +from time import time +from functools import reduce + +import discord +from discord.ext import commands as bot +from other import developerIDs + +_TIMEOUT = 60 + +_cache = None + +class DeletedCache(object): + __slots__ = ['channels'] + + def insert(self, msg): + reqt = time() + key = msg.channel.id + if key not in self.channels: + self.channels[key] = [(msg, reqt)] + else: + self.channels[key].append((msg, reqt)) + self._prune(key, reqt) + + def last(self, chn, n, name=None): + if name and len(name)>4: name = name[3:-1] #remove the borders on the id. + reqt = time() + self._prune(chn.id, reqt) + if chn.id not in self.channels: return None + s = [(msg, ts) for msg, ts in self.channels[chn.id] + if name is None or msg.author.id == name] + if not s: return None + + self.channels[chn.id] = [m for m in self.channels[chn.id] if m not in s] #remove the messages we're undeleting. + return [x[0] for x in s] + #n = len(s) + #result = (("%s deleted %d message(s) within the last minute:\n" + # % (name,n)) + # if name is not None + # else ("%d deleted message(s) within the last minute:\n" % n)) + #return result + '```' + '\n'.join(m.content for m in s) + '```' + + def __init__(self): + self.channels = dict() + + def _prune(self, key, reqt): + if key not in self.channels: return + self.channels[key] = [ + (msg, ts) for msg, ts in self.channels[key] if reqt-ts <= _TIMEOUT + ] + super().__init__() + +undeleterPred = lambda ctx: not ctx.message.channel.is_private and discord.utils.get(ctx.message.author.roles, name="Undeleter") is not None +manageMessagePred = lambda ctx: getattr(ctx.message.channel.permissions_for(ctx.message.author), 'manage_messages', None) == True + +class UndeleteCog(object): + AUTHOR_FORMAT = '{author} said:\n```\n{content}```\n' + NO_AUTHOR_FORMAT ='```\n{content}```\n' + __slots__ = ['bot'] + + @bot.command(pass_context=True, hidden=True) + @bot.check(lambda ctx: type(ctx.message.author) is discord.Member) + @bot.check(lambda ctx: undeleterPred(ctx) or manageMessagePred(ctx) or ctx.message.author in developerIDs) + async def undelete(self, ctx, n=1, name=None): + msg = ctx.message + if n < 1: return + result = _cache.last(msg.channel, n, name) + if result is not None: + FORMAT = self.AUTHOR_FORMAT if not name else self.NO_AUTHOR_FORMAT + await self.bot.say( + ('{} deleted message(s) by {} within the last minute:\n'.format(len(result), name) if name else + '{} deleted message(s) within the last minute:\n'.format(len(result))) + + reduce(lambda acc, e: acc+e, + [FORMAT.format(author=msg.author, content=msg.content) for msg in result] + , "") + ) + else: + await self.bot.say( + 'No messages deleted ' + + ('by %s ' % name if name is not None else '') + + 'within the last minute') + + def __init__(self, bot): + global _cache + _cache = DeletedCache() + self.bot = bot + async def undeleteError(self, error, ctx): + await self.bot.send_message(ctx.message.channel, 'You do not have undelete permissions.') + self.undelete.error(undeleteError) + +async def on_message_delete(msg): + _cache.insert(msg) + +async def setup(bot): + await bot.add_cog(UndeleteCog(bot)) + bot.event(on_message_delete)