Для начала, давайте определимся, что же такое VK API и какие возможности он нам предоставляет. API Вконтакте — позволяет разработчику взаимодействовать напрямую с базой данных вконтакте при помощи специальных http запросов. Чем нам, как разработчикам это может быть полезно? При помощи API мы можем извлекать самую различную информацию о пользователях, группах, записи со стены, фотографии и многое другое. Само собой, сервис имеет определенные ограничения, об этом подробнее под катом.
Предупреждаю сразу, статья не для новичков и некоторые моменты я пояснять не буду, так как статья получится очень длинной. Данными статьями я хочу лишь показать принцип работы с VK API и примеры кода. И конечно же код будет доступен на github .
Основная информация
Первое, что нам нужно для создания нашего приложения, это перейти по адресу https://vk.com/dev . Далее переходим в «Мои приложения» и нажимаем кнопку «Создать приложение» . Вконтакте позволяет регистрировать 3 типа приложений, краткое описание каждого:
- Standalone-приложение - это для мобильных клиентов, десктопных программ и сайтов на которых взаимодействие с API будет вестись из Javascript.
- Веб-сайт - если вы хотите написать скрипт для веб сайта, который будет использовать API указывайте этот вариант.
- IFrame/Flash приложение - игры в вконтакте и т.д.
Пишем имя приложения, тип выбираем «Веб-сайт», в адрес сайта и базовый домен указываем собственно адрес вашего сайта и домен. Если разрабатываете локально, можно указывать http://localhost .
Создание приложения
Наше приложение создано, теперь в разделе редактирования, переходим в «Настройки». На этой странице будут нужные нам данные, а именно id , защищенный ключ и сервисный ключ . Все это нужно для авторизации пользователя. Алгоритм взаимодействия с API очень простой:
- Пользователь авторизирует учетную запись ВК в приложении.
- Получает специальный токен.
- Выполняет запросы к API.
Теперь нужно собственно написать код, который позволит нам взаимодействовать с API. Писать мы будем на PHP с подключением некоторых библиотек. Нам понадобится composer , если у вас не установлен, то можете скачать его перейдя по этой ссылке .
В папке проекта создаем файл composer.json и записываем в него следующее:
{ "require": { "slim/slim": "^3.0", "twbs/bootstrap": "4.0.0-alpha.6", "illuminate/database": "^5.4", "slim/twig-view": "^2.2", "guzzlehttp/guzzle": "~6.0" } }
Сохраняем файл, открываем консоль и переходим в папку с нашим проектом в котором есть созданный нами файл. Выполняем команду composer install .
Этой командой мы установили мини фреймворк Slim, Bootstrap для быстрой верстки, пакет для работы с базой данных и шаблонизатор.
Структура приложения
Структура очень проста и включает в себя несколько файлов и папок.
- app — для настроек приложения, в этой папке будем держать маршруты, классы и прочие файлы настроек.
- public — основная папка в которой содержится файл index.php и файлы стилей
- resources — папка для представлений(views)
Создаем файлы
public/index.php
run();В этом файле мы только запускаем наше приложение, все настройки будем производить в других файлах. Это наша точка входа.
app/app.php
include "../vendor/autoload.php"; // Debug $config = [ "settings" => [ "displayErrorDetails" => true, "LogLevel" => "debug", "vk" => [ "client_id" => "ID ВАШЕГО ПРИЛОЖЕНИЯ", "client_secret" => "СЕКРЕТНЫЙ КЛЮЧ ВАШЕГО ПРИЛОЖЕНИЯ", "redirect_uri" => "http://vk-tutor.com/authorize", "display" => "popup", "scope" => "friends,wall,offline", "response_type" => "code" ] ], ]; $app = new Slim\App($config); // DI for twig view $container = $app->getContainer(); $container["view"] = function ($container) { $view = new \Slim\Views\Twig("../resources/views", [ "cache" => false ]); $basePath = rtrim(str_ireplace("index.php", "", $container["request"]->getUri()->getBasePath()), "/"); $view->addExtension(new Slim\Views\TwigExtension($container["router"], $basePath)); return $view; }; include "classes/VK.php"; // Routes require "routes.php";Не пугайтесь, сложного нет ничего. Просто подключаем загрузчик, внедряем зависимость для применения Twig и подключаем файл с маршрутами.
app/routes.php
get("/", function ($request, $response) { $vk = new VK($this->get("settings")["vk"]); if (isset($_SESSION["vk"])) { if (!isset($_SESSION["account"])) { // Устанавливаем токен $vk->accessToken = $_SESSION["vk"]->access_token; // Получаем информацию о текущем пользователе // Записываем всю информацию в сессию $_SESSION["account"] = $vk->getAccountInfo($_SESSION["vk"]->user_id); } return $this->view->render($response,"index.html", ["vk" => $vk, "account" => $_SESSION["account"]->response]); } return $this->view->render($response,"index.html", ["vk" => $vk]); }); $app->get("/authorize", function ($request, $response) { // Проверяем, есть ли в адресной строке код if ($request->getQueryParam("code") != NULL) { $vk = new VK($this->get("settings")["vk"]); // Получаем токен $_SESSION["vk"] = $vk->getAccessToken($request->getQueryParam("code")); } return $response->withStatus(302)->withHeader("Location", "../"); });Теперь наше приложение полностью готово к использованию. Для рендеринга страницы, нужно поместить шаблон в директорию resources/views . Можем приступать к написанию самого кода, который позволит нам взаимодействовать с API вконтакте. Поскольку нам нужно будет выполнять HTTP запросы к API, я установил Guzzle. Это HTTP клиент который позволит нам очень просто выполнять HTTP запросы к vkontakte.
Класс для работы с VK API
client_id = $params["client_id"]; $this->client_secret = $params["client_secret"]; $this->redirect_uri = $params["redirect_uri"]; $this->display = $params["display"]; $this->scope = $params["scope"]; $this->response_type = $params["response_type"]; } public function getLoginLink() { $params = [ "client_id" => $this->client_id, "redirect_uri" => $this->redirect_uri, "scope" => $this->scope, "response_type" => $this->response_type ]; return $this->loginUrl . http_build_query($params); } public function getAccessToken($code) { $client = new \GuzzleHttp\Client(); $response = $client->request("POST", $this->queryUrl, [ "form_params" => [ "client_id" => $this->client_id, "client_secret" => $this->client_secret, "redirect_uri" => $this->redirect_uri, "code" => $code, ], "verify" => false, ]); $data = json_decode($response->getBody()); return $data; } public function getAccountInfo($id) { $url = "https://api.vk.com/method/users.get"; $client = new \GuzzleHttp\Client(); $response = $client->request("POST", $url, [ "form_params" => [ "user_ids" => $id, "fields" => "photo_50,counters", "name_case" => "Nom", "access_token" => $this->accessToken ], "verify" => false ]); $data = json_decode($response->getBody()); return $data; } }Я написал небольшой класс, который пока еще умеет только авторизировать пользователя.
Метод getLoginLink() генерирует ссылку для авторизации, метод getAccessToken() запрашивает токен для доступа и последний метод getAccountInfo() загружает информацию об авторизованом пользователе.
На этом логика приложения завершена и при авторизации пользователя мы получим в сессии массив с данным пользователя и можем отобразить это.
Поскольку я подключил Twig
шаблонизатор, шаблоны у меня записываются в html файлы, но вы можете использовать и обычные php файлы или другой шаблонизатор.
Код файла для отображения информации.
{% extends "layout/app.html" %} {% block content %}
Учетная запись
{% for acc in account %}
Друзей: {{ acc.counters.friends }}
Подписчиков: {{ acc.counters.followers }}
Авторизировать пользователя
Авторизация {% endif %}
Фреймворк slim очень хорошо подходит для создания небольших приложений и API для своих проектов, поэтому я его и использовал в этой статье и будем продолжать использовать далее. В следующей статье посмотрим как можно достать посты со стены в группе и обработать их.
Начнем с основ: что такое API? Аббревиатура расшифровывается как Application Programming Interface, или интерфейс для программирования приложений. Название, вроде бы, говорит само за себя, но лучше рассмотреть более детальное объяснение.
Как уже было сказано, API – это, в первую очередь, интерфейс. Интерфейс, который позволяет разработчикам использовать готовые блоки для построения приложения. В случае с разработкой мобильных приложений в роли API может выступать библиотека для работы с "умным домом" – все нюансы реализованы в библиотеке и вы лишь обращаетесь к этому API в своём коде.
В случае веб-приложений, API может отдавать данные в отличном от стандартного HTML формате, благодаря чему им удобно пользоваться при написании собственных приложений. Сторонние общедоступные API чаще всего отдают данные в одном из двух форматов: XML или JSON. На случай, если вы решили сделать API для своего приложения, запомните, что JSON намного более лаконичен и прост в чтении, чем XML, а сервисы, предоставляющие доступ к данным в XML-формате, постепенно отказываются от последнего.
API в веб-приложениях на примерах
Некое приложение – например, Github – имеет свой API, которым могут воспользоваться другие разработчики. То, как они будут пользоваться им зависит от возможностей, которые предоставляет API и от того, насколько хорошо работает фантазия у разработчиков. позволяет, например, получать информацию о пользователе, его аватаре, читателях, репозиториях и множество других полезных и интересных сведений.
Подобным образом можно посылать запрос на любом языке, в том числе и на Ruby. Ответом на запрос будет примерно такая информация:
{ "login" : "Freika" , "id" : 3738638, "avatar_url" : "https://avatars.githubusercontent.com/u/3738638?v=3" , "gravatar_id" : "" , "url" : "https://api.github.com/users/Freika" , "html_url" : "https://github.com/Freika" , "followers_url" : "https://api.github.com/users/Freika/followers" , "following_url" : "https://api.github.com/users/Freika/following{/other_user}" , "gists_url" : "https://api.github.com/users/Freika/gists{/gist_id}" , "starred_url" : "https://api.github.com/users/Freika/starred{/owner}{/repo}" , "subscriptions_url" : "https://api.github.com/users/Freika/subscriptions" , "organizations_url" : "https://api.github.com/users/Freika/orgs" , "repos_url" : "https://api.github.com/users/Freika/repos" , "events_url" : "https://api.github.com/users/Freika/events{/privacy}" , "received_events_url" : "https://api.github.com/users/Freika/received_events" , "type" : "User" , "site_admin" : false , "name" : "Evgeniy" , "company" : "" , "blog" : "http://frey.su/" , "location" : "Barnaul" , "email" : "" , "hireable" : true , "bio" : null, "public_repos" : 39, "public_gists" : 13, "followers" : 15, "following" : 21, "created_at" : "2013-03-01T13:48:52Z" , "updated_at" : "2014-12-15T13:55:03Z" }
Как видно из блока выше, ответ содержит логин, аватар, ссылку на профиль на сайте и в API, статус пользователя, количество публичных репозиториев и прочую полезную и интересную информацию.
Одного API недостаточно
Создать полноценный API для своего приложения – лишь половина дела. Как вы предполагаете обращаться к API? Как к нему будут обращаться ваши пользователи?
Первое, что приходит в голову, это обычная серия HTTP-запросов с целью получения нужной информации, и это неправильный ответ. Самый очевидный способ в этом случае не является самым удобным и простым. Гораздо разумнее будет создать специальную библиотеку для работы с интерфейсом, в которой будут описаны все необходимые способы получения и отправки информации при помощи API.
Еще раз воспользуемся Github для приведения примера: для работы с АПИ этого прекрасного сервиса (а интерфейс у него предоставляет обширнейшие возможности) создано несколько библиотек на различных языках, например гем . В документации к таким библиотекам (и приведенному в качестве примера гему) любой заинтересованный разработчик сможет отыскать все необходимые способы получения информации от Гитхаба и отправки её обратно через API сервиса.
Таким образом, если вы создаете собственный API, подумайте, возможно стоит озаботиться созданием так же и библиотек для работы с ним на наиболее распространенных языках. И будьте готовы, что при определенном уровне востребованности вашего приложения кто-то другой может создать собственную библиотеку для работы с вашим API. Это нормально.
Полезные ссылки
В последующих статьях мы расскажем о том, как правильно создавать API, обеспечить его безопасность и ограничить доступ к части информации.
Работать с API ВКонтакте доводилось многим из нас, и каждый может сказать: работать с ним напрямую неудобно. Разумеется, было написано множество библиотек, предоставляющих удобный интерфейс работы с ним. Их можно и даже нужно использовать для облегчения работы и ускорения разработки. Сегодня tproger представляет подборку библиотек, набравших на GitHub больше всего звездочек и форков, для почти всех самых популярных ныне языков.
Java
Недавно Вконтакте выпустили официальный SDK для работы с API средствами Java. Страница проекта на GitHub содержит множество примеров, вот один из них, для публикации фотографии на странице пользователя:
PhotoUpload serverResponse = vk.photos().getWallUploadServer(actor).execute();
WallUploadResponse uploadResponse = vk.upload().photoWall(serverResponse.getUploadUrl(), file).execute();
List
C#
InTouch - кроссплатформенная библиотека для С#, которая весьма активно развивается . На момент публикации заявлена полная совместимость с версией 5.57 VK API. Вот пример работы с библиотекой:
Var clientId = 12345; // API client Id var clientSecret = "super_secret"; // API client secret var client = new InTouch(clientId, clientSecret); // Authorization works only in Windows (and WinPhone) Store Apps // otherwise you"ll need to set received "access_token" manually // using SetSessionData method. await client.Authorize(); // Gets a list of a user"s friends. var friends = await client.Friends.Get(); if (friends.IsError == false) { ShowFriendsList(friends.Data.Items.Where(friend => friend.Online)); } client.Dispose();
Или даже короче:
Using (var client = new InTouch(12345, "super_secret")) { await client.Authorize(); var friends = await client.Friends.Get(); // ... }
Node.js
Node-vkapi - современная библиотека для JavaScript, основанная на промисах. Она доступна так же в npm:
Npm install node-vkapi --only=prod --save
Вот пример кода, который публикует текст на стену пользователя с помощью node-vkapi:
Const VKApi = require("node-vkapi"); const VK = new VKApi({ app: { id: 1234567890, secret: "app-secret-key" }, auth: { login: "+79871234567", pass: "password123" } }); VK.auth.user({ scope: ["audio", "photos", "friends", "wall", "offline"] }).then(token => { return VK.call("wall.post", { owner_id: token.user_id, friends_only: 0, message: "Post to wall via node-vkapi" }).then(res => { // wall.post response return "https://vk.com/wall" + token.user_id + "_" + res.post_id; }); }).then(link => { // returned data from previous .then console.log("Post was published: " + link); }).catch(error => { // catching errors console.log(error); });
С++
VK API Lib - небольшая библиотека для работы API, написанная на чистом C++. Из зависимостей требуется только Curl. Библиотека предоставляет базовый класс VK::Client с методами авторизации и выполнения запросов к API.
На основе его вы можете создавать свои классы для работы с разделами. В примере приведен класс VK::Messages . Базовый пример использования:
#include "src/api.h" ... VK::Client api; if(api.auth(login, pass, access_token)) { cout << "Auth ok" << endl; cout << "Access token: " << api.access_token() << endl << endl; cout << api.call("wall.post", "owner_id=12345&message=Test"); } else{ cout << "Auth fail" << endl; }
Python
В настоящее время библиотека достаточно активно развивается, однако до этого в её разработке был перерыв примерно на год. Всегда хочется использовать актуальные инструменты, поэтому стоит упомянуть и другую библиотеку для Python . Вот как выглядит загрузка фотографии на сервер с её помощью (комментарии сохранены):
Import vk_api def main(): """ Пример: загрузка фото """ login, password = "[email protected]", "mypassword" vk_session = vk_api.VkApi(login, password) try: vk_session.authorization() except vk_api.AuthorizationError as error_msg: print(error_msg) return """ В VkUpload реализованы методы загрузки файлов в ВК. (Не все, если что-то понадобится - могу дописать) """ upload = vk_api.VkUpload(vk_session) photo = upload.photo(# Подставьте свои данные "D:/downloads/tube.jpg", album_id=200851098, group_id=74030368) vk_photo_url = "https://vk.com/photo{}_{}".format(photo["owner_id"], photo["id"]) print(photo, "\nLink: ", vk_photo_url) if __name__ == "__main__": main()
PHP
Библиотека на PHP, использующая, по словам автора, «сахалинские технологии», чтобы облегчить труд разработчика. Написана в ООП-стиле. Одна из самых крутых ее фич - генерация на основе запроса на PHP готового кода для специального метода ВК execute.
Пример просмотра последних 200 сообщений:
//MESSAGES
$data = $vk->request("messages.get", ["count" => 200]);
$userMap = ;
$userCache = ;
$user = new \getjump\Vk\Wrapper\User($vk);
$fetchData = function($id) use($user, &$userMap, &$userCache)
{
if(!isset($userMap[$id]))
{
$userMap[$id] = sizeof($userCache);
$userCache = $user->get($id)->response->get();
}
return $userCache[$userMap[$id]];
};
//REQUEST WILL ISSUE JUST HERE! SINCE __get overrided
$data->each(function($key, $value) use($fetchData) {
$user = $fetchData($value->user_id);
printf("[%s] %s
", $user->getName(), $value->body);
return;
});
Ruby
vkontakte_api - ruby-адаптер для ВКонтакте API. Он позволяет вызывать методы API, загружать файлы на сервера ВКонтакте, а также поддерживает все 3 доступных способа авторизации (при этом позволяя использовать стороннее решение).
Работать с ним можно, например, так:
# создаем клиент @vk = VkontakteApi::Client.new # и вызываем методы API @vk.users.get(uid: 1) # в ruby принято использовать snake_case в названиях методов, # поэтому likes.getList становится likes.get_list @vk.likes.get_list # также названия методов, которые возвращают "1" или "0", # заканчиваются на "?", а возвращаемые значения приводятся # к true или false @vk.is_app_user? # => false # если ВКонтакте ожидает получить параметр в виде списка, # разделенного запятыми, то его можно передать массивом users = @vk.users.get(uids: ) # большинство методов возвращает структуры Hashie::Mash # и массивы из них users.first.uid # => 1 users.first.first_name # => "Павел" users.first.last_name # => "Дуров" # если метод, возвращающий массив, вызывается с блоком, # то блок будет выполнен для каждого элемента, # и метод вернет обработанный массив fields = [:first_name, :last_name, :screen_name] @vk.friends.get(uid: 2, fields: fields) do |friend| "#{friend.first_name} "#{friend.screen_name}" #{friend.last_name}" end # => ["Павел "durov" Дуров"]
Perl
Простенькая, полностью асинхронная библиотека AnyEvent::VK , выполняющая все запросы по https. По сути является клиентом. Автор рекомендует использовать Async::Chain для комбинирования вложенных колбеков.
Use AnyEvent; use AnyEvent::VK; use Data::Dumper; my $vk = AnyEvent::VK->new(app_id => "Your APP ID", email => "Email/Mobile of user", password => "User Password", scope => "Application permissions",); # or my $vk = AnyEvent::VK->new(app_id => "Your APP ID", email => "Email/Mobile of user", password => "User Password", scope => "Application permissions", token => "Your access_token", expires => "Token expires timestamp", user_id => "user_id",); my $cv = AE::cv; $cv->begin; $vk->auth(sub { my $success = shift; if ($success) { my ($token, $expires_in, $user_id) = @_; $vk->request("users.get", { user_ids => "1", fields => "bdate,sex,city,verified", }, sub { my $response = shift; if ($response) { my $meta = shift; say Dumper($response); } else { my $meta = shift; warn "Request failed"; say Dumper($meta); } $cv->end; }); } else { my ($stage, $headers, $body, $cookie) = @_; warn "Auth failed"; say Dumper(@_); } $cv->end; }); $cv->recv;
Для мобильной разработки
Android (Java)
Еще одна официальная библиотека , на этот раз - для Android. Написана на Java и позволяет сильно упростить работу с API ВКонтакте из этого языка.
Пример отправки запроса:
Request.executeWithListener(new VKRequestListener() { @Override public void onComplete(VKResponse response) { //Do complete stuff } @Override public void onError(VKError error) { //Do error stuff } @Override public void onProgress(VKRequest.VKProgressType progressType, long bytesLoaded, long bytesTotal) { //I don"t really believe in progress } @Override public void attemptFailed(VKRequest request, int attemptNumber, int totalAttempts) { //More luck next time } });
Windows Phone (C#)
Последняя в этом списке официальная разработка: написанная на C# базовая библиотека для разработки на WP, похожая по функциональности на свои аналоги под Android и iOS.
Отправка запроса на список друзей:
Private void GetUserInfoButton_Click(object sender, RoutedEventArgs e)
{
VKRequest.Dispatch>(new VKRequestParameters("users.get",
"fields", "photo_200, city, country"),
(res) =>
{
if (res.ResultCode == VKResultCode.Succeeded)
{
VKExecute.ExecuteOnUIThread(() =>
{
var user = res.Data;
userImage.Source = new BitmapImage(new Uri(user.photo_200, UriKind.Absolute));
userInfo.Text = user.first_name + " " + user.last_name;
});
}
});
}
iOS (Objective C)
Официальная библиотека от ВКонтакте, предназначенная для написания приложений под iOS. Написана на Objective-C.
Пример кода с отправкой запроса:
; } else { NSLog(@"VK error: %@", error); } }];
Для хипстеров
Rust
На Github есть библиотека и для этого языка - vkrs . Она не слишком активно развивается, но это уже хоть что-то. Вот так она выглядит в деле:
Extern crate vkrs;
use std::{env, io};
use vkrs::*;
fn main() {
let api = api::Client::new();
let oauth = api.auth(env::var("VK_APP_ID").unwrap(),
env::var("VK_APP_SECRET").unwrap());
let auth_uri = oauth.auth_uri(auth::Permission::Audio).unwrap();
// Or if you want to get permissions for specific request:
// let auth_uri = oauth.auth_uri_for::
Тем, кто решил заняться разработкой приложений для ВКонтакте рано или поздно придется столкнуться с таким понятием как API (application programming interface), что в дословном переводе означает интерфейс прикладного программирования.
Более просто – API это целый комплекс разнообразных готовых функций, готовых классов, констант и т.д., которые можно использовать в сторонних . API ВКонтакте постоянно дорабатывается и сегодня это – уже полнофункциональный набор дополнений, позволяющий .
Подробно ознакомиться с документацией вы можете вот .
Последнее глобальное преобразование этой среды разработки было проведено еще в апреле 2009 года. Конечно, и после этого сайт постоянно модифицировался. Отметим основные возможности API ВКонтакте
- На API был реализован доступ приложений к друзьям, их фото, видеозаписям. Приложения сейчас могут самостоятельно создавать альбомы, размещать в них информацию . Ранее такого не было. Что важно, пользователь при установке такого приложения сам решает, открывать ему доступ к личным данным или нет.
- Рассылка приложениями уведомлений. Да, это порой надоедливое всплывающее уведомление в левой нижней части экрана также реализовано на API.
- Приглашать друзей в приложения стало проще также благодаря интерфейсу прикладного программирования , на котором была реализована данная функция массового приглашения. Впоследствии эти приглашения стали использоваться и для рекламы.
- Со страниц приложений исчезла реклама от ВК. Теперь разработчик игры или программы для ВК сам решает, нужна ли ему в его «флешке» реклама. Если да – он вправе разместить собственную и получать с нее 100% прибыль .
- Как назвали это разработчики ВК, пользователи, интегрирующие приложения, получили доступ к внутренней экономике сети. Проще говоря, стало доступным API позволяющее использовать ВКонтакте в качестве внутренних расчетов .
- Возможность запуска приложений в полноэкранном режиме . Увеличение максимального размера фрейма дало возможность для размещения дополнительных баннеров с рекламой, так что сегодня разработка приложений для ВК – это не просто удовольствие, а довольно прибыльное занятие при решении заняться рекламой, продажей баннеров с него.
- Сторонние приложения распространяются сейчас и через ленту новостей . Это еще один шаг для их популяризации. Кроме того (пусть уже и было это давно, приложению доступной стала миниатюрная иконка).
- Порядок вывода приложений также реализован на API. Сегодня по умолчанию, те игры, которые запускаются чаще всего – находятся у вас на первых позициях. Которыми давно не пользовались – найдете в конце списка.
- Благодаря API стала доступна и подробная встроенная статистика. Сегодня вы легко можете посчитать посетителей, просмотры страниц, пол, возраст, местонахождение пользователя приложения. Вся информация представляется максимально наглядный образом, в виде flash графиков, которые строятся моментально. Сделано это в первую очередь для рекламы (продажи рекламных площадей). Вы получаете статистику, видите что приложение используют десятки тысяч людей. Показываете это рекламодателю, который наверняка захочет оплатить баннер именно в таком приложении.
- Для самых популярных приложений стала возможна регистрация коротких поддоменов (например chat.vk.com и аналогичных) для простого запоминания и быстрого доступа.
- Ну и, наконец, появилась возможность вывода информации на личных страницах пользователей .