big 3.4 update part 1, still need to add data removal handling

This commit is contained in:
brandons209 2020-10-29 04:36:43 -04:00
parent 9146ce4efa
commit e9f7b86be9
48 changed files with 370 additions and 202 deletions

View file

@ -1,11 +1,12 @@
{
"author": ["Malarne (Malarne#1418)"],
"min_bot_version": "3.0.2",
"min_bot_version": "3.4.0",
"description": "A leveler cog for Red V3\nInspired by Stevy's v2 leveler cog\nPlease consult the docs at ayrobot.netlify.com for setup informations.\nThanks for using my cog !\nNB:Channel whitelist is enabled by default, you can manage that under `[p]levelerset channel whitelist` command !",
"hidden": false,
"install_msg": "Thank you for installing this leveler !\nPlease consult the docs at https://discord-cogs.readthedocs.io/en/latest/leveler.html for setup informations.\nPlease note that this cog come with bundled data, mostly the font for profile image.",
"required_cogs": {},
"requirements": ["pillow"],
"short": "Leveler tool, better than MEE6",
"tags": ["leveler", "pillow", "fun"]
"tags": ["leveler", "pillow", "fun"],
"end_user_data_statement": "Stores some level info like experience, profile description/picture, and message ID of user's last message in guild."
}

View file

@ -287,6 +287,8 @@ class Leveler(commands.Cog):
await ctx.send(file=discord.File(img))
async def listener(self, message):
if await self.bot.cog_disabled_in_guild(self, message.guild):
return
if type(message.author) != discord.Member:
# throws an error when webhooks talk, this fixes it
return
@ -330,7 +332,10 @@ class Leveler(commands.Cog):
await self.profiles._set_user_lastmessage(message.author, timenow)
lvl = await self.profiles._get_level(message.author)
if lvl == oldlvl + 1 and await self.profiles.data.guild(message.guild).lvlup_announce():
await message.channel.send(_("{} is now level {} !".format(message.author.mention, lvl)))
await message.channel.send(
_("{} is now level {} !".format(message.author.mention, lvl)),
allowed_mentions=discord.AllowedMentions.all(),
)
await self.profiles._check_exp(message.author)
await self.profiles._check_role_member(message.author)
@ -644,5 +649,7 @@ class Leveler(commands.Cog):
# Listeners
@commands.Cog.listener()
async def on_member_remove(self, member):
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
# reset level stats on leave.
await self.profiles.data.member(member).clear()

View file

