Skip to content

Commit

Permalink
Merge pull request #694 from rubenlagus/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
rubenlagus authored Nov 23, 2019
2 parents f85409f + f58def6 commit 547013d
Show file tree
Hide file tree
Showing 64 changed files with 2,193 additions and 968 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ copyright/

#File System specific files
.DS_STORE

# Default ignored files
/Bots.iws
532 changes: 270 additions & 262 deletions Bots.ipr

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ Just import add the library to your project with one of these options:
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.4.0</version>
<version>4.4.0.2</version>
</dependency>
```

```gradle
compile "org.telegram:telegrambots:4.4.0"
compile "org.telegram:telegrambots:4.4.0.2"
```

2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.4.0)
3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.4.0)
2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.4.0.2)
3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.4.0.2)

In order to use Long Polling mode, just create your own bot extending `org.telegram.telegrambots.bots.TelegramLongPollingBot`.

Expand Down
11 changes: 11 additions & 0 deletions TelegramBots.wiki/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
### <a id="4.4.0.2"></a>4.4.0.2 ###
1. Use SLF4J
2. Support case-insensitive usernames
3. Add Ability toggles and export default abilities to their own class
4. Add state machine capability to AbilityBot via ReplyFlow
5. Support backup and recovery of db vars
6. Fixes: #602, #641, #652, #691

### <a id="4.4.0.1"></a>4.4.0.1 ###
1. Bug fix when importing the project

### <a id="4.4.0"></a>4.4.0 ###
1. Update Api version [4.4](https://core.telegram.org/bots/api-changelog#july-29-2019)
2. Removed BotLogger, replaced with [log4j2](https://logging.apache.org/log4j/2.x/)
Expand Down
4 changes: 2 additions & 2 deletions TelegramBots.wiki/Getting-Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ First you need ot get the library and add it to your project. There are few poss
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.4.0</version>
<version>4.4.0.2</version>
</dependency>
```
* With **Gradle**:

```groovy
compile group: 'org.telegram', name: 'telegrambots', version: '4.4.0'
compile group: 'org.telegram', name: 'telegrambots', version: '4.4.0.2'
```

2. Don't like **Maven Central Repository**? It can also be taken from [Jitpack](https://jitpack.io/#rubenlagus/TelegramBots).
Expand Down
3 changes: 3 additions & 0 deletions TelegramBots.wiki/How-To-Update.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### <a id="4.4.0.2"></a>To version 4.4.0.2 ###
1. Logging framework has been replaced by slf4j, so now you'll need to manage your own implementation.

### <a id="4.0.0"></a>To version 4.0.0 ###
1. Replace removed method from AbsSender with `execute` requests.
2. Everything under "Telegrambots-meta" has been moved to package `org.telegram.telegrambots.meta`.
Expand Down
3 changes: 3 additions & 0 deletions TelegramBots.wiki/_Sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
* [[Simple Example]]
* [[Hello Ability]]
* [[Using Replies]]
* [[Ability Toggle]]
* [[State Machines]]
* [[Database Handling]]
* [[Ability Extensions]]
* [[Bot Testing]]
* [[Bot Recovery]]
* [[Advanced]]
Expand Down
40 changes: 40 additions & 0 deletions TelegramBots.wiki/abilities/Ability-Extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Ability Extensions
You have around 100 abilities in your bot and you're looking for a way to refactor that mess into more modular classes. `AbillityExtension` is here to support just that! It's not a secret that AbilityBot uses refactoring backstage to be able to construct all of your abilities and map them accordingly. However, AbilityBot searches initially for all methods that return an `AbilityExtension` type. Then, those extensions will be used to search for declared abilities. Here's an example.
```java
public class MrGoodGuy implements AbilityExtension {
public Ability () {
return Ability.builder()
.name("nice")
.privacy(PUBLIC)
.locality(ALL)
.action(ctx -> silent.send("You're awesome!", ctx.chatId())
);
}
}

public class MrBadGuy implements AbilityExtension {
public Ability () {
return Ability.builder()
.name("notnice")
.privacy(PUBLIC)
.locality(ALL)
.action(ctx -> silent.send("You're horrible!", ctx.chatId())
);
}
}

public class YourAwesomeBot implements AbilityBot {

// Constructor for your bot

public AbilityExtension goodGuy() {
return new MrGoodGuy();
}

public AbilityExtension badGuy() {
return new MrBadGuy();
}

// Override creatorId
}
```
42 changes: 42 additions & 0 deletions TelegramBots.wiki/abilities/Ability-Toggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Ability Toggle
Well, what if you don't like all the default abilities that AbilityBot supplies? Fear not, you are able to disable all of them, even rename them if you will!

