aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Sebastiaan Zeeff <[email protected]>2019-02-17 13:00:41 +0100
committerGravatar GitHub <[email protected]>2019-02-17 13:00:41 +0100
commite499e5b07641ff3dad211b98a67080e593d94c79 (patch)
tree09367ed3b369efb2d290a6584e65cb2d3d60fc52
parentChanging negative bot response in nominate command to be more explicit about ... (diff)
parentMerge pull request #311 from python-discord/add-team-creator (diff)
Merge branch 'master' into bb-improvements
-rw-r--r--bot/__main__.py12
-rw-r--r--bot/cogs/alias.py16
-rw-r--r--bot/cogs/filtering.py52
-rw-r--r--bot/cogs/jams.py83
-rw-r--r--bot/cogs/modlog.py4
-rw-r--r--bot/cogs/rules.py102
-rw-r--r--bot/cogs/site.py16
-rw-r--r--bot/constants.py1
-rw-r--r--config-default.yml4
9 files changed, 252 insertions, 38 deletions
diff --git a/bot/__main__.py b/bot/__main__.py
index c9d6fd61e..6928a3960 100644
--- a/bot/__main__.py
+++ b/bot/__main__.py
@@ -38,11 +38,11 @@ else:
log.warning("Timed out while waiting for RabbitMQ")
# Internal/debug
-bot.load_extension("bot.cogs.logging")
-bot.load_extension("bot.cogs.security")
bot.load_extension("bot.cogs.events")
bot.load_extension("bot.cogs.filtering")
+bot.load_extension("bot.cogs.logging")
bot.load_extension("bot.cogs.modlog")
+bot.load_extension("bot.cogs.security")
# Commands, etc
bot.load_extension("bot.cogs.antispam")
@@ -51,6 +51,7 @@ bot.load_extension("bot.cogs.bot")
bot.load_extension("bot.cogs.clean")
bot.load_extension("bot.cogs.cogs")
bot.load_extension("bot.cogs.help")
+bot.load_extension("bot.cogs.rules")
# Only load this in production
if not DEBUG_MODE:
@@ -59,12 +60,13 @@ if not DEBUG_MODE:
# Feature cogs
bot.load_extension("bot.cogs.alias")
-bot.load_extension("bot.cogs.deployment")
bot.load_extension("bot.cogs.defcon")
+bot.load_extension("bot.cogs.deployment")
bot.load_extension("bot.cogs.eval")
+bot.load_extension("bot.cogs.free")
bot.load_extension("bot.cogs.fun")
-bot.load_extension("bot.cogs.superstarify")
bot.load_extension("bot.cogs.information")
+bot.load_extension("bot.cogs.jams")
bot.load_extension("bot.cogs.moderation")
bot.load_extension("bot.cogs.off_topic_names")
bot.load_extension("bot.cogs.reddit")
@@ -72,11 +74,11 @@ bot.load_extension("bot.cogs.reminders")
bot.load_extension("bot.cogs.site")
bot.load_extension("bot.cogs.snakes")
bot.load_extension("bot.cogs.snekbox")
+bot.load_extension("bot.cogs.superstarify")
bot.load_extension("bot.cogs.tags")
bot.load_extension("bot.cogs.token_remover")
bot.load_extension("bot.cogs.utils")
bot.load_extension("bot.cogs.wolfram")
-bot.load_extension("bot.cogs.free")
if has_rmq:
bot.load_extension("bot.cogs.rmq")
diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py
index 23562ad25..bf59b6d15 100644
--- a/bot/cogs/alias.py
+++ b/bot/cogs/alias.py
@@ -103,14 +103,6 @@ class Alias:
await self.invoke(ctx, "site faq")
- @command(name="rules", hidden=True)
- async def site_rules_alias(self, ctx):
- """
- Alias for invoking <prefix>site rules.
- """
-
- await self.invoke(ctx, "site rules")
-
@command(name="reload", hidden=True)
async def cogs_reload_alias(self, ctx, *, cog_name: str):
"""
@@ -137,6 +129,14 @@ class Alias:
await self.invoke(ctx, "defcon disable")
+ @command(name="exception", hidden=True)
+ async def tags_get_traceback_alias(self, ctx):
+ """
+ Alias for invoking <prefix>tags get traceback.
+ """
+
+ await self.invoke(ctx, "tags get traceback")
+
@group(name="get",
aliases=("show", "g"),
hidden=True,
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 6b4469ceb..25aaf8420 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -1,6 +1,6 @@
import logging
import re
-from typing import Optional
+from typing import Optional, Union
import discord.errors
from dateutil.relativedelta import relativedelta
@@ -189,7 +189,25 @@ class Filtering:
log.debug(message)
- additional_embeds = msg.embeds if filter_name == "watch_rich_embeds" else None
+ additional_embeds = None
+ additional_embeds_msg = None
+
+ if filter_name == "filter_invites":
+ additional_embeds = []
+ for invite, data in triggered.items():
+ embed = discord.Embed(description=(
+ f"**Members:**\n{data['members']}\n"
+ f"**Active:**\n{data['active']}"
+ ))
+ embed.set_author(name=data["name"])
+ embed.set_thumbnail(url=data["icon"])
+ embed.set_footer(text=f"Guild Invite Code: {invite}")
+ additional_embeds.append(embed)
+ additional_embeds_msg = "For the following guild(s):"
+
+ elif filter_name == "watch_rich_embeds":
+ additional_embeds = msg.embeds
+ additional_embeds_msg = "With the following embed(s):"
# Send pretty mod log embed to mod-alerts
await self.mod_log.send_log_message(
@@ -201,6 +219,7 @@ class Filtering:
channel_id=Channels.mod_alerts,
ping_everyone=Filter.ping_everyone,
additional_embeds=additional_embeds,
+ additional_embeds_msg=additional_embeds_msg
)
break # We don't want multiple filters to trigger
@@ -282,10 +301,12 @@ class Filtering:
return bool(re.search(ZALGO_RE, text))
- async def _has_invites(self, text: str) -> bool:
+ async def _has_invites(self, text: str) -> Union[dict, bool]:
"""
- Returns True if the text contains an invite which is not on the guild_invite_whitelist in
- config.yml
+ Checks if there's any invites in the text content that aren't in the guild whitelist.
+
+ If any are detected, a dictionary of invite data is returned, with a key per invite.
+ If none are detected, False is returned.
Attempts to catch some of common ways to try to cheat the system.
"""
@@ -295,10 +316,13 @@ class Filtering:
text = text.replace("\\", "")
invites = re.findall(INVITE_RE, text, re.IGNORECASE)
+ invite_data = dict()
for invite in invites:
+ if invite in invite_data:
+ continue
response = await self.bot.http_session.get(
- f"{URLs.discord_invite_api}/{invite}"
+ f"{URLs.discord_invite_api}/{invite}", params={"with_counts": "true"}
)
response = await response.json()
guild = response.get("guild")
@@ -311,8 +335,20 @@ class Filtering:
guild_id = int(guild.get("id"))
if guild_id not in Filter.guild_invite_whitelist:
- return True
- return False
+ guild_icon_hash = guild["icon"]
+ guild_icon = (
+ "https://cdn.discordapp.com/icons/"
+ f"{guild_id}/{guild_icon_hash}.png?size=512"
+ )
+
+ invite_data[invite] = {
+ "name": guild["name"],
+ "icon": guild_icon,
+ "members": response["approximate_member_count"],
+ "active": response["approximate_presence_count"]
+ }
+
+ return invite_data if invite_data else False
@staticmethod
async def _has_rich_embed(msg: Message):
diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py
new file mode 100644
index 000000000..e68aa3d2f
--- /dev/null
+++ b/bot/cogs/jams.py
@@ -0,0 +1,83 @@
+import logging
+
+from discord import Member, PermissionOverwrite, utils
+from discord.ext import commands
+
+from bot.constants import Roles
+from bot.decorators import with_role
+
+log = logging.getLogger(__name__)
+
+
+class CodeJams:
+ """
+ Manages the code-jam related parts of our server
+ """
+
+ def __init__(self, bot: commands.Bot):
+ self.bot = bot
+
+ @commands.command()
+ @with_role(Roles.admin)
+ async def createteam(
+ self, ctx: commands.Context,
+ team_name: str, members: commands.Greedy[Member]
+ ):
+ """
+ Create a team channel in the Code Jams category, assign roles and then add
+ overwrites for the team.
+
+ The first user passed will always be the team leader.
+ """
+ code_jam_category = utils.get(ctx.guild.categories, name="Code Jam")
+
+ if code_jam_category is None:
+ log.info("Code Jam category not found, creating it.")
+
+ category_overwrites = {
+ ctx.guild.default_role: PermissionOverwrite(read_messages=False),
+ ctx.guild.me: PermissionOverwrite(read_messages=True)
+ }
+
+ code_jam_category = await ctx.guild.create_category_channel(
+ "Code Jam",
+ overwrites=category_overwrites,
+ reason="It's code jam time!"
+ )
+
+ # First member is always the team leader
+ team_channel_overwrites = {
+ members[0]: PermissionOverwrite(
+ manage_messages=True,
+ read_messages=True,
+ manage_webhooks=True
+ ),
+ ctx.guild.default_role: PermissionOverwrite(read_messages=False),
+ ctx.guild.get_role(Roles.developer): PermissionOverwrite(read_messages=False)
+ }
+
+ # Rest of members should just have read_messages
+ for member in members[1:]:
+ team_channel_overwrites[member] = PermissionOverwrite(read_messages=True)
+
+ # Create a channel for the team
+ team_channel = await ctx.guild.create_text_channel(
+ team_name,
+ overwrites=team_channel_overwrites,
+ category=code_jam_category
+ )
+
+ # Assign team leader role
+ await members[0].add_roles(ctx.guild.get_role(Roles.team_leader))
+
+ # Assign rest of roles
+ jammer_role = ctx.guild.get_role(Roles.jammer)
+ for member in members:
+ await member.add_roles(jammer_role)
+
+ await ctx.send(f":ok_hand: Team created: {team_channel.mention}")
+
+
+def setup(bot):
+ bot.add_cog(CodeJams(bot))
+ log.info("Cog loaded: CodeJams")
diff --git a/bot/cogs/modlog.py b/bot/cogs/modlog.py
index 495795b6d..65efda5ed 100644
--- a/bot/cogs/modlog.py
+++ b/bot/cogs/modlog.py
@@ -115,6 +115,7 @@ class ModLog:
files: Optional[List[File]] = None,
content: Optional[str] = None,
additional_embeds: Optional[List[Embed]] = None,
+ additional_embeds_msg: Optional[str] = None,
timestamp_override: Optional[datetime.datetime] = None,
footer: Optional[str] = None,
):
@@ -143,7 +144,8 @@ class ModLog:
log_message = await channel.send(content=content, embed=embed, files=files)
if additional_embeds:
- await channel.send("With the following embed(s):")
+ if additional_embeds_msg:
+ await channel.send(additional_embeds_msg)
for additional_embed in additional_embeds:
await channel.send(embed=additional_embed)
diff --git a/bot/cogs/rules.py b/bot/cogs/rules.py
new file mode 100644
index 000000000..eee506810
--- /dev/null
+++ b/bot/cogs/rules.py
@@ -0,0 +1,102 @@
+import re
+from typing import Optional
+
+from discord import Colour, Embed
+from discord.ext.commands import Bot, Context, command
+
+from bot.constants import Channels, Roles
+from bot.decorators import redirect_output
+from bot.pagination import LinePaginator
+
+STAFF = Roles.admin, Roles.moderator, Roles.owner
+
+
+class Rules:
+
+ def __init__(self, bot: Bot):
+ self.bot = bot
+
+ # We'll get the rules from the API when the
+ # site has been updated to the Django Framework.
+ # Hard-code the rules for now until the new RulesView is released.
+
+ self.rules = (
+ "Be polite, and do not spam.",
+
+ "Follow the [Discord Community Guidelines](https://discordapp.com/guidelines).",
+
+ "Don't intentionally make other people uncomfortable - if someone asks you to stop "
+ "discussing something, you should stop.",
+
+ "Be patient both with users asking questions, and the users answering them.",
+
+ "We will not help you with anything that might break a law or the terms of service "
+ "of any other community, site, service, or otherwise - No piracy, brute-forcing, "
+ "captcha circumvention, sneaker bots, or anything else of that nature.",
+
+ "Listen to and respect the staff members - we're here to help, but we're all human "
+ "beings.",
+
+ "All discussion should be kept within the relevant channels for the subject - See the "
+ "[channels page](https://pythondiscord.com/about/channels) for more information.",
+
+ "This is an English-speaking server, so please speak English to the best of your "
+ "ability - [Google Translate](https://translate.google.com/) should be fine if you're "
+ "not sure.",
+
+ "Keep all discussions safe for work - No gore, nudity, sexual soliciting, references "
+ "to suicide, or anything else of that nature",
+
+ "We do not allow advertisements for communities (including other Discord servers) or "
+ "commercial projects - Contact us directly if you want to discuss a partnership!"
+ )
+ self.default_desc = ("The rules and guidelines that apply to this community can be found on"
+ " our [rules page](https://pythondiscord.com/about/rules). We expect"
+ " all members of the community to have read and understood these."
+ )
+
+ @command(aliases=['r', 'rule'], name='rules')
+ @redirect_output(destination_channel=Channels.bot, bypass_roles=STAFF)
+ async def rules_command(self, ctx: Context, *, rules: Optional[str] = None):
+ """
+ Provides a link to the `rules` endpoint of the website, or displays
+ specific rules, if they are requested.
+
+ **`ctx`:** The Discord message context
+ **`rules`:** The rules a user wants to get.
+ """
+ rules_embed = Embed(title='Rules', color=Colour.blurple())
+ rules_embed.set_footer(text='https://pythondiscord.com/about/rules')
+
+ if not rules:
+ # Rules were not submitted. Return the default description.
+ rules_embed.description = self.default_desc
+ return await ctx.send(embed=rules_embed)
+
+ # Split the rules input by slash, comma or space
+ # Returns a list of ints if they're in range of rules index
+ rules_to_get = []
+ split_rules = re.split(r'[/, ]', rules)
+ for item in split_rules:
+ if not item.isdigit():
+ if not item:
+ continue
+ rule_match = re.search(r'\d?\d[:|-]1?\d', item)
+ if rule_match:
+ a, b = sorted([int(x)-1 for x in re.split(r'[:-]', rule_match.group())])
+ rules_to_get.extend(range(a, b+1))
+ else:
+ rules_to_get.append(int(item)-1)
+ final_rules = [
+ f'**{i+1}.** {self.rules[i]}' for i in sorted(rules_to_get) if i < len(self.rules)
+ ]
+
+ if not final_rules:
+ # No valid rules in rules input. Return the default description.
+ rules_embed.description = self.default_desc
+ return await ctx.send(embed=rules_embed)
+ await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3)
+
+
+def setup(bot):
+ bot.add_cog(Rules(bot))
diff --git a/bot/cogs/site.py b/bot/cogs/site.py
index 442e80cd2..e5fd645fb 100644
--- a/bot/cogs/site.py
+++ b/bot/cogs/site.py
@@ -92,22 +92,6 @@ class Site:
await ctx.send(embed=embed)
- @site_group.command(name="rules")
- async def site_rules(self, ctx: Context):
- """Info about the server's rules."""
-
- url = f"{URLs.site_schema}{URLs.site}/about/rules"
-
- embed = Embed(title="Rules")
- embed.set_footer(text=url)
- embed.colour = Colour.blurple()
- embed.description = (
- f"The rules and guidelines that apply to this community can be found on our [rules page]({url}). "
- "We expect all members of the community to have read and understood these."
- )
-
- await ctx.send(embed=embed)
-
def setup(bot):
bot.add_cog(Site(bot))
diff --git a/bot/constants.py b/bot/constants.py
index ab62cd79d..5b9b45c1c 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -372,6 +372,7 @@ class Roles(metaclass=YAMLGetter):
owner: int
verified: int
helpers: int
+ team_leader: int
class Guild(metaclass=YAMLGetter):
diff --git a/config-default.yml b/config-default.yml
index 3ce01916e..110dd12dd 100644
--- a/config-default.yml
+++ b/config-default.yml
@@ -134,6 +134,7 @@ guild:
verified: 352427296948486144
helpers: 267630620367257601
rockstars: &ROCKSTARS_ROLE 458226413825294336
+ team_leader: 501324292341104650
filter:
@@ -162,6 +163,9 @@ filter:
- 273944235143593984 # STEM
- 348658686962696195 # RLBot
- 531221516914917387 # Pallets
+ - 249111029668249601 # Gentoo
+ - 327254708534116352 # Adafruit
+ - 544525886180032552 # kennethreitz.org
domain_blacklist:
- pornhub.com