обновляем алгоритмы + добавляем асинхранной вызов
This commit is contained in:
8
src/main/java/ru/dmitriymx/vkapi/Callback.java
Normal file
8
src/main/java/ru/dmitriymx/vkapi/Callback.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package ru.dmitriymx.vkapi;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void call(JsonObject jsonObject);
|
||||||
|
void error(Throwable throwable);
|
||||||
|
}
|
||||||
16
src/main/java/ru/dmitriymx/vkapi/TaskMethod.java
Normal file
16
src/main/java/ru/dmitriymx/vkapi/TaskMethod.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package ru.dmitriymx.vkapi;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
class TaskMethod {
|
||||||
|
private final String method;
|
||||||
|
private final Map<String, String> postData;
|
||||||
|
private final Callback callback;
|
||||||
|
}
|
||||||
21
src/main/java/ru/dmitriymx/vkapi/Timer.java
Normal file
21
src/main/java/ru/dmitriymx/vkapi/Timer.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package ru.dmitriymx.vkapi;
|
||||||
|
|
||||||
|
class Timer {
|
||||||
|
private long lastTime;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
this.lastTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean after(long expendedDiffTime) {
|
||||||
|
return diff() >= expendedDiffTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean before(long expendedDiffTime) {
|
||||||
|
return diff() < expendedDiffTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
long diff() {
|
||||||
|
return System.currentTimeMillis() - lastTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,106 +5,130 @@ import com.google.gson.JsonObject;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import ru.dmitriymx.vkapi.browser.ApacheBroswe;
|
||||||
import ru.dmitriymx.vkapi.browser.Browser;
|
import ru.dmitriymx.vkapi.browser.Browser;
|
||||||
import ru.dmitriymx.vkapi.browser.Response;
|
import ru.dmitriymx.vkapi.browser.Response;
|
||||||
|
import ru.dmitriymx.vkapi.longpoll.LongPollListener;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.StringJoiner;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.SynchronousQueue;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class VkApi {
|
public class VkApi implements Runnable {
|
||||||
private static final String VKAPI_URL = "https://api.vk.com/method/";
|
private static final String VKAPI_URL = "https://api.vk.com/method/";
|
||||||
private final String accessToken;
|
private final String accessToken;
|
||||||
private final Browser browser;
|
private final Browser browser;
|
||||||
private final Gson gson;
|
private final Gson gson = new Gson();
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private String apiVersion = "5.62";
|
private String apiVersion;
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private long callPause = 1200L;
|
|
||||||
private long lastTime;
|
|
||||||
private int call = 0;
|
|
||||||
|
|
||||||
public VkApi(String accessToken, Browser browser) {
|
private Timer timer = new Timer();
|
||||||
if (accessToken == null || accessToken.trim().isEmpty()) {
|
private int countCallApiPerSecond = 0;
|
||||||
throw new RuntimeException("Access token don't be NULL or EMPTY!");
|
|
||||||
|
private BlockingQueue<TaskMethod> queueExecute = new SynchronousQueue<>();
|
||||||
|
|
||||||
|
public VkApi(String apiVersion, String accessToken) {
|
||||||
|
this(apiVersion, accessToken, new ApacheBroswe());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VkApi(String apiVersion, String accessToken, Browser browser) {
|
||||||
|
if (stringIsNullOrEmpty(apiVersion)) throw new IllegalArgumentException("API version don't be NULL or EMPTY!");
|
||||||
|
if (stringIsNullOrEmpty(accessToken)) throw new IllegalArgumentException("Access token don't be NULL or EMPTY!");
|
||||||
|
if (browser == null) throw new IllegalArgumentException("Browser don't be NULL or EMPTY!");
|
||||||
|
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
this.browser = browser;
|
this.browser = browser;
|
||||||
this.gson = new Gson();
|
|
||||||
|
timer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonObject execApi(String methodApi, Map<String, String> params) throws VkApiException {
|
public JsonObject execApi(String methodApi, Map<String, String> params) throws IOException {
|
||||||
checkCalls();
|
if (timer.before(1000L) && countCallApiPerSecond >= 3) {
|
||||||
String url = VKAPI_URL + methodApi;
|
safeSleep(timer.diff() + 100);
|
||||||
String postData = paramsToString(params);
|
timer.reset();
|
||||||
|
countCallApiPerSecond = 0;
|
||||||
Response response;
|
} else if (timer.after(1000L)) {
|
||||||
try {
|
timer.reset();
|
||||||
response = browser.post(url, postData);
|
countCallApiPerSecond = 0;
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countCallApiPerSecond++;
|
||||||
|
Response response = browser.post(VKAPI_URL + methodApi, paramsToString(params));
|
||||||
chechResponse(response);
|
chechResponse(response);
|
||||||
|
|
||||||
JsonObject jsonObject = gson.fromJson(response.getContent(), JsonObject.class);
|
return gson.fromJson(response.getContent(), JsonObject.class);
|
||||||
if (jsonObject.has("error")) {
|
|
||||||
throw new VkApiException(jsonObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonObject;
|
public void execApiAsync(String apiMethod, Map<String, String> params, Callback callback) {
|
||||||
|
queueExecute.offer(TaskMethod.builder()
|
||||||
|
.method(apiMethod)
|
||||||
|
.postData(params)
|
||||||
|
.callback(callback)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonObject longExecApi(String server, String key, long ts, long wait) throws VkApiException {
|
public LongPollListener getLongPollListener() throws IOException {
|
||||||
|
JsonObject jsonObject = execApi("messages.getLongPollServer", Collections.singletonMap("need_pts", "1"));
|
||||||
|
jsonObject = jsonObject.getAsJsonObject("response");
|
||||||
|
|
||||||
|
return new LongPollListener(
|
||||||
|
this,
|
||||||
|
jsonObject.get("server").getAsString(),
|
||||||
|
jsonObject.get("key").getAsString(),
|
||||||
|
jsonObject.get("ts").getAsLong()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while(!Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
TaskMethod task = queueExecute.take();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonObject jsonObject = execApi(task.getMethod(), task.getPostData());
|
||||||
|
|
||||||
|
if (task.getCallback() != null) {
|
||||||
|
task.getCallback().call(jsonObject);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (task.getCallback() != null) {
|
||||||
|
task.getCallback().error(e);
|
||||||
|
} else {
|
||||||
|
log.error(MessageFormat.format(
|
||||||
|
"Error execute method \"{0}\"",
|
||||||
|
task.getMethod()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject longExecApi(String server, String key, long ts, long wait) throws IOException {
|
||||||
String url = String.format("https://%s?act=a_check&key=%s&ts=%d&wait=%d&mode=2&version=1",
|
String url = String.format("https://%s?act=a_check&key=%s&ts=%d&wait=%d&mode=2&version=1",
|
||||||
server, key, ts, wait
|
server, key, ts, wait
|
||||||
);
|
);
|
||||||
|
|
||||||
Response response;
|
Response response = browser.get(url);
|
||||||
try {
|
|
||||||
response = browser.get(url);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
chechResponse(response);
|
chechResponse(response);
|
||||||
|
|
||||||
JsonObject jsonObject = gson.fromJson(response.getContent(), JsonObject.class);
|
return gson.fromJson(response.getContent(), JsonObject.class);
|
||||||
if (jsonObject.has("error")) {
|
|
||||||
throw new VkApiException(jsonObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String paramsToString(Map<String, String> params) {
|
private String paramsToString(Map<String, String> params) {
|
||||||
String collect = "";
|
final StringJoiner sj = new StringJoiner("&");
|
||||||
if (params != null && params.size() > 0) {
|
sj.add("access_token=" + accessToken).add("v=" + apiVersion);
|
||||||
collect = "&" + params.entrySet().stream()
|
params.forEach((key, value) -> sj.add(key + "=" + value));
|
||||||
.map(entry -> entry.getKey() + "=" + entry.getValue())
|
|
||||||
.collect(Collectors.joining("&"));
|
|
||||||
}
|
|
||||||
return "access_token=" + accessToken + "&v=" + apiVersion + collect;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkCalls() {
|
return sj.toString();
|
||||||
if (call >= 3) {
|
|
||||||
long currTime = System.currentTimeMillis();
|
|
||||||
long diff = currTime - lastTime;
|
|
||||||
|
|
||||||
if (diff <= callPause) {
|
|
||||||
safeSleep(callPause);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTime = System.currentTimeMillis();
|
|
||||||
call = 0;
|
|
||||||
} else {
|
|
||||||
safeSleep(callPause/3);
|
|
||||||
call++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void chechResponse(Response response) {
|
private void chechResponse(Response response) {
|
||||||
@@ -131,4 +155,8 @@ public class VkApi {
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean stringIsNullOrEmpty(String string) {
|
||||||
|
return string == null || string.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
package ru.dmitriymx.vkapi;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
public class VkApiException extends Exception {
|
|
||||||
public VkApiException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VkApiException(JsonObject jsonObject) {
|
|
||||||
this(jsonObject.getAsJsonObject("error").get("error_msg").getAsString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,12 +2,11 @@ package ru.dmitriymx.vkapi.longpoll;
|
|||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import ru.dmitriymx.vkapi.VkApi;
|
import ru.dmitriymx.vkapi.VkApi;
|
||||||
import ru.dmitriymx.vkapi.VkApiException;
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -19,6 +18,8 @@ class LPRunner implements Runnable {
|
|||||||
private final String server;
|
private final String server;
|
||||||
private final String key;
|
private final String key;
|
||||||
private long ts;
|
private long ts;
|
||||||
|
@Setter
|
||||||
|
private long timeOut;
|
||||||
Map<Class<? extends Event>, List<EventListener>> mapListeners = new HashMap<>();
|
Map<Class<? extends Event>, List<EventListener>> mapListeners = new HashMap<>();
|
||||||
|
|
||||||
LPRunner(VkApi vkApi, String server, String key, long ts) {
|
LPRunner(VkApi vkApi, String server, String key, long ts) {
|
||||||
@@ -31,13 +32,10 @@ class LPRunner implements Runnable {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
log.debug("next loop...");
|
log.debug("next loop");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JsonObject jsonObject = vkApi.longExecApi(
|
JsonObject jsonObject = vkApi.longExecApi(server, key, ts, timeOut);
|
||||||
this.server, this.key, this.ts,
|
|
||||||
25L
|
|
||||||
);
|
|
||||||
|
|
||||||
JsonArray updates = jsonObject.getAsJsonArray("updates");
|
JsonArray updates = jsonObject.getAsJsonArray("updates");
|
||||||
if (updates.size() > 0) {
|
if (updates.size() > 0) {
|
||||||
@@ -47,8 +45,8 @@ class LPRunner implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.ts = jsonObject.get("ts").getAsLong();
|
this.ts = jsonObject.get("ts").getAsLong();
|
||||||
} catch (VkApiException e) {
|
} catch (IOException e) {
|
||||||
log.error("Oops!", e);
|
log.error("Execute long poll error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,23 @@
|
|||||||
package ru.dmitriymx.vkapi.longpoll;
|
package ru.dmitriymx.vkapi.longpoll;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import ru.dmitriymx.vkapi.VkApi;
|
import ru.dmitriymx.vkapi.VkApi;
|
||||||
import ru.dmitriymx.vkapi.VkApiException;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class LongPollListener {
|
public class LongPollListener {
|
||||||
private Thread thread;
|
private final Thread thread;
|
||||||
private LPRunner lpRunner;
|
private final LPRunner lpRunner;
|
||||||
|
|
||||||
public static LongPollListener create(VkApi vkApi) throws VkApiException {
|
public LongPollListener(VkApi vkApi, String server, String key, long ts) {
|
||||||
JsonObject jsonObject = vkApi.execApi("messages.getLongPollServer",
|
|
||||||
Collections.singletonMap("need_pts", "1"));
|
|
||||||
jsonObject = jsonObject.getAsJsonObject("response");
|
|
||||||
|
|
||||||
return new LongPollListener(
|
|
||||||
vkApi,
|
|
||||||
jsonObject.get("server").getAsString(),
|
|
||||||
jsonObject.get("key").getAsString(),
|
|
||||||
jsonObject.get("ts").getAsLong()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LongPollListener(VkApi vkApi, String server, String key, long ts) {
|
|
||||||
this.lpRunner = new LPRunner(vkApi, server, key, ts);
|
this.lpRunner = new LPRunner(vkApi, server, key, ts);
|
||||||
this.thread = new Thread(this.lpRunner, "Long poll listener");
|
this.thread = new Thread(this.lpRunner, "Long poll listener");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTimeOut(long timeOut) {
|
||||||
|
lpRunner.setTimeOut(timeOut);
|
||||||
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
start(false);
|
start(false);
|
||||||
}
|
}
|
||||||
@@ -43,6 +33,10 @@ public class LongPollListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
thread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
public void register(Class<? extends Event> clazz, EventListener listener) {
|
public void register(Class<? extends Event> clazz, EventListener listener) {
|
||||||
List<EventListener> eventListeners = lpRunner.mapListeners.computeIfAbsent(clazz, list -> new ArrayList<>());
|
List<EventListener> eventListeners = lpRunner.mapListeners.computeIfAbsent(clazz, list -> new ArrayList<>());
|
||||||
eventListeners.add(listener);
|
eventListeners.add(listener);
|
||||||
|
|||||||
Reference in New Issue
Block a user