Promo Manager, Content Drops, Announcements & admin UX refactor - #85
Conversation
|
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 · |
Nitpicks 🔍
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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. 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR updates the Telegram bot's configuration to introduce environment-driven channel and group identifiers, rebrand "Tips" terminology to "Content Drops" throughout UI and commands, add a new user state flag for tracking new-user promo claims, and update admin interfaces and promo eligibility logic accordingly. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
CodeAnt AI finished reviewing your PR. |
|
Sure! I'm generating a new review now. |
|
@sourcery-ai review my full main branch for Runewager repo. Send full need fixed list, list all bugs, list all your recommendations to improve user experience and admin. |
Reviewer's GuideRefactors promo and content delivery UX by tightening new-user promo eligibility tracking, rebranding the Tips system into Content Drops with clearer admin wording, and making Telegram targets configurable via environment variables, while adding a small UX affordance for users to mark new-user promos as claimed. Sequence diagram for new-user promo claim and eligibility trackingsequenceDiagram
actor User
participant TelegramBot
participant UserStore
participant PromoMenu
participant PromoEngine
User->>TelegramBot: tap menu_claim_bonus
TelegramBot->>UserStore: getUser(ctx)
UserStore-->>TelegramBot: user(hasClaimedNewUserPromo=false/true)
TelegramBot->>PromoEngine: evaluatePromoEligibility(user,promo,logFailure)
PromoEngine->>PromoEngine: hasRedeemedAnyPromo(user.id)
PromoEngine->>PromoEngine: check promo.new_user_only && (anyRedeemed || user.hasClaimedNewUserPromo)
PromoEngine-->>TelegramBot: eligiblePromos list
TelegramBot->>PromoMenu: build promo buttons + I_claimed_successfully
PromoMenu-->>User: Promo Menu with eligible promos
User->>TelegramBot: tap promo_user_claimed_successfully
TelegramBot->>UserStore: getUser(ctx)
UserStore-->>TelegramBot: user
TelegramBot->>UserStore: set user.hasClaimedNewUserPromo=true
TelegramBot->>UserStore: set user.lastAnyPromoClaimAt=now
TelegramBot-->>User: answerCbQuery Saved
TelegramBot-->>User: reply marked as already claimed
User->>TelegramBot: tap menu_claim_bonus (later)
TelegramBot->>UserStore: getUser(ctx)
UserStore-->>TelegramBot: user(hasClaimedNewUserPromo=true)
TelegramBot->>PromoEngine: evaluatePromoEligibility(user,promo,logFailure)
PromoEngine->>PromoEngine: reject new_user_only promos when user.hasClaimedNewUserPromo
PromoEngine-->>TelegramBot: eligiblePromos (new-user promos hidden)
TelegramBot-->>User: updated Promo Menu
Class diagram for user and promo eligibility structuresclassDiagram
class User {
number id
boolean hasClaimedNewUserPromo
number claimedPromoAt
number lastAnyPromoClaimAt
Array claimedPromoCodes
boolean claimedPromo
}
class Promo {
number promo_id
string name
boolean new_user_only
boolean existing_user_only
PROMO_REQUIREMENT requirement_type
number required_wager_days
}
class PromoRequirementEnum {
string EXISTING_USER_WITH_WAGER
string OTHER
}
class PromoEngine {
evaluatePromoEligibility(user,promo,logFailure)
hasRedeemedAnyPromo(userId)
getRollingWagerForUser(user,days)
}
class MenuHandlers {
menu_claim_bonus(ctx)
promo_user_claimed_successfully(ctx)
}
User "1" --> "*" Promo : mayBeEligibleFor
PromoRequirementEnum <|-- Promo : uses
PromoEngine ..> User : readsFlags
PromoEngine ..> Promo : evaluates
MenuHandlers ..> User : uses
MenuHandlers ..> PromoEngine : calls
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The new TELEGRAM_* environment variable fallbacks (especially when
TELEGRAM_CHANNEL_ID,TELEGRAM_GROUP_ID, orTIPS_GROUPresolve to an empty string) could benefit from a centralized startup validation or explicit runtime checks so that broadcasts/content drops fail fast with a clear admin-facing error instead of silently using an invalid target. - For the
promo_user_claimed_successfullyflow, consider only showing the button when at least one new-user-only promo exists (or adding a confirmation step) to reduce the risk of users accidentally settinghasClaimedNewUserPromoand hiding legitimate future promos.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new TELEGRAM_* environment variable fallbacks (especially when `TELEGRAM_CHANNEL_ID`, `TELEGRAM_GROUP_ID`, or `TIPS_GROUP` resolve to an empty string) could benefit from a centralized startup validation or explicit runtime checks so that broadcasts/content drops fail fast with a clear admin-facing error instead of silently using an invalid target.
- For the `promo_user_claimed_successfully` flow, consider only showing the button when at least one new-user-only promo exists (or adding a confirmation step) to reduce the risk of users accidentally setting `hasClaimedNewUserPromo` and hiding legitimate future promos.
## Individual Comments
### Comment 1
<location path="index.js" line_range="5206-5211" />
<code_context>
await ctx.reply(`🎁 *Promo Menu*\n\nOnly promos you are eligible for are shown below.`, { parse_mode: 'Markdown', ...Markup.inlineKeyboard(rows) });
});
+bot.action('promo_user_claimed_successfully', async (ctx) => {
+ const user = getUser(ctx);
+ user.hasClaimedNewUserPromo = true;
+ user.lastAnyPromoClaimAt = Date.now();
+ await ctx.answerCbQuery('Saved');
+ await ctx.reply('✅ Got it — you are marked as already claimed. New-user-only promos are now hidden for your account.');
+});
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Marking `hasClaimedNewUserPromo` and `lastAnyPromoClaimAt` is not persisted, so state is lost on process restart.
This handler mutates `user.hasClaimedNewUserPromo` and `user.lastAnyPromoClaimAt` but, unlike other user updates (e.g. promo creation, tips settings), it doesn’t call the central persistence mechanism (e.g. `persistRuntimeState()`). If `getUser(ctx)` returns an in-memory object, these flags will be reset on restart and users can see new-user promos again. Please persist the user after updating these fields so the “claimed successfully” state survives restarts.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| bot.action('promo_user_claimed_successfully', async (ctx) => { | ||
| const user = getUser(ctx); | ||
| user.hasClaimedNewUserPromo = true; | ||
| user.lastAnyPromoClaimAt = Date.now(); | ||
| await ctx.answerCbQuery('Saved'); | ||
| await ctx.reply('✅ Got it — you are marked as already claimed. New-user-only promos are now hidden for your account.'); |
There was a problem hiding this comment.
issue (bug_risk): Marking hasClaimedNewUserPromo and lastAnyPromoClaimAt is not persisted, so state is lost on process restart.
This handler mutates user.hasClaimedNewUserPromo and user.lastAnyPromoClaimAt but, unlike other user updates (e.g. promo creation, tips settings), it doesn’t call the central persistence mechanism (e.g. persistRuntimeState()). If getUser(ctx) returns an in-memory object, these flags will be reset on restart and users can see new-user promos again. Please persist the user after updating these fields so the “claimed successfully” state survives restarts.
|
Hey @gamblecodezcom, I've posted a new review for you! |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The new
promo_user_claimed_successfullyflow unconditionally adds the button to the promo menu; consider only rendering it when the user is actually seeing (or has recently seen) a new-user-only promo to avoid confusion for users who have never interacted with such promos. - The announcement/content-drop targeting config now mixes
TELEGRAM_CHANNEL_ID,TELEGRAM_GROUP_ID,TIPS_GROUP,ANNOUNCE_CHANNEL, andbroadcastConfigStore.target*; it may be worth centralizing this into a single configuration helper to make precedence and expected formats (ID vs @username vs URL) explicit and avoid subtle misconfiguration when some env vars are unset.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `promo_user_claimed_successfully` flow unconditionally adds the button to the promo menu; consider only rendering it when the user is actually seeing (or has recently seen) a new-user-only promo to avoid confusion for users who have never interacted with such promos.
- The announcement/content-drop targeting config now mixes `TELEGRAM_CHANNEL_ID`, `TELEGRAM_GROUP_ID`, `TIPS_GROUP`, `ANNOUNCE_CHANNEL`, and `broadcastConfigStore.target*`; it may be worth centralizing this into a single configuration helper to make precedence and expected formats (ID vs @username vs URL) explicit and avoid subtle misconfiguration when some env vars are unset.
## Individual Comments
### Comment 1
<location path="index.js" line_range="54-59" />
<code_context>
// Channel username (or chat_id) for admin announcements via /announce
-const ANNOUNCE_CHANNEL = process.env.ANNOUNCE_CHANNEL || '@GambleCodezDrops';
+const TELEGRAM_CHANNEL_ID = process.env.TELEGRAM_CHANNEL_ID || process.env.ANNOUNCE_CHANNEL || '';
+const TELEGRAM_GROUP_ID = process.env.TELEGRAM_GROUP_ID || '';
+const ANNOUNCE_CHANNEL = TELEGRAM_CHANNEL_ID;
-// Group username (or chat_id) for automatic silent tips
-const TIPS_GROUP = process.env.TIPS_GROUP || '@GambleCodezPrizeHub';
+// Group chat_id for automatic Content Drops
+const TIPS_GROUP = process.env.TIPS_GROUP || TELEGRAM_GROUP_ID || '';
// ── Images ─────────────────────────────────────────────────────────────────
</code_context>
<issue_to_address>
**issue (bug_risk):** Empty defaults for TELEGRAM_CHANNEL_ID / TIPS_GROUP may silently disable broadcasts/drops.
These env vars used to have non-empty defaults (`@GambleCodezDrops`, `@GambleCodezPrizeHub`), but now can be `''` if unset. If callers don’t explicitly check for non-empty IDs, broadcasts/drops may silently no-op instead of failing visibly. Consider either failing fast when these are missing (throw / prominent log) or retaining a non-empty default to avoid silent misconfiguration.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| const TELEGRAM_CHANNEL_ID = process.env.TELEGRAM_CHANNEL_ID || process.env.ANNOUNCE_CHANNEL || ''; | ||
| const TELEGRAM_GROUP_ID = process.env.TELEGRAM_GROUP_ID || ''; | ||
| const ANNOUNCE_CHANNEL = TELEGRAM_CHANNEL_ID; | ||
|
|
||
| // Group username (or chat_id) for automatic silent tips | ||
| const TIPS_GROUP = process.env.TIPS_GROUP || '@GambleCodezPrizeHub'; | ||
| // Group chat_id for automatic Content Drops | ||
| const TIPS_GROUP = process.env.TIPS_GROUP || TELEGRAM_GROUP_ID || ''; |
There was a problem hiding this comment.
issue (bug_risk): Empty defaults for TELEGRAM_CHANNEL_ID / TIPS_GROUP may silently disable broadcasts/drops.
These env vars used to have non-empty defaults (@GambleCodezDrops, @GambleCodezPrizeHub), but now can be '' if unset. If callers don’t explicitly check for non-empty IDs, broadcasts/drops may silently no-op instead of failing visibly. Consider either failing fast when these are missing (throw / prominent log) or retaining a non-empty default to avoid silent misconfiguration.
|
@sourcery-ai You are the senior QA + architecture reviewer for the Runewager repo (main branch). Do a full-stack audit, not just style or diff review. Your job:
Hunt specifically for:
Deliverables:
Bias toward:
|
User description
Motivation
Description
promoStore/promoCodeStorewithpromoManagerStorepersisted todata/promo-manager-db.json, added promo schema normalization, list/query helpers, claim records and eligibility evaluation logic./pmapprove,/pmdeny), plus legacy mappings to keep old promo commands stable.sendAnnouncementTargetsimplementation.postTipToConfiguredTarget, plus/register_chatand/verify_bot_setuphelpers and test/send helpers.isHealthTlsEnabledTLS-aware health checks, safer file saves with try/catch and admin logging, added admin logs for ctrl actions, and unref() on GC timers.WEBAPP_HMAC_KEYin.env.example, expand.gitignoreto ignoredata/*.jsonand backups, addbroadcastConfigStoreand minor UX text changes across menus and keyboards.Testing
test/smoke.test.js) which includesnode --check index.jssyntax validation and the new callback handler coverage test, and all checks passed.test/smoke.test.js(test succeeded).Codex Task
CodeAnt-AI Description
Add interactive Announcements builder, Content Drops delivery improvements, admin UX tweaks, SSHV and health-check hardening, and new smoke test
What Changed
Impact
✅ Clearer announce targeting and delivery results✅ Fewer failed Content Drops due to automatic fallback and registration✅ Safer admin SSH editor saves with actionable error hints💡 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:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
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:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
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
Update Telegram targets configuration, refine new-user promo handling, and rebrand the Tips system to Content Drops in admin flows and messaging.
New Features:
Bug Fixes:
Enhancements:
Summary by CodeRabbit
Release Notes
New Features
Updates