Merge branch 'dev' into java9-support

# Conflicts:
#	telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java
#	telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/EndUser.java
#	telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/MessageContext.java
#	telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotStarterConfiguration.java
#	telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultBotOptions.java
This commit is contained in:
chmilevfa 2018-07-16 19:37:30 +02:00
commit ad3dbfdd58
43 changed files with 1475 additions and 477 deletions

View File

@ -93,6 +93,12 @@ This library use [Telegram bot API](https://core.telegram.org/bots), you can fin
## Questions or Suggestions ## Questions or Suggestions
Feel free to create issues [here](https://github.com/rubenlagus/TelegramBots/issues) as you need or join the [chat](https://telegram.me/JavaBotsApi) Feel free to create issues [here](https://github.com/rubenlagus/TelegramBots/issues) as you need or join the [chat](https://telegram.me/JavaBotsApi)
## Powered by Intellij
<p align="center">
<a href="https://www.jetbrains.com"><img src="jetbrains.png" width="75"></a>
</p>
## License ## License
MIT License MIT License

View File

@ -31,7 +31,7 @@ public class MyBot extends AbilityBot {
Now you are able to set up your proxy Now you are able to set up your proxy
#### without authentication #### Without authentication
```java ```java
public class Main { public class Main {
@ -51,13 +51,12 @@ public class Main {
TelegramBotsApi botsApi = new TelegramBotsApi(); TelegramBotsApi botsApi = new TelegramBotsApi();
// Set up Http proxy // Set up Http proxy
DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class); DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);
HttpHost httpHost = new HttpHost(PROXY_HOST, PROXY_PORT); botOptions.setProxyHost(PROXY_HOST);
botOptions.setProxyPort(PROXY_PORT);
RequestConfig requestConfig = RequestConfig.custom().setProxy(httpHost).setAuthenticationEnabled(false).build(); // Select proxy type: [HTTP|SOCKS4|SOCKS5] (default: NO_PROXY)
botOptions.setRequestConfig(requestConfig); botOptions.setProxyType(DefaultBotOptions.ProxyType.SOCKS5);
botOptions.setHttpProxy(httpHost);
// Register your newly created AbilityBot // Register your newly created AbilityBot
MyBot bot = new MyBot(BOT_TOKEN, BOT_NAME, botOptions); MyBot bot = new MyBot(BOT_TOKEN, BOT_NAME, botOptions);
@ -89,25 +88,26 @@ public class Main {
public static void main(String[] args) { public static void main(String[] args) {
try { try {
// Create the Authenticator that will return auth's parameters for proxy authentication
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(PROXY_USER, PROXY_PASSWORD.toCharArray());
}
});
ApiContextInitializer.init(); ApiContextInitializer.init();
// Create the TelegramBotsApi object to register your bots // Create the TelegramBotsApi object to register your bots
TelegramBotsApi botsApi = new TelegramBotsApi(); TelegramBotsApi botsApi = new TelegramBotsApi();
// Set up Http proxy // Set up Http proxy
DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class); DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);
CredentialsProvider credsProvider = new BasicCredentialsProvider(); botOptions.setProxyHost(PROXY_HOST);
credsProvider.setCredentials( botOptions.setProxyPort(PROXY_PORT);
new AuthScope(PROXY_HOST, PROXY_PORT), // Select proxy type: [HTTP|SOCKS4|SOCKS5] (default: NO_PROXY)
new UsernamePasswordCredentials(PROXY_USER, PROXY_PASSWORD)); botOptions.setProxyType(DefaultBotOptions.ProxyType.SOCKS5);
HttpHost httpHost = new HttpHost(PROXY_HOST, PROXY_PORT);
RequestConfig requestConfig = RequestConfig.custom().setProxy(httpHost).setAuthenticationEnabled(true).build();
botOptions.setRequestConfig(requestConfig);
botOptions.setCredentialsProvider(credsProvider);
botOptions.setHttpProxy(httpHost);
// Register your newly created AbilityBot // Register your newly created AbilityBot
MyBot bot = new MyBot(BOT_TOKEN, BOT_NAME, botOptions); MyBot bot = new MyBot(BOT_TOKEN, BOT_NAME, botOptions);
@ -119,4 +119,6 @@ public class Main {
} }
} }
} }
``` ```
If you need something more complex than one proxy, then you can create more complex Authenticator that will check host and other parameters of proxy and return auth values based on them (for more information see code of java.net.Authenticator class)

View File

@ -102,8 +102,9 @@ If you're in doubt that you're missing some code, the full code example can be i
Go ahead and "/hello" to your bot. It should respond back with "Hello World!". Go ahead and "/hello" to your bot. It should respond back with "Hello World!".
Since you've implemented an AbilityBot, you get **factory abilities** as well. Try: Since you've implemented an AbilityBot, you get **factory abilities** as well. Try:
* /commands - Prints all commands supported by the bot * /report - Prints all user-defined commands supported by the bot
* This will essentially print "hello - says hello world!". Yes! This is the information we supplied to the ability. The bot prints the commands in the format accepted by BotFather. So, whenever you change, add or remove commands, you can simply /commands and forward that message to BotFather. * This will essentially print "hello - says hello world!". Yes! This is the information we supplied to the ability. The bot prints the commands in the format accepted by BotFather. So, whenever you change, add or remove commands, you can simply /report and forward that message to BotFather.
* /commands - Prints all commands exposed by the bot (factory and user-defined, with and without info)
* /claim - Claims this bot * /claim - Claims this bot
* /backup - returns a backup of the bot database * /backup - returns a backup of the bot database
* /recover - recovers the database * /recover - recovers the database

BIN
jetbrains.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

View File

@ -7,7 +7,7 @@
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>Bots</artifactId> <artifactId>Bots</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>3.6.1</version> <version>3.6.2</version>
<modules> <modules>
<module>telegrambots</module> <module>telegrambots</module>

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId> <artifactId>telegrambots-abilities</artifactId>
<version>3.6.1</version> <version>3.6.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Telegram Ability Bot</name> <name>Telegram Ability Bot</name>

View File

