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\Models\Chat;
7
8use App\Models\Traits\Reportable;
9use App\Models\Traits\ReportableInterface;
10use App\Models\User;
11use Carbon\Carbon;
12use Carbon\CarbonImmutable;
13use Illuminate\Support\Collection;
14
15/**
16 * @property Channel $channel
17 * @property int $channel_id
18 * @property string $content
19 * @property bool $is_action
20 * @property int $message_id
21 * @property User $sender
22 * @property \Carbon\Carbon $timestamp
23 * @property int $user_id
24 */
25class Message extends Model implements ReportableInterface
26{
27 use Reportable;
28
29 public static function filterBacklogs(Channel $channel, Collection $messages): Collection
30 {
31 if (!$channel->isPublic()) {
32 return $messages;
33 }
34
35 $minTimestamp = json_time(Carbon::now()->subHours($GLOBALS['cfg']['osu']['chat']['public_backlog_limit']));
36 $ret = [];
37
38 foreach ($messages as $message) {
39 if ($message->timestamp_json > $minTimestamp) {
40 $ret[] = $message;
41 }
42 }
43
44 return collect($ret);
45 }
46
47 public ?string $uuid = null;
48
49 protected $primaryKey = 'message_id';
50 protected $casts = [
51 'is_action' => 'boolean',
52 'timestamp' => 'datetime',
53 ];
54
55 public function channel()
56 {
57 return $this->belongsTo(Channel::class, 'channel_id');
58 }
59
60 public function sender()
61 {
62 return $this->belongsTo(User::class, 'user_id');
63 }
64
65 public function scopeSince($query, $messageId)
66 {
67 return $query->where('message_id', '>', $messageId);
68 }
69
70 public function getAttribute($key)
71 {
72 return match ($key) {
73 'channel_id',
74 'content',
75 'message_id',
76 'user_id' => $this->getRawAttribute($key),
77
78 'is_action' => (bool) $this->getRawAttribute($key),
79
80 'timestamp' => $this->getTimeFast($key),
81
82 'timestamp_json' => $this->getJsonTimeFast($key),
83
84 'channel',
85 'reportedIn',
86 'sender' => $this->getRelationValue($key),
87 };
88 }
89
90 public function reportableAdditionalInfo(): ?string
91 {
92 $history = static
93 ::where('message_id', '<=', $this->getKey())
94 ->whereHas('channel', fn ($ch) => $ch->where('type', '<>', Channel::TYPES['pm']))
95 ->where('user_id', $this->user_id)
96 ->where('timestamp', '>', CarbonImmutable::now()->subDays(1))
97 ->orderBy('timestamp', 'DESC')
98 ->with('channel')
99 ->limit(5)
100 ->get()
101 ->map(fn ($m) => "**<t:{$m->timestamp->timestamp}:R> {$m->channel->name}:**\n{$m->content}\n")
102 ->reverse()
103 ->join("\n");
104
105 $channel = $this->channel;
106 $header = 'Reported in: '.($channel->isPM() ? 'pm' : '**'.$channel->name.'** ('.strtolower($channel->type).')');
107
108 return "{$header}\n\n{$history}";
109 }
110
111 public function trashed(): bool
112 {
113 return false;
114 }
115
116 protected function newReportableExtraParams(): array
117 {
118 return [
119 'reason' => 'Spam',
120 'user_id' => $this->user_id,
121 ];
122 }
123}