From bd5a14da19fb2f4279afb24bce3412f79c4423d5 Mon Sep 17 00:00:00 2001 From: M1keM1ke Date: Sun, 25 Jun 2023 18:43:38 +0300 Subject: [PATCH] Init commit --- .gitignore | 5 + Dockerfile | 11 ++ docker-compose.yml | 22 ++++ pom.xml | 65 ++++++++++ .../DownloaderBotApplication.java | 21 +++ .../mike/downloaderbot/UpdateProcessor.java | 50 ++++++++ .../mike/downloaderbot/config/BotConfig.java | 14 ++ .../ru/mike/downloaderbot/domain/Channel.java | 42 ++++++ .../mike/downloaderbot/domain/LinkData.java | 35 +++++ .../ru/mike/downloaderbot/domain/Rule.java | 39 ++++++ .../processor/command/CommandProcessor.java | 10 ++ .../command/LinkCommandProcessor.java | 60 +++++++++ .../command/RuleCommandProcessor.java | 104 +++++++++++++++ .../command/StartCommandProcessor.java | 120 ++++++++++++++++++ .../MessageUpdateTypeProcessor.java | 45 +++++++ .../updatetype/UpdateTypeProcessor.java | 10 ++ .../repository/ChannelRepository.java | 14 ++ .../repository/LinkDataRepository.java | 9 ++ .../repository/RuleRepository.java | 12 ++ .../downloaderbot/service/ChannelService.java | 26 ++++ .../service/LinkDataService.java | 34 +++++ .../service/RuleChannelService.java | 88 +++++++++++++ .../downloaderbot/service/RuleService.java | 23 ++++ src/main/resources/application.yml | 14 ++ .../db/migration/2023-06-24-init-schema.xml | 44 +++++++ .../migration/master/db.changelog-master.xml | 9 ++ 26 files changed, 926 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 pom.xml create mode 100644 src/main/java/ru/mike/downloaderbot/DownloaderBotApplication.java create mode 100644 src/main/java/ru/mike/downloaderbot/UpdateProcessor.java create mode 100644 src/main/java/ru/mike/downloaderbot/config/BotConfig.java create mode 100644 src/main/java/ru/mike/downloaderbot/domain/Channel.java create mode 100644 src/main/java/ru/mike/downloaderbot/domain/LinkData.java create mode 100644 src/main/java/ru/mike/downloaderbot/domain/Rule.java create mode 100644 src/main/java/ru/mike/downloaderbot/processor/command/CommandProcessor.java create mode 100644 src/main/java/ru/mike/downloaderbot/processor/command/LinkCommandProcessor.java create mode 100644 src/main/java/ru/mike/downloaderbot/processor/command/RuleCommandProcessor.java create mode 100644 src/main/java/ru/mike/downloaderbot/processor/command/StartCommandProcessor.java create mode 100644 src/main/java/ru/mike/downloaderbot/processor/updatetype/MessageUpdateTypeProcessor.java create mode 100644 src/main/java/ru/mike/downloaderbot/processor/updatetype/UpdateTypeProcessor.java create mode 100644 src/main/java/ru/mike/downloaderbot/repository/ChannelRepository.java create mode 100644 src/main/java/ru/mike/downloaderbot/repository/LinkDataRepository.java create mode 100644 src/main/java/ru/mike/downloaderbot/repository/RuleRepository.java create mode 100644 src/main/java/ru/mike/downloaderbot/service/ChannelService.java create mode 100644 src/main/java/ru/mike/downloaderbot/service/LinkDataService.java create mode 100644 src/main/java/ru/mike/downloaderbot/service/RuleChannelService.java create mode 100644 src/main/java/ru/mike/downloaderbot/service/RuleService.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/db/migration/2023-06-24-init-schema.xml create mode 100644 src/main/resources/db/migration/master/db.changelog-master.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba70b45 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +### Intellij Idea ### +.idea + +### Maven ### +target \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..db5ded1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +# Build stage +FROM maven:3.6.0-jdk-11-slim AS build +COPY src /home/app/src +COPY pom.xml /home/app +RUN mvn -f /home/app/pom.xml clean package + +# Package stage +FROM openjdk:11-jre-slim +COPY --from=build /home/app/target/downloader-bot-1.0-SNAPSHOT.jar /usr/local/lib/downloader-bot-1.0-SNAPSHOT.jar +EXPOSE 8080 +ENTRYPOINT ["java","-jar","/usr/local/lib/downloader-bot-1.0-SNAPSHOT.jar"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..204aa42 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' +services: + app: + image: 'docker-spring-boot-postgres:latest' + build: + context: . + container_name: app + depends_on: + - db + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/postgres + - SPRING_DATASOURCE_USERNAME=postgres + - SPRING_DATASOURCE_PASSWORD=zPAwQpsFZC + + db: + image: postgres:14.1-alpine + restart: always + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=zPAwQpsFZC + ports: + - '5432:5432' \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8f7cc44 --- /dev/null +++ b/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + ru.mike.downloaderbot + downloader-bot + 1.0-SNAPSHOT + + + 11 + 11 + + + + org.springframework.boot + spring-boot-starter-parent + 2.5.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + + com.github.pengrad + java-telegram-bot-api + 6.7.0 + + + org.projectlombok + lombok + 1.18.28 + provided + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.postgresql + postgresql + 42.6.0 + + + org.liquibase + liquibase-core + 4.22.0 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/src/main/java/ru/mike/downloaderbot/DownloaderBotApplication.java b/src/main/java/ru/mike/downloaderbot/DownloaderBotApplication.java new file mode 100644 index 0000000..4637b12 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/DownloaderBotApplication.java @@ -0,0 +1,21 @@ +package ru.mike.downloaderbot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DownloaderBotApplication implements CommandLineRunner { + @Autowired + private UpdateProcessor updateProcessor; + + public static void main(String[] args) { + SpringApplication.run(DownloaderBotApplication.class, args); + } + + @Override + public void run(String... args) { + updateProcessor.processUpdates(); + } +} diff --git a/src/main/java/ru/mike/downloaderbot/UpdateProcessor.java b/src/main/java/ru/mike/downloaderbot/UpdateProcessor.java new file mode 100644 index 0000000..b3eb221 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/UpdateProcessor.java @@ -0,0 +1,50 @@ +package ru.mike.downloaderbot; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.UpdatesListener; +import com.pengrad.telegrambot.model.Update; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.mike.downloaderbot.processor.updatetype.UpdateTypeProcessor; + +import java.util.List; +import java.util.Optional; + +@Slf4j +@Component +public class UpdateProcessor { + @Autowired + private TelegramBot telegramBot; + + @Autowired + private List updateTypeProcessors; + + public void processUpdates() { + telegramBot.setUpdatesListener(updates -> { + + for (Update update : updates) { + Optional updateTypeProcessorOpt = updateTypeProcessors.stream() + .filter(p -> p.isSuitableProcessor(update)) + .findFirst(); + + if (updateTypeProcessorOpt.isPresent()) { + updateTypeProcessorOpt.get().process(update); + } else { + log.warn("Unable to find suitable updateTypeProcessor. Update:{}", update); + } + } + + return UpdatesListener.CONFIRMED_UPDATES_ALL; + }, e -> { + if (e.response() != null) { + // Ошибка из Телеграма + e.response().errorCode(); + e.response().description(); + } else { + // Как видно проблема сети + e.printStackTrace(); + } + }); + } +} diff --git a/src/main/java/ru/mike/downloaderbot/config/BotConfig.java b/src/main/java/ru/mike/downloaderbot/config/BotConfig.java new file mode 100644 index 0000000..efeb68b --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/config/BotConfig.java @@ -0,0 +1,14 @@ +package ru.mike.downloaderbot.config; + +import com.pengrad.telegrambot.TelegramBot; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class BotConfig { + + @Bean + public TelegramBot telegramBot() { + return new TelegramBot("6181387751:AAFha962O3-tK31DghfEIhW_wTfSF-JJBlk"); + } +} diff --git a/src/main/java/ru/mike/downloaderbot/domain/Channel.java b/src/main/java/ru/mike/downloaderbot/domain/Channel.java new file mode 100644 index 0000000..c264502 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/domain/Channel.java @@ -0,0 +1,42 @@ +package ru.mike.downloaderbot.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import java.util.UUID; + +@Entity +@Table(name = "channel") +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +public class Channel { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID id; + + @Column(name = "name") + private String name; + + @Column(name = "link") + private String link; + + @Column(name = "telegram_channel_id") + private Long telegramChannelId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "channel_id") + private Rule rule; +} diff --git a/src/main/java/ru/mike/downloaderbot/domain/LinkData.java b/src/main/java/ru/mike/downloaderbot/domain/LinkData.java new file mode 100644 index 0000000..347b288 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/domain/LinkData.java @@ -0,0 +1,35 @@ +package ru.mike.downloaderbot.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.UUID; + +@Entity +@Table(name = "link_data") +@NoArgsConstructor +@Getter +@Builder +@AllArgsConstructor +public class LinkData { + @Id + private UUID id; + + @Column(name = "resource_link") + private String resourceLink; + + @Column(name = "bot_link") + private String botLink; + + /** + * Имя канала, с которого пришел пользователь по ссылке {@link LinkData#botLink} + */ + @Column(name = "channel_name") + private String channelName; +} diff --git a/src/main/java/ru/mike/downloaderbot/domain/Rule.java b/src/main/java/ru/mike/downloaderbot/domain/Rule.java new file mode 100644 index 0000000..8bca2f5 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/domain/Rule.java @@ -0,0 +1,39 @@ +package ru.mike.downloaderbot.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +@Entity +@Table(name = "rule") +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class Rule { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID id; + + @Column(name = "channel_name", unique = true) + private String channelName; + + @OneToMany(mappedBy = "rule", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private Set channels = new HashSet<>(); +} diff --git a/src/main/java/ru/mike/downloaderbot/processor/command/CommandProcessor.java b/src/main/java/ru/mike/downloaderbot/processor/command/CommandProcessor.java new file mode 100644 index 0000000..e3fc7eb --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/processor/command/CommandProcessor.java @@ -0,0 +1,10 @@ +package ru.mike.downloaderbot.processor.command; + +import com.pengrad.telegrambot.model.Update; + +public interface CommandProcessor { + + boolean isSuitableCommand(String command); + + void process(Update update); +} diff --git a/src/main/java/ru/mike/downloaderbot/processor/command/LinkCommandProcessor.java b/src/main/java/ru/mike/downloaderbot/processor/command/LinkCommandProcessor.java new file mode 100644 index 0000000..eac32ca --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/processor/command/LinkCommandProcessor.java @@ -0,0 +1,60 @@ +package ru.mike.downloaderbot.processor.command; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.mike.downloaderbot.domain.LinkData; +import ru.mike.downloaderbot.service.LinkDataService; + +import java.util.List; + +@Slf4j +@Component +public class LinkCommandProcessor implements CommandProcessor { + @Autowired + private TelegramBot telegramBot; + + @Autowired + private LinkDataService linkDataService; + + @Override + public boolean isSuitableCommand(String command) { + return command.startsWith("/link"); + } + + @Override + public void process(Update update) { + List adminsIds = List.of(283573433L, 421233011L); + + Long currentUserId = update.message().from().id(); + Long currentChatId = update.message().chat().id(); + String text = update.message().text(); + + if (!adminsIds.contains(currentUserId)) { + log.warn("User:'{}' trying to send link:{}, but it's not admin", currentUserId, text); + return; + } + + String[] splitText = text.split(" "); + + if (splitText.length != 3) { + SendMessage wrongFormatMessage = new SendMessage(currentChatId, "Неверный формат команды для генерации ссылки. " + + "Формат команды:/link <ссылка> <тег канала>"); + telegramBot.execute(wrongFormatMessage); + return; + } + + String linkText = splitText[1]; + String channelName = splitText[2]; + + LinkData createdLinkData = linkDataService.createLinkData(linkText, channelName); + + SendMessage generatedLinkMessage = new SendMessage(currentChatId, + "Сгенерированная ссылка:" + createdLinkData.getBotLink()); + + telegramBot.execute(generatedLinkMessage); + } +} diff --git a/src/main/java/ru/mike/downloaderbot/processor/command/RuleCommandProcessor.java b/src/main/java/ru/mike/downloaderbot/processor/command/RuleCommandProcessor.java new file mode 100644 index 0000000..25bdec7 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/processor/command/RuleCommandProcessor.java @@ -0,0 +1,104 @@ +package ru.mike.downloaderbot.processor.command; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.mike.downloaderbot.domain.Rule; +import ru.mike.downloaderbot.service.RuleChannelService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class RuleCommandProcessor implements CommandProcessor { + @Autowired + private TelegramBot telegramBot; + + @Autowired + private RuleChannelService ruleChannelService; + + @Override + public boolean isSuitableCommand(String command) { + return command.startsWith("/rule"); + } + + @Override + public void process(Update update) { + Long currentChatId = update.message().chat().id(); + String text = update.message().text(); + + String[] splitText = text.split(" "); + + String wrongRuleCommandFormatText = "Неверный формат команды для установки правила. " + + "Формат команды:/rule c1 c2 c3\n" + + "Где \n" + + "c1 - имя канала, для которого применяется правило подписки" + + "c2 - <имя канала, на который нужно подписаться>\n" + + "c3 - \n" + + "c2 каналов может быть много, например \"/rule c1 c2 c2 c2"; + + if (splitText.length <= 3) { + sendCommandParseErrorMessage(currentChatId, text, wrongRuleCommandFormatText); + return; + } + + String ruleBaseChannelName = splitText[1]; + + String[] ruleSubscribeChannelsNames = Arrays.copyOfRange(splitText, 2, splitText.length); + + if (ruleSubscribeChannelsNames.length % 2 != 0) { + sendCommandParseErrorMessage(currentChatId, text, wrongRuleCommandFormatText); + return; + } + + List channelsNames = new ArrayList<>(); + List channelsIds = new ArrayList<>(); + + for (int i = 0; i < ruleSubscribeChannelsNames.length; i++) { + String commandPart = ruleSubscribeChannelsNames[i]; + + if (i % 2 == 0) { + channelsNames.add(commandPart); + } else { + try { + channelsIds.add(Long.parseLong(commandPart)); + } catch (NumberFormatException e) { + sendCommandParseErrorMessage(currentChatId, text, wrongRuleCommandFormatText); + return; + } + } + } + + Rule createdRule = ruleChannelService.createRuleAndChannels(ruleBaseChannelName, channelsNames, channelsIds); + + SendMessage successCreateRuleMessage = new SendMessage(currentChatId, + createSuccessCreateRuleMessage(createdRule) + ); + telegramBot.execute(successCreateRuleMessage); + } + + private void sendCommandParseErrorMessage(Long currentChatId, String text, String wrongRuleCommandFormatText) { + log.warn("Unable to parse /rule command:'{}'", text); + + SendMessage wrongFormatMessage = new SendMessage(currentChatId, wrongRuleCommandFormatText); + telegramBot.execute(wrongFormatMessage); + } + + private String createSuccessCreateRuleMessage(Rule createdRule) { + String ruleChannelName = createdRule.getChannelName(); + String channelsStr = createdRule.getChannels().stream() + .map(c -> "Имя: " + c.getName() + ", ссылка: " + c.getLink()) + .collect(Collectors.joining("\n")); + + return "Правило создано успешно\n" + + "Имя канала правила:" + ruleChannelName + + "\nКаналы для подписки:\n" + + channelsStr; + } +} diff --git a/src/main/java/ru/mike/downloaderbot/processor/command/StartCommandProcessor.java b/src/main/java/ru/mike/downloaderbot/processor/command/StartCommandProcessor.java new file mode 100644 index 0000000..5feb42e --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/processor/command/StartCommandProcessor.java @@ -0,0 +1,120 @@ +package ru.mike.downloaderbot.processor.command; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.ChatMember; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.GetChatMember; +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.response.GetChatMemberResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.mike.downloaderbot.domain.Channel; +import ru.mike.downloaderbot.domain.LinkData; +import ru.mike.downloaderbot.service.LinkDataService; +import ru.mike.downloaderbot.service.RuleChannelService; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class StartCommandProcessor implements CommandProcessor { + @Autowired + private TelegramBot telegramBot; + + @Autowired + private LinkDataService linkDataService; + + @Autowired + private RuleChannelService ruleChannelService; + + @Override + public boolean isSuitableCommand(String command) { + return command.startsWith("/start "); + } + + @Override + public void process(Update update) { + Long currentChatId = update.message().chat().id(); + Long currentUserId = update.message().from().id(); + String text = update.message().text(); + + String[] splitText = text.split(" "); + + if (splitText.length != 2) { + log.warn("Unable to parse /start command:'{}'", text); + + SendMessage wrongFormatMessage = new SendMessage(currentChatId, + "Ой, что-то пошло не так! Попробуйте еще раз!"); + telegramBot.execute(wrongFormatMessage); + return; + } + + String linkIdStr = splitText[1]; + UUID linkId; + + try { + linkId = UUID.fromString(linkIdStr); + } catch (IllegalArgumentException e) { + log.warn("Unable to parse link id. Command:'{}', id='{}'", text, linkIdStr); + + SendMessage wrongFormatMessage = new SendMessage(currentChatId, + "Ой, что-то пошло не так! Попробуйте еще раз!"); + telegramBot.execute(wrongFormatMessage); + return; + } + + Optional linkDataOptional = linkDataService.findById(linkId); + + if (linkDataOptional.isEmpty()) { + SendMessage linkNotFoundMessage = new SendMessage(currentChatId, + "Не нашли ссылку на ресурс... Попробуйте другой!"); + telegramBot.execute(linkNotFoundMessage); + return; + } + + LinkData linkData = linkDataOptional.get(); + + String channelName = linkData.getChannelName(); + + List channels = ruleChannelService.findChannelsByRuleChannelName(channelName); + + List channelsTgIds = channels.stream() + .map(Channel::getTelegramChannelId) + .collect(Collectors.toList()); + + + if (isAnyChannelNotSubscribed(currentUserId, channelsTgIds)) { + String subscribeLinks = channels.stream() + .map(Channel::getLink) + .collect(Collectors.joining("\n")); + + String subscribeMessage = "Подпишись на каналы:\n" + subscribeLinks; + + telegramBot.execute(new SendMessage(currentChatId, subscribeMessage)); + return; + } + + SendMessage linkMessage = new SendMessage(currentChatId, + "Ссылка на ресурс: " + linkData.getResourceLink()); + telegramBot.execute(linkMessage); + } + + private boolean isAnyChannelNotSubscribed(Long currentUserId, List channelsTgIds) { + for (Long channelId : channelsTgIds) { + GetChatMember chatMember = new GetChatMember(channelId, currentUserId); + GetChatMemberResponse chatMemberResponse = telegramBot.execute(chatMember); + + if (Objects.isNull(chatMemberResponse) || Objects.isNull(chatMemberResponse.chatMember()) || + chatMemberResponse.chatMember().status().equals(ChatMember.Status.left)) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/ru/mike/downloaderbot/processor/updatetype/MessageUpdateTypeProcessor.java b/src/main/java/ru/mike/downloaderbot/processor/updatetype/MessageUpdateTypeProcessor.java new file mode 100644 index 0000000..91e2f8f --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/processor/updatetype/MessageUpdateTypeProcessor.java @@ -0,0 +1,45 @@ +package ru.mike.downloaderbot.processor.updatetype; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.SendMessage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.mike.downloaderbot.processor.command.CommandProcessor; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@Component +public class MessageUpdateTypeProcessor implements UpdateTypeProcessor { + @Autowired + private TelegramBot telegramBot; + + @Autowired + private List commandProcessors; + + @Override + public boolean isSuitableProcessor(Update update) { + return Objects.nonNull(update.message()); + } + + @Override + public void process(Update update) { + String text = update.message().text(); + Optional commandProcessorOptional = commandProcessors.stream() + .filter(cp -> cp.isSuitableCommand(text)) + .findFirst(); + + if (commandProcessorOptional.isPresent()) { + commandProcessorOptional.get().process(update); + } else { + SendMessage sendMessage = new SendMessage( + update.message().chat().id(), + "Я не понимаю тебя, попробуй другую команду!" + ); + + telegramBot.execute(sendMessage); + } + } +} diff --git a/src/main/java/ru/mike/downloaderbot/processor/updatetype/UpdateTypeProcessor.java b/src/main/java/ru/mike/downloaderbot/processor/updatetype/UpdateTypeProcessor.java new file mode 100644 index 0000000..326c762 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/processor/updatetype/UpdateTypeProcessor.java @@ -0,0 +1,10 @@ +package ru.mike.downloaderbot.processor.updatetype; + +import com.pengrad.telegrambot.model.Update; + +public interface UpdateTypeProcessor { + + boolean isSuitableProcessor(Update update); + + void process(Update update); +} diff --git a/src/main/java/ru/mike/downloaderbot/repository/ChannelRepository.java b/src/main/java/ru/mike/downloaderbot/repository/ChannelRepository.java new file mode 100644 index 0000000..4c1e83a --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/repository/ChannelRepository.java @@ -0,0 +1,14 @@ +package ru.mike.downloaderbot.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.mike.downloaderbot.domain.Channel; + +import java.util.List; +import java.util.UUID; + +public interface ChannelRepository extends JpaRepository { + + void deleteAllByRuleId(UUID id); + + List findAllByRuleId(UUID id); +} diff --git a/src/main/java/ru/mike/downloaderbot/repository/LinkDataRepository.java b/src/main/java/ru/mike/downloaderbot/repository/LinkDataRepository.java new file mode 100644 index 0000000..1e0cfae --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/repository/LinkDataRepository.java @@ -0,0 +1,9 @@ +package ru.mike.downloaderbot.repository; + +import org.springframework.data.repository.CrudRepository; +import ru.mike.downloaderbot.domain.LinkData; + +import java.util.UUID; + +public interface LinkDataRepository extends CrudRepository { +} diff --git a/src/main/java/ru/mike/downloaderbot/repository/RuleRepository.java b/src/main/java/ru/mike/downloaderbot/repository/RuleRepository.java new file mode 100644 index 0000000..f3b89d8 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/repository/RuleRepository.java @@ -0,0 +1,12 @@ +package ru.mike.downloaderbot.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.mike.downloaderbot.domain.Rule; + +import java.util.Optional; +import java.util.UUID; + +public interface RuleRepository extends JpaRepository { + + Optional findByChannelName(String name); +} diff --git a/src/main/java/ru/mike/downloaderbot/service/ChannelService.java b/src/main/java/ru/mike/downloaderbot/service/ChannelService.java new file mode 100644 index 0000000..11aa15a --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/service/ChannelService.java @@ -0,0 +1,26 @@ +package ru.mike.downloaderbot.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mike.downloaderbot.domain.Channel; +import ru.mike.downloaderbot.repository.ChannelRepository; + +import java.util.List; +import java.util.UUID; + +@Service +public class ChannelService { + @Autowired + private ChannelRepository channelRepository; + + @Transactional + public void deleteAllByRuleId(UUID id) { + channelRepository.deleteAllByRuleId(id); + } + + @Transactional(readOnly = true) + public List findAllByRuleId(UUID id) { + return channelRepository.findAllByRuleId(id); + } +} diff --git a/src/main/java/ru/mike/downloaderbot/service/LinkDataService.java b/src/main/java/ru/mike/downloaderbot/service/LinkDataService.java new file mode 100644 index 0000000..30710b5 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/service/LinkDataService.java @@ -0,0 +1,34 @@ +package ru.mike.downloaderbot.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mike.downloaderbot.domain.LinkData; +import ru.mike.downloaderbot.repository.LinkDataRepository; + +import java.util.Optional; +import java.util.UUID; + +@Service +public class LinkDataService { + public static final String BOT_LINK_PREFIX = "https://t.me/new_downloaderx_bot"; + @Autowired + private LinkDataRepository linkDataRepository; + + @Transactional + public LinkData createLinkData(String linkText, String channelName) { + UUID linkId = UUID.randomUUID(); + LinkData linkData = LinkData.builder() + .id(linkId) + .resourceLink(linkText) + .botLink(BOT_LINK_PREFIX + "?start=" + linkId) + .channelName(channelName) + .build(); + + return linkDataRepository.save(linkData); + } + + public Optional findById(UUID linkId) { + return linkDataRepository.findById(linkId); + } +} diff --git a/src/main/java/ru/mike/downloaderbot/service/RuleChannelService.java b/src/main/java/ru/mike/downloaderbot/service/RuleChannelService.java new file mode 100644 index 0000000..882c166 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/service/RuleChannelService.java @@ -0,0 +1,88 @@ +package ru.mike.downloaderbot.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mike.downloaderbot.domain.Channel; +import ru.mike.downloaderbot.domain.Rule; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +@Slf4j +@Service +public class RuleChannelService { + public static final String TELEGRAM_CHANNEL_URL_PREFIX = "https://t.me/"; + @Autowired + private RuleService ruleService; + + @Autowired + private ChannelService channelService; + + @Transactional + public Rule createRuleAndChannels( + String ruleBaseChannelName, + List channelsNames, + List channelsIds + ) { + Optional ruleOptional = ruleService.findByChannelName(ruleBaseChannelName); + Rule rule; + + if (ruleOptional.isEmpty()) { + rule = Rule.builder() + .channelName(ruleBaseChannelName) + .build(); + } else { + rule = ruleOptional.get(); + } + + Set channels = createChannels(rule, channelsNames, channelsIds); + channelService.deleteAllByRuleId(rule.getId()); + + rule.setChannels(channels); + + return ruleService.save(rule); + } + + public List findChannelsByRuleChannelName(String name) { + Optional ruleOptional = ruleService.findByChannelName(name); + + if (ruleOptional.isEmpty()) { + log.warn("Unable to find rule by name:{}", name); + return new ArrayList<>(); + } + + UUID ruleId = ruleOptional.get().getId(); + List channels = channelService.findAllByRuleId(ruleId); + + if (channels.isEmpty()) { + log.info("Channels by rule id:'{}' not found", ruleId); + } + + return channels; + } + + private Set createChannels(Rule rule, List channelsNames, List channelsIds) { + Set channels = new HashSet<>(); + + for (int i = 0; i < channelsNames.size(); i++) { + String channelName = channelsNames.get(i); + + Channel channel = Channel.builder() + .name(channelName) + .link(TELEGRAM_CHANNEL_URL_PREFIX + channelName) + .rule(rule) + .telegramChannelId(channelsIds.get(i)) + .build(); + + channels.add(channel); + } + + return channels; + } +} diff --git a/src/main/java/ru/mike/downloaderbot/service/RuleService.java b/src/main/java/ru/mike/downloaderbot/service/RuleService.java new file mode 100644 index 0000000..33e3844 --- /dev/null +++ b/src/main/java/ru/mike/downloaderbot/service/RuleService.java @@ -0,0 +1,23 @@ +package ru.mike.downloaderbot.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.mike.downloaderbot.domain.Rule; +import ru.mike.downloaderbot.repository.RuleRepository; + +import java.util.Optional; + +@Service +public class RuleService { + @Autowired + private RuleRepository ruleRepository; + + + public Optional findByChannelName(String channelName) { + return ruleRepository.findByChannelName(channelName); + } + + public Rule save(Rule rule) { + return ruleRepository.save(rule); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..8ebc0c7 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,14 @@ +spring: + datasource: + username: postgres + password: zPAwQpsFZC + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/postgres + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + hibernate: + ddl-auto: validate + open-in-view: false + liquibase: + change-log: classpath:db/migration/master/db.changelog-master.xml + enabled: true \ No newline at end of file diff --git a/src/main/resources/db/migration/2023-06-24-init-schema.xml b/src/main/resources/db/migration/2023-06-24-init-schema.xml new file mode 100644 index 0000000..9b77fbf --- /dev/null +++ b/src/main/resources/db/migration/2023-06-24-init-schema.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/migration/master/db.changelog-master.xml b/src/main/resources/db/migration/master/db.changelog-master.xml new file mode 100644 index 0000000..07fb830 --- /dev/null +++ b/src/main/resources/db/migration/master/db.changelog-master.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file