Skip to content

Full giveaway system upgrade: admin menu, test-giveaway, pinned announcements, and payout manager - #95

Merged
gamblecodezcom merged 1 commit into
mainfrom
codex/implement-complete-giveaway-system-with-test-feature
Feb 26, 2026
Merged

Full giveaway system upgrade: admin menu, test-giveaway, pinned announcements, and payout manager#95
gamblecodezcom merged 1 commit into
mainfrom
codex/implement-complete-giveaway-system-with-test-feature

Conversation

@gamblecodezcom

@gamblecodezcom gamblecodezcom commented Feb 26, 2026

Copy link
Copy Markdown
Owner

User description

Motivation

  • Complete and unify giveaway tooling so admins can start/edit/extend/end giveaways, manage participants, link target groups, build announcements, run safe test giveaways, and run payout flows without leaving the bot.
  • Ensure Test Giveaway is a fully isolated dry-run that mirrors real join/weighting logic while never affecting production data or payout flows.
  • Improve user-facing giveaway announcements and join UX (open-bot prompts, link guidance, live counters, and pin reporting).
  • Synchronize the functionality map after implementation so the repo doc is the authoritative source of truth.

Description

  • Expanded Admin Giveaway menu with the full operational set: Giveaway Defaults, Edit/Extend/End, View Participants, Group Linking Tools, Announcement Builder, Winner Payout Manager, and Run Test Giveaway (all buttons/callbacks wired).
  • Implemented a confirmation-gated Test Giveaway flow and helpers: confirmation prompt, linked-group prerequisite check, creation of an in-memory test giveaway (2 minutes, 3 winners, 0 SC, 10 fake users), fake users that appear in participant lists and obey join logic, referral-weighted selection, guaranteed inclusion of the admin as a winner, DM notifications for test winners, and full in-memory cleanup (no DB/state persistence or user history contamination).
  • Added Winner Payout Manager and bulk payout action (gw_payout_all) for real giveaways only that DMs winners, marks giveaways as paid, and wipes real-giveaway logs after payout.
  • Upgraded announcement content and behavior: added Join Giveaway + Open Bot buttons, helpful tips block, referral boost explanation, live joined count and countdown, explicit pin attempts with admin error notifications, and announcement refresh in test mode.
  • Hardened join logic for both group and DM surfaces with open-bot guidance, auto-DM linking instructions, live joined count replies, referral boost weighting stored on participant objects, and exclusion of test-mode joins from persisted user giveaway history and public lists.
  • Excluded test giveaways from persistence and user/admin listing surfaces by filtering testMode giveaways out of snapshots and feeds.
  • Updated RUNEWAGER_FUNCTIONALITY_MAP.md to reflect all new menu entries, callbacks, handlers, timeouts, and test/payout behaviors.
  • Files changed: index.js (core implementation) and RUNEWAGER_FUNCTIONALITY_MAP.md (map sync).

Testing

  • Ran syntax check with npm run check, which passed successfully.
  • Ran the full test suite with npm test; all tests passed (TAP: 35 tests, 35 passed).
  • Performed static validation to ensure new admin callbacks and command registration are included and test giveaways are excluded from persisted snapshots (unit/smoke checks exercised during npm test).

Codex Task


CodeAnt-AI Description

Complete giveaway admin tools plus safe test giveaways and bulk payout manager

What Changed

  • Admin Giveaway menu expanded with direct controls: defaults, edit/extend/end, participants view, group-linking tools, announcement builder, winner payout manager, and a guarded "Run Test Giveaway" action.
  • Test Giveaway: admins can start a confirmation-gated 2-minute test that creates 10 fake participants, selects 3 winners (admin guaranteed inclusion), sends no real SC, never persists test joins or winners, and posts a test announcement with live joined counts.
  • Announcements now include an "Open Bot" button, join tips, referral boost notice, and live joined count; failed attempts to pin announcements notify admins.
  • Join flow improvements: users are prompted to open the bot or link their Runewager username when required, eligibility checks auto-DM missing setup steps, and referral boosts are applied as weighted entries during winner selection.
  • Winner Payout Manager: admins can bulk-send payout confirmations to real winners, mark giveaways as paid, and wipe real-giveaway logs after payout.

Impact

✅ Clearer test giveaway isolation — tests never affect real users or payout records
✅ Clearer join guidance — prompts to open bot and link account reduce failed joins
✅ Fewer unpaid payout bookkeeping issues — bulk payout marks as paid and cleans logs

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by Sourcery

