Added feedback endpoint for Player Reports and saving them in the Backend.

This commit is contained in:
Vari 2024-07-28 06:32:47 +02:00
parent 0d44b991ed
commit 6fce73e3f2
10 changed files with 282 additions and 1 deletions

View File

@ -0,0 +1,44 @@
meta {
name: Report Player Feedback
type: http
seq: 3
}
post {
url: {{base_url}}/api/v1/feedback
body: json
auth: none
}
body:json {
{
"type": "PlayerReport",
"entityId": "9b70b902-1a13-474b-b5d3-4de3d07ad971",
"platformId": "PC",
"reason": "HackingExploiting",
"details": "Er spielt die Olle Granny mit dem Cookie Launcher. Dieser Pleb Noob",
"gameSpecificData": {
"matchId": "9c9fcd07-9d6a-4cdd-bc8c-db28911e70d8",
"playerInfoList": [
{
"playerId": "9b70b902-1a13-474b-b5d3-4de3d07ad971",
"characterState": "InArena",
"faction": "Hunter",
"totalXpEarned": 0,
"playtimeInSec": 34.59732437133789,
"isReportedPlayer": true,
"isReporterPlayer": false
},
{
"playerId": "9b70b8bd-0c86-4e77-bbc7-b8084e3372e0",
"characterState": "InArena",
"faction": "Runner",
"totalXpEarned": 0,
"playtimeInSec": 34.59732437133789,
"isReportedPlayer": false,
"isReporterPlayer": true
}
]
}
}
}

13
dist/app/Enums/Game/CharacterState.php vendored Normal file
View File

