v1.2.1
·
16 commits
to production
since this release
Changed
- The Friday-Sunday double-vote weekend bonus (
votes.GetDoubleVote) now
pins its day-of-week check to UTC explicitly instead of relying on
time.Now()'s implicit host-local timezone, so the boundary is the same
instant regardless of what timezone the process happens to run in. Also
addedVoteInfo.WeekendBonus, a per-entity flag reporting whether the
bonus is actively boosting that entity'sper_user/vote_timeright
now —falsefor premium bots/servers even on a bonus weekend, since
their flat premium cooldown already applies instead. Nothing in the API
previously told callers when the bonus was live. - Certification requirements loosened and diversified. The old rule
required a bot to clear servers ≥100 and unique clicks ≥30 both,
no exceptions. It's now an OR across three lowered bars (servers ≥50,
unique clicks ≥15, or votes ≥50, the last one is new), plus a new but
lenient 3-day minimum listed age. A bot excelling in one metric no
longer gets rejected for not excelling in all of them. - Servers can now be certified too a new "Server Certification"
position on/apps(extraLogicCertServer/reviewLogicCertServerin
apps/logic.go), using the same OR-of-three-metrics rule scaled to
server stats (members ≥100 in place of bot servers ≥50). New
request_server_certificationpermission. The "Certified" badge
Omniplex has always been able to render for servers had no backend path
that ever set it until now. - Premium and Shop now work for servers, not just bots:
CreatePerkData/PerkDatagained afor_typefield ("bot"or
"server", defaults to"bot"if omitted) so Stripe/PayPal checkout
and the booster-offer redemption can target either.
servers.premiumhas been a real, displayed column with no purchase
path behind it since it was added; now there is one.- Shop purchases (
POST /{target_type}/{target_id}/shop/purchase) and
all five benefit effects (routes/shop/assets/benefits.go) now
branch on target type betweenbots/serversboth tables carry
identical benefit columns. serversgained the sameboosted_until/featured_until/
supporter_badge/vote_blitz_untilcolumns bots already had
(exp/serverbenefits.sql), plus the matching read-side effects:
boosted-first sort inGET /servers/@all, afeaturedcategory in
GET /servers/@index, and a vote-blitz cooldown halving in
EntityVoteInfo's"server"case.
Added
GET /list/statsnow includestotal_pending_botsand
total_denied_bots, so consumers can show a real approved/certified/
pending/denied breakdown instead of inferring it fromtotal_botsminus
the listed count.GET /staff/shop-purchasesthe same shop-purchase data
GET /{target_type}/{target_id}/shop/purchasesalready exposes
publicly one entity at a time, but platform-wide and staff-gated
(view_shop) for abuse/fraud monitoring. No frontend consumes this yet
since the Arcadia panel UI isn't part of this repo it's ready for
whenever that side wires it up.- A standalone support ticket system. The existing
ticketstable/Ticket
type were entirely Discord-channel-shaped (channel_id,enc_key) with
no creation path anywhere in the codebase, not in the API, not in the
Discord bot nothing has ever created a ticket in this codebase. Rather
than build the Discord-integration side, tickets are now a plain web
feature reusing the same table (channel_idleft"",enc_keyleft
null):GET /tickets/topics(a small hardcoded topic catalogue, same
convention asapps.Apps),POST/GET /users/{id}/tickets,
POST /tickets/{id}/messages, andPATCH /tickets/{id}to close/reopen
(closing is open to the author or staff; reopening is staff-only, via a
newmanage_ticketspermission). New message IDs are synthesized
Discord-format snowflakes (disgoorg/snowflake'sNew) purely so
GET /tickets/{id}'s existingsnowflake.Parse-based timestamp
decoding keeps working unchanged for old and new tickets alike. - Shop purchases actually do something now.
shop_items/shop_item_benefits
have had full staff CRUD via the Arcadia panel for a while, but nothing
ever spent an entity's earned vote credits on one or defined what a
benefit's effect even was. New:POST /{target_type}/{target_id}/shop/purchase(bots only for now,
gated on a newbuy_shop_itemsentity permission) spends credits
oldest-batch-first acrossentity_vote_redeem_logs, logs the purchase
to a newshop_purchasestable, and applies every benefit ID on the
item that Popplio recognizes.- Five recognized benefit IDs, each with a real effect:
premium_days(extends the bot's premium period, identical to the
Stripe/PayPal path),priority_boost(newboosted_untilcolumn,
sorts first in/bots/@all's default order while active),
featured_slot(newfeatured_untilcolumn, surfaces the bot in a
newfeaturedcategory on/bots/@index),supporter_badge(new
permanentsupporter_badgeflag), andvote_blitz(new
vote_blitz_untilcolumn, halvesEntityVoteInfo's vote-time
cooldown while active). Unrecognized benefit IDs no-op rather than
error, so staff can still catalogue purely descriptive/future
benefits without breaking a purchase but an item with zero
recognized benefits is rejected at purchase time rather than silently
spending credits for nothing. GET /{target_type}/{target_id}/shop/purchasespurchase history,
public, same transparency level as the existing vote-credit logs.exp/shopbenefits.sql(schema: 4 newbotscolumns + the
shop_purchasestable) andexp/shopbenefits_seed.sql(optional
starter catalog rows for the 5 benefits) the seed is just a
starting point; the same rows can be created through the Arcadia
panel instead.
Changed
- Omniplex is now owned by NodeByte LTD. Remaining "Infinity Bot List" /
"Infinity Development" copy left over from the old brand — application
question text, the staff-denial DM, webhook docs, the RSS feed title
and copyright line, the auth-log embed footer, and the!delete
bot-command copy now reads "Omniplex" / "NodeByte LTD".
Fixed
- The Gold premium plan granted ~365 hours (~15 days) of premium instead
of a yearTimePeriodwas set in raw days whileGivePerksapplies
it as hours. Bronze/Silver were already correct; Gold now multiplies by
24 like they do. POST /users/{id}/redeem-payment-offer?code=BOOSTPREMIUMgranted the
perk successfully but then always fell through to a final400 Invalid offer coderesponse regardless — no caller could ever see it succeed.
It also never stampedlast_booster_claim, so the "once every 30 days"
cooldown could never actually engage. Both are fixed: a successful
redemption now returns204and updates the claim timestamp.tickets.user_idhad no foreign key constraint tousers(user_id)at
all, just a plain column — so the account data-export/deletion pipeline
(POST /users/{id}/data,routes/users/endpoints/create_data_task)
silently skipped every ticket a user had ever filed. The walker
(ddr_task.go) auto-includes any table with a real FK into an
already-registered root (users/teams), so the fix is schema-only:
exp/ticketuserfkey.sqladds the constraintNOT VALID(4 legacy
tickets reference since-deleted accounts;NOT VALIDenforces it for
all new/updated rows without deleting or nulling that history). No Go
changes needed — confirmed via a directpg_constraintcheck against
the dev DB that tickets are now walked correctly.