aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS27
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--.github/workflows/deploy.yml5
-rw-r--r--Pipfile1
-rw-r--r--Pipfile.lock49
-rw-r--r--bot/constants.py3
-rw-r--r--bot/exts/help_channels.py29
-rw-r--r--bot/exts/moderation/infraction/_scheduler.py22
-rw-r--r--bot/exts/moderation/infraction/superstarify.py77
-rw-r--r--bot/rules/discord_emojis.py8
-rw-r--r--config-default.yml3
-rw-r--r--deployment.yaml21
-rw-r--r--tests/bot/rules/test_discord_emojis.py29
13 files changed, 167 insertions, 109 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index cf5f1590d..5f5386222 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1,26 @@
-* @python-discord/core-developers
+# Request Joe and Dennis for any PR
+* @jb3 @Den4200
+
+# Extensions
+**/bot/exts/backend/sync/** @MarkKoz
+**/bot/exts/filters/*token_remover.py @MarkKoz
+**/bot/exts/moderation/*silence.py @MarkKoz
+bot/exts/info/codeblock/** @MarkKoz
+bot/exts/utils/extensions.py @MarkKoz
+bot/exts/utils/snekbox.py @MarkKoz
+bot/exts/help_channels.py @MarkKoz
+
+# Utils
+bot/utils/extensions.py @MarkKoz
+bot/utils/function.py @MarkKoz
+bot/utils/lock.py @MarkKoz
+bot/utils/scheduling.py @MarkKoz
+
+# Tests
+tests/_autospec.py @MarkKoz
+tests/bot/exts/test_cogs.py @MarkKoz
+
+# CI & Docker
+.github/workflows/** @MarkKoz
+Dockerfile @MarkKoz
+docker-compose.yml @MarkKoz
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 706ab462f..6152f1543 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -10,7 +10,7 @@ on:
jobs:
build:
- if: github.event.workflow_run.conclusion == 'success'
+ if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push'
name: Build & Push
runs-on: ubuntu-latest
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 90555a8ee..5a4aede30 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -23,6 +23,9 @@ jobs:
- name: Checkout code
uses: actions/checkout@v2
+ with:
+ repository: python-discord/kubernetes
+ token: ${{ secrets.REPO_TOKEN }}
- name: Authenticate with Kubernetes
uses: azure/k8s-set-context@v1
@@ -34,6 +37,6 @@ jobs:
uses: Azure/k8s-deploy@v1
with:
manifests: |
- deployment.yaml
+ bot/deployment.yaml
images: 'ghcr.io/python-discord/bot:${{ steps.sha_tag.outputs.tag }}'
kubectl-version: 'latest'
diff --git a/Pipfile b/Pipfile
index 103ce84cf..ae80ae2ae 100644
--- a/Pipfile
+++ b/Pipfile
@@ -26,6 +26,7 @@ requests = "~=2.22"
sentry-sdk = "~=0.14"
sphinx = "~=2.2"
statsd = "~=3.3"
+emoji = "~=0.6"
[dev-packages]
coverage = "~=5.0"
diff --git a/Pipfile.lock b/Pipfile.lock
index 6a6a1aaf6..541db1627 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "ca6b100f7ee2e6e01eec413a754fc11be064e965a255b2c4927d4a2dd1c451ec"
+ "sha256": "3ccb368599709d2970f839fc3721cfeebcd5a2700fed7231b2ce38a080828325"
},
"pipfile-spec": 6,
"requires": {
@@ -222,6 +222,13 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.16"
},
+ "emoji": {
+ "hashes": [
+ "sha256:e42da4f8d648f8ef10691bc246f682a1ec6b18373abfd9be10ec0b398823bd11"
+ ],
+ "index": "pypi",
+ "version": "==0.6.0"
+ },
"fakeredis": {
"hashes": [
"sha256:8070b7fce16f828beaef2c757a4354af91698685d5232404f1aeeb233529c7a5",
@@ -548,16 +555,18 @@
},
"pyyaml": {
"hashes": [
- "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
+ "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
+ "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a",
+ "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
- "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
+ "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e",
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
- "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
+ "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
+ "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
- "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
+ "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
- "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
- "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
+ "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
],
"index": "pypi",
@@ -581,11 +590,11 @@
},
"sentry-sdk": {
"hashes": [
- "sha256:81d7a5d8ca0b13a16666e8280127b004565aa988bfeec6481e98a8601804b215",
- "sha256:fd48f627945511c140546939b4d73815be4860cd1d2b9149577d7f6563e7bd60"
+ "sha256:1052f0ed084e532f66cb3e4ba617960d820152aee8b93fc6c05bd53861768c1c",
+ "sha256:4c42910a55a6b1fe694d5e4790d5188d105d77b5a6346c1c64cbea8c06c0e8b7"
],
"index": "pypi",
- "version": "==0.19.3"
+ "version": "==0.19.4"
},
"six": {
"hashes": [
@@ -793,11 +802,11 @@
},
"coveralls": {
"hashes": [
- "sha256:4430b862baabb3cf090d36d84d331966615e4288d8a8c5957e0fd456d0dd8bd6",
- "sha256:b3b60c17b03a0dee61952a91aed6f131e0b2ac8bd5da909389c53137811409e1"
+ "sha256:2301a19500b06649d2ec4f2858f9c69638d7699a4c63027c5d53daba666147cc",
+ "sha256:b990ba1f7bc4288e63340be0433698c1efe8217f78c689d254c2540af3d38617"
],
"index": "pypi",
- "version": "==2.1.2"
+ "version": "==2.2.0"
},
"distlib": {
"hashes": [
@@ -961,16 +970,18 @@
},
"pyyaml": {
"hashes": [
- "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
+ "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
+ "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a",
+ "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
- "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
+ "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e",
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
- "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
+ "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
+ "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
- "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
+ "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
- "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
- "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
+ "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
],
"index": "pypi",
diff --git a/bot/constants.py b/bot/constants.py
index 2126b2b37..6bb6aacd2 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -248,6 +248,7 @@ class Colours(metaclass=YAMLGetter):
soft_red: int
soft_green: int
soft_orange: int
+ bright_green: int
class DuckPond(metaclass=YAMLGetter):
@@ -354,6 +355,8 @@ class Icons(metaclass=YAMLGetter):
voice_state_green: str
voice_state_red: str
+ green_checkmark: str
+
class CleanMessages(metaclass=YAMLGetter):
section = "bot"
diff --git a/bot/exts/help_channels.py b/bot/exts/help_channels.py
index 4fd4896df..5676728e9 100644
--- a/bot/exts/help_channels.py
+++ b/bot/exts/help_channels.py
@@ -28,17 +28,21 @@ This is a Python help channel. You can claim your own help channel in the Python
"""
AVAILABLE_MSG = f"""
-This help channel is now **available**, which means that you can claim it by simply typing your \
-question into it. Once claimed, the channel will move into the **Python Help: Occupied** category, \
-and will be yours until it has been inactive for {constants.HelpChannels.idle_minutes} minutes or \
-is closed manually with `!close`. When that happens, it will be set to **dormant** and moved into \
-the **Help: Dormant** category.
-
-Try to write the best question you can by providing a detailed description and telling us what \
-you've tried already. For more information on asking a good question, \
-check out our guide on **[asking good questions]({ASKING_GUIDE_URL})**.
+**Send your question here to claim the channel**
+This channel will be dedicated to answering your question only. Others will try to answer and help you solve the issue.
+
+**Keep in mind:**
+• It's always ok to just ask your question. You don't need permission.
+• Explain what you expect to happen and what actually happens.
+• Include a code sample and error message, if you got any.
+
+For more tips, check out our guide on **[asking good questions]({ASKING_GUIDE_URL})**.
"""
+AVAILABLE_TITLE = "Available help channel"
+
+AVAILABLE_FOOTER = f"Closes after {constants.HelpChannels.idle_minutes} minutes of inactivity or when you send !close."
+
DORMANT_MSG = f"""
This help channel has been marked as **dormant**, and has been moved into the **Help: Dormant** \
category at the bottom of the channel list. It is no longer possible to send messages in this \
@@ -845,7 +849,12 @@ class HelpChannels(commands.Cog):
channel_info = f"#{channel} ({channel.id})"
log.trace(f"Sending available message in {channel_info}.")
- embed = discord.Embed(description=AVAILABLE_MSG)
+ embed = discord.Embed(
+ color=constants.Colours.bright_green,
+ description=AVAILABLE_MSG,
+ )
+ embed.set_author(name=AVAILABLE_TITLE, icon_url=constants.Icons.green_checkmark)
+ embed.set_footer(text=AVAILABLE_FOOTER)
msg = await self.get_last_message(channel)
if self.match_bot_embed(msg, DORMANT_MSG):
diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py
index bebade0ae..c062ae7f8 100644
--- a/bot/exts/moderation/infraction/_scheduler.py
+++ b/bot/exts/moderation/infraction/_scheduler.py
@@ -82,15 +82,27 @@ class InfractionScheduler:
ctx: Context,
infraction: _utils.Infraction,
user: UserSnowflake,
- action_coro: t.Optional[t.Awaitable] = None
- ) -> None:
- """Apply an infraction to the user, log the infraction, and optionally notify the user."""
+ action_coro: t.Optional[t.Awaitable] = None,
+ user_reason: t.Optional[str] = None,
+ additional_info: str = "",
+ ) -> bool:
+ """
+ Apply an infraction to the user, log the infraction, and optionally notify the user.
+
+ `user_reason`, if provided, will be sent to the user in place of the infraction reason.
+ `additional_info` will be attached to the text field in the mod-log embed.
+
+ Returns whether or not the infraction succeeded.
+ """
infr_type = infraction["type"]
icon = _utils.INFRACTION_ICONS[infr_type][0]
reason = infraction["reason"]
expiry = time.format_infraction_with_duration(infraction["expires_at"])
id_ = infraction['id']
+ if user_reason is None:
+ user_reason = reason
+
log.trace(f"Applying {infr_type} infraction #{id_} to {user}.")
# Default values for the confirmation message and mod log.
@@ -126,7 +138,7 @@ class InfractionScheduler:
log.error(f"Failed to DM {user.id}: could not fetch user (status {e.status})")
else:
# Accordingly display whether the user was successfully notified via DM.
- if await _utils.notify_infraction(user, " ".join(infr_type.split("_")).title(), expiry, reason, icon):
+ if await _utils.notify_infraction(user, infr_type.replace("_", " ").title(), expiry, user_reason, icon):
dm_result = ":incoming_envelope: "
dm_log_text = "\nDM: Sent"
@@ -198,12 +210,14 @@ class InfractionScheduler:
Member: {messages.format_user(user)}
Actor: {ctx.author.mention}{dm_log_text}{expiry_log_text}
Reason: {reason}
+ {additional_info}
"""),
content=log_content,
footer=f"ID {infraction['id']}"
)
log.info(f"Applied {infr_type} infraction #{id_} to {user}.")
+ return not failed
async def pardon_infraction(
self,
diff --git a/bot/exts/moderation/infraction/superstarify.py b/bot/exts/moderation/infraction/superstarify.py
index adfe42fcd..96dfb562f 100644
--- a/bot/exts/moderation/infraction/superstarify.py
+++ b/bot/exts/moderation/infraction/superstarify.py
@@ -5,7 +5,7 @@ import textwrap
import typing as t
from pathlib import Path
-from discord import Colour, Embed, Member
+from discord import Embed, Member
from discord.ext.commands import Cog, Context, command, has_any_role
from discord.utils import escape_markdown
@@ -143,57 +143,44 @@ class Superstarify(InfractionScheduler, Cog):
forced_nick = self.get_nick(id_, member.id)
expiry_str = format_infraction(infraction["expires_at"])
- # Apply the infraction and schedule the expiration task.
- log.debug(f"Changing nickname of {member} to {forced_nick}.")
- self.mod_log.ignore(constants.Event.member_update, member.id)
- await member.edit(nick=forced_nick, reason=reason)
- self.schedule_expiration(infraction)
+ # Apply the infraction
+ async def action() -> None:
+ log.debug(f"Changing nickname of {member} to {forced_nick}.")
+ self.mod_log.ignore(constants.Event.member_update, member.id)
+ await member.edit(nick=forced_nick, reason=reason)
old_nick = escape_markdown(old_nick)
forced_nick = escape_markdown(forced_nick)
- # Send a DM to the user to notify them of their new infraction.
- await _utils.notify_infraction(
- user=member,
- infr_type="Superstarify",
- expires_at=expiry_str,
- icon_url=_utils.INFRACTION_ICONS["superstar"][0],
- reason=f"Your nickname didn't comply with our [nickname policy]({NICKNAME_POLICY_URL})."
+ superstar_reason = f"Your nickname didn't comply with our [nickname policy]({NICKNAME_POLICY_URL})."
+ nickname_info = textwrap.dedent(f"""
+ Old nickname: `{old_nick}`
+ New nickname: `{forced_nick}`
+ """).strip()
+
+ successful = await self.apply_infraction(
+ ctx, infraction, member, action(),
+ user_reason=superstar_reason,
+ additional_info=nickname_info
)
- # Send an embed with the infraction information to the invoking context.
- log.trace(f"Sending superstar #{id_} embed.")
- embed = Embed(
- title="Congratulations!",
- colour=constants.Colours.soft_orange,
- description=(
- f"Your previous nickname, **{old_nick}**, "
- f"was so bad that we have decided to change it. "
- f"Your new nickname will be **{forced_nick}**.\n\n"
- f"You will be unable to change your nickname until **{expiry_str}**.\n\n"
- "If you're confused by this, please read our "
- f"[official nickname policy]({NICKNAME_POLICY_URL})."
+ # Send an embed with the infraction information to the invoking context if
+ # superstar was successful.
+ if successful:
+ log.trace(f"Sending superstar #{id_} embed.")
+ embed = Embed(
+ title="Congratulations!",
+ colour=constants.Colours.soft_orange,
+ description=(
+ f"Your previous nickname, **{old_nick}**, "
+ f"was so bad that we have decided to change it. "
+ f"Your new nickname will be **{forced_nick}**.\n\n"
+ f"You will be unable to change your nickname until **{expiry_str}**.\n\n"
+ "If you're confused by this, please read our "
+ f"[official nickname policy]({NICKNAME_POLICY_URL})."
+ )
)
- )
- await ctx.send(embed=embed)
-
- # Log to the mod log channel.
- log.trace(f"Sending apply mod log for superstar #{id_}.")
- await self.mod_log.send_log_message(
- icon_url=_utils.INFRACTION_ICONS["superstar"][0],
- colour=Colour.gold(),
- title="Member achieved superstardom",
- thumbnail=member.avatar_url_as(static_format="png"),
- text=textwrap.dedent(f"""
- Member: {member.mention}
- Actor: {ctx.message.author.mention}
- Expires: {expiry_str}
- Old nickname: `{old_nick}`
- New nickname: `{forced_nick}`
- Reason: {reason}
- """),
- footer=f"ID {id_}"
- )
+ await ctx.send(embed=embed)
@command(name="unsuperstarify", aliases=("release_nick", "unstar"))
async def unsuperstarify(self, ctx: Context, member: Member) -> None:
diff --git a/bot/rules/discord_emojis.py b/bot/rules/discord_emojis.py
index 6e47f0197..41faf7ee8 100644
--- a/bot/rules/discord_emojis.py
+++ b/bot/rules/discord_emojis.py
@@ -2,16 +2,17 @@ import re
from typing import Dict, Iterable, List, Optional, Tuple
from discord import Member, Message
+from emoji import demojize
-DISCORD_EMOJI_RE = re.compile(r"<:\w+:\d+>")
+DISCORD_EMOJI_RE = re.compile(r"<:\w+:\d+>|:\w+:")
CODE_BLOCK_RE = re.compile(r"```.*?```", flags=re.DOTALL)
async def apply(
last_message: Message, recent_messages: List[Message], config: Dict[str, int]
) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]:
- """Detects total Discord emojis (excluding Unicode emojis) exceeding the limit sent by a single user."""
+ """Detects total Discord emojis exceeding the limit sent by a single user."""
relevant_messages = tuple(
msg
for msg in recent_messages
@@ -19,8 +20,9 @@ async def apply(
)
# Get rid of code blocks in the message before searching for emojis.
+ # Convert Unicode emojis to :emoji: format to get their count.
total_emojis = sum(
- len(DISCORD_EMOJI_RE.findall(CODE_BLOCK_RE.sub("", msg.content)))
+ len(DISCORD_EMOJI_RE.findall(demojize(CODE_BLOCK_RE.sub("", msg.content))))
for msg in relevant_messages
)
diff --git a/config-default.yml b/config-default.yml
index 89493c4de..60eb437af 100644
--- a/config-default.yml
+++ b/config-default.yml
@@ -27,6 +27,7 @@ style:
soft_red: 0xcd6d6d
soft_green: 0x68c290
soft_orange: 0xf9cb54
+ bright_green: 0x01d277
emojis:
defcon_disabled: "<:defcondisabled:470326273952972810>"
@@ -119,6 +120,8 @@ style:
voice_state_green: "https://cdn.discordapp.com/emojis/656899770094452754.png"
voice_state_red: "https://cdn.discordapp.com/emojis/656899769905709076.png"
+ green_checkmark: "https://raw.githubusercontent.com/python-discord/branding/master/icons/checkmark/green-checkmark-dist.png"
+
guild:
id: 267624335836053506
diff --git a/deployment.yaml b/deployment.yaml
deleted file mode 100644
index ca5ff5941..000000000
--- a/deployment.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: bot
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: bot
- template:
- metadata:
- labels:
- app: bot
- spec:
- containers:
- - name: bot
- image: ghcr.io/python-discord/bot:latest
- imagePullPolicy: Always
- envFrom:
- - secretRef:
- name: bot-env
diff --git a/tests/bot/rules/test_discord_emojis.py b/tests/bot/rules/test_discord_emojis.py
index 9a72723e2..66c2d9f92 100644
--- a/tests/bot/rules/test_discord_emojis.py
+++ b/tests/bot/rules/test_discord_emojis.py
@@ -5,11 +5,12 @@ from tests.bot.rules import DisallowedCase, RuleTest
from tests.helpers import MockMessage
discord_emoji = "<:abcd:1234>" # Discord emojis follow the format <:name:id>
+unicode_emoji = "🧪"
-def make_msg(author: str, n_emojis: int) -> MockMessage:
+def make_msg(author: str, n_emojis: int, emoji: str = discord_emoji) -> MockMessage:
"""Build a MockMessage instance with content containing `n_emojis` arbitrary emojis."""
- return MockMessage(author=author, content=discord_emoji * n_emojis)
+ return MockMessage(author=author, content=emoji * n_emojis)
class DiscordEmojisRuleTests(RuleTest):
@@ -20,16 +21,22 @@ class DiscordEmojisRuleTests(RuleTest):
self.config = {"max": 2, "interval": 10}
async def test_allows_messages_within_limit(self):
- """Cases with a total amount of discord emojis within limit."""
+ """Cases with a total amount of discord and unicode emojis within limit."""
cases = (
[make_msg("bob", 2)],
[make_msg("alice", 1), make_msg("bob", 2), make_msg("alice", 1)],
+ [make_msg("bob", 2, unicode_emoji)],
+ [
+ make_msg("alice", 1, unicode_emoji),
+ make_msg("bob", 2, unicode_emoji),
+ make_msg("alice", 1, unicode_emoji)
+ ],
)
await self.run_allowed(cases)
async def test_disallows_messages_beyond_limit(self):
- """Cases with more than the allowed amount of discord emojis."""
+ """Cases with more than the allowed amount of discord and unicode emojis."""
cases = (
DisallowedCase(
[make_msg("bob", 3)],
@@ -41,6 +48,20 @@ class DiscordEmojisRuleTests(RuleTest):
("alice",),
4,
),
+ DisallowedCase(
+ [make_msg("bob", 3, unicode_emoji)],
+ ("bob",),
+ 3,
+ ),
+ DisallowedCase(
+ [
+ make_msg("alice", 2, unicode_emoji),
+ make_msg("bob", 2, unicode_emoji),
+ make_msg("alice", 2, unicode_emoji)
+ ],
+ ("alice",),
+ 4
+ )
)
await self.run_disallowed(cases)