Merge commit '203b26587f771f9b9bf0f97f8df04e18612a5fe5'

This commit is contained in:
Andrea Cavalli 2020-08-21 12:35:54 +02:00
commit 73b8f90ee2
28 changed files with 423 additions and 108 deletions

View File

@ -27,16 +27,16 @@ Just import add the library to your project with one of these options:
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
``` ```
```gradle ```gradle
compile "org.telegram:telegrambots:4.9" compile "org.telegram:telegrambots:4.9.1"
``` ```
2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.9) 2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.9.1)
3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.9) 3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.9.1)
In order to use Long Polling mode, just create your own bot extending `org.telegram.telegrambots.bots.TelegramLongPollingBot`. In order to use Long Polling mode, just create your own bot extending `org.telegram.telegrambots.bots.TelegramLongPollingBot`.

View File

@ -1,3 +1,6 @@
### <a id="4.9.1"></a>4.9.1 ###
1. Bug fixing: #767, #766, #761, #763, #776, #772, #771, #780
### <a id="4.9"></a>4.9 ### ### <a id="4.9"></a>4.9 ###
1. Update Api version [4.9](https://core.telegram.org/bots/api-changelog#june-4-2020) 1. Update Api version [4.9](https://core.telegram.org/bots/api-changelog#june-4-2020)
2. Bug fixing: #731, #749, #752 and #753 2. Bug fixing: #731, #749, #752 and #753

View File

@ -243,16 +243,16 @@ This is just one way, how you can compile it (here with maven). The example belo
Please use ```execute()``` instead. Please use ```execute()``` instead.
Example: Example:
```java ```java
SendMessage sn = new SendMessage(); SendMessage message = new SendMessage();
//add chat id and text //add chat id and text
execute(sn); execute(message);
``` ```
If you extend ```TelegramLongPollingCommandBot```, then use ```AbsSender.execute()``` instead. If you extend ```TelegramLongPollingCommandBot```, then use ```AbsSender.execute()``` instead.
## <a id="example_webhook"></a>Is there any example for WebHook? ## ## <a id="example_webhook"></a>Is there any example for WebHook? ##
Please see the example Bot for https://telegram.me/SnowcrashBot in the [TelegramBotsExample]() repo and also an [example bot for Sping Boot](https://github.com/UnAfraid/SpringTelegramBot) from [UnAfraid](https://github.com/UnAfraid) [here](https://github.com/UnAfraid/SpringTelegramBot/blob/master/src/main/java/com/github/unafraid/spring/bot/TelegramWebhookBot.java) Please see the example Bot for https://telegram.me/SnowcrashBot in the [TelegramBotsExample]() repo and also an [example bot for Sping Boot](https://github.com/UnAfraid/SpringTelegramBot) from [UnAfraid](https://github.com/UnAfraid) [here](https://github.com/UnAfraid/SpringTelegramBot/blob/master/src/main/java/com/github/unafraid/spring/bot/TelegramWebHookBot.java)
@ -268,7 +268,7 @@ public class YourApplicationMainClass {
//Add this line to initialize bots context //Add this line to initialize bots context
ApiContextInitializer.init(); ApiContextInitializer.init();
SpringApplication.run(MusicUploaderApplication.class, args); SpringApplication.run(YourApplicationMainClass.class, args);
} }
} }
``` ```

View File

@ -11,13 +11,13 @@ First you need ot get the library and add it to your project. There are few poss
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
``` ```
* With **Gradle**: * With **Gradle**:
```groovy ```groovy
compile group: 'org.telegram', name: 'telegrambots', version: '4.9' compile group: 'org.telegram', name: 'telegrambots', version: '4.9.1'
``` ```
2. Don't like **Maven Central Repository**? It can also be taken from [Jitpack](https://jitpack.io/#rubenlagus/TelegramBots). 2. Don't like **Maven Central Repository**? It can also be taken from [Jitpack](https://jitpack.io/#rubenlagus/TelegramBots).

View File

@ -33,5 +33,35 @@ As an example, if you want to restrict the updates to photos only, then you may
} }
``` ```
## Custom Command Processing
### Command Prefix
Customizing the command prefix is as simple as overriding the `getCommandPrefix` method as shown below.
```java
@Override
protected String getCommandPrefix() {
return "!";
}
```
### Command Regex Split
The method that the bot uses to capture command tokens is through the regex splitters. By default, it's set to `" "`. However, this can be customized. For example,
if you'd like to split on digits and whitespaces, then you may do the following:
```java
@Override
protected String getCommandRegexSplit() {
return "\\s\\d";
}
```
### Commands with Continuous Text
Feeling ambitious? You may allow your bot to process tokens that are technically attached to your command. Imagine you have a command
`/do` and you'd like users to send commands as `/do1` and still trigger the `do` ability. In order to do that, override the `allowContinuousText` function.
```java
@Override
protected boolean allowContinuousText() {
return true;
}
```
Please note that this may cause ability overlap. If multiple abilities can match the same command, the longest match will be taken. For example,
if you have two abilities `do` and `do1`, the command `/do1` will trigger the `do1` ability.
## Statistics ## Statistics
AbilityBot can accrue basic statistics about the usage of your abilities and replies. Simply `enableStats()` on an Ability builder or `enableStats(<name>)` on replies to activate this feature. Once activated, you may call `/stats` and the bot will print a basic list of statistics. At the moment, AbilityBot only tracks hits. In the future, this will be enhanced to track more stats. AbilityBot can accrue basic statistics about the usage of your abilities and replies. Simply `enableStats()` on an Ability builder or `enableStats(<name>)` on replies to activate this feature. Once activated, you may call `/stats` and the bot will print a basic list of statistics. At the moment, AbilityBot only tracks hits. In the future, this will be enhanced to track more stats.