@ -5,13 +5,14 @@ from redbot.core.data_manager import cog_data_path
from redbot.core.utils.mod import is_mod_or_superior
import discord
from .time_utils import *
from .utils import *
from datetime import datetime
from dateutil.relativedelta import relativedelta
import time
import os
import asyncio
import glob
import io
# plotting
from bisect import bisect_left
@ -46,66 +47,6 @@ 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:
if not os.path.exists(log):
continue
with open(log, "r") as f:
for line in f:
if str(user.id) in line:
# user change their name
if "Member username:" in line:
username = line.strip().split('"')
discrim = username[1].split("#")[-1]
username = username[-2] + "#" + discrim
names.append(username)
# user changed their 4 digit discriminator
elif "Member discriminator:" in line:
username = line.strip().split('"')[-2]
names.append(username)
# starting name
elif "Member join:" in line:
username = line.strip().split("@")[-1].split("#")
username = username[0] + "#" + username[1].split(" ")[0]
if username is not str(user):
names.append(username)
return set(names)
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 ""
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")
self.lock = asyncio.Lock()
if time:
self.time = time
else:
self.time = datetime.fromtimestamp(os.path.getmtime(path))
async def write(self, value):
async with self.lock:
self._write(value)
def close(self):
self.handle.close()
def _write(self, value):
self.time = datetime.utcnow()
self.handle.write(value)
class ActivityLogger(commands.Cog):
"""Log activity seen by bot"""
@ -259,7 +200,7 @@ class ActivityLogger(commands.Cog):
if is_mod:
try:
await ctx.send(embed=data)
await ctx.send(embed=data, allowed_mentions=discord.AllowedMentions.all())
except discord.HTTPException:
await ctx.send("I need the `Embed links` permission to send this")
else:
@ -317,19 +258,10 @@ class ActivityLogger(commands.Cog):
)
msg += f"Bans: `{bans}`, Kicks: `{kicks}`, Mutes: `{mutes}`, Warnings: `{warns}`"
if len(names) > 1:
return msg, format_list(*names)
return msg, humanize_list(names)
return msg, None
@commands.command(name="fixavglen")
@checks.is_owner()
async def fixavglen(self, ctx):
for guild in self.bot.guilds:
for member in guild.members:
async with self.config.member(member).stats() as stats:
stats["avg_len"] = stats["total_msg"] - stats["bot_cmd"]
await ctx.tick()
@commands.command(name="graphstats")
@checks.mod()
@commands.guild_only()
@ -1141,14 +1073,14 @@ 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: " + humanize_list(curr))
return
prefixes = [p for p in prefixes.split(" ")]
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: " + humanize_list(prefixes))
@logset.command(name="rotation")
async def set_rotation(self, ctx, freq: str = None):
@ -1434,16 +1366,22 @@ class ActivityLogger(commands.Cog):
# Listeners
@commands.Cog.listener()
async def on_message(self, message):
if await self.bot.cog_disabled_in_guild(self, message.guild):
return
await self.message_handler(message)
@commands.Cog.listener()
async def on_message_edit(self, before, after):
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
timestamp = before.created_at.strftime(TIMESTAMP_FORMAT)
entry = EDIT_TEMPLATE.format(before, after, timestamp)
await self.log(after.channel, entry, after.edited_at)
@commands.Cog.listener()
async def on_message_delete(self, message):
if await self.bot.cog_disabled_in_guild(self, message.guild):
return
entry_s = None
timestamp = message.created_at.strftime(TIMESTAMP_FORMAT)
try:
@ -1468,16 +1406,22 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_join(self, guild):
if await self.bot.cog_disabled_in_guild(self, guild):
return
entry = "this bot joined the guild"
await self.log(guild, entry)
@commands.Cog.listener()
async def on_guild_remove(self, guild):
if await self.bot.cog_disabled_in_guild(self, guild):
return
entry = "this bot left the guild"
await self.log(guild, entry)
@commands.Cog.listener()
async def on_guild_update(self, before, after):
if await self.bot.cog_disabled_in_guild(self, after):
return
entries = []
user = None
try:
@ -1535,6 +1479,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_role_create(self, role):
if await self.bot.cog_disabled_in_guild(self, role.guild):
return
user = None
try:
async for entry in role.guild.audit_logs(limit=2):
@ -1553,6 +1499,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_role_delete(self, role):
if await self.bot.cog_disabled_in_guild(self, role.guild):
return
user = None
try:
async for entry in role.guild.audit_logs(limit=2):
@ -1571,6 +1519,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_role_update(self, before, after):
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
entries = []
user = None
try:
@ -1655,6 +1605,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_member_join(self, member):
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
entry = "Member join: @{0} (id {0.id})".format(member)
async with self.config.user(member).past_names() as past_names:
@ -1665,6 +1617,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_member_remove(self, member):
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
user = None
try:
async for entry in member.guild.audit_logs(limit=2):
@ -1688,6 +1642,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_member_ban(self, guild, member):
if await self.bot.cog_disabled_in_guild(self, guild):
return
user = None
try:
async for entry in guild.audit_logs(limit=2):
@ -1706,6 +1662,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_member_unban(self, guild, member):
if await self.bot.cog_disabled_in_guild(self, guild):
return
user = None
try:
async for entry in guild.audit_logs(limit=2):
@ -1724,6 +1682,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_member_update(self, before, after):
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
entries = []
user = None
try:
@ -1799,6 +1759,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_channel_create(self, channel):
if await self.bot.cog_disabled_in_guild(self, channel.guild):
return
user = None
try:
async for entry in channel.guild.audit_logs(limit=2):
@ -1819,6 +1781,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_channel_delete(self, channel):
if await self.bot.cog_disabled_in_guild(self, channel.guild):
return
user = None
try:
async for entry in channel.guild.audit_logs(limit=2):
@ -1839,6 +1803,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_guild_channel_update(self, before, after):
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
user = None
try:
async for entry in after.guild.audit_logs(limit=2):
@ -1900,6 +1866,8 @@ class ActivityLogger(commands.Cog):
@commands.Cog.listener()
async def on_voice_state_update(self, member, before, after):
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
if not self.should_log(before.channel):
return
@ -1958,3 +1926,19 @@ class ActivityLogger(commands.Cog):
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))
# async def red_get_data_for_user(self, user_id: int):
# 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}
# }
# past_names = await self.config.user_from_id(user_id).past_names()
# data = {"past_names": past_names}
# for guild in self.bot.guilds:
# member = guild.get_member(user_id)
# if member:
# data[guild.name] = await self.config.member(member).stats()
async def red_delete_data_for_user(self, requester: str, user_id: int):
pass

View file

@ -3,11 +3,6 @@
"Brandons209",
"calebj"
],
"bot_version": [
3,
0,
0
],
"description": "Log messages, dms, attachments, guild updates (roles, channels, bans, kicks etc) and be able to retrieve these logs for review. Saves these right to disk and customize then to rotate log files. Tracks user's stats; how many messages they send, how many are bot commands, time spent in VC, and moderator actions against them. Also provides an upgraded userinfo command that gives these stats alongside the information Red's userinfo command gives. This is rewritten from @calebj's V2 cog.",
"hidden": false,
"install_msg": "Thank you for using this cog! Make sure to set bot prefixes using [p]logset prefixes for proper stat logging.",
@ -19,5 +14,6 @@
"log",
"activity"
],
"min_bot_version": "3.3.0"
"min_bot_version": "3.4.0",
"end_user_data_statement": "Depending on setup, can log user messages, voice channel activity, audit actions in guilds, activity statistics per guild, user name changes, and any moderation actions per guild."
}

