first commit
This commit is contained in:
655
STEAM_BOT_SETUP.md
Normal file
655
STEAM_BOT_SETUP.md
Normal file
@@ -0,0 +1,655 @@
|
||||
# Steam Bot Setup Guide
|
||||
|
||||
## 🤖 Overview
|
||||
|
||||
The Steam bot system handles trade offers for buying items from users. When a user sells items, instead of immediately crediting funds, the system:
|
||||
|
||||
1. ✅ Creates a trade offer via Steam bot
|
||||
2. ⏳ Tracks trade state (pending, accepted, declined, etc.)
|
||||
3. 💰 Credits user balance ONLY after trade is accepted
|
||||
4. 🔄 Handles failures, expirations, and retries
|
||||
|
||||
---
|
||||
|
||||
## 📋 Prerequisites
|
||||
|
||||
### 1. Steam Bot Account
|
||||
|
||||
You need a separate Steam account for the bot:
|
||||
|
||||
- ✅ Account with Steam Mobile Authenticator enabled
|
||||
- ✅ Public inventory
|
||||
- ✅ Valid trade URL
|
||||
- ✅ API key from https://steamcommunity.com/dev/apikey
|
||||
- ✅ Not limited (must have spent $5+ on Steam)
|
||||
- ✅ Trade cooldown period expired (if newly set up)
|
||||
|
||||
### 2. Required Packages
|
||||
|
||||
Already installed in package.json:
|
||||
```json
|
||||
{
|
||||
"steam-user": "^5.0.0",
|
||||
"steamcommunity": "^3.47.0",
|
||||
"steam-tradeoffer-manager": "^2.10.6",
|
||||
"steam-totp": "^2.1.1"
|
||||
}
|
||||
```
|
||||
|
||||
Install if needed:
|
||||
```bash
|
||||
npm install steam-user steamcommunity steam-tradeoffer-manager steam-totp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Getting Steam Bot Credentials
|
||||
|
||||
### Step 1: Get Shared Secret & Identity Secret
|
||||
|
||||
1. **Enable Steam Mobile Authenticator** on bot account
|
||||
2. Use one of these tools to extract secrets:
|
||||
- **SDA (Steam Desktop Authenticator)**: https://github.com/Jessecar96/SteamDesktopAuthenticator
|
||||
- **Android**: Use app like "Steam Guard Mobile Authenticator"
|
||||
- **Manual extraction**: Follow guides online
|
||||
|
||||
3. You'll need:
|
||||
- `shared_secret` - For generating 2FA codes
|
||||
- `identity_secret` - For confirming trades
|
||||
|
||||
### Step 2: Get Steam API Key
|
||||
|
||||
1. Go to: https://steamcommunity.com/dev/apikey
|
||||
2. Register domain: `turbotrades.com` (or your domain)
|
||||
3. Copy the API key
|
||||
|
||||
### Step 3: Get Bot Trade URL
|
||||
|
||||
1. Login to bot account
|
||||
2. Go to: https://steamcommunity.com/id/me/tradeoffers/privacy
|
||||
3. Copy the trade URL (looks like: `https://steamcommunity.com/tradeoffer/new/?partner=XXXXX&token=XXXXXXXX`)
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Add to your `.env` file:
|
||||
|
||||
```env
|
||||
# Steam Bot Credentials
|
||||
STEAM_BOT_USERNAME=your_bot_username
|
||||
STEAM_BOT_PASSWORD=your_bot_password
|
||||
STEAM_BOT_SHARED_SECRET=your_shared_secret_here
|
||||
STEAM_BOT_IDENTITY_SECRET=your_identity_secret_here
|
||||
|
||||
# Steam API
|
||||
STEAM_API_KEY=your_steam_api_key
|
||||
|
||||
# Bot Trade URL
|
||||
STEAM_BOT_TRADE_URL=https://steamcommunity.com/tradeoffer/new/?partner=XXXXX&token=XXXXXXXX
|
||||
|
||||
# Optional: Bot Settings
|
||||
STEAM_BOT_POLL_INTERVAL=30000
|
||||
STEAM_BOT_AUTO_START=true
|
||||
```
|
||||
|
||||
### Security Notes
|
||||
|
||||
- ⚠️ **NEVER commit `.env` to git**
|
||||
- ⚠️ Keep secrets secure
|
||||
- ⚠️ Use different account from your personal Steam
|
||||
- ⚠️ Enable Steam Guard on bot account
|
||||
- ⚠️ Use strong password
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Starting the Bot
|
||||
|
||||
### Automatic Start (Recommended)
|
||||
|
||||
Bot starts automatically when backend launches if `STEAM_BOT_AUTO_START=true`
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
You'll see:
|
||||
```
|
||||
🔐 Logging into Steam...
|
||||
✅ Steam bot logged in successfully
|
||||
✅ Steam web session established
|
||||
🤖 Steam bot ready for trades
|
||||
```
|
||||
|
||||
### Manual Start
|
||||
|
||||
```javascript
|
||||
import { getSteamBot } from './services/steamBot.js';
|
||||
|
||||
const bot = getSteamBot();
|
||||
await bot.login();
|
||||
|
||||
console.log('Bot ready:', bot.isOnline());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 How It Works
|
||||
|
||||
### User Sells Items Flow
|
||||
|
||||
```
|
||||
1. User selects items on Sell page
|
||||
2. User clicks "Sell Selected Items"
|
||||
↓
|
||||
3. Backend creates Trade record (state: pending)
|
||||
- Does NOT credit balance yet
|
||||
↓
|
||||
4. Steam bot creates trade offer
|
||||
- Requests items from user
|
||||
- User sees trade offer in Steam
|
||||
↓
|
||||
5. User accepts trade in Steam app
|
||||
↓
|
||||
6. Bot receives "trade accepted" event
|
||||
↓
|
||||
7. Backend:
|
||||
- Updates Trade state to "accepted"
|
||||
- Creates Transaction record
|
||||
- Credits user balance
|
||||
- Updates WebSocket
|
||||
↓
|
||||
8. User sees balance update in UI
|
||||
```
|
||||
|
||||
### Trade States
|
||||
|
||||
| State | Description | User Balance | Can Retry |
|
||||
|-------|-------------|--------------|-----------|
|
||||
| **pending** | Trade offer sent, waiting for user | ❌ Not credited | ✅ Yes |
|
||||
| **accepted** | User accepted, items received | ✅ Credited | ❌ No |
|
||||
| **declined** | User declined the offer | ❌ Not credited | ⚠️ Maybe |
|
||||
| **expired** | Trade offer expired (10min timeout) | ❌ Not credited | ✅ Yes |
|
||||
| **canceled** | Trade was canceled | ❌ Not credited | ✅ Yes |
|
||||
| **failed** | Technical error occurred | ❌ Not credited | ✅ Yes |
|
||||
| **escrow** | Trade in Steam escrow | ❌ Not credited | ⏳ Wait |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Database Schema
|
||||
|
||||
### Trade Model
|
||||
|
||||
```javascript
|
||||
{
|
||||
offerId: String, // Steam trade offer ID
|
||||
userId: ObjectId, // User who's selling
|
||||
steamId: String, // User's Steam ID
|
||||
state: String, // pending, accepted, declined, etc.
|
||||
items: [{
|
||||
assetId: String,
|
||||
name: String,
|
||||
price: Number,
|
||||
image: String,
|
||||
// ... item details
|
||||
}],
|
||||
totalValue: Number, // Total price of all items
|
||||
fee: Number, // Platform fee (e.g., 5%)
|
||||
feePercentage: Number, // Fee percentage
|
||||
userReceives: Number, // Amount user gets (after fees)
|
||||
tradeUrl: String, // User's trade URL
|
||||
tradeOfferUrl: String, // Link to view offer on Steam
|
||||
sentAt: Date, // When trade was sent
|
||||
acceptedAt: Date, // When user accepted
|
||||
completedAt: Date, // When balance was credited
|
||||
expiresAt: Date, // When trade expires (10min)
|
||||
transactionId: ObjectId, // Transaction created after acceptance
|
||||
sessionId: ObjectId, // User's session
|
||||
errorMessage: String, // If failed
|
||||
retryCount: Number, // How many times retried
|
||||
metadata: Mixed // Additional data
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎮 Bot Events
|
||||
|
||||
The bot emits events you can listen to:
|
||||
|
||||
```javascript
|
||||
const bot = getSteamBot();
|
||||
|
||||
// Bot is ready
|
||||
bot.on('ready', () => {
|
||||
console.log('Bot is ready!');
|
||||
});
|
||||
|
||||
// Trade accepted
|
||||
bot.on('tradeAccepted', async (offer, tradeData) => {
|
||||
console.log('Trade accepted:', offer.id);
|
||||
// Credit user balance here
|
||||
});
|
||||
|
||||
// Trade declined
|
||||
bot.on('tradeDeclined', async (offer, tradeData) => {
|
||||
console.log('Trade declined:', offer.id);
|
||||
// Notify user
|
||||
});
|
||||
|
||||
// Trade expired
|
||||
bot.on('tradeExpired', async (offer, tradeData) => {
|
||||
console.log('Trade expired:', offer.id);
|
||||
// Can retry or cancel
|
||||
});
|
||||
|
||||
// Bot error
|
||||
bot.on('error', (err) => {
|
||||
console.error('Bot error:', err);
|
||||
});
|
||||
|
||||
// Bot disconnected
|
||||
bot.on('disconnected', () => {
|
||||
console.warn('Bot disconnected, will reconnect...');
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ API Endpoints
|
||||
|
||||
### Create Trade Offer
|
||||
|
||||
```http
|
||||
POST /api/trade/create
|
||||
Authorization: Bearer <token>
|
||||
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"assetId": "123456789",
|
||||
"name": "AK-47 | Redline (Field-Tested)",
|
||||
"price": 12.50,
|
||||
"image": "https://...",
|
||||
// ... other item data
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"trade": {
|
||||
"id": "trade_id",
|
||||
"offerId": "steam_offer_id",
|
||||
"state": "pending",
|
||||
"totalValue": 12.50,
|
||||
"userReceives": 11.88,
|
||||
"items": [...],
|
||||
"tradeOfferUrl": "https://steamcommunity.com/tradeoffer/...",
|
||||
"expiresAt": "2024-01-10T12:10:00Z"
|
||||
},
|
||||
"message": "Trade offer sent! Check your Steam app to accept."
|
||||
}
|
||||
```
|
||||
|
||||
### Get Trade Status
|
||||
|
||||
```http
|
||||
GET /api/trade/:tradeId
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"trade": {
|
||||
"id": "trade_id",
|
||||
"offerId": "steam_offer_id",
|
||||
"state": "accepted",
|
||||
"totalValue": 12.50,
|
||||
"userReceives": 11.88,
|
||||
"acceptedAt": "2024-01-10T12:05:00Z",
|
||||
"timeElapsed": "5m ago"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Cancel Trade
|
||||
|
||||
```http
|
||||
POST /api/trade/:tradeId/cancel
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
### Retry Trade
|
||||
|
||||
```http
|
||||
POST /api/trade/:tradeId/retry
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Bot Management
|
||||
|
||||
### Check Bot Status
|
||||
|
||||
```javascript
|
||||
const bot = getSteamBot();
|
||||
|
||||
const health = bot.getHealth();
|
||||
console.log(health);
|
||||
// {
|
||||
// isReady: true,
|
||||
// isLoggedIn: true,
|
||||
// activeTradesCount: 5,
|
||||
// username: "bot_username"
|
||||
// }
|
||||
```
|
||||
|
||||
### Get Active Trades
|
||||
|
||||
```javascript
|
||||
const activeTrades = bot.getActiveTrades();
|
||||
console.log(`${activeTrades.length} trades active`);
|
||||
```
|
||||
|
||||
### Cancel All Pending Trades
|
||||
|
||||
```javascript
|
||||
const pendingTrades = await Trade.getPendingTrades();
|
||||
|
||||
for (const trade of pendingTrades) {
|
||||
if (trade.isExpired) {
|
||||
await bot.cancelTradeOffer(trade.offerId);
|
||||
await trade.markAsExpired();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### Test Bot Login
|
||||
|
||||
```bash
|
||||
node -e "import('./services/steamBot.js').then(async m => {
|
||||
const bot = m.getSteamBot();
|
||||
await bot.login();
|
||||
console.log('Bot logged in:', bot.isOnline());
|
||||
bot.logout();
|
||||
process.exit(0);
|
||||
})"
|
||||
```
|
||||
|
||||
### Test Trade Creation
|
||||
|
||||
```javascript
|
||||
const bot = getSteamBot();
|
||||
await bot.login();
|
||||
|
||||
const result = await bot.createTradeOffer({
|
||||
tradeUrl: "https://steamcommunity.com/tradeoffer/new/?partner=XXX&token=XXX",
|
||||
itemsToReceive: [
|
||||
{ assetid: "123456789", appid: 730, contextid: 2 }
|
||||
],
|
||||
message: "Test trade offer"
|
||||
});
|
||||
|
||||
console.log('Trade created:', result);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Monitoring
|
||||
|
||||
### Database Queries
|
||||
|
||||
```javascript
|
||||
// Get all pending trades
|
||||
const pending = await Trade.find({ state: 'pending' });
|
||||
|
||||
// Get expired trades
|
||||
const expired = await Trade.getExpiredTrades();
|
||||
|
||||
// Get trade stats
|
||||
const stats = await Trade.getStats();
|
||||
console.log(`
|
||||
Total trades: ${stats.total}
|
||||
Accepted: ${stats.accepted}
|
||||
Pending: ${stats.pending}
|
||||
Declined: ${stats.declined}
|
||||
`);
|
||||
|
||||
// Get user's trades
|
||||
const userTrades = await Trade.getUserTrades(userId);
|
||||
```
|
||||
|
||||
### Admin Panel Integration
|
||||
|
||||
Add to admin panel:
|
||||
- ✅ View pending trades
|
||||
- ✅ Cancel stale trades
|
||||
- ✅ Retry failed trades
|
||||
- ✅ View trade statistics
|
||||
- ✅ Bot health status
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Bot Won't Login
|
||||
|
||||
**Check:**
|
||||
- ✅ Username and password correct
|
||||
- ✅ Shared secret is valid
|
||||
- ✅ Steam account not limited
|
||||
- ✅ No active VAC ban
|
||||
- ✅ No trade cooldown
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Test credentials
|
||||
node -e "console.log('Username:', process.env.STEAM_BOT_USERNAME)"
|
||||
node -e "console.log('Has password:', !!process.env.STEAM_BOT_PASSWORD)"
|
||||
node -e "console.log('Has shared secret:', !!process.env.STEAM_BOT_SHARED_SECRET)"
|
||||
```
|
||||
|
||||
### Trades Not Confirming
|
||||
|
||||
**Check:**
|
||||
- ✅ Identity secret is correct
|
||||
- ✅ Mobile authenticator is active
|
||||
- ✅ Bot has trade URL
|
||||
|
||||
**Solution:**
|
||||
- Manually confirm in Steam app first time
|
||||
- Check if identity secret is correct
|
||||
- Regenerate secrets if needed
|
||||
|
||||
### Trades Stuck in Pending
|
||||
|
||||
**Reasons:**
|
||||
- User hasn't accepted yet
|
||||
- Trade expired (10 minutes)
|
||||
- Steam API issues
|
||||
- User's inventory is private
|
||||
|
||||
**Solution:**
|
||||
```javascript
|
||||
// Cancel expired trades
|
||||
const expired = await Trade.find({
|
||||
state: 'pending',
|
||||
expiresAt: { $lt: new Date() }
|
||||
});
|
||||
|
||||
for (const trade of expired) {
|
||||
await bot.cancelTradeOffer(trade.offerId);
|
||||
await trade.markAsExpired();
|
||||
}
|
||||
```
|
||||
|
||||
### Bot Keeps Disconnecting
|
||||
|
||||
**Check:**
|
||||
- ✅ Network connection stable
|
||||
- ✅ Steam not under maintenance
|
||||
- ✅ No rate limiting
|
||||
|
||||
**Solution:**
|
||||
- Implement auto-reconnect (already built-in)
|
||||
- Use connection pooling
|
||||
- Monitor logs for error patterns
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Security Best Practices
|
||||
|
||||
### 1. Secure Credentials
|
||||
- ✅ Never hardcode credentials
|
||||
- ✅ Use environment variables
|
||||
- ✅ Keep .env out of git
|
||||
- ✅ Rotate secrets regularly
|
||||
|
||||
### 2. Trade Validation
|
||||
- ✅ Verify user owns items before creating trade
|
||||
- ✅ Check item values match database
|
||||
- ✅ Validate trade URLs
|
||||
- ✅ Prevent duplicate trades
|
||||
|
||||
### 3. Rate Limiting
|
||||
- ✅ Limit trades per user per hour
|
||||
- ✅ Limit total trades per minute
|
||||
- ✅ Queue trades during high load
|
||||
|
||||
### 4. Monitoring
|
||||
- ✅ Log all trade events
|
||||
- ✅ Alert on failed trades
|
||||
- ✅ Track bot uptime
|
||||
- ✅ Monitor Steam API status
|
||||
|
||||
---
|
||||
|
||||
## 📊 Performance Optimization
|
||||
|
||||
### Trade Queue System
|
||||
|
||||
For high volume, implement trade queue:
|
||||
|
||||
```javascript
|
||||
// Redis-based queue
|
||||
import Bull from 'bull';
|
||||
|
||||
const tradeQueue = new Bull('trades', {
|
||||
redis: { host: 'localhost', port: 6379 }
|
||||
});
|
||||
|
||||
tradeQueue.process(async (job) => {
|
||||
const { userId, items } = job.data;
|
||||
return await createTradeOffer(userId, items);
|
||||
});
|
||||
|
||||
// Add to queue
|
||||
await tradeQueue.add({ userId, items }, {
|
||||
attempts: 3,
|
||||
backoff: {
|
||||
type: 'exponential',
|
||||
delay: 2000
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Retry Strategy
|
||||
|
||||
```javascript
|
||||
async function createTradeWithRetry(data, maxRetries = 3) {
|
||||
let lastError;
|
||||
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
return await bot.createTradeOffer(data);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
await sleep(Math.pow(2, i) * 1000); // Exponential backoff
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
### Phase 1: Basic Setup (You are here)
|
||||
- [x] Install packages
|
||||
- [x] Create bot service
|
||||
- [x] Create Trade model
|
||||
- [ ] Configure environment variables
|
||||
- [ ] Test bot login
|
||||
|
||||
### Phase 2: Integration
|
||||
- [ ] Update sell endpoint to create trades
|
||||
- [ ] Add trade event handlers
|
||||
- [ ] Credit balance on acceptance
|
||||
- [ ] Add trade status polling
|
||||
|
||||
### Phase 3: Frontend
|
||||
- [ ] Show trade status in UI
|
||||
- [ ] Add "View Trade" button
|
||||
- [ ] Show pending trades list
|
||||
- [ ] Add trade notifications
|
||||
|
||||
### Phase 4: Admin Tools
|
||||
- [ ] Bot health dashboard
|
||||
- [ ] Trade management panel
|
||||
- [ ] Retry/cancel controls
|
||||
- [ ] Statistics and reports
|
||||
|
||||
### Phase 5: Advanced
|
||||
- [ ] Trade queue system
|
||||
- [ ] Multiple bot support
|
||||
- [ ] Automatic retries
|
||||
- [ ] Fraud detection
|
||||
|
||||
---
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
- **Steam Web API**: https://developer.valvesoftware.com/wiki/Steam_Web_API
|
||||
- **Trade Offer Manager**: https://github.com/DoctorMcKay/node-steam-tradeoffer-manager
|
||||
- **Steam User**: https://github.com/DoctorMcKay/node-steam-user
|
||||
- **Steam TOTP**: https://github.com/DoctorMcKay/node-steam-totp
|
||||
|
||||
---
|
||||
|
||||
## ✅ Configuration Checklist
|
||||
|
||||
Before going live:
|
||||
|
||||
- [ ] Bot account created and funded ($5+ spent)
|
||||
- [ ] Steam Mobile Authenticator enabled
|
||||
- [ ] Shared secret and identity secret extracted
|
||||
- [ ] API key obtained from Steam
|
||||
- [ ] Environment variables configured
|
||||
- [ ] Bot successfully logs in
|
||||
- [ ] Test trade offer sent and accepted
|
||||
- [ ] Trade events properly handled
|
||||
- [ ] Balance credits on acceptance
|
||||
- [ ] Error handling tested
|
||||
- [ ] Monitoring set up
|
||||
- [ ] Backup credentials stored securely
|
||||
|
||||
---
|
||||
|
||||
**Status**: Implementation Ready
|
||||
**Next**: Configure bot credentials and test login
|
||||
**Priority**: High - Required for production sell functionality
|
||||
Reference in New Issue
Block a user