CheckIosAppStore.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. ];
  19. /** 页面内容中表示“已下架”的关键词(不区分大小写) */
  20. protected $delistedKeywords = [
  21. 'no longer available',
  22. 'removed this app from the app store',
  23. 'not available',
  24. 'this app is no longer',
  25. 'has been removed',
  26. ];
  27. public function handle()
  28. {
  29. $env = env('APP_ENV', 'local');
  30. $delisted = [];
  31. foreach ($this->apps as $name => $url) {
  32. $result = $this->checkApp($url);
  33. if ($result['delisted']) {
  34. $delisted[] = [
  35. 'name' => $name,
  36. 'url' => $url,
  37. 'reason' => $result['reason'],
  38. ];
  39. Util::WriteLog('ios_app_store_check', "Delisted: {$name} | {$url} | {$result['reason']}");
  40. }
  41. }
  42. if (count($delisted) > 0) {
  43. $this->notifyDelisted($env, $delisted);
  44. $this->warn('已检测到 ' . count($delisted) . ' 个包被下架,已发送 Telegram 通知。');
  45. return 1;
  46. }
  47. $this->info('所有 iOS 包状态正常。');
  48. return 0;
  49. }
  50. /**
  51. * 检测单个 App 是否可访问(未下架)
  52. * @return array ['delisted' => bool, 'reason' => string]
  53. */
  54. protected function checkApp($url)
  55. {
  56. $ch = curl_init($url);
  57. curl_setopt_array($ch, [
  58. CURLOPT_RETURNTRANSFER => true,
  59. CURLOPT_FOLLOWLOCATION => true,
  60. CURLOPT_TIMEOUT => 15,
  61. 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',
  62. CURLOPT_SSL_VERIFYPEER => true,
  63. ]);
  64. $body = curl_exec($ch);
  65. $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
  66. $error = curl_error($ch);
  67. curl_close($ch);
  68. if ($error) {
  69. return ['delisted' => true, 'reason' => "请求失败: {$error}"];
  70. }
  71. if ($code === 404) {
  72. return ['delisted' => true, 'reason' => 'HTTP 404'];
  73. }
  74. if ($code !== 200) {
  75. return ['delisted' => true, 'reason' => "HTTP {$code}"];
  76. }
  77. $bodyLower = mb_strtolower($body);
  78. foreach ($this->delistedKeywords as $keyword) {
  79. if (strpos($bodyLower, $keyword) !== false) {
  80. return ['delisted' => true, 'reason' => "页面包含下架提示: {$keyword}"];
  81. }
  82. }
  83. return ['delisted' => false, 'reason' => ''];
  84. }
  85. protected function notifyDelisted($env, array $delisted)
  86. {
  87. $lines = ["[{$env}] iOS App Store 下架检测告警"];
  88. foreach ($delisted as $item) {
  89. $lines[] = "• {$item['name']}";
  90. $lines[] = " 原因: {$item['reason']}";
  91. $lines[] = " 链接: {$item['url']}";
  92. }
  93. $message = implode("\n", $lines);
  94. try {
  95. TelegramBot::getDefault()->sendMsg($message);
  96. } catch (\Exception $e) {
  97. Util::WriteLog('ios_app_store_check', 'Telegram send error: ' . $e->getMessage());
  98. $this->error('Telegram 发送失败: ' . $e->getMessage());
  99. }
  100. }
  101. }