add black code formatter gitaction, update all cogs to follow format

This commit is contained in:
brandons209 2020-01-27 15:04:50 -05:00
parent 7e4b0c18b8
commit f5891ffb90
15 changed files with 632 additions and 527 deletions

13
.github/workflows/black-checker.yml vendored Normal file
View file

@ -0,0 +1,13 @@
name: black-checker
on: [pull_request, push]
jobs:
black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Black Code Formatter
uses: lgeiger/black-action@v1.0.1
with:
args: "--line-length 120 --target-version py38 --check ."

View file

@ -1,5 +1,6 @@
from .activitylog import ActivityLogger
async def setup(bot):
n = ActivityLogger(bot)
await n.initialize()

View file

@ -12,13 +12,9 @@ import os
import asyncio
import glob
__version__ = '3.0.0'
__version__ = "3.0.0"
TIMESTAMP_FORMAT = '%Y-%m-%d %X' # YYYY-MM-DD HH:MM:SS
#PATH_LIST = ['data', 'activitylogger']
#JSON = os.path.join(*PATH_LIST, "settings.json")
#NAMES_JSON = os.path.join(*PATH_LIST, "past_names.json")
EDIT_TIMEDELTA = timedelta(seconds=3)
TIMESTAMP_FORMAT = "%Y-%m-%d %X" # YYYY-MM-DD HH:MM:SS
# 0 is Message object
AUTHOR_TEMPLATE = "@{0.author.name}#{0.author.discriminator}(id:{0.author.id})"
@ -42,6 +38,7 @@ DELETE_AUDIT_TEMPLATE = "@{0.name}#{0.discriminator}(id:{0.id}) deleted message
MAX_LINES = 50000
def get_all_names(guild_files, user):
names = [str(user)]
for log in guild_files:
@ -68,19 +65,21 @@ def get_all_names(guild_files, user):
names.append(username)
return set(names)
def format_list(*items, join='and', delim=', '):
def format_list(*items, join="and", delim=", "):
if len(items) > 1:
return (' %s ' % join).join((delim.join(items[:-1]), items[-1]))
return (" %s " % join).join((delim.join(items[:-1]), items[-1]))
elif items:
return items[0]
else:
return ''
return ""
class LogHandle:
"""basic wrapper for logfile handles, used to keep track of stale handles"""
def __init__(self, path, time=None, mode='a', buf=1):
self.handle = open(path, mode, buf, errors='backslashreplace')
def __init__(self, path, time=None, mode="a", buf=1):
self.handle = open(path, mode, buf, errors="backslashreplace")
self.lock = asyncio.Lock()
if time:
@ -111,34 +110,13 @@ class ActivityLogger(commands.Cog):
self.bot = bot
self.config = Config.get_conf(self, identifier=9584736583, force_registration=True)
default_global = {
"attrs": {
"attachments" : False,
"default" : False,
"direct" : False,
"everything" : False,
"rotation" : "m"
}
}
self.default_guild = {
"all_s": False,
"voice": False,
"events": False,
"prefixes": []
}
self.default_channel = {
"enabled": False
}
default_user = {
"past_names": []
"attrs": {"attachments": False, "default": False, "direct": False, "everything": False, "rotation": "m"}
}
self.default_guild = {"all_s": False, "voice": False, "events": False, "prefixes": []}
self.default_channel = {"enabled": False}
default_user = {"past_names": []}
default_member = {
"stats": {
"total_msg": 0,
"bot_cmd": 0,
"avg_len": 0.0,
"vc_time_sec": 0.0,
"last_vc_time": None
}
"stats": {"total_msg": 0, "bot_cmd": 0, "avg_len": 0.0, "vc_time_sec": 0.0, "last_vc_time": None}
}
self.config.register_global(**default_global)
self.config.register_guild(**self.default_guild)
@ -203,9 +181,7 @@ class ActivityLogger(commands.Cog):
since_joined = "?"
user_joined = "Unknown"
user_created = user.created_at.strftime("%b %d, %Y %H:%M UTC")
member_number = (sorted(guild.members,
key=lambda m: m.joined_at or ctx.message.created_at)
.index(user) + 1)
member_number = sorted(guild.members, key=lambda m: m.joined_at or ctx.message.created_at).index(user) + 1
created_on = "{}\n({} days ago)".format(user_created, since_created)
joined_on = "{}\n({} days ago)".format(user_joined, since_joined)
@ -243,8 +219,7 @@ class ActivityLogger(commands.Cog):
names = pagify(names, page_length=1000)
for name in names:
data.add_field(name="Also known as:", value=name, inline=False)
data.set_footer(text="Member #{} | User ID:{}"
"".format(member_number, user.id))
data.set_footer(text="Member #{} | User ID:{}" "".format(member_number, user.id))
name = str(user)
name = " ~ ".join((name, user.nick)) if user.nick else name
@ -274,7 +249,7 @@ class ActivityLogger(commands.Cog):
stats = await self.config.member(user).stats()
async with self.config.user(user).past_names() as past_names:
if not past_names:
guild_files = sorted(glob.glob(os.path.join(PATH, 'usernames', "*.log")))
guild_files = sorted(glob.glob(os.path.join(PATH, "usernames", "*.log")))
names = get_all_names(guild_files, user)
else:
names = past_names
@ -306,7 +281,9 @@ class ActivityLogger(commands.Cog):
msg += "Average message length: `{:.2f}` words\n".format(avg_len / (num_messages - num_bot_commands))
except ZeroDivisionError:
msg += "Average message length: `{:.2f}` words\n".format(0)
msg += "Time spent in voice chat: `{:.0f}` {}.\n".format(minutes if minutes <= 120 else hours, "minutes" if minutes <= 120 else "hours")
msg += "Time spent in voice chat: `{:.0f}` {}.\n".format(
minutes if minutes <= 120 else hours, "minutes" if minutes <= 120 else "hours"
)
msg += f"Bans: `{bans}`, Kicks: `{kicks}`, Mutes: `{mutes}`"
if len(names) > 1:
return msg, format_list(*names)
@ -356,7 +333,7 @@ class ActivityLogger(commands.Cog):
for msgs in message_chunks:
temp_file = os.path.join(log_path, datetime.utcnow().strftime("%Y%m%d%X").replace(":", "") + ".txt")
with open(temp_file, encoding='utf-8', mode="w") as f:
with open(temp_file, encoding="utf-8", mode="w") as f:
f.writelines(msgs)
await ctx.channel.send(file=discord.File(temp_file))
@ -746,7 +723,7 @@ class ActivityLogger(commands.Cog):
"""
pass
@logset.command(name='everything', aliases=['global'])
@logset.command(name="everything", aliases=["global"])
async def set_everything(self, ctx, on_off: bool = None):
"""
Global override for all logging
@ -762,7 +739,7 @@ class ActivityLogger(commands.Cog):
else:
await ctx.send("Global logging override is disabled.")
@logset.command(name='default')
@logset.command(name="default")
async def set_default(self, ctx, on_off: bool = None):
"""
Sets whether logging is on or off where unset
@ -780,7 +757,7 @@ class ActivityLogger(commands.Cog):
else:
await ctx.send("Logging is disabled by default.")
@logset.command(name='dm')
@logset.command(name="dm")
async def set_direct(self, ctx, on_off: bool = None):
"""
Log direct messages?
@ -797,7 +774,7 @@ class ActivityLogger(commands.Cog):
else:
await ctx.send("Logging of direct messages is disabled.")
@logset.command(name='attachments')
@logset.command(name="attachments")
async def set_attachments(self, ctx, on_off: bool = None):
"""
Download message attachments?
@ -813,7 +790,7 @@ class ActivityLogger(commands.Cog):
else:
await ctx.send("Downloading of attachments is disabled.")
@logset.command(name='channel')
@logset.command(name="channel")
@commands.guild_only()
async def set_channel(self, ctx, on_off: bool, channel: discord.TextChannel = None):
"""
@ -830,11 +807,11 @@ class ActivityLogger(commands.Cog):
await self.config.channel(channel).enabled.set(on_off)
if on_off:
await ctx.send('Logging enabled for %s' % channel.mention)
await ctx.send("Logging enabled for %s" % channel.mention)
else:
await ctx.send('Logging disabled for %s' % channel.mention)
await ctx.send("Logging disabled for %s" % channel.mention)
@logset.command(name='server')
@logset.command(name="server")
@commands.guild_only()
async def set_guild(self, ctx, on_off: bool):
"""
@ -846,11 +823,11 @@ class ActivityLogger(commands.Cog):
await self.config.guild(guild).all_s.set(on_off)
if on_off:
await ctx.send('Logging enabled for %s' % guild)
await ctx.send("Logging enabled for %s" % guild)
else:
await ctx.send('Logging disabled for %s' % guild)
await ctx.send("Logging disabled for %s" % guild)
@logset.command(name='voice')
@logset.command(name="voice")
@commands.guild_only()
async def set_voice(self, ctx, on_off: bool):
"""
@ -862,11 +839,11 @@ class ActivityLogger(commands.Cog):
await self.config.guild(guild).voice.set(on_off)
if on_off:
await ctx.send('Voice event logging enabled for %s' % guild)
await ctx.send("Voice event logging enabled for %s" % guild)
else:
await ctx.send('Voice event logging disabled for %s' % guild)
await ctx.send("Voice event logging disabled for %s" % guild)
@logset.command(name='events')
@logset.command(name="events")
@commands.guild_only()
async def set_events(self, ctx, on_off: bool):
"""
@ -878,9 +855,9 @@ class ActivityLogger(commands.Cog):
await self.config.guild(guild).events.set(on_off)
if on_off:
await ctx.send('Logging enabled for guild events in %s' % guild)
await ctx.send("Logging enabled for guild events in %s" % guild)
else:
await ctx.send('Logging disabled for guild events in %s' % guild)
await ctx.send("Logging disabled for guild events in %s" % guild)
@logset.command(name="prefixes")
@commands.guild_only()
@ -895,16 +872,16 @@ class ActivityLogger(commands.Cog):
await self.config.guild(ctx.guild).prefixes.set([ctx.clean_prefix])
self.cache[ctx.guild.id]["prefixes"] = [ctx.clean_prefix]
return
await ctx.send("Current Prefixes: " + format_list(*curr, delim=', '))
await ctx.send("Current Prefixes: " + format_list(*curr, delim=", "))
return
prefixes = [p for p in prefixes if p != ' ']
prefixes = [p for p in prefixes if p != " "]
await self.config.guild(ctx.guild).prefixes.set(prefixes)
self.cache[ctx.guild.id]["prefixes"] = prefixes
prefixes = [f"`{p}`" for p in prefixes]
await ctx.send("Prefixes set to: " + format_list(*prefixes, delim=', '))
await ctx.send("Prefixes set to: " + format_list(*prefixes, delim=", "))
@logset.command(name='rotation')
@logset.command(name="rotation")
async def set_rotation(self, ctx, freq: str = None):
"""
Show, disable, or set the log rotation period
@ -921,12 +898,12 @@ class ActivityLogger(commands.Cog):
- y: one log file per year (starts 00:00Z Jan 1)
"""
if freq:
freq = freq.lower().strip('"\'` ')
freq = freq.lower().strip("\"'` ")
if freq in ('d', 'w', 'm', 'y', 'none', 'disable'):
adj = 'now'
if freq in ("d", "w", "m", "y", "none", "disable"):
adj = "now"
if freq in ('none', 'disable'):
if freq in ("none", "disable"):
freq = None
async with self.config.attrs() as attrs:
@ -937,53 +914,48 @@ class ActivityLogger(commands.Cog):
await self.bot.send_cmd_help(ctx)
return
else:
adj = 'currently'
adj = "currently"
freq = self.cache["rotation"]
if not freq:
await ctx.send("Log rotation is %s disabled." % adj)
else:
desc = {
'd' : 'daily',
'w' : 'weekly',
'm' : 'monthly',
'y' : 'yearly'
}[freq]
desc = {"d": "daily", "w": "weekly", "m": "monthly", "y": "yearly"}[freq]
await ctx.send('Log rotation period is %s %s.' % (adj, desc))
await ctx.send("Log rotation period is %s %s." % (adj, desc))
@staticmethod
def format_rotation_string(timestamp, rotation_code, filename=None):
kwargs = dict(hour=0, minute=0, second=0, microsecond=0)
if not rotation_code:
return filename or ''
return filename or ""
if rotation_code == 'y':
if rotation_code == "y":
kwargs.update(day=1, month=1)
start = timestamp.replace(**kwargs)
elif rotation_code == 'm':
elif rotation_code == "m":
kwargs.update(day=1)
start = timestamp.replace(**kwargs)
elif rotation_code == 'w':
elif rotation_code == "w":
start = timestamp - timedelta(days=timestamp.weekday())
spec = start.strftime('%Y%m%d')
spec = start.strftime("%Y%m%d")
if rotation_code == 'w':
spec += '--P7D'
if rotation_code == "w":
spec += "--P7D"
else:
spec += '--P1%c' % rotation_code.upper()
spec += "--P1%c" % rotation_code.upper()
if filename:
return '%s_%s' % (spec, filename)
return "%s_%s" % (spec, filename)
else:
return spec
@staticmethod
def get_voice_flags(voice_state):
flags = []
for f in ('deaf', 'mute', 'self_deaf', 'self_mute', 'self_stream', 'self_video'):
for f in ("deaf", "mute", "self_deaf", "self_mute", "self_stream", "self_video"):
if getattr(voice_state, f, None):
flags.append(f)
@ -992,11 +964,13 @@ class ActivityLogger(commands.Cog):
@staticmethod
def format_overwrite(target, channel, before, after, user=None):
if user:
target_str = 'Channel overwrites by @{1.name}#{1.discriminator}(id:{1.id}): {0.name} ({0.id}): '.format(channel, user)
target_str = "Channel overwrites by @{1.name}#{1.discriminator}(id:{1.id}): {0.name} ({0.id}): ".format(
channel, user
)
else:
target_str = 'Channel overwrites: {0.name} ({0.id}): '.format(channel)
target_str += 'role' if isinstance(target, discord.Role) else 'member'
target_str += ' {0.name} ({0.id})'.format(target)
target_str = "Channel overwrites: {0.name} ({0.id}): ".format(channel)
target_str += "role" if isinstance(target, discord.Role) else "member"
target_str += " {0.name} ({0.id})".format(target)
if before:
bpair = [x.value for x in before.pair()]
@ -1005,14 +979,14 @@ class ActivityLogger(commands.Cog):
apair = [x.value for x in after.pair()]
if before and after:
fmt = ' updated to values %i, %i (was %i, %i)'
fmt = " updated to values %i, %i (was %i, %i)"
return target_str + fmt % tuple(apair + bpair)
elif after:
return target_str + ' added with values %i, %i' % tuple(apair)
return target_str + " added with values %i, %i" % tuple(apair)
elif before:
return target_str + ' removed (was %i, %i)' % tuple(bpair)
return target_str + " removed (was %i, %i)" % tuple(bpair)
def gethandle(self, path, mode='a'):
def gethandle(self, path, mode="a"):
"""Manages logfile handles, culling stale ones and creating folders"""
if path in self.handles:
if os.path.exists(path):
@ -1047,34 +1021,34 @@ class ActivityLogger(commands.Cog):
return handle
def should_log(self, location):
if self.cache.get('everything', False):
if self.cache.get("everything", False):
return True
default = self.cache.get('default', False)
default = self.cache.get("default", False)
if type(location) is discord.Guild:
loc = self.cache[location.id]
return loc.get('all_s', False) or loc.get('events', default)
return loc.get("all_s", False) or loc.get("events", default)
elif type(location) is discord.TextChannel:
loc = self.cache[location.guild.id]
opts = [loc.get('all_s', False), self.cache[location.id].get("enabled", default)]
opts = [loc.get("all_s", False), self.cache[location.id].get("enabled", default)]
return any(opts)
elif type(location) is discord.VoiceChannel:
loc = self.cache[location.guild.id]
opts = [loc.get('all_s', False), loc.get('voice', False)]
opts = [loc.get("all_s", False), loc.get("voice", False)]
return any(opts)
elif isinstance(location, discord.abc.PrivateChannel):
return self.cache.get('direct', default)
return self.cache.get("direct", default)
else: # can't log other types
return False
def should_download(self, msg):
return self.should_log(msg.channel) and self.cache.get('attachments', False)
return self.should_log(msg.channel) and self.cache.get("attachments", False)
def process_attachment(self, message, a):
aid = a.id
@ -1086,23 +1060,23 @@ class ActivityLogger(commands.Cog):
if type(channel) is discord.TextChannel:
guildid = channel.guild.id
elif isinstance(channel, discord.abc.PrivateChannel):
guildid = 'direct'
guildid = "direct"
path = os.path.join(path, str(guildid), str(channel.id) + '_attachments')
filename = str(aid) + '_' + aname
path = os.path.join(path, str(guildid), str(channel.id) + "_attachments")
filename = str(aid) + "_" + aname
if len(filename) > 255:
target_len = 255 - len(aid) - 4
part_a = target_len // 2
part_b = target_len - part_a
filename = aid + '_' + aname[:part_a] + '...' + aname[-part_b:]
filename = aid + "_" + aname[:part_a] + "..." + aname[-part_b:]
truncated = True
else:
truncated = False
return aid, url, path, filename, truncated
async def log(self, location, text, timestamp=None, force=False, subfolder=None, mode='a'):
async def log(self, location, text, timestamp=None, force=False, subfolder=None, mode="a"):
if not timestamp:
timestamp = datetime.utcnow()
@ -1111,24 +1085,24 @@ class ActivityLogger(commands.Cog):
path = []
entry = [timestamp.strftime(TIMESTAMP_FORMAT)]
rotation = self.cache['rotation']
rotation = self.cache["rotation"]
if type(location) is discord.Guild:
path += [str(location.id), 'guild.log']
path += [str(location.id), "guild.log"]
elif type(location) is discord.TextChannel or type(location) is discord.VoiceChannel:
guildid = str(location.guild.id)
entry.append('#' + location.name)
path += [guildid, str(location.id) + '.log']
entry.append("#" + location.name)
path += [guildid, str(location.id) + ".log"]
elif isinstance(location, discord.abc.PrivateChannel):
path += ['direct', str(location.id) + '.log']
path += ["direct", str(location.id) + ".log"]
elif type(location) is discord.User or type(location) is discord.Member:
path += ['usernames', 'usernames.log']
path += ["usernames", "usernames.log"]
else:
return
if subfolder:
path.insert(-1, str(subfolder))
text = text.replace('\n', '\\n')
text = text.replace("\n", "\\n")
entry.append(text)
if rotation:
@ -1136,7 +1110,7 @@ class ActivityLogger(commands.Cog):
fname = os.path.join(PATH, *path)
handle = self.gethandle(fname, mode=mode)
await handle.write(' '.join(entry) + '\n')
await handle.write(" ".join(entry) + "\n")
async def message_handler(self, message, *args, force_attachments=None, **kwargs):
dl_attachment = self.should_download(message)
@ -1149,10 +1123,12 @@ class ActivityLogger(commands.Cog):
for a in message.attachments:
attachments += [self.process_attachment(message, a)]
entry = DOWNLOAD_TEMPLATE.format(message, [a[3] + ' (filename truncated)' if a[4] else a[3] for a in attachments])
entry = DOWNLOAD_TEMPLATE.format(
message, [a[3] + " (filename truncated)" if a[4] else a[3] for a in attachments]
)
elif message.attachments:
urls = ','.join(a.url for a in message.attachments)
urls = ",".join(a.url for a in message.attachments)
entry = ATTACHMENT_TEMPLATE.format(message, urls)
else:
entry = MESSAGE_TEMPLATE.format(message)
@ -1199,10 +1175,12 @@ class ActivityLogger(commands.Cog):
async for entry in message.guild.audit_logs(limit=1):
# target is user who had message deleted
if entry.action is discord.AuditLogAction.message_delete:
if entry.target.id == message.author.id and \
entry.extra.channel.id == message.channel.id and \
entry.created_at.timestamp() > time.time() - 3000 and \
entry.extra.count >= 1:
if (
entry.target.id == message.author.id
and entry.extra.channel.id == message.channel.id
and entry.created_at.timestamp() > time.time() - 3000
and entry.extra.count >= 1
):
entry_s = DELETE_AUDIT_TEMPLATE.format(entry.user, message, message.author, timestamp)
break
except:
@ -1215,12 +1193,12 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_join(self, guild):
entry = 'this bot joined the guild'
entry = "this bot joined the guild"
await self.log(guild, entry)
@commands.Cog.listener()
async def on_guild_remove(self, guild):
entry = 'this bot left the guild'
entry = "this bot left the guild"
await self.log(guild, entry)
@commands.Cog.listener()
@ -1236,33 +1214,43 @@ class ActivityLogger(commands.Cog):
if before.owner != after.owner:
if user:
entries.append('guild owner changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.owner} (id {0.owner.id}) to {1.owner} (id {1.owner.id})')
entries.append(
"guild owner changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.owner} (id {0.owner.id}) to {1.owner} (id {1.owner.id})"
)
else:
entries.append('guild owner changed from {0.owner} (id {0.owner.id}) to {1.owner} (id {1.owner.id})')
entries.append("guild owner changed from {0.owner} (id {0.owner.id}) to {1.owner} (id {1.owner.id})")
if before.region != after.region:
if user:
entries.append('guild region changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.region} to {1.region}')
entries.append(
"guild region changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.region} to {1.region}"
)
else:
entries.append('guild region changed from {0.region} to {1.region}')
entries.append("guild region changed from {0.region} to {1.region}")
if before.name != after.name:
if user:
entries.append('guild name changed by @{2.name}#{2.discriminator}(id:{2.id}), from "{0.name}" to "{1.name}"')
entries.append(
'guild name changed by @{2.name}#{2.discriminator}(id:{2.id}), from "{0.name}" to "{1.name}"'
)
else:
entries.append('guild name changed from "{0.name}" to "{1.name}"')
if before.icon_url != after.icon_url:
if user:
entries.append('guild icon changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.icon_url} to {1.icon_url}')
entries.append(
"guild icon changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.icon_url} to {1.icon_url}"
)
else:
entries.append('guild icon changed from {0.icon_url} to {1.icon_url}')
entries.append("guild icon changed from {0.icon_url} to {1.icon_url}")
if before.splash != after.splash:
if user:
entries.append('guild splash changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.splash} to {1.splash}')
entries.append(
"guild splash changed by @{2.name}#{2.discriminator}(id:{2.id}), from {0.splash} to {1.splash}"
)
else:
entries.append('guild splash changed from {0.splash} to {1.splash}')
entries.append("guild splash changed from {0.splash} to {1.splash}")
for e in entries:
if user:
@ -1326,45 +1314,61 @@ class ActivityLogger(commands.Cog):
if before.color != after.color:
if user:
entries.append('Role color by @{2.name}#{2.discriminator}(id:{2.id}): "{0}" (id {0.id}) changed from {0.color} to {1.color}')
entries.append(
'Role color by @{2.name}#{2.discriminator}(id:{2.id}): "{0}" (id {0.id}) changed from {0.color} to {1.color}'
)
else:
entries.append('Role color: "{0}" (id {0.id}) changed from {0.color} to {1.color}')
if before.mentionable != after.mentionable:
if after.mentionable:
if user:
entries.append('Role mentionable by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is now mentionable')
entries.append(
'Role mentionable by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is now mentionable'
)
else:
entries.append('Role mentionable: "{1.name}" (id {1.id}) is now mentionable')
else:
if user:
entries.append('Role mentionable by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is no longer mentionable')
entries.append(
'Role mentionable by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is no longer mentionable'
)
else:
entries.append('Role mentionable: "{1.name}" (id {1.id}) is no longer mentionable')
if before.hoist != after.hoist:
if after.hoist:
if user:
entries.append('Role hoist by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is now shown seperately')
entries.append(
'Role hoist by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is now shown seperately'
)
else:
entries.append('Role hoist: "{1.name}" (id {1.id}) is now shown seperately')
else:
if user:
entries.append('Role hoist by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is no longer shown seperately')
entries.append(
'Role hoist by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) is no longer shown seperately'
)
else:
entries.append('Role hoist: "{1.name}" (id {1.id}) is no longer shown seperately')
if before.permissions != after.permissions:
if user:
entries.append('Role permissions by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) changed from {0.permissions.value} '
'to {1.permissions.value}')
entries.append(
'Role permissions by @{2.name}#{2.discriminator}(id:{2.id}): "{1.name}" (id {1.id}) changed from {0.permissions.value} '
"to {1.permissions.value}"
)
else:
entries.append('Role permissions: "{1.name}" (id {1.id}) changed from {0.permissions.value} '
'to {1.permissions.value}')
entries.append(
'Role permissions: "{1.name}" (id {1.id}) changed from {0.permissions.value} '
"to {1.permissions.value}"
)
if before.position != after.position:
if user:
entries.append('Role position by @{2.name}#{2.discriminator}(id:{2.id}): "{0}" changed from {0.position} to {1.position}')
entries.append(
'Role position by @{2.name}#{2.discriminator}(id:{2.id}): "{0}" changed from {0.position} to {1.position}'
)
else:
entries.append('Role position: "{0}" changed from {0.position} to {1.position}')
@ -1376,7 +1380,7 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_member_join(self, member):
entry = 'Member join: @{0} (id {0.id})'.format(member)
entry = "Member join: @{0} (id {0.id})".format(member)
async with self.config.user(member).past_names() as past_names:
if str(member) not in past_names:
@ -1396,9 +1400,9 @@ class ActivityLogger(commands.Cog):
pass
if user:
entry = 'Member kicked by @{1.name}#{1.discriminator}(id:{1.id}): @{0} (id {0.id})'.format(member, user)
entry = "Member kicked by @{1.name}#{1.discriminator}(id:{1.id}): @{0} (id {0.id})".format(member, user)
else:
entry = 'Member leave: @{0} (id {0.id})'.format(member)
entry = "Member leave: @{0} (id {0.id})".format(member)
await self.config.member(member).clear()
await self.log(member.guild, entry)
@ -1415,9 +1419,9 @@ class ActivityLogger(commands.Cog):
pass
if user:
entry = 'Member banned by @{1.name}#{1.discriminator}(id:{1.id}): @{0} (id {0.id})'.format(member, user)
entry = "Member banned by @{1.name}#{1.discriminator}(id:{1.id}): @{0} (id {0.id})".format(member, user)
else:
entry = 'Member ban: @{0} (id {0.id})'.format(member)
entry = "Member ban: @{0} (id {0.id})".format(member)
await self.log(member.guild, entry)
@ -1433,9 +1437,9 @@ class ActivityLogger(commands.Cog):
pass
if user:
entry = 'Member unbanned by @{1.name}#{1.discriminator}(id:{1.id}): @{0} (id {0.id})'.format(member, user)
entry = "Member unbanned by @{1.name}#{1.discriminator}(id:{1.id}): @{0} (id {0.id})".format(member, user)
else:
entry = 'Member unban: @{0} (id {0.id})'.format(member)
entry = "Member unban: @{0} (id {0.id})".format(member)
await self.log(guild, entry)
@ -1445,8 +1449,10 @@ class ActivityLogger(commands.Cog):
user = None
try:
async for entry in after.guild.audit_logs(limit=1):
if entry.action is discord.AuditLogAction.member_update \
or entry.action is discord.AuditLogAction.member_role_update:
if (
entry.action is discord.AuditLogAction.member_update
or entry.action is discord.AuditLogAction.member_role_update
):
if entry.target.id == after.id:
user = entry.user
except:
@ -1454,7 +1460,9 @@ class ActivityLogger(commands.Cog):
if before.nick != after.nick:
if user:
entries.append('Member nickname changed by @{2.name}#{2.discriminator}(id:{2.id}): "@{0}" (id {0.id}) nickname change from "{0.nick}" to "{1.nick}"')
entries.append(
'Member nickname changed by @{2.name}#{2.discriminator}(id:{2.id}): "@{0}" (id {0.id}) nickname change from "{0.nick}" to "{1.nick}"'
)
else:
entries.append('Member nickname: "@{0}" (id {0.id}) changed nickname from "{0.nick}" to "{1.nick}"')
@ -1466,18 +1474,27 @@ class ActivityLogger(commands.Cog):
for r in added:
if user:
entries.append('Member role added by @{1.name}#{1.discriminator}(id:{1.id}): "{0}" (id {0.id}) role '
'was added to "@{{0}}" (id {{0.id}})'.format(r, user))
entries.append(
'Member role added by @{1.name}#{1.discriminator}(id:{1.id}): "{0}" (id {0.id}) role '
'was added to "@{{0}}" (id {{0.id}})'.format(r, user)
)
else:
entries.append('Member role add: "{0}" (id {0.id}) role '
'was added to "@{{0}}" (id {{0.id}})'.format(r))
entries.append(
'Member role add: "{0}" (id {0.id}) role ' 'was added to "@{{0}}" (id {{0.id}})'.format(r)
)
for r in removed:
if user:
entries.append('Member role removed by @{1.name}#{1.discriminator}(id:{1.id}): "{0}" (id {0.id}) role was removed from "@{{0}}" (id {{0.id}})'.format(r, user))
entries.append(
'Member role removed by @{1.name}#{1.discriminator}(id:{1.id}): "{0}" (id {0.id}) role was removed from "@{{0}}" (id {{0.id}})'.format(
r, user
)
)
else:
entries.append('Member role remove: "{0}" (id {0.id}) role '
'was removed from "@{{0}}" (id {{0.id}})'.format(r))
entries.append(
'Member role remove: "{0}" (id {0.id}) role '
'was removed from "@{{0}}" (id {{0.id}})'.format(r)
)
for e in entries:
await self.log(before.guild, e.format(before, after, user))
@ -1513,7 +1530,9 @@ class ActivityLogger(commands.Cog):
pass
if user:
entry = 'Channel created by @{1.name}#{1.discriminator}(id:{1.id}): "{0.name}" (id {0.id})'.format(channel, user)
entry = 'Channel created by @{1.name}#{1.discriminator}(id:{1.id}): "{0.name}" (id {0.id})'.format(
channel, user
)
else:
entry = 'Channel created: "{0.name}" (id {0.id})'.format(channel)
@ -1531,7 +1550,9 @@ class ActivityLogger(commands.Cog):
pass
if user:
entry = 'Channel deleted by @{1.name}#{1.discriminator}(id:{1.id}): "{0.name}" (id {0.id})'.format(channel, user)
entry = 'Channel deleted by @{1.name}#{1.discriminator}(id:{1.id}): "{0.name}" (id {0.id})'.format(
channel, user
)
else:
entry = 'Channel deleted: "{0.name}" (id {0.id})'.format(channel)
@ -1552,19 +1573,25 @@ class ActivityLogger(commands.Cog):
if before.name != after.name:
if user:
entries.append('Channel rename by @{2.name}#{2.discriminator}(id:{2.id}): "{0.name}" (id {0.id}) renamed to "{1.name}"')
entries.append(
'Channel rename by @{2.name}#{2.discriminator}(id:{2.id}): "{0.name}" (id {0.id}) renamed to "{1.name}"'
)
else:
entries.append('Channel rename: "{0.name}" (id {0.id}) renamed to "{1.name}"')
if before.topic != after.topic:
if user:
entries.append('Channel topic by @{2.name}#{2.discriminator}(id:{2.id}): "{0.name}" (id {0.id}) topic was set to "{1.topic}"')
entries.append(
'Channel topic by @{2.name}#{2.discriminator}(id:{2.id}): "{0.name}" (id {0.id}) topic was set to "{1.topic}"'
)
else:
entries.append('Channel topic: "{0.name}" (id {0.id}) topic was set to "{1.topic}"')
if before.position != after.position:
if user:
entries.append('Channel position by @{2.name}#{2.discriminator}(id:{2.id}): "{0.name}" (id {0.id}) moved from {0.position} to {1.position}')
entries.append(
'Channel position by @{2.name}#{2.discriminator}(id:{2.id}): "{0.name}" (id {0.id}) moved from {0.position} to {1.position}'
)
else:
entries.append('Channel position: "{0.name}" (id {0.id}) moved from {0.position} to {1.position}')
@ -1603,11 +1630,11 @@ class ActivityLogger(commands.Cog):
msg = "Voice channel leave: {0} (id {0.id})"
async with self.config.member(member).stats() as stats:
stats["vc_time_sec"] += (time.time() - stats["last_vc_time"])
stats["vc_time_sec"] += time.time() - stats["last_vc_time"]
stats["last_vc_time"] = None
if after.channel:
msg += ' moving to {1.channel}'
msg += " moving to {1.channel}"
await self.log(before.channel, msg.format(member, after))
@ -1618,35 +1645,35 @@ class ActivityLogger(commands.Cog):
stats["last_vc_time"] = time.time()
if before.channel:
msg += ', moved from {1.channel}'
msg += ", moved from {1.channel}"
flags = self.get_voice_flags(after)
if flags:
msg += ', flags: %s' % ','.join(flags)
msg += ", flags: %s" % ",".join(flags)
await self.log(after.channel, msg.format(member, before))
if before.deaf != after.deaf:
verb = 'deafen' if after.deaf else 'undeafen'
await self.log(before.channel, 'guild {0}: {1} (id {1.id})'.format(verb, member))
verb = "deafen" if after.deaf else "undeafen"
await self.log(before.channel, "guild {0}: {1} (id {1.id})".format(verb, member))
if before.mute != after.mute:
verb = 'mute' if after.mute else 'unmute'
await self.log(before.channel, 'guild {0}: {1} (id {1.id})'.format(verb, member))
verb = "mute" if after.mute else "unmute"
await self.log(before.channel, "guild {0}: {1} (id {1.id})".format(verb, member))
if before.self_deaf != after.self_deaf:
verb = 'deafen' if after.self_deaf else 'undeafen'
await self.log(before.channel, 'guild self-{0}: {1} (id {1.id})'.format(verb, member))
verb = "deafen" if after.self_deaf else "undeafen"
await self.log(before.channel, "guild self-{0}: {1} (id {1.id})".format(verb, member))
if before.self_mute != after.self_mute:
verb = 'mute' if after.self_mute else 'unmute'
await self.log(before.channel, 'guild self-{0}: {1} (id {1.id})'.format(verb, member))
verb = "mute" if after.self_mute else "unmute"
await self.log(before.channel, "guild self-{0}: {1} (id {1.id})".format(verb, member))
if before.self_stream != after.self_stream:
verb = 'stop-stream' if not after.self_stream else 'start-stream'
await self.log(before.channel, 'guild self-{0}: {1} (id {1.id})'.format(verb, member))
verb = "stop-stream" if not after.self_stream else "start-stream"
await self.log(before.channel, "guild self-{0}: {1} (id {1.id})".format(verb, member))
if before.self_video != after.self_video:
verb = 'start-video' if after.self_video else 'stop-video'
await self.log(before.channel, 'guild self-{0}: {1} (id {1.id})'.format(verb, member))
verb = "start-video" if after.self_video else "stop-video"
await self.log(before.channel, "guild self-{0}: {1} (id {1.id})".format(verb, member))

View file

@ -43,7 +43,6 @@ def parse_time(datetimestring: str):
return ret
def parse_time_naive(datetimestring: str):
return parser.parse(datetimestring)

View file

@ -59,9 +59,7 @@ class Admin(commands.Cog):
self.conf.register_global(serverlocked=False)
self.conf.register_guild(
announce_ignore=False,
announce_channel=None, # Integer ID
selfroles=[], # List of integer ID's
announce_ignore=False, announce_channel=None, selfroles=[], # Integer ID # List of integer ID's
)
self.__current_announcer = None
@ -114,16 +112,12 @@ class Admin(commands.Cog):
await member.add_roles(role)
except discord.Forbidden:
if not self.pass_hierarchy_check(ctx, role):
await self.complain(
ctx, T_(HIERARCHY_ISSUE), role=role, member=member, verb=_("add")
)
await self.complain(ctx, T_(HIERARCHY_ISSUE), role=role, member=member, verb=_("add"))
else:
await self.complain(ctx, T_(GENERIC_FORBIDDEN))
else:
await ctx.send(
_("I successfully added {role.name} to {member.display_name}").format(
role=role, member=member
)
_("I successfully added {role.name} to {member.display_name}").format(role=role, member=member)
)
async def _removerole(self, ctx: commands.Context, member: discord.Member, role: discord.Role):
@ -134,24 +128,18 @@ class Admin(commands.Cog):
await member.remove_roles(role)
except discord.Forbidden:
if not self.pass_hierarchy_check(ctx, role):
await self.complain(
ctx, T_(HIERARCHY_ISSUE), role=role, member=member, verb=_("remove")
)
await self.complain(ctx, T_(HIERARCHY_ISSUE), role=role, member=member, verb=_("remove"))
else:
await self.complain(ctx, T_(GENERIC_FORBIDDEN))
else:
await ctx.send(
_("I successfully removed {role.name} from {member.display_name}").format(
role=role, member=member
)
_("I successfully removed {role.name} from {member.display_name}").format(role=role, member=member)
)
@commands.command()
@commands.guild_only()
@checks.admin_or_permissions(manage_roles=True)
async def addrole(
self, ctx: commands.Context, rolename: discord.Role, *, user: MemberDefaultAuthor = None
):
async def addrole(self, ctx: commands.Context, rolename: discord.Role, *, user: MemberDefaultAuthor = None):
"""Add a role to a user.
If user is left blank it defaults to the author of the command.
@ -162,16 +150,12 @@ class Admin(commands.Cog):
# noinspection PyTypeChecker
await self._addrole(ctx, user, rolename)
else:
await self.complain(
ctx, T_(USER_HIERARCHY_ISSUE), member=user, role=rolename, verb=_("add")
)
await self.complain(ctx, T_(USER_HIERARCHY_ISSUE), member=user, role=rolename, verb=_("add"))
@commands.command()
@commands.guild_only()
@checks.admin_or_permissions(manage_roles=True)
async def removerole(
self, ctx: commands.Context, rolename: discord.Role, *, user: MemberDefaultAuthor = None
):
async def removerole(self, ctx: commands.Context, rolename: discord.Role, *, user: MemberDefaultAuthor = None):
"""Remove a role from a user.
If user is left blank it defaults to the author of the command.
@ -182,9 +166,7 @@ class Admin(commands.Cog):
# noinspection PyTypeChecker
await self._removerole(ctx, user, rolename)
else:
await self.complain(
ctx, T_(USER_HIERARCHY_ISSUE), member=user, role=rolename, verb=_("remove")
)
await self.complain(ctx, T_(USER_HIERARCHY_ISSUE), member=user, role=rolename, verb=_("remove"))
@commands.group()
@commands.guild_only()
@ -194,9 +176,7 @@ class Admin(commands.Cog):
pass
@editrole.command(name="colour", aliases=["color"])
async def editrole_colour(
self, ctx: commands.Context, role: discord.Role, value: discord.Colour
):
async def editrole_colour(self, ctx: commands.Context, role: discord.Role, value: discord.Colour):
"""Edit a role's colour.
Use double quotes if the role contains spaces.
@ -234,9 +214,7 @@ class Admin(commands.Cog):
"""
author = ctx.message.author
old_name = role.name
reason = "{}({}) changed the name of role '{}' to '{}'".format(
author.name, author.id, old_name, name
)
reason = "{}({}) changed the name of role '{}' to '{}'".format(author.name, author.id, old_name, name)
if not self.pass_user_hierarchy_check(ctx, role):
await self.complain(ctx, T_(ROLE_USER_HIERARCHY_ISSUE), role=role)
@ -285,9 +263,7 @@ class Admin(commands.Cog):
channel = ctx.channel
await self.conf.guild(ctx.guild).announce_channel.set(channel.id)
await ctx.send(
_("The announcement channel has been set to {channel.mention}").format(channel=channel)
)
await ctx.send(_("The announcement channel has been set to {channel.mention}").format(channel=channel))
@announce.command(name="ignore")
@commands.guild_only()
@ -298,15 +274,9 @@ class Admin(commands.Cog):
await self.conf.guild(ctx.guild).announce_ignore.set(not ignored)
if ignored: # Keeping original logic....
await ctx.send(
_("The server {guild.name} will receive announcements.").format(guild=ctx.guild)
)
await ctx.send(_("The server {guild.name} will receive announcements.").format(guild=ctx.guild))
else:
await ctx.send(
_("The server {guild.name} will not receive announcements.").format(
guild=ctx.guild
)
)
await ctx.send(_("The server {guild.name} will not receive announcements.").format(guild=ctx.guild))
async def _valid_selfroles(self, guild: discord.Guild) -> Tuple[discord.Role]:
"""

