api scrape + medal wearing status

This commit is contained in:
xiaomlove
2022-03-19 14:55:43 +08:00
parent 8c32b45e64
commit 4857b799b8
24 changed files with 16536 additions and 69 deletions

View File

@@ -64,4 +64,4 @@ GOOGLE_DRIVE_FOLDER_ID=
GEOIP2_DATABASE= GEOIP2_DATABASE=
EXAM_PROGRESS_UPDATE_PROBABILITY=20 EXAM_PROGRESS_UPDATE_PROBABILITY=20
ANNOUNCE_API_LOCAL_HOST= TRACKER_API_LOCAL_HOST=

View File

@@ -22,4 +22,13 @@ class TrackerController extends Controller
{ {
return $this->repository->announce($request); return $this->repository->announce($request);
} }
/**
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function scrape(Request $request): \Illuminate\Http\Response
{
return $this->repository->scrape($request);
}
} }

View File

@@ -320,8 +320,10 @@ class User extends Authenticatable
public function medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany public function medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{ {
return $this->belongsToMany(Medal::class, 'user_medals', 'uid', 'medal_id') return $this->belongsToMany(Medal::class, 'user_medals', 'uid', 'medal_id')
->withPivot(['id', 'expire_at']) ->withPivot(['id', 'expire_at', 'status'])
->withTimestamps(); ->withTimestamps()
->orderByPivot('id', 'desc')
;
} }
public function valid_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany public function valid_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
@@ -331,6 +333,11 @@ class User extends Authenticatable
}); });
} }
public function wearing_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->valid_medals()->where('user_medals.status', UserMedal::STATUS_WEARING);
}
public function getAvatarAttribute($value) public function getAvatarAttribute($value)
{ {
if ($value) { if ($value) {

View File

@@ -4,5 +4,10 @@ namespace App\Models;
class UserMedal extends NexusModel class UserMedal extends NexusModel
{ {
protected $fillable = ['uid', 'medal_id', 'expire_at']; protected $fillable = ['uid', 'medal_id', 'expire_at', 'status'];
const STATUS_NOT_WEARING = 0;
const STATUS_WEARING = 1;
} }

View File

@@ -6,6 +6,7 @@ use App\Models\HitAndRun;
use App\Models\Medal; use App\Models\Medal;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use App\Models\UserMedal;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Nexus\Database\NexusDB; use Nexus\Database\NexusDB;
@@ -66,7 +67,7 @@ class BonusRepository extends BaseRepository
if ($medal->duration > 0) { if ($medal->duration > 0) {
$expireAt = Carbon::now()->addDays($medal->duration)->toDateTimeString(); $expireAt = Carbon::now()->addDays($medal->duration)->toDateTimeString();
} }
$user->medals()->attach([$medal->id => ['expire_at' => $expireAt]]); $user->medals()->attach([$medal->id => ['expire_at' => $expireAt, 'status' => UserMedal::STATUS_NOT_WEARING]]);
}); });

View File

@@ -66,7 +66,23 @@ class MedalRepository extends BaseRepository
if ($duration > 0) { if ($duration > 0) {
$expireAt = Carbon::now()->addDays($duration)->toDateTimeString(); $expireAt = Carbon::now()->addDays($duration)->toDateTimeString();
} }
return $user->medals()->attach([$medal->id => ['expire_at' => $expireAt]]); return $user->medals()->attach([$medal->id => ['expire_at' => $expireAt, 'status' => UserMedal::STATUS_NOT_WEARING]]);
}
function toggleUserMedalStatus($id, $userId)
{
$userMedal = UserMedal::query()->findOrFail($id);
if ($userMedal->uid != $userId) {
throw new \LogicException("no privilege");
}
$current = $userMedal->status;
if ($current == UserMedal::STATUS_NOT_WEARING) {
$userMedal->status = UserMedal::STATUS_WEARING;
} elseif ($current == UserMedal::STATUS_WEARING) {
$userMedal->status = UserMedal::STATUS_NOT_WEARING;
}
$userMedal->save();
return $userMedal;
} }
} }

View File

@@ -1,4 +1,10 @@
<?php <?php
/**
* Handle announce and scrape
*
* @link https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/master/app/Http/Controllers/AnnounceController.php
* @link https://github.com/Rhilip/RidPT/blob/master/application/Controllers/Tracker/AnnounceController.php
*/
namespace App\Repositories; namespace App\Repositories;
use App\Exceptions\ClientNotAllowedException; use App\Exceptions\ClientNotAllowedException;
@@ -13,13 +19,14 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Exceptions\TrackerException; use App\Exceptions\TrackerException;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
use Rhilip\Bencode\Bencode; use Rhilip\Bencode\Bencode;
class TrackerRepository extends BaseRepository class TrackerRepository extends BaseRepository
{ {
const MIN_ANNOUNCE_WAIT_SECOND = 30; const MIN_ANNOUNCE_WAIT_SECOND = 300;
const MAX_PEER_NUM_WANT = 50; const MAX_PEER_NUM_WANT = 50;
@@ -305,10 +312,7 @@ class TrackerRepository extends BaseRepository
protected function checkTorrent($queries, User $user) protected function checkTorrent($queries, User $user)
{ {
// Check Info Hash Against Torrents Table // Check Info Hash Against Torrents Table
$torrent = Torrent::query() $torrent = $this->getTorrentByInfoHash($queries['info_hash']);
->selectRaw('id, owner, sp_state, seeders, leechers, added, banned, hr, visible, last_action, times_completed')
->where('info_hash', '=', $queries['info_hash'])
->first();
// If Torrent Doesnt Exists Return Error to Client // If Torrent Doesnt Exists Return Error to Client
if ($torrent === null) { if ($torrent === null) {
@@ -506,24 +510,23 @@ class TrackerRepository extends BaseRepository
private function generateSuccessAnnounceResponse($torrent, $queries, $user, $withPeers = true): array private function generateSuccessAnnounceResponse($torrent, $queries, $user, $withPeers = true): array
{ {
// Build Response For Bittorrent Client // Build Response For Bittorrent Client
$minInterval = self::MIN_ANNOUNCE_WAIT_SECOND;
$interval = max($this->getRealAnnounceInterval($torrent), $minInterval);
$repDict = [ $repDict = [
'interval' => $this->getRealAnnounceInterval($torrent), 'interval' => $interval + random_int(10, 100),
'min interval' => self::MIN_ANNOUNCE_WAIT_SECOND, 'min interval' => $minInterval + random_int(1, 10),
'complete' => (int) $torrent->seeders, 'complete' => (int) $torrent->seeders,
'incomplete' => (int) $torrent->leechers, 'incomplete' => (int) $torrent->leechers,
'peers' => [], 'peers' => [],
'peers6' => [], 'peers6' => [],
]; ];
do_log("[REP_DICT_BASE] " . json_encode($repDict)); do_log("[REP_DICT_BASE] " . json_encode($repDict));
if (!$withPeers) {
return $repDict;
}
/** /**
* For non `stopped` event only * For non `stopped` event only
* We query peers from database and send peer list, otherwise just quick return. * We query peers from database and send peer list, otherwise just quick return.
*/ */
if (\strtolower($queries['event']) !== 'stopped') { if (\strtolower($queries['event']) !== 'stopped' && $withPeers) {
$limit = ($queries['numwant'] <= self::MAX_PEER_NUM_WANT ? $queries['numwant'] : self::MAX_PEER_NUM_WANT); $limit = ($queries['numwant'] <= self::MAX_PEER_NUM_WANT ? $queries['numwant'] : self::MAX_PEER_NUM_WANT);
$baseQuery = Peer::query() $baseQuery = Peer::query()
->select(['peer_id', 'ip', 'port']) ->select(['peer_id', 'ip', 'port'])
@@ -769,4 +772,86 @@ class TrackerRepository extends BaseRepository
do_log(last_query(), 'debug'); do_log(last_query(), 'debug');
} }
public function scrape(Request $request): \Illuminate\Http\Response
{
do_log(nexus_json_encode($_SERVER));
try {
$infoHashArr = $this->checkScrapeFields($request);
$user = $this->checkUser($request);
$clientAllow = $this->checkClient($request);
if ($user->clientselect != $clientAllow->id) {
$this->userUpdates['clientselect'] = $clientAllow->id;
}
$repDict = $this->generateScrapeResponse($infoHashArr);
} catch (ClientNotAllowedException $exception) {
do_log("[ClientNotAllowedException] " . $exception->getMessage());
if (isset($user) && $user->showclienterror == 'no') {
$this->userUpdates['showclienterror'] = 'yes';
}
$repDict = $this->generateFailedAnnounceResponse($exception->getMessage());
} catch (TrackerException $exception) {
$repDict = $this->generateFailedAnnounceResponse($exception->getMessage());
} catch (\Exception $exception) {
//other system exception
do_log("[" . get_class($exception) . "] " . $exception->getMessage() . "\n" . $exception->getTraceAsString(), 'error');
$repDict = $this->generateFailedAnnounceResponse("system error, report to sysop please, hint: " . REQUEST_ID);
} finally {
if (isset($user) && count($this->userUpdates)) {
$user->update($this->userUpdates);
do_log(last_query(), 'debug');
}
return $this->sendFinalAnnounceResponse($repDict);
}
}
private function checkScrapeFields(Request $request): array
{
preg_match_all('/info_hash=([^&]*)/i', urldecode($request->getQueryString()), $info_hash_match);
$info_hash_array = $info_hash_match[1];
if (count($info_hash_array) < 1) {
throw new TrackerException("key: info_hash is Missing !");
} else {
foreach ($info_hash_array as $item) {
if (strlen($item) != 20) {
throw new TrackerException("Invalid info_hash ! :attribute is not 20 bytes long");
}
}
}
return $info_hash_array;
}
/**
* @param $info_hash_array
* @return array[]
* @see http://www.bittorrent.org/beps/bep_0048.html
*/
private function generateScrapeResponse($info_hash_array)
{
$torrent_details = [];
foreach ($info_hash_array as $item) {
$torrent = $this->getTorrentByInfoHash($item);
if ($torrent) {
$torrent_details[$item] = [
'complete' => (int)$torrent->seeders,
'downloaded' => (int)$torrent->times_completed,
'incomplete' => (int)$torrent->leechers,
];
}
}
return ['files' => $torrent_details];
}
private function getTorrentByInfoHash($infoHash)
{
return Cache::remember(bin2hex($infoHash), 60, function () use ($infoHash) {
$fieldRaw = 'id, owner, sp_state, seeders, leechers, added, banned, hr, visible, last_action, times_completed';
$torrent = Torrent::query()->where('info_hash', $infoHash)->selectRaw($fieldRaw)->first();
do_log("cache miss, from database: " . last_query() . ", and get: " . $torrent->id);
return $torrent;
});
}
} }

View File

@@ -14,9 +14,6 @@ define('LARAVEL_START', microtime(true));
| loading of any our classes "manually". Feels great to relax. | loading of any our classes "manually". Feels great to relax.
| |
*/ */
require __DIR__ . '/include/globalfunctions.php';
require __DIR__ . '/include/functions.php';
require __DIR__.'/vendor/autoload.php'; require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php'; $app = require_once __DIR__.'/bootstrap/app.php';

View File

@@ -3,7 +3,8 @@ defined('LARAVEL_START') || define('LARAVEL_START', microtime(true));
defined('NEXUS_START') || define('NEXUS_START', LARAVEL_START); defined('NEXUS_START') || define('NEXUS_START', LARAVEL_START);
defined('IN_NEXUS') || define('IN_NEXUS', false); defined('IN_NEXUS') || define('IN_NEXUS', false);
require dirname(__DIR__) . '/include/constants.php'; require dirname(__DIR__) . '/include/constants.php';
require dirname(__DIR__) . '/include/globalfunctions.php';
require dirname(__DIR__) . '/include/functions.php';
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Create The Application | Create The Application

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddStatusToUserMedalsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('user_medals', function (Blueprint $table) {
$table->integer('status')->default(1);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('user_medals', function (Blueprint $table) {
$table->dropColumn('status');
});
}
}

View File

@@ -3,7 +3,8 @@ defined('NEXUS_START') || define('NEXUS_START', microtime(true));
defined('IN_NEXUS') || define('IN_NEXUS', true); defined('IN_NEXUS') || define('IN_NEXUS', true);
defined('IN_TRACKER') || define('IN_TRACKER', true); defined('IN_TRACKER') || define('IN_TRACKER', true);
$rootpath= dirname(__DIR__) . '/'; $rootpath= dirname(__DIR__) . '/';
require_once $rootpath . 'include/constants.php';
require_once $rootpath . 'include/globalfunctions.php'; require_once $rootpath . 'include/globalfunctions.php';
require_once $rootpath . 'include/functions_announce.php'; require_once $rootpath . 'include/functions_announce.php';
require $rootpath . 'include/core.php';

View File

@@ -2929,7 +2929,7 @@ function commenttable($rows, $type, $parent_id, $review = false)
$uidArr = array_unique(array_column($rows, 'user')); $uidArr = array_unique(array_column($rows, 'user'));
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title'); $neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
$userInfoArr = \App\Models\User::query()->with(['valid_medals'])->find($uidArr, $neededColumns)->keyBy('id'); $userInfoArr = \App\Models\User::query()->with(['wearing_medals'])->find($uidArr, $neededColumns)->keyBy('id');
foreach ($rows as $row) foreach ($rows as $row)
{ {
@@ -2944,7 +2944,7 @@ function commenttable($rows, $type, $parent_id, $review = false)
} }
} }
print("<div style=\"margin-top: 8pt; margin-bottom: 8pt;\"><table id=\"cid".$row["id"]."\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td class=\"embedded\" width=\"99%\">#" . $row["id"] . "&nbsp;&nbsp;<font color=\"gray\">".$lang_functions['text_by']."</font>"); print("<div style=\"margin-top: 8pt; margin-bottom: 8pt;\"><table id=\"cid".$row["id"]."\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td class=\"embedded\" width=\"99%\">#" . $row["id"] . "&nbsp;&nbsp;<font color=\"gray\">".$lang_functions['text_by']."</font>");
print(build_medal_image($userInfo->valid_medals, 20) . get_username($row["user"],false,true,true,false,false,true)); print(build_medal_image($userInfo->wearing_medals, 20) . get_username($row["user"],false,true,true,false,false,true));
print("&nbsp;&nbsp;<font color=\"gray\">".$lang_functions['text_at']."</font>".gettime($row["added"]). print("&nbsp;&nbsp;<font color=\"gray\">".$lang_functions['text_at']."</font>".gettime($row["added"]).
($row["editedby"] && get_user_class() >= $commanage_class ? " - [<a href=\"comment.php?action=vieworiginal&amp;cid=".$row['id']."&amp;type=".$type."\">".$lang_functions['text_view_original']."</a>]" : "") . "</td><td class=\"embedded nowrap\" width=\"1%\"><a href=\"#top\"><img class=\"top\" src=\"pic/trans.gif\" alt=\"Top\" title=\"Top\" /></a>&nbsp;&nbsp;</td></tr></table></div>"); ($row["editedby"] && get_user_class() >= $commanage_class ? " - [<a href=\"comment.php?action=vieworiginal&amp;cid=".$row['id']."&amp;type=".$type."\">".$lang_functions['text_view_original']."</a>]" : "") . "</td><td class=\"embedded nowrap\" width=\"1%\"><a href=\"#top\"><img class=\"top\" src=\"pic/trans.gif\" alt=\"Top\" title=\"Top\" /></a>&nbsp;&nbsp;</td></tr></table></div>");
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars(trim($userRow["avatar"])) : ""); $avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars(trim($userRow["avatar"])) : "");
@@ -5296,13 +5296,24 @@ function msgalert($url, $text, $bgcolor = "red")
print("</td></tr></table></p><br />"); print("</td></tr></table></p><br />");
} }
function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = 200): string function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = 200, $withActions = false): string
{ {
$medalImages = []; $medalImages = [];
$wrapBefore = '<div style="display: inline;">';
$wrapAfter = '</div>';
foreach ($medals as $medal) { foreach ($medals as $medal) {
$medalImages[] = sprintf('<img src="%s" title="%s" style="max-height: %spx"/>', $medal->image_large, $medal->name, $maxHeight); $html = sprintf('<div style="display: inline"><img src="%s" title="%s" style="max-height: %spx"/>', $medal->image_large, $medal->name, $maxHeight);
if ($withActions) {
$checked = '';
if ($medal->pivot->status == \App\Models\UserMedal::STATUS_WEARING) {
$checked = ' checked';
}
$html .= sprintf('<label>%s<input type="checkbox" name="medal_wearing_status" value="%s"%s></label>', '佩戴', $medal->id, $checked);
}
$html .= '</div>';
$medalImages[] = $html;
} }
return implode('', $medalImages); return $wrapBefore . implode('', $medalImages) . $wrapAfter;
} }
function insert_torrent_tags($torrentId, $tagIdArr, $sync = false) function insert_torrent_tags($torrentId, $tagIdArr, $sync = false)

