update: improve cheated torrent algorithm

This commit is contained in:
Roardom
2025-05-28 12:13:59 +00:00
parent 1672d70d44
commit 596019ff53
4 changed files with 85 additions and 22 deletions

View File

@@ -38,27 +38,39 @@ class CheatedTorrentController extends Controller
'torrents.size',
'torrents.balance',
'torrents.balance_offset',
'torrents.balance_reset_at',
'torrents.created_at',
])
->selectRaw('MAX(history.completed_at) as last_completed')
->selectRaw('MAX(history.created_at) as last_started')
->selectRaw('balance + COALESCE(balance_offset, 0) AS current_balance')
->selectRaw('(CAST((balance + COALESCE(balance_offset, 0)) AS float) / CAST((size + 1) AS float)) AS times_cheated')
->join('history', 'history.torrent_id', '=', 'torrents.id')
->groupBy([
'torrents.id',
'torrents.name',
'torrents.seeders',
'torrents.leechers',
'torrents.times_completed',
'torrents.size',
'torrents.balance',
'torrents.balance_offset',
'torrents.created_at',
])
->selectRaw('balance + balance_offset AS current_balance')
->selectRaw('(balance + balance_offset) / GREATEST(size, 1) AS times_cheated')
// Exclude torrents that have active data transfer when the current balance (or its offset) is being calculated.
// The balance is inaccurate during these times since seeds only report upload data once per announce interval (max 1 hour).
// Balances are only accurate once it's been at least 1 announce interval since the last leech completed the torrent.
->whereDoesntHave(
'history',
fn ($query) => $query
// Exclude torrents where the reporting period overlapped with the balance reset, but eventually completed
->where(
fn ($query) => $query
->whereNotNull('balance_reset_at')
->whereColumn('balance_reset_at', '>', 'history.created_at')
->whereColumn(DB::raw('DATE_SUB(balance_reset_at, INTERVAL 1 HOUR)'), '<', 'history.completed_at')
->whereNotNull('completed_at')
)
// Exclude torrents where the reporting period overlapped with both the balance reset and the current balance calculation
->orWhere(
fn ($query) => $query
->whereNotNull('balance_reset_at')
->whereColumn('balance_reset_at', '>', 'history.created_at')
->whereNull('completed_at')
->where('active', '=', true)
->where('seeder', '=', false)
)
// Exclude torrents where the reporting period overlapped with right now
->orWhereColumn(DB::raw('DATE_SUB(NOW(), INTERVAL 1 HOUR)'), '<', 'created_at')
->orWhereColumn(DB::raw('DATE_SUB(NOW(), INTERVAL 1 HOUR)'), '<', 'completed_at')
)
->having('current_balance', '<>', 0)
->having('last_completed', '<', now()->subHours(2))
->having('last_started', '<', now()->subHours(2))
->orderByDesc('times_cheated')
->paginate(25),
]);
@@ -70,7 +82,8 @@ class CheatedTorrentController extends Controller
public function destroy(Torrent $cheatedTorrent): \Illuminate\Http\RedirectResponse
{
$cheatedTorrent->update([
'balance_offset' => DB::raw('balance * -1'),
'balance_offset' => DB::raw('balance * -1'),
'balance_reset_at' => now(),
]);
return to_route('staff.cheated_torrents.index')
@@ -83,7 +96,8 @@ class CheatedTorrentController extends Controller
public function massDestroy(): \Illuminate\Http\RedirectResponse
{
Torrent::query()->update([
'balance_offset' => DB::raw('balance * -1'),
'balance_offset' => DB::raw('balance * -1'),
'balance_reset_at' => now(),
]);
return to_route('staff.cheated_torrents.index')

View File

@@ -77,8 +77,9 @@ use Laravel\Scout\Searchable;
* @property int|null $distributor_id
* @property int|null $region_id
* @property bool $personal_release
* @property int|null $balance
* @property int|null $balance_offset
* @property int $balance
* @property int $balance_offset
* @property int|null $balance_reset_at
*/
class Torrent extends Model
{

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
DB::table('torrents')->whereNull('balance')->update([
'balance' => 0,
]);
DB::table('torrents')->whereNull('balance_offset')->update([
'balance_offset' => 0,
]);
Schema::table('torrents', function (Blueprint $table): void {
$table->bigInteger('balance')->default(0)->change();
$table->bigInteger('balance_offset')->default(0)->change();
$table->timestamp('balance_reset_at')->after('balance_offset')->nullable();
});
}
};

View File

@@ -390,6 +390,12 @@ parameters:
count: 1
path: app/Http/Controllers/Staff/ChatRoomController.php
-
message: '#^Anonymous function should return Illuminate\\Database\\Eloquent\\Builder\<Illuminate\\Database\\Eloquent\\Model\> but returns Illuminate\\Database\\Eloquent\\Builder\<App\\Models\\History\>\.$#'
identifier: return.type
count: 1
path: app/Http/Controllers/Staff/CheatedTorrentController.php
-
message: '#^Binary operation "\+\=" between string and int results in an error\.$#'
identifier: assignOp.invalid