@ -0,0 +1,13 @@
<?php
namespace App\Enums\Game;
enum CharacterState: string
{
case None = 'None';
case InArena = 'InArena';
case Dead = 'Dead';
case Escaped = 'Escaped';
case Quitter = 'Quitter';
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Enums\Game;
enum PlayerReportReason: string
{
case Other = 'other';
case DevImpersonation = 'PlayerDevImpersonation';
case InappropriateName = 'InappropriateNameLanguage';
case hackingExploiting = 'HackingExploiting';
case Griefing = 'GriefingToxicBehavior';
case LeavingGameHarrasment = 'LeavingGameAFKHarassment';
}

View File

@ -4,9 +4,11 @@
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\Moderation\CheckChatMessageRequest;
use App\Http\Requests\Api\Moderation\ReportPlayerRequest;
use App\Http\Requests\Api\Player\CheckUsernameRequest;
use App\Http\Responses\Api\Player\CheckUsernameResponse;
use App\Models\Admin\BadChatMessage;
use App\Models\Game\Moderation\PlayerReport;
use App\Models\User\User;
use ConsoleTVs\Profanity\Builder;
use Illuminate\Support\Facades\Auth;
@ -42,4 +44,21 @@ public function logBadMessage(User $hostUser, User $messageUser, string $message
$badMessage->message = $message;
$badMessage->save();
}
public function playerReport(ReportPlayerRequest $request) {
$reportingUser = Auth::user();
$report = new PlayerReport();
$report->reason = $request->reason;
$report->details = $request->details;
$report->match_id = $request->matchId;
$report->player_infos = $request->playerInfos->toArray();
$report->reportingUser()->associate($reportingUser);
$report->reportedUser()->associate($request->reportedPlayer);
$report->save();
return 'OK';
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests\Api\Moderation;
use App\Enums\Game\CharacterState;
use App\Enums\Game\Faction;
class ReportPlayerInfoListEntry
{
public function __construct(
public string $playerId,
public CharacterState $characterState,
public Faction $faction,
public int $totalXpEarned,
public float $playtimeInSeconds,
public bool $isReportedPlayer,
public bool $isReportingPlayer,
)
{}
public static function makeFromArray(array $array)
{
return new ReportPlayerInfoListEntry(
$array['playerId'],
CharacterState::tryFrom($array['characterState']),
Faction::tryFrom($array['faction']),
$array['totalXpEarned'],
$array['playtimeInSec'],
$array['isReportedPlayer'],
$array['isReporterPlayer']);
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace App\Http\Requests\Api\Moderation;
use App\Enums\Game\CharacterState;
use App\Enums\Game\Faction;
use App\Enums\Game\PlayerReportReason;
use App\Models\User\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Collection;
use Illuminate\Validation\Rule;
class ReportPlayerRequest extends FormRequest
{
public string $type;
public User $reportedPlayer;
public string $platform;
public PlayerReportReason $reason;
public string $details;
public string $matchId;
/** @var Collection<ReportPlayerInfoListEntry> */
public Collection $playerInfos;
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return \Auth::check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'type' => 'required|string',
'entityId' => 'required|string|exists:'.User::class.',id',
'platformId' => 'required|string',
'reason' => ['required', Rule::enum(PlayerReportReason::class)],
'details' => 'required|string',
'gameSpecificData.matchId' => 'required|string',
'gameSpecificData.playerInfoList.*.playerId' => 'required|string',
'gameSpecificData.playerInfoList.*.characterState' => ['required', Rule::enum(CharacterState::class)],
'gameSpecificData.playerInfoList.*.faction' => ['required', Rule::enum(Faction::class)],
'gameSpecificData.playerInfoList.*.totalXpEarned' => 'required|int',
'gameSpecificData.playerInfoList.*.playtimeInSec' => 'required|numeric',
'gameSpecificData.playerInfoList.*.isReportedPlayer' => 'required|bool',
'gameSpecificData.playerInfoList.*.isReporterPlayer' => 'required|bool',
];
}
protected function passedValidation()
{
$this->type = $this->input('type');
$this->reportedPlayer = User::find($this->input('entityId'));
$this->platformId = $this->input('platformId');
$this->reason = PlayerReportReason::tryFrom($this->input('reason'));
$this->details = $this->input('details');
$this->matchId = $this->input('gameSpecificData.matchId');
$playerInfos = $this->input('gameSpecificData.playerInfoList');
$this->playerInfos = collect();
foreach ($playerInfos as $playerInfo) {
$this->playerInfos->add(ReportPlayerInfoListEntry::makeFromArray($playerInfo));
}
}
}

View File

@ -75,7 +75,6 @@ public function getClaimables(): array {
return $result;
}
public function setClaimables(array $claimables): void {
$newClaimables = [];

View File

@ -0,0 +1,39 @@
<?php
namespace App\Models\Game\Moderation;
use App\Http\Requests\Api\Moderation\ReportPlayerInfoListEntry;
use App\Models\User\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Collection;
/**
* @mixin IdeHelperPlayerReport
*/
class PlayerReport extends Model
{
protected $casts = [
'player_infos' => 'array',
];
public function reportedUser(): BelongsTo
{
return $this->belongsTo(User::class, 'reported_user_id');
}
public function reportingUser(): BelongsTo
{
return $this->belongsTo(User::class, 'reporting_user_id');
}
public function playerInfos(): Collection {
$collection = collect();
foreach ($this->player_infos as $playerInfo) {
$collection->add(ReportPlayerInfoListEntry::makeFromArray($playerInfo));
}
return $collection;
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('player_reports', function (Blueprint $table) {
$table->id();
$table->foreignUuid('reported_user_id')->constrained('users');
$table->foreignUuid('reporting_user_id')->constrained('users');
$table->string('reason');
$table->text('details');
$table->uuid('match_id');
$table->json('player_infos');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('player_reports');
}
};

View File

@ -3,6 +3,7 @@
use App\Http\Controllers\Api\Auth\SteamAuthController;
use App\Http\Controllers\Api\Eula\EulaConsentController;
use App\Http\Controllers\Api\Matchmaking\MatchmakingController;
use App\Http\Controllers\Api\ModerationController;
use App\Http\Controllers\Api\Player\ChallengeController;
use App\Http\Controllers\Api\Player\CurrencyController;
use App\Http\Controllers\Api\Player\InboxController;
@ -81,6 +82,8 @@
Route::put('match/{matchId}/Kill', [MatchmakingController::class, 'kill']);
Route::put('match/{matchId}/Quit', [MatchmakingController::class, 'quit']);
Route::delete('match/{matchId}/user/{userId}', [MatchmakingController::class, 'deleteUserFromMatch']);
Route::post('feedback', [ModerationController::class, 'playerReport']);
});