Merge pull request #5155 from HDInnovations/development

(Release) UNIT3D v9.2.0
This commit is contained in:
Roardom
2025-12-03 10:01:02 +00:00
committed by GitHub
1747 changed files with 60272 additions and 60307 deletions

View File

@@ -16,3 +16,4 @@ tmdb
tvdb
tvrage
weblate
hdinnovations

View File

@@ -20,4 +20,5 @@ sslmode
sqlite
sqlsrv
stringable
timebox
wheredoesnthave

View File

@@ -1,5 +1,6 @@
hdvinnie
rhilip
obi-wana
roardom
sindre
sindresorhus

View File

@@ -8,6 +8,3 @@ charset = utf-8
spelling_language = en-US
trim_trailing_whitespace = true
insert_final_newline = true
[*.vue]
indent_size = 2

View File

@@ -8,14 +8,15 @@ jobs:
- ubuntu-22.04
php-version:
- '8.4'
- '8.5'
name: php ${{ matrix.php-version }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup PHP 8.4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
@@ -26,7 +27,7 @@ jobs:
env:
REDIS_CONFIGURE_OPTS: --enable-redis
- name: Configure Bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install Composer Dependencies
@@ -34,6 +35,6 @@ jobs:
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --optimize-autoloader
- name: Install JS Dependencies
run: bun install
run: bun ci
- name: Compile Assets (Vite)
run: bun run build

26
.github/workflows/file-permissions.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: File permissions
on: [ push, pull_request ]
jobs:
pint:
strategy:
matrix:
operating-system:
- ubuntu-22.04
name: ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set file permissions
run: sudo find ${{ github.workspace }} -type f -exec chmod 664 {} \;
- name: Set folder permissions
run: sudo find ${{ github.workspace }} -type d -exec chmod 775 {} \;
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "automation: set git file permissions"
commit_user_name: unit3d-bot
commit_user_email: unit3d_gh_bot@protonmail.com
commit_author: unit3d-bot <unit3d_gh_bot@protonmail.com>

View File

@@ -8,6 +8,7 @@ jobs:
- ubuntu-22.04
php-version:
- '8.4'
- '8.5'
name: php ${{ matrix.php-version }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
services:
@@ -30,10 +31,10 @@ jobs:
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup PHP 8.4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}

View File

@@ -8,14 +8,15 @@ jobs:
- ubuntu-22.04
php-version:
- '8.4'
- '8.5'
name: php ${{ matrix.php-version }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup PHP 8.4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
@@ -30,9 +31,9 @@ jobs:
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --optimize-autoloader
- name: Run Pint
run: ./vendor/bin/pint
run: ./vendor/bin/pint -v
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "automation: laravel pint ci"
commit_user_name: unit3d-bot

View File

@@ -12,7 +12,7 @@ jobs:
contents: write # To push a branch
pull-requests: write # To create a PR from that branch
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install latest mdbook

View File

@@ -9,6 +9,7 @@ jobs:
- ubuntu-22.04
php-version:
- '8.4'
- '8.5'
name: php ${{ matrix.php-version }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
services:
@@ -31,10 +32,10 @@ jobs:
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup PHP 8.4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
@@ -47,7 +48,7 @@ jobs:
- name: Prepare The Laravel Environment
run: cp .env.example .env
- name: Configure Bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install Composer Dependencies
@@ -55,7 +56,7 @@ jobs:
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --optimize-autoloader
- name: Install JS Dependencies
run: bun install
run: bun ci
- name: Compile Assets (Vite)
run: bun run build
- name: Generate Application Key

View File

@@ -10,22 +10,21 @@ jobs:
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Configure Bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install JS Dependencies
run: bun install
run: bun ci
- name: Run Prettier
run: bunx prettier -w *
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "automation: prettier blade ci"
commit_user_name: unit3d-bot
commit_user_email: unit3d_gh_bot@protonmail.com
commit_author: unit3d-bot <unit3d_gh_bot@protonmail.com>

View File

@@ -7,7 +7,7 @@ jobs:
operating-system:
- ubuntu-22.04
php-version:
- '8.4'
- '8.5'
name: php ${{ matrix.php-version }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
services:
@@ -30,10 +30,10 @@ jobs:
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup PHP 8.4
- name: Setup PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
@@ -88,7 +88,7 @@ jobs:
rm database/schema/mysql-schema-new.sql
- name: Commit Schema Changes
if: steps.diff.outputs.has_changes == 'true'
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: "automation: update schema dump"
commit_user_name: unit3d-bot

View File

@@ -24,16 +24,6 @@
"singleQuote": true
}
},
{
"files": [
"*.vue"
],
"options": {
"printWidth": 100,
"semi": true,
"singleQuote": true
}
},
{
"files": [
"*.scss"

View File

@@ -43,7 +43,8 @@ Examples:
## Code Style
**[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)**
- Check the code style with ``$ composer check-style`` and fix it with ``$ composer fix-style``.
- Check the code style with ``$ ./vendor/bin/pint . --test`` and fix it with ``$ ./vendor/bin/pint .``.
- Prettier is also used to format Blade, SCSS, and JS: ``$ ./node_modules/.bin/prettier . --write``.
**[Follow Laravel naming conventions](https://github.com/alexeymezenin/laravel-best-practices/blob/master/README.md)**

View File

@@ -5,11 +5,11 @@
<p align="center">
<a href="http://laravel.com"><img src="https://img.shields.io/badge/Laravel-12-f4645f.svg" /></a>
<a href="https://github.com/HDInnovations/UNIT3D/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-AGPL%20v3.0-yellow.svg" /></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/lint.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/lint.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/phpunit-test.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/phpunit-test.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/compile-assets-test.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/compile-assets-test.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/larastan.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/larastan.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/prettier-blade.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/prettier-blade.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D/actions/workflows/lint.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D/actions/workflows/lint.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D/actions/workflows/phpunit-test.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D/actions/workflows/phpunit-test.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D/actions/workflows/compile-assets-test.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D/actions/workflows/compile-assets-test.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D/actions/workflows/larastan.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D/actions/workflows/larastan.yml/badge.svg?branch=master" /></a>
<a href="https://github.com/HDInnovations/UNIT3D/actions/workflows/prettier-blade.yml/badge.svg?branch=master"><img src="https://github.com/HDInnovations/UNIT3D/actions/workflows/prettier-blade.yml/badge.svg?branch=master" /></a>
<a href="https://hosted.weblate.org/engage/unit3d/">
<img src="https://hosted.weblate.org/widget/unit3d/svg-badge.svg" alt="Translation status" />
</a>
@@ -21,13 +21,10 @@
2. [Installation](#installation)
3. [Updating](#updating)
4. [Documentation](#docs)
5. [Contributing](#contributing)
6. [Translations](#translations)
7. [License](#license)
8. [Services](#services)
9. [Discord](#discord)
10. [Donations](#donations)
11. [Shoutouts](#shoutouts)
5. [Products, Services and Support](#hdinnovations)
6. [Contributing](#contributing)
7. [Translations](#translations)
8. [License](#license)
## <a name="introduction"></a> 🧐 Introduction
@@ -36,7 +33,7 @@ UNIT3D (pronounced "united") is a modern Private Torrent Tracker software built
## <a name="installation"></a> 🖥️ Installation
No official script or documentation is available at this time.
The official script is no longer available at this time. A new one will be provided soon.
## <a name="updating"></a> 🖥️ Updating
@@ -46,11 +43,17 @@ To update your installation to the latest version, run the following command. Th
## <a name="docs"></a> 📚 Documentation
https://hdinnovations.github.io/UNIT3D/
https://hdinnovations.github.io/UNIT3D
## <a name="hdinnovations"></a> 🛠️ Products, Services and Support
HDInnovations offers a variety of services to help you with your UNIT3D instance. We offer services such as installation, updating, server tuning, dependency tuning, themes, porting from different codebase and more. We have a Discord server for support and general discussion. This is a private server, and you will need to be invited to join. There is a small fee to join the server to help support the development of UNIT3D.
https://hdinnovations.github.io/HDInnovations
## <a name="contributing"></a> 🤝 Contributing
Please read [CONTRIBUTING.md](https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/master/CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests to us. A massive thankyou to all of our <a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/graphs/contributors">contributors</a>.
Please read [CONTRIBUTING.md](https://github.com/HDInnovations/UNIT3D/blob/master/CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests to us. A massive thank you to all of our <a href="https://github.com/HDInnovations/UNIT3D/graphs/contributors">contributors</a>.
## <a name="translations"></a> 🌎 Translations
@@ -62,40 +65,6 @@ We use Weblate for translations. You can easily contribute to translations at ht
## <a name="license"></a> 📜 License
This project is licensed under the AGPL v3.0 License. See the [LICENSE](https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/master/LICENSE.md) file for details.
This project is licensed under the AGPL v3.0 License. See the [LICENSE](https://github.com/HDInnovations/UNIT3D/blob/master/LICENSE.md) file for details.
## <a name="services"></a> 🛠️ Services
HDInnovations offers a variety of services to help you with your UNIT3D instance. We offer services such as installation, updating, server tuning, dependency tuning, themes, porting from different codebase and more. For more information, please visit our email us at [hdinnovations@protonmail.com](mailto:hdinnovations@protonmail.com?subject=[Services]%20UNIT3D%20)
## <a name="discord"></a> 💬 Discord
We have a Discord server for support and general discussion. This is a private server, and you will need to be invited to join. There is a small fee to join the server to help support the development of UNIT3D. For more information about purchasing a Discord Pass, please email us at [hdinnovations@protonmail.com](mailto:hdinnovations@protonmail.com?subject=[Discord]%20UNIT3D%20)
## <a name="donations"></a> 💰 Donations
If you would like to support the development of UNIT3D, please consider donating to keep the project alive.
*Donations are non-refundable and made voluntarily without exchange of goods or services. It is support the project as a whole.*
You can donate using the following link:
[Donate](https://square.link/u/VjB1CNfm)
#### You can also donate via BTC cryptocurrency using the following addresses:
- Bitcoin (BTC): `bc1qzgrgfrw2y4x20ywzu49q50ptxg8jeuqv3tmx9v`
- Bitcoin via Email (Proton Wallet Only): `hdinnovations@protonmail.com`
## <a name="shoutouts"></a> 🎉 Shoutouts
We would like to extend our heartfelt thanks to the following companies, sites and individuals for their generous donations and support in keeping UNIT3D alive:
### 👤 Individuals
<!-- cspell:disable-next-line -->
**airclay**, **Henriette**, **Jocelyn**, **Marcus**, **Poppers**, **sam**, **samual**, **schizobolic**, **x64**
### 🏢 Companies
<a href="https://scoutapm.com/?utm_source=github&utm_medium=referral&utm_campaign=opensource_referral"><img src="https://i.postimg.cc/g29XCJDh/Logo-rounded-square-495x495-1.png" height="30px;"></a>&nbsp;
<a href="https://www.jetbrains.com/?from=UNIT3D"><img src="https://i.imgur.com/KgDXZV8.png" height="30px;"></a>&nbsp;

View File

@@ -19,4 +19,4 @@
Please email us at hdinnovations@protonmail.com
Thank you for improving the security of unit3d-community-edition.
Thank you for improving the security of unit3d.

View File

@@ -51,7 +51,7 @@ class CreateNewUser implements CreatesNewUsers
'email' => [
'required',
'string',
'email',
'email:rfc,dns',
'max:70',
'unique:users',
Rule::when(config('email-blacklist.enabled') === true, fn () => new EmailBlacklist()),

View File

@@ -1,65 +0,0 @@
<?php
declare(strict_types=1);
/**
* 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\Actions\Fortify;
use App\Models\Group;
use App\Models\User;
use App\Services\Unit3dAnnounce;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
* @throws ValidationException
*/
public function reset(User $user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
]);
$validatingGroupId = Group::query()->where('slug', '=', 'validating')->soleValue('id');
if ($user->group_id === $validatingGroupId) {
$user->group_id = Group::query()->where('slug', '=', 'user')->soleValue('id');
cache()->forget('user:'.$user->passkey);
Unit3dAnnounce::addUser($user);
}
if (!$user->hasVerifiedEmail()) {
$user->markEmailAsVerified();
}
$user->save();
$user->passwordResetHistories()->create();
}
}

View File

@@ -37,7 +37,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
'email' => [
'required',
'string',
'email',
'email:rfc,dns',
'max:255',
Rule::unique('users')->ignore($user->id),
],

View File

@@ -0,0 +1,177 @@
<?php
declare(strict_types=1);
/**
* 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\Bots;
use App\Models\IgdbGame;
use App\Models\TmdbMovie;
use App\Models\TmdbTv;
use App\Models\Torrent;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;
class IRCAnnounceBotExternal
{
public static function postAnnounceMsg(Torrent $torrent): bool
{
if (! config('irc-bot-external.is_enabled')) {
return false;
}
$appurl = config('app.url');
$announceTypeEnum = 0; // 0 NEW
$originEnum = match (true) {
$torrent->personal_release == true => 2,
$torrent->internal => 1,
default => 0,
};
$leechTypeEnum = match ($torrent->free) {
100 => 1,
75 => 2,
50 => 3,
25 => 4,
default => 0,
};
if ($torrent->doubleup) {
$leechTypeEnum = match ($leechTypeEnum) {
0 => 5,
1 => 6,
2 => 7,
3 => 8,
4 => 9,
};
}
$meta = null;
$category = $torrent->category;
if ($torrent->tmdb_movie_id > 0 || $torrent->tmdb_tv_id > 0) {
$meta = match (true) {
$category->tv_meta => TmdbTv::find($torrent->tmdb_tv_id),
$category->movie_meta => TmdbMovie::find($torrent->tmdb_movie_id),
$category->game_meta => IgdbGame::find($torrent->igdb),
default => null,
};
}
return self::post([
'id' => $torrent->id,
'url' => \sprintf('%s/torrents/%d', $appurl, $torrent->id),
'name' => $torrent->name,
'uploader' => $torrent->anon ? 'Anonymous' : $torrent->user->username,
'size' => $torrent->getSize(),
'size_bytes' => $torrent->size,
'announce_type_enum' => $announceTypeEnum,
'category_enum' => $category->id,
'category_name' => $category->name,
'origin_enum' => $originEnum,
'leech_type_enum' => $leechTypeEnum,
'upload_time_unix_epoch' => $torrent->created_at->getTimestamp(),
'freeleech' => $torrent->free > 0,
'freeleech_percent' => $torrent->free,
'double_up' => $torrent->doubleup,
'resolution' => $torrent->resolution?->name ?? '',
'type' => $torrent->type->name,
'release_year' => $meta?->release_date?->format('Y') ?? $meta?->first_air_date?->format('Y') ?? $meta?->first_release_date?->format('Y'),
'title' => $meta->title ?? $torrent->name,
'metadata' => [
'tmdb_id' => $torrent->tmdb_movie_id ?? $torrent->tmdb_tv_id,
'imdb_id' => $torrent->imdb,
'tvdb_id' => $torrent->tvdb,
'mal_id' => $torrent->mal,
'igdb_id' => $torrent->igdb,
],
]);
}
/**
* @param array<mixed> $data
*/
private static function post(array $data): bool
{
if (! self::isConfigValid()) {
return false;
}
try {
$response = self::buildHttpClient()->post(self::buildRoute(), $data);
} catch (Throwable) {
Log::error('External IRC Announce error - POST');
return false;
}
if (! $response->ok()) {
Log::notice('External IRC Announce error - POST', [
'status' => $response->status(),
'body' => $response->body(),
'data' => $data,
]);
return false;
}
return true;
}
private static function isConfigValid(): bool
{
return config('irc-bot-external.is_enabled') === true
&& config('irc-bot-external.channel') !== null
&& ((
config('irc-bot-external.unix_socket') !== null
&& config('irc-bot-external.host') === null
&& config('irc-bot-external.port') === null
) || (
config('irc-bot-external.unix_socket') === null
&& config('irc-bot-external.host') !== null
&& config('irc-bot-external.port') !== null
&& config('irc-bot-external.key') !== null
));
}
private static function buildRoute(): string
{
$channel = ltrim(config('irc-bot-external.channel'), '#');
if (config('irc-bot-external.unix_socket') === null) {
$route = 'http://'.config('irc-bot-external.host').':'.config('irc-bot-external.port').'/api/webhook/announce/'.$channel.'?apikey='.config('irc-bot-external.key');
} else {
$route = 'http://localhost/api/webhook/announce/'.$channel.'?apikey='.config('irc-bot-external.key');
}
return rtrim($route, '/');
}
private static function buildHttpClient(): \Illuminate\Http\Client\PendingRequest
{
$client = Http::createPendingRequest();
if (config('irc-bot-external.unix_socket') !== null) {
$client->withOptions([
'curl' => [
CURLOPT_UNIX_SOCKET_PATH => config('irc-bot-external.unix_socket'),
],
]);
}
return $client;
}
}

View File

@@ -287,8 +287,8 @@ class NerdBot
if ($echoes->doesntContain(fn ($echo) => $echo->bot_id == $this->bot->id)) {
$echoes->push(UserEcho::create([
'user_id' => $target->id,
'target_id' => $this->bot->id,
'user_id' => $target->id,
'bot_id' => $this->bot->id,
]));
cache()->put('user-echoes'.$target->id, $echoes, 3600);
@@ -305,9 +305,9 @@ class NerdBot
if ($audibles->doesntContain(fn ($audible) => $audible->bot_id == $this->bot->id)) {
$audibles->push(UserAudible::create([
'user_id' => $target->id,
'target_id' => $this->bot->id,
'status' => 0,
'user_id' => $target->id,
'bot_id' => $this->bot->id,
'status' => false,
]));
cache()->put('user-audibles'.$target->id, $audibles, 3600);

View File

@@ -73,14 +73,13 @@ class SystemBot
/**
* Send Gift.
*
* @param array<string> $note
* @param numeric-string $amount
*/
public function putGift(string $receiver = '', float $amount = 0, array $note = ['']): string
public function putGift(string $receiver, string $amount, string $note): string
{
$output = implode(' ', $note);
$v = validator(['receiver' => $receiver, 'amount' => $amount, 'note' => $output], [
$v = validator(['receiver' => $receiver, 'amount' => $amount, 'note' => $note], [
'receiver' => 'required|string|exists:users,username',
'amount' => \sprintf('required|numeric|min:1|max:%s', $this->target->seedbonus),
'amount' => \sprintf('required|decimal:0,2|min:1|max:%s', $this->target->seedbonus),
'note' => 'required|string',
]);
@@ -91,15 +90,16 @@ class SystemBot
return 'Your BON gift could not be sent.';
}
$value = $amount;
$recipient->increment('seedbonus', $value);
$this->target->decrement('seedbonus', $value);
$amount = (float) $amount;
$recipient->increment('seedbonus', $amount);
$this->target->decrement('seedbonus', $amount);
$gift = Gift::create([
'sender_id' => $this->target->id,
'recipient_id' => $recipient->id,
'bon' => $value,
'message' => $output,
'bon' => $amount,
'message' => $note,
]);
if ($this->target->id !== $recipient->id && $recipient->acceptsNotification($this->target, $recipient, 'bon', 'show_bon_gift')) {
@@ -110,7 +110,7 @@ class SystemBot
$recipientUrl = href_profile($recipient);
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has gifted %s BON to [url=%s]%s[/url]', $profileUrl, $this->target->username, $value, $recipientUrl, $recipient->username)
\sprintf('[url=%s]%s[/url] has gifted %s BON to [url=%s]%s[/url]', $profileUrl, $this->target->username, $amount, $recipientUrl, $recipient->username)
);
return 'Your gift to '.$recipient->username.' for '.$amount.' BON has been sent!';
@@ -173,8 +173,8 @@ class SystemBot
if ($echoes->doesntContain(fn ($echo) => $echo->bot_id == $this->bot->id)) {
$echoes->push(UserEcho::create([
'user_id' => $target->id,
'target_id' => $this->bot->id,
'user_id' => $target->id,
'bot_id' => $this->bot->id,
]));
cache()->put('user-echoes'.$target->id, $echoes, 3600);
@@ -191,9 +191,9 @@ class SystemBot
if ($audibles->doesntContain(fn ($audible) => $audible->bot_id == $this->bot->id)) {
$audibles->push(UserAudible::create([
'user_id' => $target->id,
'target_id' => $this->bot->id,
'status' => 0,
'user_id' => $target->id,
'bot_id' => $this->bot->id,
'status' => false,
]));
cache()->put('user-audibles'.$target->id, $audibles, 3600);

View File

@@ -41,7 +41,7 @@ class AutoBanDisposableUsers extends Command
*
* @var string
*/
protected $description = 'Ban User If they are using a disposable email';
protected $description = 'Ban user if they are using a disposable email';
/**
* Execute the console command.
@@ -51,7 +51,7 @@ class AutoBanDisposableUsers extends Command
final public function handle(): void
{
if (!cache()->has(config('email-blacklist.cache-key'))) {
$this->comment('Email Blacklist Cache Key Not Found. Skipping!');
$this->comment('Email blacklist cache key not found. Skipping!');
return;
}
@@ -99,6 +99,6 @@ class AutoBanDisposableUsers extends Command
}
});
$this->comment('Automated User Banning Command Complete');
$this->comment('Automated user banning command complete');
}
}

View File

@@ -39,7 +39,7 @@ class AutoBonAllocation extends Command
*
* @var string
*/
protected $description = 'Allocates Bonus Points To Users Based On Peer Activity.';
protected $description = 'Allocates bonus points to users based on peer activity.';
/**
* Execute the console command.
@@ -124,6 +124,6 @@ class AutoBonAllocation extends Command
});
}, 25);
$this->comment('Automated BON Allocation Command Complete in '.(int) now()->diffInMilliseconds($now, true).' ms');
$this->comment('Automated BON allocation command complete in '.(int) now()->diffInMilliseconds($now, true).' ms');
}
}

View File

@@ -37,7 +37,7 @@ class AutoCorrectHistory extends Command
*
* @var string
*/
protected $description = 'Corrects History Records Said To Be Active Even Though Really Are Not Due To Not Receiving A STOPPED Event From Client.';
protected $description = 'Corrects history records said to be active even though really are not due to not receiving a stopped event from client.';
/**
* Execute the console command.
@@ -54,6 +54,6 @@ class AutoCorrectHistory extends Command
'updated_at' => DB::raw('updated_at'),
]);
$this->comment('Automated History Record Correction Command Complete');
$this->comment('Automated history record correction command complete');
}
}

View File

@@ -39,7 +39,7 @@ class AutoDeactivateWarning extends Command
*
* @var string
*/
protected $description = 'Automatically Deactivates User Warnings If Expired';
protected $description = 'Automatically deactivates user warnings if expired';
/**
* Execute the console command.
@@ -58,7 +58,7 @@ class AutoDeactivateWarning extends Command
fn ($query) => $query
->where('expires_on', '<=', $current)
->orWhereHas(
'torrenttitle.history',
'torrent.history',
fn ($query) => $query
->whereColumn('history.user_id', '=', 'warnings.user_id')
->where('history.seedtime', '>=', config('hitrun.seedtime'))
@@ -70,7 +70,7 @@ class AutoDeactivateWarning extends Command
$warning->update(['active' => false]);
// Add user to usersWithExpiredWarnings array
$usersWithExpiredWarnings[$warning->user_id] = $warning->warneduser;
$usersWithExpiredWarnings[$warning->user_id] = $warning->user;
}
});
@@ -80,21 +80,21 @@ class AutoDeactivateWarning extends Command
}
// Calculate User Warning Count and Enable DL Priv If Required.
Warning::with('warneduser')
Warning::with('user')
->select(DB::raw('user_id, SUM(active = TRUE) as value'))
->groupBy('user_id')
->having('value', '<', config('hitrun.max_warnings'))
->whereRelation('warneduser', 'can_download', '=', false)
->whereRelation('user', 'can_download', '=', false)
->chunkById(100, function ($warnings): void {
foreach ($warnings as $warning) {
$warning->warneduser->update(['can_download' => 1]);
$warning->user->update(['can_download' => 1]);
cache()->forget('user:'.$warning->warneduser->passkey);
cache()->forget('user:'.$warning->user->passkey);
Unit3dAnnounce::addUser($warning->warneduser);
Unit3dAnnounce::addUser($warning->user);
}
}, 'user_id');
$this->comment('Automated Warning Deactivation Command Complete');
$this->comment('Automated warning deactivation command complete');
}
}

View File

@@ -78,6 +78,6 @@ class AutoDisableInactiveUsers extends Command
}
});
$this->comment('Automated User Disable Command Complete');
$this->comment('Automated user disable command complete');
}
}

View File

@@ -38,7 +38,7 @@ class AutoFlushPeers extends Command
*
* @var string
*/
protected $description = 'Flushes Ghost Peers';
protected $description = 'Flushes ghost peers';
/**
* Execute the console command.
@@ -97,6 +97,6 @@ class AutoFlushPeers extends Command
}
}
$this->comment('Automated Flush Ghost Peers Command Complete');
$this->comment('Automated flush ghost peers command complete');
}
}

View File

@@ -38,7 +38,7 @@ class AutoGroup extends Command
*
* @var string
*/
protected $description = 'Automatically Change A Users Group Class If Requirements Met';
protected $description = 'Automatically change a users group class if requirements met';
/**
* Execute the console command.
@@ -108,6 +108,6 @@ class AutoGroup extends Command
});
$elapsed = (int) $now->diffInSeconds(now(), true);
$this->comment('Automated User Group Command Complete ('.$elapsed.' s)');
$this->comment('Automated user group command complete ('.$elapsed.' s)');
}
}

View File

@@ -39,7 +39,7 @@ class AutoHighspeedTag extends Command
*
* @var string
*/
protected $description = 'Updates Torrents Highspeed Tag Based On Registered Seedboxes.';
protected $description = 'Updates torrents highspeed tag based on registered seedboxes.';
/**
* Execute the console command.
@@ -68,6 +68,6 @@ class AutoHighspeedTag extends Command
'updated_at' => DB::raw('updated_at'),
]);
$this->comment('Automated High Speed Torrents Command Complete');
$this->comment('Automated high speed torrents command complete');
}
}

View File

@@ -44,7 +44,7 @@ class AutoNerdStat extends Command
*
* @var string
*/
protected $description = 'Automatically Posts Daily Nerd Stat To Shoutbox';
protected $description = 'Automatically posts daily nerd stat to shoutbox';
/**
* Execute the console command.
@@ -79,26 +79,26 @@ class AutoNerdStat extends Command
// Generate the message based on the selected stat.
$message = match ($stats) {
'birthday' => config('other.title').' Birthday Is [b]'.config('other.birthdate').'[/b]!',
'logins' => 'In The Last 24 Hours [color=#93c47d][b]'.DB::table('users')->whereNotNull('last_login')->where('last_login', '>', now()->subDay())->count().'[/b][/color] Unique Users Have Logged Into '.config('other.title').'!',
'uploads' => 'In The Last 24 Hours [color=#93c47d][b]'.DB::table('torrents')->where('created_at', '>', now()->subDay())->count().'[/b][/color] Torrents Have Been Uploaded To '.config('other.title').'!',
'users' => 'In The Last 24 Hours [color=#93c47d][b]'.DB::table('users')->where('created_at', '>', now()->subDay())->count().'[/b][/color] Users Have Registered To '.config('other.title').'!',
'fl25' => 'There Are Currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 25)->count().'[/b][/color] 25% Freeleech Torrents On '.config('other.title').'!',
'fl50' => 'There Are Currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 50)->count().'[/b][/color] 50% Freeleech Torrents On '.config('other.title').'!',
'fl75' => 'There Are Currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 75)->count().'[/b][/color] 75% Freeleech Torrents On '.config('other.title').'!',
'fl100' => 'There Are Currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 100)->count().'[/b][/color] 100% Freeleech Torrents On '.config('other.title').'!',
'du' => 'There Are Currently [color=#93c47d][b]'.DB::table('torrents')->where('doubleup', '=', 1)->count().'[/b][/color] Double Upload Torrents On '.config('other.title').'!',
'peers' => 'Currently There Are [color=#93c47d][b]'.DB::table('peers')->where('active', '=', 1)->count().'[/b][/color] Peers On '.config('other.title').'!',
'bans' => 'In The Last 24 Hours [color=#dd7e6b][b]'.DB::table('bans')->whereNotNull('ban_reason')->where('created_at', '>', now()->subDay())->count().'[/b][/color] Users Have Been Banned From '.config('other.title').'!',
'unbans' => 'In The Last 24 Hours [color=#dd7e6b][b]'.DB::table('bans')->whereNotNull('unban_reason')->where('removed_at', '>', now()->subDay())->count().'[/b][/color] Users Have Been Unbanned From '.config('other.title').'!',
'warnings' => 'In The Last 24 Hours [color=#dd7e6b][b]'.DB::table('warnings')->where('created_at', '>', now()->subDay())->count().'[/b][/color] Hit and Run Warnings Have Been Issued On '.config('other.title').'!',
'king' => config('other.title').' Is King!',
default => 'Nerd Stat Error!',
'logins' => 'In the last 24 hours [color=#93c47d][b]'.DB::table('users')->whereNotNull('last_login')->where('last_login', '>', now()->subDay())->count().'[/b][/color] unique users have logged into '.config('other.title').'!',
'uploads' => 'In the last 24 hours [color=#93c47d][b]'.DB::table('torrents')->where('created_at', '>', now()->subDay())->count().'[/b][/color] torrents have been uploaded to '.config('other.title').'!',
'users' => 'In the last 24 hours [color=#93c47d][b]'.DB::table('users')->where('created_at', '>', now()->subDay())->count().'[/b][/color] users have registered to '.config('other.title').'!',
'fl25' => 'There are currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 25)->count().'[/b][/color] 25% freeleech torrents on '.config('other.title').'!',
'fl50' => 'There are currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 50)->count().'[/b][/color] 50% freeleech torrents on '.config('other.title').'!',
'fl75' => 'There are currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 75)->count().'[/b][/color] 75% freeleech torrents on '.config('other.title').'!',
'fl100' => 'There are currently [color=#93c47d][b]'.DB::table('torrents')->where('free', '=', 100)->count().'[/b][/color] 100% freeleech torrents on '.config('other.title').'!',
'du' => 'There are currently [color=#93c47d][b]'.DB::table('torrents')->where('doubleup', '=', 1)->count().'[/b][/color] double upload torrents on '.config('other.title').'!',
'peers' => 'Currently there are [color=#93c47d][b]'.DB::table('peers')->where('active', '=', 1)->count().'[/b][/color] peers on '.config('other.title').'!',
'bans' => 'In the last 24 hours [color=#dd7e6b][b]'.DB::table('bans')->whereNotNull('ban_reason')->where('created_at', '>', now()->subDay())->count().'[/b][/color] users have been banned from '.config('other.title').'!',
'unbans' => 'In the last 24 hours [color=#dd7e6b][b]'.DB::table('bans')->whereNotNull('unban_reason')->where('removed_at', '>', now()->subDay())->count().'[/b][/color] users have been unbanned from '.config('other.title').'!',
'warnings' => 'In the last 24 hours [color=#dd7e6b][b]'.DB::table('warnings')->where('created_at', '>', now()->subDay())->count().'[/b][/color] hit and run warnings have been issued on '.config('other.title').'!',
'king' => config('other.title').' is king!',
default => 'Nerd stat error!',
};
// Post the message to the chatbox.
$this->chatRepository->systemMessage($message);
// Output a success message to the console.
$this->comment('Automated Nerd Stat Command Complete');
$this->comment('Automated nerd stat command complete');
}
}

View File

@@ -37,7 +37,7 @@ class AutoPreWarning extends Command
*
* @var string
*/
protected $description = 'Automatically Sends Pre Warning Notifications To Users';
protected $description = 'Automatically sends pre warning notifications to users';
/**
* Execute the console command.
@@ -62,7 +62,7 @@ class AutoPreWarning extends Command
->whereRelation('user.group', 'is_immune', '=', false)
->whereRelation('user', 'is_donor', '=', false)
->whereHas('torrent', fn ($query) => $query->whereRaw('history.actual_downloaded > torrents.size * ?', [config('hitrun.buffer') / 100]))
->whereDoesntHave('user.warnings', fn ($query) => $query->withTrashed()->whereColumn('warnings.torrent', '=', 'history.torrent_id'))
->whereDoesntHave('user.warnings', fn ($query) => $query->withTrashed()->whereColumn('warnings.torrent_id', '=', 'history.torrent_id'))
->get();
$usersWithPreWarnings = [];
@@ -85,6 +85,6 @@ class AutoPreWarning extends Command
$user->notify(new UserPreWarning($user));
}
$this->comment('Automated User Pre-Warning Command Complete');
$this->comment('Automated user pre-warning command complete');
}
}

View File

@@ -36,7 +36,7 @@ class AutoRecycleAudits extends Command
*
* @var string
*/
protected $description = 'Recycle Audits Once X Days Old.';
protected $description = 'Recycle audits once X days old.';
/**
* Execute the console command.
@@ -49,6 +49,6 @@ class AutoRecycleAudits extends Command
->where('created_at', '<', now()->subDays(config('audit.recycle'))->max(Carbon::createFromTimestamp(0)))
->delete();
$this->comment('Automated Audit Recycle Command Complete');
$this->comment('Automated audit recycle command complete');
}
}

View File

@@ -36,7 +36,7 @@ class AutoRecycleClaimedTorrentRequests extends Command
*
* @var string
*/
protected $description = 'Recycle Torrent Requests That Were Claimed But Not Filled Within 7 Days.';
protected $description = 'Recycle torrent requests that were claimed but not filled within 7 days.';
/**
* AutoRecycleClaimedTorrentRequests Constructor.
@@ -75,6 +75,6 @@ class AutoRecycleClaimedTorrentRequests extends Command
}
});
$this->comment('Automated Request Claim Reset Command Complete');
$this->comment('Automated request claim reset command complete');
}
}

View File

@@ -35,7 +35,7 @@ class AutoRecycleFailedLogins extends Command
*
* @var string
*/
protected $description = 'Recycle Failed Logins Once 30 Days Old.';
protected $description = 'Recycle failed logins once 30 days old.';
/**
* Execute the console command.
@@ -48,6 +48,6 @@ class AutoRecycleFailedLogins extends Command
->where('created_at', '<', now()->subDays(30))
->delete();
$this->comment('Automated Purge Old Failed Logins Command Complete');
$this->comment('Automated purge old failed logins command complete');
}
}

View File

@@ -35,7 +35,7 @@ class AutoRecycleInvites extends Command
*
* @var string
*/
protected $description = 'Recycle Invites That Are Expired.';
protected $description = 'Recycle invites that are expired.';
/**
* Execute the console command.
@@ -50,6 +50,6 @@ class AutoRecycleInvites extends Command
->where('expires_on', '<', now())
->delete();
$this->comment('Automated Purge Unaccepted Invites Command Complete');
$this->comment('Automated purge unaccepted invites command complete');
}
}

View File

@@ -37,7 +37,7 @@ class AutoRefundDownload extends Command
*
* @var string
*/
protected $description = 'Refunds Download To Users Based On Seed Time.';
protected $description = 'Refunds download to users based on seed time.';
/**
* Execute the console command.
@@ -73,6 +73,6 @@ class AutoRefundDownload extends Command
'history.updated_at' => DB::raw('history.updated_at'),
]);
$this->comment('Automated Download Refund Command Complete');
$this->comment('Automated download refund command complete');
}
}

View File

@@ -47,7 +47,7 @@ class AutoRemoveFeaturedTorrent extends Command
*
* @var string
*/
protected $description = 'Automatically Removes Featured Torrents If Expired';
protected $description = 'Automatically removes featured torrents if expired';
/**
* Execute the console command.
@@ -81,6 +81,6 @@ class AutoRemoveFeaturedTorrent extends Command
cache()->forget('featured-torrent-ids');
$this->comment('Automated Removal Featured Torrents Command Complete');
$this->comment('Automated removal featured torrents command complete');
}
}

View File

@@ -40,7 +40,7 @@ class AutoRemovePersonalFreeleech extends Command
*
* @var string
*/
protected $description = 'Automatically Removes A Users Personal Freeleech If It Has Expired';
protected $description = 'Automatically removes a users personal freeleech if it has expired';
/**
* Execute the console command.
@@ -62,6 +62,6 @@ class AutoRemovePersonalFreeleech extends Command
Unit3dAnnounce::removePersonalFreeleech($pfl->user_id);
}
$this->comment('Automated Removal User Personal Freeleech Command Complete');
$this->comment('Automated removal user personal freeleech command complete');
}
}

View File

@@ -38,7 +38,7 @@ class AutoRemoveTimedTorrentBuffs extends Command
*
* @var string
*/
protected $description = 'Automatically Removes Torrent Buffs If Expired';
protected $description = 'Automatically removes torrent buffs if expired';
/**
* Execute the console command.
@@ -79,6 +79,6 @@ class AutoRemoveTimedTorrentBuffs extends Command
'bumped_at' => DB::raw('created_at'),
]);
$this->comment('Automated Removal Of Expired Torrent Buffs Command Complete');
$this->comment('Automated removal of expired torrent buffs command complete');
}
}

View File

@@ -47,6 +47,6 @@ class AutoResetUserFlushes extends Command
// Updates own_flushes for each user
User::where('own_flushes', '<', 2)->update(['own_flushes' => 2]);
$this->comment('Automated Reset User Flushes Command Complete');
$this->comment('Automated reset user flushes command complete');
}
}

View File

@@ -47,7 +47,7 @@ class AutoRewardResurrection extends Command
*
* @var string
*/
protected $description = 'Automatically Hands Out Rewards For Successful Resurrections';
protected $description = 'Automatically hands out rewards for successful resurrections';
/**
* Execute the console command.
@@ -101,6 +101,6 @@ class AutoRewardResurrection extends Command
}
});
$this->comment('Automated Reward Resurrections Command Complete');
$this->comment('Automated reward resurrections command complete');
}
}

View File

@@ -127,6 +127,6 @@ class AutoSoftDeleteDisabledUsers extends Command
$user->delete();
}
$this->comment('Automated Soft Delete Disabled Users Command Complete');
$this->comment('Automated soft delete disabled users command complete');
}
}

View File

@@ -97,7 +97,7 @@ class AutoUpsertHistories extends Command
// We need to make sure seeder and active are updated after seedtime, otherwise the seedtime logic for ensuring it's not a new announce and the left was 0 in the last announce breaks.
// Unfortunately, laravel sorts the keys in this array alphabetically when inserting so reordering the keys themselves in this array doesn't work.
// This leaves us with this hacky fix.
'seedtime' => DB::raw('CASE WHEN DATE_ADD(updated_at, INTERVAL 5400 SECOND) > VALUES(updated_at) AND seeder = TRUE AND active = TRUE AND VALUES(seeder) = TRUE THEN seedtime + TIMESTAMPDIFF(SECOND, updated_at, VALUES(updated_at)) ELSE seedtime END, seeder = VALUES(seeder), active = VALUES(active)'),
'seedtime' => DB::raw('CASE WHEN updated_at + INTERVAL 5400 SECOND > VALUES(updated_at) AND seeder = TRUE AND active = TRUE AND VALUES(seeder) = TRUE THEN seedtime + TIMESTAMPDIFF(SECOND, updated_at, VALUES(updated_at)) ELSE seedtime END, seeder = VALUES(seeder), active = VALUES(active)'),
'immune' => DB::raw('immune AND VALUES(immune)'),
'completed_at' => DB::raw('COALESCE(completed_at, VALUES(completed_at))'),
],

View File

@@ -41,7 +41,7 @@ class AutoWarning extends Command
*
* @var string
*/
protected $description = 'Automatically Post Warnings To Users Accounts and Warnings Table';
protected $description = 'Automatically post warnings to users accounts and warnings table';
/**
* Execute the console command.
@@ -66,7 +66,7 @@ class AutoWarning extends Command
->whereRelation('user.group', 'is_immune', '=', false)
->whereRelation('user', 'is_donor', '=', false)
->whereHas('torrent', fn ($query) => $query->whereRaw('history.actual_downloaded > torrents.size * ?', [config('hitrun.buffer') / 100]))
->whereDoesntHave('user.warnings', fn ($query) => $query->withTrashed()->whereColumn('warnings.torrent', '=', 'history.torrent_id'))
->whereDoesntHave('user.warnings', fn ($query) => $query->withTrashed()->whereColumn('warnings.torrent_id', '=', 'history.torrent_id'))
->get();
$usersWithWarnings = [];
@@ -75,7 +75,7 @@ class AutoWarning extends Command
Warning::create([
'user_id' => $hr->user->id,
'warned_by' => User::SYSTEM_USER_ID,
'torrent' => $hr->torrent->id,
'torrent_id' => $hr->torrent->id,
'reason' => \sprintf('Hit and Run Warning For Torrent %s', $hr->torrent->name),
'expires_on' => $carbon->copy()->addDays(config('hitrun.expire')),
'active' => true,
@@ -103,22 +103,22 @@ class AutoWarning extends Command
// Calculate User Warning Count and Disable DL Priv If Required.
Warning::query()
->with('warneduser')
->with('user')
->select(DB::raw('user_id, count(*) as value'))
->where('active', '=', 1)
->groupBy('user_id')
->having('value', '>=', config('hitrun.max_warnings'))
->whereRelation('warneduser', 'can_download', '=', true)
->whereRelation('user', 'can_download', '=', true)
->chunkById(100, function ($warnings): void {
foreach ($warnings as $warning) {
$warning->warneduser->update(['can_download' => 0]);
$warning->user->update(['can_download' => 0]);
cache()->forget('user:'.$warning->warneduser->passkey);
cache()->forget('user:'.$warning->user->passkey);
Unit3dAnnounce::addUser($warning->warneduser);
Unit3dAnnounce::addUser($warning->user);
}
}, 'user_id');
$this->comment('Automated User Warning Command Complete');
$this->comment('Automated user warning command complete');
}
}

View File

@@ -39,7 +39,7 @@ class DemoSeed extends Command
*
* @var string
*/
protected $description = 'Seeds Fake Data For Demonstration Or Testing Purposes';
protected $description = 'Seeds fake data for demonstration or testing purposes';
/**
* Execute the console command.
@@ -58,7 +58,7 @@ class DemoSeed extends Command
foreach ($this->movie_ids() as $id) {
// Users
$this->info('Creating User Account');
$this->info('Creating user account');
$uid = User::factory()->create([
'chatroom_id' => 1,
@@ -72,7 +72,7 @@ class DemoSeed extends Command
$movie = $this->fetchMovie($id);
// Torrents
$this->info('Creating Movie Torrents for Account ID #'.$uid);
$this->info('Creating movie torrents for account ID #'.$uid);
try {
$freeleech = ['0', '25', '50', '75', '100'];
@@ -253,7 +253,7 @@ Menu
foreach ($this->tv_ids() as $id) {
// Users
$this->info('Creating User Account');
$this->info('Creating user account');
$uid = User::factory()->create([
'chatroom_id' => 1,
@@ -267,7 +267,7 @@ Menu
$tv = $this->fetchTv($id);
// Torrents
$this->info('Creating TV Torrents for Account ID #'.$uid);
$this->info('Creating TV torrents for account ID #'.$uid);
try {
$freeleech = ['0', '25', '50', '75', '100'];
@@ -452,7 +452,7 @@ Menu
if ($abort) {
$this->error('Aborted ...');
$this->alert('Demo data was only PARTIALLY seeded! This is likely due to an API Request timeout.');
$this->alert('Demo data was only PARTIALLY seeded! This is likely due to an API request timeout.');
$this->alert('Ensure TMDB api key is set and run "php artisan config:clear"');
} else {
$this->alert('Demo data has been successfully seeded!');

View File

@@ -40,7 +40,7 @@ class FetchMeta extends Command
*
* @var string
*/
protected $description = 'Fetches Meta Data For New System On Preexisting Torrents';
protected $description = 'Fetches meta data for new system on preexisting torrents';
/**
* Execute the console command.

View File

@@ -110,7 +110,7 @@ class GitUpdater extends Command
$this->performUpdate();
} catch (Throwable $e) {
$this->log('Error during update: '.$e->getMessage());
$this->alert('error', 'Update Failed');
$this->alert('error', 'Update failed');
$this->error('Error: '.$e->getMessage());
if ($this->io->confirm('Would you like to restore from backup?', true)) {
@@ -253,11 +253,11 @@ Press CTRL + C ANYTIME to abort!
$this->success('Backups deleted successfully');
}
} else {
$this->alert('warning', 'Update Aborted');
$this->alert('warning', 'Update aborted');
$this->log('Update aborted by user after displaying files to update');
}
} else {
$this->alert('success', 'No Available Updates Found');
$this->alert('success', 'No available updates found');
$this->log('No updates available');
}
}

View File

@@ -39,7 +39,7 @@ class SyncPeers extends Command
*
* @var string
*/
protected $description = 'Sync Torrent Seeders/Leechers/Times Completed Count.';
protected $description = 'Sync torrent seeders/leechers/times completed count.';
/**
* Execute the console command.
@@ -86,6 +86,6 @@ class SyncPeers extends Command
]);
}, 5);
$this->info('Torrent Seeders/Leechers/Times Completed Count Synced Successfully!');
$this->info('Torrent seeders/leechers/times completed count synced successfully!');
}
}

View File

@@ -36,7 +36,7 @@ class SyncTorrentSeasonEpisode extends Command
*
* @var string
*/
protected $description = 'Syncs Season and Episode Numbers from Torrent Titles to Database';
protected $description = 'Syncs season and episode numbers from torrent titles to database';
/**
* Execute the console command.
@@ -72,6 +72,6 @@ class SyncTorrentSeasonEpisode extends Command
}
}
$this->comment('Torrent Season Episode Sync Command Complete');
$this->comment('Torrent season episode sync command complete');
}
}

View File

@@ -36,7 +36,7 @@ class TestMailSettings extends Command
*
* @var string
*/
protected $description = 'Send A Test Email To Owner Account Using The Current Mail Configuration';
protected $description = 'Send a test email to owner account using the current mail configuration';
/**
* Execute the console command.
@@ -47,7 +47,7 @@ class TestMailSettings extends Command
{
$owner = config('other.email');
$this->info('Sending Test Email To '.$owner);
$this->info('Sending test email to '.$owner);
if ($this->option('force') || $this->confirm('Do you wish to continue?', true)) {
try {
@@ -59,7 +59,7 @@ class TestMailSettings extends Command
exit(1);
}
$this->alert('Email Was Successfully Sent!');
$this->alert('Email was successfully sent!');
}
}
}

View File

@@ -22,7 +22,9 @@ enum GlobalRateLimit: string
case API = 'api';
case AUTHENTICATED_IMAGES = 'authenticated-images';
case CHAT = 'chat';
case FORGOT_PASSWORD = 'forgot-password';
case IGDB = 'igdb';
case RESET_PASSWORD = 'reset-password';
case RSS = 'rss';
case SEARCH = 'search';
case TMDB = 'tmdb';

View File

@@ -16,10 +16,12 @@ declare(strict_types=1);
namespace App\Events;
use App\Http\Resources\ChatMessageResource;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Queue\SerializesModels;
class Chatter implements ShouldBroadcastNow
@@ -28,19 +30,38 @@ class Chatter implements ShouldBroadcastNow
use InteractsWithSockets;
use SerializesModels;
public $echoes;
public ?AnonymousResourceCollection $echoes = null;
public $message;
public ?ChatMessageResource $message = null;
public $ping;
/**
* @var null|array{
* type: 'bot'|'target',
* id: int
* }
*/
public ?array $ping = null;
public $audibles;
public ?AnonymousResourceCollection $audibles = null;
/**
* Chatter Constructor.
*/
public function __construct(public string $type, public $target, $payload)
{
public function __construct(
/** @var 'echo'|'audible'|'new.message'|'new.bot'|'new.ping' $type */
public string $type,
public int $target,
/** @var (
* $type is 'echo' ? AnonymousResourceCollection
* : ($type is 'audible' ? AnonymousResourceCollection
* : ($type is 'new.message' ? ChatMessageResource
* : ($type is 'new.bot' ? ChatMessageResource
* : ($type is 'new.ping' ? array{type: 'bot'|'target', id: int}
* : never
* ))))) $payload
*/
mixed $payload,
) {
if ($type == 'echo') {
$this->echoes = $payload;
} elseif ($type == 'audible') {

View File

@@ -1,53 +0,0 @@
<?php
declare(strict_types=1);
/**
* 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\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class Ping implements ShouldBroadcastNow
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;
public array $ping;
/**
* Ping Constructor.
*/
public function __construct(public $room, int $id)
{
$this->ping = ['type' => 'room', 'id' => $id];
}
/**
* Get the channels the event should broadcast on.
*/
public function broadcastOn(): PresenceChannel
{
return new PresenceChannel('chatroom.'.$this->room);
}
public function broadcastAs(): string
{
return 'new.ping';
}
}

513
app/Helpers/Bbcode.php Executable file → Normal file
View File

@@ -21,257 +21,260 @@ use App\Models\WhitelistedImageUrl;
class Bbcode
{
/**
* @var array<
* @return array<
* string,
* array{
* openBbcode: string,
* closeBbcode: string,
* openHtml: string,
* closeHtml: string,
* block: boolean
* block: bool
* }
* > $parsers.
*/
private array $parsers = [
'h1' => [
'openBbcode' => '/^\[h1\]/i',
'closeBbcode' => '[/h1]',
'openHtml' => '<h1>',
'closeHtml' => '</h1>',
'block' => true,
],
'h2' => [
'openBbcode' => '/^\[h2\]/i',
'closeBbcode' => '[/h2]',
'openHtml' => '<h2>',
'closeHtml' => '</h2>',
'block' => true,
],
'h3' => [
'openBbcode' => '/^\[h3\]/i',
'closeBbcode' => '[/h3]',
'openHtml' => '<h3>',
'closeHtml' => '</h3>',
'block' => true,
],
'h4' => [
'openBbcode' => '/^\[h4\]/i',
'closeBbcode' => '[/h4]',
'openHtml' => '<h4>',
'closeHtml' => '</h4>',
'block' => true,
],
'h5' => [
'openBbcode' => '/^\[h5\]/i',
'closeBbcode' => '[/h5]',
'openHtml' => '<h5>',
'closeHtml' => '</h5>',
'block' => true,
],
'h6' => [
'openBbcode' => '/^\[h6\]/i',
'closeBbcode' => '[/h6]',
'openHtml' => '<h6>',
'closeHtml' => '</h6>',
'block' => true,
],
'bold' => [
'openBbcode' => '/^\[b\]/i',
'closeBbcode' => '[/b]',
'openHtml' => '<b>',
'closeHtml' => '</b>',
'block' => false,
],
'italic' => [
'openBbcode' => '/^\[i\]/i',
'closeBbcode' => '[/i]',
'openHtml' => '<i>',
'closeHtml' => '</i>',
'block' => false,
],
'underline' => [
'openBbcode' => '/^\[u\]/i',
'closeBbcode' => '[/u]',
'openHtml' => '<u>',
'closeHtml' => '</u>',
'block' => false,
],
'linethrough' => [
'openBbcode' => '/^\[s\]/i',
'closeBbcode' => '[/s]',
'openHtml' => '<s>',
'closeHtml' => '</s>',
'block' => false,
],
'size' => [
'openBbcode' => '/^\[size=(\d+)\]/i',
'closeBbcode' => '[/size]',
'openHtml' => '<span style="font-size: clamp(10px, $1px, 100px);">',
'closeHtml' => '</span>',
'block' => false,
],
'font' => [
'openBbcode' => '/^\[font=([a-z0-9 ]+)\]/i',
'closeBbcode' => '[/font]',
'openHtml' => '<span style="font-family: $1;">',
'closeHtml' => '</span>',
'block' => false,
],
'color' => [
'openBbcode' => '/^\[color=(\#[a-f0-9]{3,4}|\#[a-f0-9]{6}|\#[a-f0-9]{8}|[a-z]+)\]/i',
'closeBbcode' => '[/color]',
'openHtml' => '<span style="color: $1;">',
'closeHtml' => '</span>',
'block' => false,
],
'center' => [
'openBbcode' => '/^\[center\]/i',
'closeBbcode' => '[/center]',
'openHtml' => '<div class="bbcode-rendered__center" style="text-align: center;">',
'closeHtml' => '</div>',
'block' => true,
],
'left' => [
'openBbcode' => '/^\[left\]/i',
'closeBbcode' => '[/left]',
'openHtml' => '<div class="bbcode-rendered__left" style="text-align: left;">',
'closeHtml' => '</div>',
'block' => true,
],
'right' => [
'openBbcode' => '/^\[right\]/i',
'closeBbcode' => '[/right]',
'openHtml' => '<div class="bbcode-rendered__right" style="text-align: right;">',
'closeHtml' => '</div>',
'block' => true,
],
'quote' => [
'openBbcode' => '/^\[quote\]/i',
'closeBbcode' => '[/quote]',
'openHtml' => '<blockquote>',
'closeHtml' => '</blockquote>',
'block' => true,
],
'namedquote' => [
'openBbcode' => '/^\[quote=(.*?)\]/i',
'closeBbcode' => '[/quote]',
'openHtml' => '<blockquote><i class="fas fa-quote-left"></i> <cite>Quoting $1:</cite><p>',
'closeHtml' => '</p></blockquote>',
'block' => true,
],
'orderedlistnumerical' => [
'openBbcode' => '/^\[list=1\]/i',
'closeBbcode' => '[/list]',
'openHtml' => '<ol>',
'closeHtml' => '</ol>',
'block' => true,
],
'orderedlistalpha' => [
'openBbcode' => '/^\[list=a\]/i',
'closeBbcode' => '[/list]',
'openHtml' => '<ol type="a">',
'closeHtml' => '</ol>',
'block' => true,
],
'unorderedlist' => [
'openBbcode' => '/^\[list\]/i',
'closeBbcode' => '[/list]',
'openHtml' => '<ul>',
'closeHtml' => '</ul>',
'block' => true,
],
'code' => [
'openBbcode' => '/^\[code\]/i',
'closeBbcode' => '[/code]',
'openHtml' => '<div class="bbcode-rendered__clipboard" x-data="clipboardButton"><pre><code>',
'closeHtml' => '</code></pre><div class="bbcode-rendered__clipboard-container"><button class="bbcode-rendered__clipboard-button" x-bind="button"><i class="fa fa-clone"></i></button></div></div>',
'block' => true,
],
'pre' => [
'openBbcode' => '/^\[pre\]/i',
'closeBbcode' => '[/pre]',
'openHtml' => '<code>',
'closeHtml' => '</code>',
'block' => false,
],
'alert' => [
'openBbcode' => '/^\[alert\]/i',
'closeBbcode' => '[/alert]',
'openHtml' => '<div class="bbcode-rendered__alert">',
'closeHtml' => '</div>',
'block' => true,
],
'note' => [
'openBbcode' => '/^\[note\]/i',
'closeBbcode' => '[/note]',
'openHtml' => '<div class="bbcode-rendered__note">',
'closeHtml' => '</div>',
'block' => true,
],
'sub' => [
'openBbcode' => '/^\[sub\]/i',
'closeBbcode' => '[/sub]',
'openHtml' => '<sub>',
'closeHtml' => '</sub>',
'block' => false,
],
'sup' => [
'openBbcode' => '/^\[sup\]/i',
'closeBbcode' => '[/sup]',
'openHtml' => '<sup>',
'closeHtml' => '</sup>',
'block' => false,
],
'small' => [
'openBbcode' => '/^\[small\]/i',
'closeBbcode' => '[/small]',
'openHtml' => '<small>',
'closeHtml' => '</small>',
'block' => false,
],
'table' => [
'openBbcode' => '/^\[table\]/i',
'closeBbcode' => '[/table]',
'openHtml' => '<table>',
'closeHtml' => '</table>',
'block' => true,
],
'table-row' => [
'openBbcode' => '/^\[tr\]/i',
'closeBbcode' => '[/tr]',
'openHtml' => '<tr>',
'closeHtml' => '</tr>',
'block' => true,
],
'table-header' => [
'openBbcode' => '/^\[th\]/i',
'closeBbcode' => '[/th]',
'openHtml' => '<th>',
'closeHtml' => '</th>',
'block' => true,
],
'table-data' => [
'openBbcode' => '/^\[td\]/i',
'closeBbcode' => '[/td]',
'openHtml' => '<td>',
'closeHtml' => '</td>',
'block' => true,
],
'spoiler' => [
'openBbcode' => '/^\[spoiler\]/i',
'closeBbcode' => '[/spoiler]',
'openHtml' => '<details><summary>Spoiler</summary><div style="text-align:left;">',
'closeHtml' => '</div></details>',
'block' => false,
],
'named-spoiler' => [
'openBbcode' => '/^\[spoiler=(.*?)\]/i',
'closeBbcode' => '[/spoiler]',
'openHtml' => '<details><summary>$1</summary><div style="text-align:left;">',
'closeHtml' => '</div></details>',
'block' => false,
],
];
private static function parsers(): array
{
return [
'h1' => [
'openBbcode' => '/^\[h1\]/i',
'closeBbcode' => '[/h1]',
'openHtml' => '<h1>',
'closeHtml' => '</h1>',
'block' => true,
],
'h2' => [
'openBbcode' => '/^\[h2\]/i',
'closeBbcode' => '[/h2]',
'openHtml' => '<h2>',
'closeHtml' => '</h2>',
'block' => true,
],
'h3' => [
'openBbcode' => '/^\[h3\]/i',
'closeBbcode' => '[/h3]',
'openHtml' => '<h3>',
'closeHtml' => '</h3>',
'block' => true,
],
'h4' => [
'openBbcode' => '/^\[h4\]/i',
'closeBbcode' => '[/h4]',
'openHtml' => '<h4>',
'closeHtml' => '</h4>',
'block' => true,
],
'h5' => [
'openBbcode' => '/^\[h5\]/i',
'closeBbcode' => '[/h5]',
'openHtml' => '<h5>',
'closeHtml' => '</h5>',
'block' => true,
],
'h6' => [
'openBbcode' => '/^\[h6\]/i',
'closeBbcode' => '[/h6]',
'openHtml' => '<h6>',
'closeHtml' => '</h6>',
'block' => true,
],
'bold' => [
'openBbcode' => '/^\[b\]/i',
'closeBbcode' => '[/b]',
'openHtml' => '<b>',
'closeHtml' => '</b>',
'block' => false,
],
'italic' => [
'openBbcode' => '/^\[i\]/i',
'closeBbcode' => '[/i]',
'openHtml' => '<i>',
'closeHtml' => '</i>',
'block' => false,
],
'underline' => [
'openBbcode' => '/^\[u\]/i',
'closeBbcode' => '[/u]',
'openHtml' => '<u>',
'closeHtml' => '</u>',
'block' => false,
],
'linethrough' => [
'openBbcode' => '/^\[s\]/i',
'closeBbcode' => '[/s]',
'openHtml' => '<s>',
'closeHtml' => '</s>',
'block' => false,
],
'size' => [
'openBbcode' => '/^\[size=(\d+)\]/i',
'closeBbcode' => '[/size]',
'openHtml' => '<span style="font-size: clamp(10px, $1px, 100px);">',
'closeHtml' => '</span>',
'block' => false,
],
'font' => [
'openBbcode' => '/^\[font=([a-z0-9 ]+)\]/i',
'closeBbcode' => '[/font]',
'openHtml' => '<span style="font-family: $1;">',
'closeHtml' => '</span>',
'block' => false,
],
'color' => [
'openBbcode' => '/^\[color=(\#[a-f0-9]{3,4}|\#[a-f0-9]{6}|\#[a-f0-9]{8}|[a-z]+)\]/i',
'closeBbcode' => '[/color]',
'openHtml' => '<span style="color: $1;">',
'closeHtml' => '</span>',
'block' => false,
],
'center' => [
'openBbcode' => '/^\[center\]/i',
'closeBbcode' => '[/center]',
'openHtml' => '<div class="bbcode-rendered__center" style="text-align: center;">',
'closeHtml' => '</div>',
'block' => true,
],
'left' => [
'openBbcode' => '/^\[left\]/i',
'closeBbcode' => '[/left]',
'openHtml' => '<div class="bbcode-rendered__left" style="text-align: left;">',
'closeHtml' => '</div>',
'block' => true,
],
'right' => [
'openBbcode' => '/^\[right\]/i',
'closeBbcode' => '[/right]',
'openHtml' => '<div class="bbcode-rendered__right" style="text-align: right;">',
'closeHtml' => '</div>',
'block' => true,
],
'quote' => [
'openBbcode' => '/^\[quote\]/i',
'closeBbcode' => '[/quote]',
'openHtml' => '<blockquote>',
'closeHtml' => '</blockquote>',
'block' => true,
],
'namedquote' => [
'openBbcode' => '/^\[quote=(.*?)\]/i',
'closeBbcode' => '[/quote]',
'openHtml' => '<blockquote><i class="fas fa-quote-left"></i> <cite>Quoting $1:</cite><p>',
'closeHtml' => '</p></blockquote>',
'block' => true,
],
'orderedlistnumerical' => [
'openBbcode' => '/^\[list=1\]/i',
'closeBbcode' => '[/list]',
'openHtml' => '<ol>',
'closeHtml' => '</ol>',
'block' => true,
],
'orderedlistalpha' => [
'openBbcode' => '/^\[list=a\]/i',
'closeBbcode' => '[/list]',
'openHtml' => '<ol type="a">',
'closeHtml' => '</ol>',
'block' => true,
],
'unorderedlist' => [
'openBbcode' => '/^\[list\]/i',
'closeBbcode' => '[/list]',
'openHtml' => '<ul>',
'closeHtml' => '</ul>',
'block' => true,
],
'code' => [
'openBbcode' => '/^\[code\]/i',
'closeBbcode' => '[/code]',
'openHtml' => '<div class="bbcode-rendered__clipboard" x-data="clipboardButton"><pre><code>',
'closeHtml' => '</code></pre><div class="bbcode-rendered__clipboard-container"><button class="bbcode-rendered__clipboard-button" x-bind="button"><i class="fa fa-clone"></i></button></div></div>',
'block' => true,
],
'pre' => [
'openBbcode' => '/^\[pre\]/i',
'closeBbcode' => '[/pre]',
'openHtml' => '<code>',
'closeHtml' => '</code>',
'block' => false,
],
'alert' => [
'openBbcode' => '/^\[alert\]/i',
'closeBbcode' => '[/alert]',
'openHtml' => '<div class="bbcode-rendered__alert">',
'closeHtml' => '</div>',
'block' => true,
],
'note' => [
'openBbcode' => '/^\[note\]/i',
'closeBbcode' => '[/note]',
'openHtml' => '<div class="bbcode-rendered__note">',
'closeHtml' => '</div>',
'block' => true,
],
'sub' => [
'openBbcode' => '/^\[sub\]/i',
'closeBbcode' => '[/sub]',
'openHtml' => '<sub>',
'closeHtml' => '</sub>',
'block' => false,
],
'sup' => [
'openBbcode' => '/^\[sup\]/i',
'closeBbcode' => '[/sup]',
'openHtml' => '<sup>',
'closeHtml' => '</sup>',
'block' => false,
],
'small' => [
'openBbcode' => '/^\[small\]/i',
'closeBbcode' => '[/small]',
'openHtml' => '<small>',
'closeHtml' => '</small>',
'block' => false,
],
'table' => [
'openBbcode' => '/^\[table\]/i',
'closeBbcode' => '[/table]',
'openHtml' => '<table>',
'closeHtml' => '</table>',
'block' => true,
],
'table-row' => [
'openBbcode' => '/^\[tr\]/i',
'closeBbcode' => '[/tr]',
'openHtml' => '<tr>',
'closeHtml' => '</tr>',
'block' => true,
],
'table-header' => [
'openBbcode' => '/^\[th\]/i',
'closeBbcode' => '[/th]',
'openHtml' => '<th>',
'closeHtml' => '</th>',
'block' => true,
],
'table-data' => [
'openBbcode' => '/^\[td\]/i',
'closeBbcode' => '[/td]',
'openHtml' => '<td>',
'closeHtml' => '</td>',
'block' => true,
],
'spoiler' => [
'openBbcode' => '/^\[spoiler\]/i',
'closeBbcode' => '[/spoiler]',
'openHtml' => '<details><summary>Spoiler</summary><div style="text-align:left;">',
'closeHtml' => '</div></details>',
'block' => false,
],
'named-spoiler' => [
'openBbcode' => '/^\[spoiler=(.*?)\]/i',
'closeBbcode' => '[/spoiler]',
'openHtml' => '<details><summary>$1</summary><div style="text-align:left;">',
'closeHtml' => '</div></details>',
'block' => false,
],
];
}
/**
* Parses the BBCode string.
@@ -286,27 +289,27 @@ class Bbcode
$source = str_replace('[hr]', '<hr>', $source);
$source = preg_replace_callback(
'/\[url](.*?)\[\/url]/i',
fn ($matches) => '<a href="'.$this->sanitizeUrl($matches[1]).'">'.$this->sanitizeUrl($matches[1]).'</a>',
fn ($matches) => '<a href="'.self::sanitizeUrl($matches[1]).'">'.self::sanitizeUrl($matches[1]).'</a>',
$source
);
$source = preg_replace_callback(
'/\[url=(.*?)](.*?)\[\/url]/i',
fn ($matches) => '<a href="'.$this->sanitizeUrl($matches[1]).'">'.$matches[2].'</a>',
fn ($matches) => '<a href="'.self::sanitizeUrl($matches[1]).'">'.$matches[2].'</a>',
$source ?? ''
);
$source = preg_replace_callback(
'/\[img](.*?)\[\/img]/i',
fn ($matches) => '<img src="'.$this->sanitizeUrl($matches[1], isImage: true).'" loading="lazy" class="img-responsive" style="display: inline !important;">',
fn ($matches) => '<img src="'.self::sanitizeUrl($matches[1], isImage: true).'" loading="lazy" class="img-responsive" style="display: inline !important;">',
$source ?? ''
);
$source = preg_replace_callback(
'/\[img width=(\d+)](.*?)\[\/img]/i',
fn ($matches) => '<img src="'.$this->sanitizeUrl($matches[2], isImage: true).'" loading="lazy" width="'.$matches[1].'px">',
fn ($matches) => '<img src="'.self::sanitizeUrl($matches[2], isImage: true).'" loading="lazy" width="'.$matches[1].'px">',
$source ?? ''
);
$source = preg_replace_callback(
'/\[img=(\d+)(?:x\d+)?](.*?)\[\/img]/i',
fn ($matches) => '<img src="'.$this->sanitizeUrl($matches[2], isImage: true).'" loading="lazy" width="'.$matches[1].'px">',
fn ($matches) => '<img src="'.self::sanitizeUrl($matches[2], isImage: true).'" loading="lazy" width="'.$matches[1].'px">',
$source ?? ''
);
@@ -340,7 +343,7 @@ class Bbcode
return 'Broken comparison';
}
$validatedUrls = collect($urls)->map(fn ($url) => $this->sanitizeUrl($url, isImage: true));
$validatedUrls = collect($urls)->map(fn ($url) => self::sanitizeUrl($url, isImage: true));
$chunkedUrls = $validatedUrls->chunk(\count($comparates));
$html = view('partials.comparison', ['comparates' => $comparates, 'urls' => $chunkedUrls])->render();
$html = preg_replace('/\s+/', ' ', $html);
@@ -378,7 +381,7 @@ class Bbcode
// Is the potential tag opening or closing?
if ($source[$index + 1] === '/' && !empty($openedElements)) {
$name = array_pop($openedElements);
$el = $this->parsers[$name];
$el = self::parsers()[$name];
$tag = substr((string) $source, $index, \strlen((string) $el['closeBbcode']));
// Replace bbcode tag with html tag if found tag matches expected tag,
@@ -387,7 +390,7 @@ class Bbcode
$source = substr_replace((string) $source, (string) $el['closeHtml'], $index, \strlen((string) $el['closeBbcode']));
if ($replaceLineBreaks === true && $el['block'] === true) {
$this->handleBlockElementSpacing($source, $index, $index, $index + \strlen((string) $el['closeHtml']) - 1);
self::handleBlockElementSpacing($source, $index, $index, $index + \strlen((string) $el['closeHtml']) - 1);
}
} else {
$openedElements[] = $name;
@@ -396,7 +399,7 @@ class Bbcode
$remainingText = substr((string) $source, $index);
// Find match between found bbcode tag and valid elements
foreach ($this->parsers as $name => $el) {
foreach (self::parsers() as $name => $el) {
// The opening bbcode tag uses the regex `^` character to make
// sure only the beginning of $remainingText is matched
if (preg_match($el['openBbcode'], $remainingText, $matches) === 1) {
@@ -404,7 +407,7 @@ class Bbcode
$source = substr_replace((string) $source, $replacement, $index, \strlen($matches[0]));
if ($replaceLineBreaks === true && $el['block'] === true) {
$this->handleBlockElementSpacing($source, $index, $index, $index + \strlen($replacement) - 1);
self::handleBlockElementSpacing($source, $index, $index, $index + \strlen($replacement) - 1);
}
$openedElements[] = $name;
@@ -418,7 +421,7 @@ class Bbcode
}
while (!empty($openedElements)) {
$source .= $this->parsers[array_pop($openedElements)]['closeHtml'];
$source .= self::parsers()[array_pop($openedElements)]['closeHtml'];
}
if ($replaceLineBreaks) {
@@ -446,7 +449,7 @@ class Bbcode
* @param int $tagStartIndex The index of the first character of the tag being parsed inside `$source`. Should be the `[` character.
* @param int $tagStopIndex The index of the last character of the tag being parsed inside `$source`. Should be the `]` character.
*/
private function handleBlockElementSpacing(string &$source, int &$index, int $tagStartIndex, int $tagStopIndex): void
private static function handleBlockElementSpacing(string &$source, int &$index, int $tagStartIndex, int $tagStopIndex): void
{
// Remove two line breaks (if they exist) instead of one, since a
// line break after a block element is positioned on the line after
@@ -479,7 +482,7 @@ class Bbcode
}
}
private function sanitizeUrl(string $url, ?bool $isImage = null): string
private static function sanitizeUrl(string $url, ?bool $isImage = null): string
{
// Do NOT add `javascript`, `data` or `vbscript` here
// or else you will allow an XSS vulnerability!

View File

@@ -14,7 +14,7 @@ declare(strict_types=1);
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Models;
namespace App\Helpers;
class Language
{

View File

@@ -71,7 +71,7 @@ namespace App\Helpers;
*/
class MediaInfo
{
private const REGEX_SECTION = "/^(?:(?:general|video|audio|text|menu)(?:\s\#\d+?)*)$/i";
private const REGEX_SECTION = "/^(?:(?:general|video|audio|text|image|menu)(?:\s\#\d+?)*)$/i";
/**
* @var string[]

0
app/Helpers/StringHelper.php Executable file → Normal file
View File

View File

@@ -103,7 +103,7 @@ class SystemInformation
protected function formatBytes(int|float $bytes, int $precision = 2): string
{
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1_024));
$pow = (int) floor(($bytes ? log($bytes) : 0) / log(1_024));
$pow = min($pow, (\count(self::UNITS)) - 1);
// Uncomment one of the following alternatives
$bytes /= 1_024 ** $pow;

View File

@@ -29,6 +29,7 @@ use App\Achievements\UserMade800Uploads;
use App\Achievements\UserMade900Uploads;
use App\Achievements\UserMadeUpload;
use App\Bots\IRCAnnounceBot;
use App\Bots\IRCAnnounceBotExternal;
use App\Enums\ModerationStatus;
use App\Models\AutomaticTorrentFreeleech;
use App\Models\TmdbMovie;
@@ -147,6 +148,9 @@ class TorrentHelper
->say(\sprintf('[Link: %s/torrents/', $appurl).$id.']');
}
// Announce to external IRC service
IRCAnnounceBotExternal::postAnnounceMsg($torrent);
cache()->forget('announce-torrents:by-infohash:'.$torrent->info_hash);
Unit3dAnnounce::addTorrent($torrent);

View File

@@ -25,7 +25,7 @@ class TorrentTools
/**
* Moves and decodes the torrent.
*
* @return array<mixed>>
* @return array<mixed>
*/
public static function normalizeTorrent(UploadedFile $torrentFile)
{
@@ -173,6 +173,8 @@ class TorrentTools
|| preg_match('/^\.?____padding.*$/i', $filename)
// BEP 47 torrent padding files that many clients aren't able to handle
|| str_starts_with($filename, '.pad')
// Tilde home expansion on linux
|| str_starts_with($filename, '~')
);
}

View File

@@ -158,7 +158,7 @@ class ChatController extends Controller
$audibles->push(UserAudible::create([
'user_id' => $user->id,
'bot_id' => $bot->id,
'status' => 0,
'status' => false,
]));
cache()->put('user-audibles'.$user->id, $audibles, 3600);
@@ -252,7 +252,7 @@ class ChatController extends Controller
$audibles->push(UserAudible::create([
'user_id' => $user1Id,
'target_id' => $user2Id,
'status' => 1,
'status' => true,
]));
cache()->put('user-audibles'.$user1Id, $audibles, 3600);

View File

@@ -652,7 +652,7 @@ class TorrentController extends BaseController
$torrents = collect();
foreach ($results['hits'] ?? [] as $hit) {
$meta = $hit['movie'] ?? $hit['tv'] ?? [];
$meta = $hit['tmdb_movie'] ?? $hit['tmdb_tv'] ?? [];
/** @see TorrentResource */
$torrents->push([

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/**
* 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 Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\Group;
use App\Models\User;
use App\Services\Unit3dAnnounce;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Support\Timebox;
use Illuminate\Validation\Rules\Password as RulesPassword;
class NewPasswordController extends Controller
{
/**
* Create new password.
*/
public function create(string $token): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
return view('auth.reset-password', ['token' => $token]);
}
/**
* Store a new password.
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => RulesPassword::min(12)->mixedCase()->letters()->numbers()->uncompromised(),
]);
// Use timebox to prevent user enumeration since laravel by default
// will return early if user doesn't exist or token is invalid.
$status = new Timebox()->call(function () use ($request) {
return Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function (User $user, string $password): void {
$user->forceFill([
'password' => Hash::make($password)
])->setRememberToken(Str::random(60));
$validatingGroupId = Group::query()->where('slug', '=', 'validating')->soleValue('id');
if ($user->group_id === $validatingGroupId) {
$user->group_id = Group::query()->where('slug', '=', 'user')->soleValue('id');
cache()->forget('user:'.$user->passkey);
Unit3dAnnounce::addUser($user);
}
if (!$user->hasVerifiedEmail()) {
$user->markEmailAsVerified();
}
$user->save();
$user->passwordResetHistories()->create();
event(new PasswordReset($user));
}
);
}, 200000);
return $status === Password::PasswordReset
? redirect()->route('login')->with('status', __('passwords.reset'))
// Send the "invalid reset token" error instead of the "unknown
// user" error on failure (Account enumeration).
: back()->withErrors(['email' => [__('passwords.token')]]);
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* 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 Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class PasswordResetLinkController extends Controller
{
/**
* Show form to submit to receive new password reset link.
*/
public function create(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
return view('auth.forgot-password');
}
/**
* Send a new password reset link.
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
$request->validate(['email' => 'required|email']);
// Uses Laravel timebox internally
$_status = Password::sendResetLink(
$request->only('email')
);
// Return successful status regardless of if the user exists or if they're throttled or not.
// (Account enumeration)
return back()->with(['status' => __('passwords.sent')]);
}
}

View File

@@ -19,6 +19,7 @@ namespace App\Http\Controllers;
use App\Models\Article;
use App\Models\Category;
use App\Models\Playlist;
use App\Models\Scopes\ApprovedScope;
use App\Models\Torrent;
use App\Models\User;
use Illuminate\Support\Facades\Storage;
@@ -62,8 +63,10 @@ class AuthenticatedImageController extends Controller
return response()->file($path, self::HEADERS);
}
public function torrentBanner(Torrent $torrent): \Symfony\Component\HttpFoundation\BinaryFileResponse
public function torrentBanner(int $id): \Symfony\Component\HttpFoundation\BinaryFileResponse
{
$torrent = Torrent::withoutGlobalScope(ApprovedScope::class)->findOrFail($id);
$path = Storage::disk('torrent-banners')->path("torrent-banner_{$torrent->id}.jpg");
abort_unless(file_exists($path), 404);
@@ -71,8 +74,10 @@ class AuthenticatedImageController extends Controller
return response()->file($path, self::HEADERS);
}
public function torrentCover(Torrent $torrent): \Symfony\Component\HttpFoundation\BinaryFileResponse
public function torrentCover(int $id): \Symfony\Component\HttpFoundation\BinaryFileResponse
{
$torrent = Torrent::withoutGlobalScope(ApprovedScope::class)->findOrFail($id);
$path = Storage::disk('torrent-covers')->path("torrent-cover_{$torrent->id}.jpg");
abort_unless(file_exists($path), 404);

View File

@@ -45,6 +45,6 @@ class ContactController extends Controller
Mail::to($user->email)->send(new Contact($request->string('email')));
return to_route('home.index')
->with('success', 'Your Message Was Successfully Sent');
->with('success', 'Your message was successfully sent');
}
}

View File

@@ -48,6 +48,6 @@ class DonationController extends Controller
]);
return redirect()->route('donations.index')
->with('success', 'Thank You For Supporting Us! Please allow for up to 48 hours for staff to confirm the transaction.');
->with('success', 'Thank you for supporting us! Please allow for up to 48 hours for staff to confirm the transaction.');
}
}

View File

@@ -70,7 +70,7 @@ class HomeController extends Controller
fn () => User::with('group', 'privacy')
->withCount([
'warnings' => function (Builder $query): void {
$query->whereNotNull('torrent')->where('active', true);
$query->whereNotNull('torrent_id')->where('active', true);
},
])
->where('last_action', '>', now()->subMinutes(60))

View File

@@ -35,9 +35,6 @@ class PlaylistZipController extends Controller
// Extend The Maximum Execution Time
set_time_limit(300);
// Playlist
$playlist->load('torrents');
// Authorized User
$user = auth()->user();
@@ -50,27 +47,29 @@ class PlaylistZipController extends Controller
$announceUrl = route('announce', ['passkey' => $user->passkey]);
foreach ($playlist->torrents()->get() as $torrent) {
if (Storage::disk('torrent-files')->exists($torrent->file_name)) {
$dict = Bencode::bdecode(Storage::disk('torrent-files')->get($torrent->file_name));
$playlist->torrents()->chunk(100, function ($torrents) use ($announceUrl, $zip): void {
foreach ($torrents as $torrent) {
if (Storage::disk('torrent-files')->exists($torrent->file_name)) {
$dict = Bencode::bdecode(Storage::disk('torrent-files')->get($torrent->file_name));
// Set the announce key and add the user passkey
$dict['announce'] = $announceUrl;
// Set the announce key and add the user passkey
$dict['announce'] = $announceUrl;
// Set link to torrent as the comment
if (config('torrent.comment')) {
$dict['comment'] = config('torrent.comment').'. '.route('torrents.show', ['id' => $torrent->id]);
} else {
$dict['comment'] = route('torrents.show', ['id' => $torrent->id]);
// Set link to torrent as the comment
if (config('torrent.comment')) {
$dict['comment'] = config('torrent.comment').'. '.route('torrents.show', ['id' => $torrent->id]);
} else {
$dict['comment'] = route('torrents.show', ['id' => $torrent->id]);
}
$fileToDownload = Bencode::bencode($dict);
$filename = sanitize_filename('['.config('torrent.source').']'.$torrent->name.'.torrent');
$zip->addFile($filename, $fileToDownload);
}
$fileToDownload = Bencode::bencode($dict);
$filename = sanitize_filename('['.config('torrent.source').']'.$torrent->name.'.torrent');
$zip->addFile($filename, $fileToDownload);
}
}
});
$zip->finish();
},

View File

@@ -117,10 +117,17 @@ class PostController extends Controller
$staffer->notify(new NewPost('staff', $user, $post));
}
} else {
if ($post->anon) {
$this->chatRepository->systemMessage(\sprintf('An anonymous user has left a reply on topic [url=%s]%s[/url]', $postUrl, $topic->name));
} else {
$this->chatRepository->systemMessage(\sprintf('[url=%s]%s[/url] has left a reply on topic [url=%s]%s[/url]', $profileUrl, $user->username, $postUrl, $topic->name));
$isChatboxPrivy = $forum->permissions()
->where('read_topic', '=', true)
->whereRelation('group', 'slug', '=', 'user')
->exists();
if ($isChatboxPrivy) {
if ($post->anon) {
$this->chatRepository->systemMessage(\sprintf('An anonymous user has left a reply on topic [url=%s]%s[/url]', $postUrl, $topic->name));
} else {
$this->chatRepository->systemMessage(\sprintf('[url=%s]%s[/url] has left a reply on topic [url=%s]%s[/url]', $profileUrl, $user->username, $postUrl, $topic->name));
}
}
$topicStarter = $topic->user;

View File

@@ -44,14 +44,13 @@ class ReportController extends Controller
]);
Report::create([
'type' => 'Request',
'request_id' => $torrentRequest->id,
'torrent_id' => null,
'reporter_id' => $reportedBy->id,
'reported_user' => $reportedUser->id,
'title' => $torrentRequest->name,
'message' => $request->string('message'),
'solved' => false,
'type' => 'Request',
'reported_request_id' => $torrentRequest->id,
'reported_torrent_id' => null,
'reporter_id' => $reportedBy->id,
'reported_user_id' => $reportedUser->id,
'title' => $torrentRequest->name,
'message' => $request->string('message'),
]);
return to_route('requests.show', ['torrentRequest' => $torrentRequest])
@@ -75,14 +74,13 @@ class ReportController extends Controller
]);
Report::create([
'type' => 'Torrent',
'torrent_id' => $torrent->id,
'request_id' => null,
'reporter_id' => $reportedBy->id,
'reported_user' => $reportedUser->id,
'title' => $torrent->name,
'message' => $request->string('message'),
'solved' => false,
'type' => 'Torrent',
'reported_torrent_id' => $torrent->id,
'reported_request_id' => null,
'reporter_id' => $reportedBy->id,
'reported_user_id' => $reportedUser->id,
'title' => $torrent->name,
'message' => $request->string('message'),
]);
return to_route('torrents.show', ['id' => $id])
@@ -105,14 +103,13 @@ class ReportController extends Controller
]);
Report::create([
'type' => 'User',
'torrent_id' => null,
'request_id' => null,
'reporter_id' => $reportedBy->id,
'reported_user' => $reportedUser->id,
'title' => $reportedUser->username,
'message' => $request->string('message'),
'solved' => false,
'type' => 'User',
'reported_torrent_id' => null,
'reported_request_id' => null,
'reporter_id' => $reportedBy->id,
'reported_user_id' => $reportedUser->id,
'title' => $reportedUser->username,
'message' => $request->string('message'),
]);
return to_route('users.show', ['user' => $reportedUser])

View File

@@ -219,8 +219,6 @@ class RequestController extends Controller
$torrentRequest->update($request->validated());
$category = $torrentRequest->category;
match (true) {
$torrentRequest->tmdb_tv_id !== null => new TMDBScraper()->tv($torrentRequest->tmdb_tv_id),
$torrentRequest->tmdb_movie_id !== null => new TMDBScraper()->movie($torrentRequest->tmdb_movie_id),

View File

@@ -77,7 +77,7 @@ class ApplicationController extends Controller
Mail::to($application->email)->send(new InviteUser($invite));
return to_route('staff.applications.index')
->with('success', 'Application Approved');
->with('success', 'Application approved');
}
/**
@@ -95,6 +95,6 @@ class ApplicationController extends Controller
Mail::to($application->email)->send(new DenyApplication($request->deny));
return to_route('staff.applications.index')
->with('success', 'Application Rejected');
->with('success', 'Application rejected');
}
}

View File

@@ -58,6 +58,6 @@ class AuditController extends Controller
$audit->delete();
return to_route('staff.audits.index')
->with('success', 'Audit Record Has Successfully Been Deleted');
->with('success', 'Audit record has successfully been deleted');
}
}

View File

@@ -47,7 +47,7 @@ class AutomaticTorrentFreeleechController extends Controller
AutomaticTorrentFreeleech::create($request->validated());
return to_route('staff.automatic_torrent_freeleeches.index')
->with('success', 'Resolution Successfully Added');
->with('success', 'Resolution successfully added');
}
public function edit(AutomaticTorrentFreeleech $automaticTorrentFreeleech): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
@@ -65,7 +65,7 @@ class AutomaticTorrentFreeleechController extends Controller
$automaticTorrentFreeleech->update($request->validated());
return to_route('staff.automatic_torrent_freeleeches.index')
->with('success', 'Resolution Successfully Modified');
->with('success', 'Resolution successfully modified');
}
public function destroy(AutomaticTorrentFreeleech $automaticTorrentFreeleech): \Illuminate\Http\RedirectResponse
@@ -73,6 +73,6 @@ class AutomaticTorrentFreeleechController extends Controller
$automaticTorrentFreeleech->delete();
return to_route('staff.automatic_torrent_freeleeches.index')
->with('success', 'Resolution Successfully Deleted');
->with('success', 'Resolution successfully deleted');
}
}

View File

@@ -36,7 +36,7 @@ class BanController extends Controller
public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
return view('Staff.ban.index', [
'bans' => Ban::latest()->with('banneduser.group', 'staffuser.group')->paginate(25),
'bans' => Ban::latest()->with('user.group', 'staff.group')->paginate(25),
]);
}
@@ -66,6 +66,6 @@ class BanController extends Controller
$user->notify(new UserBan($ban));
return to_route('users.show', ['user' => $user])
->with('success', 'User Is Now Banned!');
->with('success', 'User is now banned!');
}
}

View File

@@ -64,7 +64,7 @@ class BlacklistClientController extends Controller
cache()->forget('client_blacklist');
return to_route('staff.blacklisted_clients.index')
->with('success', 'Blacklisted Client Was Updated Successfully!');
->with('success', 'Blacklisted client was updated successfully!');
}
/**
@@ -90,7 +90,7 @@ class BlacklistClientController extends Controller
cache()->forget('client_blacklist');
return to_route('staff.blacklisted_clients.index')
->with('success', 'Blacklisted Client Stored Successfully!');
->with('success', 'Blacklisted client stored successfully!');
}
/**
@@ -105,6 +105,6 @@ class BlacklistClientController extends Controller
cache()->forget('client_blacklist');
return to_route('staff.blacklisted_clients.index')
->with('success', 'Blacklisted Client Destroyed Successfully!');
->with('success', 'Blacklisted client destroyed successfully!');
}
}

View File

@@ -53,7 +53,7 @@ class BonEarningController extends Controller
$bonEarning->conditions()->upsert($request->validated('conditions', []), ['id']);
return to_route('staff.bon_earnings.index')
->with('success', 'Bon Exchange Successfully Added');
->with('success', 'Bon exchange successfully added');
}
/**
@@ -80,7 +80,7 @@ class BonEarningController extends Controller
$bonEarning->conditions()->upsert($request->validated('conditions', []), ['id']);
return to_route('staff.bon_earnings.index')
->with('success', 'Bon Exchange Successfully Modified');
->with('success', 'Bon exchange successfully modified');
}
/**
@@ -93,6 +93,6 @@ class BonEarningController extends Controller
$bonEarning->delete();
return to_route('staff.bon_earnings.index')
->with('success', 'Bon Exchange Successfully Deleted');
->with('success', 'Bon exchange successfully deleted');
}
}

View File

@@ -56,7 +56,7 @@ class BonExchangeController extends Controller
+ $request->validated());
return to_route('staff.bon_exchanges.index')
->with('success', 'Bon Exchange Successfully Added');
->with('success', 'Bon exchange successfully added');
}
/**
@@ -83,7 +83,7 @@ class BonExchangeController extends Controller
+ $request->validated());
return to_route('staff.bon_exchanges.index')
->with('success', 'Bon Exchange Successfully Modified');
->with('success', 'Bon exchange successfully modified');
}
/**
@@ -96,6 +96,6 @@ class BonExchangeController extends Controller
BonExchange::findOrFail($id)->delete();
return to_route('staff.bon_exchanges.index')
->with('success', 'Bon Exchange Successfully Deleted');
->with('success', 'Bon exchange successfully deleted');
}
}

View File

@@ -72,7 +72,7 @@ class CategoryController extends Controller
] + $request->validated());
return to_route('staff.categories.index')
->with('success', 'Category Successfully Added');
->with('success', 'Category successfully added');
}
/**
@@ -114,7 +114,7 @@ class CategoryController extends Controller
] + $request->validated());
return to_route('staff.categories.index')
->with('success', 'Category Successfully Modified');
->with('success', 'Category successfully modified');
}
/**
@@ -127,6 +127,6 @@ class CategoryController extends Controller
$category->delete();
return to_route('staff.categories.index')
->with('success', 'Category Successfully Deleted');
->with('success', 'Category successfully deleted');
}
}

View File

@@ -56,7 +56,7 @@ class ChatBotController extends Controller
$bot->update($request->validated());
return to_route('staff.bots.index')
->with('success', "The Bot Has Been Updated");
->with('success', "The bot has been updated");
}
/**
@@ -71,7 +71,7 @@ class ChatBotController extends Controller
$bot->delete();
return to_route('staff.bots.index')
->with('success', 'The Humans Vs Machines War Has Begun! Humans: 1 and Bots: 0');
->with('success', 'The Humans Vs Machines War has begun! Humans: 1 and Bots: 0');
}
/**
@@ -84,7 +84,7 @@ class ChatBotController extends Controller
]);
return to_route('staff.bots.index')
->with('success', 'The Bot Has Been Disabled');
->with('success', 'The bot has been disabled');
}
/**
@@ -97,6 +97,6 @@ class ChatBotController extends Controller
]);
return to_route('staff.bots.index')
->with('success', 'The Bot Has Been Enabled');
->with('success', 'The bot has been enabled');
}
}

View File

@@ -54,7 +54,7 @@ class ChatRoomController extends Controller
Chatroom::create($request->validated());
return to_route('staff.chatrooms.index')
->with('success', 'Chatroom Successfully Added');
->with('success', 'Chatroom successfully added');
}
/**
@@ -75,7 +75,7 @@ class ChatRoomController extends Controller
$chatroom->update($request->validated());
return to_route('staff.chatrooms.index')
->with('success', 'Chatroom Successfully Modified');
->with('success', 'Chatroom successfully modified');
}
/**
@@ -98,6 +98,6 @@ class ChatRoomController extends Controller
$chatroom->delete();
return to_route('staff.chatrooms.index')
->with('success', 'Chatroom Successfully Deleted');
->with('success', 'Chatroom successfully deleted');
}
}

View File

@@ -53,7 +53,7 @@ class ChatStatusController extends Controller
ChatStatus::create($request->validated());
return to_route('staff.statuses.index')
->with('success', 'Chat Status Successfully Added');
->with('success', 'Chat status successfully added');
}
/**
@@ -74,7 +74,7 @@ class ChatStatusController extends Controller
$chatStatus->update($request->validated());
return to_route('staff.statuses.index')
->with('success', 'Chat Status Successfully Modified');
->with('success', 'Chat status successfully modified');
}
/**
@@ -87,6 +87,6 @@ class ChatStatusController extends Controller
$chatStatus->delete();
return to_route('staff.statuses.index')
->with('success', 'Chat Status Successfully Deleted');
->with('success', 'Chat status successfully deleted');
}
}

View File

@@ -54,7 +54,7 @@ class CheatedTorrentController extends Controller
fn ($query) => $query
->whereNotNull('balance_reset_at')
->whereColumn('balance_reset_at', '>', 'history.created_at')
->whereColumn(DB::raw('DATE_SUB(balance_reset_at, INTERVAL 1 HOUR)'), '<', 'history.completed_at')
->whereColumn(DB::raw('balance_reset_at - INTERVAL 1 HOUR'), '<', 'history.completed_at')
->whereNotNull('completed_at')
)
// Exclude torrents where the reporting period overlapped with both the balance reset and the current balance calculation
@@ -67,8 +67,8 @@ class CheatedTorrentController extends Controller
->where('seeder', '=', false)
)
// Exclude torrents where the reporting period overlapped with right now
->orWhereColumn(DB::raw('DATE_SUB(NOW(), INTERVAL 1 HOUR)'), '<', 'created_at')
->orWhereColumn(DB::raw('DATE_SUB(NOW(), INTERVAL 1 HOUR)'), '<', 'completed_at')
->orWhereColumn(DB::raw('NOW() - INTERVAL 1 HOUR'), '<', 'created_at')
->orWhereColumn(DB::raw('NOW() - INTERVAL 1 HOUR'), '<', 'completed_at')
)
// Tolerance of 5%
// @phpstan-ignore argument.type (This function works with DB::raw() even though larastan doesn't think so)

View File

@@ -51,7 +51,7 @@ class DistributorController extends Controller
Distributor::create($request->validated());
return to_route('staff.distributors.index')
->with('success', 'Distributor Successfully Added');
->with('success', 'Distributor successfully added');
}
/**
@@ -72,7 +72,7 @@ class DistributorController extends Controller
$distributor->update($request->validated());
return to_route('staff.distributors.index')
->with('success', 'Distributor Successfully Modified');
->with('success', 'Distributor successfully modified');
}
/**
@@ -97,6 +97,6 @@ class DistributorController extends Controller
$distributor->delete();
return to_route('staff.distributors.index')
->with('success', 'Distributor Successfully Deleted');
->with('success', 'Distributor successfully deleted');
}
}

View File

@@ -103,7 +103,7 @@ class DonationController extends Controller
Unit3dAnnounce::addUser($donation->user);
return redirect()->route('staff.donations.index')
->with('success', 'Donation Approved!');
->with('success', 'Donation approved!');
}
/**
@@ -128,6 +128,6 @@ class DonationController extends Controller
$donation->save();
return redirect()->route('staff.donations.index')
->with('success', 'Donation Rejected!');
->with('success', 'Donation rejected!');
}
}

View File

@@ -54,7 +54,7 @@ class DonationGatewayController extends Controller
DonationGateway::create($request->validated());
return redirect()->route('staff.gateways.index')
->with('success', 'Donation Gateway Added Successfully!');
->with('success', 'Donation gateway added successfully!');
}
/**
@@ -77,7 +77,7 @@ class DonationGatewayController extends Controller
$gateway->update($request->validated());
return redirect()->route('staff.gateways.index')
->with('success', 'Donation Gateway Edited Successfully!');
->with('success', 'Donation gateway edited successfully!');
}
/**
@@ -90,6 +90,6 @@ class DonationGatewayController extends Controller
$gateway->delete();
return redirect()->route('staff.gateways.index')
->with('success', 'Donation Gateway Deleted Successfully!');
->with('success', 'Donation gateway deleted successfully!');
}
}

View File

@@ -48,7 +48,7 @@ class DonationPackageController extends Controller
DonationPackage::create($request->validated());
return redirect()->route('staff.packages.index')
->with('success', 'Donation Package Added Successfully!');
->with('success', 'Donation package added successfully!');
}
/**
@@ -67,7 +67,7 @@ class DonationPackageController extends Controller
$package->update($request->validated());
return redirect()->route('staff.packages.index')
->with('success', 'Donation Package Edited Successfully!');
->with('success', 'Donation package edited successfully!');
}
/**
@@ -78,6 +78,6 @@ class DonationPackageController extends Controller
$package->delete();
return redirect()->route('staff.packages.index')
->with('success', 'Donation Package Deleted Successfully!');
->with('success', 'Donation package deleted successfully!');
}
}

View File

@@ -69,7 +69,7 @@ class FlushController extends Controller
}
return to_route('staff.dashboard.index')
->with('success', 'Ghost Peers Have Been Flushed');
->with('success', 'Ghost peers have been flushed');
}
/**
@@ -89,6 +89,6 @@ class FlushController extends Controller
);
return to_route('staff.dashboard.index')
->with('success', 'Chatbox Has Been Flushed');
->with('success', 'Chatbox has been flushed');
}
}

View File

@@ -67,7 +67,7 @@ class GroupController extends Controller
Unit3dAnnounce::addGroup($group);
return to_route('staff.groups.index')
->with('success', 'Group Was Created Successfully!');
->with('success', 'Group was created successfully!');
}
/**
@@ -100,7 +100,7 @@ class GroupController extends Controller
Unit3dAnnounce::addGroup($group);
return to_route('staff.groups.index')
->with('success', 'Group Was Updated Successfully!');
->with('success', 'Group was updated successfully!');
}
/**

View File

@@ -69,7 +69,11 @@ class HomeController extends Controller
->selectRaw('SUM(seeder = FALSE AND active = TRUE) AS leechers')
->selectRaw('SUM(seeder = TRUE AND active = TRUE) AS seeders')
->first()),
'unsolvedReportsCount' => DB::table('reports')->whereNull('snoozed_until')->where('solved', '=', false)->count(),
'unsolvedReportsCount' => DB::table('reports')
->whereNull('snoozed_until')
->whereNull('solved_by')
->where(fn ($query) => $query->whereNull('assigned_to')->orWhere('assigned_to', '=', auth()->id()))
->count(),
'pendingApplicationsCount' => DB::table('applications')->where('status', '=', ModerationStatus::PENDING)->count(),
'certificate' => $certificate,
'uptime' => $systemInformation->uptime(),

View File

@@ -84,7 +84,7 @@ class InternalController extends Controller
$internal->update($request->validated());
return to_route('staff.internals.index')
->with('success', 'Internal Group Was Updated Successfully!');
->with('success', 'Internal group was updated successfully!');
}
/**
@@ -103,7 +103,7 @@ class InternalController extends Controller
Internal::create($request->validated());
return to_route('staff.internals.index')
->with('success', 'New Internal Group added!');
->with('success', 'New internal group added!');
}
/**
@@ -114,6 +114,6 @@ class InternalController extends Controller
$internal->delete();
return to_route('staff.internals.index')
->with('success', 'Group Has Been Removed.');
->with('success', 'Group has been removed.');
}
}

View File

@@ -50,6 +50,6 @@ class MassActionController extends Controller
}
return to_route('staff.dashboard.index')
->with('success', 'Unvalidated Accounts Are Now Validated');
->with('success', 'Unvalidated accounts are now validated');
}
}

View File

@@ -50,7 +50,7 @@ class MediaLanguageController extends Controller
MediaLanguage::create($request->validated());
return to_route('staff.media_languages.index')
->with('success', 'Media Language Successfully Added');
->with('success', 'Media language successfully added');
}
/**
@@ -71,7 +71,7 @@ class MediaLanguageController extends Controller
$mediaLanguage->update($request->validated());
return to_route('staff.media_languages.index')
->with('success', 'Media Language Successfully Updated');
->with('success', 'Media language successfully updated');
}
/**
@@ -84,6 +84,6 @@ class MediaLanguageController extends Controller
$mediaLanguage->delete();
return to_route('staff.media_languages.index')
->with('success', 'Media Language Has Successfully Been Deleted');
->with('success', 'Media language has successfully been deleted');
}
}

View File

@@ -109,7 +109,7 @@ class ModerationController extends Controller
TorrentHelper::approveHelper($id);
return to_route('staff.moderation.index')
->with('success', 'Torrent Approved');
->with('success', 'Torrent approved');
case ModerationStatus::REJECTED:
$torrent->update([
@@ -133,7 +133,7 @@ class ModerationController extends Controller
Unit3dAnnounce::addTorrent($torrent);
return to_route('staff.moderation.index')
->with('success', 'Torrent Rejected');
->with('success', 'Torrent rejected');
case ModerationStatus::POSTPONED:
$torrent->update([
@@ -157,7 +157,7 @@ class ModerationController extends Controller
Unit3dAnnounce::addTorrent($torrent);
return to_route('staff.moderation.index')
->with('success', 'Torrent Postponed');
->with('success', 'Torrent postponed');
default: // Undefined status
return to_route('torrents.show', ['id' => $id])

View File

@@ -51,7 +51,7 @@ class RegionController extends Controller
Region::create($request->validated());
return to_route('staff.regions.index')
->with('success', 'Region Successfully Added');
->with('success', 'Region successfully added');
}
/**
@@ -72,7 +72,7 @@ class RegionController extends Controller
$region->update($request->validated());
return to_route('staff.regions.index')
->with('success', 'Region Successfully Modified');
->with('success', 'Region successfully modified');
}
/**
@@ -86,6 +86,6 @@ class RegionController extends Controller
$region->delete();
return to_route('staff.regions.index')
->with('success', 'Region Successfully Deleted');
->with('success', 'Region successfully deleted');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* 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 Obi-Wana
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Controllers\Staff;
use App\Http\Controllers\Controller;
use App\Http\Requests\Staff\StoreReportAssigneeRequest;
use App\Models\Report;
use App\Models\User;
use App\Notifications\NewReportAssigned;
use Illuminate\Http\Request;
class ReportAssigneeController extends Controller
{
final public function store(StoreReportAssigneeRequest $request, Report $report): \Illuminate\Http\RedirectResponse
{
$report->update($request->validated());
$assignedStaff = User::findOrFail($request->integer('assigned_to'));
$assignedStaff->notify(new NewReportAssigned($report));
return to_route('staff.reports.show', ['report' => $report])
->with('success', trans('ticket.assigned-success'));
}
final public function destroy(Request $request, Report $report): \Illuminate\Http\RedirectResponse
{
$report->update([
'assigned_to' => null,
]);
return to_route('staff.reports.show', ['report' => $report])
->with('success', trans('ticket.unassigned-success'));
}
}

View File

@@ -19,8 +19,10 @@ namespace App\Http\Controllers\Staff;
use App\Http\Controllers\Controller;
use App\Http\Requests\Staff\UpdateReportRequest;
use App\Models\Conversation;
use App\Models\Group;
use App\Models\PrivateMessage;
use App\Models\Report;
use App\Models\User;
/**
* @see \Tests\Todo\Feature\Http\Controllers\ReportControllerTest
@@ -43,7 +45,11 @@ class ReportController extends Controller
// cspell:ignore punct
preg_match_all('#\bhttps?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', (string) $report->message, $match);
return view('Staff.report.show', ['report' => $report, 'urls' => $match[0]]);
return view('Staff.report.show', [
'report' => $report,
'urls' => $match[0],
'staff' => User::whereIn('group_id', Group::where('is_modo', '=', 1)->whereNot('slug', '=', 'bot')->select('id'))->get(),
]);
}
/**
@@ -53,12 +59,15 @@ class ReportController extends Controller
{
$staff = auth()->user();
if ($report->solved) {
if ($report->solved_by !== null) {
return to_route('staff.reports.index')
->withErrors('This Report Has Already Been Solved');
->withErrors('This report has already been solved');
}
$report->update(['solved' => true, 'staff_id' => $staff->id] + $request->validated());
$report->update([
'solved_by' => $staff->id,
'solved_at' => now(),
] + $request->validated());
$conversation = Conversation::create(['subject' => 'Your Report Has A New Verdict']);

View File

@@ -50,7 +50,7 @@ class ResolutionController extends Controller
Resolution::create($request->validated());
return to_route('staff.resolutions.index')
->with('success', 'Resolution Successfully Added');
->with('success', 'Resolution successfully added');
}
/**
@@ -71,7 +71,7 @@ class ResolutionController extends Controller
$resolution->update($request->validated());
return to_route('staff.resolutions.index')
->with('success', 'Resolution Successfully Modified');
->with('success', 'Resolution successfully modified');
}
/**
@@ -84,6 +84,6 @@ class ResolutionController extends Controller
$resolution->delete();
return to_route('staff.resolutions.index')
->with('success', 'Resolution Successfully Deleted');
->with('success', 'Resolution successfully deleted');
}
}

Some files were not shown because too many files have changed in this diff Show More