/**
 *	получение данных по модераторам и запись их в memfs и memcache
 *	moderatorsDetails[ uid ] = array( name => '', bansCount => '' )
 *	содержит uid, имя модератора и, возможно, кол-во банов bansCount,
 *	которое устанавливается в chat.php при бане
 */
function DumpModeratorsDetails()
{
    global $memcache;
    //$memcache->Delete( MODERATORS_DETAILS_MEMCACHE_KEY );
    $moderatorsDetails = $memcache->Get(MODERATORS_DETAILS_MEMCACHE_KEY);
    //SaveForDebug( 'cron dump moder details debug, get from memcache: ' . var_export( $moderatorsDetails, true ) );
    // попытка считать статистику из файла, если ее нет в memcache
    if ($moderatorsDetails === FALSE) {
        $moderatorsDetails = GetModeratorDetailsFromFile();
    }
    if ($moderatorsDetails === FALSE) {
        //получаем из базы
        $queryString = '
			SELECT users.uid, name
			FROM users
			INNER JOIN users_roles using(uid)
			WHERE rid IN (3,4,5)
			AND status = 1
			GROUP BY users.uid';
        $db = GetDb();
        $queryResult = $db->Query($queryString);
        if ($queryResult === FALSE) {
            SaveForDebug(CHAT_RUNTIME_ERROR . ' cron dump moderrators details 1');
        }
        while ($moderatorDetail = $queryResult->fetch_assoc()) {
            $moderatorsDetails[$moderatorDetail['uid']]['name'] = $moderatorDetail['name'];
        }
        //SaveForDebug( 'moderatorsDetails dump from bd' );
    }
    $memcache->Set(MODERATORS_DETAILS_MEMCACHE_KEY, $moderatorsDetails, CHAT_MODERATORS_DETAILS_TTL);
    //SaveForDebug( 'send to memcache: ' . var_export( $moderatorsDetails, true ) );
    $dataJS = 'var moderatorsDetails = ' . json_encode($moderatorsDetails);
    file_put_contents(CHAT_MODERATORS_DETAILS, $dataJS);
}
Пример #2
0
function SaveNextNum($lastGeneratedSigId, $lastSigId = 0, $debugOutput)
{
    global $sigUpdatedCount, $db;
    if ($lastSigId == 0) {
        $lastSigId = $lastGeneratedSigId + $sigUpdatedCount;
    }
    $queryString = "UPDATE " . DB_TABLE_PREFIX . "options SET value='{$lastSigId}'\r\n\t\tWHERE name='lastSigId'";
    $result = $db->Query($queryString);
    $debugOutput .= $sigUpdatedCount . ' ' . date('H:i', time()) . ' lastSigId = ' . $lastSigId;
    echo $debugOutput;
    SaveForDebug($debugOutput);
}
Пример #3
0
<?php