View file

@ -66,9 +66,7 @@ class Announcer:
try:
await channel.send(self.message)
except discord.Forbidden:
await bot_owner.send(
_("I could not announce to server: {server.id}").format(server=g)
)
await bot_owner.send(_("I could not announce to server: {server.id}").format(server=g))
await asyncio.sleep(0.5)
self.active = False

View file

@ -1,5 +1,6 @@
from .events import Events
def setup(bot):
n = Events(bot)
bot.add_cog(n)

View file

@ -11,11 +11,30 @@ import pytz
from tzlocal import get_localzone
basic_colors = [discord.Colour.blue(), discord.Colour.teal(), discord.Colour.dark_teal(), discord.Colour.green(), discord.Colour.dark_green(), discord.Colour.dark_blue(), discord.Colour.purple(), discord.Colour.dark_purple(), discord.Colour.magenta(), discord.Colour.gold(), discord.Colour.orange(), discord.Colour.red(), discord.Colour.dark_red(), discord.Colour.blurple(), discord.Colour.greyple()]
basic_colors = [
discord.Colour.blue(),
discord.Colour.teal(),
discord.Colour.dark_teal(),
discord.Colour.green(),
discord.Colour.dark_green(),
discord.Colour.dark_blue(),
discord.Colour.purple(),
discord.Colour.dark_purple(),
discord.Colour.magenta(),
discord.Colour.gold(),
discord.Colour.orange(),
discord.Colour.red(),
discord.Colour.dark_red(),
discord.Colour.blurple(),
discord.Colour.greyple(),
]
class Events(commands.Cog):
"""
Set events that track time since set events
"""
def __init__(self, bot):
super().__init__()
self.config = Config.get_conf(self, identifier=6748392754)
@ -55,14 +74,29 @@ class Events(commands.Cog):
elapsed_time = datetime.datetime.utcnow() - start_time
embed = discord.Embed(title=event_name, colour=random.choice(basic_colors))
embed.add_field(name="Event time", value=start_time.replace(tzinfo=pytz.utc).astimezone(self.timezone).strftime("%b %d, %Y, %H:%M"))
embed.add_field(
name="Event time",
value=start_time.replace(tzinfo=pytz.utc).astimezone(self.timezone).strftime("%b %d, %Y, %H:%M"),
)
day_msg = "{} day{},".format(elapsed_time.days, "s" if elapsed_time.days > 1 else "")
hour_msg = " {} hour{}".format(int(elapsed_time.seconds / 60 / 60), "s" if int(elapsed_time.seconds / 60 / 60) > 1 else "")
hour_msg = " {} hour{}".format(
int(elapsed_time.seconds / 60 / 60), "s" if int(elapsed_time.seconds / 60 / 60) > 1 else ""
)
if elapsed_time.days > 0 or int(elapsed_time.seconds / 60 / 60) > 0:
minute_msg = ", and {} minute{}".format(int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60), "s" if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1 else "")
minute_msg = ", and {} minute{}".format(
int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60),
"s" if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1 else "",
)
else:
minute_msg = "{} minute{}".format(int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60), "s" if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1 else "")
msg = "{}{}{}".format(day_msg if elapsed_time.days > 0 else "", hour_msg if int(elapsed_time.seconds / 60 / 60) > 0 else "", minute_msg)
minute_msg = "{} minute{}".format(
int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60),
"s" if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1 else "",
)
msg = "{}{}{}".format(
day_msg if elapsed_time.days > 0 else "",
hour_msg if int(elapsed_time.seconds / 60 / 60) > 0 else "",
minute_msg,
)
embed.add_field(name="Elapsed time", value=msg)
message = await channel.send(embed=embed)
async with self.config.guild(guild).events() as events:
@ -98,13 +132,20 @@ class Events(commands.Cog):
msg += "```"
await ctx.send(msg)
await ctx.send("Please choose which event you want to delete. (type number in chat)")
def m_check(m):
try:
return m.author.id == ctx.author.id and m.channel.id == ctx.channel.id and int(m.content) <= counter and int(m.content) >= 0
return (
m.author.id == ctx.author.id
and m.channel.id == ctx.channel.id
and int(m.content) <= counter
and int(m.content) >= 0
)
except:
return False
try:
response = await self.bot.wait_for('message', timeout=30, check=m_check)
response = await self.bot.wait_for("message", timeout=30, check=m_check)
except:
await ctx.send("Timed out, event deletion cancelled.")
return
@ -179,14 +220,35 @@ class Events(commands.Cog):
elapsed_time = datetime.datetime.utcnow() - start_time
embed = message.embeds[0]
embed.clear_fields()
embed.add_field(name="Event time", value=start_time.replace(tzinfo=pytz.utc).astimezone(self.timezone).strftime("%b %d, %Y, %H:%M"))
embed.add_field(
name="Event time",
value=start_time.replace(tzinfo=pytz.utc)
.astimezone(self.timezone)
.strftime("%b %d, %Y, %H:%M"),
)
day_msg = "{} day{},".format(elapsed_time.days, "s" if elapsed_time.days > 1 else "")
hour_msg = " {} hour{}".format(int(elapsed_time.seconds / 60 / 60), "s" if int(elapsed_time.seconds / 60 / 60) > 1 else "")
hour_msg = " {} hour{}".format(
int(elapsed_time.seconds / 60 / 60), "s" if int(elapsed_time.seconds / 60 / 60) > 1 else ""
)
if elapsed_time.days > 0 or int(elapsed_time.seconds / 60 / 60) > 0:
minute_msg = ", and {} minute{}".format(int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60), "s" if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1 else "")
minute_msg = ", and {} minute{}".format(
int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60),
"s"
if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1
else "",
)
else:
minute_msg = "{} minute{}".format(int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60), "s" if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1 else "")
msg = "{}{}{}".format(day_msg if elapsed_time.days > 0 else "", hour_msg if int(elapsed_time.seconds / 60 / 60) > 0 else "", minute_msg)
minute_msg = "{} minute{}".format(
int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60),
"s"
if int(elapsed_time.seconds / 60 - int(elapsed_time.seconds / 60 / 60) * 60) > 1
else "",
)
msg = "{}{}{}".format(
day_msg if elapsed_time.days > 0 else "",
hour_msg if int(elapsed_time.seconds / 60 / 60) > 0 else "",
minute_msg,
)
embed.add_field(name="Elapsed time", value=msg)
await message.edit(embed=embed)
await asyncio.sleep(30)