View File

@ -9,12 +9,12 @@ As with any Java project, you will need to set your dependencies.
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId> <artifactId>telegrambots-abilities</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
``` ```
* **Gradle** * **Gradle**
```groovy ```groovy
implementation group: 'org.telegram', name: 'telegrambots-abilities', version: '4.9' implementation group: 'org.telegram', name: 'telegrambots-abilities', version: '4.9.1'
``` ```
* [JitPack](https://jitpack.io/#rubenlagus/TelegramBots) * [JitPack](https://jitpack.io/#rubenlagus/TelegramBots)

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>4.9</version> <version>4.9.1</version>
<modules> <modules>
<module>telegrambots</module> <module>telegrambots</module>

View File

@ -18,19 +18,19 @@ Usage
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId> <artifactId>telegrambots-abilities</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
``` ```
**Gradle** **Gradle**
```gradle ```gradle
compile "org.telegram:telegrambots-abilities:4.9" compile "org.telegram:telegrambots-abilities:4.9.1"
``` ```
**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.9) **JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.9.1)
**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.9) **Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.9.1)
Motivation Motivation
---------- ----------

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>Bots</artifactId> <artifactId>Bots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</parent> </parent>
<artifactId>telegrambots-abilities</artifactId> <artifactId>telegrambots-abilities</artifactId>
@ -84,7 +84,7 @@
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>

View File

@ -3,9 +3,9 @@ package org.telegram.abilitybots.api.bot;
import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.toggle.AbilityToggle; import org.telegram.abilitybots.api.toggle.AbilityToggle;
import org.telegram.abilitybots.api.toggle.DefaultToggle; import org.telegram.abilitybots.api.toggle.DefaultToggle;
import org.telegram.telegrambots.bots.DefaultAbsSender;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException; import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.meta.generics.LongPollingBot; import org.telegram.telegrambots.meta.generics.LongPollingBot;
import org.telegram.telegrambots.util.WebhookUtils; import org.telegram.telegrambots.util.WebhookUtils;
@ -13,7 +13,7 @@ import org.telegram.telegrambots.util.WebhookUtils;
import static org.telegram.abilitybots.api.db.MapDBContext.onlineInstance; import static org.telegram.abilitybots.api.db.MapDBContext.onlineInstance;
/** /**
* The default AbilityBot class implements {@link LongPollingBot}. It delegates all updates to a {@link TelegramLongPollingBot} instance. * The default AbilityBot class implements {@link LongPollingBot}. It delegates all updates to a {@link DefaultAbsSender} instance.
* *
* @author Abbas Abou Daya * @author Abbas Abou Daya
*/ */

View File

