diff --git a/pom.xml b/pom.xml index 7c2819b..2477f60 100644 --- a/pom.xml +++ b/pom.xml @@ -59,8 +59,29 @@ - ${project.artifactId}-${project.version} + ${project.artifactId} + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + initialize + + set-system-properties + + + + + mercatino.version + ${project.version} + + + + + + maven-compiler-plugin 3.8.0 @@ -137,6 +158,7 @@ maven-assembly-plugin + generic package single @@ -169,7 +191,7 @@ com.xspacesoft.kowalski7cc BotRevolution - 5.0b-Snapshot + 5.0c-Snapshot junit diff --git a/readme.md b/readme.md index 5ad7bcd..a4593b4 100644 --- a/readme.md +++ b/readme.md @@ -1,13 +1,84 @@ # Mercatino unixMiB -## Che cosa è il mercatino? +## Che cosa è il mercatino Il mercatino dell'usato di unixMiB ti permette di creare inserzioni per vendere quello che non usi più. -## Cosa ho bisogno per accedere al mercatino? +## Cosa ho bisogno per accedere al mercatino Per utilizzare il mercatino è sufficiente un account [Telegram](https://telegram.org/) per iniziare subito a chattare. -## Come accedo al mercatino? +## Come accedo al mercatino -Per avviare una chat con il bot recati alla pagina [https://t.me/unixmib_mercatino_bot](https://t.me/unixmib_mercatino_bot) o cerca [@unixmib_mercatino_bot](https://t.me/unixmib_mercatino_bot) in Telegram +Per iniziare una chat con il bot recati alla pagina [https://t.me/unixmib_mercatino_bot](https://t.me/unixmib_mercatino_bot) o cerca [@unixmib_mercatino_bot](https://t.me/unixmib_mercatino_bot) in Telegram + +## Configurazione del bot + +- Bot token di Telegram da [@botfather](https://t.me/botfather) +- ID del canale da usare come bacheca del mercatino +- ID degli amministratori iniziali (opzionale) +- ID del gruppo di amministrazione (opzionale) + + +### Parametri mediante file JSON + +`config.json` + +```json +{ + "token": "Token del bot", + "board": "ID del canale usato come bacheca", + "admins": [123456, 123456, 123456], + "group": "ID del gruppo di amministratori" +} +``` + +### Parametri mediante variabili d'ambiente + +Configurazione consigliata con un container Docker + +- `MERCATUNO_TOKEN` = "Token del bot" +- `MERCATINO_BOARD` = "ID del canale usato come bacheca" +- `MERCATINO_ADMINS` = 123456:123456:123456 +- `MERCATINO_GROUP` = "ID del gruppo di amministratori" + +Un esempio nella riga di comando sarà: + +```bash +java -DMERCATUNO_TOKEN=token -DMERCATINO_BOARD=id -DMERCATINO_ADMINS=id:id:id -DMERCATINO_GROUP=id -jar mercatino.jar +``` + +### Parametri mediante argomento + +- `-t`: Token del bot +- `-b`: ID del canale usato come bacheca +- `-a`: Stringa di amministratori separata da ':' +- `-g`: ID del gruppo di amministratori + +Un esempio nella riga di comando sarà: + +```bash +java -jar mercatino.jar -t token -b id -a 123456:123456:123456 -g id +``` + +## Docker container tags + +- `190304`, `190304-alpine`, `alpine`, `latest` (190304/alpine) + +- `190304-windowsservercore`, `190304-windowsservercore-ltsc2019`, `windowsservercore`, `windowsservercore-ltsc2019` (190304/windows/servercore/ltsc2019) + +- `190303`, `190303-alpine` (190303/alpine) + +- `190303-windowsservercore`, `190303-windowsservercore-ltsc2019` (190303/windows/servercore/ltsc2019) + +- `190228`, `190228-alpine` (190228/alpine) + +- `190228-windowsservercore`, `190228-windowsservercore-ltsc2019` (199228/windows/servercore/ltsc2019) + +- `190223`, `190223-stretch`, `stretch` (190223/stretch) + +- `190223-windowsservercore`, `190223-windowsservercore-ltsc2019` (190223/windows/servercore/ltsc2019) + +### Shared tags + +- `latest` \ No newline at end of file diff --git a/src/main/java/io/github/unixmib/mercatino/Advertisement.java b/src/main/java/io/github/unixmib/mercatino/Advertisement.java index e59d09a..b19edf1 100644 --- a/src/main/java/io/github/unixmib/mercatino/Advertisement.java +++ b/src/main/java/io/github/unixmib/mercatino/Advertisement.java @@ -21,10 +21,12 @@ import com.kowalski7cc.botrevolution.types.media.PhotoSize; import java.util.List; +import java.util.Optional; public class Advertisement { private User owner; + private Optional publisherOverride; private List photoSizes; private String title; private String description; @@ -49,6 +51,13 @@ public Advertisement setOwner(User owner) { return this; } + public Optional getPublisherOverride() { return publisherOverride;} + + public Advertisement setPublisherOverride(Optional publisherOverride) { + this.publisherOverride = publisherOverride; + return this; + } + public List getPhotoSizes() { return photoSizes; } diff --git a/src/main/java/io/github/unixmib/mercatino/BotCommand.java b/src/main/java/io/github/unixmib/mercatino/BotCommand.java new file mode 100644 index 0000000..e847f19 --- /dev/null +++ b/src/main/java/io/github/unixmib/mercatino/BotCommand.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2019 Kowalski7cc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package io.github.unixmib.mercatino; + +public interface BotCommand { + + String getCommand(); + + String getState(); +} diff --git a/src/main/java/io/github/unixmib/mercatino/BotLogic.java b/src/main/java/io/github/unixmib/mercatino/BotLogic.java index 731689e..f8ebd9e 100644 --- a/src/main/java/io/github/unixmib/mercatino/BotLogic.java +++ b/src/main/java/io/github/unixmib/mercatino/BotLogic.java @@ -1,16 +1,16 @@ /** * Copyright (C) 2019 Kowalski7cc - * + *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + *

* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. - * + *

* You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ @@ -23,12 +23,14 @@ import com.kowalski7cc.botrevolution.types.repymarkups.replykeyboards.ReplyKeyboardBuilder; import com.kowalski7cc.botrevolution.types.repymarkups.replykeyboards.ReplyKeyboardRemove; -import java.util.Optional; import java.util.UUID; +import java.util.function.Consumer; + +import static java.util.Optional.*; public class BotLogic { - public static StatesManager load(StatesManager statesManager, TelegramBot telegramBot) { + public static StatesManager loadFSM(StatesManager statesManager, TelegramBot telegramBot) { statesManager.newState("start", o -> { telegramBot.sendMessage().setChatID(o.getChat()).setText("Ciao" + o.getFrom() @@ -67,6 +69,18 @@ public static StatesManager load(StatesManager statesManager, message.getChat().getPrivateChat() .ifPresent(privateChat -> statesManager.store .put("advertisement", new Advertisement(privateChat.toUser()))); + + // Check if message contains publish id override command and user is admin, then set it if true + message.getFrom().ifPresent(user -> DataStore.getAdmins() + .stream() + .filter(user.getId()::equals) + .findFirst() + .ifPresentOrElse(integer -> new CommandParser(message).getParameters().ifPresent(s -> + { + getAdvertisementData(statesManager).setPublisherOverride(of(Integer.parseInt(s))); + System.out.println("OVERRIDE MODE: " + user.getId() + " -> " + s); + }), () -> getAdvertisementData(statesManager).setPublisherOverride(empty()))); + telegramBot.sendMessage() .setChatID(message.getChat()) .setText("Inviami il titolo della tua inserzione") @@ -144,16 +158,16 @@ public static StatesManager load(StatesManager statesManager, statesManager.newState("read_publish", message -> message.getText() - .map(s -> Optional.of(s) + .map(s -> of(s) .filter(s1 -> s1.equals("Pubblica")) .map(s1 -> { var adv = getAdvertisementData(statesManager); String uuid = UUID.randomUUID().toString(); - DataStore.getAdvertisementMap().put(uuid, adv); - DataStore.getAdmins().forEach(aLong -> { + DataStore.getAdvertisements().put(uuid, adv); + DataStore.getAdmins().forEach(id -> { try { telegramBot.sendPhoto() - .setChatID(aLong) + .setChatID(id.longValue()) .setCaption(adv.getTitle() + "\n" + adv.getDescription()) .setPhoto(adv.getPhotoSizes().get(0).getFileID()) .setReplyMarkup(new InlineKeyboardBuilder().addRow() @@ -161,12 +175,12 @@ public static StatesManager load(StatesManager statesManager, .setCallbackData("publish:" + uuid) .build() .buildButton("Cancella") - .setCallbackData("delete:"+uuid) + .setCallbackData("delete:" + uuid) .build() .build().build()) .send(); } catch (Exception e) { - System.out.println("Inpossibile contattare l'admin " + aLong); + System.out.println("MODERATION ERROR: Impossibile contattare l'admin " + id); } }); @@ -174,10 +188,10 @@ public static StatesManager load(StatesManager statesManager, .setChatID(message.getChat()) .setText("Ottimo. Il tuo annuncio sarà presto pubblicato") .setReplyMarkup(new ReplyKeyboardRemove()) - .send(); + .send(); return "show_hint"; }) - .or(() -> Optional.of(s).filter(s1 -> s1.equals("Annulla")).map(s1 -> { + .or(() -> of(s).filter(s1 -> s1.equals("Annulla")).map(s1 -> { telegramBot.sendMessage() .setChatID(message.getChat()) .setText("Pubblicazione annullata") @@ -213,6 +227,39 @@ public static StatesManager load(StatesManager statesManager, return "show_hint"; }); + statesManager.newState("selfpromote", message -> { + Consumer msg = text -> telegramBot.sendMessage() + .setChatID(message.getChat()) + .setText(text) + .send(); + Runnable error = () -> msg.accept("Errore durante l'elaborazione"); + + DataStore.getAdminsGroup().ifPresentOrElse(s -> message.getFrom() + .ifPresentOrElse(user -> telegramBot.getChatMember() + .setChatID(s) + .setUserID(user) + .send() + .ifPresentOrElse(chatMember -> { + if (chatMember.getStatus().equals("left")) { + msg.accept("Non hai i privilegi per eseguire questa operazione. Questo incidente verrà segnalato."); + System.out.println("SELFPROMOTE FAILED: " + message.getFrom()); + } else { + DataStore.addAdmin(user.getId()); + msg.accept("Privilegi elevati con successo"); + System.out.println("SELFPROMOTE SUCCEDED: " + message.getFrom()); + } + }, () -> error.run()), () -> error.run()), () -> error.run()); + return "show_hint"; + }); + + statesManager.newState("unknownCommand", message -> { + telegramBot.sendMessage() + .setChatID(message.getChat()) + .setText("Comando non valido") + .send(); + return "show_hint"; + }); + statesManager.setInitialState("start"); statesManager.reset(); diff --git a/src/main/java/io/github/unixmib/mercatino/Command.java b/src/main/java/io/github/unixmib/mercatino/Command.java new file mode 100644 index 0000000..5d715c3 --- /dev/null +++ b/src/main/java/io/github/unixmib/mercatino/Command.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2019 Kowalski7cc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package io.github.unixmib.mercatino; + +public enum Command implements BotCommand { + + START("start", "start"), + VENDI("vendi", "new_advertisement"), + HELP("help", "show_hint"), + BACHECA("bacheca", "bacheca"), + ANNULLA("annulla", "abort"), + SELFPROMOTE("selfpromote", "selfpromote"); + + private String command; + private String state; + + Command(String command, String state) { + this.command = command; + this.state = state; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public String getState() { + return state; + } +} diff --git a/src/main/java/io/github/unixmib/mercatino/CommandList.java b/src/main/java/io/github/unixmib/mercatino/CommandList.java new file mode 100644 index 0000000..2f64c43 --- /dev/null +++ b/src/main/java/io/github/unixmib/mercatino/CommandList.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2019 Kowalski7cc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package io.github.unixmib.mercatino; + +// TODO This class should probably moved to BotRevolution Library +public interface CommandList { + + String getCommand(); + + String getState(); +} diff --git a/src/main/java/io/github/unixmib/mercatino/CommandManager.java b/src/main/java/io/github/unixmib/mercatino/CommandManager.java new file mode 100644 index 0000000..5ebde5b --- /dev/null +++ b/src/main/java/io/github/unixmib/mercatino/CommandManager.java @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2019 Kowalski7cc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package io.github.unixmib.mercatino; + +import com.kowalski7cc.botrevolution.types.Message; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +// TODO This class should probably moved to BotRevolution Library +public class CommandManager { + + private Map commands; + private String errorState; + + public CommandManager() { + commands = new HashMap<>(); + } + + public CommandManager loadCommandsFromKeys(T[] values) { + List.of(values).forEach(t -> commands.put(t.getCommand(), t.getState())); + return this; + } + + public CommandManager newCommand(String command, String state) { + commands.put(command.startsWith("/")?command.substring(1):command, state); + return this; + } + + public CommandManager setErrorState(String errorState) { + this.errorState = errorState; + return this; + } + + public void runCommand(StatesManager statesManager, Message message) { + new CommandParser(message).ifPresent((command, parameters) -> statesManager.reset() + .jumpToState(commands.getOrDefault(command, errorState)) + .apply(message)); + } + + public void runCommandOrElse(StatesManager statesManager, Message message, Runnable runnable) { + new CommandParser(message).ifPresentOrElse((command, parameters) -> statesManager.reset() + .jumpToState(commands.getOrDefault(command, errorState)) + .apply(message), runnable); + } + + +} diff --git a/src/main/java/io/github/unixmib/mercatino/CommandParser.java b/src/main/java/io/github/unixmib/mercatino/CommandParser.java index 44d573e..e74fb07 100644 --- a/src/main/java/io/github/unixmib/mercatino/CommandParser.java +++ b/src/main/java/io/github/unixmib/mercatino/CommandParser.java @@ -26,6 +26,7 @@ import java.util.Optional; import java.util.function.BiConsumer; +// TODO This class should probably moved to BotRevolution Library public class CommandParser { private Message message; diff --git a/src/main/java/io/github/unixmib/mercatino/DataStore.java b/src/main/java/io/github/unixmib/mercatino/DataStore.java index 28f157e..5471d51 100644 --- a/src/main/java/io/github/unixmib/mercatino/DataStore.java +++ b/src/main/java/io/github/unixmib/mercatino/DataStore.java @@ -20,75 +20,116 @@ import com.kowalski7cc.botrevolution.TelegramBot; import com.kowalski7cc.botrevolution.types.Message; import com.kowalski7cc.botrevolution.types.chat.Chat; +import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; +import java.io.File; +import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; public class DataStore { private static Map> chats; - private static List admins; + private static List admins; private static Map advertisementMap; + private static Optional adminsGroup; private static String board; public static TelegramBot telegramBot; + public final static String CONFIG_FILE_NAME = "config.json"; + public static CommandManager commandManager; - public static void loadDataStore() { + + /** + * Main method called fot initializing data store + */ + public static void initialize() { try { - var options = new JSONObject(Files.readAllLines(Paths.get("config.json")).stream().collect(Collectors.joining())); - telegramBot = new TelegramBot(options.getString("token")); - setBoard(options.getString("board")); - setAdmins(options.getJSONArray("admins") - .toList() - .parallelStream() - .map(o -> Long.parseLong(o.toString())) - .collect(Collectors.toList())); - } catch (Exception e) { - telegramBot = new TelegramBot(System.getenv("MERCATINO_TELEGRAM_TOKEN")); - setBoard(System.getenv("MERCATINO_BOARD_ID")); - setAdmins(Optional.ofNullable(System.getenv("MERCATINO_ADMINS_IDS")) - .map(s -> List.of(s.split(":")).parallelStream().map(Long::parseLong).collect(Collectors.toList())) - .orElse(List.of())); + loadConfigFromFile(new File(CONFIG_FILE_NAME)); + } catch (IOException e) { + loadConfigFromEnvironment(); } - advertisementMap = new HashMap<>(); chats = new HashMap<>(); + commandManager = new CommandManager().loadCommandsFromKeys(Command.values()); + } + + /** + * Method used to loadFSM bot configuration from provided JSON file + * @param configFile File as configuration input + * @throws IOException when configFile is not found or unable to read + * @throws JSONException when configFile is malformed + */ + private static void loadConfigFromFile(File configFile) throws IOException, JSONException { + var data = new JSONObject(Files.readAllLines(configFile.toPath()).stream() + .collect(Collectors.joining())); + telegramBot = new TelegramBot(data.getString(ParameterKey.TOKEN.getJson())); + board = data.getString(ParameterKey.BOARD.getJson()); + adminsGroup = Optional.ofNullable(data.optString(ParameterKey.ADMINS_GROUP.getJson())); + // Admins can be not provided, they can self promote if they belong to admin group + admins = Optional.ofNullable(data.optJSONArray(ParameterKey.ADMINS.getJson())) + .stream() + .map(JSONArray::toList) + .map(o -> Integer.parseInt(o.toString())) + .collect(Collectors.toList()); + } + + /** + * + * @throws NullPointerException when a required env variable is not found + */ + private static void loadConfigFromEnvironment() throws NullPointerException { + telegramBot = new TelegramBot(Objects.requireNonNull(System.getenv(ParameterKey.TOKEN.getEnv()), + "Token not provided")); + setBoardID(Objects.requireNonNull(System.getenv(ParameterKey.BOARD.getEnv()), + "Board ID not provided")); + // Admins can be not provided, they can self promote if they belong to admin group + admins = Optional.ofNullable(System.getenv(ParameterKey.ADMINS.getEnv())) + .map(s -> List.of(s.split(":")) + .stream() + .map(Integer::parseInt) + .collect(Collectors.toList())) + .orElse(List.of()); + adminsGroup = Optional.ofNullable(System.getenv(ParameterKey.ADMINS_GROUP.getEnv())); } + /* + private static void loadConfigFromArument(Optional argument) throws NullPointerException { + argument.map(s -> s.split(" ")).orElseThrow(() -> new NullPointerException()); + } + */ + public static Map> getChats() { return chats; } - public static void setChats(Map> chats) { - DataStore.chats = chats; + public static List getAdmins() { + return admins; } - public static List getAdmins() { - return admins; + public static void addAdmin(Integer id) { + admins.add(id); } - public static void setAdmins(List admins) { - DataStore.admins = admins; + public static void removeAdmin(Integer id) { + admins.remove(id); } - public static Map getAdvertisementMap() { - return advertisementMap; + public static Optional getAdminsGroup() { + return adminsGroup; } - public static void setAdvertisementMap(Map advertisementMap) { - DataStore.advertisementMap = advertisementMap; + public static Map getAdvertisements() { + return advertisementMap; } - public static String getBoard() { + public static String getBoardID() { return board; } - public static void setBoard(String board) { + public static void setBoardID(String board) { DataStore.board = board; } } diff --git a/src/main/java/io/github/unixmib/mercatino/Main.java b/src/main/java/io/github/unixmib/mercatino/Main.java index 4a97ae6..72a35cd 100644 --- a/src/main/java/io/github/unixmib/mercatino/Main.java +++ b/src/main/java/io/github/unixmib/mercatino/Main.java @@ -31,9 +31,10 @@ public class Main { public static void main(String[] args) throws InterruptedException { printLicense(); - DataStore.loadDataStore(); + DataStore.initialize(); DataStore.telegramBot.getReceiver().startPolling(); - DataStore.telegramBot.getMe().ifPresentOrElse(user -> System.out.println("Connected as " + user.getFirstName()), + DataStore.telegramBot.getMe() + .ifPresentOrElse(user -> System.out.println("Connected as " + user.getFirstName() + " https://t.me/" + user.getUsername().orElse("")), () -> System.exit(1)); while (DataStore.telegramBot.getReceiver().isPolling()) { synchronized (DataStore.telegramBot.getReceiver().getUpdates()) { @@ -41,12 +42,16 @@ public static void main(String[] args) throws InterruptedException { } while (!DataStore.telegramBot.getUpdates().isEmpty()) - CompletableFuture.runAsync(() -> apply(DataStore.telegramBot.getUpdates().poll(), DataStore.telegramBot)).join(); + CompletableFuture.runAsync(() -> apply(DataStore.telegramBot.getUpdates() + .poll(), DataStore.telegramBot)) + .join(); } } private static void printLicense() { + var version = System.getProperties().getProperty("mercatino.version"); System.out.println("MercatinoBot - unixMiB https://unixmib.github.io/\n" + + "Build " + (version==null?"developement":version) + "\n" + "Copyright (C) 2019 Kowalski7cc\n" + "\n" + "This program is free software: you can redistribute it and/or modify\n" + @@ -70,38 +75,23 @@ private static void apply(Update update, TelegramBot tg) { // handle incoming messages update.getMessage().ifPresent(message -> message.getChat() .getPrivateChat() - .ifPresent(privateChat -> new CommandParser(message).ifPresentOrElse((command, parameters) -> { - switch (command) { - case "start": - getState(privateChat, tg).reset().apply(message); - break; - case "vendi": - getState(privateChat, tg).reset().jumpToState("new_advertisement").apply(message); - break; - case "help": - getState(privateChat, tg).reset().jumpToState("show_hint").apply(message); - break; - case "bacheca": - getState(privateChat, tg).reset().jumpToState("bacheca").apply(message); - break; - case "annulla": - getState(privateChat, tg).reset().jumpToState("abort").apply(message); - break; - default: - tg.sendMessage().setChatID(message.getChat()).setText("Comando non valido").send(); - break; - } - }, () -> getState(privateChat, tg).apply(message)))); + .ifPresent(privateChat -> DataStore.commandManager + // TODO Make CommandManager part of BotRevolution Library + .runCommandOrElse(getState(privateChat, tg), message, + () -> getState(privateChat, tg).apply(message)))); + + // Clean up this garbage update.getCallbackQuery().ifPresent(callbackQuery -> callbackQuery.getData() .ifPresent(s -> { var request = s.split(":"); switch (request[0]) { case "delmsg": { + // Allow users to delete published post try { tg.deleteMessage() .setMessageID(Integer.valueOf(request[1])) - .setChatID(DataStore.getBoard()) + .setChatID(DataStore.getBoardID()) .send().get(); } catch (TelegramException e) { callbackQuery.getMessage().ifPresent(message -> { @@ -122,6 +112,8 @@ private static void apply(Update update, TelegramBot tg) { } } break; + + case "new_advertisement": tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) @@ -131,8 +123,11 @@ private static void apply(Update update, TelegramBot tg) { jumpToState("new_advertisement") .apply(message)); break; + + case "publish": - DataStore.getAdvertisementMap().computeIfAbsent(request[1], s1 -> { + // Send notification to moderator if post not found + DataStore.getAdvertisements().computeIfAbsent(request[1], s1 -> { tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) .setText("Annuncio non trovato, probabilmente è già stata effettuata la pubblicazione") @@ -145,30 +140,57 @@ private static void apply(Update update, TelegramBot tg) { .send()); return null; }); - DataStore.getAdvertisementMap().computeIfPresent(request[1], (s1, advertisement) -> { + + // Publish post on channel, get message id, send confirmation with callback id + DataStore.getAdvertisements().computeIfPresent(request[1], (s1, advertisement) -> { var msg = tg.sendPhoto() - .setChatID(DataStore.getBoard()) + .setChatID(DataStore.getBoardID()) .setPhoto(advertisement.getPhotoSizes().get(0).getFileID()) .setCaption(advertisement.getTitle() + "\n" + advertisement.getDescription()) .setReplyMarkup(new InlineKeyboardBuilder().addRow() .buildButton("Invia richiesta") - .setCallbackData("contact:" + advertisement.getOwner().getId()) + // TEST: does int client work as id? + .setCallbackData("contact:" + advertisement.getPublisherOverride() + .orElse(advertisement.getOwner().getId())) .build() .build().addRow().buildButton("Gruppo unixMiB") .setUrl("https://t.me/unixmib").build().build().build()) .send(); - msg.ifPresent(message -> tg.sendMessage().setText("Il tuo annuncio \"" + advertisement.getTitle() - + "\" è stato pubblicato") - .setChatID(Long.valueOf(advertisement.getOwner().getId())) - .setReplyMarkup(new InlineKeyboardBuilder().addRow() - .buildButton("Cancella annuncio") - .setCallbackData("delmsg:" + message.getMessageID()) - .build() - .build().build()).send()); + + // Send confirmation to user + try { + msg.ifPresent(message -> { + tg.sendMessage() + .setText("Il tuo annuncio \"" + advertisement.getTitle() + + "\" è stato pubblicato") + .setChatID(advertisement.getPublisherOverride() + .orElse(advertisement.getOwner().getId()).longValue()) + .setReplyMarkup(new InlineKeyboardBuilder().addRow() + .buildButton("Cancella annuncio") + .setCallbackData("delmsg:" + message.getMessageID()) + .build() + .build().build()).send(); + + advertisement.getPublisherOverride().ifPresent(integer -> callbackQuery.getMessage() + .ifPresent(modMessage -> tg.sendMessage() + .setText("Messaggio di conferma inviato in privato all'utente in override") + .setChatID(modMessage.getChat()) + .send())); + }); + } catch (TelegramException e) { + callbackQuery.getMessage().ifPresent(message -> tg.sendMessage() + .setText("Errore nell'invio del messaggio di conferma all'utente in override") + .setChatID(message.getChat()) + .send()); + } + + // Delete moderation message callbackQuery.getMessage().ifPresent(message -> tg.deleteMessage() .setChatID(message.getChat()) .setMessageID(message) .send()); + + // Answer to callback try { tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) @@ -177,14 +199,16 @@ private static void apply(Update update, TelegramBot tg) { .send(); } catch (TelegramException e) { // Query timed out - System.out.println("QueryManager: " + e.toString() + ", Query ID: " + + System.out.println("TIMEOUT QueryManager: " + e.toString() + ", Query ID: " + callbackQuery.getId()); } return null; }); break; + + case "delete": - DataStore.getAdvertisementMap().computeIfAbsent(request[1], s1 -> { + DataStore.getAdvertisements().computeIfAbsent(request[1], s1 -> { try { tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) @@ -200,7 +224,7 @@ private static void apply(Update update, TelegramBot tg) { return null; } }); - DataStore.getAdvertisementMap().computeIfPresent(request[1], (s1, advertisement) -> { + DataStore.getAdvertisements().computeIfPresent(request[1], (s1, advertisement) -> { tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) .setText("Annuncio cancellato") @@ -213,9 +237,13 @@ private static void apply(Update update, TelegramBot tg) { return null; }); break; + + case "contact": + // Send a message to post publisher, if user doesn't have an username, fail with notification. callbackQuery.getFrom().getUsername().ifPresentOrElse(s1 -> { try { + // Send a message to publisher with user ID in the button tg.sendMessage() .setChatID(request[1]) .setText("Ciao, hai avuto una richiesta da " + callbackQuery.getFrom() @@ -226,6 +254,8 @@ private static void apply(Update update, TelegramBot tg) { .build() .build().build()) .send(); + + // Notify user on successful poke tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) .setText("Richiesta inviata!") @@ -233,6 +263,7 @@ private static void apply(Update update, TelegramBot tg) { .setCacheTime(10) .send(); } catch (Exception e) { + // If user blocked the bot, notify user try { tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) @@ -253,6 +284,8 @@ private static void apply(Update update, TelegramBot tg) { .send()); break; + + default: tg.answerCallbackQuery() .setCallbackQueryID(callbackQuery) @@ -267,7 +300,7 @@ private static void apply(Update update, TelegramBot tg) { private static StatesManager getState(Chat chat, TelegramBot telegramBot) { return DataStore.getChats().compute(chat, (chat1, statesManager) -> Optional.ofNullable(statesManager) - .orElse(BotLogic.load(new StatesManager<>(), telegramBot))); + .orElse(BotLogic.loadFSM(new StatesManager<>(), telegramBot))); } } \ No newline at end of file diff --git a/src/main/java/io/github/unixmib/mercatino/ParameterKey.java b/src/main/java/io/github/unixmib/mercatino/ParameterKey.java new file mode 100644 index 0000000..2cba53b --- /dev/null +++ b/src/main/java/io/github/unixmib/mercatino/ParameterKey.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2019 Kowalski7cc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package io.github.unixmib.mercatino; + +/** + * This class is used from DataStore class + */ +public enum ParameterKey { + + TOKEN("token", "MERCATINO_TOKEN", "t"), + BOARD("board", "MERCATINO_BOARD", "b"), + ADMINS("admins", "MERCATINO_ADMINS", "a"), + ADMINS_GROUP("group", "MERCATINO_GROUP", "g"); + + private String json; + private String env; + private String param; + + ParameterKey(String json, String env, String param) { + this.json = json; + this.env = env; + this.param = param; + } + + public String getJson() { + return json; + } + + public String getEnv() { + return env; + } + + public String getParam() { + return param; + } +} diff --git a/src/main/java/io/github/unixmib/mercatino/StatesManager.java b/src/main/java/io/github/unixmib/mercatino/StatesManager.java index 7b04e40..8dff4b6 100644 --- a/src/main/java/io/github/unixmib/mercatino/StatesManager.java +++ b/src/main/java/io/github/unixmib/mercatino/StatesManager.java @@ -20,6 +20,7 @@ import java.util.*; import java.util.function.Function; +// TODO This class should probably moved to BotRevolution Library public class StatesManager { private Map> states;