View file

@ -1,4 +1,5 @@
from .pony import Pony
def setup(bot):
bot.add_cog(Pony())

View file

@ -7,18 +7,14 @@ import os
import traceback
import json
class Pony(commands.Cog):
def __init__(self):
super().__init__()
self.config = Config.get_conf(self, identifier=7384662719)
default_global = {
"maxfilters":50
}
self.default_guild = {
"filters": ["-meme", "safe", "-spoiler:*", "-vulgar"],
"verbose": False
}
default_global = {"maxfilters": 50}
self.default_guild = {"filters": ["-meme", "safe", "-spoiler:*", "-vulgar"], "verbose": False}
self.config.register_guild(**self.default_guild)
self.config.register_global(**default_global)
@ -40,7 +36,7 @@ class Pony(commands.Cog):
"""
Gives a random picture of our mascot!
"""
await fetch_image(self, ctx, randomize=True, mascot=True, tags=['safe,', 'coe'])
await fetch_image(self, ctx, randomize=True, mascot=True, tags=["safe,", "coe"])
@commands.group()
@commands.guild_only()
@ -100,10 +96,10 @@ class Pony(commands.Cog):
guild = ctx.guild
filters = await self.config.guild(guild).filters()
if filters:
filter_list = '\n'.join(sorted(filters))
filter_list = "\n".join(sorted(filters))
target_guild = "{}'s".format(guild.name)
else:
filter_list = '\n'.join(sorted(filters["default"]))
filter_list = "\n".join(sorted(filters["default"]))
target_guild = "Default"
await ctx.send("{} pony filter list contains:```\n{}```".format(target_guild, filter_list))
@ -178,7 +174,7 @@ class Pony(commands.Cog):
if guild is None:
continue
await self.config.guild(guild).verbose.set(json_guild_verbose['verbose'])
await self.config.guild(guild).verbose.set(json_guild_verbose["verbose"])
msg += "**{}**\n".format(guild)
if len(msg) + 100 > 2000:
await ctx.send(msg)
@ -263,7 +259,7 @@ class Pony(commands.Cog):
# Fetch the image or display an error
try:
async with aiohttp.ClientSession(loop=ctx.bot.loop) as session:
async with session.get(search, headers={'User-Agent': "Booru-Cogs (https://git.io/booru)"}) as r:
async with session.get(search, headers={"User-Agent": "Booru-Cogs (https://git.io/booru)"}) as r:
website = await r.json()
if randomize:
if "id" in website:

