Недавно решил написать программу, с помощью которой можно будет постить новости с картинкой в группе ВКонтакте. Поискав в интернете чьи-нибудь работы, я нашел куски кода на php, а на C# ничего не было. Нашел библиотеку для работы с VK API на C#, но там не было нужных для меня API. Я решил, что самому будет проще написать, чем искать, заодно и поделюсь с тем, кого это интересует.

Для этого нам понадобиться: standalone-приложение и токен для этого приложения.

Зарегистрируем standalone-приложение и получаем токен:
  1. Внизу сайта заходим в раздел «Разработчикам»;
  2. Жмем кнопку «Создать приложение»;
  3. Вводим название приложение и выбираем «Standalone-приложение»;
  4. После создания заходим на вкладку «Настройки» и копируем «ID приложения»;
  5. Затем переходим по ссылке:
    oauth.vk.com/authorize?client_id=[ID]&scope=wall,photos,audio,video,docs,notes,groups,messages,notifications,stats,ads,notify,friends,offline&redirect_uri=http://oauth.vk.com/blank.html&display=page&response_type=token

    , где [ID] – id, который мы скопировали в пункте 4);
  6. Копируем из адресной строки браузера адрес и оттуда сохраняем access_token. С помощью этого токена мы сможем получить доступ к API.