View File

@@ -337,4 +337,25 @@ function check_client($peer_id, $agent, &$agent_familyid)
} }
} }
function request_local_api($api)
{
$start = microtime(true);
$ch = curl_init();
$options = [
CURLOPT_URL => sprintf('%s?%s', trim($api, '/'), $_SERVER['QUERY_STRING']),
CURLOPT_USERAGENT => $_SERVER["HTTP_USER_AGENT"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 60,
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$log = sprintf(
"[LOCAL_ANNOUNCE_API] [%s] options: %s, response(%s): %s",
number_format(microtime(true) - $start, 3), nexus_json_encode($options), gettype($response), $response
);
do_log($log);
return $response;
}
?> ?>

View File

@@ -669,24 +669,3 @@ function get_hr_ratio($uped, $downed)
return $ratio; return $ratio;
} }
function request_local_announce_api($announceApiLocalHost)
{
$start = microtime(true);
$ch = curl_init();
$options = [
CURLOPT_URL => sprintf('%s/api/announce?%s', trim($announceApiLocalHost, '/'), $_SERVER['QUERY_STRING']),
CURLOPT_USERAGENT => $_SERVER["HTTP_USER_AGENT"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 60,
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$log = sprintf(
"[LOCAL_ANNOUNCE_API] [%s] options: %s, response(%s): %s",
number_format(microtime(true) - $start, 3), json_encode($options), gettype($response), $response
);
do_log($log);
return $response;
}

16251
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
}, },
"devDependencies": { "devDependencies": {
"axios": "^0.21", "axios": "^0.21",
"chokidar": "^3.5.3",
"laravel-mix": "^6.0.6", "laravel-mix": "^6.0.6",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"postcss": "^8.1.14" "postcss": "^8.1.14"

29
public/ajax.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
require "../include/bittorrent.php";
dbconn();
loggedinorreturn();
$action = $_POST['action'] ?? 'noAction';
$params = $_POST['params'] ?? [];
function noAction()
{
throw new \RuntimeException("no Action");
}
try {
$result = call_user_func($action, $params);
exit(json_encode(success($result)));
} catch (\Throwable $exception) {
exit(json_encode(fail($exception->getMessage(), $_POST)));
}
function toggleUserMedalStatus($params)
{
global $CURUSER;
$rep = new \App\Repositories\MedalRepository();
return $rep->toggleUserMedalStatus($params['id'], $CURUSER['id']);
}

View File

@@ -1,22 +1,17 @@
<?php <?php
defined('NEXUS_START') || define('NEXUS_START', microtime(true)); require '../include/bittorrent_announce.php';
defined('IN_NEXUS') || define('IN_NEXUS', true); $apiLocalHost = nexus_env('TRACKER_API_LOCAL_HOST');
defined('IN_TRACKER') || define('IN_TRACKER', true); if ($apiLocalHost) {
require __DIR__ . '/../include/constants.php'; do_log("[TRACKER_API_LOCAL_HOST] $apiLocalHost");
require __DIR__ . '/../include/globalfunctions.php'; $response = request_local_api(trim($apiLocalHost, '/') . '/api/announce');
require __DIR__ . '/../include/functions_announce.php';
$announceApiLocalHost = nexus_env('ANNOUNCE_API_LOCAL_HOST');
if ($announceApiLocalHost) {
do_log("[GOING_TO_REQUEST_LOCAL_ANNOUNCE_URL] $announceApiLocalHost");
$response = request_local_announce_api($announceApiLocalHost);
if (empty($response)) { if (empty($response)) {
err("error from ANNOUNCE_API_LOCAL_HOST"); err("error from TRACKER_API_LOCAL_HOST");
} else { } else {
exit(benc_resp_raw($response)); exit(benc_resp_raw($response));
} }
} }
//continue the normal process //continue the normal process
require_once('../include/bittorrent_announce.php'); require ROOT_PATH . 'include/core.php';
dbconn_announce(); dbconn_announce();
do_log(nexus_json_encode($_SERVER)); do_log(nexus_json_encode($_SERVER));
//1. BLOCK ACCESS WITH WEB BROWSERS AND CHEATS! //1. BLOCK ACCESS WITH WEB BROWSERS AND CHEATS!

View File

@@ -38,7 +38,7 @@ if (!$row) {
) { ) {
permissiondenied(); permissiondenied();
} else { } else {
$owner = \App\Models\User::query()->with(['valid_medals'])->find($row['owner']); $owner = \App\Models\User::query()->with(['wearing_medals'])->find($row['owner']);
if (!$owner) { if (!$owner) {
$owner = \App\Models\User::defaultUser(); $owner = \App\Models\User::defaultUser();
} }
@@ -87,10 +87,10 @@ if (!$row) {
if (get_user_class() < $viewanonymous_class) if (get_user_class() < $viewanonymous_class)
$uprow = "<i>".$lang_details['text_anonymous']."</i>"; $uprow = "<i>".$lang_details['text_anonymous']."</i>";
else else
$uprow = "<i>".$lang_details['text_anonymous']."</i> (" . build_medal_image($owner->valid_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) . ")"; $uprow = "<i>".$lang_details['text_anonymous']."</i> (" . build_medal_image($owner->wearing_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) . ")";
} }
else { else {
$uprow = (isset($row['owner']) ? build_medal_image($owner->valid_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) : "<i>".$lang_details['text_unknown']."</i>"); $uprow = (isset($row['owner']) ? build_medal_image($owner->wearing_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) : "<i>".$lang_details['text_unknown']."</i>");
} }
if ($CURUSER["id"] == $row["owner"]) if ($CURUSER["id"] == $row["owner"])

View File

@@ -632,7 +632,7 @@ if ($action == "viewtopic")
$uidArr = array_keys($uidArr); $uidArr = array_keys($uidArr);
unset($arr); unset($arr);
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title'); $neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
$userInfoArr = \App\Models\User::query()->with(['valid_medals'])->find($uidArr, $neededColumns)->keyBy('id'); $userInfoArr = \App\Models\User::query()->with(['wearing_medals'])->find($uidArr, $neededColumns)->keyBy('id');
$pn = 0; $pn = 0;
$lpr = get_last_read_post_id($topicid); $lpr = get_last_read_post_id($topicid);
@@ -674,7 +674,7 @@ if ($action == "viewtopic")
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars($arr2["avatar"]) : ""); $avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars($arr2["avatar"]) : "");
$uclass = get_user_class_image($arr2["class"]); $uclass = get_user_class_image($arr2["class"]);
$by = build_medal_image($userInfo->valid_medals, 20) . get_username($posterid,false,true,true,false,false,true); $by = build_medal_image($userInfo->wearing_medals, 20) . get_username($posterid,false,true,true,false,false,true);
if (!$avatar) if (!$avatar)
$avatar = "pic/default_avatar.png"; $avatar = "pic/default_avatar.png";

