PHP
October 3, 2020

Динамическая обложка для группы ВК, последний подписчик, топ комментатор и прочее

Для установки динамической шапки требуется совсем немного, мы будем использовать компьютер как сервер, в дальнейшем сам скрипт можете установить на купленный сервер или обратиться в нашу группу для установки вашей шапки на ваше сообщество -> В личные сообщения сообщества KotOFF

Создание шапки происходит в 4 этапа, о них по порядку

1. Создание самой шапки.
2. Создание скрипта.
3. Установка OpenServer.
4. Настройка сервера и шапки.

1. Создание самой шапки.

Шапка должна иметь вот такой вид примерно:

На шапке заранее размещаете будущие отделы для аватарок и имен тех пользователей, который мы будем определять с помощью скрипта

Для создания такой шапки можно обратиться в группу, которая делает очень дешево такие шапки и оформления для сообществ — > В личные сообщения сообщества Тот самый Лев

На этом с 1 пунктом закончим, переходим к созданию скрипта

2. Создание скрипта.

Этот процесс самый трудоемкий, поэтому если Вы не хотите вдаваться в подробности, то можете сразу скачать архив и перейти к 3 и 4 пункту настройки.

Создаем файл с расширением api.php (Скрипт работает на версии php +5.6) и переходим в редактор Sublime Text или любой другой и вставляем туда этот код:

<?php

require_once('config.php');

function DownloadImages($url, $filename){
    $ch = curl_init($url);
    $fp = fopen($filename, 'wb');
    curl_setopt($ch, CURLOPT_FILE, $fp);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_exec($ch);
    curl_close($ch);
    fclose($fp);
}  

function getPOST($url, $post) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url); //урл сайта к которому обращаемся 
    curl_setopt($ch, CURLOPT_HEADER, false); //выводим заголовки
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); //теперь curl вернет нам ответ, а не выведет
    curl_setopt($ch, CURLOPT_POST, true); //передача данных методом POST
    curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36');
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //тут переменные которые будут переданы методом POST
    $result = curl_exec($ch);
    curl_close($ch);
    
    return $result;
}
    
function getApiMethod($method_name, $params) {
    global $access_token;
    global $api_version;

    // Сделаем проверки на токен и версию апи, если их не указали, добавим.
    if (!array_key_exists('access_token', $params) && !is_null($access_token)) {
        $params['access_token'] = $access_token;
    }

    if (!array_key_exists('v', $params) && !is_null($api_version)) {
        $params['v'] = $api_version;
    }
    
    // Сортируем массив по ключам
    ksort($params);
    
    // Отправим запрос
    return(getPOST('https://api.vk.com/method/'.$method_name, $params));
}

?>

сохраняем и создаем еще 1 файл с названием config.php и вставляем в него следующий код:

<?php

// KOTOFF.NET 
// Здесь вводим токен своего ПРОФИЛЯ а не сообщества. Получить можно здесь https://vkhost.github.io/
$access_token = 'ТОКЕН';
// ID группы
$group_id = 'ИД ГРУППЫ';
// Круглые аватарки [true - круглые false - квадратные]
$roundingOff = true;
// Шрифт текста (положить свой в папку font)
$font = "UniNeue-HeavyItalic.otf"; 

/* ----------------------- ПОСЛЕДНИЙ ПОДПИСАВШИЙСЯ ----------------------- */

// Показывать последнего подписчика [true - показывать false - нет]
$show_last_subscribe = true; 
// Размер шрифта
$last_subscribe_font_size = 20;
// Цвет текста
$last_subscribe_font_color = '255,255,255';
// Ширина аватарки
$last_subscribe_width = 137;
// Высота аватарки
$last_subscribe_height = 137;
// Координаты аватарки по оси Х
$last_subscribe_photo_pixel_x = 44;
// Координаты аватарки по оси Y
$last_subscribe_photo_pixel_y = 66;
// Координаты имени и фамилии по оси Х
$last_subscribe_text_pixel_x = 108;
// Координаты имени и фамилии по оси Y
$last_subscribe_text_pixel_y = 235;


/* ------------------------ ТОП ПО КОЛ-ВУ ЛАЙКОВ ------------------------ */

