1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Console\Commands;
7
8use App\Enums\Ruleset;
9use App\Models\Beatmapset;
10use Illuminate\Console\Command;
11
12class ModdingRankCommand extends Command
13{
14 /**
15 * The console command name.
16 *
17 * @var string
18 */
19 protected $signature = 'modding:rank {--no-wait} {--count-only}';
20
21 /**
22 * The console command description.
23 *
24 * @var string
25 */
26 protected $description = 'Rank maps in queue.';
27
28 private bool $countOnly = false;
29 private bool $noWait = false;
30
31 public static function getStats(Ruleset $ruleset)
32 {
33 $rankedTodayCount = Beatmapset::ranked()
34 ->withoutTrashed()
35 ->withModesForRanking($ruleset->value)
36 ->where('approved_date', '>=', now()->subDays())
37 ->count();
38
39 return [
40 'availableQuota' => $GLOBALS['cfg']['osu']['beatmapset']['rank_per_day'] - $rankedTodayCount,
41 'inQueue' => Beatmapset::toBeRanked($ruleset)->count(),
42 'rankedToday' => $rankedTodayCount,
43 ];
44 }
45
46 /**
47 * Execute the console command.
48 *
49 * @return mixed
50 */
51 public function handle()
52 {
53 $this->countOnly = get_bool($this->option('count-only'));
54 $this->noWait = get_bool($this->option('no-wait'));
55
56 if ($this->countOnly) {
57 $this->info('Number of beatmapsets in queue:');
58 } else {
59 $this->info('Ranking beatmapsets...');
60 }
61
62 $rulesets = Ruleset::cases();
63
64 shuffle($rulesets);
65
66 foreach ($rulesets as $ruleset) {
67 $this->waitRandom();
68
69 if ($this->countOnly) {
70 $stats = static::getStats($ruleset);
71 $this->info($ruleset->name);
72 foreach ($stats as $key => $value) {
73 $this->line("{$key}: {$value}");
74 }
75 $this->newLine();
76 } else {
77 $this->rankAll($ruleset);
78 }
79 }
80
81 $this->info('Done');
82 }
83
84 private function rankAll(Ruleset $ruleset)
85 {
86 $this->info("Ranking beatmapsets with at least mode: {$ruleset->name}");
87 $stats = static::getStats($ruleset);
88
89 $this->info("{$stats['rankedToday']} beatmapsets ranked last 24 hours. Can rank {$stats['availableQuota']} more");
90
91 if ($stats['availableQuota'] <= 0) {
92 return;
93 }
94
95 $toRankLimit = min($GLOBALS['cfg']['osu']['beatmapset']['rank_per_run'], $stats['availableQuota']);
96
97 $toBeRanked = Beatmapset::tobeRanked($ruleset)
98 ->orderBy('queued_at', 'ASC')
99 ->limit($toRankLimit)
100 ->get();
101
102 $this->info("{$stats['inQueue']} beatmapset(s) in ranking queue");
103 $this->info("Ranking {$toBeRanked->count()} beatmapset(s)");
104
105 foreach ($toBeRanked as $beatmapset) {
106 $this->waitRandom();
107 $this->info("Ranking beatmapset: {$beatmapset->getKey()}");
108 $beatmapset->rank();
109 }
110 }
111
112 private function waitRandom()
113 {
114 if ($this->noWait || $this->countOnly) {
115 return;
116 }
117
118 $delay = rand(5, 120);
119 $this->info("Pausing for {$delay} seconds...");
120 sleep($delay);
121 }
122}