aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar wookie184 <[email protected]>2023-03-03 16:34:52 +0000
committerGravatar GitHub <[email protected]>2023-03-03 16:34:52 +0000
commitf2e009d7a793d15d32812cfb44a4c1f7706190a3 (patch)
tree557a6cb33bde19f0c7c450483a60c249f1816653
parentEdit comment to match wookie's review (diff)
parentMerge pull request #2430 from python-discord/dependabot/pip/pydis-core-9.5.1 (diff)
Merge branch 'main' into snekbox/handling-input-error-message
-rw-r--r--.github/dependabot.yml12
-rw-r--r--.github/workflows/build-deploy.yml80
-rw-r--r--.github/workflows/build.yml63
-rw-r--r--.github/workflows/deploy.yml46
-rw-r--r--.github/workflows/lint-test.yml15
-rw-r--r--.github/workflows/main.yml47
-rw-r--r--.github/workflows/sentry_release.yml9
-rw-r--r--.github/workflows/status_embed.yaml4
-rw-r--r--bot/exts/info/help.py11
-rw-r--r--bot/exts/recruitment/talentpool/_review.py29
-rw-r--r--poetry.lock16
-rw-r--r--pyproject.toml2
-rw-r--r--tests/bot/exts/recruitment/talentpool/test_review.py14
13 files changed, 194 insertions, 154 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..f60e94af8
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+version: 2
+updates:
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ reviewers:
+ - "python-discord/devops"
diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml
new file mode 100644
index 000000000..2582a4113
--- /dev/null
+++ b/.github/workflows/build-deploy.yml
@@ -0,0 +1,80 @@
+name: Build & Deploy
+
+on:
+ workflow_call:
+ inputs:
+ sha-tag:
+ description: "A short-form SHA tag for the commit that triggered this workflow"
+ required: true
+ type: string
+
+
+jobs:
+ build:
+ name: Build & Push
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ # The current version (v2) of Docker's build-push action uses
+ # buildx, which comes with BuildKit features that help us speed
+ # up our builds using additional cache features. Buildx also
+ # has a lot of other features that are not as relevant to us.
+ #
+ # See https://github.com/docker/build-push-action
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Login to Github Container Registry
+ uses: docker/login-action@v2
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ # Build and push the container to the GitHub Container
+ # Repository. The container will be tagged as "latest"
+ # and with the short SHA of the commit.
+
+ - name: Build and push
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ file: ./Dockerfile
+ push: true
+ cache-from: type=registry,ref=ghcr.io/python-discord/bot:latest
+ cache-to: type=inline
+ tags: |
+ ghcr.io/python-discord/bot:latest
+ ghcr.io/python-discord/bot:${{ inputs.sha-tag }}
+ build-args: |
+ git_sha=${{ github.sha }}
+
+ deploy:
+ name: Deploy
+ needs: build
+ runs-on: ubuntu-latest
+ environment: production
+ steps:
+ - name: Checkout Kubernetes repository
+ uses: actions/checkout@v3
+ with:
+ repository: python-discord/kubernetes
+
+ - uses: azure/setup-kubectl@v3
+
+ - name: Authenticate with Kubernetes
+ uses: azure/k8s-set-context@v3
+ with:
+ method: kubeconfig
+ kubeconfig: ${{ secrets.KUBECONFIG }}
+
+ - name: Deploy to Kubernetes
+ uses: azure/k8s-deploy@v4
+ with:
+ manifests: |
+ namespaces/default/bot/deployment.yaml
+ images: 'ghcr.io/python-discord/bot:${{ inputs.sha-tag }}'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index f8f2c8888..000000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-name: Build
-
-on:
- workflow_run:
- workflows: ["Lint & Test"]
- branches:
- - main
- types:
- - completed
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- build:
- if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push'
- name: Build & Push
- runs-on: ubuntu-latest
-
- steps:
- # Create a commit SHA-based tag for the container repositories
- - name: Create SHA Container Tag
- id: sha_tag
- run: |
- tag=$(cut -c 1-7 <<< $GITHUB_SHA)
- echo "::set-output name=tag::$tag"
-
- - name: Checkout code
- uses: actions/checkout@v2
-
- # The current version (v2) of Docker's build-push action uses
- # buildx, which comes with BuildKit features that help us speed
- # up our builds using additional cache features. Buildx also
- # has a lot of other features that are not as relevant to us.
- #
- # See https://github.com/docker/build-push-action
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
-
- - name: Login to Github Container Registry
- uses: docker/login-action@v1
- with:
- registry: ghcr.io
- username: ${{ github.repository_owner }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- # Build and push the container to the GitHub Container
- # Repository. The container will be tagged as "latest"
- # and with the short SHA of the commit.
- - name: Build and push
- uses: docker/build-push-action@v2
- with:
- context: .
- file: ./Dockerfile
- push: true
- cache-from: type=registry,ref=ghcr.io/python-discord/bot:latest
- cache-to: type=inline
- tags: |
- ghcr.io/python-discord/bot:latest
- ghcr.io/python-discord/bot:${{ steps.sha_tag.outputs.tag }}
- build-args: |
- git_sha=${{ github.sha }}
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
deleted file mode 100644
index 79eef8821..000000000
--- a/.github/workflows/deploy.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-name: Deploy
-
-on:
- workflow_run:
- workflows: ["Build"]
- branches:
- - main
- types:
- - completed
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- build:
- environment: production
- if: github.event.workflow_run.conclusion == 'success'
- name: Build & Push
- runs-on: ubuntu-latest
-
- steps:
- - name: Create SHA Container Tag
- id: sha_tag
- run: |
- tag=$(cut -c 1-7 <<< $GITHUB_SHA)
- echo "::set-output name=tag::$tag"
-
- - name: Checkout code
- uses: actions/checkout@v2
- with:
- repository: python-discord/kubernetes
-
- - name: Authenticate with Kubernetes
- uses: azure/k8s-set-context@v1
- with:
- method: kubeconfig
- kubeconfig: ${{ secrets.KUBECONFIG }}
-
- - name: Deploy to Kubernetes
- uses: Azure/k8s-deploy@v1
- with:
- manifests: |
- namespaces/default/bot/deployment.yaml
- images: 'ghcr.io/python-discord/bot:${{ steps.sha_tag.outputs.tag }}'
- kubectl-version: 'latest'
diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml
index a331659e6..051ca2265 100644
--- a/.github/workflows/lint-test.yml
+++ b/.github/workflows/lint-test.yml
@@ -1,14 +1,7 @@
name: Lint & Test
on:
- push:
- branches:
- - main
- pull_request:
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
+ workflow_call
jobs:
lint-test:
@@ -35,13 +28,11 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install Python Dependencies
- uses: HassanAbouelela/actions/setup-python@setup-python_v1.3.1
+ uses: HassanAbouelela/actions/setup-python@setup-python_v1.4.0
with:
- # Set dev=true to install flake8 extensions, which are dev dependencies
- dev: true
python_version: '3.10'
# Check all of our non-dev dependencies are compatible with the MIT license.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 000000000..0f972b16f
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,47 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+
+jobs:
+ lint-test:
+ uses: ./.github/workflows/lint-test.yml
+
+
+ generate-sha-tag:
+ if: github.ref == 'refs/heads/main'
+ runs-on: ubuntu-latest
+ outputs:
+ sha-tag: ${{ steps.sha-tag.outputs.sha-tag }}
+ steps:
+ - name: Create SHA Container tag
+ id: sha-tag
+ run: |
+ tag=$(cut -c 1-7 <<< $GITHUB_SHA)
+ echo "sha-tag=$tag" >> $GITHUB_OUTPUT
+
+
+ build-deploy:
+ if: github.ref == 'refs/heads/main'
+ uses: ./.github/workflows/build-deploy.yml
+ needs:
+ - lint-test
+ - generate-sha-tag
+ with:
+ sha-tag: ${{ needs.generate-sha-tag.outputs.sha-tag }}
+ secrets: inherit
+
+ sentry-release:
+ if: github.ref == 'refs/heads/main'
+ uses: ./.github/workflows/sentry_release.yml
+ needs: build-deploy
+ secrets: inherit
diff --git a/.github/workflows/sentry_release.yml b/.github/workflows/sentry_release.yml
index 48f5e50f4..cdc8f37d5 100644
--- a/.github/workflows/sentry_release.yml
+++ b/.github/workflows/sentry_release.yml
@@ -1,20 +1,15 @@
name: Create Sentry release
on:
- push:
- branches:
- - main
+ workflow_call
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
jobs:
create_sentry_release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@main
+ uses: actions/checkout@v3
- name: Create a Sentry.io release
uses: tclindner/[email protected]
diff --git a/.github/workflows/status_embed.yaml b/.github/workflows/status_embed.yaml
index 4178c366d..1923965ab 100644
--- a/.github/workflows/status_embed.yaml
+++ b/.github/workflows/status_embed.yaml
@@ -3,9 +3,7 @@ name: Status Embed
on:
workflow_run:
workflows:
- - Lint & Test
- - Build
- - Deploy
+ - CI
types:
- completed
diff --git a/bot/exts/info/help.py b/bot/exts/info/help.py
index 48f840e51..8950f4936 100644
--- a/bot/exts/info/help.py
+++ b/bot/exts/info/help.py
@@ -244,15 +244,20 @@ class CustomHelpCommand(HelpCommand):
choices.update(cog.category for cog in self.context.bot.cogs.values() if hasattr(cog, "category"))
return choices
- async def command_not_found(self, string: str) -> "HelpQueryNotFound":
+ async def command_not_found(self, query: str) -> "HelpQueryNotFound":
"""
Handles when a query does not match a valid command, group, cog or category.
Will return an instance of the `HelpQueryNotFound` exception with the error message and possible matches.
"""
choices = list(await self.get_all_help_choices())
- result = process.extract(default_process(string), choices, scorer=fuzz.ratio, score_cutoff=60, processor=None)
- return HelpQueryNotFound(f'Query "{string}" not found.', {choice[0]: choice[1] for choice in result})
+ result = process.extract(default_process(query), choices, scorer=fuzz.ratio, score_cutoff=60, processor=None)
+
+ # Trim query to avoid embed limits when sending the error.
+ if len(query) >= 100:
+ query = query[:100] + "..."
+
+ return HelpQueryNotFound(f'Query "{query}" not found.', {choice[0]: choice[1] for choice in result})
async def subcommand_not_found(self, command: Command, string: str) -> "HelpQueryNotFound":
"""
diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py
index f41e08fe1..b9e76b60f 100644
--- a/bot/exts/recruitment/talentpool/_review.py
+++ b/bot/exts/recruitment/talentpool/_review.py
@@ -9,6 +9,7 @@ from datetime import datetime, timedelta, timezone
from typing import List, Optional, Union
import discord
+from async_rediscache import RedisCache
from discord import Embed, Emoji, Member, Message, NotFound, PartialMessage, TextChannel
from pydis_core.site_api import ResponseCodeError
@@ -52,6 +53,11 @@ NOMINATION_MESSAGE_REGEX = re.compile(
class Reviewer:
"""Manages, formats, and publishes reviews of helper nominees."""
+ # RedisCache[
+ # "last_vote_date": float | POSIX UTC timestamp.
+ # ]
+ status_cache = RedisCache()
+
def __init__(self, bot: Bot, nomination_api: NominationAPI):
self.bot = bot
self.api = nomination_api
@@ -82,8 +88,18 @@ class Reviewer:
"""
voting_channel = self.bot.get_channel(Channels.nomination_voting)
+ last_vote_timestamp = await self.status_cache.get("last_vote_date")
+ if last_vote_timestamp:
+ last_vote_date = datetime.fromtimestamp(last_vote_timestamp, tz=timezone.utc)
+ time_since_last_vote = datetime.now(timezone.utc) - last_vote_date
+
+ if time_since_last_vote < MIN_REVIEW_INTERVAL:
+ log.debug("Most recent review was less than %s ago, cancelling check", MIN_REVIEW_INTERVAL)
+ return False
+ else:
+ log.info("Date of last vote not found in cache, a vote may be sent early")
+
review_count = 0
- is_first_message = True
async for msg in voting_channel.history():
# Try and filter out any non-review messages. We also only want to count
# one message from reviews split over multiple messages. We use fixed text
@@ -91,14 +107,6 @@ class Reviewer:
if not msg.author.bot or "for Helper!" not in msg.content:
continue
- if is_first_message:
- time_since_message_created = datetime.now(timezone.utc) - msg.created_at
- if time_since_message_created < MIN_REVIEW_INTERVAL:
- log.debug("Most recent review was less than %s ago, cancelling check", MIN_REVIEW_INTERVAL)
- return False
-
- is_first_message = False
-
review_count += 1
if review_count >= MAX_ONGOING_REVIEWS:
@@ -182,6 +190,9 @@ class Reviewer:
)
message = await thread.send(f"<@&{Roles.mod_team}> <@&{Roles.admins}>")
+ now = datetime.now(tz=timezone.utc)
+ await self.status_cache.set("last_vote_date", now.timestamp())
+
await self.api.edit_nomination(nomination.id, reviewed=True, thread_id=thread.id)
bump_cog: ThreadBumper = self.bot.get_cog("ThreadBumper")
diff --git a/poetry.lock b/poetry.lock
index db694b8f9..924f1645e 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -454,14 +454,14 @@ cli = ["clevercsv (==0.7.4)", "click (==8.1.3)", "pyyaml (==6.0)", "toml (==0.10
[[package]]
name = "discord-py"
-version = "2.2.0"
+version = "2.2.2"
description = "A Python wrapper for the Discord API"
category = "main"
optional = false
python-versions = ">=3.8.0"
files = [
- {file = "discord.py-2.2.0-py3-none-any.whl", hash = "sha256:012e98571af6847467e81f9501bbe4c6ebfe292c842f5ef8e951908839ee1cd0"},
- {file = "discord.py-2.2.0.tar.gz", hash = "sha256:a92d69ab6f982998693d0c371ea19235fa0f9900b50068fc461086d02c33e6bb"},
+ {file = "discord.py-2.2.2-py3-none-any.whl", hash = "sha256:38fc52a784727b8e5e5749267089400035b187a009028eddfabeb182abcc6d52"},
+ {file = "discord.py-2.2.2.tar.gz", hash = "sha256:b9944056bcb5711b2d04088848fd004466cf117c15c84fa798bf55470f28275f"},
]
[package.dependencies]
@@ -1479,20 +1479,20 @@ email = ["email-validator (>=1.0.3)"]
[[package]]
name = "pydis-core"
-version = "9.5.0"
+version = "9.5.1"
description = "PyDis core provides core functionality and utility to the bots of the Python Discord community."
category = "main"
optional = false
python-versions = ">=3.10.0,<3.12.0"
files = [
- {file = "pydis_core-9.5.0-py3-none-any.whl", hash = "sha256:35834274a80b86a5426f27cb546b3fada8a5711bbf01bbcf1b0a8860a2afee94"},
- {file = "pydis_core-9.5.0.tar.gz", hash = "sha256:1cf9c223af9b5377e08cc0eb046a12130518e4743afbdd4f052d5556b7dae805"},
+ {file = "pydis_core-9.5.1-py3-none-any.whl", hash = "sha256:50bbf1800fe228dd60ba6624615815f45139c105512ef701c556ee7dedaa91eb"},
+ {file = "pydis_core-9.5.1.tar.gz", hash = "sha256:83b89117def529c8b130f22c9f8cd46211df6329b039eedd5020098e656aa198"},
]
[package.dependencies]
aiodns = "3.0.0"
async-rediscache = {version = "1.0.0rc2", extras = ["fakeredis"], optional = true, markers = "extra == \"async-rediscache\""}
-"discord.py" = "2.2.0"
+"discord.py" = "2.2.2"
statsd = "4.0.1"
[package.extras]
@@ -2314,4 +2314,4 @@ multidict = ">=4.0"
[metadata]
lock-version = "2.0"
python-versions = "3.10.*"
-content-hash = "95d9251abd0a6125c99cc0c290fd8f97e9003cfe95cde3666ccc1817f751e814"
+content-hash = "9e554d23838cde31200630eda42ce0f1ead91a0f521d10362037f6d4290d3529"
diff --git a/pyproject.toml b/pyproject.toml
index 13efe1f34..8950bcbc8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,7 @@ license = "MIT"
python = "3.10.*"
# See https://bot-core.pythondiscord.com/ for docs.
-pydis_core = { version = "9.5.0", extras = ["async-rediscache"] }
+pydis_core = { version = "9.5.1", extras = ["async-rediscache"] }
redis = "4.3.5"
fakeredis = { version = "2.0.0", extras = ["lua"] }
diff --git a/tests/bot/exts/recruitment/talentpool/test_review.py b/tests/bot/exts/recruitment/talentpool/test_review.py
index 03c78ab08..1ddb73ab0 100644
--- a/tests/bot/exts/recruitment/talentpool/test_review.py
+++ b/tests/bot/exts/recruitment/talentpool/test_review.py
@@ -67,6 +67,7 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase):
MockMessage(author=self.bot_user, content="Not a review", created_at=not_too_recent),
MockMessage(author=self.bot_user, content="Not a review", created_at=not_too_recent),
],
+ not_too_recent.timestamp(),
True,
),
@@ -77,6 +78,7 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase):
MockMessage(author=self.bot_user, content="Zig for Helper!", created_at=not_too_recent),
MockMessage(author=self.bot_user, content="Scaleios for Helper!", created_at=not_too_recent),
],
+ not_too_recent.timestamp(),
False,
),
@@ -85,6 +87,7 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase):
[
MockMessage(author=self.bot_user, content="Chrisjl for Helper!", created_at=too_recent),
],
+ too_recent.timestamp(),
False,
),
@@ -96,18 +99,25 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase):
MockMessage(author=self.bot_user, content="wookie for Helper!", created_at=not_too_recent),
MockMessage(author=self.bot_user, content="Not a review", created_at=not_too_recent),
],
+ not_too_recent.timestamp(),
True,
),
# No messages, so ready.
- ([], True),
+ ([], None, True),
)
- for messages, expected in cases:
+ for messages, last_review_timestamp, expected in cases:
with self.subTest(messages=messages, expected=expected):
self.voting_channel.history = AsyncIterator(messages)
+
+ cache_get_mock = AsyncMock(return_value=last_review_timestamp)
+ self.reviewer.status_cache.get = cache_get_mock
+
res = await self.reviewer.is_ready_for_review()
+
self.assertIs(res, expected)
+ cache_get_mock.assert_called_with("last_vote_date")
@patch("bot.exts.recruitment.talentpool._review.MIN_NOMINATION_TIME", timedelta(days=7))
async def test_get_nomination_to_review(self):