View file

@ -1,59 +0,0 @@
# thanks to @Sinbad for time parsing!
from __future__ import annotations
import re
from datetime import datetime as dt
from typing import Optional
import pytz
from dateutil import parser
from dateutil.tz import gettz
from dateutil.relativedelta import relativedelta
TIME_RE_STRING = r"\s?".join(
[
r"((?P<years>\d+?)\s?(years?|y))?",
r"((?P<months>\d+?)\s?(months?|mt))?",
r"((?P<weeks>\d+?)\s?(weeks?|w))?",
r"((?P<days>\d+?)\s?(days?|d))?",
r"((?P<hours>\d+?)\s?(hours?|hrs|hr?))?",
r"((?P<minutes>\d+?)\s?(minutes?|mins?|m(?!o)))?", # prevent matching "months"
r"((?P<seconds>\d+?)\s?(seconds?|secs?|s))?",
]
)
TIME_RE = re.compile(TIME_RE_STRING, re.I)
def gen_tzinfos():
for zone in pytz.common_timezones:
try:
tzdate = pytz.timezone(zone).localize(dt.utcnow(), is_dst=None)
except pytz.NonExistentTimeError:
pass
else:
tzinfo = gettz(zone)
if tzinfo:
yield tzdate.tzname(), tzinfo
def parse_time(datetimestring: str):
tzinfo = dict(gen_tzinfos())
ret = parser.parse(datetimestring, tzinfos=tzinfo)
if ret.tzinfo is not None:
ret = ret.astimezone(pytz.utc)
return ret
def parse_time_naive(datetimestring: str):
return parser.parse(datetimestring)
def parse_timedelta(argument: str) -> Optional[relativedelta]:
matches = TIME_RE.match(argument)
if matches:
params = {k: int(v) for k, v in matches.groupdict().items() if v}
if params:
return relativedelta(**params)
return None

112
activitylog/utils.py Normal file
View file

@ -0,0 +1,112 @@
# thanks to @Sinbad for time parsing!
from __future__ import annotations
import re
from datetime import datetime as dt
from typing import Optional
import os
import asyncio
import pytz
from dateutil import parser
from dateutil.tz import gettz
from dateutil.relativedelta import relativedelta
TIME_RE_STRING = r"\s?".join(
[
r"((?P<years>\d+?)\s?(years?|y))?",
r"((?P<months>\d+?)\s?(months?|mt))?",
r"((?P<weeks>\d+?)\s?(weeks?|w))?",
r"((?P<days>\d+?)\s?(days?|d))?",
r"((?P<hours>\d+?)\s?(hours?|hrs|hr?))?",
r"((?P<minutes>\d+?)\s?(minutes?|mins?|m(?!o)))?", # prevent matching "months"
r"((?P<seconds>\d+?)\s?(seconds?|secs?|s))?",
]
)
TIME_RE = re.compile(TIME_RE_STRING, re.I)
def gen_tzinfos():
for zone in pytz.common_timezones:
try:
tzdate = pytz.timezone(zone).localize(dt.utcnow(), is_dst=None)
except pytz.NonExistentTimeError:
pass
else:
tzinfo = gettz(zone)
if tzinfo:
yield tzdate.tzname(), tzinfo
def parse_time(datetimestring: str):
tzinfo = dict(gen_tzinfos())
ret = parser.parse(datetimestring, tzinfos=tzinfo)
if ret.tzinfo is not None:
ret = ret.astimezone(pytz.utc)
return ret
def parse_time_naive(datetimestring: str):
return parser.parse(datetimestring)
def parse_timedelta(argument: str) -> Optional[relativedelta]:
matches = TIME_RE.match(argument)
if matches:
params = {k: int(v) for k, v in matches.groupdict().items() if v}
if params:
return relativedelta(**params)
return None
def get_all_names(guild_files, user):
names = [str(user)]
for log in guild_files:
if not os.path.exists(log):
continue
with open(log, "r") as f:
for line in f:
if str(user.id) in line:
# user change their name
if "Member username:" in line:
username = line.strip().split('"')
discrim = username[1].split("#")[-1]
username = username[-2] + "#" + discrim
names.append(username)
# user changed their 4 digit discriminator
elif "Member discriminator:" in line:
username = line.strip().split('"')[-2]
names.append(username)
# starting name
elif "Member join:" in line:
username = line.strip().split("@")[-1].split("#")
username = username[0] + "#" + username[1].split(" ")[0]
if username is not str(user):
names.append(username)
return set(names)
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")
self.lock = asyncio.Lock()
if time:
self.time = time
else:
self.time = dt.fromtimestamp(os.path.getmtime(path))
async def write(self, value):
async with self.lock:
self._write(value)
def close(self):
self.handle.close()
def _write(self, value):
self.time = dt.utcnow()
self.handle.write(value)

