CheckIosAppStore.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Notification\TelegramBot;
  4. use App\Util;
  5. use Illuminate\Console\Command;
  6. /**
  7. * 检测配置的 iOS App Store 包是否被下架,若下架则通过 Telegram 通知。
  8. */
  9. class CheckIosAppStore extends Command
  10. {
  11. protected $signature = 'ios:check-app-store';
  12. protected $description = '检测 iOS App Store 包是否被下架,下架时 Telegram 通知';
  13. /** 要检测的 iOS 包列表:name => url */
  14. protected $apps = [
  15. 'Slots Magnet Revolution' => 'https://apps.apple.com/us/app/slots-magnet-revolution/id6759797102',
  16. 'Viking Mysteric Slots' => 'https://apps.apple.com/us/app/viking-mysteric-slots/id6759851510',
  17. 'Slots Chemistry Pirate' => 'https://apps.apple.com/us/app/slots-chemistry-pirate/id6759852588',
  18. // 'ceshi' => 'https://apps.apple.com/us/app/pixiepineslotsgrid/id6758513278'
  19. ];
  20. /** 页面内容中表示“已下架”的关键词(不区分大小写) */
  21. protected $delistedKeywords = [
  22. 'no longer available',
  23. 'removed this app from the app store',
  24. 'not available',
  25. 'this app is no longer',
  26. 'has been removed',
  27. ];
  28. public function handle()
  29. {
  30. $env = env('APP_ENV', 'local');
  31. $delisted = [];
  32. foreach ($this->apps as $name => $url) {
  33. $result = $this->checkApp($url);
  34. if ($result['delisted']) {
  35. $delisted[] = [
  36. 'name' => $name,
  37. 'url' => $url,
  38. 'reason' => $result['reason'],
  39. ];
  40. Util::WriteLog('ios_app_store_check', "Delisted: {$name} | {$url} | {$result['reason']}");
  41. }
  42. }
  43. if (count($delisted) > 0) {
  44. $this->notifyDelisted($env, $delisted);
  45. $this->warn('已检测到 ' . count($delisted) . ' 个包被下架,已发送 Telegram 通知。');
  46. return 1;
  47. }
  48. $this->info('所有 iOS 包状态正常。');
  49. return 0;
  50. }
  51. /**
  52. * 检测单个 App 是否可访问(未下架)
  53. * @return array ['delisted' => bool, 'reason' => string]
  54. */
  55. protected function checkApp($url)
  56. {
  57. $ch = curl_init($url);
  58. curl_setopt_array($ch, [
  59. CURLOPT_RETURNTRANSFER => true,
  60. CURLOPT_FOLLOWLOCATION => true,
  61. CURLOPT_TIMEOUT => 15,
  62. CURLOPT_USERAGENT => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  63. CURLOPT_SSL_VERIFYPEER => true,
  64. ]);
  65. $body = curl_exec($ch);
  66. $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
  67. $error = curl_error($ch);
  68. curl_close($ch);
  69. if ($error) {
  70. return ['delisted' => true, 'reason' => "请求失败: {$error}"];
  71. }
  72. if ($code === 404) {
  73. return ['delisted' => true, 'reason' => 'HTTP 404'];
  74. }
  75. if ($code !== 200) {
  76. return ['delisted' => true, 'reason' => "HTTP {$code}"];
  77. }
  78. $bodyLower = mb_strtolower($body);
  79. foreach ($this->delistedKeywords as $keyword) {
  80. if (strpos($bodyLower, $keyword) !== false) {
  81. return ['delisted' => true, 'reason' => "页面包含下架提示: {$keyword}"];
  82. }
  83. }
  84. return ['delisted' => false, 'reason' => ''];
  85. }
  86. protected function notifyDelisted($env, array $delisted)
  87. {
  88. $lines = ["[{$env}] iOS App Store 下架检测告警"];
  89. foreach ($delisted as $item) {
  90. $lines[] = "• {$item['name']}";
  91. $lines[] = " 原因: {$item['reason']}";
  92. $lines[] = " 链接: {$item['url']}";
  93. }
  94. $message = implode("\n", $lines);
  95. try {
  96. TelegramBot::getDefault()->sendMsg($message);
  97. } catch (\Exception $e) {
  98. Util::WriteLog('ios_app_store_check', 'Telegram send error: ' . $e->getMessage());
  99. $this->error('Telegram 发送失败: ' . $e->getMessage());
  100. }
  101. }
  102. }