You may pass a custom toggle to your abilitybot constructor to customize how these abilities get registered.

## The Barebone Toggle
The barebone toggle is used to **turn off** all the default abilities that come with the bot (ban, unban, demote, promte, etc...). To use the barebone toggle, call your super constructor with:
```java
import org.telegram.abilitybots.api.toggle.BareboneToggle;

public class YourAwesomeBot extends AbilityBot {
public YourAwesomeBot(String token, String username) {
BareboneToggle toggle = new BareboneToggle();
super(token, username, toggle);
}

// Override ceatorId()
}
```
Obviously, you can export this as a parameter that you can pass to your bot's constructor.

## The Custom Toggle
The custom toggle allows you to customize or turn off the names of the abilities that the abilitybot exposes.
```java
import org.telegram.abilitybots.api.toggle.CustomToggle;

public class YourAwesomeBot extends AbilityBot {
public YourAwesomeBot(String token, String username) {
CustomToggle toggle = new CustomToggle()
.turnOff("ban")
.toggle("promote", "upgrade");

super(token, username, toggle);
}

// Override ceatorId()
}
```

With these changes, the ability "ban" is no longer available and the "promote" ability has been renamed to "upgrade".
## The Default Toggle
The default toggle is automatically used if the user does not specify a toggle. The default toggle allows all the abilities to be effective and unchanged.
2 changes: 1 addition & 1 deletion TelegramBots.wiki/abilities/Simple-Example.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ As with any Java project, you will need to set your dependencies.
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId>
<version>4.4.0</version>
<version>4.4.0.2</version>
</dependency>
```
* **Gradle**
Expand Down
134 changes: 134 additions & 0 deletions TelegramBots.wiki/abilities/State-Machines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# State Machines

AbilityBot supports state machines using ReplyFlows. Internally, they set and transition the state of the user based on their actions so far.
Developers may declare this flow control in either a bottom-up or a top-down approach. If you're already familiar with what a `Reply` is, consider ReplyFlows as the cherry on top.

## Usage
A ReplyFlow can not be directly instantiated; it must be built. First, let's create
some basic replies.
```java
Reply saidLeft = Reply.of(upd ->
silent.send("Sir, I have gone left.", getChatId(upd)),
hasMessageWith("left"));

