Merge branch 'roleping-update' into 'main'

Roleping update

Closes #59 and #38

See merge request stark-industries/j.a.r.v.i.s.!23
This commit is contained in:
Zeva Rose 2021-07-27 03:28:08 +00:00
commit 4482cb1cd0
16 changed files with 687 additions and 370 deletions

View file

@ -10,6 +10,7 @@ from psutil import Process
from jarvis import logo, tasks, utils
from jarvis.config import get_config
from jarvis.db import DBManager
from jarvis.events import guild, member, message
if asyncio.get_event_loop().is_closed():
asyncio.set_event_loop(asyncio.new_event_loop())
@ -24,7 +25,7 @@ jarvis = commands.Bot(
)
slash = SlashCommand(jarvis, sync_commands=True, sync_on_cog_reload=True)
jarvis_self = Process()
__version__ = "1.7.0"
__version__ = "1.8.0"
db = DBManager(get_config().mongo).mongo
@ -70,6 +71,14 @@ def run(ctx=None):
jarvis.max_messages = config.max_messages
tasks.init()
# Add event listeners
listeners = [
guild.GuildEventHandler(jarvis),
member.MemberEventHandler(jarvis),
message.MessageEventHandler(jarvis),
]
jarvis.run(config.token, bot=True, reconnect=True)
for cog in jarvis.cogs:
session = getattr(cog, "_session", None)

View file