Upgrade the giveaway system with richer admin controls, safe test giveaways, improved user join/announcement UX, and a bulk payout manager while keeping test data fully isolated from production.

New Features:

  • Expand the Admin Giveaway tools menu to include defaults management, edit/extend/end controls, participant viewing, group linking tools, an announcement builder, a winner payout manager, and a guarded test giveaway launcher.
  • Introduce a confirmation-gated Test Giveaway flow that runs short-lived, in-memory giveaways with fake participants, admin-included winners, and auto-cleanup without persisting data or affecting real user history.
  • Add a Winner Payout Manager with a bulk payout action that summarizes unpaid giveaways, DMs winners, marks giveaways as paid, and clears their logs from history.
  • Enhance giveaway announcements with join guidance, referral boost info, live joined counts and countdowns, explicit Open Bot links, and admin notifications when pinning fails.
  • Improve join handling across group and DM surfaces by enforcing surface rules, guiding users to open the bot or link accounts, and reflecting referral boosts as weighted entries in winner selection.

Enhancements:

  • Exclude test-mode giveaways from runtime snapshots, active lists, and public user histories to keep all user-visible data production-only.

Documentation:

  • Update RUNEWAGER_FUNCTIONALITY_MAP.md to document the expanded giveaway admin tools, test giveaway behavior, payout manager, and related flows.

…inking + pinned announcements + payout flow + map sync
@codeant-ai

codeant-ai Bot commented Feb 26, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai

coderabbitai Bot commented Feb 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@gamblecodezcom has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 2aafaa8 and 209c683.

📒 Files selected for processing (2)
  • RUNEWAGER_FUNCTIONALITY_MAP.md
  • index.js
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-complete-giveaway-system-with-test-feature

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 and usage tips.

@gamblecodezcom
gamblecodezcom merged commit b95e1ea into main Feb 26, 2026
5 checks passed
@gamblecodezcom
gamblecodezcom deleted the codex/implement-complete-giveaway-system-with-test-feature branch February 26, 2026 21:39
@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Feb 26, 2026
@codeant-ai

codeant-ai Bot commented Feb 26, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

@gamblecodezcom

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@sourcery-ai

sourcery-ai Bot commented Feb 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

Upgrades the giveaway system by expanding admin tools, introducing an isolated test-giveaway flow with fake participants and weighted winner selection, enriching announcements/join UX, excluding test data from persistence and listings, and adding a bulk payout manager for real giveaways, with documentation synced in the functionality map.

Sequence diagram for confirmation-gated test giveaway flow

sequenceDiagram
    actor Admin
    participant Bot
    participant ApprovedGroupsStore
    participant GiveawayStore
    participant UserStore
    participant TelegramAPI

    Admin->>Bot: press admin_cmd_testgiveaway
    Bot->>Admin: show warning + groupHint + Confirm/Cancel

    Admin->>Bot: press tgw_confirm_start
    Bot->>ApprovedGroupsStore: hasAnyLinkedGroup
    ApprovedGroupsStore-->>Bot: result
    alt no linked group
        Bot->>Admin: reply cannot start, show group linking tools
    else has linked group
        Bot->>Bot: startConfiguredTestGiveaway
        Bot->>GiveawayStore: createGiveaway(testMode=true, dryRun=true)
        Bot->>GiveawayStore: add 10 fake participants
        Bot->>TelegramAPI: sendMessage buildGiveawayAnnouncementText
        TelegramAPI-->>Bot: sent message_id
        Bot->>GiveawayStore: store announcementMsgId, pinnedMsgId
        Bot->>TelegramAPI: pinChatMessage
        TelegramAPI-->>Bot: success or error
        alt pin failed
            Bot->>Admin: notifyAdmins pin failure
        end
        Bot->>GiveawayStore: set endTimer (2 minutes)
        Bot->>Admin: reply test giveaway started summary
    end

    Note over Bot,GiveawayStore: After 2 minutes endTimer fires

    Bot->>GiveawayStore: get test giveaway by id
    alt still running
        Bot->>Bot: runTestGiveawayFinale
        Bot->>GiveawayStore: build weighted participant pool
        Bot->>UserStore: get admin user for guaranteed winner
        UserStore-->>Bot: admin user or null
        Bot->>GiveawayStore: select winners with weighting + admin inclusion
        Bot->>GiveawayStore: update status ended_test
        Bot->>TelegramAPI: unpinChatMessage
        Bot->>TelegramAPI: sendMessage test summary to chat
        loop for each real winner
            Bot->>TelegramAPI: sendMessage DM test winner notice
        end
        Bot->>TelegramAPI: sendMessage admin summary
        Bot->>GiveawayStore: clear participants and winners, delete running giveaway
    else not running
        Bot->>Bot: no-op
    end