View file

@ -63,6 +63,8 @@ class Birthday(commands.Cog):
async def check_bdays(self):
for guild in self.bot.guilds:
if await self.bot.cog_disabled_in_guild(self, guild):
continue
for member in guild.members:
await self.check_member_bday(member)
@ -97,7 +99,7 @@ class Birthday(commands.Cog):
else:
embed.description = f"Happy Birthday to {member.mention}!"
try:
await channel.send(embed=embed)
await channel.send(embed=embed, allow_mentions=True)
except:
pass

View file

@ -6,7 +6,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "Cog used for setting birthdays and announcing them and giving people roles on their birthday",
@ -17,5 +17,6 @@
"tags": [
"brandons209",
"pancakesparkle"
]
],
"end_user_data_statement": "This cog will store a user's birthday."
}

View file

@ -1,9 +1,10 @@
{
"author" : ["Brandons209", "Jinatku"],
"bot_version": [3,0,0],
"bot_version": [3,4,0],
"install_msg" : "Let your users confess their terrible terrible crimes in secrecy!",
"short" : "Confess secretly.",
"description" : "Confess in a confession room to repent your crimes.",
"tags" : ["confession", "crime", "secret"],
"hidden" : false
"hidden" : false,
"end_user_data_statement": "This cog won't store anything for a user."
}

View file

@ -4,7 +4,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "Allow setting up costs for any commands of your bot, with user, and role hierarchy. Also allows setting roles who do not have to pay for commands. Has receptions DMed and updated to allow users to track their spending.",
@ -15,5 +15,6 @@
"tags": [
"brandons209",
"redbot"
]
],
"end_user_data_statement": "This cog won't store anything for a user."
}

View file

@ -126,7 +126,8 @@ class CostManager(commands.Cog):
except:
if receipt != -1: # send mention if first time getting this message
await ctx.send(
f"Hey {member.mention}, please turn on DMs from server members in your settings so I can send you receipts for purchase of commands."
f"Hey {member.mention}, please turn on DMs from server members in your settings so I can send you receipts for purchase of commands.",
allowed_mentions=discord.AllowedMentions.all(),
)
await self.config.member(member).receipt.set(-1)
@ -400,7 +401,8 @@ class CostManager(commands.Cog):
currency_name = await bank.get_currency_name(ctx.guild)
balance = await bank.get_balance(ctx.author)
message = await ctx.send(
f"Sorry {ctx.author.name}, you do not have enough {currency_name} to use that command. (Cost: {cost}, Balance: {balance})"
f"Sorry {ctx.author.name}, you do not have enough {currency_name} to use that command. (Cost: {cost}, Balance: {balance})",
allowed_mentions=discord.AllowedMentions.all(),
)
await asyncio.sleep(10)
await message.delete()

View file

@ -70,6 +70,8 @@ class Disable(commands.Cog):
@commands.Cog.listener()
async def on_command_error(self, ctx, exception):
if await self.bot.cog_disabled_in_guild(self, ctx.guild):
return
if isinstance(exception, DisabledError):
msg = await self.config.guild(ctx.guild).disabled_message()
await ctx.send(msg.format(ctx.command.name))

View file

@ -4,7 +4,7 @@
],
"bot_version": [
3,
3,
4,
0
],
"description": "Small cog that allows disabling all bot commands for everyone except Admins/owner in a guild. Nice for things like bot setup. You can also set the error message when a person trys using a command when its disabled.",
@ -15,5 +15,6 @@
"tags": [
"brandons209",
"redbot"
]
],
"end_user_data_statement": "This cog won't store any data for users."
}

View file

@ -49,6 +49,8 @@ class EconomyTrickle(commands.Cog):
@commands.Cog.listener()
async def on_message(self, message):
if await self.bot.cog_disabled_in_guild(self, message.guild):
return
if message.guild and await self.config.guild(message.guild).active():
self.recordhandler.proccess_message(message)

View file

@ -10,5 +10,6 @@
"short": "Trickle money into user's account on activity.",
"description": "Trickle money into user's account on activity, modified by Brandons209 from Sinbad.",
"hidden": false,
"min_bot_version": "3.1.8"
"min_bot_version": "3.4.0",
"end_user_data_statement": "This cog only stores internal variables per user."
}

View file

@ -6,5 +6,7 @@
"INSTALL_MSG" : "Thanks for installing EveryoneEmoji.",
"REQUIREMENTS" : [],
"TAGS" : ["emoji", "EveryoneEmoji", "everyone"],
"DISABLED" : false
"DISABLED" : false,
"min_bot_version": "3.4.0",
"end_user_data_statement": "This cog stores no data."
}

