dubs-server 1.0.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/.claude/settings.local.json +280 -0
- package/CLAUDE.md +46 -0
- package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
- package/CURRENT_SESSION.md +171 -0
- package/CURRENT_SESSION_DRAW.md +516 -0
- package/MARCH_MADNESS_SURVIVOR.md +254 -0
- package/PANDA.md +166 -0
- package/Procfile +4 -0
- package/README.md +476 -0
- package/controllers/livescoresController.js +376 -0
- package/controllers/pickemController.js +554 -0
- package/controllers/survivorAdminController.js +887 -0
- package/controllers/survivorController.js +623 -0
- package/cron/oracleMonitor.js +77 -0
- package/cron/pickemOracleMonitor.js +73 -0
- package/data/jackpot-history.json +952 -0
- package/data/ncaaTeams.js +406 -0
- package/documentation/API_SECURITY_GUIDE.md +327 -0
- package/documentation/ARCADE_API.md +593 -0
- package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
- package/documentation/ARCADE_QUICKSTART.md +242 -0
- package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
- package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
- package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
- package/documentation/CLAIM_STATUS_FIX.md +67 -0
- package/documentation/CLI_TOOL_GUIDE.md +372 -0
- package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
- package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
- package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
- package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
- package/documentation/DATABASE_QUERIES.md +269 -0
- package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
- package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
- package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
- package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
- package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
- package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
- package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
- package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
- package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
- package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
- package/documentation/HEROKU_DEPLOYMENT.md +134 -0
- package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
- package/documentation/JACKPOT_API.md +521 -0
- package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
- package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
- package/documentation/JWT_QUICK_SETUP.md +268 -0
- package/documentation/JWT_TESTING_GUIDE.md +404 -0
- package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
- package/documentation/KEEPER_SETUP.md +206 -0
- package/documentation/KEEPER_STATE_MACHINE.md +423 -0
- package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
- package/documentation/LOCAL_VOTING_TEST.md +279 -0
- package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
- package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
- package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
- package/documentation/PNL_TRACKING_SETUP.md +189 -0
- package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
- package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
- package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
- package/documentation/QUICK_AUTH_SETUP.md +99 -0
- package/documentation/QUICK_DEPLOY.md +224 -0
- package/documentation/QUICK_FIX.md +114 -0
- package/documentation/QUICK_START.md +152 -0
- package/documentation/REFEREE_MODE_GUIDE.md +392 -0
- package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
- package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
- package/documentation/RUN_MIGRATION_NOW.md +39 -0
- package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
- package/documentation/SETUP_GUIDE.md +184 -0
- package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
- package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
- package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
- package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
- package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
- package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
- package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
- package/ecosystem.config.js +65 -0
- package/env.template +125 -0
- package/middleware/apiKeyAuth.js +136 -0
- package/middleware/authenticate.js +214 -0
- package/middleware/developerUserAuth.js +76 -0
- package/middleware/socketAuth.js +69 -0
- package/package.json +49 -0
- package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
- package/postman/Dubs-API-v1.postman_collection.json +205 -0
- package/postman/Dubs_Developer_API.postman_collection.json +662 -0
- package/postman/QUICKSTART.md +118 -0
- package/postman/QUICK_REFERENCE.md +246 -0
- package/postman/README.md +71 -0
- package/postman/VOTING_API_GUIDE.md +426 -0
- package/refactor/Animations.md +148 -0
- package/refactor/Chat.md +252 -0
- package/routes/actionsRoutes.js +699 -0
- package/routes/adminRoutes.js +370 -0
- package/routes/analyticsRoutes.js +1262 -0
- package/routes/arcadeRoutes.js +557 -0
- package/routes/authRoutes.js +2310 -0
- package/routes/avatarRoutes.js +85 -0
- package/routes/botRoutes.js +211 -0
- package/routes/chatRoutes.js +377 -0
- package/routes/cryptoPriceRoutes.js +105 -0
- package/routes/developerRoutes.js +4201 -0
- package/routes/deviceRoutes.js +214 -0
- package/routes/dmRoutes.js +167 -0
- package/routes/esportsRoutes.js +806 -0
- package/routes/exchangeRateRoutes.js +233 -0
- package/routes/gamesRoutes.js +3028 -0
- package/routes/jackpotRoutes.js +754 -0
- package/routes/keeperMonitoringRoutes.js +156 -0
- package/routes/keeperWebhookRoutes.js +466 -0
- package/routes/livescoresRoutes.js +31 -0
- package/routes/pickemAdminRoutes.js +199 -0
- package/routes/pickemRoutes.js +231 -0
- package/routes/playerStatsRoutes.js +147 -0
- package/routes/portfolioRoutes.js +217 -0
- package/routes/promoRoutes.js +418 -0
- package/routes/referralEarningsRoutes.js +392 -0
- package/routes/socialRoutes.js +459 -0
- package/routes/sportsRoutes.js +1271 -0
- package/routes/survivorAdminRoutes.js +345 -0
- package/routes/survivorRoutes.js +756 -0
- package/routes/uploadRoutes.js +256 -0
- package/routes/userProfileRoutes.js +244 -0
- package/routes/whatsNewRoutes.js +331 -0
- package/scripts/.claude/settings.local.json +15 -0
- package/scripts/README.md +170 -0
- package/scripts/RESTART_EVERYTHING.sh +104 -0
- package/scripts/add-claim-columns.sql +48 -0
- package/scripts/add-crypto-prices-cache.sql +27 -0
- package/scripts/add-exchange-rates-cache.sql +40 -0
- package/scripts/add-game-invite-column.sql +23 -0
- package/scripts/add-game-invite-notification.sql +33 -0
- package/scripts/add-game-invite-telegram-pref.sql +16 -0
- package/scripts/add-game-joined-notification.sql +16 -0
- package/scripts/add-game-joined-pref.js +40 -0
- package/scripts/add-game-joined-preference.sql +6 -0
- package/scripts/add-game-start-notifications.sql +41 -0
- package/scripts/add-notification-flags-to-games.sql +55 -0
- package/scripts/add-pending-game-dismissals.sql +19 -0
- package/scripts/add-preferred-currency.sql +34 -0
- package/scripts/add-winner-columns.js +61 -0
- package/scripts/add_mention_system.sql +53 -0
- package/scripts/add_payment_system.sql +96 -0
- package/scripts/add_sports_event_id_column.sql +22 -0
- package/scripts/analyze-cohort-data-heroku.js +276 -0
- package/scripts/analyze-cohort-data.js +295 -0
- package/scripts/analyze-prod-cohorts.sh +10 -0
- package/scripts/backfill-matchup-images.js +245 -0
- package/scripts/backfill-missing-signatures.js +175 -0
- package/scripts/backfill-referral-earnings.js +202 -0
- package/scripts/check-chat-schema.js +130 -0
- package/scripts/check-db.sh +14 -0
- package/scripts/check_oracle_in_game.js +54 -0
- package/scripts/cleanup-database.js +193 -0
- package/scripts/clear-notification-cache.js +85 -0
- package/scripts/convert-mnemonic.js +50 -0
- package/scripts/create-users-table.sql +44 -0
- package/scripts/debug-cohort-counts.js +248 -0
- package/scripts/debug-winner-calc.js +84 -0
- package/scripts/deploy-payment-system.sh +118 -0
- package/scripts/deploy-to-heroku.sh +63 -0
- package/scripts/diagnose-locked-round.js +143 -0
- package/scripts/dubs-cli.js +720 -0
- package/scripts/dump-account.js +65 -0
- package/scripts/find-vrf-offset.js +48 -0
- package/scripts/fix-chat-notifications-constraint.sql +122 -0
- package/scripts/fix-claim-columns.js +124 -0
- package/scripts/fix-constraint-now.js +44 -0
- package/scripts/fix-lock-timestamps.js +96 -0
- package/scripts/fix-locked-round.sh +126 -0
- package/scripts/fix-missing-badges.sql +91 -0
- package/scripts/fix-payment-notifications.sql +41 -0
- package/scripts/force-new-round.js +55 -0
- package/scripts/force-resolve-and-claim.js +278 -0
- package/scripts/important/README.md +115 -0
- package/scripts/important/authority-force-lock.js +197 -0
- package/scripts/important/authority-resolve-game.js +267 -0
- package/scripts/important/check-game-status.js +373 -0
- package/scripts/important/list-pending-games-by-version.js +270 -0
- package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
- package/scripts/initialize-jackpot.js +111 -0
- package/scripts/jackpot/.claude/settings.local.json +10 -0
- package/scripts/jackpot/force-reset.js +84 -0
- package/scripts/jackpot/initialize-mainnet.js +100 -0
- package/scripts/jackpot/keeper.js +742 -0
- package/scripts/jackpot/status.js +107 -0
- package/scripts/jackpot/update-round-duration.js +143 -0
- package/scripts/keeper-bot.js +112 -0
- package/scripts/list-pending-games.js +131 -0
- package/scripts/migrate-chat-v2.js +127 -0
- package/scripts/migrate-chat-winners.js +84 -0
- package/scripts/migrate-chat.sh +17 -0
- package/scripts/migrate-game-invite.js +83 -0
- package/scripts/migrate-heroku-game-notifications.sh +159 -0
- package/scripts/migrations/001_analytics_tables.sql +422 -0
- package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
- package/scripts/migrations/003_referral_earnings.sql +208 -0
- package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
- package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
- package/scripts/migrations/005_push_notifications.sql +55 -0
- package/scripts/migrations/006_add_draw_team_players.sql +28 -0
- package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
- package/scripts/migrations/007_add_gif_url.sql +8 -0
- package/scripts/migrations/008_add_connect4_columns.sql +139 -0
- package/scripts/migrations/008_add_pool_tracking.sql +22 -0
- package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
- package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
- package/scripts/migrations/011_create_developer_tables.sql +67 -0
- package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
- package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
- package/scripts/migrations/013_add_network_mode.sql +18 -0
- package/scripts/migrations/014_create_developer_app_users.sql +19 -0
- package/scripts/migrations/015_add_ui_config.sql +4 -0
- package/scripts/migrations/016_add_resolution_secret.sql +4 -0
- package/scripts/migrations/017_add_external_game_id.sql +3 -0
- package/scripts/migrations/018_create_pickem_tables.sql +115 -0
- package/scripts/migrations/019_expo_push_tokens.sql +19 -0
- package/scripts/migrations/create_whats_new_tables.sql +88 -0
- package/scripts/migrations/drop_live_games_tables.sql +34 -0
- package/scripts/open-jackpot-round.js +85 -0
- package/scripts/purge-all-data.sh +329 -0
- package/scripts/purge-all-data.sql +142 -0
- package/scripts/purge-heroku-data.sh +149 -0
- package/scripts/purge-heroku-data.sql +62 -0
- package/scripts/rebuild-heroku-database.sh +113 -0
- package/scripts/recover-funds.js +357 -0
- package/scripts/regenerate-epl-images.js +278 -0
- package/scripts/resize-s3-matchup-images.js +374 -0
- package/scripts/resolve-direct.js +88 -0
- package/scripts/resolve-mock-game.js +124 -0
- package/scripts/resolve-pickem-game.js +55 -0
- package/scripts/resolve-round-manual.js +83 -0
- package/scripts/resolve-stuck-game.js +382 -0
- package/scripts/resolve-stuck-round.js +42 -0
- package/scripts/run-connect4-migration.sh +16 -0
- package/scripts/run-mention-migration.sh +32 -0
- package/scripts/run-payment-migration.sh +51 -0
- package/scripts/run-preferred-currency-migration.sh +31 -0
- package/scripts/run-referral-earnings-migration.sh +32 -0
- package/scripts/run-survivor-outcome-migration.sh +16 -0
- package/scripts/seed-test-users.js +346 -0
- package/scripts/setup-auth-tables.js +78 -0
- package/scripts/setup-complete-database.sql +992 -0
- package/scripts/setup-database-fresh.sh +359 -0
- package/scripts/setup-heroku-keeper.sh +48 -0
- package/scripts/setup-keeper-database.js +83 -0
- package/scripts/setup-keeper-state-db.sql +110 -0
- package/scripts/setup-oracle.sh +39 -0
- package/scripts/setup-pnl-tracking.js +111 -0
- package/scripts/start-devnet.sh +14 -0
- package/scripts/test-arcade-devnet.sh +160 -0
- package/scripts/test-arcade-match.sh +109 -0
- package/scripts/test-automatic-mode.sh +239 -0
- package/scripts/test-connect4-cancel-claim.js +370 -0
- package/scripts/test-connect4-e2e.js +369 -0
- package/scripts/test-connect4-resolve.js +369 -0
- package/scripts/test-game-state-endpoint.js +136 -0
- package/scripts/test-invite-notification.js +86 -0
- package/scripts/test-jackpot-api.sh +71 -0
- package/scripts/test-poll-confirmation.js +267 -0
- package/scripts/test-resolve-game.js +271 -0
- package/scripts/test-resolve-signature.js +223 -0
- package/scripts/test-signature-preservation.js +124 -0
- package/scripts/test-state-machine.js +291 -0
- package/scripts/test-webhook-receiver.js +60 -0
- package/scripts/update-notification-constraint.js +52 -0
- package/scripts/verify-account-layout.js +145 -0
- package/scripts/verify-winner-algorithm.js +278 -0
- package/server.js +5259 -0
- package/services/arcadeMatchService.js +763 -0
- package/services/automaticGameOracle.js +1596 -0
- package/services/chatService.js +1612 -0
- package/services/connect4GameService.js +1049 -0
- package/services/connect4NotificationService.js +374 -0
- package/services/cryptoPriceService.js +223 -0
- package/services/customGameResolver.js +260 -0
- package/services/db.js +79 -0
- package/services/directMessageService.js +389 -0
- package/services/discordNotifications.js +160 -0
- package/services/exchangeRateService.js +289 -0
- package/services/expoPushService.js +314 -0
- package/services/gamesCacheService.js +539 -0
- package/services/jackpotHistory.js +331 -0
- package/services/jackpotService.js +856 -0
- package/services/keeperStateService.js +355 -0
- package/services/matchupImageService.js +591 -0
- package/services/notificationCacheService.js +407 -0
- package/services/pickemOracle.js +440 -0
- package/services/playerStatsService.js +389 -0
- package/services/portfolioService.js +555 -0
- package/services/promoService.js +757 -0
- package/services/promoTreasuryService.js +239 -0
- package/services/pushNotifications.js +353 -0
- package/services/redisService.js +422 -0
- package/services/referralEarningsService.js +728 -0
- package/services/s3Service.js +396 -0
- package/services/socialService.js +1202 -0
- package/services/survivorOracle.js +469 -0
- package/services/survivorSimulator.js +475 -0
- package/services/telegramNotifications.js +461 -0
- package/services/userProfileStatsService.js +1185 -0
- package/services/whatsNewService.js +388 -0
- package/utils/urlHelper.js +95 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test Script: Connect4 Game Resolution with Polling Confirmation
|
|
4
|
+
*
|
|
5
|
+
* Tests the resolveGame function in connect4GameService.js
|
|
6
|
+
* to verify the Alchemy-compatible polling confirmation works.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node test-connect4-resolve.js <gameId> # Resolve specific game
|
|
10
|
+
* node test-connect4-resolve.js --find-pending # Find and list pending games
|
|
11
|
+
* node test-connect4-resolve.js --dry-run <gameId> # Show what would happen
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
require('dotenv').config();
|
|
15
|
+
const { Connection, Keypair, PublicKey, Transaction, TransactionInstruction } = require('@solana/web3.js');
|
|
16
|
+
const { pool } = require('../services/db');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
// Configuration
|
|
21
|
+
const RPC_URL = process.env.SOLANA_RPC_URL || process.env.SOLANA_NETWORK;
|
|
22
|
+
const PROGRAM_ID = new PublicKey(process.env.PROGRAM_ID || '85wJGp9uc8w2FeKX9CEHsudTo1UVCrmuRFy37oCcaoG1');
|
|
23
|
+
const RESOLVE_AUTO_DISCRIMINATOR = Buffer.from([245, 33, 115, 150, 82, 150, 28, 193]);
|
|
24
|
+
|
|
25
|
+
// Parse args
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const options = {
|
|
28
|
+
gameId: null,
|
|
29
|
+
findPending: false,
|
|
30
|
+
dryRun: false,
|
|
31
|
+
help: false
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
args.forEach((arg, i) => {
|
|
35
|
+
if (arg === '--find-pending') options.findPending = true;
|
|
36
|
+
else if (arg === '--dry-run') {
|
|
37
|
+
options.dryRun = true;
|
|
38
|
+
options.gameId = args[i + 1];
|
|
39
|
+
}
|
|
40
|
+
else if (arg === '--help') options.help = true;
|
|
41
|
+
else if (!arg.startsWith('--')) options.gameId = arg;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
console.log('๐งช Connect4 Resolution Test (with Polling Confirmation)');
|
|
45
|
+
console.log('========================================================');
|
|
46
|
+
console.log(`RPC URL: ${RPC_URL || 'NOT SET'}`);
|
|
47
|
+
console.log(`Program ID: ${PROGRAM_ID.toString()}`);
|
|
48
|
+
console.log('');
|
|
49
|
+
|
|
50
|
+
if (!RPC_URL) {
|
|
51
|
+
console.error('โ ERROR: SOLANA_RPC_URL environment variable is not set!');
|
|
52
|
+
console.log(' Set it to your Alchemy RPC URL');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const connection = new Connection(RPC_URL, 'confirmed');
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Load oracle keypair
|
|
60
|
+
*/
|
|
61
|
+
function loadOracleKeypair() {
|
|
62
|
+
if (process.env.ORACLE_WALLET_JSON) {
|
|
63
|
+
const secretKey = JSON.parse(process.env.ORACLE_WALLET_JSON);
|
|
64
|
+
return Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const walletPath = process.env.ORACLE_WALLET_PATH || path.join(__dirname, '../wallets/oracle.json');
|
|
68
|
+
if (fs.existsSync(walletPath)) {
|
|
69
|
+
const secretKey = JSON.parse(fs.readFileSync(walletPath, 'utf-8'));
|
|
70
|
+
return Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw new Error('Oracle keypair not found');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Poll for transaction confirmation using getSignatureStatuses
|
|
78
|
+
* This is the Alchemy-compatible approach (no WebSocket subscriptions)
|
|
79
|
+
*/
|
|
80
|
+
async function pollTransactionConfirmation(signature, lastValidBlockHeight, timeout = 30000) {
|
|
81
|
+
const start = Date.now();
|
|
82
|
+
console.log(`๐ Polling confirmation for ${signature}`);
|
|
83
|
+
console.log(` lastValidBlockHeight: ${lastValidBlockHeight}`);
|
|
84
|
+
|
|
85
|
+
let pollCount = 0;
|
|
86
|
+
while (Date.now() - start < timeout) {
|
|
87
|
+
pollCount++;
|
|
88
|
+
try {
|
|
89
|
+
// Check if blockhash has expired
|
|
90
|
+
const currentBlockHeight = await connection.getBlockHeight('confirmed');
|
|
91
|
+
if (currentBlockHeight > lastValidBlockHeight) {
|
|
92
|
+
throw new Error(`Transaction expired: blockhash no longer valid (current: ${currentBlockHeight}, lastValid: ${lastValidBlockHeight})`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Poll signature status
|
|
96
|
+
const response = await connection.getSignatureStatuses([signature], {
|
|
97
|
+
searchTransactionHistory: true
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const status = response?.value?.[0];
|
|
101
|
+
if (status) {
|
|
102
|
+
console.log(` [Poll ${pollCount}] confirmationStatus: ${status.confirmationStatus}, err: ${status.err ? JSON.stringify(status.err) : 'null'}`);
|
|
103
|
+
|
|
104
|
+
if (status.err) {
|
|
105
|
+
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized') {
|
|
109
|
+
console.log(`โ
Transaction confirmed: ${status.confirmationStatus}`);
|
|
110
|
+
return status;
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
console.log(` [Poll ${pollCount}] Status: null (not confirmed yet)`);
|
|
114
|
+
}
|
|
115
|
+
} catch (pollErr) {
|
|
116
|
+
if (pollErr.message?.includes('expired') || pollErr.message?.includes('failed')) {
|
|
117
|
+
throw pollErr;
|
|
118
|
+
}
|
|
119
|
+
console.warn(` [Poll ${pollCount}] Error (will retry): ${pollErr.message}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Find pending Connect4 games
|
|
130
|
+
*/
|
|
131
|
+
async function findPendingGames() {
|
|
132
|
+
console.log('๐ Finding pending Connect4 games...\n');
|
|
133
|
+
|
|
134
|
+
const result = await pool.query(`
|
|
135
|
+
SELECT
|
|
136
|
+
g.game_id, g.game_status, g.connect4_winner, g.is_resolved, g.created_by,
|
|
137
|
+
g.buy_in, g.created_at,
|
|
138
|
+
creator.wallet_address as creator_wallet,
|
|
139
|
+
opponent.wallet_address as opponent_wallet
|
|
140
|
+
FROM games g
|
|
141
|
+
LEFT JOIN user_game_refs creator ON g.game_id = creator.game_id AND creator.role = 'creator'
|
|
142
|
+
LEFT JOIN user_game_refs opponent ON g.game_id = opponent.game_id AND opponent.role = 'opponent'
|
|
143
|
+
WHERE g.game_type = 'connect4'
|
|
144
|
+
AND g.game_status IN ('playing', 'waiting', 'pending')
|
|
145
|
+
AND g.is_resolved = FALSE
|
|
146
|
+
ORDER BY g.created_at DESC
|
|
147
|
+
LIMIT 10
|
|
148
|
+
`);
|
|
149
|
+
|
|
150
|
+
if (result.rows.length === 0) {
|
|
151
|
+
console.log(' No pending games found');
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(`Found ${result.rows.length} pending game(s):\n`);
|
|
156
|
+
result.rows.forEach(g => {
|
|
157
|
+
console.log(` Game ID: ${g.game_id}`);
|
|
158
|
+
console.log(` Status: ${g.game_status}`);
|
|
159
|
+
console.log(` Creator: ${g.creator_wallet?.slice(0, 16) || g.created_by?.slice(0, 16)}...`);
|
|
160
|
+
console.log(` Opponent: ${g.opponent_wallet?.slice(0, 16) || 'N/A'}...`);
|
|
161
|
+
console.log(` Buy-in: ${g.buy_in} SOL`);
|
|
162
|
+
console.log(` Created: ${g.created_at}`);
|
|
163
|
+
console.log('');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return result.rows;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get game details
|
|
171
|
+
*/
|
|
172
|
+
async function getGameDetails(gameId) {
|
|
173
|
+
const result = await pool.query(`
|
|
174
|
+
SELECT
|
|
175
|
+
g.game_id, g.game_status, g.connect4_winner as winner, g.is_resolved,
|
|
176
|
+
g.created_by, g.buy_in, g.game_address as game_pda, g.claim_signature,
|
|
177
|
+
creator.wallet_address as creator_wallet,
|
|
178
|
+
creator.claimed_at as creator_claimed_at
|
|
179
|
+
FROM games g
|
|
180
|
+
LEFT JOIN user_game_refs creator ON g.game_id = creator.game_id AND creator.role = 'creator'
|
|
181
|
+
WHERE g.game_id = $1
|
|
182
|
+
`, [gameId]);
|
|
183
|
+
|
|
184
|
+
if (result.rows.length === 0) {
|
|
185
|
+
throw new Error(`Game ${gameId} not found`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return result.rows[0];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Build resolve instruction (for cancel: winner = null)
|
|
193
|
+
*/
|
|
194
|
+
function buildResolveInstruction(gamePDA, gameIdBuf, winner, oracleKeypair) {
|
|
195
|
+
// Winner: 0 = Team A wins, 1 = Team B wins, 2 = Draw, 255 = No winner (cancel)
|
|
196
|
+
let winnerByte;
|
|
197
|
+
if (winner === null || winner === 'cancel') {
|
|
198
|
+
winnerByte = 255; // No winner - refund both
|
|
199
|
+
} else if (winner === 'creator' || winner === 'teamA' || winner === 0) {
|
|
200
|
+
winnerByte = 0;
|
|
201
|
+
} else if (winner === 'opponent' || winner === 'teamB' || winner === 1) {
|
|
202
|
+
winnerByte = 1;
|
|
203
|
+
} else if (winner === 'draw') {
|
|
204
|
+
winnerByte = 2;
|
|
205
|
+
} else {
|
|
206
|
+
throw new Error(`Invalid winner value: ${winner}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const data = Buffer.concat([
|
|
210
|
+
RESOLVE_AUTO_DISCRIMINATOR,
|
|
211
|
+
gameIdBuf,
|
|
212
|
+
Buffer.from([winnerByte])
|
|
213
|
+
]);
|
|
214
|
+
|
|
215
|
+
const OPERATOR_WALLET = new PublicKey(process.env.OPERATOR_WALLET || 'BVZXwZpfgyzTBdRFHohkHZppPHnAyqyctRsKy3vWfQib');
|
|
216
|
+
|
|
217
|
+
const keys = [
|
|
218
|
+
{ pubkey: gamePDA, isSigner: false, isWritable: true },
|
|
219
|
+
{ pubkey: oracleKeypair.publicKey, isSigner: true, isWritable: true },
|
|
220
|
+
{ pubkey: OPERATOR_WALLET, isSigner: false, isWritable: true },
|
|
221
|
+
{ pubkey: require('@solana/web3.js').SystemProgram.programId, isSigner: false, isWritable: false },
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
return new TransactionInstruction({
|
|
225
|
+
keys,
|
|
226
|
+
programId: PROGRAM_ID,
|
|
227
|
+
data
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Resolve a Connect4 game (cancel)
|
|
233
|
+
*/
|
|
234
|
+
async function resolveGame(gameId, winner = null) {
|
|
235
|
+
console.log(`\n๐ Resolving Connect4 Game: ${gameId}`);
|
|
236
|
+
console.log('------------------------------------------');
|
|
237
|
+
|
|
238
|
+
const game = await getGameDetails(gameId);
|
|
239
|
+
console.log(` Status: ${game.game_status}`);
|
|
240
|
+
console.log(` is_resolved: ${game.is_resolved}`);
|
|
241
|
+
console.log(` Winner: ${game.winner || 'null'}`);
|
|
242
|
+
console.log(` Game PDA: ${game.game_pda || 'N/A'}`);
|
|
243
|
+
console.log(` Creator: ${game.creator_wallet || game.created_by}`);
|
|
244
|
+
|
|
245
|
+
if (game.is_resolved) {
|
|
246
|
+
console.log('\nโ ๏ธ Game is already resolved!');
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Load oracle
|
|
251
|
+
const oracleKeypair = loadOracleKeypair();
|
|
252
|
+
console.log(` Oracle: ${oracleKeypair.publicKey.toString()}`);
|
|
253
|
+
|
|
254
|
+
// Derive game PDA if not stored
|
|
255
|
+
let gamePDA;
|
|
256
|
+
if (game.game_pda) {
|
|
257
|
+
gamePDA = new PublicKey(game.game_pda);
|
|
258
|
+
} else {
|
|
259
|
+
// Derive from game_id
|
|
260
|
+
const gameIdNum = BigInt(gameId.replace('connect4-', '').split('-')[0]);
|
|
261
|
+
const gameIdBuf = Buffer.alloc(8);
|
|
262
|
+
gameIdBuf.writeBigUInt64LE(gameIdNum);
|
|
263
|
+
|
|
264
|
+
[gamePDA] = PublicKey.findProgramAddressSync(
|
|
265
|
+
[Buffer.from('game'), gameIdBuf],
|
|
266
|
+
PROGRAM_ID
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
console.log(` Derived PDA: ${gamePDA.toString()}`);
|
|
270
|
+
|
|
271
|
+
// Check if account exists on-chain
|
|
272
|
+
const accountInfo = await connection.getAccountInfo(gamePDA);
|
|
273
|
+
if (!accountInfo) {
|
|
274
|
+
console.log('\nโ Game account not found on-chain!');
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
console.log(` On-chain account: ${accountInfo.data.length} bytes`);
|
|
278
|
+
|
|
279
|
+
// Build game ID buffer
|
|
280
|
+
const gameIdNum = BigInt(gameId.replace('connect4-', '').split('-')[0]);
|
|
281
|
+
const gameIdBuf = Buffer.alloc(8);
|
|
282
|
+
gameIdBuf.writeBigUInt64LE(gameIdNum);
|
|
283
|
+
|
|
284
|
+
// Build instruction
|
|
285
|
+
const ix = buildResolveInstruction(gamePDA, gameIdBuf, winner, oracleKeypair);
|
|
286
|
+
|
|
287
|
+
// Build transaction
|
|
288
|
+
const tx = new Transaction().add(ix);
|
|
289
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
|
|
290
|
+
tx.recentBlockhash = blockhash;
|
|
291
|
+
tx.feePayer = oracleKeypair.publicKey;
|
|
292
|
+
tx.sign(oracleKeypair);
|
|
293
|
+
|
|
294
|
+
console.log(`\n๐ก Sending resolve transaction...`);
|
|
295
|
+
console.log(` Blockhash: ${blockhash}`);
|
|
296
|
+
console.log(` lastValidBlockHeight: ${lastValidBlockHeight}`);
|
|
297
|
+
|
|
298
|
+
const signature = await connection.sendRawTransaction(tx.serialize());
|
|
299
|
+
console.log(` Signature: ${signature}`);
|
|
300
|
+
console.log(` https://solscan.io/tx/${signature}`);
|
|
301
|
+
|
|
302
|
+
// Use polling confirmation (Alchemy-compatible)
|
|
303
|
+
console.log(`\nโณ Waiting for confirmation (polling)...`);
|
|
304
|
+
try {
|
|
305
|
+
await pollTransactionConfirmation(signature, lastValidBlockHeight, 30000);
|
|
306
|
+
console.log(`\nโ
Game resolved successfully!`);
|
|
307
|
+
|
|
308
|
+
// Update database
|
|
309
|
+
await pool.query(`
|
|
310
|
+
UPDATE games SET
|
|
311
|
+
is_resolved = TRUE,
|
|
312
|
+
game_status = 'cancelled',
|
|
313
|
+
connect4_winner = NULL,
|
|
314
|
+
claim_signature = $1,
|
|
315
|
+
resolved_at = NOW(),
|
|
316
|
+
updated_at = NOW()
|
|
317
|
+
WHERE game_id = $2
|
|
318
|
+
`, [signature, gameId]);
|
|
319
|
+
|
|
320
|
+
console.log(` Database updated`);
|
|
321
|
+
return signature;
|
|
322
|
+
|
|
323
|
+
} catch (confirmErr) {
|
|
324
|
+
if (confirmErr.message?.includes('AlreadyResolved')) {
|
|
325
|
+
console.log(`\nโ ๏ธ Game was already resolved on-chain`);
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
throw confirmErr;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Main
|
|
334
|
+
*/
|
|
335
|
+
async function main() {
|
|
336
|
+
if (options.help) {
|
|
337
|
+
console.log('Usage:');
|
|
338
|
+
console.log(' node test-connect4-resolve.js <gameId> # Resolve (cancel) game');
|
|
339
|
+
console.log(' node test-connect4-resolve.js --find-pending # List pending games');
|
|
340
|
+
console.log(' node test-connect4-resolve.js --dry-run <gameId> # Show details only');
|
|
341
|
+
process.exit(0);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
if (options.findPending) {
|
|
346
|
+
await findPendingGames();
|
|
347
|
+
} else if (options.dryRun && options.gameId) {
|
|
348
|
+
console.log('DRY RUN - showing game details only\n');
|
|
349
|
+
const game = await getGameDetails(options.gameId);
|
|
350
|
+
console.log(JSON.stringify(game, null, 2));
|
|
351
|
+
} else if (options.gameId) {
|
|
352
|
+
await resolveGame(options.gameId, null); // null = cancel
|
|
353
|
+
} else {
|
|
354
|
+
console.log('No game ID provided. Use --find-pending to list games.');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error('\nโ Error:', error.message);
|
|
359
|
+
if (error.logs) {
|
|
360
|
+
console.error('Program logs:');
|
|
361
|
+
error.logs.forEach(log => console.error(` ${log}`));
|
|
362
|
+
}
|
|
363
|
+
process.exit(1);
|
|
364
|
+
} finally {
|
|
365
|
+
await pool.end();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
main();
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test Game State Endpoint
|
|
4
|
+
* Tests the /api/games/:gameId/state endpoint for different users
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
require('dotenv').config();
|
|
8
|
+
const axios = require('axios');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
|
|
11
|
+
// Create axios instance that ignores SSL cert issues (for testing only!)
|
|
12
|
+
const axiosInstance = axios.create({
|
|
13
|
+
httpsAgent: new https.Agent({
|
|
14
|
+
rejectUnauthorized: false
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const API_URL = process.env.DUBS_SERVER_URL || 'http://localhost:3001';
|
|
19
|
+
|
|
20
|
+
// Test game ID from database
|
|
21
|
+
const TEST_GAME_ID = 'sport-1764007906160-dnxc6zbps';
|
|
22
|
+
|
|
23
|
+
// Test wallets - all 3 joined away team and WON
|
|
24
|
+
const TEST_WALLETS = {
|
|
25
|
+
creator: '3sgQXvLKs4XfBTxG3jY2J2E5NougemfC6i4nZbUVx8xt', // Away team (WINNER)
|
|
26
|
+
joiner1: 'E2SXYN3GVvQyEo1Pei8PUsyTmFqgkfuK7jTau9CrpfYC', // Away team (WINNER)
|
|
27
|
+
joiner2: 'CMt4nTCGmTXu7LsH5ujsnUsnBpb6qk7hQJAUPvHoW4aD', // Away team (WINNER)
|
|
28
|
+
nonParticipant: 'NonParticipantWallet123'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
async function testGameState(walletAddress, label) {
|
|
32
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
33
|
+
console.log(`Testing: ${label}`);
|
|
34
|
+
console.log(`Wallet: ${walletAddress}`);
|
|
35
|
+
console.log('='.repeat(60));
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const response = await axiosInstance.get(
|
|
39
|
+
`${API_URL}/api/games/${TEST_GAME_ID}/state`,
|
|
40
|
+
{
|
|
41
|
+
headers: {
|
|
42
|
+
'x-wallet-address': walletAddress
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
console.log('\nโ
Response Status:', response.status);
|
|
48
|
+
console.log('\n๐ Game State:');
|
|
49
|
+
console.log(JSON.stringify(response.data, null, 2));
|
|
50
|
+
|
|
51
|
+
// Validate expected fields
|
|
52
|
+
const state = response.data;
|
|
53
|
+
console.log('\n๐ Validation:');
|
|
54
|
+
console.log(` isLocked: ${state.isLocked} ${state.isLocked !== undefined ? 'โ
' : 'โ'}`);
|
|
55
|
+
console.log(` isResolved: ${state.isResolved} ${state.isResolved !== undefined ? 'โ
' : 'โ'}`);
|
|
56
|
+
console.log(` totalPlayers: ${state.totalPlayers} ${state.totalPlayers !== undefined ? 'โ
' : 'โ'}`);
|
|
57
|
+
|
|
58
|
+
if (state.isResolved) {
|
|
59
|
+
console.log(` winner: ${state.winner} ${state.winner ? 'โ
' : 'โ'}`);
|
|
60
|
+
console.log(` homeScore: ${state.homeScore} ${state.homeScore !== undefined ? 'โ
' : 'โ'}`);
|
|
61
|
+
console.log(` awayScore: ${state.awayScore} ${state.awayScore !== undefined ? 'โ
' : 'โ'}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log(` userParticipated: ${state.userParticipated} ${state.userParticipated !== undefined ? 'โ
' : 'โ'}`);
|
|
65
|
+
|
|
66
|
+
if (state.userParticipated) {
|
|
67
|
+
console.log(` userTeamChoice: ${state.userTeamChoice} ${state.userTeamChoice ? 'โ
' : 'โ'}`);
|
|
68
|
+
console.log(` userWon: ${state.userWon} ${state.userWon !== undefined ? 'โ
' : 'โ'}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Determine expected button state
|
|
72
|
+
console.log('\n๐ฎ Expected Button State:');
|
|
73
|
+
if (state.isResolved) {
|
|
74
|
+
if (state.userParticipated) {
|
|
75
|
+
if (state.userWon) {
|
|
76
|
+
console.log(' โ
Should show: "๐ Claim Prize" (green, pulsing)');
|
|
77
|
+
} else {
|
|
78
|
+
console.log(' โ Should show: "โ๏ธ You Lost" (red)');
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
console.log(' ๐๏ธ Should show: "View Results" (grey)');
|
|
82
|
+
}
|
|
83
|
+
} else if (state.isLocked) {
|
|
84
|
+
console.log(' ๐ Should show: "Game Started" (grey, disabled)');
|
|
85
|
+
} else {
|
|
86
|
+
console.log(' ๐ Should show: "Join Bet" (gradient, clickable)');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('\nโ Error:', error.message);
|
|
91
|
+
if (error.response) {
|
|
92
|
+
console.error('Status:', error.response.status);
|
|
93
|
+
console.error('Data:', error.response.data);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function runTests() {
|
|
99
|
+
console.log('๐งช Testing Game State Endpoint');
|
|
100
|
+
console.log(`API URL: ${API_URL}`);
|
|
101
|
+
console.log(`Game ID: ${TEST_GAME_ID}`);
|
|
102
|
+
|
|
103
|
+
// Test 1: Creator (who participated)
|
|
104
|
+
await testGameState(TEST_WALLETS.creator, 'Creator/Participant');
|
|
105
|
+
|
|
106
|
+
// Test 2: Joiner 1
|
|
107
|
+
await testGameState(TEST_WALLETS.joiner1, 'Joiner #1');
|
|
108
|
+
|
|
109
|
+
// Test 3: Joiner 2
|
|
110
|
+
await testGameState(TEST_WALLETS.joiner2, 'Joiner #2');
|
|
111
|
+
|
|
112
|
+
// Test 4: Non-participant
|
|
113
|
+
await testGameState(TEST_WALLETS.nonParticipant, 'Non-Participant (Spectator)');
|
|
114
|
+
|
|
115
|
+
// Test 5: No wallet header
|
|
116
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
117
|
+
console.log('Testing: No Wallet Header (Public Access)');
|
|
118
|
+
console.log('='.repeat(60));
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const response = await axiosInstance.get(`${API_URL}/api/games/${TEST_GAME_ID}/state`);
|
|
122
|
+
console.log('\nโ
Response Status:', response.status);
|
|
123
|
+
console.log('\n๐ Game State:');
|
|
124
|
+
console.log(JSON.stringify(response.data, null, 2));
|
|
125
|
+
console.log('\n๐ Should NOT include: userParticipated, userWon, userTeamChoice');
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error('\nโ Error:', error.message);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log('\n' + '='.repeat(60));
|
|
131
|
+
console.log('โ
Tests Complete!');
|
|
132
|
+
console.log('='.repeat(60));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
runTests().catch(console.error);
|
|
136
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test script for game invite notifications with CTA button
|
|
3
|
+
*
|
|
4
|
+
* Usage: node test-invite-notification.js <gameId> <telegramUserId> [botToken]
|
|
5
|
+
*
|
|
6
|
+
* Example:
|
|
7
|
+
* node test-invite-notification.js sport-1766923697972-8viqv89wv 7874505364
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
require('dotenv').config();
|
|
11
|
+
|
|
12
|
+
const GAME_ID = process.argv[2];
|
|
13
|
+
const TELEGRAM_USER_ID = process.argv[3];
|
|
14
|
+
const BOT_TOKEN = process.argv[4] || process.env.TELEGRAM_BOT_TOKEN;
|
|
15
|
+
|
|
16
|
+
if (!GAME_ID || !TELEGRAM_USER_ID) {
|
|
17
|
+
console.error('Usage: node test-invite-notification.js <gameId> <telegramUserId> [botToken]');
|
|
18
|
+
console.error('Example: node test-invite-notification.js sport-1766923697972-8viqv89wv 7874505364');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!BOT_TOKEN) {
|
|
23
|
+
console.error('โ No bot token provided. Either set TELEGRAM_BOT_TOKEN in .env or pass as 3rd argument.');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const axios = require('axios');
|
|
28
|
+
const urlHelper = require('../utils/urlHelper');
|
|
29
|
+
|
|
30
|
+
const TELEGRAM_API = `https://api.telegram.org/bot${BOT_TOKEN}`;
|
|
31
|
+
|
|
32
|
+
async function sendTestInviteNotification() {
|
|
33
|
+
console.log('\n๐งช Test Game Invite Notification');
|
|
34
|
+
console.log('='.repeat(40));
|
|
35
|
+
console.log(`๐ Game ID: ${GAME_ID}`);
|
|
36
|
+
console.log(`๐ค Telegram User ID: ${TELEGRAM_USER_ID}`);
|
|
37
|
+
console.log(`๐ Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
38
|
+
|
|
39
|
+
const joinUrl = urlHelper.getGameShareUrl(GAME_ID);
|
|
40
|
+
console.log(`\n๐ Join URL: ${joinUrl}`);
|
|
41
|
+
|
|
42
|
+
const inviterUsername = '78naim';
|
|
43
|
+
const gameTitle = 'Los Angeles Rams @ Atlanta Falcons';
|
|
44
|
+
const buyIn = '0.1';
|
|
45
|
+
|
|
46
|
+
const notificationText = `๐ *Notification*\n\n๐ฎ ${inviterUsername} invited you to join their bet!\n\n${inviterUsername} invited you to join their ${gameTitle} bet! ${buyIn} SOL buy-in`;
|
|
47
|
+
|
|
48
|
+
const payload = {
|
|
49
|
+
chat_id: TELEGRAM_USER_ID,
|
|
50
|
+
text: notificationText,
|
|
51
|
+
parse_mode: 'Markdown',
|
|
52
|
+
disable_web_page_preview: true,
|
|
53
|
+
reply_markup: {
|
|
54
|
+
inline_keyboard: [[
|
|
55
|
+
{ text: '๐ฎ Join Game', url: joinUrl }
|
|
56
|
+
]]
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
console.log('\n๐ค Sending notification...');
|
|
62
|
+
const response = await axios.post(`${TELEGRAM_API}/sendMessage`, payload);
|
|
63
|
+
|
|
64
|
+
if (response.data.ok) {
|
|
65
|
+
console.log(`\nโ
Notification sent successfully!`);
|
|
66
|
+
console.log(`๐ฌ Message ID: ${response.data.result.message_id}`);
|
|
67
|
+
console.log(`\n๐ฑ Next steps:
|
|
68
|
+
1. Check your Telegram for the notification
|
|
69
|
+
2. You should see a "๐ฎ Join Game" button
|
|
70
|
+
3. Tap the button to test the deferred deep link
|
|
71
|
+
4. Verify it takes you to the game join page
|
|
72
|
+
`);
|
|
73
|
+
} else {
|
|
74
|
+
console.error('โ Telegram API returned not OK:', response.data);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error.response) {
|
|
78
|
+
console.error(`โ Telegram API error (${error.response.status}):`, error.response.data?.description || error.message);
|
|
79
|
+
} else {
|
|
80
|
+
console.error('โ Failed to send:', error.message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
sendTestInviteNotification();
|
|
86
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# ๐ฐ Test script for Jackpot API
|
|
4
|
+
# Tests all endpoints to ensure they're working
|
|
5
|
+
|
|
6
|
+
BASE_URL="http://localhost:3001/jackpot"
|
|
7
|
+
|
|
8
|
+
echo "๐ฐ Testing Dubs Jackpot API"
|
|
9
|
+
echo "============================="
|
|
10
|
+
echo ""
|
|
11
|
+
|
|
12
|
+
# Colors
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
BLUE='\033[0;34m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
RED='\033[0;31m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
|
|
19
|
+
# Test health endpoint
|
|
20
|
+
echo -e "${BLUE}1. Testing health endpoint...${NC}"
|
|
21
|
+
curl -s $BASE_URL/health | jq '.'
|
|
22
|
+
echo ""
|
|
23
|
+
|
|
24
|
+
# Test config endpoint
|
|
25
|
+
echo -e "${BLUE}2. Testing config endpoint...${NC}"
|
|
26
|
+
curl -s $BASE_URL/config | jq '.'
|
|
27
|
+
echo ""
|
|
28
|
+
|
|
29
|
+
# Test current round
|
|
30
|
+
echo -e "${BLUE}3. Testing current round endpoint...${NC}"
|
|
31
|
+
curl -s $BASE_URL/round/current | jq '.'
|
|
32
|
+
echo ""
|
|
33
|
+
|
|
34
|
+
# Test stats endpoint
|
|
35
|
+
echo -e "${BLUE}4. Testing stats endpoint...${NC}"
|
|
36
|
+
curl -s $BASE_URL/stats | jq '.'
|
|
37
|
+
echo ""
|
|
38
|
+
|
|
39
|
+
# Test building an enter transaction
|
|
40
|
+
echo -e "${BLUE}5. Testing build enter transaction...${NC}"
|
|
41
|
+
curl -s -X POST $BASE_URL/build/enter \
|
|
42
|
+
-H "Content-Type: application/json" \
|
|
43
|
+
-d '{
|
|
44
|
+
"playerAddress": "57voP1Y8U4ztX2YAcHveK3JFvVRn1n6T6iUnHdAW6xr9",
|
|
45
|
+
"amount": 100000000
|
|
46
|
+
}' | jq '.transaction | length'
|
|
47
|
+
echo " characters in transaction"
|
|
48
|
+
echo ""
|
|
49
|
+
|
|
50
|
+
# Test building open round transaction
|
|
51
|
+
echo -e "${BLUE}6. Testing build open-round transaction...${NC}"
|
|
52
|
+
curl -s -X POST $BASE_URL/build/open-round \
|
|
53
|
+
-H "Content-Type: application/json" \
|
|
54
|
+
-d '{
|
|
55
|
+
"keeperAddress": "57voP1Y8U4ztX2YAcHveK3JFvVRn1n6T6iUnHdAW6xr9"
|
|
56
|
+
}' | jq '.roundId, .roundPda'
|
|
57
|
+
echo ""
|
|
58
|
+
|
|
59
|
+
echo -e "${GREEN}โ
API Test Complete!${NC}"
|
|
60
|
+
echo ""
|
|
61
|
+
echo "๐ Full API docs: JACKPOT_API.md"
|
|
62
|
+
echo "๐ฐ Program ID: bqoSjTSPLweMuqNG6jy39jmzGyZvZdWjsr4csGfD8F6"
|
|
63
|
+
echo ""
|
|
64
|
+
echo "๐ Start server: node server.js"
|
|
65
|
+
echo "๐ป Access at: http://localhost:3001/jackpot"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|