-
-
Notifications
You must be signed in to change notification settings - Fork 224
Для обновления на версию 1.30.2 - 1.32.0 необходимо сначала установить предварительную версию NLog 4.5.0-rc05 или выше. С версии 1.33.0 Установка происходит как обычно
- Реализовать интерфейс ICapthaSolver (пример реализации интерфейса) и пробросить реализацию в конструктор:
var api = new VkApi(LogManager.CreateNullLogger(), new CptchCaptchaSolver());
Api.Messages.Send(new MessagesSendParams
{
UserId = 12345678, //Id получателя
Message = "Message", //Сообщение
RandomId = new Random().Next(999999) //ужасный уникальный идентификатор
});
Ниже представлен пример, как можно прикрепить к сообщению файл, который уже загружен на сервера ВК:
var albumid = 123456789;
var photos = Api.Photo.Get(new PhotoGetParams
{
AlbumId = PhotoAlbumType.Id(albumid),
OwnerId = Api.UserId.Value
});
Api.Messages.Send(new MessagesSendParams
{
Attachments = photos,
Message = "Message",
PeerId = Api.UserId.Value
});
Данный способ позволяет прикрепить к сообщению локальный файлы, а также файлы, взятые из интернета, то есть прикрепление файла через ссылку.
Если нужно отправить картинку, то для прикрепления её к сообщению можно использовать следующий метод:
public async void SendMessageWithImage(this VkApi Api)
{
var userId = 12345678; //Получатель сообщения
// Получить адрес сервера для загрузки картинок в сообщении
var uploadServer = Api.Photo.GetMessagesUploadServer(userId);
// Загрузить картинку на сервер VK.
var response = await UploadFile(uploadServer.UploadUrl,
"https://www.gstatic.com/webp/gallery/1.jpg", "jpg");
// Сохранить загруженный файл
var attachment = Api.Photo.SaveMessagesPhoto(response);
//Отправить сообщение с нашим вложением
Api.Messages.Send(new MessagesSendParams
{
UserId = userId, //Id получателя
Message = "Message", //Сообщение
Attachments = attachment, //Вложение
RandomId = new Random().Next(999999) //Уникальный идентификатор
});
}
Для прикрепления вложения к сообщению аналогичный метод будет выглядеть следующим образом:
public async void SendMessageWithFile(this VkApi Api)
{
var userId = 12345678; //Получатель сообщения
// Получить адрес сервера для загрузки файлов в сообщении
var uploadServer = Api.Docs.GetMessagesUploadServer(userId);
// Загрузить файл на сервер VK.
var response = await UploadFile(uploadServer.UploadUrl,
"https://i.gifer.com/D446.gif", "gif");
// Сохранить загруженный файл
var title = "Test Gif"; //Название файла
var attachment = new List<MediaAttachment>
{
Api.Docs.Save(response, title ?? Guid.NewGuid().ToString())[0].Instance
};
//Отправить сообщение с нашим вложением
Api.Messages.Send(new MessagesSendParams
{
UserId = userId, //Id получателя
Message = "Message", //Сообщение
Attachments = attachment, //Вложение
RandomId = new Random().Next(999999) //Уникальный идентификатор
});
}
Данный метод является универсальным для любых типов файлов.
Если файл взят из интернета то в метод UploadFile в качестве аргумента file мы передаем ссылку на этот файл.
Если мы загружаем локальный файл, то в аргумент file мы передаем путь к этому файлу.
private async Task<string> UploadFile(string serverUrl, string file, string fileExtension)
{
// Получение массива байтов из файла
var data = GetBytes(file);
// Создание запроса на загрузку файла на сервер
using (var client = new HttpClient())
{
var requestContent = new MultipartFormDataContent();
var content = new ByteArrayContent(data);
content.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
requestContent.Add(content, "file", $"file.{fileExtension}");
var response = client.PostAsync(serverUrl, requestContent).Result;
return Encoding.Default.GetString(await response.Content.ReadAsByteArrayAsync());
}
}
В зависимости от того, где мы берем файл: локально или из интернета, есть две реализации метода GetBytes.
Для файла, взятого из интернета, нам достаточно передать ссылку на этот файл в данный метод:
private byte[] GetBytes(string fileUrl)
{
using (var webClient = new WebClient())
{
return webClient.DownloadData(fileUrl);
}
}
Если мы используем локальный файл, то нужно передать путь к файлу в следующий метод:
private byte[] GetBytes(string filePath)
{
return File.ReadAllBytes(filePath);
}
Для загрузки из файла можно воспользоваться тем же методом, заранее прочитав все байты документа при помощи File.ReadAllBytes(path)
/// <summary>
/// Загружает документ на сервер ВК.
/// </summary>
/// <param name="vkApi">Вк апи.</param>
/// <param name="data">Аттачмент, байты которого будут отправлены на сервер</param>
/// <param name="docMessageType">Тип документа - документ или аудиосообщение.</param>
/// <param name="peerId">Идентификатор назначения</param>
/// <param name="filename">Итоговое название документа</param>
/// <returns>Аттачмент для отправки вместе с сообщением.</returns>
public static async Task<MediaAttachment> LoadDocumentToChatAsync(VkApi vkApi, byte[] data,
DocMessageType docMessageType, long peerId, string filename)
{
var uploadServer = vkApi.Docs.GetMessagesUploadServer(peerId, docMessageType);
var r = await UploadFile(uploadServer.UploadUrl, data);
var documents = vkApi.Docs.Save(r, filename ?? Guid.NewGuid().ToString());
if (documents.Count != 1)
throw new ArgumentException($"Error while loading document attachment to {uploadServer.UploadUrl}");
return documents[0];
}
/// <summary>
/// Загружает массив байт на указанный url
/// </summary>
/// <param name="url">Адрес для загрузки</param>
/// <param name="data">Массив данных для загрузки</param>
/// <returns>Строка, которую вернул сервер.</returns>
public static async Task<string> UploadFile(string url, byte[] data) {
using (var client = new HttpClient()) {
var requestContent = new MultipartFormDataContent();
var documentContent= new ByteArrayContent(data);
documentContent.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data");
requestContent.Add(documentContent, "file", "audio.webm");
var response = await client.PostAsync(url, requestContent);
return Encoding.ASCII.GetString(await response.Content.ReadAsByteArrayAsync());
}
}
Для примера используем простую клавиатуру с одной кнопкой, имеющей надпись "Привет":
var keyboard = new MessageKeyboard
{
Buttons = new List<List<MessageKeyboardButton>>
{
new List<MessageKeyboardButton>
{
new MessageKeyboardButton
{
Action = new MessageKeyboardButtonAction
{
Type = KeyboardButtonActionType.Text, //Тип кнопки клавиатуры
Label = "Привет", //Надпись на кнопке
},
Color = KeyboardButtonColor.Default //Цвет кнопки
}
}
}
};
Так же есть построитель клавиатур который предосталяет удобный Fluent интерфейс
var keyboard = new KeyboardBuilder()
.AddButton("Подтвердить", "btnValue", KeyboardButtonColor.Primary)
.SetInline(false)
.SetOneTime()
.AddLine()
.AddButton("Отменить", "btnValue", KeyboardButtonColor.Primary)
.Build();
Для отправки данной клавиатуры нужно просто передать ее при отправке сообщения в свойстве Keyboard:
Api.Messages.Send(new MessagesSendParams
{
UserId = 12345678, //Id получателя
Message = "Message", //Сообщение
Keyboard = keyboard, // Клавиатура
RandomId = new Random().Next(999999) //Уникальный идентификатор
});
Как работать с payload кнопок:
// У нас есть данная клавиатура, которую мы отправим с сообщением (см.выше):
var keyboard = new KeyboardBuilder()
.AddButton("Привет", "hello", KeyboardButtonColor.Primary) // "hello" является полезной нагрузкой (payload)
.SetInline(false)
.Build();
// Воспользуемся обработчиком сообщений VKMessageManager с авторизацией от группы (см.ниже)
VKMessageManager manager = new VKMessageManager();
manager.OnNewMessage += (message, sender) => {
switch (message.Payload) // Получаем payload кнопок, после чего обрабатываем его
{
case "{\"button\":\"hello\"}": // payload кнопок приходит в данном формате, "hello" наша полезная нагрузка (payload)
await _api.Messages.SendAsync(new MessagesSendParams()
{
PeerId = message.PeerId.Value, // Отправим сообщение туда, откуда получили
RandomId = random.Next(int.MinValue, int.MaxValue), // Уникальный идентификатор
Message = "Кнопка \"Привет\" работает!!!" // Сообщение
});
break;
}
};
manager.StartMessagesHandling();
Подробную информацию о клавиатурах можно найти здесь.
Api.Messages.Send(new MessagesSendParams
{
UserId = 12345678, //Id получателя
Message = "Message", //Сообщение
Lat = 55.7531773, //Ширина
Longitude = 37.6157659, //Долгота
RandomId = new Random().Next(999999) //Уникальный идентификатор
});
Указав ширину и долготу, мы отправим пользователю сообщение с картой, на которой будет отмечена точка с данными координатами.
Как обрабатывать входящие сообщения пользователя (так же такие события как, к примеру, смена аватарки беседы и т.п.)
Тут можно посмотреть коды событий
Тут и тут можно посмотреть готовые обертки обработки входящих сообщений для пользователя и группы соответственно
Класс обработчика:
class VKMessageManager
{
private VkApi _api = new VkApi();
private ulong ts;
private ulong? pts;
//Событие для уведомления о новом сообщении
public event Action<Message, User> OnNewMessage;
public VKMessageManager()
{
//Авторизуемся с учетной записью пользователя.
//Для обхода блокировки сообщений используем ApplicationId какого-нибудь официально зарегистрированного приложения
//либо используем Bypass. В примере ApplicationId приложения Kate Mobile.
_api.Authorize(new ApiAuthParams() {
ApplicationId = 2685278,
Login = "login", //email или телефон
Password = "password", //пароль от учетной записи
Settings = Settings.All //берем полный доступ
});
}
public void StartMessagesHandling()
{
//Соединяемся с сервером Long Poll запросов и получаем необходимые ts и pts
LongPollServerResponse longPoolServerResponse = _api.Messages.GetLongPollServer(needPts: true);
ts = Convert.ToUInt64(longPoolServerResponse.Ts);
pts = longPoolServerResponse.Pts;
//В отдельном потоке запускаем метод, который будет постоянно опрашивать Long Poll сервер на наличие новых сообщений
new Thread(LongPollEventLoop).Start();
}
public void LongPollEventLoop()
{
//Запускаем бесконечный цикл опроса
while (true) {
//Отправляем запрос на сервер
LongPollHistoryResponse longPollResponse = _api.Messages.GetLongPollHistory(new MessagesGetLongPollHistoryParams() {
Ts = ts,
Pts = pts,
Fields = UsersFields.Photo100 //Указывает поля, которые будут возвращаться для каждого профиля. В данном примере для каждого отправителя сообщения получаем фото 100х100
});
//Получаем новый pts
pts = longPollResponse.NewPts;
//Тут пробегаемся по массиву событий
foreach (var longPollResult in longPollResponse.History)
{
switch (longPollResult[0])
{
//Код 4 - новое сообщение
case 4:
//Тут логика обработки сообщения
//К примеру, возбуждаем (ахх~) событие
OnNewMessage?.Invoke(
longPollResponse.Messages[0],
longPollResponse.Profiles
.Where(u => u.Id == longPollResponse.Messages[0].FromId)
.FirstOrDefault()
);
//longPollResponse.Messages[i] - сообщение
//longPollResponse.Profiles.Where(u => u.Id == longPollResponse.Messages[i].FromId).FirstOrDefault() - отправитель сообщения
break;
}
}
}
}
}
Использование:
//В консольном приложении этот код помещается в public static void Main()
VKMessageManager manager = new VKMessageManager();
manager.OnNewMessage += (message, sender) => {
//Обрабатываем входящее сообщение
switch (message.Text.ToLower()) //входящие сообщения преобразуем в нижний регистр во избежании проблем с ним
{
case "привет":
await _api.Messages.SendAsync(new MessagesSendParams()
{
PeerId = message.PeerId.Value,
RandomId = random.Next(int.MinValue, int.MaxValue),
Message = "Привет!!!"
});
break;
}
};
manager.StartMessagesHandling();
public static Uri DecodeAudioUrl(this Uri audioUrl)
{
var segments = audioUrl.Segments.ToList();
segments.RemoveAt((segments.Count - 1) / 2);
segments.RemoveAt(segments.Count - 1);
segments[segments.Count - 1] = segments[segments.Count - 1].Replace("/", ".mp3");
return new Uri($"{audioUrl.Scheme}://{audioUrl.Host}{string.Join("", segments)}{audioUrl.Query}");
}
//...
audio.Url.DecodeAudioUrl();