View file

@ -5,7 +5,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "Provides a way to quickly remove a user for all chats and isolate them to a single channel. Supports setting times, automatic removal of isolate, modlog integration, custom role overrides, automatic server setup, and a lot more. This is different from punish as it always remove all a user's roles and by default makes the user not read all channels.",
@ -16,5 +16,6 @@
"tags": [
"brandons209",
"isolate"
]
],
"end_user_data_statement": "This cog stores members who are currently isolated for moderation purposes."
}

View file

@ -1306,6 +1306,8 @@ class Isolate(commands.Cog):
@commands.Cog.listener()
async def on_guild_channel_create(self, channel):
"""Run when new channels are created and set up role permissions"""
if await self.bot.cog_disabled_in_guild(self, channel.guild):
return
role = await self.get_role(channel.guild, quiet=True)
if not role:
return
@ -1315,6 +1317,8 @@ class Isolate(commands.Cog):
@commands.Cog.listener()
async def on_member_update(self, before, after):
"""Remove scheduled unisolate when manually removed role"""
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
try:
assert before.roles != after.roles
guild_data = await self.config.guild(before.guild).ISOLATED()
@ -1343,6 +1347,8 @@ class Isolate(commands.Cog):
@commands.Cog.listener()
async def on_member_join(self, member):
"""Restore Isolation if isolated user leaves/rejoins"""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
guild = member.guild
isolated = await self.config.guild(guild).ISOLATED()
data = isolated.get(str(member.id), {})
@ -1366,6 +1372,8 @@ class Isolate(commands.Cog):
@commands.Cog.listener()
async def on_voice_state_update(self, member, before, after):
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
if not after.channel:
return
@ -1386,6 +1394,8 @@ class Isolate(commands.Cog):
@commands.Cog.listener()
async def on_member_ban(self, member):
"""Remove Isolation record when member is banned."""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
guild = member.guild
data = await self.config.guild(guild).ISOLATED()
member_data = data.get(str(member.id))

View file

@ -145,9 +145,9 @@ def role_from_string(guild, rolename, roles=None):
if role:
return role
rolename = rolename.lower()
role = discord.utils.find(lambda r: r.name.lower() == rolename, roles)
else:
rolename = rolename.lower()
role = discord.utils.find(lambda r: r.name.lower() == rolename, roles)
return role

View file

@ -4,7 +4,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "Uses markov chains to generate unique text in your guild! Text generated is based on what users say per channel. The markov model can be reset if needed as well per channel.",
@ -18,5 +18,6 @@
"markov",
"generaton",
"markov chain"
]
],
"end_user_data_statement": "This doesn't store any user data."
}

View file

@ -159,11 +159,13 @@ class Markov(commands.Cog):
embed.set_thumbnail(url=avatar)
embed.set_footer(text=f"Generated by {member.display_name}")
await ctx.send(embed=embed)
await ctx.send(embed=embed, allowed_mentions=discord.AllowedMentions.all())
# Listener
@commands.Cog.listener()
async def on_message(self, message):
if await self.bot.cog_disabled_in_guild(self, message.guild):
return
# updates model
content = message.content
guild = message.guild

View file

@ -4,7 +4,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "More admin commands like purging inactive users, member count display for channel name, add roles based on author's current roles, log new user accounts joining, and more!",
@ -15,5 +15,6 @@
"tags": [
"brandons209",
"redbot"
]
],
"end_user_data_statement": "This will store a user's last few messages (depending on configuration)"
}

View file