// Показывать пользователя который за сегодня набрал большее кол-во лайков к комментариям [true - показывать false - нет]
$show_top_like = true; 
// Размер шрифта
$top_like_font_size = 20;
// Цвет текста
$top_like_font_color = '255,255,255';
// Ширина аватарки
$top_like_width = 137;
// Высота аватарки
$top_like_height = 137;
// Координаты аватарки по оси Х
$top_like_photo_pixel_x = 229;
// Координаты аватарки по оси Y
$top_like_photo_pixel_y = 66;
// Координаты имени и фамилии по оси Х
$top_like_text_pixel_x = 295;
// Координаты имени и фамилии по оси Y
$top_like_text_pixel_y = 235;


/* ----------------------- ТОП ПО КОЛ-ВУ КОММЕНТОВ ----------------------- */

// Показывать пользователя который за сегодня оставил большее кол-во комментариев [true - показывать false - нет]
$show_top_comments = true;
// Размер шрифта
$top_comments_font_size = 20;
// Цвет текста
$top_comments_font_color = '255,255,255';
// Ширина аватарки
$top_comments_width = 137;
// Высота аватарки
$top_comments_height = 137;
// Координаты аватарки по оси Х
$top_comments_photo_pixel_x = 416;
// Координаты аватарки по оси Y
$top_comments_photo_pixel_y = 66;
// Координаты имени и фамилии по оси Х
$top_comments_text_pixel_x = 485;
// Координаты имени и фамилии по оси Y
$top_comments_text_pixel_y = 235;

// Домашняя директория скрипта
define('BASEPATH', str_replace('\\', '/', dirname(__FILE__)) . '/');
// Временная зона
date_default_timezone_set('Europe/Moscow');
// Путь к финальному изображению. 
// Минимальный размер обложки 795 x 200
$output_header = BASEPATH.'header/output.png';
// Путь к изображению которое будет браться за основу. 
// Минимальный размер обложки 795 x 200
$image_bg = BASEPATH.'header/bg.jpg';
// Версия API
$api_version = "5.63";

?>

В данном коде нужно указать токен и ID самой группы. Токен должен быть пользовательский
Пример:

$access_token = 'd398d012e185e01edf145450b7899d90b49a6dfee30af063a788deb6a33219249db3463664cd4a0354a57';
$group_id = '176771278';

Теперь создаем директорию (папку) с названием header и туда загружаем нашу шапку с таким навзанием — bg.jpg

После этого нужно создать еще 1 папку, где будут хранится наши шрифты для имен пользователей, назовем ее font и загружаем туда наши шрифты, скачать можно ниже

Возвращаемся туда, где создавали наши файлы и создаем финальный файл под названием index.php и давайте более детальнее разберем этот код

<?php

require_once('config.php');
require_once('api.php');

header('Content-type: text/html; charset=utf-8');

// Получим текущую дату
$date_today = date('Ymd');

if($show_top_like or $show_top_comments) {
    setLog('Получаю посты группы');
    // Получим посты со стены
    // больше 100 постов получать нет смысла, так как в вк ограничение
    // разрешено постить не больше 50 постов в сутки.
    $wall_get = getApiMethod('wall.get', array(
        'owner_id' => '-'.$group_id,
        'count' => '50'
    ));

    if($wall_get) {
        $wall_get = json_decode($wall_get, true);

        $countlike = array();
        $countcomments = array();
        
        foreach($wall_get['response']['items'] as $wall) {
            
            // Получим кол-во комментариев к посту
            $count = $wall['comments']['count'];
            $offset = 0;

            if($count > 0) { 
                // Получим все комментарии, так как их может быть больше 100.
                while($offset < $count){
                    setLog('Получаю кол-во комментариев к посту '.$wall['id']);
                    // Отправим запрос на получение комментариев
                    $comments_get = getApiMethod('wall.getComments', array(
                        'owner_id' => '-'.$group_id,
                        'post_id' => $wall['id'],
                        'need_likes' => '1',
                        'count' => '100',
                        'offset' => $offset
                    ));
                    
                    if($comments_get) {
                        $comments_get = json_decode($comments_get, true);

                        foreach($comments_get['response']['items'] as $comments) {
                            
                            if($date_today == date('Ymd', $comments['date'])) {
                                // В двух словах мы заносим данные в массив, суммируя их
                                if(!isset($countcomments[$comments['from_id']]) and !isset($countlike[$comments['from_id']])) {
                                    $countcomments[$comments['from_id']] = 1;
                                    $countlike[$comments['from_id']] = $comments['likes']['count'];   
                                } else {
                                    $countcomments[$comments['from_id']]++;
                                    $countlike[$comments['from_id']] += $comments['likes']['count'];
                                } 
                                var_dump($comments);
                            }
                            
                        }  
                    }

                    if($offset<$count) 
                        $offset = $offset + 100;
                }
            }

        }
    }
}



if($show_top_like) {
$day_like_top = 0;


if(count($countlike) > 0) {
    // Теперь найдем кто суммарно получил большее кол-во лайков к комментариям
    $value = max($countlike); 
    $day_like_top = array_search($value, $countlike);
    setLog('Получаю ID кто сумарно набрал большее кол-во лайков к комментариям '.$day_like_top);

    if($day_like_top > 0) {
        $user_top_like = getApiMethod('users.get', array(
            'user_ids' => $day_like_top,
            'fields' => 'photo_200'
        ));

        if($user_top_like) {
            $user_top_like = json_decode($user_top_like, true);

            $top_like_name = $user_top_like['response'][0]['first_name'];
            $top_like_lastname = $user_top_like['response'][0]['last_name'];
            $top_like_photo = $user_top_like['response'][0]['photo_200'];
            
            // Скачиваем фото
            if(!empty($top_like_name) && !empty($top_like_lastname) && !empty($top_like_photo)){
                DownloadImages($top_like_photo, 'header/top_likes.jpg');
            }
        }
    }
}

}


if($show_top_comments) {
    $day_comment_top = 0;




if(count($countcomments) > 0) {
        // Теперь найдем кто суммарно написал больше всех комментариев
        $value = max($countcomments); 
        $day_comment_top = array_search($value, $countcomments);
        setLog('Получаю ID кто суммарно написал больше всех комментариев '.$day_comment_top);

        if($day_comment_top > 0) {
            $user_top_comment = getApiMethod('users.get', array(
                'user_ids' => $day_comment_top,
                'fields' => 'photo_200'
            ));

            if($user_top_comment) {
                $user_top_comment = json_decode($user_top_comment, true);

                $top_comment_name = $user_top_comment['response'][0]['first_name'];
                $top_comment_lastname = $user_top_comment['response'][0]['last_name'];
                $top_comment_photo = $user_top_comment['response'][0]['photo_200'];
                
                // Скачиваем фото
                if(!empty($top_comment_name) && !empty($top_comment_lastname) && !empty($top_comment_photo)){
                    DownloadImages($top_comment_photo, 'header/top_comments.jpg');
                }
            }
        }
    }
}


if($show_last_subscribe) {
    // Теперь найдем последнего подписчика
    $last_subscribe = getApiMethod('groups.getMembers', array(
                'group_id' => $group_id,
                'sort' => 'time_desc',
                'count' => '1',
                'fields' => 'photo_200',
                'access_token' => $access_token
            ));

    if($last_subscribe) {
        $last_subscribe = json_decode($last_subscribe, true);

        $members_count = $last_subscribe['response']['count'];
        $last_subscribe_firstname = $last_subscribe['response']['items'][0]['first_name'];
        $last_subscribe_lastname = $last_subscribe['response']['items'][0]['last_name'];
        $last_subscribe_photo = $last_subscribe['response']['items'][0]['photo_200'];

        setLog('Получаю последнего вступившего в группу '.$last_subscribe_firstname.' '.$last_subscribe_lastname);
        
        // Скачиваем фото
        if(!empty($last_subscribe_firstname) && !empty($last_subscribe_lastname) && !empty($last_subscribe_photo)){
            DownloadImages($last_subscribe_photo, 'header/last_subscribe.jpg');
        }

    }
}
sleep(3);
if($show_weather){
    $ResultWeatherApi = getPOST('http://api.openweathermap.org/data/2.5/weather', array(
        'id' => $weather_city_id,
        'units' => 'metric',
        'CNT' => '1',
        'lang' => 'ru',
        'appid' => $weather_api_id
    ));

    $s = array(
        'id' => $weather_city_id,
        'units' => 'metric',
        'CNT' => '1',
        'lang' => 'ru',
        'appid' => $weather_api_id
    );
}

// -----------------------------------------------------------------------------
// --------------------------------- РИСОВАНИЕ ---------------------------------
// -----------------------------------------------------------------------------
setLog('Создание обложки');

$draw = new ImagickDraw(); 
$bg = new Imagick($image_bg);
$draw->setFont(BASEPATH."/font/".$font);
$draw->setTextAlignment(Imagick::ALIGN_CENTER);

