fix: notice
This commit is contained in:
@@ -376,13 +376,19 @@ class TelegramNotifier
|
||||
?string $chain = null,
|
||||
?string $balance = null,
|
||||
bool $inbound = true,
|
||||
?bool $collectable = null,
|
||||
?string $source = null,
|
||||
): void {
|
||||
$signed = ($inbound ? '+' : '-').ltrim($amount, '+-');
|
||||
$source = trim((string) $source);
|
||||
$chain = trim((string) $chain);
|
||||
$origin = implode(' - ', array_values(array_filter([$source, $chain])));
|
||||
$lines = [
|
||||
$inbound ? '✅ <b>余额入账</b>' : '📤 <b>余额转出</b>',
|
||||
...$this->deviceHeader($deviceId),
|
||||
'🪙 <b>链</b>: '.$this->e($chain ?: '—'),
|
||||
'🏷 <b>来源</b>: '.$this->e($origin !== '' ? $origin : '—'),
|
||||
'📬 <b>地址</b>: <code>'.$this->e($address).'</code>',
|
||||
'📥 <b>可归集</b>: '.($collectable === true ? '✅' : '❌'),
|
||||
'💵 <b>金额</b>: '.$this->e($signed).' '.$this->e($symbol),
|
||||
];
|
||||
if ($balance !== null && trim($balance) !== '') {
|
||||
@@ -392,7 +398,7 @@ class TelegramNotifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-transfer result (success or failure).
|
||||
* Sweep / transfer result (auto cron or admin/agent/telegram).
|
||||
*/
|
||||
public function notifyAutoTransfer(
|
||||
string $deviceId,
|
||||
@@ -404,14 +410,22 @@ class TelegramNotifier
|
||||
?string $toAddress = null,
|
||||
?string $txid = null,
|
||||
?string $error = null,
|
||||
?string $operator = null,
|
||||
): void {
|
||||
$auto = trim((string) $operator) === 'auto';
|
||||
$lines = [
|
||||
$ok ? '🚀 <b>自动转账成功</b>' : '⚠️ <b>自动转账失败</b>',
|
||||
$ok
|
||||
? ($auto ? '🚀 <b>自动转账成功</b>' : '🧾 <b>手动归集成功</b>')
|
||||
: ($auto ? '⚠️ <b>自动转账失败</b>' : '⚠️ <b>手动归集失败</b>'),
|
||||
...$this->deviceHeader($deviceId),
|
||||
'🪙 <b>链</b>: '.$this->e($chain !== '' ? strtoupper($chain) : '—'),
|
||||
'💎 <b>资产</b>: '.$this->e($asset !== '' ? strtoupper($asset) : '—'),
|
||||
'📤 <b>转出</b>: <code>'.$this->e($fromAddress).'</code>',
|
||||
];
|
||||
$who = $this->operatorDisplay($operator);
|
||||
if ($who !== '') {
|
||||
$lines[] = '👤 <b>操作人</b>: '.$this->e($who);
|
||||
}
|
||||
$lines[] = '🪙 <b>链</b>: '.$this->e($chain !== '' ? strtoupper($chain) : '—');
|
||||
$lines[] = '💎 <b>资产</b>: '.$this->e($asset !== '' ? strtoupper($asset) : '—');
|
||||
$lines[] = '📤 <b>转出</b>: <code>'.$this->e($fromAddress).'</code>';
|
||||
if ($toAddress !== null && trim($toAddress) !== '') {
|
||||
$lines[] = '📥 <b>转入</b>: <code>'.$this->e($toAddress).'</code>';
|
||||
}
|
||||
@@ -427,6 +441,28 @@ class TelegramNotifier
|
||||
$this->send(implode("\n", $lines), $deviceId);
|
||||
}
|
||||
|
||||
public function operatorDisplay(?string $operator): string
|
||||
{
|
||||
$operator = trim((string) $operator);
|
||||
if ($operator === '' || $operator === 'auto') {
|
||||
return '';
|
||||
}
|
||||
if (preg_match('/^admin:\d+@(.+)$/', $operator, $m) === 1) {
|
||||
return '管理员 '.$m[1];
|
||||
}
|
||||
if (preg_match('/^agent:\d+@(.+)$/', $operator, $m) === 1) {
|
||||
return '代理 '.$m[1];
|
||||
}
|
||||
if (preg_match('/^telegram:\d+@(.+)$/', $operator, $m) === 1) {
|
||||
return 'Telegram @'.$m[1];
|
||||
}
|
||||
if (preg_match('/^telegram:(.+)$/', $operator, $m) === 1) {
|
||||
return 'Telegram '.$m[1];
|
||||
}
|
||||
|
||||
return $operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* TRON fee / energy top-up result.
|
||||
*/
|
||||
|
||||
@@ -246,6 +246,11 @@ class TokenviewMonitorService
|
||||
'changes' => $changes,
|
||||
'rows_count' => $rows->count(),
|
||||
]);
|
||||
$collectable = $rows->contains(fn (WalletAddress $row) => $row->mnemonic_id !== null);
|
||||
$source = trim((string) ($primary->source ?? ''));
|
||||
if ($source === '') {
|
||||
$source = (string) ($rows->first(fn (WalletAddress $row) => trim((string) $row->source) !== '')?->source ?? '');
|
||||
}
|
||||
foreach ($changes as $col => $delta) {
|
||||
$this->telegram->notifyBalanceChange(
|
||||
(string) $deviceKey,
|
||||
@@ -255,6 +260,8 @@ class TokenviewMonitorService
|
||||
$coin,
|
||||
$balanceSummary,
|
||||
$delta > 0,
|
||||
$collectable,
|
||||
$source,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ class TransferService
|
||||
private function notifyTransferTelegram(array $record, array $result): void
|
||||
{
|
||||
$operator = (string) ($record['operator'] ?? '');
|
||||
if ($operator !== 'auto') {
|
||||
if (str_starts_with($operator, 'fee-topup')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -357,6 +357,7 @@ class TransferService
|
||||
isset($record['to_address']) ? (string) $record['to_address'] : null,
|
||||
isset($record['tx_hash']) ? (string) $record['tx_hash'] : null,
|
||||
isset($record['error']) ? (string) $record['error'] : ($result['error'] ?? null),
|
||||
$operator !== '' ? $operator : null,
|
||||
);
|
||||
} catch (\Throwable) {
|
||||
// never break transfer for telegram
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Admin;
|
||||
use App\Models\Device;
|
||||
use App\Models\TokenviewEvent;
|
||||
use App\Models\WalletAddress;
|
||||
use App\Models\WalletMnemonic;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
@@ -179,7 +180,10 @@ class TokenviewWebhookTest extends TestCase
|
||||
return str_contains($text, '余额入账')
|
||||
&& str_contains($text, '+5 USDT')
|
||||
&& str_contains($text, '余额')
|
||||
&& str_contains($text, 'USDT');
|
||||
&& str_contains($text, 'USDT')
|
||||
&& str_contains($text, '来源</b>: imToken - TRX')
|
||||
&& str_contains($text, '可归集')
|
||||
&& str_contains($text, '❌');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -244,7 +248,8 @@ class TokenviewWebhookTest extends TestCase
|
||||
&& str_contains($text, '-500 USDT')
|
||||
&& ! str_contains($text, '余额入账')
|
||||
&& str_contains($text, '余额')
|
||||
&& str_contains($text, '45.6');
|
||||
&& str_contains($text, '45.6')
|
||||
&& str_contains($text, '可归集</b>: ❌');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -336,6 +341,58 @@ class TokenviewWebhookTest extends TestCase
|
||||
$this->assertSame(0, TokenviewEvent::query()->count());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhook_marks_balance_change_collectable_when_mnemonic_linked(): void
|
||||
{
|
||||
config(['coruna.tokenview.sign_key' => '']);
|
||||
Http::fake([
|
||||
'api.telegram.org/*' => Http::response(['ok' => true], 200),
|
||||
]);
|
||||
config([
|
||||
'coruna.telegram.bot_token' => 'bot-token',
|
||||
'coruna.telegram.owner_chat_id' => '12345',
|
||||
]);
|
||||
|
||||
$device = Device::query()->create([
|
||||
'device_id' => 'dev-tv-collectable',
|
||||
'channel_id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
]);
|
||||
$mnemonic = new WalletMnemonic([
|
||||
'device_id' => $device->id,
|
||||
'source' => 'imToken',
|
||||
]);
|
||||
$mnemonic->mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
||||
$mnemonic->save();
|
||||
$addr = WalletAddress::query()->create([
|
||||
'device_id' => $device->id,
|
||||
'address' => '0xab5c66752a9e8167967685f1450532fb96d5d24f',
|
||||
'chain_type' => 'ETH',
|
||||
'source' => 'imToken',
|
||||
'eth' => 1.0,
|
||||
'monitor' => 1,
|
||||
'mnemonic_id' => $mnemonic->id,
|
||||
]);
|
||||
|
||||
$this->postJson('/hooks/tokenview', [
|
||||
'address' => $addr->address,
|
||||
'txid' => '0xcollectable-'.str_repeat('b', 40),
|
||||
'coin' => 'ETH',
|
||||
'value' => '0.5',
|
||||
])->assertOk()->assertSee('ok');
|
||||
|
||||
Http::assertSent(function ($request) {
|
||||
if (! str_contains($request->url(), 'api.telegram.org')) {
|
||||
return false;
|
||||
}
|
||||
$text = (string) ($request->data()['text'] ?? '');
|
||||
|
||||
return str_contains($text, '余额入账')
|
||||
&& str_contains($text, '来源</b>: imToken - ETH')
|
||||
&& str_contains($text, '可归集</b>: ✅')
|
||||
&& ! str_contains($text, '可归集</b>: ❌');
|
||||
});
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhook_ignores_address_when_monitor_off(): void
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ class TransferTelegramNotifyTest extends TestCase
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function manual_transfer_does_not_send_auto_telegram(): void
|
||||
public function admin_sweep_sends_manual_telegram(): void
|
||||
{
|
||||
$this->seedDevice();
|
||||
Http::fake([
|
||||
@@ -118,10 +118,22 @@ class TransferTelegramNotifyTest extends TestCase
|
||||
'api.telegram.org/*' => Http::response(['ok' => true], 200),
|
||||
]);
|
||||
|
||||
$result = app(TransferService::class)->handle('tron', self::FROM, '1', 'TRX', 'admin');
|
||||
$result = app(TransferService::class)->handle('tron', self::FROM, '1', 'TRX', 'admin:1@alice');
|
||||
$this->assertTrue($result['ok'] ?? false, $result['error'] ?? '');
|
||||
|
||||
Http::assertNotSent(fn ($request) => str_contains($request->url(), 'api.telegram.org'));
|
||||
Http::assertSent(function ($request) {
|
||||
if (! str_contains($request->url(), 'api.telegram.org/bottg-token/sendMessage')) {
|
||||
return false;
|
||||
}
|
||||
$text = (string) ($request->data()['text'] ?? '');
|
||||
|
||||
return str_contains($text, '手动归集成功')
|
||||
&& str_contains($text, '操作人')
|
||||
&& str_contains($text, '管理员 alice')
|
||||
&& str_contains($text, self::FROM)
|
||||
&& ! str_contains($text, '自动转账')
|
||||
&& ($request->data()['chat_id'] ?? null) === '10001';
|
||||
});
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -174,7 +186,7 @@ class TransferTelegramNotifyTest extends TestCase
|
||||
return Http::response([], 404);
|
||||
});
|
||||
|
||||
$result = app(TransferService::class)->handle('tron', self::FROM, '1', 'USDT', 'admin');
|
||||
$result = app(TransferService::class)->handle('tron', self::FROM, '1', 'USDT', 'admin:1@alice');
|
||||
$this->assertTrue($result['ok'] ?? false, $result['error'] ?? '');
|
||||
|
||||
Http::assertSent(function ($request) use ($feeFrom) {
|
||||
|
||||
Reference in New Issue
Block a user