@ -170,6 +170,8 @@ class MoreAdmin(commands.Cog):
SLEEP_TIME = 300
while True:
for guild in self.bot.guilds:
if await self.bot.cog_disabled_in_guild(self, guild):
continue
channel = await self.config.guild(guild).user_count_channel()
if channel:
channel = guild.get_channel(channel)
@ -799,13 +801,13 @@ class MoreAdmin(commands.Cog):
@commands.command(hidden=True)
@commands.guild_only()
async def say(self, ctx, *, content: str):
await ctx.send(escape(content, mass_mentions=True), allowed_mentions=discord.AllowedMentions())
await ctx.send(escape(content, mass_mentions=True), allowed_mentions=discord.AllowedMentions.all())
@commands.command(hidden=True)
@commands.guild_only()
async def selfdm(self, ctx, *, content: str):
try:
await ctx.author.send(content)
await ctx.author.send(content, allowed_mentions=discord.AllowedMentions.all())
except:
await ctx.send(
"I couldn't send you the DM, make sure to turn on messages from server members! Here is the message:"
@ -827,7 +829,7 @@ class MoreAdmin(commands.Cog):
return
try:
await message.edit(content=msg)
await message.edit(content=msg, allowed_mentions=discord.AllowedMentions.all())
except:
await ctx.send("Could not edit message.")
@ -839,7 +841,7 @@ class MoreAdmin(commands.Cog):
Sends a message to a channel from Aurelia.
"""
try:
await channel.send(msg) # , allowed_mentions=discord.AllowedMentions())
await channel.send(msg, allowed_mentions=discord.AllowedMentions.all())
except:
await ctx.send("Could not send message in that channel.")
@ -974,6 +976,10 @@ class MoreAdmin(commands.Cog):
@commands.Cog.listener()
async def on_member_join(self, member):
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
sus_threshold = await self.config.guild(member.guild).sus_user_threshold()
if not sus_threshold:
return
@ -1003,6 +1009,8 @@ class MoreAdmin(commands.Cog):
@commands.Cog.listener()
async def on_message(self, message):
if await self.bot.cog_disabled_in_guild(self, message.guild):
return
# Set user's last message
if not message.guild:
return

View file

@ -4,7 +4,7 @@
],
"bot_version": [
3,
3,
4,
0
],
"description": "Allow nitro boosters to add an emoji to the server, with channel logging.",
@ -17,5 +17,6 @@
"nitro",
"boost",
"emoji"
]
],
"end_user_data_statement": "This cog will store a user's custom emojis in each guild."
}

View file

@ -285,6 +285,8 @@ class NitroEmoji(commands.Cog):
@commands.Cog.listener()
async def on_member_update(self, before, after):
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
# check if they stopped boosting
if (before.premium_since != after.premium_since and after.premium_since is None) or before.roles != after.roles:
boosts = await self.get_boosts(after)
@ -312,6 +314,8 @@ class NitroEmoji(commands.Cog):
@commands.Cog.listener()
async def on_member_leave(self, member):
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
# remove all member emojis on leave
emojis = await self.config.member(member).emojis()
for emoji in emojis:
@ -324,6 +328,8 @@ class NitroEmoji(commands.Cog):
@commands.Cog.listener()
async def on_guild_emojis_update(self, guild, before, after):
if await self.bot.cog_disabled_in_guild(self, guild):
return
b_e = set(before)
a_e = set(after)
diff = b_e - a_e

View file

@ -14,5 +14,7 @@
],
"requirements": [
"tabulate"
]
],
"min_bot_version": "3.4.0",
"end_user_data_statement": "This will store what a user's custom role is if they have one."
}

View file

@ -370,6 +370,8 @@ class PersonalRoles(commands.Cog):
@commands.Cog.listener("on_member_join")
async def role_persistance(self, member):
"""Automatically give already assigned roles on join"""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
role = await self.config.member(member).role()
if role:
role = member.guild.get_role(role)
@ -382,6 +384,8 @@ class PersonalRoles(commands.Cog):
@commands.Cog.listener("on_member_remove")
async def remove_role(self, member):
""" Delete personal role if member leaves."""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
role = await self.config.member(member).role()
auto = await self.config.member(member).auto_role()
role = member.guild.get_role(role)
@ -396,6 +400,8 @@ class PersonalRoles(commands.Cog):
@commands.Cog.listener("on_member_update")
async def modify_roles(self, before, after):
""" Delete personal role if member looses their auto role or looses their personal role """
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
role = await self.config.member(after).role()
role = before.guild.get_role(role)
if not role:

View file

@ -6,7 +6,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "Search derpibooru for images of ponies! Fully customizable filter list!",
@ -20,5 +20,6 @@
"pony",
"mlp",
"image"
]
],
"end_user_data_statement": "This doesn't store any data."
}

View file

@ -5,7 +5,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "Provides a way to quickly remove a user for all chats and isolate them to a single channel. Supports setting times, automatic removal of punish, modlog integration, removing roles so that role overrides work, custom role overrides, automatic server setup, and a lot more.",
@ -16,5 +16,6 @@
"tags": [
"brandons209",
"punish"
]
],
"end_user_data_statement": "This will store who is currently punished."
}

View file

@ -18,7 +18,7 @@ import textwrap
log = logging.getLogger("red.punish")
__version__ = "3.0.0"
__version__ = "3.3.0"
PURGE_MESSAGES = 1 # for cpunish
@ -1373,6 +1373,8 @@ class Punish(commands.Cog):
# Listeners
@commands.Cog.listener()
async def on_guild_channel_create(self, channel):
if await self.bot.cog_disabled_in_guild(self, channel.guild):
return
"""Run when new channels are created and set up role permissions"""
role = await self.get_role(channel.guild, quiet=True)
if not role:
@ -1383,6 +1385,8 @@ class Punish(commands.Cog):
@commands.Cog.listener()
async def on_member_update(self, before, after):
"""Remove scheduled unpunish when manually removed role"""
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
try:
assert before.roles != after.roles
guild_data = await self.config.guild(before.guild).PUNISHED()
@ -1411,6 +1415,8 @@ class Punish(commands.Cog):
@commands.Cog.listener()
async def on_member_join(self, member):
"""Restore punishment if punished user leaves/rejoins"""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
guild = member.guild
punished = await self.config.guild(guild).PUNISHED()
data = punished.get(str(member.id), {})
@ -1434,6 +1440,8 @@ class Punish(commands.Cog):
@commands.Cog.listener()
async def on_voice_state_update(self, member, before, after):
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
if not after.channel:
return
@ -1454,6 +1462,8 @@ class Punish(commands.Cog):
@commands.Cog.listener()
async def on_member_ban(self, member):
"""Remove punishment record when member is banned."""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
guild = member.guild
data = await self.config.guild(guild).PUNISHED()
member_data = data.get(str(member.id))

View file

@ -145,9 +145,9 @@ def role_from_string(guild, rolename, roles=None):
if role:
return role
rolename = rolename.lower()
role = discord.utils.find(lambda r: r.name.lower() == rolename, roles)
else:
rolename = rolename.lower()
role = discord.utils.find(lambda r: r.name.lower() == rolename, roles)
return role

View file

@ -6,5 +6,7 @@
"INSTALL_MSG" : "Thanks for installing ReactPoll. Please make sure your bot has the 'Manage Messages' permission so it can manage emoji reactions on messages.",
"REQUIREMENTS" : ["python-dateutil", "pytz"],
"TAGS" : ["poll", "reaction", "emoji", "react"],
"DISABLED" : false
"DISABLED" : false,
"min_bot_version": "3.4.0",
"end_user_data_statement": "This cog won't store user data."
}

View file

@ -135,6 +135,10 @@ class ReactPoll(commands.Cog):
guild = self.bot.get_guild(payload.guild_id)
if not guild:
return
if await self.bot.cog_disabled_in_guild(self, guild):
return
user = guild.get_member(payload.user_id)
message = await self.bot.get_channel(payload.channel_id).fetch_message(payload.message_id)
# Listener is required to remove bad reactions

View file

@ -43,6 +43,8 @@ class EventMixin(MixinMeta):
Section has been optimized assuming member._roles
remains an iterable containing snowflakes
"""
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
await self.wait_for_ready()
if before._roles == after._roles:
return
@ -85,6 +87,8 @@ class EventMixin(MixinMeta):
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
await self.wait_for_ready()
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
guild = member.guild
if not guild.me.guild_permissions.manage_roles:
return
@ -134,6 +138,9 @@ class EventMixin(MixinMeta):
await self.maybe_update_guilds(guild)
else:
return
if await self.bot.cog_disabled_in_guild(self, guild):
return
member = guild.get_member(payload.user_id)
if member is None or member.bot:
@ -182,6 +189,8 @@ class EventMixin(MixinMeta):
if not guild:
# Where's it go?
return
if await self.bot.cog_disabled_in_guild(self, guild):
return
member = guild.get_member(payload.user_id)
if not member or member.bot:
return

View file

@ -17,5 +17,6 @@
"massrole"
],
"hidden": false,
"min_bot_version": "3.2.0a0.dev1"
"min_bot_version": "3.4.0",
"end_user_data_statement": "This will only store sticky roles for users."
}

