vk-java-sdk
vk-java-sdk copied to clipboard
Is it OK?
import com.vk.api.sdk.httpclient.HttpTransportClient;
import com.vk.api.sdk.client.*;
import com.vk.api.sdk.objects.*;
import com.vk.api.sdk.objects.messages.*;
import com.vk.api.sdk.client.actors.GroupActor;
import com.vk.api.sdk.exceptions.ApiException;
import com.vk.api.sdk.exceptions.ClientException;
import com.vk.api.sdk.queries.messages.*;
import com.vk.api.sdk.events.*;
import com.vk.api.sdk.events.longpoll.*;
class MyHandler extends GroupLongPollApi
{
public MyHandler (VkApiClient client, GroupActor actor)
{ super (client, actor, 25); }
@Override
protected void messageNew (Integer groupId, Message message)
{
System.out.println (message.getText());
}
}
public class App
{
private static final String token = "......";
private static final int group_id = .......;
public static void main (String[] args) throws ClientException, ApiException, InterruptedException
{
VkApiClient vk = new VkApiClient (HttpTransportClient.getInstance());
GroupActor actor = new GroupActor (group_id, token);
MyHandler myhnd = new MyHandler (vk, actor);
myhnd.run();
System.out.println ("READY");
}
}
Запускается. Если написать самой группе, то выводит null, если зайти в диалог с группой и начать печатать сообщения для отправки, то ошибка NullPointerException. Если написать сообщение в беседе, куда добавлена группа (как бот), то нет никакой реакции. Это вообще нормально? Зачем выкладывать такой SDK, что код выше не работает? П.С. Пробовал переделать так, что сервер LongPoll получается по запросу "client.messages().getLongPollServer(actor)" (т.е. запрос не groups.getLongPollServer, а messages.getLongPollServer; если первое только для событий самой группы, а не тех бесед, куда её добавили как бота), тоже ниего неработает, но это другой вопрос конечно
Чтобы увидеть что приходит от сервера можно поставить точку останова на строке 191 класса EventsHandler и в дебаггере посмотреть что объект message содержит валидный json, где в поле body можно увидеть сообщение, отправленное группе. Проблема в том, что модель, в которую десериализуется этот json, не соответствует. Из-за этого в уже переопределённый метод messageNew попадет неконсистентный объект, у которого поле text пустое, поэтому и выводится null Следовательно, сам хендлер работает и проблема кроется в объекте модели, и проблема эта уже известна (сам жду когда починят :))
Хорошо. Я ПОЛНОСТЬЮ осознал всё; и позже напишу с чем ошибки связаны (их две разные, на разных сторонах, и они не компенсируют друг друга). И сегодня могу отправить код, показать как исправил, что переделал так, что всё должно работать как часы. Но даст ли ВК какую-нибудь премию за это? (если да, то чем). В случае, если код окажется подходящим.
Нет, не совсем так... Ошибки такие:
- Если есть поле body, та значит пользователь выбрал раннюю версию API, не 130-ую.
- Обработка enum, получаемого из парсинга json. (в EventsHandler) Например
enum E { C1, C2; }
void somefun (E e) {
switch (e) {
case C1: System.println("C1"); break
default: System.println("Unexpected");
}
}
Вызов somefun (new E (E.C2)) приведёт к выводу "Unexpected", а вот при somefun(null) будет NullPointerException и "вылет". Т.е. нужно как-то так
void somefun (E e) {
if (e == null) {System.println("Unexpected"); return;}
switch (e) {
case C1: System.println("C1"); break
default: System.println("Unhandled");
}
}
- Неоднозначности в самой схеме API, а именно часть callback_***** описывает сам объект события, другая поле object у него. Для части событий (напрмер "typing_state" или как-то так) объект вообще не описал, с другой стороны есть объекты связанные с донатами, для которых не определено одноимённый тип события. Посмотрев на MessageNew, я заметил, что оно представляет сам event типа "message_new" и наследуется от Base (который тоже представляет event), и в MessageNew определено поле object типа MessageObject, чья структура соотв. API. Но оказалось, что таких только несколько штук, наследуемых от Base...
- Да и то, пришлось закомментировать поле type у них вручную, т.к. оно наследуется от Base (ошибка от json, что два поля с равными аннотациями); а в Base также вручную переправить private на protected у всех полей.
- Ну и конечно сам список event'ов важно иметь согласованный с API, до этого он был из старой версии; при этом строки по-моему кое-где "съехали" на одну позицию (как вроде описывают objet для события на 2 строки выше).
Основываясь на таком наследовании (как MessageNew от Base, отображая event типа "message_new") написал код. и загрузил сюда. Правда, там еще есть небольшие изменения "под себя" (например run это исполнение в текущем потоке, runThread -- в новом), но основные изменения описаны выше. https://github.com/Dim00/vk-java-sdk
С сообщениями работает отлично. Потом придётся видимо в "старом" стиле поправить, т.е. когда для каждого названия-типа события определён через перечисление тип object'а у его класса, например MessageObject у "message_new"; только нужно аккуратно сверять соответствие по схеме, названия не одноимённые; а классы Base, MessageNew, MessageEdit и т.д. вообще тогда удалить что-бы не смущали.
Небольшой временный фикс этой проблемы, который я использую:
static class MyLongPollHandler extends GroupLongPollApi {
protected LongPollHandler(VkApiClient client, GroupActor actor, int waitTime) {
super(client, actor, waitTime);
}
public void messageNewFix(Integer groupId, MessageFix messagefix) {
var message = messagefix.getMessage();
// Ваш код
}
private static class MessageFix {
@SerializedName("message")
private Message message;
public Message getMessage() {
return message;
}
}
@Override
protected String parse(CallbackMessage message) {
if (message.getType() == Events.MESSAGE_NEW) {
messageFixNew(message.getGroupId(), gson.fromJson(message.getObject(), MessageFix.class));
return "OK";
}
return super.parse(message);
}
}
Он немного уродливый, конечно, но дело выполняет, так что, быть может, кому-то понадобится, пока не починят.
Небольшой временный фикс этой проблемы, который я использую:
static class MyLongPollHandler extends GroupLongPollApi { protected LongPollHandler(VkApiClient client, GroupActor actor, int waitTime) { super(client, actor, waitTime); } public void messageNewFix(Integer groupId, MessageFix messagefix) { var message = messagefix.getMessage(); // Ваш код } private static class MessageFix { @SerializedName("message") private Message message; public Message getMessage() { return message; } } @Override protected String parse(CallbackMessage message) { if (message.getType() == Events.MESSAGE_NEW) { messageFixNew(message.getGroupId(), gson.fromJson(message.getObject(), MessageFix.class)); return "OK"; } return super.parse(message); } }
Он немного уродливый, конечно, но дело выполняет, так что, быть может, кому-то понадобится, пока не починят.
Пример отличный! Только падает с NPE, когда приходит событие "typing" и т.п., так как в SDK в принципе нет поддержки таких событий. Лечится очень просто - в методе parse
всегда возвращайте OK
, либо же парсите самостоятельно. Костыль-костыльный..
Можно также использовать com.vk.api.sdk.objects.callback.MessageObject
, чтобы достичь такого же результата. В #265 лежит исправление с использованием этого объекта.
...
public void messageObjectNew(Integer groupId, MessageObject messageObject) {
var message = messageObject.getMessage();
// ...
}
@Override
protected String parse(CallbackMessage message) {
if (message.getType() == null) {
return "OK";
}
if (message.getType() == Events.MESSAGE_NEW) {
messageObjectNew(
message.getGroupId(), gson.fromJson(message.getObject(), MessageObject.class));
return "OK";
}
return super.parse(message);
}
...
Спасибо авторам за фиксы!
Актуально для 1.0.14
Вариант временного фикса на Kotlin
data class MessageWrapper(
val message: Message
)
/* Workaround for empty object being passed to messageNew */
override fun parse(message: CallbackMessage): String {
if (message.type != Events.MESSAGE_NEW) {
return super.parse(message)
}
val (chatMessage) = gson.fromJson(
message.getObject(),
MessageWrapper::class.java
)
messageNew(message.groupId, chatMessage)
return "OK"
}
Халатность разрабов ВК меня просто поражает
Я думаю, проблема не в халатности, а в том, что проект заброшен
Я думаю, проблема не в халатности, а в том, что проект заброшен
Почему тогда репозиторий не переведён в статус архива?
Я думаю, проблема не в халатности, а в том, что проект заброшен
Почему тогда репозиторий не переведён в статус архива?
А ты лучше не подкидывай такие идеи, так может хоть вспомнят о нем в один день ;^)
Я думаю, проблема не в халатности, а в том, что проект заброшен
Почему тогда репозиторий не переведён в статус архива?
А ты лучше не подкидывай такие идеи, так может хоть вспомнят о нем в один день ;^)
Может быть и так, однако библиотека обновилась 13 дней назад в централе
Добраться до LongPoll'а не успели, рассчитываю что до нового года успеем с подарком починки.