Chaque gagnant sort d'un tirage aléatoire pondéré. Chaque chance que vous gagnez devient une place dans un pool : 5 chances, 5 places. À la fin du giveaway, notre code tire des places au hasard avec crypto.randomInt, un générateur cryptographiquement sûr, sans graine que quelqu'un pourrait prédire ou influencer. Le code exact de sélection est lisible plus haut sur cette page.
Transparence de l'algorithme
Comment ScopliDrop tire les gagnants au sort,
et pourquoi c'est vérifiable
Notre algorithme open source garantit un tirage équitable. Plus de tâches, plus de chances, mais chaque participant peut vraiment gagner. Aucune manipulation, aucun favoritisme, aucune exception.
Notre promesse de transparence
La confiance se gagne par la transparence. Chaque étape de notre tirage au sort peut être inspectée. Il n'y a ni boîte noire, ni algorithme caché, ni paramètre secret. Ce que vous voyez est exactement ce qui se passe.
L'algorithme derrière le tirage
Chaque gagnant est tiré par ce code, pris tel quel dans notre base de code. Un aléatoire cryptographiquement sûr, sans fonction cachée ni paramètre secret.
WinnerSelection.ts
1// src/lib/services/WinnerSelection.ts2import { randomInt } from 'crypto';3import { GiveawayEntry } from '@/components/dashboard/giveaways/types';45/**6 * Select winners using weighted random selection7 * @param entries Array of giveaway entries with user IDs and entry counts8 * @param winnerCount Number of winners to select9 * @param hasFraudDetection Whether the server has fraud detection enabled (premium+)10 * @param cooldownExcludedUserIds Optional array of user IDs to exclude due to winner cooldown11 * @param geoExcludedUserIds Optional array of user IDs to exclude due to geo restriction12 * (warn-mode entrants and block-mode strays that slipped past the submit gate)13 * @param referralExcludedUserIds Optional array of user IDs to exclude because they are14 * below the giveaway's Invite-to-Win referral requirement (premium feature)15 * @returns Array of selected winner user IDs16 */17export function selectWinners(18 entries: GiveawayEntry[],19 winnerCount: number,20 hasFraudDetection: boolean = true, // Default to true for backward compatibility21 cooldownExcludedUserIds: string[] = [], // Users excluded due to recent wins (cooldown)22 geoExcludedUserIds: string[] = [], // Users excluded due to geo restriction23 referralExcludedUserIds: string[] = [] // Users below the Invite-to-Win referral requirement24): string[] {25 if (!entries || entries.length === 0) {26 return [];27 }2829 // Create a Set for O(1) lookup of cooldown excluded users30 const cooldownExcludedSet = new Set(cooldownExcludedUserIds);3132 // Filter out:33 // 1. Red-flagged entries ONLY if server has fraud detection enabled (premium+)34 // 2. Users on winner cooldown (won from this server recently)35 // 3. Users whose entry country falls under the giveaway's geo restriction36 // 4. Users below the giveaway's Invite-to-Win referral requirement37 let eligibleEntries = hasFraudDetection38 ? entries.filter(entry => entry.fraud_flag !== 'red')39 : entries; // Free servers: include all entries (ignore fraud flags)4041 // Apply cooldown exclusions (silent exclusion - users can still enter but won't win)42 if (cooldownExcludedSet.size > 0) {43 const beforeCount = eligibleEntries.length;44 eligibleEntries = eligibleEntries.filter(entry => !cooldownExcludedSet.has(entry.user_id));45 const excluded = beforeCount - eligibleEntries.length;46 if (excluded > 0) {47 console.log(`[Winner Selection] Excluded ${excluded} entries due to winner cooldown`);48 }49 }5051 // Apply geo exclusions (warn mode: entered with disclosure, can't win)52 const geoExcludedSet = new Set(geoExcludedUserIds);53 if (geoExcludedSet.size > 0) {54 const beforeCount = eligibleEntries.length;55 eligibleEntries = eligibleEntries.filter(entry => !geoExcludedSet.has(entry.user_id));56 const excluded = beforeCount - eligibleEntries.length;57 if (excluded > 0) {58 console.log(`[Winner Selection] Excluded ${excluded} entries due to geo restriction`);59 }60 }6162 // Apply Invite-to-Win exclusions (entrants below the referral requirement;63 // participants were told up-front they must invite X friends to be eligible)64 const referralExcludedSet = new Set(referralExcludedUserIds);65 if (referralExcludedSet.size > 0) {66 const beforeCount = eligibleEntries.length;67 eligibleEntries = eligibleEntries.filter(entry => !referralExcludedSet.has(entry.user_id));68 const excluded = beforeCount - eligibleEntries.length;69 if (excluded > 0) {70 console.log(`[Winner Selection] Excluded ${excluded} entries below the Invite-to-Win referral requirement`);71 }72 }7374 // If no eligible entries remain (after fraud + cooldown + geo + referral filtering), return empty array75 if (eligibleEntries.length === 0) {76 console.warn('[Winner Selection] No eligible entries after filtering (fraud flags + cooldown + geo + referral-requirement exclusions)');77 return [];78 }7980 // Create weighted pool of entries based on entry count81 const entryPool: string[] = [];8283 for (const entry of eligibleEntries) {84 // Add user_id to the pool based on their entry count85 for (let i = 0; i < entry.entries_count; i++) {86 entryPool.push(entry.user_id);87 }88 }8990 // Randomly select winners without duplicates91 const winners: string[] = [];92 const winnerSet = new Set<string>();9394 // Limit winner count to available unique participants (only eligible entries)95 const uniqueParticipants = new Set(eligibleEntries.map(entry => entry.user_id));96 const availableWinnerCount = Math.min(winnerCount, uniqueParticipants.size);97 98 // If there are no entries or the pool is empty, return empty array99 if (availableWinnerCount === 0 || entryPool.length === 0) {100 return [];101 }102 103 // Select winners104 while (winners.length < availableWinnerCount && entryPool.length > 0) {105 // Select a random entry from the pool106 const randomIndex = randomInt(entryPool.length);107 const selectedUserId = entryPool[randomIndex];108109 // If this user hasn't already been selected as a winner, add them110 if (!winnerSet.has(selectedUserId)) {111 winners.push(selectedUserId);112 winnerSet.add(selectedUserId);113 }114115 // Remove via swap-and-pop (O(1)) instead of splice (O(n)).116 // Selection is uniform random so order of remaining elements doesn't matter.117 entryPool[randomIndex] = entryPool[entryPool.length - 1];118 entryPool.pop();119 }120 121 return winners;122}Les étapes clés de l'algorithme
Cliquez sur une étape pour voir son implémentation dans le code
Ligne 2
Aléatoire sécurisé
Les gagnants sont tirés avec crypto.randomInt de Node, un générateur cryptographiquement sûr. Aucune graine à prédire, rien à manipuler, pas même par nous.
Ligne 38
Filtrage fraude et cooldown
Avant le tirage, les participations signalées comme frauduleuses sont retirées sur les serveurs avec détection de fraude, et les membres encore sous cooldown des gagnants sont mis de côté. Les serveurs gratuits gardent toutes les participations.
Ligne 86
Création du pool pondéré
L'équité commence ici. Chaque membre est ajouté au pool une fois par chance gagnée : 5 chances, 5 places. Proportionnel, jamais garanti.
Ligne 106
Le tirage
Un index aléatoire dans le pool pondéré désigne chaque gagnant. Plus de chances veut simplement dire plus de places, et chaque place a exactement la même probabilité.
Ligne 110
Pas de doublon
Un ensemble de gagnants garantit que personne ne gagne deux fois, et nous ne tirons jamais plus de gagnants qu'il n'y a de participants éligibles distincts.
Comment fonctionne l'équité
Voyez exactement comment notre algorithme donne à chaque participant une chance équitable, avec de meilleures chances pour ceux qui s'impliquent le plus.
1
Collecte des chances
Les membres font des tâches et gagnent des chances selon leurs actions
2
Création du pool pondéré
Chaque chance est ajoutée au pool de sélection selon les tâches validées
3
Sélection aléatoire
Un aléatoire cryptographiquement sûr pioche dans le pool pondéré
4
Pas de doublon
Chaque membre ne peut gagner qu'une fois par giveaway
5
Confirmation des gagnants
Les gagnants tirés sont vérifiés et annoncés
Exemple concret
Alice fait 3 tâches, Bob en fait 1, Charlie en fait 5. Le pool contient 9 chances au total, avec des probabilités proportionnelles.
Alice
3 chances gagnées
33 % de chances
Bob
1 chance gagnée
11 % de chances
Charlie
5 chances gagnées
56 % de chances
Le pool (9 chances au total)
Chaque point représente une chance. Le gagnant est tiré au hasard parmi toutes les chances.
Fini les giveaways truqués
Un bot giveaway Discord qui montre exactement comment les gagnants sont choisis
Le problème
La plupart des bots giveaway Discord sont des boîtes noires. Même quand le bot est honnête et vraiment aléatoire, votre communauté n'a aucun moyen de le vérifier. Elle ne voit pas l'algorithme, ne peut pas contrôler le processus, ne peut pas prouver que les gagnants n'ont pas été choisis à la main.
Sans transparence, chaque giveaway paraît suspect. Vos membres commencent à se demander si c'est vraiment équitable. La confiance meurt, l'engagement chute.
On en avait assez de voir des communautés mourir parce que la confiance était rompue
La solution
Ne craignez plus jamais une baisse d'engagement à cause d'accusations de giveaway truqué. Quand un membre doute de l'équité, envoyez-lui le lien de cette page.
Le bot giveaway ScopliDrop certifie que les propriétaires de serveur ne peuvent ni modifier un giveaway ni choisir les gagnants. Point.
Il existe bien un bouton « Terminer le giveaway », mais il ne fait que déclencher notre algorithme de tirage, rien d'autre. Les admins du serveur NE PEUVENT PAS choisir les gagnants, par aucun moyen. C'est notre promesse.
Vos questions sur l'équité
Comment nous garantissons des giveaways transparents, sûrs et vraiment équitables pour tous les participants.
Rejoignez la révolution de l'équité
Prêt à lancer des giveaways vraiment équitables
dès aujourd'hui ?
Rejoignez des milliers de communautés Discord passées à des giveaways transparents et vérifiables. Commencez gratuitement, prouvez l'équité dès le premier jour.