Fortnite Website Support

  - todo bot/calendar
parent a716de87
......@@ -40,6 +40,18 @@ class Client extends Http
throw new FortniteApiNetworkException();
}
/**
* @param Account $account
* @return Stats
* @throws FortniteApiNetworkException
* @throws \Exception
* @throws \Throwable
*/
public function updateAccount(Account $account): Stats
{
return $this->getAccountRoyaleStats($account, $account->fortnite->epic_id);
}
/**
* @param string $id
* @return string
......@@ -138,6 +150,8 @@ class Client extends Http
$allowedAttributes = ['kills', 'matchesplayed', 'score', 'minutesplayed', 'lastmodified', 'top1', 'top3', 'top5',
'top6', 'top10', 'top12', 'top25'];
$oldMatches = $statModel->getMatchesSum();
foreach ($normalized[$platform] as $group => $stats) {
foreach ($stats as $key => $item) {
if (! in_array($key, $allowedAttributes)) {
......@@ -149,6 +163,13 @@ class Client extends Http
}
}
// Increment inactive-ness, if nothing changes
if ($oldMatches !== $statModel->getMatchesSum()) {
$statModel->inactiveCounter = 0;
} else {
$statModel->inactiveCounter++;
}
return $statModel->saveOrFail();
}
......
......@@ -52,6 +52,7 @@ use Onyx\User;
* @property int $squad_top25
* @property Carbon $created_at
* @property Carbon $updated_at
* @property int $inactiveCounter
* @property Account $account
* @property User $user
*/
......@@ -71,6 +72,13 @@ class Stats extends Model
*/
protected $guarded = ['id'];
public $dates = [
'solo_lastmodified',
'duo_lastmodified',
'squad_lastmodified',
'updated_at',
'created_at'
];
public static function boot()
{
......@@ -100,6 +108,18 @@ class Stats extends Model
// Public Methods
//---------------------------------------------------------------------------------
public function getLastUpdatedRelative(): string
{
$date = new Carbon($this->updated_at);
return $date->diffForHumans();
}
public function getMatchesSum(): int
{
return $this->squad_matchesplayed + $this->duo_matchesplayed + $this->solo_matchesplayed;
}
public function account()
{
return $this->belongsTo('Onyx\Account');
......
<?php
namespace PandaLove\Commands;
use Illuminate\Contracts\Bus\SelfHandling;
use Onyx\Account;
use Onyx\Fortnite\Client;
class UpdateFortniteAccount extends Command implements SelfHandling
{
private $account;
/**
* Create a new command instance.
*
* @param \Onyx\Account $account
*/
public function __construct(Account $account)
{
$this->account = $account;
}
/**
* @throws \Throwable
*/
public function handle()
{
$client = new Client();
\DB::transaction(function () use ($client) {
$client->updateAccount($this->account);
});
}
}
......@@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Onyx\Fortnite\Objects\Stats;
use Onyx\XboxLive\Enums\Console;
use PandaLove\Commands\UpdateFortniteAccount;
use PandaLove\Http\Controllers\Controller;
/**
......@@ -38,12 +39,90 @@ class ProfileController extends Controller
}
}
public function checkForUpdate($gamertag = '', $platform = Console::Xbox)
public function checkForUpdate(string $id = "")
{
if ($this->request->ajax() && !\Agent::isRobot()) {
try {
/** @var Stats $stats */
$stats = Stats::where('epic_id', $id)->firstOrFail();
// We don't care about non-panda members
if (!$stats->account->isPandaLove()) {
$this->inactiveCounter = 1;
}
// check for 10 inactive checks
if ($stats->inactiveCounter >= $this->inactiveCounter) {
return response()->json([
'updated' => false,
'frozen' => true,
'last_update' => 'This account hasn\'t had new data in awhile. - <a href="'.
\URL::action('Fortnite\ProfileController@manualUpdate', [$stats->epic_id]).'" class="ui horizontal green label no_underline">Update Manually</a>',
]);
}
if ($stats->updated_at->diffInMinutes() >= $this->refreshRateInMinutes) {
$this->dispatch(new UpdateFortniteAccount($stats->account));
return response()->json([
'updated' => true,
'frozen' => false,
'last_update' => $stats->getLastUpdatedRelative(),
]);
}
return response()->json([
'updated' => false,
'frozen' => false,
'last_update' => $stats->getLastUpdatedRelative(),
]);
} catch (ModelNotFoundException $e) {
return response()->json([
'error' => 'Gamertag not found',
]);
}
}
}
public function manualUpdate($gamertag, $platform = Console::Xbox)
public function manualUpdate(string $id = "")
{
if (\Auth::check()) {
try {
/** @var Stats $stats */
$stats = Stats::where('epic_id', $id)->firstOrFail();
$inactive = $stats->inactiveCounter;
$this->dispatch(new UpdateFortniteAccount($stats->account));
// reload account
$stats = Stats::where('epic_id', $id)->firstOrFail();
if ($stats->inactive_counter > $inactive) {
\Log::warning('[FN]'.$stats->account->gamertag.' was updated with no data ('.\Request::ip().')');
// they manually refreshed a profile with no data changes. ugh
return redirect('fortnite/profile/'.$stats->epic_id)
->with('flash_message', [
'close' => 'true',
'type' => 'yellow',
'header' => 'Uh oh',
'body' => 'No data changed! Please do not update accounts unless you know they are out of date.',
]);
} else {
return redirect('fortnite/profile/'.$stats->epic_id);
}
} catch (ModelNotFoundException $e) {
\App::abort(404);
}
} else {
return redirect('fortnite/profile/'.$id)
->with('flash_message', [
'close' => 'true',
'type' => 'yellow',
'header' => 'Uh oh',
'body' => 'You must be signed in to manually update accounts',
]);
}
}
}
<?php
namespace PandaLove\Http\Controllers\Fortnite;
use Illuminate\Http\Request;
use Onyx\Account;
use Onyx\Fortnite\Objects\Stats;
use PandaLove\Http\Controllers\Controller;
class RosterController extends Controller
{
/**
* @var \Illuminate\Http\Request
*/
private $request;
public function __construct(Request $request)
{
parent::__construct();
$this->request = $request;
}
public function getIndex()
{
$accounts = Stats::with('user', 'account')
->whereHas('user', function ($query) {
$query->where('isPanda', true);
})
->join('accounts', 'accounts.id', '=', 'fortnite_stats.account_id')
->orderBy('accounts.gamertag', 'ASC')
->paginate(10);
return view('fortnite.roster', [
'members' => $accounts,
'description' => 'PandaLove Fortnite Roster page',
'title' => 'PandaLove Fortnite Roster',
]);
}
}
......@@ -26,6 +26,9 @@ Route::get('/destiny2/profile/{console}/{gamertag}/{characterId?}', 'Destiny2\Pr
Route::controller('/xbox/api/v1', 'Xbox\ApiV1Controller');
//# Fortnite
Route::controller('/fortnite/roster', 'Fortnite\RosterController');
Route::get('/fortnite/profile/check-for-update/{id}', 'Fortnite\ProfileController@checkForUpdate');
Route::get('/fortnite/profile/manual-update/{id}', 'Fortnite\ProfileController@manualUpdate');
Route::get('/fortnite/profile/{id}', 'Fortnite\ProfileController@index');
//# Halo 5
......
......@@ -28,6 +28,7 @@ class AddFortniteTables extends Migration
$this->places($table, 'squad');
$table->timestamps();
$table->integer('inactiveCounter', false, true)->default(0);
$table->foreign('account_id')->references('id')->on('accounts');
$table->foreign('user_id')->references('id')->on('users');
});
......
......@@ -302,6 +302,21 @@
.playtime-table:before {
content: "Playtime: ";
}
.fnwins-table:before {
content: "Wins: ";
}
.fntop3-table:before {
content: "Top3: ";
}
.fnsolomatches-table:before {
content: "Solo Matches: ";
}
.fnduomatches-table:before {
content: "Duo Matches: ";
}
.fnsquadmatches-table:before {
content: "Squad Matches: ";
}
.ui.statistics.four > .statistic > .value {
font-size: 2.1rem !important;
}
......
......@@ -48,10 +48,35 @@
@section('inline-js')
<script type="text/javascript">
$('.menu .item').tab();
$(function() {
$.ajax({
url: '{{ URL::action('Fortnite\ProfileController@checkForUpdate', [$stats->epic_id]) }}',
success: function(result) {
$msg = $("#update-message");
if (result.updated && result.frozen == false) {
$msg.removeClass('icon').addClass('green');
$("#update-message .content p").empty().text("Account Updated! Refresh for new data");
} else if (result.updated == false && result.frozen == false) {
$msg.removeClass('icon').addClass('blue');
$("#update-message .content p").empty().text("Account last updated: " + result.last_update);
} else if (result.frozen) {
$msg.removeClass('icon').addClass('yellow');
$("#update-message .content p").empty().html(result.last_update);
}
$("#update-message i").remove();
$("#update-message .header").remove();
}
});
})
</script>
@append
@section('inline-css')
<style type="text/css">
.no_underline {
text-decoration: none;
}
</style>
@append
\ No newline at end of file
@extends('app')
@section('content')
<div class="wrapper style1">
<article class="container no-image" id="top">
<div class="row">
<div class="12u">
<header>
<h1>Meet <strong>Panda Love</strong></h1>
</header>
@include('includes.fortnite.roster.table')
</div>
</div>
</article>
</div>
@endsection
\ No newline at end of file
......@@ -20,7 +20,7 @@
<a href="{{ URL::action('Destiny2\RosterController@getIndex') }}" class="button big scrolly">Destiny 2</a>
<a href="{{ URL::action('Halo5\RosterController@getIndex') }}" class="button big scrolly">Halo 5</a>
<a href="{{ Url::action('Overwatch\RosterController@getIndex') }}" class="button big scrolly">Overwatch</a>
<a href="#" class="button big scrolly">Fortnite</a>
<a href="{{ Url::action('Fortnite\RosterController@getIndex') }}" class="button big scrolly">Fortnite</a>
</div>
</div>
</article>
......
<div class="ui seven statistics">
<div class="{{ $stats->{$key . '_top1'} > 0 ? 'green' : 'red' }} statistic">
<div class="value">
{{ $stats->{$key . '_top1'} }}
</div>
<div class="label">
Wins
</div>
</div>
<div class="{{ $stats->{$key . '_top3'} > 0 ? 'green' : 'red' }} statistic">
<div class="value">
{{ $stats->{$key . '_top3'} }}
</div>
<div class="label">
Top 3
</div>
</div>
<div class="{{ $stats->{$key . '_top5'} > 0 ? 'green' : 'red' }} statistic">
<div class="value">
{{ $stats->{$key . '_top5'} }}
</div>
<div class="label">
Top 5
</div>
</div>
<div class="{{ $stats->{$key . '_top6'} > 0 ? 'green' : 'red' }} statistic">
<div class="value">
{{ $stats->{$key . '_top6'} }}
</div>
<div class="label">
Top 6
</div>
</div>
<div class="{{ $stats->{$key . '_top10'} > 0 ? 'green' : 'red' }} statistic">
<div class="value">
{{ $stats->{$key . '_top10'} }}
</div>
<div class="label">
Top 10
</div>
</div>
<div class="{{ $stats->{$key . '_top12'} > 0 ? 'green' : 'red' }} statistic">
<div class="value">
{{ $stats->{$key . '_top12'} }}
</div>
<div class="label">
Top 12
</div>
</div>
<div class="{{ $stats->{$key . '_top25'} > 0 ? 'green' : 'red' }} statistic">
<div class="value">
{{ $stats->{$key . '_top25'} }}
</div>
<div class="label">
Top 25
</div>
</div>
</div>
<br />
<div class="ui three statistics">
<div class="statistic">
<div class="value">
{{ number_format($stats->{$key . '_kills'}) }}
</div>
<div class="label">
Kills
</div>
</div>
<div class="statistic">
<div class="value">
{{ number_format($stats->{$key . '_matchesplayed'}) }}
</div>
<div class="label">
Matches
</div>
</div>
<div class="statistic">
<div class="value">
{{ number_format($stats->{$key . '_score'}) }}
</div>
<div class="label">
Score
</div>
</div>
</div>
<div class="ui info message">
Time Played: <strong>{{ Onyx\Laravel\Helpers\Text::timeDuration(($stats->{$key . '_minutesplayed'} * 60), 'dhm') }}</strong>,
Last Played: <strong>{{ $stats->{$key . '_lastmodified'}->diffForHumans() }}</strong>
</div>
\ No newline at end of file
DUO
\ No newline at end of file
<div class="ui attached segment">
<div class="row">
<div class="12u">
@include('includes.fortnite.profile.stats_block', ['key' => 'duo'])
</div>
</div>
</div>
\ No newline at end of file
SOLO
\ No newline at end of file
<div class="ui attached segment">
<div class="row">
<div class="12u">
@include('includes.fortnite.profile.stats_block', ['key' => 'solo'])
</div>
</div>
</div>
\ No newline at end of file
SQUAD
\ No newline at end of file
<div class="ui attached segment">
<div class="row">
<div class="12u">
@include('includes.fortnite.profile.stats_block', ['key' => 'squad'])
</div>
</div>
</div>
\ No newline at end of file
<?php
/** @var Onyx\Fortnite\Objects\Stats $member */
?>
<table class="ui striped compact table">
<thead class="desktop only">
<tr>
<th>Gamertag</th>
<th>Solo / Duo / Squad Wins</th>
<th>Solo / Duo / Squad Top3</th>
<th>Solo Matches</th>
<th>Duo Matches</th>
<th>Squad Matches</th>
</tr>
</thead>
<tbody>
@foreach($members as $member)
<tr>
<td><a href="{{ URL::action('Fortnite\ProfileController@index', [$member->epic_id]) }}">{{ $member->account->gamertag }}</a></td>
<td class="center aligned fnwins-table">
<?= $member->solo_top1 . ' / ' . $member->duo_top1 . ' / ' . $member->squad_top1; ?>
</td>
<td class="center aligned fntop3-table">
<?= $member->solo_top3 . ' / ' . $member->duo_top3 . ' / ' . $member->squad_top3; ?>
</td>
<td class="center aligned fnsolomatches-table">
<?= $member->solo_matchesplayed; ?>
</td>
<td class="center aligned fnduomatches-table">
<?= $member->duo_matchesplayed; ?>
</td>
<td class="center aligned fnsquadmatches-table">
<?= $member->squad_matchesplayed; ?>
</td>
</tr>
@endforeach
</tbody>
</table>
{!! with(new Onyx\Laravel\SemanticPresenter($members))->render() !!}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment