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.ts
2import { randomInt } from 'crypto';
3import { GiveawayEntry } from '@/components/dashboard/giveaways/types';
4
5/**
6 * Select winners using weighted random selection
7 * @param entries Array of giveaway entries with user IDs and entry counts
8 * @param winnerCount Number of winners to select
9 * @param hasFraudDetection Whether the server has fraud detection enabled (premium+)
10 * @param cooldownExcludedUserIds Optional array of user IDs to exclude due to winner cooldown
11 * @param geoExcludedUserIds Optional array of user IDs to exclude due to geo restriction
12 * (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 are
14 * below the giveaway's Invite-to-Win referral requirement (premium feature)
15 * @returns Array of selected winner user IDs
16 */
17export function selectWinners(
18 entries: GiveawayEntry[],
19 winnerCount: number,
20 hasFraudDetection: boolean = true, // Default to true for backward compatibility
21 cooldownExcludedUserIds: string[] = [], // Users excluded due to recent wins (cooldown)
22 geoExcludedUserIds: string[] = [], // Users excluded due to geo restriction
23 referralExcludedUserIds: string[] = [] // Users below the Invite-to-Win referral requirement
24): string[] {
25 if (!entries || entries.length === 0) {
26 return [];
27 }
28
29 // Create a Set for O(1) lookup of cooldown excluded users
30 const cooldownExcludedSet = new Set(cooldownExcludedUserIds);
31
32 // 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 restriction
36 // 4. Users below the giveaway's Invite-to-Win referral requirement
37 let eligibleEntries = hasFraudDetection
38 ? entries.filter(entry => entry.fraud_flag !== 'red')
39 : entries; // Free servers: include all entries (ignore fraud flags)
40
41 // 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 }
50
51 // 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 }
61
62 // 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 }
73
74 // If no eligible entries remain (after fraud + cooldown + geo + referral filtering), return empty array
75 if (eligibleEntries.length === 0) {
76 console.warn('[Winner Selection] No eligible entries after filtering (fraud flags + cooldown + geo + referral-requirement exclusions)');
77 return [];
78 }
79
80 // Create weighted pool of entries based on entry count
81 const entryPool: string[] = [];
82
83 for (const entry of eligibleEntries) {
84 // Add user_id to the pool based on their entry count
85 for (let i = 0; i < entry.entries_count; i++) {
86 entryPool.push(entry.user_id);
87 }
88 }
89
90 // Randomly select winners without duplicates
91 const winners: string[] = [];
92 const winnerSet = new Set<string>();
93
94 // 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 array
99 if (availableWinnerCount === 0 || entryPool.length === 0) {
100 return [];
101 }
102
103 // Select winners
104 while (winners.length < availableWinnerCount && entryPool.length > 0) {
105 // Select a random entry from the pool
106 const randomIndex = randomInt(entryPool.length);
107 const selectedUserId = entryPool[randomIndex];
108
109 // If this user hasn't already been selected as a winner, add them
110 if (!winnerSet.has(selectedUserId)) {
111 winners.push(selectedUserId);
112 winnerSet.add(selectedUserId);
113 }
114
115 // 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.