require_once 'config.php';
require_once 'utils.php';
require_once 'db.php';
$db = new MySqlDb(DB_HOST, DB_NAME, DB_USER, DB_PASSWORD);
$queryString = 'DELETE FROM ' . DB_TABLE_PREFIX . 'data WHERE ';
$debugOutput = '';
$doesOldSigExist = false;
foreach (glob(SIG_BASEDIR . '*.png') as $filename) {
    if (time() - filemtime($filename) > SIG_TTL) {
        $doesOldSigExist = true;
        // echo "$filename - ".date( 'd M H:i:s', filemtime( $filename ) ). "\n";
        preg_match("#/([\\d]+).png#", $filename, $match);
        if ($match[1] !== '') {
            $queryString .= 'sigId="' . $match[1] . '" OR ';
            $debugOutput .= $match[1] . ', ';
        }
        @unlink($filename);
    }
}
if ($doesOldSigExist) {
    $queryString = substr($queryString, 0, strlen($queryString) - 4);
    $queryResult = $db->Query($queryString);
    if ($queryResult) {
        $debugOutput = 'Cleaner delete sigs:' . $debugOutput;
        SaveForDebug($debugOutput);
    }
} else {
    SaveForDebug('Cleaner doesn\'t found old sigs.');
}
Пример #4
0
require_once 'core.php';
header('Content-Type: application/javascript');
$db = GetDb();
$smilesResult = $db->Query('SELECT * FROM chat_smile ORDER BY `page` ASC, `sid` ASC');
if ($smilesResult === FALSE) {
    SaveForDebug('Fetching smiles failed');
    return;
}
$smiles = array();
while ($smile = $smilesResult->fetch_assoc()) {
    $smile['roles'] = array();
    $smiles[$smile['code']] = $smile;
}
$rolesResult = $db->Query('SELECT * FROM role_smiles');
if ($rolesResult === FALSE) {
    SaveForDebug('Fetching role smiles failed');
    return;
}
$smile_roles = array();
while ($role = $rolesResult->fetch_assoc()) {
    $rid = intval($role['rid']);
    foreach (explode(',', $role['smiles']) as $smile) {
        if (array_key_exists($smile, $smiles)) {
            $smiles[$smile]['roles'][] = $rid;
        }
    }
}
$public = array();
$private = array();
foreach ($smiles as $smile) {
    /*if ( count( array_diff( $smile['roles'], array( 2 ) ) ) > 0 ) {
<?php

require_once 'core.php';
require_once 'utils.php';
global $memcache;
$memcache->Delete(MODERATORS_DETAILS_MEMCACHE_KEY);
unlink(CHAT_MODERATORS_DETAILS);
SaveForDebug('cron clear moderators details');
Пример #6
0
    /**
    *  бан пользователя
    *  @param int banUid id пользователя, которого баним
    *  @param string banUserName его имя
    *  @param int banDurationInMin длительность бана в минутах
    *  @param int banMessageId id сообщения, за которое выдается бан
    *	@param int channelId id канала юзера
    *  @param int banReasonId id причины бана, для модеров 0
    *  @param bool isAutoBan если автобан, TRUE, иначе FALSE
    *  @return array возвращает массив вида
    *  array(
    			'code' => 0,// код результата: 0 для ошибки | 1 для успеха
    			'error' => 'hack';// текст ошибки
    		)
    */
    public function BanUser($banUid, $banUserName, $banDurationInMin, $banMessageId, $channelId, $banReasonId = 0, $isAutoBan = FALSE)
    {
        $banUid = (int) $banUid;
        $banMessageId = (int) $banMessageId;
        $banReasonId = (int) $banReasonId;
        $banDurationInMin = (int) $banDurationInMin;
        $channelId = (int) $channelId;
        // выдаем ошибку, если есть права, но неправильный id сообщения
        if ($this->user['rights'] === 1 && $banMessageId < 0 || $this->user['rights'] != 1 && $isAutoBan === FALSE || $banUid === 0 || $banUserName === '' || $banDurationInMin === 0 || $banReasonId < 0) {
            SaveForDebug(var_export($_REQUEST, TRUE));
            $result = array('code' => 0, 'error' => CHAT_RUNTIME_ERROR . '2');
            return $result;
        }
        $banDuration = $banDurationInMin * 60;
        $banExpirationTime = CURRENT_TIME + $banDuration;
        // проверяем флаг в memcache на случай бана от модератора или граждан
        $banInfoMemcacheKey = 'Chat_uid_' . $banUid . '_BanInfo';
        // делаем через Add, чтобы одновременно проверить отсутствие флага
        // и установить его
        $isUserBanned = $this->memcache->Add($banInfoMemcacheKey, array('banTime' => CURRENT_TIME, 'banExpirationTime' => $banExpirationTime), $banDuration);
        if ($isUserBanned === FALSE) {
            $result = array('code' => 0, 'error' => 'Уже забанен');
            return $result;
        }
        list($banUserName) = $this->db->PrepareParams($banUserName);
        $moderatorName = $this->user['name'];
        $moderatorId = $this->user['uid'];
        $queryString = '
			INSERT INTO chat_ban
			( uid, moderatorId, banExpirationTime, banMessageId, banReasonId, banTime,
			banDuration )
			VALUES( "' . $banUid . '", "' . $moderatorId . '", "' . $banExpirationTime . '", "' . $banMessageId . '", "' . $banReasonId . '", "' . CURRENT_TIME . '", "' . $banDuration . '")';
        $queryResult = $this->db->Query($queryString);
        if ($queryResult === FALSE) {
            // в случае ошибки с запросом удаляем флаг в мемкеше,
            // чтобы юзера можно было забанить в следующий раз
            $this->memcache->Delete($banInfoMemcacheKey);
            $result = array('code' => 0, 'error' => CHAT_RUNTIME_ERROR . '3');
            return $result;
        }
        if (CHAT_DELETE_BANNED_USERS_MESSAGE && $isAutoBan === FALSE) {
            $queryString = '
				UPDATE chat_message
				SET deletedBy = "' . $moderatorId . '"
				WHERE id = "' . $banMessageId . '"';
            $queryResult = $this->db->Query($queryString);
            if ($queryResult === FALSE) {
                $this->memcache->Delete($banInfoMemcacheKey);
                $result = array('code' => 0, 'error' => CHAT_RUNTIME_ERROR . '4');
                return $result;
            }
        }
        // сохраняем для модераторов кол-во банов
        if ($this->user['type'] === 'chatAdmin') {
            $moderatorsDetails = $this->memcache->Get(MODERATORS_DETAILS_MEMCACHE_KEY);
            // попытка считать статистику из файла, если ее нет в memcache
            if ($moderatorsDetails === FALSE) {
                $moderatorsDetails = GetModeratorDetailsFromFile();
            }
            if ($moderatorsDetails !== FALSE) {
                if (isset($moderatorsDetails[$moderatorId])) {
                    // TODO += ?
                    if (isset($moderatorsDetails[$moderatorId]['bansCount'])) {
                        $moderatorsDetails[$moderatorId]['bansCount'] = $moderatorsDetails[$moderatorId]['bansCount'] + 1;
                    } else {
                        $moderatorsDetails[$moderatorId]['bansCount'] = 1;
                    }
                } else {
                    $moderatorsDetails[$moderatorId] = array('name' => $moderatorName, 'bansCount' => 1);
                }
                $this->memcache->Set(MODERATORS_DETAILS_MEMCACHE_KEY, $moderatorsDetails, CHAT_MODERATORS_DETAILS_TTL);
            }
        }
        if (!$isAutoBan) {
            // кэш текущего канала
            $this->WriteChannelCache($channelId);
            // кэш модераторов
            $this->WriteChannelCache(-1);
        }
        $message = $moderatorName . ' забанил ' . $banUserName . ' на ' . $banDurationInMin . ' минут.';
        $this->WriteSystemMessage($message);
        $result = array('code' => 1, 'error' => '');
        return $result;
    }
Пример #7
0
    /**
     * Определение длительности нового бана с учетом количества предыдущих и
     * причины бана
     * @param int uid - id пользователя, которого банят
     * @param int reasonId - id причины
     * @return array массив с ключами banDuration - длительность нового бана и
     * banSerialNumber - порядковый номер этого бана за просматриваемый период 
     */
    private function GetBanDuration($uid, $reasonId)
    {
        $banSerialNumber = 1;
        $timePenaltyForBan = CURRENT_TIME - CITIZEN_REPEATED_BAN_TTL * 86400;
        $queryString = '
			SELECT uid
			FROM chat_ban
			WHERE uid = "' . $uid . '"
			AND status = 1
			AND banReasonId = "' . $reasonId . '"
			AND banTime > ' . $timePenaltyForBan . '
			GROUP BY banExpirationTime';
        $queryResult = $this->db->Query($queryString);
        if ($queryResult === FALSE) {
            SaveForDebug($queryString);
            exit;
        }
        $banSerialNumber += $queryResult->num_rows;
        // геометрическая прогрессия 2^( n-1 ) * base,
        // где n - порядковый номер бана, base - длительность 1го бана
        // попросту - удвоение
        $banDuration = pow(2, $banSerialNumber - 1) * $this->GetBanDurationByReasonId($reasonId);
        $result = array('banDuration' => $banDuration, 'banSerialNumber' => $banSerialNumber);
        return $result;
    }
Пример #8
0
    /**
     *	получение истории
     *	@param channelId id канала
     *	@param startDate дата начала
     *	@param endDate дата конца
     *	@param userNames имена пользователей, разделенные ;
     *	@param boolean isModeratorRequest флаг true, если запрос от модератора, иначе false
     *	@param historyCache имя файла для сохранения кэша, если не задано, будет
     *	использоваться 
     */
    public function Get($channelId, $startDate, $endDate, $userNames = '', $isModeratorRequest = false, $historyCache = '')
    {
        if ($isModeratorRequest == true) {
            $timeDifference = CHAT_HISTORY_MAX_TIME_DIFFERENCE_MODERATOR;
        } else {
            $timeDifference = CHAT_HISTORY_MAX_TIME_DIFFERENCE;
        }
        $startDate = $this->PrepareDate($startDate);
        $endDate = $this->PrepareDate($endDate);
        $startTime = strtotime($startDate);
        $endTime = strtotime($endDate);
        if ($startTime === FALSE || $endTime === FALSE || $endTime - $startTime > $timeDifference) {
            $result = array('messages' => '', 'error' => CHAT_HISTORY_CHECK_PARAMS);
            return $result;
        }
        $options = '';
        $userNamesCopy = '';
        if ($userNames != '') {
            $userNames = $this->PrepareUserNames($userNames);
            $userNamesCopy = $userNames;
            $userNames = explode(';', $userNames);
            if (count($userNames) > 0) {
                $options = '(';
                $operator = '';
                $this->db = GetDb();
                foreach ($userNames as $userName) {
                    list($userName) = $this->db->PrepareParams($userName);
                    // если длина логина больше максимальной, прислали что-то не то
                    if (mb_strlen($userName) > CHAT_MAX_USERNAME_LENGTH) {
                        SaveForDebug($userNamesCopy . "\n\n" . $userName);
                        $result = array('messages' => '', 'error' => CHAT_USERNAME_TOO_LONG);
                        return $result;
                    }
                    $options .= $operator . 'name="' . $userName . '"';
                    if ($operator === '') {
                        $operator = ' OR ';
                    }
                }
                $options .= ') AND ';
            }
        }
        if (!($channelId == '' || $channelId == 'all')) {
            $channelId = (int) $channelId;
            $options .= 'channelId = "' . $channelId . '" AND ';
        }
        $queryString = '
			SELECT id, chat_message.uid, name, message, date, channelId, (
				SELECT IFNULL((
					SELECT min(rid)
					FROM users_roles
					WHERE users_roles.uid = chat_message.uid), 2
				)
			) as rid
			FROM chat_message
			INNER JOIN users using(uid)
			WHERE ' . $options . '
			date BETWEEN STR_TO_DATE("' . $startDate . '", "%Y-%m-%d %H:%i:%s")
			AND STR_TO_DATE("' . $endDate . '", "%Y-%m-%d %H:%i:%s")
			AND deletedBy is NULL';
        //echo '<!--' . $queryString . '-->';exit;
        $this->db = GetDb();
        $queryResult = $this->db->Query($queryString);
        if ($queryResult === false) {
            SaveForDebug($queryResult);
            $result = array('messages' => '', 'error' => CHAT_RUNTIME_ERROR . ' history 1');
            return $result;
        }
        $messages = array();
        while ($msg = $queryResult->fetch_assoc()) {
            $messages[] = $msg;
        }
        $dataJson = json_encode(array('messages' => $messages));
        if ($historyCache == '') {
            $historyCache = $channelId . '_' . $startDate . '_' . $endDate . '_' . $userNamesCopy . '.json';
            $historyCache = preg_replace('#[\\s]+#uis', '_', $historyCache);
        }
        $historyCache = CHAT_HISTORY_MEMFS_DIR . '/' . $historyCache;
        file_put_contents($historyCache, $dataJson);
        $historyCacheGz = $historyCache . '.gz';
        $historyCacheGzFile = gzopen($historyCacheGz, 'w');
        gzwrite($historyCacheGzFile, $dataJson);
        gzclose($historyCacheGzFile);
        $result = array('messages' => $messages, 'error' => '');
        return $result;
    }
    /**
     *	получение истории автомодерации
     *	@param int channelId id канала
     *	@param string startDate дата начала
     *	@param string endDate дата конца
     *	@param string userNames имена модераторов, разделенные ;
     *	@param string bannedNickNames имена забаненных пользователей, разделенные ;
     *	@param boolean isModeratorRequest флаг true, если запрос от модератора, иначе false
     *	@param string historyCache имя файла для сохранения кэша, если не задано, будет
     *	использоваться last.json
     */
    public function Get($channelId, $startDate, $endDate, $userNames = '', $bannedNickNames = '', $isModeratorRequest = false, $historyCache = '')
    {
        if ($isModeratorRequest == true) {
            $timeDifference = CHAT_HISTORY_MAX_TIME_DIFFERENCE_MODERATOR;
        } else {
            $timeDifference = CHAT_HISTORY_MAX_TIME_DIFFERENCE;
        }
        $startDate = $this->PrepareDate($startDate);
        $endDate = $this->PrepareDate($endDate);
        $startTime = strtotime($startDate);
        $endTime = strtotime($endDate);
        if ($startTime === FALSE || $endTime === FALSE || $endTime - $startTime > $timeDifference) {
            $result = array('messages' => '', 'error' => CHAT_HISTORY_CHECK_PARAMS);
            return $result;
        }
        $options = '';
        $userNamesCopy = '';
        if ($userNames != '') {
            $userNames = $this->PrepareNickNames($userNames);
            $userNamesCopy = $userNames;
            $userNames = explode(';', $userNames);
            if (count($userNames) > 0) {
                $options = '(';
                $operator = '';
                $this->db = GetDb();
                foreach ($userNames as $currentNick) {
                    list($currentNick) = $this->db->PrepareParams($currentNick);
                    // если длина логина больше максимальной, прислали что-то не то
                    if (mb_strlen($currentNick) > CHAT_MAX_USERNAME_LENGTH) {
                        SaveForDebug($userNamesCopy . "\n\n" . $currentNick);
                        $result = array('messages' => '', 'error' => CHAT_USERNAME_TOO_LONG);
                        return $result;
                    }
                    $options .= $operator . ' mu.name = "' . $currentNick . '"';
                    if ($operator === '') {
                        $operator = ' OR ';
                    }
                }
                $options .= ') AND ';
            }
        }
        $bannedNickNamesCopy = '';
        if ($bannedNickNames != '') {
            $bannedNickNames = $this->PrepareNickNames($bannedNickNames);
            $bannedNickNamesCopy = $bannedNickNames;
            $bannedNickNames = explode(';', $bannedNickNames);
            if (count($bannedNickNames) > 0) {
                $options .= '(';
                $operator = '';
                $this->db = GetDb();
                foreach ($bannedNickNames as $currentNick) {
                    list($currentNick) = $this->db->PrepareParams($currentNick);
                    // если длина логина больше максимальной, прислали что-то не то
                    if (mb_strlen($currentNick) > CHAT_MAX_USERNAME_LENGTH) {
                        SaveForDebug($userNamesCopy . "\n\n" . $currentNick);
                        $result = array('messages' => '', 'error' => CHAT_USERNAME_TOO_LONG);
                        return $result;
                    }
                    $options .= $operator . ' ru.name = "' . $currentNick . '"';
                    if ($operator === '') {
                        $operator = ' OR ';
                    }
                }
                $options .= ') AND ';
            }
        }
        $channelOptions = '';
        if (!($channelId == '' || $channelId == 'all')) {
            $channelId = (int) $channelId;
            $options .= 'channelId = "' . $channelId . '" AND ';
        }
        $queryString = '
			SELECT chat_ban.id, ru.uid as bannedUserId, ru.name as userName,
			chat_ban.banExpirationTime, chat_ban.banMessageId, chat_ban.banTime,
			chat_ban.banDuration, chat_ban.banModificationUserId,
			chat_message.message as bannedForMessage, chat_ban.status as chatBanStatus,
			chat_ban.banReasonId, mu.name as moderatorName, banModificationReason
			FROM chat_ban
			INNER JOIN users AS ru ON ru.uid = chat_ban.uid
			INNER JOIN users AS mu ON mu.uid = chat_ban. moderatorId
			LEFT OUTER JOIN chat_message on chat_message.id = banMessageId
			WHERE ' . $options . '
			banTime BETWEEN "' . strtotime($startDate) . '" AND "' . strtotime($endDate) . '"';
        //echo '<!--' . $queryString . '-->';
        $this->db = GetDb();
        $queryResult = $this->db->Query($queryString);
        if ($queryResult === false) {
            SaveForDebug($queryResult);
            $result = array('messages' => '', 'error' => CHAT_RUNTIME_ERROR . ' am history 1');
            return $result;
        }
        $messages = array();
        while ($msg = $queryResult->fetch_assoc()) {
            $messages[] = $msg;
        }
        $dataJson = json_encode(array('messages' => $messages));
        if ($historyCache == '') {
            $historyCache = $channelId . '_' . $startDate . '_' . $endDate . '_' . $userNamesCopy . '_' . $bannedNickNamesCopy . '.json';
            $historyCache = preg_replace('#[\\s]+#uis', '_', $historyCache);
        }
        $historyCache = CHAT_AUTOMODERATION_HISTORY_MEMFS_DIR . '/' . $historyCache;
        file_put_contents($historyCache, $dataJson);
        $historyCacheGz = $historyCache . '.gz';
        $historyCacheGzFile = gzopen($historyCacheGz, 'w');
        gzwrite($historyCacheGzFile, $dataJson);
        gzclose($historyCacheGzFile);
        $result = array('messages' => $messages, 'error' => '');
        return $result;
    }
Пример #10
0
Файл: sig.php Проект: shrz/sig
 /**
  * @param int $sigId
  * @param int $playerId
  * @param int $bnetServerNum
  * @param string $playerAccount
  * @param int $sigBackgroundIndex
  * @param int $playerStatsIndex
  * @param string $lang
  * @param string $region
  * @param int $characterCode
  * @return boolean
  */
 public static function Create($sigId, $playerId, $bnetServerNum, $playerAccount, $sigBackgroundIndex = 1, $playerStatsIndex, $lang, $region, $characterCode)
 {
     // ник
     preg_match('/[^a-z0-9\\-ǂ]*/ui', $playerAccount, $match);
     if ($match[0]) {
         // с русскими символами
         $fontAccount = 'calibri.ttf';
     } else {
         $fontAccount = 'eurostile_ext_med.otf';
     }
     $fontAccount = SIG_BASEDIR . 'php/' . $fontAccount;
     // echo $playerAccount.' '.$fontAccount."\n";
     // языки \ локали
     $locale['ru']['notRanked'] = ' НЕТ РЕЙТИНГА';
     $locale['ru']['place'] = 'МЕСТО';
     $locale['ru']['points'] = 'ОЧКИ';
     $locale['ru']['region'] = 'РЕГИОН';
     $locale['ru']['wins'] = 'ПОБЕД';
     $locale['en']['notRanked'] = ' NOT YET RANKED';
     $locale['en']['place'] = 'PLACE';
     $locale['en']['points'] = 'POINTS';
     $locale['en']['region'] = 'REGION';
     $locale['en']['wins'] = 'WINS';
     if (!($lang == 'ru' || $lang == 'en')) {
         $lang = 'ru';
     }
     $bnetSubDomain = 'eu';
     if ($region === 'US' || $region === 'KR') {
         $bnetSubDomain = strtolower($region);
     } elseif ($region === 0) {
         $region = false;
     }
     $sigPath = SIG_BASEDIR . $sigId . '.png';
     require_once 'network.php';
     $playerAccountUrl = urlencode($playerAccount);
     $relativePlayerAccountUrl = "/sc2/en/profile/{$playerId}/{$bnetServerNum}/{$playerAccountUrl}/";
     $relativePlayerAccountUrlForRegExp = "/sc2/en/profile/{$playerId}/{$bnetServerNum}/{$playerAccount}/";
     $data = Network::GetHTTPData("http://{$bnetSubDomain}.battle.net" . $relativePlayerAccountUrl . 'ladder/', 'id="portrait', 'class="module-right');
     if (!$data) {
         return false;
     }
     switch ($playerStatsIndex) {
         case 0:
             $playerStatsType = '1v1';
             break;
         case 1:
             $playerStatsType = '2v2';
             break;
         case 2:
             $playerStatsType = '3v3';
             break;
         case 3:
             $playerStatsType = '4v4';
             break;
         default:
             $playerStatsType = '1v1';
             break;
     }
     if ($characterCode) {
         $regionX = 133;
     } else {
         $regionX = 170;
     }
     if ($characterCode || $region) {
         $playerAccountY = 19;
     } else {
         $playerAccountY = 24;
     }
     // получаем число ачивок
     preg_match('/h3>([\\d]*)</si', $data, $match);
     $playerAchievements = $match[1];
     // определяем координаты портрета персонажа
     if (preg_match("#portraits/([-\\d]+).jpg.*?'\\) ([-\\d]+)px ([-\\d]+)px no#si", $data, $match)) {
         // из какой заготовки будем брать
         $portraitImg = 'portraits-' . $match[1];
         // координаты, откуда будем брать
         $portraitX = abs($match[2] * PORTRAIT_KOEFICENT);
         $portraitY = abs($match[3] * PORTRAIT_KOEFICENT);
     } else {
         SaveForDebug("portraitImg is empty, http://{$bnetSubDomain}.battle.net" . $relativePlayerAccountUrl . 'ladder/');
         return FALSE;
     }
     if (preg_match('#<a class="league" href="([\\d]+)\\#current-rank">[\\w\\s]+<strong>' . $playerStatsType . '[\\w\\s]*</strong>.*?<a href="' . $relativePlayerAccountUrlForRegExp . '"[\\s]+class="race-([\\w]+)".*?Rank[\\s]+([\\d]+)#si', $data, $match)) {
         $currentRankUrl = $match[1];
         // место
         $isPlayerHasRank = true;
         $playerRank = $locale[$lang]['place'] . ': ' . $match[3];
         // раса
         $playerRace = $match[2];
     } else {
         $playerStats = '  ' . $playerStatsType . $locale[$lang]['notRanked'];
         $leagueImg = 'none';
         $isPlayerHasRank = false;
         $playerRace = 'random';
     }
     if ($isPlayerHasRank) {
         $data = Network::GetHTTPData('http://' . $bnetSubDomain . '.battle.net' . $relativePlayerAccountUrl . 'ladder/' . $currentRankUrl, '<head.*>', 'id="current-rank".*?tr class="row2"');
         if (!$data) {
             return false;
         }
         // лига
         if (preg_match_all('|<title>' . $playerStatsType . ' ([\\w]+) |si', $data, $match)) {
             $leagueImg = strtolower($match[1][0]);
         } else {
             $leagueImg = 'none';
         }
         if (preg_match('|id="current-rank"(.*?)tr class|si', $data, $match)) {
             $data = $match[1];
             if (preg_match_all('|<td class="align-center">([\\d]+)</td>|si', $data, $match)) {
                 $playerPoints = $locale[$lang]['points'] . ': ' . $match[1][0];
                 $wins = $match[1][1];
                 if (isset($match[1][2])) {
                     $lose = $match[1][2];
                     $playerStats = "{$wins} / {$lose}";
                     $playerWinRate = round($wins * 100 / ($wins + $lose)) . ' %';
                 } else {
                     $playerStats = $locale[$lang]['wins'] . ': ' . $wins;
                 }
             }
         }
     }
     $playerImgResource = imagecreatetruecolor(358, 68);
     // берем шаблон
     $templateImg = imagecreatefrompng(SIG_BASEDIR . 'img/sig/med_back_' . $sigBackgroundIndex . '.png');
     // белый цвет по контуру становится прозрачным
     $transparentColor = imagecolorallocate($playerImgResource, 255, 255, 255);
     imagecolortransparent($playerImgResource, $transparentColor);
     // берем портрет
     $portrait = imagecreatefrompng(SIG_BASEDIR . 'img/sig/' . $portraitImg . '.png');
     // лигу
     $league = imagecreatefrompng(SIG_BASEDIR . 'img/sig/leg_' . $leagueImg . '.png');
     // расу
     $raceTemplateImg = imagecreatefrompng(SIG_BASEDIR . "img/sig/race_{$playerRace}.png");
     // выбираем подходящий цвет текста и фон для bnetId
     list($textColor, $bnetIdTpl) = Sig::SelectTextColorAndBnetIdTplByBackgroundIndex($templateImg, $sigBackgroundIndex);
     // выводим все
     // первая строка, первый блок
     if (isset($playerRank)) {
         imagettftext($templateImg, TEXT_FONT_SIZE, 0, 101, 46, $textColor, FONT_STATS_RANK, $playerRank);
     }
     // первая строка, второй блок
     if (isset($playerPoints)) {
         imagettftext($templateImg, TEXT_FONT_SIZE, 0, 162, 46, $textColor, FONT_STATS_RANK, $playerPoints);
     }
     // вторая строка, первый блок
     if (isset($playerStats)) {
         imagettftext($templateImg, TEXT_FONT_SIZE, 0, 101, 57, $textColor, FONT_STATS_RANK, $playerStats);
     }
     // вторая строка, второй блок
     if (isset($playerWinRate)) {
         imagettftext($templateImg, TEXT_FONT_SIZE, 0, 162, 57, $textColor, FONT_STATS_RANK, $playerWinRate);
     }
     $box_nik = imagettfbbox(11, 0, $fontAccount, $playerAccount);
     // 237 - 12 - $box_nik[ 2 ] = 225 - $box_nik[ 2 ]
     imagettftext($templateImg, 11, 0, 223 - $box_nik[2], $playerAccountY, $textColor, $fontAccount, $playerAccount);
     if (isset($playerAchievements)) {
         imagettftext($templateImg, 9, 0, 302, 19, $textColor, FONT_ACHIEVEMENTS, $playerAchievements);
     }
     if (isset($playerStatsType)) {
         imagettftext($templateImg, 9, 0, 320, 51, $textColor, FONT_ACHIEVEMENTS, $playerStatsType);
     }
     if ($characterCode) {
         imagettftext($templateImg, 8, 0, 191, 31, $textColor, FONT_STATS_RANK, ' ID: ' . $characterCode);
     }
     if ($region) {
         imagettftext($templateImg, 8, 0, $regionX, 31, $textColor, FONT_STATS_RANK, $locale[$lang]['region'] . ': ' . $region);
     }
     imagecopyresampled($playerImgResource, $templateImg, 0, 0, 0, 0, 358, 68, imagesx($templateImg), imagesy($templateImg));
     imagecopyresampled($playerImgResource, $portrait, 237, 12, $portraitX, $portraitY, 46, 46, 46, 46);
     imagecopyresampled($playerImgResource, $league, 292, 31, 0, 0, 26, 28, 26, 28);
     imagecopyresampled($playerImgResource, $raceTemplateImg, 219, 43, 0, 0, 18, 18, 18, 18);
     imagepng($playerImgResource, $sigPath);
     // не забываем освобождать за собой память
     imagedestroy($playerImgResource);
     imagedestroy($templateImg);
     imagedestroy($portrait);
     imagedestroy($league);
     imagedestroy($raceTemplateImg);
     return true;
 }