git-slot-machine 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation/slotMachine.js +47 -44
- package/dist/index.js +4 -1
- package/dist/templates/post-commit.d.ts +1 -1
- package/dist/templates/post-commit.js +3 -11
- package/package.json +6 -1
- package/.claude/release.md +0 -74
- package/CHANGELOG.md +0 -241
- package/dist/animation/slotMachine.d.ts.map +0 -1
- package/dist/animation/slotMachine.js.map +0 -1
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js.map +0 -1
- package/dist/balance.d.ts.map +0 -1
- package/dist/balance.js.map +0 -1
- package/dist/commands/auth.d.ts.map +0 -1
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/balance.d.ts.map +0 -1
- package/dist/commands/balance.js.map +0 -1
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/play.d.ts.map +0 -1
- package/dist/commands/play.js.map +0 -1
- package/dist/commands/spin.d.ts.map +0 -1
- package/dist/commands/spin.js.map +0 -1
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/patterns.d.ts.map +0 -1
- package/dist/patterns.js.map +0 -1
- package/dist/secrets.d.ts.map +0 -1
- package/dist/secrets.js.map +0 -1
- package/dist/templates/post-commit.d.ts.map +0 -1
- package/dist/templates/post-commit.js.map +0 -1
- package/dist/utils/amendDetector.d.ts.map +0 -1
- package/dist/utils/amendDetector.js.map +0 -1
- package/dist/utils/fetch-polyfill.d.ts.map +0 -1
- package/dist/utils/fetch-polyfill.js.map +0 -1
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js.map +0 -1
- package/jest.config.js +0 -12
- package/src/animation/slotMachine.ts +0 -159
- package/src/api.ts +0 -207
- package/src/balance.ts +0 -118
- package/src/commands/auth.ts +0 -92
- package/src/commands/balance.ts +0 -28
- package/src/commands/config.ts +0 -59
- package/src/commands/init.ts +0 -259
- package/src/commands/play.ts +0 -196
- package/src/commands/spin.ts +0 -17
- package/src/commands/sync.ts +0 -49
- package/src/commands/test.ts +0 -19
- package/src/config.ts +0 -189
- package/src/index.ts +0 -132
- package/src/patterns.test.ts +0 -44
- package/src/patterns.ts +0 -313
- package/src/secrets.ts +0 -44
- package/src/templates/post-commit.ts +0 -23
- package/src/utils/amendDetector.ts +0 -74
- package/src/utils/fetch-polyfill.ts +0 -13
- package/src/utils/git.ts +0 -88
- package/test.txt +0 -2
- package/tsconfig.json +0 -21
package/src/patterns.ts
DELETED
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
export enum PatternType {
|
|
2
|
-
SECRET = 'SECRET', // Hidden combos
|
|
3
|
-
LUCKY_SEVENS = 'LUCKY_SEVENS', // Secret: 7777777
|
|
4
|
-
ALL_SAME = 'ALL_SAME',
|
|
5
|
-
SIX_OF_KIND = 'SIX_OF_KIND',
|
|
6
|
-
STRAIGHT_7 = 'STRAIGHT_7',
|
|
7
|
-
FULLEST_HOUSE = 'FULLEST_HOUSE', // 4-3
|
|
8
|
-
FIVE_OF_KIND = 'FIVE_OF_KIND',
|
|
9
|
-
STRAIGHT_6 = 'STRAIGHT_6',
|
|
10
|
-
FOUR_OF_KIND = 'FOUR_OF_KIND',
|
|
11
|
-
ALL_LETTERS = 'ALL_LETTERS',
|
|
12
|
-
STRAIGHT_5 = 'STRAIGHT_5',
|
|
13
|
-
THREE_OF_KIND_PLUS_THREE = 'THREE_OF_KIND_PLUS_THREE', // 3-3-1
|
|
14
|
-
FULLER_HOUSE = 'FULLER_HOUSE', // 3-2-2
|
|
15
|
-
FULL_HOUSE = 'FULL_HOUSE', // 3-2-1-1
|
|
16
|
-
THREE_PAIR = 'THREE_PAIR',
|
|
17
|
-
THREE_OF_KIND = 'THREE_OF_KIND',
|
|
18
|
-
TWO_PAIR = 'TWO_PAIR',
|
|
19
|
-
ALL_NUMBERS = 'ALL_NUMBERS',
|
|
20
|
-
ONE_PAIR = 'ONE_PAIR',
|
|
21
|
-
NO_WIN = 'NO_WIN'
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface PatternResult {
|
|
25
|
-
type: PatternType;
|
|
26
|
-
name: string;
|
|
27
|
-
payout: number;
|
|
28
|
-
description: string;
|
|
29
|
-
highlightIndices: number[]; // Indices of characters to highlight
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const PAYOUTS: Record<PatternType, { name: string; payout: number; description: string }> = {
|
|
33
|
-
[PatternType.SECRET]: { name: 'SECRET', payout: 0, description: '???' }, // Placeholder, actual values from secrets.ts
|
|
34
|
-
[PatternType.LUCKY_SEVENS]: { name: 'LUCKY SEVENS', payout: 1000000, description: 'The ultimate jackpot: 7777777' },
|
|
35
|
-
[PatternType.ALL_SAME]: { name: 'JACKPOT', payout: 100000, description: 'All same character' },
|
|
36
|
-
[PatternType.STRAIGHT_7]: { name: 'LUCKY SEVEN', payout: 50000, description: 'Seven sequential hex digits' },
|
|
37
|
-
[PatternType.STRAIGHT_6]: { name: 'BIG STRAIGHT', payout: 25000, description: 'Six sequential hex digits' },
|
|
38
|
-
[PatternType.SIX_OF_KIND]: { name: 'HEXTET', payout: 10000, description: 'Six of a kind' },
|
|
39
|
-
[PatternType.FULLEST_HOUSE]: { name: 'FULLEST HOUSE', payout: 5000, description: '4 + 3 of a kind' },
|
|
40
|
-
[PatternType.STRAIGHT_5]: { name: 'STRAIGHT', payout: 2500, description: 'Five sequential hex digits' },
|
|
41
|
-
[PatternType.FIVE_OF_KIND]: { name: 'FIVE OF A KIND', payout: 2000, description: 'Five of a kind' },
|
|
42
|
-
[PatternType.THREE_OF_KIND_PLUS_THREE]: { name: 'DOUBLE TRIPLE', payout: 1000, description: 'Two three of a kinds' },
|
|
43
|
-
[PatternType.THREE_PAIR]: { name: 'THREE PAIR', payout: 500, description: 'Three consecutive pairs' },
|
|
44
|
-
[PatternType.FULLER_HOUSE]: { name: 'FULLER HOUSE', payout: 400, description: '3 + 2 + 2 of a kind' },
|
|
45
|
-
[PatternType.FULL_HOUSE]: { name: 'FULL HOUSE', payout: 50, description: '3 + 2 of a kind' },
|
|
46
|
-
[PatternType.ALL_LETTERS]: { name: 'ALPHABET SOUP', payout: 250, description: 'Only letters (a-f)' },
|
|
47
|
-
[PatternType.FOUR_OF_KIND]: { name: 'FOUR OF A KIND', payout: 200, description: 'Four of a kind' },
|
|
48
|
-
[PatternType.THREE_OF_KIND]: { name: 'THREE OF A KIND', payout: 25, description: 'Three of a kind' },
|
|
49
|
-
[PatternType.ALL_NUMBERS]: { name: 'ALL NUMBERS', payout: 50, description: 'Only numbers (0-9)' },
|
|
50
|
-
[PatternType.TWO_PAIR]: { name: 'TWO PAIR', payout: 25, description: 'Two consecutive pairs' },
|
|
51
|
-
[PatternType.ONE_PAIR]: { name: 'ONE PAIR', payout: 10, description: 'One consecutive pair' },
|
|
52
|
-
[PatternType.NO_WIN]: { name: 'NO WIN', payout: 0, description: 'No winning pattern' }
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
function countCharacters(hash: string): Map<string, number> {
|
|
56
|
-
const counts = new Map<string, number>();
|
|
57
|
-
for (const char of hash) {
|
|
58
|
-
counts.set(char, (counts.get(char) || 0) + 1);
|
|
59
|
-
}
|
|
60
|
-
return counts;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function getCountDistribution(counts: Map<string, number>): number[] {
|
|
64
|
-
return Array.from(counts.values()).sort((a, b) => b - a);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function hasSequentialRun(hash: string, length: number): boolean {
|
|
68
|
-
const hexValues = '0123456789abcdef';
|
|
69
|
-
|
|
70
|
-
for (let i = 0; i <= hash.length - length; i++) {
|
|
71
|
-
const substring = hash.substring(i, i + length);
|
|
72
|
-
|
|
73
|
-
// Check ascending
|
|
74
|
-
let isAscending = true;
|
|
75
|
-
for (let j = 0; j < substring.length - 1; j++) {
|
|
76
|
-
const currentIndex = hexValues.indexOf(substring[j]);
|
|
77
|
-
const nextIndex = hexValues.indexOf(substring[j + 1]);
|
|
78
|
-
if (nextIndex !== currentIndex + 1) {
|
|
79
|
-
isAscending = false;
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (isAscending) return true;
|
|
84
|
-
|
|
85
|
-
// Check descending
|
|
86
|
-
let isDescending = true;
|
|
87
|
-
for (let j = 0; j < substring.length - 1; j++) {
|
|
88
|
-
const currentIndex = hexValues.indexOf(substring[j]);
|
|
89
|
-
const nextIndex = hexValues.indexOf(substring[j + 1]);
|
|
90
|
-
if (nextIndex !== currentIndex - 1) {
|
|
91
|
-
isDescending = false;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (isDescending) return true;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function isAllLetters(hash: string): boolean {
|
|
102
|
-
return /^[a-f]+$/i.test(hash);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function isAllNumbers(hash: string): boolean {
|
|
106
|
-
return /^[0-9]+$/.test(hash);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function countConsecutivePairs(hash: string): number {
|
|
110
|
-
// Count pairs of consecutive identical characters (e.g., "33" or "bb")
|
|
111
|
-
let pairCount = 0;
|
|
112
|
-
let i = 0;
|
|
113
|
-
|
|
114
|
-
while (i < hash.length - 1) {
|
|
115
|
-
if (hash[i] === hash[i + 1]) {
|
|
116
|
-
pairCount++;
|
|
117
|
-
i += 2; // Skip both characters of the pair
|
|
118
|
-
} else {
|
|
119
|
-
i++;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return pairCount;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function getHighlightIndices(hash: string, type: PatternType): number[] {
|
|
127
|
-
const lowerHash = hash.toLowerCase();
|
|
128
|
-
|
|
129
|
-
// For straights, highlight all characters in the sequential run
|
|
130
|
-
if (type === PatternType.STRAIGHT_7 || type === PatternType.STRAIGHT_6 || type === PatternType.STRAIGHT_5) {
|
|
131
|
-
const length = type === PatternType.STRAIGHT_7 ? 7 : type === PatternType.STRAIGHT_6 ? 6 : 5;
|
|
132
|
-
const hexValues = '0123456789abcdef';
|
|
133
|
-
|
|
134
|
-
for (let i = 0; i <= lowerHash.length - length; i++) {
|
|
135
|
-
const substring = lowerHash.substring(i, i + length);
|
|
136
|
-
let isSequential = true;
|
|
137
|
-
|
|
138
|
-
// Check ascending
|
|
139
|
-
for (let j = 0; j < substring.length - 1; j++) {
|
|
140
|
-
const currentIndex = hexValues.indexOf(substring[j]);
|
|
141
|
-
const nextIndex = hexValues.indexOf(substring[j + 1]);
|
|
142
|
-
if (nextIndex !== currentIndex + 1 && nextIndex !== currentIndex - 1) {
|
|
143
|
-
isSequential = false;
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (isSequential) {
|
|
149
|
-
return Array.from({ length }, (_, idx) => i + idx);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// For all letters, all numbers, all same, or lucky sevens - highlight everything
|
|
155
|
-
if (type === PatternType.ALL_LETTERS || type === PatternType.ALL_NUMBERS ||
|
|
156
|
-
type === PatternType.ALL_SAME || type === PatternType.LUCKY_SEVENS) {
|
|
157
|
-
return [0, 1, 2, 3, 4, 5, 6];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// For frequency-based patterns, highlight characters that appear in the pattern
|
|
161
|
-
const counts = countCharacters(lowerHash);
|
|
162
|
-
const indices: number[] = [];
|
|
163
|
-
|
|
164
|
-
if (type === PatternType.SIX_OF_KIND || type === PatternType.FIVE_OF_KIND ||
|
|
165
|
-
type === PatternType.FOUR_OF_KIND || type === PatternType.THREE_OF_KIND) {
|
|
166
|
-
// Find the character with the highest count and highlight all occurrences
|
|
167
|
-
const targetCount = type === PatternType.SIX_OF_KIND ? 6 :
|
|
168
|
-
type === PatternType.FIVE_OF_KIND ? 5 :
|
|
169
|
-
type === PatternType.FOUR_OF_KIND ? 4 : 3;
|
|
170
|
-
|
|
171
|
-
for (const [char, count] of counts.entries()) {
|
|
172
|
-
if (count === targetCount) {
|
|
173
|
-
for (let i = 0; i < lowerHash.length; i++) {
|
|
174
|
-
if (lowerHash[i] === char) {
|
|
175
|
-
indices.push(i);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
else if (type === PatternType.FULLEST_HOUSE || type === PatternType.FULLER_HOUSE ||
|
|
183
|
-
type === PatternType.FULL_HOUSE || type === PatternType.THREE_OF_KIND_PLUS_THREE) {
|
|
184
|
-
// Highlight all paired/tripled characters
|
|
185
|
-
for (const [char, count] of counts.entries()) {
|
|
186
|
-
if (count >= 2) {
|
|
187
|
-
for (let i = 0; i < lowerHash.length; i++) {
|
|
188
|
-
if (lowerHash[i] === char) {
|
|
189
|
-
indices.push(i);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else if (type === PatternType.THREE_PAIR || type === PatternType.TWO_PAIR || type === PatternType.ONE_PAIR) {
|
|
196
|
-
// Highlight consecutive pairs only (e.g., "aa" and "bb" in "aa1bb2c")
|
|
197
|
-
let i = 0;
|
|
198
|
-
while (i < lowerHash.length - 1) {
|
|
199
|
-
if (lowerHash[i] === lowerHash[i + 1]) {
|
|
200
|
-
indices.push(i);
|
|
201
|
-
indices.push(i + 1);
|
|
202
|
-
i += 2; // Skip both characters of the pair
|
|
203
|
-
} else {
|
|
204
|
-
i++;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return indices.sort((a, b) => a - b);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export function detectPattern(hash: string): PatternResult {
|
|
213
|
-
// Validate input
|
|
214
|
-
if (hash.length !== 7) {
|
|
215
|
-
throw new Error('Hash must be 7 characters');
|
|
216
|
-
}
|
|
217
|
-
if (!/^[0-9a-f]+$/i.test(hash)) {
|
|
218
|
-
throw new Error('Hash must contain only hex characters');
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const lowerHash = hash.toLowerCase();
|
|
222
|
-
const counts = countCharacters(lowerHash);
|
|
223
|
-
const distribution = getCountDistribution(counts);
|
|
224
|
-
|
|
225
|
-
// Detect pattern - check in order of rarity/value
|
|
226
|
-
let type: PatternType;
|
|
227
|
-
|
|
228
|
-
// Check for the secret ultimate jackpot: 7777777
|
|
229
|
-
if (lowerHash === '7777777') {
|
|
230
|
-
type = PatternType.LUCKY_SEVENS;
|
|
231
|
-
}
|
|
232
|
-
// Check for all same first (highest value)
|
|
233
|
-
else if (distribution[0] === 7) {
|
|
234
|
-
type = PatternType.ALL_SAME;
|
|
235
|
-
}
|
|
236
|
-
// Check for 6 of a kind
|
|
237
|
-
else if (distribution[0] === 6) {
|
|
238
|
-
type = PatternType.SIX_OF_KIND;
|
|
239
|
-
}
|
|
240
|
-
// Check for straight 7 (very rare)
|
|
241
|
-
else if (hasSequentialRun(lowerHash, 7)) {
|
|
242
|
-
type = PatternType.STRAIGHT_7;
|
|
243
|
-
}
|
|
244
|
-
// Check for fullest house (4-3)
|
|
245
|
-
else if (distribution[0] === 4 && distribution[1] === 3) {
|
|
246
|
-
type = PatternType.FULLEST_HOUSE;
|
|
247
|
-
}
|
|
248
|
-
// Check for 5 of a kind
|
|
249
|
-
else if (distribution[0] === 5) {
|
|
250
|
-
type = PatternType.FIVE_OF_KIND;
|
|
251
|
-
}
|
|
252
|
-
// Check for straight 6
|
|
253
|
-
else if (hasSequentialRun(lowerHash, 6)) {
|
|
254
|
-
type = PatternType.STRAIGHT_6;
|
|
255
|
-
}
|
|
256
|
-
// Check for 4 of a kind
|
|
257
|
-
else if (distribution[0] === 4) {
|
|
258
|
-
type = PatternType.FOUR_OF_KIND;
|
|
259
|
-
}
|
|
260
|
-
// Check for all letters
|
|
261
|
-
else if (isAllLetters(lowerHash)) {
|
|
262
|
-
type = PatternType.ALL_LETTERS;
|
|
263
|
-
}
|
|
264
|
-
// Check for straight 5
|
|
265
|
-
else if (hasSequentialRun(lowerHash, 5)) {
|
|
266
|
-
type = PatternType.STRAIGHT_5;
|
|
267
|
-
}
|
|
268
|
-
// Check for double triple (3-3-1)
|
|
269
|
-
else if (distribution[0] === 3 && distribution[1] === 3) {
|
|
270
|
-
type = PatternType.THREE_OF_KIND_PLUS_THREE;
|
|
271
|
-
}
|
|
272
|
-
// Check for fuller house (3-2-2)
|
|
273
|
-
else if (distribution[0] === 3 && distribution[1] === 2 && distribution[2] === 2) {
|
|
274
|
-
type = PatternType.FULLER_HOUSE;
|
|
275
|
-
}
|
|
276
|
-
// Check for full house (3-2-1-1)
|
|
277
|
-
else if (distribution[0] === 3 && distribution[1] === 2) {
|
|
278
|
-
type = PatternType.FULL_HOUSE;
|
|
279
|
-
}
|
|
280
|
-
// Check for 3 of a kind
|
|
281
|
-
else if (distribution[0] === 3) {
|
|
282
|
-
type = PatternType.THREE_OF_KIND;
|
|
283
|
-
}
|
|
284
|
-
// Check for three consecutive pairs
|
|
285
|
-
else if (countConsecutivePairs(lowerHash) === 3) {
|
|
286
|
-
type = PatternType.THREE_PAIR;
|
|
287
|
-
}
|
|
288
|
-
// Check for two consecutive pairs
|
|
289
|
-
else if (countConsecutivePairs(lowerHash) === 2) {
|
|
290
|
-
type = PatternType.TWO_PAIR;
|
|
291
|
-
}
|
|
292
|
-
// Check for all numbers
|
|
293
|
-
else if (isAllNumbers(lowerHash)) {
|
|
294
|
-
type = PatternType.ALL_NUMBERS;
|
|
295
|
-
}
|
|
296
|
-
// Check for one consecutive pair
|
|
297
|
-
else if (countConsecutivePairs(lowerHash) === 1) {
|
|
298
|
-
type = PatternType.ONE_PAIR;
|
|
299
|
-
}
|
|
300
|
-
// Everything else is no win
|
|
301
|
-
else {
|
|
302
|
-
type = PatternType.NO_WIN;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const config = PAYOUTS[type];
|
|
306
|
-
return {
|
|
307
|
-
type,
|
|
308
|
-
name: config.name,
|
|
309
|
-
payout: config.payout,
|
|
310
|
-
description: config.description,
|
|
311
|
-
highlightIndices: getHighlightIndices(hash, type)
|
|
312
|
-
};
|
|
313
|
-
}
|
package/src/secrets.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'crypto';
|
|
2
|
-
|
|
3
|
-
interface S {
|
|
4
|
-
h: string; // sha256 hash
|
|
5
|
-
p: number; // payout
|
|
6
|
-
n: number[]; // name as char codes
|
|
7
|
-
e?: number; // emoji code point
|
|
8
|
-
f?: number; // flood flag
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Sorted by payout descending
|
|
12
|
-
const _: S[] = [
|
|
13
|
-
// Legendary tier (42069)
|
|
14
|
-
{ h: '56379c14419d2363ce258293ed6adc3684ae4926aba335a49b75d7483b9f85ae', p: 42069, n: [66,65,68,32,70,79,79,68], e: 0x1F922, f: 1 },
|
|
15
|
-
{ h: '475fbf8c77411b86a660c8d61f181e464a50572e8f1ff4ffb28e75289625d494', p: 42069, n: [67,79,70,70,69,69,83], e: 0x2615, f: 1 },
|
|
16
|
-
{ h: 'a0d9961dc6d7e4059ec746b8ab9a20f330c1308266c5197da77e7bda839457b9', p: 42069, n: [68,69,65,68,32,66,69,68], e: 0x1F480, f: 1 },
|
|
17
|
-
{ h: 'a3fe33e094647d5071cb6d3719bd7b55eb1ad1fb06f2c33923e66363fd805020', p: 42069, n: [68,73,83,69,65,83,69], e: 0x1F9A0, f: 1 },
|
|
18
|
-
// Great tier (6969)
|
|
19
|
-
{ h: '011a1a79ee662806e273538d1a0036b8090f939efd05e312f402ed9fdcaf5f25', p: 6969, n: [73,67,69,32,67,79,76,68], e: 0x1F976, f: 1 },
|
|
20
|
-
{ h: '432fe036b0239c1551947f5a1f93006f2f62e4c5512909453e539eba348b678f', p: 6969, n: [83,65,68,32,70,65,67,69], e: 0x1F622, f: 1 },
|
|
21
|
-
{ h: 'dcf79a973cb6e6d4567c17e24865d39370db2bc6a167ebdcbeaedcda83906350', p: 6969, n: [66,79,65,83,84,69,68], e: 0x1F525, f: 1 },
|
|
22
|
-
// Good tier (1337)
|
|
23
|
-
{ h: '70d8dec01053bbf890288c8927f605f209533b52512dafd9f145e891d6d3f0ae', p: 1337, n: [66,65,68,32,67,65,70,69] },
|
|
24
|
-
{ h: '2c8d79f36c7758d1268998039ca8a565cb6687958516f370e10086b45c2a1ea7', p: 1337, n: [68,69,70,65,67,69,68] },
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
export interface SecretResult {
|
|
28
|
-
name: string;
|
|
29
|
-
payout: number;
|
|
30
|
-
emoji?: string;
|
|
31
|
-
flood: boolean;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function checkSecret(hash: string): SecretResult | null {
|
|
35
|
-
const h = createHash('sha256').update(hash.toLowerCase()).digest('hex');
|
|
36
|
-
const m = _.find(s => s.h === h);
|
|
37
|
-
if (!m) return null;
|
|
38
|
-
return {
|
|
39
|
-
name: String.fromCharCode(...m.n),
|
|
40
|
-
payout: m.p,
|
|
41
|
-
emoji: m.e ? String.fromCodePoint(m.e) : undefined,
|
|
42
|
-
flood: m.f === 1,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export const POST_COMMIT_HOOK = `#!/bin/sh
|
|
2
|
-
# Git Slot Machine post-commit hook
|
|
3
|
-
|
|
4
|
-
# Get the commit hash
|
|
5
|
-
HASH=$(git rev-parse --short=7 HEAD)
|
|
6
|
-
|
|
7
|
-
# Find git-slot-machine binary
|
|
8
|
-
if command -v git-slot-machine >/dev/null 2>&1; then
|
|
9
|
-
GSM=git-slot-machine
|
|
10
|
-
elif [ -f "./node_modules/.bin/git-slot-machine" ]; then
|
|
11
|
-
GSM=./node_modules/.bin/git-slot-machine
|
|
12
|
-
else
|
|
13
|
-
echo "git-slot-machine not found, skipping animation"
|
|
14
|
-
exit 0
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
|
-
# In Claude Code, skip animation frames and show only the result line
|
|
18
|
-
if [ -n "$CLAUDECODE" ]; then
|
|
19
|
-
"$GSM" play "$HASH" --small 2>/dev/null | tail -1
|
|
20
|
-
else
|
|
21
|
-
"$GSM" play "$HASH" --small
|
|
22
|
-
fi
|
|
23
|
-
`;
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
|
|
3
|
-
interface AmendDetectionResult {
|
|
4
|
-
isAmended: boolean;
|
|
5
|
-
recentAmendCount: number;
|
|
6
|
-
suspiciousActivity: boolean;
|
|
7
|
-
recentAmendHashes: string[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Detects if the current commit appears to be the result of hash grinding
|
|
12
|
-
* by checking git reflog for suspicious amend patterns
|
|
13
|
-
*/
|
|
14
|
-
export function detectAmendGrinding(timeWindowMinutes: number = 5, suspiciousThreshold: number = 5): AmendDetectionResult {
|
|
15
|
-
try {
|
|
16
|
-
// Get reflog entries from the last timeWindowMinutes
|
|
17
|
-
// Format: %gd = reflog selector, %gs = reflog subject, %H = commit hash, %ct = committer timestamp
|
|
18
|
-
const reflogOutput = execSync(
|
|
19
|
-
`git reflog --since="${timeWindowMinutes} minutes ago" --format="%gs|%H|%ct"`,
|
|
20
|
-
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }
|
|
21
|
-
).trim();
|
|
22
|
-
|
|
23
|
-
if (!reflogOutput) {
|
|
24
|
-
return {
|
|
25
|
-
isAmended: false,
|
|
26
|
-
recentAmendCount: 0,
|
|
27
|
-
suspiciousActivity: false,
|
|
28
|
-
recentAmendHashes: [],
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const entries = reflogOutput.split('\n');
|
|
33
|
-
const amendEntries = entries.filter(entry => entry.includes('commit (amend)'));
|
|
34
|
-
const amendHashes = amendEntries.map(entry => {
|
|
35
|
-
const parts = entry.split('|');
|
|
36
|
-
return parts[1]?.substring(0, 7) || '';
|
|
37
|
-
}).filter(hash => hash.length === 7);
|
|
38
|
-
|
|
39
|
-
const recentAmendCount = amendEntries.length;
|
|
40
|
-
const suspiciousActivity = recentAmendCount >= suspiciousThreshold;
|
|
41
|
-
|
|
42
|
-
// Check if the most recent action was an amend
|
|
43
|
-
const isAmended = entries.length > 0 && entries[0].includes('commit (amend)');
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
isAmended,
|
|
47
|
-
recentAmendCount,
|
|
48
|
-
suspiciousActivity,
|
|
49
|
-
recentAmendHashes: amendHashes,
|
|
50
|
-
};
|
|
51
|
-
} catch (error) {
|
|
52
|
-
// If git reflog fails, assume no amending
|
|
53
|
-
return {
|
|
54
|
-
isAmended: false,
|
|
55
|
-
recentAmendCount: 0,
|
|
56
|
-
suspiciousActivity: false,
|
|
57
|
-
recentAmendHashes: [],
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get a warning message for suspicious amend activity
|
|
64
|
-
*/
|
|
65
|
-
export function getAmendWarningMessage(result: AmendDetectionResult): string | null {
|
|
66
|
-
if (!result.suspiciousActivity) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return `⚠️ Suspicious Activity Detected\n` +
|
|
71
|
-
` ${result.recentAmendCount} commit amends in the last 5 minutes\n` +
|
|
72
|
-
` This pattern suggests hash grinding/gaming\n` +
|
|
73
|
-
` Your commit will be flagged for review`;
|
|
74
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Polyfill fetch for Node.js < 18
|
|
2
|
-
// Node 18+ has native fetch, older versions need node-fetch
|
|
3
|
-
|
|
4
|
-
export async function getFetch(): Promise<typeof fetch> {
|
|
5
|
-
// Check if native fetch exists (Node 18+)
|
|
6
|
-
if (typeof globalThis.fetch !== 'undefined') {
|
|
7
|
-
return globalThis.fetch;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Fall back to node-fetch for Node < 18
|
|
11
|
-
const nodeFetch = await import('node-fetch');
|
|
12
|
-
return nodeFetch.default as unknown as typeof fetch;
|
|
13
|
-
}
|
package/src/utils/git.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
|
|
3
|
-
export function getCurrentCommitHash(): string {
|
|
4
|
-
try {
|
|
5
|
-
const fullHash = execSync('git rev-parse HEAD', {
|
|
6
|
-
encoding: 'utf-8',
|
|
7
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
8
|
-
}).trim();
|
|
9
|
-
|
|
10
|
-
return fullHash.substring(0, 7);
|
|
11
|
-
} catch (error) {
|
|
12
|
-
throw new Error('Not a git repository or no commits yet');
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getCurrentCommitFullHash(): string {
|
|
17
|
-
try {
|
|
18
|
-
const fullHash = execSync('git rev-parse HEAD', {
|
|
19
|
-
encoding: 'utf-8',
|
|
20
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
21
|
-
}).trim();
|
|
22
|
-
|
|
23
|
-
return fullHash;
|
|
24
|
-
} catch (error) {
|
|
25
|
-
throw new Error('Not a git repository or no commits yet');
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function isGitRepo(): boolean {
|
|
30
|
-
try {
|
|
31
|
-
execSync('git rev-parse --is-inside-work-tree', {
|
|
32
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
33
|
-
});
|
|
34
|
-
return true;
|
|
35
|
-
} catch {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function detectGitHubUsername(): string | null {
|
|
41
|
-
// Try 1: Check GitHub CLI (gh)
|
|
42
|
-
try {
|
|
43
|
-
const ghUser = execSync('gh api user --jq .login', {
|
|
44
|
-
encoding: 'utf-8',
|
|
45
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
46
|
-
}).trim();
|
|
47
|
-
|
|
48
|
-
if (ghUser) {
|
|
49
|
-
return ghUser;
|
|
50
|
-
}
|
|
51
|
-
} catch {
|
|
52
|
-
// gh not installed or not authenticated, continue to next method
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Try 2: Check git config github.user
|
|
56
|
-
try {
|
|
57
|
-
const githubUser = execSync('git config github.user', {
|
|
58
|
-
encoding: 'utf-8',
|
|
59
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
60
|
-
}).trim();
|
|
61
|
-
|
|
62
|
-
if (githubUser) {
|
|
63
|
-
return githubUser;
|
|
64
|
-
}
|
|
65
|
-
} catch {
|
|
66
|
-
// github.user not set, continue to next method
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Try 3: Extract from GitHub noreply email
|
|
70
|
-
try {
|
|
71
|
-
const email = execSync('git config user.email', {
|
|
72
|
-
encoding: 'utf-8',
|
|
73
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
74
|
-
}).trim();
|
|
75
|
-
|
|
76
|
-
// Match patterns like:
|
|
77
|
-
// - username@users.noreply.github.com
|
|
78
|
-
// - 12345+username@users.noreply.github.com (with ID prefix)
|
|
79
|
-
const match = email.match(/^(?:\d+\+)?(.+)@users\.noreply\.github\.com$/);
|
|
80
|
-
if (match) {
|
|
81
|
-
return match[1];
|
|
82
|
-
}
|
|
83
|
-
} catch {
|
|
84
|
-
// user.email not set or no match
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return null;
|
|
88
|
-
}
|
package/test.txt
DELETED
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ES2022",
|
|
5
|
-
"moduleResolution": "node",
|
|
6
|
-
"lib": ["ES2022"],
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"declaration": true,
|
|
15
|
-
"declarationMap": true,
|
|
16
|
-
"sourceMap": true,
|
|
17
|
-
"types": ["node"]
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*"],
|
|
20
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
21
|
-
}
|