View file

@ -1,5 +1,6 @@
from .punish import Punish
async def setup(bot):
punish = Punish(bot)
await punish.initialize()

View file

@ -2,7 +2,8 @@ class Memoizer:
"""
General purpose cache for function results. Appends positional args, overlays kwargs. Both must be hashable.
"""
__slots__ = ['_cache', '_func', '_args', '_kwargs']
__slots__ = ["_cache", "_func", "_args", "_kwargs"]
def __init__(self, func, *args, **kwargs):
self._cache = {}

View file

@ -16,21 +16,22 @@ import logging
import time
import textwrap
log = logging.getLogger('red.punish')
log = logging.getLogger("red.punish")
__version__ = '3.0.0'
__version__ = "3.0.0"
PURGE_MESSAGES = 1 # for cpunish
DEFAULT_ROLE_NAME = 'Punished'
DEFAULT_ROLE_NAME = "Punished"
DEFAULT_TEXT_OVERWRITE = discord.PermissionOverwrite(send_messages=False, send_tts_messages=False, add_reactions=False)
DEFAULT_VOICE_OVERWRITE = discord.PermissionOverwrite(speak=False, connect=False)
DEFAULT_TIMEOUT_OVERWRITE = discord.PermissionOverwrite(send_messages=True, read_messages=True)
QUEUE_TIME_CUTOFF = 30
DEFAULT_TIMEOUT = '5m'
DEFAULT_CASE_MIN_LENGTH = '5m' # only create modlog cases when length is longer than this
DEFAULT_TIMEOUT = "5m"
DEFAULT_CASE_MIN_LENGTH = "5m" # only create modlog cases when length is longer than this
class Punish(commands.Cog):
"""
@ -38,6 +39,7 @@ class Punish(commands.Cog):
do other things that can be denied using discord permissions. Includes
auto-setup and more.
"""
def __init__(self, bot):
super().__init__()
@ -53,7 +55,7 @@ class Punish(commands.Cog):
"VOICE_OVERWRITE": overwrite_to_dict(DEFAULT_VOICE_OVERWRITE),
"ROLE_ID": None,
"NITRO_ID": None,
"CHANNEL_ID": None
"CHANNEL_ID": None,
}
self.config.register_guild(**default_guild)
@ -100,7 +102,7 @@ class Punish(commands.Cog):
elif user:
await self._punish_cmd_common(ctx, user, duration, reason)
@punish.command(name='cstart')
@punish.command(name="cstart")
@commands.guild_only()
@checks.mod()
async def punish_cstart(self, ctx, user: discord.Member, duration: str = None, *, reason: str = None):
@ -121,7 +123,7 @@ class Punish(commands.Cog):
except discord.errors.Forbidden:
await ctx.send("Punishment set, but I need permissions to manage messages to clean up.")
@punish.command(name='list')
@punish.command(name="list")
@commands.guild_only()
@checks.mod()
async def punish_list(self, ctx):
@ -135,7 +137,7 @@ class Punish(commands.Cog):
guild = ctx.guild
guild_id = guild.id
now = time.time()
headers = ['Member', 'Remaining', 'Moderator', 'Reason']
headers = ["Member", "Remaining", "Moderator", "Reason"]
punished = await self.config.guild(guild).PUNISHED()
embeds = []
@ -143,13 +145,13 @@ class Punish(commands.Cog):
for i, data in enumerate(punished.items()):
member_id, data = data
member_name = getmname(member_id, guild)
moderator = getmname(data['by'], guild)
reason = data['reason']
until = data['until']
moderator = getmname(data["by"], guild)
reason = data["reason"]
until = data["until"]
sort = until or float("inf")
remaining = generate_timespec(until - now, short=True) if until else 'forever'
remaining = generate_timespec(until - now, short=True) if until else "forever"
row = [member_name, remaining, moderator, reason or 'No reason set.']
row = [member_name, remaining, moderator, reason or "No reason set."]
embed = discord.Embed(title="Punish List", colour=discord.Colour.from_rgb(255, 0, 0))
for header, row_val in zip(headers, row):
@ -164,7 +166,7 @@ class Punish(commands.Cog):
await menu(ctx, embeds, DEFAULT_CONTROLS)
@punish.command(name='clean')
@punish.command(name="clean")
@commands.guild_only()
@checks.mod()
async def punish_clean(self, ctx, clean_pending: bool = False):
@ -189,14 +191,14 @@ class Punish(commands.Cog):
if not mid.isdigit() or guild.get_member(mid):
continue
elif clean_pending or ((mdata['until'] or 0) < now):
del(data[mid])
elif clean_pending or ((mdata["until"] or 0) < now):
del data[mid]
count += 1
await self.config.guild(guild).PUNISHED.set(data)
await ctx.send('Cleaned %i absent members from the list.' % count)
await ctx.send("Cleaned %i absent members from the list." % count)
@punish.command(name='clean-bans')
@punish.command(name="clean-bans")
@commands.guild_only()
@checks.mod()
async def punish_clean_bans(self, ctx):
@ -220,13 +222,13 @@ class Punish(commands.Cog):
continue
elif mid in ban_ids:
del(data[mid])
del data[mid]
count += 1
await self.config.guild(guild).PUNISHED.set(data)
await ctx.send('Cleaned %i banned users from the list.' % count)
await ctx.send("Cleaned %i banned users from the list." % count)
@punish.command(name='warn')
@punish.command(name="warn")
@commands.guild_only()
@checks.mod_or_permissions(manage_messages=True)
async def punish_warn(self, ctx, user: discord.Member, *, reason: str = None):
@ -234,16 +236,15 @@ class Punish(commands.Cog):
Warns a user with boilerplate about the rules
"""
msg = ['Hey %s, ' % user.mention]
msg.append("you're doing something that might get you muted if you keep "
"doing it.")
msg = ["Hey %s, " % user.mention]
msg.append("you're doing something that might get you muted if you keep " "doing it.")
if reason:
msg.append(" Specifically, %s." % reason)
msg.append("Be sure to review the guild rules.")
await ctx.send(' '.join(msg))
await ctx.send(" ".join(msg))
@punish.command(name='end', aliases=['remove'])
@punish.command(name="end", aliases=["remove"])
@commands.guild_only()
@checks.mod()
async def punish_end(self, ctx, user: discord.Member, *, reason: str = None):
@ -260,28 +261,28 @@ class Punish(commands.Cog):
now = time.time()
punished = await self.config.guild(guild).PUNISHED()
data = punished.get(str(user.id), {})
removed_roles_parsed = resolve_role_list(guild, data.get('removed_roles', []))
removed_roles_parsed = resolve_role_list(guild, data.get("removed_roles", []))
if role and role in user.roles:
msg = 'Punishment manually ended early by %s.' % ctx.author
msg = "Punishment manually ended early by %s." % ctx.author
original_start = data.get('start')
original_end = data.get('until')
original_start = data.get("start")
original_end = data.get("until")
remaining = original_end and (original_end - now)
if remaining:
msg += ' %s was left' % generate_timespec(round(remaining))
msg += " %s was left" % generate_timespec(round(remaining))
if original_start:
msg += ' of the original %s.' % generate_timespec(round(original_end - original_start))
msg += " of the original %s." % generate_timespec(round(original_end - original_start))
else:
msg += '.'
msg += "."
if reason:
msg += '\n\nReason for ending early: ' + reason
msg += "\n\nReason for ending early: " + reason
if data.get('reason'):
msg += '\n\nOriginal reason was: ' + data['reason']
if data.get("reason"):
msg += "\n\nOriginal reason was: " + data["reason"]
updated_reason = str(msg) # copy string
@ -290,30 +291,34 @@ class Punish(commands.Cog):
msg += "\nRestored role(s): {}".format(names_list)
if not await self._unpunish(user, reason=updated_reason, update=True, moderator=moderator):
msg += '\n\n(failed to send punishment end notification DM)'
msg += "\n\n(failed to send punishment end notification DM)"
await ctx.send(msg)
elif data: # This shouldn't happen, but just in case
now = time.time()
until = data.get('until')
remaining = until and generate_timespec(round(until - now)) or 'forever'
until = data.get("until")
remaining = until and generate_timespec(round(until - now)) or "forever"
data_fmt = '\n'.join([
"**Reason:** %s" % (data.get('reason') or 'no reason set'),
data_fmt = "\n".join(
[
"**Reason:** %s" % (data.get("reason") or "no reason set"),
"**Time remaining:** %s" % remaining,
"**Moderator**: %s" % (user.guild.get_member(data.get('by')) or 'Missing ID#%s' % data.get('by'))
])
del(punished[str(user.id)])
"**Moderator**: %s" % (user.guild.get_member(data.get("by")) or "Missing ID#%s" % data.get("by")),
]
)
del punished[str(user.id)]
await self.config.guild(guild).PUNISHED.set(punished)
await ctx.send("That user doesn't have the %s role, but they still have a data entry. I removed it, "
"but in case it's needed, this is what was there:\n\n%s" % (role.name, data_fmt))
await ctx.send(
"That user doesn't have the %s role, but they still have a data entry. I removed it, "
"but in case it's needed, this is what was there:\n\n%s" % (role.name, data_fmt)
)
elif role:
await ctx.send("That user doesn't have the %s role." % role.name)
else:
await ctx.send("The punish role couldn't be found in this guild.")
@punish.command(name='reason')
@punish.command(name="reason")
@commands.guild_only()
@checks.mod()
async def punish_reason(self, ctx, user: discord.Member, *, reason: str = None):
@ -325,19 +330,21 @@ class Punish(commands.Cog):
data = punished.get(str(user.id), None)
if not data:
await ctx.send("That user doesn't have an active punishment entry. To update modlog "
"cases manually, use the `%sreason` command." % ctx.prefix)
await ctx.send(
"That user doesn't have an active punishment entry. To update modlog "
"cases manually, use the `%sreason` command." % ctx.prefix
)
return
punished[str(user.id)]['reason'] = reason
punished[str(user.id)]["reason"] = reason
await self.config.guild(guild).PUNISHED.set(punished)
if reason:
msg = 'Reason updated.'
msg = "Reason updated."
else:
msg = 'Reason cleared.'
msg = "Reason cleared."
caseno = data.get('caseno')
caseno = data.get("caseno")
try:
case = await modlog.get_case(caseno, guild, self.bot)
except:
@ -348,12 +355,12 @@ class Punish(commands.Cog):
moderator = ctx.author
try:
edits = {'reason': reason}
edits = {"reason": reason}
if moderator.id != data.get('by'):
edits['amended_by'] = moderator
if moderator.id != data.get("by"):
edits["amended_by"] = moderator
edits['modified_at'] = ctx.message.created_at.timestamp()
edits["modified_at"] = ctx.message.created_at.timestamp()
await case.edit(edits)
except:
@ -504,23 +511,23 @@ class Punish(commands.Cog):
try:
# Combine sets to get the baseline (roles they'd have normally)
member_roles |= set(role_memo.filter(member_data['removed_roles'], skip_nulls=True))
member_roles |= set(role_memo.filter(member_data["removed_roles"], skip_nulls=True))
except KeyError:
pass
# update new removed roles with intersection of guild removal list and baseline
new_removed = guild_remove_roles & member_roles
punished[str(member.id)]['removed_roles'] = [r.id for r in new_removed]
punished[str(member.id)]["removed_roles"] = [r.id for r in new_removed]
member_roles -= guild_remove_roles
# can't restore, so skip (remove from set)
for role in (member_roles - original_roles):
for role in member_roles - original_roles:
if role >= highest_role:
member_roles.discard(role)
# can't remove, so skip (re-add to set)
for role in (original_roles - member_roles):
for role in original_roles - member_roles:
if role >= highest_role:
member_roles.add(role)
@ -541,7 +548,7 @@ class Punish(commands.Cog):
await ctx.send(msg)
@punishset.command(name='setup')
@punishset.command(name="setup")
async def punishset_setup(self, ctx):
"""
(Re)configures the punish role and channel overrides
@ -568,28 +575,29 @@ class Punish(commands.Cog):
perms = discord.Permissions.none()
role = await guild.create_role(name=default_name, permissions=perms, reason="punish cog.")
else:
msgobj = await ctx.send('%s role exists... ' % role.name)
msgobj = await ctx.send("%s role exists... " % role.name)
if role.position != (guild.me.top_role.position - 1):
if role < guild.me.top_role:
await msgobj.edit(content=msgobj.content + 'moving role to higher position... ')
await msgobj.edit(content=msgobj.content + "moving role to higher position... ")
await role.edit(position=guild.me.top_role.position - 1)
else:
await msgobj.edit(content=msgobj.content + 'role is too high to manage.'
' Please move it to below my highest role.')
await msgobj.edit(
content=msgobj.content + "role is too high to manage." " Please move it to below my highest role."
)
return
await msgobj.edit(content=msgobj.content + '(re)configuring channels... ')
await msgobj.edit(content=msgobj.content + "(re)configuring channels... ")
for channel in guild.channels:
await self.setup_channel(channel, role)
await msgobj.edit(content=msgobj.content + 'done.')
await msgobj.edit(content=msgobj.content + "done.")
if role and role.id != role_id:
await self.config.guild(guild).ROLE_ID.set(role.id)
@punishset.command(name='channel')
@punishset.command(name="channel")
async def punishset_channel(self, ctx, channel: discord.TextChannel = None):
"""
Sets or shows the punishment "timeout" channel.
@ -613,13 +621,16 @@ class Punish(commands.Cog):
await ctx.send("The timeout channel is currently %s." % current.mention)
else:
if current == channel:
await ctx.send("The timeout channel is already %s. If you need to repair its permissions, use `%spunishset setup`." % (current.mention, ctx.prefix))
await ctx.send(
"The timeout channel is already %s. If you need to repair its permissions, use `%spunishset setup`."
% (current.mention, ctx.prefix)
)
return
await self.config.guild(guild).CHANNEL_ID.set(channel.id)
role = await self.get_role(guild, create=True)
update_msg = '{} to the %s role' % role
update_msg = "{} to the %s role" % role
grants = []
denies = []
perms = permissions_for_roles(channel, role)
@ -631,7 +642,7 @@ class Punish(commands.Cog):
if getattr(perms, perm) != value:
setattr(overwrite, perm, value)
name = perm.replace('_', ' ').title().replace("Tts", "TTS")
name = perm.replace("_", " ").title().replace("Tts", "TTS")
if value:
grants.append(name)
@ -640,8 +651,8 @@ class Punish(commands.Cog):
# Any changes made? Apply them.
if grants or denies:
grants = grants and ('grant ' + format_list(*grants))
denies = denies and ('deny ' + format_list(*denies))
grants = grants and ("grant " + format_list(*grants))
denies = denies and ("deny " + format_list(*denies))
to_join = [x for x in (grants, denies) if x]
update_msg = update_msg.format(format_list(*to_join))
@ -655,14 +666,14 @@ class Punish(commands.Cog):
await self.setup_channel(current, role)
if channel.permissions_for(guild.me).manage_roles:
await ctx.send(info('Updating permissions in %s to %s...' % (channel.mention, update_msg)))
await ctx.send(info("Updating permissions in %s to %s..." % (channel.mention, update_msg)))
await channel.set_permissions(role, overwrite=overwrite)
else:
await ctx.send(error("I don't have permissions to %s." % update_msg))
await ctx.send("Timeout channel set to %s." % channel.mention)
@punishset.command(name='clear-channel')
@punishset.command(name="clear-channel")
async def punishset_clear_channel(self, ctx):
"""
Clears the timeout channel and resets its permissions
@ -678,7 +689,7 @@ class Punish(commands.Cog):
if current.permissions_for(guild.me).manage_roles:
role = await self.get_role(guild, quiet=True)
await self.setup_channel(current, role)
msg = ' and its permissions reset'
msg = " and its permissions reset"
else:
msg = ", but I don't have permissions to reset its permissions."
@ -686,7 +697,7 @@ class Punish(commands.Cog):
else:
await ctx.send("No timeout channel has been set yet.")
@punishset.command(name='case-min')
@punishset.command(name="case-min")
async def punishset_case_min(self, ctx, *, timespec: str = None):
"""
Set/disable or display the minimum punishment case duration
@ -699,11 +710,11 @@ class Punish(commands.Cog):
if not timespec:
if current:
await ctx.send('Punishments longer than %s will create cases.' % generate_timespec(current))
await ctx.send("Punishments longer than %s will create cases." % generate_timespec(current))
else:
await ctx.send("Punishment case creation is disabled.")
else:
if timespec.strip('\'"').lower() == 'disable':
if timespec.strip("'\"").lower() == "disable":
value = None
else:
try:
@ -714,9 +725,9 @@ class Punish(commands.Cog):
await self.config.guild(guild).CASE_MIN_LENGTH.set(value)
await ctx.send('Punishments longer than %s will create cases.' % generate_timespec(value))
await ctx.send("Punishments longer than %s will create cases." % generate_timespec(value))
@punishset.command(name='overrides')
@punishset.command(name="overrides")
async def punishset_overrides(self, ctx, *, channel_id: int = None):
"""
Copy or display the punish role overrides
@ -751,52 +762,58 @@ class Punish(commands.Cog):
confirm_msg = "Are you sure you want to copy overrides from this channel?"
if channel.type is discord.ChannelType.text:
key = 'text'
key = "text"
elif channel.type is discord.ChannelType.voice:
key = 'voice'
key = "voice"
else:
await ctx.send(error("Unknown channel type!"))
return
if confirm_msg:
await ctx.send(warning(confirm_msg + '(reply `yes` within 30s to confirm)'))
await ctx.send(warning(confirm_msg + "(reply `yes` within 30s to confirm)"))
def check(m):
return m.author == ctx.author and m.channel == ctx.channel
try:
reply = await self.bot.wait_for('message', check=check, timeout=30.0)
if reply.content.strip(' `"\'').lower() != 'yes':
await ctx.send('Commmand cancelled.')
reply = await self.bot.wait_for("message", check=check, timeout=30.0)
if reply.content.strip(" `\"'").lower() != "yes":
await ctx.send("Commmand cancelled.")
return
except asyncio.TimeoutError:
await ctx.send('Timed out waiting for a response.')
await ctx.send("Timed out waiting for a response.")
return
if key == 'text':
if key == "text":
await self.config.guild(guild).TEXT_OVERWRITE.set(overwrite_to_dict(overwrite))
else:
await self.config.guild(guild).VOICE_OVERWRITE.set(overwrite_to_dict(overwrite))
await ctx.send("{} channel overrides set to:\n".format(key.title()) +
format_permissions(overwrite) +
"\n\nRun `%spunishset setup` to apply them to all channels." % ctx.prefix)
await ctx.send(
"{} channel overrides set to:\n".format(key.title())
+ format_permissions(overwrite)
+ "\n\nRun `%spunishset setup` to apply them to all channels." % ctx.prefix
)
else:
msg = []
for key in ('text', 'voice'):
if key == 'text':
for key in ("text", "voice"):
if key == "text":
data = await self.config.guild(guild).TEXT_OVERWRITE()
else:
data = await self.config.guild(guild).VOICE_OVERWRITE()
title = '%s permission overrides:' % key.title()
title = "%s permission overrides:" % key.title()
if data == overwrite_to_dict(DEFAULT_TEXT_OVERWRITE) or data == overwrite_to_dict(DEFAULT_VOICE_OVERWRITE):
title = title[:-1] + ' (defaults):'
if data == overwrite_to_dict(DEFAULT_TEXT_OVERWRITE) or data == overwrite_to_dict(
DEFAULT_VOICE_OVERWRITE
):
title = title[:-1] + " (defaults):"
msg.append(bold(title) + '\n' + format_permissions(overwrite_from_dict(data)))
msg.append(bold(title) + "\n" + format_permissions(overwrite_from_dict(data)))
await ctx.send('\n\n'.join(msg))
await ctx.send("\n\n".join(msg))
@punishset.command(name='reset-overrides')
async def punishset_reset_overrides(self, ctx, channel_type: str = 'both'):
@punishset.command(name="reset-overrides")
async def punishset_reset_overrides(self, ctx, channel_type: str = "both"):
"""
Resets the punish role overrides for text, voice or both (default)
@ -804,21 +821,21 @@ class Punish(commands.Cog):
for newly created channels.
"""
channel_type = channel_type.strip('`"\' ').lower()
channel_type = channel_type.strip("`\"' ").lower()
msg = []
for key in ('text', 'voice'):
if channel_type not in ['both', key]:
for key in ("text", "voice"):
if channel_type not in ["both", key]:
continue
title = '%s permission overrides reset to:' % key.title()
title = "%s permission overrides reset to:" % key.title()
if key == 'text':
if key == "text":
await self.config.guild(guild).TEXT_OVERWRITE.set(overwrite_to_dict(DEFAULT_TEXT_OVERWRITE))
msg.append(bold(title) + '\n' + format_permissions(overwrite_to_dict(DEFAULT_TEXT_OVERWRITE)))
msg.append(bold(title) + "\n" + format_permissions(overwrite_to_dict(DEFAULT_TEXT_OVERWRITE)))
else:
await self.config.guild(guild).VOICE_OVERWRITE.set(overwrite_to_dict(DEFAULT_VOICE_OVERWRITE))
msg.append(bold(title) + '\n' + format_permissions(overwrite_to_dict(DEFAULT_VOICE_OVERWRITE)))
msg.append(bold(title) + "\n" + format_permissions(overwrite_to_dict(DEFAULT_VOICE_OVERWRITE)))
if not msg:
await ctx.send("Invalid channel type. Use `text`, `voice`, or `both` (the default, if not specified)")
@ -826,7 +843,7 @@ class Punish(commands.Cog):
msg.append("Run `%spunishset setup` to apply them to all channels." % ctx.prefix)
await ctx.send('\n\n'.join(msg))
await ctx.send("\n\n".join(msg))
async def get_role(self, guild, quiet=False, create=False):
role_id = await self.config.guild(guild).ROLE_ID()
@ -848,19 +865,19 @@ class Punish(commands.Cog):
if not quiet:
msgobj = await ctx.send(msg)
log.debug('Creating punish role in %s' % guild.name)
log.debug("Creating punish role in %s" % guild.name)
perms = discord.Permissions.none()
role = await guild.create_role(name=DEFAULT_ROLE_NAME, permissions=perms, reason="punish cog.")
await role.edit(position=guild.me.top_role.position - 1)
if not quiet:
await msgobj.edit(content=msgobj.content + '\nconfiguring channels... ')
await msgobj.edit(content=msgobj.content + "\nconfiguring channels... ")
for channel in guild.channels:
await self.setup_channel(channel, role)
if not quiet:
await msgobj.edit(content=msgobj.content + '\ndone.')
await msgobj.edit(content=msgobj.content + "\ndone.")
if role and role.id != role_id:
await self.config.guild(guild).ROLE_ID.set(role.id)
@ -906,23 +923,23 @@ class Punish(commands.Cog):
for member_id, data in punished.items():
until = data['until']
until = data["until"]
member = guild.get_member(member_id)
if until and (until - time.time()) < 0:
if member:
reason = 'Punishment removal overdue, maybe the bot was offline. '
reason = "Punishment removal overdue, maybe the bot was offline. "
if data['reason']:
reason += data['reason']
if data["reason"]:
reason += data["reason"]
await self._unpunish(member, reason=reason)
else: # member disappeared
del(punished[str(member_id)])
del punished[str(member_id)]
elif member:
# re-check roles
user_roles = set(member.roles)
removed_roles = set(role_memo.filter(data.get('removed_roles', ()), skip_nulls=True))
removed_roles = set(role_memo.filter(data.get("removed_roles", ()), skip_nulls=True))
removed_roles = user_roles & {r for r in removed_roles if r < me.top_role}
user_roles -= removed_roles
@ -954,7 +971,7 @@ class Punish(commands.Cog):
except Exception:
pass
log.debug('queue manager dying')
log.debug("queue manager dying")
while not self.queue.empty():
self.queue.get_nowait()
@ -1038,7 +1055,7 @@ class Punish(commands.Cog):
remove_role_set = set(resolve_role_list(guild, remove_role_set))
punished = await self.config.guild(guild).PUNISHED()
current = punished.get(str(member.id), {})
reason = reason or current.get('reason') # don't clear if not given
reason = reason or current.get("reason") # don't clear if not given
hierarchy_allowed = ctx.author.top_role > member.top_role
case_min_length = await self.config.guild(guild).CASE_MIN_LENGTH()
nitro_role = await self.config.guild(guild).NITRO_ID()
@ -1051,7 +1068,7 @@ class Punish(commands.Cog):
await ctx.send("You can't punish the bot.")
return
if duration and duration.lower() in ['forever', 'inf', 'infinite']:
if duration and duration.lower() in ["forever", "inf", "infinite"]:
duration = None
else:
if not duration:
@ -1071,7 +1088,7 @@ class Punish(commands.Cog):
if role is None:
return
elif role >= guild.me.top_role:
await ctx.send('The %s role is too high for me to manage.' % role)
await ctx.send("The %s role is too high for me to manage." % role)
return
# Call time() after getting the role due to potential creation delay
@ -1085,22 +1102,26 @@ class Punish(commands.Cog):
try:
if current:
case_number = current.get('caseno')
case_number = current.get("caseno")
try:
case = await modlog.get_case(case_number, guild, self.bot)
except: # shouldn't happen
await ctx.send(warning("Error, modlog case not found, but user is punished with case.\nTry unpunishing and punishing again."))
await ctx.send(
warning(
"Error, modlog case not found, but user is punished with case.\nTry unpunishing and punishing again."
)
)
return
moderator = ctx.author
try:
edits = {'reason': reason}
edits = {"reason": reason}
if moderator.id != current.get('by'):
edits['amended_by'] = moderator
if moderator.id != current.get("by"):
edits["amended_by"] = moderator
edits['modified_at'] = ctx.message.created_at.timestamp()
edits["modified_at"] = ctx.message.created_at.timestamp()
await case.edit(edits)
except Exception as e:
@ -1110,7 +1131,16 @@ class Punish(commands.Cog):
updating_case = True
else:
case = await modlog.create_case(self.bot, guild, now_date, "Timed Mute", member, moderator=ctx.author, reason=reason, until=mod_until)
case = await modlog.create_case(
self.bot,
guild,
now_date,
"Timed Mute",
member,
moderator=ctx.author,
reason=reason,
until=mod_until,
)
case_number = case.case_number
except Exception as e:
@ -1118,18 +1148,18 @@ class Punish(commands.Cog):
else:
case_number = None
subject = 'the %s role' % role.name
subject = "the %s role" % role.name
if str(member.id) in punished:
if role in member.roles:
msg = '{0} already had the {1.name} role; resetting their timer.'
msg = "{0} already had the {1.name} role; resetting their timer."
else:
msg = '{0} is missing the {1.name} role for some reason. I added it and reset their timer.'
msg = "{0} is missing the {1.name} role for some reason. I added it and reset their timer."
elif role in member.roles:
msg = '{0} already had the {1.name} role, but had no timer; setting it now.'
msg = "{0} already had the {1.name} role, but had no timer; setting it now."
else:
msg = 'Applied the {1.name} role to {0}.'
subject = 'it'
msg = "Applied the {1.name} role to {0}."
subject = "it"
msg = msg.format(member, role)
@ -1137,24 +1167,24 @@ class Punish(commands.Cog):
timespec = generate_timespec(duration)
if using_default:
timespec += ' (the default)'
timespec += " (the default)"
msg += ' I will remove %s in %s.' % (subject, timespec)
msg += " I will remove %s in %s." % (subject, timespec)
if case_error:
if isinstance(case_error, CaseMessageNotFound):
case_error = 'the case message could not be found'
case_error = "the case message could not be found"
elif isinstance(case_error, NoModLogAccess):
case_error = 'I do not have access to the modlog channel'
case_error = "I do not have access to the modlog channel"
else:
case_error = None
if case_error:
verb = 'updating' if updating_case else 'creating'
msg += '\n\n' + warning('There was an error %s the modlog case: %s.' % (verb, case_error))
verb = "updating" if updating_case else "creating"
msg += "\n\n" + warning("There was an error %s the modlog case: %s." % (verb, case_error))
elif case_number:
verb = 'updated' if updating_case else 'created'
msg += ' I also %s case #%i in the modlog.' % (verb, case_number)
verb = "updated" if updating_case else "created"
msg += " I also %s case #%i in the modlog." % (verb, case_number)
voice_overwrite = await self.config.guild(guild).VOICE_OVERWRITE()
@ -1175,12 +1205,12 @@ class Punish(commands.Cog):
# build lists of roles that *should* be removed and ones that *can* be
removed_roles = user_roles & remove_role_set
too_high_to_remove = {r for r in removed_roles if r >= guild.me.top_role}
user_roles -= (removed_roles - too_high_to_remove)
user_roles -= removed_roles - too_high_to_remove
user_roles.add(role) # add punish role to the set
await member.edit(roles=user_roles, reason=f"punish {member}")
else:
removed_roles = set(resolve_role_list(guild, current.get('removed_roles', [])))
removed_roles = set(resolve_role_list(guild, current.get("removed_roles", [])))
too_high_to_remove = {r for r in removed_roles if r >= guild.me.top_role}
if removed_roles:
@ -1190,8 +1220,10 @@ class Punish(commands.Cog):
if too_high_to_remove:
fmt_list = format_list(*(r.name for r in removed_roles))
msg += "\n" + warning("These roles were too high to remove (fix hierarchy, then run "
"`{}punishset sync-roles`): {}".format(ctx.prefix, fmt_list))
msg += "\n" + warning(
"These roles were too high to remove (fix hierarchy, then run "
"`{}punishset sync-roles`): {}".format(ctx.prefix, fmt_list)
)
if member.voice:
muted = member.voice.mute
else:
@ -1199,13 +1231,13 @@ class Punish(commands.Cog):
async with self.config.guild(guild).PUNISHED() as punished:
punished[str(member.id)] = {
'start' : current.get('start') or now, # don't override start time if updating
'until' : until,
'by' : current.get('by') or ctx.author.id, # don't override original moderator
'reason' : reason,
'unmute' : overwrite_denies_speak and not muted,
'caseno' : case_number,
'removed_roles' : [r.id for r in removed_roles]
"start": current.get("start") or now, # don't override start time if updating
"until": until,
"by": current.get("by") or ctx.author.id, # don't override original moderator
"reason": reason,
"unmute": overwrite_denies_speak and not muted,
"caseno": case_number,
"removed_roles": [r.id for r in removed_roles],
}
if member.voice and overwrite_denies_speak:
@ -1256,8 +1288,8 @@ class Punish(commands.Cog):
if role:
data = await self.config.guild(guild).PUNISHED()
member_data = data.get(str(member.id), {})
caseno = member_data.get('caseno')
removed_roles = set(resolve_role_list(guild, member_data.get('removed_roles', [])))
caseno = member_data.get("caseno")
removed_roles = set(resolve_role_list(guild, member_data.get("removed_roles", [])))
# Has to be done first to prevent triggering listeners
await self._unpunish_data(member)
@ -1280,20 +1312,20 @@ class Punish(commands.Cog):
await member.edit(roles=user_roles, reason="punish end")
if update and caseno:
until = member_data.get('until') or False
until = member_data.get("until") or False
# fallback gracefully
moderator = moderator or guild.get_member(member_data.get('by')) or guild.me
moderator = moderator or guild.get_member(member_data.get("by")) or guild.me
if until:
until = datetime.utcfromtimestamp(until).timestamp()
edits = {'reason': reason}
edits = {"reason": reason}
if moderator.id != data.get('by'):
edits['amended_by'] = moderator
if moderator.id != data.get("by"):
edits["amended_by"] = moderator
edits['modified_at'] = time.time()
edits['until'] = until
edits["modified_at"] = time.time()
edits["until"] = until
try:
case = await modlog.get_case(caseno, guild, self.bot)
@ -1301,7 +1333,7 @@ class Punish(commands.Cog):
except Exception:
pass
if member_data.get('unmute', False):
if member_data.get("unmute", False):
if member.voice:
if member.voice.channel:
await member.edit(mute=False)
@ -1313,7 +1345,7 @@ class Punish(commands.Cog):
if quiet:
return True
msg = 'Your punishment in %s has ended.' % member.guild.name
msg = "Your punishment in %s has ended." % member.guild.name
if reason:
msg += "\nReason: %s" % reason
@ -1323,8 +1355,9 @@ class Punish(commands.Cog):
if too_high_to_restore:
fmt_list = format_list(*(r.name for r in too_high_to_restore))
msg += "\n" + warning("These roles were too high for me to restore: {}. "
"Ask a mod for help.".format(fmt_list))
msg += "\n" + warning(
"These roles were too high for me to restore: {}. " "Ask a mod for help.".format(fmt_list)
)
try:
await member.send(msg)
@ -1338,7 +1371,7 @@ class Punish(commands.Cog):
async with self.config.guild(guild).PUNISHED() as punished:
if str(member.id) in punished:
del(punished[str(member.id)])
del punished[str(member.id)]
# Listeners
@commands.Cog.listener()
@ -1365,14 +1398,14 @@ class Punish(commands.Cog):
new_roles = {role.id: role for role in after.roles}
if role in before.roles and role.id not in new_roles:
msg = 'Punishment manually ended early by a moderator/admin.'
msg = "Punishment manually ended early by a moderator/admin."
if member_data['reason']:
msg += '\nReason was: ' + member_data['reason']
if member_data["reason"]:
msg += "\nReason was: " + member_data["reason"]
await self._unpunish(after, reason=msg, update=True)
else:
to_remove = {new_roles.get(role_id) for role_id in member_data.get('removed_roles', [])}
to_remove = {new_roles.get(role_id) for role_id in member_data.get("removed_roles", [])}
to_remove = [r for r in to_remove if r and r < after.guild.me.top_role]
if to_remove:
@ -1393,7 +1426,7 @@ class Punish(commands.Cog):
member = self.bot.get_guild(guild.id).get_member(member.id)
role = await self.get_role(member.guild, quiet=True)
until = data['until']
until = data["until"]
duration = until - time.time()
if role and duration > 0:
@ -1433,7 +1466,7 @@ class Punish(commands.Cog):
msg = "Punishment ended early due to ban."
if member_data.get('reason'):
msg += '\n\nOriginal reason was: ' + member_data['reason']
if member_data.get("reason"):
msg += "\n\nOriginal reason was: " + member_data["reason"]
await self._unpunish(member, reason=msg, apply_roles=False, update=True, quiet=True)