// Последний подписчик
if($show_last_subscribe) {
    $file_name = BASEPATH.'header/last_subscribe.jpg';

    if(file_exists($file_name) && $show_last_subscribe) {
        $last_subscribe_photo = new Imagick($file_name);
        if($roundingOff==true) {
            RoundingOff($last_subscribe_photo, $last_subscribe_width,$last_subscribe_height);
        }

        $draw->setFontSize($last_subscribe_font_size);
        $draw->setFillColor("rgb(".$last_subscribe_font_color.")");

        $bg->compositeImage($last_subscribe_photo, Imagick::COMPOSITE_DEFAULT, $last_subscribe_photo_pixel_x, $last_subscribe_photo_pixel_y);
        $bg->annotateImage($draw, $last_subscribe_text_pixel_x, $last_subscribe_text_pixel_y, 0, mb_strtoupper($last_subscribe_firstname."\n".$last_subscribe_lastname, 'UTF-8'));
    }
}

// Топ по комментам
$file_name = BASEPATH.'header/top_comments.jpg';

if(file_exists($file_name) && $show_top_comments) {
    $top_comments_photo = new Imagick($file_name);
    if($roundingOff==true) {
        RoundingOff($top_comments_photo, $top_comments_width,$top_comments_height);
    }

    $draw->setFontSize($top_comments_font_size);
    $draw->setFillColor("rgb(".$top_comments_font_color.")");

    $bg->compositeImage($top_comments_photo, Imagick::COMPOSITE_DEFAULT, $top_comments_photo_pixel_x, $top_comments_photo_pixel_y);
    $bg->annotateImage($draw, $top_comments_text_pixel_x, $top_comments_text_pixel_y, 0, mb_strtoupper($top_comment_name."\n".$top_comment_lastname, 'UTF-8'));
}

// Топ по лайкам
$file_name = BASEPATH.'header/top_likes.jpg';

if(file_exists($file_name) && $show_top_like) {
    $top_like_photo = new Imagick($file_name);
    if($roundingOff==true) {
        RoundingOff($top_like_photo, $top_like_width,$top_like_height);
    }

    $draw->setFontSize($top_like_font_size);
    $draw->setFillColor("rgb(".$top_like_font_color.")");

    $bg->compositeImage($top_like_photo, Imagick::COMPOSITE_DEFAULT, $top_like_photo_pixel_x, $top_like_photo_pixel_y);
    $bg->annotateImage($draw, $top_like_text_pixel_x, $top_like_text_pixel_y, 0, mb_strtoupper($top_like_name."\n".$top_like_lastname, 'UTF-8'));
}

$bg->setImageFormat("png");
$bg->writeImage($output_header);

//echo '<img src="'.'header/output.png'.'">';

// -----------------------------------------------------------------------------
// --------------------------- ЗАГРУЗКА НА СЕРВЕР ------------------------------
// -----------------------------------------------------------------------------

// Получим адресс сервера
$getUrl = getApiMethod('photos.getOwnerCoverPhotoUploadServer', array(
    'group_id' => $group_id,
    'crop_x2' => '1590'
));
setLog('Получаю адресс сервера '.$getUrl);


if($getUrl) {
    $getUrl = json_decode($getUrl, true);

    $url = $getUrl['response']['upload_url'];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, array('photo' => new CURLFile($output_header, 'image/jpeg', 'image0')));
    $upload = curl_exec( $ch );
    curl_close( $ch );

    if($upload) {
        $upload = json_decode($upload, true);

        $getUrl = getApiMethod('photos.saveOwnerCoverPhoto', array(
            'hash' => $upload['hash'],
            'photo' => $upload['photo'],
        ));
        
        setLog('Загружаю обложку '.$getUrl);

        if(stripos($getUrl, 'response":{"images":[{')) {
            print_r('Успешно загрузили обложку <a href ="https://kotoff.net/ target="_blank" >kotoff.net</a></br>');
             echo '<p>*** Больше всех сегодня лайков набрал: <a href ="https://vk.com/id'.$day_like_top.'" target="_blank" >'.$top_like_name.' '.$top_like_lastname.' - '.$countlike[$day_like_top].'</a> шт.</p></br>';
            echo '<p>*** Больше всех сегодня комментариев написал: <a href ="https://vk.com/id'.$day_comment_top.'" target="_blank" >'.$top_comment_name.' '.$top_comment_lastname.' - '.$countcomments[$day_comment_top].'</a> шт.</p></br>';
              echo '<p>*** Последний подписчик <a href ="https://vk.com/id'.$day_like_top.'" target="_blank" >'.$last_subscribe_firstname.' '.$last_subscribe_lastname.'</a></p></br>';
             echo '<br><img src="'.'header/output.png'.'">';
            setLog('Загружаю обложку в '.$group_id);
        } else {
            print_r('Ошибка при загрузке обложки '.$getUrl);
            setLog('Ошибка при загрузке обложки '.$getUrl);
        }
        
    }
   
}



