Skip to content

refactor: Phase 1 — shared helpers rollout (respond_error + typed config accessors)#122

Merged
rolling-codes merged 8 commits into
mainfrom
refactor/dry-phase1-config-helpers
Jul 25, 2026
Merged

refactor: Phase 1 — shared helpers rollout (respond_error + typed config accessors)#122
rolling-codes merged 8 commits into
mainfrom
refactor/dry-phase1-config-helpers

Conversation

@rolling-codes

@rolling-codes rolling-codes commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • New helpers in easycord/plugins/_shared.py: respond_error(ctx, msg), get_id, set_id, get_ids, set_ids — pure over ServerConfig, no I/O or lock changes
  • respond_error adopted across 18 plugins (~100 sites): collapses the ubiquitous await ctx.respond(msg, ephemeral=True) error/guard pattern. Success responses and result embeds stay explicit
  • Typed config accessors in tickets.py: get_id/set_id replace bare get_other/set_other for support_role_id and log_channel_id, adding str→int coercion and None-removal semantics
  • 29 new tests in test_shared_helpers.py: coercion, defaults, None-delete, round-trips, ephemeral flag

All 6 commits are behavior-preserving — no concurrency semantics changed, no public API breaks.

Test plan

  • 1,811 tests passing (1,782 baseline + 29 new)
  • ruff check --select E9,F63,F7,F82 clean
  • python scripts/verify_plugin_tests.py — all plugin thresholds met
  • Targeted tests run per plugin after each commit

What was deferred

Per plan: GuildLockManager dedup (Phase 2), send_safe adoption (Phase 3), require_admin + EmbedBuilder convergence (Phase 4) each ship as their own PR.

Summary by Sourcery

Introduce shared helpers for ephemeral error responses and typed config ID accessors, and adopt them across plugins while adding coverage tests.

New Features:

  • Add shared respond_error helper for consistent ephemeral error messaging across plugins.
  • Provide typed config accessors get_id, set_id, get_ids, and set_ids for snowflake IDs in ServerConfig.

Enhancements:

  • Refactor multiple plugins to use the shared respond_error helper instead of inline ephemeral error responses.
  • Update tickets plugin to use typed ID accessors for support and log channel configuration values, improving type safety and normalization.

Tests:

  • Add comprehensive tests for shared helpers, covering ID coercion, deletion semantics, collections handling, and respond_error behavior.

tee1339 added 6 commits July 25, 2026 10:52
…essors

Add five pure helpers to easycord/plugins/_shared.py:
- respond_error(ctx, message): collapses the 107-site ephemeral error pattern
- get_id / set_id: str↔int coercion over ServerConfig.get_other/set_other; None deletes
- get_ids / set_ids: list variant with per-item coercion; missing key returns []

All are pure over ServerConfig (no I/O, no lock) so they slot inside existing
mutate/guild_lock spans without changing concurrency semantics.

Tests: 29 new cases in test_shared_helpers.py covering coercion, defaults,
None-delete, non-coercible skipping, and respond ephemeral flag.
…tion

Replace bare ctx.respond(msg, ephemeral=True) error/guard calls with
respond_error() from _shared. Success embeds and confirmations stay explicit.
economy, starboard, polls, birthday, verification, word_filter,
suggestions, tickets, server_stats, levels, giveaway,
scheduled_announcements — error/guard paths now use respond_error()
from _shared. Success confirms and embeds stay explicit.
Replace error/guard ephemeral responds. Success and info responses
(status display, history list, cancellation confirm) stay explicit.
…nd log_channel_id

Replace bare cfg.get_other("support_role_id"/"log_channel_id") and
cfg.set_other(...) calls with get_id/set_id from _shared. Adds str→int
coercion and None-removal semantics that raw get_other/set_other lack.
tags: not-found, unauthorized, and empty-list paths.
welcome: five guild-guard paths.
Success responses stay explicit.
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sourcery-ai

sourcery-ai Bot commented Jul 25, 2026

Copy link
Copy Markdown