Loading

Sequence diagram for Winner Payout Manager bulk payout flow

sequenceDiagram
    actor Admin
    participant Bot
    participant GiveawayStore
    participant TelegramAPI

    Admin->>Bot: press admin_gw_payout_manager
    Bot->>GiveawayStore: get history giveaways
    GiveawayStore-->>Bot: history list
    Bot->>Bot: filter ended, not paidOut, not testMode, has winners
    alt no unpaid
        Bot->>Admin: reply no unpaid real giveaways
    else unpaid exist
        Bot->>Admin: reply payout summary + button gw_payout_all
    end

    Admin->>Bot: press gw_payout_all
    Bot->>Bot: requireAdmin
    Bot->>Admin: answerCbQuery Processing payouts...
    Bot->>GiveawayStore: get history giveaways
    GiveawayStore-->>Bot: history list
    Bot->>Bot: filter unpaid real giveaways as targets
    alt no targets
        Bot->>Admin: reply No unpaid real giveaways found
    else have targets
        loop for each target giveaway
            Bot->>Bot: set giveaway.paidOut = true
            Bot->>Bot: increment giveawayCount
            loop for each winner in giveaway.winners
                Bot->>Bot: increment winnerCount
                Bot->>TelegramAPI: sendMessage winner payout confirmation
                TelegramAPI-->>Bot: success or error
            end
            Bot->>GiveawayStore: delete giveaway from history
        end
        Bot->>Admin: reply payout summary and logs wiped message
    end
Loading

ER diagram for giveaways, users, and participants with test and weighting fields

