From 42cda650270ae1606cfaad95214a0c168c623d28 Mon Sep 17 00:00:00 2001 From: xiaomlove Date: Thu, 31 Oct 2024 01:55:16 +0800 Subject: [PATCH] improve announce for paid torrents and H&R --- app/Repositories/TorrentRepository.php | 102 ++++++++++++++++++++++++- include/constants.php | 2 +- include/functions.php | 8 +- public/announce.php | 75 +++++++++--------- public/details.php | 2 +- resources/lang/en/message.php | 4 + resources/lang/zh_CN/message.php | 4 + resources/lang/zh_TW/message.php | 4 + 8 files changed, 153 insertions(+), 48 deletions(-) diff --git a/app/Repositories/TorrentRepository.php b/app/Repositories/TorrentRepository.php index c93f57801..83f7c67ae 100644 --- a/app/Repositories/TorrentRepository.php +++ b/app/Repositories/TorrentRepository.php @@ -41,10 +41,17 @@ class TorrentRepository extends BaseRepository { - const BOUGHT_USER_CACHE_KEY_PREFIX = "torrent_purchasers:"; + const BOUGHT_USER_CACHE_KEY_PREFIX = "torrent_purchasers"; + + const BUY_FAIL_CACHE_KEY_PREFIX = "torrent_purchase_fails"; const PIECES_HASH_CACHE_KEY = "torrent_pieces_hash"; + const BUY_STATUS_SUCCESS = 0; + const BUY_STATUS_NOT_YET = -1; + + + /** * fetch torrent list * @@ -764,15 +771,102 @@ public function loadBoughtUser($torrentId): int return $total; } - public function addBoughtUserToCache($torrentId, $uid) + /** + * 购买成功缓存,保存为 hash,一个种子一个 hash,永久有效 + * @param $uid + * @param $torrentId + * @return void + * @throws \RedisException + */ + public function addBuySuccessCache($uid, $torrentId): void { NexusDB::redis()->hSet($this->getBoughtUserCacheKey($torrentId), $uid, 1); } + public function hasBuySuccessCache($uid, $torrentId): bool + { + return NexusDB::redis()->hGet($this->getBoughtUserCacheKey($torrentId), $uid) == 1; + } - private function getBoughtUserCacheKey($torrentId): string + /** + * 获取购买种子的缓存状态 + * + * @param $uid + * @param $torrentId + * @return int + */ + public function getBuyStatus($uid, $torrentId): int + { + //查询是否已经购买 + if ($this->hasBuySuccessCache($uid, $torrentId)) { + return self::BUY_STATUS_SUCCESS; + } + //是否购买失败过 + $buyFailCount = $this->getBuyFailCache($uid, $torrentId); + if ($buyFailCount > 0) { + //根据失败次数,禁用下载权限并做提示等 + return $buyFailCount; + } + //购买失败缓存失效后,再重新查询数据库确定最终状态 + $hasBuyFromDB = TorrentBuyLog::query()->where("uid", $uid)->where("torrent_id", $torrentId)->exists(); + if ($hasBuyFromDB) { + //标记购买成功, 返回已购买 + $this->addBuySuccessCache($uid, $torrentId); + return self::BUY_STATUS_SUCCESS; + } else { + //返回未购买,前端可执行购买逻辑 + return self::BUY_STATUS_NOT_YET; + } + } + + /** + * 添加购买失败缓存, 结果累加 + * @param $uid + * @param $torrentId + * @return void + * @throws \RedisException + */ + public function addBuyFailCache($uid, $torrentId): void + { + $key = $this->getBuyFailCacheKey($uid, $torrentId); + $result = NexusDB::redis()->incr($key); + if ($result == 1) { + NexusDB::redis()->expire($key, 3600); + } + } + + /** + * 获取失败缓存 ,结果是失败的次数 + * + * @param $uid + * @param $torrentId + * @return int + * @throws \RedisException + */ + public function getBuyFailCache($uid, $torrentId): int + { + return intval(NexusDB::redis()->get($this->getBuyFailCacheKey($uid, $torrentId))); + } + + /** + * 购买成功缓存 key + * @param $torrentId + * @return string + */ + public function getBoughtUserCacheKey($torrentId): string + { + return sprintf("%s:%s", self::BOUGHT_USER_CACHE_KEY_PREFIX, $torrentId); + } + + /** + * 购买失败缓存 key + * @param int $userId + * @param int $torrentId + * @return string + */ + public function getBuyFailCacheKey(int $userId, int $torrentId): string { - return self::BOUGHT_USER_CACHE_KEY_PREFIX . $torrentId; + return sprintf("%s:%s:%s", self::BUY_FAIL_CACHE_KEY_PREFIX, $userId, $torrentId); } public function addPiecesHashCache(int $torrentId, string $piecesHash): bool|int|\Redis diff --git a/include/constants.php b/include/constants.php index f03d4ca22..bda539d3a 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ render(); + /* $pending_invitee = $Cache->get_value('user_'.$CURUSER["id"].'_pending_invitee_count'); if ($pending_invitee == ""){ @@ -5907,7 +5909,11 @@ function get_ip_location_from_geoip($ip): bool|array function msgalert($url, $text, $bgcolor = "red") { print("
\n"); - print("".$text.""); + if (!empty($url)) { + print("".$text.""); + } else { + print("".$text.""); + } print("

"); } diff --git a/public/announce.php b/public/announce.php index 50370fe50..6c99a9f9b 100644 --- a/public/announce.php +++ b/public/announce.php @@ -164,6 +164,13 @@ $redis->set("$passkeyInvalidKey:$passkey", TIMENOW, ['ex' => 24*3600]); warn("Invalid passkey! Re-download the .torrent from $BASEURL"); } +if ($az["enabled"] == "no") + warn("Your account is disabled!", 300); +elseif ($az["parked"] == "yes") + warn("Your account is parked! (Read the FAQ)", 300); +elseif ($az["downloadpos"] == "no") + warn("Your downloading privileges have been disabled! (Read the rules)", 300); + $userid = intval($az['id'] ?? 0); unset($GLOBALS['CURUSER']); $CURUSER = $GLOBALS["CURUSER"] = $az; @@ -401,13 +408,6 @@ if ($valid[0] >= 1 && $seeder == 'no') err("You already are downloading the same torrent. You may only leech from one location at a time.", 300); if ($valid[0] >= 3 && $seeder == 'yes') err("You cannot seed the same torrent from more than 3 locations.", 300); - if ($az["enabled"] == "no") - warn("Your account is disabled!", 300); - elseif ($az["parked"] == "yes") - warn("Your account is parked! (Read the FAQ)", 300); - elseif ($az["downloadpos"] == "no") - warn("Your downloading privileges have been disabled! (Read the rules)", 300); - if ($az["class"] < UC_VIP) { $ratio = (($az["downloaded"] > 0) ? ($az["uploaded"] / $az["downloaded"]) : 1); @@ -451,43 +451,48 @@ && $torrent['owner'] != $userid && get_setting("torrent.paid_torrent_enabled") == "yes" ) { - $hasBuyCacheKey = \App\Repositories\TorrentRepository::BOUGHT_USER_CACHE_KEY_PREFIX . $torrentid; - $hasBuy = $redis->hGet($hasBuyCacheKey, $userid); - if ($hasBuy === false) { - //no cache - $lockName = "load_torrent_bought_user:$torrentid"; - $loadBoughtLock = new \Nexus\Database\NexusLock($lockName, 300); - if ($loadBoughtLock->get()) { - //get lock, do load - executeCommand("torrent:load_bought_user $torrentid", "string", true, false); - } else { - do_log("can not get loadBoughtLock: $lockName", 'debug'); + $torrentRep = new \App\Repositories\TorrentRepository(); + $buyStatus = $torrentRep->getBuyStatus($userid, $torrentid); + if ($buyStatus > 0) { + do_log(sprintf("user: %v buy torrent: %v fail count: %v", $userid, $torrentid, $buyStatus), "error"); + if ($buyStatus > 3) { + //warn + \App\Utils\MsgAlert::getInstance()->add( + "announce_paid_torrent_too_many_times", + time() + 86400, + "announce to paid torrent and fail too many times, please make sure you have enough bonus!", + "", + "black" + ); + } + if ($buyStatus > 10) { + //disable download + (new \App\Repositories\UserRepository())->updateDownloadPrivileges(null, $userid, 'no', 'announce_paid_torrent_too_many_times'); } - //simple cache the hasBuy result - $hasBuy = \Nexus\Database\NexusDB::remember(sprintf("user_has_buy_torrent:%s:%s", $userid, $torrentid), 86400*10, function () use($userid, $torrentid) { - $exists = \App\Models\TorrentBuyLog::query()->where('uid', $userid)->where('torrent_id', $torrentid)->exists(); - return intval($exists); - }); + warn("purchase fail, please try again later, please make sure you have enough bonus", 300); } - if (!$hasBuy) { - $lock = new \Nexus\Database\NexusLock("buying_torrent:$userid", 5); + if ($buyStatus == \App\Repositories\TorrentRepository::BUY_STATUS_NOT_YET) { + //one by one + $lock = new \Nexus\Database\NexusLock("buying_torrent", 5); if (!$lock->get()) { $msg = "buying torrent, wait!"; do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg", 'error'); - err($msg); + warn($msg, 300); } $bonusRep = new \App\Repositories\BonusRepository(); try { $bonusRep->consumeToBuyTorrent($az['id'], $torrent['id'], 'Web'); - $redis->hSet($hasBuyCacheKey, $userid, 1); + $torrentRep->addBuySuccessCache($userid, $torrentid); $lock->release(); } catch (\Exception $exception) { $msg = $exception->getMessage(); do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg " . $exception->getTraceAsString(), 'error'); + $torrentRep->addBuyFailCache($userid, $torrentid); $lock->release(); err($msg); } } + } } else // continue an existing session @@ -570,19 +575,7 @@ if ($event != 'stopped') { $isPeerExistResultSet = sql_query("select id from peers where $selfwhere limit 1"); if (mysql_num_rows($isPeerExistResultSet) == 0) { - $cacheKey = 'peers:connectable:'.$ip.'-'.$port.'-'.$agent; - $connectable = \Nexus\Database\NexusDB::remember($cacheKey, 3600, function () use ($ip, $port) { - if (isIPV6($ip)) { - $sockres = @fsockopen("tcp://[".$ip."]",$port,$errno,$errstr,1); - } else { - $sockres = @fsockopen($ip, $port, $errno, $errstr, 1); - } - if (is_resource($sockres)) { - fclose($sockres); - return 'yes'; - } - return 'no'; - }); + $connectable = "yes"; $insertPeerSql = "INSERT INTO peers (torrent, userid, peer_id, ip, port, connectable, uploaded, downloaded, to_go, started, last_action, seeder, agent, downloadoffset, uploadoffset, passkey, ipv4, ipv6, is_seed_box) VALUES ($torrentid, $userid, ".sqlesc($peer_id).", ".sqlesc($ip).", $port, '$connectable', $uploaded, $downloaded, $left, $dt, $dt, '$seeder', ".sqlesc($agent).", $downloaded, $uploaded, ".sqlesc($passkey).", ".sqlesc($ipv4).", ".sqlesc($ipv6).", ".intval($isIPSeedBox).")"; do_log("[INSERT PEER] peer not exists for $selfwhere, do insert with $insertPeerSql"); @@ -617,7 +610,7 @@ $hrLog = sprintf("[HR_LOG] user: %d, torrent: %d, hrMode: %s", $userid, $torrentid, $hrMode); if ($hrMode == \App\Models\HitAndRun::MODE_GLOBAL || ($hrMode == \App\Models\HitAndRun::MODE_MANUAL && $torrent['hr'] == \App\Models\Torrent::HR_YES)) { $hrCacheKey = sprintf("hit_and_run:%d:%d", $userid, $torrentid); - $hrExists = \Nexus\Database\NexusDB::remember($hrCacheKey, 24*3600, function () use ($torrentid, $userid) { + $hrExists = \Nexus\Database\NexusDB::remember($hrCacheKey, mt_rand(86400*365*5, 86400*365*10), function () use ($torrentid, $userid) { return \App\Models\HitAndRun::query()->where("uid", $userid)->where("torrent_id", $torrentid)->exists(); }); $hrLog .= ", hrExists: $hrExists"; diff --git a/public/details.php b/public/details.php index 9801a6223..24dc8eebe 100644 --- a/public/details.php +++ b/public/details.php @@ -6,7 +6,7 @@ loggedinorreturn(); $id = intval($_GET["id"] ?? 0); $customField = new \Nexus\Field\Field(); -int_check($id); +int_check($id, true); if (!isset($id) || !$id) die(); diff --git a/resources/lang/en/message.php b/resources/lang/en/message.php index c9f9abf68..3b726590b 100644 --- a/resources/lang/en/message.php +++ b/resources/lang/en/message.php @@ -19,6 +19,10 @@ 'subject' => 'Download permission canceled', 'body' => 'Your download permission has been cancelled due to excessive upload speed, please file if you are a seed box user.' , ], + 'download_disable_announce_paid_torrent_too_many_times' => [ + 'subject' => 'Download permission canceled', + 'body' => 'Your download permission has been cancelled due to announce to paid torrent too many times, please make sure you have enough bonus.' , + ], 'download_enable' => [ 'subject' => 'Download permission restored', 'body' => 'Your download privileges restored, you can now download torrents. By: :operator', diff --git a/resources/lang/zh_CN/message.php b/resources/lang/zh_CN/message.php index 4e1950395..3c57994c8 100644 --- a/resources/lang/zh_CN/message.php +++ b/resources/lang/zh_CN/message.php @@ -19,6 +19,10 @@ 'subject' => '下载权限取消', 'body' => '你因上传速度过快下载权限被取消,若是盒子用户请备案。', ], + 'download_disable_announce_paid_torrent_too_many_times' => [ + 'subject' => '下载权限取消', + 'body' => '你因向付费种子汇报失败次数过多下载权限被取消,请确保你有足够的魔力。', + ], 'download_enable' => [ 'subject' => '下载权限恢复', 'body' => '你的下载权限恢复,你现在可以下载种子。By: :operator', diff --git a/resources/lang/zh_TW/message.php b/resources/lang/zh_TW/message.php index b1035fc96..0769ec957 100644 --- a/resources/lang/zh_TW/message.php +++ b/resources/lang/zh_TW/message.php @@ -18,6 +18,10 @@ 'subject' => '下載權限取消', 'body' => '你因上傳速度過快下載權限被取消,若是盒子用戶請備案。', ], + 'download_disable_announce_paid_torrent_too_many_times' => [ + 'subject' => '下载权限取消', + 'body' => '你因向付費種子匯報失敗次數過多下載權限被取消,請確保你有足夠的魔力。', + ], 'download_enable' => [ 'subject' => '下載權限恢復', 'body' => '你的下載權限恢復,你現在可以下載種子。By: :operator',