vk-java-sdk icon indicating copy to clipboard operation
vk-java-sdk copied to clipboard

Is it OK?

Open Dim00 opened this issue 3 years ago • 12 comments

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; если первое только для событий самой группы, а не тех бесед, куда её добавили как бота), тоже ниего неработает, но это другой вопрос конечно

Dim00 avatar Nov 02 '21 21:11 Dim00

Чтобы увидеть что приходит от сервера можно поставить точку останова на строке 191 класса EventsHandler и в дебаггере посмотреть что объект message содержит валидный json, где в поле body можно увидеть сообщение, отправленное группе. Проблема в том, что модель, в которую десериализуется этот json, не соответствует. Из-за этого в уже переопределённый метод messageNew попадет неконсистентный объект, у которого поле text пустое, поэтому и выводится null Следовательно, сам хендлер работает и проблема кроется в объекте модели, и проблема эта уже известна (сам жду когда починят :))

mkfl3x avatar Nov 02 '21 22:11 mkfl3x

Хорошо. Я ПОЛНОСТЬЮ осознал всё; и позже напишу с чем ошибки связаны (их две разные, на разных сторонах, и они не компенсируют друг друга). И сегодня могу отправить код, показать как исправил, что переделал так, что всё должно работать как часы. Но даст ли ВК какую-нибудь премию за это? (если да, то чем). В случае, если код окажется подходящим.

Dim00 avatar Nov 03 '21 18:11 Dim00

Нет, не совсем так... Ошибки такие:

  1. Если есть поле body, та значит пользователь выбрал раннюю версию API, не 130-ую.
  2. Обработка 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");
    }
}
  1. Неоднозначности в самой схеме API, а именно часть callback_***** описывает сам объект события, другая поле object у него. Для части событий (напрмер "typing_state" или как-то так) объект вообще не описал, с другой стороны есть объекты связанные с донатами, для которых не определено одноимённый тип события. Посмотрев на MessageNew, я заметил, что оно представляет сам event типа "message_new" и наследуется от Base (который тоже представляет event), и в MessageNew определено поле object типа MessageObject, чья структура соотв. API. Но оказалось, что таких только несколько штук, наследуемых от Base...
  2. Да и то, пришлось закомментировать поле type у них вручную, т.к. оно наследуется от Base (ошибка от json, что два поля с равными аннотациями); а в Base также вручную переправить private на protected у всех полей.
  3. Ну и конечно сам список 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 и т.д. вообще тогда удалить что-бы не смущали.

Dim00 avatar Nov 05 '21 06:11 Dim00

Небольшой временный фикс этой проблемы, который я использую:

    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);
        }
    }

Он немного уродливый, конечно, но дело выполняет, так что, быть может, кому-то понадобится, пока не починят.

wtlgo avatar Dec 08 '21 18:12 wtlgo

Небольшой временный фикс этой проблемы, который я использую:

    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

a1k0u avatar Jun 06 '23 20:06 a1k0u

Вариант временного фикса на 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"
    }

Халатность разрабов ВК меня просто поражает

H4kt avatar Oct 30 '23 14:10 H4kt

Я думаю, проблема не в халатности, а в том, что проект заброшен

wtlgo avatar Oct 30 '23 14:10 wtlgo

Я думаю, проблема не в халатности, а в том, что проект заброшен

Почему тогда репозиторий не переведён в статус архива?

H4kt avatar Oct 30 '23 14:10 H4kt

Я думаю, проблема не в халатности, а в том, что проект заброшен

Почему тогда репозиторий не переведён в статус архива?

А ты лучше не подкидывай такие идеи, так может хоть вспомнят о нем в один день ;^)

wtlgo avatar Oct 30 '23 14:10 wtlgo

Я думаю, проблема не в халатности, а в том, что проект заброшен

Почему тогда репозиторий не переведён в статус архива?

А ты лучше не подкидывай такие идеи, так может хоть вспомнят о нем в один день ;^)

Может быть и так, однако библиотека обновилась 13 дней назад в централе

H4kt avatar Oct 30 '23 14:10 H4kt

Добраться до LongPoll'а не успели, рассчитываю что до нового года успеем с подарком починки.

aotd1 avatar Nov 13 '23 16:11 aotd1