@ -1,6 +1,12 @@
package org.telegram.abilitybots.api.bot; package org.telegram.abilitybots.api.bot;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;
import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.*; import org.telegram.abilitybots.api.objects.*;
import org.telegram.abilitybots.api.sender.DefaultSender; import org.telegram.abilitybots.api.sender.DefaultSender;
@ -14,6 +20,7 @@ import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatAdm
import org.telegram.telegrambots.meta.api.methods.send.SendDocument; import org.telegram.telegrambots.meta.api.methods.send.SendDocument;
import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.bots.TelegramLongPollingBot; import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
@ -26,29 +33,31 @@ import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.MultimapBuilder.hashKeys;
import static java.lang.String.format; import static java.lang.String.format;
import static java.time.ZonedDateTime.now; import static java.time.ZonedDateTime.now;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.Comparator.comparing;
import static java.util.Objects.nonNull; import static java.util.Objects.nonNull;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static java.util.function.Function.identity;
import static java.util.regex.Pattern.CASE_INSENSITIVE; import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static java.util.regex.Pattern.compile; import static java.util.regex.Pattern.compile;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toMap;
import static jersey.repackaged.com.google.common.base.Throwables.propagate; import static jersey.repackaged.com.google.common.base.Throwables.propagate;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.telegram.abilitybots.api.db.MapDBContext.onlineInstance; import static org.telegram.abilitybots.api.db.MapDBContext.onlineInstance;
import static org.telegram.abilitybots.api.objects.Ability.builder; import static org.telegram.abilitybots.api.objects.Ability.builder;
import static org.telegram.abilitybots.api.objects.EndUser.fromUser;
import static org.telegram.abilitybots.api.objects.Flag.*; import static org.telegram.abilitybots.api.objects.Flag.*;
import static org.telegram.abilitybots.api.objects.Locality.*; import static org.telegram.abilitybots.api.objects.Locality.*;
import static org.telegram.abilitybots.api.objects.MessageContext.newContext; import static org.telegram.abilitybots.api.objects.MessageContext.newContext;
import static org.telegram.abilitybots.api.objects.Privacy.*; import static org.telegram.abilitybots.api.objects.Privacy.*;
import static org.telegram.abilitybots.api.util.AbilityMessageCodes.*;
import static org.telegram.abilitybots.api.util.AbilityUtils.*; import static org.telegram.abilitybots.api.util.AbilityUtils.*;
/** /**
@ -63,10 +72,11 @@ import static org.telegram.abilitybots.api.util.AbilityUtils.*;
* <li>Sets the user as the {@link Privacy#CREATOR} of the bot</li> * <li>Sets the user as the {@link Privacy#CREATOR} of the bot</li>
* <li>Only the user with the ID returned by {@link AbilityBot#creatorId()} can genuinely claim the bot</li> * <li>Only the user with the ID returned by {@link AbilityBot#creatorId()} can genuinely claim the bot</li>
* </ul> * </ul>
* <li>/commands - reports all user-defined commands (abilities)</li> * <li>/report - reports all user-defined commands (abilities)</li>
* <ul> * <ul>
* <li>The same format acceptable by BotFather</li> * <li>The same format acceptable by BotFather</li>
* </ul> * </ul>
* <li>/commands - returns a list of all possible bot commands based on the privacy of the requesting user</li>
* <li>/backup - returns a backup of the bot database</li> * <li>/backup - returns a backup of the bot database</li>
* <li>/recover - recovers the database</li> * <li>/recover - recovers the database</li>
* <li>/promote <code>@username</code> - promotes user to bot admin</li> * <li>/promote <code>@username</code> - promotes user to bot admin</li>
@ -101,10 +111,7 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
protected static final String BACKUP = "backup"; protected static final String BACKUP = "backup";
protected static final String RECOVER = "recover"; protected static final String RECOVER = "recover";
protected static final String COMMANDS = "commands"; protected static final String COMMANDS = "commands";
protected static final String REPORT = "report";
// Messages
protected static final String RECOVERY_MESSAGE = "I am ready to receive the backup file. Please reply to this message with the backup file attached.";
protected static final String RECOVER_SUCCESS = "I have successfully recovered.";
// DB and sender // DB and sender
protected final DBContext db; protected final DBContext db;
@ -148,9 +155,9 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
} }
/** /**
* @return the map of ID -> EndUser * @return the map of ID -> User
*/ */
protected Map<Integer, EndUser> users() { protected Map<Integer, User> users() {
return db.getMap(USERS); return db.getMap(USERS);
} }
@ -175,6 +182,20 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
return db.getSet(ADMINS); return db.getSet(ADMINS);
} }
/**
* @return the immutable map of String -> Ability
*/
public Map<String, Ability> abilities() {
return abilities;
}
/**
* @return the immutable list carrying the embedded replies
*/
public List<Reply> replies() {
return replies;
}
/** /**
* This method contains the stream of actions that are applied on any update. * This method contains the stream of actions that are applied on any update.
* <p> * <p>
@ -235,7 +256,7 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
* @param username the username of the required user * @param username the username of the required user
* @return the user * @return the user
*/ */
protected EndUser getUser(String username) { protected User getUser(String username) {
Integer id = userIds().get(username.toLowerCase()); Integer id = userIds().get(username.toLowerCase());
if (id == null) { if (id == null) {
throw new IllegalStateException(format("Could not find ID corresponding to username [%s]", username)); throw new IllegalStateException(format("Could not find ID corresponding to username [%s]", username));
@ -250,26 +271,27 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
* @param id the id of the required user * @param id the id of the required user
* @return the user * @return the user
*/ */
protected EndUser getUser(int id) { protected User getUser(int id) {
EndUser endUser = users().get(id); User user = users().get(id);
if (endUser == null) { if (user == null) {
throw new IllegalStateException(format("Could not find user corresponding to id [%d]", id)); throw new IllegalStateException(format("Could not find user corresponding to id [%d]", id));
} }
return endUser; return user;
} }
/** /**
* Gets the user with the specified username. If user was not found, the bot will send a message on Telegram. * Gets the user with the specified username. If user was not found, the bot will send a message on Telegram.
* *
* @param username the username of the required user * @param username the username of the required user
* @param ctx the message context with the originating user
* @return the id of the user * @return the id of the user
*/ */
protected int getUserIdSendError(String username, long chatId) { protected int getUserIdSendError(String username, MessageContext ctx) {
try { try {
return getUser(username).id(); return getUser(username).getId();
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
silent.send(format("Sorry, I could not find the user [%s].", username), chatId); silent.send(getLocalizedMessage(USER_NOT_FOUND, ctx.user().getLanguageCode(), username), ctx.chatId());
throw propagate(ex); throw propagate(ex);
} }
} }
@ -292,9 +314,9 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
*/ */
public Ability reportCommands() { public Ability reportCommands() {
return builder() return builder()
.name(COMMANDS) .name(REPORT)
.locality(ALL) .locality(ALL)
.privacy(PUBLIC) .privacy(CREATOR)
.input(0) .input(0)
.action(ctx -> { .action(ctx -> {
String commands = abilities.entrySet().stream() String commands = abilities.entrySet().stream()
@ -306,7 +328,64 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
}) })
.sorted() .sorted()
.reduce((a, b) -> format("%s%n%s", a, b)) .reduce((a, b) -> format("%s%n%s", a, b))
.orElse("No public commands found."); .orElse(getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode()));
silent.send(commands, ctx.chatId());
})
.build();
}
/**
* Default format:
* <p>
* PUBLIC
* <p>
* [command1] - [description1]
* <p>
* [command2] - [description2]
* <p>
* GROUP_ADMIN
* <p>
* [command1] - [description1]
* <p>
* ...
*
* @return the ability to print commands based on the privacy of the requesting user
*/
public Ability commands() {
return builder()
.name(COMMANDS)
.locality(USER)
.privacy(PUBLIC)
.input(0)
.action(ctx -> {
Privacy privacy = getPrivacy(ctx.update(), ctx.user().getId());
ListMultimap<Privacy, String> abilitiesPerPrivacy = abilities.entrySet().stream()
.map(entry -> {
String name = entry.getValue().name();
String info = entry.getValue().info();
if (!isEmpty(info))
return Pair.of(entry.getValue().privacy(), format("/%s - %s", name, info));
return Pair.of(entry.getValue().privacy(), format("/%s", name));
})
.sorted(comparing(Pair::b))
.collect(() -> hashKeys().arrayListValues().build(),
(map, pair) -> map.put(pair.a(), pair.b()),
Multimap::putAll);
String commands = abilitiesPerPrivacy.asMap().entrySet().stream()
.filter(entry -> privacy.compareTo(entry.getKey()) >= 0)
.sorted(comparing(Entry::getKey))
.map(entry ->
entry.getValue().stream()
.reduce(entry.getKey().toString(), (a, b) -> format("%s\n%s", a, b))
)
.collect(joining("\n"));
if (commands.isEmpty())
commands = getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode());
silent.send(commands, ctx.chatId()); silent.send(commands, ctx.chatId());
}) })
@ -361,23 +440,27 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
.locality(USER) .locality(USER)
.privacy(CREATOR) .privacy(CREATOR)
.input(0) .input(0)
.action(ctx -> silent.forceReply(RECOVERY_MESSAGE, ctx.chatId())) .action(ctx -> silent.forceReply(
getLocalizedMessage(ABILITY_RECOVER_MESSAGE, ctx.user().getLanguageCode()), ctx.chatId()))
.reply(update -> { .reply(update -> {
Long chatId = update.getMessage().getChatId(); String replyToMsg = update.getMessage().getReplyToMessage().getText();
String fileId = update.getMessage().getDocument().getFileId(); String recoverMessage = getLocalizedMessage(ABILITY_RECOVER_MESSAGE, AbilityUtils.getUser(update).getLanguageCode());
if (!replyToMsg.equals(recoverMessage))
return;
String fileId = update.getMessage().getDocument().getFileId();
try (FileReader reader = new FileReader(downloadFileWithId(fileId))) { try (FileReader reader = new FileReader(downloadFileWithId(fileId))) {
String backupData = IOUtils.toString(reader); String backupData = IOUtils.toString(reader);
if (db.recover(backupData)) { if (db.recover(backupData)) {
silent.send(RECOVER_SUCCESS, chatId); send(ABILITY_RECOVER_SUCCESS, update);
} else { } else {
silent.send("Oops, something went wrong during recovery.", chatId); send(ABILITY_RECOVER_FAIL, update);
} }
} catch (Exception e) { } catch (Exception e) {
BotLogger.error("Could not recover DB from backup", TAG, e); BotLogger.error("Could not recover DB from backup", TAG, e);
silent.send("I have failed to recover.", chatId); send(ABILITY_RECOVER_ERROR, update);
} }
}, MESSAGE, DOCUMENT, REPLY, isReplyTo(RECOVERY_MESSAGE)) }, MESSAGE, DOCUMENT, REPLY)
.build(); .build();
} }
@ -398,23 +481,23 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
.input(1) .input(1)
.action(ctx -> { .action(ctx -> {
String username = stripTag(ctx.firstArg()); String username = stripTag(ctx.firstArg());
int userId = getUserIdSendError(username, ctx.chatId()); int userId = getUserIdSendError(username, ctx);
String bannedUser; String bannedUser;
// Protection from abuse // Protection from abuse
if (userId == creatorId()) { if (userId == creatorId()) {
userId = ctx.user().id(); userId = ctx.user().getId();
bannedUser = isNullOrEmpty(ctx.user().username()) ? addTag(ctx.user().username()) : ctx.user().shortName(); bannedUser = isNullOrEmpty(ctx.user().getUserName()) ? addTag(ctx.user().getUserName()) : shortName(ctx.user());
} else { } else {
bannedUser = addTag(username); bannedUser = addTag(username);
} }
Set<Integer> blacklist = blacklist(); Set<Integer> blacklist = blacklist();
if (blacklist.contains(userId)) if (blacklist.contains(userId))
silent.sendMd(format("%s is already *banned*.", escape(bannedUser)), ctx.chatId()); sendMd(ABILITY_BAN_FAIL, ctx, escape(bannedUser));
else { else {
blacklist.add(userId); blacklist.add(userId);
silent.sendMd(format("%s is now *banned*.", escape(bannedUser)), ctx.chatId()); sendMd(ABILITY_BAN_SUCCESS, ctx, escape(bannedUser));
} }
}) })
.post(commitTo(db)) .post(commitTo(db))
@ -434,14 +517,14 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
.input(1) .input(1)
.action(ctx -> { .action(ctx -> {
String username = stripTag(ctx.firstArg()); String username = stripTag(ctx.firstArg());
Integer userId = getUserIdSendError(username, ctx.chatId()); Integer userId = getUserIdSendError(username, ctx);
Set<Integer> blacklist = blacklist(); Set<Integer> blacklist = blacklist();
if (!blacklist.remove(userId)) if (!blacklist.remove(userId))
silent.sendMd(format("@%s is *not* on the *blacklist*.", escape(username)), ctx.chatId()); silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_FAIL, ctx.user().getLanguageCode(), escape(username)), ctx.chatId());
else { else {
silent.sendMd(format("@%s, your ban has been *lifted*.", escape(username)), ctx.chatId()); silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_SUCCESS, ctx.user().getLanguageCode(), escape(username)), ctx.chatId());
} }
}) })
.post(commitTo(db)) .post(commitTo(db))
@ -459,14 +542,14 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
.input(1) .input(1)
.action(ctx -> { .action(ctx -> {
String username = stripTag(ctx.firstArg()); String username = stripTag(ctx.firstArg());
Integer userId = getUserIdSendError(username, ctx.chatId()); Integer userId = getUserIdSendError(username, ctx);
Set<Integer> admins = admins(); Set<Integer> admins = admins();
if (admins.contains(userId)) if (admins.contains(userId))
silent.sendMd(format("@%s is already an *admin*.", escape(username)), ctx.chatId()); sendMd(ABILITY_PROMOTE_FAIL, ctx, escape(username));
else { else {
admins.add(userId); admins.add(userId);
silent.sendMd(format("@%s has been *promoted*.", escape(username)), ctx.chatId()); sendMd(ABILITY_PROMOTE_SUCCESS, ctx, escape(username));
} }
}).post(commitTo(db)) }).post(commitTo(db))
.build(); .build();
@ -483,13 +566,13 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
.input(1) .input(1)
.action(ctx -> { .action(ctx -> {
String username = stripTag(ctx.firstArg()); String username = stripTag(ctx.firstArg());
Integer userId = getUserIdSendError(username, ctx.chatId()); Integer userId = getUserIdSendError(username, ctx);
Set<Integer> admins = admins(); Set<Integer> admins = admins();
if (admins.remove(userId)) { if (admins.remove(userId)) {
silent.sendMd(format("@%s has been *demoted*.", escape(username)), ctx.chatId()); sendMd(ABILITY_DEMOTE_SUCCESS, ctx, escape(username));
} else { } else {
silent.sendMd(format("@%s is *not* an *admin*.", escape(username)), ctx.chatId()); sendMd(ABILITY_DEMOTE_FAIL, ctx, escape(username));
} }
}) })
.post(commitTo(db)) .post(commitTo(db))
@ -505,29 +588,36 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
return builder() return builder()
.name(CLAIM) .name(CLAIM)
.locality(ALL) .locality(ALL)
.privacy(PUBLIC) .privacy(CREATOR)
.input(0) .input(0)
.action(ctx -> { .action(ctx -> {
if (ctx.user().id() == creatorId()) { Set<Integer> admins = admins();
Set<Integer> admins = admins(); int id = creatorId();
int id = creatorId();
long chatId = ctx.chatId();
if (admins.contains(id)) if (admins.contains(id))
silent.send("You're already my master.", chatId); send(ABILITY_CLAIM_FAIL, ctx);
else { else {
admins.add(id); admins.add(id);
silent.send("You're now my master.", chatId); send(ABILITY_CLAIM_SUCCESS, ctx);
}
} else {
// This is not a joke
abilities.get(BAN).action().accept(newContext(ctx.update(), ctx.user(), ctx.chatId(), ctx.user().username()));
} }
}) })
.post(commitTo(db)) .post(commitTo(db))
.build(); .build();
} }
private Optional<Message> send(String message, MessageContext ctx, String... args) {
return silent.send(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId());
}
private Optional<Message> sendMd(String message, MessageContext ctx, String... args) {
return silent.sendMd(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId());
}
private Optional<Message> send(String message, Update upd) {
Long chatId = upd.getMessage().getChatId();
return silent.send(getLocalizedMessage(message, AbilityUtils.getUser(upd).getLanguageCode()), chatId);
}
/** /**
* Registers the declared abilities using method reflection. Also, replies are accumulated using the built abilities and standalone methods that return a Reply. * Registers the declared abilities using method reflection. Also, replies are accumulated using the built abilities and standalone methods that return a Reply.
* <p> * <p>
@ -538,7 +628,10 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
abilities = stream(this.getClass().getMethods()) abilities = stream(this.getClass().getMethods())
.filter(method -> method.getReturnType().equals(Ability.class)) .filter(method -> method.getReturnType().equals(Ability.class))
.map(this::returnAbility) .map(this::returnAbility)
.collect(toMap(ability -> ability.name().toLowerCase(), identity())); .collect(ImmutableMap::<String, Ability>builder,
(b, a) -> b.put(a.name(), a),
(b1, b2) -> b1.putAll(b2.build()))
.build();
Stream<Reply> methodReplies = stream(this.getClass().getMethods()) Stream<Reply> methodReplies = stream(this.getClass().getMethods())
.filter(method -> method.getReturnType().equals(Reply.class)) .filter(method -> method.getReturnType().equals(Reply.class))
@ -547,7 +640,11 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
Stream<Reply> abilityReplies = abilities.values().stream() Stream<Reply> abilityReplies = abilities.values().stream()
.flatMap(ability -> ability.replies().stream()); .flatMap(ability -> ability.replies().stream());
replies = Stream.concat(methodReplies, abilityReplies).collect(toList()); replies = Stream.concat(methodReplies, abilityReplies).collect(
ImmutableList::<Reply>builder,
Builder::add,
(b1, b2) -> b1.addAll(b2.build()))
.build();
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
BotLogger.error(TAG, "Duplicate names found while registering abilities. Make sure that the abilities declared don't clash with the reserved ones.", e); BotLogger.error(TAG, "Duplicate names found while registering abilities. Make sure that the abilities declared don't clash with the reserved ones.", e);
throw propagate(e); throw propagate(e);
@ -597,7 +694,7 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
Pair<MessageContext, Ability> getContext(Trio<Update, Ability, String[]> trio) { Pair<MessageContext, Ability> getContext(Trio<Update, Ability, String[]> trio) {
Update update = trio.a(); Update update = trio.a();
EndUser user = fromUser(AbilityUtils.getUser(update)); User user = AbilityUtils.getUser(update);
return Pair.of(newContext(update, user, getChatId(update), trio.c()), trio.b()); return Pair.of(newContext(update, user, getChatId(update), trio.c()), trio.b());
} }
@ -615,7 +712,12 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
boolean isOk = abilityTokens == 0 || (tokens.length > 0 && tokens.length == abilityTokens); boolean isOk = abilityTokens == 0 || (tokens.length > 0 && tokens.length == abilityTokens);
if (!isOk) if (!isOk)
silent.send(format("Sorry, this feature requires %d additional %s.", abilityTokens, abilityTokens == 1 ? "input" : "inputs"), getChatId(trio.a())); silent.send(
getLocalizedMessage(
CHECK_INPUT_FAIL,
AbilityUtils.getUser(trio.a()).getLanguageCode(),
abilityTokens, abilityTokens == 1 ? "input" : "inputs"),
getChatId(trio.a()));
return isOk; return isOk;
} }
@ -627,30 +729,46 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
boolean isOk = abilityLocality == ALL || locality == abilityLocality; boolean isOk = abilityLocality == ALL || locality == abilityLocality;
if (!isOk) if (!isOk)
silent.send(format("Sorry, %s-only feature.", abilityLocality.toString().toLowerCase()), getChatId(trio.a())); silent.send(
getLocalizedMessage(
CHECK_LOCALITY_FAIL,
AbilityUtils.getUser(trio.a()).getLanguageCode(),
abilityLocality.toString().toLowerCase()),
getChatId(trio.a()));
return isOk; return isOk;
} }
boolean checkPrivacy(Trio<Update, Ability, String[]> trio) { boolean checkPrivacy(Trio<Update, Ability, String[]> trio) {
Update update = trio.a(); Update update = trio.a();
EndUser user = fromUser(AbilityUtils.getUser(update)); User user = AbilityUtils.getUser(update);
Privacy privacy; Privacy privacy;
int id = user.id(); int id = user.getId();
privacy = isCreator(id) ? CREATOR : isAdmin(id) ? ADMIN : isGroupAdmin(update, id)? GROUP_ADMIN : PUBLIC; privacy = getPrivacy(update, id);
boolean isOk = privacy.compareTo(trio.b().privacy()) >= 0; boolean isOk = privacy.compareTo(trio.b().privacy()) >= 0;
if (!isOk) if (!isOk)
silent.send("Sorry, you don't have the required access level to do that.", getChatId(trio.a())); silent.send(
getLocalizedMessage(
CHECK_PRIVACY_FAIL,
AbilityUtils.getUser(trio.a()).getLanguageCode()),
getChatId(trio.a()));
return isOk; return isOk;
} }
@NotNull
private Privacy getPrivacy(Update update, int id) {
return isCreator(id) ?
CREATOR : isAdmin(id) ?
ADMIN : (isGroupUpdate(update) || isSuperGroupUpdate(update)) && isGroupAdmin(update, id) ?
GROUP_ADMIN : PUBLIC;
}
private boolean isGroupAdmin(Update update, int id) { private boolean isGroupAdmin(Update update, int id) {
GetChatAdministrators admins = new GetChatAdministrators().setChatId(getChatId(update)); GetChatAdministrators admins = new GetChatAdministrators().setChatId(getChatId(update));
return isGroupUpdate(update) && silent.execute(admins) return silent.execute(admins)
.orElse(new ArrayList<>()).stream() .orElse(new ArrayList<>()).stream()
.anyMatch(member -> member.getUser().getId() == id); .anyMatch(member -> member.getUser().getId() == id);
} }
@ -694,9 +812,9 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
} }
Update addUser(Update update) { Update addUser(Update update) {
EndUser endUser = fromUser(AbilityUtils.getUser(update)); User endUser = AbilityUtils.getUser(update);
users().compute(endUser.id(), (id, user) -> { users().compute(endUser.getId(), (id, user) -> {
if (user == null) { if (user == null) {
updateUserId(user, endUser); updateUserId(user, endUser);
return endUser; return endUser;
@ -714,15 +832,15 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
return update; return update;
} }
private void updateUserId(EndUser oldUser, EndUser newUser) { private void updateUserId(User oldUser, User newUser) {
if (oldUser != null && oldUser.username() != null) { if (oldUser != null && oldUser.getUserName() != null) {
// Remove old username -> ID // Remove old username -> ID
userIds().remove(oldUser.username()); userIds().remove(oldUser.getUserName());
} }
if (newUser.username() != null) { if (newUser.getUserName() != null) {
// Add new mapping with the new username // Add new mapping with the new username
userIds().put(newUser.username().toLowerCase(), newUser.id()); userIds().put(newUser.getUserName().toLowerCase(), newUser.getId());
} }
} }
@ -750,6 +868,7 @@ public abstract class AbilityBot extends TelegramLongPollingBot {
return sender.downloadFile(sender.execute(new GetFile().setFileId(fileId))); return sender.downloadFile(sender.execute(new GetFile().setFileId(fileId)));
} }
private String escape(String username) { private String escape(String username) {
return username.replace("_", "\\_"); return username.replace("_", "\\_");
} }

View File

@ -39,6 +39,13 @@ public interface DBContext extends Closeable {
*/ */
<T> Set<T> getSet(String name); <T> Set<T> getSet(String name);
/**
* @param name the unique name of the {@link Var}
* @param <T> the type that the variable holds
* @return the variable with the specified name
*/
<T> Var<T> getVar(String name);
/** /**
* @return a high-level summary of the database structures (Sets, Lists, Maps, ...) present. * @return a high-level summary of the database structures (Sets, Lists, Maps, ...) present.
*/ */

View File

@ -3,6 +3,7 @@ package org.telegram.abilitybots.api.db;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.mapdb.Atomic;
import org.mapdb.DB; import org.mapdb.DB;
import org.mapdb.DBMaker; import org.mapdb.DBMaker;
import org.mapdb.Serializer; import org.mapdb.Serializer;
@ -93,6 +94,11 @@ public class MapDBContext implements DBContext {
return (Set<T>) db.<T>hashSet(name, JAVA).createOrOpen(); return (Set<T>) db.<T>hashSet(name, JAVA).createOrOpen();
} }
@Override
public <T> Var<T> getVar(String name) {
return new MapDBVar<>((Atomic.Var<T>) db.atomicVar(name).createOrOpen());
}
@Override @Override
public String summary() { public String summary() {
return stream(db.getAllNames().spliterator(), false) return stream(db.getAllNames().spliterator(), false)

View File

@ -0,0 +1,49 @@
package org.telegram.abilitybots.api.db;
import com.google.common.base.MoreObjects;
import org.mapdb.Atomic;
import java.util.Objects;
/**
* The MapDB variant for {@link DBContext#getVar(String)}.
*
* @param <T> the type of the inner variable
*/
public final class MapDBVar<T> implements Var<T> {
private Atomic.Var<T> var;
public MapDBVar(Atomic.Var<T> var) {
this.var = var;
}
@Override
public T get() {
return var.get();
}
@Override
public void set(T var) {
this.var.set(var);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MapDBVar<?> mapDBVar = (MapDBVar<?>) o;
return Objects.equals(var, mapDBVar.var);
}
@Override
public int hashCode() {
return Objects.hash(var);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("var", var)
.toString();
}
}

View File

@ -0,0 +1,19 @@
package org.telegram.abilitybots.api.db;
/**
* The interface governing a variable for abstract getters and setters.
* @param <T> the type of the variable
*
* @author Abbas Abou Daya
*/
public interface Var<T> {
/**
* @return the variable contained
*/
T get();
/**
* @param var the new variable value
*/
void set(T var);
}

View File

@ -1,138 +0,0 @@
package org.telegram.abilitybots.api.objects;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import org.telegram.telegrambots.meta.api.objects.User;
import java.io.Serializable;
import java.util.Objects;
import java.util.StringJoiner;
import static org.apache.commons.lang3.StringUtils.isEmpty;
/**
* This class serves the purpose of separating the basic Telegram {@link User} and the augmented {@link EndUser}.
* <p>
* It adds proper hashCode, equals, toString as well as useful utility methods such as {@link EndUser#shortName} and {@link EndUser#fullName}.
*
* @author Abbas Abou Daya
*/
public final class EndUser implements Serializable {
@JsonProperty("id")
private final Integer id;
@JsonProperty("firstName")
private final String firstName;
@JsonProperty("lastName")
private final String lastName;
@JsonProperty("username")
private final String username;
private EndUser(Integer id, String firstName, String lastName, String username) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.username = username;
}
@JsonCreator
public static EndUser endUser(@JsonProperty("id") Integer id,
@JsonProperty("firstName") String firstName,
@JsonProperty("lastName") String lastName,
@JsonProperty("username") String username) {
return new EndUser(id, firstName, lastName, username);
}
/**
* Constructs an {@link EndUser} from a {@link User}.
*
* @param user the Telegram user
* @return an augmented end-user
*/
public static EndUser fromUser(User user) {
return new EndUser(user.getId(), user.getFirstName(), user.getLastName(), user.getUserName());
}
public int id() {
return id;
}
public String firstName() {
return firstName;
}
public String lastName() {
return lastName;
}
public String username() {
return username;
}
/**
* The full name is identified as the concatenation of the first and last name, separated by a space.
* This method can return an empty name if both first and last name are empty.
*
* @return the full name of the user
*/
public String fullName() {
StringJoiner name = new StringJoiner(" ");
if (!isEmpty(firstName))
name.add(firstName);
if (!isEmpty(lastName))
name.add(lastName);
return name.toString();
}
/**
* The short name is one of the following:
* <ol>
* <li>First name</li>
* <li>Last name</li>
* <li>Username</li>
* </ol>
* The method will try to return the first valid name in the specified order.
*
* @return the short name of the user
*/
public String shortName() {
if (!isEmpty(firstName))
return firstName;
if (!isEmpty(lastName))
return lastName;
return username;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
EndUser endUser = (EndUser) o;
return Objects.equals(id, endUser.id) &&
Objects.equals(firstName, endUser.firstName) &&
Objects.equals(lastName, endUser.lastName) &&
Objects.equals(username, endUser.username);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, username);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("firstName", firstName)
.add("lastName", lastName)
.add("username", username)
.toString();
}
}

