Merge pull request #276 from rubenlagus/dev

Dev
This commit is contained in:
Ruben Bermudez 2017-07-21 16:25:27 +02:00 committed by GitHub
commit c45e480d6d
46 changed files with 5310 additions and 281 deletions

128
Bots.ipr
View File

@ -17,6 +17,7 @@
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="telegrambots" />
<module name="telegrambots-abilities" />
<module name="telegrambots-extensions" />
<module name="telegrambots-meta" />
</profile>
@ -30,12 +31,14 @@
<bytecodeTargetLevel>
<module name="Bots" target="1.5" />
<module name="telegrambots" target="1.8" />
<module name="telegrambots-abilities" target="1.8" />
<module name="telegrambots-extensions" target="1.8" />
<module name="telegrambots-meta" target="1.8" />
</bytecodeTargetLevel>
</component>
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/telegrambots" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/telegrambots-abilities" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/telegrambots-extensions" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/telegrambots-meta" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
@ -126,6 +129,9 @@
<exe-path>/usr/local/bin/bower</exe-path>
<config-path />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="coroutinesWarn" value="false" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
@ -264,6 +270,7 @@
<modules>
<module fileurl="file://$PROJECT_DIR$/Bots.iml" filepath="$PROJECT_DIR$/Bots.iml" />
<module fileurl="file://$PROJECT_DIR$/telegrambots/telegrambots.iml" filepath="$PROJECT_DIR$/telegrambots/telegrambots.iml" />
<module fileurl="file://$PROJECT_DIR$/telegrambots-abilities/telegrambots-abilities.iml" filepath="$PROJECT_DIR$/telegrambots-abilities/telegrambots-abilities.iml" />
<module fileurl="file://$PROJECT_DIR$/telegrambots-extensions/telegrambots-extensions.iml" filepath="$PROJECT_DIR$/telegrambots-extensions/telegrambots-extensions.iml" />
<module fileurl="file://$PROJECT_DIR$/telegrambots-meta/telegrambots-meta.iml" filepath="$PROJECT_DIR$/telegrambots-meta/telegrambots-meta.iml" />
</modules>
@ -497,6 +504,17 @@
<root url="jar://$MAVEN_REPOSITORY$/javax/ws/rs/javax.ws.rs-api/2.0.1/javax.ws.rs-api-2.0.1-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: junit:junit:4.11">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.11/junit-4.11.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.11/junit-4.11-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.11/junit-4.11-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: junit:junit:4.12">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.12/junit-4.12.jar!/" />
@ -508,6 +526,39 @@
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.12/junit-4.12-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: net.jcip:jcip-annotations:1.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: net.jpountz.lz4:lz4:1.3.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.apache.commons:commons-lang3:3.5">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.apache.httpcomponents:httpclient:4.5.3">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpclient/4.5.3/httpclient-4.5.3.jar!/" />
@ -541,6 +592,39 @@
<root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpmime/4.5.3/httpmime-4.5.3-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.eclipse.collections:eclipse-collections-api:7.1.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections-api/7.1.1/eclipse-collections-api-7.1.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections-api/7.1.1/eclipse-collections-api-7.1.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections-api/7.1.1/eclipse-collections-api-7.1.1-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.eclipse.collections:eclipse-collections-forkjoin:7.1.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections-forkjoin/7.1.1/eclipse-collections-forkjoin-7.1.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections-forkjoin/7.1.1/eclipse-collections-forkjoin-7.1.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections-forkjoin/7.1.1/eclipse-collections-forkjoin-7.1.1-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.eclipse.collections:eclipse-collections:7.1.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections/7.1.1/eclipse-collections-7.1.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections/7.1.1/eclipse-collections-7.1.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/collections/eclipse-collections/7.1.1/eclipse-collections-7.1.1-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.glassfish.grizzly:grizzly-framework:2.3.28">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/grizzly/grizzly-framework/2.3.28/grizzly-framework-2.3.28.jar!/" />
@ -816,6 +900,28 @@
<root url="jar://$MAVEN_REPOSITORY$/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.jetbrains.kotlin:kotlin-runtime:1.0.7">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-runtime/1.0.7/kotlin-runtime-1.0.7.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-runtime/1.0.7/kotlin-runtime-1.0.7-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-runtime/1.0.7/kotlin-runtime-1.0.7-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.0.7">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.0.7/kotlin-stdlib-1.0.7.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.0.7/kotlin-stdlib-1.0.7-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.0.7/kotlin-stdlib-1.0.7-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.json:json:20160810">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/json/json/20160810/json-20160810.jar!/" />
@ -827,6 +933,28 @@
<root url="jar://$MAVEN_REPOSITORY$/org/json/json/20160810/json-20160810-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.mapdb:elsa:3.0.0-M5">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/mapdb/elsa/3.0.0-M5/elsa-3.0.0-M5.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/mapdb/elsa/3.0.0-M5/elsa-3.0.0-M5-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/mapdb/elsa/3.0.0-M5/elsa-3.0.0-M5-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.mapdb:mapdb:3.0.4">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/mapdb/mapdb/3.0.4/mapdb-3.0.4.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/mapdb/mapdb/3.0.4/mapdb-3.0.4-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/mapdb/mapdb/3.0.4/mapdb-3.0.4-sources.jar!/" />
</SOURCES>
</library>
<library name="Maven: org.mockito:mockito-all:2.0.2-beta">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/mockito/mockito-all/2.0.2-beta/mockito-all-2.0.2-beta.jar!/" />

View File

@ -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>3.1.2</version>
<version>3.2</version>
</dependency>
```
```gradle
compile "org.telegram:telegrambots:3.1.2"
compile "org.telegram:telegrambots:3.2"
```
2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/3.1.2)
3. Download the jar(including all dependencies) from [here](https://github.com/rubenlagus/TelegramBots/releases/tag/v3.1.2)
2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/3.2)
3. Download the jar(including all dependencies) from [here](https://github.com/rubenlagus/TelegramBots/releases/tag/v3.2)
In order to use Long Polling mode, just create your own bot extending `org.telegram.telegrambots.bots.TelegramLongPollingBot`.

View File

@ -82,4 +82,15 @@
### <a id="3.1.2"></a>3.1.2 ###
1. Fix bug #266
1. Fix bug #266
### <a id="3.2"></a>3.2 ###
1. Support for Api Version [3.2](https://core.telegram.org/bots/api-changelog#july-21-2017)
2. Deprecated all redundant methods in AbsSender, will be removed in next major release
3. New Abstract methods `addStickerToSet`, `createNewStickerSet` and `uploadStickerFile` in AbsSender.
4. Abilities module
5. Removed deprecated methods from previous versions
6. Bug fixing: #257, #270
7. Simplify code from DefaultAbsSender: #272
**[[How to update to version 3.2|How-To-Update#3.2]]**

View File

@ -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>3.1.2</version>
<version>3.2</version>
</dependency>
```
* With **Gradle**:
```groovy
compile group: 'org.telegram', name: 'telegrambots', version: '3.1.2'
compile group: 'org.telegram', name: 'telegrambots', version: '3.2'
```
2. Don't like **Maven Central Repository**? It can also be taken from [Jitpack](https://jitpack.io/#rubenlagus/TelegramBots).

View File

