|
@@ -0,0 +1,486 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+
|
|
|
|
|
+namespace App\Console\Commands;
|
|
|
|
|
+
|
|
|
|
|
+use App\Notification\TelegramBot;
|
|
|
|
|
+use App\Util;
|
|
|
|
|
+use Illuminate\Console\Command;
|
|
|
|
|
+use Illuminate\Support\Facades\Redis;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 定时检测 Google Play 上架包是否仍可访问(大致对应「未下架」)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * 规则与 iOS 侧类似:
|
|
|
|
|
+ * - online : HTTP 200 且页面含正常 Play 详情特征
|
|
|
|
|
+ * - delisted : HTTP 404,或 200 但为 Play「Not Found」错误页
|
|
|
|
|
+ * - unknown : 超时、429、5xx、页面过短无法判断等
|
|
|
|
|
+ *
|
|
|
|
|
+ * 连续 delisted / unknown 达阈值才告警;恢复 online 会清计数与已通知标记。
|
|
|
|
|
+ */
|
|
|
|
|
+class CheckGooglePlayStore extends Command
|
|
|
|
|
+{
|
|
|
|
|
+ protected $signature = 'google:check-play-store';
|
|
|
|
|
+
|
|
|
|
|
+ protected $description = '检测 Google Play 包是否疑似下架,告警时 Telegram sendMsgAndImportant';
|
|
|
|
|
+
|
|
|
|
|
+ /** 展示名 => Play 详情页完整 URL(须含 id= 包名) */
|
|
|
|
|
+ protected array $apps = [
|
|
|
|
|
+ 'google-AFVegas-Nights-228' => 'https://play.google.com/store/apps/details?id=com.oipjuwenkjhde.kjhbnmuiejnd',
|
|
|
|
|
+ 'google-PrimeAF-Gaming-229' => 'https://play.google.com/store/apps/details?id=com.rewubsbdqkk.koeesowemli',
|
|
|
|
|
+ 'google-test111' => 'https://play.google.com/store/apps/details?id=com.rewubsbdqkk.123',
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ protected int $delistedConfirmTimes = 3;
|
|
|
|
|
+
|
|
|
|
|
+ protected int $unknownConfirmTimes = 3;
|
|
|
|
|
+
|
|
|
|
|
+ protected int $counterExpireSeconds = 3600;
|
|
|
|
|
+
|
|
|
|
|
+ protected int $httpMaxAttempts = 3;
|
|
|
|
|
+
|
|
|
|
|
+ protected int $sleepMinMicroseconds = 200000;
|
|
|
|
|
+
|
|
|
|
|
+ protected int $sleepMaxMicroseconds = 500000;
|
|
|
|
|
+
|
|
|
|
|
+ public function handle()
|
|
|
|
|
+ {
|
|
|
|
|
+ if ($this->apps === []) {
|
|
|
|
|
+ $this->warn('未配置 Google Play 检测列表(CheckGooglePlayStore::$apps),跳过。');
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $env = env('APP_ENV', 'local');
|
|
|
|
|
+
|
|
|
|
|
+ $results = [];
|
|
|
|
|
+ $delistedNotified = [];
|
|
|
|
|
+ $unknownNotified = [];
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($this->apps as $name => $url) {
|
|
|
|
|
+ $result = $this->checkSingleApp($name, $url);
|
|
|
|
|
+ $results[] = $result;
|
|
|
|
|
+
|
|
|
|
|
+ Util::WriteLog(
|
|
|
|
|
+ 'google_play_check',
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ '[%s] %s | %s | %s',
|
|
|
|
|
+ strtoupper($result['status']),
|
|
|
|
|
+ $name,
|
|
|
|
|
+ $url,
|
|
|
|
|
+ $result['reason']
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ $pkg = $result['package'];
|
|
|
|
|
+
|
|
|
|
|
+ if ($result['status'] === 'online') {
|
|
|
|
|
+ $this->clearCounters($pkg);
|
|
|
|
|
+ } elseif ($result['status'] === 'delisted') {
|
|
|
|
|
+ $count = $this->increaseCounter($this->getDelistedCounterKey($pkg));
|
|
|
|
|
+ $this->clearCounter($this->getUnknownCounterKey($pkg));
|
|
|
|
|
+
|
|
|
|
|
+ if ($count >= $this->delistedConfirmTimes) {
|
|
|
|
|
+ if (!$this->hasAlreadyNotified('delisted', $pkg)) {
|
|
|
|
|
+ $delistedNotified[] = [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $url,
|
|
|
|
|
+ 'reason' => $result['reason'],
|
|
|
|
|
+ 'count' => $count,
|
|
|
|
|
+ ];
|
|
|
|
|
+ $this->markNotified('delisted', $pkg);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } elseif ($result['status'] === 'unknown') {
|
|
|
|
|
+ $count = $this->increaseCounter($this->getUnknownCounterKey($pkg));
|
|
|
|
|
+ $this->clearCounter($this->getDelistedCounterKey($pkg));
|
|
|
|
|
+
|
|
|
|
|
+ if ($count >= $this->unknownConfirmTimes) {
|
|
|
|
|
+ if (!$this->hasAlreadyNotified('unknown', $pkg)) {
|
|
|
|
|
+ $unknownNotified[] = [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $url,
|
|
|
|
|
+ 'reason' => $result['reason'],
|
|
|
|
|
+ 'count' => $count,
|
|
|
|
|
+ ];
|
|
|
|
|
+ $this->markNotified('unknown', $pkg);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ usleep(random_int($this->sleepMinMicroseconds, $this->sleepMaxMicroseconds));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!empty($delistedNotified)) {
|
|
|
|
|
+ $this->notifyDelisted($env, $delistedNotified);
|
|
|
|
|
+ $this->warn('已发送下架告警: ' . count($delistedNotified) . ' 个');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!empty($unknownNotified)) {
|
|
|
|
|
+ $this->notifyUnknown($env, $unknownNotified);
|
|
|
|
|
+ $this->warn('已发送检测异常告警: ' . count($unknownNotified) . ' 个');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($results as $item) {
|
|
|
|
|
+ $this->line(sprintf(
|
|
|
|
|
+ '[%s] %s - %s',
|
|
|
|
|
+ strtoupper($item['status']),
|
|
|
|
|
+ $item['name'],
|
|
|
|
|
+ $item['reason']
|
|
|
|
|
+ ));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (empty($delistedNotified) && empty($unknownNotified)) {
|
|
|
|
|
+ $this->info('本次检测完成,无需告警。');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return empty($delistedNotified) ? 0 : 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @return array{name:string,url:string,package:string,status:string,reason:string}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected function checkSingleApp(string $name, string $playUrl): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $parsed = $this->parsePlayStoreUrl($playUrl);
|
|
|
|
|
+
|
|
|
|
|
+ if ($parsed === null) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => '_badurl_' . md5($playUrl),
|
|
|
|
|
+ 'status' => 'unknown',
|
|
|
|
|
+ 'reason' => '无法从 URL 解析包名 id=',
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $package = $parsed['package'];
|
|
|
|
|
+ $requestUrl = $this->buildDetailsUrl($package);
|
|
|
|
|
+
|
|
|
|
|
+ $resp = $this->httpGetWithRetry($requestUrl, $this->httpMaxAttempts);
|
|
|
|
|
+
|
|
|
|
|
+ if (!empty($resp['error'])) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'unknown',
|
|
|
|
|
+ 'reason' => '请求失败: ' . $resp['error'],
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $code = (int) ($resp['code'] ?? 0);
|
|
|
|
|
+ $body = (string) ($resp['body'] ?? '');
|
|
|
|
|
+
|
|
|
|
|
+ if (in_array($code, [403, 429, 500, 502, 503, 504], true)) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'unknown',
|
|
|
|
|
+ 'reason' => "HTTP {$code}",
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($code === 404) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'delisted',
|
|
|
|
|
+ 'reason' => 'Play 返回 HTTP 404(包不存在或已下架)',
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($code !== 200) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'unknown',
|
|
|
|
|
+ 'reason' => "HTTP {$code}",
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($this->isPlayNotFoundPage($body)) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'delisted',
|
|
|
|
|
+ 'reason' => 'Play 错误页(Not Found 等)',
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($this->isLikelyNormalDetailPage($body)) {
|
|
|
|
|
+ $this->clearNotifiedFlags($package);
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'online',
|
|
|
|
|
+ 'reason' => '详情页正常(含 Play 前端特征)',
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $len = strlen($body);
|
|
|
|
|
+
|
|
|
|
|
+ if ($len < 15000) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'unknown',
|
|
|
|
|
+ 'reason' => "HTTP 200 但正文过短({$len} bytes),无法判断",
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'name' => $name,
|
|
|
|
|
+ 'url' => $playUrl,
|
|
|
|
|
+ 'package' => $package,
|
|
|
|
|
+ 'status' => 'unknown',
|
|
|
|
|
+ 'reason' => 'HTTP 200 但未识别为正常详情页(页面结构可能变更)',
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function isPlayNotFoundPage(string $body): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ if (stripos($body, '<title>Not Found</title>') !== false) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (preg_match('/requested\s+URL\s+was\s+not\s+found/i', $body)) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function isLikelyNormalDetailPage(string $body): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ if ($this->isPlayNotFoundPage($body)) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (strlen($body) < 30000) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 当前 Play Web 详情页常见内嵌数据;404 短页不含
|
|
|
|
|
+ if (strpos($body, 'WIZ_global_data') === false) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 从详情链接解析包名。
|
|
|
|
|
+ */
|
|
|
|
|
+ protected function parsePlayStoreUrl(string $url): ?array
|
|
|
|
|
+ {
|
|
|
|
|
+ $parts = parse_url($url);
|
|
|
|
|
+ if (empty($parts['host']) || stripos($parts['host'], 'play.google.com') === false) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ $query = $parts['query'] ?? '';
|
|
|
|
|
+ if ($query === '') {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ parse_str($query, $q);
|
|
|
|
|
+ if (empty($q['id']) || !is_string($q['id'])) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ $id = $q['id'];
|
|
|
|
|
+ if (!preg_match('/^[a-zA-Z][a-zA-Z0-9._]*$/', $id)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ['package' => $id];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function buildDetailsUrl(string $package): string
|
|
|
|
|
+ {
|
|
|
|
|
+ return 'https://play.google.com/store/apps/details?id=' . rawurlencode($package) . '&hl=en&gl=US';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @return array{code:int,body:string,error:string}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected function httpGetWithRetry(string $url, int $maxAttempts = 3): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $attempt = 0;
|
|
|
|
|
+ $last = [
|
|
|
|
|
+ 'code' => 0,
|
|
|
|
|
+ 'body' => '',
|
|
|
|
|
+ 'error' => '',
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ while ($attempt < $maxAttempts) {
|
|
|
|
|
+ $attempt++;
|
|
|
|
|
+
|
|
|
|
|
+ $ch = curl_init($url);
|
|
|
|
|
+ curl_setopt_array($ch, [
|
|
|
|
|
+ CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
+ CURLOPT_FOLLOWLOCATION => true,
|
|
|
|
|
+ CURLOPT_TIMEOUT => 45,
|
|
|
|
|
+ CURLOPT_CONNECTTIMEOUT => 15,
|
|
|
|
|
+ CURLOPT_SSL_VERIFYPEER => true,
|
|
|
|
|
+ CURLOPT_HTTPHEADER => [
|
|
|
|
|
+ 'Accept: text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
|
|
|
|
|
+ 'Accept-Language: en-US,en;q=0.9',
|
|
|
|
|
+ 'Cache-Control: no-cache',
|
|
|
|
|
+ ],
|
|
|
|
|
+ CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ $body = curl_exec($ch);
|
|
|
|
|
+ $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
|
+ $error = curl_error($ch);
|
|
|
|
|
+ curl_close($ch);
|
|
|
|
|
+
|
|
|
|
|
+ $last = [
|
|
|
|
|
+ 'code' => $code,
|
|
|
|
|
+ 'body' => is_string($body) ? $body : '',
|
|
|
|
|
+ 'error' => $error ?: '',
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ if ($last['error'] === '' && ($code === 200 || $code === 404)) {
|
|
|
|
|
+ return $last;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $shouldRetry = $last['error'] !== '' || in_array($code, [429, 500, 502, 503, 504], true);
|
|
|
|
|
+
|
|
|
|
|
+ if (!$shouldRetry || $attempt >= $maxAttempts) {
|
|
|
|
|
+ return $last;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $sleepSeconds = $attempt === 1 ? 2 : ($attempt === 2 ? 5 : 8);
|
|
|
|
|
+ sleep($sleepSeconds);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $last;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function getDelistedCounterKey(string $package): string
|
|
|
|
|
+ {
|
|
|
|
|
+ return 'google:play:delisted:' . $package;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function getUnknownCounterKey(string $package): string
|
|
|
|
|
+ {
|
|
|
|
|
+ return 'google:play:unknown:' . $package;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function getDelistedNotifiedKey(string $package): string
|
|
|
|
|
+ {
|
|
|
|
|
+ return 'google:play:notified:delisted:' . $package;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function getUnknownNotifiedKey(string $package): string
|
|
|
|
|
+ {
|
|
|
|
|
+ return 'google:play:notified:unknown:' . $package;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function increaseCounter(string $key): int
|
|
|
|
|
+ {
|
|
|
|
|
+ $count = (int) Redis::incr($key);
|
|
|
|
|
+ Redis::expire($key, $this->counterExpireSeconds);
|
|
|
|
|
+
|
|
|
|
|
+ return $count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function clearCounters(string $package): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $this->clearCounter($this->getDelistedCounterKey($package));
|
|
|
|
|
+ $this->clearCounter($this->getUnknownCounterKey($package));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function clearCounter(string $key): void
|
|
|
|
|
+ {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Redis::del($key);
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ Util::WriteLog('google_play_check', 'Redis del error: ' . $e->getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function hasAlreadyNotified(string $type, string $package): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ try {
|
|
|
|
|
+ $key = $type === 'delisted'
|
|
|
|
|
+ ? $this->getDelistedNotifiedKey($package)
|
|
|
|
|
+ : $this->getUnknownNotifiedKey($package);
|
|
|
|
|
+
|
|
|
|
|
+ return (bool) Redis::exists($key);
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ Util::WriteLog('google_play_check', 'Redis exists error: ' . $e->getMessage());
|
|
|
|
|
+
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function markNotified(string $type, string $package): void
|
|
|
|
|
+ {
|
|
|
|
|
+ try {
|
|
|
|
|
+ $key = $type === 'delisted'
|
|
|
|
|
+ ? $this->getDelistedNotifiedKey($package)
|
|
|
|
|
+ : $this->getUnknownNotifiedKey($package);
|
|
|
|
|
+
|
|
|
|
|
+ Redis::setex($key, $this->counterExpireSeconds, 1);
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ Util::WriteLog('google_play_check', 'Redis setex error: ' . $e->getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function clearNotifiedFlags(string $package): void
|
|
|
|
|
+ {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Redis::del(
|
|
|
|
|
+ $this->getDelistedNotifiedKey($package),
|
|
|
|
|
+ $this->getUnknownNotifiedKey($package)
|
|
|
|
|
+ );
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ Util::WriteLog('google_play_check', 'Redis clear notified flags error: ' . $e->getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function notifyDelisted(string $env, array $items): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $lines = ["[{$env}] Google Play 下架/不可访问检测告警"];
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($items as $item) {
|
|
|
|
|
+ $lines[] = '• ' . $item['name'];
|
|
|
|
|
+ $lines[] = ' 原因: ' . $item['reason'];
|
|
|
|
|
+ $lines[] = ' 连续次数: ' . $item['count'];
|
|
|
|
|
+ $lines[] = ' 链接: ' . $item['url'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $this->sendTelegramMessage(implode("\n", $lines));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function notifyUnknown(string $env, array $items): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $lines = ["[{$env}] Google Play 检测异常告警"];
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($items as $item) {
|
|
|
|
|
+ $lines[] = '• ' . $item['name'];
|
|
|
|
|
+ $lines[] = ' 原因: ' . $item['reason'];
|
|
|
|
|
+ $lines[] = ' 连续次数: ' . $item['count'];
|
|
|
|
|
+ $lines[] = ' 链接: ' . $item['url'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $this->sendTelegramMessage(implode("\n", $lines));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected function sendTelegramMessage(string $message): void
|
|
|
|
|
+ {
|
|
|
|
|
+ try {
|
|
|
|
|
+ TelegramBot::getDefault()->sendMsgAndImportant($message);
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ Util::WriteLog('google_play_check', 'Telegram send error: ' . $e->getMessage());
|
|
|
|
|
+ $this->error('Telegram 发送失败: ' . $e->getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|