@ -3,7 +3,6 @@ package org.telegram.abilitybots.api.bot;
import com.google.common.collect.*; import com.google.common.collect.*;
import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.db.DBContext;
@ -18,7 +17,6 @@ import org.telegram.abilitybots.api.util.Pair;
import org.telegram.abilitybots.api.util.Trio; import org.telegram.abilitybots.api.util.Trio;
import org.telegram.telegrambots.bots.DefaultAbsSender; import org.telegram.telegrambots.bots.DefaultAbsSender;
import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatAdministrators; import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatAdministrators;
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;
@ -27,6 +25,7 @@ import org.telegram.telegrambots.meta.api.objects.User;
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.concurrent.Callable;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -37,6 +36,8 @@ import static com.google.common.collect.Sets.difference;
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.comparingInt;
import static java.util.Objects.isNull;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
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;
@ -51,7 +52,7 @@ import static org.telegram.abilitybots.api.util.AbilityUtils.*;
/** /**
* The <b>father</b> of all ability bots. Bots that need to utilize abilities need to extend this bot. * The <b>father</b> of all ability bots. Bots that need to utilize abilities need to extend this bot.
* <p> * <p>
* It's important to note that this bot strictly extends {@link TelegramLongPollingBot}. * It's important to note that this bot strictly extends {@link DefaultAbsSender}.
* <p> * <p>
* All bots extending the {@link BaseAbilityBot} get implicit abilities: * All bots extending the {@link BaseAbilityBot} get implicit abilities:
* <ul> * <ul>
@ -130,35 +131,35 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
/** /**
* @return the map of <ID,User> * @return the map of <ID,User>
*/ */
protected Map<Integer, User> users() { public Map<Integer, User> users() {
return db.getMap(USERS); return db.getMap(USERS);
} }
/** /**
* @return the map of <Username,ID> * @return the map of <Username,ID>
*/ */
protected Map<String, Integer> userIds() { public Map<String, Integer> userIds() {
return db.getMap(USER_ID); return db.getMap(USER_ID);
} }
/** /**
* @return a blacklist containing all the IDs of the banned users * @return a blacklist containing all the IDs of the banned users
*/ */
protected Set<Integer> blacklist() { public Set<Integer> blacklist() {
return db.getSet(BLACKLIST); return db.getSet(BLACKLIST);
} }
/** /**
* @return an admin set of all the IDs of bot administrators * @return an admin set of all the IDs of bot administrators
*/ */
protected Set<Integer> admins() { public Set<Integer> admins() {
return db.getSet(ADMINS); return db.getSet(ADMINS);
} }
/** /**
* @return a mapping of ability and reply names to their corresponding statistics * @return a mapping of ability and reply names to their corresponding statistics
*/ */
protected Map<String, Stats> stats() { public Map<String, Stats> stats() {
return stats; return stats;
} }
@ -221,6 +222,32 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
return botUsername; return botUsername;
} }
public Privacy getPrivacy(Update update, int id) {
return isCreator(id) ?
CREATOR : isAdmin(id) ?
ADMIN : (isGroupUpdate(update) || isSuperGroupUpdate(update)) && isGroupAdmin(update, id) ?
GROUP_ADMIN : PUBLIC;
}
public boolean isGroupAdmin(Update update, int id) {
return isGroupAdmin(getChatId(update), id);
}
public boolean isGroupAdmin(long chatId, int id) {
GetChatAdministrators admins = new GetChatAdministrators().setChatId(chatId);
return silent.execute(admins)
.orElse(new ArrayList<>()).stream()
.anyMatch(member -> member.getUser().getId() == id);
}
public boolean isCreator(int id) {
return id == creatorId();
}
public boolean isAdmin(Integer id) {
return admins().contains(id);
}
/** /**
* Test the update against the provided global flags. The default implementation is a passthrough to all updates. * Test the update against the provided global flags. The default implementation is a passthrough to all updates.
* <p> * <p>
@ -233,6 +260,18 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
return true; return true;
} }
protected String getCommandPrefix() {
return "/";
}
protected String getCommandRegexSplit() {
return " ";
}
protected boolean allowContinuousText() {
return false;
}
/** /**
* 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>
@ -277,7 +316,8 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
// Replies can be standalone or attached to abilities, fetch those too // Replies can be standalone or attached to abilities, fetch those too
Stream<Reply> abilityReplies = abilities.values().stream() Stream<Reply> abilityReplies = abilities.values().stream()
.flatMap(ability -> ability.replies().stream()); .flatMap(ability -> ability.replies().stream())
.flatMap(Reply::stream);
// Now create the replies registry (list) // Now create the replies registry (list)
replies = Stream.concat(abilityReplies, extensionReplies).collect( replies = Stream.concat(abilityReplies, extensionReplies).collect(
@ -401,8 +441,12 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
} }
boolean checkBlacklist(Update update) { boolean checkBlacklist(Update update) {
Integer id = AbilityUtils.getUser(update).getId(); User user = getUser(update);
if (isNull(user)) {
return true;
}
int id = user.getId();
return id == creatorId() || !blacklist().contains(id); return id == creatorId() || !blacklist().contains(id);
} }
@ -458,30 +502,6 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
return isOk; return isOk;
} }
@NotNull
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) {
GetChatAdministrators admins = new GetChatAdministrators().setChatId(getChatId(update));
return silent.execute(admins)
.orElse(new ArrayList<>()).stream()
.anyMatch(member -> member.getUser().getId() == id);
}
private boolean isCreator(int id) {
return id == creatorId();
}
private boolean isAdmin(Integer id) {
return admins().contains(id);
}
boolean validateAbility(Trio<Update, Ability, String[]> trio) { boolean validateAbility(Trio<Update, Ability, String[]> trio) {
return trio.b() != null; return trio.b() != null;
} }
@ -493,17 +513,28 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
if (!update.hasMessage() || !msg.hasText()) if (!update.hasMessage() || !msg.hasText())
return Trio.of(update, abilities.get(DEFAULT), new String[]{}); return Trio.of(update, abilities.get(DEFAULT), new String[]{});
String[] tokens = msg.getText().split(" "); Ability ability;
String[] tokens;
if (tokens[0].startsWith("/")) { if (allowContinuousText()) {
String abilityToken = stripBotUsername(tokens[0].substring(1)).toLowerCase(); String abName = abilities.keySet().stream()
Ability ability = abilities.get(abilityToken); .filter(name -> msg.getText().startsWith(format("%s%s", getCommandPrefix(), name)))
tokens = Arrays.copyOfRange(tokens, 1, tokens.length); .max(comparingInt(String::length))
return Trio.of(update, ability, tokens); .orElse(DEFAULT);
tokens = msg.getText()
.replaceFirst(getCommandPrefix() + abName, "")
.split(getCommandRegexSplit());
ability = abilities.get(abName);
} else { } else {
Ability ability = abilities.get(DEFAULT); tokens = msg.getText().split(getCommandRegexSplit());
return Trio.of(update, ability, tokens); if (tokens[0].startsWith(getCommandPrefix())) {
String abilityToken = stripBotUsername(tokens[0].substring(1)).toLowerCase();
ability = abilities.get(abilityToken);
tokens = Arrays.copyOfRange(tokens, 1, tokens.length);
} else {
ability = abilities.get(DEFAULT);
}
} }
return Trio.of(update, ability, tokens);
} }
private String stripBotUsername(String token) { private String stripBotUsername(String token) {
@ -556,15 +587,25 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
boolean filterReply(Update update) { boolean filterReply(Update update) {
return replies.stream() return replies.stream()
.filter(reply -> reply.isOkFor(update)) .filter(reply -> runSilently(() -> reply.isOkFor(update), reply.name()))
.map(reply -> { .map(reply -> runSilently(() -> {
reply.actOn(update); reply.actOn(update);
updateReplyStats(reply); updateReplyStats(reply);
return false; return false;
}) }, reply.name()))
.reduce(true, Boolean::logicalAnd); .reduce(true, Boolean::logicalAnd);
} }
boolean runSilently(Callable<Boolean> callable, String name) {
try {
return callable.call();
} catch(Exception ex) {
log.error(format("Reply [%s] failed to check for conditions. " +
"Make sure you're safeguarding against all possible updates.", name));
}
return false;
}
boolean checkMessageFlags(Trio<Update, Ability, String[]> trio) { boolean checkMessageFlags(Trio<Update, Ability, String[]> trio) {
Ability ability = trio.b(); Ability ability = trio.b();
Update update = trio.a(); Update update = trio.a();

View File

@ -2,10 +2,8 @@ package org.telegram.abilitybots.api.objects;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -14,7 +12,6 @@ import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
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.
@ -37,6 +34,13 @@ public class Reply {
statsEnabled = false; statsEnabled = false;
} }
Reply(List<Predicate<Update>> conditions, Consumer<Update> action, String name) {
this(conditions, action);
if (Objects.nonNull(name)) {
enableStats(name);
}
}
public static Reply of(Consumer<Update> action, List<Predicate<Update>> conditions) { public static Reply of(Consumer<Update> action, List<Predicate<Update>> conditions) {
return new Reply(conditions, action); return new Reply(conditions, action);
} }

View File

@ -20,8 +20,8 @@ public class ReplyFlow extends Reply {
private final Set<Reply> nextReplies; private final Set<Reply> nextReplies;
private ReplyFlow(List<Predicate<Update>> conditions, Consumer<Update> action, Set<Reply> nextReplies) { private ReplyFlow(List<Predicate<Update>> conditions, Consumer<Update> action, Set<Reply> nextReplies, String name) {
super(conditions, action); super(conditions, action, name);
this.nextReplies = nextReplies; this.nextReplies = nextReplies;
} }
@ -50,6 +50,7 @@ public class ReplyFlow extends Reply {
private List<Predicate<Update>> conds; private List<Predicate<Update>> conds;
private Consumer<Update> action; private Consumer<Update> action;
private Set<Reply> nextReplies; private Set<Reply> nextReplies;
private String name;
private ReplyFlowBuilder(DBContext db, int id) { private ReplyFlowBuilder(DBContext db, int id) {
conds = new ArrayList<>(); conds = new ArrayList<>();
@ -67,6 +68,11 @@ public class ReplyFlow extends Reply {
return this; return this;
} }
public ReplyFlowBuilder enableStats(String name) {
this.name = name;
return this;
}
public ReplyFlowBuilder onlyIf(Predicate<Update> pred) { public ReplyFlowBuilder onlyIf(Predicate<Update> pred) {
conds.add(pred); conds.add(pred);
return this; return this;
@ -79,7 +85,7 @@ public class ReplyFlow extends Reply {
db.<Long, Integer>getMap(STATES).remove(chatId); db.<Long, Integer>getMap(STATES).remove(chatId);
}); });
Reply statefulReply = Reply.of(statefulAction, statefulConditions); Reply statefulReply = new Reply(statefulConditions, statefulAction, nextReply.name());
nextReplies.add(statefulReply); nextReplies.add(statefulReply);
return this; return this;
} }
@ -87,7 +93,7 @@ public class ReplyFlow extends Reply {
public ReplyFlowBuilder next(ReplyFlow nextReplyFlow) { public ReplyFlowBuilder next(ReplyFlow nextReplyFlow) {
List<Predicate<Update>> statefulConditions = toStateful(nextReplyFlow.conditions()); List<Predicate<Update>> statefulConditions = toStateful(nextReplyFlow.conditions());
ReplyFlow statefulReplyFlow = new ReplyFlow(statefulConditions, nextReplyFlow.action(), nextReplyFlow.nextReplies()); ReplyFlow statefulReplyFlow = new ReplyFlow(statefulConditions, nextReplyFlow.action(), nextReplyFlow.nextReplies(), nextReplyFlow.name());
nextReplies.add(statefulReplyFlow); nextReplies.add(statefulReplyFlow);
return this; return this;
} }
@ -95,12 +101,21 @@ public class ReplyFlow extends Reply {
public ReplyFlow build() { public ReplyFlow build() {
if (action == null) if (action == null)
action = upd -> {}; action = upd -> {};
Consumer<Update> statefulAction = action.andThen(upd -> {
Long chatId = AbilityUtils.getChatId(upd);
db.<Long, Integer>getMap(STATES).put(chatId, id);
});
return new ReplyFlow(conds, statefulAction, nextReplies); Consumer<Update> statefulAction;
if (nextReplies.size() > 0) {
statefulAction = action.andThen(upd -> {
Long chatId = AbilityUtils.getChatId(upd);
db.<Long, Integer>getMap(STATES).put(chatId, id);
});
} else {
statefulAction = action.andThen(upd -> {
Long chatId = AbilityUtils.getChatId(upd);
db.<Long, Integer>getMap(STATES).remove(chatId);
});
}
return new ReplyFlow(conds, statefulAction, nextReplies, name);
} }
@NotNull @NotNull

View File

@ -17,6 +17,7 @@ import java.util.function.Predicate;
import static java.util.ResourceBundle.Control.FORMAT_PROPERTIES; import static java.util.ResourceBundle.Control.FORMAT_PROPERTIES;
import static java.util.ResourceBundle.Control.getNoFallbackControl; import static java.util.ResourceBundle.Control.getNoFallbackControl;
import static java.util.ResourceBundle.getBundle; import static java.util.ResourceBundle.getBundle;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.telegram.abilitybots.api.objects.Flag.*; import static org.telegram.abilitybots.api.objects.Flag.*;
@ -57,6 +58,10 @@ public final class AbilityUtils {
* @throws IllegalStateException if the user could not be found * @throws IllegalStateException if the user could not be found
*/ */
public static User getUser(Update update) { public static User getUser(Update update) {
return defaultIfNull(getUserElseThrow(update), EMPTY_USER);
}
private static User getUserElseThrow(Update update) {
if (MESSAGE.test(update)) { if (MESSAGE.test(update)) {
return update.getMessage().getFrom(); return update.getMessage().getFrom();
} else if (CALLBACK_QUERY.test(update)) { } else if (CALLBACK_QUERY.test(update)) {
@ -199,16 +204,16 @@ public final class AbilityUtils {
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) { public static String getLocalizedMessage(String messageCode, Locale locale, Object... arguments) {
ResourceBundle bundle; ResourceBundle bundle;
if (locale == null) { if (locale == null) {
bundle = getBundle("messages", Locale.ROOT); bundle = getBundle("messages", Locale.ROOT);
} else { } else {
try { try {
bundle = getBundle( bundle = getBundle(
"messages", "messages",
locale, locale,
getNoFallbackControl(FORMAT_PROPERTIES)); getNoFallbackControl(FORMAT_PROPERTIES));
} catch (MissingResourceException e) { } catch (MissingResourceException e) {
bundle = getBundle("messages", Locale.ROOT); bundle = getBundle("messages", Locale.ROOT);
} }
@ -217,7 +222,7 @@ public final class AbilityUtils {
return MessageFormat.format(message, arguments); return MessageFormat.format(message, arguments);
} }
public static String getLocalizedMessage(String messageCode, String languageCode, Object...arguments){ public static String getLocalizedMessage(String messageCode, String languageCode, Object... arguments) {
Locale locale = Strings.isNullOrEmpty(languageCode) ? null : Locale.forLanguageTag(languageCode); Locale locale = Strings.isNullOrEmpty(languageCode) ? null : Locale.forLanguageTag(languageCode);
return getLocalizedMessage(messageCode, locale, arguments); return getLocalizedMessage(messageCode, locale, arguments);
} }
@ -247,8 +252,8 @@ public final class AbilityUtils {
* The full name is identified as the concatenation of the first and last name, separated by a space. * 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. * This method can return an empty name if both first and last name are empty.
* *
* @return the full name of the user
* @param user * @param user
* @return the full name of the user
*/ */
public static String fullName(User user) { public static String fullName(User user) {
StringJoiner name = new StringJoiner(" "); StringJoiner name = new StringJoiner(" ");
@ -262,6 +267,6 @@ public final class AbilityUtils {
} }
public static String escape(String username) { public static String escape(String username) {
return username.replace("_", "\\_"); return username.replace("_", "\\_");
} }
} }

View File

@ -644,6 +644,21 @@ public class AbilityBotTest {
verify(silent, times(1)).send(expected, GROUP_ID); verify(silent, times(1)).send(expected, GROUP_ID);
} }
@Test
void canProcessChannelPosts() {
Update update = mock(Update.class);
Message message = mock(Message.class);
when(message.getChatId()).thenReturn(1L);
when(update.getChannelPost()).thenReturn(message);
when(update.hasChannelPost()).thenReturn(true);
bot.onUpdateReceived(update);
String expected = "test channel post";
verify(silent, times(1)).send(expected, 1);
}
private void handlesAllUpdates(Consumer<Update> utilMethod) { private void handlesAllUpdates(Consumer<Update> utilMethod) {
Arrays.stream(Update.class.getMethods()) Arrays.stream(Update.class.getMethods())
// filter to all these methods of hasXXX (hasPoll, hasMessage, etc...) // filter to all these methods of hasXXX (hasPoll, hasMessage, etc...)

View File

@ -0,0 +1,102 @@
package org.telegram.abilitybots.api.bot;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.Ability;
import org.telegram.abilitybots.api.sender.SilentSender;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.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.TestUtils.mockFullUpdate;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
import static org.telegram.abilitybots.api.objects.Ability.builder;
import static org.telegram.abilitybots.api.objects.Locality.ALL;
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
public class ContinuousTextTest {
private static final User USER = new User(1, "first", false, "last", "username", null);
private DBContext db;
private SilentSender silent;
private ContinuousTextBot bot;
@BeforeEach
void setUp() {
db = offlineInstance("db");
bot = new ContinuousTextBot(EMPTY, EMPTY, db);
silent = mock(SilentSender.class);
bot.silent = silent;
}
@AfterEach
void tearDown() throws IOException {
db.clear();
db.close();
}
@Test
void processesContinuousText() {
Update update = mockFullUpdate(bot, USER, "/do2");
bot.onUpdateReceived(update);
verify(silent, times(1))
.send("2", USER.getId());
}
@Test
void matchesLongestAbilityName() {
Update update = mockFullUpdate(bot, USER, "/do1");
bot.onUpdateReceived(update);
verify(silent, times(1))
.send("longer ability name", USER.getId());
}
public static class ContinuousTextBot extends AbilityBot {
public ContinuousTextBot(String token, String username, DBContext db) {
super(token, username, db);
}
@Override
public int creatorId() {
return 1337;
}
@Override
protected boolean allowContinuousText() {
return true;
}
public Ability continuousTextAbility() {
return builder()
.name("do")
.privacy(PUBLIC)
.locality(ALL)
.input(0)
.action(ctx -> silent.send(ctx.firstArg(), ctx.chatId()))
.build();
}
public Ability continuousTextSimilarAbility() {
return builder()
.name("do1")
.privacy(PUBLIC)
.locality(ALL)
.input(0)
.action(ctx -> silent.send("longer ability name", ctx.chatId()))
.build();
}
}
}

View File

@ -3,6 +3,7 @@ package org.telegram.abilitybots.api.bot;
import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.Ability; import org.telegram.abilitybots.api.objects.Ability;
import org.telegram.abilitybots.api.objects.Ability.AbilityBuilder; import org.telegram.abilitybots.api.objects.Ability.AbilityBuilder;
import org.telegram.abilitybots.api.objects.Flag;
import org.telegram.abilitybots.api.objects.Reply; import org.telegram.abilitybots.api.objects.Reply;
import org.telegram.abilitybots.api.toggle.AbilityToggle; import org.telegram.abilitybots.api.toggle.AbilityToggle;
@ -72,6 +73,12 @@ public class DefaultBot extends AbilityBot {
.build(); .build();
} }
public Reply channelPostReply() {
return Reply.of(
upd -> silent.send("test channel post", upd.getChannelPost().getChatId()), Flag.CHANNEL_POST
);
}
public Ability testAbility() { public Ability testAbility() {
return getDefaultBuilder().build(); return getDefaultBuilder().build();
} }

View File

@ -5,17 +5,18 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.Flag; import org.telegram.abilitybots.api.objects.*;
import org.telegram.abilitybots.api.objects.Reply;
import org.telegram.abilitybots.api.objects.ReplyFlow;
import org.telegram.abilitybots.api.sender.MessageSender; import org.telegram.abilitybots.api.sender.MessageSender;
import org.telegram.abilitybots.api.sender.SilentSender; import org.telegram.abilitybots.api.sender.SilentSender;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.polls.Poll; import org.telegram.telegrambots.meta.api.objects.polls.Poll;
import java.io.IOException; import java.io.IOException;
import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
import static com.google.common.collect.Sets.newHashSet;
import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -122,6 +123,33 @@ public class ReplyFlowTest {
assertTrue(bot.filterReply(update)); assertTrue(bot.filterReply(update));
} }
@Test
void replyFlowsAreWorkingWhenDefinedInAbilities() {
Update update1 = mockFullUpdate(bot, USER, "one");
Update update2 = mockFullUpdate(bot, USER, "two");
long chatId = getChatId(update1);
// Trigger and verify first reply stage
assertFalse(bot.filterReply(update1));
verify(silent, only()).send("First reply", chatId);
assertTrue(db.<Long, Integer>getMap(STATES).containsKey(chatId), "User is not in initial state");
// Resetting the mock now helps with verification later
reset(silent);
// Trigger and verify second reply stage
assertFalse(bot.filterReply(update2));
verify(silent, only()).send("Second reply", chatId);
assertFalse(db.<Long, Integer>getMap(STATES).containsKey(chatId), "User is still in a state");
}
@Test
void replyFlowsPertainNames() {
Set<String> replyNames = bot.replies().stream().map(Reply::name).collect(Collectors.toSet());
assertTrue(replyNames.containsAll(newHashSet("FIRST", "SECOND")));
}
public static class ReplyFlowBot extends AbilityBot { public static class ReplyFlowBot extends AbilityBot {
private ReplyFlowBot(String botToken, String botUsername, DBContext db) { private ReplyFlowBot(String botToken, String botUsername, DBContext db) {
@ -153,6 +181,43 @@ public class ReplyFlowTest {
.build(); .build();
} }
public Reply errantReply() {
return Reply.of(
upd -> {
throw new RuntimeException("Throwing an exception inside the update consumer");
},
upd -> {
throw new RuntimeException("Throwing an exception inside the reply conditions (flags)");
});
}
public Ability replyFlowsWithAbility() {
Reply replyWithVk = ReplyFlow.builder(db, 2)
.enableStats("SECOND")
.action(upd -> {
silent.send("Second reply", upd.getMessage().getChatId());
})
.onlyIf(hasMessageWith("two"))
.build();
Reply replyWithNickname = ReplyFlow.builder(db, 1)
.enableStats("FIRST")
.action(upd -> {
silent.send("First reply", upd.getMessage().getChatId());
})
.onlyIf(hasMessageWith("one"))
.next(replyWithVk)
.build();
return Ability.builder()
.name("trigger")
.privacy(Privacy.PUBLIC)
.locality(Locality.ALL)
.action(ctx -> silent.send("I'm in an ability", ctx.chatId()))
.reply(replyWithNickname)
.build();
}
@NotNull @NotNull
private Predicate<Update> hasMessageWith(String msg) { private Predicate<Update> hasMessageWith(String msg) {
return upd -> Flag.MESSAGE.test(upd) && upd.getMessage().getText().equalsIgnoreCase(msg); return upd -> Flag.MESSAGE.test(upd) && upd.getMessage().getText().equalsIgnoreCase(msg);

View File

@ -15,7 +15,7 @@ Usage
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-chat-session-bot</artifactId> <artifactId>telegrambots-chat-session-bot</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
``` ```

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>Bots</artifactId> <artifactId>Bots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</parent> </parent>
<artifactId>telegrambots-chat-session-bot</artifactId> <artifactId>telegrambots-chat-session-bot</artifactId>
@ -84,7 +84,7 @@
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core --> <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->

View File

@ -5,6 +5,8 @@ import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionContext; import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.meta.ApiContext;
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.bots.TelegramLongPollingBot; import org.telegram.telegrambots.bots.TelegramLongPollingBot;
@ -22,6 +24,10 @@ public abstract class TelegramLongPollingSessionBot extends TelegramLongPollingB
} }
public TelegramLongPollingSessionBot(ChatIdConverter chatIdConverter){ public TelegramLongPollingSessionBot(ChatIdConverter chatIdConverter){
this(chatIdConverter, ApiContext.getInstance(DefaultBotOptions.class));
}
public TelegramLongPollingSessionBot(ChatIdConverter chatIdConverter, DefaultBotOptions defaultBotOptions){
this.setSessionManager(new DefaultSessionManager()); this.setSessionManager(new DefaultSessionManager());
this.setChatIdConverter(chatIdConverter); this.setChatIdConverter(chatIdConverter);
AbstractSessionDAO sessionDAO = (AbstractSessionDAO) sessionManager.getSessionDAO(); AbstractSessionDAO sessionDAO = (AbstractSessionDAO) sessionManager.getSessionDAO();

View File

@ -16,12 +16,12 @@ Just import add the library to your project with one of these options:
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambotsextensions</artifactId> <artifactId>telegrambotsextensions</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
``` ```
2. Using Gradle: 2. Using Gradle:
```gradle ```gradle
compile "org.telegram:telegrambotsextensions:4.9" compile "org.telegram:telegrambotsextensions:4.9.1"
``` ```

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>Bots</artifactId> <artifactId>Bots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</parent> </parent>
<artifactId>telegrambotsextensions</artifactId> <artifactId>telegrambotsextensions</artifactId>
@ -75,7 +75,7 @@
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>Bots</artifactId> <artifactId>Bots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</parent> </parent>
<artifactId>telegrambots-meta</artifactId> <artifactId>telegrambots-meta</artifactId>

View File

@ -39,11 +39,13 @@ public class SendInvoice extends BotApiMethod<Message> {
private static final String NEED_PHONE_NUMBER_FIELD = "need_phone_number"; private static final String NEED_PHONE_NUMBER_FIELD = "need_phone_number";
private static final String NEED_EMAIL_FIELD = "need_email"; private static final String NEED_EMAIL_FIELD = "need_email";
private static final String NEED_SHIPPING_ADDRESS_FIELD = "need_shipping_address"; private static final String NEED_SHIPPING_ADDRESS_FIELD = "need_shipping_address";
private static final String SEND_PHONE_NUMBER_TO_PROVIDER_FIELD = "send_phone_number_to_provider";
private static final String SEND_EMAIL_TO_PROVIDER_FIELD = "send_email_to_provider";
private static final String IS_FLEXIBLE_FIELD = "is_flexible"; private static final String IS_FLEXIBLE_FIELD = "is_flexible";
private static final String DISABLE_NOTIFICATION_FIELD = "disable_notification"; private static final String DISABLE_NOTIFICATION_FIELD = "disable_notification";
private static final String REPLY_TO_MESSAGE_ID_FIELD = "reply_to_message_id"; private static final String REPLY_TO_MESSAGE_ID_FIELD = "reply_to_message_id";
private static final String REPLY_MARKUP_FIELD = "reply_markup"; private static final String REPLY_MARKUP_FIELD = "reply_markup";
private static final String PRIVIDER_DATA_FIELD = "provider_data"; private static final String PROVIDER_DATA_FIELD = "provider_data";
@JsonProperty(CHATID_FIELD) @JsonProperty(CHATID_FIELD)
private Integer chatId; ///< Unique identifier for the target private chat private Integer chatId; ///< Unique identifier for the target private chat
@ -87,6 +89,11 @@ public class SendInvoice extends BotApiMethod<Message> {
private Boolean disableNotification; ///< Optional. Sends the message silently. Users will receive a notification with no sound. private Boolean disableNotification; ///< Optional. Sends the message silently. Users will receive a notification with no sound.
@JsonProperty(REPLY_TO_MESSAGE_ID_FIELD) @JsonProperty(REPLY_TO_MESSAGE_ID_FIELD)
private Integer replyToMessageId; ///< Optional. If the message is a reply, ID of the original message private Integer replyToMessageId; ///< Optional. If the message is a reply, ID of the original message
@JsonProperty(SEND_PHONE_NUMBER_TO_PROVIDER_FIELD)
private Boolean sendPhoneNumberToProvider; ///< Optional. Pass True, if user's phone number should be sent to provider
@JsonProperty(SEND_EMAIL_TO_PROVIDER_FIELD)
private Boolean sendEmailToProvider; ///< Optional. Pass True, if user's email address should be sent to provider
/** /**
* Optional. A JSON-serialized object for an inline keyboard. * Optional. A JSON-serialized object for an inline keyboard.
* *
@ -99,11 +106,10 @@ public class SendInvoice extends BotApiMethod<Message> {
* *
* @note A detailed description of required fields should be provided by the payment provider. * @note A detailed description of required fields should be provided by the payment provider.
*/ */
@JsonProperty(PRIVIDER_DATA_FIELD) @JsonProperty(PROVIDER_DATA_FIELD)
private String providerData; private String providerData;
/**
/**
* Build an empty SendInvoice object * Build an empty SendInvoice object
*/ */
public SendInvoice() { public SendInvoice() {
@ -277,6 +283,20 @@ public class SendInvoice extends BotApiMethod<Message> {
return this; return this;
} }
public Boolean getSendPhoneNumberToProvider() { return sendPhoneNumberToProvider; }
public SendInvoice setSendPhoneNumberToProvider(Boolean sendPhoneNumberToProvider) {
this.sendPhoneNumberToProvider = sendPhoneNumberToProvider;
return this;
}
public Boolean getSendEmailToProvider() { return sendEmailToProvider; }
public SendInvoice setSendEmailToProvider(Boolean sendEmailToProvider) {
this.sendEmailToProvider = sendEmailToProvider;
return this;
}
public Boolean getFlexible() { public Boolean getFlexible() {
return isFlexible; return isFlexible;
} }
@ -396,6 +416,8 @@ public class SendInvoice extends BotApiMethod<Message> {
", needPhoneNumber=" + needPhoneNumber + ", needPhoneNumber=" + needPhoneNumber +
", needEmail=" + needEmail + ", needEmail=" + needEmail +
", needShippingAddress=" + needShippingAddress + ", needShippingAddress=" + needShippingAddress +
", sendPhoneNumberToProvider=" + sendPhoneNumberToProvider +
", sendEmailToProvider=" + sendEmailToProvider +
", isFlexible=" + isFlexible + ", isFlexible=" + isFlexible +
", disableNotification=" + disableNotification + ", disableNotification=" + disableNotification +
", replyToMessageId=" + replyToMessageId + ", replyToMessageId=" + replyToMessageId +

View File

@ -18,14 +18,14 @@ Usage
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-spring-boot-starter</artifactId> <artifactId>telegrambots-spring-boot-starter</artifactId>
<version>4.1.2</version> <version>4.9.1</version>
</dependency> </dependency>
``` ```
**Gradle** **Gradle**
```gradle ```gradle
compile "org.telegram:telegrambots-spring-boot-starter:4.1.2" compile "org.telegram:telegrambots-spring-boot-starter:4.9.1"
``` ```
Motivation Motivation
@ -45,7 +45,7 @@ public class YourApplicationMainClass {
//Add this line to initialize bots context //Add this line to initialize bots context
ApiContextInitializer.init(); ApiContextInitializer.init();
SpringApplication.run(MusicUploaderApplication.class, args); SpringApplication.run(YourApplicationMainClass.class, args);
} }
} }
``` ```

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>Bots</artifactId> <artifactId>Bots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</parent> </parent>
<artifactId>telegrambots-spring-boot-starter</artifactId> <artifactId>telegrambots-spring-boot-starter</artifactId>
@ -79,7 +79,7 @@
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>Bots</artifactId> <artifactId>Bots</artifactId>
<version>4.9</version> <version>4.9.1</version>
</parent> </parent>
<artifactId>telegrambots</artifactId> <artifactId>telegrambots</artifactId>
@ -95,7 +95,7 @@
<dependency> <dependency>
<groupId>org.telegram</groupId> <groupId>org.telegram</groupId>
<artifactId>telegrambots-meta</artifactId> <artifactId>telegrambots-meta</artifactId>
<version>4.9</version> <version>4.9.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>