View File

@@ -31,9 +31,6 @@ if (file_exists(__DIR__.'/../storage/framework/maintenance.php')) {
| |
*/ */
require dirname(__DIR__) . '/include/globalfunctions.php';
require dirname(__DIR__) . '/include/functions.php';
require __DIR__.'/../vendor/autoload.php'; require __DIR__.'/../vendor/autoload.php';
/* /*

View File

@@ -1,5 +1,17 @@
<?php <?php
require_once('../include/bittorrent_announce.php'); require_once('../include/bittorrent_announce.php');
$apiLocalHost = nexus_env('TRACKER_API_LOCAL_HOST');
if ($apiLocalHost) {
do_log("[TRACKER_API_LOCAL_HOST] $apiLocalHost");
$response = request_local_api(trim($apiLocalHost, '/') . '/api/scrape');
if (empty($response)) {
err("error from TRACKER_API_LOCAL_HOST");
} else {
exit(benc_resp_raw($response));
}
}
require ROOT_PATH . 'include/core.php';
//require_once('../include/benc.php'); //require_once('../include/benc.php');
dbconn_announce(); dbconn_announce();

View File

@@ -275,7 +275,7 @@ if ($user["avatar"])
tr_small($lang_userdetails['row_avatar'], return_avatar_image(htmlspecialchars(trim($user["avatar"]))), 1); tr_small($lang_userdetails['row_avatar'], return_avatar_image(htmlspecialchars(trim($user["avatar"]))), 1);
if ($userInfo->valid_medals->isNotEmpty()) { if ($userInfo->valid_medals->isNotEmpty()) {
tr_small($lang_userdetails['row_medal'], build_medal_image($userInfo->valid_medals), 1); tr_small($lang_userdetails['row_medal'], build_medal_image($userInfo->valid_medals, 200, $CURUSER['id'] == $user['id']), 1);
} }
$uclass = get_user_class_image($user["class"]); $uclass = get_user_class_image($user["class"]);
@@ -500,5 +500,21 @@ if (get_user_class() >= $prfmanage_class && $user["class"] < get_user_class())
} }
} }
end_main_frame(); end_main_frame();
echo <<<EOT
<script>
jQuery('input[type="checkbox"][name="medal_wearing_status"]').on("change", function (e) {
let input = jQuery(this);
let checked = input.prop("checked")
jQuery.post('ajax.php', {params: {id: this.value}, action: 'toggleUserMedalStatus'}, function (response) {
console.log(response)
if (response.ret != 0) {
input.prop("checked", !checked)
alert(response.msg)
}
}, 'json')
})
</script>
EOT;
stdfoot(); stdfoot();
?> ?>

View File

@@ -2,3 +2,4 @@
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
Route::get('announce', [\App\Http\Controllers\TrackerController::class, 'announce']); Route::get('announce', [\App\Http\Controllers\TrackerController::class, 'announce']);
Route::get('scrape', [\App\Http\Controllers\TrackerController::class, 'scrape']);