View File

@ -11,7 +11,7 @@ import static java.util.Objects.nonNull;
/** /**
* Flags are an conditions that are applied on an {@link Update}. * Flags are an conditions that are applied on an {@link Update}.
* <p> * <p>
* They can be used on {@link AbilityBuilder#flag(Flag...)} and on the post conditions in {@link AbilityBuilder#reply(Consumer, Predicate[])}. * They can be used on {@link AbilityBuilder#flag(Predicate[])} and on the post conditions in {@link AbilityBuilder#reply(Consumer, Predicate[])}.
* *
* @author Abbas Abou Daya * @author Abbas Abou Daya
*/ */

View File

@ -3,6 +3,7 @@ package org.telegram.abilitybots.api.objects;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import java.util.Arrays; import java.util.Arrays;
@ -14,26 +15,26 @@ import java.util.Arrays;
* @author Abbas Abou Daya * @author Abbas Abou Daya
*/ */
public class MessageContext { public class MessageContext {
private final EndUser user; private final User user;
private final Long chatId; private final Long chatId;
private final String[] arguments; private final String[] arguments;
private final Update update; private final Update update;
private MessageContext(Update update, EndUser user, Long chatId, String[] arguments) { private MessageContext(Update update, User user, Long chatId, String[] arguments) {
this.user = user; this.user = user;
this.chatId = chatId; this.chatId = chatId;
this.update = update; this.update = update;
this.arguments = arguments; this.arguments = arguments;
} }
public static MessageContext newContext(Update update, EndUser user, Long chatId, String... arguments) { public static MessageContext newContext(Update update, User user, Long chatId, String... arguments) {
return new MessageContext(update, user, chatId, arguments); return new MessageContext(update, user, chatId, arguments);
} }
/** /**
* @return the originating Telegram user of this update * @return the originating Telegram user of this update
*/ */
public EndUser user() { public User user() {
return user; return user;
} }

View File

@ -14,7 +14,7 @@ import static java.util.Arrays.asList;
/** /**
* A reply consists of update conditionals and an action to be applied on the update. * A reply consists of update conditionals and an action to be applied on the update.
* <p> * <p>
* If an update satisfies the {@link Reply#conditions}set by the reply, then it's safe to {@link Reply#actOn(Update)}. * If an update satisfies the {@link Reply#conditions} set by the reply, then it's safe to {@link Reply#actOn(Update)}.
* *
* @author Abbas Abou Daya * @author Abbas Abou Daya
*/ */

View File

@ -0,0 +1,31 @@
package org.telegram.abilitybots.api.util;
public final class AbilityMessageCodes {
public static String USER_NOT_FOUND = "userNotFound";
public static String CHECK_INPUT_FAIL = "checkInput.fail";
public static String CHECK_LOCALITY_FAIL = "checkLocality.fail";
public static String CHECK_PRIVACY_FAIL = "checkPrivacy.fail";
public static String ABILITY_COMMANDS_NOT_FOUND = "ability.commands.notFound";
public static String ABILITY_RECOVER_SUCCESS = "ability.recover.success";
public static String ABILITY_RECOVER_FAIL = "ability.recover.fail";
public static String ABILITY_RECOVER_MESSAGE = "ability.recover.message";
public static String ABILITY_RECOVER_ERROR = "ability.recover.error";
public static String ABILITY_BAN_SUCCESS = "ability.ban.success";
public static String ABILITY_BAN_FAIL = "ability.ban.fail";
public static String ABILITY_UNBAN_SUCCESS = "ability.unban.success";
public static String ABILITY_UNBAN_FAIL = "ability.unban.fail";
public static String ABILITY_PROMOTE_SUCCESS = "ability.promote.success";
public static String ABILITY_PROMOTE_FAIL = "ability.promote.fail";
public static String ABILITY_DEMOTE_SUCCESS = "ability.demote.success";
public static String ABILITY_DEMOTE_FAIL = "ability.demote.fail";
public static String ABILITY_CLAIM_SUCCESS = "ability.claim.success";
public static String ABILITY_CLAIM_FAIL = "ability.claim.fail";
}

View File

@ -1,13 +1,23 @@
package org.telegram.abilitybots.api.util; package org.telegram.abilitybots.api.util;
import com.google.common.base.Strings;
import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.MessageContext; import org.telegram.abilitybots.api.objects.MessageContext;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User; import org.telegram.telegrambots.meta.api.objects.User;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringJoiner;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import static java.util.ResourceBundle.Control.FORMAT_PROPERTIES;
import static java.util.ResourceBundle.Control.getNoFallbackControl;
import static java.util.ResourceBundle.getBundle;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.telegram.abilitybots.api.objects.Flag.*; import static org.telegram.abilitybots.api.objects.Flag.*;
/** /**
@ -86,6 +96,28 @@ public final class AbilityUtils {
} }
} }
/**
* A "best-effort" boolean stating whether the update is a super-group message or not.
*
* @param update a Telegram {@link Update}
* @return whether the update is linked to a group
*/
public static boolean isSuperGroupUpdate(Update update) {
if (MESSAGE.test(update)) {
return update.getMessage().isSuperGroupMessage();
} else if (CALLBACK_QUERY.test(update)) {
return update.getCallbackQuery().getMessage().isSuperGroupMessage();
} else if (CHANNEL_POST.test(update)) {
return update.getChannelPost().isSuperGroupMessage();
} else if (EDITED_CHANNEL_POST.test(update)) {
return update.getEditedChannelPost().isSuperGroupMessage();
} else if (EDITED_MESSAGE.test(update)) {
return update.getEditedMessage().isSuperGroupMessage();
} else {
return false;
}
}
/** /**
* Fetches the direct chat ID of the specified update. * Fetches the direct chat ID of the specified update.
* *
@ -150,4 +182,66 @@ public final class AbilityUtils {
public static Predicate<Update> isReplyTo(String msg) { public static Predicate<Update> isReplyTo(String msg) {
return update -> update.getMessage().getReplyToMessage().getText().equals(msg); return update -> update.getMessage().getReplyToMessage().getText().equals(msg);
} }
}
public static String getLocalizedMessage(String messageCode, Locale locale, Object...arguments) {
ResourceBundle bundle;
if (locale == null) {
bundle = getBundle("messages", Locale.ROOT);
} else {
try {
bundle = getBundle(
"messages",
locale,
getNoFallbackControl(FORMAT_PROPERTIES));
} catch (MissingResourceException e) {
bundle = getBundle("messages", Locale.ROOT);
}
}
String message = bundle.getString(messageCode);
return MessageFormat.format(message, arguments);
}
public static String getLocalizedMessage(String messageCode, String languageCode, Object...arguments){
Locale locale = Strings.isNullOrEmpty(languageCode) ? null : Locale.forLanguageTag(languageCode);
return getLocalizedMessage(messageCode, locale, arguments);
}
/**
* The short name is one of the following:
* <ol>
* <li>First name</li>
* <li>Last name</li>
* <li>Username</li>
* </ol>
* The method will try to return the first valid name in the specified order.
*
* @return the short name of the user
*/
public static String shortName(User user) {
if (!isEmpty(user.getFirstName()))
return user.getFirstName();
if (!isEmpty(user.getLastName()))
return user.getLastName();
return user.getUserName();
}
/**
* The full name is identified as the concatenation of the first and last name, separated by a space.
* This method can return an empty name if both first and last name are empty.
*
* @return the full name of the user
* @param user
*/
public static String fullName(User user) {
StringJoiner name = new StringJoiner(" ");
if (!isEmpty(user.getFirstName()))
name.add(user.getFirstName());
if (!isEmpty(user.getLastName()))
name.add(user.getLastName());
return name.toString();
}
}

View File

@ -0,0 +1,27 @@
ability.commands.notFound=No available commands found.
ability.recover.success=I have successfully recovered.
ability.recover.fail=Oops, something went wrong during recovery.
ability.recover.message=I am ready to receive the backup file. Please reply to this message with the backup file attached.
ability.recover.error=I have failed to recover.
ability.ban.success={0} is now *banned*.
ability.ban.fail={0} is already *banned*.
ability.unban.success=@{0}, your ban has been *lifted*.
ability.unban.fail=@{0} is *not* on the *blacklist*.
ability.promote.success=@{0} has been *promoted*.
ability.promote.fail=@{0} is already an *admin*.
ability.demote.success=@{0} has been *demoted*.
ability.demote.fail=@{0} is *not* an *admin*.
ability.claim.success=You''re now my master.
ability.claim.fail=You''re already my master.
checkInput.fail=Sorry, this feature requires {0,number,integer} additional {1}.
checkLocality.fail=Sorry, {0}-only feature.
checkPrivacy.fail=Sorry, you don''t have the required access level to do that.
userNotFound=Sorry, I could not find the user [{0}].

View File

@ -0,0 +1,81 @@
package org.telegram.abilitybots.api.bot;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.MessageContext;
import org.telegram.abilitybots.api.sender.MessageSender;
import org.telegram.abilitybots.api.sender.SilentSender;
import org.telegram.telegrambots.api.objects.User;
import java.io.IOException;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import static org.telegram.abilitybots.api.bot.AbilityBotTest.mockContext;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
public class AbilityBotI18nTest {
private static final User NO_LANGUAGE_USER = new User(1, "first", false, "last", "username", null);
private static final User ITALIAN_USER = new User(2, "first", false, "last", "username", "it-IT");
private DBContext db;
private NoPublicCommandsBot bot;
private MessageSender sender;
private SilentSender silent;
@Before
public void setUp() {
db = offlineInstance("db");
bot = new NoPublicCommandsBot(EMPTY, EMPTY, db);
sender = mock(MessageSender.class);
silent = mock(SilentSender.class);
bot.sender = sender;
bot.silent = silent;
}
@Test
public void missingPublicCommandsLocalizedInEnglishByDefault() {
MessageContext context = mockContext(NO_LANGUAGE_USER);
bot.reportCommands().action().accept(context);
verify(silent, times(1))
.send("No available commands found.", NO_LANGUAGE_USER.getId());
}
@Test
public void missingPublicCommandsLocalizedInItalian() {
MessageContext context = mockContext(ITALIAN_USER);
bot.reportCommands().action().accept(context);
verify(silent, times(1))
.send("Non sono presenti comandi disponibile.", ITALIAN_USER.getId());
}
@After
public void tearDown() throws IOException {
db.clear();
db.close();
}
public static class NoPublicCommandsBot extends AbilityBot {
protected NoPublicCommandsBot(String botToken, String botUsername, DBContext db) {
super(botToken, botUsername, db);
}
@Override
public int creatorId() {
return 1;
}
}
}

View File

@ -36,11 +36,8 @@ import static org.junit.Assert.*;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.mockito.internal.verification.VerificationModeFactory.times;
import static org.telegram.abilitybots.api.bot.AbilityBot.RECOVERY_MESSAGE;
import static org.telegram.abilitybots.api.bot.AbilityBot.RECOVER_SUCCESS;
import static org.telegram.abilitybots.api.bot.DefaultBot.getDefaultBuilder; import static org.telegram.abilitybots.api.bot.DefaultBot.getDefaultBuilder;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
import static org.telegram.abilitybots.api.objects.EndUser.endUser;
import static org.telegram.abilitybots.api.objects.Flag.DOCUMENT; import static org.telegram.abilitybots.api.objects.Flag.DOCUMENT;
import static org.telegram.abilitybots.api.objects.Flag.MESSAGE; import static org.telegram.abilitybots.api.objects.Flag.MESSAGE;
import static org.telegram.abilitybots.api.objects.Locality.ALL; import static org.telegram.abilitybots.api.objects.Locality.ALL;
@ -49,12 +46,16 @@ import static org.telegram.abilitybots.api.objects.MessageContext.newContext;
import static org.telegram.abilitybots.api.objects.Privacy.*; import static org.telegram.abilitybots.api.objects.Privacy.*;
public class AbilityBotTest { public class AbilityBotTest {
// Messages
private static final String RECOVERY_MESSAGE = "I am ready to receive the backup file. Please reply to this message with the backup file attached.";
private static final String RECOVER_SUCCESS = "I have successfully recovered.";
private static final String[] EMPTY_ARRAY = {}; private static final String[] EMPTY_ARRAY = {};
private static final long GROUP_ID = 10L; private static final long GROUP_ID = 10L;
private static final String TEST = "test"; private static final String TEST = "test";
private static final String[] TEXT = {TEST}; private static final String[] TEXT = {TEST};
public static final EndUser MUSER = endUser(1, "first", "last", "username"); public static final User USER = new User(1, "first", false, "last", "username", null);
public static final EndUser CREATOR = endUser(1337, "creatorFirst", "creatorLast", "creatorUsername"); public static final User CREATOR = new User(1337, "creatorFirst", false, "creatorLast", "creatorUsername", null);
private DefaultBot bot; private DefaultBot bot;
private DBContext db; private DBContext db;
@ -75,39 +76,39 @@ public class AbilityBotTest {
@Test @Test
public void sendsPrivacyViolation() { public void sendsPrivacyViolation() {
Update update = mockFullUpdate(MUSER, "/admin"); Update update = mockFullUpdate(USER, "/admin");
bot.onUpdateReceived(update); bot.onUpdateReceived(update);
verify(silent, times(1)).send("Sorry, you don't have the required access level to do that.", MUSER.id()); verify(silent, times(1)).send("Sorry, you don't have the required access level to do that.", USER.getId());
} }
@Test @Test
public void sendsLocalityViolation() { public void sendsLocalityViolation() {
Update update = mockFullUpdate(MUSER, "/group"); Update update = mockFullUpdate(USER, "/group");
bot.onUpdateReceived(update); bot.onUpdateReceived(update);
verify(silent, times(1)).send(format("Sorry, %s-only feature.", "group"), MUSER.id()); verify(silent, times(1)).send(format("Sorry, %s-only feature.", "group"), USER.getId());
} }
@Test @Test
public void sendsInputArgsViolation() { public void sendsInputArgsViolation() {
Update update = mockFullUpdate(MUSER, "/count 1 2 3"); Update update = mockFullUpdate(USER, "/count 1 2 3");
bot.onUpdateReceived(update); bot.onUpdateReceived(update);
verify(silent, times(1)).send(format("Sorry, this feature requires %d additional inputs.", 4), MUSER.id()); verify(silent, times(1)).send(format("Sorry, this feature requires %d additional inputs.", 4), USER.getId());
} }
@Test @Test
public void canProcessRepliesIfSatisfyRequirements() { public void canProcessRepliesIfSatisfyRequirements() {
Update update = mockFullUpdate(MUSER, "must reply"); Update update = mockFullUpdate(USER, "must reply");
// False means the update was not pushed down the stream since it has been consumed by the reply // False means the update was not pushed down the stream since it has been consumed by the reply
assertFalse(bot.filterReply(update)); assertFalse(bot.filterReply(update));
verify(silent, times(1)).send("reply", MUSER.id()); verify(silent, times(1)).send("reply", USER.getId());
} }
@Test @Test
@ -144,8 +145,8 @@ public class AbilityBotTest {
@Test @Test
public void canDemote() { public void canDemote() {
addUsers(MUSER); addUsers(USER);
bot.admins().add(MUSER.id()); bot.admins().add(USER.getId());
MessageContext context = defaultContext(); MessageContext context = defaultContext();
@ -158,33 +159,33 @@ public class AbilityBotTest {
@Test @Test
public void canPromote() { public void canPromote() {
addUsers(MUSER); addUsers(USER);
MessageContext context = defaultContext(); MessageContext context = defaultContext();
bot.promoteAdmin().action().accept(context); bot.promoteAdmin().action().accept(context);
Set<Integer> actual = bot.admins(); Set<Integer> actual = bot.admins();
Set<Integer> expected = newHashSet(MUSER.id()); Set<Integer> expected = newHashSet(USER.getId());
assertEquals("Could not sudo user", expected, actual); assertEquals("Could not sudo user", expected, actual);
} }
@Test @Test
public void canBanUser() { public void canBanUser() {
addUsers(MUSER); addUsers(USER);
MessageContext context = defaultContext(); MessageContext context = defaultContext();
bot.banUser().action().accept(context); bot.banUser().action().accept(context);
Set<Integer> actual = bot.blacklist(); Set<Integer> actual = bot.blacklist();
Set<Integer> expected = newHashSet(MUSER.id()); Set<Integer> expected = newHashSet(USER.getId());
assertEquals("The ban was not emplaced", expected, actual); assertEquals("The ban was not emplaced", expected, actual);
} }
@Test @Test
public void canUnbanUser() { public void canUnbanUser() {
addUsers(MUSER); addUsers(USER);
bot.blacklist().add(MUSER.id()); bot.blacklist().add(USER.getId());
MessageContext context = defaultContext(); MessageContext context = defaultContext();
@ -197,65 +198,42 @@ public class AbilityBotTest {
@NotNull @NotNull
private MessageContext defaultContext() { private MessageContext defaultContext() {
MessageContext context = mock(MessageContext.class); return mockContext(CREATOR, GROUP_ID, USER.getUserName());
when(context.user()).thenReturn(CREATOR);
when(context.firstArg()).thenReturn(MUSER.username());
return context;
} }
@Test @Test
public void cannotBanCreator() { public void cannotBanCreator() {
addUsers(MUSER, CREATOR); addUsers(USER, CREATOR);
MessageContext context = mock(MessageContext.class); MessageContext context = mockContext(USER, GROUP_ID, CREATOR.getUserName());
when(context.user()).thenReturn(MUSER);
when(context.firstArg()).thenReturn(CREATOR.username());
bot.banUser().action().accept(context); bot.banUser().action().accept(context);
Set<Integer> actual = bot.blacklist(); Set<Integer> actual = bot.blacklist();
Set<Integer> expected = newHashSet(MUSER.id()); Set<Integer> expected = newHashSet(USER.getId());
assertEquals("Impostor was not added to the blacklist", expected, actual); assertEquals("Impostor was not added to the blacklist", expected, actual);
} }
private void addUsers(EndUser... users) { private void addUsers(User... users) {
Arrays.stream(users).forEach(user -> { Arrays.stream(users).forEach(user -> {
bot.users().put(user.id(), user); bot.users().put(user.getId(), user);
bot.userIds().put(user.username().toLowerCase(), user.id()); bot.userIds().put(user.getUserName().toLowerCase(), user.getId());
}); });
} }
@Test @Test
public void creatorCanClaimBot() { public void creatorCanClaimBot() {
MessageContext context = mock(MessageContext.class); MessageContext context = mockContext(CREATOR, GROUP_ID);
when(context.user()).thenReturn(CREATOR);
bot.claimCreator().action().accept(context); bot.claimCreator().action().accept(context);
Set<Integer> actual = bot.admins(); Set<Integer> actual = bot.admins();
Set<Integer> expected = newHashSet(CREATOR.id()); Set<Integer> expected = newHashSet(CREATOR.getId());
assertEquals("Creator was not properly added to the super admins set", expected, actual); assertEquals("Creator was not properly added to the super admins set", expected, actual);
} }
@Test
public void userGetsBannedIfClaimsBot() {
addUsers(MUSER);
MessageContext context = mock(MessageContext.class);
when(context.user()).thenReturn(MUSER);
bot.claimCreator().action().accept(context);
Set<Integer> actual = bot.blacklist();
Set<Integer> expected = newHashSet(MUSER.id());
assertEquals("Could not find user on the blacklist", expected, actual);
actual = bot.admins();
expected = emptySet();
assertEquals("Admins set is not empty", expected, actual);
}
@Test @Test
public void bannedCreatorPassesBlacklistCheck() { public void bannedCreatorPassesBlacklistCheck() {
bot.blacklist().add(CREATOR.id()); bot.blacklist().add(CREATOR.getId());
Update update = mock(Update.class); Update update = mock(Update.class);
Message message = mock(Message.class); Message message = mock(Message.class);
User user = mock(User.class); User user = mock(User.class);
@ -270,37 +248,35 @@ public class AbilityBotTest {
public void canAddUser() { public void canAddUser() {
Update update = mock(Update.class); Update update = mock(Update.class);
Message message = mock(Message.class); Message message = mock(Message.class);
User user = mock(User.class);
mockAlternateUser(update, message, user, MUSER); mockAlternateUser(update, message, USER);
bot.addUser(update); bot.addUser(update);
Map<String, Integer> expectedUserIds = ImmutableMap.of(MUSER.username(), MUSER.id()); Map<String, Integer> expectedUserIds = ImmutableMap.of(USER.getUserName(), USER.getId());
Map<Integer, EndUser> expectedUsers = ImmutableMap.of(MUSER.id(), MUSER); Map<Integer, User> expectedUsers = ImmutableMap.of(USER.getId(), USER);
assertEquals("User was not added", expectedUserIds, bot.userIds()); assertEquals("User was not added", expectedUserIds, bot.userIds());
assertEquals("User was not added", expectedUsers, bot.users()); assertEquals("User was not added", expectedUsers, bot.users());
} }
@Test @Test
public void canEditUser() { public void canEditUser() {
addUsers(MUSER); addUsers(USER);
Update update = mock(Update.class); Update update = mock(Update.class);
Message message = mock(Message.class); Message message = mock(Message.class);
User user = mock(User.class);
String newUsername = MUSER.username() + "-test"; String newUsername = USER.getUserName() + "-test";
String newFirstName = MUSER.firstName() + "-test"; String newFirstName = USER.getFirstName() + "-test";
String newLastName = MUSER.lastName() + "-test"; String newLastName = USER.getLastName() + "-test";
int sameId = MUSER.id(); int sameId = USER.getId();
EndUser changedUser = endUser(sameId, newFirstName, newLastName, newUsername); User changedUser = new User(sameId, newFirstName, false, newLastName, newUsername, null);
mockAlternateUser(update, message, user, changedUser); mockAlternateUser(update, message, changedUser);
bot.addUser(update); bot.addUser(update);
Map<String, Integer> expectedUserIds = ImmutableMap.of(changedUser.username(), changedUser.id()); Map<String, Integer> expectedUserIds = ImmutableMap.of(changedUser.getUserName(), changedUser.getId());
Map<Integer, EndUser> expectedUsers = ImmutableMap.of(changedUser.id(), changedUser); Map<Integer, User> expectedUsers = ImmutableMap.of(changedUser.getId(), changedUser);
assertEquals("User was not properly edited", bot.userIds(), expectedUserIds); assertEquals("User was not properly edited", bot.userIds(), expectedUserIds);
assertEquals("User was not properly edited", expectedUsers, expectedUsers); assertEquals("User was not properly edited", expectedUsers, expectedUsers);
} }
@ -311,13 +287,13 @@ public class AbilityBotTest {
Ability validAbility = getDefaultBuilder().build(); Ability validAbility = getDefaultBuilder().build();
Trio<Update, Ability, String[]> validPair = Trio.of(null, validAbility, null); Trio<Update, Ability, String[]> validPair = Trio.of(null, validAbility, null);
assertEquals("Bot can't validate ability properly", false, bot.validateAbility(invalidPair)); assertFalse("Bot can't validate ability properly", bot.validateAbility(invalidPair));
assertEquals("Bot can't validate ability properly", true, bot.validateAbility(validPair)); assertTrue("Bot can't validate ability properly", bot.validateAbility(validPair));
} }
@Test @Test
public void canCheckInput() { public void canCheckInput() {
Update update = mockFullUpdate(MUSER, "/something"); Update update = mockFullUpdate(USER, "/something");
Ability abilityWithOneInput = getDefaultBuilder() Ability abilityWithOneInput = getDefaultBuilder()
.build(); .build();
Ability abilityWithZeroInput = getDefaultBuilder() Ability abilityWithZeroInput = getDefaultBuilder()
@ -327,15 +303,15 @@ public class AbilityBotTest {
Trio<Update, Ability, String[]> trioOneArg = Trio.of(update, abilityWithOneInput, TEXT); Trio<Update, Ability, String[]> trioOneArg = Trio.of(update, abilityWithOneInput, TEXT);
Trio<Update, Ability, String[]> trioZeroArg = Trio.of(update, abilityWithZeroInput, TEXT); Trio<Update, Ability, String[]> trioZeroArg = Trio.of(update, abilityWithZeroInput, TEXT);
assertEquals("Unexpected result when applying token filter", true, bot.checkInput(trioOneArg)); assertTrue("Unexpected result when applying token filter", bot.checkInput(trioOneArg));
trioOneArg = Trio.of(update, abilityWithOneInput, addAll(TEXT, TEXT)); trioOneArg = Trio.of(update, abilityWithOneInput, addAll(TEXT, TEXT));
assertEquals("Unexpected result when applying token filter", false, bot.checkInput(trioOneArg)); assertFalse("Unexpected result when applying token filter", bot.checkInput(trioOneArg));
assertEquals("Unexpected result when applying token filter", true, bot.checkInput(trioZeroArg)); assertTrue("Unexpected result when applying token filter", bot.checkInput(trioZeroArg));
trioZeroArg = Trio.of(update, abilityWithZeroInput, EMPTY_ARRAY); trioZeroArg = Trio.of(update, abilityWithZeroInput, EMPTY_ARRAY);
assertEquals("Unexpected result when applying token filter", true, bot.checkInput(trioZeroArg)); assertTrue("Unexpected result when applying token filter", bot.checkInput(trioZeroArg));
} }
@Test @Test
@ -355,14 +331,14 @@ public class AbilityBotTest {
mockUser(update, message, user); mockUser(update, message, user);
assertEquals("Unexpected result when checking for privacy", true, bot.checkPrivacy(publicTrio)); assertTrue("Unexpected result when checking for privacy", bot.checkPrivacy(publicTrio));
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(groupAdminTrio)); assertFalse("Unexpected result when checking for privacy", bot.checkPrivacy(groupAdminTrio));
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(adminTrio)); assertFalse("Unexpected result when checking for privacy", bot.checkPrivacy(adminTrio));
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(creatorTrio)); assertFalse("Unexpected result when checking for privacy", bot.checkPrivacy(creatorTrio));
} }
@Test @Test
public void canValidateGroupAdminPrivacy() throws TelegramApiException { public void canValidateGroupAdminPrivacy() {
Update update = mock(Update.class); Update update = mock(Update.class);
Message message = mock(Message.class); Message message = mock(Message.class);
User user = mock(User.class); User user = mock(User.class);
@ -379,11 +355,11 @@ public class AbilityBotTest {
when(silent.execute(any(GetChatAdministrators.class))).thenReturn(Optional.of(newArrayList(member))); when(silent.execute(any(GetChatAdministrators.class))).thenReturn(Optional.of(newArrayList(member)));
assertEquals("Unexpected result when checking for privacy", true, bot.checkPrivacy(groupAdminTrio)); assertTrue("Unexpected result when checking for privacy", bot.checkPrivacy(groupAdminTrio));
} }
@Test @Test
public void canRestrictNormalUsersFromGroupAdminAbilities() throws TelegramApiException { public void canRestrictNormalUsersFromGroupAdminAbilities() {
Update update = mock(Update.class); Update update = mock(Update.class);
Message message = mock(Message.class); Message message = mock(Message.class);
User user = mock(User.class); User user = mock(User.class);
@ -396,7 +372,7 @@ public class AbilityBotTest {
when(silent.execute(any(GetChatAdministrators.class))).thenReturn(empty()); when(silent.execute(any(GetChatAdministrators.class))).thenReturn(empty());
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(groupAdminTrio)); assertFalse("Unexpected result when checking for privacy", bot.checkPrivacy(groupAdminTrio));
} }
@Test @Test
@ -408,10 +384,10 @@ public class AbilityBotTest {
Trio<Update, Ability, String[]> creatorTrio = Trio.of(update, creatorAbility, TEXT); Trio<Update, Ability, String[]> creatorTrio = Trio.of(update, creatorAbility, TEXT);
bot.admins().add(MUSER.id()); bot.admins().add(USER.getId());
mockUser(update, message, user); mockUser(update, message, user);
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(creatorTrio)); assertFalse("Unexpected result when checking for privacy", bot.checkPrivacy(creatorTrio));
} }
@Test @Test
@ -430,24 +406,23 @@ public class AbilityBotTest {
mockUser(update, message, user); mockUser(update, message, user);
when(message.isUserMessage()).thenReturn(true); when(message.isUserMessage()).thenReturn(true);
assertEquals("Unexpected result when checking for locality", true, bot.checkLocality(publicTrio)); assertTrue("Unexpected result when checking for locality", bot.checkLocality(publicTrio));
assertEquals("Unexpected result when checking for locality", true, bot.checkLocality(userTrio)); assertTrue("Unexpected result when checking for locality", bot.checkLocality(userTrio));
assertEquals("Unexpected result when checking for locality", false, bot.checkLocality(groupTrio)); assertFalse("Unexpected result when checking for locality", bot.checkLocality(groupTrio));
} }
@Test @Test
public void canRetrieveContext() { public void canRetrieveContext() {
Update update = mock(Update.class); Update update = mock(Update.class);
Message message = mock(Message.class); Message message = mock(Message.class);
User user = mock(User.class);
Ability ability = getDefaultBuilder().build(); Ability ability = getDefaultBuilder().build();
Trio<Update, Ability, String[]> trio = Trio.of(update, ability, TEXT); Trio<Update, Ability, String[]> trio = Trio.of(update, ability, TEXT);
when(message.getChatId()).thenReturn(GROUP_ID); when(message.getChatId()).thenReturn(GROUP_ID);
mockUser(update, message, user); mockUser(update, message, USER);
Pair<MessageContext, Ability> actualPair = bot.getContext(trio); Pair<MessageContext, Ability> actualPair = bot.getContext(trio);
Pair<MessageContext, Ability> expectedPair = Pair.of(newContext(update, MUSER, GROUP_ID, TEXT), ability); Pair<MessageContext, Ability> expectedPair = Pair.of(newContext(update, USER, GROUP_ID, TEXT), ability);
assertEquals("Unexpected result when fetching for context", expectedPair, actualPair); assertEquals("Unexpected result when fetching for context", expectedPair, actualPair);
} }
@ -455,7 +430,7 @@ public class AbilityBotTest {
@Test @Test
public void defaultGlobalFlagIsTrue() { public void defaultGlobalFlagIsTrue() {
Update update = mock(Update.class); Update update = mock(Update.class);
assertEquals("Unexpected result when checking for the default global flags", true, bot.checkGlobalFlags(update)); assertTrue("Unexpected result when checking for the default global flags", bot.checkGlobalFlags(update));
} }
@Test(expected = ArithmeticException.class) @Test(expected = ArithmeticException.class)
@ -542,24 +517,69 @@ public class AbilityBotTest {
Trio<Update, Ability, String[]> docTrio = Trio.of(update, documentAbility, TEXT); Trio<Update, Ability, String[]> docTrio = Trio.of(update, documentAbility, TEXT);
Trio<Update, Ability, String[]> textTrio = Trio.of(update, textAbility, TEXT); Trio<Update, Ability, String[]> textTrio = Trio.of(update, textAbility, TEXT);
assertEquals("Unexpected result when checking for message flags", false, bot.checkMessageFlags(docTrio)); assertFalse("Unexpected result when checking for message flags", bot.checkMessageFlags(docTrio));
assertEquals("Unexpected result when checking for message flags", true, bot.checkMessageFlags(textTrio)); assertTrue("Unexpected result when checking for message flags", bot.checkMessageFlags(textTrio));
} }
@Test @Test
public void canReportCommands() { public void canReportCommands() {
MessageContext context = mockContext(USER, GROUP_ID);
bot.reportCommands().action().accept(context);
verify(silent, times(1)).send("default - dis iz default command", GROUP_ID);
}
@NotNull
public static MessageContext mockContext(User user) {
return mockContext(user, user.getId());
}
@NotNull
public static MessageContext mockContext(User user, long groupId, String... args) {
Update update = mock(Update.class);
Message message = mock(Message.class);
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
when(message.getFrom()).thenReturn(user);
when(message.hasText()).thenReturn(true);
return newContext(update, user, groupId, args);
}
@Test
public void canPrintCommandsBasedOnPrivacy() {
Update update = mock(Update.class); Update update = mock(Update.class);
Message message = mock(Message.class); Message message = mock(Message.class);
when(update.hasMessage()).thenReturn(true); when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message); when(update.getMessage()).thenReturn(message);
when(message.hasText()).thenReturn(true); when(message.hasText()).thenReturn(true);
MessageContext context = mock(MessageContext.class); MessageContext creatorCtx = newContext(update, CREATOR, GROUP_ID);
when(context.chatId()).thenReturn(GROUP_ID);
bot.reportCommands().action().accept(context); bot.commands().action().accept(creatorCtx);
verify(silent, times(1)).send("default - dis iz default command", GROUP_ID); String expected = "PUBLIC\n/commands\n/count\n/default - dis iz default command\n/group\n/test\nADMIN\n/admin\n/ban\n/demote\n/promote\n/unban\nCREATOR\n/backup\n/claim\n/recover\n/report";
verify(silent, times(1)).send(expected, GROUP_ID);
}
@Test
public void printsOnlyPublicCommandsForNormalUser() {
Update update = mock(Update.class);
Message message = mock(Message.class);
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
when(message.hasText()).thenReturn(true);
MessageContext userCtx = newContext(update, USER, GROUP_ID);
bot.commands().action().accept(userCtx);
String expected = "PUBLIC\n/commands\n/count\n/default - dis iz default command\n/group\n/test";
verify(silent, times(1)).send(expected, GROUP_ID);
} }
@After @After
@ -568,26 +588,14 @@ public class AbilityBotTest {
db.close(); db.close();
} }
private User mockUser(EndUser fromUser) {
User user = mock(User.class);
when(user.getId()).thenReturn(fromUser.id());
when(user.getUserName()).thenReturn(fromUser.username());
when(user.getFirstName()).thenReturn(fromUser.firstName());
when(user.getLastName()).thenReturn(fromUser.lastName());
return user;
}
@NotNull @NotNull
private Update mockFullUpdate(EndUser fromUser, String args) { private Update mockFullUpdate(User user, String args) {
bot.users().put(MUSER.id(), MUSER); bot.users().put(USER.getId(), USER);
bot.users().put(CREATOR.id(), CREATOR); bot.users().put(CREATOR.getId(), CREATOR);
bot.userIds().put(CREATOR.username(), CREATOR.id()); bot.userIds().put(CREATOR.getUserName(), CREATOR.getId());
bot.userIds().put(MUSER.username(), MUSER.id()); bot.userIds().put(USER.getUserName(), USER.getId());
bot.admins().add(CREATOR.id()); bot.admins().add(CREATOR.getId());
User user = mockUser(fromUser);
Update update = mock(Update.class); Update update = mock(Update.class);
when(update.hasMessage()).thenReturn(true); when(update.hasMessage()).thenReturn(true);
@ -596,7 +604,7 @@ public class AbilityBotTest {
when(message.getText()).thenReturn(args); when(message.getText()).thenReturn(args);
when(message.hasText()).thenReturn(true); when(message.hasText()).thenReturn(true);
when(message.isUserMessage()).thenReturn(true); when(message.isUserMessage()).thenReturn(true);
when(message.getChatId()).thenReturn((long) fromUser.id()); when(message.getChatId()).thenReturn((long) user.getId());
when(update.getMessage()).thenReturn(message); when(update.getMessage()).thenReturn(message);
return update; return update;
} }
@ -605,17 +613,9 @@ public class AbilityBotTest {
when(update.hasMessage()).thenReturn(true); when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message); when(update.getMessage()).thenReturn(message);
when(message.getFrom()).thenReturn(user); when(message.getFrom()).thenReturn(user);
when(user.getFirstName()).thenReturn(MUSER.firstName());
when(user.getLastName()).thenReturn(MUSER.lastName());
when(user.getId()).thenReturn(MUSER.id());
when(user.getUserName()).thenReturn(MUSER.username());
} }
private void mockAlternateUser(Update update, Message message, User user, EndUser changedUser) { private void mockAlternateUser(Update update, Message message, User user) {
when(user.getId()).thenReturn(changedUser.id());
when(user.getFirstName()).thenReturn(changedUser.firstName());
when(user.getLastName()).thenReturn(changedUser.lastName());
when(user.getUserName()).thenReturn(changedUser.username());
when(message.getFrom()).thenReturn(user); when(message.getFrom()).thenReturn(user);
when(update.hasMessage()).thenReturn(true); when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message); when(update.getMessage()).thenReturn(message);
@ -627,10 +627,12 @@ public class AbilityBotTest {
Message botMessage = mock(Message.class); Message botMessage = mock(Message.class);
Document document = mock(Document.class); Document document = mock(Document.class);
when(message.getFrom()).thenReturn(CREATOR);
when(update.getMessage()).thenReturn(message); when(update.getMessage()).thenReturn(message);
when(message.getDocument()).thenReturn(document); when(message.getDocument()).thenReturn(document);
when(botMessage.getText()).thenReturn(RECOVERY_MESSAGE); when(botMessage.getText()).thenReturn(RECOVERY_MESSAGE);
when(message.isReply()).thenReturn(true); when(message.isReply()).thenReturn(true);
when(update.hasMessage()).thenReturn(true);
when(message.hasDocument()).thenReturn(true); when(message.hasDocument()).thenReturn(true);
when(message.getReplyToMessage()).thenReturn(botMessage); when(message.getReplyToMessage()).thenReturn(botMessage);
when(message.getChatId()).thenReturn(GROUP_ID); when(message.getChatId()).thenReturn(GROUP_ID);

View File

@ -3,7 +3,7 @@ package org.telegram.abilitybots.api.db;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.telegram.abilitybots.api.objects.EndUser; import org.telegram.telegrambots.api.objects.User;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
@ -12,12 +12,11 @@ import java.util.Set;
import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.format; import static java.lang.String.format;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.telegram.abilitybots.api.bot.AbilityBot.USERS; import static org.telegram.abilitybots.api.bot.AbilityBot.USERS;
import static org.telegram.abilitybots.api.bot.AbilityBot.USER_ID; import static org.telegram.abilitybots.api.bot.AbilityBot.USER_ID;
import static org.telegram.abilitybots.api.bot.AbilityBotTest.CREATOR; import static org.telegram.abilitybots.api.bot.AbilityBotTest.CREATOR;
import static org.telegram.abilitybots.api.bot.AbilityBotTest.MUSER; import static org.telegram.abilitybots.api.bot.AbilityBotTest.USER;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
public class MapDBContextTest { public class MapDBContextTest {
@ -32,22 +31,22 @@ public class MapDBContextTest {
@Test @Test
public void canRecoverDB() { public void canRecoverDB() {
Map<Integer, EndUser> users = db.getMap(USERS); Map<Integer, User> users = db.getMap(USERS);
Map<String, Integer> userIds = db.getMap(USER_ID); Map<String, Integer> userIds = db.getMap(USER_ID);
users.put(CREATOR.id(), CREATOR); users.put(CREATOR.getId(), CREATOR);
users.put(MUSER.id(), MUSER); users.put(USER.getId(), USER);
userIds.put(CREATOR.username(), CREATOR.id()); userIds.put(CREATOR.getUserName(), CREATOR.getId());
userIds.put(MUSER.username(), MUSER.id()); userIds.put(USER.getUserName(), USER.getId());
db.getSet("AYRE").add(123123); db.getSet("AYRE").add(123123);
Map<Integer, EndUser> originalUsers = newHashMap(users); Map<Integer, User> originalUsers = newHashMap(users);
String beforeBackupInfo = db.info(USERS); String beforeBackupInfo = db.info(USERS);
Object jsonBackup = db.backup(); Object jsonBackup = db.backup();
db.clear(); db.clear();
boolean recovered = db.recover(jsonBackup); boolean recovered = db.recover(jsonBackup);
Map<Integer, EndUser> recoveredUsers = db.getMap(USERS); Map<Integer, User> recoveredUsers = db.getMap(USERS);
String afterRecoveryInfo = db.info(USERS); String afterRecoveryInfo = db.info(USERS);
assertTrue("Could not recover database successfully", recovered); assertTrue("Could not recover database successfully", recovered);
@ -56,24 +55,24 @@ public class MapDBContextTest {
} }
@Test @Test
public void canFallbackDBIfRecoveryFails() throws IOException { public void canFallbackDBIfRecoveryFails() {
Set<EndUser> users = db.getSet(USERS); Set<User> users = db.getSet(USERS);
users.add(CREATOR); users.add(CREATOR);
users.add(MUSER); users.add(USER);
Set<EndUser> originalSet = newHashSet(users); Set<User> originalSet = newHashSet(users);
Object jsonBackup = db.backup(); Object jsonBackup = db.backup();
String corruptBackup = "!@#$" + String.valueOf(jsonBackup); String corruptBackup = "!@#$" + String.valueOf(jsonBackup);
boolean recovered = db.recover(corruptBackup); boolean recovered = db.recover(corruptBackup);
Set<EndUser> recoveredSet = db.getSet(USERS); Set<User> recoveredSet = db.getSet(USERS);
assertEquals("Recovery was successful from a CORRUPT backup", false, recovered); assertFalse("Recovery was successful from a CORRUPT backup", recovered);
assertEquals("Set before and after corrupt recovery are not equal", originalSet, recoveredSet); assertEquals("Set before and after corrupt recovery are not equal", originalSet, recoveredSet);
} }
@Test @Test
public void canGetSummary() throws IOException { public void canGetSummary() {
String anotherTest = TEST + 1; String anotherTest = TEST + 1;
db.getSet(TEST).add(TEST); db.getSet(TEST).add(TEST);
db.getSet(anotherTest).add(anotherTest); db.getSet(anotherTest).add(anotherTest);
@ -86,7 +85,7 @@ public class MapDBContextTest {
} }
@Test @Test
public void canGetInfo() throws IOException { public void canGetInfo() {
db.getSet(TEST).add(TEST); db.getSet(TEST).add(TEST);
String actualInfo = db.info(TEST); String actualInfo = db.info(TEST);
@ -97,10 +96,27 @@ public class MapDBContextTest {
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void cantGetInfoFromNonexistentDBStructureName() throws IOException { public void cantGetInfoFromNonexistentDBStructureName() {
db.info(TEST); db.info(TEST);
} }
@Test
public void canGetAndSetVariables() {
String varName = "somevar";
Var<User> var = db.getVar(varName);
var.set(CREATOR);
db.commit();
var = db.getVar(varName);
assertEquals(var.get(), CREATOR);
var.set(USER);
db.commit();
Var<User> changedVar = db.getVar(varName);
assertEquals(changedVar.get(), USER);
}
@After @After
public void tearDown() throws IOException { public void tearDown() throws IOException {
db.clear(); db.clear();

View File

@ -0,0 +1 @@
ability.commands.notFound=Non sono presenti comandi disponibile.

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambotsextensions</artifactId> <artifactId>telegrambotsextensions</artifactId>
<version>3.6.1</version> <version>3.6.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Telegram Bots Extensions</name> <name>Telegram Bots Extensions</name>

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-meta</artifactId> <artifactId>telegrambots-meta</artifactId>
<version>3.6.1</version> <version>3.6.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Telegram Bots Meta</name> <name>Telegram Bots Meta</name>

View File

@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.telegram.telegrambots.meta.api.interfaces.BotApiObject; import org.telegram.telegrambots.meta.api.interfaces.BotApiObject;
import java.util.Objects;
/** /**
* @author Ruben Bermudez * @author Ruben Bermudez
* @version 3.0 * @version 3.0
@ -35,6 +37,15 @@ public class User implements BotApiObject {
super(); super();
} }
public User(Integer id, String firstName, Boolean isBot, String lastName, String userName, String languageCode) {
this.id = id;
this.firstName = firstName;
this.isBot = isBot;
this.lastName = lastName;
this.userName = userName;
this.languageCode = languageCode;
}
public Integer getId() { public Integer getId() {
return id; return id;
} }
@ -59,6 +70,24 @@ public class User implements BotApiObject {
return isBot; return isBot;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(firstName, user.firstName) &&
Objects.equals(isBot, user.isBot) &&
Objects.equals(lastName, user.lastName) &&
Objects.equals(userName, user.userName) &&
Objects.equals(languageCode, user.languageCode);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, isBot, lastName, userName, languageCode);
}
@Override @Override
public String toString() { public String toString() {
return "User{" + return "User{" +

View File

@ -16,7 +16,7 @@ public class WebhookInfo implements BotApiObject {
private static final String URL_FIELD = "url"; private static final String URL_FIELD = "url";
private static final String HASCUSTOMCERTIFICATE_FIELD = "has_custom_certificate"; private static final String HASCUSTOMCERTIFICATE_FIELD = "has_custom_certificate";
private static final String PENDINGUPDATESCOUNT_FIELD = "pending_updates_count"; private static final String PENDINGUPDATECOUNT_FIELD = "pending_update_count";
private static final String MAXCONNECTIONS_FIELD = "max_connections"; private static final String MAXCONNECTIONS_FIELD = "max_connections";
private static final String ALLOWEDUPDATES_FIELD = "allowed_updates"; private static final String ALLOWEDUPDATES_FIELD = "allowed_updates";
private static final String LASTERRORDATE_FIELD = "last_error_date"; private static final String LASTERRORDATE_FIELD = "last_error_date";
@ -26,7 +26,7 @@ public class WebhookInfo implements BotApiObject {
private String url; ///< Webhook URL, may be empty if webhook is not set up private String url; ///< Webhook URL, may be empty if webhook is not set up
@JsonProperty(HASCUSTOMCERTIFICATE_FIELD) @JsonProperty(HASCUSTOMCERTIFICATE_FIELD)
private Boolean hasCustomCertificate; ///< True, if a custom certificate was provided for webhook certificate checks private Boolean hasCustomCertificate; ///< True, if a custom certificate was provided for webhook certificate checks
@JsonProperty(PENDINGUPDATESCOUNT_FIELD) @JsonProperty(PENDINGUPDATECOUNT_FIELD)
private Integer pendingUpdatesCount; ///< Number updates awaiting delivery private Integer pendingUpdatesCount; ///< Number updates awaiting delivery
@JsonProperty(LASTERRORDATE_FIELD) @JsonProperty(LASTERRORDATE_FIELD)
private Integer lastErrorDate; ///< Optional. Unix time for the most recent error that happened when trying to deliver an update via webhook private Integer lastErrorDate; ///< Optional. Unix time for the most recent error that happened when trying to deliver an update via webhook

View File

@ -54,7 +54,10 @@ public abstract class AbsSender {
} }
// Send Requests // Send Requests
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SendMessage
*/
@Deprecated @Deprecated
public final Message sendMessage(SendMessage sendMessage) throws TelegramApiException { public final Message sendMessage(SendMessage sendMessage) throws TelegramApiException {
if (sendMessage == null) { if (sendMessage == null) {
@ -64,6 +67,10 @@ public abstract class AbsSender {
return sendApiMethod(sendMessage); return sendApiMethod(sendMessage);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see AnswerInlineQuery
*/
@Deprecated @Deprecated
public final Boolean answerInlineQuery(AnswerInlineQuery answerInlineQuery) throws TelegramApiException { public final Boolean answerInlineQuery(AnswerInlineQuery answerInlineQuery) throws TelegramApiException {
if (answerInlineQuery == null) { if (answerInlineQuery == null) {
@ -73,6 +80,10 @@ public abstract class AbsSender {
return sendApiMethod(answerInlineQuery); return sendApiMethod(answerInlineQuery);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SendChatAction
*/
@Deprecated @Deprecated
public final Boolean sendChatAction(SendChatAction sendChatAction) throws TelegramApiException { public final Boolean sendChatAction(SendChatAction sendChatAction) throws TelegramApiException {
if (sendChatAction == null) { if (sendChatAction == null) {
@ -82,6 +93,10 @@ public abstract class AbsSender {
return sendApiMethod(sendChatAction); return sendApiMethod(sendChatAction);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see ForwardMessage
*/
@Deprecated @Deprecated
public final Message forwardMessage(ForwardMessage forwardMessage) throws TelegramApiException { public final Message forwardMessage(ForwardMessage forwardMessage) throws TelegramApiException {
if (forwardMessage == null) { if (forwardMessage == null) {
@ -91,6 +106,10 @@ public abstract class AbsSender {
return sendApiMethod(forwardMessage); return sendApiMethod(forwardMessage);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SendLocation
*/
@Deprecated @Deprecated
public final Message sendLocation(SendLocation sendLocation) throws TelegramApiException { public final Message sendLocation(SendLocation sendLocation) throws TelegramApiException {
if (sendLocation == null) { if (sendLocation == null) {
@ -100,6 +119,10 @@ public abstract class AbsSender {
return sendApiMethod(sendLocation); return sendApiMethod(sendLocation);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SendVenue
*/
@Deprecated @Deprecated
public final Message sendVenue(SendVenue sendVenue) throws TelegramApiException { public final Message sendVenue(SendVenue sendVenue) throws TelegramApiException {
if (sendVenue == null) { if (sendVenue == null) {
@ -109,6 +132,10 @@ public abstract class AbsSender {
return sendApiMethod(sendVenue); return sendApiMethod(sendVenue);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SendContact
*/
@Deprecated @Deprecated
public final Message sendContact(SendContact sendContact) throws TelegramApiException { public final Message sendContact(SendContact sendContact) throws TelegramApiException {
if (sendContact == null) { if (sendContact == null) {
@ -118,6 +145,10 @@ public abstract class AbsSender {
return sendApiMethod(sendContact); return sendApiMethod(sendContact);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see KickChatMember
*/
@Deprecated @Deprecated
public final Boolean kickMember(KickChatMember kickChatMember) throws TelegramApiException { public final Boolean kickMember(KickChatMember kickChatMember) throws TelegramApiException {
if (kickChatMember == null) { if (kickChatMember == null) {
@ -126,6 +157,10 @@ public abstract class AbsSender {
return sendApiMethod(kickChatMember); return sendApiMethod(kickChatMember);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see UnbanChatMember
*/
@Deprecated @Deprecated
public final Boolean unbanMember(UnbanChatMember unbanChatMember) throws TelegramApiException { public final Boolean unbanMember(UnbanChatMember unbanChatMember) throws TelegramApiException {
if (unbanChatMember == null) { if (unbanChatMember == null) {
@ -134,6 +169,10 @@ public abstract class AbsSender {
return sendApiMethod(unbanChatMember); return sendApiMethod(unbanChatMember);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see LeaveChat
*/
@Deprecated @Deprecated
public final Boolean leaveChat(LeaveChat leaveChat) throws TelegramApiException { public final Boolean leaveChat(LeaveChat leaveChat) throws TelegramApiException {
if (leaveChat == null) { if (leaveChat == null) {
@ -142,6 +181,10 @@ public abstract class AbsSender {
return sendApiMethod(leaveChat); return sendApiMethod(leaveChat);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see GetChat
*/
@Deprecated @Deprecated
public final Chat getChat(GetChat getChat) throws TelegramApiException { public final Chat getChat(GetChat getChat) throws TelegramApiException {
if (getChat == null) { if (getChat == null) {
@ -150,6 +193,10 @@ public abstract class AbsSender {
return sendApiMethod(getChat); return sendApiMethod(getChat);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see ExportChatInviteLink
*/
@Deprecated @Deprecated
public final String exportChatInviteLink(ExportChatInviteLink exportChatInviteLink) throws TelegramApiException { public final String exportChatInviteLink(ExportChatInviteLink exportChatInviteLink) throws TelegramApiException {
if (exportChatInviteLink == null) { if (exportChatInviteLink == null) {
@ -158,6 +205,10 @@ public abstract class AbsSender {
return sendApiMethod(exportChatInviteLink); return sendApiMethod(exportChatInviteLink);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see GetChatAdministrators
*/
@Deprecated @Deprecated
public final List<ChatMember> getChatAdministrators(GetChatAdministrators getChatAdministrators) throws TelegramApiException { public final List<ChatMember> getChatAdministrators(GetChatAdministrators getChatAdministrators) throws TelegramApiException {
if (getChatAdministrators == null) { if (getChatAdministrators == null) {
@ -166,6 +217,10 @@ public abstract class AbsSender {
return sendApiMethod(getChatAdministrators); return sendApiMethod(getChatAdministrators);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see GetChatMember
*/
@Deprecated @Deprecated
public final ChatMember getChatMember(GetChatMember getChatMember) throws TelegramApiException { public final ChatMember getChatMember(GetChatMember getChatMember) throws TelegramApiException {
if (getChatMember == null) { if (getChatMember == null) {
@ -174,6 +229,10 @@ public abstract class AbsSender {
return sendApiMethod(getChatMember); return sendApiMethod(getChatMember);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see GetChatMemberCount
*/
@Deprecated @Deprecated
public final Integer getChatMemberCount(GetChatMemberCount getChatMemberCount) throws TelegramApiException { public final Integer getChatMemberCount(GetChatMemberCount getChatMemberCount) throws TelegramApiException {
if (getChatMemberCount == null) { if (getChatMemberCount == null) {
@ -182,6 +241,10 @@ public abstract class AbsSender {
return sendApiMethod(getChatMemberCount); return sendApiMethod(getChatMemberCount);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see EditMessageText
*/
@Deprecated @Deprecated
public final Serializable editMessageText(EditMessageText editMessageText) throws TelegramApiException { public final Serializable editMessageText(EditMessageText editMessageText) throws TelegramApiException {
if (editMessageText == null) { if (editMessageText == null) {
@ -190,6 +253,10 @@ public abstract class AbsSender {
return sendApiMethod(editMessageText); return sendApiMethod(editMessageText);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see EditMessageCaption
*/
@Deprecated @Deprecated
public final Serializable editMessageCaption(EditMessageCaption editMessageCaption) throws TelegramApiException { public final Serializable editMessageCaption(EditMessageCaption editMessageCaption) throws TelegramApiException {
if (editMessageCaption == null) { if (editMessageCaption == null) {
@ -198,6 +265,10 @@ public abstract class AbsSender {
return sendApiMethod(editMessageCaption); return sendApiMethod(editMessageCaption);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see EditMessageReplyMarkup
*/
@Deprecated @Deprecated
public final Serializable editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup) throws TelegramApiException { public final Serializable editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup) throws TelegramApiException {
if (editMessageReplyMarkup == null) { if (editMessageReplyMarkup == null) {
@ -206,6 +277,10 @@ public abstract class AbsSender {
return sendApiMethod(editMessageReplyMarkup); return sendApiMethod(editMessageReplyMarkup);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see AnswerCallbackQuery
*/
@Deprecated @Deprecated
public final Boolean answerCallbackQuery(AnswerCallbackQuery answerCallbackQuery) throws TelegramApiException { public final Boolean answerCallbackQuery(AnswerCallbackQuery answerCallbackQuery) throws TelegramApiException {
if (answerCallbackQuery == null) { if (answerCallbackQuery == null) {
@ -214,6 +289,10 @@ public abstract class AbsSender {
return sendApiMethod(answerCallbackQuery); return sendApiMethod(answerCallbackQuery);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see GetUserProfilePhotos
*/
@Deprecated @Deprecated
public final UserProfilePhotos getUserProfilePhotos(GetUserProfilePhotos getUserProfilePhotos) throws TelegramApiException { public final UserProfilePhotos getUserProfilePhotos(GetUserProfilePhotos getUserProfilePhotos) throws TelegramApiException {
if (getUserProfilePhotos == null) { if (getUserProfilePhotos == null) {
@ -223,6 +302,10 @@ public abstract class AbsSender {
return sendApiMethod(getUserProfilePhotos); return sendApiMethod(getUserProfilePhotos);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see GetFile
*/
@Deprecated @Deprecated
public final File getFile(GetFile getFile) throws TelegramApiException { public final File getFile(GetFile getFile) throws TelegramApiException {
if(getFile == null){ if(getFile == null){
@ -243,6 +326,10 @@ public abstract class AbsSender {
return sendApiMethod(getWebhookInfo); return sendApiMethod(getWebhookInfo);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SetGameScore
*/
@Deprecated @Deprecated
public final Serializable setGameScore(SetGameScore setGameScore) throws TelegramApiException { public final Serializable setGameScore(SetGameScore setGameScore) throws TelegramApiException {
if(setGameScore == null){ if(setGameScore == null){
@ -251,6 +338,10 @@ public abstract class AbsSender {
return sendApiMethod(setGameScore); return sendApiMethod(setGameScore);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see GetGameHighScores
*/
@Deprecated @Deprecated
public final Serializable getGameHighScores(GetGameHighScores getGameHighScores) throws TelegramApiException { public final Serializable getGameHighScores(GetGameHighScores getGameHighScores) throws TelegramApiException {
if(getGameHighScores == null){ if(getGameHighScores == null){
@ -259,6 +350,10 @@ public abstract class AbsSender {
return sendApiMethod(getGameHighScores); return sendApiMethod(getGameHighScores);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SendGame
*/
@Deprecated @Deprecated
public final Message sendGame(SendGame sendGame) throws TelegramApiException { public final Message sendGame(SendGame sendGame) throws TelegramApiException {
if(sendGame == null){ if(sendGame == null){
@ -267,6 +362,10 @@ public abstract class AbsSender {
return sendApiMethod(sendGame); return sendApiMethod(sendGame);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see DeleteWebhook
*/
@Deprecated @Deprecated
public final Boolean deleteWebhook(DeleteWebhook deleteWebhook) throws TelegramApiException { public final Boolean deleteWebhook(DeleteWebhook deleteWebhook) throws TelegramApiException {
if(deleteWebhook == null){ if(deleteWebhook == null){
@ -275,6 +374,10 @@ public abstract class AbsSender {
return sendApiMethod(deleteWebhook); return sendApiMethod(deleteWebhook);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SendInvoice
*/
@Deprecated @Deprecated
public final Message sendInvoice(SendInvoice sendInvoice) throws TelegramApiException { public final Message sendInvoice(SendInvoice sendInvoice) throws TelegramApiException {
if(sendInvoice == null){ if(sendInvoice == null){
@ -283,6 +386,10 @@ public abstract class AbsSender {
return sendApiMethod(sendInvoice); return sendApiMethod(sendInvoice);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see AnswerShippingQuery
*/
@Deprecated @Deprecated
public final Boolean answerShippingQuery(AnswerShippingQuery answerShippingQuery) throws TelegramApiException { public final Boolean answerShippingQuery(AnswerShippingQuery answerShippingQuery) throws TelegramApiException {
if(answerShippingQuery == null){ if(answerShippingQuery == null){
@ -291,6 +398,10 @@ public abstract class AbsSender {
return sendApiMethod(answerShippingQuery); return sendApiMethod(answerShippingQuery);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see AnswerPreCheckoutQuery
*/
@Deprecated @Deprecated
public final Boolean answerPreCheckoutQuery(AnswerPreCheckoutQuery answerPreCheckoutQuery) throws TelegramApiException { public final Boolean answerPreCheckoutQuery(AnswerPreCheckoutQuery answerPreCheckoutQuery) throws TelegramApiException {
if(answerPreCheckoutQuery == null){ if(answerPreCheckoutQuery == null){
@ -299,6 +410,10 @@ public abstract class AbsSender {
return sendApiMethod(answerPreCheckoutQuery); return sendApiMethod(answerPreCheckoutQuery);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see DeleteMessage
*/
@Deprecated @Deprecated
public final Boolean deleteMessage(DeleteMessage deleteMessage) throws TelegramApiException { public final Boolean deleteMessage(DeleteMessage deleteMessage) throws TelegramApiException {
if(deleteMessage == null){ if(deleteMessage == null){
@ -307,6 +422,10 @@ public abstract class AbsSender {
return sendApiMethod(deleteMessage); return sendApiMethod(deleteMessage);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see DeleteChatPhoto
*/
@Deprecated @Deprecated
public final Boolean deleteChatPhoto(DeleteChatPhoto deleteChatPhoto) throws TelegramApiException { public final Boolean deleteChatPhoto(DeleteChatPhoto deleteChatPhoto) throws TelegramApiException {
if(deleteChatPhoto == null){ if(deleteChatPhoto == null){
@ -315,6 +434,10 @@ public abstract class AbsSender {
return sendApiMethod(deleteChatPhoto); return sendApiMethod(deleteChatPhoto);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see PinChatMessage
*/
@Deprecated @Deprecated
public final Boolean pinChatMessage(PinChatMessage pinChatMessage) throws TelegramApiException { public final Boolean pinChatMessage(PinChatMessage pinChatMessage) throws TelegramApiException {
if(pinChatMessage == null){ if(pinChatMessage == null){
@ -323,6 +446,10 @@ public abstract class AbsSender {
return sendApiMethod(pinChatMessage); return sendApiMethod(pinChatMessage);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see UnpinChatMessage
*/
@Deprecated @Deprecated
public final Boolean unpinChatMessage(UnpinChatMessage unpinChatMessage) throws TelegramApiException { public final Boolean unpinChatMessage(UnpinChatMessage unpinChatMessage) throws TelegramApiException {
if(unpinChatMessage == null){ if(unpinChatMessage == null){
@ -331,6 +458,10 @@ public abstract class AbsSender {
return sendApiMethod(unpinChatMessage); return sendApiMethod(unpinChatMessage);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see PromoteChatMember
*/
@Deprecated @Deprecated
public final Boolean promoteChatMember(PromoteChatMember promoteChatMember) throws TelegramApiException { public final Boolean promoteChatMember(PromoteChatMember promoteChatMember) throws TelegramApiException {
if(promoteChatMember == null){ if(promoteChatMember == null){
@ -339,6 +470,10 @@ public abstract class AbsSender {
return sendApiMethod(promoteChatMember); return sendApiMethod(promoteChatMember);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see RestrictChatMember
*/
@Deprecated @Deprecated
public final Boolean restrictChatMember(RestrictChatMember restrictChatMember) throws TelegramApiException { public final Boolean restrictChatMember(RestrictChatMember restrictChatMember) throws TelegramApiException {
if(restrictChatMember == null){ if(restrictChatMember == null){
@ -347,6 +482,10 @@ public abstract class AbsSender {
return sendApiMethod(restrictChatMember); return sendApiMethod(restrictChatMember);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SetChatDescription
*/
@Deprecated @Deprecated
public final Boolean setChatDescription(SetChatDescription setChatDescription) throws TelegramApiException { public final Boolean setChatDescription(SetChatDescription setChatDescription) throws TelegramApiException {
if(setChatDescription == null){ if(setChatDescription == null){
@ -355,6 +494,10 @@ public abstract class AbsSender {
return sendApiMethod(setChatDescription); return sendApiMethod(setChatDescription);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) execute} Method instead
* @see SetChatTitle
*/
@Deprecated @Deprecated
public final Boolean setChatTitle(SetChatTitle setChatTitle) throws TelegramApiException { public final Boolean setChatTitle(SetChatTitle setChatTitle) throws TelegramApiException {
if(setChatTitle == null){ if(setChatTitle == null){
@ -365,6 +508,10 @@ public abstract class AbsSender {
// Send Requests Async // Send Requests Async
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SendMessage
*/
@Deprecated @Deprecated
public final void sendMessageAsync(SendMessage sendMessage, SentCallback<Message> sentCallback) throws TelegramApiException { public final void sendMessageAsync(SendMessage sendMessage, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendMessage == null) { if (sendMessage == null) {
@ -378,6 +525,10 @@ public abstract class AbsSender {
sendApiMethodAsync(sendMessage, sentCallback); sendApiMethodAsync(sendMessage, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see AnswerInlineQuery
*/
@Deprecated @Deprecated
public final void answerInlineQueryAsync(AnswerInlineQuery answerInlineQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void answerInlineQueryAsync(AnswerInlineQuery answerInlineQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerInlineQuery == null) { if (answerInlineQuery == null) {
@ -391,6 +542,10 @@ public abstract class AbsSender {
sendApiMethodAsync(answerInlineQuery, sentCallback); sendApiMethodAsync(answerInlineQuery, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SendChatAction
*/
@Deprecated @Deprecated
public final void sendChatActionAsync(SendChatAction sendChatAction, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void sendChatActionAsync(SendChatAction sendChatAction, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (sendChatAction == null) { if (sendChatAction == null) {
@ -404,6 +559,10 @@ public abstract class AbsSender {
sendApiMethodAsync(sendChatAction, sentCallback); sendApiMethodAsync(sendChatAction, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see ForwardMessage
*/
@Deprecated @Deprecated
public final void forwardMessageAsync(ForwardMessage forwardMessage, SentCallback<Message> sentCallback) throws TelegramApiException { public final void forwardMessageAsync(ForwardMessage forwardMessage, SentCallback<Message> sentCallback) throws TelegramApiException {
if (forwardMessage == null) { if (forwardMessage == null) {
@ -417,6 +576,10 @@ public abstract class AbsSender {
sendApiMethodAsync(forwardMessage, sentCallback); sendApiMethodAsync(forwardMessage, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SendLocation
*/
@Deprecated @Deprecated
public final void sendLocationAsync(SendLocation sendLocation, SentCallback<Message> sentCallback) throws TelegramApiException { public final void sendLocationAsync(SendLocation sendLocation, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendLocation == null) { if (sendLocation == null) {
@ -430,6 +593,10 @@ public abstract class AbsSender {
sendApiMethodAsync(sendLocation, sentCallback); sendApiMethodAsync(sendLocation, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SendVenue
*/
@Deprecated @Deprecated
public final void sendVenueAsync(SendVenue sendVenue, SentCallback<Message> sentCallback) throws TelegramApiException { public final void sendVenueAsync(SendVenue sendVenue, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendVenue == null) { if (sendVenue == null) {
@ -443,6 +610,10 @@ public abstract class AbsSender {
sendApiMethodAsync(sendVenue, sentCallback); sendApiMethodAsync(sendVenue, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SendContact
*/
@Deprecated @Deprecated
public final void sendContactAsync(SendContact sendContact, SentCallback<Message> sentCallback) throws TelegramApiException { public final void sendContactAsync(SendContact sendContact, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendContact == null) { if (sendContact == null) {
@ -455,6 +626,10 @@ public abstract class AbsSender {
sendApiMethodAsync(sendContact, sentCallback); sendApiMethodAsync(sendContact, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see KickChatMember
*/
@Deprecated @Deprecated
public final void kickMemberAsync(KickChatMember kickChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void kickMemberAsync(KickChatMember kickChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (kickChatMember == null) { if (kickChatMember == null) {
@ -467,6 +642,10 @@ public abstract class AbsSender {
sendApiMethodAsync(kickChatMember, sentCallback); sendApiMethodAsync(kickChatMember, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see UnbanChatMember
*/
@Deprecated @Deprecated
public final void unbanMemberAsync(UnbanChatMember unbanChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void unbanMemberAsync(UnbanChatMember unbanChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (unbanChatMember == null) { if (unbanChatMember == null) {
@ -479,6 +658,10 @@ public abstract class AbsSender {
sendApiMethodAsync(unbanChatMember, sentCallback); sendApiMethodAsync(unbanChatMember, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see LeaveChat
*/
@Deprecated @Deprecated
public final void leaveChatAsync(LeaveChat leaveChat, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void leaveChatAsync(LeaveChat leaveChat, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (leaveChat == null) { if (leaveChat == null) {
@ -490,6 +673,10 @@ public abstract class AbsSender {
sendApiMethodAsync(leaveChat, sentCallback); sendApiMethodAsync(leaveChat, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see GetChat
*/
@Deprecated @Deprecated
public final void getChatAsync(GetChat getChat, SentCallback<Chat> sentCallback) throws TelegramApiException { public final void getChatAsync(GetChat getChat, SentCallback<Chat> sentCallback) throws TelegramApiException {
if (getChat == null) { if (getChat == null) {
@ -501,6 +688,10 @@ public abstract class AbsSender {
sendApiMethodAsync(getChat, sentCallback); sendApiMethodAsync(getChat, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see ExportChatInviteLink
*/
@Deprecated @Deprecated
public final void exportChatInviteLinkAsync(ExportChatInviteLink exportChatInviteLink, SentCallback<String> sentCallback) throws TelegramApiException { public final void exportChatInviteLinkAsync(ExportChatInviteLink exportChatInviteLink, SentCallback<String> sentCallback) throws TelegramApiException {
if (exportChatInviteLink == null) { if (exportChatInviteLink == null) {
@ -512,6 +703,10 @@ public abstract class AbsSender {
sendApiMethodAsync(exportChatInviteLink, sentCallback); sendApiMethodAsync(exportChatInviteLink, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see GetChatAdministrators
*/
@Deprecated @Deprecated
public final void getChatAdministratorsAsync(GetChatAdministrators getChatAdministrators, SentCallback<ArrayList<ChatMember>> sentCallback) throws TelegramApiException { public final void getChatAdministratorsAsync(GetChatAdministrators getChatAdministrators, SentCallback<ArrayList<ChatMember>> sentCallback) throws TelegramApiException {
if (getChatAdministrators == null) { if (getChatAdministrators == null) {
@ -523,6 +718,10 @@ public abstract class AbsSender {
sendApiMethodAsync(getChatAdministrators, sentCallback); sendApiMethodAsync(getChatAdministrators, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see GetChatMember
*/
@Deprecated @Deprecated
public final void getChatMemberAsync(GetChatMember getChatMember, SentCallback<ChatMember> sentCallback) throws TelegramApiException { public final void getChatMemberAsync(GetChatMember getChatMember, SentCallback<ChatMember> sentCallback) throws TelegramApiException {
if (getChatMember == null) { if (getChatMember == null) {
@ -534,6 +733,10 @@ public abstract class AbsSender {
sendApiMethodAsync(getChatMember, sentCallback); sendApiMethodAsync(getChatMember, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see GetChatMemberCount
*/
@Deprecated @Deprecated
public final void getChatMemberCountAsync(GetChatMemberCount getChatMemberCount, SentCallback<Integer> sentCallback) throws TelegramApiException { public final void getChatMemberCountAsync(GetChatMemberCount getChatMemberCount, SentCallback<Integer> sentCallback) throws TelegramApiException {
if (getChatMemberCount == null) { if (getChatMemberCount == null) {
@ -546,6 +749,10 @@ public abstract class AbsSender {
sendApiMethodAsync(getChatMemberCount, sentCallback); sendApiMethodAsync(getChatMemberCount, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see EditMessageText
*/
@Deprecated @Deprecated
public final void editMessageTextAsync(EditMessageText editMessageText, SentCallback<Serializable> sentCallback) throws TelegramApiException { public final void editMessageTextAsync(EditMessageText editMessageText, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (editMessageText == null) { if (editMessageText == null) {
@ -558,6 +765,10 @@ public abstract class AbsSender {
sendApiMethodAsync(editMessageText, sentCallback); sendApiMethodAsync(editMessageText, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see EditMessageCaption
*/
@Deprecated @Deprecated
public final void editMessageCaptionAsync(EditMessageCaption editMessageCaption, SentCallback<Serializable> sentCallback) throws TelegramApiException { public final void editMessageCaptionAsync(EditMessageCaption editMessageCaption, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (editMessageCaption == null) { if (editMessageCaption == null) {
@ -570,6 +781,10 @@ public abstract class AbsSender {
sendApiMethodAsync(editMessageCaption, sentCallback); sendApiMethodAsync(editMessageCaption, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see EditMessageReplyMarkup
*/
@Deprecated @Deprecated
public final void editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup, SentCallback<Serializable> sentCallback) throws TelegramApiException { public final void editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (editMessageReplyMarkup == null) { if (editMessageReplyMarkup == null) {
@ -582,6 +797,10 @@ public abstract class AbsSender {
sendApiMethodAsync(editMessageReplyMarkup, sentCallback); sendApiMethodAsync(editMessageReplyMarkup, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see AnswerCallbackQuery
*/
@Deprecated @Deprecated
public final void answerCallbackQueryAsync(AnswerCallbackQuery answerCallbackQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void answerCallbackQueryAsync(AnswerCallbackQuery answerCallbackQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerCallbackQuery == null) { if (answerCallbackQuery == null) {
@ -594,6 +813,10 @@ public abstract class AbsSender {
sendApiMethodAsync(answerCallbackQuery, sentCallback); sendApiMethodAsync(answerCallbackQuery, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see GetUserProfilePhotos
*/
@Deprecated @Deprecated
public final void getUserProfilePhotosAsync(GetUserProfilePhotos getUserProfilePhotos, SentCallback<UserProfilePhotos> sentCallback) throws TelegramApiException { public final void getUserProfilePhotosAsync(GetUserProfilePhotos getUserProfilePhotos, SentCallback<UserProfilePhotos> sentCallback) throws TelegramApiException {
if (getUserProfilePhotos == null) { if (getUserProfilePhotos == null) {
@ -606,6 +829,10 @@ public abstract class AbsSender {
sendApiMethodAsync(getUserProfilePhotos, sentCallback); sendApiMethodAsync(getUserProfilePhotos, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see GetFile
*/
@Deprecated @Deprecated
public final void getFileAsync(GetFile getFile, SentCallback<File> sentCallback) throws TelegramApiException { public final void getFileAsync(GetFile getFile, SentCallback<File> sentCallback) throws TelegramApiException {
if (getFile == null) { if (getFile == null) {
@ -632,6 +859,10 @@ public abstract class AbsSender {
sendApiMethodAsync(new GetWebhookInfo(), sentCallback); sendApiMethodAsync(new GetWebhookInfo(), sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SetGameScore
*/
@Deprecated @Deprecated
public final void setGameScoreAsync(SetGameScore setGameScore, SentCallback<Serializable> sentCallback) throws TelegramApiException { public final void setGameScoreAsync(SetGameScore setGameScore, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (setGameScore == null) { if (setGameScore == null) {
@ -643,6 +874,10 @@ public abstract class AbsSender {
sendApiMethodAsync(setGameScore, sentCallback); sendApiMethodAsync(setGameScore, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see GetGameHighScores
*/
@Deprecated @Deprecated
public final void getGameHighScoresAsync(GetGameHighScores getGameHighScores, SentCallback<ArrayList<GameHighScore>> sentCallback) throws TelegramApiException { public final void getGameHighScoresAsync(GetGameHighScores getGameHighScores, SentCallback<ArrayList<GameHighScore>> sentCallback) throws TelegramApiException {
if (getGameHighScores == null) { if (getGameHighScores == null) {
@ -654,6 +889,10 @@ public abstract class AbsSender {
sendApiMethodAsync(getGameHighScores, sentCallback); sendApiMethodAsync(getGameHighScores, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SendGame
*/
@Deprecated @Deprecated
public final void sendGameAsync(SendGame sendGame, SentCallback<Message> sentCallback) throws TelegramApiException { public final void sendGameAsync(SendGame sendGame, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendGame == null) { if (sendGame == null) {
@ -665,6 +904,10 @@ public abstract class AbsSender {
sendApiMethodAsync(sendGame, sentCallback); sendApiMethodAsync(sendGame, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see DeleteWebhook
*/
@Deprecated @Deprecated
public final void deleteWebhook(DeleteWebhook deleteWebhook, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void deleteWebhook(DeleteWebhook deleteWebhook, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (deleteWebhook == null) { if (deleteWebhook == null) {
@ -676,6 +919,10 @@ public abstract class AbsSender {
sendApiMethodAsync(deleteWebhook, sentCallback); sendApiMethodAsync(deleteWebhook, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SendInvoice
*/
@Deprecated @Deprecated
public final void sendInvoice(SendInvoice sendInvoice, SentCallback<Message> sentCallback) throws TelegramApiException { public final void sendInvoice(SendInvoice sendInvoice, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendInvoice == null) { if (sendInvoice == null) {
@ -687,6 +934,10 @@ public abstract class AbsSender {
sendApiMethodAsync(sendInvoice, sentCallback); sendApiMethodAsync(sendInvoice, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see AnswerShippingQuery
*/
@Deprecated @Deprecated
public final void answerShippingQuery(AnswerShippingQuery answerShippingQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void answerShippingQuery(AnswerShippingQuery answerShippingQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerShippingQuery == null) { if (answerShippingQuery == null) {
@ -698,6 +949,10 @@ public abstract class AbsSender {
sendApiMethodAsync(answerShippingQuery, sentCallback); sendApiMethodAsync(answerShippingQuery, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see AnswerPreCheckoutQuery
*/
@Deprecated @Deprecated
public final void answerPreCheckoutQuery(AnswerPreCheckoutQuery answerPreCheckoutQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void answerPreCheckoutQuery(AnswerPreCheckoutQuery answerPreCheckoutQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerPreCheckoutQuery == null) { if (answerPreCheckoutQuery == null) {
@ -709,6 +964,10 @@ public abstract class AbsSender {
sendApiMethodAsync(answerPreCheckoutQuery, sentCallback); sendApiMethodAsync(answerPreCheckoutQuery, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see DeleteMessage
*/
@Deprecated @Deprecated
public final void deleteMessage(DeleteMessage deleteMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void deleteMessage(DeleteMessage deleteMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (deleteMessage == null) { if (deleteMessage == null) {
@ -720,6 +979,10 @@ public abstract class AbsSender {
sendApiMethodAsync(deleteMessage, sentCallback); sendApiMethodAsync(deleteMessage, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see DeleteChatPhoto
*/
@Deprecated @Deprecated
public final void deleteChatPhoto(DeleteChatPhoto deleteChatPhoto, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void deleteChatPhoto(DeleteChatPhoto deleteChatPhoto, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (deleteChatPhoto == null) { if (deleteChatPhoto == null) {
@ -731,6 +994,10 @@ public abstract class AbsSender {
sendApiMethodAsync(deleteChatPhoto, sentCallback); sendApiMethodAsync(deleteChatPhoto, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see PinChatMessage
*/
@Deprecated @Deprecated
public final void pinChatMessage(PinChatMessage pinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void pinChatMessage(PinChatMessage pinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (pinChatMessage == null) { if (pinChatMessage == null) {
@ -742,6 +1009,10 @@ public abstract class AbsSender {
sendApiMethodAsync(pinChatMessage, sentCallback); sendApiMethodAsync(pinChatMessage, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see UnpinChatMessage
*/
@Deprecated @Deprecated
public final void unpinChatMessage(UnpinChatMessage unpinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void unpinChatMessage(UnpinChatMessage unpinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (unpinChatMessage == null) { if (unpinChatMessage == null) {
@ -753,6 +1024,10 @@ public abstract class AbsSender {
sendApiMethodAsync(unpinChatMessage, sentCallback); sendApiMethodAsync(unpinChatMessage, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see PromoteChatMember
*/
@Deprecated @Deprecated
public final void promoteChatMember(PromoteChatMember promoteChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void promoteChatMember(PromoteChatMember promoteChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (promoteChatMember == null) { if (promoteChatMember == null) {
@ -764,6 +1039,10 @@ public abstract class AbsSender {
sendApiMethodAsync(promoteChatMember, sentCallback); sendApiMethodAsync(promoteChatMember, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see RestrictChatMember
*/
@Deprecated @Deprecated
public final void restrictChatMember(RestrictChatMember restrictChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void restrictChatMember(RestrictChatMember restrictChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (restrictChatMember == null) { if (restrictChatMember == null) {
@ -775,6 +1054,10 @@ public abstract class AbsSender {
sendApiMethodAsync(restrictChatMember, sentCallback); sendApiMethodAsync(restrictChatMember, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SetChatDescription
*/
@Deprecated @Deprecated
public final void setChatDescription(SetChatDescription setChatDescription, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void setChatDescription(SetChatDescription setChatDescription, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (setChatDescription == null) { if (setChatDescription == null) {
@ -786,6 +1069,10 @@ public abstract class AbsSender {
sendApiMethodAsync(setChatDescription, sentCallback); sendApiMethodAsync(setChatDescription, sentCallback);
} }
/**
* Deprecated. Use {@link #execute(BotApiMethod) executeAsync} Method instead
* @see SetChatTitle
*/
@Deprecated @Deprecated
public final void setChatTitle(SetChatTitle setChatTitle, SentCallback<Boolean> sentCallback) throws TelegramApiException { public final void setChatTitle(SetChatTitle setChatTitle, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (setChatTitle == null) { if (setChatTitle == null) {

View File

@ -25,7 +25,7 @@ Usage
**Gradle** **Gradle**
```gradle ```gradle
compile "org.telegram:telegrambots-spring-boot-starter:3.6" compile "org.telegram:telegrambots-spring-boot-starter:3.6.1"
``` ```
Motivation Motivation
@ -39,8 +39,6 @@ Your main spring boot class should look like this:
```java ```java
@SpringBootApplication @SpringBootApplication
//Add this annotation to enable automatic bots initializing
@EnableTelegramBots
public class YourApplicationMainClass { public class YourApplicationMainClass {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-spring-boot-starter</artifactId> <artifactId>telegrambots-spring-boot-starter</artifactId>
<version>3.6.1</version> <version>3.6.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Telegram Bots Spring Boot Starter</name> <name>Telegram Bots Spring Boot Starter</name>
@ -60,7 +60,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<bots.version>3.6.1</bots.version> <bots.version>3.6.1</bots.version>
<spring-boot.version>1.5.10.RELEASE</spring-boot.version> <spring-boot.version>2.0.2.RELEASE</spring-boot.version>
</properties> </properties>
<dependencies> <dependencies>
@ -75,11 +75,41 @@
<artifactId>spring-boot</artifactId> <artifactId>spring-boot</artifactId>
<version>${spring-boot.version}</version> <version>${spring-boot.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId> <artifactId>spring-boot-autoconfigure</artifactId>
<version>${spring-boot.version}</version> <version>${spring-boot.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
<version>3.9.1</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>2.0.2-beta</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,10 +0,0 @@
package org.telegram.telegrambots.starter;
import org.springframework.context.annotation.Import;
/**
* Imports configuration #TelegramBotStarterConfiguration in spring context.
*/
@Import(TelegramBotStarterConfiguration.class)
public @interface EnableTelegramBots {
}

View File

@ -0,0 +1,45 @@
package org.telegram.telegrambots.starter;
import java.util.List;
import java.util.Objects;
import org.springframework.beans.factory.InitializingBean;
import org.telegram.telegrambots.TelegramBotsApi;
import org.telegram.telegrambots.exceptions.TelegramApiException;
import org.telegram.telegrambots.generics.LongPollingBot;
import org.telegram.telegrambots.generics.WebhookBot;
/**
* Receives all beand which are #LongPollingBot and #WebhookBot and register them in #TelegramBotsApi.
*/
public class TelegramBotInitializer implements InitializingBean {
private final TelegramBotsApi telegramBotsApi;
private final List<LongPollingBot> longPollingBots;
private final List<WebhookBot> webHookBots;
public TelegramBotInitializer(TelegramBotsApi telegramBotsApi,
List<LongPollingBot> longPollingBots,
List<WebhookBot> webHookBots) {
Objects.requireNonNull(telegramBotsApi);
Objects.requireNonNull(longPollingBots);
Objects.requireNonNull(webHookBots);
this.telegramBotsApi = telegramBotsApi;
this.longPollingBots = longPollingBots;
this.webHookBots = webHookBots;
}
@Override
public void afterPropertiesSet() throws Exception {
try {
for (LongPollingBot bot : longPollingBots) {
telegramBotsApi.registerBot(bot);
}
for (WebhookBot bot : webHookBots) {
telegramBotsApi.registerBot(bot);
}
} catch (TelegramApiException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,55 +1,37 @@
package org.telegram.telegrambots.starter; package org.telegram.telegrambots.starter;
import org.springframework.beans.factory.annotation.Autowired; import java.util.Collections;
import org.springframework.boot.CommandLineRunner; import java.util.List;
import java.util.Optional;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.meta.generics.LongPollingBot; import org.telegram.telegrambots.meta.generics.LongPollingBot;
import org.telegram.telegrambots.meta.generics.WebhookBot; import org.telegram.telegrambots.meta.generics.WebhookBot;
import java.util.List;
/** /**
* Receives all beand which are #LongPollingBot and #WebhookBot and register them in #TelegramBotsApi.
* #TelegramBotsApi added to spring context as well * #TelegramBotsApi added to spring context as well
*/ */
@Configuration @Configuration
public class TelegramBotStarterConfiguration implements CommandLineRunner { @ConditionalOnProperty(prefix="telegrambots",name = "enabled", havingValue = "true", matchIfMissing = true)
public class TelegramBotStarterConfiguration {
private final List<LongPollingBot> longPollingBots;
private final List<WebhookBot> webHookBots;
@Autowired
private TelegramBotsApi telegramBotsApi;
public TelegramBotStarterConfiguration(List<LongPollingBot> longPollingBots,
List<WebhookBot> webHookBots) {
this.longPollingBots = longPollingBots;
this.webHookBots = webHookBots;
}
@Override
public void run(String... args) {
try {
for (LongPollingBot bot : longPollingBots) {
telegramBotsApi.registerBot(bot);
}
for (WebhookBot bot : webHookBots) {
telegramBotsApi.registerBot(bot);
}
} catch (TelegramApiException e) {
e.printStackTrace();
}
}
@Bean @Bean
@ConditionalOnMissingBean(TelegramBotsApi.class) @ConditionalOnMissingBean(TelegramBotsApi.class)
public TelegramBotsApi telegramBotsApi() { public TelegramBotsApi telegramBotsApi() {
return new TelegramBotsApi(); return new TelegramBotsApi();
} }
@Bean
@ConditionalOnMissingBean
public TelegramBotInitializer telegramBotInitializer(TelegramBotsApi telegramBotsApi,
Optional<List<LongPollingBot>> longPollingBots,
Optional<List<WebhookBot>> webHookBots) {
return new TelegramBotInitializer(telegramBotsApi,
longPollingBots.orElseGet(Collections::emptyList),
webHookBots.orElseGet(Collections::emptyList));
}
} }

View File

@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.telegram.telegrambots.starter.TelegramBotStarterConfiguration

View File

@ -0,0 +1,99 @@
package org.telegram.telegrambots.starter;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import static org.mockito.Mockito.*;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.telegram.telegrambots.TelegramBotsApi;
import org.telegram.telegrambots.generics.LongPollingBot;
import org.telegram.telegrambots.generics.WebhookBot;
public class TestTelegramBotStarterConfiguration {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MockTelegramBotsApi.class, TelegramBotStarterConfiguration.class));
@Test
public void createMockTelegramBotsApiWithDefaultSettings() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(TelegramBotsApi.class);
assertThat(context).hasSingleBean(TelegramBotInitializer.class);
assertThat(context).doesNotHaveBean(LongPollingBot.class);
assertThat(context).doesNotHaveBean(WebhookBot.class);
verifyNoMoreInteractions(context.getBean(TelegramBotsApi.class));
});
}
@Test
public void createOnlyLongPollingBot() {
this.contextRunner.withUserConfiguration(LongPollingBotConfig.class)
.run((context) -> {
assertThat(context).hasSingleBean(LongPollingBot.class);
assertThat(context).doesNotHaveBean(WebhookBot.class);
TelegramBotsApi telegramBotsApi = context.getBean(TelegramBotsApi.class);
verify(telegramBotsApi, times(1)).registerBot( context.getBean(LongPollingBot.class) );
verifyNoMoreInteractions(telegramBotsApi);
});
}
@Test
public void createOnlyWebhookBot() {
this.contextRunner.withUserConfiguration(WebhookBotConfig.class)
.run((context) -> {
assertThat(context).hasSingleBean(WebhookBot.class);
assertThat(context).doesNotHaveBean(LongPollingBot.class);
TelegramBotsApi telegramBotsApi = context.getBean(TelegramBotsApi.class);
verify(telegramBotsApi, times(1)).registerBot( context.getBean(WebhookBot.class) );
verifyNoMoreInteractions(telegramBotsApi);
});
}
@Test
public void createLongPoolingBotAndWebhookBot() {
this.contextRunner.withUserConfiguration(LongPollingBotConfig.class, WebhookBotConfig.class)
.run((context) -> {
assertThat(context).hasSingleBean(LongPollingBot.class);
assertThat(context).hasSingleBean(WebhookBot.class);
TelegramBotsApi telegramBotsApi = context.getBean(TelegramBotsApi.class);
verify(telegramBotsApi, times(1)).registerBot( context.getBean(LongPollingBot.class) );
verify(telegramBotsApi, times(1)).registerBot( context.getBean(WebhookBot.class) );
//verifyNoMoreInteractions(telegramBotsApi);
});
}
@Configuration
static class MockTelegramBotsApi{
@Bean
public TelegramBotsApi telegramBotsApi() {
return mock(TelegramBotsApi.class);
}
}
@Configuration
static class LongPollingBotConfig{
@Bean
public LongPollingBot longPollingBot() {
return mock(LongPollingBot.class);
}
}
@Configuration
static class WebhookBotConfig{
@Bean
public WebhookBot webhookBot() {
return mock(WebhookBot.class);
}
}
}

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
<version>3.6.1</version> <version>3.6.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Telegram Bots</name> <name>Telegram Bots</name>

View File

@ -31,6 +31,7 @@ import org.telegram.telegrambots.meta.updateshandlers.SentCallback;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -53,7 +54,7 @@ public abstract class DefaultAbsSender extends AbsSender {
protected final ExecutorService exe; protected final ExecutorService exe;
private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectMapper objectMapper = new ObjectMapper();
private final DefaultBotOptions options; private final DefaultBotOptions options;
private volatile CloseableHttpClient httpclient; private volatile CloseableHttpClient httpClient;
private volatile RequestConfig requestConfig; private volatile RequestConfig requestConfig;
protected DefaultAbsSender(DefaultBotOptions options) { protected DefaultAbsSender(DefaultBotOptions options) {
@ -61,10 +62,10 @@ public abstract class DefaultAbsSender extends AbsSender {
this.exe = Executors.newFixedThreadPool(options.getMaxThreads()); this.exe = Executors.newFixedThreadPool(options.getMaxThreads());
this.options = options; this.options = options;
httpclient = TelegramHttpClientBuilder.build(options); httpClient = TelegramHttpClientBuilder.build(options);
configureHttpContext();
requestConfig = options.getRequestConfig(); requestConfig = options.getRequestConfig();
if (requestConfig == null) { if (requestConfig == null) {
requestConfig = RequestConfig.copy(RequestConfig.custom().build()) requestConfig = RequestConfig.copy(RequestConfig.custom().build())
.setSocketTimeout(SOCKET_TIMEOUT) .setSocketTimeout(SOCKET_TIMEOUT)
@ -73,6 +74,22 @@ public abstract class DefaultAbsSender extends AbsSender {
} }
} }
private void configureHttpContext() {
if (options.getProxyType() != DefaultBotOptions.ProxyType.NO_PROXY) {
InetSocketAddress socksaddr = new InetSocketAddress(options.getProxyHost(), options.getProxyPort());
options.getHttpContext().setAttribute("socketAddress", socksaddr);
}
if (options.getProxyType() == DefaultBotOptions.ProxyType.SOCKS4) {
options.getHttpContext().setAttribute("socksVersion", 4);
}
if (options.getProxyType() == DefaultBotOptions.ProxyType.SOCKS5) {
options.getHttpContext().setAttribute("socksVersion", 5);
}
}
/** /**
* Returns the token of the bot to be able to perform Telegram Api Requests * Returns the token of the bot to be able to perform Telegram Api Requests
* @return Token of the bot * @return Token of the bot
@ -731,7 +748,7 @@ public abstract class DefaultAbsSender extends AbsSender {
} }
private String sendHttpPostRequest(HttpPost httppost) throws IOException { private String sendHttpPostRequest(HttpPost httppost) throws IOException {
try (CloseableHttpResponse response = httpclient.execute(httppost)) { try (CloseableHttpResponse response = httpClient.execute(httppost, options.getHttpContext())) {
HttpEntity ht = response.getEntity(); HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht); BufferedHttpEntity buf = new BufferedHttpEntity(ht);
return EntityUtils.toString(buf, StandardCharsets.UTF_8); return EntityUtils.toString(buf, StandardCharsets.UTF_8);

View File

@ -1,8 +1,8 @@
package org.telegram.telegrambots.bots; package org.telegram.telegrambots.bots;
import org.apache.http.HttpHost;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.protocol.HttpContext;
import org.telegram.telegrambots.meta.ApiConstants; import org.telegram.telegrambots.meta.ApiConstants;
import org.telegram.telegrambots.meta.generics.BotOptions; import org.telegram.telegrambots.meta.generics.BotOptions;
import org.telegram.telegrambots.updatesreceivers.ExponentialBackOff; import org.telegram.telegrambots.updatesreceivers.ExponentialBackOff;
@ -18,17 +18,27 @@ import java.util.List;
public class DefaultBotOptions implements BotOptions { public class DefaultBotOptions implements BotOptions {
private int maxThreads; ///< Max number of threads used for async methods executions (default 1) private int maxThreads; ///< Max number of threads used for async methods executions (default 1)
private RequestConfig requestConfig; private RequestConfig requestConfig;
private volatile HttpContext httpContext;
private ExponentialBackOff exponentialBackOff; private ExponentialBackOff exponentialBackOff;
private Integer maxWebhookConnections; private Integer maxWebhookConnections;
private String baseUrl; private String baseUrl;
private List<String> allowedUpdates; private List<String> allowedUpdates;
private ProxyType proxyType;
private String proxyHost;
private int proxyPort;
private CredentialsProvider credentialsProvider; public enum ProxyType {
private HttpHost httpProxy; NO_PROXY,
HTTP,
SOCKS4,
SOCKS5
}
public DefaultBotOptions() { public DefaultBotOptions() {
maxThreads = 1; maxThreads = 1;
baseUrl = ApiConstants.BASE_URL; baseUrl = ApiConstants.BASE_URL;
httpContext = HttpClientContext.create();
proxyType = ProxyType.NO_PROXY;
} }
@Override @Override
@ -56,6 +66,14 @@ public class DefaultBotOptions implements BotOptions {
return maxWebhookConnections; return maxWebhookConnections;
} }
public HttpContext getHttpContext() {
return httpContext;
}
public void setHttpContext(HttpContext httpContext) {
this.httpContext = httpContext;
}
public void setMaxWebhookConnections(Integer maxWebhookConnections) { public void setMaxWebhookConnections(Integer maxWebhookConnections) {
this.maxWebhookConnections = maxWebhookConnections; this.maxWebhookConnections = maxWebhookConnections;
} }
@ -88,19 +106,27 @@ public class DefaultBotOptions implements BotOptions {
this.exponentialBackOff = exponentialBackOff; this.exponentialBackOff = exponentialBackOff;
} }
public CredentialsProvider getCredentialsProvider() { public ProxyType getProxyType() {
return credentialsProvider; return proxyType;
} }
public void setCredentialsProvider(CredentialsProvider credentialsProvider) { public void setProxyType(ProxyType proxyType) {
this.credentialsProvider = credentialsProvider; this.proxyType = proxyType;
} }
public HttpHost getHttpProxy() { public String getProxyHost() {
return httpProxy; return proxyHost;
} }
public void setHttpProxy(HttpHost httpProxy) { public void setProxyHost(String proxyHost) {
this.httpProxy = httpProxy; this.proxyHost = proxyHost;
}
public int getProxyPort() {
return proxyPort;
}
public void setProxyPort(int proxyPort) {
this.proxyPort = proxyPort;
} }
} }

View File

@ -1,10 +1,19 @@
package org.telegram.telegrambots.facilities; package org.telegram.telegrambots.facilities;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.ProxyAuthenticationStrategy; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.facilities.proxysocketfactorys.HttpConnectionSocketFactory;
import org.telegram.telegrambots.facilities.proxysocketfactorys.HttpSSLConnectionSocketFactory;
import org.telegram.telegrambots.facilities.proxysocketfactorys.SocksSSLConnectionSocketFactory;
import org.telegram.telegrambots.facilities.proxysocketfactorys.SocksConnectionSocketFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -16,22 +25,30 @@ public class TelegramHttpClientBuilder {
public static CloseableHttpClient build(DefaultBotOptions options) { public static CloseableHttpClient build(DefaultBotOptions options) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create() HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.setSSLHostnameVerifier(new NoopHostnameVerifier()) .setSSLHostnameVerifier(new NoopHostnameVerifier())
.setConnectionManager(createConnectionManager(options))
.setConnectionTimeToLive(70, TimeUnit.SECONDS) .setConnectionTimeToLive(70, TimeUnit.SECONDS)
.setMaxConnTotal(100); .setMaxConnTotal(100);
if (options.getHttpProxy() != null) {
httpClientBuilder.setProxy(options.getHttpProxy());
if (options.getCredentialsProvider() != null) {
httpClientBuilder
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
.setDefaultCredentialsProvider(options.getCredentialsProvider());
}
}
return httpClientBuilder.build(); return httpClientBuilder.build();
} }
private static HttpClientConnectionManager createConnectionManager(DefaultBotOptions options) {
Registry<ConnectionSocketFactory> registry;
switch (options.getProxyType()) {
case NO_PROXY:
return null;
case HTTP:
registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", new HttpConnectionSocketFactory())
.register("https", new HttpSSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build();
return new PoolingHttpClientConnectionManager(registry);
case SOCKS4:
case SOCKS5:
registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", new SocksConnectionSocketFactory())
.register("https", new SocksSSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build();
return new PoolingHttpClientConnectionManager(registry);
}
return null;
}
} }

View File

@ -0,0 +1,33 @@
package org.telegram.telegrambots.facilities.proxysocketfactorys;
import org.apache.http.HttpHost;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
public class HttpConnectionSocketFactory extends PlainConnectionSocketFactory {
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socketAddress = (InetSocketAddress) context.getAttribute("socketAddress");
Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress);
return new Socket(proxy);
}
@Override
public Socket connectSocket(
int connectTimeout,
Socket socket,
HttpHost host,
InetSocketAddress remoteAddress,
InetSocketAddress localAddress,
HttpContext context) throws IOException {
String hostName = host.getHostName();
int port = remoteAddress.getPort();
InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port);
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}

View File

@ -0,0 +1,40 @@
package org.telegram.telegrambots.facilities.proxysocketfactorys;
import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
public class HttpSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
public HttpSSLConnectionSocketFactory(final SSLContext sslContext) {
super(sslContext, new NoopHostnameVerifier());
}
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socketAddress = (InetSocketAddress) context.getAttribute("socketAddress");
Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress);
return new Socket(proxy);
}
@Override
public Socket connectSocket(
int connectTimeout,
Socket socket,
HttpHost host,
InetSocketAddress remoteAddress,
InetSocketAddress localAddress,
HttpContext context) throws IOException {
String hostName = host.getHostName();
int port = remoteAddress.getPort();
InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port);
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}

View File

@ -0,0 +1,37 @@
package org.telegram.telegrambots.facilities.proxysocketfactorys;
import org.apache.http.HttpHost;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;
import sun.net.SocksProxy;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
public class SocksConnectionSocketFactory extends PlainConnectionSocketFactory {
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socketAddress");
int socksVersion = (Integer) context.getAttribute("socksVersion");
Proxy proxy = SocksProxy.create(socksaddr, socksVersion);
return new Socket(proxy);
}
@Override
public Socket connectSocket(
int connectTimeout,
Socket socket,
HttpHost host,
InetSocketAddress remoteAddress,
InetSocketAddress localAddress,
HttpContext context) throws IOException {
String hostName = host.getHostName();
int port = remoteAddress.getPort();
InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port);
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}

View File

@ -0,0 +1,43 @@
package org.telegram.telegrambots.facilities.proxysocketfactorys;
import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;
import sun.net.SocksProxy;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
public class SocksSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
public SocksSSLConnectionSocketFactory(final SSLContext sslContext) {
super(sslContext, new NoopHostnameVerifier());
}
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socketAddress");
int socksVersion = (Integer) context.getAttribute("socksVersion");
Proxy proxy = SocksProxy.create(socksaddr, socksVersion);
return new Socket(proxy);
}
@Override
public Socket connectSocket(
int connectTimeout,
Socket socket,
HttpHost host,
InetSocketAddress remoteAddress,
InetSocketAddress localAddress,
HttpContext context) throws IOException {
String hostName = host.getHostName();
int port = remoteAddress.getPort();
InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(hostName, port);
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}

View File

@ -241,7 +241,7 @@ public class DefaultBotSession implements BotSession {
httpPost.setConfig(requestConfig); httpPost.setConfig(requestConfig);
httpPost.setEntity(new StringEntity(objectMapper.writeValueAsString(request), ContentType.APPLICATION_JSON)); httpPost.setEntity(new StringEntity(objectMapper.writeValueAsString(request), ContentType.APPLICATION_JSON));
try (CloseableHttpResponse response = httpclient.execute(httpPost)) { try (CloseableHttpResponse response = httpclient.execute(httpPost, options.getHttpContext())) {
HttpEntity ht = response.getEntity(); HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht); BufferedHttpEntity buf = new BufferedHttpEntity(ht);
String responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8); String responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);