function RoundingOff($_imagick, $width, $height) {
    $_imagick->adaptiveResizeImage($width, $height, 100);
    $_imagick->setImageFormat('png');
        
    $_imagick->roundCornersImage(
        90, 90, 0, 0, 0
    );
}

function setLog($message) {
    $log_file_name = 'logs.txt';

    if(file_exists($log_file_name)) {
        $log = array_diff(explode("\r\n", file_get_contents($log_file_name)), array(''));
    }

    $log[] = date("m.d.Y-H:i:s").' | '.$message;

    if(file_put_contents($log_file_name, implode("\r\n", $log))) {
        return true;
    } else {
        return false;
    }
}


?>

Данный код собирает посты и с помощью циклов считает лайки, комментарии и определяет последнего вступившего. Код полностью рабочий, более детальную настройку рассмотрим в 4 пункте данной статьи. У данного скрипта есть несколько недоработок, часть из них была уже исправлена, для платных групп мы используем собственную версию скрипта. Сохраняем все файлы и переходим к 3 пункту настроек.

3. Установка OpenServer.

Для работы шапки нам потребуется сервер, а что бы не покупать и не платить деньги, мы будем использовать Open Server Panel — это портативная серверная платформа и программная среда для разработки

Переходим на официальный сайт и скачиваем дистрибутив-> Скачать Open Server

Нам предлагают на выбор 3 версии дистрибутива (ULTIMATE PREMIUM BASIC) с различным включенным функционалом, нам подойдет самый простой — BASIC, но если есть возможность, советую скачивать ULTIMATE версию!

После скачивания, распаковываем архив в удобное для Вас место, у Вас появится папка OSPanel

Заходим в нее и запускаем наш сервер

В трее, у Вас появится красный флажок, сервер нужно запустить выбрав зеленый

После запуска, там же выбираем Папка с сайтами и открываем уже созданный каталог с сайтом localhost, удаляем все что там есть и загружаем наши созданные файлы

Все готово, переходим к настройке и запуску.

4. Настройка сервера и шапки.

После успешной настройки, переходим к настройке, перейдя в браузере по адресу — http://localhost/index.php:

Если получили Warning похожий на этот:

То Вам нужно проверить правильно ли Вы указали токен профиля или ID группы, получить ID группы можно перейдя настройки сообщества

Если у Вас стоит короткое название, то просто откройте любую фотографию в группе и скопируйте ID с браузерной строки, все что после photo-ID_ЦИФРЫ (без минуса):

Если же Вы получили шапку:

Это значит что Вы сделали все правильно, давайте перейдем в группу, напишем пост, комментарий и поставим лайк:

Снова переходим на — http://localhost/index.php и смотрим что у нас получилось:

Как видим все работает, аватарки и текст можно перемещать, так как в Вашей шапке расположение может быть другое как тут например

Для этого открываем файл config.php

И меняем координаты, это один из трудоемких процессов, так как здесь нужно будет поиграться с положением и найти те самые координаты, примерное расположение можно узнавать используя Paint, наводим курсор в верхнюю правую точку устанавливаемого аватара и ниже получаем координаты:

Это условные координаты по оси Х и Y, далее просто смещаете по несколько пикселей в одну из сторон и находите оптимальную точку.

Настройка на этом практически завершена. Осталось сделать так, что бы скрипт сам обновлял ее, допустим каждые 5 минут, заходим в настройки OpenServer из трея и выбираем вкладку планировщик задач, прописываем команду:

%progdir%\modules\wget\bin\wget.exe -q —no-cache http://localhost/index.php

И указываем тайминги, в какое время нужно запускать скрипт, мы выставили что бы скрипт запускался каждые 5 минут в любой час, день, неделю и месяц, нажимаем добавить и сохраняем, сервер сам перезагрузится и планировщик задач начнет выполнять команду в указанные временные интервалы

При желании, можете загрузить скрипт на хостинг, и использовать CRON для запуска этого скрипта в нужное время.

На этом у меня все, пишите свои комментарии, задавайте вопросы, мы постараемся ответить на них, всем динамичных шапок