Reply saidRight = Reply.of(upd ->
silent.send("Sir, I have gone right.", getChatId(upd)),
hasMessageWith("right"));
```
The first `Reply` effectively replies to any message that has the text "left". Once such a message is received, the
bot replies with "Sir, I have gone left". Likewise, the bot acts for when "right" is encountered.

What if now, you'd like to protect those two replies behind one more reply? Let's say, the bot first should ask the user to give it directions.
This means that people can't tell your bot to turn left or right UNLESS the bot asks for directions. Let's trigger that when the user sends "wake up" to the bot!

```java
// We instantiate a ReplyFlow builder with our internal db (DBContext instance) passed
// State is always preserved in the db of the bot and remains even after termination
ReplyFlow.builder(db)
// Just like replies, a ReplyFlow can take an action, here we want to send a
// statement to prompt the user for directions!
.action(upd -> silent.send("Command me to go left or right!", getChatId(upd)))
// We should only trigger this flow when the user says "wake up"
.onlyIf(hasMessageWith("wake up"))
// The next method takes in an object of type Reply.
// Here we chain our replies together
.next(saidLeft)
// We chain one more reply, which is when the user commands your bot to go right
.next(saidRight)
// Finally, we build our ReplyFlow
.build();
```
For the sake of completeness, here's the auxiliary method `hasMessageWith`.
```java
private Predicate<Update> hasMessageWith(String msg) {
return upd -> upd.getMessage().getText().equalsIgnoreCase(msg);
}
```
To run this example in your own AbilityBot, just have a method return that ReplyFlow we just built. Yup, it's that easy, just like how you're used to
building replies and abilities.
## More Complex States
Let's say that your bot becomes naughty when the user asks it to go left. We want the bot to say "I don't know how to go left." when the user commands it to go left. We would also like to chain more commands after this state. Here's
how that's done.
We must create a new ReplyFlow that would be chained to the initial one. Here's what our left flow would look like.
```java
ReplyFlow leftflow = ReplyFlow.builder(db)
.action(upd -> silent.send("I don't know how to go left.", getChatId(upd)))
.onlyIf(hasMessageWith("left"))
.next(saidLeft)
.build();
```
And now, saidLeft reply becomes:
```java
Reply saidLeft = Reply.of(upd -> silent.send("Sir, I have gone left.", getChatId(upd)),
hasMessageWith("go left or else"));
```
Now, after your naughty bot retaliates, the user can say "go left or else" to force the bot to go left. Awesome, our logic now looks like this:
<div align="center">

![Alt text](./img/replyflow_diagram.svg)
<img src="./img/replyflow_diagram.svg">

</div>

## Complete Example
```java
public static class ReplyFlowBot extends AbilityBot {
public class ReplyFlowBot extends AbilityBot {
public ReplyFlowBot(String botToken, String botUsername) {
super(botToken, botUsername);
}

@Override
public int creatorId() {
return <YOUR ID HERE>;
}

public ReplyFlow directionFlow() {
Reply saidLeft = Reply.of(upd -> silent.send("Sir, I have gone left.", getChatId(upd)),
hasMessageWith("go left or else"));

ReplyFlow leftflow = ReplyFlow.builder(db)
.action(upd -> silent.send("I don't know how to go left.", getChatId(upd)))
.onlyIf(hasMessageWith("left"))
.next(saidLeft).build();

Reply saidRight = Reply.of(upd -> silent.send("Sir, I have gone right.", getChatId(upd)),
hasMessageWith("right"));

return ReplyFlow.builder(db)
.action(upd -> silent.send("Command me to go left or right!", getChatId(upd)))
.onlyIf(hasMessageWith("wake up"))
.next(leftflow)
.next(saidRight)
.build();
}

@NotNull
private Predicate<Update> hasMessageWith(String msg) {
return upd -> upd.getMessage().getText().equalsIgnoreCase(msg);
}
}
```
## Inline Declaration
As you can see in the above example, we used a bottom-up approach. We declared the leaf replies before we got to the root reply flow.
If you'd rather have a top-down approach, then you may declare your replies inline to achieve that.
```java
ReplyFlow.builder(db)
.action(upd -> silent.send("Command me to go left or right!", getChatId(upd)))
.onlyIf(hasMessageWith("wake up"))
.next(Reply.of(upd ->
silent.send("Sir, I have gone left.", getChatId(upd)),
hasMessageWith("left")))
.next(Reply.of(upd ->
silent.send("Sir, I have gone right.", getChatId(upd)),
hasMessageWith("right")))
.build();
```
## State Consistency
Under the hood, AbilityBot will generate integers that represent the state of the instigating user. However,
if you add more replies and reply flows, these integers may no longer be consistent. If you'd like to always have consistent state IDs, you
should always pass a unique ID to the ReplyFlow builder like so:
```java
ReplyFlow.builder(db, <ID HERE>)
```
4 changes: 2 additions & 2 deletions TelegramBots.wiki/abilities/Using-Replies.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ public Ability playWithMe() {
.privacy(PUBLIC)
.locality(ALL)
.input(0)
.action(ctx -> sender.forceReply(playMessage, ctx.chatId()))
.action(ctx -> silent.forceReply(playMessage, ctx.chatId()))
// The signature of a reply is -> (Consumer<Update> action, Predicate<Update>... conditions)
// So, we first declare the action that takes an update (NOT A MESSAGECONTEXT) like the action above
// The reason of that is that a reply can be so versatile depending on the message, context becomes an inefficient wrapping
.reply(upd -> {
// Prints to console
System.out.println("I'm in a reply!");
// Sends message
sender.send("It's been nice playing with you!", upd.getMessage().getChatId());
silent.send("It's been nice playing with you!", upd.getMessage().getChatId());
},
// Now we start declaring conditions, MESSAGE is a member of the enum Flag class
// That class contains out-of-the-box predicates for your replies!
Expand Down
Loading

0 comments on commit 547013d

Please sign in to comment.