@ -7,6 +7,7 @@ from discord_slash.utils.manage_commands import create_option
from jarvis.db.types import Lock
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.permissions import admin_or_permissions
class LockCog(CacheCog):
@ -65,7 +66,7 @@ class LockCog(CacheCog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_channels=True)
async def _lock(
self,
ctx: SlashContext,
@ -111,7 +112,7 @@ class LockCog(CacheCog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_channels=True)
async def _unlock(
self,
ctx: SlashContext,

View file

@ -9,6 +9,7 @@ from jarvis.config import get_config
from jarvis.db import DBManager
from jarvis.db.types import Lock
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.permissions import admin_or_permissions
class LockdownCog(CacheCog):
@ -35,7 +36,7 @@ class LockdownCog(CacheCog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_channels=True)
async def _lockdown_start(
self,
ctx: SlashContext,

View file

@ -1,71 +1,79 @@
from discord import Role
from datetime import datetime, timedelta
from ButtonPaginator import Paginator
from discord import Member, Role
from discord.ext import commands
from discord_slash import SlashContext, cog_ext
from discord_slash.model import ButtonStyle
from discord_slash.utils.manage_commands import create_option
from jarvis.db.types import Setting
from jarvis.db.types import Roleping
from jarvis.utils import build_embed
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.field import Field
from jarvis.utils.permissions import admin_or_permissions
class RolepingCog(commands.Cog):
class RolepingCog(CacheCog):
def __init__(self, bot):
self.bot = bot
super().__init__(bot)
@cog_ext.cog_subcommand(
base="roleping",
name="block",
description="Add a role to the roleping blocklist",
name="add",
description="Add a role to roleping",
options=[
create_option(
name="role",
description="Role to add to blocklist",
description="Role to add to roleping",
option_type=8,
required=True,
)
],
)
@commands.has_permissions(administrator=True)
async def _roleping_block(self, ctx: SlashContext, role: Role):
roles = Setting.get(guild=ctx.guild.id, setting="roleping")
if not roles:
roles = Setting(guild=ctx.guild.id, setting="roleping", value=[])
@admin_or_permissions(manage_guild=True)
async def _roleping_add(self, ctx: SlashContext, role: Role):
roleping = Roleping.get(guild=ctx.guild.id, role=role.id)
if not roleping:
roleping = Roleping(
role=role.id,
guild=ctx.guild.id,
admin=ctx.author.id,
active=True,
bypass={"roles": [], "users": []},
)
if role.id in roles.value:
else:
await ctx.send(
f"Role `{role.name}` already in blocklist.", hidden=True
f"Role `{role.name}` already in roleping.", hidden=True
)
return
roles.value.append(role.id)
roles.update()
await ctx.send(f"Role `{role.name}` added to blocklist.")
roleping.insert()
await ctx.send(f"Role `{role.name}` added to roleping.")
@cog_ext.cog_subcommand(
base="roleping",
name="allow",
description="Remove a role from the roleping blocklist",
name="remove",
description="Remove a role from the roleping",
options=[
create_option(
name="role",
description="Role to remove from blocklist",
description="Role to remove from roleping",
option_type=8,
required=True,
)
],
)
@commands.has_permissions(administrator=True)
async def _roleping_allow(self, ctx: SlashContext, role: Role):
roles = Setting.get(guild=ctx.guild.id, setting="roleping")
if not roles:
await ctx.send("No blocklist configured.", hidden=True)
@admin_or_permissions(manage_guild=True)
async def _roleping_remove(self, ctx: SlashContext, role: Role):
roleping = Roleping.get(guild=ctx.guild.id, role=role.id)
if not roleping:
await ctx.send("Roleping does not exist", hidden=True)
return
if role.id not in roles.value:
await ctx.send(
f"Role `{role.name}` not in blocklist.", hidden=True
)
return
roles.value.remove(role.id)
roles.update()
await ctx.send(f"Role `{role.name}` removed blocklist.")
roleping.delete()
await ctx.send(f"Role `{role.name}` removed from roleping.")
@cog_ext.cog_subcommand(
base="roleping",
@ -73,19 +81,298 @@ class RolepingCog(commands.Cog):
description="List all blocklisted roles",
)
async def _roleping_list(self, ctx: SlashContext):
roles = Setting.get(guild=ctx.guild.id, setting="roleping")
if not roles:
await ctx.send("No blocklist configured.", hidden=True)
exists = self.check_cache(ctx)
if exists:
await ctx.defer(hidden=True)
await ctx.send(
"Please use existing interaction: "
+ f"{exists['paginator']._message.jump_url}",
hidden=True,
)
return
message = "Blocklisted Roles:\n```\n"
if not roles.value:
await ctx.send("No roles blocklisted.", hidden=True)
rolepings = Roleping.get_many(guild=ctx.guild.id)
if not rolepings:
await ctx.send("No rolepings configured", hidden=True)
return
for role in roles.value:
role = ctx.guild.get_role(role)
if not role:
continue
message += role.name + "\n"
message += "```"
await ctx.send(message)
embeds = []
for roleping in rolepings:
role = ctx.guild.get_role(roleping.role)
bypass_roles = list(
filter(
lambda x: x.id in roleping.bypass["roles"], ctx.guild.roles
)
)
bypass_roles = [
r.mention or "||`[redacted]`||" for r in bypass_roles
]
bypass_users = [
ctx.guild.get_member(u).mention or "||`[redacted]`||"
for u in roleping.bypass["users"]
]
bypass_roles = bypass_roles or ["None"]
bypass_users = bypass_users or ["None"]
embed = build_embed(
title="Roleping",
description=role.mention,
color=str(role.color),
fields=[
Field(
name="Created At",
value=roleping.created_at.strftime(
"%a, %b %d, %Y %I:%M %p"
),
inline=False,
),
Field(name="Active", value=str(roleping.active)),
Field(
name="Bypass Users",
value="\n".join(bypass_users),
),
Field(
name="Bypass Roles",
value="\n".join(bypass_roles),
),
],
)
admin = ctx.guild.get_member(roleping.admin)
if not admin:
admin = self.bot.user
embed.set_author(
name=admin.nick or admin.name, icon_url=admin.avatar_url
)
embed.set_footer(
text=f"{admin.name}#{admin.discriminator} | {admin.id}"
)
embeds.append(embed)
paginator = Paginator(
bot=self.bot,
ctx=ctx,
embeds=embeds,
only=ctx.author,
timeout=60 * 5, # 5 minute timeout
disable_after_timeout=True,
use_extend=len(embeds) > 2,
left_button_style=ButtonStyle.grey,
right_button_style=ButtonStyle.grey,
basic_buttons=["", ""],
)
self.cache[hash(paginator)] = {
"user": ctx.author.id,
"guild": ctx.guild.id,
"timeout": datetime.utcnow() + timedelta(minutes=5),
"command": ctx.subcommand_name,
"paginator": paginator,
}
await paginator.start()
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="bypass",
name="user",
description="Add a user as a bypass to a roleping",
base_desc="Block roles from being pinged",
sub_group_desc="Allow specific users/roles to ping rolepings",
options=[
create_option(
name="user",
description="User to add",
option_type=6,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _roleping_bypass_user(
self, ctx: SlashContext, user: Member, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if user.id in roleping.bypass["users"]:
await ctx.send(f"{user.mention} already in bypass", hidden=True)
return
if len(roleping.bypass["users"]) == 10:
await ctx.send(
"Already have 10 users in bypass. "
"Please consider using roles for roleping bypass",
hidden=True,
)
return
matching_role = list(
filter(lambda x: x.id in roleping.bypass["roles"], user.roles)
)
if matching_role:
await ctx.send(
f"{user.mention} already has bypass "
f"via {matching_role[0].mention}",
hidden=True,
)
return
roleping.bypass["users"].append(user.id)
roleping.update()
await ctx.send(
f"{user.nick or user.name} user bypass added for `{rping.name}`"
)
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="bypass",
name="role",
description="Add a role as a bypass to a roleping",
base_desc="Block roles from being pinged",
sub_group_desc="Allow specific users/roles to ping rolepings",
options=[
create_option(
name="role",
description="Role to add",
option_type=8,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _roleping_bypass_role(
self, ctx: SlashContext, role: Role, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if role.id in roleping.bypass["roles"]:
await ctx.send(f"{role.mention} already in bypass", hidden=True)
return
if len(roleping.bypass["roles"]) == 10:
await ctx.send(
"Already have 10 roles in bypass. "
"Please consider consolidating roles for roleping bypass",
hidden=True,
)
return
roleping.bypass["roles"].append(role.id)
roleping.update()
await ctx.send(f"{role.name} role bypass added for `{rping.name}`")
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="restore",
name="user",
description="Remove a role bypass",
base_desc="Block roles from being pinged",
sub_group_desc="Remove a bypass from a roleping (restoring it)",
options=[
create_option(
name="user",
description="User to add",
option_type=6,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _roleping_restore_user(
self, ctx: SlashContext, user: Member, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if user.id not in roleping.bypass["users"]:
await ctx.send(f"{user.mention} not in bypass", hidden=True)
return
roleping.bypass["users"].delete(user.id)
roleping.update()
await ctx.send(
f"{user.nick or user.name} user bypass removed for `{rping.name}`"
)
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="restore",
name="role",
description="Remove a role bypass",
base_desc="Block roles from being pinged",
sub_group_desc="Remove a bypass from a roleping (restoring it)",
options=[
create_option(
name="role",
description="Role to add",
option_type=8,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _roleping_restore_role(
self, ctx: SlashContext, role: Role, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if role.id in roleping.bypass["roles"]:
await ctx.send(f"{role.mention} already in bypass", hidden=True)
return
if len(roleping.bypass["roles"]) == 10:
await ctx.send(
"Already have 10 roles in bypass. "
"Please consider consolidating roles for roleping bypass",
hidden=True,
)
return
roleping.bypass["roles"].append(role.id)
roleping.update()
await ctx.send(f"{role.name} role bypass added for `{rping.name}`")

View file

@ -11,6 +11,7 @@ from jarvis.db.types import MongoSort, Warning
from jarvis.utils import build_embed
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.field import Field
from jarvis.utils.permissions import admin_or_permissions
class WarningCog(CacheCog):
@ -41,7 +42,7 @@ class WarningCog(CacheCog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _warn(
self, ctx: SlashContext, user: User, reason: str, duration: int = 24
):
@ -99,7 +100,7 @@ class WarningCog(CacheCog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _warnings(self, ctx: SlashContext, user: User, active: bool = 1):
active = bool(active)
exists = self.check_cache(ctx, user_id=user.id, active=active)
@ -132,7 +133,7 @@ class WarningCog(CacheCog):
else:
fields = []
for warn in active_warns:
admin = ctx.guild.get(warn.admin)
admin = ctx.guild.get_member(warn.admin)
admin_name = "||`[redacted]`||"
if admin:
admin_name = f"{admin.name}#{admin.discriminator}"

View file

@ -8,6 +8,7 @@ from discord_slash.utils.manage_commands import create_option
from jarvis.data.unicode import emoji_list
from jarvis.db.types import Autoreact
from jarvis.utils.permissions import admin_or_permissions
class AutoReactCog(commands.Cog):
@ -28,7 +29,7 @@ class AutoReactCog(commands.Cog):
)
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _autoreact_create(self, ctx: SlashContext, channel: TextChannel):
if not isinstance(channel, TextChannel):
await ctx.send("Channel must be a text channel", hidden=True)
@ -62,6 +63,7 @@ class AutoReactCog(commands.Cog):
)
],
)
@admin_or_permissions(manage_guild=True)
async def _autoreact_delete(self, ctx, channel: TextChannel):
exists = Autoreact.get(guild=ctx.guild.id, channel=channel.id).delete()
if exists:
@ -90,7 +92,7 @@ class AutoReactCog(commands.Cog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _autoreact_add(self, ctx, channel: TextChannel, emote: str):
await ctx.defer()
custom_emoji = self.custom_emote.match(emote)
@ -152,7 +154,7 @@ class AutoReactCog(commands.Cog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _autoreact_remove(self, ctx, channel: TextChannel, emote: str):
exists = Autoreact.get(guild=ctx.guild.id, channel=channel.id)
if not exists:
@ -185,7 +187,7 @@ class AutoReactCog(commands.Cog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _autoreact_list(self, ctx, channel: TextChannel):
exists = Autoreact.get(guild=ctx.guild.id, channel=channel.id)
if not exists:

View file

@ -14,6 +14,7 @@ from discord_slash.model import ButtonStyle
from jarvis.config import get_config
from jarvis.db import DBManager
from jarvis.utils import build_embed
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.field import Field
guild_ids = [578757004059738142, 520021794380447745, 862402786116763668]
@ -25,26 +26,13 @@ invites = re.compile(
)
class CTCCog(commands.Cog):
class CTCCog(CacheCog):
def __init__(self, bot):
self.bot = bot
super().__init__(bot)
mconf = get_config().mongo
self.db = DBManager(mconf).mongo
self._session = aiohttp.ClientSession()
self.url = "https://completethecodetwo.cards/pw"
self.cache = {}
self._expire_interaction.start()
def check_cache(self, ctx: SlashContext, **kwargs):
if not kwargs:
kwargs = {}
return find(
lambda x: x["command"] == ctx.subcommand_name
and x["user"] == ctx.author.id
and x["guild"] == ctx.guild.id
and all(x[k] == v for k, v in kwargs.items()),
self.cache.values(),
)
@cog_ext.cog_subcommand(
base="ctc2",
@ -178,15 +166,6 @@ class CTCCog(commands.Cog):
await paginator.start()
@loop(minutes=1)
async def _expire_interaction(self):
keys = list(self.cache.keys())
for key in keys:
if self.cache[key]["timeout"] <= datetime.utcnow() + timedelta(
minutes=1
):
del self.cache[key]
def setup(bot):
bot.add_cog(CTCCog(bot))

View file

@ -11,32 +11,21 @@ from discord_slash.utils.manage_commands import create_choice, create_option
from jarvis.config import get_config
from jarvis.utils import build_embed
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.field import Field
guild_ids = [862402786116763668]
class GitlabCog(commands.Cog):
class GitlabCog(CacheCog):
def __init__(self, bot):
self.bot = bot
super().__init__(bot)
config = get_config()
self._gitlab = gitlab.Gitlab(
"https://git.zevaryx.com", private_token=config.gitlab_token
)
# J.A.R.V.I.S. GitLab ID is 29
self.project = self._gitlab.projects.get(29)
self.cache = {}
self._expire_interaction.start()
def check_cache(self, ctx: SlashContext, **kwargs):
if not kwargs:
kwargs = {}
return find(
lambda x: x["command"] == ctx.subcommand_name
and x["user"] == ctx.author.id
and all(x[k] == v for k, v in kwargs.items()),
self.cache.values(),
)
@cog_ext.cog_subcommand(
base="gl",
@ -379,6 +368,7 @@ class GitlabCog(commands.Cog):
self.cache[hash(paginator)] = {
"user": ctx.author.id,
"guild": ctx.guild.id,
"timeout": datetime.utcnow() + timedelta(minutes=5),
"command": ctx.subcommand_name,
"state": state,
@ -470,6 +460,7 @@ class GitlabCog(commands.Cog):
self.cache[hash(paginator)] = {
"user": ctx.author.id,
"guild": ctx.guild.id,
"timeout": datetime.utcnow() + timedelta(minutes=5),
"command": ctx.subcommand_name,
"state": state,
@ -539,6 +530,7 @@ class GitlabCog(commands.Cog):
self.cache[hash(paginator)] = {
"user": ctx.author.id,
"guild": ctx.guild.id,
"timeout": datetime.utcnow() + timedelta(minutes=5),
"command": ctx.subcommand_name,
"paginator": paginator,
@ -546,15 +538,6 @@ class GitlabCog(commands.Cog):
await paginator.start()
@loop(minutes=1)
async def _expire_interaction(self):
keys = list(self.cache.keys())
for key in keys:
if self.cache[key]["timeout"] <= datetime.utcnow() + timedelta(
minutes=1
):
del self.cache[key]
def setup(bot):
if get_config().gitlab_token:

View file

@ -4,12 +4,10 @@ from datetime import datetime, timedelta
from typing import Optional
from bson import ObjectId
from ButtonPaginator import Paginator
from discord.ext import commands
from discord.ext.tasks import loop
from discord.utils import find
from discord_slash import SlashContext, cog_ext
from discord_slash.model import ButtonStyle
from discord_slash.utils.manage_commands import create_option
from discord_slash.utils.manage_components import (
create_actionrow,
@ -20,6 +18,7 @@ from discord_slash.utils.manage_components import (
from jarvis.db.types import Reminder
from jarvis.utils import build_embed
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.field import Field
valid = re.compile(r"[\w\s\-\\/.!@#$%^*()+=<>,\u0080-\U000E0FFF]*")
@ -29,23 +28,11 @@ invites = re.compile(
)
class RemindmeCog(commands.Cog):
class RemindmeCog(CacheCog):
def __init__(self, bot):
self.bot = bot
self.cache = {}
super().__init__(bot)
self._remind.start()
def check_cache(self, ctx: SlashContext, **kwargs):
if not kwargs:
kwargs = {}
return find(
lambda x: x["command"] == ctx.subcommand_name
and x["user"] == ctx.author.id
and x["guild"] == ctx.guild.id
and all(x[k] == v for k, v in kwargs.items()),
self.cache.values(),
)
@cog_ext.cog_slash(
name="remindme",
description="Set a reminder",
@ -318,15 +305,6 @@ class RemindmeCog(commands.Cog):
component["disabled"] = True
await message.edit(components=components)
@loop(minutes=1)
async def _expire_interaction(self):
keys = list(self.cache.keys())
for key in keys:
if self.cache[key]["timeout"] <= datetime.utcnow() + timedelta(
minutes=1
):
del self.cache[key]
@loop(seconds=15)
async def _remind(self):
reminders = Reminder.get_active(

View file

@ -6,6 +6,7 @@ from discord_slash.utils.manage_commands import create_option
from jarvis.db.types import Setting
from jarvis.utils import build_embed
from jarvis.utils.field import Field
from jarvis.utils.permissions import admin_or_permissions
class RolegiverCog(commands.Cog):
@ -25,7 +26,7 @@ class RolegiverCog(commands.Cog):
)
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _rolegiver_add(self, ctx: SlashContext, role: Role):
setting = Setting.get(guild=ctx.guild.id, setting="rolegiver")
if setting and role.id in setting.value:
@ -82,7 +83,7 @@ class RolegiverCog(commands.Cog):
)
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _rolegiver_remove(self, ctx: SlashContext, role: Role):
setting = Setting.get(guild=ctx.guild.id, setting="rolegiver")
if not setting or (setting and not setting.value):

View file

@ -10,7 +10,6 @@ from jarvis.utils.permissions import admin_or_permissions
class SettingsCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.cache = {}
def update_settings(self, setting, value, guild):
setting = Setting(setting=setting, value=value, guild=guild)
@ -31,7 +30,7 @@ class SettingsCog(commands.Cog):
)
],
)
@admin_or_permissions(mute_members=True)
@admin_or_permissions(manage_guild=True)
async def _mute(self, ctx, role: Role):
await ctx.defer()
self.update_settings("mute", role.id, ctx.guild.id)
@ -50,7 +49,7 @@ class SettingsCog(commands.Cog):
)
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _modlog(self, ctx, channel: TextChannel):
if not isinstance(channel, TextChannel):
await ctx.send("Channel must be a TextChannel", hidden=True)
@ -73,7 +72,7 @@ class SettingsCog(commands.Cog):
)
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _userlog(self, ctx, channel: TextChannel):
if not isinstance(channel, TextChannel):
await ctx.send("Channel must be a TextChannel", hidden=True)
@ -96,7 +95,7 @@ class SettingsCog(commands.Cog):
)
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _massmention(self, ctx, amount: int):
await ctx.defer()
self.update_settings("massmention", amount, ctx.guild.id)
@ -115,7 +114,7 @@ class SettingsCog(commands.Cog):
)
],
)
@admin_or_permissions(kick_members=True, ban_members=True)
@admin_or_permissions(manage_guild=True)
async def _verified(self, ctx, role: Role):
await ctx.defer()
self.update_settings("verified", role.id, ctx.guild.id)
@ -134,7 +133,7 @@ class SettingsCog(commands.Cog):
)
],
)
@admin_or_permissions(kick_members=True, ban_members=True)
@admin_or_permissions(manage_guild=True)
async def _unverified(self, ctx, role: Role):
await ctx.defer()
self.update_settings("unverified", role.id, ctx.guild.id)

View file

@ -5,6 +5,7 @@ from discord_slash.utils.manage_commands import create_option
from jarvis.db.types import Star, Starboard
from jarvis.utils import build_embed
from jarvis.utils.permissions import admin_or_permissions
supported_images = [
"image/png",
@ -24,7 +25,7 @@ class StarboardCog(commands.Cog):
name="list",
description="Lists all Starboards",
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _list(self, ctx):
starboards = Starboard.get_many(guild=ctx.guild.id)
if starboards != []:
@ -48,7 +49,7 @@ class StarboardCog(commands.Cog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _create(self, ctx, channel: TextChannel):
if channel not in ctx.guild.channels:
await ctx.send(
@ -88,7 +89,7 @@ class StarboardCog(commands.Cog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _delete(self, ctx, channel: TextChannel):
deleted = Starboard.get(
channel=channel.id, guild=ctx.guild.id
@ -129,7 +130,7 @@ class StarboardCog(commands.Cog):
),
],
)
@commands.has_permissions(administrator=True)
@admin_or_permissions(manage_guild=True)
async def _star_add(
self,
ctx: SlashContext,
@ -216,66 +217,67 @@ class StarboardCog(commands.Cog):
"Message saved to Starboard.\n" + f"See it in {starboard.mention}"
)
@cog_ext.cog_subcommand(
base="star",
name="delete",
description="Delete a starred message",
guild_ids=[
862402786116763668,
418094694325813248,
578757004059738142,
],
options=[
create_option(
name="id",
description="Star to delete",
option_type=4,
required=True,
),
create_option(
name="starboard",
description="Starboard to delete star from",
option_type=7,
required=True,
),
],
)
async def _star_get(
self,
ctx: SlashContext,
id: int,
starboard: TextChannel,
):
if not isinstance(starboard, TextChannel):
await ctx.send("Channel must be a TextChannel", hidden=True)
return
exists = Starboard.get(channel=starboard.id, guild=ctx.guild.id)
if not exists:
await ctx.send(
f"Starboard does not exist in {starboard.mention}. "
+ "Please create it first",
hidden=True,
)
return
star = Star.get(
starboard=starboard.id,
id=id,
guild=ctx.guild.id,
active=True,
@cog_ext.cog_subcommand(
base="star",
name="delete",
description="Delete a starred message",
guild_ids=[
862402786116763668,
418094694325813248,
578757004059738142,
],
options=[
create_option(
name="id",
description="Star to delete",
option_type=4,
required=True,
),
create_option(
name="starboard",
description="Starboard to delete star from",
option_type=7,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _star_delete(
self,
ctx: SlashContext,
id: int,
starboard: TextChannel,
):
if not isinstance(starboard, TextChannel):
await ctx.send("Channel must be a TextChannel", hidden=True)
return
exists = Starboard.get(channel=starboard.id, guild=ctx.guild.id)
if not exists:
await ctx.send(
f"Starboard does not exist in {starboard.mention}. "
+ "Please create it first",
hidden=True,
)
if not star:
await ctx.send(f"No star exists with id {id}", hidden=True)
return
return
message = await starboard.fetch_message(star.star)
if message:
await message.delete()
star = Star.get(
starboard=starboard.id,
id=id,
guild=ctx.guild.id,
active=True,
)
if not star:
await ctx.send(f"No star exists with id {id}", hidden=True)
return
star.active = False
star.update()
message = await starboard.fetch_message(star.star)
if message:
await message.delete()
await ctx.send(f"Star {id} deleted")
star.active = False
star.update()
await ctx.send(f"Star {id} deleted")
def setup(bot):

View file

@ -28,6 +28,7 @@ coll_lookup = {
"Mute": "mutes",
"Purge": "purges",
"Reminder": "reminders",
"Roleping": "rolepings",
"Setting": "settings",
"Starboard": "starboard",
"Star": "stars",
@ -460,6 +461,27 @@ class Reminder(MongoObject, ActiveObject):
created_at: datetime = field(default_factory=datetime.utcnow)
@dataclass
class Roleping(MongoObject, ActiveObject):
"""
Guild Roleping object
:param _id: MongoDB ID
:param role: Blocked role
:param guild: ID of origin guild
:param admin: Admin who added roleping
:param bypass: Roles/users who may bypass this roleping
:param active: If the roleping is disabled
:param created_at: Time the roleping was created
"""
role: int
guild: int
admin: int
bypass: dict
created_at: datetime = field(default_factory=datetime.utcnow)
@dataclass
class Setting(MongoObject):
"""

View file

@ -2,25 +2,30 @@ import asyncio
from discord.utils import find
from jarvis import jarvis
from jarvis.db.types import Setting
@jarvis.event
async def on_guild_join(guild):
general = find(lambda x: x.name == "general", guild.channels)
if general and general.permissions_for(guild.me).send_messages:
await general.send(
"Allow me to introduce myself. I am J.A.R.V.I.S., a virtual "
+ "artificial intelligence, and I'm here to assist you with a "
+ "variety of tasks as best I can, "
+ "24 hours a day, seven days a week."
)
await asyncio.sleep(1)
await general.send("Importing all preferences from home interface...")
class GuildEventHandler(object):
def __init__(self, bot):
self.bot = bot
self.bot.add_listener(self.on_guild_join)
# Set some default settings
setting = Setting(guild=guild.id, setting="massmention", value=5)
setting.insert()
async def on_guild_join(self, guild):
general = find(lambda x: x.name == "general", guild.channels)
if general and general.permissions_for(guild.me).send_messages:
await general.send(
"Allow me to introduce myself. I am J.A.R.V.I.S., a virtual "
+ "artificial intelligence, and I'm here to assist you with a "
+ "variety of tasks as best I can, "
+ "24 hours a day, seven days a week."
)
await asyncio.sleep(1)
await general.send(
"Importing all preferences from home interface..."
)
await general.send("Systems are now fully operational")
# Set some default settings
setting = Setting(guild=guild.id, setting="massmention", value=5)
setting.insert()
await general.send("Systems are now fully operational")

View file

@ -1,20 +1,25 @@
from discord import Member
from jarvis import jarvis
from jarvis.db.types import Mute, Setting
@jarvis.event
async def on_member_join(user: Member):
guild = user.guild
mutes = Mute.get_active(guild=guild.id)
if mutes and len(mutes) >= 1:
mute_role = Setting.get(guild=guild.id, setting="mute")
role = guild.get_role(mute_role.value)
await user.add_roles(
role, reason="User is muted still muted from prior mute"
)
unverified = Setting.get(guild=guild.id, setting="unverified")
if unverified:
role = guild.get_role(unverified.value)
await user.add_roles(role, reason="User just joined and is unverified")
class MemberEventHandler(object):
def __init__(self, bot):
self.bot = bot
self.bot.add_listener(self.on_member_join)
async def on_member_join(self, user: Member):
guild = user.guild
mutes = Mute.get_active(guild=guild.id)
if mutes and len(mutes) >= 1:
mute_role = Setting.get(guild=guild.id, setting="mute")
role = guild.get_role(mute_role.value)
await user.add_roles(
role, reason="User is muted still muted from prior mute"
)
unverified = Setting.get(guild=guild.id, setting="unverified")
if unverified:
role = guild.get_role(unverified.value)
await user.add_roles(
role, reason="User just joined and is unverified"
)

View file

@ -3,9 +3,8 @@ import re
from discord import DMChannel, Message
from discord.utils import find
from jarvis import jarvis
from jarvis.config import get_config
from jarvis.db.types import Autopurge, Autoreact, Setting
from jarvis.db.types import Autopurge, Autoreact, Roleping, Setting, Warning
from jarvis.utils import build_embed
from jarvis.utils.field import Field
@ -15,57 +14,183 @@ invites = re.compile(
)
async def autopurge(message):
autopurge = Autopurge.get(
guild=message.guild.id, channel=message.channel.id
)
if autopurge:
await message.delete(delay=autopurge.delay)
class MessageEventHandler(object):
def __init__(self, bot):
self.bot = bot
self.bot.add_listener(self.on_message)
self.bot.add_listener(self.on_message_edit)
async def autoreact(message):
autoreact = Autoreact.get(
guild=message.guild.id,
channel=message.channel.id,
)
if autoreact:
for reaction in autoreact.reactions:
await message.add_reaction(reaction)
async def checks(message):
# #tech
channel = find(
lambda x: x.id == 599068193339736096, message.channel_mentions
)
if channel and message.author.id == 293795462752894976:
await channel.send(
content="https://cdn.discordapp.com/attachments/"
+ "664621130044407838/805218508866453554/tech.gif"
async def autopurge(self, message: Message):
autopurge = Autopurge.get(
guild=message.guild.id, channel=message.channel.id
)
content = re.sub(r"\s+", "", message.content)
match = invites.search(content)
if match:
guild_invites = await message.guild.invites()
allowed = [x.code for x in guild_invites] + [
"dbrand",
"VtgZntXcnZ",
]
if match.group(1) not in allowed:
await message.delete()
if autopurge:
await message.delete(delay=autopurge.delay)
async def autoreact(self, message: Message):
autoreact = Autoreact.get(
guild=message.guild.id,
channel=message.channel.id,
)
if autoreact:
for reaction in autoreact.reactions:
await message.add_reaction(reaction)
async def checks(self, message: Message):
# #tech
channel = find(
lambda x: x.id == 599068193339736096, message.channel_mentions
)
if channel and message.author.id == 293795462752894976:
await channel.send(
content="https://cdn.discordapp.com/attachments/"
+ "664621130044407838/805218508866453554/tech.gif"
)
content = re.sub(r"\s+", "", message.content)
match = invites.search(content)
if match:
guild_invites = await message.guild.invites()
allowed = [x.code for x in guild_invites] + [
"dbrand",
"VtgZntXcnZ",
]
if match.group(1) not in allowed:
await message.delete()
warning = Warning(
active=True,
admin=get_config().client_id,
duration=24,
guild=message.guild.id,
reason="Sent an invite link",
user=message.author.id,
)
warning.insert()
fields = [
Field(
"Reason",
"Sent an invite link",
False,
)
]
embed = build_embed(
title="Warning",
description=f"{message.author.mention} has been warned",
fields=fields,
)
embed.set_author(
name=message.author.nick
if message.author.nick
else message.author.name,
icon_url=message.author.avatar_url,
)
embed.set_footer(
text=f"{message.author.name}#"
+ f"{message.author.discriminator} "
+ f"| {message.author.id}"
)
await message.channel.send(embed=embed)
async def massmention(self, message: Message):
massmention = Setting.get(
guild=message.guild.id,
setting="massmention",
)
if (
massmention.value > 0
and len(message.mentions)
- (1 if message.author in message.mentions else 0)
> massmention.value
):
warning = Warning(
active=True,
admin=get_config().client_id,
duration=24,
guild=message.guild.id,
reason="Sent an invite link",
reason="Mass Mention",
user=message.author.id,
)
warning.insert()
fields = [Field("Reason", "Mass Mention", False)]
embed = build_embed(
title="Warning",
description=f"{message.author.mention} has been warned",
fields=fields,
)
embed.set_author(
name=message.author.nick
if message.author.nick
else message.author.name,
icon_url=message.author.avatar_url,
)
embed.set_footer(
text=f"{message.author.name}#{message.author.discriminator} "
+ f"| {message.author.id}"
)
await message.channel.send(embed=embed)
async def roleping(self, message: Message):
rolepings = Roleping.get_active(guild=message.guild.id)
if not rolepings:
return
# Get all role IDs involved with message
roles = []
for mention in message.role_mentions:
roles.append(mention.id)
for mention in message.mentions:
for role in mention.roles:
roles.append(role.id)
if not roles:
return
# Get all roles that are rolepinged
roleping_ids = [r.role for r in rolepings]
# Get roles in rolepings
role_in_rolepings = list(filter(lambda x: x in roleping_ids, roles))
# Check if the user has the role, so they are allowed to ping it
user_missing_role = any(
x.id not in roleping_ids for x in message.author.roles
)
# Admins can ping whoever
user_is_admin = message.author.guild_permissions.administrator
# Check if user in a bypass list
user_has_bypass = False
for roleping in rolepings:
if message.author.id in roleping.bypass["users"]:
user_has_bypass = True
break
if any(
role.id in roleping.bypass["roles"]
for role in message.author.roles
):
user_has_bypass = True
break
if (
role_in_rolepings
and user_missing_role
and not user_is_admin
and not user_has_bypass
):
warning = Warning(
active=True,
admin=get_config().client_id,
duration=24,
guild=message.guild.id,
reason="Pinged a blocked role/user with a blocked role",
user=message.author.id,
)
warning.insert()
fields = [
Field(
"Reason",
"Sent an invite link",
"Pinged a blocked role/user with a blocked role",
False,
)
]
@ -81,108 +206,25 @@ async def checks(message):
icon_url=message.author.avatar_url,
)
embed.set_footer(
text=f"{message.author.name}#"
+ f"{message.author.discriminator} "
text=f"{message.author.name}#{message.author.discriminator} "
+ f"| {message.author.id}"
)
await message.channel.send(embed=embed)
async def on_message(self, message: Message):
if (
not isinstance(message.channel, DMChannel)
and not message.author.bot
):
await self.autoreact(message)
await self.massmention(message)
await self.roleping(message)
await self.autopurge(message)
await self.checks(message)
await self.bot.process_commands(message)
async def massmention(message):
massmention = Setting.get(
guild=message.guild.id,
setting="massmention",
)
if (
massmention.value > 0
and len(message.mentions)
- (1 if message.author in message.mentions else 0)
> massmention.value
):
warning = Warning(
active=True,
admin=get_config().client_id,
duration=24,
guild=message.guild.id,
reason="Mass Mention",
user=message.author.id,
)
warning.insert()
fields = [Field("Reason", "Mass Mention", False)]
embed = build_embed(
title="Warning",
description=f"{message.author.mention} has been warned",
fields=fields,
)
embed.set_author(
name=message.author.nick
if message.author.nick
else message.author.name,
icon_url=message.author.avatar_url,
)
embed.set_footer(
text=f"{message.author.name}#{message.author.discriminator} "
+ f"| {message.author.id}"
)
await message.channel.send(embed=embed)
async def roleping(message):
roleping = Setting.get(guild=message.guild.id, setting="roleping")
roles = []
for mention in message.role_mentions:
roles.append(mention.id)
for mention in message.mentions:
for role in mention.roles:
roles.append(role.id)
if (
roleping
and any(x in roleping.value for x in roles)
and not any(x.id in roleping.value for x in message.author.roles)
):
warning = Warning(
active=True,
admin=get_config().client_id,
duration=24,
guild=message.guild.id,
reason="Pinged a blocked role/user with a blocked role",
user=message.author.id,
)
warning.insert()
fields = [
Field(
"Reason",
"Pinged a blocked role/user with a blocked role",
False,
)
]
embed = build_embed(
title="Warning",
description=f"{message.author.mention} has been warned",
fields=fields,
)
embed.set_author(
name=message.author.nick
if message.author.nick
else message.author.name,
icon_url=message.author.avatar_url,
)
embed.set_footer(
text=f"{message.author.name}#{message.author.discriminator} "
+ f"| {message.author.id}"
)
await message.channel.send(embed=embed)
@jarvis.event
async def on_message(message: Message):
if (
not isinstance(message.channel, DMChannel)
and message.author.id != jarvis.user.id
):
await autoreact(message)
await massmention(message)
await roleping(message)
await autopurge(message)
await checks(message)
await jarvis.process_commands(message)
async def on_message_edit(self, before: Message, after: Message):
if not isinstance(after.channel, DMChannel) and not after.author.bot:
await self.massmention(after)
await self.roleping(after)
await self.checks(after)