All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.telegram.telegrambots.abilitybots.api.bot.DefaultAbilities Maven / Gradle / Ivy

There is a newer version: 8.0.0
Show newest version
package org.telegram.telegrambots.abilitybots.api.bot;

import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.telegram.telegrambots.abilitybots.api.db.DBContext;
import org.telegram.telegrambots.abilitybots.api.objects.Ability;
import org.telegram.telegrambots.abilitybots.api.objects.MessageContext;
import org.telegram.telegrambots.abilitybots.api.objects.Privacy;
import org.telegram.telegrambots.abilitybots.api.util.AbilityExtension;
import org.telegram.telegrambots.abilitybots.api.util.AbilityUtils;
import org.telegram.telegrambots.abilitybots.api.util.Pair;
import org.telegram.telegrambots.abilitybots.api.objects.Locality;
import org.telegram.telegrambots.meta.api.methods.GetFile;
import org.telegram.telegrambots.meta.api.methods.send.SendDocument;
import org.telegram.telegrambots.meta.api.objects.InputFile;
import org.telegram.telegrambots.meta.api.objects.message.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintStream;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.MultimapBuilder.hashKeys;
import static java.lang.String.format;
import static java.util.Comparator.comparing;
import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.telegram.telegrambots.abilitybots.api.objects.Ability.builder;
import static org.telegram.telegrambots.abilitybots.api.objects.Flag.DOCUMENT;
import static org.telegram.telegrambots.abilitybots.api.objects.Flag.MESSAGE;
import static org.telegram.telegrambots.abilitybots.api.objects.Flag.REPLY;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_BAN_FAIL;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_BAN_SUCCESS;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_CLAIM_FAIL;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_CLAIM_SUCCESS;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_COMMANDS_NOT_FOUND;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_DEMOTE_FAIL;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_DEMOTE_SUCCESS;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_PROMOTE_FAIL;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_PROMOTE_SUCCESS;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_ERROR;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_FAIL;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_MESSAGE;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_SUCCESS;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_UNBAN_FAIL;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.ABILITY_UNBAN_SUCCESS;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityMessageCodes.USER_NOT_FOUND;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityUtils.addTag;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityUtils.escape;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityUtils.getLocalizedMessage;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityUtils.shortName;
import static org.telegram.telegrambots.abilitybots.api.util.AbilityUtils.stripTag;

@Slf4j
public final class DefaultAbilities implements AbilityExtension {
  // Default commands
  public static final String CLAIM = "claim";
  public static final String BAN = "ban";
  public static final String PROMOTE = "promote";
  public static final String DEMOTE = "demote";
  public static final String UNBAN = "unban";
  public static final String BACKUP = "backup";
  public static final String RECOVER = "recover";
  public static final String COMMANDS = "commands";
  public static final String REPORT = "report";
  public static final String STATS = "stats";
  private final BaseAbilityBot bot;

  public DefaultAbilities(BaseAbilityBot bot) {
    this.bot = bot;
  }

