update: brawl stars player stats (WIP) & brawl stars player bind (2 DC user)
This commit is contained in:
		
							parent
							
								
									c2a48e6d5f
								
							
						
					
					
						commit
						e21628c09c
					
				
							
								
								
									
										
											BIN
										
									
								
								assets/brawl_logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/brawl_logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 138 KiB | 
							
								
								
									
										16
									
								
								bun.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								bun.lock
									
									
									
									
									
								
							| @ -12,7 +12,7 @@ | ||||
|       }, | ||||
|       "devDependencies": { | ||||
|         "@types/bun": "latest", | ||||
|         "prisma": "^6.6.0", | ||||
|         "prisma": "^6.7.0", | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "typescript": "^5", | ||||
| @ -78,17 +78,17 @@ | ||||
| 
 | ||||
|     "@prisma/client": ["@prisma/client@6.6.0", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-vfp73YT/BHsWWOAuthKQ/1lBgESSqYqAWZEYyTdGXyFAHpmewwWL2Iz6ErIzkj4aHbuc6/cGSsE6ZY+pBO04Cg=="], | ||||
| 
 | ||||
|     "@prisma/config": ["@prisma/config@6.6.0", "", { "dependencies": { "esbuild": ">=0.12 <1", "esbuild-register": "3.6.0" } }, "sha512-d8FlXRHsx72RbN8nA2QCRORNv5AcUnPXgtPvwhXmYkQSMF/j9cKaJg+9VcUzBRXGy9QBckNzEQDEJZdEOZ+ubA=="], | ||||
|     "@prisma/config": ["@prisma/config@6.7.0", "", { "dependencies": { "esbuild": ">=0.12 <1", "esbuild-register": "3.6.0" } }, "sha512-di8QDdvSz7DLUi3OOcCHSwxRNeW7jtGRUD2+Z3SdNE3A+pPiNT8WgUJoUyOwJmUr5t+JA2W15P78C/N+8RXrOA=="], | ||||
| 
 | ||||
|     "@prisma/debug": ["@prisma/debug@6.6.0", "", {}, "sha512-DL6n4IKlW5k2LEXzpN60SQ1kP/F6fqaCgU/McgaYsxSf43GZ8lwtmXLke9efS+L1uGmrhtBUP4npV/QKF8s2ZQ=="], | ||||
|     "@prisma/debug": ["@prisma/debug@6.7.0", "", {}, "sha512-RabHn9emKoYFsv99RLxvfG2GHzWk2ZI1BuVzqYtmMSIcuGboHY5uFt3Q3boOREM9de6z5s3bQoyKeWnq8Fz22w=="], | ||||
| 
 | ||||
|     "@prisma/engines": ["@prisma/engines@6.6.0", "", { "dependencies": { "@prisma/debug": "6.6.0", "@prisma/engines-version": "6.6.0-53.f676762280b54cd07c770017ed3711ddde35f37a", "@prisma/fetch-engine": "6.6.0", "@prisma/get-platform": "6.6.0" } }, "sha512-nC0IV4NHh7500cozD1fBoTwTD1ydJERndreIjpZr/S3mno3P6tm8qnXmIND5SwUkibNeSJMpgl4gAnlqJ/gVlg=="], | ||||
|     "@prisma/engines": ["@prisma/engines@6.7.0", "", { "dependencies": { "@prisma/debug": "6.7.0", "@prisma/engines-version": "6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed", "@prisma/fetch-engine": "6.7.0", "@prisma/get-platform": "6.7.0" } }, "sha512-3wDMesnOxPrOsq++e5oKV9LmIiEazFTRFZrlULDQ8fxdub5w4NgRBoxtWbvXmj2nJVCnzuz6eFix3OhIqsZ1jw=="], | ||||
| 
 | ||||
|     "@prisma/engines-version": ["@prisma/engines-version@6.6.0-53.f676762280b54cd07c770017ed3711ddde35f37a", "", {}, "sha512-JzRaQ5Em1fuEcbR3nUsMNYaIYrOT1iMheenjCvzZblJcjv/3JIuxXN7RCNT5i6lRkLodW5ojCGhR7n5yvnNKrw=="], | ||||
|     "@prisma/engines-version": ["@prisma/engines-version@6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed", "", {}, "sha512-EvpOFEWf1KkJpDsBCrih0kg3HdHuaCnXmMn7XFPObpFTzagK1N0Q0FMnYPsEhvARfANP5Ok11QyoTIRA2hgJTA=="], | ||||
| 
 | ||||
|     "@prisma/fetch-engine": ["@prisma/fetch-engine@6.6.0", "", { "dependencies": { "@prisma/debug": "6.6.0", "@prisma/engines-version": "6.6.0-53.f676762280b54cd07c770017ed3711ddde35f37a", "@prisma/get-platform": "6.6.0" } }, "sha512-Ohfo8gKp05LFLZaBlPUApM0M7k43a0jmo86YY35u1/4t+vuQH9mRGU7jGwVzGFY3v+9edeb/cowb1oG4buM1yw=="], | ||||
|     "@prisma/fetch-engine": ["@prisma/fetch-engine@6.7.0", "", { "dependencies": { "@prisma/debug": "6.7.0", "@prisma/engines-version": "6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed", "@prisma/get-platform": "6.7.0" } }, "sha512-zLlAGnrkmioPKJR4Yf7NfW3hftcvqeNNEHleMZK9yX7RZSkhmxacAYyfGsCcqRt47jiZ7RKdgE0Wh2fWnm7WsQ=="], | ||||
| 
 | ||||
|     "@prisma/get-platform": ["@prisma/get-platform@6.6.0", "", { "dependencies": { "@prisma/debug": "6.6.0" } }, "sha512-3qCwmnT4Jh5WCGUrkWcc6VZaw0JY7eWN175/pcb5Z6FiLZZ3ygY93UX0WuV41bG51a6JN/oBH0uywJ90Y+V5eA=="], | ||||
|     "@prisma/get-platform": ["@prisma/get-platform@6.7.0", "", { "dependencies": { "@prisma/debug": "6.7.0" } }, "sha512-i9IH5lO4fQwnMLvQLYNdgVh9TK3PuWBfQd7QLk/YurnAIg+VeADcZDbmhAi4XBBDD+hDif9hrKyASu0hbjwabw=="], | ||||
| 
 | ||||
|     "@puppeteer/browsers": ["@puppeteer/browsers@2.10.2", "", { "dependencies": { "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.1", "tar-fs": "^3.0.8", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-i4Ez+s9oRWQbNjtI/3+jxr7OH508mjAKvza0ekPJem0ZtmsYHP3B5dq62+IaBHKaGCOuqJxXzvFLUhJvQ6jtsQ=="], | ||||
| 
 | ||||
| @ -230,7 +230,7 @@ | ||||
| 
 | ||||
|     "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], | ||||
| 
 | ||||
|     "prisma": ["prisma@6.6.0", "", { "dependencies": { "@prisma/config": "6.6.0", "@prisma/engines": "6.6.0" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-SYCUykz+1cnl6Ugd8VUvtTQq5+j1Q7C0CtzKPjQ8JyA2ALh0EEJkMCS+KgdnvKW1lrxjtjCyJSHOOT236mENYg=="], | ||||
|     "prisma": ["prisma@6.7.0", "", { "dependencies": { "@prisma/config": "6.7.0", "@prisma/engines": "6.7.0" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-vArg+4UqnQ13CVhc2WUosemwh6hr6cr6FY2uzDvCIFwH8pu8BXVv38PktoMLVjtX7sbYThxbnZF5YiR8sN2clw=="], | ||||
| 
 | ||||
|     "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], | ||||
| 
 | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| [{"name":"mcs","type":1,"nsfw":false,"description":"Qeury a Minecraft server","contexts":[0,1,2],"integration_types":[0],"options":[{"name":"ip","description":"Server IP","type":3,"name_localizations":{},"description_localizations":{},"autocomplete":false},{"name":"port","description":"Server Port","type":10,"name_localizations":{},"description_localizations":{},"autocomplete":false}]},{"name":"ping","type":1,"nsfw":false,"description":"Show latency with Discord","contexts":[0,1,2],"integration_types":[0],"options":[]}] | ||||
| [{"name":"mcs","type":1,"nsfw":false,"description":"Qeury a Minecraft server","contexts":[0,1,2],"integration_types":[0],"options":[{"name":"ip","description":"Server IP","type":3,"name_localizations":{},"description_localizations":{},"autocomplete":false},{"name":"port","description":"Server Port","type":10,"name_localizations":{},"description_localizations":{},"autocomplete":false}]},{"name":"brawlbind","type":1,"nsfw":false,"description":"Associate your Discord account with your Brawl Stars account","contexts":[0,1,2],"integration_types":[0],"options":[{"name":"playertag","description":"Your Brawl Stars player tag","required":true,"type":3,"name_localizations":{},"description_localizations":{},"autocomplete":false}]},{"name":"ping","type":1,"nsfw":false,"description":"Show latency with Discord","contexts":[0,1,2],"integration_types":[0],"options":[]},{"name":"brawlstats","type":1,"nsfw":false,"description":"Query your Brawl Stars player stats","contexts":[0,1,2],"integration_types":[0],"options":[]}] | ||||
| @ -5,10 +5,10 @@ | ||||
|   "private": true, | ||||
|   "devDependencies": { | ||||
|     "@types/bun": "latest", | ||||
|     "prisma": "^6.6.0" | ||||
|     "prisma": "^6.7.0" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "dev": "bun --env-file=.env --hot src/index.ts" | ||||
|     "dev": "bun --env-file=.env --watch src/index.ts" | ||||
|   }, | ||||
|   "peerDependencies": { | ||||
|     "typescript": "^5" | ||||
|  | ||||
| @ -22,3 +22,12 @@ model mcsBind { | ||||
|   @@map("mcs_binds") | ||||
|   @@index([channelId]) | ||||
| } | ||||
| 
 | ||||
| model brawlPlayer { | ||||
|   id        String   @id @default(auto()) @map("_id") @db.ObjectId | ||||
|   userId    String | ||||
|   playerTag String | ||||
| 
 | ||||
|   @@map("brawl_players") | ||||
|   @@index([userId]) | ||||
| } | ||||
							
								
								
									
										86
									
								
								src/commands/brawlBind.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/commands/brawlBind.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | ||||
| import { | ||||
|   Command, | ||||
|   Declare, | ||||
|   Options, | ||||
|   createStringOption, | ||||
|   type CommandContext | ||||
| } from 'seyfert'; | ||||
| 
 | ||||
| import { prisma } from '..'; | ||||
| 
 | ||||
| const options = { | ||||
|   playertag: createStringOption({ | ||||
|     description: 'Your Brawl Stars player tag', | ||||
|     required: true, | ||||
|   }), | ||||
| } | ||||
| 
 | ||||
| @Declare({ | ||||
|   name: 'brawlbind', | ||||
|   description: 'Associate your Discord account with your Brawl Stars account', | ||||
| }) | ||||
| @Options(options) | ||||
| export default class brawlBindCommand extends Command { | ||||
|   async run(ctx: CommandContext<typeof options>) { | ||||
|     let tag = ctx.options.playertag | ||||
|     if (!tag.startsWith("#")) tag = "#" + tag | ||||
|     const playerTagRegex = /^#[0-9A-Z]{6,10}$/ | ||||
|     if (!playerTagRegex.test(ctx.options.playertag)) { | ||||
|       await ctx.write({ | ||||
|         content: 'Invalid player tag (Example: `#A1B2C3D4E`)' | ||||
|       }) | ||||
|       return | ||||
|     } | ||||
|     const userId: string = ctx.author.id | ||||
|     console.log(tag + `\n` + userId) | ||||
|     const res = await prisma.brawlPlayer.findFirst({ | ||||
|       where: { | ||||
|         userId: userId | ||||
|       } | ||||
|     }) | ||||
|     if (res && res.playerTag !== tag) { | ||||
|       try { | ||||
|         await prisma.brawlPlayer.update({ | ||||
|           where: { | ||||
|             id: res.id, | ||||
|             userId: userId | ||||
|           }, | ||||
|           data: { | ||||
|             playerTag: tag | ||||
|           } | ||||
|         }) | ||||
|       } catch (e) { | ||||
|         await ctx.write({ | ||||
|           content: `${e}` | ||||
|         }) | ||||
|         return | ||||
|       } | ||||
|       await ctx.write({ | ||||
|         content: `Updated your playertag from ${res.playerTag} to \`${tag}\`` | ||||
|       }) | ||||
|       return | ||||
|     } else if (res && res.playerTag === tag) { | ||||
|       await ctx.write({ | ||||
|         content: `Nothing Changed, playertag already associated to \`${tag}\`` | ||||
|       }) | ||||
|     } | ||||
|     if (!res) { | ||||
|       try { | ||||
|         await prisma.brawlPlayer.create({ | ||||
|           data: { | ||||
|             userId: userId, | ||||
|             playerTag: tag | ||||
|           } | ||||
|         }) | ||||
|       } catch (e) { | ||||
|         await ctx.write({ | ||||
|           content: `${e}` | ||||
|         }) | ||||
|       } | ||||
|       await ctx.write({ | ||||
|         content: `Associated to your playertag \`${tag}\`` | ||||
|       }) | ||||
|       return | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										143
									
								
								src/commands/brawlStats.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/commands/brawlStats.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| import { | ||||
|   AttachmentBuilder, | ||||
|   Command, | ||||
|   Declare, | ||||
|   Embed, | ||||
|   Options, | ||||
|   createStringOption, | ||||
|   type CommandContext | ||||
| } from 'seyfert'; | ||||
| 
 | ||||
| import { prisma, browser } from '..'; | ||||
| 
 | ||||
| interface BrawlPlayer { | ||||
|   name: string; | ||||
|   tag: string; | ||||
|   namecolor: string; | ||||
|   nameColor: string; | ||||
|   icon: any; | ||||
|   trophies: number; | ||||
|   highestTrophies: number; | ||||
|   expLevel: number; | ||||
|   expPoints: number; | ||||
|   isQualifiedFromChampionshipChallenge: boolean; | ||||
|   "3vs3Victories": number; | ||||
|   soloVictories: number; | ||||
|   duoVictories: number; | ||||
|   bestRoboRumbleTime: number; | ||||
|   bestTimeAsBigBrawler: number; | ||||
|   club: any; | ||||
|   brawlers: any[]; | ||||
| } | ||||
| 
 | ||||
| @Declare({ | ||||
|   name: 'brawlstats', | ||||
|   description: 'Query your Brawl Stars player stats' | ||||
| }) | ||||
| 
 | ||||
| export default class brawlStatsCommand extends Command { | ||||
|   async run(ctx: CommandContext) { | ||||
|     const res = await prisma.brawlPlayer.findFirst({ | ||||
|       where: { | ||||
|         userId: ctx.author.id | ||||
|       } | ||||
|     }) | ||||
|     if (!res) { | ||||
|       await ctx.write({ | ||||
|         content: `Your Discord account has not associated to a Brawl Stars playertag, use \`brawlbind\` to associate` | ||||
|       }) | ||||
|       return | ||||
|     } | ||||
|     let tag = res.playerTag.replace('#', '%23') | ||||
|     const data = await fetchBrawlPlayer(tag) as BrawlPlayer | ||||
|     if (!data || data === null) { | ||||
|       await ctx.write({ | ||||
|         content: `ERROR, CONTACT ADMIN` | ||||
|       }) | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     const page = await browser.newPage(); | ||||
|     const result = generateBrawlStatsHtml(data) | ||||
|      | ||||
|     await page.setViewport({ width: 650, height: 200 }) | ||||
|     await page.setContent(result) | ||||
|     await page.waitForSelector('body') | ||||
|     const screenshot = await page.screenshot({ | ||||
|       encoding: 'base64', | ||||
|       type: 'webp', | ||||
|       fullPage: true | ||||
|     }) | ||||
|     await page.close() | ||||
|      | ||||
|     const buffer = Buffer.from(screenshot, 'base64') | ||||
|     await ctx.write({ | ||||
|       files: [ | ||||
|         new AttachmentBuilder() | ||||
|           .setName('brawlstats.png') | ||||
|           .setFile('buffer', buffer) | ||||
|       ] | ||||
|     }); | ||||
|     return | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function generateBrawlStatsHtml(data: BrawlPlayer): string { | ||||
|   const stats = [ | ||||
|     `Player: ${data.name}`, | ||||
|     `Trophies: ${data.trophies}`, | ||||
|     `3vs3 Victories: ${data['3vs3Victories']}`, | ||||
|     `Solo Victories: ${data.soloVictories}`, | ||||
|     `Duo Victories: ${data.duoVictories}`, | ||||
|     `Total Brawlers: ${data.brawlers.length}` | ||||
|   ].join('<br>') | ||||
| 
 | ||||
|   return ` | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta name="viewport" content="width=auto, initial-scale=1.0"> | ||||
|   <script src="https://cdn.tailwindcss.com"></script> | ||||
|   <style> | ||||
|     body { | ||||
|       background-color: #1e1e2e; | ||||
|       color: #cdd6f4; | ||||
|     } | ||||
|   </style> | ||||
| </head> | ||||
| <body style="width: 650px"> | ||||
|   <div class="container mx-auto py-4"> | ||||
|     <div class="px-6 flex items-center"> | ||||
|       <img src="https://cdn-assets-eu.frontify.com/s3/frontify-enterprise-files-eu/eyJwYXRoIjoic3VwZXJjZWxsXC9maWxlXC9WeGZCYTJZM2VidFliNHhRNDJhdS5wbmcifQ:supercell:4JRrhrJjKTux8065H80-L2EiHN2bJg9E9QuhQD9ztIs?width=2400" class="h-32 w-32 object-contain mr-6" alt="Brawl Stars Logo" /> | ||||
|       <div class="text-lg font-bold text-[#cdd6f4]">${stats}</div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <footer class="bg-[#313244] text-center py-2"> | ||||
|     <p class="text-sm text-[#cdd6f4]">Generated by Fantastic Spoon - Discord</p> | ||||
|     <p class="text-sm text-[#cdd6f4]">Open Sourced on git.itzdrli.cc © 2025 itzdrli - All Rights Reserved. </p> | ||||
|   </footer> | ||||
| </body> | ||||
| </html>`;
 | ||||
| } | ||||
| 
 | ||||
| async function fetchBrawlPlayer(playerTag: string) { | ||||
|   const brawlUrl = `https://api.brawlstars.com/v1/players/${playerTag}` | ||||
|   try { | ||||
|     const res = await fetch(brawlUrl, { | ||||
|       method: 'GET', | ||||
|       headers: { | ||||
|         'Authorization': `Bearer ${process.env.BRAWL_KEY}`, | ||||
|         'Accept': 'application/json' | ||||
|       } | ||||
|     }) | ||||
|     if (!res.ok) { | ||||
|       console.error(`Error fetching Brawl Stars player data: ${res.statusText}`) | ||||
|       return null | ||||
|     } | ||||
|     return res.json() | ||||
|   } catch (e) { | ||||
|     console.error(e) | ||||
|     return null | ||||
|   } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user