Brandon209-Red-bot-Cogs/isolate/utils.py
2022-05-30 17:44:54 -04:00

220 lines
5.9 KiB
Python

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),
)
class BadTimeExpr(Exception):
pass
def _find_unit(unit):
for names, length in UNIT_TABLE:
if any(n.startswith(unit) for n in names):
return names, length
raise BadTimeExpr("Invalid unit: %s" % 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 = sum(map(_timespec_sec, filter(None, time)))
return int(time)
def _timespec_sec(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():
raise BadTimeExpr("Not a number: '%s'" % atoms[0])
else:
names, length = _find_unit("seconds")
try:
return float(atoms[0]) * length
except ValueError:
raise BadTimeExpr("invalid value: '%s'" % atoms[0])
def generate_timespec(sec: int, short=False, micro=False) -> str:
timespec = []
sec = int(sec)
neg = sec < 0
sec = abs(sec)
for names, length in UNIT_TABLE:
n, sec = divmod(sec, length)
if n:
if micro:
s = "%d%s" % (n, names[2])
elif short:
s = "%d%s" % (n, names[1])
else:
s = "%d %s" % (n, names[0])
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)
segments = timespec[:-1], timespec[-1:]
spec = " and ".join(", ".join(x) for x in segments)
elif timespec:
spec = timespec[0]
else:
return "0"
if neg:
spec += " ago"
return spec
def format_list(*items, join="and", delim=", "):
if len(items) > 1:
return (" %s " % join).join((delim.join(items[:-1]), items[-1]))
elif items:
return items[0]
else:
return ""
def overwrite_to_dict(overwrite):
allow, deny = overwrite.pair()
return {"allow": allow.value, "deny": deny.value}
def format_permissions(permissions, include_null=False):
entries = []
for perm, value in sorted(permissions, key=lambda t: t[0]):
if value is True:
symbol = "\N{WHITE HEAVY CHECK MARK}"
elif value is False:
symbol = "\N{NO ENTRY SIGN}"
elif include_null:
symbol = "\N{RADIO BUTTON}"
else:
continue
entries.append(symbol + " " + perm.replace("_", " ").title().replace("Tts", "TTS"))
if entries:
return "\n".join(entries)
else:
return "No permission entries."
def getmname(mid, guild):
member = discord.utils.get(guild.members, id=int(mid))
if member:
return str(member)
else:
return "(absent user #%s)" % mid
def role_from_string(guild, rolename, roles=None):
if rolename is None:
return None
if roles is None:
roles = guild.roles
else:
roles = [r for r in roles if isinstance(r, discord.Role)]
if type(rolename) == int:
role = discord.utils.get(roles, id=rolename)
if role:
return role
else:
rolename = rolename.lower()
role = discord.utils.find(lambda r: r.name.lower() == rolename, roles)
return role
def resolve_role_list(guild: discord.guild, roles: list) -> list:
gen = (role_from_string(guild, name) for name in roles)
return list(filter(None, gen))
def permissions_for_roles(channel, *roles):
"""
Calculates the effective permissions for a role or combination of roles.
Naturally, if no roles are given, the default role's permissions are used
"""
default = channel.guild.default_role
base = discord.Permissions(default.permissions.value)
# Apply all role values
for role in roles:
base.value |= role.permissions.value
# guild-wide Administrator -> True for everything
# Bypass all channel-specific overrides
if base.administrator:
return discord.Permissions.all()
role_ids = set(map(lambda r: r.id, roles))
denies = 0
allows = 0
# Apply channel specific role permission overwrites
for target, overwrite in channel.overwrites.items():
# Handle default role first, if present
allow, deny = overwrite.pair()
allow, deny = allow.value, deny.value
if overwrite == default:
base.handle_overwrite(allow=allow, deny=deny)
if isinstance(target, discord.Role) and target.id in role_ids:
denies |= deny
allows |= allow
base.handle_overwrite(allow=allows, deny=denies)
# if you can't send a message in a channel then you can't have certain
# permissions as well
if not base.send_messages:
base.send_tts_messages = False
base.mention_everyone = False
base.embed_links = False
base.attach_files = False
# if you can't read a channel then you have no permissions there
if not base.read_messages:
denied = discord.Permissions.all_channel()
base.value &= ~denied.value
# text channels do not have voice related permissions
if channel.type is discord.ChannelType.text:
denied = discord.Permissions.voice()
base.value &= ~denied.value
return base
def overwrite_from_dict(data):
allow = discord.Permissions(data.get("allow", 0))
deny = discord.Permissions(data.get("deny", 0))
return discord.PermissionOverwrite.from_pair(allow, deny)