raw = $raw; } public function __toString(): string { if($this->raw & self::INHERIT) return 'inherit'; return '#' . str_pad(dechex($this->raw & 0xFFFFFF), 6, '0', STR_PAD_LEFT); } } class SeriaUser { private const KEY_CHARS = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'; private const KEY_LENGTH = 48; private ?PDO $pdo; private string $user_id = ''; private string $user_name = 'Anonymous'; private int $user_colour = SeriaUserColour::INHERIT; private int $user_rank = 0; private int $user_permissions = 0; private ?string $user_pass_key = null; private int $user_bytes_downloaded = 0; private int $user_bytes_uploaded = 0; public function __construct(?PDO $pdo = null) { $this->pdo = $pdo; } public function getId(): string { return $this->user_id; } public function getName(): string { return $this->user_name; } public function getColour(): SeriaUserColour { return new SeriaUserColour($this->user_colour); } public function getColourRaw(): int { return $this->user_colour; } public function getRank(): int { return $this->user_rank; } public function getPermissionsRaw(): int { return $this->user_permissions; } public function getPassKey(): string { if(!$this->isLoggedIn()) return ''; if(($passKey = $this->user_pass_key) === null) return $this->resetPassKey(); return $passKey; } public function getBytesDownloaded(): int { return $this->user_bytes_downloaded; } public function getBytesUploaded(): int { return $this->user_bytes_uploaded; } public function isLoggedIn() { return !empty($this->user_id); } public function isFlash(): bool { return $this->user_id === '1'; } public function canCreateTorrents(): bool { return $this->isFlash() || $this->user_id === '32' || $this->user_id === '145'; } public function canApproveTorrents(): bool { return $this->isFlash(); } public function canRecalculateInfoHash(): bool { return $this->isFlash(); } public function calculateRatio(): float { $bd = $this->getBytesDownloaded(); if($bd === 0) return 0; return $this->getBytesUploaded() / $bd; } public function getActiveTransferCounts(): stdClass { return SeriaTorrentPeer::countUserStats($this->pdo, $this); } public function getProfileSubmissions(): array { return $this->getSubmissions(-1, 3); } public function getSubmissions(int $startAt, int $take): array { return SeriaTorrent::byUser($this->pdo, $this, $startAt, $take); } public static function generatePassKey(int $length): string { $keyChars = strlen(self::KEY_CHARS) - 1; $bytes = str_repeat("\0", $length); for($i = 0; $i < $length; ++$i) $bytes[$i] = self::KEY_CHARS[random_int(0, $keyChars)]; return $bytes; } public function resetPassKey(): string { if(!$this->isLoggedIn()) return ''; $updatePassKey = $this->pdo->prepare('UPDATE `ser_users` SET `user_pass_key` = :pass WHERE `user_id` = :user'); $updatePassKey->bindValue('user', $this->user_id); do { $updatePassKey->bindValue('pass', $passKey = self::generatePassKey(self::KEY_LENGTH)); } while(!$updatePassKey->execute()); return $this->user_pass_key = $passKey; } public function incrementTransferCounts(int $bytesDownloaded, int $bytesUploaded): void { if(!$this->isLoggedIn()) return; $updateStats = $this->pdo->prepare('UPDATE `ser_users` SET `user_bytes_downloaded` = `user_bytes_downloaded` + :downloaded, `user_bytes_uploaded` = `user_bytes_uploaded` + :uploaded WHERE `user_id` = :user'); $updateStats->bindValue('downloaded', $bytesDownloaded); $updateStats->bindValue('uploaded', $bytesUploaded); $updateStats->bindValue('user', $this->user_id); if($updateStats->execute()) { $this->user_bytes_downloaded += $bytesDownloaded; $this->user_bytes_uploaded += $bytesUploaded; } } public static function anonymous(): self { return new static; } public static function fromMisuzu(PDO $pdo, stdClass $info): self { $update = $pdo->prepare('INSERT INTO `ser_users` (`user_id`, `user_name`, `user_colour`, `user_rank`, `user_permissions`) VALUES (:id, :name_1, :colour_1, :rank_1, :perms_1) ON DUPLICATE KEY UPDATE `user_name` = :name_2, `user_colour` = :colour_2, `user_rank` = :rank_2, `user_permissions` = :perms_2'); $update->bindValue('id', $info->user_id); $update->bindValue('name_1', $info->username); $update->bindValue('name_2', $info->username); $update->bindValue('colour_1', $info->colour_raw); $update->bindValue('colour_2', $info->colour_raw); $update->bindValue('rank_1', $info->hierarchy); $update->bindValue('rank_2', $info->hierarchy); $update->bindValue('perms_1', $info->perms); $update->bindValue('perms_2', $info->perms); $update->execute(); return self::byId($pdo, $info->user_id); } public static function byId(PDO $pdo, string $userId): self { $getUser = $pdo->prepare('SELECT `user_id`, `user_name`, `user_colour`, `user_rank`, `user_permissions`, `user_pass_key`, `user_bytes_downloaded`, `user_bytes_uploaded` FROM `ser_users` WHERE `user_id` = :user'); $getUser->bindValue('user', $userId); if(!$getUser->execute()) throw new SeriaUserExtremelyNotFoundException; if(($obj = $getUser->fetchObject(self::class, [$pdo])) === false) throw new SeriaUserNotFoundException; return $obj; } public static function byName(PDO $pdo, string $userName): self { $getUser = $pdo->prepare('SELECT `user_id`, `user_name`, `user_colour`, `user_rank`, `user_permissions`, `user_pass_key`, `user_bytes_downloaded`, `user_bytes_uploaded` FROM `ser_users` WHERE `user_name` = :user'); $getUser->bindValue('user', $userName); if(!$getUser->execute()) throw new SeriaUserExtremelyNotFoundException; if(($obj = $getUser->fetchObject(self::class, [$pdo])) === false) throw new SeriaUserNotFoundException; return $obj; } public static function byPassKey(PDO $pdo, string $passKey): self { $getUser = $pdo->prepare('SELECT `user_id`, `user_name`, `user_colour`, `user_rank`, `user_permissions`, `user_pass_key`, `user_bytes_downloaded`, `user_bytes_uploaded` FROM `ser_users` WHERE `user_pass_key` = :pass'); $getUser->bindValue('pass', $passKey); if(!$getUser->execute()) throw new SeriaUserExtremelyNotFoundException; if(($obj = $getUser->fetchObject(self::class, [$pdo])) === false) throw new SeriaUserNotFoundException; return $obj; } }