View file

@ -206,7 +206,11 @@ class MassManagementMixin(MixinMeta):
embed = discord.Embed(description=description)
if ctx.guild:
embed.color = ctx.guild.me.color
await ctx.send(embed=embed, content=f"Search results for {ctx.author.mention}")
await ctx.send(
embed=embed,
content=f"Search results for {ctx.author.mention}",
allowed_mentions=discord.AllowedMentions.all(),
)
else:
await self.send_maybe_chunked_csv(ctx, list(members))
@ -251,6 +255,7 @@ class MassManagementMixin(MixinMeta):
await ctx.send(
content=f"Data for {ctx.author.mention}",
files=[discord.File(data, filename=filename)],
allowed_mentions=discord.AllowedMentions.all(),
)
csvf.close()
data.close()

19
roleplay/info.json Normal file
View file

@ -0,0 +1,19 @@
{
"author": [
"TheBluekr"
],
"bot_version": [
3,
4,
0
],
"description": "Roleplay commands for booping, hugging, etc.",
"hidden": false,
"install_msg": "Thanks for using my cog!",
"requirements": [],
"tags": [
"brandons209",
"Som3thing"
],
"end_user_data_statement": "No data is stored."
}

View file

@ -4,16 +4,16 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "",
"description": "Tracks specified roles on when they are added, removed, and who updates them.",
"hidden": false,
"install_msg": "",
"install_msg": "Thank you for using my cog!",
"requirements": [],
"short": "",
"tags": [
"brandons209",
"pancakesparkle"
]
}
],
"end_user_data_statement": "Depending on setup, can log user messages, voice channel activity, audit actions in guilds, activity statistics per guild, user name changes, and any moderation actions per guild."
}

