Files
nexusphp/app/Repositories/HitAndRunRepository.php

507 lines
20 KiB
PHP
Raw Normal View History

2021-06-21 02:01:26 +08:00
<?php
namespace App\Repositories;
use App\Models\HitAndRun;
use App\Models\Message;
2022-10-01 00:11:22 +08:00
use App\Models\SearchBox;
2021-06-21 02:01:26 +08:00
use App\Models\Setting;
use App\Models\User;
use App\Models\UserBanLog;
use Carbon\Carbon;
2022-10-01 00:11:22 +08:00
use Elasticsearch\Endpoints\Search;
2022-04-17 16:38:44 +08:00
use Illuminate\Database\Eloquent\Builder;
2022-05-13 03:12:38 +08:00
use Illuminate\Support\Arr;
2021-06-21 02:01:26 +08:00
use Illuminate\Support\Facades\DB;
2022-10-01 00:11:22 +08:00
use Nexus\Database\NexusDB;
2021-06-21 02:01:26 +08:00
class HitAndRunRepository extends BaseRepository
{
2022-04-17 16:38:44 +08:00
public function getList(array $params)
{
$query = HitAndRun::query()->with(['user', 'torrent', 'snatch']);
if (!empty($params['status'])) {
$query->where('status', $params['status']);
}
if (!empty($params['uid'])) {
$query->where('uid', $params['uid']);
}
if (!empty($params['torrent_id'])) {
$query->where('torrent_id', $params['torrent_id']);
}
if (!empty($params['username'])) {
$query->whereHas('user', function (Builder $query) use ($params) {
return $query->where('username', $params['username']);
});
}
$query->orderBy('id', 'desc');
return $query->paginate();
}
public function store(array $params)
{
$model = HitAndRun::query()->create($params);
return $model;
}
public function update(array $params, $id)
{
$model = HitAndRun::query()->findOrFail($id);
$model->update($params);
return $model;
}
public function getDetail($id)
{
$model = HitAndRun::query()->with(['user', 'torrent', 'snatch'])->findOrFail($id);
return $model;
}
public function delete($id)
{
$model = HitAndRun::query()->findOrFail($id);
$result = $model->delete();
return $result;
}
2021-06-21 02:01:26 +08:00
2022-05-13 03:12:38 +08:00
public function bulkDelete(array $params, User $user)
{
$result = $this->getBulkQuery($params)->delete();
do_log(sprintf(
'user: %s bulk delete by filter: %s, result: %s',
$user->id, json_encode($params), json_encode($result)
), 'alert');
return $result;
}
private function getBulkQuery(array $params): Builder
{
$query = HitAndRun::query();
$hasWhere = false;
$validFilter = ['uid', 'id'];
foreach ($validFilter as $item) {
if (!empty($params[$item])) {
$hasWhere = true;
$query->whereIn($item, Arr::wrap($params[$item]));
}
}
if (!$hasWhere) {
throw new \InvalidArgumentException("No filter.");
}
return $query;
}
2022-10-01 00:11:22 +08:00
public function cronjobUpdateStatus($uid = null, $torrentId = null, $ignoreTime = false)
2021-06-21 02:01:26 +08:00
{
2022-10-01 18:26:18 +08:00
$diffInSection = HitAndRun::diffInSection();
2022-10-01 00:11:22 +08:00
$browseMode = Setting::get('main.browsecat');
2022-10-01 18:26:18 +08:00
$setting = HitAndRun::getConfig('*', $browseMode);
if ($setting['mode'] != HitAndRun::MODE_DISABLED) {
2022-10-01 18:26:18 +08:00
$setting['diff_in_section'] = $diffInSection;
$setting['search_box_id'] = $browseMode;
2022-10-01 18:26:18 +08:00
$this->doCronjobUpdateStatus($setting, $uid, $torrentId, $ignoreTime);
$this->checkAndDisableUser($setting);
2022-10-01 00:11:22 +08:00
}
$specialMode = Setting::get('main.specialcat');
if ($diffInSection && $browseMode != $specialMode) {
$setting = HitAndRun::getConfig('*', $specialMode);
if ($setting['mode'] != HitAndRun::MODE_DISABLED) {
$setting['diff_in_section'] = $diffInSection;
$setting['search_box_id'] = $specialMode;
$this->doCronjobUpdateStatus($setting, $uid, $torrentId, $ignoreTime);
$this->checkAndDisableUser($setting);
}
}
2022-10-01 00:11:22 +08:00
}
2022-10-01 18:26:18 +08:00
private function doCronjobUpdateStatus(array $setting, $uid = null, $torrentId = null, $ignoreTime = false)
2022-10-01 00:11:22 +08:00
{
2022-10-27 20:31:02 +08:00
do_log("setting: " . json_encode($setting) . ", uid: $uid, torrentId: $torrentId, ignoreTime: " . var_export($ignoreTime, true));
2021-06-21 02:01:26 +08:00
$size = 1000;
$page = 1;
if (empty($setting['mode'])) {
do_log("H&R not set.");
return false;
}
if ($setting['mode'] == HitAndRun::MODE_DISABLED) {
do_log("H&R mode is disabled.");
return false;
}
2021-06-22 12:56:03 +08:00
if (empty($setting['inspect_time'])) {
do_log("H&R inspect_time is not set.");
return false;
}
2021-06-21 02:01:26 +08:00
$query = HitAndRun::query()
->where('status', HitAndRun::STATUS_INSPECTING)
->with([
2022-10-01 00:11:22 +08:00
'torrent' => function ($query) {$query->select(['id', 'size', 'name', 'category']);},
2021-06-21 02:01:26 +08:00
'snatch',
'user' => function ($query) {$query->select(['id', 'username', 'lang', 'class', 'donoruntil', 'enabled', 'notifs']);},
2021-06-21 02:01:26 +08:00
'user.language',
]);
if (!is_null($uid)) {
$query->where('uid', $uid);
}
if (!is_null($torrentId)) {
$query->where('torrent_id', $torrentId);
}
2022-03-28 19:52:10 +08:00
if (!$ignoreTime) {
$query->where('created_at', '<', Carbon::now()->subHours($setting['inspect_time']));
}
2022-10-01 18:26:18 +08:00
if ($setting['diff_in_section']) {
$query->whereHas('torrent.basic_category', function (Builder $query) use ($setting) {
return $query->where('mode', $setting['search_box_id']);
});
}
2021-06-21 02:01:26 +08:00
$successCounts = 0;
2022-05-01 21:15:00 +08:00
$disabledUsers = [];
2021-06-21 02:01:26 +08:00
while (true) {
$logPrefix = "page: $page, size: $size";
$rows = $query->forPage($page, $size)->get();
do_log("$logPrefix, counts: " . $rows->count());
if ($rows->isEmpty()) {
do_log("$logPrefix, no more data..." . last_query());
break;
}
foreach ($rows as $row) {
2022-03-28 19:52:10 +08:00
$currentLog = "$logPrefix, [HANDLING] " . $row->toJson();
2021-06-21 02:01:26 +08:00
do_log($logPrefix);
if (!$row->user) {
do_log("$currentLog, user not exists, remove it!", 'error');
$row->delete();
2021-06-21 02:01:26 +08:00
continue;
}
if (!$row->snatch) {
2022-03-28 19:52:10 +08:00
do_log("$currentLog, snatch not exists, skip!", 'error');
2021-06-21 02:01:26 +08:00
continue;
}
if (!$row->torrent) {
do_log("$currentLog, torrent not exists, remove it!", 'error');
$row->delete();
2021-06-21 02:01:26 +08:00
continue;
}
2022-03-28 19:52:10 +08:00
//If is VIP or above OR donated, pass
if ($row->user->class >= HitAndRun::MINIMUM_IGNORE_USER_CLASS || $row->user->isDonating()) {
2022-02-23 21:17:25 +08:00
$result = $this->reachedBySpecialUserClass($row);
if ($result) {
$successCounts++;
}
continue;
}
2021-06-21 02:01:26 +08:00
//check seed time
$targetSeedTime = $row->snatch->seedtime;
$requireSeedTime = bcmul($setting['seed_time_minimum'], 3600);
2022-03-28 19:52:10 +08:00
do_log("$currentLog, targetSeedTime: $targetSeedTime, requireSeedTime: $requireSeedTime");
2021-06-21 02:01:26 +08:00
if ($targetSeedTime >= $requireSeedTime) {
2022-10-01 18:26:18 +08:00
$result = $this->reachedBySeedTime($row, $setting);
2021-06-21 02:01:26 +08:00
if ($result) {
$successCounts++;
}
continue;
}
//check share ratio
$targetShareRatio = bcdiv($row->snatch->uploaded, $row->torrent->size, 4);
$requireShareRatio = $setting['ignore_when_ratio_reach'];
2022-03-28 19:52:10 +08:00
do_log("$currentLog, targetShareRatio: $targetShareRatio, requireShareRatio: $requireShareRatio");
2021-06-21 02:01:26 +08:00
if ($targetShareRatio >= $requireShareRatio) {
2022-10-01 18:26:18 +08:00
$result = $this->reachedByShareRatio($row, $setting);
2021-06-21 02:01:26 +08:00
if ($result) {
$successCounts++;
}
continue;
}
//unreached
2022-03-28 21:15:02 +08:00
if ($row->created_at->addHours($setting['inspect_time'])->lte(Carbon::now())) {
2022-10-01 18:26:18 +08:00
$result = $this->unreached($row, $setting, !isset($disabledUsers[$row->uid]));
2022-03-28 21:08:00 +08:00
if ($result) {
$successCounts++;
2022-05-01 21:15:00 +08:00
$disabledUsers[$row->uid] = true;
2022-03-28 21:08:00 +08:00
}
2021-06-21 02:01:26 +08:00
}
}
$page++;
}
2022-10-01 18:26:18 +08:00
do_log("[CRONJOB_UPDATE_HR_DONE]");
2021-06-21 02:01:26 +08:00
return $successCounts;
}
private function geReachedMessage(HitAndRun $hitAndRun): array
{
return [
'receiver' => $hitAndRun->uid,
'added' => Carbon::now()->toDateTimeString(),
'subject' => nexus_trans('hr.reached_message_subject', ['hit_and_run_id' => $hitAndRun->id], $hitAndRun->user->locale),
'msg' => nexus_trans('hr.reached_message_content', [
'completed_at' => format_datetime($hitAndRun->snatch->completedat),
2021-06-21 02:01:26 +08:00
'torrent_id' => $hitAndRun->torrent_id,
'torrent_name' => $hitAndRun->torrent->name,
], $hitAndRun->user->locale),
];
}
2022-10-01 18:26:18 +08:00
private function reachedByShareRatio(HitAndRun $hitAndRun, array $setting): bool
2021-06-21 02:01:26 +08:00
{
do_log(__METHOD__);
$comment = nexus_trans('hr.reached_by_share_ratio_comment', [
'now' => Carbon::now()->toDateTimeString(),
2022-10-01 18:26:18 +08:00
'seed_time_minimum' => $setting['seed_time_minimum'],
2021-06-21 02:01:26 +08:00
'seed_time' => bcdiv($hitAndRun->snatch->seedtime, 3600, 1),
'share_ratio' => get_hr_ratio($hitAndRun->snatch->uploaded, $hitAndRun->snatch->downloaded),
2022-10-01 18:26:18 +08:00
'ignore_when_ratio_reach' => $setting['ignore_when_ratio_reach'],
2021-06-21 02:01:26 +08:00
], $hitAndRun->user->locale);
$update = [
'comment' => $comment
];
2022-02-23 21:17:25 +08:00
return $this->inspectingToReached($hitAndRun, $update, __FUNCTION__);
2021-06-21 02:01:26 +08:00
}
2022-10-01 18:26:18 +08:00
private function reachedBySeedTime(HitAndRun $hitAndRun, array $setting): bool
2021-06-21 02:01:26 +08:00
{
do_log(__METHOD__);
$comment = nexus_trans('hr.reached_by_seed_time_comment', [
'now' => Carbon::now()->toDateTimeString(),
'seed_time' => bcdiv($hitAndRun->snatch->seedtime, 3600, 1),
2022-10-01 18:26:18 +08:00
'seed_time_minimum' => $setting['seed_time_minimum'],
2021-06-21 02:01:26 +08:00
], $hitAndRun->user->locale);
$update = [
'comment' => $comment
];
2022-02-23 21:17:25 +08:00
return $this->inspectingToReached($hitAndRun, $update, __FUNCTION__);
}
private function reachedBySpecialUserClass(HitAndRun $hitAndRun): bool
{
do_log(__METHOD__);
$comment = nexus_trans('hr.reached_by_special_user_class_comment', [
'user_class_text' => $hitAndRun->user->class_text,
], $hitAndRun->user->locale);
$update = [
'comment' => $comment
];
return $this->inspectingToReached($hitAndRun, $update, __FUNCTION__);
}
private function inspectingToReached(HitAndRun $hitAndRun, array $update, string $logPrefix = ''): bool
{
$update['status'] = HitAndRun::STATUS_REACHED;
2021-06-21 02:01:26 +08:00
$affectedRows = DB::table($hitAndRun->getTable())
->where('id', $hitAndRun->id)
->where('status', HitAndRun::STATUS_INSPECTING)
->update($update);
2022-02-23 21:17:25 +08:00
do_log("[$logPrefix], " . last_query() . ", affectedRows: $affectedRows");
2021-06-21 02:01:26 +08:00
if ($affectedRows != 1) {
2022-02-23 21:17:25 +08:00
do_log($hitAndRun->toJson() . ", [$logPrefix], affectedRows != 1, skip!", 'notice');
2021-06-21 02:01:26 +08:00
return false;
}
if ($hitAndRun->user->acceptNotification('hr_reached')) {
$message = $this->geReachedMessage($hitAndRun);
Message::query()->insert($message);
} else {
do_log($hitAndRun->toJson() . ", [$logPrefix], user do not accept hr_reached notification", 'notice');
}
2021-06-21 02:01:26 +08:00
return true;
}
2022-10-01 18:26:18 +08:00
private function unreached(HitAndRun $hitAndRun, array $setting, $disableUser = true): bool
2021-06-21 02:01:26 +08:00
{
2022-05-01 21:15:00 +08:00
do_log(sprintf('hitAndRun: %s, disableUser: %s', $hitAndRun->toJson(), var_export($disableUser, true)));
2021-06-21 02:01:26 +08:00
$comment = nexus_trans('hr.unreached_comment', [
'now' => Carbon::now()->toDateTimeString(),
'seed_time' => bcdiv($hitAndRun->snatch->seedtime, 3600, 1),
2022-10-01 18:26:18 +08:00
'seed_time_minimum' => $setting['seed_time_minimum'],
2021-06-21 02:01:26 +08:00
'share_ratio' => get_hr_ratio($hitAndRun->snatch->uploaded, $hitAndRun->snatch->downloaded),
'torrent_size' => mksize($hitAndRun->torrent->size),
2022-10-01 18:26:18 +08:00
'ignore_when_ratio_reach' => $setting['ignore_when_ratio_reach']
2021-06-21 02:01:26 +08:00
], $hitAndRun->user->locale);
$update = [
'status' => HitAndRun::STATUS_UNREACHED,
'comment' => $comment
];
$affectedRows = DB::table($hitAndRun->getTable())
->where('id', $hitAndRun->id)
->where('status', HitAndRun::STATUS_INSPECTING)
->update($update);
do_log("[H&R_UNREACHED], " . last_query() . ", affectedRows: $affectedRows");
if ($affectedRows != 1) {
do_log($hitAndRun->toJson() . ", [H&R_UNREACHED], affectedRows != 1, skip!", 'notice');
return false;
}
$message = [
'receiver' => $hitAndRun->uid,
'added' => Carbon::now()->toDateTimeString(),
'subject' => nexus_trans('hr.unreached_message_subject', ['hit_and_run_id' => $hitAndRun->id], $hitAndRun->user->locale),
'msg' => nexus_trans('hr.unreached_message_content', [
'completed_at' => format_datetime($hitAndRun->snatch->completedat),
2021-06-21 02:01:26 +08:00
'torrent_id' => $hitAndRun->torrent_id,
'torrent_name' => $hitAndRun->torrent->name,
], $hitAndRun->user->locale),
];
Message::query()->insert($message);
return true;
}
private function checkAndDisableUser(array $setting): void
{
2022-10-27 20:31:02 +08:00
$logPrefix = "setting: " . json_encode($setting);
$disableCounts = HitAndRun::getConfig('ban_user_when_counts_reach', $setting['search_box_id']);
if ($disableCounts <= 0) {
do_log("$logPrefix, disableCounts: $disableCounts <= 0, invalid, return", 'error');
return;
}
$query = HitAndRun::query()
->selectRaw("count(*) as counts, uid")
->where('status', HitAndRun::STATUS_UNREACHED)
->groupBy('uid')
->having("counts", '>=', $disableCounts)
;
2022-10-01 18:26:18 +08:00
if ($setting['diff_in_section']) {
$query->whereHas('torrent.basic_category', function (Builder $query) use ($setting) {
2022-10-01 18:26:18 +08:00
return $query->where('mode', $setting['search_box_id']);
});
}
$result = $query->get();
if ($result->isEmpty()) {
2022-10-27 20:31:02 +08:00
do_log("$logPrefix, No user to disable: " . last_query());
return;
}
$users = User::query()
->with('language')
2022-11-08 19:06:37 +08:00
->where('class', '<', User::CLASS_VIP)
->where('enabled', User::ENABLED_YES)
2022-11-08 19:06:37 +08:00
->where('donor', 'no')
2022-10-27 20:31:02 +08:00
->find($result->pluck('uid')->toArray(), ['id', 'username', 'lang']);
do_log("$logPrefix, Going to disable user: " . json_encode($users->toArray()));
foreach ($users as $user) {
$locale = $user->locale;
$comment = nexus_trans('hr.unreached_disable_comment', [], $locale);
$user->updateWithModComment(['enabled' => User::ENABLED_NO], sprintf('%s - %s', date('Y-m-d'), $comment));
2021-06-21 02:01:26 +08:00
$message = [
'receiver' => $user->id,
2021-06-21 02:01:26 +08:00
'added' => Carbon::now()->toDateTimeString(),
'subject' => $comment,
'msg' => nexus_trans('hr.unreached_disable_message_content', [
2022-10-27 20:31:02 +08:00
'ban_user_when_counts_reach' => $disableCounts,
], $locale),
2021-06-21 02:01:26 +08:00
];
Message::query()->insert($message);
$userBanLog = [
'uid' => $user->id,
'username' => $user->username,
'reason' => $comment
];
UserBanLog::query()->insert($userBanLog);
do_log("Disable user: " . nexus_json_encode($userBanLog));
2021-06-21 02:01:26 +08:00
}
}
2022-02-23 15:19:09 +08:00
public function getStatusStats($uid, $formatted = true)
{
2022-10-01 18:26:18 +08:00
$diffInSection = HitAndRun::diffInSection();
if ($diffInSection) {
2022-10-01 00:11:22 +08:00
$sql = "select hit_and_runs.status, categories.mode, count(*) as counts from hit_and_runs left join torrents on torrents.id = hit_and_runs.torrent_id left join categories on categories.id = torrents.category where hit_and_runs.uid = $uid group by hit_and_runs.status, categories.mode";
} else {
$sql = "select hit_and_runs.status, count(*) as counts from hit_and_runs where uid = $uid group by status";
}
$results = NexusDB::select($sql);
do_log("user: $uid, sql: $sql, results: " . json_encode($results));
2022-10-01 00:11:22 +08:00
if (!$formatted) {
return $results;
}
2022-10-01 18:26:18 +08:00
if ($diffInSection) {
2022-10-01 00:11:22 +08:00
$grouped = [];
foreach ($results as $item) {
$grouped[$item['mode']][$item['status']] = $item['counts'];
}
$out = [];
foreach (SearchBox::listSections() as $key => $info) {
$out[] = sprintf(
2022-10-27 20:42:15 +08:00
'%s: %s/<font color="red">%s</font>/%s',
2022-10-01 00:11:22 +08:00
$info['text'],
$grouped[$info['mode']][HitAndRun::STATUS_INSPECTING] ?? 0,
$grouped[$info['mode']][HitAndRun::STATUS_UNREACHED] ?? 0,
HitAndRun::getConfig('ban_user_when_counts_reach', $info['mode'])
);
}
return implode(" ", $out);
} else {
$grouped = [];
foreach ($results as $item) {
$grouped[$item['status']] = $item['counts'];
}
2022-10-01 00:11:22 +08:00
foreach (SearchBox::listSections() as $key => $info) {
if ($key == SearchBox::SECTION_BROWSE) {
return sprintf(
2022-10-27 20:42:15 +08:00
'%s/<font color="red">%s</font>/%s',
$grouped[HitAndRun::STATUS_INSPECTING] ?? 0,
$grouped[HitAndRun::STATUS_UNREACHED] ?? 0,
2022-10-01 00:11:22 +08:00
HitAndRun::getConfig('ban_user_when_counts_reach', $info['mode'])
);
}
}
2022-02-23 15:19:09 +08:00
}
}
2022-04-17 16:38:44 +08:00
2022-10-01 00:11:22 +08:00
2022-05-13 03:12:38 +08:00
public function listStatus(): array
2022-04-17 16:38:44 +08:00
{
$results = [];
foreach (HitAndRun::$status as $key => $value) {
$results[] = ['status' => $key, 'text' => nexus_trans('hr.status_' . $key)];
}
return $results;
}
public function pardon($id, User $user): bool
{
$model = HitAndRun::query()->findOrFail($id);
2022-06-08 14:15:59 +08:00
if (!in_array($model->status, $this->getCanPardonStatus())) {
2022-04-17 16:38:44 +08:00
throw new \LogicException("Can't be pardoned due to status is: " . $model->status_text . " !");
}
$model->status = HitAndRun::STATUS_PARDONED;
2022-07-02 15:08:23 +08:00
$model->comment = $this->getCommentUpdateRaw(addslashes(date('Y-m-d') . ' - Pardon by ' . $user->username));
2022-04-17 16:38:44 +08:00
$model->save();
return true;
}
2022-05-13 03:12:38 +08:00
public function bulkPardon(array $params, User $user): int
{
2022-06-08 14:15:59 +08:00
$query = $this->getBulkQuery($params)->whereIn('status', $this->getCanPardonStatus());
2022-05-13 03:12:38 +08:00
$update = [
'status' => HitAndRun::STATUS_PARDONED,
'comment' => $this->getCommentUpdateRaw(addslashes('Pardon by ' . $user->username)),
];
$affected = $query->update($update);
do_log(sprintf(
'user: %s bulk pardon by filter: %s, affected: %s',
$user->id, json_encode($params), $affected
), 'alert');
return $affected;
}
private function getCommentUpdateRaw($comment): \Illuminate\Database\Query\Expression
{
2024-12-25 22:30:55 +08:00
return NexusDB::raw(sprintf("if (comment = '', '%s', concat('\n', '%s', comment))", $comment, $comment));
2022-05-13 03:12:38 +08:00
}
2022-06-08 14:15:59 +08:00
private function getCanPardonStatus(): array
{
return HitAndRun::CAN_PARDON_STATUS;
2022-06-08 14:15:59 +08:00
}
2022-10-01 00:11:22 +08:00
public function renderOnUploadPage($value, $searchBoxId): string
{
if (HitAndRun::getConfig('mode', $searchBoxId) == \App\Models\HitAndRun::MODE_MANUAL && user_can('torrent_hr')) {
$hrRadio = sprintf('<label><input type="radio" name="hr[%s]" value="0"%s />NO</label>', $searchBoxId, $value == 0 ? ' checked' : '');
$hrRadio .= sprintf('<label><input type="radio" name="hr[%s]" value="1"%s />YES</label>', $searchBoxId, $value == 1 ? ' checked' : '');
return tr('H&R', $hrRadio, 1, "mode_$searchBoxId", true);
}
return '';
}
2021-06-21 02:01:26 +08:00
}