Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ Use our Encryption tool for btc worker addresses here:

https://github.com/warioishere/blitzpool-message-encryptor-for-TG

### 📢 NTFY Notifications

BlitzPool can mirror its Telegram bot interactions over [ntfy](https://ntfy.sh/) topics.
Enable the service by setting the following optional environment variables:

```
NTFY_SERVER_URL=<https://your-ntfy-server>
NTFY_ACCESS_TOKEN=<token if required>
NTFY_TOPIC_PREFIX=<optional prefix>
NTFY_DIFF_NOTIFICATIONS=true # publish best-diff alerts
```

On startup the pool subscribes to topics for all known BTC addresses using the
`<prefix><address>` convention. Post commands like `/subscribe` or `/stats` to the
topic of your address and the service will reply on the same channel:

```
curl -d /stats $NTFY_SERVER_URL/myPrefix1ABC...
curl -d "/subscribe 1DEF..." $NTFY_SERVER_URL/myPrefix1ABC...
```

#### 🛠️ Extra Services
- Integrated `blockTemplateInterval` configuration
- Hashrate corrections and updated statistics endpoints
Expand Down
6 changes: 6 additions & 0 deletions full-setup/blitzpool-example.env
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ DIFFICULTY_CHECK_INTERVAL_MS=60000
TELEGRAM_BOT_TOKEN="xxx"
TELEGRAM_DIFF_NOTIFICATIONS=true

#optional ntfy notification service
NTFY_SERVER_URL=
NTFY_ACCESS_TOKEN=
NTFY_TOPIC_PREFIX=
NTFY_DIFF_NOTIFICATIONS=false

#optional discord bot
#DISCORD_BOT_CLIENTID=
#DISCORD_BOT_GUILD_ID=
Expand Down
49 changes: 49 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"discord.js": "^14.11.0",
"eventsource": "^4.0.0",
"merkle-lib": "^2.0.10",
"node-telegram-bot-api": "^0.61.0",
"reflect-metadata": "^0.1.13",
Expand All @@ -54,6 +55,7 @@
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/eventsource": "^1.1.15",
"@types/big.js": "^6.1.6",
"@types/cron": "^2.0.1",
"@types/express": "^4.17.13",
Expand Down
8 changes: 8 additions & 0 deletions src/ORM/client/client.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,12 @@ export class ClientService {
return result;
}

public async getAllAddresses(): Promise<string[]> {
const rows = await this.clientRepository
.createQueryBuilder('client')
.select('DISTINCT client.address', 'address')
.getRawMany();
return rows.map(r => r.address);
}

}
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { NotificationService } from './services/notification.service';
import { StratumV1JobsService } from './services/stratum-v1-jobs.service';
import { StratumV1Service } from './services/stratum-v1.service';
import { TelegramService } from './services/telegram.service';
import { NtfyService } from './services/ntfy.service';
import { ExternalSharesService } from './services/external-shares.service';
import { ExternalShareController } from './controllers/external-share/external-share.controller';
import { ExternalSharesModule } from './ORM/external-shares/external-shares.module';
Expand Down Expand Up @@ -73,6 +74,7 @@ const ORMModules = [
AppService,
StratumV1Service,
TelegramService,
NtfyService,
BitcoinRpcService,
NotificationService,
BitcoinAddressValidator,
Expand Down
43 changes: 43 additions & 0 deletions src/services/common-command-handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ClientService } from '../ORM/client/client.service';
import { AddressSettingsService } from '../ORM/address-settings/address-settings.service';
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
import { NumberSuffix } from '../utils/NumberSuffix';

export interface StatsMessages {
de: string;
en: string;
}

export async function buildStatsMessage(
address: string,
clientService: ClientService,
addressSettingsService: AddressSettingsService,
clientStatisticsService: ClientStatisticsService,
numberSuffix: NumberSuffix
): Promise<StatsMessages | null> {
const workers = await clientService.getByAddress(address);
if (!workers || workers.length === 0) {
return null;
}
const totalHashrate = workers.reduce((sum, w) => sum + (w.hashRate ?? 0), 0);
const totalHashrateTH = totalHashrate / 1e12;
const lastSeenSeconds = Math.floor((Date.now() - new Date(workers[0].updatedAt).getTime()) / 1000);
const totalShares = await clientStatisticsService.getTotalSharesForAddress(address);
const addressSettings = await addressSettingsService.getSettings(address, false);
const bestDiffRaw = addressSettings?.bestDifficulty ?? 0;
const bestDifficultyG = bestDiffRaw / 1e9;

return {
de: `📈 Stats für deine Adresse:\n` +
`- Aktuelle Hashrate: ${totalHashrateTH.toFixed(2)} TH/s\n` +
`- Gesamt-Shares: ${numberSuffix.to(totalShares)}\n` +
`- Letzter Share: vor ${lastSeenSeconds} Sekunden\n` +
`- Beste Difficulty: ${bestDifficultyG.toFixed(2)} G`,
en: `📈 Stats for your address:\n` +
`- Current hashrate: ${totalHashrateTH.toFixed(2)} TH/s\n` +
`- Total shares: ${numberSuffix.to(totalShares)}\n` +
`- Last share: ${lastSeenSeconds} seconds ago\n` +
`- Best difficulty: ${bestDifficultyG.toFixed(2)} G`,
};
}

6 changes: 5 additions & 1 deletion src/services/notification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { Block } from 'bitcoinjs-lib';

import { DiscordService } from './discord.service';
import { TelegramService } from './telegram.service';
import { NtfyService } from './ntfy.service';


@Injectable()
export class NotificationService implements OnModuleInit {

constructor(
private readonly telegramService: TelegramService,
private readonly discordService: DiscordService
private readonly discordService: DiscordService,
private readonly ntfyService: NtfyService,
) { }

async onModuleInit(): Promise<void> {
Expand All @@ -20,10 +22,12 @@ export class NotificationService implements OnModuleInit {
public async notifySubscribersBlockFound(address: string, height: number, block: Block, message: string) {
await this.discordService.notifySubscribersBlockFound(height, block, message);
await this.telegramService.notifySubscribersBlockFound(address, height, block, message);
await this.ntfyService.notifySubscribersBlockFound(address, height, block, message);
}

public async notifySubscribersBestDiff(address: string, submissionDifficulty: number) {
await this.discordService.notifySubscribersBestDiff(submissionDifficulty);
await this.telegramService.notifySubscribersBestDiff(address, submissionDifficulty);
await this.ntfyService.notifySubscribersBestDiff(address, submissionDifficulty);
}
}
Loading