diff --git a/.editorconfig b/.editorconfig index f7e1f865b..95f41c378 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,23 +4,15 @@ root = true [*] charset = utf-8 indent_style = space -indent_size = 4 +indent_size = 2 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true ij_any_keep_indents_on_empty_lines = false -# quote_type = single - -[*.ts] -ij_typescript_force_quote_style = true -ij_typescript_use_double_quotes = false -# ij_typescript_imports_wrap = split_into_lines -ij_typescript_spaces_within_imports = true -ij_typescript_spaces_within_object_type_braces = true -ij_typescript_spaces_within_object_literal_braces = true +quote_type = single -[*.{html,json,twig}] -indent_size = 2 +# [*.php] +# indent_size = 4 [*.md] max_line_length = off diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 58fa96f6c..8dc242a5b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -26,12 +26,23 @@ Please delete options that are not relevant. **Test Configuration**: - - MyAAC Version: (latest: 0.8.15) + - MyAAC Version: (latest: 0.8.16) - Browser: + - [ ] Chrome + - [ ] Firefox + - [ ] Opera + - [ ] Safari + - [ ] Edge + - [ ] Other - Operating System: + - [ ] Windows + - [ ] Ubuntu + - [ ] MacOS + - [ ] Other ## Checklist + - [ ] I've created separated branch from main updated - [ ] My code follows the style guidelines of this project - [ ] I followed project rules, best practices, and code indentation - [ ] I have performed a self-review of my own code diff --git a/.github/labeler.yml b/.github/labeler.yml index 1a6a87f13..0824a62b8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -9,7 +9,7 @@ - any: ['install/**/*'] "Area: Plugins": - - any: ['plugins/**/*'] + - any: ['plugins/**/*', 'payments/**/*'] "Area: System": - any: ['system/**/*'] @@ -20,3 +20,5 @@ "Area: Tools": - any: ['tools/**/*'] +"Area: Workflow": + - any: ['./github/**/*'] diff --git a/.github/workflows/phplint.yml b/.github/workflows/phplint.yml index a920166b2..c8ce1fa73 100644 --- a/.github/workflows/phplint.yml +++ b/.github/workflows/phplint.yml @@ -1,16 +1,13 @@ name: PHP Linting on: - pull_request: - branches: [master] - push: - branches: [master] - + pull_request: + types: [ opened, synchronize, reopened, ready_for_review ] jobs: - phplint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: overtrue/phplint@3.4.0 - with: - path: . - options: --exclude="system/libs/polyfill-mbstring/bootstrap80.php" + phplint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: overtrue/phplint@3.4.0 + with: + path: . + options: --exclude="system/libs/polyfill-mbstring/bootstrap80.php" diff --git a/.github/workflows/prettier-php.yml b/.github/workflows/prettier-php.yml new file mode 100644 index 000000000..ac0f5a269 --- /dev/null +++ b/.github/workflows/prettier-php.yml @@ -0,0 +1,62 @@ +--- +#--- +#name: Format Prettier +#on: +# pull_request: +# types: [ opened, synchronize, reopened, ready_for_review ] +# merge_group: +# push: +# paths: +# - "/**" +#jobs: +# cancel-runs: +# if: github.event_name == 'pull_request' && github.ref != 'refs/heads/main' +# runs-on: ubuntu-latest +# steps: +# - name: Cancel Previous Runs +# uses: styfle/cancel-workflow-action@0.9.1 +# with: +# access_token: ${{ github.token }} +# +# prettier: +# runs-on: ubuntu-latest +# steps: +# - name: Set up Git +# if: ${{ github.ref != 'refs/heads/main' }} +# run: | +# git config --global user.email "github-actions[bot]@users.noreply.github.com" +# git config --global user.name "GitHub Actions" +# +# - name: Actions checkout +# if: ${{ github.ref != 'refs/heads/main' }} +# uses: actions/checkout@v3 +# with: +# repository: ${{ github.event.pull_request.head.repo.full_name }} +# ref: ${{ github.event.pull_request.head.ref }} +# token: ${{ secrets.GITHUB_TOKEN }} +# +# - name: Setup Node.js +# uses: actions/setup-node@v3 +# with: +# node-version: 18 +# +# - name: Install Dependencies +# run: npm install --save-dev prettier @prettier/plugin-php husky +# +# - name: Run prettier code format +# run: npm run prettier:format +# +# +## npx prettier --write $(git diff --name-only --diff-filter d | grep -E '\.(php|css|js)$' | xargs) --plugin=@prettier/plugin-php +# +# # npx prettier --write "**/workflow_test/*.{php,css,js}" --plugin=@prettier/plugin-php +# +# - name: Run add and commit +# if: ${{ github.ref != 'refs/heads/main' }} +# uses: EndBug/add-and-commit@v9 +# with: +# author_name: GitHub Actions +# author_email: github-actions[bot]@users.noreply.github.com +# message: "PHP code format - (prettier)" +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.husky/_/.gitignore b/.husky/_/.gitignore new file mode 100644 index 000000000..f59ec20aa --- /dev/null +++ b/.husky/_/.gitignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh new file mode 100644 index 000000000..cec959a6b --- /dev/null +++ b/.husky/_/husky.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env sh +if [ -z "$husky_skip_init" ]; then + debug () { + if [ "$HUSKY_DEBUG" = "1" ]; then + echo "husky (debug) - $1" + fi + } + + readonly hook_name="$(basename -- "$0")" + debug "starting $hook_name..." + + if [ "$HUSKY" = "0" ]; then + debug "HUSKY env variable is set to 0, skipping hook" + exit 0 + fi + + if [ -f ~/.huskyrc ]; then + debug "sourcing ~/.huskyrc" + . ~/.huskyrc + fi + + readonly husky_skip_init=1 + export husky_skip_init + sh -e "$0" "$@" + exitCode="$?" + + if [ $exitCode != 0 ]; then + echo "husky - $hook_name hook exited with code $exitCode (error)" + fi + + if [ $exitCode = 127 ]; then + echo "husky - command not found in PATH=$PATH" + fi + + exit $exitCode +fi diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..cf0c46b93 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx --no-install lint-staged diff --git a/.prettierignore.txt b/.prettierignore.txt new file mode 100644 index 000000000..aca70b982 --- /dev/null +++ b/.prettierignore.txt @@ -0,0 +1,15 @@ +# Ignore system libs + +plugins/ +system/libs/PagSeguroLibrary +system/libs/phpmailer +system/libs/polyfill-mbstring +system/libs/pot +system/libs/semver +system/libs/Twig +system/cache +system/php_sessions +node_modules + +# Files +login.php diff --git a/.prettierrc.txt b/.prettierrc.txt new file mode 100644 index 000000000..b36f6ee80 --- /dev/null +++ b/.prettierrc.txt @@ -0,0 +1,43 @@ +{ + "overrides": [ + { + "files": [ + "*.php" + ], + "options": { + "parser": "php", + "singleQuote": true, + "semi": false, + "tabWidth": 2, + "printWidth": 100, + "trailingComma": "none" + } + }, + { + "files": [ + "*.css" + ], + "options": { + "arrowParens": "always", + "bracketSameLine": false, + "bracketSpacing": true, + "semi": true, + "experimentalTernaries": false, + "singleQuote": false, + "jsxSingleQuote": false, + "quoteProps": "as-needed", + "trailingComma": "all", + "singleAttributePerLine": false, + "htmlWhitespaceSensitivity": "css", + "vueIndentScriptAndStyle": false, + "proseWrap": "preserve", + "insertPragma": true, + "requirePragma": false, + "tabWidth": 2, + "useTabs": false, + "embeddedLanguageFormatting": "auto", + "printWidth": 80 + } + } + ] +} diff --git a/login.php b/login.php index 1ae3483cc..04b108f89 100644 --- a/login.php +++ b/login.php @@ -1,36 +1,36 @@ getAttribute('startdate') : $table1->getAttribute('enddate'); - return date_create("{$date}")->format('U'); - } else { - foreach ($table1 as $attr) { - if ($attr) { - return $attr->getAttribute($table2); - } - } + if ($table1) { + if ($date) { + $date = ($table2) ? $table1->getAttribute('startdate') : $table1->getAttribute('enddate'); + return date_create("{$date}")->format('U'); + } else { + foreach ($table1 as $attr) { + if ($attr) { + return $attr->getAttribute($table2); } + } } + } } $request = file_get_contents('php://input'); @@ -38,129 +38,124 @@ function parseEvent($table1, $date, $table2) $action = $result->type ?? ''; switch ($action) { - case 'cacheinfo': - $playersonline = $db->query("select count(*) from `players_online`")->fetchAll(); - die(json_encode([ - 'playersonline' => (intval($playersonline[0][0])), - 'twitchstreams' => 0, - 'twitchviewer' => 0, - 'gamingyoutubestreams' => 0, - 'gamingyoutubeviewer' => 0 - ])); - break; - - case 'eventschedule': - $eventlist = []; - $file_path = config('server_path') . 'data/XML/events.xml'; - if (!file_exists($file_path)) { - die(json_encode([])); - } - $xml = new DOMDocument; - $xml->load($file_path); - $tableevent = $xml->getElementsByTagName('event'); - - foreach ($tableevent as $event) { - if ($event) { - $eventlist[] = [ - 'colorlight' => parseEvent($event->getElementsByTagName('colors'), false, 'colorlight'), - 'colordark' => parseEvent($event->getElementsByTagName('colors'), false, 'colordark'), - 'description' => parseEvent($event->getElementsByTagName('description'), false, 'description'), - 'displaypriority' => intval(parseEvent($event->getElementsByTagName('details'), false, 'displaypriority')), - 'enddate' => intval(parseEvent($event, true, false)), - 'isseasonal' => getBoolean(intval(parseEvent($event->getElementsByTagName('details'), false, 'isseasonal'))), - 'name' => $event->getAttribute('name'), - 'startdate' => intval(parseEvent($event, true, true)), - 'specialevent' => intval(parseEvent($event->getElementsByTagName('details'), false, 'specialevent')) - ]; - } - } - die(json_encode(['eventlist' => $eventlist, 'lastupdatetimestamp' => time()])); - break; - - case 'boostedcreature': - $creatureBoost = $db->query("select * from " . $db->tableName('boosted_creature'))->fetchAll(); - $bossBoost = $db->query("select * from " . $db->tableName('boosted_boss'))->fetchAll(); - die(json_encode([ - 'boostedcreature' => true, - 'creatureraceid' => intval($creatureBoost[0]['raceid']), - 'bossraceid' => intval($bossBoost[0]['raceid']) - ])); - break; - - case 'login': - - $ip = configLua('ip'); - $port = configLua('gameProtocolPort'); - - // default world info - $world = [ - 'id' => 0, - 'name' => configLua('serverName'), - 'externaladdress' => $ip, - 'externaladdressprotected' => $ip, - 'externaladdressunprotected' => $ip, - 'externalport' => $port, - 'externalportprotected' => $port, - 'externalportunprotected' => $port, - 'previewstate' => 0, - 'location' => 'BRA', // BRA, EUR, USA - 'anticheatprotection' => false, - 'pvptype' => array_search(configLua('worldType'), ['pvp', 'no-pvp', 'pvp-enforced']), - 'istournamentworld' => false, - 'restrictedstore' => false, - 'currenttournamentphase' => 2 + case 'cacheinfo': + $playersonline = $db->query("SELECT count(*) FROM `players_online`")->fetchAll(); + die(json_encode([ + 'playersonline' => (intval($playersonline[0][0])), + 'twitchstreams' => 0, + 'twitchviewer' => 0, + 'gamingyoutubestreams' => 0, + 'gamingyoutubeviewer' => 0 + ])); + + case 'eventschedule': + $eventlist = []; + $file_path = config('server_path') . 'data/XML/events.xml'; + if (!file_exists($file_path)) { + die(json_encode([])); + } + $xml = new DOMDocument; + $xml->load($file_path); + $tableevent = $xml->getElementsByTagName('event'); + + foreach ($tableevent as $event) { + if ($event) { + $eventlist[] = [ + 'colorlight' => parseEvent($event->getElementsByTagName('colors'), false, 'colorlight'), + 'colordark' => parseEvent($event->getElementsByTagName('colors'), false, 'colordark'), + 'description' => parseEvent($event->getElementsByTagName('description'), false, 'description'), + 'displaypriority' => intval(parseEvent($event->getElementsByTagName('details'), false, 'displaypriority')), + 'enddate' => intval(parseEvent($event, true, false)), + 'isseasonal' => getBoolean(intval(parseEvent($event->getElementsByTagName('details'), false, 'isseasonal'))), + 'name' => $event->getAttribute('name'), + 'startdate' => intval(parseEvent($event, true, true)), + 'specialevent' => intval(parseEvent($event->getElementsByTagName('details'), false, 'specialevent')) ]; + } + } + die(json_encode(['eventlist' => $eventlist, 'lastupdatetimestamp' => time()])); + + case 'boostedcreature': + $creatureBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_creature'))->fetchAll(); + $bossBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_boss'))->fetchAll(); + die(json_encode([ + 'boostedcreature' => true, + 'creatureraceid' => intval($creatureBoost[0]['raceid']), + 'bossraceid' => intval($bossBoost[0]['raceid']) + ])); + + case 'login': + $ip = configLua('ip'); + $port = configLua('gameProtocolPort'); + + // default world info + $world = [ + 'id' => 0, + 'name' => configLua('serverName'), + 'externaladdress' => $ip, + 'externaladdressprotected' => $ip, + 'externaladdressunprotected' => $ip, + 'externalport' => $port, + 'externalportprotected' => $port, + 'externalportunprotected' => $port, + 'previewstate' => 0, + 'location' => 'BRA', // BRA, EUR, USA + 'anticheatprotection' => false, + 'pvptype' => array_search(configLua('worldType'), ['pvp', 'no-pvp', 'pvp-enforced']), + 'istournamentworld' => false, + 'restrictedstore' => false, + 'currenttournamentphase' => 2 + ]; - $account = new OTS_Account(); - $account->findByEmail($result->email); - $config_salt_enabled = fieldExist('salt', 'accounts'); - $current_password = encrypt(($config_salt_enabled ? $account->getCustomField('salt') : '') . $result->password); + $account = new OTS_Account(); + $account->findByEmail($result->email); + $config_salt_enabled = fieldExist('salt', 'accounts'); + $current_password = encrypt(($config_salt_enabled ? $account->getCustomField('salt') : '') . $result->password); - if (!$account->isLoaded() || $account->getPassword() != $current_password) { - sendError('Email or password is not correct.'); - } + if (!$account->isLoaded() || $account->getPassword() != $current_password) { + sendError('Email or password is not correct.'); + } - // common columns - $columns = 'name, level, sex, vocation, looktype, lookhead, lookbody, looklegs, lookfeet, lookaddons, lastlogin, isreward, istutorial, ismain, hidden'; - $players = $db->query("select {$columns} from players where account_id = {$account->getId()} AND deletion = 0"); - $characters = []; - if ($players && $players->rowCount() > 0) { - $players = $players->fetchAll(); - foreach ($players as $player) { - $characters[] = createChar($config, $player); - } - } + // common columns + $columns = 'name, level, sex, vocation, looktype, lookhead, lookbody, looklegs, lookfeet, lookaddons, lastlogin, isreward, istutorial, ismain, hidden'; + $players = $db->query("SELECT {$columns} FROM players WHERE account_id = {$account->getId()} AND deletion = 0"); + $characters = []; + if ($players && $players->rowCount() > 0) { + $players = $players->fetchAll(); + foreach ($players as $player) { + $characters[] = createChar($config, $player); + } + } - $query = $db->query("select `premdays`, `lastday` from `accounts` where `id` = {$account->getId()}"); - $premU = 0; - if ($query->rowCount() > 0) { - $premU = checkPremium($db, $query->fetch(), $account); - } else { - sendError("Error while fetching your account data. Please contact admin."); - } + $query = $db->query("SELECT `premdays`, `lastday` FROM `accounts` WHERE `id` = {$account->getId()}"); + $premU = 0; + if ($query->rowCount() > 0) { + $premU = checkPremium($db, $query->fetch(), $account); + } else { + sendError("Error while fetching your account data. Please contact admin."); + } - $worlds = [$world]; - $playdata = compact('worlds', 'characters'); - $session = [ - 'sessionkey' => "$result->email\n$result->password", - 'lastlogintime' => $account ? $account->getLastLogin() : 0, - 'ispremium' => $account->isPremium(), - 'premiumuntil' => $premU, - 'status' => 'active', // active, frozen or suspended - 'returnernotification' => false, - 'showrewardnews' => true, - 'isreturner' => true, - 'fpstracking' => false, - 'optiontracking' => false, - 'tournamentticketpurchasestate' => 0, - 'emailcoderequest' => false - ]; - die(json_encode(compact('session', 'playdata'))); - break; + $worlds = [$world]; + $playdata = compact('worlds', 'characters'); + $session = [ + 'sessionkey' => "$result->email\n$result->password", + 'lastlogintime' => $account ? $account->getLastLogin() : 0, + 'ispremium' => $account->isPremium(), + 'premiumuntil' => $premU, + 'status' => 'active', // active, frozen or suspended + 'returnernotification' => false, + 'showrewardnews' => true, + 'isreturner' => true, + 'fpstracking' => false, + 'optiontracking' => false, + 'tournamentticketpurchasestate' => 0, + 'emailcoderequest' => false + ]; + die(json_encode(compact('session', 'playdata'))); - default: - sendError("Unrecognized event {$action}."); - break; + default: + sendError("Unrecognized event {$action}."); + break; } /** @@ -171,25 +166,25 @@ function parseEvent($table1, $date, $table2) */ function createChar($config, $player) { - return [ - 'worldid' => 0, - 'name' => $player['name'], - 'ismale' => intval($player['sex']) === 1, - 'tutorial' => (bool)$player['istutorial'], - 'level' => intval($player['level']), - 'vocation' => $config['vocations'][$player['vocation']], - 'outfitid' => intval($player['looktype']), - 'headcolor' => intval($player['lookhead']), - 'torsocolor' => intval($player['lookbody']), - 'legscolor' => intval($player['looklegs']), - 'detailcolor' => intval($player['lookfeet']), - 'addonsflags' => intval($player['lookaddons']), - 'ishidden' => (bool)$player['hidden'], - 'istournamentparticipant' => false, - 'ismaincharacter' => (bool)($player['ismain']), - 'dailyrewardstate' => intval($player['isreward']), - 'remainingdailytournamentplaytime' => 0 - ]; + return [ + 'worldid' => 0, + 'name' => $player['name'], + 'ismale' => intval($player['sex']) === 1, + 'tutorial' => (bool)$player['istutorial'], + 'level' => intval($player['level']), + 'vocation' => $config['vocations'][$player['vocation']], + 'outfitid' => intval($player['looktype']), + 'headcolor' => intval($player['lookhead']), + 'torsocolor' => intval($player['lookbody']), + 'legscolor' => intval($player['looklegs']), + 'detailcolor' => intval($player['lookfeet']), + 'addonsflags' => intval($player['lookaddons']), + 'ishidden' => (bool)$player['hidden'], + 'istournamentparticipant' => false, + 'ismaincharacter' => (bool)($player['ismain']), + 'dailyrewardstate' => intval($player['isreward']), + 'remainingdailytournamentplaytime' => 0 + ]; } /** @@ -201,27 +196,27 @@ function createChar($config, $player) */ function checkPremium($db, $query, $account) { - $lastDay = (int)$query['lastday']; - $timeNow = time(); - - if ($lastDay < $timeNow) { - $premDays = 0; - $lastDay = 0; - } else if ($lastDay == 0) { - $premDays = 0; + $lastDay = (int)$query['lastday']; + $timeNow = time(); + + if ($lastDay < $timeNow) { + $premDays = 0; + $lastDay = 0; + } else if ($lastDay == 0) { + $premDays = 0; + } else { + $daysLeft = (int)(($lastDay - $timeNow) / 86400); + $timeLeft = (int)(($lastDay - $timeNow) % 86400); + if ($daysLeft > 0) { + $premDays = $daysLeft; + } else if ($timeLeft > 0) { + $premDays = 1; } else { - $daysLeft = (int)(($lastDay - $timeNow) / 86400); - $timeLeft = (int)(($lastDay - $timeNow) % 86400); - if ($daysLeft > 0) { - $premDays = $daysLeft; - } else if ($timeLeft > 0) { - $premDays = 1; - } else { - $premDays = 0; - $lastDay = 0; - } + $premDays = 0; + $lastDay = 0; } + } - $db->query("update `accounts` set `premdays` = {$premDays}, `lastday` = {$lastDay} where `id` = {$account->getId()}"); - return $lastDay; + $db->query("UPDATE `accounts` SET `premdays` = {$premDays}, `lastday` = {$lastDay} WHERE `id` = {$account->getId()}"); + return $lastDay; } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..ff419a0f3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1029 @@ +{ + "name": "myaac_fork", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@prettier/plugin-php": "^0.22.2", + "git-format-staged": "^3.1.1", + "husky": "^8.0.0", + "lint-staged": "^15.2.2", + "prettier": "3.2.5", + "pretty-quick": "^4.0.0" + } + }, + "node_modules/@prettier/plugin-php": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@prettier/plugin-php/-/plugin-php-0.22.2.tgz", + "integrity": "sha512-md0+7tNbsP0oy+wIP3KZZc6fzx1k1jtWaMjOy/gM8yU9f2BDYEi+iHOc/UNPihYvPI28zFTbjvlhH4QXQjQwNg==", + "dev": true, + "dependencies": { + "linguist-languages": "^7.27.0", + "php-parser": "^3.1.5" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dev": true, + "dependencies": { + "type-fest": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-format-staged": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/git-format-staged/-/git-format-staged-3.1.1.tgz", + "integrity": "sha512-P749fkktaiAchFZKR7bgdvruzhvbcIDr1uRBrS9/Wdimb7wH1Twchz9gOixj8tUaHVMuXY/ckDojfOwV6AxgPA==", + "dev": true, + "bin": { + "git-format-staged": "git-format-staged" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/linguist-languages": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/linguist-languages/-/linguist-languages-7.27.0.tgz", + "integrity": "sha512-Wzx/22c5Jsv2ag+uKy+ITanGA5hzvBZngrNGDXLTC7ZjGM6FLCYGgomauTkxNJeP9of353OM0pWqngYA180xgw==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "15.2.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz", + "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", + "dev": true, + "dependencies": { + "chalk": "5.3.0", + "commander": "11.1.0", + "debug": "4.3.4", + "execa": "8.0.1", + "lilconfig": "3.0.0", + "listr2": "8.0.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.4" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz", + "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.0.0", + "rfdc": "^1.3.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/php-parser": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/php-parser/-/php-parser-3.1.5.tgz", + "integrity": "sha512-jEY2DcbgCm5aclzBdfW86GM6VEIWcSlhTBSHN1qhJguVePlYe28GhwS0yoeLYXpM2K8y6wzLwrbq814n2PHSoQ==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-quick": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-4.0.0.tgz", + "integrity": "sha512-M+2MmeufXb/M7Xw3Afh1gxcYpj+sK0AxEfnfF958ktFeAyi5MsKY5brymVURQLgPLV1QaF5P4pb2oFJ54H3yzQ==", + "dev": true, + "dependencies": { + "execa": "^5.1.1", + "find-up": "^5.0.0", + "ignore": "^5.3.0", + "mri": "^1.2.0", + "picocolors": "^1.0.0", + "picomatch": "^3.0.1", + "tslib": "^2.6.2" + }, + "bin": { + "pretty-quick": "lib/cli.mjs" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/pretty-quick/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/pretty-quick/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-quick/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/pretty-quick/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-quick/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pretty-quick/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-quick/node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pretty-quick/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/pretty-quick/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..d63f08276 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "scripts": { + "prettier:format": "prettier --write \"**/{*.php,*.html}\" --plugin=@prettier/plugin-php", + "prettier:styles": "prettier --write \"**/*.css\"", + "prepare": "husky install" + }, + "devDependencies": { + "@prettier/plugin-php": "^0.22.2", + "git-format-staged": "^3.1.1", + "husky": "^8.0.0", + "lint-staged": "^15.2.2", + "prettier": "3.2.5", + "pretty-quick": "^4.0.0" + }, + "lint-staged": { + "**/*.{php,html}": "npm run prettier:format", + "*.css": "npm run prettier:styles" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + } +} diff --git a/test/config.php b/test/config.php new file mode 100644 index 000000000..2fc8b52a8 --- /dev/null +++ b/test/config.php @@ -0,0 +1,388 @@ + + * @author OpenTibiaBR + * @copyright 2023 MyAAC + * @link https://github.com/opentibiabr/myaac + */ + +$config = array( + // directories & files + 'server_path' => '', // path to the server directory (same directory where config file is located) + + /** + * Environment Setting + * + * if you use this script on your live server - set to 'prod' (production) + * if you want to test and debug the script locally, or develop plugins, set to 'dev' (development) + * WARNING: on 'dev' cache is disabled, so site will be significantly slower !!! + * WARNING2: on 'dev' all PHP errors/warnings are displayed + * Recommended: 'prod' cause of speed (page load time is better) + */ + 'env' => 'prod', // 'prod' for production and 'dev' for development + + 'template' => 'tibiacom', // template used by website (kathrine, tibiacom) + 'template_allow_change' => false, // allow users to choose their own template while browsing website? + + 'vocations_amount' => 4, // how much basic vocations your server got (without promotion) + + // what client version are you using on this OT? + // used for the Downloads page and some templates as well + 'client' => 1321, // 1321 = client 13.21 + + 'session_prefix' => 'myaac_', // must be unique for every site on your server + 'friendly_urls' => false, // mod_rewrite is required for this, it makes links looks more elegant to eye, and also are SEO friendly (example: https://localhost/guilds/Testing instead of https://localhost?subtopic=guilds&name=Testing). Remember to rename .htaccess.dist to .htaccess + 'gzip_output' => false, // gzip page content before sending it to the browser, uses less bandwidth but more cpu cycles + + // gesior backward support (templates & pages) + // allows using gesior templates and pages with myaac + // might bring some performance when disabled + 'backward_support' => true, + + // head options (html) + 'meta_description' => 'Tibia is a free massive multiplayer online role playing game (MMORPG).', // description of the site + 'meta_keywords' => 'free online game, free multiplayer game, ots, open tibia server', // keywords list separated by commas + 'title_separator' => ' - ', + + // footer + 'footer' => ''/*'
Your Server © 2016. All rights reserved.'*/, + + 'language' => 'en', // default language (currently only 'en' available) + 'language_allow_change' => false, + + 'visitors_counter' => true, + 'visitors_counter_ttl' => 10, // how long visitor will be marked as online (in minutes) + 'views_counter' => true, + + // cache system. by default file cache is used + 'cache_engine' => 'auto', // apc, apcu, eaccelerator, xcache, file, auto, or blank to disable. + 'cache_prefix' => 'myaac_', // have to be unique if running more MyAAC instances on the same server (except file system cache) + + // database details (leave blank for auto detect from config.lua) + 'database_host' => '', + 'database_port' => '', // leave blank to default 3306 + 'database_user' => '', + 'database_password' => '', + 'database_name' => '', + 'database_log' => false, // should database queries be logged and and saved into system/logs/database.log? + 'database_socket' => '', // set if you want to connect to database through socket (example: /var/run/mysqld/mysqld.sock) + 'database_persistent' => false, // use database permanent connection (like server), may speed up your site + + // multiworld system (only TFS 0.3) + 'multiworld' => false, // use multiworld system? + 'worlds' => array( // list of worlds + //'1' => 'Your World Name', + //'2' => 'Your Second World Name' + ), + + // images + 'outfit_images_url' => './outfit/animoutfit.php', // set to animoutfit.php for animated outfit + 'item_images_url' => 'images/items/', // set to images/items if you host your own items in images folder + + // account + 'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager) + 'account_login_by_email' => true, // use email instead of Account Name like in latest Tibia + 'account_login_by_email_fallback' => false, // allow also additionally login by Account Name/Number (for users that might forget their email) + 'account_create_auto_login' => false, // auto login after creating account? + 'account_create_character_create' => true, // allow directly to create character on create account page? + 'account_mail_verify' => false, // force users to confirm their email addresses when registering account + 'account_mail_confirmed_reward' => [ // reward users for confirming their E-Mails + // account_mail_verify needs to be enabled too + 'premium_days' => 0, + 'coins_transferable' => 0, + 'coins' => 0, + 'message' => 'You received %d %s for confirming your E-Mail address.' // example: You received 20 coins for confirming your E-Mail address. + ], + 'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email) + 'account_premium_days' => 0, // default premium days on new account + 'account_premium_coins' => 0, // default coins on new account + 'account_welcome_mail' => false, // send welcome email when user registers + 'account_welcome_mail_show_pass' => false, // send password in welcome email + 'account_mail_change' => 2, // how many days user need to change email to account - block hackers + 'account_country' => true, // user will be able to set country of origin when registering account, this information will be viewable in others places aswell + 'account_country_recognize' => true, // should country of user be automatically recognized by his IP? This makes an external API call to http://ipinfo.io + + 'account_change_coin_type' => 'coins', // which coin you want to use, coins or coins_transferable to buy changes at site + 'account_change_character_name' => false, // can user change their character name for coins? + 'account_change_character_name_coins' => 250, // cost of name change + 'account_change_character_sex' => false, // can user change their character sex for coins? + 'account_change_character_sex_coins' => 150, // cost of sex change + 'account_change_character_main' => true, // can user change their main character for coins? + 'account_change_character_main_coins' => 250, // cost of main change + 'characters_per_account' => 10, // max. number of characters per account + 'account_update_info_on_register' => true, // let player update your 'Public Information' when register at first time only + + // recovery key + 'recovery_key_length' => 15, // length of recovery key code + 'account_show_rk' => false, + 'generate_new_reckey' => true, // let player generate new recovery key, he will receive e-mail with new rec key (not display on page, hacker can't generate rec key) + 'generate_new_reckey_price' => 250, // coins price for new recovery key + + // mail + 'mail_enabled' => false, // is aac maker configured to send e-mails? + 'mail_address' => 'no-reply@your-server.org', // server e-mail address (from:) + 'mail_admin' => 'your-address@your-server.org', // admin email address, where mails from contact form will be sent + 'mail_signature' => array( // signature that will be included at the end of every message sent using _mail function + 'plain' => ""/*"--\nMy Server,\nhttp://www.myserver.com"*/, + 'html' => ''/*'
My Server,\nmyserver.com'*/ + ), + 'smtp_enabled' => false, // send by smtp or mail function (set false if use mail function, set to true if you use GMail or Microsoft Outlook) + 'smtp_host' => '', // mail host. smtp.gmail.com for GMail / smtp-mail.outlook.com for Microsoft Outlook + 'smtp_port' => 25, // 25 (default) / 465 (ssl, GMail) / 587 (tls, Microsoft Outlook) + 'smtp_auth' => true, // need authorization? + 'smtp_user' => 'admin@example.org', // here your email username + 'smtp_pass' => '', + 'smtp_secure' => '', // What kind of encryption to use on the SMTP connection. Options: '', 'ssl' (GMail) or 'tls' (Microsoft Outlook) + 'smtp_debug' => false, // set true to debug (you will see more info in error.log) + + // reCAPTCHA (prevent spam bots) + 'recaptcha_enabled' => false, // enable recaptcha verification code + 'recaptcha_site_key' => '', // get your own site and secret keys at https://www.google.com/recaptcha + 'recaptcha_secret_key' => '', + 'recaptcha_theme' => 'light', // light, dark + + // e-mail senders + 'send_mail_when_change_password' => true, // send e-mail with new password when change password to account + 'send_mail_when_generate_reckey' => true, // send e-mail with rec key (key is displayed on page anyway when generate) + + // genders (aka sex) + 'genders' => array( + 0 => 'Female', + 1 => 'Male' + ), + + // vocations + 'vocations' => array( + 0 => 'None', + 1 => 'Sorcerer', + 2 => 'Druid', + 3 => 'Paladin', + 4 => 'Knight', + 5 => 'Master Sorcerer', + 6 => 'Elder Druid', + 7 => 'Royal Paladin', + 8 => 'Elite Knight', + ), + + // new character config + 'character_samples' => array( // vocations, format: ID_of_vocation => 'Name of Character to copy' + //0 => 'Rook Sample', + 1 => 'Sorcerer Sample', + 2 => 'Druid Sample', + 3 => 'Paladin Sample', + 4 => 'Knight Sample' + ), + + 'use_character_sample_skills' => false, + + // it must show limited number of players after using search in character page + 'characters_search_limit' => 15, + + // town list used when creating character + // won't be displayed if there is only one item (rookgaard for example) + 'character_towns' => array(1), + + // characters length + // This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum length to be 21. + 'character_name_min_length' => 4, + 'character_name_max_length' => 21, + + // list of towns + // if you use TFS 1.3 with support for 'towns' table in database, then you can ignore this - it will be configured automatically (generated from your .OTBM map) + 'towns' => array( + 0 => 'No Town', + 1 => 'Tutorial City', + 5 => 'AbDendriel', + 6 => 'Carlin', + 8 => 'Thais', + 9 => 'Venore', + 10 => 'Ankrahmun', + 11 => 'Edron', + 12 => 'Farmine', + 13 => 'Darashia', + 14 => 'Liberty Bay', + 15 => 'Port Hope', + 16 => 'Svargrond', + 17 => 'Yalahar', + 20 => 'Rathleton' + ), + + // guilds + 'guild_management' => true, // enable guild management system on the site? + 'guild_need_level' => 100, // min. level to form a guild + 'guild_need_premium' => true, // require premium account to form a guild? + 'guild_image_size_kb' => 80, // maximum size of the guild logo image in KB (kilobytes) + 'guild_description_chars_limit' => 1000, // limit of guild description + 'guild_description_lines_limit' => 6, // limit of lines, if description has more lines it will be showed as long text, without 'enters' + 'guild_motd_chars_limit' => 150, // limit of MOTD (message of the day) that is shown later in the game on the guild channel + + // online page + 'online_record' => true, // display players record? + 'online_vocations' => false, // display vocation statistics? + 'online_vocations_images' => false, // display vocation images? + 'online_skulls' => true, // display skull images + 'online_outfit' => true, + 'online_afk' => false, + + // support list page + 'team_style' => 2, // 1/2 (1 - normal table, 2 - in boxes, grouped by group id) + 'team_display_status' => true, + 'team_display_lastlogin' => true, + 'team_display_world' => false, + 'team_display_outfit' => true, + + // bans page + 'bans_limit' => 50, + 'bans_display_all' => true, // should all bans be displayed? (sorted page by page) + + // highscores page + 'highscores_vocation_box' => true, // show 'Choose a vocation' box on the highscores (allowing peoples to sort highscores by vocation)? + 'highscores_vocation' => true, // show player vocation under his nickname? + 'highscores_frags' => false, // show 'Frags' tab (best fraggers on the server)? Only 0.3 + 'highscores_balance' => false, // show 'Balance' tab (richest players on the server) + 'highscores_outfit' => true, // show player outfit? + 'highscores_country_box' => false, // doesnt work yet! (not implemented) + 'highscores_groups_hidden' => 3, // this group id and higher won't be shown on the highscores + 'highscores_ids_hidden' => array(0), // this ids of players will be hidden on the highscores (should be ids of samples) + 'highscores_length' => 100, // how many records per page on highscores + + // characters page + 'characters' => array( // what things to display on character view page (true/false in each option) + 'level' => true, + 'experience' => true, + 'magic_level' => true, + 'balance' => true, + 'marriage_info' => true, // only 0.3 + 'outfit' => true, + 'creation_date' => true, + 'quests' => true, + 'skills' => true, + 'equipment' => true, + 'frags' => true, + 'deleted' => false, // should deleted characters from same account be still listed on the list of characters? When enabled it will show that character is "[DELETED]" + ), + 'quests' => array( // Canary Storages + 'Demon Helmet' => 40077, // Storage.Quest.U6_4.DemonHelmet.Rewards.DemonHelmet + 'Annihilator' => 10102, + 'Pits Of Inferno' => 52003, // Storage.PitsOfInferno.WeaponReward + 'Inquisition' => 51127, // Storage.TheInquisition.Reward + 'Demon Oak' => 51700,// Maybe 51700 + 'SoulWar Quest' => 47223, // Storage.Quest.U12_40.SoulWar.QuestReward + 'Yalahar Quest' => 51249, // Storage.InServiceofYalahar.DoorToReward + //'Some Quest' => 123, + //'Some Quest Two' => 456, + ), // quests list (displayed in character view), name => storage + + 'achievements_base' => 300000, + + 'signature_enabled' => false, + 'signature_type' => 'tibian', // signature engine to use: tibian, mango, gesior + 'signature_cache_time' => 5, // how long to store cached file (in minutes), default 5 minutes + 'signature_browser_cache' => 60, // how long to cache by browser (in minutes), default 1 hour + + 'allow_menu_animated' => true, // allow menu with animated gifs + + // news page + 'news_limit' => 5, // limit of news on the latest news page + 'news_ticker_limit' => 5, // limit of news in tickers (mini news) (0 to disable) + 'news_date_format' => 'j.n.Y', // check php manual date() function for more info about this + 'news_author' => false, // show author of the news + + // banner home + 'banner_status' => false, + 'banner_image' => '500x660.png', // templates->tibiacom->images->carousel + 'banner_link' => 'www.instagram.com', + + // status bar + 'status_bar' => true, + 'client_link' => 'https://github.com/dudantas/tibia-client/releases/tag/13.21.13839', // link to download tibia client + 'discord_link' => 'https://discord.com/invite/gvTj5sh9Mp', // link to join discord channel + 'whatsapp_link' => '5511912345678', // wa.me/5511912345678 + 'instagram_link' => 'profile', // www.instagram.com/profile + 'facebook_link' => 'page', // www.facebook.com/page + 'collapse_status' => true, + + // events + 'events_xml' => 'data/xml/events.xml', + + // slide + 'carousel_status' => true, + 'carousel' => array( + 'carousel_1' => 'runemaster_small.jpg', + 'carousel_2' => 'merrygarb_small.jpg', + 'carousel_3' => 'mothcape_small.jpg', + ), + + // load page + 'pace_load' => true, // load page top bar + 'pace_theme' => 'flat-top', // big-counter, bounce, center-atom, center-circle, center-radar, center-simple, corner-indicator, fill-left, flash, flat-top, loading-bar, max-osx, material, minimal + 'pace_color' => 'white', // black, blue, green, orange, pink, purple, red, silver, white, yellow + + // char bazaar + 'bazaar_create' => 50, // price to create auction + 'bazaar_tax' => 12, // tax to bid + 'bazaar_bid' => 50, // price to bid + 'bazaar_accountid' => 1, // account id to move auction character + + // gifts/shop system + 'gifts_system' => true, + + // support/system + 'bug_report' => true, // this configurable has no effect, its always enabled + + // forum + 'forum' => 'site', // link to the server forum, set to "site" if you want to use build in forum system, otherwise leave empty if you aren't going to use any forum + 'forum_level_required' => 0, // level required to post, 0 to disable + 'forum_post_interval' => 30, // in seconds + 'forum_posts_per_page' => 20, + 'forum_threads_per_page' => 20, + // uncomment to force use table for forum + //'forum_table_prefix' => 'z_', // what forum mysql table to use, z_ (for gesior old forum) or myaac_ (for myaac) + + // last kills + 'last_kills_limit' => 50, // max. number of deaths shown on the last kills page + + // status, took automatically from config file if empty + 'status_enabled' => true, // you can disable status checking by settings this to "false" + 'status_ip' => '', + 'status_port' => '', + 'status_timeout' => 2, // how long to wait for the initial response from the server (default: 2 seconds) + + // how often to connect to server and update status (default: every minute) + // if your status timeout in config.lua is bigger, that it will be used instead + // when server is offline, it will be checked every time web refreshes, ignoring this variable + 'status_interval' => 60, + + // admin panel + 'admin_panel_modules' => 'lastlogin,coinstransferable,coins,donates', + + // other + 'email_lai_sec_interval' => 60, // time in seconds between e-mails to one account from lost account interface, block spam + 'google_analytics_id' => '', // e.g.: UA-XXXXXXX-X + 'experiencetable_columns' => 4, // how many columns to display in experience table page. * experiencetable_rows, 5 = 500 (will show up to 500 level) + 'experiencetable_rows' => 500, // till how many levels in one column + 'date_timezone' => 'America/Sao_Paulo', // more info at http://php.net/manual/en/timezones.php + 'footer_show_load_time' => true, // display load time of the page in the footer + + 'npc' => [], + + // character name blocked + 'character_name_blocked' => [ + 'prefix' => [], + 'names' => [], + 'words' => [], + ], + + 'enablePagseguroLocal' => false, // set true to enable donate and boxes page on localhost. +); diff --git a/test/login.php b/test/login.php new file mode 100644 index 000000000..04b108f89 --- /dev/null +++ b/test/login.php @@ -0,0 +1,222 @@ +getAttribute('startdate') : $table1->getAttribute('enddate'); + return date_create("{$date}")->format('U'); + } else { + foreach ($table1 as $attr) { + if ($attr) { + return $attr->getAttribute($table2); + } + } + } + } +} + +$request = file_get_contents('php://input'); +$result = json_decode($request); +$action = $result->type ?? ''; + +switch ($action) { + case 'cacheinfo': + $playersonline = $db->query("SELECT count(*) FROM `players_online`")->fetchAll(); + die(json_encode([ + 'playersonline' => (intval($playersonline[0][0])), + 'twitchstreams' => 0, + 'twitchviewer' => 0, + 'gamingyoutubestreams' => 0, + 'gamingyoutubeviewer' => 0 + ])); + + case 'eventschedule': + $eventlist = []; + $file_path = config('server_path') . 'data/XML/events.xml'; + if (!file_exists($file_path)) { + die(json_encode([])); + } + $xml = new DOMDocument; + $xml->load($file_path); + $tableevent = $xml->getElementsByTagName('event'); + + foreach ($tableevent as $event) { + if ($event) { + $eventlist[] = [ + 'colorlight' => parseEvent($event->getElementsByTagName('colors'), false, 'colorlight'), + 'colordark' => parseEvent($event->getElementsByTagName('colors'), false, 'colordark'), + 'description' => parseEvent($event->getElementsByTagName('description'), false, 'description'), + 'displaypriority' => intval(parseEvent($event->getElementsByTagName('details'), false, 'displaypriority')), + 'enddate' => intval(parseEvent($event, true, false)), + 'isseasonal' => getBoolean(intval(parseEvent($event->getElementsByTagName('details'), false, 'isseasonal'))), + 'name' => $event->getAttribute('name'), + 'startdate' => intval(parseEvent($event, true, true)), + 'specialevent' => intval(parseEvent($event->getElementsByTagName('details'), false, 'specialevent')) + ]; + } + } + die(json_encode(['eventlist' => $eventlist, 'lastupdatetimestamp' => time()])); + + case 'boostedcreature': + $creatureBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_creature'))->fetchAll(); + $bossBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_boss'))->fetchAll(); + die(json_encode([ + 'boostedcreature' => true, + 'creatureraceid' => intval($creatureBoost[0]['raceid']), + 'bossraceid' => intval($bossBoost[0]['raceid']) + ])); + + case 'login': + $ip = configLua('ip'); + $port = configLua('gameProtocolPort'); + + // default world info + $world = [ + 'id' => 0, + 'name' => configLua('serverName'), + 'externaladdress' => $ip, + 'externaladdressprotected' => $ip, + 'externaladdressunprotected' => $ip, + 'externalport' => $port, + 'externalportprotected' => $port, + 'externalportunprotected' => $port, + 'previewstate' => 0, + 'location' => 'BRA', // BRA, EUR, USA + 'anticheatprotection' => false, + 'pvptype' => array_search(configLua('worldType'), ['pvp', 'no-pvp', 'pvp-enforced']), + 'istournamentworld' => false, + 'restrictedstore' => false, + 'currenttournamentphase' => 2 + ]; + + $account = new OTS_Account(); + $account->findByEmail($result->email); + $config_salt_enabled = fieldExist('salt', 'accounts'); + $current_password = encrypt(($config_salt_enabled ? $account->getCustomField('salt') : '') . $result->password); + + if (!$account->isLoaded() || $account->getPassword() != $current_password) { + sendError('Email or password is not correct.'); + } + + // common columns + $columns = 'name, level, sex, vocation, looktype, lookhead, lookbody, looklegs, lookfeet, lookaddons, lastlogin, isreward, istutorial, ismain, hidden'; + $players = $db->query("SELECT {$columns} FROM players WHERE account_id = {$account->getId()} AND deletion = 0"); + $characters = []; + if ($players && $players->rowCount() > 0) { + $players = $players->fetchAll(); + foreach ($players as $player) { + $characters[] = createChar($config, $player); + } + } + + $query = $db->query("SELECT `premdays`, `lastday` FROM `accounts` WHERE `id` = {$account->getId()}"); + $premU = 0; + if ($query->rowCount() > 0) { + $premU = checkPremium($db, $query->fetch(), $account); + } else { + sendError("Error while fetching your account data. Please contact admin."); + } + + $worlds = [$world]; + $playdata = compact('worlds', 'characters'); + $session = [ + 'sessionkey' => "$result->email\n$result->password", + 'lastlogintime' => $account ? $account->getLastLogin() : 0, + 'ispremium' => $account->isPremium(), + 'premiumuntil' => $premU, + 'status' => 'active', // active, frozen or suspended + 'returnernotification' => false, + 'showrewardnews' => true, + 'isreturner' => true, + 'fpstracking' => false, + 'optiontracking' => false, + 'tournamentticketpurchasestate' => 0, + 'emailcoderequest' => false + ]; + die(json_encode(compact('session', 'playdata'))); + + default: + sendError("Unrecognized event {$action}."); + break; +} + +/** + * Function to create char in list + * @param $config + * @param $player + * @return array + */ +function createChar($config, $player) +{ + return [ + 'worldid' => 0, + 'name' => $player['name'], + 'ismale' => intval($player['sex']) === 1, + 'tutorial' => (bool)$player['istutorial'], + 'level' => intval($player['level']), + 'vocation' => $config['vocations'][$player['vocation']], + 'outfitid' => intval($player['looktype']), + 'headcolor' => intval($player['lookhead']), + 'torsocolor' => intval($player['lookbody']), + 'legscolor' => intval($player['looklegs']), + 'detailcolor' => intval($player['lookfeet']), + 'addonsflags' => intval($player['lookaddons']), + 'ishidden' => (bool)$player['hidden'], + 'istournamentparticipant' => false, + 'ismaincharacter' => (bool)($player['ismain']), + 'dailyrewardstate' => intval($player['isreward']), + 'remainingdailytournamentplaytime' => 0 + ]; +} + +/** + * Function to check account has premium time and update days + * @param $db + * @param $query + * @param $account + * @return float|int + */ +function checkPremium($db, $query, $account) +{ + $lastDay = (int)$query['lastday']; + $timeNow = time(); + + if ($lastDay < $timeNow) { + $premDays = 0; + $lastDay = 0; + } else if ($lastDay == 0) { + $premDays = 0; + } else { + $daysLeft = (int)(($lastDay - $timeNow) / 86400); + $timeLeft = (int)(($lastDay - $timeNow) % 86400); + if ($daysLeft > 0) { + $premDays = $daysLeft; + } else if ($timeLeft > 0) { + $premDays = 1; + } else { + $premDays = 0; + $lastDay = 0; + } + } + + $db->query("UPDATE `accounts` SET `premdays` = {$premDays}, `lastday` = {$lastDay} WHERE `id` = {$account->getId()}"); + return $lastDay; +} diff --git a/test/style.css b/test/style.css new file mode 100644 index 000000000..4565c1285 --- /dev/null +++ b/test/style.css @@ -0,0 +1,68 @@ +.slidecontainer { + width: 100%; +} + +.slider { + -webkit-appearance: none; + width: 100%; + + outline: none; + opacity: 0.7; + -webkit-transition: 0.2s; + transition: opacity 0.2s; +} + +.slider:hover { + opacity: 1; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 15px; + height: 25px; + background: #3c8dbc; + cursor: pointer; +} + +.slider::-moz-range-thumb { + width: 25px; + height: 25px; + background: #3c8dbc; + cursor: pointer; +} + +td.details-control { + text-align: center; + color: forestgreen; + cursor: pointer; +} + +tr.shown td.details-control { + text-align: center; + color: red; +} + +.table { + --bs-table-striped-color: #b8c7d0; +} +a { + text-decoration: none; +} +.skin-blue .main-header .navbar { + padding-bottom: 0px; + padding-top: 0px; +} +tr { + color: #b8c7d0; +} +.nav-tabs .nav-link { + color: #fff; + border-bottom: 1px solid #fff; + background: #202634; + border-radius: 5px 5px 0px 0px; +} +ul.nav.nav-tabs.nav-justified.control-sidebar-tabs { + background: #202634; + padding: 20px; +}