View file

@ -2,13 +2,14 @@ import re
import discord
UNIT_TABLE = (
(('weeks', 'wks', 'w'), 60 * 60 * 24 * 7),
(('days', 'dys', 'd'), 60 * 60 * 24),
(('hours', 'hrs', 'h'), 60 * 60),
(('minutes', 'mins', 'm'), 60),
(('seconds', 'secs', 's'), 1),
(("weeks", "wks", "w"), 60 * 60 * 24 * 7),
(("days", "dys", "d"), 60 * 60 * 24),
(("hours", "hrs", "h"), 60 * 60),
(("minutes", "mins", "m"), 60),
(("seconds", "secs", "s"), 1),
)
class BadTimeExpr(Exception):
pass
@ -23,24 +24,23 @@ def _find_unit(unit):
def parse_time(time):
time = time.lower()
if not time.isdigit():
time = re.split(r'\s*([\d.]+\s*[^\d\s,;]*)(?:[,;\s]|and)*', time)
time = re.split(r"\s*([\d.]+\s*[^\d\s,;]*)(?:[,;\s]|and)*", time)
time = sum(map(_timespec_sec, filter(None, time)))
return int(time)
def _timespec_sec(expr):
atoms = re.split(r'([\d.]+)\s*([^\d\s]*)', expr)
atoms = re.split(r"([\d.]+)\s*([^\d\s]*)", expr)
atoms = list(filter(None, atoms))
if len(atoms) > 2: # This shouldn't ever happen
raise BadTimeExpr("invalid expression: '%s'" % expr)
elif len(atoms) == 2:
names, length = _find_unit(atoms[1])
if atoms[0].count('.') > 1 or \
not atoms[0].replace('.', '').isdigit():
if atoms[0].count(".") > 1 or not atoms[0].replace(".", "").isdigit():
raise BadTimeExpr("Not a number: '%s'" % atoms[0])
else:
names, length = _find_unit('seconds')
names, length = _find_unit("seconds")
try:
return float(atoms[0]) * length
@ -59,48 +59,46 @@ def generate_timespec(sec: int, short=False, micro=False) -> str:
if n:
if micro:
s = '%d%s' % (n, names[2])
s = "%d%s" % (n, names[2])
elif short:
s = '%d%s' % (n, names[1])
s = "%d%s" % (n, names[1])
else:
s = '%d %s' % (n, names[0])
s = "%d %s" % (n, names[0])
if n <= 1 and not (micro and names[2] == 's'):
s = s.rstrip('s')
if n <= 1 and not (micro and names[2] == "s"):
s = s.rstrip("s")
timespec.append(s)
if len(timespec) > 1:
if micro:
spec = ''.join(timespec)
spec = "".join(timespec)
segments = timespec[:-1], timespec[-1:]
spec = ' and '.join(', '.join(x) for x in segments)
spec = " and ".join(", ".join(x) for x in segments)
elif timespec:
spec = timespec[0]
else:
return '0'
return "0"
if neg:
spec += ' ago'
spec += " ago"
return spec
def format_list(*items, join='and', delim=', '):
def format_list(*items, join="and", delim=", "):
if len(items) > 1:
return (' %s ' % join).join((delim.join(items[:-1]), items[-1]))
return (" %s " % join).join((delim.join(items[:-1]), items[-1]))
elif items:
return items[0]
else:
return ''
return ""
def overwrite_to_dict(overwrite):
allow, deny = overwrite.pair()
return {
'allow' : allow.value,
'deny' : deny.value
}
return {"allow": allow.value, "deny": deny.value}
def format_permissions(permissions, include_null=False):
@ -116,10 +114,10 @@ def format_permissions(permissions, include_null=False):
else:
continue
entries.append(symbol + ' ' + perm.replace('_', ' ').title().replace("Tts", "TTS"))
entries.append(symbol + " " + perm.replace("_", " ").title().replace("Tts", "TTS"))
if entries:
return '\n'.join(entries)
return "\n".join(entries)
else:
return "No permission entries."
@ -130,7 +128,7 @@ def getmname(mid, guild):
if member:
return str(member)
else:
return '(absent user #%s)' % mid
return "(absent user #%s)" % mid
def role_from_string(guild, rolename, roles=None):
@ -186,7 +184,7 @@ def permissions_for_roles(channel, *roles):
if overwrite.id == default.id:
base.handle_overwrite(allow=overwrite.allow, deny=overwrite.deny)
if overwrite.type == 'role' and overwrite.id in role_ids:
if overwrite.type == "role" and overwrite.id in role_ids:
denies |= overwrite.deny
allows |= overwrite.allow
@ -218,6 +216,6 @@ def permissions_for_roles(channel, *roles):
def overwrite_from_dict(data):
allow = discord.Permissions(data.get('allow', 0))
deny = discord.Permissions(data.get('deny', 0))
allow = discord.Permissions(data.get("allow", 0))
deny = discord.Permissions(data.get("deny", 0))
return discord.PermissionOverwrite.from_pair(allow, deny)