🧙 Sourcery has finished reviewing your pull request!


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions github-actions Bot added the enhancement New feature or request label Jul 25, 2026
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rolling-codes, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 53 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f6f7c330-6957-4a18-8022-fa4d2871c349

📥 Commits

Reviewing files that changed from the base of the PR and between 003898f and 3e23343.

📒 Files selected for processing (2)
  • easycord/plugins/juicewrld.py
  • tests/test_shared_helpers.py
📝 Walkthrough

Walkthrough

Adds shared ephemeral error and ServerConfig ID helpers, migrates plugin error paths to use them, updates ticket configuration access, and adds comprehensive helper tests.

Changes

Shared helper adoption

Layer / File(s) Summary
Shared helper contracts and tests
easycord/plugins/_shared.py, tests/test_shared_helpers.py
Adds respond_error, typed single- and multi-ID configuration accessors, and tests for coercion, omission, iterable handling, and ephemeral responses.
Ticket configuration and failure handling
easycord/plugins/tickets.py
Uses get_id and set_id for ticket configuration IDs and respond_error for ticket failure paths.
Plugin error response migration
easycord/plugins/{auto_role,birthday,economy,giveaway,juicewrld,levels,openclaw,polls,reminder,reputation,scheduled_announcements,server_stats,starboard,suggestions,tags,verification,welcome,word_filter}.py
Replaces inline ephemeral error responses with the shared helper across command validation, authorization, empty-state, and exception branches.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: tee, tee1339

Poem

