Merge pull request #2401 from HDInnovations/Refund-System

(Add) Refund System
This commit is contained in:
HDVinnie
2023-06-15 19:46:15 -04:00
committed by GitHub
21 changed files with 253 additions and 1 deletions
@@ -0,0 +1,73 @@
<?php
/**
* 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 HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Console\Commands;
use App\Models\History;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
class AutoRefundDownload extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'auto:refund_download';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Refunds Download To Users Based On Seed Time.';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$now = Carbon::now();
$MIN_SEEDTIME = config('hitrun.seedtime');
$FULL_REFUND_SEEDTIME = 12 * 30 * 24 * 60 * 60 + $MIN_SEEDTIME;
$COMMAND_RUN_PERIOD = 24 * 60 * 60; // This command is run every 24 hours
History::query()
->selectRaw('LEAST(1, history.seedtime / ?) * torrents.size - history.refunded_download as refunded_download_delta', [$FULL_REFUND_SEEDTIME])
->join('torrents', 'torrents.id', '=', 'history.torrent_id')
->join('users', 'users.id', '=', 'history.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('history.active', '=', 1)
->where('history.seeder', '=', 1)
->where('history.seedtime', '>=', $MIN_SEEDTIME)
->where('history.seedtime', '<=', $FULL_REFUND_SEEDTIME + $MIN_SEEDTIME + $COMMAND_RUN_PERIOD)
->where('history.created_at', '>=', $now->copy()->subSeconds($MIN_SEEDTIME))
->whereColumn('torrents.user_id', '!=', 'history.user_id')
->when(! config('other.refundable'), fn ($query) => $query->where(
fn ($query) => $query
->where('groups.is_refundable', '=', 1)
->orWhere('torrents.refundable', '=', 1)
))
->update([
'history.refunded_download' => DB::raw('history.refunded_download + (@delta := LEAST(1, history.seedtime / '.(int) $FULL_REFUND_SEEDTIME.') * torrents.size - history.refunded_download)'),
'users.downloaded' => DB::raw('users.downloaded - @delta'),
'history.updated_at' => DB::raw('history.updated_at'),
]);
$this->comment('Automated Download Refund Command Complete');
}
}
+1
View File
@@ -46,6 +46,7 @@ class Kernel extends ConsoleKernel
$schedule->command('auto:reset_user_flushes')->daily();
$schedule->command('auto:stats_clients')->daily();
$schedule->command('auto:remove_torrent_buffs')->hourly();
$schedule->command('auto:refund_download')->daily();
$schedule->command('auto:torrent_balance')->hourly();
//$schedule->command('auto:ban_disposable_users')->weekends();
//$schedule->command('backup:clean')->daily();
@@ -262,4 +262,35 @@ class TorrentBuffController extends Controller
return to_route('torrent', ['id' => $torrent->id])
->withErrors('You Dont Have Enough Freeleech Tokens Or Already Have One Activated On This Torrent.');
}
/**
* Set Torrents Refudable Status.
*/
public function setRefundable(Request $request, $id)
{
$user = $request->user();
abort_unless($user->group->is_modo || $user->group->is_internal, 403);
$torrent = Torrent::withAnyStatus()->findOrFail($id);
$torrent_url = href_torrent($torrent);
if ($torrent->refundable == 0) {
$torrent->refundable = 1;
$this->chatRepository->systemMessage(
sprintf('Ladies and Gents, [url=%s]%s[/url] is now refundable! Grab It While You Can! :fire:', $torrent_url, $torrent->name)
);
} else {
$torrent->refundable = 0;
$this->chatRepository->systemMessage(
sprintf('Ladies and Gents, [url=%s]%s[/url] is no longer refundable! :poop:', $torrent_url, $torrent->name)
);
}
$torrent->save();
return to_route('torrent', ['id' => $torrent->id])
->withSuccess('Torrent\'s Refundable Status Has Been Adjusted!');
}
}
@@ -515,6 +515,7 @@ class TorrentController extends Controller
$torrent->moderated_at = Carbon::now();
$torrent->moderated_by = 1; //System ID
$torrent->free = $user->group->is_modo || $user->group->is_internal ? $request->input('free') : 0;
$torrent->refundable = $user->group->is_modo || $user->group->is_internal ? $request->input('refundable') : 0;
$resolutionRule = 'nullable|exists:resolutions,id';
if ($category->movie_meta || $category->tv_meta) {
@@ -557,6 +558,7 @@ class TorrentController extends Controller
'stream' => 'required',
'sd' => 'required',
'free' => 'sometimes|between:0,100',
'refundable' => 'sometimes|bool',
]);
if ($v->fails()) {
@@ -76,6 +76,7 @@ class UserController extends Controller
->selectRaw('SUM(uploaded) as credited_upload_sum')
->selectRaw('SUM(actual_downloaded) as download_sum')
->selectRaw('SUM(downloaded) as credited_download_sum')
->selectRaw('SUM(refunded_download) as refunded_download_sum')
->selectRaw('SUM(seedtime) as seedtime_sum')
->selectRaw('SUM(actual_downloaded > 0) as download_count')
->selectRaw('COUNT(*) as count')
+10
View File
@@ -89,6 +89,16 @@ return [
*/
'doubleup' => false,
/*
|--------------------------------------------------------------------------
| Refund Torrent Download
|--------------------------------------------------------------------------
|
| Global Refund Download
|
*/
'refundable' => false,
/*
|--------------------------------------------------------------------------
| Min Ratio
+1
View File
@@ -29,6 +29,7 @@ class GroupFactory extends Factory
'is_immune' => $this->faker->boolean(),
'is_freeleech' => $this->faker->boolean(),
'is_double_upload' => $this->faker->boolean(),
'is_refundable' => $this->faker->boolean(),
'can_upload' => $this->faker->boolean(),
'is_incognito' => $this->faker->boolean(),
'autogroup' => $this->faker->boolean(),
@@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('history', function (Blueprint $table): void {
$table->unsignedBigInteger('refunded_download')
->after('downloaded')
->default(0);
});
}
};
@@ -0,0 +1,20 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('torrents', function (Blueprint $table): void {
$table->boolean('refundable')->after('doubleup')
->default(0);
});
}
};
@@ -0,0 +1,19 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('groups', function (Blueprint $table): void {
$table->boolean('is_refundable')->after('is_double_upload')->default(0);
});
}
};
+1
View File
@@ -155,6 +155,7 @@ return [
'ready' => 'This file is ready for download',
'recent-bumped' => 'Recently Bumped',
'recommendations' => 'Recommendations',
'refundable' => 'Refundable',
'rejected' => 'Rejected',
'region' => 'Region',
'released' => 'Released',
@@ -206,6 +206,19 @@
Double Upload
</label>
</p>
<p class="form__group">
<input name="is_refundable" type="hidden" value="0">
<input
id="is_refundable"
class="form__checkbox"
name="is_refundable"
type="checkbox"
value="1"
>
<label class="form__label" for="is_refundable">
Refundable Download
</label>
</p>
<p class="form__group">
<input name="is_incognito" type="hidden" value="0">
<input
@@ -223,6 +223,20 @@
Double Upload
</label>
</p>
<p class="form__group">
<input name="is_refundable" type="hidden" value="0">
<input
id="is_refundable"
class="form__checkbox"
name="is_refundable"
type="checkbox"
value="1"
@checked($group->is_refundable)
>
<label class="form__label" for="is_refundable">
Refundable Download
</label>
</p>
<p class="form__group">
<input name="is_incognito" type="hidden" value="0">
<input
@@ -46,6 +46,7 @@
<th>Immune</th>
<th>Freeleech</th>
<th>Double Upload</th>
<th>Refundable</th>
<th>Incognito</th>
<th>Upload</th>
<th>Autogroup</th>
@@ -134,6 +135,13 @@
<i class="{{ config('other.font-awesome') }} fa-times text-red"></i>
@endif
</td>
<td>
@if ($group->is_refundable)
<i class="{{ config('other.font-awesome') }} fa-check text-green"></i>
@else
<i class="{{ config('other.font-awesome') }} fa-times text-red"></i>
@endif
</td>
<td>
@if ($group->is_incognito)
<i class="{{ config('other.font-awesome') }} fa-check text-green"></i>
@@ -49,6 +49,11 @@
title="@if(config('other.doubleup')){{ __('torrent.global-double-upload') }}&NewLine;@endif&ZeroWidthSpace;@if(auth()->user()->group->is_double_upload){{ __('torrent.special-double_upload') }}&NewLine;@endif&ZeroWidthSpace;@if($torrent->doubleup > 0)100% {{ __('torrent.double-upload') }}@if($torrent->du_until !== null) (expires {{ $torrent->du_until->diffForHumans() }})@endif&ZeroWidthSpace;@endif"
></i>
@endif
@if ($torrent->refundable || auth()->user()->group->is_refundable)
<i class="{{ config('other.font-awesome') }} fa-percentage"
title='{{ __('torrent.refundable') }}'>
</i>
@endif
@if ($torrent->sticky)
<i
class="{{ config('other.font-awesome') }} fa-thumbtack torrent-icons__sticky"
@@ -46,6 +46,7 @@
<th>{{ __('torrent.completed') }}</th>
<th>{{ __('common.upload') }}</th>
<th>{{ __('common.download') }}</th>
<th>{{ __('common.refunded-download') }}</th>
<th>{{ __('common.added') }}</th>
<th>{{ __('torrent.last-update') }}</th>
<th>{{ __('torrent.completed_at') }}</th>
@@ -89,6 +90,11 @@
</span>
</span>
</td>
<td>
<span class="text-orange" title="{{ __('torrent.credited') }} {{ strtolower(__('common.download')) }}">
({{ App\Helpers\StringHelper::formatBytes($history->refunded_download, 2) }})
</span>
</td>
<td>
<time datetime="{{ $history->created_at }}" title="{{ $history->created_at }}">
{{ $history->created_at ? $history->created_at->diffForHumans() : 'N/A' }}
@@ -73,4 +73,4 @@
<li>
@include('components.partials._torrent-icons', ['personalFreeleech' => $personal_freeleech])
</li>
</ul>
</ul>
@@ -156,6 +156,27 @@
</div>
</dialog>
</li>
<li>
@if ($torrent->refundable == 0)
<form action="{{ route('refundable', ['id' => $torrent->id]) }}"
method="POST"
style="display: inline;">
@csrf
<button type="submit" class="form__button form__button--outlined">
<i class="{{ config('other.font-awesome') }} fa-repeat"></i> {{ __('torrent.refundable') }}
</button>
</form>
@else
<form action="{{ route('refundable', ['id' => $torrent->id]) }}"
method="POST"
style="display: inline;">
@csrf
<button type="submit" class="form__button form__button--outlined">
<i class="{{ config('other.font-awesome') }} fa-repeat"></i> {{ __('torrent.revoke') }} {{ __('torrent.refundable') }}
</button>
</form>
@endif
</li>
<li>
@if ($torrent->sticky == 0)
<form
@@ -493,6 +493,8 @@
<dd>{{ App\Helpers\StringHelper::formatBytes($history->download_sum ?? 0, 2) }}</dd>
<dt>{{ __('torrent.torrent') }} {{ __('common.download') }} ({{ __('torrent.credited') }})</dt>
<dd>{{ App\Helpers\StringHelper::formatBytes($history->credited_download_sum ?? 0, 2) }}</dd>
<dt>{{ __('torrent.torrent') }} {{ __('common.download') }} ({{ __('torrent.refunded') }})</dt>
<dd>{{ App\Helpers\StringHelper::formatBytes($history->refunded_download_sum ?? 0, 2) }}</dd>
<dt>{{ __('bon.bon') }} {{ __('common.upload') }}</dt>
<dd>{{ App\Helpers\StringHelper::formatBytes($boughtUpload, 2) }}</dd>
</dl>
+1
View File
@@ -209,6 +209,7 @@ Route::group(['middleware' => 'language'], function (): void {
Route::post('/{id}/torrent_feature', [App\Http\Controllers\TorrentBuffController::class, 'grantFeatured'])->name('torrent_feature');
Route::post('/{id}/torrent_revokefeature', [App\Http\Controllers\TorrentBuffController::class, 'revokeFeatured'])->name('torrent_revokefeature');
Route::post('/{id}/freeleech_token', [App\Http\Controllers\TorrentBuffController::class, 'freeleechToken'])->name('freeleech_token');
Route::post('/{id}/refundable', [App\Http\Controllers\TorrentBuffController::class, 'setRefundable'])->name('refundable');
});
// Poll System
@@ -102,6 +102,7 @@ class GroupControllerTest extends TestCase
'is_immune' => $group->is_immune,
'is_freeleech' => $group->is_freeleech,
'is_double_upload' => $group->is_double_upload,
'is_refundable' => $group->is_refundable,
'can_upload' => $group->can_upload,
'is_incognito' => $group->is_incognito,
'autogroup' => $group->autogroup,
@@ -136,6 +137,7 @@ class GroupControllerTest extends TestCase
'is_immune' => $group->is_immune,
'is_freeleech' => $group->is_freeleech,
'is_double_upload' => $group->is_double_upload,
'is_refundable' => $group->is_refundable,
'can_upload' => $group->can_upload,
'is_incognito' => $group->is_incognito,
'autogroup' => $group->autogroup,