Вход Регистрация
Файл: upload/core/vendor/longman/telegram-bot/src/Commands/AdminCommands/CleanupCommand.php
Строк: 306
<?php

/**
 * This file is part of the TelegramBot package.
 *
 * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace LongmanTelegramBotCommandsAdminCommands;

use 
LongmanTelegramBotCommandsAdminCommand;
use 
LongmanTelegramBotDB;
use 
LongmanTelegramBotEntitiesServerResponse;
use 
LongmanTelegramBotExceptionTelegramException;
use 
LongmanTelegramBotRequest;
use 
LongmanTelegramBotTelegramLog;
use 
PDOException;

/**
 * User "/cleanup" command
 *
 * Configuration options:
 *
 * $telegram->setCommandConfig('cleanup', [
 *     // Define which tables should be cleaned.
 *     'tables_to_clean' => [
 *         'message',
 *         'edited_message',
 *     ],
 *     // Define how old cleaned entries should be.
 *     'clean_older_than' => [
 *         'message'        => '7 days',
 *         'edited_message' => '30 days',
 *     ]
 * );
 */
class CleanupCommand extends AdminCommand
{
    
/**
     * @var string
     */
    
protected $name 'cleanup';

    
/**
     * @var string
     */
    
protected $description 'Clean up the database from old records';

    
/**
     * @var string
     */
    
protected $usage '/cleanup [dry] <days> or /cleanup [dry] <count> <unit> (e.g. 3 weeks)';

    
/**
     * @var string
     */
    
protected $version '1.1.0';

    
/**
     * @var bool
     */
    
protected $need_mysql true;

    
/**
     * Default tables to clean, cleaning 'chat', 'user' and 'user_chat' by default is bad practice!
     *
     * @var array
     */
    
protected static $default_tables_to_clean = [
        
'callback_query',
        
'chosen_inline_result',
        
'conversation',
        
'edited_message',
        
'inline_query',
        
'message',
        
'request_limiter',
        
'telegram_update',
    ];

    
/**
     * By default, remove records older than X days/hours/anything from these tables.
     *
     * @var array
     */
    
protected static $default_clean_older_than = [
        
'callback_query'       => '30 days',
        
'chat'                 => '365 days',
        
'chosen_inline_result' => '30 days',
        
'conversation'         => '90 days',
        
'edited_message'       => '30 days',
        
'inline_query'         => '30 days',
        
'message'              => '30 days',
        
'poll'                 => '90 days',
        
'request_limiter'      => '1 minute',
        
'shipping_query'       => '90 days',
        
'telegram_update'      => '30 days',
        
'user'                 => '365 days',
        
'user_chat'            => '365 days',
    ];

    
/**
     * Set command config
     *
     * @param string $custom_time
     *
     * @return array
     */
    
private function getSettings($custom_time ''): array
    {
        
$tables_to_clean      self::$default_tables_to_clean;
        
$user_tables_to_clean $this->getConfig('tables_to_clean');
        if (
is_array($user_tables_to_clean)) {
            
$tables_to_clean $user_tables_to_clean;
        }

        
$clean_older_than      self::$default_clean_older_than;
        
$user_clean_older_than $this->getConfig('clean_older_than');
        if (
is_array($user_clean_older_than)) {
            
$clean_older_than array_merge($clean_older_than$user_clean_older_than);
        }

        
// Convert numeric-only values to days.
        
array_walk($clean_older_than, function (&$time) use ($custom_time) {
            if (!empty(
$custom_time)) {
                
$time $custom_time;
            }
            if (
is_numeric($time)) {
                
$time .= ' days';
            }
        });

        return 
compact('tables_to_clean''clean_older_than');
    }

    
/**
     * Get SQL queries array based on settings provided
     *
     * @param $settings
     *
     * @return array
     * @throws TelegramException
     */
    
private function getQueries($settings): array
    {
        if (empty(
$settings) || !is_array($settings)) {
            throw new 
TelegramException('Settings variable is not an array or is empty!');
        }

        
// Convert all clean_older_than times to correct format.
        
$clean_older_than $settings['clean_older_than'];
        foreach (
$clean_older_than as $table => $time) {
            
$clean_older_than[$table] = date('Y-m-d H:i:s'strtotime('-' $time));
        }
        
$tables_to_clean $settings['tables_to_clean'];

        
$queries = [];

        if (
in_array('telegram_update'$tables_to_cleantrue)) {
            
$queries[] = sprintf(
                
'DELETE FROM `%3$s`
                WHERE id IN (
                    SELECT id FROM (
                        SELECT id FROM `%3$s`
                        WHERE `id` != '
%1$s'
                        AND `chat_id` NOT IN (
                          SELECT `id`
                          FROM `%4$s`
                          WHERE `%3$s`.`chat_id` = `id`
                          AND `updated_at` < '
%2$s'
                        )
                        AND (
                          `message_id` IS NOT NULL
                          AND `message_id` IN (
                            SELECT `id`
                            FROM `%5$s`
                            WHERE `date` < '
%2$s'
                          )
                        )
                        OR (
                          `edited_message_id` IS NOT NULL
                          AND `edited_message_id` IN (
                            SELECT `id`
                            FROM `%6$s`
                            WHERE `edit_date` < '
%2$s'
                          )
                        )
                        OR (
                          `inline_query_id` IS NOT NULL
                          AND `inline_query_id` IN (
                            SELECT `id`
                            FROM `%7$s`
                            WHERE `created_at` < '
%2$s'
                          )
                        )
                        OR (
                          `chosen_inline_result_id` IS NOT NULL
                          AND `chosen_inline_result_id` IN (
                            SELECT `id`
                            FROM `%8$s`
                            WHERE `created_at` < '
%2$s'
                          )
                        )
                        OR (
                          `callback_query_id` IS NOT NULL
                          AND `callback_query_id` IN (
                            SELECT `id`
                            FROM `%9$s`
                            WHERE `created_at` < '
%2$s'
                          )
                        )
                    ) a
                )
            '
,
                
$this->getUpdate()->getUpdateId(),
                
$clean_older_than['telegram_update'],
                
TB_TELEGRAM_UPDATE,
                
TB_CHAT,
                
TB_MESSAGE,
                
TB_EDITED_MESSAGE,
                
TB_INLINE_QUERY,
                
TB_CHOSEN_INLINE_RESULT,
                
TB_CALLBACK_QUERY
            
);
        }

        if (
in_array('user_chat'$tables_to_cleantrue)) {
            
$queries[] = sprintf(
                
'DELETE FROM `%1$s`
                WHERE `user_id` IN (
                  SELECT `id`
                  FROM `%2$s`
                  WHERE `updated_at` < '
%3$s'
                )
            '
,
                
TB_USER_CHAT,
                
TB_USER,
                
$clean_older_than['chat']
            );
        }

        
// Simple.
        
$simple_tables = [
            
'user'            => ['table' => TB_USER'field' => 'updated_at'],
            
'chat'            => ['table' => TB_CHAT'field' => 'updated_at'],
            
'conversation'    => ['table' => TB_CONVERSATION'field' => 'updated_at'],
            
'poll'            => ['table' => TB_POLL'field' => 'created_at'],
            
'request_limiter' => ['table' => TB_REQUEST_LIMITER'field' => 'created_at'],
            
'shipping_query'  => ['table' => TB_SHIPPING_QUERY'field' => 'created_at'],
        ];

        foreach (
array_intersect(array_keys($simple_tables), $tables_to_clean) as $table_to_clean) {
            
$queries[] = sprintf(
                
'DELETE FROM `%1$s`
                WHERE `%2$s` < '
%3$s'
            '
,
                
$simple_tables[$table_to_clean]['table'],
                
$simple_tables[$table_to_clean]['field'],
                
$clean_older_than[$table_to_clean]
            );
        }

        
// Queries.
        
$query_tables = [
            
'inline_query'         => ['table' => TB_INLINE_QUERY'field' => 'created_at'],
            
'chosen_inline_result' => ['table' => TB_CHOSEN_INLINE_RESULT'field' => 'created_at'],
            
'callback_query'       => ['table' => TB_CALLBACK_QUERY'field' => 'created_at'],
        ];
        foreach (
array_intersect(array_keys($query_tables), $tables_to_clean) as $table_to_clean) {
            
$queries[] = sprintf(
                
'DELETE FROM `%1$s`
                WHERE `%2$s` < '
%3$s'
                  AND `id` NOT IN (
                    SELECT `%4$s`
                    FROM `%5$s`
                    WHERE `%4$s` = `%1$s`.`id`
                  )
            '
,
                
$query_tables[$table_to_clean]['table'],
                
$query_tables[$table_to_clean]['field'],
                
$clean_older_than[$table_to_clean],
                
$table_to_clean '_id',
                
TB_TELEGRAM_UPDATE
            
);
        }

        
// Messages
        
if (in_array('edited_message'$tables_to_cleantrue)) {
            
$queries[] = sprintf(
                
'DELETE FROM `%1$s`
                WHERE `edit_date` < '
%2$s'
                  AND `id` NOT IN (
                    SELECT `message_id`
                    FROM `%3$s`
                    WHERE `edited_message_id` = `%1$s`.`id`
                  )
            '
,
                
TB_EDITED_MESSAGE,
                
$clean_older_than['edited_message'],
                
TB_TELEGRAM_UPDATE
            
);
        }

        if (
in_array('message'$tables_to_cleantrue)) {
            
$queries[] = sprintf(
                
'DELETE FROM `%1$s`
                WHERE id IN (
                    SELECT id
                    FROM (
                        SELECT id
                        FROM  `%1$s`
                        WHERE `date` < '
%2$s'
                          AND `id` NOT IN (
                            SELECT `message_id`
                            FROM `%3$s`
                            WHERE `message_id` = `%1$s`.`id`
                          )
                          AND `id` NOT IN (
                            SELECT `message_id`
                            FROM `%4$s`
                            WHERE `message_id` = `%1$s`.`id`
                          )
                          AND `id` NOT IN (
                            SELECT `message_id`
                            FROM `%5$s`
                            WHERE `message_id` = `%1$s`.`id`
                          )
                          AND `id` NOT IN (
                            SELECT a.`reply_to_message` FROM `%1$s` a
                            INNER JOIN `%1$s` b ON b.`id` = a.`reply_to_message` AND b.`chat_id` = a.`reply_to_chat`
                          )
                        ORDER BY `id` DESC
                     ) a
                 )
            '
,
                
TB_MESSAGE,
                
$clean_older_than['message'],
                
TB_EDITED_MESSAGE,
                
TB_TELEGRAM_UPDATE,
                
TB_CALLBACK_QUERY
            
);
        }

        return 
$queries;
    }

    
/**
     * Execution if MySQL is required but not available
     *
     * @return ServerResponse
     * @throws TelegramException
     */
    
public function executeNoDb(): ServerResponse
    
{
        return 
$this->replyToChat('*No database connection!*', ['parse_mode' => 'Markdown']);
    }

    
/**
     * Command execute method
     *
     * @return ServerResponse
     * @throws TelegramException
     */
    
public function execute(): ServerResponse
    
{
        
$message $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
        
$text    $message->getText(true);

        
// Dry run?
        
$dry_run strpos($text'dry') !== false;
        
$text    trim(str_replace('dry'''$text));

        
$settings $this->getSettings($text);
        
$queries  $this->getQueries($settings);

        if (
$dry_run) {
            return 
$this->replyToUser('Queries:' PHP_EOL implode(PHP_EOL$queries));
        }

        
$infos = [];
        foreach (
$settings['tables_to_clean'] as $table) {
            
$info "*{$table}*";

            if (isset(
$settings['clean_older_than'][$table])) {
                
$info .= " ({$settings['clean_older_than'][$table]})";
            }

            
$infos[] = $info;
        }

        
$data = [
            
'chat_id'    => $message->getFrom()->getId(),
            
'parse_mode' => 'Markdown',
        ];

        
$data['text'] = 'Cleaning up tables:' PHP_EOL implode(PHP_EOL$infos);
        
Request::sendMessage($data);

        
$rows 0;
        
$pdo  DB::getPdo();
        try {
            
$pdo->beginTransaction();

            foreach (
$queries as $query) {
                
// Delete in chunks to not block / improve speed on big tables.
                
$query .= ' LIMIT 10000';
                while (
$dbq $pdo->query($query)) {
                    if (
$dbq->rowCount() === 0) {
                        continue 
2;
                    }
                    
$rows += $dbq->rowCount();
                }

                
TelegramLog::error('Error while executing query: ' $query);
            }

            
// commit changes to the database and end transaction
            
$pdo->commit();

            
$data['text'] = "*Database cleanup done!* _(removed {$rows} rows)_";
        } catch (
PDOException $e) {
            
$data['text'] = '*Database cleanup failed!* _(check your error logs)_';

            
// rollback changes on exception
            // useful if you want to track down error you can't replicate it when some of the data is already deleted
            
$pdo->rollBack();

            
TelegramLog::error($e->getMessage());
        }

        return 
Request::sendMessage($data);
    }
}
Онлайн: 0
Реклама