I’m a rabbit with helpers tucked neat,
Making errors ephemeral and sweet.
IDs hop into lists,
Ticket configs persist,
And every command gets a cleaner beat!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: a shared helpers rollout for error responses and typed config accessors.
Description check ✅ Passed The description matches the changeset and summarizes the new helpers, plugin refactors, and tests accurately.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/dry-phase1-config-helpers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Consider introducing a lightweight Protocol or base interface for the ctx object used by respond_error so you can drop the type: ignore[attr-defined] and make the helper’s contract clearer to type checkers and callers.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider introducing a lightweight `Protocol` or base interface for the `ctx` object used by `respond_error` so you can drop the `type: ignore[attr-defined]` and make the helper’s contract clearer to type checkers and callers.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Comment thread easycord/plugins/juicewrld.py Fixed
Comment thread tests/test_shared_helpers.py Fixed
rolling-codes and others added 2 commits July 25, 2026 11:22
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@easycord/plugins/_shared.py`:
- Around line 38-46: Update get_id() and get_ids() to validate raw configuration
values before coercion: accept only decimal string representations of positive
Discord snowflakes, rejecting bools, floats, zero, negative values, and
malformed strings. Preserve returning None or excluding invalid entries
according to each function’s existing contract, and avoid relying on int() alone
for validation.

In `@easycord/plugins/auto_role.py`:
- Line 12: Re-export respond_error and every other consumed shared helper from
easycord’s public API, then update imports in easycord/plugins/auto_role.py
(12-12), easycord/plugins/juicewrld.py (105-105), easycord/plugins/levels.py
(14-14), easycord/plugins/server_stats.py (12-12),
easycord/plugins/suggestions.py (12-12), easycord/plugins/tags.py (9-9),
easycord/plugins/verification.py (13-13), easycord/plugins/welcome.py (16-16),
and easycord/plugins/word_filter.py (12-12) to import through easycord rather
than ._shared; tags.py must switch all consumed shared helpers.
- Line 85: Localize every listed error response by resolving each message
through the relevant command context’s ctx.t(...) before passing it to
respond_error. Apply this to easycord/plugins/auto_role.py lines 85-85, 101-101,
116-123, and 135-138; easycord/plugins/juicewrld.py lines 436-444, 483, 539,
567, 622, 632, 655, 693, 713, 760, and 792; easycord/plugins/levels.py lines
362-365; easycord/plugins/server_stats.py lines 136, 166, and 188;
easycord/plugins/suggestions.py lines 82-87, 122, 160, 173, 184, and 197;
easycord/plugins/verification.py lines 246-249, 270-273, and 336-339;
easycord/plugins/welcome.py lines 146, 155, 164, 181, and 198; and
easycord/plugins/word_filter.py lines 100, 116, 129-132, 134-145, 148-153, and
166, preserving each existing message and response flow.

In `@easycord/plugins/birthday.py`:
- Line 13: Re-export respond_error from easycord/__init__.py, then replace the
private ._shared import with the public easycord import in
easycord/plugins/birthday.py:13, economy.py:13, giveaway.py:16, openclaw.py:15,
polls.py:14, reminder.py:14, reputation.py:13, scheduled_announcements.py:14,
and starboard.py:12; all consumers must use the stable public API.
- Line 324: Replace every hardcoded user-facing error response with the
appropriate localization lookup through LocalizationManager, preserving each
existing validation and error path. Update birthday.py lines 324 and 396;
economy.py lines 323-327; giveaway.py lines 286-289, 304, and 391; openclaw.py
lines 119-120, 159-160, 172-173, 195-196, 267-268, and 279-280; reminder.py
lines 206-207, 213-214, 268-269, and 310-311; reputation.py lines 104-105,
108-109, 120-121, 171-172, and 205-206; scheduled_announcements.py lines 211-212
and 268-269; and starboard.py lines 279-280. Add or reuse localization keys for
the birthday, transfer, giveaway, OpenClaw, reminder, reputation,
scheduled-announcement, and threshold messages without introducing hardcoded
response strings.
- Around line 324-325: Move the respond_error calls outside the per-guild
configuration lock: in easycord/plugins/birthday.py lines 324-325,
easycord/plugins/reminder.py lines 310-311, and easycord/plugins/reputation.py
lines 120-121, record the missing-birthday, missing-reminder, or cooldown result
during the _guild_lock load/check flow, then perform the response after the lock
exits and before continuing or returning.

In `@easycord/plugins/giveaway.py`:
- Around line 303-305: Move the SENDABLE_CHANNEL_TYPES validation in the
giveaway command before the initial ctx.respond(embed=embed) call. Return
through respond_error immediately for non-sendable channels, and only create or
persist the giveaway and send the initial response after the channel check
passes.

In `@easycord/plugins/server_stats.py`:
- Line 166: Update the error handling around respond_error in the stat-channel
creation flow to log exc server-side instead of interpolating it into the
user-facing response. Replace the raw exception text with the existing generic
localized failure message, preserving the exception details only in the server
log.

In `@easycord/plugins/tickets.py`:
- Line 13: Update the tickets plugin import to obtain get_id, respond_error, and
set_id from the stable easycord public API instead of the underscore-prefixed
_shared module. Export any of these helpers not already exposed through
easycord/__init__.py, preserving their existing behavior.
- Line 345: Localize every migrated ticket error response through ctx.t(...)
before passing it to respond_error: update easycord/plugins/tickets.py lines
345-345, 408-409, 451-452, and 496-496, covering ticket creation, closed-ticket,
and add-user failure messages while preserving existing error details and
ephemerality.
- Around line 408-409: In both invalid-ticket branches at
easycord/plugins/tickets.py lines 408-409 and 451-452, record the not-open state
while inside the _guild_lock context, exit the async with block, then call
respond_error so Discord I/O occurs after releasing the lock; apply the same
deferred-response flow in both sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7320e4d1-734c-4b8d-99b3-4b477cc14f83

📥 Commits

Reviewing files that changed from the base of the PR and between 601e535 and 003898f.

📒 Files selected for processing (21)
  • easycord/plugins/_shared.py
  • easycord/plugins/auto_role.py
  • easycord/plugins/birthday.py
  • easycord/plugins/economy.py
  • easycord/plugins/giveaway.py
  • easycord/plugins/juicewrld.py
  • easycord/plugins/levels.py
  • easycord/plugins/openclaw.py
  • easycord/plugins/polls.py
  • easycord/plugins/reminder.py
  • easycord/plugins/reputation.py
  • easycord/plugins/scheduled_announcements.py
  • easycord/plugins/server_stats.py
  • easycord/plugins/starboard.py
  • easycord/plugins/suggestions.py
  • easycord/plugins/tags.py
  • easycord/plugins/tickets.py
  • easycord/plugins/verification.py
  • easycord/plugins/welcome.py
  • easycord/plugins/word_filter.py
  • tests/test_shared_helpers.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Analyze (Python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

For any question about the codebase, query graphify against graphify-out/graph.json before opening source, documentation, or context files; do not read the deprecated Wiki or rebuild the graph unless explicitly asked.

Files:

  • easycord/plugins/economy.py
  • easycord/plugins/reminder.py
  • easycord/plugins/polls.py
  • easycord/plugins/scheduled_announcements.py
  • easycord/plugins/birthday.py
  • easycord/plugins/openclaw.py
  • easycord/plugins/verification.py
  • easycord/plugins/starboard.py
  • easycord/plugins/server_stats.py
  • tests/test_shared_helpers.py
  • easycord/plugins/reputation.py
  • easycord/plugins/auto_role.py
  • easycord/plugins/tags.py
  • easycord/plugins/suggestions.py
  • easycord/plugins/levels.py
  • easycord/plugins/giveaway.py
  • easycord/plugins/_shared.py
  • easycord/plugins/welcome.py
  • easycord/plugins/word_filter.py
  • easycord/plugins/tickets.py
  • easycord/plugins/juicewrld.py
easycord/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

easycord/**/*.py: Treat easycord/__init__.py as the stable public API. Import consumers from easycord, never from _-prefixed internal modules or helpers.
Use ctx.user or ctx.member, not ctx.author; access ctx.is_admin as a property and never call it as ctx.is_admin().
Store per-guild plugin state in the database layer using per-guild namespaced records, never on the Plugin instance itself.
Never hardcode plugin response strings; retrieve localized text through ctx.t(...).
Always await asynchronous ToolLimiter methods, including check_limit, reset_user, and reset_tool.
Require an explicit ToolSafety annotation when registering an @ai_tool; keep RESTRICTED tools unexposed.
Preserve Discord command-registration constraints before synchronization: names must be at most 32 characters and match [-_a-z0-9], descriptions at most 100 characters, and commands/options at most 25 choices or options as applicable.
Use ServerConfigStore.mutate(guild_id, fn) for every per-guild load-modify-save operation. The callback must be synchronous and local; perform Discord or network I/O outside the lock.
Never perform an unguarded per-guild configuration read-modify-write. Equivalent plugin-specific per-guild locks are acceptable when held across the entire operation, but Discord or network I/O must not occur while the configuration lock is held.
For event-path plugins such as @on("message"), route every destructive action through one governed method that owns rate limiting, channel narrowing, and Discord error handling; Discord exceptions must not escape into the dispatcher.
Before calling .send() on a channel obtained from context or Discord, narrow its type with SENDABLE_CHANNEL_TYPES using isinstance; do not call .send() on an unnarrowed channel.
Initialize LevelsPlugin._cooldowns sentinels to float("-inf"), not 0.0, so the first message always passes.
sync_commands() must raise on removals unless confirm_removals=True is exp...

Files:

  • easycord/plugins/economy.py
  • easycord/plugins/reminder.py
  • easycord/plugins/polls.py
  • easycord/plugins/scheduled_announcements.py
  • easycord/plugins/birthday.py
  • easycord/plugins/openclaw.py
  • easycord/plugins/verification.py
  • easycord/plugins/starboard.py
  • easycord/plugins/server_stats.py
  • easycord/plugins/reputation.py
  • easycord/plugins/auto_role.py
  • easycord/plugins/tags.py
  • easycord/plugins/suggestions.py
  • easycord/plugins/levels.py
  • easycord/plugins/giveaway.py
  • easycord/plugins/_shared.py
  • easycord/plugins/welcome.py
  • easycord/plugins/word_filter.py
  • easycord/plugins/tickets.py
  • easycord/plugins/juicewrld.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Run critical-error Ruff checks with ruff check easycord tests --select E9,F63,F7,F82 and verify plugin test-count thresholds with python scripts/verify_plugin_tests.py.

Files:

  • easycord/plugins/economy.py
  • easycord/plugins/reminder.py
  • easycord/plugins/polls.py
  • easycord/plugins/scheduled_announcements.py
  • easycord/plugins/birthday.py
  • easycord/plugins/openclaw.py
  • easycord/plugins/verification.py
  • easycord/plugins/starboard.py
  • easycord/plugins/server_stats.py
  • tests/test_shared_helpers.py
  • easycord/plugins/reputation.py
  • easycord/plugins/auto_role.py
  • easycord/plugins/tags.py
  • easycord/plugins/suggestions.py
  • easycord/plugins/levels.py
  • easycord/plugins/giveaway.py
  • easycord/plugins/_shared.py
  • easycord/plugins/welcome.py
  • easycord/plugins/word_filter.py
  • easycord/plugins/tickets.py
  • easycord/plugins/juicewrld.py
easycord/plugins/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Plugin responses must obtain strings through LocalizationManager; do not hardcode user-facing response strings.

Files:

  • easycord/plugins/economy.py
  • easycord/plugins/reminder.py
  • easycord/plugins/polls.py
  • easycord/plugins/scheduled_announcements.py
  • easycord/plugins/birthday.py
  • easycord/plugins/openclaw.py
  • easycord/plugins/verification.py
  • easycord/plugins/starboard.py
  • easycord/plugins/server_stats.py
  • easycord/plugins/reputation.py
  • easycord/plugins/auto_role.py
  • easycord/plugins/tags.py
  • easycord/plugins/suggestions.py
  • easycord/plugins/levels.py
  • easycord/plugins/giveaway.py
  • easycord/plugins/_shared.py
  • easycord/plugins/welcome.py
  • easycord/plugins/word_filter.py
  • easycord/plugins/tickets.py
  • easycord/plugins/juicewrld.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use easycord.testing helpers such as invoke, FakeContextBuilder, and PluginTestSuite for command and plugin tests instead of requiring a live Discord connection.
When constructing plugins manually in tests, use MyPlugin.__new__, assign plugin._bot = bot, and then call Plugin.__init__(plugin); do not assign plugin.bot.

Run tests with pytest; async tests use pytest-asyncio with asyncio_mode = "auto" and therefore require no manual event-loop setup.

Files:

  • tests/test_shared_helpers.py
**/test*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain at least 20 tests per plugin, as verified by scripts/verify_plugin_tests.py.

Files:

  • tests/test_shared_helpers.py
🔇 Additional comments (7)
easycord/plugins/_shared.py (2)

7-27: LGTM!


49-54: LGTM!

Also applies to: 71-73

easycord/plugins/tickets.py (1)

116-116: LGTM!

Also applies to: 147-148, 302-303, 330-330, 484-484

tests/test_shared_helpers.py (1)

1-214: 📐 Maintainability & Code Quality

No changes needed.

easycord/plugins/levels.py (1)

328-328: LGTM!

Also applies to: 337-337, 399-399, 412-412, 422-422, 435-435

easycord/plugins/tags.py (1)

76-81: LGTM!

Also applies to: 89-104

easycord/plugins/polls.py (1)

297-306: LGTM!

Comment thread easycord/plugins/_shared.py
Comment thread easycord/plugins/auto_role.py
Comment thread easycord/plugins/auto_role.py
Comment thread easycord/plugins/birthday.py
Comment thread easycord/plugins/birthday.py
Comment thread easycord/plugins/giveaway.py
Comment thread easycord/plugins/server_stats.py
Comment thread easycord/plugins/tickets.py
Comment thread easycord/plugins/tickets.py
Comment thread easycord/plugins/tickets.py
@rolling-codes
rolling-codes merged commit 662f589 into main Jul 25, 2026
34 of 35 checks passed
@rolling-codes
rolling-codes deleted the refactor/dry-phase1-config-helpers branch July 25, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request plugin tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants