Official Discord Activity for Player Map.
Runs inside Discord voice channels, text channels, and DMs. Players can view profiles, review teammates, and interact with the Intuition reputation graph — directly in Discord.
- Node.js >= 18
- pnpm
- A Discord application with Activities enabled (Developer Portal)
- A Privy account (dashboard.privy.io)
- Clone this repo
pnpm install- Copy
.env.exampleto.envand fill in your keys - Configure Discord Developer Portal:
- Activities → Enable Activities
- URL Mappings:
/→ your cloudflared tunnel URL (see Development below)
- Configure Privy Dashboard:
- Add
https://<DISCORD_APP_ID>.discordsays.comto Allowed Origins - Add
http://localhost:5173to Allowed Origins - Login methods: wallet, email, google, twitter
- Add
# Terminal 1 — OAuth backend (port 3001)
DISCORD_CLIENT_ID=xxx DISCORD_CLIENT_SECRET=xxx pnpm dev:api
# Terminal 2 — Vite dev server
pnpm devApp available at http://localhost:5173
Discord cannot reach localhost directly — requires a public tunnel + unified server.
# Terminal 1 — Build + unified server (static + API on port 5173)
pnpm build && EXTERNAL_URL=https://xxx.trycloudflare.com DISCORD_CLIENT_ID=xxx DISCORD_CLIENT_SECRET=xxx pnpm serve:discord
# Terminal 2 — Cloudflare tunnel
cloudflared tunnel --url http://localhost:5173Copy the generated URL (e.g. funky-name.trycloudflare.com) into:
Discord Developer Portal → Your App → Activities → URL Mappings → /
- Open Discord → Voice channel
- Click the Activities icon (rocket)
- Select your app
- If not visible: enable Developer Activity Shelf in Discord Settings → Advanced
playermap-discord/
├── src/
│ ├── providers/
│ │ ├── DiscordProvider.tsx # Discord SDK init + OAuth flow
│ │ ├── WalletProvider.tsx # Privy + wagmi + React Query
│ │ └── AppProviders.tsx # Composes Discord + Wallet
│ ├── components/
│ │ ├── PlayerMapView.tsx # Main player map UI
│ │ ├── WalletLoginPage.tsx # External wallet connection page
│ │ ├── TxSignPage.tsx # External transaction signing page
│ │ └── DiscordBadge.tsx # Discord user badge
│ ├── utils/ # discordEnv, bigintJson
│ ├── types/ # player-map type declarations
│ └── constants/ # player-map config constants
├── api/
│ ├── unified-server.ts # Prod: static files + OAuth + proxies
│ └── server.ts # Dev: OAuth server only (port 3001)
└── public/
└── aframe.min.js # Served locally (Discord CSP blocks CDN)
Discord Activity runs in an iframe — some wallets can't sign from inside it. When a transaction is needed:
- App generates a session token and opens an external URL (
?session=<token>) WalletLoginPageconnects the wallet and stores the address via/sessionTxSignPagefetches tx params via/pending-tx/:tokenand submits the result- The iframe polls for the result and continues
- React 18 + TypeScript + Vite
- @discord/embedded-app-sdk
- @privy-io/react-auth + wagmi + viem
- Intuition Mainnet (chain id: 1155)
- Express (unified server)
- player-map (npm)