Development
Development Environment Setup
Prerequisites
- Node.js 18+ and npm
- Git
- SQLite3
- Text editor (VSCode recommended)
- Telegram account for testing
Initial Setup
# Clone repository
git clone https://github.com/your-repo/somnia-validator-bot.git
cd somnia-validator-bot
# Install dependencies
npm install
# Copy environment template
cp .env.example .env
# Configure environment variables
nano .env
Environment Configuration
# Bot Configuration
BOT_TOKEN=your_bot_token_from_botfather
TELEGRAM_ADMIN_IDS=your_telegram_id
# Blockchain Configuration
SOMNIA_RPC_URL=https://dream.somnia.network
NODE_COMMITTEE_CONTRACT_ADDRESS=0x...
SOMNIA_STAKING_CONTRACT_ADDRESS=0x...
# Development Settings
NODE_ENV=development
LOG_LEVEL=debug
Development Workflow
Code Structure
src/
├── bot.ts # Main bot entry point
├── config/ # Configuration management
├── commands/ # Command handlers
├── services/ # Business logic
├── utils/ # Utility functions
├── types/ # TypeScript types
└── locales/ # Translations
Running in Development
# Run with hot reload
npm run dev
# Run with debugger
npm run debug
# Check types
npm run type-check
# Lint code
npm run lint
# Format code
npm run format
VSCode Configuration
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Bot",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/src/bot.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"env": {
"NODE_ENV": "development"
}
}
]
}
Adding Features
Creating a New Command
- Create command handler:
// src/commands/mycommand.ts
import { Context } from 'telegraf';
export async function myCommandHandler(ctx: Context) {
const userId = ctx.from?.id;
const userLang = await getUserLanguage(userId);
await ctx.reply(t('mycommand.response', userLang));
}
- Register in bot:
// src/bot.ts
import { myCommandHandler } from './commands/mycommand';
bot.command('mycommand', myCommandHandler);
- Add translations:
// locales/en.json
{
"mycommand": {
"response": "This is my new command!"
}
}
Adding Database Tables
- Create migration:
// src/migrations/add_my_table.ts
export async function up(db: Database) {
await db.run(`
CREATE TABLE my_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
}
- Update types:
// src/types/database.ts
export interface MyTableRow {
id: number;
data: string;
created_at: Date;
}
- Add database functions:
// src/database.ts
export async function insertMyData(data: string): Promise<void> {
return new Promise((resolve, reject) => {
db.run(
'INSERT INTO my_table (data) VALUES (?)',
[data],
(err) => err ? reject(err) : resolve()
);
});
}
Testing During Development
Manual Testing
- Create test bot:
- Talk to @BotFather
- Create new bot for testing
- Get test token
- Test database:
# Create test database
cp somnia_validator_bot.db test.db
# Use test database
DATABASE_PATH=./test.db npm run dev
- Test commands:
# Use test admin ID
TELEGRAM_ADMIN_IDS=your_test_id npm run dev
Automated Testing
# Run all tests
npm test
# Run specific test
npm test -- database.test.ts
# Run with coverage
npm run test:coverage
# Watch mode
npm run test:watch
Debugging
Debug Logging
import { logger } from './utils/logger';
// Add debug logs
logger.debug('Processing validator', {
address: validatorAddress,
state: currentState
});
// Conditional debug
if (config.app.debug) {
console.log('Detailed debug info:', data);
}
Common Issues
- Bot not responding:
- Check BOT_TOKEN is correct
- Verify bot is not running elsewhere
- Check network connectivity
- Database locked:
- Ensure only one instance running
- Check file permissions
- Use WAL mode
- RPC errors:
- Verify RPC URL is accessible
- Check rate limits
- Monitor network latency
Code Style
TypeScript Guidelines
// Use explicit types
function processData(data: ValidatorData): ProcessedResult {
// ...
}
// Use interfaces for objects
interface UserPreferences {
language: string;
notifications: boolean;
}
// Use enums for constants
enum CommandType {
User = 'user',
Admin = 'admin'
}
Error Handling
// Always handle errors
try {
const result = await riskyOperation();
return result;
} catch (error) {
logger.error('Operation failed', error);
throw new CustomError('Operation failed', error);
}
// Use custom errors
class ValidationError extends Error {
constructor(message: string, public field: string) {
super(message);
this.name = 'ValidationError';
}
}
Performance Considerations
Development Profiling
// Time operations
console.time('fetchValidatorData');
const data = await fetchValidatorData(address);
console.timeEnd('fetchValidatorData');
// Memory profiling
if (process.env.NODE_ENV === 'development') {
setInterval(() => {
const usage = process.memoryUsage();
console.log('Memory:', usage);
}, 10000);
}
Development Tools
- nodemon: Auto-restart on changes
- ts-node: Run TypeScript directly
- source-map-support: Better stack traces
- dotenv: Environment management
Git Workflow
Branch Strategy
# Feature branch
git checkout -b feature/new-command
# Bug fix
git checkout -b fix/subscription-error
# Hotfix
git checkout -b hotfix/critical-bug
Commit Messages
# Format: type(scope): description
git commit -m "feat(commands): add validator stats command"
git commit -m "fix(database): resolve connection timeout"
git commit -m "docs(readme): update installation instructions"
git commit -m "refactor(monitoring): optimize batch processing"
Pre-commit Hooks
.husky/pre-commit:
#!/bin/sh
npm run lint
npm run type-check
npm test