View file

@ -14,6 +14,7 @@ import asyncio
import os
import json
class RolePlay(commands.Cog):
def __init__(self):
super().__init__()
@ -31,7 +32,8 @@ class RolePlay(commands.Cog):
"lotsa spaghetti",
"a brick",
"a slice of cheese",
"my foot"],
"my foot",
],
"high_iq_msgs": [
"wow!",
"that's pretty big.",
@ -40,7 +42,8 @@ class RolePlay(commands.Cog):
"someone here is actually smart.",
"thats a dab.",
"<:aureliawink:549481308519399425>",
"you must of watched Rick and Morty."],
"you must of watched Rick and Morty.",
],
"low_iq_msgs": [
":rofl:",
"oof.",
@ -49,7 +52,8 @@ class RolePlay(commands.Cog):
"awww you're special aren't you.",
":crying_cat_face:",
"god I'm sorry (not).",
"I didn't know people could have IQ that low."]
"I didn't know people could have IQ that low.",
],
}
self.config.register_guild(**self.default_guild)
@ -104,12 +108,11 @@ class RolePlay(commands.Cog):
elif user.id == botid:
user = ctx.message.author
botname = ctx.bot.user.name
await ctx.send("`-" + botname + " slaps " + user.display_name +
" multiple times with " +
(choice(slap_items) + "-`"))
await ctx.send(
"`-" + botname + " slaps " + user.display_name + " multiple times with " + (choice(slap_items) + "-`")
)
else:
await ctx.send("`-slaps " + user.display_name + " with " +
(choice(slap_items) + "-`"))
await ctx.send("`-slaps " + user.display_name + " with " + (choice(slap_items) + "-`"))
@slap.command(name="add")
@checks.admin()
@ -173,7 +176,6 @@ class RolePlay(commands.Cog):
except json.decoder.JSONDecodeError:
await ctx.send("Invalid or malformed json file.")
@commands.group(invoke_without_command=True)
async def iq(self, ctx, *users: discord.Member):
"""
@ -199,7 +201,9 @@ class RolePlay(commands.Cog):
iqs = sorted(iqs.items(), key=lambda x: x[1])
for user, iq in iqs:
msg += "{}'s iq is {}, {}\n".format(user.display_name, iq, choice(high_iq_msgs) if int(iq) > 130 else choice(low_iq_messages))
msg += "{}'s iq is {}, {}\n".format(
user.display_name, iq, choice(high_iq_msgs) if int(iq) > 130 else choice(low_iq_messages)
)
await ctx.send(msg)