  /**
   * 

* Format of the report: *

* [command1] - [description1] *

* [command2] - [description2] *

* ... *

* Once you invoke it, the bot will send the available commands to the chat. This is a public ability so anyone can invoke it. *

* Usage: /commands * * @return the ability to report commands defined by the child bot. */ public Ability reportCommands() { return builder() .name(REPORT) .locality(Locality.ALL) .privacy(Privacy.CREATOR) .input(0) .action(ctx -> { String commands = bot.getAbilities().values().stream() .filter(ability -> nonNull(ability.info())) .map(ability -> { String name = ability.name(); String info = ability.info(); return format("%s - %s", name, info); }) .sorted() .reduce((a, b) -> format("%s%n%s", a, b)) .orElse(getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode())); bot.silent.send(commands, ctx.chatId()); }) .build(); } /** * Default format: *

* PUBLIC *

* [command1] - [description1] *

* [command2] - [description2] *

* GROUP_ADMIN *

* [command1] - [description1] *

* ... * * @return the ability to print commands based on the privacy of the requesting user */ public Ability commands() { return builder() .name(COMMANDS) .locality(Locality.USER) .privacy(Privacy.PUBLIC) .input(0) .action(ctx -> { Privacy privacy = bot.getPrivacy(ctx.update(), ctx.user().getId()); ListMultimap abilitiesPerPrivacy = bot.getAbilities().values().stream() .map(ability -> { String name = ability.name(); String info = ability.info(); if (!isEmpty(info)) return Pair.of(ability.privacy(), format("/%s - %s", name, info)); return Pair.of(ability.privacy(), format("/%s", name)); }) .sorted(comparing(Pair::b)) .collect(() -> hashKeys().arrayListValues().build(), (map, pair) -> map.put(pair.a(), pair.b()), Multimap::putAll); String commands = abilitiesPerPrivacy.asMap().entrySet().stream() .filter(entry -> privacy.compareTo(entry.getKey()) >= 0) .sorted(comparing(Map.Entry::getKey)) .map(entry -> entry.getValue().stream() .reduce(entry.getKey().toString(), (a, b) -> format("%s\n%s", a, b)) ) .collect(joining("\n")); if (commands.isEmpty()) commands = getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode()); bot.silent.send(commands, ctx.chatId()); }) .build(); } /** * @return the ability to report statistics for abilities and replies. */ public Ability reportStats() { return builder() .name(STATS) .locality(Locality.ALL) .privacy(Privacy.ADMIN) .input(0) .action(ctx -> { String stats = bot.getStats().entrySet().stream() .map(entry -> format("%s: %d", entry.getKey(), entry.getValue().hits())) .reduce(new StringJoiner("\n"), StringJoiner::add, StringJoiner::merge) .toString(); bot.silent.send(stats, ctx.chatId()); }) .build(); } /** * This backup ability returns the object defined by {@link DBContext#backup()} as a message document. *

* This is a high-profile ability and is restricted to the CREATOR only. *

* Usage: /backup * * @return the ability to back-up the database of the bot */ public Ability backupDB() { return builder() .name(BACKUP) .locality(Locality.USER) .privacy(Privacy.CREATOR) .input(0) .action(ctx -> { File backup = new File("backup.json"); try (PrintStream printStream = new PrintStream(backup)) { printStream.print(bot.db.backup()); bot.telegramClient.execute(SendDocument.builder() .document(new InputFile(backup)) .chatId(ctx.chatId()) .build() ); } catch (FileNotFoundException e) { log.error("Error while fetching backup", e); } catch (TelegramApiException e) { log.error("Error while sending document/backup file", e); } }) .build(); } /** * Recovers the bot database using {@link DBContext#recover(Object)}. *

* The bot recovery process hugely depends on the implementation of the recovery method of {@link DBContext}. *

* Usage: /recover * * @return the ability to recover the database of the bot */ public Ability recoverDB() { return builder() .name(RECOVER) .locality(Locality.USER) .privacy(Privacy.CREATOR) .input(0) .action(ctx -> bot.silent.forceReply( getLocalizedMessage(ABILITY_RECOVER_MESSAGE, ctx.user().getLanguageCode()), ctx.chatId())) .reply((bot, update) -> { String replyToMsg = update.getMessage().getReplyToMessage().getText(); String recoverMessage = getLocalizedMessage(ABILITY_RECOVER_MESSAGE, AbilityUtils.getUser(update).getLanguageCode()); if (!replyToMsg.equals(recoverMessage)) return; String fileId = update.getMessage().getDocument().getFileId(); try (FileReader reader = new FileReader(downloadFileWithId(fileId))) { String backupData = IOUtils.toString(reader); if (bot.db.recover(backupData)) { send(ABILITY_RECOVER_SUCCESS, update); } else { send(ABILITY_RECOVER_FAIL, update); } } catch (Exception e) { log.error("Could not recover DB from backup", e); send(ABILITY_RECOVER_ERROR, update); } }, MESSAGE, DOCUMENT, REPLY) .build(); } /** * Banned users are accumulated in the blacklist. Use {@link DBContext#getSet(String)} with name specified by {@link BaseAbilityBot#BLACKLIST}. *

* Usage: /ban @username *

* Note that admins who try to ban the creator, get banned. * * @return the ability to ban the user from any kind of bot interaction */ public Ability banUser() { return builder() .name(BAN) .locality(Locality.ALL) .privacy(Privacy.ADMIN) .input(1) .action(ctx -> { String username = stripTag(ctx.firstArg()); long userId = getUserIdSendError(username, ctx); String bannedUser; // Protection from abuse if (userId == bot.creatorId()) { userId = ctx.user().getId(); bannedUser = isNullOrEmpty(ctx.user().getUserName()) ? addTag(ctx.user().getUserName()) : shortName(ctx.user()); } else { bannedUser = addTag(username); } Set blacklist = bot.blacklist(); if (blacklist.contains(userId)) sendMd(ABILITY_BAN_FAIL, ctx, escape(bannedUser)); else { blacklist.add(userId); sendMd(ABILITY_BAN_SUCCESS, ctx, escape(bannedUser)); } }) .build(); } /** * Usage: /unban @username * * @return the ability to unban a user */ public Ability unbanUser() { return builder() .name(UNBAN) .locality(Locality.ALL) .privacy(Privacy.ADMIN) .input(1) .action(ctx -> { String username = stripTag(ctx.firstArg()); Long userId = getUserIdSendError(username, ctx); Set blacklist = bot.blacklist(); if (!blacklist.remove(userId)) bot.silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_FAIL, ctx.user().getLanguageCode(), escape(username)), ctx.chatId()); else { bot.silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_SUCCESS, ctx.user().getLanguageCode(), escape(username)), ctx.chatId()); } }) .build(); } /** * @return the ability to promote a user to a bot admin */ public Ability promoteAdmin() { return builder() .name(PROMOTE) .locality(Locality.ALL) .privacy(Privacy.ADMIN) .input(1) .action(ctx -> { String username = stripTag(ctx.firstArg()); Long userId = getUserIdSendError(username, ctx); Set admins = bot.admins(); if (admins.contains(userId)) sendMd(ABILITY_PROMOTE_FAIL, ctx, escape(username)); else { admins.add(userId); sendMd(ABILITY_PROMOTE_SUCCESS, ctx, escape(username)); } }) .build(); } /** * @return the ability to demote an admin to a user */ public Ability demoteAdmin() { return builder() .name(DEMOTE) .locality(Locality.ALL) .privacy(Privacy.ADMIN) .input(1) .action(ctx -> { String username = stripTag(ctx.firstArg()); Long userId = getUserIdSendError(username, ctx); Set admins = bot.admins(); if (admins.remove(userId)) { sendMd(ABILITY_DEMOTE_SUCCESS, ctx, escape(username)); } else { sendMd(ABILITY_DEMOTE_FAIL, ctx, escape(username)); } }) .build(); } /** * Regular users and admins who try to claim the bot will get banned. * * @return the ability to claim yourself as the master and creator of the bot */ public Ability claimCreator() { return builder() .name(CLAIM) .locality(Locality.ALL) .privacy(Privacy.CREATOR) .input(0) .action(ctx -> { Set admins = bot.admins(); long id = bot.creatorId(); if (admins.contains(id)) send(ABILITY_CLAIM_FAIL, ctx); else { admins.add(id); send(ABILITY_CLAIM_SUCCESS, ctx); } }) .build(); } /** * Gets the user with the specified username. * * @param username the username of the required user * @return the user */ private User getUser(String username) { Long id = bot.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 */ private User getUser(long id) { User user = bot.users().get(id); if (user == null) { throw new IllegalStateException(format("Could not find user corresponding to id [%d]", id)); } return user; } /** * Gets the user with the specified username. If user was not found, the bot will send a message on Telegram. * * @param username the username of the required user * @param ctx the message context with the originating user * @return the id of the user */ private long getUserIdSendError(String username, MessageContext ctx) { try { return getUser(username).getId(); } catch (IllegalStateException ex) { bot.silent.send(getLocalizedMessage(USER_NOT_FOUND, ctx.user().getLanguageCode(), username), ctx.chatId()); throw ex; } } private Optional send(String message, MessageContext ctx, String... args) { return bot.silent.send(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId()); } private Optional sendMd(String message, MessageContext ctx, String... args) { return bot.silent.sendMd(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId()); } private Optional send(String message, Update upd) { Long chatId = upd.getMessage().getChatId(); return bot.silent.send(getLocalizedMessage(message, AbilityUtils.getUser(upd).getLanguageCode()), chatId); } protected File downloadFileWithId(String fileId) throws TelegramApiException { return bot.telegramClient.downloadFile(bot.telegramClient.execute(GetFile.builder().fileId(fileId).build())); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy