Compare commits
42 Commits
aikaterna-
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7fd5930f9 | ||
|
|
fd199aea93 | ||
|
|
e894bf21bc | ||
|
|
9de37b1fd1 | ||
|
|
e31efdba0c | ||
|
|
492e9244d6 | ||
|
|
01aa67fa9a | ||
|
|
adb3efa746 | ||
|
|
2cc04c3ac0 | ||
|
|
b2384eb7df | ||
|
|
4d66912fa2 | ||
|
|
f0f3a4171a | ||
|
|
7f227e762c | ||
|
|
f5b089f3ec | ||
|
|
ea207bccb0 | ||
|
|
cbd9901857 | ||
|
|
74901f5e37 | ||
|
|
d13075f0c6 | ||
|
|
76559c6b25 | ||
|
|
deaba0dde0 | ||
|
|
f33671601b | ||
|
|
7dd97fb2c0 | ||
|
|
eaee1acf21 | ||
|
|
02ebcf7192 | ||
|
|
567fb3077c | ||
|
|
829308f42a | ||
|
|
4e4f26f2db | ||
|
|
2d8cf0a1e4 | ||
|
|
a372ff997e | ||
|
|
f209aa2975 | ||
|
|
f8a501285f | ||
|
|
3112153c57 | ||
|
|
d2291ba7a9 | ||
|
|
2a1231f42f | ||
|
|
2b5e99b403 | ||
|
|
48132fe5b8 | ||
|
|
8f23276ad2 | ||
|
|
ae1567425c | ||
|
|
115144d9e3 | ||
|
|
37c808a9a1 | ||
|
|
fa0b3a6389 | ||
|
|
93465b2140 |
19
README.md
19
README.md
@@ -1,5 +1,12 @@
|
||||
# aikaterna-cogs
|
||||
Cogs for Red-DiscordBot by Twentysix26.
|
||||
**v2 cogs support has ended. I will still provide bugfixes on these cogs if it is significant enough, but no new features will be added.**
|
||||
|
||||
**Red v3 cogs can be found on the v3 branch of this repo. (https://github.com/aikaterna/aikaterna-cogs/tree/v3)**
|
||||
|
||||
________________
|
||||
|
||||
Cogs for v2 Red-DiscordBot by Twentysix26.
|
||||
|
||||
|
||||
autoeconomy - New users that join the server will be automatically given a bank account.
|
||||
|
||||
@@ -15,8 +22,12 @@ hunting - By Paddolicious#8880. It hunts birds... and things that fly.
|
||||
|
||||
imgwelcome - Welcome users to your server with a customized image.
|
||||
|
||||
modclean - Clean the last 100 entries in the mod-log of Discord invite names.
|
||||
|
||||
otherbot - Have multiple Red instances and want to know when one goes offline? Edit this cog and load it on your watcher bot.
|
||||
|
||||
post - Upload a saved audio playlist to the chat channel, or upload a cog. Owner only.
|
||||
|
||||
pug - Warcraft pug checker. A port of PugBot's module: https://github.com/reznok/PugBot
|
||||
|
||||
radio - A hidden unpublished gem from Paddo, with a couple edits. Plays http audio streams like icecast and mp3 streams.
|
||||
@@ -27,10 +38,8 @@ seen - By Paddolicious#8880. Check when the user was last active on a server.
|
||||
|
||||
serverlimit - Limit the bot to joining servers with over 25 members.
|
||||
|
||||
wolfram - Paddolicious#8880's old wolfram cog.
|
||||
|
||||
The chatterbot cog that was previously on this repo has been removed in favor of: https://github.com/Nobleasskicker/GrandeCogs/tree/master/chat
|
||||
|
||||
Q: Why do you have some of Paddo's cogs on your repo?
|
||||
A: He recently made an announcement about finding a new home for a few cogs. I've picked up a couple and I welcome PRs or feature requests. I'm also intending to port these for Red v3 as I can.
|
||||
youtube - Paddolicious#8880's old youtube cog, backported from my v3 version.
|
||||
|
||||
Feel free to join the server for these cogs if you'd like. https://discord.gg/th6eS3T
|
||||
|
||||
@@ -19,8 +19,7 @@ class AutoEconomy:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.settings = dataIO.load_json('data/autoeconomy/settings.json')
|
||||
self.banksettings = dataIO.load_json('data/economy/settings.json')
|
||||
self.version = "0.1.1b"
|
||||
self.version = "0.1.2"
|
||||
|
||||
async def save_settings(self):
|
||||
dataIO.save_json('data/autoeconomy/settings.json', self.settings)
|
||||
@@ -85,31 +84,64 @@ class AutoEconomy:
|
||||
"""Displays the autoeconomy version."""
|
||||
await self.bot.say("autoeconomy version {}.".format(self.version))
|
||||
|
||||
async def on_member_join(self, member):
|
||||
@autoeconomy.command(name="massregister", pass_context=True, no_pm=True)
|
||||
async def massregister(self, ctx):
|
||||
"""Mass register existing users."""
|
||||
econ_cog = self.bot.get_cog('Economy')
|
||||
if not econ_cog:
|
||||
return await self.bot.say("This requires economy to be loaded.")
|
||||
server = ctx.message.server
|
||||
if server.id not in econ_cog.bank.accounts:
|
||||
return await self.bot.say(
|
||||
"I can't register people for a bank that doesn't exist yet."
|
||||
)
|
||||
|
||||
count = 0
|
||||
for member in server.members:
|
||||
init_balance = econ_cog.settings[server.id].get("REGISTER_CREDITS", 0)
|
||||
try:
|
||||
econ_cog.bank.create_account(member, initial_balance=init_balance)
|
||||
except Exception:
|
||||
continue
|
||||
else:
|
||||
count += 1
|
||||
|
||||
await self.bot.say(
|
||||
"I've opened up new economy entries for "
|
||||
"{}/{} members.".format(count, len(server.members))
|
||||
)
|
||||
|
||||
async def on_member_join(self, member, mass_register=False):
|
||||
server = member.server
|
||||
|
||||
if server.id not in self.settings:
|
||||
self.settings[server.id] = deepcopy(default_settings)
|
||||
await self.save_settings()
|
||||
if not self.settings[server.id]["TOGGLE"]:
|
||||
if not (self.settings[server.id]["TOGGLE"] or mass_register):
|
||||
return
|
||||
|
||||
channel = self.settings[server.id]["CHANNEL"]
|
||||
channel_object = self.bot.get_channel(channel)
|
||||
econ_cog = self.bot.get_cog('Economy')
|
||||
|
||||
if server.id not in econ_cog.bank.accounts:
|
||||
return
|
||||
if not econ_cog:
|
||||
return
|
||||
|
||||
bank = self.bot.get_cog('Economy').bank
|
||||
init_balance = econ_cog.settings[server.id].get("REGISTER_CREDITS", 0)
|
||||
|
||||
try:
|
||||
bank.create_account(member)
|
||||
bank.create_account(member, initial_balance=init_balance)
|
||||
except Exception:
|
||||
if self.settings[server.id]["DEBUG"]:
|
||||
if self.settings[server.id]["DEBUG"] and not mass_register:
|
||||
await self.bot.send_message(channel_object, "Economy account already exists for {}.".format(member.name))
|
||||
return
|
||||
if self.banksettings[server.id]["REGISTER_CREDITS"]:
|
||||
reg_credits = self.banksettings[server.id]["REGISTER_CREDITS"]
|
||||
bank.deposit_credits(member, reg_credits)
|
||||
if self.settings[server.id]["DEBUG"]:
|
||||
return False
|
||||
else:
|
||||
if self.settings[server.id]["DEBUG"] and not mass_register:
|
||||
await self.bot.send_message(channel_object, "Bank account opened for {} and initial credits given.".format(member.name))
|
||||
return
|
||||
return True
|
||||
|
||||
|
||||
def check_folders():
|
||||
|
||||
@@ -20,67 +20,83 @@ class ChatChart:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
def create_chart(self, top, others):
|
||||
def create_chart(self, top, others, channel):
|
||||
plt.clf()
|
||||
sizes = [x[1] for x in top]
|
||||
labels = ["{} {:g}%".format(x[0], x[1]) for x in top]
|
||||
if len(top) >= 10:
|
||||
if len(top) >= 20:
|
||||
sizes = sizes + [others]
|
||||
labels = labels + ["Others {:g}%".format(others)]
|
||||
|
||||
title = plt.title('User activity in the last 5000 messages')
|
||||
if len(channel.name) >= 19:
|
||||
channel_name = '{}...'.format(channel.name[:19])
|
||||
else:
|
||||
channel_name = channel.name
|
||||
title = plt.title("Stats in #{}".format(channel_name), color="white")
|
||||
title.set_va("top")
|
||||
title.set_ha("left")
|
||||
title.set_ha("center")
|
||||
plt.gca().axis("equal")
|
||||
colors = ['r', 'darkorange', 'gold', 'y', 'olivedrab', 'green', 'darkcyan', 'mediumblue', 'darkblue', 'blueviolet', 'indigo']
|
||||
colors = ['r', 'darkorange', 'gold', 'y', 'olivedrab', 'green', 'darkcyan', 'mediumblue', 'darkblue', 'blueviolet', 'indigo', 'orchid', 'mediumvioletred', 'crimson', 'chocolate', 'yellow', 'limegreen','forestgreen','dodgerblue','slateblue','gray']
|
||||
pie = plt.pie(sizes, colors=colors, startangle=0)
|
||||
plt.legend(pie[0], labels, bbox_to_anchor=(0.7, 0.5), loc="center", fontsize=10,
|
||||
bbox_transform=plt.gcf().transFigure)
|
||||
bbox_transform=plt.gcf().transFigure, facecolor='#ffffff')
|
||||
plt.subplots_adjust(left=0.0, bottom=0.1, right=0.45)
|
||||
image_object = BytesIO()
|
||||
plt.savefig(image_object, format='PNG')
|
||||
plt.savefig(image_object, format='PNG', facecolor='#36393E')
|
||||
image_object.seek(0)
|
||||
return image_object
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
@commands.command(pass_context=True, no_pm=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.channel)
|
||||
async def chatchart(self, ctx):
|
||||
async def chatchart(self, ctx, channel: discord.Channel=None):
|
||||
"""
|
||||
Generates a pie chart, representing the last 5000 messages in this channel.
|
||||
Generates a pie chart, representing the last 5000 messages in the specified channel.
|
||||
"""
|
||||
channel = ctx.message.channel
|
||||
e = discord.Embed(description="Loading...", colour=0x00ccff)
|
||||
e.set_thumbnail(url="https://i.imgur.com/vSp4xRk.gif")
|
||||
em = await self.bot.say(embed=e)
|
||||
|
||||
|
||||
if channel is None:
|
||||
channel = ctx.message.channel
|
||||
history = []
|
||||
async for msg in self.bot.logs_from(channel, 5000):
|
||||
history.append(msg)
|
||||
if not channel.permissions_for(ctx.message.author).read_messages == True:
|
||||
await self.bot.delete_message(em)
|
||||
return await self.bot.say("You're not allowed to access that channel.")
|
||||
try:
|
||||
async for msg in self.bot.logs_from(channel, 5000):
|
||||
history.append(msg)
|
||||
except discord.errors.Forbidden:
|
||||
await self.bot.delete_message(em)
|
||||
return await self.bot.say("No permissions to read that channel.")
|
||||
msg_data = {'total count': 0, 'users': {}}
|
||||
|
||||
for msg in history:
|
||||
if len(msg.author.name) >= 20:
|
||||
short_name = '{}...'.format(msg.author.name[:20]).replace("$", "\$")
|
||||
else:
|
||||
short_name = msg.author.name.replace("$", "\$")
|
||||
whole_name = '{}#{}'.format(short_name, msg.author.discriminator)
|
||||
if msg.author.bot:
|
||||
pass
|
||||
elif msg.author.name in msg_data['users']:
|
||||
msg_data['users'][msg.author.name]['msgcount'] += 1
|
||||
elif whole_name in msg_data['users']:
|
||||
msg_data['users'][whole_name]['msgcount'] += 1
|
||||
msg_data['total count'] += 1
|
||||
else:
|
||||
msg_data['users'][msg.author.name] = {}
|
||||
msg_data['users'][msg.author.name]['msgcount'] = 1
|
||||
msg_data['users'][whole_name] = {}
|
||||
msg_data['users'][whole_name]['msgcount'] = 1
|
||||
msg_data['total count'] += 1
|
||||
|
||||
for usr in msg_data['users']:
|
||||
pd = float(msg_data['users'][usr]['msgcount']) / float(msg_data['total count'])
|
||||
msg_data['users'][usr]['percent'] = round(pd * 100, 1)
|
||||
|
||||
top_ten = heapq.nlargest(10, [(x, msg_data['users'][x][y])
|
||||
top_ten = heapq.nlargest(20, [(x, msg_data['users'][x][y])
|
||||
for x in msg_data['users']
|
||||
for y in msg_data['users'][x]
|
||||
if y == 'percent'], key=lambda x: x[1])
|
||||
others = 100 - sum(x[1] for x in top_ten)
|
||||
img = self.create_chart(top_ten, others)
|
||||
img = self.create_chart(top_ten, others, channel)
|
||||
await self.bot.delete_message(em)
|
||||
await self.bot.send_file(channel, img, filename="chart.png")
|
||||
await self.bot.send_file(ctx.message.channel, img, filename="chart.png")
|
||||
|
||||
|
||||
def check_folders():
|
||||
|
||||
@@ -55,7 +55,11 @@ class ImgWelcome:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.settings = dataIO.load_json('data/imgwelcome/settings.json')
|
||||
self.version = "0.1.6"
|
||||
self.version = "0.1.8"
|
||||
self.session = aiohttp.ClientSession()
|
||||
|
||||
def __unload(self):
|
||||
self.session.close()
|
||||
|
||||
async def save_settings(self):
|
||||
dataIO.save_json('data/imgwelcome/settings.json', self.settings)
|
||||
@@ -152,7 +156,7 @@ class ImgWelcome:
|
||||
drawtwo.text((152, 70), uname, font=name_font_small, fill=(fontcolor))
|
||||
|
||||
if len(uname) >= 33:
|
||||
drawtwo.text((152, 73), uname, 1, name_font_smallest, (textoutline))
|
||||
_outline((152, 73), uname, 1, name_font_smallest, (textoutline))
|
||||
drawtwo.text((152, 73), uname, font=name_font_smallest, fill=(fontcolor))
|
||||
|
||||
if test_member_number is None:
|
||||
@@ -220,7 +224,7 @@ class ImgWelcome:
|
||||
await self.save_settings()
|
||||
|
||||
async def _get_profile(self, url):
|
||||
async with aiohttp.get(url) as r:
|
||||
async with self.session.get(url) as r:
|
||||
image = await r.content.read()
|
||||
with open('data/imgwelcome/profilepic.png', 'wb') as f:
|
||||
f.write(image)
|
||||
@@ -407,7 +411,7 @@ class ImgWelcome:
|
||||
|
||||
if success:
|
||||
try:
|
||||
async with aiohttp.get(bg_url) as r:
|
||||
async with self.session.get(bg_url) as r:
|
||||
image = await r.content.read()
|
||||
if not os.path.exists('data/imgwelcome/{}'.format(server.id)):
|
||||
os.makedirs('data/imgwelcome/{}'.format(server.id))
|
||||
@@ -587,14 +591,14 @@ class ImgWelcome:
|
||||
await self.bot.send_typing(channel_object)
|
||||
image_object = await self._create_welcome(member, member.avatar_url)
|
||||
await self.bot.send_file(channel_object, image_object, filename="welcome.png")
|
||||
if (len(member.server.members) % 100) == 0 or (len(member.server.members) == 1337) and self.settings[server.id]["SPECIAL_USERS"]:
|
||||
if ((len(member.server.members) % 100) == 0 or (len(member.server.members) == 1337)) and self.settings[server.id]["BONUSES"]["SPECIAL_USERS"]:
|
||||
msg = "\N{PARTY POPPER} Thanks <@" + member.id + ">, you're the ***" + str(len(member.server.members)) + "*** th user on this server! \N{PARTY POPPER}"
|
||||
await self.bot.send_message(channel_object, msg)
|
||||
date_join = datetime.datetime.strptime(str(member.created_at), "%Y-%m-%d %H:%M:%S.%f")
|
||||
date_now = datetime.datetime.now(datetime.timezone.utc)
|
||||
date_now = date_now.replace(tzinfo=None)
|
||||
since_join = date_now - date_join
|
||||
if since_join.days < 7 and self.settings[server.id]["ACCOUNT_WARNINGS"]:
|
||||
if since_join.days < 7 and self.settings[server.id]["BONUSES"]["ACCOUNT_WARNINGS"]:
|
||||
await self.bot.send_message(channel_object, "\N{WARNING SIGN} This account was created less than a week ago (" + str(since_join.days) + " days ago)")
|
||||
|
||||
|
||||
|
||||
8
modclean/info.json
Normal file
8
modclean/info.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"AUTHOR" : "aikaterna and sitryk",
|
||||
"INSTALL_MSG" : "Use `[p]modclean #mod-log` to clean your mod log channel. Edits out Discord invite links from banned or kicked users.",
|
||||
"NAME" : "modclean",
|
||||
"SHORT" : "Clean your mod log channel of Discord invite names.",
|
||||
"DESCRIPTION" : "Clean your mod log channel of Discord invite names.",
|
||||
"TAGS": ["modlog"]
|
||||
}
|
||||
40
modclean/modclean.py
Normal file
40
modclean/modclean.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import discord
|
||||
import re
|
||||
from cogs.utils import checks
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
class ModClean:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(no_pm=True, pass_context=True)
|
||||
@checks.is_owner()
|
||||
async def modclean(self, ctx, modchannel: discord.Channel = None):
|
||||
"""Clean a v2 mod-log channel of invite names."""
|
||||
if not modchannel:
|
||||
return await self.bot.say(
|
||||
"Please use the mod channel in the command. ({}modclean #channelname)".format(
|
||||
ctx.prefix
|
||||
)
|
||||
)
|
||||
|
||||
IL_raw = r"(discordapp.com/invite|discord.me|discord.gg)(?:/#)?(?:/invite)?/([a-z0-9\-]+)"
|
||||
InvLink = re.compile(IL_raw, re.I)
|
||||
|
||||
try:
|
||||
async for m in self.bot.logs_from(modchannel, 100):
|
||||
if not (m.author == ctx.message.server.me):
|
||||
continue
|
||||
elif InvLink.search(m.content) is None:
|
||||
continue
|
||||
else:
|
||||
new_cont = InvLink.sub("[REMOVED LINK]", m.content)
|
||||
await self.bot.edit_message(m, new_cont)
|
||||
except discord.errors.Forbidden:
|
||||
return await self.bot.say("No permissions to read that channel.")
|
||||
await self.bot.say("Done.")
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(ModClean(bot))
|
||||
@@ -109,24 +109,24 @@ class PicWelcome:
|
||||
print(e)
|
||||
|
||||
serverimage = Image
|
||||
|
||||
if success:
|
||||
try:
|
||||
async with aiohttp.get(bg_url) as r:
|
||||
image = await r.content.read()
|
||||
if not os.path.exists('data/picwelcome/{}'.format(server.id)):
|
||||
os.makedirs('data/picwelcome/{}'.format(server.id))
|
||||
serverbg = 'data/picwelcome/{}/serverpic.png'.format(server.id)
|
||||
file_suffix = bg_url.rsplit('.', 1)[1]
|
||||
serverbg = 'data/picwelcome/{}/serverpic.{}'.format(server.id, file_suffix)
|
||||
with open(serverbg, 'wb') as f:
|
||||
f.write(image)
|
||||
serverimage = Image.open(serverbg).convert('RGBA')
|
||||
success = True
|
||||
|
||||
except Exception as e:
|
||||
success = False
|
||||
print(e)
|
||||
|
||||
if success:
|
||||
self.settings[server.id]['PICTURE'] = "data/picwelcome/" + ctx.message.server.id + "/serverpic.png"
|
||||
self.settings[server.id]['PICTURE'] = "data/picwelcome/{}/serverpic.{}".format(ctx.message.server.id, file_suffix)
|
||||
await self.save_settings()
|
||||
await self.bot.say('Welcome image for this server set to uploaded file.')
|
||||
else:
|
||||
@@ -152,12 +152,10 @@ class PicWelcome:
|
||||
serverpicture = self.settings[server.id]["PICTURE"]
|
||||
await self.bot.send_file(channel_object, serverpicture)
|
||||
|
||||
|
||||
def check_folders():
|
||||
if not os.path.exists('data/picwelcome/'):
|
||||
os.mkdir('data/picwelcome/')
|
||||
|
||||
|
||||
def check_files():
|
||||
if not dataIO.is_valid_json('data/picwelcome/settings.json'):
|
||||
defaults = {}
|
||||
|
||||
31
post/post.py
Normal file
31
post/post.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from discord.ext import commands
|
||||
from .utils import checks
|
||||
|
||||
class Post:
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(no_pm=True, pass_context=True)
|
||||
@checks.is_owner()
|
||||
async def postsongs(self, ctx, playlist):
|
||||
"""Posts a playlist."""
|
||||
try:
|
||||
await self.bot.send_file(ctx.message.channel, 'data/audio/playlists/{}/{}.txt'.format(ctx.message.server.id, playlist))
|
||||
except FileNotFoundError:
|
||||
try:
|
||||
await self.bot.send_file(ctx.message.channel, 'data/audio/playlists/{}.txt'.format(playlist))
|
||||
except FileNotFoundError:
|
||||
await self.bot.say("No playlist named {}.".format(playlist))
|
||||
|
||||
@commands.command(no_pm=True, pass_context=True)
|
||||
@checks.is_owner()
|
||||
async def postcog(self, ctx, cogname):
|
||||
"""Posts a cog."""
|
||||
try:
|
||||
await self.bot.send_file(ctx.message.channel, 'cogs/{}.py'.format(cogname))
|
||||
except FileNotFoundError:
|
||||
await self.bot.say("No cog named {}.".format(cogname))
|
||||
|
||||
def setup(bot):
|
||||
n = Post(bot)
|
||||
bot.add_cog(n)
|
||||
@@ -44,13 +44,18 @@ class Radio:
|
||||
async def _list(self, ctx):
|
||||
"""List saved stream URLs."""
|
||||
server = ctx.message.server
|
||||
message = '```\n'
|
||||
message += '{:<30}{}\n\n'.format('NAME', 'URL')
|
||||
message_list = []
|
||||
if server.id in self.memory:
|
||||
for stream in self.memory[server.id]:
|
||||
message += '{:<30}{}\n'.format(stream, self.memory[server.id][stream])
|
||||
message += '```'
|
||||
await self.bot.say(message)
|
||||
message = '{:<30}{}\n'.format(stream, self.memory[server.id][stream])
|
||||
message_list.append(message)
|
||||
sorted_list = sorted(message_list, key=str.lower)
|
||||
msg = '```'
|
||||
msg += '{:<30}{}\n\n'.format('NAME', 'URL')
|
||||
for sorted_msg in sorted_list:
|
||||
msg += sorted_msg
|
||||
msg += '```'
|
||||
await self.bot.say(msg)
|
||||
|
||||
@_radio.command(no_pm=True, pass_context=True, name='add')
|
||||
async def _add(self, ctx, name: str, url: str):
|
||||
@@ -75,6 +80,13 @@ class Radio:
|
||||
else:
|
||||
await self.bot.say('Nothing in memory yet')
|
||||
|
||||
@_radio.command(no_pm=True, pass_context=True, name='remove')
|
||||
async def _remove(self, ctx, name: str):
|
||||
"""Remove a saved radio stream."""
|
||||
server = ctx.message.server
|
||||
await self.remove_from_memory(server, name)
|
||||
await self.bot.say('Removed {} from memory.'.format(name))
|
||||
|
||||
async def save_memory(self):
|
||||
dataIO.save_json(self.memory_path, self.memory)
|
||||
|
||||
@@ -84,6 +96,10 @@ class Radio:
|
||||
self.memory[server.id][name.lower()] = url
|
||||
await self.save_memory()
|
||||
|
||||
async def remove_from_memory(self, server, name):
|
||||
del self.memory[server.id][name.lower()]
|
||||
await self.save_memory()
|
||||
|
||||
async def join_voice_channel(self, channel):
|
||||
try:
|
||||
await self.bot.join_voice_channel(channel)
|
||||
|
||||
9
tools/info.json
Normal file
9
tools/info.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"AUTHOR" : "aikaterna, Sitryk, and Axas",
|
||||
"INSTALL_MSG" : "Thanks for installing.",
|
||||
"NAME" : "tools",
|
||||
"SHORT" : "A collection of tools for mods.",
|
||||
"DESCRIPTION" : "A collection of channel, user, and server tools for mods.",
|
||||
"REQUIREMENTS": ["tabulate"],
|
||||
"TAGS": ["tools"]
|
||||
}
|
||||
813
tools/tools.py
Normal file
813
tools/tools.py
Normal file
@@ -0,0 +1,813 @@
|
||||
# Most of these tools are thanks to Sitryk.
|
||||
# Credit for the findcog/cmd_lookup command belongs to Axas, thanks for the inspiration for
|
||||
# the findcog command in Red v3.
|
||||
|
||||
from discord.ext import commands
|
||||
from .utils.chat_formatting import pagify, box, escape_mass_mentions
|
||||
from .utils.dataIO import dataIO
|
||||
from .utils import checks
|
||||
from __main__ import send_cmd_help
|
||||
from tabulate import tabulate
|
||||
import discord
|
||||
import glob
|
||||
import os
|
||||
import datetime
|
||||
import asyncio
|
||||
import discord
|
||||
import random
|
||||
import inspect
|
||||
|
||||
ini = "```ini\n{0}\n```"
|
||||
|
||||
|
||||
class Tools:
|
||||
"""Mod and Admin tools."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
@commands.group(pass_context=True, no_pm=True)
|
||||
async def access(self, ctx):
|
||||
"""Check channel access."""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await send_cmd_help(ctx)
|
||||
return
|
||||
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
@access.command(pass_context=True)
|
||||
async def compare(self, ctx, user: discord.User, server: discord.Server = None):
|
||||
"""Compare channel access with [user]"""
|
||||
author = ctx.message.author
|
||||
if user is None:
|
||||
return
|
||||
if server is None:
|
||||
server = ctx.message.server
|
||||
|
||||
text_channels = [c for c in server.channels if str(c.type) == "text"]
|
||||
voice_channels = [c for c in server.channels if str(c.type) == "voice"]
|
||||
|
||||
author_text_channels = [
|
||||
c.name for c in text_channels if c.permissions_for(author).read_messages is True
|
||||
]
|
||||
author_voice_channels = [
|
||||
c.name for c in voice_channels if c.permissions_for(author).connect is True
|
||||
]
|
||||
|
||||
user_text_channels = [
|
||||
c.name for c in text_channels if c.permissions_for(user).read_messages is True
|
||||
]
|
||||
user_voice_channels = [
|
||||
c.name for c in voice_channels if c.permissions_for(user).connect is True
|
||||
]
|
||||
|
||||
author_only_t = set(author_text_channels) - set(
|
||||
user_text_channels
|
||||
) # text channels only the author has access to
|
||||
author_only_v = set(author_voice_channels) - set(
|
||||
user_voice_channels
|
||||
) # voice channels only the author has access to
|
||||
|
||||
user_only_t = set(user_text_channels) - set(
|
||||
author_text_channels
|
||||
) # text channels only the user has access to
|
||||
user_only_v = set(user_voice_channels) - set(
|
||||
author_voice_channels
|
||||
) # voice channels only the user has access to
|
||||
|
||||
common_t = list(
|
||||
set(text_channels) - author_only_t - user_only_t
|
||||
) # text channels that author and user have in common
|
||||
common_v = list(
|
||||
set(voice_channels) - author_only_v - user_only_v
|
||||
) # voice channels that author and user have in common
|
||||
|
||||
msg = "```ini\n"
|
||||
msg += "{} [TEXT CHANNELS IN COMMON]:\n\n{}\n\n".format(
|
||||
len(common_t), ", ".join([c.name for c in common_t])
|
||||
)
|
||||
msg += "{} [TEXT CHANNELS {} HAS ACCESS TO]:\n\n{}\n\n".format(
|
||||
len(user_only_t), user.name.upper(), ", ".join(list(user_only_t))
|
||||
)
|
||||
msg += "{} [TEXT CHANNELS YOU HAVE ACCESS TO]:\n\n{}\n\n".format(
|
||||
len(author_only_t), ", ".join(list(author_only_t))
|
||||
)
|
||||
msg += "{} [VOICE CHANNELS IN COMMON]:\n\n{}\n\n".format(
|
||||
len(common_v), ", ".join([c.name for c in common_v])
|
||||
)
|
||||
msg += "{} [VOICE CHANNELS {} HAS ACCESS TO]:\n\n{}\n\n".format(
|
||||
len(user_only_v), user.name.upper(), ", ".join(list(user_only_v))
|
||||
)
|
||||
msg += "{} [VOICE CHANNELS YOU HAVE ACCESS TO]:\n\n{}\n\n".format(
|
||||
len(author_only_v), ", ".join(list(author_only_v))
|
||||
)
|
||||
msg += "```"
|
||||
await self.bot.say(msg)
|
||||
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
@access.command(pass_context=True)
|
||||
async def text(self, ctx, user: discord.Member = None, server: discord.Server = None):
|
||||
"""Fetch which text channels you have access to."""
|
||||
author = ctx.message.author
|
||||
if server is None:
|
||||
server = ctx.message.server
|
||||
if user is None:
|
||||
user = author
|
||||
|
||||
can_access = [
|
||||
c.name
|
||||
for c in server.channels
|
||||
if c.permissions_for(user).read_messages == True and str(c.type) == "text"
|
||||
]
|
||||
text_channels = [c.name for c in server.channels if str(c.type) == "text"]
|
||||
|
||||
prefix = "You have" if user.id == author.id else user.name + " has"
|
||||
msg = "```ini\n[{} access to {} out of {} text channels]\n\n".format(
|
||||
prefix, len(can_access), len(text_channels)
|
||||
)
|
||||
|
||||
msg += "[ACCESS]:\n{}\n\n".format(", ".join(can_access))
|
||||
msg += "[NO ACCESS]:\n{}\n```".format(
|
||||
", ".join(list(set(text_channels) - set(can_access)))
|
||||
)
|
||||
await self.bot.say(msg)
|
||||
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
@access.command(pass_context=True)
|
||||
async def voice(self, ctx, user: discord.Member = None, server: discord.Server = None):
|
||||
"""Fetch which voice channels you have access to."""
|
||||
author = ctx.message.author
|
||||
if server is None:
|
||||
server = ctx.message.server
|
||||
if user is None:
|
||||
user = author
|
||||
|
||||
can_access = [
|
||||
c.name
|
||||
for c in server.channels
|
||||
if c.permissions_for(user).connect is True and str(c.type) == "voice"
|
||||
]
|
||||
voice_channels = [c.name for c in server.channels if str(c.type) == "voice"]
|
||||
|
||||
prefix = "You have" if user.id == author.id else user.name + " has"
|
||||
msg = "```ini\n[{} access to {} out of {} voice channels]\n\n".format(
|
||||
prefix, len(can_access), len(voice_channels)
|
||||
)
|
||||
|
||||
msg += "[ACCESS]:\n{}\n\n".format(", ".join(can_access))
|
||||
msg += "[NO ACCESS]:\n{}\n```".format(
|
||||
", ".join(list(set(voice_channels) - set(can_access)))
|
||||
)
|
||||
await self.bot.say(msg)
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
@checks.admin_or_permissions(manage_server=True)
|
||||
async def banlist(self, ctx):
|
||||
"""Displays the server's banlist."""
|
||||
try:
|
||||
banlist = await self.bot.get_bans(ctx.message.server)
|
||||
except discord.errors.Forbidden:
|
||||
await self.bot.say("I do not have the `Ban Members` permission.")
|
||||
return
|
||||
bancount = len(banlist)
|
||||
if bancount == 0:
|
||||
banlist = "No users are banned from this server."
|
||||
else:
|
||||
banlist = ", ".join(map(str, banlist))
|
||||
|
||||
for page in pagify(banlist, ["\n"], shorten_by=13, page_length=2000):
|
||||
await self.bot.say(box(page, "ini"))
|
||||
|
||||
@commands.command(pass_context=True, no_pm=True)
|
||||
async def cid(self, ctx, channel: discord.Channel = None):
|
||||
"""Shows the channel ID."""
|
||||
if not channel:
|
||||
channel = ctx.message.channel
|
||||
await self.bot.say("**#{0.name} ID:** {0.id}".format(channel))
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
async def cinfo(self, ctx, channel: discord.Channel = None):
|
||||
"""Shows channel information. Defaults to current text channel."""
|
||||
yesno = {True: "Yes", False: "No"}
|
||||
if not channel:
|
||||
channel = ctx.message.channel
|
||||
|
||||
load = "```\nLoading channel info...```"
|
||||
waiting = await self.bot.say(load)
|
||||
|
||||
try:
|
||||
caller = inspect.currentframe().f_back.f_code.co_name
|
||||
except:
|
||||
pass
|
||||
|
||||
data = "```ini\n"
|
||||
if caller == "whatis":
|
||||
data == "[Server]: {}\n".format(channel.server.name)
|
||||
data += "[Name]: {}\n".format(escape_mass_mentions(str(channel)))
|
||||
data += "[ID]: {}\n".format(channel.id)
|
||||
data += "[Default]: {}\n".format(yesno[channel.is_default])
|
||||
data += "[Private]: {}\n".format(yesno[channel.is_private])
|
||||
if str(channel.type) == "text" and channel.topic != "":
|
||||
data += "[Topic]: {}\n".format(channel.topic)
|
||||
data += "[Position]: {}\n".format(channel.position)
|
||||
data += "[Created]: {} ago\n".format(self._dynamic_time(channel.created_at))
|
||||
data += "[Type]: {}\n".format(channel.type)
|
||||
if str(channel.type) == "voice":
|
||||
data += "[Users]: {}\n".format(len(channel.voice_members))
|
||||
data += "[User limit]: {}\n".format(channel.user_limit)
|
||||
data += "[Bitrate]: {}\n".format(channel.bitrate)
|
||||
data += "```"
|
||||
await asyncio.sleep(2)
|
||||
await self.bot.edit_message(waiting, data)
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
@checks.is_owner()
|
||||
async def ecogs(self, ctx):
|
||||
"""Lists status of installed cogs"""
|
||||
owner_cog = self.bot.get_cog("Owner")
|
||||
total_cogs = owner_cog._list_cogs()
|
||||
loaded = [c.__module__.split(".")[1] for c in self.bot.cogs.values()]
|
||||
unloaded = [c.split(".")[1] for c in total_cogs if c.split(".")[1] not in loaded]
|
||||
if not unloaded:
|
||||
unloaded = ["None"]
|
||||
|
||||
items = {
|
||||
0: {
|
||||
"cogs": sorted(loaded),
|
||||
"msg": "**{} loaded:**\n".format(len(loaded)),
|
||||
"colour": discord.Colour.dark_green(),
|
||||
},
|
||||
1: {
|
||||
"cogs": sorted(unloaded),
|
||||
"msg": "**{} unloaded:**\n".format(len(unloaded)),
|
||||
"colour": discord.Colour.dark_red(),
|
||||
},
|
||||
}
|
||||
for index, em in enumerate(items):
|
||||
e = discord.Embed(
|
||||
description=items[index]["msg"] + ", ".join(items[index]["cogs"]),
|
||||
colour=items[index]["colour"],
|
||||
)
|
||||
await self.bot.say(embed=e)
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
async def eid(self, ctx, emoji):
|
||||
"""Get an id for a custom emoji."""
|
||||
if emoji[0] != "<":
|
||||
await self.bot.say(
|
||||
"I could not an ID for this emoji, this may be because it is not a custom emoji."
|
||||
)
|
||||
return
|
||||
id = emoji.split(":")[2][:-1]
|
||||
await self.bot.say(id)
|
||||
|
||||
@checks.is_owner()
|
||||
@commands.command(aliases=["find", "cmd_lookup"])
|
||||
async def findcog(self, command: str):
|
||||
"""Cog search by command.
|
||||
This is only applicable for loaded cogs that were installed through [p]cog install."""
|
||||
try:
|
||||
cog_name = self.bot.get_cog(self.bot.get_command(command).cog_name).__module__[5:]
|
||||
except:
|
||||
await self.bot.say(
|
||||
"Either that command doesn't exist, or the cog this command belongs to wasn't added through the downloader cog."
|
||||
)
|
||||
return
|
||||
repos = dataIO.load_json("data/downloader/repos.json")
|
||||
cog_path = (
|
||||
lambda x: "\n".join(
|
||||
[
|
||||
filename
|
||||
for filename in glob.iglob("data/downloader/**/*.py", recursive=True)
|
||||
if "{}.py".format(x) == filename[((len("{}.py".format(x))) * -1) :]
|
||||
]
|
||||
)
|
||||
)(cog_name)
|
||||
if not cog_path:
|
||||
await self.bot.say("This is a command that's in a cog that's not published in a repo.")
|
||||
return
|
||||
if os.name == "nt":
|
||||
repo = cog_path.split(os.sep)[1]
|
||||
else:
|
||||
repo = cog_path.split(os.sep)[2]
|
||||
if "url" not in repos[repo]:
|
||||
with open("data/downloader/" + repo + "/.git/config", "r") as f:
|
||||
url = re.findall(r"(http(s)?:\/\/[a-zA-Z0-9\:\.\-\_\/\?\=\%]*)", f.read())[0][0]
|
||||
else:
|
||||
url = repos[repo]["url"]
|
||||
await self.bot.say(
|
||||
box(
|
||||
"Command name: {}\nMade by: {}\nRepo: {}\nCog Name: {}.py".format(
|
||||
command, url.split("/")[3], url, cog_name
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@checks.admin_or_permissions(manage_roles=True)
|
||||
@commands.command(pass_context=True)
|
||||
async def inrole(self, ctx, *, rolename):
|
||||
"""Check members in the role specified."""
|
||||
await self.bot.send_typing(ctx.message.channel)
|
||||
role = discord.utils.find(
|
||||
lambda r: r.name.lower() == rolename.lower(), ctx.message.server.roles
|
||||
)
|
||||
|
||||
if role is None:
|
||||
roles = []
|
||||
for r in ctx.message.server.roles:
|
||||
if rolename.lower() in r.name.lower():
|
||||
roles.append(r)
|
||||
|
||||
if len(roles) == 1:
|
||||
role = roles[0]
|
||||
elif len(roles) < 1:
|
||||
await self.bot.say("no roles found")
|
||||
return
|
||||
else:
|
||||
msg = "**Roles found with** {} **in the name.**\n\n".format(rolename)
|
||||
tbul8 = []
|
||||
for num, role in enumerate(roles):
|
||||
tbul8.append([num + 1, role.name])
|
||||
m1 = await self.bot.say(msg + tabulate(tbul8, tablefmt="plain"))
|
||||
response = await self.bot.wait_for_message(
|
||||
author=ctx.message.author, channel=ctx.message.channel, timeout=25
|
||||
)
|
||||
if response is None:
|
||||
await self.bot.delete_message(m1)
|
||||
return
|
||||
elif response.content.isdigit():
|
||||
await self.bot.delete_message(m1)
|
||||
return
|
||||
else:
|
||||
response = int(response.content)
|
||||
|
||||
if response not in range(0, len(roles) + 1):
|
||||
await self.bot.delete_message(m1)
|
||||
return
|
||||
elif response == 0:
|
||||
await self.bot.delete_message(m1)
|
||||
return
|
||||
else:
|
||||
role = roles[response - 1]
|
||||
|
||||
if (
|
||||
role is not None
|
||||
and len([m for m in ctx.message.server.members if role in m.roles]) < 50
|
||||
):
|
||||
awaiter = await self.bot.say(
|
||||
embed=discord.Embed(description="Getting member names...")
|
||||
)
|
||||
await asyncio.sleep(2.5)
|
||||
role_member = discord.Embed(
|
||||
description="**{1} users found in the {0} role.**\n".format(
|
||||
role.name, len([m for m in ctx.message.server.members if role in m.roles])
|
||||
)
|
||||
)
|
||||
role_users = [m.display_name for m in ctx.message.server.members if role in m.roles]
|
||||
if not role_users:
|
||||
role_member.add_field(name="Users", value="None.")
|
||||
else:
|
||||
role_member.add_field(name="Users", value="\n".join(role_users))
|
||||
await self.bot.edit_message(awaiter, embed=role_member)
|
||||
|
||||
elif len([m for m in ctx.message.server.members if role in m.roles]) > 50:
|
||||
awaiter = await self.bot.say(
|
||||
embed=discord.Embed(description="Getting member names...")
|
||||
)
|
||||
await asyncio.sleep(2.5)
|
||||
await self.bot.edit_message(
|
||||
awaiter,
|
||||
embed=discord.Embed(
|
||||
description="List is too long for **{0}** role, **{1}** members found.\n".format(
|
||||
role.name, len([m.mention for m in server.members if role in m.roles])
|
||||
)
|
||||
),
|
||||
)
|
||||
else:
|
||||
embed = discord.Embed(description="Role was not found.")
|
||||
await self.bot.edit_message(embed=embed)
|
||||
|
||||
@commands.command(pass_context=True, no_pm=True)
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
async def newusers(self, ctx, count: int = 5, server: discord.Server = None):
|
||||
"""Lists the newest 5 members."""
|
||||
if server is None:
|
||||
server = ctx.message.server
|
||||
count = max(min(count, 25), 5)
|
||||
members = sorted(server.members, key=lambda m: m.joined_at, reverse=True)[:count]
|
||||
e = discord.Embed(title="New Members")
|
||||
for member in members:
|
||||
msg = "**Joined Server:** {} ago\n**Account created:** {} ago".format(
|
||||
self._dynamic_time(member.joined_at), self._dynamic_time(member.created_at)
|
||||
)
|
||||
e.add_field(
|
||||
name="{0.display_name} (ID: {0.id})".format(member), value=msg, inline=False
|
||||
)
|
||||
await self.bot.say(embed=e)
|
||||
|
||||
@commands.command(pass_context=True, no_pm=True)
|
||||
async def sid(self, ctx):
|
||||
"""Shows the server ID."""
|
||||
await self.bot.say("**{0.name} ID:** {0.id}".format(ctx.message.server))
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
async def userstats(self, ctx, this_server: bool = False):
|
||||
"""A small amount of user stats."""
|
||||
embeds = {}
|
||||
if this_server:
|
||||
members = set([x for x in ctx.message.server.members])
|
||||
else:
|
||||
members = set([x for x in self.bot.get_all_members()])
|
||||
|
||||
items = {
|
||||
2: {
|
||||
"users": len([e.name for e in members if e.status == discord.Status.idle]),
|
||||
"colour": discord.Colour.orange(),
|
||||
},
|
||||
3: {
|
||||
"users": len([e.name for e in members if e.status == discord.Status.dnd]),
|
||||
"colour": discord.Colour.red(),
|
||||
},
|
||||
4: {
|
||||
"users": len([e.name for e in members if e.status == discord.Status.offline]),
|
||||
"colour": discord.Colour.dark_grey(),
|
||||
},
|
||||
1: {
|
||||
"users": len([e.name for e in members if e.status == discord.Status.online]),
|
||||
"colour": discord.Colour.green(),
|
||||
},
|
||||
0: {
|
||||
"users": len([e.name for e in members if e.game and e.game.url]),
|
||||
"colour": discord.Colour.dark_purple(),
|
||||
},
|
||||
}
|
||||
|
||||
for item in items:
|
||||
embeds[item] = discord.Embed(
|
||||
description="Users: {}".format(items[item]["users"]), colour=items[item]["colour"]
|
||||
)
|
||||
for i, em in enumerate(embeds):
|
||||
await self.bot.say(embed=embeds[i])
|
||||
|
||||
@commands.command(pass_context=True, no_pm=True)
|
||||
async def sinfo(self, ctx, server: discord.Server = None):
|
||||
"""Shows server information."""
|
||||
if server is None:
|
||||
server = ctx.message.server
|
||||
online = str(
|
||||
len(
|
||||
[
|
||||
m.status
|
||||
for m in server.members
|
||||
if str(m.status) == "online" or str(m.status) == "idle"
|
||||
]
|
||||
)
|
||||
)
|
||||
total_users = str(len(server.members))
|
||||
text_channels = [x for x in server.channels if str(x.type) == "text"]
|
||||
voice_channels = [x for x in server.channels if str(x.type) == "voice"]
|
||||
|
||||
load = "```\nLoading server info...```"
|
||||
waiting = await self.bot.say(load)
|
||||
|
||||
data = "```ini\n"
|
||||
data += "[Name]: {}\n".format(server.name)
|
||||
data += "[ID]: {}\n".format(server.id)
|
||||
data += "[Region]: {}\n".format(server.region)
|
||||
data += "[Owner]: {}\n".format(server.owner)
|
||||
data += "[Users]: {}/{}\n".format(online, total_users)
|
||||
data += "[Text]: {} channels\n".format(len(text_channels))
|
||||
data += "[Voice]: {} channels\n".format(len(voice_channels))
|
||||
data += "[Emojis]: {}\n".format(len(server.emojis))
|
||||
data += "[Roles]: {} \n".format(len(server.roles))
|
||||
data += "[Created]: {} ago\n```".format(self._dynamic_time(server.created_at))
|
||||
await asyncio.sleep(3)
|
||||
await self.bot.edit_message(waiting, data)
|
||||
|
||||
@commands.command(pass_context=True, no_pm=True)
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
async def perms(self, ctx, user: discord.Member = None):
|
||||
"""Fetch a specific user's permissions."""
|
||||
if user is None:
|
||||
user = ctx.message.author
|
||||
|
||||
perms = iter(ctx.message.channel.permissions_for(user))
|
||||
perms_we_have = "```diff\n"
|
||||
perms_we_dont = ""
|
||||
for x in perms:
|
||||
if "True" in str(x):
|
||||
perms_we_have += "+\t{0}\n".format(str(x).split("'")[1])
|
||||
else:
|
||||
perms_we_dont += "-\t{0}\n".format(str(x).split("'")[1])
|
||||
await self.bot.say("{0}{1}```".format(perms_we_have, perms_we_dont))
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
async def rid(self, ctx, rolename):
|
||||
"""Shows the id of a role, use quotes on the role."""
|
||||
await self.bot.send_typing(ctx.message.channel)
|
||||
if rolename is discord.Role:
|
||||
role = rolename
|
||||
else:
|
||||
role = self._role_from_string(ctx.message.server, rolename)
|
||||
if role is None:
|
||||
return await self.bot.say(embed=discord.Embed(description="Cannot find role."))
|
||||
await self.bot.say(
|
||||
embed=discord.Embed(description="**{}** ID: {}".format(rolename, role.id))
|
||||
)
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
async def rinfo(self, ctx, rolename):
|
||||
"""Shows role info, use quotes on the role."""
|
||||
server = ctx.message.server
|
||||
colour = str(random.randint(0, 0xFFFFFF))
|
||||
colour = int(colour, 16)
|
||||
await self.bot.send_typing(ctx.message.channel)
|
||||
|
||||
try:
|
||||
caller = inspect.currentframe().f_back.f_code.co_name
|
||||
except:
|
||||
pass
|
||||
|
||||
if type(rolename) is not discord.Role:
|
||||
role = discord.utils.find(
|
||||
lambda r: r.name.lower() == rolename.lower(), ctx.message.server.roles
|
||||
)
|
||||
else:
|
||||
role = rolename
|
||||
if role is None:
|
||||
await self.bot.say("That role cannot be found.")
|
||||
return
|
||||
if role is not None:
|
||||
perms = iter(role.permissions)
|
||||
perms_we_have = ""
|
||||
perms_we_dont = ""
|
||||
for x in perms:
|
||||
if "True" in str(x):
|
||||
perms_we_have += "{0}\n".format(str(x).split("'")[1])
|
||||
else:
|
||||
perms_we_dont += "{0}\n".format(str(x).split("'")[1])
|
||||
msg = discord.Embed(description="Gathering role stats...", colour=role.color)
|
||||
if role.color is None:
|
||||
role.color = discord.Colour(value=colour)
|
||||
msg2 = await self.bot.say(embed=msg)
|
||||
em = discord.Embed(colour=role.colour)
|
||||
if caller == "whatis":
|
||||
em.add_field(name="Server", value=role.server.name)
|
||||
em.add_field(name="Role Name", value=role.name)
|
||||
em.add_field(name="Created", value=self._dynamic_time(role.created_at))
|
||||
em.add_field(
|
||||
name="Users in Role",
|
||||
value=len([m for m in ctx.message.server.members if role in m.roles]),
|
||||
)
|
||||
em.add_field(name="Id", value=role.id)
|
||||
em.add_field(name="Color", value=role.color)
|
||||
em.add_field(name="Position", value=role.position)
|
||||
em.add_field(name="Valid Permissons", value="{}".format(perms_we_have))
|
||||
em.add_field(name="Invalid Permissons", value="{}".format(perms_we_dont))
|
||||
em.set_thumbnail(url=role.server.icon_url)
|
||||
try:
|
||||
await self.bot.edit_message(msg2, embed=em)
|
||||
except discord.HTTPException:
|
||||
perms_msg = "```diff\n"
|
||||
role = discord.utils.find(
|
||||
lambda r: r.name.lower() == rolename.lower(), ctx.message.server.roles
|
||||
)
|
||||
if role is None:
|
||||
await bot.say("That role cannot be found.")
|
||||
return
|
||||
if role is not None:
|
||||
perms = iter(role.permissions)
|
||||
perms_we_have2 = ""
|
||||
perms_we_dont2 = ""
|
||||
for x in perms:
|
||||
if "True" in str(x):
|
||||
perms_we_have2 += "+{0}\n".format(str(x).split("'")[1])
|
||||
else:
|
||||
perms_we_dont2 += "-{0}\n".format(str(x).split("'")[1])
|
||||
await self.bot.say(
|
||||
"{}Name: {}\nCreated: {}\nUsers in Role : {}\nId : {}\nColor : {}\nPosition : {}\nValid Perms : \n{}\nInvalid Perms : \n{}```".format(
|
||||
perms_msg,
|
||||
role.name,
|
||||
self._dynamic_time(role.created_at),
|
||||
len([m for m in server.members if role in m.roles]),
|
||||
role.id,
|
||||
role.color,
|
||||
role.position,
|
||||
perms_we_have2,
|
||||
perms_we_dont2,
|
||||
)
|
||||
)
|
||||
await self.bot.delete_message(msg2)
|
||||
|
||||
@commands.command(pass_context=True, hidden=True)
|
||||
@checks.mod_or_permissions(manage_messages=True)
|
||||
async def sharedservers(self, ctx, user: discord.Member = None):
|
||||
"""Shows shared server info. Defaults to author."""
|
||||
author = ctx.message.author
|
||||
server = ctx.message.server
|
||||
if not user:
|
||||
user = author
|
||||
seen = len(
|
||||
set(
|
||||
[
|
||||
member.server.name
|
||||
for member in self.bot.get_all_members()
|
||||
if member.name == user.name
|
||||
]
|
||||
)
|
||||
)
|
||||
sharedservers = str(
|
||||
set(
|
||||
[
|
||||
member.server.name
|
||||
for member in self.bot.get_all_members()
|
||||
if member.name == user.name
|
||||
]
|
||||
)
|
||||
)
|
||||
for shared in sharedservers:
|
||||
shared = "".strip("'").join(sharedservers).strip("'")
|
||||
shared = shared.strip("{").strip("}")
|
||||
|
||||
data = "[Servers]: {} shared\n".format(seen)
|
||||
data += "[In Servers]: {}\n".format(shared)
|
||||
|
||||
for page in pagify(data, ["\n"], shorten_by=13, page_length=2000):
|
||||
await self.bot.say(box(page, "ini"))
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
async def uinfo(self, ctx, user: discord.Member = None):
|
||||
"""Shows user information. Defaults to author."""
|
||||
if not user:
|
||||
user = ctx.message.author
|
||||
try:
|
||||
caller = inspect.currentframe().f_back.f_code.co_name
|
||||
except:
|
||||
pass
|
||||
roles = [x.name for x in user.roles if x.name != "@everyone"]
|
||||
if not roles:
|
||||
roles = ["None"]
|
||||
seen = str(
|
||||
len(
|
||||
set(
|
||||
[
|
||||
member.server.name
|
||||
for member in self.bot.get_all_members()
|
||||
if member.id == user.id
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
load = "```\nLoading user info...```"
|
||||
waiting = await self.bot.say(load)
|
||||
|
||||
data = "```ini\n"
|
||||
data += "[Name]: {}\n".format(escape_mass_mentions(str(user)))
|
||||
data += "[Nickname]: {}\n".format(escape_mass_mentions(str(user.nick)))
|
||||
data += "[ID]: {}\n".format(user.id)
|
||||
data += "[Status]: {}\n".format(user.status)
|
||||
data += "[Servers]: {} shared\n".format(seen)
|
||||
if user.game is None:
|
||||
pass
|
||||
elif user.game.url is None:
|
||||
data += "[Playing]: {}\n".format(escape_mass_mentions(str(user.game)))
|
||||
else:
|
||||
data += "[Streaming]: [{}]({})\n".format(
|
||||
escape_mass_mentions(str(user.game)), escape_mass_mentions(user.game.url)
|
||||
)
|
||||
passed = (ctx.message.timestamp - user.created_at).days
|
||||
data += "[Created]: {} ago\n".format(self._dynamic_time(user.created_at))
|
||||
joined_at = self.fetch_joined_at(user, ctx.message.server)
|
||||
if caller != "whatis":
|
||||
data += "[Joined]: {} ago\n".format(self._dynamic_time(joined_at))
|
||||
data += "[Roles]: {}\n".format(", ".join(roles))
|
||||
data += "[In Voice]: {}\n".format(str(user.voice_channel))
|
||||
data += "[AFK]: {}\n".format(user.is_afk)
|
||||
data += "```"
|
||||
await asyncio.sleep(3)
|
||||
await self.bot.edit_message(waiting, data)
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
async def whatis(self, ctx, id):
|
||||
"""What is it?"""
|
||||
server = ctx.message.server
|
||||
channel = ctx.message.channel
|
||||
author = ctx.message.author
|
||||
|
||||
it_is = False
|
||||
msg = False
|
||||
|
||||
if server.id == id:
|
||||
it_is = server
|
||||
elif channel.id == id:
|
||||
it_is = channel
|
||||
elif author.id == id:
|
||||
it_is = author
|
||||
|
||||
if not it_is:
|
||||
for server in self.bot.servers:
|
||||
if server.id == id:
|
||||
it_is = server
|
||||
break
|
||||
if not it_is:
|
||||
for emoji in self.bot.get_all_emojis():
|
||||
if emoji.id == id:
|
||||
it_is = emoji
|
||||
break
|
||||
if not it_is:
|
||||
for server in self.bot.servers:
|
||||
for role in server.roles:
|
||||
if role.id == id:
|
||||
it_is = role
|
||||
break
|
||||
if not it_is:
|
||||
for member in self.bot.get_all_members():
|
||||
if member.id == id:
|
||||
it_is = member
|
||||
break
|
||||
if not it_is:
|
||||
for channel in self.bot.get_all_channels():
|
||||
if channel.id == id:
|
||||
it_is = channel
|
||||
break
|
||||
|
||||
if not msg:
|
||||
if type(it_is) == discord.Channel:
|
||||
await ctx.invoke(self.cinfo, it_is)
|
||||
elif type(it_is) == discord.Server:
|
||||
await ctx.invoke(self.sinfo, it_is)
|
||||
elif type(it_is) == discord.User or type(it_is) == discord.Member:
|
||||
await ctx.invoke(self.uinfo, it_is)
|
||||
elif type(it_is) == discord.Role:
|
||||
await ctx.invoke(self.roleinfo, it_is)
|
||||
elif type(it_is) == discord.Emoji:
|
||||
await self.bot.say(
|
||||
"<:{0.name}:{0.id}>\n```ini\n[NAME]: {0.name}\n[SERVER]: {0.server}\n[URL]: {0.url}```".format(
|
||||
it_is
|
||||
)
|
||||
)
|
||||
else:
|
||||
await self.bot.say(
|
||||
"I could not find anything for this ID, I do not support Message IDs"
|
||||
)
|
||||
else:
|
||||
await self.bot.say("```\nNothing found for this ID```")
|
||||
|
||||
@staticmethod
|
||||
def _dynamic_time(time):
|
||||
date_join = datetime.datetime.strptime(str(time), "%Y-%m-%d %H:%M:%S.%f")
|
||||
date_now = datetime.datetime.now(datetime.timezone.utc)
|
||||
date_now = date_now.replace(tzinfo=None)
|
||||
since_join = date_now - date_join
|
||||
|
||||
m, s = divmod(int(since_join.total_seconds()), 60)
|
||||
h, m = divmod(m, 60)
|
||||
d, h = divmod(h, 24)
|
||||
|
||||
if d > 0:
|
||||
msg = "{0}d {1}h"
|
||||
elif d == 0 and h > 0:
|
||||
msg = "{1}h {2}m"
|
||||
elif d == 0 and h == 0 and m > 0:
|
||||
msg = "{2}m {3}s"
|
||||
elif d == 0 and h == 0 and m == 0 and s > 0:
|
||||
msg = "{3}s"
|
||||
else:
|
||||
msg = ""
|
||||
return msg.format(d, h, m, s)
|
||||
|
||||
def fetch_joined_at(self, user, server):
|
||||
return user.joined_at
|
||||
|
||||
def _role_from_string(self, server, rolename, roles=None):
|
||||
if roles is None:
|
||||
roles = server.roles
|
||||
role = discord.utils.find(lambda r: r.name.lower() == rolename.lower(), roles)
|
||||
return role
|
||||
|
||||
|
||||
def setup(bot):
|
||||
cmds = [
|
||||
"access",
|
||||
"banlist",
|
||||
"cid",
|
||||
"cinfo",
|
||||
"ecogs",
|
||||
"eid",
|
||||
"findcog",
|
||||
"inrole",
|
||||
"newusers",
|
||||
"perms",
|
||||
"rid",
|
||||
"rinfo",
|
||||
"sid",
|
||||
"sinfo",
|
||||
"uinfo",
|
||||
"userstatst",
|
||||
"whatis",
|
||||
]
|
||||
for cmd in cmds:
|
||||
bot.remove_command(cmd)
|
||||
bot.add_cog(Tools(bot))
|
||||
7
wolfram/info.json
Normal file
7
wolfram/info.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"AUTHOR" : "Paddolicious#8880",
|
||||
"NAME" : "wolfram",
|
||||
"SHORT" : "Query Wolfram|Alpha.",
|
||||
"DESCRIPTION" : "A cog to query Wolfram|Alpha.",
|
||||
"TAGS": ["wolfram"]
|
||||
}
|
||||
82
wolfram/wolfram.py
Normal file
82
wolfram/wolfram.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import os
|
||||
import aiohttp
|
||||
from discord.ext import commands
|
||||
import xml.etree.ElementTree as ET
|
||||
from cogs.utils.dataIO import dataIO
|
||||
from .utils import checks
|
||||
from .utils.chat_formatting import escape_mass_mentions
|
||||
from .utils.chat_formatting import box
|
||||
from __main__ import send_cmd_help
|
||||
|
||||
|
||||
class Wolfram:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.settings = dataIO.load_json("data/wolfram/settings.json")
|
||||
|
||||
@commands.command(pass_context=True, name="wolfram", aliases=["ask"])
|
||||
async def _wolfram(self, ctx, *arguments: str):
|
||||
"""
|
||||
Ask Wolfram Alpha any question
|
||||
"""
|
||||
api_key = self.settings["WOLFRAM_API_KEY"]
|
||||
if api_key:
|
||||
url = "http://api.wolframalpha.com/v2/query?"
|
||||
query = " ".join(arguments)
|
||||
payload = {"input": query, "appid": api_key}
|
||||
headers = {"user-agent": "Red-cog/1.0.0"}
|
||||
conn = aiohttp.TCPConnector(verify_ssl=False)
|
||||
session = aiohttp.ClientSession(connector=conn)
|
||||
async with session.get(url, params=payload, headers=headers) as r:
|
||||
result = await r.text()
|
||||
session.close()
|
||||
root = ET.fromstring(result)
|
||||
a = []
|
||||
for pt in root.findall(".//plaintext"):
|
||||
if pt.text:
|
||||
a.append(pt.text.capitalize())
|
||||
if len(a) < 1:
|
||||
message = "There is as yet insufficient data for a meaningful answer."
|
||||
else:
|
||||
message = "\n".join(a[0:3])
|
||||
else:
|
||||
message = (
|
||||
"No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/"
|
||||
)
|
||||
message = escape_mass_mentions(message)
|
||||
await self.bot.say(box(message))
|
||||
|
||||
@commands.command(pass_context=True, name="setwolframapi", aliases=["setwolfram"])
|
||||
@checks.is_owner()
|
||||
async def _setwolframapi(self, ctx, key: str):
|
||||
"""
|
||||
Set the api-key
|
||||
"""
|
||||
if key:
|
||||
self.settings["WOLFRAM_API_KEY"] = key
|
||||
dataIO.save_json("data/wolfram/settings.json", self.settings)
|
||||
await self.bot.say("Key set.")
|
||||
else:
|
||||
await send_cmd_help(ctx)
|
||||
|
||||
|
||||
def check_folder():
|
||||
if not os.path.exists("data/wolfram"):
|
||||
print("Creating data/wolfram folder...")
|
||||
os.makedirs("data/wolfram")
|
||||
|
||||
|
||||
def check_file():
|
||||
data = {}
|
||||
data["WOLFRAM_API_KEY"] = False
|
||||
f = "data/wolfram/settings.json"
|
||||
if not dataIO.is_valid_json(f):
|
||||
print("Creating default settings.json...")
|
||||
dataIO.save_json(f, data)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
check_folder()
|
||||
check_file()
|
||||
n = Wolfram(bot)
|
||||
bot.add_cog(n)
|
||||
7
youtube/info.json
Normal file
7
youtube/info.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"AUTHOR" : "Paddolicious#8880 and aikaterna#1393",
|
||||
"NAME" : "youtube",
|
||||
"SHORT" : "Look up videos on YouTube.",
|
||||
"DESCRIPTION" : "Look up videos on YouTube.",
|
||||
"TAGS": ["youtube"]
|
||||
}
|
||||
41
youtube/youtube.py
Normal file
41
youtube/youtube.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from discord.ext import commands
|
||||
import aiohttp
|
||||
import re
|
||||
|
||||
|
||||
class YouTube:
|
||||
"""Le YouTube Cog"""
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.session = aiohttp.ClientSession()
|
||||
|
||||
async def _youtube_results(self, query: str):
|
||||
try:
|
||||
headers = {"user-agent": "Red-cog/2.0"}
|
||||
async with self.session.get("https://www.youtube.com/results", params={"search_query": query}, headers=headers) as r:
|
||||
result = await r.text()
|
||||
yt_find = re.findall(r"{\"videoId\":\"(.{11})", result)
|
||||
url_list = []
|
||||
for track in yt_find:
|
||||
url = "https://www.youtube.com/watch?v={}".format(track)
|
||||
if url not in url_list:
|
||||
url_list.append(url)
|
||||
|
||||
except Exception as e:
|
||||
url_list = ["Something went terribly wrong! [{}]".format(e)]
|
||||
|
||||
return url_list
|
||||
|
||||
@commands.command(pass_context=True)
|
||||
async def youtube(self, ctx, *, query: str):
|
||||
"""Search on Youtube."""
|
||||
result = await self._youtube_results(query)
|
||||
if result:
|
||||
await self.bot.say(result[0])
|
||||
else:
|
||||
await self.bot.say("Nothing found. Try again later.")
|
||||
|
||||
|
||||
def setup(bot):
|
||||
n = YouTube(bot)
|
||||
bot.add_cog(n)
|
||||
Reference in New Issue
Block a user