Приступим к самой программе. Для программы нам понадобиться библиотека Json.Net (можно скачать в NuGet).
  1. Скачаем Json.Net через NuGet;
  2. Создадим класс «vkWallPost», подключим пространство имен «Newtonsoft.Json.Linq» и «System.IO», добавим в него поле token и конструктор (при создании объекта класса vkWallPost будет передавать токен, который получили в пункте 4);
    Код
    class vkWallPost
    {
            string token;
            public vkWallPost(string token)
            {
                this.token = token;
            }
    }
    
  3. Добавим в класс метод Response с модификатором доступа private. Метод будет возвращать нам ответа сервера API в string;
    Код
            private string Response(string request_path)    //на вход подаем URL API, например https://api.vk.com/method/photos.getWallUploadServer?group_id=123
            {
                string response = string.Empty;
                HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(request_path);   //отправление запроса к серверу API
                HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();      //получение ответа от сервера API
                Stream receiveStream = Response.GetResponseStream();
                Encoding encode = System.Text.Encoding.GetEncoding("utf-8");   
                StreamReader readStream = new StreamReader(receiveStream, encode);   
    
                Char[] read = new Char[256];
                int count = readStream.Read(read, 0, 256);
    
                while (count > 0)
                {
                    String line = new String(read, 0, count);
                    response += line + "\r\n";                     
                    count = readStream.Read(read, 0, 256);
                }
    
                Response.Close(); 
                readStream.Close(); 
    
                return response;     
            }
    

  4. Добавим в класс метод photosGetWallUploadServer с модификатором private, чтобы получить URL для загрузки изображения на сервер ВКонтакте. На вход подаем group_id (не важно отрицательный или положительный);
    Код
            private string photosGetWallUploadServer(int group_id)    //получить сервер для загрузки фото на стену (возвращает upload_url)
            {
                string request_path = "https://api.vk.com/method/photos.getWallUploadServer?";    //формируем ссылку с нужными параметрами для запроса к API
                request_path += "group_id" + group_id;
                request_path += "&v=5.21";
                request_path += "&access_token=" + token;
    
                var json = JObject.Parse(Response(request_path));     //json парсер
                return json["response"]["upload_url"].ToString();       //возвращает upload_url
            }
    

  5. Добавим в класс метод photosUploadPhotoToURL с модификатором private. Метод возвращает объект класса JObject. На вход подаем URL, полученный из метода photosGetWallUploadServer и адрес фото, которое хотим загрузить (например, E:/img.png. Если ввести адрес вида: «www.img.ru/img.png», то работать не будет!);
    Код
            private JObject photosUploadPhotoToURL(string URL, string file_path)    //загрузка фото на сервер
            {
                WebClient myWebClient = new WebClient(); 
                byte[] responseArray = myWebClient.UploadFile(URL, file_path); 
                var json = JObject.Parse(System.Text.Encoding.ASCII.GetString(responseArray));
    
                return json;
            }
    

  6. Добавляем метод photosSaveWallPhoto. Модификатор доступа private, возвращает JObject. Нужен для того, чтобы получить id фотографии и owner_id (id того, кто загрузил). На вход подаем server, photo, hash, полученный из метода photosUploadPhotoToURL;
    Код
            private JObject photosSaveWallPhoto(string server, string photo, string hash)
            {
                string request_path = "https://api.vk.com/method/photos.saveWallPhoto?";
                request_path += "server=" + server;
                request_path += "&photo=" + photo;
                request_path += "&hash=" + hash;
                request_path += "&v=5.21";
                request_path += "&access_token=" + token;
    
                var json = JObject.Parse((Response(request_path).Replace("[", String.Empty)).Replace("]", String.Empty));       //сначала убираем '[' и ']' из ответа сервера, а зачем парсим
                return json;  //возвращаем объект класса JObject
            }
    

  7. Создадим метод wallPost, который будет отправлять команду API серверу, чтобы он добавил пост в группу с соотвествующими введенными данными. На вход подаем owner_id — id группы или пользователя (для группы должен быть отрицательным), по умолчанию посты будут видны всем пользователям и опубликованы от имени группы. Если хотим, чтобы посто был виден только для друзей, то передаем «friends_only: 1» и если не от имени группы, то from_group: 1. Message — сообщение, которое будет добавлено в пост и attachments в виде photoOwnerId_PhotoId, где OwnerId и PhotoId получены из метода photosSaveWallPhoto. publish_date — время, в которое пост будет опубликован (в формате unixtime);
    Код
            private string wallPost(int owner_id, int friends_only = 0, int from_group = 1, string message = "", string attachments = "", long publish_date = 0)                    //пост на стенку
            {
                if (message == "" && attachments == "") return "Error: message and attachments is empty!";                //не вызывать API, если msg and attach пустые
    
                string request_path = "https://api.vk.com/method/wall.post?";     //путь обращения к vk.api
                request_path += "owner_id=" + owner_id;         
                request_path += "&friends_only=" + friends_only;                   
                request_path += "&from_group=" + from_group;             
                if (message != string.Empty) request_path += "&message=" + message;  
                if (attachments != string.Empty) request_path += "&attachments=" + attachments;   
                if (publish_date != 0) request_path += "&publish_date=" + publish_date;     
                request_path += "&v=5.21";
                request_path += "&access_token=" + token;                                                          //токен (задается в конструкторе)
    
                return Response(request_path);
            }
    

  8. Теперь добавим последний метод, который будет собирать все, что надо и добавлять новость в группу. Название AddWallPost, модификатор public. На вход обязательно подаем group_id (gid) должен быть отрицательным, если это группа, message (сообщение, например «Hello, world!»), attachment (вида E:/img.png), если хотим, чтобы пост опубликовался в какое-то определенное время в течении дня, то на вход подаем publish_date в формате 12:30 или 17:03. Округление в большую сторону до числа, кратного 5 т.к. ВКонтакте можно публиковать только во время, кратное 5, например 13:00, 10:05, 11:10. Метод вернет ответ севера с id поста.

    Код
            public string AddWallPost(int gid, string message = "", string attachment = "", string publish_date = "")
            {
                if (message == "" && attachment == "") return "Error";
    
                string response = string.Empty;
                int hour = int.Parse(publish_date.Split(':')[0]);
                int min = int.Parse(publish_date.Split(':')[1]);
                if ((min % 5) != 0) min += 5 - (min % 5);
                if (min == 60)
                {
                    hour++;
                    min = 0;
                }
                    
                DateTime data_time = new DateTime(2014, 5, 30, hour, min, 0);
                long time = (data_time.ToUniversalTime().Ticks - 621355968000000000) / 10000000; //перевод числа в unixtime
    
                if (attachment != "")
                {
                    string img_path = photosGetWallUploadServer(gid);
                    var resp = photosUploadPhotoToURL(img_path, attachment);
                    resp = photosSaveWallPhoto(resp["server"].ToString(), resp["photo"].ToString(), resp["hash"].ToString());
                    attachment = "photo" + resp["response"]["owner_id"] + "_" + resp["response"]["id"];
                }
    
                return wallPost(gid, message: message, attachments: attachment, publish_date: time);
            }
    </spoiler>




  9. Надеюсь, что статья оказалась полезной для тех, кто искал информацию на эту тему.