2018-07-17 19:23:54 +02:00
|
|
|
package de.mrbesen.telegram;
|
|
|
|
|
2019-02-07 06:30:28 +01:00
|
|
|
import de.mrbesen.telegram.AsyncHandler.Callback;
|
|
|
|
import de.mrbesen.telegram.AsyncHandler.Task;
|
2019-02-07 18:35:42 +01:00
|
|
|
import de.mrbesen.telegram.MessageBuilder.AsyncSendable;
|
2018-07-18 22:31:05 +02:00
|
|
|
import de.mrbesen.telegram.MessageBuilder.SendableMessage;
|
2018-07-18 04:44:04 +02:00
|
|
|
import de.mrbesen.telegram.commands.CommandManager;
|
2020-02-29 11:59:32 +01:00
|
|
|
import de.mrbesen.telegram.commands.FeedbackCommand;
|
2018-07-18 04:44:04 +02:00
|
|
|
import de.mrbesen.telegram.event.Event;
|
|
|
|
import de.mrbesen.telegram.event.EventManager;
|
2021-01-16 14:53:13 +01:00
|
|
|
import de.mrbesen.telegram.event.events.*;
|
2018-09-15 17:49:25 +02:00
|
|
|
import de.mrbesen.telegram.log.Log;
|
|
|
|
import de.mrbesen.telegram.log.Log4JLog;
|
|
|
|
import de.mrbesen.telegram.log.SimpleLog;
|
2018-07-18 22:31:05 +02:00
|
|
|
import de.mrbesen.telegram.objects.JSONBased.Member;
|
2021-01-19 21:19:53 +01:00
|
|
|
import de.mrbesen.telegram.objects.TFile;
|
|
|
|
import de.mrbesen.telegram.objects.TMessage;
|
|
|
|
import de.mrbesen.telegram.objects.TReplyMarkup;
|
|
|
|
import de.mrbesen.telegram.objects.TUser;
|
2018-09-15 17:49:25 +02:00
|
|
|
import de.mrbesen.telegram.objects.TUser.Status;
|
2020-02-29 11:59:32 +01:00
|
|
|
import lombok.Getter;
|
|
|
|
import lombok.Setter;
|
|
|
|
import org.json.JSONArray;
|
|
|
|
import org.json.JSONObject;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStreamWriter;
|
2020-11-05 01:59:42 +01:00
|
|
|
import java.net.HttpURLConnection;
|
2020-02-29 11:59:32 +01:00
|
|
|
import java.net.URL;
|
|
|
|
import java.net.URLEncoder;
|
2020-11-21 22:50:01 +01:00
|
|
|
import java.util.*;
|
2021-01-20 23:42:30 +01:00
|
|
|
import java.util.function.*;
|
2020-11-21 22:50:01 +01:00
|
|
|
import java.util.stream.Collector;
|
|
|
|
|
|
|
|
import static java.util.stream.Collector.Characteristics.IDENTITY_FINISH;
|
|
|
|
import static java.util.stream.Collector.Characteristics.UNORDERED;
|
2018-07-18 04:44:04 +02:00
|
|
|
|
2018-07-17 19:23:54 +02:00
|
|
|
public class TelegramAPI implements Runnable {
|
|
|
|
|
2020-11-04 17:17:18 +01:00
|
|
|
private static final String API_URL_DEFAULT = "https://api.telegram.org/bot";
|
2019-11-20 17:41:51 +01:00
|
|
|
private static final String TOKENREGEX = "^\\d{4,10}:[\\w-]{12,64}$";
|
2019-03-29 17:59:23 +01:00
|
|
|
private static final int TELEGRAMFILESIZELIMIT = 20000000;//20MB filesize https://core.telegram.org/bots/api#sending-files
|
2021-01-16 14:53:13 +01:00
|
|
|
public static final String APIVERSION = "3.10";//Jan 16, 2021
|
2018-07-18 04:44:04 +02:00
|
|
|
|
|
|
|
private int msg_offset = 0;
|
2019-05-20 19:35:12 +02:00
|
|
|
private int updateInterval = 60;
|
2018-07-17 19:23:54 +02:00
|
|
|
private String apikey;
|
2019-04-28 22:26:31 +02:00
|
|
|
private String botname;
|
2018-07-17 19:23:54 +02:00
|
|
|
private Thread thread;
|
|
|
|
private boolean run = true;
|
2020-02-29 08:04:33 +01:00
|
|
|
@Getter @Setter
|
2019-05-20 19:35:12 +02:00
|
|
|
private boolean longpolling = true;
|
2020-02-29 08:04:33 +01:00
|
|
|
@Setter @Getter
|
|
|
|
private boolean disableFeedback = false;
|
2020-11-04 17:17:18 +01:00
|
|
|
private String apiurl;
|
2019-02-25 02:51:17 +01:00
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
private String helpmessage = "generic helppage\nuse TelegramAPI.setHelpText(java.lang.String) to change this.";
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2019-02-08 07:27:00 +01:00
|
|
|
//stats
|
|
|
|
protected int fetchedUpdates = 0;
|
|
|
|
protected long start = 0;
|
2019-02-11 17:24:21 +01:00
|
|
|
|
2020-12-02 19:57:37 +01:00
|
|
|
private Map<Long, TUser> users = new TreeMap<>();
|
2020-02-29 08:04:33 +01:00
|
|
|
@Setter
|
|
|
|
private List<Long> admins = new LinkedList<>(); //required for feedback
|
2019-02-25 02:51:17 +01:00
|
|
|
private CommandManager cmdmgr = new CommandManager(this);
|
2018-07-18 04:44:04 +02:00
|
|
|
private EventManager evntmgr = new EventManager();
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2021-01-02 17:48:26 +01:00
|
|
|
private FeedbackCommand feedbackCmd = null;
|
2020-02-29 11:59:32 +01:00
|
|
|
|
2019-02-07 06:30:28 +01:00
|
|
|
//async
|
2020-12-30 09:41:12 +01:00
|
|
|
private AsyncHandler async = null;
|
2019-02-07 06:30:28 +01:00
|
|
|
public static Callback<Throwable, Void> IOE400supressor = new Callback<Throwable, Void>() {
|
|
|
|
@Override
|
|
|
|
public Void call(Throwable t) throws Throwable {
|
|
|
|
if(t instanceof IOException) {
|
|
|
|
if(t.getMessage().startsWith("Server returned HTTP response code: 400"))
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
throw t;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-02-01 20:24:50 +01:00
|
|
|
Log log = new Log4JLog();
|
2018-07-17 19:23:54 +02:00
|
|
|
|
2019-04-28 22:26:31 +02:00
|
|
|
/**
|
|
|
|
* please only use if bot is not used in groups
|
|
|
|
* @param apikey
|
|
|
|
*/
|
2020-11-04 17:17:18 +01:00
|
|
|
|
|
|
|
public TelegramAPI(String apikey) { this(null, apikey); }
|
|
|
|
|
|
|
|
public TelegramAPI(String apiurl, String apikey) {
|
|
|
|
this(apiurl, apikey, "");
|
2019-04-28 22:26:31 +02:00
|
|
|
}
|
|
|
|
|
2020-11-04 17:17:18 +01:00
|
|
|
public TelegramAPI(String apiurl, String apikey, String botname) {
|
|
|
|
this.apiurl = (apiurl == null ? API_URL_DEFAULT : apiurl);
|
2020-05-05 17:18:48 +02:00
|
|
|
this.botname = botname != null ? botname : "";
|
2019-04-28 22:26:31 +02:00
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
if (!apikey.matches(TOKENREGEX) ) {
|
2019-03-01 04:49:20 +01:00
|
|
|
throw new IllegalArgumentException("Invalid API key: " + apikey);
|
2018-07-18 04:44:04 +02:00
|
|
|
}
|
2020-11-04 17:17:18 +01:00
|
|
|
this.apiurl += this.apikey = apikey;
|
2018-07-17 19:23:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void start() {
|
2020-12-30 09:41:12 +01:00
|
|
|
start(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//start with a specified threadcount for async operations
|
|
|
|
public void start(int threadCount) {
|
2018-07-17 19:23:54 +02:00
|
|
|
if(thread == null) {
|
2020-12-30 09:41:12 +01:00
|
|
|
async = new AsyncHandler(this, threadCount);
|
2020-02-29 08:04:33 +01:00
|
|
|
if(!disableFeedback) {
|
|
|
|
//init Feedback
|
2020-02-29 11:59:32 +01:00
|
|
|
feedbackCmd = new FeedbackCommand(this, admins);
|
|
|
|
cmdmgr.registerCommand("feedback", feedbackCmd);
|
2020-02-29 08:04:33 +01:00
|
|
|
}
|
2018-07-17 19:23:54 +02:00
|
|
|
run = true;
|
|
|
|
thread = new Thread(this, "TelegramAPI");
|
|
|
|
thread.start();
|
|
|
|
} else {
|
|
|
|
throw new IllegalStateException("Still Running.");
|
2020-06-13 20:21:55 +02:00
|
|
|
}
|
2018-07-17 19:23:54 +02:00
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
|
2020-02-29 08:04:33 +01:00
|
|
|
public void addAdmin(long admin) {
|
|
|
|
admins.add(admin);
|
|
|
|
}
|
|
|
|
|
2020-12-01 14:38:27 +01:00
|
|
|
public boolean isAdmin(long admin) { return admins.contains(admin); }
|
|
|
|
|
2019-02-07 06:30:28 +01:00
|
|
|
public void request(Task task) {
|
|
|
|
async.enque(task);
|
|
|
|
}
|
|
|
|
|
2021-01-19 21:19:53 +01:00
|
|
|
public void requestAsync(String request, String parameter, long userid) {
|
|
|
|
this.async.enque(request, parameter, userid);
|
2019-02-07 06:30:28 +01:00
|
|
|
}
|
2020-06-15 00:28:37 +02:00
|
|
|
|
2021-01-19 21:19:53 +01:00
|
|
|
public JSONObject request(String request, String parameter, long userid) throws IOException {
|
|
|
|
return request(request, parameter, userid,true);
|
2019-02-25 00:21:31 +01:00
|
|
|
}
|
|
|
|
|
2021-01-19 21:19:53 +01:00
|
|
|
public JSONObject request(String request, String parameter, long userid, boolean logging) throws IOException {
|
2018-07-17 19:23:54 +02:00
|
|
|
//do https stuff
|
2020-08-27 13:29:58 +02:00
|
|
|
boolean toomany = true; //für retry after 429 error
|
|
|
|
int trycount = 0;
|
|
|
|
while(toomany) {
|
|
|
|
toomany = false;
|
|
|
|
++trycount;
|
2020-11-04 17:17:18 +01:00
|
|
|
URL url = new URL(apiurl + "/" + request);
|
2020-11-05 01:59:42 +01:00
|
|
|
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
2020-08-27 13:29:58 +02:00
|
|
|
con.setDoInput(true);
|
|
|
|
con.setDoOutput(true);
|
|
|
|
OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream());
|
|
|
|
wr.write(parameter);
|
|
|
|
wr.flush();
|
|
|
|
|
|
|
|
if (logging) {
|
|
|
|
String small = parameter;
|
|
|
|
if (small.length() > 60) {
|
|
|
|
small = small.substring(0, Math.min(60, small.length())) + "...";
|
|
|
|
}
|
2020-09-17 15:05:54 +02:00
|
|
|
log.log("request: " + request + " content " + small + " -> " + con.getResponseCode() + " " + con.getResponseMessage());
|
2020-08-27 13:29:58 +02:00
|
|
|
}
|
|
|
|
int response = con.getResponseCode();
|
|
|
|
if (response == 200) {
|
|
|
|
return new JSONObject(readfromIS(con.getInputStream()));
|
|
|
|
} else {
|
|
|
|
String errdesc = "[No description available]";
|
|
|
|
try {
|
|
|
|
//try to read error message
|
|
|
|
JSONObject returned = new JSONObject(readfromIS(con.getErrorStream()));
|
|
|
|
errdesc = returned.getString("description");
|
|
|
|
} catch (Throwable ignore) {
|
|
|
|
}
|
|
|
|
|
2020-09-17 15:05:54 +02:00
|
|
|
log.log("Request failed error: \"" + errdesc + "\" detailed request: " + request + "?" + parameter);
|
|
|
|
|
2020-08-27 13:29:58 +02:00
|
|
|
//catch 429 too many error
|
|
|
|
if (response == 429) {
|
2020-08-27 13:33:40 +02:00
|
|
|
if(trycount < 10) toomany = true;
|
2020-08-27 13:29:58 +02:00
|
|
|
|
|
|
|
//try to read timeout
|
|
|
|
//too Many Requests: retry after 19
|
|
|
|
int timeout = 10;
|
|
|
|
int idx = errdesc.lastIndexOf(" ");
|
|
|
|
try {
|
|
|
|
timeout = Integer.parseInt(errdesc.substring(idx))+1;
|
|
|
|
System.out.println("timeout read: " + timeout);
|
|
|
|
} catch(NumberFormatException | StringIndexOutOfBoundsException e ) {}
|
|
|
|
try {
|
|
|
|
System.out.println("Got 429 -> sleep for " + timeout + " seconds");
|
|
|
|
Thread.sleep(timeout * 1000);
|
|
|
|
} catch(InterruptedException e) {}
|
2020-08-27 13:33:40 +02:00
|
|
|
|
|
|
|
if(trycount < 10) continue;
|
2021-01-19 21:19:53 +01:00
|
|
|
} else if(response == 403) {
|
|
|
|
if(errdesc.equals("Forbidden: bot was blocked by the user")) {
|
|
|
|
if(userid != 0)
|
|
|
|
evntmgr.callEvent(new UserBlockedBotEvent(getUser(userid)));
|
|
|
|
}
|
2020-08-27 13:29:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
throw new APIError(parameter, request, con.getResponseCode(), null, errdesc);
|
|
|
|
//throw new IOException("API request returned HTTP " + con.getResponseCode() + " (" + con.getResponseMessage() + ") for action " + request + "\nurl: " + url.toString() + "\nparams: " + parameter + "\nerror description: " + errdesc);
|
2020-02-16 11:21:19 +01:00
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
}
|
2020-08-27 13:33:40 +02:00
|
|
|
//unreachable code?
|
|
|
|
throw new IllegalStateException();
|
2018-07-17 19:23:54 +02:00
|
|
|
}
|
2019-02-11 17:24:21 +01:00
|
|
|
|
2019-02-08 07:27:00 +01:00
|
|
|
protected String readfromIS(InputStream is) {
|
2019-02-08 03:15:31 +01:00
|
|
|
Scanner s = new Scanner(is);
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
while(s.hasNextLine()) {
|
|
|
|
sb.append(s.nextLine()).append('\n');
|
|
|
|
}
|
|
|
|
s.close();
|
|
|
|
return sb.toString();
|
|
|
|
}
|
2020-06-15 00:28:37 +02:00
|
|
|
|
2019-05-07 16:10:50 +02:00
|
|
|
public void sendAsync(long chatid, String msg) {
|
|
|
|
sendMessage(new MessageBuilder().setAsync().setReciver(chatid).setText(msg).build());
|
|
|
|
}
|
2018-07-17 19:23:54 +02:00
|
|
|
|
2019-02-01 16:56:30 +01:00
|
|
|
public TMessage sendMessage(TUser user, String text) {
|
|
|
|
MessageBuilder builder = new MessageBuilder();
|
|
|
|
builder.setReciver(user.getID());
|
|
|
|
builder.setText(text);
|
|
|
|
return sendMessage(builder.build());
|
|
|
|
}
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2018-07-18 22:31:05 +02:00
|
|
|
public TMessage sendMessage(SendableMessage msg) {
|
|
|
|
try {
|
2019-02-07 18:35:42 +01:00
|
|
|
if(msg instanceof AsyncSendable) {
|
2019-02-08 03:15:31 +01:00
|
|
|
AsyncSendable asyncm = (AsyncSendable) msg;
|
2019-02-07 18:35:42 +01:00
|
|
|
Callback<JSONObject, TMessage> adapter = getCallbackAdapter(this);
|
2019-02-08 03:15:31 +01:00
|
|
|
adapter.next = asyncm.callback;
|
2021-01-19 21:19:53 +01:00
|
|
|
Task t = new Task(msg.getCommand(), msg.getQ(), msg.getUserid(), adapter);
|
2019-02-08 03:15:31 +01:00
|
|
|
t.setExceptionhandl(asyncm.excpt == null ? IOE400supressor : asyncm.excpt);
|
2019-02-07 18:35:42 +01:00
|
|
|
async.enque(t);
|
|
|
|
} else {
|
2021-01-19 21:19:53 +01:00
|
|
|
JSONObject o = request(msg.getCommand(), msg.getQ(), msg.getUserid(), true);
|
2019-02-07 18:35:42 +01:00
|
|
|
return new TMessage(o.getJSONObject("result"), this);
|
|
|
|
}
|
2018-07-18 22:31:05 +02:00
|
|
|
} catch(IOException e) {
|
2019-02-07 18:35:42 +01:00
|
|
|
log.log("", e);
|
2018-07-18 22:31:05 +02:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2019-02-11 17:24:21 +01:00
|
|
|
|
2020-11-21 22:50:01 +01:00
|
|
|
/**
|
|
|
|
* is always async
|
|
|
|
* @param cmds cmdname -> description
|
|
|
|
*/
|
|
|
|
public void setBotCommand(Map<String, String> cmds) {
|
|
|
|
//build json obj
|
|
|
|
JSONArray arr = cmds.entrySet().stream().map(e -> new JSONObject().put("command", e.getKey()).put("description", e.getKey())).collect(new Collector<JSONObject, JSONArray, JSONArray>() {
|
|
|
|
@Override
|
|
|
|
public Supplier<JSONArray> supplier() {
|
|
|
|
return JSONArray::new;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public BiConsumer<JSONArray, JSONObject> accumulator() {
|
|
|
|
return JSONArray::put;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public BinaryOperator<JSONArray> combiner() {
|
|
|
|
return (a,b) -> { b.forEach(a::put); return b; };
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Function<JSONArray, JSONArray> finisher() {
|
|
|
|
return a -> a;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Set<Characteristics> characteristics() {
|
|
|
|
Set<Characteristics> c = new HashSet<>();
|
|
|
|
c.add(IDENTITY_FINISH);
|
|
|
|
c.add(UNORDERED);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
//commit
|
2021-01-19 21:19:53 +01:00
|
|
|
Task t = new Task("setMyCommands", "commands=" + arr.toString(), 0, null);
|
2020-11-21 22:50:01 +01:00
|
|
|
async.enque(t);
|
|
|
|
}
|
|
|
|
|
2021-01-02 17:48:26 +01:00
|
|
|
public void setFeedbackCallback(Function<TUser, String> clb) {
|
|
|
|
if(!disableFeedback && feedbackCmd != null)
|
|
|
|
feedbackCmd.setFeedbackCallback(clb);
|
|
|
|
}
|
|
|
|
|
2020-11-26 20:08:21 +01:00
|
|
|
public TFile getFile(final String fileid) throws IOException {
|
2021-01-19 21:19:53 +01:00
|
|
|
JSONObject jfile = request("getFile", "file_id=" + fileid, 0);
|
2020-11-26 20:08:21 +01:00
|
|
|
return new TFile(jfile.getJSONObject("result"));
|
|
|
|
}
|
|
|
|
|
2021-01-20 23:42:30 +01:00
|
|
|
public void getFile(final String fileid, Consumer<TFile> callback) {
|
|
|
|
async.enque(new Task("getFile", "file_id=" + fileid, 0, new Callback<JSONObject, Void>() {
|
|
|
|
@Override
|
|
|
|
public Void call(JSONObject j) throws Throwable {
|
|
|
|
TFile file = new TFile(j.getJSONObject("result"));
|
|
|
|
callback.accept(file);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2018-07-19 12:23:26 +02:00
|
|
|
public void sendTypedMessage(final String msg, final TUser user, final int seconds) {
|
2019-02-06 18:32:42 +01:00
|
|
|
new Thread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
user.sendStatus(Status.Typing);
|
|
|
|
try {
|
|
|
|
Thread.sleep(seconds*1000);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
}
|
|
|
|
sendMessage(new MessageBuilder().setText(msg).setReciver(user.getID()).build());
|
|
|
|
|
2018-07-19 12:23:26 +02:00
|
|
|
}
|
2020-06-15 00:28:37 +02:00
|
|
|
}).start();
|
2018-07-19 12:23:26 +02:00
|
|
|
}
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2019-10-20 17:08:25 +02:00
|
|
|
public void answerCallbackQuery(String callbackid, String text, boolean async) {
|
|
|
|
if(callbackid == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(async) {
|
2021-01-19 21:19:53 +01:00
|
|
|
requestAsync("answerCallbackQuery", "callback_query_id=" + callbackid + "&text=" + text, 0);
|
2019-10-20 17:08:25 +02:00
|
|
|
} else {
|
|
|
|
try {
|
2021-01-19 21:19:53 +01:00
|
|
|
request("answerCallbackQuery", "callback_query_id=" + callbackid + "&text=" + text, 0);
|
2019-10-20 17:08:25 +02:00
|
|
|
} catch(IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 00:21:31 +01:00
|
|
|
/**
|
2019-04-11 12:31:07 +02:00
|
|
|
* creates internal APIEror, when message is not modyfied!
|
2019-02-25 00:21:31 +01:00
|
|
|
* @param newCaption
|
|
|
|
* @param chatid
|
|
|
|
* @param msg_id
|
|
|
|
* @param rm
|
|
|
|
* @param async
|
|
|
|
* @param clb
|
|
|
|
*/
|
2019-04-13 01:58:28 +02:00
|
|
|
public void updateCaption(final String newCaption, long chatid, int msg_id, TReplyMarkup rm, boolean async, Callback<JSONObject, ?> clb) {
|
2019-02-11 17:24:21 +01:00
|
|
|
try {
|
2019-02-11 17:39:59 +01:00
|
|
|
String rply = "";
|
|
|
|
if(rm != null)
|
|
|
|
rply = "&reply_markup=" + URLEncoder.encode(rm.toJSONString(), "UTF-8");
|
|
|
|
String q = "chat_id=" + chatid + "&message_id=" + msg_id + "&caption=" + URLEncoder.encode(newCaption, "UTF-8") + rply;
|
2019-02-11 17:24:21 +01:00
|
|
|
if(async) {
|
2021-01-19 21:19:53 +01:00
|
|
|
Task t = new Task("editMessageCaption", q, chatid);
|
2019-04-11 12:31:07 +02:00
|
|
|
t.setExceptionhandl(new Callback<Throwable, Void>() {
|
|
|
|
@Override
|
|
|
|
public Void call(Throwable j) throws Throwable {
|
|
|
|
if(j instanceof APIError) {
|
|
|
|
String errmsg = ((APIError) j).getMessage();
|
|
|
|
if(errmsg.equals("Bad Request: message is not modified") || errmsg.equals("Bad Request: message to edit not found")) {
|
|
|
|
//both have code 400
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw j;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.async.enque(t);
|
2019-02-11 17:24:21 +01:00
|
|
|
} else {
|
2021-01-19 21:19:53 +01:00
|
|
|
request("editMessageCaption", q, chatid);
|
2019-02-11 17:24:21 +01:00
|
|
|
}
|
|
|
|
} catch(IOException e) {
|
|
|
|
log.log("", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-13 01:58:28 +02:00
|
|
|
public void updateMarkup(long chatid, int msg_id, TReplyMarkup rm, boolean async) {
|
2019-03-02 04:52:57 +01:00
|
|
|
try {
|
|
|
|
if(rm == null) return;//nope
|
2020-06-15 00:28:37 +02:00
|
|
|
|
2019-03-02 04:52:57 +01:00
|
|
|
String q = "chat_id=" + chatid + "&message_id=" + msg_id + "&reply_markup=" + URLEncoder.encode(rm.toJSONString(), "UTF-8");
|
2020-06-15 00:28:37 +02:00
|
|
|
|
2019-03-02 04:52:57 +01:00
|
|
|
if(async) {
|
2021-01-19 21:19:53 +01:00
|
|
|
this.async.enque("editMessageReplyMarkup", q, chatid);
|
2019-03-02 04:52:57 +01:00
|
|
|
} else {
|
2021-01-19 21:19:53 +01:00
|
|
|
request("editMessageReplyMarkup", q, chatid);
|
2019-03-02 04:52:57 +01:00
|
|
|
}
|
|
|
|
} catch(IOException e) {
|
|
|
|
log.log("", e);
|
|
|
|
}
|
|
|
|
}
|
2020-06-15 00:28:37 +02:00
|
|
|
|
2018-07-17 19:23:54 +02:00
|
|
|
public void stop() {
|
|
|
|
run = false;
|
2020-06-13 20:21:55 +02:00
|
|
|
if(thread == null) return;
|
2018-07-17 19:23:54 +02:00
|
|
|
thread.interrupt();
|
|
|
|
thread = null;
|
2020-06-15 00:28:37 +02:00
|
|
|
log.log("TelegramAPI stoped.");
|
2018-07-17 19:23:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2019-02-08 07:27:00 +01:00
|
|
|
start = System.currentTimeMillis();
|
2019-02-25 15:59:36 +01:00
|
|
|
if(longpolling) {
|
|
|
|
while(run) {
|
|
|
|
fetchUpdates();
|
|
|
|
fetchedUpdates++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while(run) {
|
|
|
|
long runstart = System.currentTimeMillis();
|
|
|
|
fetchUpdates();
|
|
|
|
fetchedUpdates++;
|
|
|
|
try {
|
|
|
|
int wait = (int) (updateInterval - (System.currentTimeMillis() - runstart));
|
|
|
|
if(wait > 0)
|
|
|
|
Thread.sleep(wait);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
break;
|
|
|
|
}
|
2018-07-17 19:23:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
|
|
|
|
public boolean isRunning() {
|
|
|
|
return thread.isAlive();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void fetchUpdates() {
|
|
|
|
try {
|
2021-01-19 21:19:53 +01:00
|
|
|
processUpdates(request("getUpdates", "offset=" + msg_offset + "&timeout=" + (longpolling ? updateInterval : 1), 0,false));
|
2018-07-18 04:44:04 +02:00
|
|
|
} catch (IOException e) {
|
2018-09-15 17:49:25 +02:00
|
|
|
log.log("error getting updates.", e);
|
2019-11-08 10:30:12 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
Thread.sleep(100);
|
|
|
|
} catch(InterruptedException ignored) {}
|
2019-02-01 18:42:49 +01:00
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void processUpdates(JSONObject object) {
|
|
|
|
if(object == null)
|
|
|
|
return;
|
|
|
|
JSONArray arr_results = object.getJSONArray("result");
|
|
|
|
for(int i = 0; !arr_results.isNull(i); i++) {
|
|
|
|
JSONObject entry = arr_results.getJSONObject(i);
|
|
|
|
TelegramAPIUpdate upd = new TelegramAPIUpdate(entry, this);
|
|
|
|
msg_offset = (upd.update_id+1 > msg_offset ? upd.update_id+1 : msg_offset);
|
|
|
|
}
|
|
|
|
}
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
public TUser getUser(String name) {
|
2020-12-02 19:57:37 +01:00
|
|
|
for(TUser us : users.values()) {
|
2018-07-18 04:44:04 +02:00
|
|
|
if(us.getName().equals(name))
|
|
|
|
return us;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-12-02 19:57:37 +01:00
|
|
|
public TUser getUser(long id, boolean createIfNotExitsts) {
|
|
|
|
TUser u = users.get(id);
|
|
|
|
if(u != null || !createIfNotExitsts)
|
|
|
|
return u;
|
|
|
|
u = new TUser(id, this);
|
2021-01-19 21:19:53 +01:00
|
|
|
evntmgr.callEvent(new NewUserEvent(u));
|
2020-12-02 19:57:37 +01:00
|
|
|
users.put(id, u);
|
2019-02-05 20:49:35 +01:00
|
|
|
return u;
|
2018-07-18 04:44:04 +02:00
|
|
|
}
|
|
|
|
|
2020-12-02 19:57:37 +01:00
|
|
|
public TUser getUser(long id) {
|
|
|
|
return getUser(id, true);
|
|
|
|
}
|
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
/**
|
|
|
|
* gets a user by id from the known user lists,
|
|
|
|
* if no user found, it creates a new User and adds it to the list.
|
|
|
|
* @param json
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public TUser getUser(JSONObject json) {
|
2020-12-02 19:57:37 +01:00
|
|
|
long id = json.getLong("id");
|
|
|
|
TUser user = getUser(id, false);
|
2018-07-18 04:44:04 +02:00
|
|
|
if(user != null)
|
|
|
|
return user;
|
|
|
|
user = new TUser(json, this);
|
2020-12-02 19:57:37 +01:00
|
|
|
users.put(id, user);
|
2018-07-18 04:44:04 +02:00
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setHelpText(String helptext) {
|
|
|
|
helpmessage = ( helptext == null ? "" : helptext);
|
|
|
|
}
|
|
|
|
|
2018-09-15 17:49:25 +02:00
|
|
|
/**
|
|
|
|
* Set the logging method, use {@link Log4JLog} for usage of LOG4J,
|
|
|
|
* or {@link SimpleLog} for usage of System.out.println();
|
|
|
|
* use Null t odisable logging the default is {@link SimpleLog}
|
|
|
|
* @param l
|
|
|
|
*/
|
|
|
|
public void setLog(Log l) {
|
|
|
|
if(l == null)
|
|
|
|
log = new Log();
|
|
|
|
else
|
|
|
|
log = l;
|
|
|
|
}
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
public CommandManager getCommandManager() {
|
|
|
|
return cmdmgr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public EventManager getEventManager() {
|
|
|
|
return evntmgr;
|
|
|
|
}
|
2020-06-15 00:28:37 +02:00
|
|
|
|
2019-05-20 19:35:12 +02:00
|
|
|
/**
|
2021-01-16 14:53:13 +01:00
|
|
|
* seconds to wait for longpolling or milliseconds to wait between shortpolling
|
2019-05-20 19:35:12 +02:00
|
|
|
* @param d
|
|
|
|
*/
|
2018-07-18 04:44:04 +02:00
|
|
|
public void setUpdateInterval(int d) {
|
|
|
|
if(d < 0)
|
|
|
|
throw new IllegalArgumentException("UpdateInterval is not allowed to be negative.");
|
|
|
|
updateInterval = d;
|
|
|
|
}
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
public String getHelpMessage() {
|
|
|
|
return helpmessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getupdateInterval() {
|
|
|
|
return updateInterval;
|
|
|
|
}
|
2019-02-11 17:24:21 +01:00
|
|
|
|
2019-02-08 07:27:00 +01:00
|
|
|
public float getUpdatesperSecond() {
|
|
|
|
if(start == 0)
|
|
|
|
return -1;
|
|
|
|
return fetchedUpdates / ((float) (System.currentTimeMillis() - start) / 1000);
|
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
|
2019-03-29 17:59:23 +01:00
|
|
|
public static boolean isSendable(long filesize) {
|
|
|
|
return filesize < TELEGRAMFILESIZELIMIT;
|
|
|
|
}
|
2020-06-15 00:28:37 +02:00
|
|
|
|
2019-02-07 06:30:28 +01:00
|
|
|
public static Callback<JSONObject, TMessage> getCallbackAdapter(TelegramAPI api) {
|
|
|
|
return new Callback<JSONObject, TMessage>() {
|
|
|
|
@Override
|
|
|
|
public TMessage call(JSONObject j) {
|
|
|
|
return new TMessage(j.getJSONObject("result"), api);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
protected class TelegramAPIUpdate {
|
|
|
|
protected int update_id = 0;
|
|
|
|
private TMessage msg = null;
|
|
|
|
private boolean isCommand = false;
|
|
|
|
|
|
|
|
protected TelegramAPIUpdate(JSONObject json, TelegramAPI api) {
|
|
|
|
update_id = json.getInt("update_id");
|
|
|
|
|
|
|
|
if(json.has("message")) {
|
|
|
|
msg = new TMessage(json.getJSONObject("message"), api);
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
String text = msg.getText();
|
|
|
|
if(text != null) {
|
2019-04-28 22:26:31 +02:00
|
|
|
if(text.matches("^\\/(\\w*)(@(\\w*))?")) {
|
2018-07-18 04:44:04 +02:00
|
|
|
text = text.substring(1);//remove '/'
|
|
|
|
isCommand = true;
|
2019-04-28 22:26:31 +02:00
|
|
|
if(text.contains("@")) {//check name
|
|
|
|
int at = text.indexOf('@');
|
|
|
|
int end = text.indexOf(' ', at+1);
|
|
|
|
if(end == -1)
|
|
|
|
end = text.length();
|
|
|
|
String botname = text.substring(at+1, end);
|
|
|
|
if(botname.equalsIgnoreCase(api.botname)) {
|
|
|
|
cmdmgr.onCommand(text, msg.getFrom(), msg);
|
|
|
|
} else
|
|
|
|
api.log.log("other botname found: " + botname);
|
|
|
|
} else {
|
|
|
|
cmdmgr.onCommand(text, msg.getFrom(), msg);
|
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
} else {
|
|
|
|
Event e = null;
|
2021-01-16 14:53:13 +01:00
|
|
|
if(msg.has(Member.audio)) {
|
|
|
|
e = new UserSendAudioEvent(msg);
|
|
|
|
} else if(msg.has(Member.video)) {
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2021-01-16 14:53:13 +01:00
|
|
|
} else if(msg.has(Member.document)) {
|
|
|
|
e = new UserSendDocumentEvent(msg);
|
|
|
|
} else if(msg.has(Member.photo)) {
|
|
|
|
e = new UserSendPhotoEvent(msg);
|
|
|
|
} else if(msg.has(Member.invoice)) {
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2021-01-16 14:53:13 +01:00
|
|
|
} else if(msg.has(Member.location)) {
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2021-01-16 14:53:13 +01:00
|
|
|
} else if(msg.has(Member.video_note)) {
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2021-01-16 14:53:13 +01:00
|
|
|
} else if(msg.has(Member.game)) {
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2021-01-16 14:53:13 +01:00
|
|
|
} else if(msg.has(Member.contact)) {
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2021-01-16 14:53:13 +01:00
|
|
|
} else if(msg.has(Member.sticker)) {
|
2019-02-06 18:32:42 +01:00
|
|
|
|
2018-07-18 04:44:04 +02:00
|
|
|
} else {
|
2021-01-16 14:53:13 +01:00
|
|
|
e = new UserSendMessageEvent(msg);
|
2020-02-29 11:59:32 +01:00
|
|
|
//call feedback cmd first
|
2020-05-05 17:18:48 +02:00
|
|
|
if(!disableFeedback) {
|
|
|
|
if (feedbackCmd.onMsg((UserSendMessageEvent) e)) {
|
|
|
|
e = null;
|
|
|
|
}
|
2020-02-29 11:59:32 +01:00
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
}
|
|
|
|
getEventManager().callEvent(e);
|
|
|
|
}
|
|
|
|
}
|
2019-02-06 23:52:15 +01:00
|
|
|
} else if(json.has("callback_query")) {
|
|
|
|
JSONObject cbq = json.getJSONObject("callback_query");
|
|
|
|
TUser from = api.getUser(cbq.getJSONObject("from"));
|
|
|
|
String data = cbq.getString("data");
|
2019-10-20 17:08:25 +02:00
|
|
|
String id = cbq.getString("id");
|
2019-02-07 03:24:55 +01:00
|
|
|
TMessage msg = new TMessage(cbq.getJSONObject("message"), api);
|
2020-02-29 11:59:32 +01:00
|
|
|
UserCallbackEvent event = new UserCallbackEvent(from, data, id, msg);
|
|
|
|
|
2020-05-05 17:18:48 +02:00
|
|
|
if(!disableFeedback) {
|
|
|
|
if (!feedbackCmd.onCallback(event))
|
|
|
|
getEventManager().callEvent(event);
|
|
|
|
} else {
|
2020-02-29 11:59:32 +01:00
|
|
|
getEventManager().callEvent(event);
|
2020-05-05 17:18:48 +02:00
|
|
|
}
|
2018-07-18 04:44:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected int getUpdate_id() {
|
|
|
|
return update_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected TMessage getMsg() {
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected TUser getUser() {
|
|
|
|
return msg.getFrom();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean isCommand() {
|
|
|
|
return isCommand;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum JSONObjectType {
|
|
|
|
User,
|
|
|
|
Chat,
|
|
|
|
Message,
|
|
|
|
MessageEntity,
|
|
|
|
Audio,
|
|
|
|
Document,
|
|
|
|
Game,
|
|
|
|
Sticker,
|
|
|
|
Video,
|
|
|
|
Voice,
|
|
|
|
Videonote,
|
|
|
|
Location,
|
|
|
|
Venue,
|
|
|
|
Contact;
|
|
|
|
|
|
|
|
boolean isArray = false;
|
|
|
|
void setArray(boolean b) {
|
|
|
|
isArray = b;
|
|
|
|
}
|
|
|
|
}
|
2019-02-11 17:24:21 +01:00
|
|
|
|
2019-02-08 03:15:31 +01:00
|
|
|
public class APIError extends IOException {
|
|
|
|
/**
|
2020-06-15 00:28:37 +02:00
|
|
|
*
|
2019-02-08 03:15:31 +01:00
|
|
|
*/
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
String params;
|
|
|
|
String method;
|
|
|
|
int returncode;
|
|
|
|
public String getParams() {
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
public String getMethod() {
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
public int getReturncode() {
|
|
|
|
return returncode;
|
|
|
|
}
|
|
|
|
public void setParams(String params) {
|
|
|
|
this.params = params;
|
|
|
|
}
|
|
|
|
public void setMethod(String method) {
|
|
|
|
this.method = method;
|
|
|
|
}
|
|
|
|
public void setReturncode(int returncode) {
|
|
|
|
this.returncode = returncode;
|
|
|
|
}
|
|
|
|
public APIError(String params, String method, int returncode, Throwable t, String msg) {
|
|
|
|
super(msg, t);
|
|
|
|
this.params = params;
|
|
|
|
this.method = method;
|
|
|
|
this.returncode = returncode;
|
|
|
|
}
|
|
|
|
public APIError(String arg0, Throwable arg1) {
|
|
|
|
super(arg0, arg1);
|
|
|
|
}
|
|
|
|
public APIError(String arg0) {
|
|
|
|
super(arg0);
|
|
|
|
}
|
|
|
|
public APIError(Throwable arg0) {
|
|
|
|
super(arg0);
|
|
|
|
}
|
|
|
|
}
|
2018-07-17 19:23:54 +02:00
|
|
|
}
|