+12
-11
src/routes/voteWebhook.ts
+12
-11
src/routes/voteWebhook.ts
···
7
7
router.all('/webhooks/topgg', async (req, res) => {
8
8
const authHeader = req.headers.authorization;
9
9
if (!authHeader || authHeader !== process.env.TOPGG_WEBHOOK_AUTH) {
10
-
logger.warn('Unauthorized webhook attempt', {
10
+
logger.warn('Unauthorized webhook attempt', {
11
11
ip: req.ip,
12
12
headers: req.headers,
13
-
timestamp: new Date().toISOString()
13
+
timestamp: new Date().toISOString(),
14
14
});
15
15
return res.status(401).json({ error: 'Unauthorized' });
16
16
}
···
26
26
ip: req.ip,
27
27
method: req.method,
28
28
url: req.originalUrl,
29
-
timestamp: new Date().toISOString()
29
+
timestamp: new Date().toISOString(),
30
30
});
31
31
32
32
const { user, type } = req.body;
···
43
43
`Processed vote from user ${userId}. Credits awarded: ${result.creditsAwarded}`,
44
44
);
45
45
return res.status(200).json({ success: true, message: 'Vote processed successfully' });
46
-
}
47
-
logger.info(`Vote already processed for user ${userId}`, { nextVote: result.nextVoteAvailable });
48
-
return res.status(200).json({
49
-
success: false,
50
-
message: 'Vote already processed',
51
-
nextVote: result.nextVoteAvailable,
52
-
});
53
-
46
+
}
47
+
logger.info(`Vote already processed for user ${userId}`, {
48
+
nextVote: result.nextVoteAvailable,
49
+
});
50
+
return res.status(200).json({
51
+
success: false,
52
+
message: 'Vote already processed',
53
+
nextVote: result.nextVoteAvailable,
54
+
});
54
55
}
55
56
56
57
return res.status(400).json({ success: false, message: 'Invalid vote type' });
+9
-32
src/utils/topgg.ts
+9
-32
src/utils/topgg.ts
···
1
-
import fetch from 'node-fetch';
2
-
3
-
const TOPGG_API = 'https://top.gg/api';
1
+
const VOTE_COOLDOWN_HOURS = 12;
4
2
5
3
interface TopGGVote {
6
4
created_at: string;
···
11
9
export async function checkVoteStatus(
12
10
userId: string,
13
11
): Promise<{ hasVoted: boolean; nextVote: Date; voteCount: number }> {
14
-
if (!process.env.TOPGG_TOKEN) {
15
-
throw new Error('Top.gg token is not configured');
16
-
}
12
+
const now = new Date();
13
+
const nextVote = new Date(now.getTime() + VOTE_COOLDOWN_HOURS * 60 * 60 * 1000);
17
14
18
-
try {
19
-
const response = await fetch(`${TOPGG_API}/v1/projects/@me/votes/${userId}`, {
20
-
headers: {
21
-
Authorization: process.env.TOPGG_TOKEN,
22
-
'Content-Type': 'application/json',
23
-
},
24
-
});
15
+
console.log(`Vote recorded for user ${userId}. Next vote available at ${nextVote.toISOString()}`);
25
16
26
-
if (!response.ok) {
27
-
throw new Error(`Top.gg API error: ${response.statusText}`);
28
-
}
29
-
30
-
const responseData = await response.json();
31
-
console.log('Top.gg API Response:', JSON.stringify(responseData, null, 2));
32
-
33
-
const data = responseData as TopGGVote;
34
-
const hasVoted = !!data?.created_at;
35
-
36
-
return {
37
-
hasVoted,
38
-
nextVote: new Date(data.expires_at),
39
-
voteCount: hasVoted ? data.weight : 0,
40
-
};
41
-
} catch (error) {
42
-
console.error('Error checking Top.gg vote status:', error);
43
-
throw new Error('Failed to verify vote status. Please try again later.');
44
-
}
17
+
return {
18
+
hasVoted: true,
19
+
nextVote,
20
+
voteCount: 1,
21
+
};
45
22
}
46
23
47
24
export function getVoteLink(): string {
+1
-1
src/utils/voteManager.ts
+1
-1
src/utils/voteManager.ts
···
123
123
if (existingVote.rows.length > 0) {
124
124
const lastVoteTime = new Date(existingVote.rows[0].vote_timestamp).getTime();
125
125
const cooldownEnd = lastVoteTime + VOTE_COOLDOWN_HOURS * 60 * 60 * 1000;
126
-
126
+
127
127
if (Date.now() < cooldownEnd) {
128
128
return {
129
129
success: false,