View file

@ -200,6 +200,8 @@ class RoleTracker(commands.Cog):
"""
Listens for role updates and more
"""
if await self.bot.cog_disabled_in_guild(self, after.guild):
return
if before.roles != after.roles:
user = self.bot.user

View file

@ -5,9 +5,10 @@
"install_msg": "Thanks for install.",
"short": "Quickly list server and channel rules.",
"description": "Quickly list server and channel rules.",
"min_bot_version": "3.2.1",
"min_bot_version": "3.4.0",
"tags": [
"rules",
"rule"
]
],
"end_user_data_statement": "This only stores rules added by admins of guilds."
}

View file

@ -16,6 +16,6 @@
"python-dateutil",
"pytz"
],
"min_bot_version": "3.3.0",
"min_bot_version": "3.4.0",
"end_user_data_statement": "This cog does not persistently store data or metadata about users. It does store commands provided for intended later use along with the user ID of the person who scheduled it.\nUsers may delete their own data with or without making a data request."
}

View file

@ -577,7 +577,7 @@ class Scheduler(commands.Cog):
@helpers.command(name="say")
async def say(self, ctx: commands.GuildContext, *, content: str):
await ctx.send(content, allowed_mentions=discord.AllowedMentions())
await ctx.send(content, allowed_mentions=discord.AllowedMentions.all())
@helpers.command(name="selfwhisper")
async def swhisp(self, ctx: commands.GuildContext, *, content: str):

View file

@ -5,11 +5,12 @@
"install_msg": "Thanks for install.",
"short": "Play sound effects!",
"description": "Play sound effects, either via links or files. Integrates with economy and cost manager cog.",
"min_bot_version": "3.3.0",
"min_bot_version": "3.4.0",
"requirements": ["tabulate"],
"tags": [
"sfx",
"saysound",
"audio"
]
],
"end_user_data_statement": "This doesn't store any user data."
}

View file

@ -6,5 +6,7 @@
"INSTALL_MSG" : "Thanks for installing SmartReact.",
"REQUIREMENTS" : [],
"TAGS" : ["reaction", "smart", "auto"],
"DISABLED" : false
"DISABLED" : false,
"min_bot_version": "3.4.0",
"end_user_data_statement": "This doesn't store any user data."
}

View file

@ -114,6 +114,8 @@ class SmartReact(commands.Cog):
# Thanks irdumb#1229 for the help making this "more Pythonic"
@commands.Cog.listener()
async def on_message(self, message):
if await self.bot.cog_disabled_in_guild(self, message.guild):
return
if not message.guild:
return
if message.author == self.bot.user:

View file

@ -3,7 +3,7 @@
],
"bot_version": [
3,
0,
4,
0
],
"description": "Trick or treat! Sometimes you get some credits, sometimes you get tricked and lose them!",
@ -14,4 +14,5 @@
"tags": [
"brandons209",
"Halloween"
]
],
"end_user_data_statement": "This doesn't store any user data."

View file

@ -1,6 +1,7 @@
{
"author": [
"tmerc"
"tmerc",
"brandons209"
],
"install_msg": "Thanks for installing!",
"name": "Welcome",
@ -13,5 +14,7 @@
"leave",
"ban",
"utility"
]
],
"min_bot_version": "3.4.0",
"end_user_data_statement": "This cog doesn't store any user data."
}

View file

@ -570,6 +570,8 @@ class Welcome(commands.Cog):
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member) -> None:
"""Listens for member joins."""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
guild: discord.Guild = member.guild
guild_settings = self.config.guild(guild)
@ -601,12 +603,16 @@ class Welcome(commands.Cog):
@commands.Cog.listener()
async def on_member_remove(self, member: discord.Member) -> None:
"""Listens for member leaves."""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
await self.__handle_event(member.guild, member, "leave")
@commands.Cog.listener()
async def on_member_ban(self, guild: discord.Guild, member: discord.Member) -> None:
"""Listens for user bans."""
if await self.bot.cog_disabled_in_guild(self, member.guild):
return
await self.__handle_event(guild, member, "ban")
@ -804,7 +810,8 @@ class Welcome(commands.Cog):
return await channel.send(
format_str.format(
member=user, server=guild, bot=user, count=count or "", plural=plural, roles=roles, stats=stats
)
),
allowed_mentions=discord.AllowedMentions.all(),
)
except discord.Forbidden:
log.error(
@ -848,7 +855,10 @@ class Welcome(commands.Cog):
message_format = await self.config.guild(member.guild).join.whisper.message()
try:
await member.send(message_format.format(member=member, server=member.guild))
await member.send(
message_format.format(member=member, server=member.guild),
allowed_mentions=discord.AllowedMentions.all(),
)
except discord.Forbidden:
log.error(
f"Failed to send DM to member ID {member.id} (server ID {member.guild.id}): insufficient permissions"