@chiraitori/hoyolab-core
Version:
Core utilities for HoYoLab automation - daily check-ins and code redemption with smart rate limiting
457 lines (352 loc) • 11.6 kB
Markdown
# Advanced Usage Guide
## Table of Contents
- [Installation](#installation)
- [Cookie Setup](#cookie-setup)
- [Basic Usage](#basic-usage)
- [Advanced Features](#advanced-features)
- [Error Handling](#error-handling)
- [Utilities](#utilities)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)
## Installation
```bash
npm install @chiraitori/hoyolab-core
```
## Cookie Setup
### Obtaining Your Cookie
1. **Visit HoYoLab**: Go to [hoyolab.com](https://www.hoyolab.com/) and log in
2. **Open DevTools**: Press F12 or right-click → Inspect
3. **Navigate to Storage**:
- Chrome/Edge: Application → Cookies
- Firefox: Storage → Cookies
4. **Copy Required Values**:
- `ltoken_v2` (required)
- `ltuid_v2` (required)
- `ltmid_v2` (required)
- `cookie_token_v2` (for code redemption)
- `account_mid_v2` (for code redemption)
- `account_id_v2` (for code redemption)
### Cookie Format
```javascript
const cookie = "ltoken_v2=v2_xxx; ltuid_v2=123456; ltmid_v2=xxx; cookie_token_v2=xxx; account_mid_v2=xxx; account_id_v2=123456";
```
### Validating Your Cookie
```javascript
const { utils } = require('@chiraitori/hoyolab-core');
const validation = utils.validateCookie(yourCookie);
if (!validation.valid) {
console.error('Invalid cookie:', validation.error);
} else {
console.log('Cookie valid!');
console.log('Can redeem codes:', validation.hasRedeemTokens);
}
```
## Basic Usage
### Simple Check-in
```javascript
const { HoyoLabClient, Games } = require('@chiraitori/hoyolab-core');
const client = new HoyoLabClient({ cookie: 'your_cookie_here' });
// Check in to Genshin Impact
const result = await client.dailyCheckIn(Games.GENSHIN_IMPACT);
console.log(utils.formatCheckInResult(result));
```
### Code Redemption
```javascript
// Redeem a specific code
const redeemResult = await client.redeemCode(Games.GENSHIN_IMPACT, 'PROMOCODE123');
// Fetch and redeem all available codes
const codes = await client.fetchAvailableCodes(Games.GENSHIN_IMPACT);
for (const code of codes) {
try {
await client.redeemCode(Games.GENSHIN_IMPACT, code.code);
console.log(`Redeemed: ${code.code}`);
} catch (error) {
console.error(`Failed: ${code.code} - ${error.message}`);
}
}
```
## Advanced Features
### Multi-Account Support
```javascript
const { HoyoLabClient, Games } = require('@chiraitori/hoyolab-core');
const accounts = [
{ cookie: 'account1_cookie', name: 'Main Account' },
{ cookie: 'account2_cookie', name: 'Alt Account' }
];
for (const account of accounts) {
const client = new HoyoLabClient({ cookie: account.cookie });
try {
const result = await client.dailyCheckIn(Games.GENSHIN_IMPACT);
console.log(`${account.name}: ${utils.formatCheckInResult(result)}`);
} catch (error) {
console.error(`${account.name}: ${error.message}`);
}
}
```
### Rate Limiting
```javascript
const { utils } = require('@chiraitori/hoyolab-core');
// Create rate limiter (10 calls per minute)
const rateLimiter = new utils.RateLimiter(10, 60000);
for (const code of codes) {
await rateLimiter.waitIfNeeded();
try {
await client.redeemCode(Games.GENSHIN_IMPACT, code.code);
} catch (error) {
console.error(error.message);
}
}
```
### Retry with Backoff
```javascript
const { utils } = require('@chiraitori/hoyolab-core');
const performCheckIn = () => client.dailyCheckIn(Games.GENSHIN_IMPACT);
const result = await utils.retryWithBackoff(performCheckIn, {
maxRetries: 3,
baseDelay: 1000,
maxDelay: 10000
});
```
### Caching
```javascript
const { utils } = require('@chiraitori/hoyolab-core');
// Cache for 10 minutes
const cache = new utils.SimpleCache(600000);
// Check cache first
let codes = cache.get('genshin_codes');
if (!codes) {
codes = await client.fetchAvailableCodes(Games.GENSHIN_IMPACT);
cache.set('genshin_codes', codes);
}
```
### Timezone-Aware Operations
```javascript
const { utils } = require('@chiraitori/hoyolab-core');
const lastCheckIn = new Date('2025-06-01T10:00:00Z');
const userRegion = 'NA';
if (utils.isNewDay(userRegion, lastCheckIn)) {
console.log('New day detected, performing check-in...');
await client.dailyCheckIn(Games.GENSHIN_IMPACT);
}
```
### Region Mapping
Each HoYoverse game uses different region codes. The package provides game-specific region mapping:
```javascript
const { getRegionName, Games } = require('@chiraitori/hoyolab-core');
// Map game-specific region codes to readable names
const genshinRegion = getRegionName(Games.GENSHIN_IMPACT, 'os_usa'); // 'NA'
const starrailRegion = getRegionName(Games.HONKAI_STAR_RAIL, 'prod_official_eur'); // 'EU'
const zenlessRegion = getRegionName(Games.ZENLESS_ZONE_ZERO, 'prod_gf_jp'); // 'SEA'
// Handle account information with proper region names
const accounts = await client.getAccounts(Games.GENSHIN_IMPACT);
for (const account of accounts) {
console.log(`Account: ${account.nickname}`);
console.log(`Region: ${getRegionName(Games.GENSHIN_IMPACT, account.region)}`);
console.log(`UID: ${account.uid}`);
}
```
#### Supported Region Mappings
| Game | Region Code | Display Name |
|------|-------------|--------------|
| **Genshin Impact** | `os_usa` | NA |
| | `os_euro` | EU |
| | `os_asia` | SEA |
| | `os_cht` | TW/HK/MO |
| **Honkai Star Rail** | `prod_official_usa` | NA |
| | `prod_official_eur` | EU |
| | `prod_official_asia` | SEA |
| | `prod_official_cht` | TW/HK/MO |
| **Zenless Zone Zero** | `prod_gf_us` | NA |
| | `prod_gf_eu` | EU |
| | `prod_gf_jp` | SEA |
| | `prod_gf_sg` | TW/HK/MO |
## Error Handling
### Comprehensive Error Handling
```javascript
const { ErrorCodes } = require('@chiraitori/hoyolab-core');
try {
await client.dailyCheckIn(Games.GENSHIN_IMPACT);
} catch (error) {
switch (error.code) {
case ErrorCodes.INVALID_COOKIE:
console.log('🚫 Cookie expired. Please update your cookie.');
// Trigger cookie refresh logic
break;
case ErrorCodes.ALREADY_CHECKED_IN:
console.log('✅ Already checked in today.');
break;
case ErrorCodes.RATE_LIMITED:
console.log('⏰ Rate limited. Waiting 60 seconds...');
await utils.sleep(60000);
// Retry logic here
break;
case ErrorCodes.NETWORK_ERROR:
console.log('🌐 Network error. Check your connection.');
break;
default:
console.error('❌ Unexpected error:', error.message);
console.error('Details:', error.details);
}
}
```
### Graceful Degradation
```javascript
const games = [Games.GENSHIN_IMPACT, Games.HONKAI_STAR_RAIL, Games.ZENLESS_ZONE_ZERO];
const results = {};
for (const game of games) {
try {
results[game] = await client.dailyCheckIn(game);
} catch (error) {
results[game] = { error: error.message, code: error.code };
// Continue with other games even if one fails
if (error.code !== ErrorCodes.INVALID_COOKIE) {
continue;
} else {
// Stop if cookie is invalid for all games
break;
}
}
}
```
## Utilities
### Helper Functions
```javascript
const { utils } = require('@chiraitori/hoyolab-core');
// Format results for display
const result = await client.dailyCheckIn(Games.GENSHIN_IMPACT);
console.log(utils.formatCheckInResult(result));
// Sleep/delay
await utils.sleep(5000); // 5 seconds
// Get timezone offset
const offset = utils.getTimezoneOffset('NA'); // -300 minutes
// Check if new day
const isNewDay = utils.isNewDay('EU', lastCheckDate);
```
## Best Practices
### 1. Cookie Management
```javascript
// Store cookies securely
const cookies = {
main: process.env.HOYOLAB_COOKIE_MAIN,
alt: process.env.HOYOLAB_COOKIE_ALT
};
// Validate before use
for (const [name, cookie] of Object.entries(cookies)) {
const validation = utils.validateCookie(cookie);
if (!validation.valid) {
console.error(`Invalid cookie for ${name}: ${validation.error}`);
}
}
```
### 2. Scheduled Operations
```javascript
// Use node-cron for scheduling
const cron = require('node-cron');
// Run daily at 1 AM
cron.schedule('0 1 * * *', async () => {
console.log('Running daily check-ins...');
const games = [Games.GENSHIN_IMPACT, Games.HONKAI_STAR_RAIL];
for (const game of games) {
try {
const result = await client.dailyCheckIn(game);
console.log(`${game}: ${utils.formatCheckInResult(result)}`);
} catch (error) {
console.error(`${game}: ${error.message}`);
}
}
});
```
### 3. Logging
```javascript
const fs = require('fs');
function logResult(game, result, error = null) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
game,
success: !error,
result: error ? error.message : result,
errorCode: error ? error.code : null
};
fs.appendFileSync('hoyolab.log', JSON.stringify(logEntry) + '\n');
}
try {
const result = await client.dailyCheckIn(Games.GENSHIN_IMPACT);
logResult(Games.GENSHIN_IMPACT, result);
} catch (error) {
logResult(Games.GENSHIN_IMPACT, null, error);
}
```
### 4. Configuration Management
```javascript
const config = {
cookies: {
main: process.env.HOYOLAB_COOKIE_MAIN
},
games: [Games.GENSHIN_IMPACT, Games.HONKAI_STAR_RAIL],
retryOptions: {
maxRetries: 3,
baseDelay: 1000
},
rateLimiting: {
maxCalls: 10,
windowMs: 60000
}
};
const client = new HoyoLabClient({
cookie: config.cookies.main,
userAgent: 'MyApp/1.0.0'
});
```
## Troubleshooting
### Common Issues
1. **"Missing required cookie token"**
- Ensure all required tokens are present in cookie
- Check cookie format and spelling
2. **"Invalid or expired cookie"**
- Re-obtain cookie from browser
- Ensure you're logged in to HoYoLab
3. **"Rate limited"**
- Add delays between requests
- Use the built-in RateLimiter utility
4. **"Already checked in today"**
- Normal behavior, check-in only once per day
- Consider timezone differences
5. **"Code redemption not supported"**
- Some games don't support code redemption
- Ensure you have redemption tokens in cookie
### Debug Mode
```javascript
const client = new HoyoLabClient({
cookie: 'your_cookie',
userAgent: 'MyApp/1.0.0 (debug)'
});
// Enable verbose logging
process.env.DEBUG = 'hoyolab-core:*';
```
### Testing
```javascript
// Test with a minimal example
const { HoyoLabClient, Games, utils } = require('@chiraitori/hoyolab-core');
async function test() {
const cookie = 'your_test_cookie';
// Validate cookie first
const validation = utils.validateCookie(cookie);
console.log('Cookie validation:', validation);
if (!validation.valid) {
console.error('Invalid cookie, stopping test');
return;
}
const client = new HoyoLabClient({ cookie });
try {
const accounts = await client.getAccounts();
console.log('Accounts:', accounts);
const result = await client.dailyCheckIn(Games.GENSHIN_IMPACT);
console.log('Check-in result:', result);
} catch (error) {
console.error('Test failed:', error.message);
console.error('Error code:', error.code);
}
}
test();
```