@ -27,4 +27,9 @@
### <a id="3.0.2"></a>To version 3.0.2 ###
1. If you were using `TelegramLongPollingCommandBot`, add the new [extensions dependency](https://github.com/rubenlagus/TelegramBots/tree/master/telegrambots-extensions) to your maven and fix import statements in your project.
2. If you were using `TelegramLongPollingCommandBot`, make sure you start using constructors with username and prevent overriding `getUsername` method.
2. If you were using `TelegramLongPollingCommandBot`, make sure you start using constructors with username and prevent overriding `getUsername` method.
### <a id="3.2"></a>To version 3.2 ###
1. Replace usage of all deprecated methods from AbsSender with methods `execute` or `executeAsync`.
2. If you are extending AbsSender class, implement new added methods.

View File

@ -7,12 +7,13 @@
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<packaging>pom</packaging>
<version>3.1.2</version>
<version>3.2</version>
<modules>
<module>telegrambots</module>
<module>telegrambots-meta</module>
<module>telegrambots-extensions</module>
<module>telegrambots-abilities</module>
</modules>
<licenses>
@ -25,6 +26,6 @@
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
<bots.version>3.1.2</bots.version>
<bots.version>3.2</bots.version>
</properties>
</project>

View File

@ -0,0 +1,170 @@
<div align="center">
<img src="https://github.com/addo37/AbilityBots/blob/gh-pages/images/API%20BOT-03.png?raw=true" alt="abilitybots" width="200" height="200"/>
[![Build Status](https://travis-ci.org/rubenlagus/TelegramBots.svg?branch=master)](https://travis-ci.org/rubenlagus/TelegramBots)
[![Jitpack](https://jitpack.io/v/rubenlagus/TelegramBots.svg)](https://jitpack.io/#rubenlagus/TelegramBots)
[![JavaDoc](http://svgur.com/i/1Ex.svg)](https://addo37.github.io/AbilityBots/)
[![Telegram](http://trellobot.doomdns.org/telegrambadge.svg)](https://telegram.me/JavaBotsApi)
[![ghit.me](https://ghit.me/badge.svg?repo=rubenlagus/TelegramBots)](https://ghit.me/repo/rubenlagus/TelegramBots)
</div>
Usage
-----
**Maven**
```xml
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId>
<version>3.2</version>
</dependency>
```
**Gradle**
```gradle
compile "org.telegram:telegrambots-abilities:3.2"
```
**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v3.2)
**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v3.2)
Motivation
----------
Ever since I've started programming bots for Telegram, I've been using the Telegram Bot Java API. It's a basic and nicely done API that is a 1-to-1 translation of the HTTP API exposed by Telegram.
Dealing with a basic API has its advantages and disadvantages. Obviously, there's nothing hidden. If it's there on Telegram, it's here in the Java API.
When you want to implement a feature in your bot, you start asking these questions:
* The **WHO**?
* Who is going to use this feature? Should they be allowed to use all the features?
* The **WHAT**?
* Under what conditions should I allow this feature?
* Should the message have a photo? A document? Oh, maybe a callback query?
* The **HOW**?
* If my bot crashes, how can I resume my operation?
* Should I utilize a DB?
* How can I separate logic execution of different features?
* How can I unit-test my feature outside of Telegram?
Every time you write a command or a feature, you will need to answer these questions and ensure that your feature logic works.
Ability Bot Abstraction
-----------------------
After implementing my fifth bot using that API, I had had it with the amount of **boilerplate code** that was needed for every added feature. Methods were getting overly-complex and readability became subpar.
That is where the notion of another layer of abstraction (AbilityBot) began taking shape.
The AbilityBot abstraction defines a new object, named **Ability**. An ability combines conditions, flags, action, post-action and replies.
As an example, here is a code-snippet of an ability that creates a ***/hello*** command:
```java
public Ability sayHelloWorld() {
return Ability
.builder()
.name("hello")
.info("says hello world!")
.input(0)
.locality(USER)
.privacy(ADMIN)
.action(ctx -> sender.send("Hello world!", ctx.chatId()))
.post(ctx -> sender.send("Bye world!", ctx.chatId()))
.build();
}
```
Here is a breakdown of the above code snippet:
* *.name()* - the name of the ability (essentially, this is the command)
* *.info()* - provides information for the command
* More on this later, but it basically centralizes command information in-code.
* *.input()* - the number of input arguments needed, 0 is for do-not-care
* *.locality()* - this answers where you want the ability to be available
* In GROUP, USER private chats or ALL (both)
* *.privacy()* - this answers who you want to access your ability
* CREATOR, ADMIN, or everyone as PUBLIC
* *.action()* - the feature logic resides here (a lambda function that takes a *MessageContext*)
* *MessageContext* provides fast accessors for the **chatId**, **user** and the underlying **update**. It also conforms to the specifications of the basic API.
* *.post()* - the logic executed **after** your main action finishes execution
The following is a snippet of how this would look like with the plain basic API.
```java
@Override
public void onUpdateReceived(Update update) {
// Global checks...
// Switch, if, logic to route to hello world method
// Execute method
}
public void sayHelloWorld(Update update) {
if (!update.hasMessage() || !update.getMessage().isUserMessage() || !update.getMessage().hasText() || update.getMessage.getText().isEmpty())
return;
User maybeAdmin = update.getMessage().getFrom();
/* Query DB for if the user is an admin, can be SQL, Reddis, Ignite, etc...
If user is not an admin, then return here.
*/
SendMessage snd = new SendMessage();
snd.setChatId(update.getMessage().getChatId());
snd.setText("Hello world!");
try {
sendMessage(snd);
} catch (TelegramApiException e) {
BotLogger.error("Could not send message", TAG, e);
}
}
```
I will leave you the choice to decide between the two snippets as to which is more **readable**, **writable** and **testable**.
***You can do so much more with abilities, besides plain commands. Head over to our [examples](#examples) to check out all of its features!***
Objective
---------
The AbilityBot abstraction intends to provide the following:
* New feature is a new **Ability**, a new method - no fuss, zero overhead, no cross-code with other features
* Argument length on a command is as easy as changing a single integer
* Privacy settings per Ability - access levels to Abilities! User | Admin | Creator
* Embedded database - available for every declared ability
* Proxy sender interface - enhances testability; accurate results pre-release
Alongside these exciting core features of the AbilityBot, the following have been introduced:
* The bot automatically maintains an up-to-date set of all the users who have contacted the bot
* up-to-date: if a user changes their Username, First Name or Last Name, the bot updates the respective field in the embedded-DB
* Backup and recovery for the DB
* Default implementation relies on JSON/Jackson
* Ban and unban users from accessing your bots
* The bot will execute the shortest path to discard the update the next time they try to spam
* Promote and demote users as bot administrators
* Allows admins to execute admin abilities
What's next?
------------
I am looking forward to:
* Provide a trigger to record metrics per ability
* Implement AsyncAbility
* Maintain integration with the latest updates on the basic API
* Enrich the bot with features requested by the community
Examples
-------------------
* [Example Bots](https://github.com/addo37/ExampleBots)
Do you have a project that uses **AbilityBots**? Let us know!
Support
-------
For issues and features, please use GitHub's [issues](https://github.com/rubenlagus/TelegramBots/issues) tab.
For quick feedback, chatting or just having fun, please come and join us in our Telegram Supergroup.
[![Telegram](http://trellobot.doomdns.org/telegrambadge.svg)](https://telegram.me/JavaBotsApi)
Credits
-------
This project would not have been made possible had it not been for [Ruben](https://github.com/rubenlagus)'s work with the [Telegram Bot Java API](https://github.com/rubenlagus/TelegramBots).
I strongly urge you to check out that project and implement a bot to have a sense of how the basic API feels like.
Ruben has done a great job in supplying a clear and straightforward API that conforms to Telegram's HTTP API.
There is also a chat for that API.

View File

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId>
<version>3.2</version>
<packaging>jar</packaging>
<name>Telegram Ability Bot</name>
<url>https://github.com/rubenlagus/TelegramBots</url>
<description>AbilityBot Extension and Abstraction</description>
<issueManagement>
<url>https://github.com/rubenlagus/TelegramBots/issues</url>
<system>GitHub Issues</system>
</issueManagement>
<scm>
<url>https://github.com/rubenlagus/TelegramBots</url>
<connection>scm:git:git://github.com/rubenlagus/TelegramBots.git</connection>
<developerConnection>scm:git:git@github.com:rubenlagus/TelegramBots.git</developerConnection>
</scm>
<ciManagement>
<url>https://travis-ci.org/rubenlagus/TelegramBots</url>
<system>Travis</system>
</ciManagement>
<developers>
<developer>
<email>abbas.aboudayya@gmail.com</email>
<name>Abbas Abou Daya</name>
<url>https://github.com/addo37</url>
<id>addo37</id>
</developer>
<developer>
<email>rberlopez@gmail.com</email>
<name>Ruben Bermudez</name>
<url>https://github.com/rubenlagus</url>
<id>rubenlagus</id>
</developer>
</developers>
<licenses>
<license>
<name>MIT License</name>
<url>http://www.opensource.org/licenses/mit-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<bots.version>3.2</bots.version>
<commonslang.version>3.5</commonslang.version>
<mapdb.version>3.0.4</mapdb.version>
<guava.version>19.0</guava.version>
</properties>
<dependencies>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>${bots.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commonslang.version}</version>
</dependency>
<dependency>
<groupId>org.mapdb</groupId>
<artifactId>mapdb</artifactId>
<version>${mapdb.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</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>
<build>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.3</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>clean-project</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.7.201606060606</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
<configuration>
<rules>
<DependencyConvergence />
</rules>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -0,0 +1,743 @@
package org.telegram.abilitybots.api.bot;
import org.apache.commons.io.IOUtils;
import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.*;
import org.telegram.abilitybots.api.sender.DefaultMessageSender;
import org.telegram.abilitybots.api.sender.MessageSender;
import org.telegram.abilitybots.api.util.AbilityUtils;
import org.telegram.abilitybots.api.util.Pair;
import org.telegram.abilitybots.api.util.Trio;
import org.telegram.telegrambots.api.methods.GetFile;
import org.telegram.telegrambots.api.methods.send.SendDocument;
import org.telegram.telegrambots.api.objects.Message;
import org.telegram.telegrambots.api.objects.Update;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.exceptions.TelegramApiException;
import org.telegram.telegrambots.logging.BotLogger;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static java.time.ZonedDateTime.now;
import static java.util.Arrays.stream;
import static java.util.Objects.nonNull;
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.compile;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static jersey.repackaged.com.google.common.base.Throwables.propagate;
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.EndUser.fromUser;
import static org.telegram.abilitybots.api.objects.Flag.*;
import static org.telegram.abilitybots.api.objects.Locality.*;
import static org.telegram.abilitybots.api.objects.MessageContext.newContext;
import static org.telegram.abilitybots.api.objects.Privacy.*;
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.
* <p>
* It's important to note that this bot strictly extends {@link TelegramLongPollingBot}.
* <p>
* All bots extending the {@link AbilityBot} get implicit abilities:
* <ul>
* <li>/claim - Claims this bot</li>
* <ul>
* <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>
* </ul>
* <li>/commands - reports all user-defined commands (abilities)</li>
* <ul>
* <li>The same format acceptable by BotFather</li>
* </ul>
* <li>/backup - returns a backup of the bot database</li>
* <li>/recover - recovers the database</li>
* <li>/promote <code>@username</code> - promotes user to bot admin</li>
* <li>/demote <code>@username</code> - demotes bot admin to user</li>
* <li>/ban <code>@username</code> - bans the user from accessing your bot commands and features</li>
* <li>/unban <code>@username</code> - lifts the ban from the user</li>
* </ul>
* <p>
* Additional information of the implicit abilities are present in the methods that declare them.
* <p>
* The two most important handles in the AbilityBot are the {@link DBContext} <b><code>db</code></b> and the {@link MessageSender} <b><code>sender</code></b>.
* All bots extending AbilityBot can use both handles in their update consumers.
*
* @author Abbas Abou Daya
*/
public abstract class AbilityBot extends TelegramLongPollingBot {
private static final String TAG = AbilityBot.class.getSimpleName();
// DB objects
public static final String ADMINS = "ADMINS";
public static final String USERS = "USERS";
public static final String USER_ID = "USER_ID";
public static final String BLACKLIST = "BLACKLIST";
// Factory commands
protected static final String DEFAULT = "default";
protected static final String CLAIM = "claim";
protected static final String BAN = "ban";
protected static final String PROMOTE = "promote";
protected static final String DEMOTE = "demote";
protected static final String UNBAN = "unban";
protected static final String BACKUP = "backup";
protected static final String RECOVER = "recover";
protected static final String COMMANDS = "commands";
// 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
protected final DBContext db;
protected MessageSender sender;
// Bot token and username
private final String botToken;
private final String botUsername;
// Ability registry
private Map<String, Ability> abilities;
// Reply registry
private List<Reply> replies;
protected AbilityBot(String botToken, String botUsername, DBContext db, DefaultBotOptions botOptions) {
super(botOptions);
this.botToken = botToken;
this.botUsername = botUsername;
this.db = db;
this.sender = new DefaultMessageSender(this);
registerAbilities();
}
protected AbilityBot(String botToken, String botUsername, DBContext db) {
this(botToken, botUsername, db, new DefaultBotOptions());
}
protected AbilityBot(String botToken, String botUsername, DefaultBotOptions botOptions) {
this(botToken, botUsername, onlineInstance(botUsername), botOptions);
}
protected AbilityBot(String botToken, String botUsername) {
this(botToken, botUsername, onlineInstance(botUsername));
}
public abstract int creatorId();
/**
* @return the map of ID -> EndUser
*/
protected Map<Integer, EndUser> users() {
return db.getMap(USERS);
}
/**
* @return the map of Username -> ID
*/
protected Map<String, Integer> userIds() {
return db.getMap(USER_ID);
}
/**
* @return a blacklist containing all the IDs of the banned users
*/
protected Set<Integer> blacklist() {
return db.getSet(BLACKLIST);
}
/**
* @return an admin set of all the IDs of bot administrators
*/
protected Set<Integer> admins() {
return db.getSet(ADMINS);
}
/**
* This method contains the stream of actions that are applied on any update.
* <p>
* It will correctly handle addition of users into the DB and the execution of abilities and replies.
*
* @param update the update received by Telegram's API
*/
@Override
public void onUpdateReceived(Update update) {
BotLogger.info(format("New update [%s] received at %s", update.getUpdateId(), now()), format("%s - %s", TAG, botUsername));
BotLogger.info(update.toString(), TAG);
long millisStarted = System.currentTimeMillis();
Stream.of(update)
.filter(this::checkGlobalFlags)
.filter(this::checkBlacklist)
.map(this::addUser)
.filter(this::filterReply)
.map(this::getAbility)
.filter(this::validateAbility)
.filter(this::checkPrivacy)
.filter(this::checkLocality)
.filter(this::checkInput)
.filter(this::checkMessageFlags)
.map(this::getContext)
.map(this::consumeUpdate)
.forEach(this::postConsumption);
long processingTime = System.currentTimeMillis() - millisStarted;
BotLogger.info(format("Processing of update [%s] ended at %s%n---> Processing time: [%d ms] <---%n", update.getUpdateId(), now(), processingTime), format("%s - %s", TAG, botUsername));
}
@Override
public String getBotToken() {
return botToken;
}
@Override
public String getBotUsername() {
return botUsername;
}
/**
* Test the update against the provided global flags. The default implementation requires a {@link Flag#MESSAGE}.
* <p>
* This method should be <b>overridden</b> if the user wants updates that don't require a MESSAGE to pass through.
*
* @param update a Telegram {@link Update}
* @return <tt>true</tt> if the update satisfies the global flags
*/
protected boolean checkGlobalFlags(Update update) {
return MESSAGE.test(update);
}
/**
* Gets the user with the specified username.
*
* @param username the username of the required user
* @return the user
*/
protected EndUser getUser(String username) {
Integer id = userIds().get(username.toLowerCase());
if (id == null) {
throw new IllegalStateException(format("Could not find ID corresponding to username [%s]", username));
}
return getUser(id);
}
/**
* Gets the user with the specified ID.
*
* @param id the id of the required user
* @return the user
*/
protected EndUser getUser(int id) {
EndUser endUser = users().get(id);
if (endUser == null) {
throw new IllegalStateException(format("Could not find user corresponding to id [%d]", id));
}
return endUser;
}
/**
* 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
* @return the id of the user
*/
protected int getUserIdSendError(String username, long chatId) {
try {
return getUser(username).id();
} catch (IllegalStateException ex) {
sender.send(format("Sorry, I could not find the user [%s].", username), chatId);
throw propagate(ex);
}
}
/**
* <p>
* Format of the report:
* <p>
* [command1] - [description1]
* <p>
* [command2] - [description2]
* <p>
* ...
* <p>
* Once you invoke it, the bot will send the available commands to the chat. This is a public ability so anyone can invoke it.
* <p>
* Usage: <code>/commands</code>
*
* @return the ability to report commands defined by the child bot.
*/
public Ability reportCommands() {
return builder()
.name(COMMANDS)
.locality(ALL)
.privacy(PUBLIC)
.input(0)
.action(ctx -> {
String commands = abilities.entrySet().stream()
.filter(entry -> nonNull(entry.getValue().info()))
.map(entry -> {
String name = entry.getValue().name();
String info = entry.getValue().info();
return format("%s - %s", name, info);
})
.sorted()
.reduce((a, b) -> format("%s%n%s", a, b))
.orElse("No public commands found.");
sender.send(commands, ctx.chatId());
})
.build();
}
/**
* This backup ability returns the object defined by {@link DBContext#backup()} as a message document.
* <p>
* This is a high-profile ability and is restricted to the CREATOR only.
* <p>
* Usage: <code>/backup</code>
*
* @return the ability to back-up the database of the bot
*/
public Ability backupDB() {
return builder()
.name(BACKUP)
.locality(USER)
.privacy(CREATOR)
.input(0)
.action(ctx -> {
File backup = new File("backup.json");
try (PrintStream printStream = new PrintStream(backup)) {
printStream.print(db.backup());
sender.sendDocument(new SendDocument()
.setNewDocument(backup)
.setChatId(ctx.chatId())
);
} catch (FileNotFoundException e) {
BotLogger.error("Error while fetching backup", TAG, e);
} catch (TelegramApiException e) {
BotLogger.error("Error while sending document/backup file", TAG, e);
}
})
.build();
}
/**
* Recovers the bot database using {@link DBContext#recover(Object)}.
* <p>
* The bot recovery process hugely depends on the implementation of the recovery method of {@link DBContext}.
* <p>
* Usage: <code>/recover</code>
*
* @return the ability to recover the database of the bot
*/
public Ability recoverDB() {
return builder()
.name(RECOVER)
.locality(USER)
.privacy(CREATOR)
.input(0)
.action(ctx -> sender.forceReply(RECOVERY_MESSAGE, ctx.chatId()))
.reply(update -> {
Long chatId = update.getMessage().getChatId();
String fileId = update.getMessage().getDocument().getFileId();
try (FileReader reader = new FileReader(downloadFileWithId(fileId))) {
String backupData = IOUtils.toString(reader);
if (db.recover(backupData)) {
sender.send(RECOVER_SUCCESS, chatId);
} else {
sender.send("Oops, something went wrong during recovery.", chatId);
}
} catch (Exception e) {
BotLogger.error("Could not recover DB from backup", TAG, e);
sender.send("I have failed to recover.", chatId);
}
}, MESSAGE, DOCUMENT, REPLY, isReplyTo(RECOVERY_MESSAGE))
.build();
}
/**
* Banned users are accumulated in the blacklist. Use {@link DBContext#getSet(String)} with name specified by {@link AbilityBot#BLACKLIST}.
* <p>
* Usage: <code>/ban @username</code>
* <p>
* <u>Note that admins who try to ban the creator, get banned.</u>
*
* @return the ability to ban the user from any kind of <b>bot interaction</b>
*/
public Ability banUser() {
return builder()
.name(BAN)
.locality(ALL)
.privacy(ADMIN)
.input(1)
.action(ctx -> {
String username = stripTag(ctx.firstArg());
int userId = getUserIdSendError(username, ctx.chatId());
String bannedUser;
// Protection from abuse
if (userId == creatorId()) {
userId = ctx.user().id();
bannedUser = isNullOrEmpty(ctx.user().username()) ? addTag(ctx.user().username()) : ctx.user().shortName();
} else {
bannedUser = addTag(username);
}
Set<Integer> blacklist = blacklist();
if (blacklist.contains(userId))
sender.sendMd(format("%s is already *banned*.", bannedUser), ctx.chatId());
else {
blacklist.add(userId);
sender.sendMd(format("%s is now *banned*.", bannedUser), ctx.chatId());
}
})
.post(commitTo(db))
.build();
}
/**
* Usage: <code>/unban @username</code>
*
* @return the ability to unban a user
*/
public Ability unbanUser() {
return builder()
.name(UNBAN)
.locality(ALL)
.privacy(ADMIN)
.input(1)
.action(ctx -> {
String username = stripTag(ctx.firstArg());
Integer userId = getUserIdSendError(username, ctx.chatId());
Set<Integer> blacklist = blacklist();
if (!blacklist.remove(userId))
sender.sendMd(format("@%s is *not* on the *blacklist*.", username), ctx.chatId());
else {
sender.sendMd(format("@%s, your ban has been *lifted*.", username), ctx.chatId());
}
})
.post(commitTo(db))
.build();
}
/**
* @return the ability to promote a user to a bot admin
*/
public Ability promoteAdmin() {
return builder()
.name(PROMOTE)
.locality(ALL)
.privacy(ADMIN)
.input(1)
.action(ctx -> {
String username = stripTag(ctx.firstArg());
Integer userId = getUserIdSendError(username, ctx.chatId());
Set<Integer> admins = admins();
if (admins.contains(userId))
sender.sendMd(format("@%s is already an *admin*.", username), ctx.chatId());
else {
admins.add(userId);
sender.sendMd(format("@%s has been *promoted*.", username), ctx.chatId());
}
}).post(commitTo(db))
.build();
}
/**
* @return the ability to demote an admin to a user
*/
public Ability demoteAdmin() {
return builder()
.name(DEMOTE)
.locality(ALL)
.privacy(ADMIN)
.input(1)
.action(ctx -> {
String username = stripTag(ctx.firstArg());
Integer userId = getUserIdSendError(username, ctx.chatId());
Set<Integer> admins = admins();
if (admins.remove(userId)) {
sender.sendMd(format("@%s has been *demoted*.", username), ctx.chatId());
} else {
sender.sendMd(format("@%s is *not* an *admin*.", username), ctx.chatId());
}
})
.post(commitTo(db))
.build();
}
/**
* Regular users and admins who try to claim the bot will get <b>banned</b>.
*
* @return the ability to claim yourself as the master and creator of the bot
*/
public Ability claimCreator() {
return builder()
.name(CLAIM)
.locality(ALL)
.privacy(PUBLIC)
.input(0)
.action(ctx -> {
if (ctx.user().id() == creatorId()) {
Set<Integer> admins = admins();
int id = creatorId();
long chatId = ctx.chatId();
if (admins.contains(id))
sender.send("You're already my master.", chatId);
else {
admins.add(id);
sender.send("You're now my master.", chatId);
}
} else {
// This is not a joke
abilities.get(BAN).action().accept(newContext(ctx.update(), ctx.user(), ctx.chatId(), ctx.user().username()));
}
})
.post(commitTo(db))
.build();
}
/**
* Registers the declared abilities using method reflection. Also, replies are accumulated using the built abilities and standalone methods that return a Reply.
* <p>
* <b>Only abilities and replies with the <u>public</u> accessor are registered!</b>
*/
private void registerAbilities() {
try {
abilities = stream(this.getClass().getMethods())
.filter(method -> method.getReturnType().equals(Ability.class))
.map(this::returnAbility)
.collect(toMap(Ability::name, identity()));
Stream<Reply> methodReplies = stream(this.getClass().getMethods())
.filter(method -> method.getReturnType().equals(Reply.class))
.map(this::returnReply);
Stream<Reply> abilityReplies = abilities.values().stream()
.flatMap(ability -> ability.replies().stream());
replies = Stream.concat(methodReplies, abilityReplies).collect(toList());
} 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);
throw propagate(e);
}
}
/**
* Invokes the method and retrieves its return {@link Ability}.
*
* @param method a method that returns an ability
* @return the ability returned by the method
*/
private Ability returnAbility(Method method) {
try {
return (Ability) method.invoke(this);
} catch (IllegalAccessException | InvocationTargetException e) {
BotLogger.error("Could not add ability", TAG, e);
throw propagate(e);
}
}
/**
* Invokes the method and retrieves its returned Reply.
*
* @param method a method that returns a reply
* @return the reply returned by the method
*/
private Reply returnReply(Method method) {
try {
return (Reply) method.invoke(this);
} catch (IllegalAccessException | InvocationTargetException e) {
BotLogger.error("Could not add reply", TAG, e);
throw propagate(e);
}
}
private void postConsumption(Pair<MessageContext, Ability> pair) {
ofNullable(pair.b().postAction())
.ifPresent(consumer -> consumer.accept(pair.a()));
}
Pair<MessageContext, Ability> consumeUpdate(Pair<MessageContext, Ability> pair) {
pair.b().action().accept(pair.a());
return pair;
}
Pair<MessageContext, Ability> getContext(Trio<Update, Ability, String[]> trio) {
Update update = trio.a();
EndUser user = fromUser(AbilityUtils.getUser(update));
return Pair.of(newContext(update, user, getChatId(update), trio.c()), trio.b());
}
boolean checkBlacklist(Update update) {
Integer id = AbilityUtils.getUser(update).getId();
return id == creatorId() || !blacklist().contains(id);
}
boolean checkInput(Trio<Update, Ability, String[]> trio) {
String[] tokens = trio.c();
int abilityTokens = trio.b().tokens();
boolean isOk = abilityTokens == 0 || (tokens.length > 0 && tokens.length == abilityTokens);
if (!isOk)
sender.send(String.format("Sorry, this feature requires %d additional %s.", abilityTokens, abilityTokens == 1 ? "input" : "inputs"), getChatId(trio.a()));
return isOk;
}
boolean checkLocality(Trio<Update, Ability, String[]> trio) {
Update update = trio.a();
Locality locality = isUserMessage(update) ? USER : GROUP;
Locality abilityLocality = trio.b().locality();
boolean isOk = abilityLocality == ALL || locality == abilityLocality;
if (!isOk)
sender.send(String.format("Sorry, %s-only feature.", abilityLocality.toString().toLowerCase()), getChatId(trio.a()));
return isOk;
}
boolean checkPrivacy(Trio<Update, Ability, String[]> trio) {
Update update = trio.a();
EndUser user = fromUser(AbilityUtils.getUser(update));
Privacy privacy;
int id = user.id();
privacy = isCreator(id) ? CREATOR : isAdmin(id) ? ADMIN : PUBLIC;
boolean isOk = privacy.compareTo(trio.b().privacy()) >= 0;
if (!isOk)
sender.send(String.format("Sorry, %s-only feature.", trio.b().privacy().toString().toLowerCase()), getChatId(trio.a()));
return isOk;
}
private boolean isCreator(int id) {
return id == creatorId();
}
private boolean isAdmin(Integer id) {
return admins().contains(id);
}
boolean validateAbility(Trio<Update, Ability, String[]> trio) {
return trio.b() != null;
}
Trio<Update, Ability, String[]> getAbility(Update update) {
// Handle updates without messages
// Passing through this function means that the global flags have passed
Message msg = update.getMessage();
if (!update.hasMessage() || !msg.hasText())
return Trio.of(update, abilities.get(DEFAULT), new String[]{});
// Priority goes to text before captions
String[] tokens = msg.getText().split(" ");
if (tokens[0].startsWith("/")) {
String abilityToken = stripBotUsername(tokens[0].substring(1));
Ability ability = abilities.get(abilityToken);
tokens = Arrays.copyOfRange(tokens, 1, tokens.length);
return Trio.of(update, ability, tokens);
} else {
Ability ability = abilities.get(DEFAULT);
return Trio.of(update, ability, tokens);
}
}
private String stripBotUsername(String token) {
return compile(format("@%s", botUsername), CASE_INSENSITIVE)
.matcher(token)
.replaceAll("");
}
Update addUser(Update update) {
EndUser endUser = fromUser(AbilityUtils.getUser(update));
users().compute(endUser.id(), (id, user) -> {
if (user == null) {
updateUserId(user, endUser);
return endUser;
}
if (!user.equals(endUser)) {
updateUserId(user, endUser);
return endUser;
}
return user;
});
db.commit();
return update;
}
private void updateUserId(EndUser oldUser, EndUser newUser) {
if (oldUser != null && oldUser.username() != null) {
// Remove old username -> ID
userIds().remove(oldUser.username());
}
if (newUser.username() != null) {
// Add new mapping with the new username
userIds().put(newUser.username().toLowerCase(), newUser.id());
}
}
boolean filterReply(Update update) {
return replies.stream()
.filter(reply -> reply.isOkFor(update))
.map(reply -> {
reply.actOn(update);
return false;
})
.reduce(true, Boolean::logicalAnd);
}
boolean checkMessageFlags(Trio<Update, Ability, String[]> trio) {
Ability ability = trio.b();
Update update = trio.a();
// The following variable is required to avoid bug #JDK-8044546
BiFunction<Boolean, Predicate<Update>, Boolean> flagAnd = (flag, nextFlag) -> flag && nextFlag.test(update);
return ability.flags().stream()
.reduce(true, flagAnd, Boolean::logicalAnd);
}
private File downloadFileWithId(String fileId) throws TelegramApiException {
return sender.downloadFile(sender.getFile(new GetFile().setFileId(fileId)));
}
}

View File

@ -0,0 +1,85 @@
package org.telegram.abilitybots.api.db;
import org.telegram.abilitybots.api.bot.AbilityBot;
import org.telegram.telegrambots.api.objects.Update;
import java.io.Closeable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This interface represents the high-level methods exposed to the user when handling an {@link Update}.
* Example usage:
* <p><code>Ability.builder().action(ctx -> {db.getSet(USERS); doSomething();})* </code></p>
* {@link AbilityBot} contains a handle on the <code>db</code> that the user can use inside his declared abilities.
*
* @author Abbas Abou Daya
*/
public interface DBContext extends Closeable {
/**
* @param name the unique name of the {@link List}
* @param <T> the type that the List holds
* @return the List with the specified name
*/
<T> List<T> getList(String name);
/**
* @param name the unique name of the {@link Map}
* @param <K> the type of the Map keys
* @param <V> the type of the Map values
* @return the Map with the specified name
*/
<K, V> Map<K, V> getMap(String name);
/**
* @param name the unique name of the {@link Set}
* @param <T> the type that the Set holds
* @return the Set with the specified name
*/
<T> Set<T> getSet(String name);
/**
* @return a high-level summary of the database structures (Sets, Lists, Maps, ...) present.
*/
String summary();
/**
* Implementations of this method are free to return any object such as XML, JSON, etc...
*
* @return a backup of the DB
*/
Object backup();
/**
* The object passed to this method need to conform to the implementation of the {@link DBContext#backup()} method.
*
* @param backup the backup of the database containing all the structures
* @return <tt>true</tt> if the database successfully recovered
*/
boolean recover(Object backup);
/**
* @param name the name of the data structure
* @return the high-level information of the structure
*/
String info(String name);
/**
* Commits the database to its persistent layer. Implementations are free to not implement this method as it is not compulsory.
*/
void commit();
/**
* Clears the data structures present in the database.
* <p>
* This method does not delete the data-structure themselves, but leaves them empty.
*/
void clear();
/**
* @param name the name of the data structure
* @return <tt>true</tt> if this database contains the specified structure name
*/
boolean contains(String name);
}

View File

@ -0,0 +1,224 @@
package org.telegram.abilitybots.api.db;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.Serializer;
import org.telegram.abilitybots.api.util.Pair;
import org.telegram.telegrambots.logging.BotLogger;
import java.io.IOException;
import java.util.*;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.format;
import static java.util.Objects.isNull;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.StreamSupport.stream;
import static org.mapdb.Serializer.JAVA;
import static org.telegram.abilitybots.api.bot.AbilityBot.USERS;
/**
* An implementation of {@link DBContext} that relies on a {@link DB}.
*
* @author Abbas Abou Daya
* @see <a href="https://github.com/jankotek/mapdb">MapDB project</a>
*/
@SuppressWarnings("unchecked")
public class MapDBContext implements DBContext {
private static final String TAG = DBContext.class.getSimpleName();
private final DB db;
private final ObjectMapper objectMapper;
public MapDBContext(DB db) {
this.db = db;
objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
}
/**
* This DB returned by this method does not trigger deletion on JVM shutdown.
*
* @param name name of the DB file
* @return an online instance of {@link MapDBContext}
*/
public static DBContext onlineInstance(String name) {
DB db = DBMaker
.fileDB(name)
.fileMmapEnableIfSupported()
.closeOnJvmShutdown()
.transactionEnable()
.make();
return new MapDBContext(db);
}
/**
* This DB returned by this method gets deleted on JVM shutdown.
*
* @param name name of the DB file
* @return an offline instance of {@link MapDBContext}
*/
public static DBContext offlineInstance(String name) {
DB db = DBMaker
.fileDB(name)
.fileMmapEnableIfSupported()
.closeOnJvmShutdown()
.cleanerHackEnable()
.transactionEnable()
.fileDeleteAfterClose()
.make();
return new MapDBContext(db);
}
@Override
public <T> List<T> getList(String name) {
return (List<T>) db.<T>indexTreeList(name, Serializer.JAVA).createOrOpen();
}
@Override
public <K, V> Map<K, V> getMap(String name) {
return db.<K, V>hashMap(name, JAVA, JAVA).createOrOpen();
}
@Override
public <T> Set<T> getSet(String name) {
return (Set<T>) db.<T>hashSet(name, JAVA).createOrOpen();
}
@Override
public String summary() {
return stream(db.getAllNames().spliterator(), false)
.map(this::info)
.reduce(new StringJoiner("\n"), StringJoiner::add, StringJoiner::merge)
.toString();
}
@Override
public Object backup() {
Map<String, Object> collectedMap = localCopy();
return writeAsString(collectedMap);
}
@Override
public boolean recover(Object backup) {
Map<String, Object> snapshot = localCopy();
try {
Map<String, Object> backupData = objectMapper.readValue(backup.toString(), new TypeReference<HashMap<String, Object>>() {
});
doRecover(backupData);
return true;
} catch (IOException e) {
BotLogger.error(format("Could not recover DB data from file with String representation %s", backup), TAG, e);
// Attempt to fallback to data snapshot before recovery
doRecover(snapshot);
return false;
}
}
@Override
public String info(String name) {
Object struct = db.get(name);
if (isNull(struct))
throw new IllegalStateException(format("DB structure with name [%s] does not exist", name));
if (struct instanceof Set)
return format("%s - Set - %d", name, ((Set) struct).size());
else if (struct instanceof List)
return format("%s - List - %d", name, ((List) struct).size());
else if (struct instanceof Map)
return format("%s - Map - %d", name, ((Map) struct).size());
else
return format("%s - %s", name, struct.getClass().getSimpleName());
}
@Override
public void commit() {
db.commit();
}
@Override
public void clear() {
db.getAllNames().forEach(name -> {
Object struct = db.get(name);
if (struct instanceof Collection)
((Collection) struct).clear();
else if (struct instanceof Map)
((Map) struct).clear();
});
commit();
}
@Override
public boolean contains(String name) {
return db.exists(name);
}
@Override
public void close() throws IOException {
db.close();
}
/**
* @return a local non-thread safe copy of the database
*/
private Map<String, Object> localCopy() {
return db.getAll().entrySet().stream().map(entry -> {
Object struct = entry.getValue();
if (struct instanceof Set)
return Pair.of(entry.getKey(), newHashSet((Set) struct));
else if (struct instanceof List)
return Pair.of(entry.getKey(), newArrayList((List) struct));
else if (struct instanceof Map)
return Pair.of(entry.getKey(), newHashMap((Map) struct));
else
return Pair.of(entry.getKey(), struct);
}).collect(toMap(pair -> (String) pair.a(), Pair::b));
}
private void doRecover(Map<String, Object> backupData) {
clear();
backupData.forEach((name, value) -> {
if (value instanceof Set) {
Set entrySet = (Set) value;
getSet(name).addAll(entrySet);
} else if (value instanceof Map) {
Map<Object, Object> entryMap = (Map) value;
// TODO: This is ugly
// Special handling of USERS since the key is an integer. JSON by default considers a map a JSONObject.
// Keys are serialized and deserialized as String
if (name.equals(USERS))
entryMap = entryMap.entrySet().stream()
.map(entry -> Pair.of(Integer.parseInt(entry.getKey().toString()), entry.getValue()))
.collect(toMap(Pair::a, Pair::b));
getMap(name).putAll(entryMap);
} else if (value instanceof List) {
List entryList = (List) value;
getList(name).addAll(entryList);
} else {
BotLogger.error(TAG, format("Unable to identify object type during DB recovery, entry name: %s", name));
}
});
commit();
}
private String writeAsString(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
BotLogger.info(format("Failed to read the JSON representation of object: %s", obj), TAG, e);
return "Error reading required data...";
}
}
}

View File

@ -0,0 +1,208 @@
package org.telegram.abilitybots.api.objects;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import org.telegram.telegrambots.api.objects.Update;
import org.telegram.telegrambots.logging.BotLogger;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static java.util.Objects.hash;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.*;
/**
* An ability is a fully-fledged bot action that contains all the necessary information to process:
* <ol>
* <li>A response to a command</li>
* <li>A post-response to a command</li>
* <li>A reply to a sequence of actions</li>
* </ol>
* <p>
* In-order to instantiate an ability, you can call {@link Ability#builder()} to get the {@link AbilityBuilder}.
* Once you're done setting your ability, you'll call {@link AbilityBuilder#build()} to get your constructed ability.
* <p>
* The only optional fields in an ability are {@link Ability#info}, {@link Ability#postAction}, {@link Ability#flags} and {@link Ability#replies}.
*
* @author Abbas Abou Daya
*/
public final class Ability {
private static final String TAG = Ability.class.getSimpleName();
private final String name;
private final String info;
private final Locality locality;
private final Privacy privacy;
private final int argNum;
private final Consumer<MessageContext> action;
private final Consumer<MessageContext> postAction;
private final List<Reply> replies;
private final List<Predicate<Update>> flags;
@SafeVarargs
private Ability(String name, String info, Locality locality, Privacy privacy, int argNum, Consumer<MessageContext> action, Consumer<MessageContext> postAction, List<Reply> replies, Predicate<Update>... flags) {
checkArgument(!isEmpty(name), "Method name cannot be empty");
checkArgument(!containsWhitespace(name), "Method name cannot contain spaces");
checkArgument(isAlphanumeric(name), "Method name can only be alpha-numeric", name);
this.name = name;
this.info = info;
this.locality = checkNotNull(locality, "Please specify a valid locality setting. Use the Locality enum class");
this.privacy = checkNotNull(privacy, "Please specify a valid privacy setting. Use the Privacy enum class");
checkArgument(argNum >= 0, "The number of arguments the method can handle CANNOT be negative. " +
"Use the number 0 if the method ignores the arguments OR uses as many as appended");
this.argNum = argNum;
this.action = checkNotNull(action, "Method action can't be empty. Please assign a function by using .action() method");
if (postAction == null)
BotLogger.info(TAG, format("No post action was detected for method with name [%s]", name));
this.flags = ofNullable(flags).map(Arrays::asList).orElse(newArrayList());
this.postAction = postAction;
this.replies = replies;
}
public static AbilityBuilder builder() {
return new AbilityBuilder();
}
public String name() {
return name;
}
public String info() {
return info;
}
public Locality locality() {
return locality;
}
public Privacy privacy() {
return privacy;
}
public int tokens() {
return argNum;
}
public Consumer<MessageContext> action() {
return action;
}
public Consumer<MessageContext> postAction() {
return postAction;
}
public List<Reply> replies() {
return replies;
}
public List<Predicate<Update>> flags() {
return flags;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.add("locality", locality)
.add("privacy", privacy)
.add("argNum", argNum)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Ability ability = (Ability) o;
return argNum == ability.argNum &&
Objects.equal(name, ability.name) &&
locality == ability.locality &&
privacy == ability.privacy;
}
@Override
public int hashCode() {
return hash(name, info, locality, privacy, argNum, action, postAction, replies, flags);
}
public static class AbilityBuilder {
private String name;
private String info;
private Privacy privacy;
private Locality locality;
private int argNum;
private Consumer<MessageContext> consumer;
private Consumer<MessageContext> postConsumer;
private List<Reply> replies;
private Flag[] flags;
private AbilityBuilder() {
replies = newArrayList();
}
public AbilityBuilder action(Consumer<MessageContext> consumer) {
this.consumer = consumer;
return this;
}
public AbilityBuilder name(String name) {
this.name = name;
return this;
}
public AbilityBuilder info(String info) {
this.info = info;
return this;
}
public AbilityBuilder flag(Flag... flags) {
this.flags = flags;
return this;
}
public AbilityBuilder locality(Locality type) {
this.locality = type;
return this;
}
public AbilityBuilder input(int argNum) {
this.argNum = argNum;
return this;
}
public AbilityBuilder privacy(Privacy privacy) {
this.privacy = privacy;
return this;
}
public AbilityBuilder post(Consumer<MessageContext> postConsumer) {
this.postConsumer = postConsumer;
return this;
}
@SafeVarargs
public final AbilityBuilder reply(Consumer<Update> action, Predicate<Update>... conditions) {
replies.add(Reply.of(action, conditions));
return this;
}
public Ability build() {
return new Ability(name, info, locality, privacy, argNum, consumer, postConsumer, replies, flags);
}
}
}

View File

@ -0,0 +1,138 @@
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.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

@ -0,0 +1,46 @@
package org.telegram.abilitybots.api.objects;
import org.telegram.abilitybots.api.objects.Ability.AbilityBuilder;
import org.telegram.telegrambots.api.objects.Update;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static java.util.Objects.nonNull;
/**
* Flags are an conditions that are applied on an {@link Update}.
* <p>
* They can be used on {@link AbilityBuilder#flag(Flag...)} and on the post conditions in {@link AbilityBuilder#reply(Consumer, Predicate[])}.
*
* @author Abbas Abou Daya
*/
public enum Flag implements Predicate<Update> {
// Update Flags
NONE(update -> true),
MESSAGE(Update::hasMessage),
CALLBACK_QUERY(Update::hasCallbackQuery),
CHANNEL_POST(Update::hasChannelPost),
EDITED_CHANNEL_POST(Update::hasEditedChannelPost),
EDITED_MESSAGE(Update::hasEditedMessage),
INLINE_QUERY(Update::hasInlineQuery),
CHOSEN_INLINE_QUERY(Update::hasChosenInlineQuery),
// Message Flags
REPLY(update -> update.getMessage().isReply()),
DOCUMENT(upd -> upd.getMessage().hasDocument()),
TEXT(upd -> upd.getMessage().hasText()),
PHOTO(upd -> upd.getMessage().hasPhoto()),
LOCATION(upd -> upd.getMessage().hasLocation()),
CAPTION(upd -> nonNull(upd.getMessage().getCaption()));
private final Predicate<Update> predicate;
Flag(Predicate<Update> predicate) {
this.predicate = predicate;
}
public boolean test(Update update) {
return nonNull(update) && predicate.test(update);
}
}

View File

@ -0,0 +1,23 @@
package org.telegram.abilitybots.api.objects;
/**
* Locality identifies the location in which you want your message to be accessed.
* <p>
* If locality of your message is set to <code>USER</code>, then the ability will only be executed if its being called in a user private chat.
*
* @author Abbas Abou Daya
*/
public enum Locality {
/**
* Ability would be valid for groups and private user chats
*/
ALL,
/**
* Only user chats
*/
USER,
/**
* Only group chats
*/
GROUP
}

View File

@ -0,0 +1,123 @@
package org.telegram.abilitybots.api.objects;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import org.telegram.telegrambots.api.objects.Update;
import java.util.Arrays;
/**
* MessageContext is a wrapper class to the {@link Update}, originating end-user and the arguments present in its message (if any).
* <p>
* A user is not bound to the usage of this higher level context as it's possible to fetch the underlying {@link Update} using {@link MessageContext#update()}.
*
* @author Abbas Abou Daya
*/
public class MessageContext {
private final EndUser user;
private final Long chatId;
private final String[] arguments;
private final Update update;
private MessageContext(Update update, EndUser user, Long chatId, String[] arguments) {
this.user = user;
this.chatId = chatId;
this.update = update;
this.arguments = arguments;
}
public static MessageContext newContext(Update update, EndUser user, Long chatId, String... arguments) {
return new MessageContext(update, user, chatId, arguments);
}
/**
* @return the originating Telegram user of this update
*/
public EndUser user() {
return user;
}
/**
* @return the originating chatId, maps correctly to both group and user-private chats
*/
public Long chatId() {
return chatId;
}
/**
* If there's no message in the update, then this will an empty array.
*
* @return the text sent by the user message.
*/
public String[] arguments() {
return arguments;
}
/**
* @return the first argument directly after the command
* @throws IllegalStateException if message has no arguments
*/
public String firstArg() {
checkLength();
return arguments[0];
}
/**
* @return the second argument directly after the command
* @throws IllegalStateException if message has no arguments
*/
public String secondArg() {
checkLength();
return arguments[1 % arguments.length];
}
/**
* @return the third argument directly after the command
* @throws IllegalStateException if message has no arguments
*/
public String thirdArg() {
checkLength();
return arguments[2 % arguments.length];
}
/**
* @return the actual update behind this context
*/
public Update update() {
return update;
}
private void checkLength() {
if (arguments.length == 0)
throw new IllegalStateException("This message has no arguments");
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("user", user)
.add("chatId", chatId)
.add("arguments", arguments)
.add("update", update)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
MessageContext that = (MessageContext) o;
return Objects.equal(user, that.user) &&
Objects.equal(chatId, that.chatId) &&
Arrays.equals(arguments, that.arguments) &&
Objects.equal(update, that.update);
}
@Override
public int hashCode() {
return Objects.hashCode(user, chatId, Arrays.hashCode(arguments), update);
}
}

View File

@ -0,0 +1,21 @@
package org.telegram.abilitybots.api.objects;
/**
* Privacy represents a restriction on <b>who</b> can use the ability.
*
* @author Abbas Abou Daya
*/
public enum Privacy {
/**
* Anybody who is not a bot admin or its creator will be considered as a public user.
*/
PUBLIC,
/**
* A global admin of the bot, regardless of the group the bot is in.
*/
ADMIN,
/**
* The creator of the bot.
*/
CREATOR
}

View File

@ -0,0 +1,69 @@
package org.telegram.abilitybots.api.objects;
import com.google.common.base.MoreObjects;
import org.telegram.telegrambots.api.objects.Update;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static java.util.Arrays.asList;
/**
* A reply consists of update conditionals and an action to be applied on the update.
* <p>
* 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
*/
public final class Reply {
public final List<Predicate<Update>> conditions;
public final Consumer<Update> action;
private Reply(List<Predicate<Update>> conditions, Consumer<Update> action) {
this.conditions = conditions;
this.action = action;
}
@SafeVarargs
public static Reply of(Consumer<Update> action, Predicate<Update>... conditions) {
return new Reply(asList(conditions), action);
}
public boolean isOkFor(Update update) {
// The following variable is required to avoid bug #JDK-8044546
BiFunction<Boolean, Predicate<Update>, Boolean> stateAnd = (state, cond) -> state && cond.test(update);
return conditions.stream().reduce(true, stateAnd, Boolean::logicalAnd);
}
public void actOn(Update update) {
action.accept(update);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Reply reply = (Reply) o;
return Objects.equals(conditions, reply.conditions) &&
Objects.equals(action, reply.action);
}
@Override
public int hashCode() {
return Objects.hash(conditions, action);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("conditions", conditions)
.add("action", action)
.toString();
}
}

View File

@ -0,0 +1,493 @@
package org.telegram.abilitybots.api.sender;
import org.telegram.telegrambots.api.methods.*;
import org.telegram.telegrambots.api.methods.games.GetGameHighScores;
import org.telegram.telegrambots.api.methods.games.SetGameScore;
import org.telegram.telegrambots.api.methods.groupadministration.*;
import org.telegram.telegrambots.api.methods.pinnedmessages.PinChatMessage;
import org.telegram.telegrambots.api.methods.pinnedmessages.UnpinChatMessage;
import org.telegram.telegrambots.api.methods.send.*;
import org.telegram.telegrambots.api.methods.updates.DeleteWebhook;
import org.telegram.telegrambots.api.methods.updatingmessages.DeleteMessage;
import org.telegram.telegrambots.api.methods.updatingmessages.EditMessageCaption;
import org.telegram.telegrambots.api.methods.updatingmessages.EditMessageReplyMarkup;
import org.telegram.telegrambots.api.methods.updatingmessages.EditMessageText;
import org.telegram.telegrambots.api.objects.*;
import org.telegram.telegrambots.api.objects.games.GameHighScore;
import org.telegram.telegrambots.api.objects.replykeyboard.ForceReplyKeyboard;
import org.telegram.telegrambots.bots.DefaultAbsSender;
import org.telegram.telegrambots.exceptions.TelegramApiException;
import org.telegram.telegrambots.logging.BotLogger;
import org.telegram.telegrambots.updateshandlers.DownloadFileCallback;
import org.telegram.telegrambots.updateshandlers.SentCallback;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;
/**
* The default implementation of the {@link MessageSender}. This serves as a proxy to the {@link DefaultAbsSender} methods.
* <p>Most of the methods below will be directly calling the bot's similar functions. However, there are some methods introduced to ease sending messages such as:</p>
* <ol>
* <li>{@link DefaultMessageSender#sendMd(String, long)} - with markdown</li>
* <li>{@link DefaultMessageSender#send(String, long)} - without markdown</li>
* </ol>
*
* @author Abbas Abou Daya
*/
public class DefaultMessageSender implements MessageSender {
private static final String TAG = MessageSender.class.getName();
private DefaultAbsSender bot;
public DefaultMessageSender(DefaultAbsSender bot) {
this.bot = bot;
}
@Override
public Optional<Message> send(String message, long id) {
return doSendMessage(message, id, false);
}
@Override
public Optional<Message> sendMd(String message, long id) {
return doSendMessage(message, id, true);
}
@Override
public Optional<Message> forceReply(String message, long id) {
SendMessage msg = new SendMessage();
msg.setText(message);
msg.setChatId(id);
msg.setReplyMarkup(new ForceReplyKeyboard());
return optionalSendMessage(msg);
}
@Override
public Boolean answerInlineQuery(AnswerInlineQuery answerInlineQuery) throws TelegramApiException {
return bot.execute(answerInlineQuery);
}
@Override
public Boolean sendChatAction(SendChatAction sendChatAction) throws TelegramApiException {
return bot.execute(sendChatAction);
}
@Override
public Message forwardMessage(ForwardMessage forwardMessage) throws TelegramApiException {
return bot.execute(forwardMessage);
}
@Override
public Message sendLocation(SendLocation sendLocation) throws TelegramApiException {
return bot.execute(sendLocation);
}
@Override
public Message sendVenue(SendVenue sendVenue) throws TelegramApiException {
return bot.execute(sendVenue);
}
@Override
public Message sendContact(SendContact sendContact) throws TelegramApiException {
return bot.execute(sendContact);
}
@Override
public Boolean kickMember(KickChatMember kickChatMember) throws TelegramApiException {
return bot.execute(kickChatMember);
}
@Override
public Boolean unbanMember(UnbanChatMember unbanChatMember) throws TelegramApiException {
return bot.execute(unbanChatMember);
}
@Override
public Boolean leaveChat(LeaveChat leaveChat) throws TelegramApiException {
return bot.execute(leaveChat);
}
@Override
public Chat getChat(GetChat getChat) throws TelegramApiException {
return bot.execute(getChat);
}
@Override
public List<ChatMember> getChatAdministrators(GetChatAdministrators getChatAdministrators) throws TelegramApiException {
return bot.execute(getChatAdministrators);
}
@Override
public ChatMember getChatMember(GetChatMember getChatMember) throws TelegramApiException {
return bot.execute(getChatMember);
}
@Override
public Integer getChatMemberCount(GetChatMemberCount getChatMemberCount) throws TelegramApiException {
return bot.execute(getChatMemberCount);
}
@Override
public Boolean setChatPhoto(SetChatPhoto setChatPhoto) throws TelegramApiException {
return bot.setChatPhoto(setChatPhoto);
}
@Override
public Boolean deleteChatPhoto(DeleteChatPhoto deleteChatPhoto) throws TelegramApiException {
return bot.execute(deleteChatPhoto);
}
@Override
public void deleteChatPhoto(DeleteChatPhoto deleteChatPhoto, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(deleteChatPhoto, sentCallback);
}
@Override
public Boolean pinChatMessage(PinChatMessage pinChatMessage) throws TelegramApiException {
return bot.execute(pinChatMessage);
}
@Override
public void pinChatMessage(PinChatMessage pinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(pinChatMessage, sentCallback);
}
@Override
public Boolean unpinChatMessage(UnpinChatMessage unpinChatMessage) throws TelegramApiException {
return bot.execute(unpinChatMessage);
}
@Override
public void unpinChatMessage(UnpinChatMessage unpinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(unpinChatMessage, sentCallback);
}
@Override
public Boolean promoteChatMember(PromoteChatMember promoteChatMember) throws TelegramApiException {
return bot.execute(promoteChatMember);
}
@Override
public void promoteChatMember(PromoteChatMember promoteChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(promoteChatMember, sentCallback);
}
@Override
public Boolean restrictChatMember(RestrictChatMember restrictChatMember) throws TelegramApiException {
return bot.execute(restrictChatMember);
}
@Override
public void restrictChatMember(RestrictChatMember restrictChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(restrictChatMember, sentCallback);
}
@Override
public Boolean setChatDescription(SetChatDescription setChatDescription) throws TelegramApiException {
return bot.execute(setChatDescription);
}
@Override
public void setChatDescription(SetChatDescription setChatDescription, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(setChatDescription, sentCallback);
}
@Override
public Boolean setChatTite(SetChatTitle setChatTitle) throws TelegramApiException {
return bot.execute(setChatTitle);
}
@Override
public void setChatTite(SetChatTitle setChatTitle, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(setChatTitle, sentCallback);
}
@Override
public String exportChatInviteLink(ExportChatInviteLink exportChatInviteLink) throws TelegramApiException {
return bot.execute(exportChatInviteLink);
}
@Override
public void exportChatInviteLinkAsync(ExportChatInviteLink exportChatInviteLink, SentCallback<String> sentCallback) throws TelegramApiException {
bot.executeAsync(exportChatInviteLink, sentCallback);
}
@Override
public Boolean deleteMessage(DeleteMessage deleteMessage) throws TelegramApiException {
return bot.execute(deleteMessage);
}
@Override
public void deleteMessageAsync(DeleteMessage deleteMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(deleteMessage, sentCallback);
}
@Override
public Serializable editMessageText(EditMessageText editMessageText) throws TelegramApiException {
return bot.execute(editMessageText);
}
@Override
public Serializable editMessageCaption(EditMessageCaption editMessageCaption) throws TelegramApiException {
return bot.execute(editMessageCaption);
}
@Override
public Serializable editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup) throws TelegramApiException {
return bot.execute(editMessageReplyMarkup);
}
@Override
public Boolean answerCallbackQuery(AnswerCallbackQuery answerCallbackQuery) throws TelegramApiException {
return bot.execute(answerCallbackQuery);
}
@Override
public UserProfilePhotos getUserProfilePhotos(GetUserProfilePhotos getUserProfilePhotos) throws TelegramApiException {
return bot.execute(getUserProfilePhotos);
}
@Override
public java.io.File downloadFile(String path) throws TelegramApiException {
return bot.downloadFile(path);
}
@Override
public void downloadFileAsync(String path, DownloadFileCallback<String> callback) throws TelegramApiException {
bot.downloadFileAsync(path, callback);
}
@Override
public java.io.File downloadFile(File file) throws TelegramApiException {
return bot.downloadFile(file);
}
@Override
public void downloadFileAsync(File file, DownloadFileCallback<File> callback) throws TelegramApiException {
bot.downloadFileAsync(file, callback);
}
@Override
public File getFile(GetFile getFile) throws TelegramApiException {
return bot.execute(getFile);
}
@Override
public User getMe() throws TelegramApiException {
return bot.getMe();
}
@Override
public WebhookInfo getWebhookInfo() throws TelegramApiException {
return bot.getWebhookInfo();
}
@Override
public Serializable setGameScore(SetGameScore setGameScore) throws TelegramApiException {
return bot.execute(setGameScore);
}
@Override
public Serializable getGameHighScores(GetGameHighScores getGameHighScores) throws TelegramApiException {
return bot.execute(getGameHighScores);
}
@Override
public Message sendGame(SendGame sendGame) throws TelegramApiException {
return bot.execute(sendGame);
}
@Override
public Boolean deleteWebhook(DeleteWebhook deleteWebhook) throws TelegramApiException {
return bot.execute(deleteWebhook);
}
@Override
public Message sendMessage(SendMessage sendMessage) throws TelegramApiException {
return bot.execute(sendMessage);
}
@Override
public void sendMessageAsync(SendMessage sendMessage, SentCallback<Message> sentCallback) throws TelegramApiException {
bot.executeAsync(sendMessage, sentCallback);
}
@Override
public void answerInlineQueryAsync(AnswerInlineQuery answerInlineQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(answerInlineQuery, sentCallback);
}
@Override
public void sendChatActionAsync(SendChatAction sendChatAction, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(sendChatAction, sentCallback);
}
@Override
public void forwardMessageAsync(ForwardMessage forwardMessage, SentCallback<Message> sentCallback) throws TelegramApiException {
bot.executeAsync(forwardMessage, sentCallback);
}
@Override
public void sendLocationAsync(SendLocation sendLocation, SentCallback<Message> sentCallback) throws TelegramApiException {
bot.executeAsync(sendLocation, sentCallback);
}
@Override
public void sendVenueAsync(SendVenue sendVenue, SentCallback<Message> sentCallback) throws TelegramApiException {
bot.executeAsync(sendVenue, sentCallback);
}
@Override
public void sendContactAsync(SendContact sendContact, SentCallback<Message> sentCallback) throws TelegramApiException {
bot.executeAsync(sendContact, sentCallback);
}
@Override
public void kickMemberAsync(KickChatMember kickChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(kickChatMember, sentCallback);
}
@Override
public void unbanMemberAsync(UnbanChatMember unbanChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(unbanChatMember, sentCallback);
}
@Override
public void leaveChatAsync(LeaveChat leaveChat, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(leaveChat, sentCallback);
}
@Override
public void getChatAsync(GetChat getChat, SentCallback<Chat> sentCallback) throws TelegramApiException {
bot.executeAsync(getChat, sentCallback);
}
@Override
public void getChatAdministratorsAsync(GetChatAdministrators getChatAdministrators, SentCallback<ArrayList<ChatMember>> sentCallback) throws TelegramApiException {
bot.executeAsync(getChatAdministrators, sentCallback);
}
@Override
public void getChatMemberAsync(GetChatMember getChatMember, SentCallback<ChatMember> sentCallback) throws TelegramApiException {
bot.executeAsync(getChatMember, sentCallback);
}
@Override
public void getChatMemberCountAsync(GetChatMemberCount getChatMemberCount, SentCallback<Integer> sentCallback) throws TelegramApiException {
bot.executeAsync(getChatMemberCount, sentCallback);
}
@Override
public void editMessageTextAsync(EditMessageText editMessageText, SentCallback<Serializable> sentCallback) throws TelegramApiException {
bot.executeAsync(editMessageText, sentCallback);
}
@Override
public void editMessageCaptionAsync(EditMessageCaption editMessageCaption, SentCallback<Serializable> sentCallback) throws TelegramApiException {
bot.executeAsync(editMessageCaption, sentCallback);
}
@Override
public void editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup, SentCallback<Serializable> sentCallback) throws TelegramApiException {
bot.executeAsync(editMessageReplyMarkup, sentCallback);
}
@Override
public void answerCallbackQueryAsync(AnswerCallbackQuery answerCallbackQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(answerCallbackQuery, sentCallback);
}
@Override
public void getUserProfilePhotosAsync(GetUserProfilePhotos getUserProfilePhotos, SentCallback<UserProfilePhotos> sentCallback) throws TelegramApiException {
bot.executeAsync(getUserProfilePhotos, sentCallback);
}
@Override
public void getFileAsync(GetFile getFile, SentCallback<File> sentCallback) throws TelegramApiException {
bot.executeAsync(getFile, sentCallback);
}
@Override
public void getMeAsync(SentCallback<User> sentCallback) throws TelegramApiException {
bot.getMeAsync(sentCallback);
}
@Override
public void getWebhookInfoAsync(SentCallback<WebhookInfo> sentCallback) throws TelegramApiException {
bot.getWebhookInfoAsync(sentCallback);
}
@Override
public void setGameScoreAsync(SetGameScore setGameScore, SentCallback<Serializable> sentCallback) throws TelegramApiException {
bot.executeAsync(setGameScore, sentCallback);
}
@Override
public void getGameHighScoresAsync(GetGameHighScores getGameHighScores, SentCallback<ArrayList<GameHighScore>> sentCallback) throws TelegramApiException {
bot.executeAsync(getGameHighScores, sentCallback);
}
@Override
public void sendGameAsync(SendGame sendGame, SentCallback<Message> sentCallback) throws TelegramApiException {
bot.executeAsync(sendGame, sentCallback);
}
@Override
public void deleteWebhook(DeleteWebhook deleteWebhook, SentCallback<Boolean> sentCallback) throws TelegramApiException {
bot.executeAsync(deleteWebhook, sentCallback);
}
@Override
public Message sendDocument(SendDocument sendDocument) throws TelegramApiException {
return bot.sendDocument(sendDocument);
}
@Override
public Message sendPhoto(SendPhoto sendPhoto) throws TelegramApiException {
return bot.sendPhoto(sendPhoto);
}
@Override
public Message sendVideo(SendVideo sendVideo) throws TelegramApiException {
return bot.sendVideo(sendVideo);
}
@Override
public Message sendSticker(SendSticker sendSticker) throws TelegramApiException {
return bot.sendSticker(sendSticker);
}
@Override
public Message sendAudio(SendAudio sendAudio) throws TelegramApiException {
return bot.sendAudio(sendAudio);
}
@Override
public Message sendVoice(SendVoice sendVoice) throws TelegramApiException {
return bot.sendVoice(sendVoice);
}
private Optional<Message> doSendMessage(String txt, long groupId, boolean format) {
SendMessage smsg = new SendMessage();
smsg.setChatId(groupId);
smsg.setText(txt);
smsg.enableMarkdown(format);
return optionalSendMessage(smsg);
}
private Optional<Message> optionalSendMessage(SendMessage smsg) {
try {
return ofNullable(sendMessage(smsg));
} catch (TelegramApiException e) {
BotLogger.error("Could not send message", TAG, e);
return empty();
}
}
}

View File

@ -0,0 +1,200 @@
package org.telegram.abilitybots.api.sender;
import org.telegram.telegrambots.api.methods.*;
import org.telegram.telegrambots.api.methods.games.GetGameHighScores;
import org.telegram.telegrambots.api.methods.games.SetGameScore;
import org.telegram.telegrambots.api.methods.groupadministration.*;
import org.telegram.telegrambots.api.methods.pinnedmessages.PinChatMessage;
import org.telegram.telegrambots.api.methods.pinnedmessages.UnpinChatMessage;
import org.telegram.telegrambots.api.methods.send.*;
import org.telegram.telegrambots.api.methods.updates.DeleteWebhook;
import org.telegram.telegrambots.api.methods.updatingmessages.DeleteMessage;
import org.telegram.telegrambots.api.methods.updatingmessages.EditMessageCaption;
import org.telegram.telegrambots.api.methods.updatingmessages.EditMessageReplyMarkup;
import org.telegram.telegrambots.api.methods.updatingmessages.EditMessageText;
import org.telegram.telegrambots.api.objects.*;
import org.telegram.telegrambots.api.objects.games.GameHighScore;
import org.telegram.telegrambots.bots.DefaultAbsSender;
import org.telegram.telegrambots.exceptions.TelegramApiException;
import org.telegram.telegrambots.updateshandlers.DownloadFileCallback;
import org.telegram.telegrambots.updateshandlers.SentCallback;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* A sender interface that replicates {@link DefaultAbsSender} methods.
*
* @author Abbas Abou Daya
*/
public interface MessageSender {
Optional<Message> send(String message, long id);
Optional<Message> sendMd(String message, long id);
Optional<Message> forceReply(String message, long id);
Boolean answerInlineQuery(AnswerInlineQuery answerInlineQuery) throws TelegramApiException;
Boolean sendChatAction(SendChatAction sendChatAction) throws TelegramApiException;
Message forwardMessage(ForwardMessage forwardMessage) throws TelegramApiException;
Message sendLocation(SendLocation sendLocation) throws TelegramApiException;
Message sendVenue(SendVenue sendVenue) throws TelegramApiException;
Message sendContact(SendContact sendContact) throws TelegramApiException;
Boolean kickMember(KickChatMember kickChatMember) throws TelegramApiException;
Boolean unbanMember(UnbanChatMember unbanChatMember) throws TelegramApiException;
Boolean leaveChat(LeaveChat leaveChat) throws TelegramApiException;
Chat getChat(GetChat getChat) throws TelegramApiException;
List<ChatMember> getChatAdministrators(GetChatAdministrators getChatAdministrators) throws TelegramApiException;
ChatMember getChatMember(GetChatMember getChatMember) throws TelegramApiException;
Integer getChatMemberCount(GetChatMemberCount getChatMemberCount) throws TelegramApiException;
Boolean setChatPhoto(SetChatPhoto setChatPhoto) throws TelegramApiException;
Boolean deleteChatPhoto(DeleteChatPhoto deleteChatPhoto) throws TelegramApiException;
void deleteChatPhoto(DeleteChatPhoto deleteChatPhoto, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Boolean pinChatMessage(PinChatMessage pinChatMessage) throws TelegramApiException;
void pinChatMessage(PinChatMessage pinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Boolean unpinChatMessage(UnpinChatMessage unpinChatMessage) throws TelegramApiException;
void unpinChatMessage(UnpinChatMessage unpinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Boolean promoteChatMember(PromoteChatMember promoteChatMember) throws TelegramApiException;
void promoteChatMember(PromoteChatMember promoteChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Boolean restrictChatMember(RestrictChatMember restrictChatMember) throws TelegramApiException;
void restrictChatMember(RestrictChatMember restrictChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Boolean setChatDescription(SetChatDescription setChatDescription) throws TelegramApiException;
void setChatDescription(SetChatDescription setChatDescription, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Boolean setChatTite(SetChatTitle setChatTitle) throws TelegramApiException;
void setChatTite(SetChatTitle setChatTitle, SentCallback<Boolean> sentCallback) throws TelegramApiException;
String exportChatInviteLink(ExportChatInviteLink exportChatInviteLink) throws TelegramApiException;
void exportChatInviteLinkAsync(ExportChatInviteLink exportChatInviteLink, SentCallback<String> sentCallback) throws TelegramApiException;
Boolean deleteMessage(DeleteMessage deleteMessage) throws TelegramApiException;
void deleteMessageAsync(DeleteMessage deleteMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Serializable editMessageText(EditMessageText editMessageText) throws TelegramApiException;
Serializable editMessageCaption(EditMessageCaption editMessageCaption) throws TelegramApiException;
Serializable editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup) throws TelegramApiException;
Boolean answerCallbackQuery(AnswerCallbackQuery answerCallbackQuery) throws TelegramApiException;
UserProfilePhotos getUserProfilePhotos(GetUserProfilePhotos getUserProfilePhotos) throws TelegramApiException;
java.io.File downloadFile(String path) throws TelegramApiException;
void downloadFileAsync(String path, DownloadFileCallback<String> callback) throws TelegramApiException;
java.io.File downloadFile(File file) throws TelegramApiException;
void downloadFileAsync(File file, DownloadFileCallback<File> callback) throws TelegramApiException;
File getFile(GetFile getFile) throws TelegramApiException;
User getMe() throws TelegramApiException;
WebhookInfo getWebhookInfo() throws TelegramApiException;
Serializable setGameScore(SetGameScore setGameScore) throws TelegramApiException;
Serializable getGameHighScores(GetGameHighScores getGameHighScores) throws TelegramApiException;
Message sendGame(SendGame sendGame) throws TelegramApiException;
Boolean deleteWebhook(DeleteWebhook deleteWebhook) throws TelegramApiException;
Message sendMessage(SendMessage sendMessage) throws TelegramApiException;
void sendMessageAsync(SendMessage sendMessage, SentCallback<Message> sentCallback) throws TelegramApiException;
void answerInlineQueryAsync(AnswerInlineQuery answerInlineQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException;
void sendChatActionAsync(SendChatAction sendChatAction, SentCallback<Boolean> sentCallback) throws TelegramApiException;
void forwardMessageAsync(ForwardMessage forwardMessage, SentCallback<Message> sentCallback) throws TelegramApiException;
void sendLocationAsync(SendLocation sendLocation, SentCallback<Message> sentCallback) throws TelegramApiException;
void sendVenueAsync(SendVenue sendVenue, SentCallback<Message> sentCallback) throws TelegramApiException;
void sendContactAsync(SendContact sendContact, SentCallback<Message> sentCallback) throws TelegramApiException;
void kickMemberAsync(KickChatMember kickChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException;
void unbanMemberAsync(UnbanChatMember unbanChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException;
void leaveChatAsync(LeaveChat leaveChat, SentCallback<Boolean> sentCallback) throws TelegramApiException;
void getChatAsync(GetChat getChat, SentCallback<Chat> sentCallback) throws TelegramApiException;
void getChatAdministratorsAsync(GetChatAdministrators getChatAdministrators, SentCallback<ArrayList<ChatMember>> sentCallback) throws TelegramApiException;
void getChatMemberAsync(GetChatMember getChatMember, SentCallback<ChatMember> sentCallback) throws TelegramApiException;
void getChatMemberCountAsync(GetChatMemberCount getChatMemberCount, SentCallback<Integer> sentCallback) throws TelegramApiException;
void editMessageTextAsync(EditMessageText editMessageText, SentCallback<Serializable> sentCallback) throws TelegramApiException;
void editMessageCaptionAsync(EditMessageCaption editMessageCaption, SentCallback<Serializable> sentCallback) throws TelegramApiException;
void editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup, SentCallback<Serializable> sentCallback) throws TelegramApiException;
void answerCallbackQueryAsync(AnswerCallbackQuery answerCallbackQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException;
void getUserProfilePhotosAsync(GetUserProfilePhotos getUserProfilePhotos, SentCallback<UserProfilePhotos> sentCallback) throws TelegramApiException;
void getFileAsync(GetFile getFile, SentCallback<File> sentCallback) throws TelegramApiException;
void getMeAsync(SentCallback<User> sentCallback) throws TelegramApiException;
void getWebhookInfoAsync(SentCallback<WebhookInfo> sentCallback) throws TelegramApiException;
void setGameScoreAsync(SetGameScore setGameScore, SentCallback<Serializable> sentCallback) throws TelegramApiException;
void getGameHighScoresAsync(GetGameHighScores getGameHighScores, SentCallback<ArrayList<GameHighScore>> sentCallback) throws TelegramApiException;
void sendGameAsync(SendGame sendGame, SentCallback<Message> sentCallback) throws TelegramApiException;
void deleteWebhook(DeleteWebhook deleteWebhook, SentCallback<Boolean> sentCallback) throws TelegramApiException;
Message sendDocument(SendDocument sendDocument) throws TelegramApiException;
Message sendPhoto(SendPhoto sendPhoto) throws TelegramApiException;
Message sendVideo(SendVideo sendVideo) throws TelegramApiException;
Message sendSticker(SendSticker sendSticker) throws TelegramApiException;
Message sendAudio(SendAudio sendAudio) throws TelegramApiException;
Message sendVoice(SendVoice sendVoice) throws TelegramApiException;
}

View File

@ -0,0 +1,131 @@
package org.telegram.abilitybots.api.util;
import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.MessageContext;
import org.telegram.telegrambots.api.objects.Update;
import org.telegram.telegrambots.api.objects.User;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static org.telegram.abilitybots.api.objects.Flag.*;
/**
* Helper and utility methods
*/
public final class AbilityUtils {
private AbilityUtils() {
}
/**
* @param username any username
* @return the username with the preceding "@" stripped off
*/
public static String stripTag(String username) {
String lowerCase = username.toLowerCase();
return lowerCase.startsWith("@") ? lowerCase.substring(1, lowerCase.length()) : lowerCase;
}
/**
* Commits to DB.
*
* @param db the database to commit on
* @return a lambda consumer that takes in a {@link MessageContext}, used in post actions for abilities
*/
public static Consumer<MessageContext> commitTo(DBContext db) {
return ctx -> db.commit();
}
/**
* Fetches the user who caused the update.
*
* @param update a Telegram {@link Update}
* @return the originating user
* @throws IllegalStateException if the user could not be found
*/
public static User getUser(Update update) {
if (MESSAGE.test(update)) {
return update.getMessage().getFrom();
} else if (CALLBACK_QUERY.test(update)) {
return update.getCallbackQuery().getFrom();
} else if (INLINE_QUERY.test(update)) {
return update.getInlineQuery().getFrom();
} else if (CHANNEL_POST.test(update)) {
return update.getChannelPost().getFrom();
} else if (EDITED_CHANNEL_POST.test(update)) {
return update.getEditedChannelPost().getFrom();
} else if (EDITED_MESSAGE.test(update)) {
return update.getEditedMessage().getFrom();
} else if (CHOSEN_INLINE_QUERY.test(update)) {
return update.getChosenInlineQuery().getFrom();
} else {
throw new IllegalStateException("Could not retrieve originating user from update");
}
}
/**
* Fetches the direct chat ID of the specified update.
*
* @param update a Telegram {@link Update}
* @return the originating chat ID
* @throws IllegalStateException if the chat ID could not be found
*/
public static Long getChatId(Update update) {
if (MESSAGE.test(update)) {
return update.getMessage().getChatId();
} else if (CALLBACK_QUERY.test(update)) {
return update.getCallbackQuery().getMessage().getChatId();
} else if (INLINE_QUERY.test(update)) {
return (long) update.getInlineQuery().getFrom().getId();
} else if (CHANNEL_POST.test(update)) {
return update.getChannelPost().getChatId();
} else if (EDITED_CHANNEL_POST.test(update)) {
return update.getEditedChannelPost().getChatId();
} else if (EDITED_MESSAGE.test(update)) {
return update.getEditedMessage().getChatId();
} else if (CHOSEN_INLINE_QUERY.test(update)) {
return (long) update.getChosenInlineQuery().getFrom().getId();
} else {
throw new IllegalStateException("Could not retrieve originating chat ID from update");
}
}
/**
* @param update a Telegram {@link Update}
* @return <tt>true</tt> if the update contains contains a private user message
*/
public static boolean isUserMessage(Update update) {
if (MESSAGE.test(update)) {
return update.getMessage().isUserMessage();
} else if (CALLBACK_QUERY.test(update)) {
return update.getCallbackQuery().getMessage().isUserMessage();
} else if (CHANNEL_POST.test(update)) {
return update.getChannelPost().isUserMessage();
} else if (EDITED_CHANNEL_POST.test(update)) {
return update.getEditedChannelPost().isUserMessage();
} else if (EDITED_MESSAGE.test(update)) {
return update.getEditedMessage().isUserMessage();
} else if (CHOSEN_INLINE_QUERY.test(update) || INLINE_QUERY.test(update)) {
return true;
} else {
throw new IllegalStateException("Could not retrieve update context origin (user/group)");
}
}
/**
* @param username the username to add the tag to
* @return the username prefixed with the "@" tag.
*/
public static String addTag(String username) {
return "@" + username;
}
/**
* @param msg the message to be replied to
* @return a predicate that asserts that the update is a reply to the specified message.
*/
public static Predicate<Update> isReplyTo(String msg) {
return update -> update.getMessage().getReplyToMessage().getText().equals(msg);
}
}

View File

@ -0,0 +1,57 @@
package org.telegram.abilitybots.api.util;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import java.util.Objects;
public final class Pair<A, B> {
@JsonProperty("a")
private final A a;
@JsonProperty("b")
private final B b;
private Pair(A a, B b) {
this.a = a;
this.b = b;
}
@JsonCreator
public static <A, B> Pair<A, B> of(@JsonProperty("a") A a, @JsonProperty("b") B b) {
return new Pair<>(a, b);
}
public A a() {
return a;
}
public B b() {
return b;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(a, pair.a) &&
Objects.equals(b, pair.b);
}
@Override
public int hashCode() {
return Objects.hash(a, b);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("a", a)
.add("b", b)
.toString();
}
}

View File

@ -0,0 +1,66 @@
package org.telegram.abilitybots.api.util;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import java.util.Objects;
public class Trio<A, B, C> {
@JsonProperty("a")
private final A a;
@JsonProperty("b")
private final B b;
@JsonProperty("c")
private final C c;
private Trio(A a, B b, C c) {
this.a = a;
this.b = b;
this.c = c;
}
@JsonCreator
public static <A, B, C> Trio<A, B, C> of(@JsonProperty("a") A a, @JsonProperty("b") B b, @JsonProperty("c") C c) {
return new Trio<>(a, b, c);
}
public A a() {
return a;
}
public B b() {
return b;
}
public C c() {
return c;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Trio<?, ?, ?> trio = (Trio<?, ?, ?>) o;
return Objects.equals(a, trio.a) &&
Objects.equals(b, trio.b) &&
Objects.equals(c, trio.c);
}
@Override
public int hashCode() {
return Objects.hash(a, b, c);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("a", a)
.add("b", b)
.add("c", c)
.toString();
}
}

View File

@ -0,0 +1,588 @@
package org.telegram.abilitybots.api.bot;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Matchers;
import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.*;
import org.telegram.abilitybots.api.sender.MessageSender;
import org.telegram.abilitybots.api.util.Pair;
import org.telegram.abilitybots.api.util.Trio;
import org.telegram.telegrambots.api.objects.*;
import org.telegram.telegrambots.exceptions.TelegramApiException;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.format;
import static java.util.Collections.emptySet;
import static org.apache.commons.lang3.ArrayUtils.addAll;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
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.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.MESSAGE;
import static org.telegram.abilitybots.api.objects.Locality.ALL;
import static org.telegram.abilitybots.api.objects.Locality.GROUP;
import static org.telegram.abilitybots.api.objects.MessageContext.newContext;
import static org.telegram.abilitybots.api.objects.Privacy.ADMIN;
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
public class AbilityBotTest {
private static final String[] EMPTY_ARRAY = {};
private static final long GROUP_ID = 10L;
private static final String TEST = "test";
private static final String[] TEXT = {TEST};
public static final EndUser MUSER = endUser(1, "first", "last", "username");
public static final EndUser CREATOR = endUser(1337, "creatorFirst", "creatorLast", "creatorUsername");
private DefaultBot bot;
private DBContext db;
private MessageSender sender;
@Before
public void setUp() {
db = offlineInstance("db");
bot = new DefaultBot(EMPTY, EMPTY, db);
sender = mock(MessageSender.class);
bot.setSender(sender);
}
@Test
public void sendsPrivacyViolation() {
Update update = mockFullUpdate(MUSER, "/admin");
bot.onUpdateReceived(update);
verify(sender, times(1)).send(format("Sorry, %s-only feature.", "admin"), MUSER.id());
}
@Test
public void sendsLocalityViolation() {
Update update = mockFullUpdate(MUSER, "/group");
bot.onUpdateReceived(update);
verify(sender, times(1)).send(format("Sorry, %s-only feature.", "group"), MUSER.id());
}
@Test
public void sendsInputArgsViolation() {
Update update = mockFullUpdate(MUSER, "/count 1 2 3");
bot.onUpdateReceived(update);
verify(sender, times(1)).send(format("Sorry, this feature requires %d additional inputs.", 4), MUSER.id());
}
@Test
public void canProcessRepliesIfSatisfyRequirements() {
Update update = mockFullUpdate(MUSER, "must reply");
// False means the update was not pushed down the stream since it has been consumed by the reply
assertFalse(bot.filterReply(update));
verify(sender, times(1)).send("reply", MUSER.id());
}
@Test
public void canBackupDB() throws TelegramApiException {
MessageContext context = defaultContext();
bot.backupDB().action().accept(context);
verify(sender, times(1)).sendDocument(any());
}
@Test
public void canRecoverDB() throws TelegramApiException, IOException {
Update update = mockBackupUpdate();
Object backup = getDbBackup();
java.io.File backupFile = createBackupFile(backup);
when(sender.downloadFile(Matchers.any(File.class))).thenReturn(backupFile);
bot.recoverDB().replies().get(0).actOn(update);
verify(sender, times(1)).send(RECOVER_SUCCESS, GROUP_ID);
assertEquals("Bot recovered but the DB is still not in sync", db.getSet(TEST), newHashSet(TEST));
assertTrue("Could not delete backup file", backupFile.delete());
}
@Test
public void canFilterOutReplies() {
Update update = mock(Update.class);
when(update.hasMessage()).thenReturn(false);
assertTrue(bot.filterReply(update));
}
@Test
public void canDemote() {
addUsers(MUSER);
bot.admins().add(MUSER.id());
MessageContext context = defaultContext();
bot.demoteAdmin().action().accept(context);
Set<Integer> actual = bot.admins();
Set<Integer> expected = emptySet();
assertEquals("Could not sudont super-admin", expected, actual);
}
@Test
public void canPromote() {
addUsers(MUSER);
MessageContext context = defaultContext();
bot.promoteAdmin().action().accept(context);
Set<Integer> actual = bot.admins();
Set<Integer> expected = newHashSet(MUSER.id());
assertEquals("Could not sudo user", expected, actual);
}
@Test
public void canBanUser() {
addUsers(MUSER);
MessageContext context = defaultContext();
bot.banUser().action().accept(context);
Set<Integer> actual = bot.blacklist();
Set<Integer> expected = newHashSet(MUSER.id());
assertEquals("The ban was not emplaced", expected, actual);
}
@Test
public void canUnbanUser() {
addUsers(MUSER);
bot.blacklist().add(MUSER.id());
MessageContext context = defaultContext();
bot.unbanUser().action().accept(context);
Set<Integer> actual = bot.blacklist();
Set<Integer> expected = newHashSet();
assertEquals("The ban was not lifted", expected, actual);
}
@NotNull
private MessageContext defaultContext() {
MessageContext context = mock(MessageContext.class);
when(context.user()).thenReturn(CREATOR);
when(context.firstArg()).thenReturn(MUSER.username());
return context;
}
@Test
public void cannotBanCreator() {
addUsers(MUSER, CREATOR);
MessageContext context = mock(MessageContext.class);
when(context.user()).thenReturn(MUSER);
when(context.firstArg()).thenReturn(CREATOR.username());
bot.banUser().action().accept(context);
Set<Integer> actual = bot.blacklist();
Set<Integer> expected = newHashSet(MUSER.id());
assertEquals("Impostor was not added to the blacklist", expected, actual);
}
private void addUsers(EndUser... users) {
Arrays.stream(users).forEach(user -> {
bot.users().put(user.id(), user);
bot.userIds().put(user.username().toLowerCase(), user.id());
});
}
@Test
public void creatorCanClaimBot() {
MessageContext context = mock(MessageContext.class);
when(context.user()).thenReturn(CREATOR);
bot.claimCreator().action().accept(context);
Set<Integer> actual = bot.admins();
Set<Integer> expected = newHashSet(CREATOR.id());
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
public void bannedCreatorPassesBlacklistCheck() {
bot.blacklist().add(CREATOR.id());
Update update = mock(Update.class);
Message message = mock(Message.class);
User user = mock(User.class);
mockUser(update, message, user);
boolean notBanned = bot.checkBlacklist(update);
assertTrue("Creator is banned", notBanned);
}
@Test
public void canAddUser() {
Update update = mock(Update.class);
Message message = mock(Message.class);
User user = mock(User.class);
mockAlternateUser(update, message, user, MUSER);
bot.addUser(update);
Map<String, Integer> expectedUserIds = ImmutableMap.of(MUSER.username(), MUSER.id());
Map<Integer, EndUser> expectedUsers = ImmutableMap.of(MUSER.id(), MUSER);
assertEquals("User was not added", expectedUserIds, bot.userIds());
assertEquals("User was not added", expectedUsers, bot.users());
}
@Test
public void canEditUser() {
addUsers(MUSER);
Update update = mock(Update.class);
Message message = mock(Message.class);
User user = mock(User.class);
String newUsername = MUSER.username() + "-test";
String newFirstName = MUSER.firstName() + "-test";
String newLastName = MUSER.lastName() + "-test";
int sameId = MUSER.id();
EndUser changedUser = endUser(sameId, newFirstName, newLastName, newUsername);
mockAlternateUser(update, message, user, changedUser);
bot.addUser(update);
Map<String, Integer> expectedUserIds = ImmutableMap.of(changedUser.username(), changedUser.id());
Map<Integer, EndUser> expectedUsers = ImmutableMap.of(changedUser.id(), changedUser);
assertEquals("User was not properly edited", bot.userIds(), expectedUserIds);
assertEquals("User was not properly edited", expectedUsers, expectedUsers);
}
@Test
public void canValidateAbility() {
Trio<Update, Ability, String[]> invalidPair = Trio.of(null, null, null);
Ability validAbility = getDefaultBuilder().build();
Trio<Update, Ability, String[]> validPair = Trio.of(null, validAbility, null);
assertEquals("Bot can't validate ability properly", false, bot.validateAbility(invalidPair));
assertEquals("Bot can't validate ability properly", true, bot.validateAbility(validPair));
}
@Test
public void canCheckInput() {
Update update = mockFullUpdate(MUSER, "/something");
Ability abilityWithOneInput = getDefaultBuilder()
.build();
Ability abilityWithZeroInput = getDefaultBuilder()
.input(0)
.build();
Trio<Update, Ability, String[]> trioOneArg = Trio.of(update, abilityWithOneInput, TEXT);
Trio<Update, Ability, String[]> trioZeroArg = Trio.of(update, abilityWithZeroInput, TEXT);
assertEquals("Unexpected result when applying token filter", true, bot.checkInput(trioOneArg));
trioOneArg = Trio.of(update, abilityWithOneInput, addAll(TEXT, TEXT));
assertEquals("Unexpected result when applying token filter", false, bot.checkInput(trioOneArg));
assertEquals("Unexpected result when applying token filter", true, bot.checkInput(trioZeroArg));
trioZeroArg = Trio.of(update, abilityWithZeroInput, EMPTY_ARRAY);
assertEquals("Unexpected result when applying token filter", true, bot.checkInput(trioZeroArg));
}
@Test
public void canCheckPrivacy() {
Update update = mock(Update.class);
Message message = mock(Message.class);
org.telegram.telegrambots.api.objects.User user = mock(User.class);
Ability publicAbility = getDefaultBuilder().privacy(PUBLIC).build();
Ability adminAbility = getDefaultBuilder().privacy(ADMIN).build();
Ability creatorAbility = getDefaultBuilder().privacy(Privacy.CREATOR).build();
Trio<Update, Ability, String[]> publicTrio = Trio.of(update, publicAbility, TEXT);
Trio<Update, Ability, String[]> adminTrio = Trio.of(update, adminAbility, TEXT);
Trio<Update, Ability, String[]> creatorTrio = Trio.of(update, creatorAbility, TEXT);
mockUser(update, message, user);
assertEquals("Unexpected result when checking for privacy", true, bot.checkPrivacy(publicTrio));
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(adminTrio));
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(creatorTrio));
}
@Test
public void canBlockAdminsFromCreatorAbilities() {
Update update = mock(Update.class);
Message message = mock(Message.class);
org.telegram.telegrambots.api.objects.User user = mock(User.class);
Ability creatorAbility = getDefaultBuilder().privacy(Privacy.CREATOR).build();
Trio<Update, Ability, String[]> creatorTrio = Trio.of(update, creatorAbility, TEXT);
bot.admins().add(MUSER.id());
mockUser(update, message, user);
assertEquals("Unexpected result when checking for privacy", false, bot.checkPrivacy(creatorTrio));
}
@Test
public void canCheckLocality() {
Update update = mock(Update.class);
Message message = mock(Message.class);
User user = mock(User.class);
Ability allAbility = getDefaultBuilder().locality(ALL).build();
Ability userAbility = getDefaultBuilder().locality(Locality.USER).build();
Ability groupAbility = getDefaultBuilder().locality(GROUP).build();
Trio<Update, Ability, String[]> publicTrio = Trio.of(update, allAbility, TEXT);
Trio<Update, Ability, String[]> userTrio = Trio.of(update, userAbility, TEXT);
Trio<Update, Ability, String[]> groupTrio = Trio.of(update, groupAbility, TEXT);
mockUser(update, message, user);
when(message.isUserMessage()).thenReturn(true);
assertEquals("Unexpected result when checking for locality", true, bot.checkLocality(publicTrio));
assertEquals("Unexpected result when checking for locality", true, bot.checkLocality(userTrio));
assertEquals("Unexpected result when checking for locality", false, bot.checkLocality(groupTrio));
}
@Test
public void canRetrieveContext() {
Update update = mock(Update.class);
Message message = mock(Message.class);
User user = mock(User.class);
Ability ability = getDefaultBuilder().build();
Trio<Update, Ability, String[]> trio = Trio.of(update, ability, TEXT);
when(message.getChatId()).thenReturn(GROUP_ID);
mockUser(update, message, user);
Pair<MessageContext, Ability> actualPair = bot.getContext(trio);
Pair<MessageContext, Ability> expectedPair = Pair.of(newContext(update, MUSER, GROUP_ID, TEXT), ability);
assertEquals("Unexpected result when fetching for context", expectedPair, actualPair);
}
@Test
public void canCheckGlobalFlags() {
Update update = mock(Update.class);
Message message = mock(Message.class);
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
assertEquals("Unexpected result when checking for locality", true, bot.checkGlobalFlags(update));
}
@Test(expected = ArithmeticException.class)
public void canConsumeUpdate() {
Ability ability = getDefaultBuilder()
.action((context) -> {
int x = 1 / 0;
}).build();
MessageContext context = mock(MessageContext.class);
Pair<MessageContext, Ability> pair = Pair.of(context, ability);
bot.consumeUpdate(pair);
}
@Test
public void canFetchAbility() {
Update update = mock(Update.class);
Message message = mock(Message.class);
String text = "/test";
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
when(update.getMessage().hasText()).thenReturn(true);
when(message.getText()).thenReturn(text);
Trio<Update, Ability, String[]> trio = bot.getAbility(update);
Ability expected = bot.testAbility();
Ability actual = trio.b();
assertEquals("Wrong ability was fetched", expected, actual);
}
@Test
public void canFetchDefaultAbility() {
Update update = mock(Update.class);
Message message = mock(Message.class);
String text = "test tags";
when(update.getMessage()).thenReturn(message);
when(message.getText()).thenReturn(text);
Trio<Update, Ability, String[]> trio = bot.getAbility(update);
Ability expected = bot.defaultAbility();
Ability actual = trio.b();
assertEquals("Wrong ability was fetched", expected, actual);
}
@Test
public void canCheckAbilityFlags() {
Update update = mock(Update.class);
Message message = mock(Message.class);
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
when(message.hasDocument()).thenReturn(false);
when(message.hasText()).thenReturn(true);
Ability documentAbility = getDefaultBuilder().flag(DOCUMENT, MESSAGE).build();
Ability textAbility = getDefaultBuilder().flag(Flag.TEXT, MESSAGE).build();
Trio<Update, Ability, String[]> docTrio = Trio.of(update, documentAbility, TEXT);
Trio<Update, Ability, String[]> textTrio = Trio.of(update, textAbility, TEXT);
assertEquals("Unexpected result when checking for message flags", false, bot.checkMessageFlags(docTrio));
assertEquals("Unexpected result when checking for message flags", true, bot.checkMessageFlags(textTrio));
}
@Test
public void canReportCommands() {
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 context = mock(MessageContext.class);
when(context.chatId()).thenReturn(GROUP_ID);
bot.reportCommands().action().accept(context);
verify(sender, times(1)).send("default - dis iz default command", GROUP_ID);
}
@After
public void tearDown() throws IOException {
db.clear();
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
private Update mockFullUpdate(EndUser fromUser, String args) {
bot.users().put(MUSER.id(), MUSER);
bot.users().put(CREATOR.id(), CREATOR);
bot.userIds().put(CREATOR.username(), CREATOR.id());
bot.userIds().put(MUSER.username(), MUSER.id());
bot.admins().add(CREATOR.id());
User user = mockUser(fromUser);
Update update = mock(Update.class);
when(update.hasMessage()).thenReturn(true);
Message message = mock(Message.class);
when(message.getFrom()).thenReturn(user);
when(message.getText()).thenReturn(args);
when(message.hasText()).thenReturn(true);
when(message.isUserMessage()).thenReturn(true);
when(message.getChatId()).thenReturn((long) fromUser.id());
when(update.getMessage()).thenReturn(message);
return update;
}
private void mockUser(Update update, Message message, User user) {
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
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) {
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(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
}
private Update mockBackupUpdate() {
Update update = mock(Update.class);
Message message = mock(Message.class);
Message botMessage = mock(Message.class);
Document document = mock(Document.class);
when(update.getMessage()).thenReturn(message);
when(message.getDocument()).thenReturn(document);
when(botMessage.getText()).thenReturn(RECOVERY_MESSAGE);
when(message.isReply()).thenReturn(true);
when(message.hasDocument()).thenReturn(true);
when(message.getReplyToMessage()).thenReturn(botMessage);
when(message.getChatId()).thenReturn(GROUP_ID);
return update;
}
private Object getDbBackup() {
db.getSet(TEST).add(TEST);
Object backup = db.backup();
db.clear();
return backup;
}
private java.io.File createBackupFile(Object backup) throws IOException {
java.io.File backupFile = new java.io.File(TEST);
BufferedWriter writer = Files.newWriter(backupFile, Charset.defaultCharset());
writer.write(backup.toString());
writer.flush();
writer.close();
return backupFile;
}
}

View File

@ -0,0 +1,78 @@
package org.telegram.abilitybots.api.bot;
import com.google.common.annotations.VisibleForTesting;
import org.telegram.abilitybots.api.db.DBContext;
import org.telegram.abilitybots.api.objects.Ability;
import org.telegram.abilitybots.api.objects.Ability.AbilityBuilder;
import org.telegram.abilitybots.api.sender.MessageSender;
import static org.telegram.abilitybots.api.objects.Ability.builder;
import static org.telegram.abilitybots.api.objects.Flag.CALLBACK_QUERY;
import static org.telegram.abilitybots.api.objects.Flag.MESSAGE;
import static org.telegram.abilitybots.api.objects.Locality.*;
import static org.telegram.abilitybots.api.objects.Privacy.ADMIN;
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
public class DefaultBot extends AbilityBot {
public DefaultBot(String token, String username, DBContext db) {
super(token, username, db);
}
public static AbilityBuilder getDefaultBuilder() {
return builder()
.name("test")
.privacy(PUBLIC)
.locality(ALL)
.input(1)
.action(ctx -> {
});
}
@Override
public int creatorId() {
return 1337;
}
public Ability defaultAbility() {
return getDefaultBuilder()
.name(DEFAULT)
.info("dis iz default command")
.reply(upd -> sender.send("reply", upd.getMessage().getChatId()), MESSAGE, update -> update.getMessage().getText().equals("must reply"))
.reply(upd -> sender.send("reply", upd.getCallbackQuery().getMessage().getChatId()), CALLBACK_QUERY)
.build();
}
public Ability adminAbility() {
return getDefaultBuilder()
.name("admin")
.privacy(ADMIN)
.build();
}
public Ability groupAbility() {
return getDefaultBuilder()
.name("group")
.privacy(PUBLIC)
.locality(GROUP)
.build();
}
public Ability multipleInputAbility() {
return getDefaultBuilder()
.name("count")
.privacy(PUBLIC)
.locality(USER)
.input(4)
.build();
}
public Ability testAbility() {
return getDefaultBuilder().build();
}
@VisibleForTesting
void setSender(MessageSender sender) {
this.sender = sender;
}
}

View File

@ -0,0 +1,109 @@
package org.telegram.abilitybots.api.db;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.telegram.abilitybots.api.objects.EndUser;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
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.AbilityBotTest.CREATOR;
import static org.telegram.abilitybots.api.bot.AbilityBotTest.MUSER;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
public class MapDBContextTest {
private static final String TEST = "TEST";
private DBContext db;
@Before
public void setUp() {
db = offlineInstance("db");
}
@Test
public void canRecoverDB() throws IOException {
Map<Integer, EndUser> users = db.getMap(USERS);
Map<String, Integer> userIds = db.getMap(USER_ID);
users.put(CREATOR.id(), CREATOR);
users.put(MUSER.id(), MUSER);
userIds.put(CREATOR.username(), CREATOR.id());
userIds.put(MUSER.username(), MUSER.id());
db.getSet("AYRE").add(123123);
Map<Integer, EndUser> originalUsers = newHashMap(users);
String beforeBackupInfo = db.info(USERS);
Object jsonBackup = db.backup();
db.clear();
boolean recovered = db.recover(jsonBackup);
Map<Integer, EndUser> recoveredUsers = db.getMap(USERS);
String afterRecoveryInfo = db.info(USERS);
assertTrue("Could not recover database successfully", recovered);
assertEquals("Map info before and after recovery is different", beforeBackupInfo, afterRecoveryInfo);
assertEquals("Map before and after recovery are not equal", originalUsers, recoveredUsers);
}
@Test
public void canFallbackDBIfRecoveryFails() throws IOException {
Set<EndUser> users = db.getSet(USERS);
users.add(CREATOR);
users.add(MUSER);
Set<EndUser> originalSet = newHashSet(users);
Object jsonBackup = db.backup();
String corruptBackup = "!@#$" + String.valueOf(jsonBackup);
boolean recovered = db.recover(corruptBackup);
Set<EndUser> recoveredSet = db.getSet(USERS);
assertEquals("Recovery was successful from a CORRUPT backup", false, recovered);
assertEquals("Set before and after corrupt recovery are not equal", originalSet, recoveredSet);
}
@Test
public void canGetSummary() throws IOException {
String anotherTest = TEST + 1;
db.getSet(TEST).add(TEST);
db.getSet(anotherTest).add(anotherTest);
String actualSummary = db.summary();
// Name - Type - Number of "rows"
String expectedSummary = format("%s - Set - 1\n%s - Set - 1", TEST, anotherTest);
assertEquals("Actual DB summary does not match that of the expected", expectedSummary, actualSummary);
}
@Test
public void canGetInfo() throws IOException {
db.getSet(TEST).add(TEST);
String actualInfo = db.info(TEST);
// JSON
String expectedInfo = "TEST - Set - 1";
assertEquals("Actual DB structure info does not match that of the expected", expectedInfo, actualInfo);
}
@Test(expected = IllegalStateException.class)
public void cantGetInfoFromNonexistentDBStructureName() throws IOException {
db.info(TEST);
}
@After
public void tearDown() throws IOException {
db.clear();
db.close();
}
}

View File

@ -0,0 +1,58 @@
package org.telegram.abilitybots.api.objects;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.telegram.abilitybots.api.bot.DefaultBot.getDefaultBuilder;
public class AbilityTest {
@Test(expected = IllegalArgumentException.class)
public void argumentsCannotBeNegative() {
getDefaultBuilder().input(-4).build();
}
@Test(expected = IllegalArgumentException.class)
public void nameCannotBeEmpty() {
getDefaultBuilder().name("").build();
}
@Test(expected = IllegalArgumentException.class)
public void nameCannotBeNull() {
getDefaultBuilder().name(null).build();
}
@Test(expected = NullPointerException.class)
public void consumerCannotBeNull() {
getDefaultBuilder().action(null).build();
}
@Test(expected = NullPointerException.class)
public void localityCannotBeNull() {
getDefaultBuilder().locality(null).build();
}
@Test(expected = NullPointerException.class)
public void privacyCannotBeNull() {
getDefaultBuilder().privacy(null).build();
}
@Test(expected = IllegalArgumentException.class)
public void nameCannotContainSpaces() {
getDefaultBuilder().name("test test").build();
}
@Test
public void abilityEqualsMethod() {
Ability ability1 = getDefaultBuilder().build();
Ability ability2 = getDefaultBuilder().build();
Ability ability3 = getDefaultBuilder().name("anotherconsumer").build();
Ability ability4 = getDefaultBuilder().action((context) -> {
}).build();
assertEquals("Abilities should not be equal", ability1, ability2);
assertEquals("Abilities should not be equal", ability1, ability4);
assertNotEquals("Abilities should be equal", ability1, ability3);
}
}

View File

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

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambotsextensions</artifactId>
<version>3.1.2</version>
<version>3.2</version>
<packaging>jar</packaging>
<name>Telegram Bots Extensions</name>
@ -59,7 +59,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<bots.version>3.1.2</bots.version>
<bots.version>3.2</bots.version>
</properties>
<dependencies>

View File

@ -24,17 +24,6 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB
private final CommandRegistry commandRegistry;
private String botUsername;
/**
* Creates a TelegramLongPollingCommandBot using default options
* Use ICommandRegistry's methods on this bot to register commands
*
* @deprecated Uses {@link #TelegramLongPollingCommandBot(String)} instead
*/
@Deprecated
public TelegramLongPollingCommandBot() {
this(ApiContext.getInstance(DefaultBotOptions.class));
}
/**
* Creates a TelegramLongPollingCommandBot using default options
* Use ICommandRegistry's methods on this bot to register commands
@ -45,19 +34,6 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB
this(ApiContext.getInstance(DefaultBotOptions.class), botUsername);
}
/**
* Creates a TelegramLongPollingCommandBot with custom options and allowing commands with
* usernames
* Use ICommandRegistry's methods on this bot to register commands
* @param options Bot options
*
* @deprecated Use {@link #TelegramLongPollingCommandBot(DefaultBotOptions, String)} instead
*/
@Deprecated
public TelegramLongPollingCommandBot(DefaultBotOptions options) {
this(options, true);
}
/**
* Creates a TelegramLongPollingCommandBot with custom options and allowing commands with
* usernames
@ -69,21 +45,6 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB
this(options, true, botUsername);
}
/**
* Creates a TelegramLongPollingCommandBot
* Use ICommandRegistry's methods on this bot to register commands
* @param options Bot options
* @param allowCommandsWithUsername true to allow commands with parameters (default),
* false otherwise
*
* @deprecated Use {@link #TelegramLongPollingCommandBot(DefaultBotOptions, boolean, String)} instead
*/
@Deprecated
public TelegramLongPollingCommandBot(DefaultBotOptions options, boolean allowCommandsWithUsername) {
super(options);
this.commandRegistry = new CommandRegistry(allowCommandsWithUsername, getBotUsername());
}
/**
* Creates a TelegramLongPollingCommandBot
* Use ICommandRegistry's methods on this bot to register commands
@ -164,11 +125,10 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB
}
/**
* TODO This method will become final in next mayor release, avoid overriding it
* @return Bot username
*/
@Override
public String getBotUsername() {
public final String getBotUsername() {
return botUsername;
}

View File

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

View File

@ -50,6 +50,7 @@ public class ApiContext {
return INJECTOR;
}
@SuppressWarnings("unchecked")
private static class ApiModule extends AbstractModule {
@Override
protected void configure() {

View File

@ -0,0 +1,190 @@
package org.telegram.telegrambots.api.methods.stickers;
import com.fasterxml.jackson.core.type.TypeReference;
import org.telegram.telegrambots.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.api.objects.replykeyboard.ApiResponse;
import org.telegram.telegrambots.api.objects.stickers.MaskPosition;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.exceptions.TelegramApiValidationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Ruben Bermudez
* @version 1.0
* Use this method to add a new sticker to a set created by the bot. Returns True on success.
*/
public class AddStickerToSet extends PartialBotApiMethod<Boolean> {
public static final String PATH = "addStickerToSet";
public static final String USERID_FIELD = "user_id";
public static final String NAME_FIELD = "name";
public static final String PNGSTICKER_FIELD = "png_sticker";
public static final String EMOJIS_FIELD = "emojis";
public static final String MASKPOSITION_FIELD = "mask_position";
private Integer userId; ///< User identifier of sticker set owner
private String name; ///< Sticker set name
private String emojis; ///< One or more emoji corresponding to the sticker
private MaskPosition maskPosition; ///< Position where the mask should be placed on faces
/**
* Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px,
* and either width or height must be exactly 512px. Pass a file_id as a String to send a file
* that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram
* to get a file from the Internet, or upload a new one using multipart/form-data.
*/
private Boolean isNewPngSticker;
private String pngSticker;
private File pngStickerFile; ///< New sticker file
private InputStream pngStickerStream; ///< New sticker stream
private String pngStickerName; ///< New sticker stream name
public AddStickerToSet() {
super();
}
public AddStickerToSet(Integer userId, String name, String emojis) {
this.userId = checkNotNull(userId);
this.name = checkNotNull(name);
this.emojis = checkNotNull(emojis);
}
public Integer getUserId() {
return userId;
}
public AddStickerToSet setUserId(Integer userId) {
this.userId = userId;
return this;
}
public String getPngSticker() {
return pngSticker;
}
public AddStickerToSet setPngSticker(String pngSticker) {
this.pngSticker = pngSticker;
this.isNewPngSticker = false;
return this;
}
public File getPngStickerFile() {
return pngStickerFile;
}
public AddStickerToSet setPngStickerFile(File pngStickerFile) {
Objects.requireNonNull(pngStickerFile, "pngStickerFile cannot be null!");
this.pngStickerFile = pngStickerFile;
this.isNewPngSticker = true;
return this;
}
public InputStream getPngStickerStream() {
return pngStickerStream;
}
public AddStickerToSet setPngStickerStream(String pngStickerName, InputStream pngStickerStream) {
Objects.requireNonNull(pngStickerName, "pngStickerName cannot be null!");
Objects.requireNonNull(pngStickerStream, "pngStickerStream cannot be null!");
this.pngStickerStream = pngStickerStream;
this.pngStickerName = pngStickerName;
this.isNewPngSticker = true;
return this;
}
public String getPngStickerName() {
return pngStickerName;
}
public Boolean isNewPngSticker() {
return isNewPngSticker;
}
public String getName() {
return name;
}
public AddStickerToSet setName(String name) {
this.name = name;
return this;
}
public String getEmojis() {
return emojis;
}
public AddStickerToSet setEmojis(String emojis) {
this.emojis = emojis;
return this;
}
public MaskPosition getMaskPosition() {
return maskPosition;
}
public AddStickerToSet setMaskPosition(MaskPosition maskPosition) {
this.maskPosition = maskPosition;
return this;
}
@Override
public Boolean deserializeResponse(String answer) throws TelegramApiRequestException {
try {
ApiResponse<Boolean> result = OBJECT_MAPPER.readValue(answer,
new TypeReference<ApiResponse<Boolean>>(){});
if (result.getOk()) {
return result.getResult();
} else {
throw new TelegramApiRequestException("Error creating new sticker set", result);
}
} catch (IOException e) {
throw new TelegramApiRequestException("Unable to deserialize response", e);
}
}
@Override
public void validate() throws TelegramApiValidationException {
if (userId == null || userId <= 0) {
throw new TelegramApiValidationException("userId can't be empty", this);
}
if (name == null || name.isEmpty()) {
throw new TelegramApiValidationException("name can't be empty", this);
}
if (emojis == null || emojis.isEmpty()) {
throw new TelegramApiValidationException("emojis can't be empty", this);
}
if (isNewPngSticker) {
if (pngStickerFile == null && pngStickerStream == null) {
throw new TelegramApiValidationException("PngSticker can't be empty", this);
}
if (pngStickerStream != null && (pngStickerName == null || pngStickerName.isEmpty())) {
throw new TelegramApiValidationException("PngSticker name can't be empty", this);
}
} else if (pngSticker == null) {
throw new TelegramApiValidationException("PngSticker can't be empty", this);
}
if (maskPosition != null) {
maskPosition.validate();
}
}
@Override
public String toString() {
return "AddStickerToSet{" +
"userId=" + userId +
", name='" + name + '\'' +
", emojis='" + emojis + '\'' +
", maskPosition=" + maskPosition +
", isNewPngSticker=" + isNewPngSticker +
", pngSticker='" + pngSticker + '\'' +
", pngStickerFile=" + pngStickerFile +
", pngStickerStream=" + pngStickerStream +
", pngStickerName='" + pngStickerName + '\'' +
'}';
}
}

View File

@ -0,0 +1,220 @@
package org.telegram.telegrambots.api.methods.stickers;
import com.fasterxml.jackson.core.type.TypeReference;
import org.telegram.telegrambots.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.api.objects.replykeyboard.ApiResponse;
import org.telegram.telegrambots.api.objects.stickers.MaskPosition;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.exceptions.TelegramApiValidationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Ruben Bermudez
* @version 1.0
* Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set.
* Returns True on success.
*/
public class CreateNewStickerSet extends PartialBotApiMethod<Boolean> {
public static final String PATH = "createNewStickerSet";
public static final String USERID_FIELD = "user_id";
public static final String NAME_FIELD = "name";
public static final String TITLE_FIELD = "title";
public static final String PNGSTICKER_FIELD = "png_sticker";
public static final String EMOJIS_FIELD = "emojis";
public static final String CONTAINSMASKS_FIELD = "contains_masks";
public static final String MASKPOSITION_FIELD = "mask_position";
private Integer userId; ///< User identifier of created sticker set owner
/**
* Name of sticker set, to be used in t.me/addstickers/<name> URLs.
* Can contain only english letters, digits and underscores.
* Must begin with a letter, can't contain consecutive underscores and must end in _by_<bot username>.
* <bot_username> is case insensitive. 7-64 characters.
*/
private String name; ///< Sticker set title, 1-64 characters
private String title; ///< User identifier of created sticker set owner
private String emojis; ///< One or more emoji corresponding to the sticker
private Boolean containsMasks; ///< Pass True, if a set of mask stickers should be created
private MaskPosition maskPosition; ///< Position where the mask should be placed on faces
/**
* Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px,
* and either width or height must be exactly 512px.
* Pass a file_id as a String to send a file that already exists on the Telegram servers,
* pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one
* using multipart/form-data. More info on Sending Files »
*/
private Boolean isNewPngSticker;
private String pngSticker;
private java.io.File pngStickerFile; ///< New sticker file
private InputStream pngStickerStream; ///< New sticker stream
private String pngStickerName; ///< New sticker stream name
public CreateNewStickerSet() {
super();
}
public CreateNewStickerSet(Integer userId, String name, String title, String emojis) {
this.userId = checkNotNull(userId);
this.name = checkNotNull(name);
this.title = checkNotNull(title);
this.emojis = checkNotNull(emojis);
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getPngSticker() {
return pngSticker;
}
public CreateNewStickerSet setPngSticker(String pngSticker) {
this.pngSticker = pngSticker;
this.isNewPngSticker = false;
return this;
}
public File getPngStickerFile() {
return pngStickerFile;
}
public CreateNewStickerSet setPngStickerFile(File pngStickerFile) {
Objects.requireNonNull(pngStickerFile, "pngStickerFile cannot be null!");
this.pngStickerFile = pngStickerFile;
this.isNewPngSticker = true;
return this;
}
public InputStream getPngStickerStream() {
return pngStickerStream;
}
public CreateNewStickerSet setPngStickerStream(String pngStickerName, InputStream pngStickerStream) {
Objects.requireNonNull(pngStickerName, "pngStickerName cannot be null!");
Objects.requireNonNull(pngStickerStream, "pngStickerStream cannot be null!");
this.pngStickerStream = pngStickerStream;
this.pngStickerName = pngStickerName;
this.isNewPngSticker = true;
return this;
}
public String getPngStickerName() {
return pngStickerName;
}
public Boolean isNewPngSticker() {
return isNewPngSticker;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getEmojis() {
return emojis;
}
public void setEmojis(String emojis) {
this.emojis = emojis;
}
public Boolean getContainsMasks() {
return containsMasks;
}
public void setContainsMasks(Boolean containsMasks) {
this.containsMasks = containsMasks;
}
public MaskPosition getMaskPosition() {
return maskPosition;
}
public void setMaskPosition(MaskPosition maskPosition) {
this.maskPosition = maskPosition;
}
@Override
public Boolean deserializeResponse(String answer) throws TelegramApiRequestException {
try {
ApiResponse<Boolean> result = OBJECT_MAPPER.readValue(answer,
new TypeReference<ApiResponse<Boolean>>(){});
if (result.getOk()) {
return result.getResult();
} else {
throw new TelegramApiRequestException("Error creating new sticker set", result);
}
} catch (IOException e) {
throw new TelegramApiRequestException("Unable to deserialize response", e);
}
}
@Override
public void validate() throws TelegramApiValidationException {
if (userId == null || userId <= 0) {
throw new TelegramApiValidationException("userId can't be empty", this);
}
if (name == null || name.isEmpty()) {
throw new TelegramApiValidationException("name can't be empty", this);
}
if (title == null || title.isEmpty()) {
throw new TelegramApiValidationException("userId can't be empty", this);
}
if (emojis == null || emojis.isEmpty()) {
throw new TelegramApiValidationException("emojis can't be empty", this);
}
if (isNewPngSticker) {
if (pngStickerFile == null && pngStickerStream == null) {
throw new TelegramApiValidationException("PngSticker can't be empty", this);
}
if (pngStickerStream != null && (pngStickerName == null || pngStickerName.isEmpty())) {
throw new TelegramApiValidationException("PngSticker name can't be empty", this);
}
} else if (pngSticker == null) {
throw new TelegramApiValidationException("PngSticker can't be empty", this);
}
if (maskPosition != null) {
maskPosition.validate();
}
}
@Override
public String toString() {
return "CreateNewStickerSet{" +
"userId=" + userId +
", name='" + name + '\'' +
", title='" + title + '\'' +
", emojis='" + emojis + '\'' +
", containsMasks=" + containsMasks +
", maskPosition=" + maskPosition +
", isNewPngSticker=" + isNewPngSticker +
", pngSticker='" + pngSticker + '\'' +
", pngStickerFile=" + pngStickerFile +
", pngStickerStream=" + pngStickerStream +
", pngStickerName='" + pngStickerName + '\'' +
'}';
}
}

View File

@ -0,0 +1,78 @@
package org.telegram.telegrambots.api.methods.stickers;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import org.telegram.telegrambots.api.methods.BotApiMethod;
import org.telegram.telegrambots.api.objects.replykeyboard.ApiResponse;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.exceptions.TelegramApiValidationException;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Ruben Bermudez
* @version 1.0
* Use this method to delete a sticker from a set created by the bot. Returns True on success.
*/
public class DeleteStickerFromSet extends BotApiMethod<Boolean> {
private static final String PATH = "deleteStickerFromSet";
private static final String STICKER_FIELD = "sticker";
@JsonProperty(STICKER_FIELD)
private String sticker; ///< File identifier of the sticker
public DeleteStickerFromSet() {
super();
}
public DeleteStickerFromSet(String sticker) {
super();
this.sticker = checkNotNull(sticker);
}
@Override
public String getMethod() {
return PATH;
}
@Override
public Boolean deserializeResponse(String answer) throws TelegramApiRequestException {
try {
ApiResponse<Boolean> result = OBJECT_MAPPER.readValue(answer,
new TypeReference<ApiResponse<Boolean>>(){});
if (result.getOk()) {
return result.getResult();
} else {
throw new TelegramApiRequestException("Error deleting sticker from set", result);
}
} catch (IOException e) {
throw new TelegramApiRequestException("Unable to deserialize response", e);
}
}
@Override
public void validate() throws TelegramApiValidationException {
if (sticker == null || sticker.isEmpty()) {
throw new TelegramApiValidationException("sticker can't be null", this);
}
}
public String getSticker() {
return sticker;
}
public DeleteStickerFromSet setSticker(String sticker) {
this.sticker = sticker;
return this;
}
@Override
public String toString() {
return "DeleteStickerFromSet{" +
"sticker='" + sticker + '\'' +
'}';
}
}

View File

@ -0,0 +1,79 @@
package org.telegram.telegrambots.api.methods.stickers;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import org.telegram.telegrambots.api.methods.BotApiMethod;
import org.telegram.telegrambots.api.objects.replykeyboard.ApiResponse;
import org.telegram.telegrambots.api.objects.stickers.StickerSet;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.exceptions.TelegramApiValidationException;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Ruben Bermudez
* @version 1.0
* Use this method to get a sticker set. On success, a StickerSet object is returned.
*/
public class GetStickerSet extends BotApiMethod<StickerSet> {
private static final String PATH = "getStickerSet";
private static final String NAME_FIELD = "name";
@JsonProperty(NAME_FIELD)
private String name; ///< Name of the sticker set
public GetStickerSet(String name) {
super();
this.name = checkNotNull(name);
}
public GetStickerSet() {
super();
}
@Override
public String getMethod() {
return PATH;
}
@Override
public StickerSet deserializeResponse(String answer) throws TelegramApiRequestException {
try {
ApiResponse<StickerSet> result = OBJECT_MAPPER.readValue(answer,
new TypeReference<ApiResponse<StickerSet>>(){});
if (result.getOk()) {
return result.getResult();
} else {
throw new TelegramApiRequestException("Error getting sticker set", result);
}
} catch (IOException e) {
throw new TelegramApiRequestException("Unable to deserialize response", e);
}
}
@Override
public void validate() throws TelegramApiValidationException {
if (name == null || name.isEmpty()) {
throw new TelegramApiValidationException("Name can't be null", this);
}
}
public String getName() {
return name;
}
public GetStickerSet setName(String name) {
this.name = name;
return this;
}
@Override
public String toString() {
return "GetStickerSet{" +
"name='" + name + '\'' +
'}';
}
}

View File

@ -0,0 +1,94 @@
package org.telegram.telegrambots.api.methods.stickers;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import org.telegram.telegrambots.api.methods.BotApiMethod;
import org.telegram.telegrambots.api.objects.replykeyboard.ApiResponse;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.exceptions.TelegramApiValidationException;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Ruben Bermudez
* @version 1.0
* Use this method to move a sticker in a set created by the bot to a specific position. Returns True on success.
*/
public class SetStickerPositionInSet extends BotApiMethod<Boolean> {
private static final String PATH = "getStickerSet";
private static final String STICKER_FIELD = "sticker";
private static final String POSITION_FIELD = "position";
@JsonProperty(STICKER_FIELD)
private String sticker; ///< File identifier of the sticker
@JsonProperty(STICKER_FIELD)
private Integer position; ///< New sticker position in the set, zero-based
public SetStickerPositionInSet(String sticker, Integer position) {
this.sticker = checkNotNull(sticker);
this.position = checkNotNull(position);
}
public SetStickerPositionInSet() {
super();
}
@Override
public String getMethod() {
return PATH;
}
@Override
public Boolean deserializeResponse(String answer) throws TelegramApiRequestException {
try {
ApiResponse<Boolean> result = OBJECT_MAPPER.readValue(answer,
new TypeReference<ApiResponse<Boolean>>(){});
if (result.getOk()) {
return result.getResult();
} else {
throw new TelegramApiRequestException("Error setting sticker position in set", result);
}
} catch (IOException e) {
throw new TelegramApiRequestException("Unable to deserialize response", e);
}
}
@Override
public void validate() throws TelegramApiValidationException {
if (sticker == null || sticker.isEmpty()) {
throw new TelegramApiValidationException("sticker can't be null", this);
}
if (position == null || position > 0) {
throw new TelegramApiValidationException("position can't be null", this);
}
}
public String getSticker() {
return sticker;
}
public SetStickerPositionInSet setSticker(String sticker) {
this.sticker = sticker;
return this;
}
public Integer getPosition() {
return position;
}
public SetStickerPositionInSet setPosition(Integer position) {
this.position = position;
return this;
}
@Override
public String toString() {
return "SetStickerPositionInSet{" +
"sticker='" + sticker + '\'' +
", position=" + position +
'}';
}
}

View File

@ -0,0 +1,114 @@
package org.telegram.telegrambots.api.methods.stickers;
import com.fasterxml.jackson.core.type.TypeReference;
import org.telegram.telegrambots.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.api.objects.File;
import org.telegram.telegrambots.api.objects.replykeyboard.ApiResponse;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.exceptions.TelegramApiValidationException;
import java.io.IOException;
import java.io.InputStream;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Ruben Bermudez
* @version 1.0
* Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet
* methods (can be used multiple times). Returns the uploaded File on success.
*/
public class UploadStickerFile extends PartialBotApiMethod<File> {
public static final String PATH = "uploadStickerFile";
public static final String USERID_FIELD = "name";
public static final String PNGSTICKER_FIELD = "png_sticker";
private Integer userId; ///< User identifier of sticker file owner
/**
* Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px,
* and either width or height must be exactly 512px. More info on Sending Files »
*/
private java.io.File newPngStickerFile; ///< New sticker file
private InputStream newPngStickerStream; ///< New sticker stream
private String newPngStickerName; ///< New sticker stream name
public UploadStickerFile() {
super();
}
public UploadStickerFile(Integer userId) {
super();
this.userId = checkNotNull(userId);
}
@Override
public File deserializeResponse(String answer) throws TelegramApiRequestException {
try {
ApiResponse<File> result = OBJECT_MAPPER.readValue(answer,
new TypeReference<ApiResponse<File>>(){});
if (result.getOk()) {
return result.getResult();
} else {
throw new TelegramApiRequestException("Error uploading sticker set", result);
}
} catch (IOException e) {
throw new TelegramApiRequestException("Unable to deserialize response", e);
}
}
@Override
public void validate() throws TelegramApiValidationException {
if (userId == null || userId <= 0) {
throw new TelegramApiValidationException("userId can't be empty", this);
}
if (newPngStickerFile == null && newPngStickerStream == null) {
throw new TelegramApiValidationException("file or stream must be present", this);
}
if (newPngStickerStream != null && (newPngStickerName == null || newPngStickerName.isEmpty())) {
throw new TelegramApiValidationException("Stream name must be present", this);
}
}
public Integer getUserId() {
return userId;
}
public UploadStickerFile setUserId(Integer userId) {
this.userId = userId;
return this;
}
public java.io.File getNewPngStickerFile() {
return newPngStickerFile;
}
public UploadStickerFile setNewPngSticker(java.io.File newPngStickerFile) {
this.newPngStickerFile = newPngStickerFile;
return this;
}
public InputStream getNewPngStickerStream() {
return newPngStickerStream;
}
public UploadStickerFile setNewPngSticker(String newPngStickerName, InputStream newPngStickerStream) {
this.newPngStickerName = newPngStickerName;
this.newPngStickerStream = newPngStickerStream;
return this;
}
public String getNewPngStickerName() {
return newPngStickerName;
}
@Override
public String toString() {
return "UploadStickerFile{" +
"userId=" + userId +
", newPngStickerFile=" + newPngStickerFile +
", newPngStickerStream=" + newPngStickerStream +
", newPngStickerName='" + newPngStickerName + '\'' +
'}';
}
}

View File

@ -5,6 +5,7 @@ import org.telegram.telegrambots.api.interfaces.BotApiObject;
import org.telegram.telegrambots.api.objects.games.Game;
import org.telegram.telegrambots.api.objects.payments.Invoice;
import org.telegram.telegrambots.api.objects.payments.SuccessfulPayment;
import org.telegram.telegrambots.api.objects.stickers.Sticker;
import java.util.List;

View File

@ -0,0 +1,89 @@
package org.telegram.telegrambots.api.objects.stickers;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.telegram.telegrambots.api.interfaces.InputBotApiObject;
import org.telegram.telegrambots.api.interfaces.Validable;
import org.telegram.telegrambots.exceptions.TelegramApiValidationException;
/**
* @author Ruben Bermudez
* @version 3.2
* This object describes the position on faces where a mask should be placed by default.
*/
public class MaskPosition implements InputBotApiObject, Validable {
private static final String POINT_FIELD = "point";
private static final String XSHIFT_FIELD = "x_shift";
private static final String YSHIFT_FIELD = "y_shift";
private static final String SCALE_FIELD = "scale";
@JsonProperty(POINT_FIELD)
private String point; ///< The part of the face relative to which the mask should be placed. One of forehead, eyes, mouth, or chin.
@JsonProperty(XSHIFT_FIELD)
private Float xShift; ///< Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position.
@JsonProperty(YSHIFT_FIELD)
private Float yShift; ///< Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position.
@JsonProperty(SCALE_FIELD)
private Float scale; ///< Mask scaling coefficient. For example, 2.0 means double size.
public MaskPosition() {
super();
}
public String getPoint() {
return point;
}
public Float getxShift() {
return xShift;
}
public Float getyShift() {
return yShift;
}
public Float getScale() {
return scale;
}
public void setPoint(String point) {
this.point = point;
}
public void setxShift(Float xShift) {
this.xShift = xShift;
}
public void setyShift(Float yShift) {
this.yShift = yShift;
}
public void setScale(Float scale) {
this.scale = scale;
}
@Override
public String toString() {
return "MaskPosition{" +
"point='" + point + '\'' +
", xShift=" + xShift +
", yShift=" + yShift +
", scale=" + scale +
'}';
}
@Override
public void validate() throws TelegramApiValidationException {
if (point == null || point.isEmpty()) {
throw new TelegramApiValidationException("point can't be empty", this);
}
if (xShift == null) {
throw new TelegramApiValidationException("xShift can't be empty", this);
}
if (yShift == null) {
throw new TelegramApiValidationException("yShift can't be empty", this);
}
if (scale == null) {
throw new TelegramApiValidationException("scale can't be empty", this);
}
}
}

View File

@ -1,14 +1,14 @@
package org.telegram.telegrambots.api.objects;
package org.telegram.telegrambots.api.objects.stickers;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.telegram.telegrambots.api.interfaces.BotApiObject;
import org.telegram.telegrambots.api.objects.PhotoSize;
/**
* @author Ruben Bermudez
* @version 1.0
* @brief This object represents a sticker.
* @date 20 of June of 2015
*/
public class Sticker implements BotApiObject {
@ -18,6 +18,8 @@ public class Sticker implements BotApiObject {
private static final String THUMB_FIELD = "thumb";
private static final String FILESIZE_FIELD = "file_size";
private static final String EMOJI_FIELD = "emoji";
private static final String SETNAME_FIELD = "set_name";
private static final String MASKPOSITON_FIELD = "mask_position";
@JsonProperty(FILEID_FIELD)
private String fileId; ///< Unique identifier for this file
@ -31,6 +33,10 @@ public class Sticker implements BotApiObject {
private Integer fileSize; ///< Optional. File size
@JsonProperty(EMOJI_FIELD)
private String emoji; ///< Optional. Emoji associated with the sticker
@JsonProperty(SETNAME_FIELD)
private String setName; ///< Optional. Name of the sticker set to which the sticker belongs
@JsonProperty(MASKPOSITON_FIELD)
private String maskPosition; ///< Optional. For mask stickers, the position where the mask should be placed
public Sticker() {
super();
@ -60,6 +66,14 @@ public class Sticker implements BotApiObject {
return emoji;
}
public String getSetName() {
return setName;
}
public String getMaskPosition() {
return maskPosition;
}
@Override
public String toString() {
return "Sticker{" +
@ -68,7 +82,9 @@ public class Sticker implements BotApiObject {
", height=" + height +
", thumb=" + thumb +
", fileSize=" + fileSize +
", emoji=" + emoji +
", emoji='" + emoji + '\'' +
", setName='" + setName + '\'' +
", maskPosition='" + maskPosition + '\'' +
'}';
}
}

View File

@ -0,0 +1,57 @@
package org.telegram.telegrambots.api.objects.stickers;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.telegram.telegrambots.api.interfaces.BotApiObject;
import java.util.List;
/**
* @author Ruben Bermudez
* @version 1.0
* This object represents a sticker set.
*/
public class StickerSet implements BotApiObject {
private static final String NAME_FIELD = "name";
private static final String TITLE_FIELD = "title";
private static final String CONTAINSMASKS_FIELD = "contains_masks";
private static final String STICKERS_FIELD = "stickers";
@JsonProperty(NAME_FIELD)
private String name;
@JsonProperty(TITLE_FIELD)
private String title;
@JsonProperty(CONTAINSMASKS_FIELD)
private Boolean containsMasks;
@JsonProperty(STICKERS_FIELD)
private List<Sticker> stickers;
public StickerSet() {
super();
}
public String getName() {
return name;
}
public String getTitle() {
return title;
}
public Boolean getContainsMasks() {
return containsMasks;
}
public List<Sticker> getStickers() {
return stickers;
}
@Override
public String toString() {
return "StickerSet{" +
"name='" + name + '\'' +
", title='" + title + '\'' +
", containsMasks=" + containsMasks +
", stickers=" + stickers +
'}';
}
}

View File

@ -7,6 +7,9 @@ import org.telegram.telegrambots.api.methods.groupadministration.*;
import org.telegram.telegrambots.api.methods.pinnedmessages.PinChatMessage;
import org.telegram.telegrambots.api.methods.pinnedmessages.UnpinChatMessage;
import org.telegram.telegrambots.api.methods.send.*;
import org.telegram.telegrambots.api.methods.stickers.AddStickerToSet;
import org.telegram.telegrambots.api.methods.stickers.CreateNewStickerSet;
import org.telegram.telegrambots.api.methods.stickers.UploadStickerFile;
import org.telegram.telegrambots.api.methods.updates.DeleteWebhook;
import org.telegram.telegrambots.api.methods.updates.GetWebhookInfo;
import org.telegram.telegrambots.api.methods.updatingmessages.DeleteMessage;
@ -52,6 +55,7 @@ public abstract class AbsSender {
// Send Requests
@Deprecated
public final Message sendMessage(SendMessage sendMessage) throws TelegramApiException {
if (sendMessage == null) {
throw new TelegramApiException("Parameter sendMessage can not be null");
@ -60,6 +64,7 @@ public abstract class AbsSender {
return sendApiMethod(sendMessage);
}
@Deprecated
public final Boolean answerInlineQuery(AnswerInlineQuery answerInlineQuery) throws TelegramApiException {
if (answerInlineQuery == null) {
throw new TelegramApiException("Parameter answerInlineQuery can not be null");
@ -68,6 +73,7 @@ public abstract class AbsSender {
return sendApiMethod(answerInlineQuery);
}
@Deprecated
public final Boolean sendChatAction(SendChatAction sendChatAction) throws TelegramApiException {
if (sendChatAction == null) {
throw new TelegramApiException("Parameter sendChatAction can not be null");
@ -76,6 +82,7 @@ public abstract class AbsSender {
return sendApiMethod(sendChatAction);
}
@Deprecated
public final Message forwardMessage(ForwardMessage forwardMessage) throws TelegramApiException {
if (forwardMessage == null) {
throw new TelegramApiException("Parameter forwardMessage can not be null");
@ -84,6 +91,7 @@ public abstract class AbsSender {
return sendApiMethod(forwardMessage);
}
@Deprecated
public final Message sendLocation(SendLocation sendLocation) throws TelegramApiException {
if (sendLocation == null) {
throw new TelegramApiException("Parameter sendLocation can not be null");
@ -92,6 +100,7 @@ public abstract class AbsSender {
return sendApiMethod(sendLocation);
}
@Deprecated
public final Message sendVenue(SendVenue sendVenue) throws TelegramApiException {
if (sendVenue == null) {
throw new TelegramApiException("Parameter sendVenue can not be null");
@ -100,6 +109,7 @@ public abstract class AbsSender {
return sendApiMethod(sendVenue);
}
@Deprecated
public final Message sendContact(SendContact sendContact) throws TelegramApiException {
if (sendContact == null) {
throw new TelegramApiException("Parameter sendContact can not be null");
@ -108,6 +118,7 @@ public abstract class AbsSender {
return sendApiMethod(sendContact);
}
@Deprecated
public final Boolean kickMember(KickChatMember kickChatMember) throws TelegramApiException {
if (kickChatMember == null) {
throw new TelegramApiException("Parameter kickChatMember can not be null");
@ -115,6 +126,7 @@ public abstract class AbsSender {
return sendApiMethod(kickChatMember);
}
@Deprecated
public final Boolean unbanMember(UnbanChatMember unbanChatMember) throws TelegramApiException {
if (unbanChatMember == null) {
throw new TelegramApiException("Parameter unbanChatMember can not be null");
@ -122,6 +134,7 @@ public abstract class AbsSender {
return sendApiMethod(unbanChatMember);
}
@Deprecated
public final Boolean leaveChat(LeaveChat leaveChat) throws TelegramApiException {
if (leaveChat == null) {
throw new TelegramApiException("Parameter leaveChat can not be null");
@ -129,6 +142,7 @@ public abstract class AbsSender {
return sendApiMethod(leaveChat);
}
@Deprecated
public final Chat getChat(GetChat getChat) throws TelegramApiException {
if (getChat == null) {
throw new TelegramApiException("Parameter getChat can not be null");
@ -136,6 +150,7 @@ public abstract class AbsSender {
return sendApiMethod(getChat);
}
@Deprecated
public final String exportChatInviteLink(ExportChatInviteLink exportChatInviteLink) throws TelegramApiException {
if (exportChatInviteLink == null) {
throw new TelegramApiException("Parameter exportChatInviteLink can not be null");
@ -143,6 +158,7 @@ public abstract class AbsSender {
return sendApiMethod(exportChatInviteLink);
}
@Deprecated
public final List<ChatMember> getChatAdministrators(GetChatAdministrators getChatAdministrators) throws TelegramApiException {
if (getChatAdministrators == null) {
throw new TelegramApiException("Parameter getChatAdministrators can not be null");
@ -150,6 +166,7 @@ public abstract class AbsSender {
return sendApiMethod(getChatAdministrators);
}
@Deprecated
public final ChatMember getChatMember(GetChatMember getChatMember) throws TelegramApiException {
if (getChatMember == null) {
throw new TelegramApiException("Parameter getChatMember can not be null");
@ -157,6 +174,7 @@ public abstract class AbsSender {
return sendApiMethod(getChatMember);
}
@Deprecated
public final Integer getChatMemberCount(GetChatMemberCount getChatMemberCount) throws TelegramApiException {
if (getChatMemberCount == null) {
throw new TelegramApiException("Parameter getChatMemberCount can not be null");
@ -164,6 +182,7 @@ public abstract class AbsSender {
return sendApiMethod(getChatMemberCount);
}
@Deprecated
public final Serializable editMessageText(EditMessageText editMessageText) throws TelegramApiException {
if (editMessageText == null) {
throw new TelegramApiException("Parameter editMessageText can not be null");
@ -171,6 +190,7 @@ public abstract class AbsSender {
return sendApiMethod(editMessageText);
}
@Deprecated
public final Serializable editMessageCaption(EditMessageCaption editMessageCaption) throws TelegramApiException {
if (editMessageCaption == null) {
throw new TelegramApiException("Parameter editMessageCaption can not be null");
@ -178,6 +198,7 @@ public abstract class AbsSender {
return sendApiMethod(editMessageCaption);
}
@Deprecated
public final Serializable editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup) throws TelegramApiException {
if (editMessageReplyMarkup == null) {
throw new TelegramApiException("Parameter editMessageReplyMarkup can not be null");
@ -185,6 +206,7 @@ public abstract class AbsSender {
return sendApiMethod(editMessageReplyMarkup);
}
@Deprecated
public final Boolean answerCallbackQuery(AnswerCallbackQuery answerCallbackQuery) throws TelegramApiException {
if (answerCallbackQuery == null) {
throw new TelegramApiException("Parameter answerCallbackQuery can not be null");
@ -192,6 +214,7 @@ public abstract class AbsSender {
return sendApiMethod(answerCallbackQuery);
}
@Deprecated
public final UserProfilePhotos getUserProfilePhotos(GetUserProfilePhotos getUserProfilePhotos) throws TelegramApiException {
if (getUserProfilePhotos == null) {
throw new TelegramApiException("Parameter getUserProfilePhotos can not be null");
@ -200,6 +223,7 @@ public abstract class AbsSender {
return sendApiMethod(getUserProfilePhotos);
}
@Deprecated
public final File getFile(GetFile getFile) throws TelegramApiException {
if(getFile == null){
throw new TelegramApiException("Parameter getFile can not be null");
@ -211,9 +235,7 @@ public abstract class AbsSender {
}
public final User getMe() throws TelegramApiException {
GetMe getMe = new GetMe();
return sendApiMethod(getMe);
return sendApiMethod(new GetMe());
}
public final WebhookInfo getWebhookInfo() throws TelegramApiException {
@ -221,6 +243,7 @@ public abstract class AbsSender {
return sendApiMethod(getWebhookInfo);
}
@Deprecated
public final Serializable setGameScore(SetGameScore setGameScore) throws TelegramApiException {
if(setGameScore == null){
throw new TelegramApiException("Parameter setGameScore can not be null");
@ -228,6 +251,7 @@ public abstract class AbsSender {
return sendApiMethod(setGameScore);
}
@Deprecated
public final Serializable getGameHighScores(GetGameHighScores getGameHighScores) throws TelegramApiException {
if(getGameHighScores == null){
throw new TelegramApiException("Parameter getGameHighScores can not be null");
@ -235,6 +259,7 @@ public abstract class AbsSender {
return sendApiMethod(getGameHighScores);
}
@Deprecated
public final Message sendGame(SendGame sendGame) throws TelegramApiException {
if(sendGame == null){
throw new TelegramApiException("Parameter sendGame can not be null");
@ -242,6 +267,7 @@ public abstract class AbsSender {
return sendApiMethod(sendGame);
}
@Deprecated
public final Boolean deleteWebhook(DeleteWebhook deleteWebhook) throws TelegramApiException {
if(deleteWebhook == null){
throw new TelegramApiException("Parameter deleteWebhook can not be null");
@ -249,6 +275,7 @@ public abstract class AbsSender {
return sendApiMethod(deleteWebhook);
}
@Deprecated
public final Message sendInvoice(SendInvoice sendInvoice) throws TelegramApiException {
if(sendInvoice == null){
throw new TelegramApiException("Parameter sendInvoice can not be null");
@ -256,6 +283,7 @@ public abstract class AbsSender {
return sendApiMethod(sendInvoice);
}
@Deprecated
public final Boolean answerShippingQuery(AnswerShippingQuery answerShippingQuery) throws TelegramApiException {
if(answerShippingQuery == null){
throw new TelegramApiException("Parameter answerShippingQuery can not be null");
@ -263,6 +291,7 @@ public abstract class AbsSender {
return sendApiMethod(answerShippingQuery);
}
@Deprecated
public final Boolean answerPreCheckoutQuery(AnswerPreCheckoutQuery answerPreCheckoutQuery) throws TelegramApiException {
if(answerPreCheckoutQuery == null){
throw new TelegramApiException("Parameter answerPreCheckoutQuery can not be null");
@ -270,6 +299,7 @@ public abstract class AbsSender {
return sendApiMethod(answerPreCheckoutQuery);
}
@Deprecated
public final Boolean deleteMessage(DeleteMessage deleteMessage) throws TelegramApiException {
if(deleteMessage == null){
throw new TelegramApiException("Parameter deleteMessage can not be null");
@ -277,6 +307,7 @@ public abstract class AbsSender {
return sendApiMethod(deleteMessage);
}
@Deprecated
public final Boolean deleteChatPhoto(DeleteChatPhoto deleteChatPhoto) throws TelegramApiException {
if(deleteChatPhoto == null){
throw new TelegramApiException("Parameter deleteChatPhoto can not be null");
@ -284,6 +315,7 @@ public abstract class AbsSender {
return sendApiMethod(deleteChatPhoto);
}
@Deprecated
public final Boolean pinChatMessage(PinChatMessage pinChatMessage) throws TelegramApiException {
if(pinChatMessage == null){
throw new TelegramApiException("Parameter pinChatMessage can not be null");
@ -291,6 +323,7 @@ public abstract class AbsSender {
return sendApiMethod(pinChatMessage);
}
@Deprecated
public final Boolean unpinChatMessage(UnpinChatMessage unpinChatMessage) throws TelegramApiException {
if(unpinChatMessage == null){
throw new TelegramApiException("Parameter unpinChatMessage can not be null");
@ -298,6 +331,7 @@ public abstract class AbsSender {
return sendApiMethod(unpinChatMessage);
}
@Deprecated
public final Boolean promoteChatMember(PromoteChatMember promoteChatMember) throws TelegramApiException {
if(promoteChatMember == null){
throw new TelegramApiException("Parameter promoteChatMember can not be null");
@ -305,6 +339,7 @@ public abstract class AbsSender {
return sendApiMethod(promoteChatMember);
}
@Deprecated
public final Boolean restrictChatMember(RestrictChatMember restrictChatMember) throws TelegramApiException {
if(restrictChatMember == null){
throw new TelegramApiException("Parameter restrictChatMember can not be null");
@ -312,6 +347,7 @@ public abstract class AbsSender {
return sendApiMethod(restrictChatMember);
}
@Deprecated
public final Boolean setChatDescription(SetChatDescription setChatDescription) throws TelegramApiException {
if(setChatDescription == null){
throw new TelegramApiException("Parameter setChatDescription can not be null");
@ -319,6 +355,7 @@ public abstract class AbsSender {
return sendApiMethod(setChatDescription);
}
@Deprecated
public final Boolean setChatTitle(SetChatTitle setChatTitle) throws TelegramApiException {
if(setChatTitle == null){
throw new TelegramApiException("Parameter setChatTitle can not be null");
@ -328,6 +365,7 @@ public abstract class AbsSender {
// Send Requests Async
@Deprecated
public final void sendMessageAsync(SendMessage sendMessage, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendMessage == null) {
throw new TelegramApiException("Parameter sendMessage can not be null");
@ -340,6 +378,7 @@ public abstract class AbsSender {
sendApiMethodAsync(sendMessage, sentCallback);
}
@Deprecated
public final void answerInlineQueryAsync(AnswerInlineQuery answerInlineQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerInlineQuery == null) {
throw new TelegramApiException("Parameter answerInlineQuery can not be null");
@ -352,6 +391,7 @@ public abstract class AbsSender {
sendApiMethodAsync(answerInlineQuery, sentCallback);
}
@Deprecated
public final void sendChatActionAsync(SendChatAction sendChatAction, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (sendChatAction == null) {
throw new TelegramApiException("Parameter sendChatAction can not be null");
@ -364,6 +404,7 @@ public abstract class AbsSender {
sendApiMethodAsync(sendChatAction, sentCallback);
}
@Deprecated
public final void forwardMessageAsync(ForwardMessage forwardMessage, SentCallback<Message> sentCallback) throws TelegramApiException {
if (forwardMessage == null) {
throw new TelegramApiException("Parameter forwardMessage can not be null");
@ -376,6 +417,7 @@ public abstract class AbsSender {
sendApiMethodAsync(forwardMessage, sentCallback);
}
@Deprecated
public final void sendLocationAsync(SendLocation sendLocation, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendLocation == null) {
throw new TelegramApiException("Parameter sendLocation can not be null");
@ -388,6 +430,7 @@ public abstract class AbsSender {
sendApiMethodAsync(sendLocation, sentCallback);
}
@Deprecated
public final void sendVenueAsync(SendVenue sendVenue, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendVenue == null) {
throw new TelegramApiException("Parameter sendVenue can not be null");
@ -400,6 +443,7 @@ public abstract class AbsSender {
sendApiMethodAsync(sendVenue, sentCallback);
}
@Deprecated
public final void sendContactAsync(SendContact sendContact, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendContact == null) {
throw new TelegramApiException("Parameter sendContact can not be null");
@ -411,6 +455,7 @@ public abstract class AbsSender {
sendApiMethodAsync(sendContact, sentCallback);
}
@Deprecated
public final void kickMemberAsync(KickChatMember kickChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (kickChatMember == null) {
throw new TelegramApiException("Parameter kickChatMember can not be null");
@ -422,6 +467,7 @@ public abstract class AbsSender {
sendApiMethodAsync(kickChatMember, sentCallback);
}
@Deprecated
public final void unbanMemberAsync(UnbanChatMember unbanChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (unbanChatMember == null) {
throw new TelegramApiException("Parameter unbanChatMember can not be null");
@ -433,6 +479,7 @@ public abstract class AbsSender {
sendApiMethodAsync(unbanChatMember, sentCallback);
}
@Deprecated
public final void leaveChatAsync(LeaveChat leaveChat, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (leaveChat == null) {
throw new TelegramApiException("Parameter leaveChat can not be null");
@ -443,6 +490,7 @@ public abstract class AbsSender {
sendApiMethodAsync(leaveChat, sentCallback);
}
@Deprecated
public final void getChatAsync(GetChat getChat, SentCallback<Chat> sentCallback) throws TelegramApiException {
if (getChat == null) {
throw new TelegramApiException("Parameter getChat can not be null");
@ -453,6 +501,7 @@ public abstract class AbsSender {
sendApiMethodAsync(getChat, sentCallback);
}
@Deprecated
public final void exportChatInviteLinkAsync(ExportChatInviteLink exportChatInviteLink, SentCallback<String> sentCallback) throws TelegramApiException {
if (exportChatInviteLink == null) {
throw new TelegramApiException("Parameter exportChatInviteLink can not be null");
@ -463,6 +512,7 @@ public abstract class AbsSender {
sendApiMethodAsync(exportChatInviteLink, sentCallback);
}
@Deprecated
public final void getChatAdministratorsAsync(GetChatAdministrators getChatAdministrators, SentCallback<ArrayList<ChatMember>> sentCallback) throws TelegramApiException {
if (getChatAdministrators == null) {
throw new TelegramApiException("Parameter getChatAdministrators can not be null");
@ -473,6 +523,7 @@ public abstract class AbsSender {
sendApiMethodAsync(getChatAdministrators, sentCallback);
}
@Deprecated
public final void getChatMemberAsync(GetChatMember getChatMember, SentCallback<ChatMember> sentCallback) throws TelegramApiException {
if (getChatMember == null) {
throw new TelegramApiException("Parameter getChatMember can not be null");
@ -483,6 +534,7 @@ public abstract class AbsSender {
sendApiMethodAsync(getChatMember, sentCallback);
}
@Deprecated
public final void getChatMemberCountAsync(GetChatMemberCount getChatMemberCount, SentCallback<Integer> sentCallback) throws TelegramApiException {
if (getChatMemberCount == null) {
throw new TelegramApiException("Parameter getChatMemberCount can not be null");
@ -494,6 +546,7 @@ public abstract class AbsSender {
sendApiMethodAsync(getChatMemberCount, sentCallback);
}
@Deprecated
public final void editMessageTextAsync(EditMessageText editMessageText, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (editMessageText == null) {
throw new TelegramApiException("Parameter editMessageText can not be null");
@ -505,6 +558,7 @@ public abstract class AbsSender {
sendApiMethodAsync(editMessageText, sentCallback);
}
@Deprecated
public final void editMessageCaptionAsync(EditMessageCaption editMessageCaption, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (editMessageCaption == null) {
throw new TelegramApiException("Parameter editMessageCaption can not be null");
@ -516,6 +570,7 @@ public abstract class AbsSender {
sendApiMethodAsync(editMessageCaption, sentCallback);
}
@Deprecated
public final void editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (editMessageReplyMarkup == null) {
throw new TelegramApiException("Parameter editMessageReplyMarkup can not be null");
@ -527,6 +582,7 @@ public abstract class AbsSender {
sendApiMethodAsync(editMessageReplyMarkup, sentCallback);
}
@Deprecated
public final void answerCallbackQueryAsync(AnswerCallbackQuery answerCallbackQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerCallbackQuery == null) {
throw new TelegramApiException("Parameter answerCallbackQuery can not be null");
@ -538,6 +594,7 @@ public abstract class AbsSender {
sendApiMethodAsync(answerCallbackQuery, sentCallback);
}
@Deprecated
public final void getUserProfilePhotosAsync(GetUserProfilePhotos getUserProfilePhotos, SentCallback<UserProfilePhotos> sentCallback) throws TelegramApiException {
if (getUserProfilePhotos == null) {
throw new TelegramApiException("Parameter getUserProfilePhotos can not be null");
@ -549,6 +606,7 @@ public abstract class AbsSender {
sendApiMethodAsync(getUserProfilePhotos, sentCallback);
}
@Deprecated
public final void getFileAsync(GetFile getFile, SentCallback<File> sentCallback) throws TelegramApiException {
if (getFile == null) {
throw new TelegramApiException("Parameter getFile can not be null");
@ -564,19 +622,17 @@ public abstract class AbsSender {
if (sentCallback == null) {
throw new TelegramApiException("Parameter sentCallback can not be null");
}
GetMe getMe = new GetMe();
sendApiMethodAsync(getMe, sentCallback);
sendApiMethodAsync(new GetMe(), sentCallback);
}
public final void getWebhookInfoAsync(SentCallback<WebhookInfo> sentCallback) throws TelegramApiException {
if (sentCallback == null) {
throw new TelegramApiException("Parameter sentCallback can not be null");
}
GetWebhookInfo getWebhookInfo = new GetWebhookInfo();
sendApiMethodAsync(getWebhookInfo, sentCallback);
sendApiMethodAsync(new GetWebhookInfo(), sentCallback);
}
@Deprecated
public final void setGameScoreAsync(SetGameScore setGameScore, SentCallback<Serializable> sentCallback) throws TelegramApiException {
if (setGameScore == null) {
throw new TelegramApiException("Parameter setGameScore can not be null");
@ -587,6 +643,7 @@ public abstract class AbsSender {
sendApiMethodAsync(setGameScore, sentCallback);
}
@Deprecated
public final void getGameHighScoresAsync(GetGameHighScores getGameHighScores, SentCallback<ArrayList<GameHighScore>> sentCallback) throws TelegramApiException {
if (getGameHighScores == null) {
throw new TelegramApiException("Parameter getGameHighScores can not be null");
@ -597,6 +654,7 @@ public abstract class AbsSender {
sendApiMethodAsync(getGameHighScores, sentCallback);
}
@Deprecated
public final void sendGameAsync(SendGame sendGame, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendGame == null) {
throw new TelegramApiException("Parameter sendGame can not be null");
@ -607,6 +665,7 @@ public abstract class AbsSender {
sendApiMethodAsync(sendGame, sentCallback);
}
@Deprecated
public final void deleteWebhook(DeleteWebhook deleteWebhook, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (deleteWebhook == null) {
throw new TelegramApiException("Parameter deleteWebhook can not be null");
@ -617,6 +676,7 @@ public abstract class AbsSender {
sendApiMethodAsync(deleteWebhook, sentCallback);
}
@Deprecated
public final void sendInvoice(SendInvoice sendInvoice, SentCallback<Message> sentCallback) throws TelegramApiException {
if (sendInvoice == null) {
throw new TelegramApiException("Parameter sendInvoice can not be null");
@ -627,6 +687,7 @@ public abstract class AbsSender {
sendApiMethodAsync(sendInvoice, sentCallback);
}
@Deprecated
public final void answerShippingQuery(AnswerShippingQuery answerShippingQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerShippingQuery == null) {
throw new TelegramApiException("Parameter answerShippingQuery can not be null");
@ -637,6 +698,7 @@ public abstract class AbsSender {
sendApiMethodAsync(answerShippingQuery, sentCallback);
}
@Deprecated
public final void answerPreCheckoutQuery(AnswerPreCheckoutQuery answerPreCheckoutQuery, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (answerPreCheckoutQuery == null) {
throw new TelegramApiException("Parameter answerPreCheckoutQuery can not be null");
@ -647,6 +709,7 @@ public abstract class AbsSender {
sendApiMethodAsync(answerPreCheckoutQuery, sentCallback);
}
@Deprecated
public final void deleteMessage(DeleteMessage deleteMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (deleteMessage == null) {
throw new TelegramApiException("Parameter deleteMessage can not be null");
@ -657,6 +720,7 @@ public abstract class AbsSender {
sendApiMethodAsync(deleteMessage, sentCallback);
}
@Deprecated
public final void deleteChatPhoto(DeleteChatPhoto deleteChatPhoto, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (deleteChatPhoto == null) {
throw new TelegramApiException("Parameter deleteChatPhoto can not be null");
@ -667,6 +731,7 @@ public abstract class AbsSender {
sendApiMethodAsync(deleteChatPhoto, sentCallback);
}
@Deprecated
public final void pinChatMessage(PinChatMessage pinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (pinChatMessage == null) {
throw new TelegramApiException("Parameter pinChatMessage can not be null");
@ -677,6 +742,7 @@ public abstract class AbsSender {
sendApiMethodAsync(pinChatMessage, sentCallback);
}
@Deprecated
public final void unpinChatMessage(UnpinChatMessage unpinChatMessage, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (unpinChatMessage == null) {
throw new TelegramApiException("Parameter unpinChatMessage can not be null");
@ -687,6 +753,7 @@ public abstract class AbsSender {
sendApiMethodAsync(unpinChatMessage, sentCallback);
}
@Deprecated
public final void promoteChatMember(PromoteChatMember promoteChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (promoteChatMember == null) {
throw new TelegramApiException("Parameter promoteChatMember can not be null");
@ -697,6 +764,7 @@ public abstract class AbsSender {
sendApiMethodAsync(promoteChatMember, sentCallback);
}
@Deprecated
public final void restrictChatMember(RestrictChatMember restrictChatMember, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (restrictChatMember == null) {
throw new TelegramApiException("Parameter restrictChatMember can not be null");
@ -707,6 +775,7 @@ public abstract class AbsSender {
sendApiMethodAsync(restrictChatMember, sentCallback);
}
@Deprecated
public final void setChatDescription(SetChatDescription setChatDescription, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (setChatDescription == null) {
throw new TelegramApiException("Parameter setChatDescription can not be null");
@ -717,6 +786,7 @@ public abstract class AbsSender {
sendApiMethodAsync(setChatDescription, sentCallback);
}
@Deprecated
public final void setChatTitle(SetChatTitle setChatTitle, SentCallback<Boolean> sentCallback) throws TelegramApiException {
if (setChatTitle == null) {
throw new TelegramApiException("Parameter setChatTitle can not be null");
@ -763,6 +833,30 @@ public abstract class AbsSender {
*/
public abstract Boolean setChatPhoto(SetChatPhoto setChatPhoto) throws TelegramApiException;
/**
* Adds a new sticker to a set (https://core.telegram.org/bots/api#addStickerToSet)
* @param addStickerToSet Information of the sticker to set
* @return If success, true is returned
* @throws TelegramApiException If there is any error adding the sticker to the set
*/
public abstract Boolean addStickerToSet(AddStickerToSet addStickerToSet) throws TelegramApiException;
/**
* Creates a new sticker set (https://core.telegram.org/bots/api#createNewStickerSet)
* @param createNewStickerSet Information of the sticker set to create
* @return If success, true is returned
* @throws TelegramApiException If there is any error creating the new sticker set
*/
public abstract Boolean createNewStickerSet(CreateNewStickerSet createNewStickerSet) throws TelegramApiException;
/**
* Upload a new file as sticker (https://core.telegram.org/bots/api#uploadStickerFile)
* @param uploadStickerFile Information of the file to upload as sticker
* @return If success, true is returned
* @throws TelegramApiException If there is any error uploading the new file
*/
public abstract File uploadStickerFile(UploadStickerFile uploadStickerFile) throws TelegramApiException;
// Simplified methods
protected abstract <T extends Serializable, Method extends BotApiMethod<T>, Callback extends SentCallback<T>> void sendApiMethodAsync(Method method, Callback callback);

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>3.1.2</version>
<version>3.2</version>
<packaging>jar</packaging>
<name>Telegram Bots</name>
@ -66,7 +66,7 @@
<jackson.version>2.8.7</jackson.version>
<jacksonanotation.version>2.8.0</jacksonanotation.version>
<commonio.version>2.5</commonio.version>
<bots.version>3.1.2</bots.version>
<bots.version>3.2</bots.version>
</properties>
<dependencyManagement>

View File

@ -17,6 +17,9 @@ import org.apache.http.util.EntityUtils;
import org.telegram.telegrambots.api.methods.BotApiMethod;
import org.telegram.telegrambots.api.methods.groupadministration.SetChatPhoto;
import org.telegram.telegrambots.api.methods.send.*;
import org.telegram.telegrambots.api.methods.stickers.AddStickerToSet;
import org.telegram.telegrambots.api.methods.stickers.CreateNewStickerSet;
import org.telegram.telegrambots.api.methods.stickers.UploadStickerFile;
import org.telegram.telegrambots.api.objects.File;
import org.telegram.telegrambots.api.objects.Message;
import org.telegram.telegrambots.exceptions.TelegramApiException;
@ -89,105 +92,48 @@ public abstract class DefaultAbsSender extends AbsSender {
throw new TelegramApiException("Parameter file can not be null");
}
String url = File.getFileUrl(getBotToken(), filePath);
java.io.File output;
try {
output = java.io.File.createTempFile(Long.toString(System.currentTimeMillis()), ".tmp");
FileUtils.copyURLToFile(new URL(url), output);
} catch (MalformedURLException e) {
throw new TelegramApiException("Wrong url for file: " + url);
} catch (IOException e) {
throw new TelegramApiRequestException("Error downloading the file", e);
}
return output;
String tempFileName = Long.toString(System.currentTimeMillis());
return downloadToTemporaryFileWrappingExceptions(url, tempFileName);
}
public final java.io.File downloadFile(File file) throws TelegramApiException {
if(file == null){
throw new TelegramApiException("Parameter file can not be null");
}
assertParamNotNull(file, "file");
String url = file.getFileUrl(getBotToken());
java.io.File output;
try {
output = java.io.File.createTempFile(file.getFileId(), ".tmp");
FileUtils.copyURLToFile(new URL(url), output);
} catch (MalformedURLException e) {
throw new TelegramApiException("Wrong url for file: " + url);
} catch (IOException e) {
throw new TelegramApiRequestException("Error downloading the file", e);
}
return output;
String tempFileName = file.getFileId();
return downloadToTemporaryFileWrappingExceptions(url, tempFileName);
}
public final void downloadFileAsync(String filePath, DownloadFileCallback<String> callback) throws TelegramApiException {
if(filePath == null || filePath.isEmpty()){
throw new TelegramApiException("Parameter filePath can not be null");
}
if (callback == null) {
throw new TelegramApiException("Parameter callback can not be null");
}
exe.submit(new Runnable() {
@Override
public void run() {
String url = File.getFileUrl(getBotToken(), filePath);
try {
java.io.File output = java.io.File.createTempFile(Long.toString(System.currentTimeMillis()), ".tmp");
FileUtils.copyURLToFile(new URL(url), output);
callback.onResult(filePath, output);
} catch (MalformedURLException e) {
callback.onException(filePath, new TelegramApiException("Wrong url for file: " + url));
} catch (IOException e) {
callback.onException(filePath, new TelegramApiRequestException("Error downloading the file", e));
}
}
});
assertParamNotNull(callback, "callback");
String url = File.getFileUrl(getBotToken(), filePath);
String tempFileName = Long.toString(System.currentTimeMillis());
exe.submit(getDownloadFileAsyncJob(filePath, callback, url, tempFileName));
}
public final void downloadFileAsync(File file, DownloadFileCallback<File> callback) throws TelegramApiException {
if(file == null){
throw new TelegramApiException("Parameter file can not be null");
}
if (callback == null) {
throw new TelegramApiException("Parameter callback can not be null");
}
exe.submit(new Runnable() {
@Override
public void run() {
String url = file.getFileUrl(getBotToken());
try {
java.io.File output = java.io.File.createTempFile(file.getFileId(), ".tmp");
FileUtils.copyURLToFile(new URL(url), output);
callback.onResult(file, output);
} catch (MalformedURLException e) {
callback.onException(file, new TelegramApiException("Wrong url for file: " + url));
} catch (IOException e) {
callback.onException(file, new TelegramApiRequestException("Error downloading the file", e));
}
}
});
assertParamNotNull(file, "file");
assertParamNotNull(callback, "callback");
String url = file.getFileUrl(getBotToken());
String tempFileName = file.getFileId();
exe.submit(getDownloadFileAsyncJob(file, callback, url, tempFileName));
}
// Specific Send Requests
@Override
public final Message sendDocument(SendDocument sendDocument) throws TelegramApiException {
if(sendDocument == null){
throw new TelegramApiException("Parameter sendDocument can not be null");
}
assertParamNotNull(sendDocument, "sendDocument");
sendDocument.validate();
String responseContent;
try {
String url = getBaseUrl() + SendDocument.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SendDocument.CHATID_FIELD, sendDocument.getChatId());
builder.addTextBody(SendDocument.CHATID_FIELD, sendDocument.getChatId(), TEXT_PLAIN_CONTENT_TYPE);
if (sendDocument.isNewDocument()) {
if (sendDocument.getNewDocumentFile() != null) {
builder.addBinaryBody(SendDocument.DOCUMENT_FIELD, sendDocument.getNewDocumentFile());
@ -197,47 +143,37 @@ public abstract class DefaultAbsSender extends AbsSender {
builder.addBinaryBody(SendDocument.DOCUMENT_FIELD, new java.io.File(sendDocument.getDocument()), ContentType.APPLICATION_OCTET_STREAM, sendDocument.getDocumentName());
}
} else {
builder.addTextBody(SendDocument.DOCUMENT_FIELD, sendDocument.getDocument());
builder.addTextBody(SendDocument.DOCUMENT_FIELD, sendDocument.getDocument(), TEXT_PLAIN_CONTENT_TYPE);
}
if (sendDocument.getReplyMarkup() != null) {
builder.addTextBody(SendDocument.REPLYMARKUP_FIELD, objectMapper.writeValueAsString(sendDocument.getReplyMarkup()), TEXT_PLAIN_CONTENT_TYPE);
}
if (sendDocument.getReplyToMessageId() != null) {
builder.addTextBody(SendDocument.REPLYTOMESSAGEID_FIELD, sendDocument.getReplyToMessageId().toString());
builder.addTextBody(SendDocument.REPLYTOMESSAGEID_FIELD, sendDocument.getReplyToMessageId().toString(), TEXT_PLAIN_CONTENT_TYPE);
}
if (sendDocument.getCaption() != null) {
builder.addTextBody(SendDocument.CAPTION_FIELD, sendDocument.getCaption(), TEXT_PLAIN_CONTENT_TYPE);
}
if (sendDocument.getDisableNotification() != null) {
builder.addTextBody(SendDocument.DISABLENOTIFICATION_FIELD, sendDocument.getDisableNotification().toString());
builder.addTextBody(SendDocument.DISABLENOTIFICATION_FIELD, sendDocument.getDisableNotification().toString(), TEXT_PLAIN_CONTENT_TYPE);
}
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return sendDocument.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to send document", e);
}
return sendDocument.deserializeResponse(responseContent);
}
@Override
public final Message sendPhoto(SendPhoto sendPhoto) throws TelegramApiException {
if(sendPhoto == null){
throw new TelegramApiException("Parameter sendPhoto can not be null");
}
assertParamNotNull(sendPhoto, "sendPhoto");
sendPhoto.validate();
String responseContent;
try {
String url = getBaseUrl() + SendPhoto.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SendPhoto.CHATID_FIELD, sendPhoto.getChatId());
@ -267,30 +203,20 @@ public abstract class DefaultAbsSender extends AbsSender {
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return sendPhoto.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to send photo", e);
}
return sendPhoto.deserializeResponse(responseContent);
}
@Override
public final Message sendVideo(SendVideo sendVideo) throws TelegramApiException {
if(sendVideo == null){
throw new TelegramApiException("Parameter sendVideo can not be null");
}
assertParamNotNull(sendVideo, "sendVideo");
sendVideo.validate();
String responseContent;
try {
String url = getBaseUrl() + SendVideo.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SendVideo.CHATID_FIELD, sendVideo.getChatId());
@ -329,30 +255,20 @@ public abstract class DefaultAbsSender extends AbsSender {
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return sendVideo.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to send video", e);
}
return sendVideo.deserializeResponse(responseContent);
}
@Override
public final Message sendVideoNote(SendVideoNote sendVideoNote) throws TelegramApiException {
if(sendVideoNote == null){
throw new TelegramApiException("Parameter sendVideoNote can not be null");
}
assertParamNotNull(sendVideoNote, "sendVideoNote");
sendVideoNote.validate();
String responseContent;
try {
String url = getBaseUrl() + SendVideoNote.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SendVideoNote.CHATID_FIELD, sendVideoNote.getChatId());
@ -386,30 +302,20 @@ public abstract class DefaultAbsSender extends AbsSender {
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return sendVideoNote.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to send video note", e);
}
return sendVideoNote.deserializeResponse(responseContent);
}
@Override
public final Message sendSticker(SendSticker sendSticker) throws TelegramApiException {
if(sendSticker == null){
throw new TelegramApiException("Parameter sendSticker can not be null");
}
assertParamNotNull(sendSticker, "sendSticker");
sendSticker.validate();
String responseContent;
try {
String url = getBaseUrl() + SendSticker.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SendSticker.CHATID_FIELD, sendSticker.getChatId());
@ -436,16 +342,10 @@ public abstract class DefaultAbsSender extends AbsSender {
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return sendSticker.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to send sticker", e);
}
return sendSticker.deserializeResponse(responseContent);
}
/**
@ -456,16 +356,11 @@ public abstract class DefaultAbsSender extends AbsSender {
*/
@Override
public final Message sendAudio(SendAudio sendAudio) throws TelegramApiException {
if(sendAudio == null){
throw new TelegramApiException("Parameter sendAudio can not be null");
}
assertParamNotNull(sendAudio, "sendAudio");
sendAudio.validate();
String responseContent;
try {
String url = getBaseUrl() + SendAudio.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SendAudio.CHATID_FIELD, sendAudio.getChatId());
if (sendAudio.isNewAudio()) {
@ -504,16 +399,10 @@ public abstract class DefaultAbsSender extends AbsSender {
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return sendAudio.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to send sticker", e);
}
return sendAudio.deserializeResponse(responseContent);
}
/**
@ -525,16 +414,11 @@ public abstract class DefaultAbsSender extends AbsSender {
*/
@Override
public final Message sendVoice(SendVoice sendVoice) throws TelegramApiException {
if(sendVoice == null){
throw new TelegramApiException("Parameter sendVoice can not be null");
}
assertParamNotNull(sendVoice, "sendVoice");
sendVoice.validate();
String responseContent;
try {
String url = getBaseUrl() + SendVoice.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SendVoice.CHATID_FIELD, sendVoice.getChatId());
if (sendVoice.isNewVoice()) {
@ -566,30 +450,20 @@ public abstract class DefaultAbsSender extends AbsSender {
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return sendVoice.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to send voice", e);
}
return sendVoice.deserializeResponse(responseContent);
}
@Override
public Boolean setChatPhoto(SetChatPhoto setChatPhoto) throws TelegramApiException {
if(setChatPhoto == null){
throw new TelegramApiException("Parameter setChatPhoto can not be null");
}
assertParamNotNull(setChatPhoto, "setChatPhoto");
setChatPhoto.validate();
String responseContent;
try {
String url = getBaseUrl() + SetChatPhoto.PATH;
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(SetChatPhoto.CHATID_FIELD, setChatPhoto.getChatId());
@ -601,16 +475,104 @@ public abstract class DefaultAbsSender extends AbsSender {
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
return setChatPhoto.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to set chat photo", e);
}
}
return setChatPhoto.deserializeResponse(responseContent);
@Override
public Boolean addStickerToSet(AddStickerToSet addStickerToSet) throws TelegramApiException {
assertParamNotNull(addStickerToSet, "addStickerToSet");
addStickerToSet.validate();
try {
String url = getBaseUrl() + AddStickerToSet.PATH;
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(AddStickerToSet.USERID_FIELD, addStickerToSet.getUserId().toString(), TEXT_PLAIN_CONTENT_TYPE);
builder.addTextBody(AddStickerToSet.NAME_FIELD, addStickerToSet.getName(), TEXT_PLAIN_CONTENT_TYPE);
builder.addTextBody(AddStickerToSet.EMOJIS_FIELD, addStickerToSet.getEmojis(), TEXT_PLAIN_CONTENT_TYPE);
if (addStickerToSet.isNewPngSticker()) {
if (addStickerToSet.getPngStickerFile() != null) {
builder.addBinaryBody(AddStickerToSet.PNGSTICKER_FIELD, addStickerToSet.getPngStickerFile());
} else if (addStickerToSet.getPngStickerStream() != null) {
builder.addBinaryBody(AddStickerToSet.PNGSTICKER_FIELD, addStickerToSet.getPngStickerStream(), ContentType.APPLICATION_OCTET_STREAM, addStickerToSet.getPngStickerName());
} else {
builder.addBinaryBody(AddStickerToSet.PNGSTICKER_FIELD, new java.io.File(addStickerToSet.getPngSticker()), ContentType.create("image/png"), addStickerToSet.getPngStickerName());
}
} else {
builder.addTextBody(AddStickerToSet.PNGSTICKER_FIELD, addStickerToSet.getPngSticker());
}
if (addStickerToSet.getMaskPosition() != null) {
builder.addTextBody(AddStickerToSet.MASKPOSITION_FIELD, objectMapper.writeValueAsString(addStickerToSet.getMaskPosition()), TEXT_PLAIN_CONTENT_TYPE);
}
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
return addStickerToSet.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to add sticker to set", e);
}
}
@Override
public Boolean createNewStickerSet(CreateNewStickerSet createNewStickerSet) throws TelegramApiException {
assertParamNotNull(createNewStickerSet, "createNewStickerSet");
createNewStickerSet.validate();
try {
String url = getBaseUrl() + CreateNewStickerSet.PATH;
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(CreateNewStickerSet.USERID_FIELD, createNewStickerSet.getUserId().toString(), TEXT_PLAIN_CONTENT_TYPE);
builder.addTextBody(CreateNewStickerSet.NAME_FIELD, createNewStickerSet.getName(), TEXT_PLAIN_CONTENT_TYPE);
builder.addTextBody(CreateNewStickerSet.TITLE_FIELD, createNewStickerSet.getTitle(), TEXT_PLAIN_CONTENT_TYPE);
builder.addTextBody(CreateNewStickerSet.EMOJIS_FIELD, createNewStickerSet.getEmojis(), TEXT_PLAIN_CONTENT_TYPE);
builder.addTextBody(CreateNewStickerSet.CONTAINSMASKS_FIELD, createNewStickerSet.getContainsMasks().toString());
if (createNewStickerSet.isNewPngSticker()) {
if (createNewStickerSet.getPngStickerFile() != null) {
builder.addBinaryBody(CreateNewStickerSet.PNGSTICKER_FIELD, createNewStickerSet.getPngStickerFile());
} else if (createNewStickerSet.getPngStickerStream() != null) {
builder.addBinaryBody(CreateNewStickerSet.PNGSTICKER_FIELD, createNewStickerSet.getPngStickerStream(), ContentType.APPLICATION_OCTET_STREAM, createNewStickerSet.getPngStickerName());
} else {
builder.addBinaryBody(CreateNewStickerSet.PNGSTICKER_FIELD, new java.io.File(createNewStickerSet.getPngSticker()), ContentType.create("image/png"), createNewStickerSet.getPngStickerName());
}
} else {
builder.addTextBody(CreateNewStickerSet.PNGSTICKER_FIELD, createNewStickerSet.getPngSticker());
}
if (createNewStickerSet.getMaskPosition() != null) {
builder.addTextBody(CreateNewStickerSet.MASKPOSITION_FIELD, objectMapper.writeValueAsString(createNewStickerSet.getMaskPosition()), TEXT_PLAIN_CONTENT_TYPE);
}
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
return createNewStickerSet.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to create new sticker set", e);
}
}
@Override
public File uploadStickerFile(UploadStickerFile uploadStickerFile) throws TelegramApiException {
assertParamNotNull(uploadStickerFile, "uploadStickerFile");
uploadStickerFile.validate();
try {
String url = getBaseUrl() + UploadStickerFile.PATH;
HttpPost httppost = configuredHttpPost(url);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody(UploadStickerFile.USERID_FIELD, uploadStickerFile.getUserId().toString(), TEXT_PLAIN_CONTENT_TYPE);
if (uploadStickerFile.getNewPngStickerFile() != null) {
builder.addBinaryBody(UploadStickerFile.PNGSTICKER_FIELD, uploadStickerFile.getNewPngStickerFile());
} else if (uploadStickerFile.getNewPngStickerStream() != null) {
builder.addBinaryBody(UploadStickerFile.PNGSTICKER_FIELD, uploadStickerFile.getNewPngStickerStream(), ContentType.APPLICATION_OCTET_STREAM, uploadStickerFile.getNewPngStickerName());
}
HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
return uploadStickerFile.deserializeResponse(sendHttpPostRequest(httppost));
} catch (IOException e) {
throw new TelegramApiException("Unable to upload new sticker file", e);
}
}
// Simplified methods
@ -622,21 +584,11 @@ public abstract class DefaultAbsSender extends AbsSender {
@Override
public void run() {
try {
method.validate();
String url = getBaseUrl() + method.getMethod();
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
httppost.addHeader("charset", StandardCharsets.UTF_8.name());
httppost.setEntity(new StringEntity(objectMapper.writeValueAsString(method), ContentType.APPLICATION_JSON));
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
String responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
try {
callback.onResult(method, method.deserializeResponse(responseContent));
} catch (TelegramApiRequestException e) {
callback.onError(method, e);
}
String responseContent = sendMethodRequest(method);
try {
callback.onResult(method, method.deserializeResponse(responseContent));
} catch (TelegramApiRequestException e) {
callback.onError(method, e);
}
} catch (IOException | TelegramApiValidationException e) {
callback.onException(method, e);
@ -648,27 +600,76 @@ public abstract class DefaultAbsSender extends AbsSender {
@Override
protected final <T extends Serializable, Method extends BotApiMethod<T>> T sendApiMethod(Method method) throws TelegramApiException {
method.validate();
String responseContent;
try {
String url = getBaseUrl() + method.getMethod();
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
httppost.addHeader("charset", StandardCharsets.UTF_8.name());
httppost.setEntity(new StringEntity(objectMapper.writeValueAsString(method), ContentType.APPLICATION_JSON));
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
String responseContent = sendMethodRequest(method);
return method.deserializeResponse(responseContent);
} catch (IOException e) {
throw new TelegramApiException("Unable to execute " + method.getMethod() + " method", e);
}
}
return method.deserializeResponse(responseContent);
private <T> Runnable getDownloadFileAsyncJob(T fileIdentifier, DownloadFileCallback<T> callback, String url, String tempFileName) {
//noinspection Convert2Lambda
return new Runnable() {
@Override
public void run() {
try {
callback.onResult(fileIdentifier, downloadToTemporaryFile(url, tempFileName));
} catch (MalformedURLException e) {
callback.onException(fileIdentifier, new TelegramApiException("Wrong url for file: " + url));
} catch (IOException e) {
callback.onException(fileIdentifier, new TelegramApiRequestException("Error downloading the file", e));
}
}
};
}
private java.io.File downloadToTemporaryFileWrappingExceptions(String url, String tempFileName) throws TelegramApiException {
try {
return downloadToTemporaryFile(url, tempFileName);
} catch (MalformedURLException e) {
throw new TelegramApiException("Wrong url for file: " + url);
} catch (IOException e) {
throw new TelegramApiRequestException("Error downloading the file", e);
}
}
private java.io.File downloadToTemporaryFile(String url, String tempFileName) throws IOException {
java.io.File output = java.io.File.createTempFile(tempFileName, ".tmp");
FileUtils.copyURLToFile(new URL(url), output);
return output;
}
private <T extends Serializable, Method extends BotApiMethod<T>> String sendMethodRequest(Method method) throws TelegramApiValidationException, IOException {
method.validate();
String url = getBaseUrl() + method.getMethod();
HttpPost httppost = configuredHttpPost(url);
httppost.addHeader("charset", StandardCharsets.UTF_8.name());
httppost.setEntity(new StringEntity(objectMapper.writeValueAsString(method), ContentType.APPLICATION_JSON));
return sendHttpPostRequest(httppost);
}
private String sendHttpPostRequest(HttpPost httppost) throws IOException {
try (CloseableHttpResponse response = httpclient.execute(httppost)) {
HttpEntity ht = response.getEntity();
BufferedHttpEntity buf = new BufferedHttpEntity(ht);
return EntityUtils.toString(buf, StandardCharsets.UTF_8);
}
}
private HttpPost configuredHttpPost(String url) {
HttpPost httppost = new HttpPost(url);
httppost.setConfig(requestConfig);
return httppost;
}
protected String getBaseUrl() {
return options.getBaseUrl() + getBotToken() + "/";
}
private void assertParamNotNull(Object param, String paramName) throws TelegramApiException {
if (param == null) {
throw new TelegramApiException("Parameter " + paramName + " can not be null");
}
}
}

View File

@ -1,9 +1,7 @@
package org.telegram.telegrambots.updatesreceivers;
import com.google.inject.Inject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Inject;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
@ -21,15 +19,12 @@ import org.telegram.telegrambots.api.methods.updates.GetUpdates;
import org.telegram.telegrambots.api.objects.Update;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.generics.BotOptions;
import org.telegram.telegrambots.generics.BotSession;
import org.telegram.telegrambots.generics.LongPollingBot;
import org.telegram.telegrambots.generics.UpdatesHandler;
import org.telegram.telegrambots.generics.UpdatesReader;
import org.telegram.telegrambots.generics.*;
import org.telegram.telegrambots.logging.BotLogger;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidParameterException;
import java.util.List;
@ -229,6 +224,8 @@ public class DefaultBotSession implements BotSession {
BotLogger.severe(responseContent, LOGTAG, e);
}
}
} catch (SocketTimeoutException e) {
BotLogger.fine(LOGTAG, e);
} catch (InvalidObjectException | TelegramApiRequestException e) {
BotLogger.severe(LOGTAG, e);
}