erDiagram
    Giveaway {
        int id
        string status
        boolean testMode
        boolean dryRun
        boolean isTestGiveaway
        boolean persistDisabled
        boolean excludeFromLists
        boolean paidOut
        int maxWinners
        int scPerWinner
        int durationMinutes
        int minParticipants
        int chatId
        string chatTitle
        int pinnedMsgId
        int announcementMsgId
    }

    User {
        int id
        string tgUsername
        string firstName
        string runewagerUsername
        boolean verified
        int boostExpiresAt
        string giveawayHistory_json
    }

    Participant {
        int userId
        string tgUsername
        string firstName
        string runewagerUsername
        boolean hasJoinedChannel
        boolean hasJoinedGroup
        int joinedAt
        boolean isFake
        int boostedWeight
    }

    Giveaway ||--o{ Participant : has_participants
    User ||--o{ Participant : joins_as
    Giveaway ||--o{ User : has_winners
Loading

Flow diagram for expanded Admin Giveaway Tools menu and actions

flowchart TD
    A_AdminGiveawayToolsKeyboard["AdminGiveawayToolsKeyboard"]

    A_AdminGiveawayToolsKeyboard --> B_StartGiveaway["Button: Start Giveaway (admin_cmd_start_giveaway)"]
    A_AdminGiveawayToolsKeyboard --> C_GiveawayDefaults["Button: Giveaway Defaults (admin_gw_defaults)"]
    A_AdminGiveawayToolsKeyboard --> D_EditGiveaway["Button: Edit Giveaway (admin_cmd_giveaway_status)"]
    A_AdminGiveawayToolsKeyboard --> E_ExtendGiveaway["Button: Extend Giveaway (admin_cmd_giveaway_status)"]
    A_AdminGiveawayToolsKeyboard --> F_EndGiveaway["Button: End Giveaway (admin_cmd_giveaway_status)"]
    A_AdminGiveawayToolsKeyboard --> G_ViewParticipants["Button: View Participants (admin_cmd_giveaway_status)"]
    A_AdminGiveawayToolsKeyboard --> H_GroupLinkingTools["Button: Group Linking Tools (admin_gw_group_linking)"]
    A_AdminGiveawayToolsKeyboard --> I_AnnouncementBuilder["Button: Announcement Builder (admin_cmd_announce_start)"]
    A_AdminGiveawayToolsKeyboard --> J_WinnerPayoutManager["Button: Winner Payout Manager (admin_gw_payout_manager)"]
    A_AdminGiveawayToolsKeyboard --> K_RunTestGiveaway["Button: Run Test Giveaway (admin_cmd_testgiveaway)"]
    A_AdminGiveawayToolsKeyboard --> L_GiveawayStatus["Button: Giveaway Status (admin_cmd_giveaway_status)"]
    A_AdminGiveawayToolsKeyboard --> M_AdminDashboard["Button: Admin Dashboard (open_admin_dashboard)"]
    A_AdminGiveawayToolsKeyboard --> N_MainMenu["Button: Main Menu (to_main_menu)"]
    A_AdminGiveawayToolsKeyboard --> O_Cancel["Button: Cancel (to_main_menu)"]

    C_GiveawayDefaults --> B_StartGiveaway
    H_GroupLinkingTools --> K_RunTestGiveaway
    J_WinnerPayoutManager --> P_PayoutAll["Action: gw_payout_all"]
    K_RunTestGiveaway --> Q_TestConfirmPrompt["Prompt: Confirm Test Start (tgw_confirm_start)"]
Loading

File-Level Changes

Change Details Files
Filter test-mode giveaways out of runtime snapshots and all user/admin-facing active/history listings so tests never appear as real giveaways.
  • Updated runtime state snapshot serialization to exclude giveaways where testMode is true from running and history collections.
  • Adjusted active giveaways text and various menus that list running giveaways to filter out testMode items.
  • Ensured the public giveaways menu and group /giveaway status messaging only consider non-test giveaways.
index.js
Expand the Admin Giveaway tools menu and add dedicated handlers for defaults, group-linking tools, test giveaways, and the payout manager.
  • Rebuilt the admin giveaway tools inline keyboard to expose start, defaults, edit/extend/end, participants, group-linking, announcement builder, payout manager, and test giveaway controls.
  • Implemented admin_gw_defaults and admin_gw_group_linking actions to describe default giveaway settings and show/manage approved group links.
  • Implemented admin_gw_payout_manager to summarize recent unpaid giveaways and expose a bulk payout action, plus wired test giveaway start from the admin menu with confirmation and linking guidance.
index.js
Improve join flow UX and safety across DM/group surfaces, including open-bot guidance, linking DMs, referral weighting, and test-mode behavior.
  • Added open-bot URL construction and use in join failure replies to guide users from groups into bot DMs when required.
  • Enhanced eligibility handling so missing account-link errors trigger DMs to the user and group replies with an Open Bot button, and added a guard to require verified users to open the bot before joining from a group.
  • Extended participant records with isFake and boostedWeight flags, made referral boosts affect join metadata, and suppressed test-mode joins from user giveaway history while updating live joined counts and refreshing test announcements on join.
index.js
Introduce a Winner Payout Manager with a bulk payout action for real giveaways that DMs winners, marks giveaways as paid, and cleans history.
  • Added an admin_gw_payout_manager action that surfaces up to the 10 most recent unpaid, ended, non-test giveaways with winners, and offers a bulk payout CTA.
  • Implemented the gw_payout_all callback that iterates unpaid giveaways, marks them as paidOut, DM-notifies each winner of processed payout, deletes them from history, and reports a payout summary to the admin.
index.js
Upgrade announcement content/behavior for giveaways, including richer copy, Open Bot button, live counters, and pin failure reporting.
  • Adjusted announceGiveaway text to clarify prize wording, countdown label, referral boost behavior, add a helpful tips block, and show current participant count at announcement time.
  • Extended the announcement keyboard with an Open Bot URL button alongside join/details/eligibility controls.
  • Changed pinning logic to notify admins via notifyAdmins when a giveaway announcement (including test) fails to pin instead of silently ignoring errors.
index.js
Replace simple random winner selection with referral-weighted sampling while maintaining unique winners.
  • In finalizeGiveaway, built a weighted pool of participants based on active referral boosts and sampled without replacement to determine winners instead of using a uniform pickRandomUnique.
  • Ensured the new selection process respects the configured maxWinners and eligible participant count while preserving uniqueness constraints.
  • Propagated boosted-weight semantics from participant/user state so boosted users have higher winning probability without duplication.
index.js
Redesign the Test Giveaway flow as a fully in-memory, confirmation-gated, 2-minute dry run with fake users, weighted selection, admin winner guarantee, and cleanup.
  • Introduced helpers for detecting linked groups, building/refeshing test giveaway announcement text/keyboards, and enforcing that at least one group is linked before starting tests.
  • Changed /testgiveaway and admin_cmd_testgiveaway to use a confirmation step, then startConfiguredTestGiveaway which creates a 2-minute, 3-winner, 0-SC test giveaway with 10 fake participants, pins and refreshes its announcement, and schedules auto-finalization.
  • Reimplemented test participant listing, abort, and finale flows so test giveaways remain only in running state, guarantee admin inclusion as a winner when possible, DM real test winners, unpin/cancel timers, and fully clear test participants/winners without persisting to history.
index.js
Add a new bot command and synchronize documentation with the upgraded giveaway and payout features.
  • Registered a new 'language' command in the global REGISTERED_COMMANDS set.
  • Updated the RUNEWAGER_FUNCTIONALITY_MAP to describe the upgraded admin giveaway menu, test giveaway configuration and isolation, announcement and pin behavior, weighted winner selection, and the bulk payout manager lifecycle.
index.js
RUNEWAGER_FUNCTIONALITY_MAP.md

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

@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 found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="index.js" line_range="11426-11434" />
<code_context>
   }

-  giveaway.winners = pickRandomUnique(participants, Math.min(giveaway.maxWinners, eligibleCount));
+  const weightedPool = [];
+  for (const participant of participants) {
+    const pUser = userStore.get(participant.userId);
+    const weight = pUser && pUser.boostExpiresAt > Date.now() ? 2 : 1;
+    for (let i = 0; i < weight; i += 1) weightedPool.push(participant);
+  }
+  const pickedIds = new Set();
+  const selected = [];
+  while (selected.length < Math.min(giveaway.maxWinners, eligibleCount) && weightedPool.length > 0) {
+    const candidate = weightedPool[Math.floor(Math.random() * weightedPool.length)];
+    if (!candidate || pickedIds.has(candidate.userId)) continue;
</code_context>
<issue_to_address>
**issue (bug_risk):** Weighted winner selection loop can get stuck when all remaining weighted entries are already picked.

In `finalizeGiveaway`, the loop draws from `weightedPool` but never removes entries. Once `pickedIds` contains every distinct `userId` in `weightedPool`, the `while` condition (`weightedPool.length > 0`) can remain true while every iteration hits the `continue`, so the loop may never terminate.

To avoid this, you should either remove the sampled element from `weightedPool` on each iteration, or pre-build a list of unique participants with associated weights and sample without replacement from that list. For example:

```js
const weightedPool = [];
for (const participant of participants) {
  const pUser = userStore.get(participant.userId);
  const weight = pUser && pUser.boostExpiresAt > Date.now() ? 2 : 1;
  for (let i = 0; i < weight; i += 1) weightedPool.push(participant);
}

const target = Math.min(giveaway.maxWinners, eligibleCount);
const pickedIds = new Set();
const selected = [];

while (selected.length < target && weightedPool.length > 0) {
  const idx = Math.floor(Math.random() * weightedPool.length);
  const candidate = weightedPool[idx];
  weightedPool.splice(idx, 1); // ensure progress
  if (!candidate || pickedIds.has(candidate.userId)) continue;
  selected.push(candidate);
  pickedIds.add(candidate.userId);
}

giveaway.winners = selected;
```
</issue_to_address>

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.

Comment thread index.js
Comment on lines +11426 to +11434
const weightedPool = [];
for (const participant of participants) {
const pUser = userStore.get(participant.userId);
const weight = pUser && pUser.boostExpiresAt > Date.now() ? 2 : 1;
for (let i = 0; i < weight; i += 1) weightedPool.push(participant);
}
const pickedIds = new Set();
const selected = [];
while (selected.length < Math.min(giveaway.maxWinners, eligibleCount) && weightedPool.length > 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Weighted winner selection loop can get stuck when all remaining weighted entries are already picked.

In finalizeGiveaway, the loop draws from weightedPool but never removes entries. Once pickedIds contains every distinct userId in weightedPool, the while condition (weightedPool.length > 0) can remain true while every iteration hits the continue, so the loop may never terminate.

To avoid this, you should either remove the sampled element from weightedPool on each iteration, or pre-build a list of unique participants with associated weights and sample without replacement from that list. For example:

const weightedPool = [];
for (const participant of participants) {
  const pUser = userStore.get(participant.userId);
  const weight = pUser && pUser.boostExpiresAt > Date.now() ? 2 : 1;
  for (let i = 0; i < weight; i += 1) weightedPool.push(participant);
}

const target = Math.min(giveaway.maxWinners, eligibleCount);
const pickedIds = new Set();
const selected = [];

while (selected.length < target && weightedPool.length > 0) {
  const idx = Math.floor(Math.random() * weightedPool.length);
  const candidate = weightedPool[idx];
  weightedPool.splice(idx, 1); // ensure progress
  if (!candidate || pickedIds.has(candidate.userId)) continue;
  selected.push(candidate);
  pickedIds.add(candidate.userId);
}

giveaway.winners = selected;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@sourcery-ai review my full main branch

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sure! I'm generating a new review now.

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 @gamblecodezcom, I've posted a new review for you!

@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 found 1 issue, and left some high level feedback:

  • The referral weighting logic is now implemented in multiple places (e.g., gw_join_ storing boostedWeight, finalizeGiveaway recomputing from userStore, and runTestGiveawayFinale using boostedWeight), which can drift over time; consider extracting a single helper for computing entry weights and using it consistently in both real and test winner selection.
  • Filtering out testMode giveaways from snapshots and all the various listing surfaces is currently done with inline .filter((g) => !g.testMode) calls in several handlers; you might want to centralize this into a helper (or accessor on giveawayStore) to reduce repetition and avoid missing a new surface in future changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The referral weighting logic is now implemented in multiple places (e.g., `gw_join_` storing `boostedWeight`, `finalizeGiveaway` recomputing from `userStore`, and `runTestGiveawayFinale` using `boostedWeight`), which can drift over time; consider extracting a single helper for computing entry weights and using it consistently in both real and test winner selection.
- Filtering out `testMode` giveaways from snapshots and all the various listing surfaces is currently done with inline `.filter((g) => !g.testMode)` calls in several handlers; you might want to centralize this into a helper (or accessor on `giveawayStore`) to reduce repetition and avoid missing a new surface in future changes.

## Individual Comments

### Comment 1
<location path="index.js" line_range="11219-11220" />
<code_context>
       await bot.telegram.pinChatMessage(giveaway.chatId, sent.message_id, { disable_notification: true });
       giveaway.pinnedMsgId = sent.message_id;
-    } catch (_) { /* no pin permission — not fatal */ }
+    } catch (e) {
+      await notifyAdmins(`⚠️ Failed to pin giveaway #${giveaway.id} in chat ${giveaway.chatId}: ${e.message}`);
+    }
   }
</code_context>
<issue_to_address>
**issue (bug_risk):** Pin failure now not only gets logged but also awaits `notifyAdmins`, which could cause extra failures or noise.

This change makes a previously non-fatal pin failure effectively fatal by awaiting `notifyAdmins` inside the `catch`:
- If `notifyAdmins` throws (e.g. network/rate limits), `announceGiveaway` will now reject on a pin-permission issue.
- Chats without pin permission could generate a lot of admin noise.

If you want to preserve the non-blocking behavior, you could treat admin notification as best-effort:
```js
} catch (e) {
  notifyAdmins(`⚠️ Failed to pin giveaway #${giveaway.id} in chat ${giveaway.chatId}: ${e.message}`).catch(() => {});
}
```
so pin/admin-notify failures don’t break the main flow.
</issue_to_address>

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.

Comment thread index.js
Comment on lines +11219 to +11220
} catch (e) {
await notifyAdmins(`⚠️ Failed to pin giveaway #${giveaway.id} in chat ${giveaway.chatId}: ${e.message}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Pin failure now not only gets logged but also awaits notifyAdmins, which could cause extra failures or noise.

This change makes a previously non-fatal pin failure effectively fatal by awaiting notifyAdmins inside the catch:

  • If notifyAdmins throws (e.g. network/rate limits), announceGiveaway will now reject on a pin-permission issue.
  • Chats without pin permission could generate a lot of admin noise.

If you want to preserve the non-blocking behavior, you could treat admin notification as best-effort:

} catch (e) {
  notifyAdmins(`⚠️ Failed to pin giveaway #${giveaway.id} in chat ${giveaway.chatId}: ${e.message}`).catch(() => {});
}

so pin/admin-notify failures don’t break the main flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant