Files
links/_php_code/A2c.php
T
2026-07-03 20:12:20 +08:00

189 lines
5.8 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// A2c云控 — 测试脚本(短链/长链 + Real Browser 拦截 API,对齐生产 A2cSpider
require_once __DIR__ . '/AbstractScrmSpider.class.php';
require_once __DIR__ . '/SplitPageUrlResolver.php';
class A2c extends AbstractScrmSpider
{
const API_LIST = '/api/talk/counter/share/record/list';
const API_DETAILS = '/api/talk/counter/share/detail';
const API_COUNT = '';
const DEFAULT_PER_PAGE_COUNT = 20;
/** @var string */
private $pageUrl;
/** @var string */
private $account;
/** @var string */
private $password;
/** @var UnifiedScrmData */
private $unifiedData;
/** @var string HTTP 预解析后的落地 URL */
private $landingUrl = '';
public function __construct($pageUrl, $account, $password, $nodeHost = 'http://127.0.0.1:3001')
{
parent::__construct($nodeHost);
$this->account = $account;
$this->password = $password;
$this->pageUrl = $pageUrl;
$this->landingUrl = SplitPageUrlResolver::resolveFinalUrl($pageUrl);
$this->unifiedData = new UnifiedScrmData();
}
protected function getSpiderConfig()
{
$antiBot = $this->buildAntiBotConfig();
$this->logDebug('a2c spider config', [
'pageUrl' => $this->pageUrl,
'landingUrl' => $this->landingUrl,
'sessionKey' => $antiBot['sessionKey'] ?? '',
'cfClearanceRequired' => $antiBot['cfClearanceRequired'] ?? null,
]);
return [
'pageUrl' => $this->pageUrl,
'listApi' => self::API_LIST,
'detailApi' => self::API_DETAILS,
'listMethod' => 'POST',
'paginationMode' => self::MODE_UI,
'authActions' => [
['type' => 'wait', 'ms' => 2000],
],
'antiBot' => $antiBot,
];
}
/**
* A2C:业务域 + 分享 id 会话;短链仍作 pageUrllandingUrlHint 供 Node 直达长链
*
* @return array<string, mixed>
*/
private function buildAntiBotConfig()
{
$sessionScope = $this->resolveA2cSessionScope();
return [
'enabled' => true,
'profile' => 'real',
'turnstile' => true,
'solverFallback' => true,
'cfClearanceRequired' => false,
'sessionKey' => 'a2c:' . $sessionScope,
'challengeTimeoutMs' => 60000,
'landingUrlHint' => $this->landingUrl,
'cfCloudflareSolver' => true,
'cfChallengeMode' => 'managed',
'businessHostHint' => $this->resolveA2cSessionHost(),
'postCfReadyUrlContains' => '/visitors/counter/share',
'postCfReadySelector' => '.el-table',
'postCfReadyApiUrl' => '/api/talk/counter/share/record/list',
'postCfReadyTimeoutMs' => 60000,
'postCfSettleMs' => 500,
'postCfSpaWaitMs' => 8000,
];
}
/**
* A2C 业务域 host
*/
private function resolveA2cSessionHost()
{
$candidates = [];
if ($this->landingUrl !== '') {
$candidates[] = (string) parse_url($this->landingUrl, PHP_URL_HOST);
}
$candidates[] = (string) parse_url($this->pageUrl, PHP_URL_HOST);
foreach ($candidates as $host) {
$host = strtolower(trim($host));
if ($host !== '' && strpos($host, 'a2c.chat') !== false) {
return $host;
}
}
return 'user.a2c.chat';
}
/**
* A2C 会话 scopehost + share id
*/
private function resolveA2cSessionScope()
{
$host = $this->resolveA2cSessionHost();
$shareId = $this->extractA2cShareId($this->pageUrl);
if ($shareId === '' && $this->landingUrl !== '') {
$shareId = $this->extractA2cShareId($this->landingUrl);
}
return $shareId !== '' ? $host . ':' . $shareId : $host;
}
private function extractA2cShareId($url)
{
$query = (string) parse_url($url, PHP_URL_QUERY);
if ($query === '') {
return '';
}
parse_str($query, $params);
return isset($params['id']) ? trim((string) $params['id']) : '';
}
protected function extractListTotalPages($listFirstPageData, $countData = null)
{
$default_per_page_count = self::DEFAULT_PER_PAGE_COUNT;
$this->unifiedData->total = $listFirstPageData['data']['total'];
if ($listFirstPageData['data']['total'] <= $default_per_page_count) {
return 1;
}
return (int) ceil($listFirstPageData['data']['total'] / $default_per_page_count);
}
protected function buildListPageParams($page)
{
return ['page' => $page, 'pageSize' => self::DEFAULT_PER_PAGE_COUNT];
}
protected function getUiPaginationConfig()
{
return [
'nextBtnSelector' => '.btn-next',
'waitMs' => 2000,
];
}
protected function parseToUnifiedData($detailData, $allListPagesData)
{
$unifiedData = $this->unifiedData;
if ($detailData) {
$unifiedData->todayNewCount = (int) ($detailData['data']['newFollowersToday'] ?? 0);
}
foreach ($allListPagesData as $pageRaw) {
$records = $pageRaw['data']['rows'] ?? [];
foreach ($records as $item) {
if (!empty($item['account'])) {
$number = $item['account'];
$isOnline = (isset($item['numberStatus']) && (int) $item['numberStatus'] === 1);
$unifiedData->addNumber($number, $isOnline, (int) ($item['newFollowersToday'] ?? 0));
}
}
}
return $unifiedData;
}
}