From de5bd454577647f8e56aa37f3175ac57c0105925 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Wed, 6 Jul 2022 00:10:35 +0400 Subject: [PATCH 01/50] initial commit --- .gitignore | 33 ++ .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 58727 bytes .mvn/wrapper/maven-wrapper.properties | 2 + mvnw | 316 ++++++++++++++++++ mvnw.cmd | 188 +++++++++++ pom.xml | 68 ++++ .../tgbotusers/TgBotUsersApplication.java | 13 + .../tgbotusers/config/AppConfig.java | 29 ++ .../tgbotusers/config/GroupConfig.java | 25 ++ .../tgbotusers/controller/BotController.java | 33 ++ .../tgbotusers/dto/BackendResponse.java | 6 + .../tgbotusers/dto/enums/BotStatus.java | 6 + .../tgbotusers/model/Administrator.java | 15 + .../tgbotusers/model/Congratulation.java | 32 ++ .../codewithoutus/tgbotusers/model/Group.java | 24 ++ .../tgbotusers/model/JoinedUser.java | 21 ++ .../tgbotusers/model/ModeratorGroup.java | 24 ++ .../codewithoutus/tgbotusers/model/User.java | 24 ++ .../tgbotusers/model/UserGroup.java | 19 ++ .../repository/AdministratorRepository.java | 9 + .../repository/CongratulationRepository.java | 9 + .../repository/JoinedUserRepository.java | 9 + .../repository/ModeratorGroupRepository.java | 9 + .../repository/UserGroupRepository.java | 9 + .../tgbotusers/services/BotService.java | 52 +++ src/main/resources/application.yml | 27 ++ .../TgBotUsersApplicationTests.java | 13 + 27 files changed, 1015 insertions(+) create mode 100644 .gitignore create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/Group.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/User.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/UserGroup.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/JoinedUserRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/services/BotService.java create mode 100644 src/main/resources/application.yml create mode 100644 src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c1dd12f17644411d6e840bd5a10c6ecda0175f18 GIT binary patch literal 58727 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%0ANsA|A3-jj|r`KAo%oTajX3>^E` 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 YM{}PByk10KD_nuQ4E7Du?}+~TKh4V)`~Uy| literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..22f219d --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..82bef94 --- /dev/null +++ b/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.1 + + + org.codewithoutus + tg-bot-users + 0.0.1-SNAPSHOT + tg-bot-users + Demo project for Spring Boot + + 17 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + com.github.pengrad + java-telegram-bot-api + 6.1.0 + + + + org.postgresql + postgresql + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java new file mode 100644 index 0000000..9b6617e --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java @@ -0,0 +1,13 @@ +package org.codewithoutus.tgbotusers; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TgBotUsersApplication { + + public static void main(String[] args) { + SpringApplication.run(TgBotUsersApplication.class, args); + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java new file mode 100644 index 0000000..cd56674 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java @@ -0,0 +1,29 @@ +package org.codewithoutus.tgbotusers.config; + +import com.pengrad.telegrambot.TelegramBot; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@Component +@Configuration +@ConfigurationProperties(prefix = "telegram") +public class AppConfig { + + private int longPollingTimeout; + private int multiplicity; + private int incrementalSaves; + private String botUserName; + private String botToken; + + @Bean + public TelegramBot telegramBot() { + return new TelegramBot(botToken); + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java new file mode 100644 index 0000000..e63f4e5 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -0,0 +1,25 @@ +package org.codewithoutus.tgbotusers.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Getter +@Setter +@Component +@Configuration +@ConfigurationProperties(prefix = "telegram.groups") +public class GroupConfig { + private List groups; + + + public record ModeratorGroup(int id, List userGroups) { + } + + public record UserGroup(int id) { + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java new file mode 100644 index 0000000..dc6ebb0 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -0,0 +1,33 @@ +package org.codewithoutus.tgbotusers.controller; + +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.dto.BackendResponse; +import org.codewithoutus.tgbotusers.services.BotService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/backend") +@RequiredArgsConstructor +public class BotController { + + private final BotService botService; + + @GetMapping("/start") + private BackendResponse startBotBackend() { + return botService.start(); + } + + @GetMapping("/stop") + private BackendResponse stopBotBackend() { + return botService.stop(); + } + + @GetMapping("/sendMessage") + private BackendResponse sendMessage(@RequestParam String message) { + return botService.sendMessage(message); + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java b/src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java new file mode 100644 index 0000000..99a1dbf --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java @@ -0,0 +1,6 @@ +package org.codewithoutus.tgbotusers.dto; + +import org.codewithoutus.tgbotusers.dto.enums.BotStatus; + +public record BackendResponse(boolean ok, BotStatus status) { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java b/src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java new file mode 100644 index 0000000..5fc7795 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java @@ -0,0 +1,6 @@ +package org.codewithoutus.tgbotusers.dto.enums; + +public enum BotStatus { + START, + STOP +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java new file mode 100644 index 0000000..f79abbd --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java @@ -0,0 +1,15 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "administrators") +@Getter +@Setter +public class Administrator extends User { + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java new file mode 100644 index 0000000..3508765 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java @@ -0,0 +1,32 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "congratulations") +@Getter +@Setter +public class Congratulation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Integer id; + + @OneToOne + @JoinColumn(name = "user_id", columnDefinition="INT NOT NULL") + private JoinedUser joinedUser; + + @Column(name = "number", columnDefinition = "INT NOT NULL") + private Integer number; +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Group.java b/src/main/java/org/codewithoutus/tgbotusers/model/Group.java new file mode 100644 index 0000000..4db6cac --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Group.java @@ -0,0 +1,24 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +@Getter +@Setter +public class Group { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Integer id; + + @Column(name = "group_id", columnDefinition = "INT NOT NULL") + private Integer groupId; +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java new file mode 100644 index 0000000..ded8306 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java @@ -0,0 +1,21 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "joined_users") +@Getter +@Setter +public class JoinedUser extends User { + + @Column(name = "chat_id", columnDefinition = "INT NOT NULL") + private Integer chatId; + + @Column(name = "number", columnDefinition = "INT NOT NULL") + private Integer number; +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java new file mode 100644 index 0000000..25559f6 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java @@ -0,0 +1,24 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.Table; +import java.util.Set; + +@Entity +@Table(name = "user_groups") +@Getter +@Setter +public class ModeratorGroup extends Group { + + @ManyToMany + @JoinTable(name = "moderators2users", + joinColumns = {@JoinColumn(name = "moderator_group_id", referencedColumnName = "id", columnDefinition="INT NOT NULL")}, + inverseJoinColumns = {@JoinColumn(name = "user_group_id", referencedColumnName = "id", columnDefinition="INT NOT NULL")}) + private Set userGroups; +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/User.java b/src/main/java/org/codewithoutus/tgbotusers/model/User.java new file mode 100644 index 0000000..c7ec248 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/User.java @@ -0,0 +1,24 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +@Getter +@Setter +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Integer id; + + @Column(name = "user_id", columnDefinition = "INT NOT NULL") + Integer userId; +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserGroup.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserGroup.java new file mode 100644 index 0000000..78cb1f1 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserGroup.java @@ -0,0 +1,19 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.ManyToMany; +import javax.persistence.Table; +import java.util.Set; + +@Entity +@Table(name = "user_groups") +@Getter +@Setter +public class UserGroup extends Group { + + @ManyToMany(mappedBy = "userGroups") + private Set moderatorsGroups; +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java new file mode 100644 index 0000000..8b2467c --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java @@ -0,0 +1,9 @@ +package org.codewithoutus.tgbotusers.repository; + +import org.codewithoutus.tgbotusers.model.Administrator; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AdministratorRepository extends CrudRepository { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java new file mode 100644 index 0000000..524065a --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java @@ -0,0 +1,9 @@ +package org.codewithoutus.tgbotusers.repository; + +import org.codewithoutus.tgbotusers.model.Congratulation; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CongratulationRepository extends CrudRepository { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/JoinedUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/JoinedUserRepository.java new file mode 100644 index 0000000..ce83753 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/JoinedUserRepository.java @@ -0,0 +1,9 @@ +package org.codewithoutus.tgbotusers.repository; + +import org.codewithoutus.tgbotusers.model.JoinedUser; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface JoinedUserRepository extends CrudRepository { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java new file mode 100644 index 0000000..6ba88c6 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java @@ -0,0 +1,9 @@ +package org.codewithoutus.tgbotusers.repository; + +import org.codewithoutus.tgbotusers.model.ModeratorGroup; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ModeratorGroupRepository extends CrudRepository { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java new file mode 100644 index 0000000..c9d8203 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java @@ -0,0 +1,9 @@ +package org.codewithoutus.tgbotusers.repository; + +import org.codewithoutus.tgbotusers.model.UserGroup; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserGroupRepository extends CrudRepository { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java new file mode 100644 index 0000000..42adc8b --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java @@ -0,0 +1,52 @@ +package org.codewithoutus.tgbotusers.services; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.UpdatesListener; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.GetUpdates; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.config.AppConfig; +import org.codewithoutus.tgbotusers.config.GroupConfig; +import org.codewithoutus.tgbotusers.dto.BackendResponse; +import org.codewithoutus.tgbotusers.dto.enums.BotStatus; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BotService { + + private final AppConfig appConfig; + private final GroupConfig groupConfig; + private final TelegramBot bot; + + + public BackendResponse start() { + + GetUpdates getUpdates = new GetUpdates().timeout(appConfig.getLongPollingTimeout()); + bot.setUpdatesListener(updates -> { + updates.forEach(this::process); + return UpdatesListener.CONFIRMED_UPDATES_ALL; + }, + getUpdates); + return new BackendResponse(true, BotStatus.START); + } + + public BackendResponse stop() { + bot.removeGetUpdatesListener(); + return new BackendResponse(true, BotStatus.STOP); + } + + + private void process(Update update) { + + } + + + public BackendResponse sendMessage(String message) { + SendMessage sendMessage = new SendMessage(-644481529, message); + bot.execute(sendMessage); + return new BackendResponse(true, BotStatus.START); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..6954291 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + datasource: + url: jdbc:postgresql://localhost:5432/tg-bot-users + username: tg-admin + password: 9en2w0oc + hikari: + maximum-pool-size: 2 + jpa: + hibernate: + ddl-auto: update + +logging: + level: + root: info + +telegram: +# multiplicity: 500 +# incremental-saves: 2 + long-polling-timeout: 1000 + + bot-user-name: ${BOT_USER_NAME} + bot-token: ${BOT_TOKEN} + + groups: + - moderator-group: -644481529 + user-groups: + - -606991893 diff --git a/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java b/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java new file mode 100644 index 0000000..c65754d --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java @@ -0,0 +1,13 @@ +package org.codewithoutus.tgbotusers; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class TgBotUsersApplicationTests { + + @Test + void contextLoads() { + } + +} From dc19409a49a6a87de508f0e284e0b54e9fb77b9d Mon Sep 17 00:00:00 2001 From: Aleksei Date: Thu, 7 Jul 2022 18:40:55 +0400 Subject: [PATCH 02/50] created updates handlers frame, was performed minor changes of model --- .../tgbotusers/config/AppConfig.java | 2 - .../tgbotusers/config/GroupConfig.java | 17 ++-- .../tgbotusers/controller/BotController.java | 8 +- .../tgbotusers/handler/CallbackHandler.java | 4 + .../handler/ChatJoinRequestHandler.java | 80 +++++++++++++++++++ .../tgbotusers/handler/Handler.java | 5 ++ .../tgbotusers/handler/MessageHandler.java | 4 + .../tgbotusers/handler/UpdateProcessor.java | 54 +++++++++++++ .../tgbotusers/model/Administrator.java | 2 - .../model/{Group.java => Chat.java} | 8 +- .../tgbotusers/model/Congratulation.java | 11 +-- .../tgbotusers/model/JoinedUser.java | 11 ++- .../tgbotusers/model/ModeratorChat.java | 23 ++++++ .../tgbotusers/model/ModeratorGroup.java | 24 ------ .../codewithoutus/tgbotusers/model/User.java | 6 +- .../model/{UserGroup.java => UserChat.java} | 7 +- .../repository/ModeratorGroupRepository.java | 4 +- .../repository/UserGroupRepository.java | 4 +- .../services/AdministratorService.java | 7 ++ .../tgbotusers/services/BotService.java | 11 ++- .../services/CongratulationService.java | 7 ++ .../services/JoinedUserService.java | 18 +++++ .../services/ModeratorGroupService.java | 7 ++ .../tgbotusers/services/UserGroupService.java | 7 ++ 24 files changed, 265 insertions(+), 66 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java rename src/main/java/org/codewithoutus/tgbotusers/model/{Group.java => Chat.java} (74%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java rename src/main/java/org/codewithoutus/tgbotusers/model/{UserGroup.java => UserChat.java} (62%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/services/AdministratorService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/services/CongratulationService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/services/JoinedUserService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/services/ModeratorGroupService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/services/UserGroupService.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java index cd56674..32501c9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java @@ -6,11 +6,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; @Getter @Setter -@Component @Configuration @ConfigurationProperties(prefix = "telegram") public class AppConfig { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java index e63f4e5..2d59b84 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -1,25 +1,32 @@ package org.codewithoutus.tgbotusers.config; +import lombok.Data; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; import java.util.List; +import java.util.Objects; @Getter @Setter -@Component @Configuration @ConfigurationProperties(prefix = "telegram.groups") -public class GroupConfig { +public class GroupConfig { private List groups; - public record ModeratorGroup(int id, List userGroups) { + @Getter + @Setter + public class ModeratorGroup { + private int id; + private List userGroups; } - public record UserGroup(int id) { + @Getter + @Setter + public class UserGroup { + private int id; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index dc6ebb0..20fd09b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -25,9 +25,9 @@ private BackendResponse stopBotBackend() { return botService.stop(); } - @GetMapping("/sendMessage") - private BackendResponse sendMessage(@RequestParam String message) { - return botService.sendMessage(message); - } +// @GetMapping("/sendMessage") +// private BackendResponse sendMessage(@RequestParam String message) { +// return botService.sendMessage(message); +// } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java new file mode 100644 index 0000000..01fcd2d --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java @@ -0,0 +1,4 @@ +package org.codewithoutus.tgbotusers.handler; + +public class CallbackHandler { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java new file mode 100644 index 0000000..b930a1b --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java @@ -0,0 +1,80 @@ +package org.codewithoutus.tgbotusers.handler; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.ChatJoinRequest; +import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.request.GetChatMember; +import com.pengrad.telegrambot.request.GetChatMemberCount; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.JoinedUser; +import org.codewithoutus.tgbotusers.services.JoinedUserService; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.LocalDateTime; + +@Component +@RequiredArgsConstructor +public class ChatJoinRequestHandler implements Handler { + private static final int MULTIPLICITY = 500; + private static final int INCREMENTAL_SAVES = 2; + + private final TelegramBot bot; + private final JoinedUserService joinedUserService; + + @Override + public void handle(Object handledObject) { + ChatJoinRequest chatJoinRequest = (ChatJoinRequest) handledObject; + + long chatId = chatJoinRequest.chat().id(); + int count = getCount(chatId); + if (count % MULTIPLICITY > INCREMENTAL_SAVES) { + return; + } + + long userId = chatJoinRequest.from().id(); + User user = getUser(chatId, userId); + if (user.isBot()) { + return; + } + + int date = chatJoinRequest.date(); + + JoinedUser joinedUser = createJoinedUser(user, chatId, count, date); + joinedUserService.save(joinedUser); + + createNotification(user); + sendNotification(); + } + + private int getCount(long chatId) { + var getChatMemberCount = new GetChatMemberCount(chatId); + var getChatMemberCountResponse = bot.execute(getChatMemberCount); + return getChatMemberCountResponse.count(); + } + + private User getUser(long chatId, long userId) { + var getChatMember = new GetChatMember(chatId, userId); + var getChatMemberResponse = bot.execute(getChatMember); + return getChatMemberResponse.chatMember().user(); + } + + private JoinedUser createJoinedUser(User user, long chatId, int count, int date) { + JoinedUser joinedUser = new JoinedUser(); + joinedUser.setUserId(user.id()); + joinedUser.setChatId(chatId); + joinedUser.setNumber(count); + Instant time = Instant.ofEpochSecond(date); + joinedUser.setJoinTime(LocalDateTime.from(time)); + return joinedUser; + } + + private void createNotification(User user) { + + } + + private void sendNotification() { + + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java new file mode 100644 index 0000000..04a9287 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java @@ -0,0 +1,5 @@ +package org.codewithoutus.tgbotusers.handler; + +public interface Handler { + void handle(Object handledObject); +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java new file mode 100644 index 0000000..db675c4 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java @@ -0,0 +1,4 @@ +package org.codewithoutus.tgbotusers.handler; + +public class MessageHandler { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java new file mode 100644 index 0000000..9c737fa --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java @@ -0,0 +1,54 @@ +package org.codewithoutus.tgbotusers.handler; + +import com.pengrad.telegrambot.model.CallbackQuery; +import com.pengrad.telegrambot.model.ChatJoinRequest; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.MessageEntity; +import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UpdateProcessor { + + private final ChatJoinRequestHandler chatJoinRequestHandler; + private final CallbackHandler callbackHandler; + private final MessageHandler messageHandler; + + private Update currentUpdate; + + public void updateProcess(Update update) { + currentUpdate = update; + + ChatJoinRequest chatJoinRequest = currentUpdate.chatJoinRequest(); + if (chatJoinRequest != null) { + handleChatJoinRequest(chatJoinRequest); + } + + CallbackQuery callbackQuery = currentUpdate.callbackQuery(); + if (callbackQuery != null) { + handleCallback(callbackQuery); + } + + Message message = currentUpdate.message(); + if (message != null) { + handleCommand(message); + } + } + + private void handleChatJoinRequest(ChatJoinRequest chatJoinRequest) { + + } + + private void handleCallback(CallbackQuery callbackQuery) { + + } + + private void handleCommand(Message message) { + MessageEntity[] entities = message.entities(); + if (entities != null && entities.length > 0) { + + } + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java index f79abbd..814b22f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java @@ -4,10 +4,8 @@ import lombok.Setter; import javax.persistence.Entity; -import javax.persistence.Table; @Entity -@Table(name = "administrators") @Getter @Setter public class Administrator extends User { diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Group.java b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java similarity index 74% rename from src/main/java/org/codewithoutus/tgbotusers/model/Group.java rename to src/main/java/org/codewithoutus/tgbotusers/model/Chat.java index 4db6cac..e435ca7 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Group.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java @@ -12,13 +12,13 @@ @MappedSuperclass @Getter @Setter -public class Group { +public class Chat { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") private Integer id; - @Column(name = "group_id", columnDefinition = "INT NOT NULL") - private Integer groupId; + @Column(nullable = false, unique = true) + private Long groupId; + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java index 3508765..73bc330 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java @@ -9,24 +9,21 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; @Entity -@Table(name = "congratulations") @Getter @Setter public class Congratulation { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") private Integer id; - @OneToOne - @JoinColumn(name = "user_id", columnDefinition="INT NOT NULL") + @OneToOne(optional = false, orphanRemoval = true) + @JoinColumn(name = "joined_user_id", nullable = false, unique = true) private JoinedUser joinedUser; - - @Column(name = "number", columnDefinition = "INT NOT NULL") - private Integer number; + } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java index ded8306..246d5d1 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java @@ -6,16 +6,19 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import java.time.LocalDateTime; @Entity -@Table(name = "joined_users") @Getter @Setter public class JoinedUser extends User { - @Column(name = "chat_id", columnDefinition = "INT NOT NULL") - private Integer chatId; + @Column(nullable = false) + private Long chatId; - @Column(name = "number", columnDefinition = "INT NOT NULL") + @Column(nullable = false) private Integer number; + + @Column(nullable = false) + private LocalDateTime joinTime; } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java new file mode 100644 index 0000000..625d4b6 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java @@ -0,0 +1,23 @@ +package org.codewithoutus.tgbotusers.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import java.util.List; +import java.util.Set; + +@Entity +@Getter +@Setter +public class ModeratorChat extends Chat { + + @ManyToMany + @JoinTable(name = "moderators2users", + joinColumns = @JoinColumn(name = "moderator_chat_id"), + inverseJoinColumns = @JoinColumn(name = "user_chat_id")) + private List userGroups; +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java deleted file mode 100644 index 25559f6..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorGroup.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.codewithoutus.tgbotusers.model; - -import lombok.Getter; -import lombok.Setter; - -import javax.persistence.Entity; -import javax.persistence.JoinColumn; -import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; -import javax.persistence.Table; -import java.util.Set; - -@Entity -@Table(name = "user_groups") -@Getter -@Setter -public class ModeratorGroup extends Group { - - @ManyToMany - @JoinTable(name = "moderators2users", - joinColumns = {@JoinColumn(name = "moderator_group_id", referencedColumnName = "id", columnDefinition="INT NOT NULL")}, - inverseJoinColumns = {@JoinColumn(name = "user_group_id", referencedColumnName = "id", columnDefinition="INT NOT NULL")}) - private Set userGroups; -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/User.java b/src/main/java/org/codewithoutus/tgbotusers/model/User.java index c7ec248..10c162a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/User.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/User.java @@ -16,9 +16,9 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") private Integer id; - @Column(name = "user_id", columnDefinition = "INT NOT NULL") - Integer userId; + @Column(nullable = false, unique = true) + Long userId; + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserGroup.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java similarity index 62% rename from src/main/java/org/codewithoutus/tgbotusers/model/UserGroup.java rename to src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java index 78cb1f1..ddad6f2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserGroup.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java @@ -5,15 +5,14 @@ import javax.persistence.Entity; import javax.persistence.ManyToMany; -import javax.persistence.Table; +import java.util.List; import java.util.Set; @Entity -@Table(name = "user_groups") @Getter @Setter -public class UserGroup extends Group { +public class UserChat extends Chat { @ManyToMany(mappedBy = "userGroups") - private Set moderatorsGroups; + private List moderatorChats; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java index 6ba88c6..e828068 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java @@ -1,9 +1,9 @@ package org.codewithoutus.tgbotusers.repository; -import org.codewithoutus.tgbotusers.model.ModeratorGroup; +import org.codewithoutus.tgbotusers.model.ModeratorChat; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository -public interface ModeratorGroupRepository extends CrudRepository { +public interface ModeratorGroupRepository extends CrudRepository { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java index c9d8203..0dcc6ec 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java @@ -1,9 +1,9 @@ package org.codewithoutus.tgbotusers.repository; -import org.codewithoutus.tgbotusers.model.UserGroup; +import org.codewithoutus.tgbotusers.model.UserChat; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository -public interface UserGroupRepository extends CrudRepository { +public interface UserGroupRepository extends CrudRepository { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/AdministratorService.java b/src/main/java/org/codewithoutus/tgbotusers/services/AdministratorService.java new file mode 100644 index 0000000..3204366 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/services/AdministratorService.java @@ -0,0 +1,7 @@ +package org.codewithoutus.tgbotusers.services; + +import org.springframework.stereotype.Service; + +@Service +public class AdministratorService { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java index 42adc8b..cc3a82b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java @@ -10,7 +10,6 @@ import org.codewithoutus.tgbotusers.config.GroupConfig; import org.codewithoutus.tgbotusers.dto.BackendResponse; import org.codewithoutus.tgbotusers.dto.enums.BotStatus; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service @@ -20,9 +19,13 @@ public class BotService { private final AppConfig appConfig; private final GroupConfig groupConfig; private final TelegramBot bot; + private BotStatus status; public BackendResponse start() { + if (status.equals(BotStatus.START)) { + return new BackendResponse(false, status); + } GetUpdates getUpdates = new GetUpdates().timeout(appConfig.getLongPollingTimeout()); bot.setUpdatesListener(updates -> { @@ -30,12 +33,14 @@ public BackendResponse start() { return UpdatesListener.CONFIRMED_UPDATES_ALL; }, getUpdates); - return new BackendResponse(true, BotStatus.START); + status = BotStatus.START; + return new BackendResponse(true, status); } public BackendResponse stop() { + status = BotStatus.STOP; bot.removeGetUpdatesListener(); - return new BackendResponse(true, BotStatus.STOP); + return new BackendResponse(true, status); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/CongratulationService.java b/src/main/java/org/codewithoutus/tgbotusers/services/CongratulationService.java new file mode 100644 index 0000000..a6abc82 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/services/CongratulationService.java @@ -0,0 +1,7 @@ +package org.codewithoutus.tgbotusers.services; + +import org.springframework.stereotype.Service; + +@Service +public class CongratulationService { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/JoinedUserService.java b/src/main/java/org/codewithoutus/tgbotusers/services/JoinedUserService.java new file mode 100644 index 0000000..6ff98c9 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/services/JoinedUserService.java @@ -0,0 +1,18 @@ +package org.codewithoutus.tgbotusers.services; + +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.JoinedUser; +import org.codewithoutus.tgbotusers.repository.JoinedUserRepository; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class JoinedUserService { + + private final JoinedUserRepository joinedUserRepository; + + public void save(JoinedUser joinedUser) { + joinedUserRepository.save(joinedUser); + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/ModeratorGroupService.java b/src/main/java/org/codewithoutus/tgbotusers/services/ModeratorGroupService.java new file mode 100644 index 0000000..0f2eb01 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/services/ModeratorGroupService.java @@ -0,0 +1,7 @@ +package org.codewithoutus.tgbotusers.services; + +import org.springframework.stereotype.Service; + +@Service +public class ModeratorGroupService { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/UserGroupService.java b/src/main/java/org/codewithoutus/tgbotusers/services/UserGroupService.java new file mode 100644 index 0000000..7a9ba98 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/services/UserGroupService.java @@ -0,0 +1,7 @@ +package org.codewithoutus.tgbotusers.services; + +import org.springframework.stereotype.Service; + +@Service +public class UserGroupService { +} From a94b7522f4dd07bfd01ac46d5173644c63403f7c Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Thu, 7 Jul 2022 23:48:59 +0700 Subject: [PATCH 03/50] clear log --- .../org/codewithoutus/tgbotusers/config/PropsFromFile.java | 4 ++++ src/main/resources/application.yml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java b/src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java new file mode 100644 index 0000000..d247efc --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java @@ -0,0 +1,4 @@ +package org.codewithoutus.tgbotusers.config; + +public class PropsFromFile { +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6954291..30805e3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,8 @@ spring: datasource: url: jdbc:postgresql://localhost:5432/tg-bot-users - username: tg-admin - password: 9en2w0oc + username: postgres + password: testtest hikari: maximum-pool-size: 2 jpa: From 0ce78d69128fe2228bd81c15bf47a2302ad40dbe Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 8 Jul 2022 18:02:33 +0400 Subject: [PATCH 04/50] created new services, refactored application frame, made minor changes of model, add new empty config classes --- .../tgbotusers/config/AppConfig.java | 6 +- .../tgbotusers/config/BotConfig.java | 18 +++++ .../tgbotusers/config/GroupConfig.java | 2 - .../tgbotusers/config/NotificationConfig.java | 4 + .../tgbotusers/controller/BotController.java | 8 +- .../tgbotusers/handler/CallbackHandler.java | 4 - .../handler/CallbackQueryHandler.java | 14 ++++ .../handler/ChatJoinRequestHandler.java | 77 ++++++------------- .../tgbotusers/handler/Handler.java | 6 +- .../tgbotusers/handler/MessageHandler.java | 12 ++- .../tgbotusers/handler/UpdateProcessor.java | 4 +- .../codewithoutus/tgbotusers/model/Chat.java | 2 +- .../tgbotusers/model/ModeratorChat.java | 3 +- .../tgbotusers/model/UserChat.java | 3 +- ...tory.java => ModeratorChatRepository.java} | 2 +- ...epository.java => UserChatRepository.java} | 2 +- .../AdministratorService.java | 2 +- .../service/BotExecutorService.java | 27 +++++++ .../{services => service}/BotService.java | 18 +---- .../service/CallbackQueryService.java | 11 +++ .../service/ChatJoinRequestService.java | 50 ++++++++++++ .../CongratulationService.java | 2 +- .../JoinedUserService.java | 2 +- .../tgbotusers/service/MessageService.java | 11 +++ .../ModeratorGroupService.java | 2 +- .../service/NotificationService.java | 25 ++++++ .../service/TelegramEntityService.java | 8 ++ .../tgbotusers/service/UpdateService.java | 22 ++++++ .../UserGroupService.java | 2 +- 29 files changed, 246 insertions(+), 103 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java rename src/main/java/org/codewithoutus/tgbotusers/repository/{ModeratorGroupRepository.java => ModeratorChatRepository.java} (71%) rename src/main/java/org/codewithoutus/tgbotusers/repository/{UserGroupRepository.java => UserChatRepository.java} (73%) rename src/main/java/org/codewithoutus/tgbotusers/{services => service}/AdministratorService.java (67%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java rename src/main/java/org/codewithoutus/tgbotusers/{services => service}/BotService.java (73%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java rename src/main/java/org/codewithoutus/tgbotusers/{services => service}/CongratulationService.java (67%) rename src/main/java/org/codewithoutus/tgbotusers/{services => service}/JoinedUserService.java (90%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java rename src/main/java/org/codewithoutus/tgbotusers/{services => service}/ModeratorGroupService.java (67%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java rename src/main/java/org/codewithoutus/tgbotusers/{services => service}/UserGroupService.java (66%) diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java index 32501c9..2b6d751 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java @@ -18,10 +18,6 @@ public class AppConfig { private int incrementalSaves; private String botUserName; private String botToken; - - @Bean - public TelegramBot telegramBot() { - return new TelegramBot(botToken); - } + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java new file mode 100644 index 0000000..9b1018e --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java @@ -0,0 +1,18 @@ +package org.codewithoutus.tgbotusers.config; + +import com.pengrad.telegrambot.TelegramBot; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class BotConfig { + + private final AppConfig appConfig; + + @Bean + public TelegramBot telegramBot() { + return new TelegramBot(appConfig.getBotToken()); + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java index 2d59b84..69e6576 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -1,13 +1,11 @@ package org.codewithoutus.tgbotusers.config; -import lombok.Data; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.List; -import java.util.Objects; @Getter @Setter diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java new file mode 100644 index 0000000..7b35b8d --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java @@ -0,0 +1,4 @@ +package org.codewithoutus.tgbotusers.config; + +public class NotificationConfig { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index 20fd09b..679df5d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -2,10 +2,9 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.dto.BackendResponse; -import org.codewithoutus.tgbotusers.services.BotService; +import org.codewithoutus.tgbotusers.service.BotService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -25,9 +24,4 @@ private BackendResponse stopBotBackend() { return botService.stop(); } -// @GetMapping("/sendMessage") -// private BackendResponse sendMessage(@RequestParam String message) { -// return botService.sendMessage(message); -// } - } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java deleted file mode 100644 index 01fcd2d..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackHandler.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -public class CallbackHandler { -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java new file mode 100644 index 0000000..4fc553a --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java @@ -0,0 +1,14 @@ +package org.codewithoutus.tgbotusers.handler; + +import com.pengrad.telegrambot.model.Update; + +import java.util.Collections; +import java.util.Map; + +public class CallbackQueryHandler implements Handler { + + @Override + public Map handle(Update update) { + return Collections.emptyMap(); + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java index b930a1b..999a519 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java @@ -1,80 +1,49 @@ package org.codewithoutus.tgbotusers.handler; -import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.model.ChatJoinRequest; +import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.User; -import com.pengrad.telegrambot.request.GetChatMember; -import com.pengrad.telegrambot.request.GetChatMemberCount; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.JoinedUser; -import org.codewithoutus.tgbotusers.services.JoinedUserService; +import org.codewithoutus.tgbotusers.service.BotExecutorService; import org.springframework.stereotype.Component; -import java.time.Instant; -import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Map; @Component @RequiredArgsConstructor public class ChatJoinRequestHandler implements Handler { - private static final int MULTIPLICITY = 500; - private static final int INCREMENTAL_SAVES = 2; + private static final int MULTIPLICITY = 500; // TODO replace on config + private static final int INCREMENTAL_SAVES = 2; // TODO replace on config - private final TelegramBot bot; - private final JoinedUserService joinedUserService; + private final BotExecutorService botExecutorService; @Override - public void handle(Object handledObject) { - ChatJoinRequest chatJoinRequest = (ChatJoinRequest) handledObject; + public Map handle(Update update) { + + ChatJoinRequest chatJoinRequest = update.chatJoinRequest(); + if (chatJoinRequest == null) { + return Collections.emptyMap(); + } long chatId = chatJoinRequest.chat().id(); - int count = getCount(chatId); + int count = botExecutorService.getCount(chatId); if (count % MULTIPLICITY > INCREMENTAL_SAVES) { - return; + return Collections.emptyMap(); } - - long userId = chatJoinRequest.from().id(); - User user = getUser(chatId, userId); + + User user = chatJoinRequest.from(); if (user.isBot()) { - return; + return Collections.emptyMap(); } int date = chatJoinRequest.date(); - JoinedUser joinedUser = createJoinedUser(user, chatId, count, date); - joinedUserService.save(joinedUser); - - createNotification(user); - sendNotification(); - } - - private int getCount(long chatId) { - var getChatMemberCount = new GetChatMemberCount(chatId); - var getChatMemberCountResponse = bot.execute(getChatMemberCount); - return getChatMemberCountResponse.count(); - } - - private User getUser(long chatId, long userId) { - var getChatMember = new GetChatMember(chatId, userId); - var getChatMemberResponse = bot.execute(getChatMember); - return getChatMemberResponse.chatMember().user(); - } - - private JoinedUser createJoinedUser(User user, long chatId, int count, int date) { - JoinedUser joinedUser = new JoinedUser(); - joinedUser.setUserId(user.id()); - joinedUser.setChatId(chatId); - joinedUser.setNumber(count); - Instant time = Instant.ofEpochSecond(date); - joinedUser.setJoinTime(LocalDateTime.from(time)); - return joinedUser; - } - - private void createNotification(User user) { - - } - - private void sendNotification() { - + return Map.of( + "userId", user.id(), + "chatId", chatId, + "count", count, + "date", date); } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java index 04a9287..79c5353 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java @@ -1,5 +1,9 @@ package org.codewithoutus.tgbotusers.handler; +import com.pengrad.telegrambot.model.Update; + +import java.util.Map; + public interface Handler { - void handle(Object handledObject); + Map handle(Update update); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java index db675c4..12b4091 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java @@ -1,4 +1,14 @@ package org.codewithoutus.tgbotusers.handler; -public class MessageHandler { +import com.pengrad.telegrambot.model.Update; + +import java.util.Collections; +import java.util.Map; + +public class MessageHandler implements Handler { + + @Override + public Map handle(Update update) { + return Collections.emptyMap(); + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java index 9c737fa..a40fb7b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java @@ -8,12 +8,12 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -@Component +@Deprecated @RequiredArgsConstructor public class UpdateProcessor { private final ChatJoinRequestHandler chatJoinRequestHandler; - private final CallbackHandler callbackHandler; + private final CallbackQueryHandler callbackQueryHandler; private final MessageHandler messageHandler; private Update currentUpdate; diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java index e435ca7..fa500ac 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java @@ -19,6 +19,6 @@ public class Chat { private Integer id; @Column(nullable = false, unique = true) - private Long groupId; + private Long chatId; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java index 625d4b6..d8524fc 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java @@ -8,7 +8,6 @@ import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import java.util.List; -import java.util.Set; @Entity @Getter @@ -19,5 +18,5 @@ public class ModeratorChat extends Chat { @JoinTable(name = "moderators2users", joinColumns = @JoinColumn(name = "moderator_chat_id"), inverseJoinColumns = @JoinColumn(name = "user_chat_id")) - private List userGroups; + private List userChats; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java index ddad6f2..f61dc91 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java @@ -6,13 +6,12 @@ import javax.persistence.Entity; import javax.persistence.ManyToMany; import java.util.List; -import java.util.Set; @Entity @Getter @Setter public class UserChat extends Chat { - @ManyToMany(mappedBy = "userGroups") + @ManyToMany(mappedBy = "userChats") private List moderatorChats; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java similarity index 71% rename from src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java rename to src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java index e828068..7020e4a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorGroupRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java @@ -5,5 +5,5 @@ import org.springframework.stereotype.Repository; @Repository -public interface ModeratorGroupRepository extends CrudRepository { +public interface ModeratorChatRepository extends CrudRepository { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java similarity index 73% rename from src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java rename to src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java index 0dcc6ec..8931a06 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserGroupRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java @@ -5,5 +5,5 @@ import org.springframework.stereotype.Repository; @Repository -public interface UserGroupRepository extends CrudRepository { +public interface UserChatRepository extends CrudRepository { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/AdministratorService.java b/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java similarity index 67% rename from src/main/java/org/codewithoutus/tgbotusers/services/AdministratorService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java index 3204366..01116e3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/AdministratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.services; +package org.codewithoutus.tgbotusers.service; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java new file mode 100644 index 0000000..da08287 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java @@ -0,0 +1,27 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.request.GetChatMember; +import com.pengrad.telegrambot.request.GetChatMemberCount; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BotExecutorService { + private final TelegramBot bot; + + + public int getCount(long chatId) { + var getChatMemberCount = new GetChatMemberCount(chatId); + var getChatMemberCountResponse = bot.execute(getChatMemberCount); + return getChatMemberCountResponse.count(); + } + + public User getUser(long chatId, long userId) { + var getChatMember = new GetChatMember(chatId, userId); + var getChatMemberResponse = bot.execute(getChatMember); + return getChatMemberResponse.chatMember().user(); + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java similarity index 73% rename from src/main/java/org/codewithoutus/tgbotusers/services/BotService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/BotService.java index cc3a82b..378e183 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java @@ -1,10 +1,8 @@ -package org.codewithoutus.tgbotusers.services; +package org.codewithoutus.tgbotusers.service; import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.UpdatesListener; -import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.request.GetUpdates; -import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.config.AppConfig; import org.codewithoutus.tgbotusers.config.GroupConfig; @@ -19,6 +17,7 @@ public class BotService { private final AppConfig appConfig; private final GroupConfig groupConfig; private final TelegramBot bot; + private final UpdateService updateService; private BotStatus status; @@ -29,7 +28,7 @@ public BackendResponse start() { GetUpdates getUpdates = new GetUpdates().timeout(appConfig.getLongPollingTimeout()); bot.setUpdatesListener(updates -> { - updates.forEach(this::process); + updates.forEach(updateService::process); return UpdatesListener.CONFIRMED_UPDATES_ALL; }, getUpdates); @@ -43,15 +42,4 @@ public BackendResponse stop() { return new BackendResponse(true, status); } - - private void process(Update update) { - - } - - - public BackendResponse sendMessage(String message) { - SendMessage sendMessage = new SendMessage(-644481529, message); - bot.execute(sendMessage); - return new BackendResponse(true, BotStatus.START); - } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java new file mode 100644 index 0000000..d5b5565 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java @@ -0,0 +1,11 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.model.Update; + +public class CallbackQueryService implements TelegramEntityService { + + @Override + public void process(Update update) { + + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java new file mode 100644 index 0000000..b1b7c94 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java @@ -0,0 +1,50 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.handler.ChatJoinRequestHandler; +import org.codewithoutus.tgbotusers.model.JoinedUser; +import org.codewithoutus.tgbotusers.repository.JoinedUserRepository; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class ChatJoinRequestService implements TelegramEntityService { + + private final ChatJoinRequestHandler chatJoinRequestHandler; + private final JoinedUserService joinedUserService; + private final NotificationService notificationService; + + + @Override + public void process(Update update) { + Map userData = chatJoinRequestHandler.handle(update); + if (userData.isEmpty()) { + return; + } + + JoinedUser joinedUser = buildJoinedUser(userData); + joinedUserService.save(joinedUser); + + notificationService.notifyModerators(joinedUser); + } + + private JoinedUser buildJoinedUser(Map userData) { + + JoinedUser joinedUser = new JoinedUser(); + + joinedUser.setUserId((Long) userData.get("userId")); + joinedUser.setChatId((Long) userData.get("chatId")); + joinedUser.setNumber((Integer) userData.get("count")); + Instant time = Instant.ofEpochSecond((Long) userData.get("data")); + joinedUser.setJoinTime(LocalDateTime.from(time)); + + return joinedUser; + } + + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/CongratulationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java similarity index 67% rename from src/main/java/org/codewithoutus/tgbotusers/services/CongratulationService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java index a6abc82..f2b335b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/CongratulationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.services; +package org.codewithoutus.tgbotusers.service; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/JoinedUserService.java b/src/main/java/org/codewithoutus/tgbotusers/service/JoinedUserService.java similarity index 90% rename from src/main/java/org/codewithoutus/tgbotusers/services/JoinedUserService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/JoinedUserService.java index 6ff98c9..796f139 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/JoinedUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/JoinedUserService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.services; +package org.codewithoutus.tgbotusers.service; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.model.JoinedUser; diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java new file mode 100644 index 0000000..43b3cd4 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java @@ -0,0 +1,11 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.model.Update; + +public class MessageService implements TelegramEntityService { + + @Override + public void process(Update update) { + + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/ModeratorGroupService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorGroupService.java similarity index 67% rename from src/main/java/org/codewithoutus/tgbotusers/services/ModeratorGroupService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/ModeratorGroupService.java index 0f2eb01..81022a9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/ModeratorGroupService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorGroupService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.services; +package org.codewithoutus.tgbotusers.service; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java new file mode 100644 index 0000000..93dc0f7 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java @@ -0,0 +1,25 @@ +package org.codewithoutus.tgbotusers.service; + +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.JoinedUser; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class NotificationService { + + + + public void notifyModerators(JoinedUser joinedUser) { + createNotification(joinedUser); + sendNotification(); + } + + private void createNotification(JoinedUser joinedUser) { + + } + + private void sendNotification() { + + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java b/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java new file mode 100644 index 0000000..ad21d41 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java @@ -0,0 +1,8 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.model.Update; + +public interface TelegramEntityService { + + void process(Update update); +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java new file mode 100644 index 0000000..eea38ad --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java @@ -0,0 +1,22 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UpdateService { + + private final ChatJoinRequestService chatJoinRequestService; + private final CallbackQueryService callbackQueryService; + private final MessageService messageService; + + + public void process(Update update) { + chatJoinRequestService.process(update); + callbackQueryService.process(update); + messageService.process(update); + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/UserGroupService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserGroupService.java similarity index 66% rename from src/main/java/org/codewithoutus/tgbotusers/services/UserGroupService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/UserGroupService.java index 7a9ba98..f8535be 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/UserGroupService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserGroupService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.services; +package org.codewithoutus.tgbotusers.service; import org.springframework.stereotype.Service; From 5378ce8f5d707c4cfb990b77c9c7146a68df2296 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 8 Jul 2022 22:55:36 +0400 Subject: [PATCH 05/50] add notification logic (not finished) --- .../codewithoutus/tgbotusers/model/Chat.java | 3 + .../codewithoutus/tgbotusers/model/User.java | 2 + .../service/AdministratorService.java | 2 + .../service/CallbackQueryService.java | 4 + .../service/CongratulationService.java | 2 + .../tgbotusers/service/MessageService.java | 4 + ...Service.java => ModeratorChatService.java} | 4 +- .../service/NotificationService.java | 99 ++++++++++++++++++- ...GroupService.java => UserChatService.java} | 4 +- .../tgbotusers/service/enums/TextButton.java | 16 +++ 10 files changed, 134 insertions(+), 6 deletions(-) rename src/main/java/org/codewithoutus/tgbotusers/service/{UserGroupService.java => ModeratorChatService.java} (51%) rename src/main/java/org/codewithoutus/tgbotusers/service/{ModeratorGroupService.java => UserChatService.java} (52%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/enums/TextButton.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java index fa500ac..7df9a76 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java @@ -1,7 +1,9 @@ package org.codewithoutus.tgbotusers.model; +import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.NaturalId; import javax.persistence.Column; import javax.persistence.GeneratedValue; @@ -18,6 +20,7 @@ public class Chat { @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; + @NaturalId @Column(nullable = false, unique = true) private Long chatId; diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/User.java b/src/main/java/org/codewithoutus/tgbotusers/model/User.java index 10c162a..b4ecb33 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/User.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/User.java @@ -2,6 +2,7 @@ import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.NaturalId; import javax.persistence.Column; import javax.persistence.GeneratedValue; @@ -18,6 +19,7 @@ public class User { @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; + @NaturalId @Column(nullable = false, unique = true) Long userId; diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java b/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java index 01116e3..b3ffade 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java @@ -1,7 +1,9 @@ package org.codewithoutus.tgbotusers.service; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor public class AdministratorService { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java index d5b5565..5f7773d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java @@ -1,7 +1,11 @@ package org.codewithoutus.tgbotusers.service; import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +@Service +@RequiredArgsConstructor public class CallbackQueryService implements TelegramEntityService { @Override diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java index f2b335b..4f06cd1 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java @@ -1,7 +1,9 @@ package org.codewithoutus.tgbotusers.service; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor public class CongratulationService { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java index 43b3cd4..52ea458 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java @@ -1,7 +1,11 @@ package org.codewithoutus.tgbotusers.service; import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +@Service +@RequiredArgsConstructor public class MessageService implements TelegramEntityService { @Override diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserGroupService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java similarity index 51% rename from src/main/java/org/codewithoutus/tgbotusers/service/UserGroupService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java index f8535be..f99b31a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserGroupService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java @@ -1,7 +1,9 @@ package org.codewithoutus.tgbotusers.service; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service -public class UserGroupService { +@RequiredArgsConstructor +public class ModeratorChatService { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java index 93dc0f7..fbfd889 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java @@ -1,25 +1,116 @@ package org.codewithoutus.tgbotusers.service; +import com.google.gson.JsonObject; +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.model.request.InlineKeyboardButton; +import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import com.pengrad.telegrambot.request.GetChat; +import com.pengrad.telegrambot.request.GetChatMember; +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.response.SendResponse; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.config.GroupConfig; import org.codewithoutus.tgbotusers.model.JoinedUser; +import org.codewithoutus.tgbotusers.service.enums.TextButton; +import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Map; + @Service @RequiredArgsConstructor public class NotificationService { + private static final String NOTIFICATION_TEMPLATE = "\uD83C\uDF89 В \"%s\" группу вступил юбилейный пользователь %s (%s), %s. \n" + "Время вступления %s"; + private static final String NO_NICK = "ника нет"; + + private final GroupConfig groupConfig; + private final TelegramBot bot; public void notifyModerators(JoinedUser joinedUser) { - createNotification(joinedUser); - sendNotification(); + Map notification = createNotification(joinedUser); + sendNotification(notification, joinedUser); + } + + private Map createNotification(JoinedUser joinedUser) { + Map joiningData = getJoiningData(joinedUser); + + String text = String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), joiningData.get("memberName"), joiningData.get("nickName"), joiningData.get("number"), joiningData.get("joinTime")); + InlineKeyboardMarkup keyboard = createKeyboard(joinedUser); + + return Map.of("text", text, "keyboard", keyboard); } - private void createNotification(JoinedUser joinedUser) { + // TODO organize mailing + private void sendNotification(Map notification, JoinedUser joinedUser) { + groupConfig.getGroups(); + + var sendMessage = new SendMessage( + joinedUser.getChatId(), + (String) notification.get("text")) + .replyMarkup((InlineKeyboardMarkup) notification.get("keyboard")); + + bot.execute(sendMessage); + } + private Map getJoiningData(JoinedUser joinedUser) { + + Long chatId = joinedUser.getChatId(); + var getChat = new GetChat(chatId); + var getChatResponse = bot.execute(getChat); + String chatName = getChatResponse.chat().title(); + + Long userId = joinedUser.getUserId(); + var getChatMember = new GetChatMember(chatId, userId); + var getChatMemberResponse = bot.execute(getChatMember); + User user = getChatMemberResponse.chatMember().user(); + String firstName = user.firstName(); + String lastName = user.lastName(); + String memberName = firstName + ((lastName == null) ? "" : (" " + lastName)); + + String userName = user.username(); + String nickName = userName == null ? NO_NICK : userName; + + Integer number = joinedUser.getNumber(); + + DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT); + String joinTime = joinedUser.getJoinTime().format(formatter); + + return Map.of("chatName", chatName, "memberName", memberName, "nickName", nickName, "number", number, "joinTime", joinTime); } - private void sendNotification() { + private InlineKeyboardMarkup createKeyboard(JoinedUser joinedUser) { + + var inlineKeyboardMarkup = new InlineKeyboardMarkup(); + + InlineKeyboardButton[] buttons = new InlineKeyboardButton[]{new InlineKeyboardButton(TextButton.CONGRATULATE.getSolution()).callbackData(getCongratulateCallbackData(joinedUser)), new InlineKeyboardButton(TextButton.DECLINE.getSolution()).callbackData(getDeclineCallbackData(joinedUser))}; + inlineKeyboardMarkup.addRow(buttons); + + return inlineKeyboardMarkup; + } + private String getCongratulateCallbackData(JoinedUser joinedUser) { + JsonObject jsonObject = getJsonObject(joinedUser); + jsonObject.addProperty("command", "congratulate"); + return jsonObject.getAsString(); } + + private String getDeclineCallbackData(JoinedUser joinedUser) { + JsonObject jsonObject = getJsonObject(joinedUser); + jsonObject.addProperty("command", "decline"); + return jsonObject.getAsString(); + } + + @NotNull + private JsonObject getJsonObject(JoinedUser joinedUser) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("userId", joinedUser.getUserId()); + jsonObject.addProperty("chatId", joinedUser.getChatId()); + return jsonObject; + } + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorGroupService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java similarity index 52% rename from src/main/java/org/codewithoutus/tgbotusers/service/ModeratorGroupService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java index 81022a9..4597525 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorGroupService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java @@ -1,7 +1,9 @@ package org.codewithoutus.tgbotusers.service; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service -public class ModeratorGroupService { +@RequiredArgsConstructor +public class UserChatService { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/enums/TextButton.java b/src/main/java/org/codewithoutus/tgbotusers/service/enums/TextButton.java new file mode 100644 index 0000000..1fbf941 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/enums/TextButton.java @@ -0,0 +1,16 @@ +package org.codewithoutus.tgbotusers.service.enums; + +import lombok.Getter; + +@Getter +public enum TextButton { + + CONGRATULATE ("Поздравить \uD83E\uDD73"), + DECLINE ("Отклонить 🚫"); + + private final String solution; + + TextButton(String solution) { + this.solution = solution; + } +} From b2838c1eb91f3721753aa51b049dea2ebc923008 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Sat, 9 Jul 2022 13:21:09 +0400 Subject: [PATCH 06/50] added working properties' configuration (group properties) --- .../tgbotusers/config/AppConfig.java | 3 --- .../tgbotusers/config/GroupConfig.java | 19 ++++--------------- .../codewithoutus/tgbotusers/model/Chat.java | 1 - .../tgbotusers/service/BotService.java | 2 +- .../service/NotificationService.java | 2 +- src/main/resources/application.yml | 12 ++++++------ 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java index 2b6d751..a53dbc7 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java @@ -1,10 +1,8 @@ package org.codewithoutus.tgbotusers.config; -import com.pengrad.telegrambot.TelegramBot; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Getter @@ -18,6 +16,5 @@ public class AppConfig { private int incrementalSaves; private String botUserName; private String botToken; - } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java index 69e6576..194b65b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -2,6 +2,7 @@ import lombok.Getter; import lombok.Setter; +import org.codewithoutus.tgbotusers.model.ModeratorChat; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -10,21 +11,9 @@ @Getter @Setter @Configuration -@ConfigurationProperties(prefix = "telegram.groups") +@ConfigurationProperties(prefix = "telegram") public class GroupConfig { - private List groups; - - @Getter - @Setter - public class ModeratorGroup { - private int id; - private List userGroups; - } - - @Getter - @Setter - public class UserGroup { - private int id; - } + private List moderatorChats; + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java index 7df9a76..44ddfd4 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java @@ -1,6 +1,5 @@ package org.codewithoutus.tgbotusers.model; -import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.NaturalId; diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java index 378e183..a7a40fb 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java @@ -22,7 +22,7 @@ public class BotService { public BackendResponse start() { - if (status.equals(BotStatus.START)) { + if (status != null && status.equals(BotStatus.START)) { return new BackendResponse(false, status); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java index fbfd889..690c66d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java @@ -47,7 +47,7 @@ public void notifyModerators(JoinedUser joinedUser) { // TODO organize mailing private void sendNotification(Map notification, JoinedUser joinedUser) { - groupConfig.getGroups(); + groupConfig.getModeratorChats(); var sendMessage = new SendMessage( joinedUser.getChatId(), diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6954291..12d4439 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,14 +14,14 @@ logging: root: info telegram: -# multiplicity: 500 -# incremental-saves: 2 + multiplicity: 500 + incremental-saves: 2 long-polling-timeout: 1000 bot-user-name: ${BOT_USER_NAME} bot-token: ${BOT_TOKEN} - groups: - - moderator-group: -644481529 - user-groups: - - -606991893 + moderator-chats: + - chat-id: -644481529 + user-chats: + - chat-id: -606991893 From 6a10e1fa41a3288c6c5322a00f7c6f370fcbeb08 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Sun, 10 Jul 2022 21:15:09 +0400 Subject: [PATCH 07/50] refactored application frame, refactored model --- .../tgbotusers/config/GroupConfig.java | 11 ++- .../handler/CallbackQueryHandler.java | 18 ++++- .../handler/ChatJoinRequestHandler.java | 21 ++++-- .../tgbotusers/handler/Handler.java | 2 +- .../tgbotusers/handler/MessageHandler.java | 18 ++++- .../tgbotusers/handler/UpdateHandler.java | 25 +++++++ .../tgbotusers/handler/UpdateProcessor.java | 54 -------------- .../tgbotusers/model/Administrator.java | 17 ++++- .../codewithoutus/tgbotusers/model/Chat.java | 26 ------- .../tgbotusers/model/Congratulation.java | 9 +-- .../tgbotusers/model/ModeratorChat.java | 15 +++- .../codewithoutus/tgbotusers/model/User.java | 7 +- .../tgbotusers/model/UserChat.java | 17 ++++- .../{JoinedUser.java => UserJoining.java} | 16 ++++- .../repository/UserJoiningRepository.java | 10 +++ ...serRepository.java => UserRepository.java} | 4 +- .../service/BotInitializerService.java | 52 ++++++++++++++ .../tgbotusers/service/BotService.java | 15 ++-- .../service/CallbackQueryService.java | 7 +- .../service/ChatJoinRequestService.java | 41 +++++------ .../tgbotusers/service/JoinedUserService.java | 18 ----- .../tgbotusers/service/MessageService.java | 7 +- .../service/NotificationService.java | 72 +++++++++++-------- .../service/TelegramEntityService.java | 4 +- .../tgbotusers/service/UpdateService.java | 22 ------ .../service/UserJoiningService.java | 18 +++++ .../tgbotusers/service/UserService.java | 7 ++ .../{TextButton.java => ButtonText.java} | 5 +- src/main/resources/application.yml | 10 +-- 29 files changed, 328 insertions(+), 220 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/Chat.java rename src/main/java/org/codewithoutus/tgbotusers/model/{JoinedUser.java => UserJoining.java} (58%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java rename src/main/java/org/codewithoutus/tgbotusers/repository/{JoinedUserRepository.java => UserRepository.java} (55%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/JoinedUserService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/UserService.java rename src/main/java/org/codewithoutus/tgbotusers/service/enums/{TextButton.java => ButtonText.java} (80%) diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java index 194b65b..39f5c47 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -14,6 +14,15 @@ @ConfigurationProperties(prefix = "telegram") public class GroupConfig { - private List moderatorChats; + private List moderatorChats; + + + public record ModeratorChatProp(long id, List userChats) { + + } + + public record UserChatProp(long id) { + + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java index 4fc553a..d632163 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java @@ -1,14 +1,28 @@ package org.codewithoutus.tgbotusers.handler; import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.service.CallbackQueryService; +import org.springframework.stereotype.Component; import java.util.Collections; import java.util.Map; +@Component +@RequiredArgsConstructor public class CallbackQueryHandler implements Handler { + private final CallbackQueryService callbackQueryService; + @Override - public Map handle(Update update) { - return Collections.emptyMap(); + public boolean handle(Update update) { + + // TODO implement logic + + Map callbackQueryData = Map.of(); + + callbackQueryService.process(callbackQueryData); + + return true; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java index 999a519..735997a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java @@ -5,9 +5,9 @@ import com.pengrad.telegrambot.model.User; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.service.BotExecutorService; +import org.codewithoutus.tgbotusers.service.ChatJoinRequestService; import org.springframework.stereotype.Component; -import java.util.Collections; import java.util.Map; @Component @@ -17,33 +17,40 @@ public class ChatJoinRequestHandler implements Handler { private static final int INCREMENTAL_SAVES = 2; // TODO replace on config private final BotExecutorService botExecutorService; + private final ChatJoinRequestService chatJoinRequestService; @Override - public Map handle(Update update) { + public boolean handle(Update update) { ChatJoinRequest chatJoinRequest = update.chatJoinRequest(); if (chatJoinRequest == null) { - return Collections.emptyMap(); + return false; } + // TODO проверить из чата модераторов + long chatId = chatJoinRequest.chat().id(); int count = botExecutorService.getCount(chatId); if (count % MULTIPLICITY > INCREMENTAL_SAVES) { - return Collections.emptyMap(); + return false; } User user = chatJoinRequest.from(); if (user.isBot()) { - return Collections.emptyMap(); + return false; } int date = chatJoinRequest.date(); - - return Map.of( + + Map userData = Map.of( "userId", user.id(), "chatId", chatId, "count", count, "date", date); + + chatJoinRequestService.process(userData); + + return true; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java index 79c5353..cc75264 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java @@ -5,5 +5,5 @@ import java.util.Map; public interface Handler { - Map handle(Update update); + boolean handle(Update update); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java index 12b4091..3e3cd1c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java @@ -1,14 +1,28 @@ package org.codewithoutus.tgbotusers.handler; import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.service.MessageService; +import org.springframework.stereotype.Component; import java.util.Collections; import java.util.Map; +@Component +@RequiredArgsConstructor public class MessageHandler implements Handler { + private final MessageService messageService; + @Override - public Map handle(Update update) { - return Collections.emptyMap(); + public boolean handle(Update update) { + + // TODO implement logic + + Map messageData = Map.of(); + + messageService.process(messageData); + + return true; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java new file mode 100644 index 0000000..77f7631 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java @@ -0,0 +1,25 @@ +package org.codewithoutus.tgbotusers.handler; + +import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class UpdateHandler implements Handler { + + private final ChatJoinRequestHandler chatJoinRequestHandler; + private final CallbackQueryHandler callbackQueryHandler; + private final MessageHandler messageHandler; + + @Override + public boolean handle(Update update) { + boolean chatJoinRequestHandlerResult = chatJoinRequestHandler.handle(update); + boolean callbackHandlerResult = callbackQueryHandler.handle(update); + boolean messageHandlerResult = messageHandler.handle(update); + + return chatJoinRequestHandlerResult || callbackHandlerResult || messageHandlerResult; + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java deleted file mode 100644 index a40fb7b..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateProcessor.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -import com.pengrad.telegrambot.model.CallbackQuery; -import com.pengrad.telegrambot.model.ChatJoinRequest; -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.MessageEntity; -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Deprecated -@RequiredArgsConstructor -public class UpdateProcessor { - - private final ChatJoinRequestHandler chatJoinRequestHandler; - private final CallbackQueryHandler callbackQueryHandler; - private final MessageHandler messageHandler; - - private Update currentUpdate; - - public void updateProcess(Update update) { - currentUpdate = update; - - ChatJoinRequest chatJoinRequest = currentUpdate.chatJoinRequest(); - if (chatJoinRequest != null) { - handleChatJoinRequest(chatJoinRequest); - } - - CallbackQuery callbackQuery = currentUpdate.callbackQuery(); - if (callbackQuery != null) { - handleCallback(callbackQuery); - } - - Message message = currentUpdate.message(); - if (message != null) { - handleCommand(message); - } - } - - private void handleChatJoinRequest(ChatJoinRequest chatJoinRequest) { - - } - - private void handleCallback(CallbackQuery callbackQuery) { - - } - - private void handleCommand(Message message) { - MessageEntity[] entities = message.entities(); - if (entities != null && entities.length > 0) { - - } - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java index 814b22f..e7d1f6f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java @@ -2,12 +2,25 @@ import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.NaturalId; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; @Entity @Getter @Setter -public class Administrator extends User { - +public class Administrator { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @NaturalId + @Column(nullable = false) + Long userId; + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java b/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java deleted file mode 100644 index 44ddfd4..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Chat.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.codewithoutus.tgbotusers.model; - -import lombok.Getter; -import lombok.Setter; -import org.hibernate.annotations.NaturalId; - -import javax.persistence.Column; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.MappedSuperclass; - -@MappedSuperclass -@Getter -@Setter -public class Chat { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @NaturalId - @Column(nullable = false, unique = true) - private Long chatId; - -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java index 73bc330..2600203 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java @@ -9,9 +9,7 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; import javax.persistence.OneToOne; -import javax.persistence.Table; @Entity @Getter @@ -23,7 +21,10 @@ public class Congratulation { private Integer id; @OneToOne(optional = false, orphanRemoval = true) - @JoinColumn(name = "joined_user_id", nullable = false, unique = true) - private JoinedUser joinedUser; + @JoinColumn(name = "user_joining_id", nullable = false) + private UserJoining userJoining; + + @Column(nullable = false) + private Integer number; } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java index d8524fc..37485fd 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java @@ -2,8 +2,13 @@ import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.NaturalId; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; @@ -12,7 +17,15 @@ @Entity @Getter @Setter -public class ModeratorChat extends Chat { +public class ModeratorChat { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @NaturalId + @Column(nullable = false, unique = true) + private Long chatId; @ManyToMany @JoinTable(name = "moderators2users", diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/User.java b/src/main/java/org/codewithoutus/tgbotusers/model/User.java index b4ecb33..72ec5db 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/User.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/User.java @@ -5,12 +5,15 @@ import org.hibernate.annotations.NaturalId; import javax.persistence.Column; +import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; +import javax.persistence.Table; -@MappedSuperclass +@Entity +@Table(name = "_user") @Getter @Setter public class User { @@ -20,7 +23,7 @@ public class User { private Integer id; @NaturalId - @Column(nullable = false, unique = true) + @Column(nullable = false) Long userId; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java index f61dc91..41e6567 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java @@ -2,16 +2,31 @@ import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.NaturalId; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; import javax.persistence.ManyToMany; +import javax.persistence.OneToMany; import java.util.List; @Entity @Getter @Setter -public class UserChat extends Chat { +public class UserChat { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @NaturalId + @Column(nullable = false, unique = true) + private Long chatId; @ManyToMany(mappedBy = "userChats") private List moderatorChats; + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java similarity index 58% rename from src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java rename to src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java index 246d5d1..d1b7431 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/JoinedUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java @@ -5,13 +5,22 @@ import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.Table; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; import java.time.LocalDateTime; @Entity @Getter @Setter -public class JoinedUser extends User { +public class UserJoining { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(nullable = false) + private Long userId; @Column(nullable = false) private Long chatId; @@ -21,4 +30,5 @@ public class JoinedUser extends User { @Column(nullable = false) private LocalDateTime joinTime; -} \ No newline at end of file + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java new file mode 100644 index 0000000..fcb10a7 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java @@ -0,0 +1,10 @@ +package org.codewithoutus.tgbotusers.repository; + +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserJoiningRepository extends CrudRepository { +} + diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/JoinedUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java similarity index 55% rename from src/main/java/org/codewithoutus/tgbotusers/repository/JoinedUserRepository.java rename to src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java index ce83753..bfdf1ed 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/JoinedUserRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java @@ -1,9 +1,9 @@ package org.codewithoutus.tgbotusers.repository; -import org.codewithoutus.tgbotusers.model.JoinedUser; +import org.codewithoutus.tgbotusers.model.User; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository -public interface JoinedUserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository { } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java new file mode 100644 index 0000000..9e14994 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java @@ -0,0 +1,52 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.ChatAdministratorRights; +import com.pengrad.telegrambot.request.GetMyDefaultAdministratorRights; +import com.pengrad.telegrambot.request.SetMyDefaultAdministratorRights; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.config.GroupConfig; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BotInitializerService { + + private final GroupConfig groupConfig; + private final TelegramBot bot; + + + public void initialiseBot() { + initialiseAdministratorRights(); + initialiseGroups(); + } + + private void initialiseAdministratorRights() { + if (!administratorRightsIsCorrect()) { + setAdministratorRights(); + } + } + + private boolean administratorRightsIsCorrect() { + var getRights = new GetMyDefaultAdministratorRights(); + var response = bot.execute(getRights); + var rights = response.result(); + return rights.canInviteUsers(); + } + + private void setAdministratorRights() { + var chatAdministratorRights = new ChatAdministratorRights(); // TODO add isAnonymous(boolean) + chatAdministratorRights.canInviteUsers(true); + + var setMyDefaultAdministratorRights = new SetMyDefaultAdministratorRights(); + setMyDefaultAdministratorRights.rights(chatAdministratorRights); + bot.execute(setMyDefaultAdministratorRights); + } + + + private void initialiseGroups() { + + } + + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java index a7a40fb..4fb78f0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java @@ -5,9 +5,9 @@ import com.pengrad.telegrambot.request.GetUpdates; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.config.AppConfig; -import org.codewithoutus.tgbotusers.config.GroupConfig; import org.codewithoutus.tgbotusers.dto.BackendResponse; import org.codewithoutus.tgbotusers.dto.enums.BotStatus; +import org.codewithoutus.tgbotusers.handler.UpdateHandler; import org.springframework.stereotype.Service; @Service @@ -15,9 +15,8 @@ public class BotService { private final AppConfig appConfig; - private final GroupConfig groupConfig; private final TelegramBot bot; - private final UpdateService updateService; + private final UpdateHandler updateHandler; private BotStatus status; @@ -28,17 +27,23 @@ public BackendResponse start() { GetUpdates getUpdates = new GetUpdates().timeout(appConfig.getLongPollingTimeout()); bot.setUpdatesListener(updates -> { - updates.forEach(updateService::process); + updates.forEach(updateHandler::handle); return UpdatesListener.CONFIRMED_UPDATES_ALL; }, getUpdates); status = BotStatus.START; + return new BackendResponse(true, status); } public BackendResponse stop() { - status = BotStatus.STOP; + if (status == null || status.equals(BotStatus.STOP)) { + return new BackendResponse(false, status); + } + bot.removeGetUpdatesListener(); + status = BotStatus.STOP; + return new BackendResponse(true, status); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java index 5f7773d..e69911e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java @@ -1,15 +1,18 @@ package org.codewithoutus.tgbotusers.service; -import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.Map; + @Service @RequiredArgsConstructor public class CallbackQueryService implements TelegramEntityService { @Override - public void process(Update update) { + public void process(Map callbackQueryData) { + // TODO implement logic + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java index b1b7c94..bda1274 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java @@ -1,10 +1,7 @@ package org.codewithoutus.tgbotusers.service; -import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.handler.ChatJoinRequestHandler; -import org.codewithoutus.tgbotusers.model.JoinedUser; -import org.codewithoutus.tgbotusers.repository.JoinedUserRepository; +import org.codewithoutus.tgbotusers.model.UserJoining; import org.springframework.stereotype.Service; import java.time.Instant; @@ -15,35 +12,29 @@ @RequiredArgsConstructor public class ChatJoinRequestService implements TelegramEntityService { - private final ChatJoinRequestHandler chatJoinRequestHandler; - private final JoinedUserService joinedUserService; + private final UserJoiningService userJoiningService; private final NotificationService notificationService; - - @Override - public void process(Update update) { - Map userData = chatJoinRequestHandler.handle(update); - if (userData.isEmpty()) { - return; - } + + public void process(Map userData) { + UserJoining userJoining = buildUserJoining(userData); - JoinedUser joinedUser = buildJoinedUser(userData); - joinedUserService.save(joinedUser); + userJoiningService.save(userJoining); - notificationService.notifyModerators(joinedUser); + notificationService.notifyModerators(userJoining); } - private JoinedUser buildJoinedUser(Map userData) { - - JoinedUser joinedUser = new JoinedUser(); - - joinedUser.setUserId((Long) userData.get("userId")); - joinedUser.setChatId((Long) userData.get("chatId")); - joinedUser.setNumber((Integer) userData.get("count")); + private UserJoining buildUserJoining(Map userData) { + + UserJoining userJoining = new UserJoining(); + + userJoining.setUserId((Long) userData.get("userId")); + userJoining.setChatId((Long) userData.get("chatId")); + userJoining.setNumber((Integer) userData.get("count")); Instant time = Instant.ofEpochSecond((Long) userData.get("data")); - joinedUser.setJoinTime(LocalDateTime.from(time)); + userJoining.setJoinTime(LocalDateTime.from(time)); - return joinedUser; + return userJoining; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/JoinedUserService.java b/src/main/java/org/codewithoutus/tgbotusers/service/JoinedUserService.java deleted file mode 100644 index 796f139..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/JoinedUserService.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.JoinedUser; -import org.codewithoutus.tgbotusers.repository.JoinedUserRepository; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class JoinedUserService { - - private final JoinedUserRepository joinedUserRepository; - - public void save(JoinedUser joinedUser) { - joinedUserRepository.save(joinedUser); - } - -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java index 52ea458..4a7c103 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java @@ -1,15 +1,18 @@ package org.codewithoutus.tgbotusers.service; -import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.Map; + @Service @RequiredArgsConstructor public class MessageService implements TelegramEntityService { @Override - public void process(Update update) { + public void process(Map messageData) { + // TODO implement logic + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java index 690c66d..3fe6be0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java @@ -8,11 +8,10 @@ import com.pengrad.telegrambot.request.GetChat; import com.pengrad.telegrambot.request.GetChatMember; import com.pengrad.telegrambot.request.SendMessage; -import com.pengrad.telegrambot.response.SendResponse; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.config.GroupConfig; -import org.codewithoutus.tgbotusers.model.JoinedUser; -import org.codewithoutus.tgbotusers.service.enums.TextButton; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.service.enums.ButtonText; import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; @@ -24,92 +23,103 @@ @RequiredArgsConstructor public class NotificationService { - private static final String NOTIFICATION_TEMPLATE = "\uD83C\uDF89 В \"%s\" группу вступил юбилейный пользователь %s (%s), %s. \n" + "Время вступления %s"; + private static final String NOTIFICATION_TEMPLATE = + "\uD83C\uDF89 В \"%s\" группу вступил юбилейный пользователь %s (%s), %s. \n" + "Время вступления %s"; private static final String NO_NICK = "ника нет"; private final GroupConfig groupConfig; private final TelegramBot bot; - public void notifyModerators(JoinedUser joinedUser) { - Map notification = createNotification(joinedUser); - sendNotification(notification, joinedUser); + public void notifyModerators(UserJoining userJoining) { + Map notification = createNotification(userJoining); + sendNotification(notification, userJoining); } - private Map createNotification(JoinedUser joinedUser) { - Map joiningData = getJoiningData(joinedUser); + private Map createNotification(UserJoining userJoining) { + Map joiningData = getJoiningData(userJoining); - String text = String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), joiningData.get("memberName"), joiningData.get("nickName"), joiningData.get("number"), joiningData.get("joinTime")); - InlineKeyboardMarkup keyboard = createKeyboard(joinedUser); + String text = String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), + joiningData.get("userName"), joiningData.get("nickName"), + joiningData.get("number"), joiningData.get("joinTime")); + InlineKeyboardMarkup keyboard = createKeyboard(userJoining); return Map.of("text", text, "keyboard", keyboard); } // TODO organize mailing - private void sendNotification(Map notification, JoinedUser joinedUser) { + private void sendNotification(Map notification, UserJoining userJoining) { groupConfig.getModeratorChats(); var sendMessage = new SendMessage( - joinedUser.getChatId(), + userJoining.getChatId(), (String) notification.get("text")) .replyMarkup((InlineKeyboardMarkup) notification.get("keyboard")); bot.execute(sendMessage); } - private Map getJoiningData(JoinedUser joinedUser) { + private Map getJoiningData(UserJoining userJoining) { - Long chatId = joinedUser.getChatId(); + Long chatId = userJoining.getChatId(); var getChat = new GetChat(chatId); var getChatResponse = bot.execute(getChat); String chatName = getChatResponse.chat().title(); - Long userId = joinedUser.getUserId(); + Long userId = userJoining.getUserId(); var getChatMember = new GetChatMember(chatId, userId); var getChatMemberResponse = bot.execute(getChatMember); User user = getChatMemberResponse.chatMember().user(); String firstName = user.firstName(); String lastName = user.lastName(); - String memberName = firstName + ((lastName == null) ? "" : (" " + lastName)); + String userName = firstName + ((lastName == null) ? "" : (" " + lastName)); - String userName = user.username(); - String nickName = userName == null ? NO_NICK : userName; + String nickName = user.username(); + nickName = nickName == null ? NO_NICK : nickName; - Integer number = joinedUser.getNumber(); + Integer number = userJoining.getNumber(); DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT); - String joinTime = joinedUser.getJoinTime().format(formatter); + String joinTime = userJoining.getJoinTime().format(formatter); - return Map.of("chatName", chatName, "memberName", memberName, "nickName", nickName, "number", number, "joinTime", joinTime); + return Map.of("chatName", chatName, + "userName", userName, + "nickName", nickName, + "number", number, + "joinTime", joinTime); } - private InlineKeyboardMarkup createKeyboard(JoinedUser joinedUser) { + private InlineKeyboardMarkup createKeyboard(UserJoining userJoining) { var inlineKeyboardMarkup = new InlineKeyboardMarkup(); - InlineKeyboardButton[] buttons = new InlineKeyboardButton[]{new InlineKeyboardButton(TextButton.CONGRATULATE.getSolution()).callbackData(getCongratulateCallbackData(joinedUser)), new InlineKeyboardButton(TextButton.DECLINE.getSolution()).callbackData(getDeclineCallbackData(joinedUser))}; + InlineKeyboardButton[] buttons = new InlineKeyboardButton[]{ + new InlineKeyboardButton(ButtonText.CONGRATULATE.getSolution()) + .callbackData(getCongratulateCallbackData(userJoining)), + new InlineKeyboardButton(ButtonText.DECLINE.getSolution()) + .callbackData(getDeclineCallbackData(userJoining))}; inlineKeyboardMarkup.addRow(buttons); return inlineKeyboardMarkup; } - private String getCongratulateCallbackData(JoinedUser joinedUser) { - JsonObject jsonObject = getJsonObject(joinedUser); + private String getCongratulateCallbackData(UserJoining userJoining) { + JsonObject jsonObject = getJsonObject(userJoining); jsonObject.addProperty("command", "congratulate"); return jsonObject.getAsString(); } - private String getDeclineCallbackData(JoinedUser joinedUser) { - JsonObject jsonObject = getJsonObject(joinedUser); + private String getDeclineCallbackData(UserJoining userJoining) { + JsonObject jsonObject = getJsonObject(userJoining); jsonObject.addProperty("command", "decline"); return jsonObject.getAsString(); } @NotNull - private JsonObject getJsonObject(JoinedUser joinedUser) { + private JsonObject getJsonObject(UserJoining userJoining) { JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("userId", joinedUser.getUserId()); - jsonObject.addProperty("chatId", joinedUser.getChatId()); + jsonObject.addProperty("userId", userJoining.getUserId()); + jsonObject.addProperty("chatId", userJoining.getChatId()); return jsonObject; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java b/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java index ad21d41..6e142f5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java @@ -2,7 +2,9 @@ import com.pengrad.telegrambot.model.Update; +import java.util.Map; + public interface TelegramEntityService { - void process(Update update); + void process(Map data); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java deleted file mode 100644 index eea38ad..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UpdateService.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class UpdateService { - - private final ChatJoinRequestService chatJoinRequestService; - private final CallbackQueryService callbackQueryService; - private final MessageService messageService; - - - public void process(Update update) { - chatJoinRequestService.process(update); - callbackQueryService.process(update); - messageService.process(update); - } - -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java new file mode 100644 index 0000000..b5816ec --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java @@ -0,0 +1,18 @@ +package org.codewithoutus.tgbotusers.service; + +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.repository.UserJoiningRepository; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UserJoiningService { + + private final UserJoiningRepository userJoiningRepository; + + public void save(UserJoining userJoining) { + userJoiningRepository.save(userJoining); + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserService.java new file mode 100644 index 0000000..d55a1a8 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserService.java @@ -0,0 +1,7 @@ +package org.codewithoutus.tgbotusers.service; + +import org.springframework.stereotype.Service; + +@Service +public class UserService { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/enums/TextButton.java b/src/main/java/org/codewithoutus/tgbotusers/service/enums/ButtonText.java similarity index 80% rename from src/main/java/org/codewithoutus/tgbotusers/service/enums/TextButton.java rename to src/main/java/org/codewithoutus/tgbotusers/service/enums/ButtonText.java index 1fbf941..f39cdfc 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/enums/TextButton.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/enums/ButtonText.java @@ -3,14 +3,15 @@ import lombok.Getter; @Getter -public enum TextButton { +public enum ButtonText { CONGRATULATE ("Поздравить \uD83E\uDD73"), DECLINE ("Отклонить 🚫"); private final String solution; - TextButton(String solution) { + ButtonText(String solution) { this.solution = solution; } + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 12d4439..2a9e925 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -21,7 +21,9 @@ telegram: bot-user-name: ${BOT_USER_NAME} bot-token: ${BOT_TOKEN} - moderator-chats: - - chat-id: -644481529 - user-chats: - - chat-id: -606991893 + groups: + + moderator-chats: + - chat-id: -1001710185537 + user-chats: + - chat-id: -1001793694006 From ce8174e1666313728458639225f1f207fd384103 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Mon, 11 Jul 2022 13:21:43 +0700 Subject: [PATCH 08/50] create default properties and class Config --- .../tgbotusers/config/BotConfig.java | 23 +++++++++ .../tgbotusers/config/NotificationConfig.java | 51 +++++++++++++++++++ src/main/resources/application-bot.yml | 27 ++++++++++ src/main/resources/application-main.yml | 20 ++++++++ 4 files changed, 121 insertions(+) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java create mode 100644 src/main/resources/application-bot.yml create mode 100644 src/main/resources/application-main.yml diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java new file mode 100644 index 0000000..7cfbc64 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java @@ -0,0 +1,23 @@ +package org.codewithoutus.tgbotusers.config; + +import lombok.Getter; +import lombok.Setter; +import org.codewithoutus.tgbotusers.model.Group; +import org.codewithoutus.tgbotusers.model.User; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Getter +@Setter +@Component +@Configuration +@ConfigurationProperties(prefix = "bot-setting") +public class BotConfig { + + public Map listModerGroups; + public Map listUserGroups; + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java new file mode 100644 index 0000000..795e53e --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java @@ -0,0 +1,51 @@ +package org.codewithoutus.tgbotusers.config; + + +import lombok.Getter; +import lombok.Setter; +import org.codewithoutus.tgbotusers.model.Group; +import org.codewithoutus.tgbotusers.model.User; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +@Getter +@Setter +@Component +@Configuration +@ConfigurationProperties(prefix = "bot-setting.message-templates") +public class NotificationConfig { + +/*Default properties (specified by setting SpringApplication.setDefaultProperties). + создать дефолтный проперти дял юсера,если пустой то тогда как нужно +@ConfigurationProperties это не только про загрузку свойств из application.properties файла. + Возможности Spring Boot Configuration гораздо шире — поддерживается 17 (!) + разных источников свойств в строгом приоритете. Можно определить дефолт в application.properties + и перекрыть его через переменную окружения, JVM properties, профиль, тестовые свойства и т.п. + Что дает очень мощные возможности для переконфигурирования приложения в нужном окружении + и сильно упрощает конфигурацию. + */ + +// join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 +// join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} +// join-user-info: 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления} +// final String format = "%-40s %s%n"; + + + private String JOIN_CONGRATULATION="\uD83C\uDF89Поздравляю,%s, как же удачно попали в нужное время и в нужное время!%nВы %d участник коммьюнити.%nВас ждут плюшки и печенюшки!\uD83C\uDF89"; + + private final String JOIN_ALERT="join-congratulation"; + private final String JOIN_USER_INFO="join-congratulation"; + + public Map listModerGroups; + public Map listUserGroups; + public List anniversaryNumbers; + public String userName; + + public String pic= " ";//"\uD83C\uDF89";//🎉 + + +} diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml new file mode 100644 index 0000000..3316fcf --- /dev/null +++ b/src/main/resources/application-bot.yml @@ -0,0 +1,27 @@ +bot-setting: + groups-settings: + rewrite-database-saved-settings: false + moderator-groups: + - id: 11111 + user-groups: + - 222 + - 333 + - 444 + - id: 22222 + user-groups: + - 444 + - 555 + - 666 + anniversary-numbers: + - 100 + - 500 + - 777 + - 1000 + - 5000 + - 10000 + + message-templates: + join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 + join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} + join-user-info: 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления} + diff --git a/src/main/resources/application-main.yml b/src/main/resources/application-main.yml new file mode 100644 index 0000000..f77f0ab --- /dev/null +++ b/src/main/resources/application-main.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: ${DATASOURCE_URL} + username: ${DATASOURCE_USER} + password: ${DATASOURCE_PASS} + hikari: + maximum-pool-size: 2 + jpa: + hibernate: + ddl-auto: update + +logging: + level: + root: info + +bot-settings: + bot-user-name: ${BOT_USER_NAME} + bot-token: ${BOT_TOKEN} + long-polling-timeout: 1000 + From 3acd82e0308bd220639e8e6486e1a618207206b3 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Mon, 11 Jul 2022 18:18:38 +0700 Subject: [PATCH 09/50] edit prop --- .../tgbotusers/config/BotConfig.java | 7 ++++--- .../tgbotusers/config/GroupConfig.java | 17 ++++++++++++----- .../tgbotusers/config/NotificationConfig.java | 16 +++++++--------- src/main/resources/application-bot.yml | 4 ++++ src/main/resources/application-main.yml | 7 ++++--- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java index 7cfbc64..24be577 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java @@ -14,10 +14,11 @@ @Setter @Component @Configuration -@ConfigurationProperties(prefix = "bot-setting") +@ConfigurationProperties(prefix = "bot-setting.bot-setting") public class BotConfig { - public Map listModerGroups; - public Map listUserGroups; + public int longPollingTimeout; + public String botToken; + public String botName; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java index e63f4e5..5adda13 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -7,19 +7,26 @@ import org.springframework.stereotype.Component; import java.util.List; +import java.util.Map; @Getter @Setter @Component @Configuration -@ConfigurationProperties(prefix = "telegram.groups") +@ConfigurationProperties(prefix = "bot-setting.groups-settings") public class GroupConfig { - private List groups; - - + + public List anniversaryNumbers; + private Map> moderatorGroups; + private Map> userGroups; + + + private List groups; // 24-28 строки были тут public record ModeratorGroup(int id, List userGroups) { } - public record UserGroup(int id) { } + + } + diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java index 795e53e..aec66d9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java @@ -35,17 +35,15 @@ public class NotificationConfig { // final String format = "%-40s %s%n"; - private String JOIN_CONGRATULATION="\uD83C\uDF89Поздравляю,%s, как же удачно попали в нужное время и в нужное время!%nВы %d участник коммьюнити.%nВас ждут плюшки и печенюшки!\uD83C\uDF89"; + private String JOIN_CONGRATULATION; + private String JOIN_ALERT; + private String JOIN_USER_INFO; - private final String JOIN_ALERT="join-congratulation"; - private final String JOIN_USER_INFO="join-congratulation"; +// public List anniversaryNumbers; +// public String userName; +// +// public String PATCH_IMG;//"\uD83C\uDF89";//🎉 - public Map listModerGroups; - public Map listUserGroups; - public List anniversaryNumbers; - public String userName; - - public String pic= " ";//"\uD83C\uDF89";//🎉 } diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 3316fcf..2b05c28 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,4 +1,5 @@ bot-setting: + groups-settings: rewrite-database-saved-settings: false moderator-groups: @@ -20,6 +21,9 @@ bot-setting: - 5000 - 10000 + + + message-templates: join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} diff --git a/src/main/resources/application-main.yml b/src/main/resources/application-main.yml index f77f0ab..27d297a 100644 --- a/src/main/resources/application-main.yml +++ b/src/main/resources/application-main.yml @@ -14,7 +14,8 @@ logging: root: info bot-settings: - bot-user-name: ${BOT_USER_NAME} - bot-token: ${BOT_TOKEN} - long-polling-timeout: 1000 + bot-setting: + bot-user-name: ${BOT_USER_NAME} + bot-token: ${BOT_TOKEN} + long-polling-timeout: 1000 From 8c0c75028f12a053056dcd5574934cea5b838372 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Mon, 11 Jul 2022 19:01:09 +0700 Subject: [PATCH 10/50] edit prop.rev2 --- .../codewithoutus/tgbotusers/config/BotConfig.java | 4 +++- .../codewithoutus/tgbotusers/config/GroupConfig.java | 4 +++- .../tgbotusers/config/NotificationConfig.java | 10 ++++++---- .../tgbotusers/controller/BotController.java | 12 ++++++++++++ src/main/resources/application-bot.yml | 6 ++---- src/main/resources/application-main.yml | 1 - 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java index 24be577..6b14752 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java @@ -2,6 +2,7 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; import org.codewithoutus.tgbotusers.model.Group; import org.codewithoutus.tgbotusers.model.User; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -10,11 +11,12 @@ import java.util.Map; +@ToString @Getter @Setter @Component @Configuration -@ConfigurationProperties(prefix = "bot-setting.bot-setting") +@ConfigurationProperties(prefix = "bot-settings") public class BotConfig { public int longPollingTimeout; diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java index 5adda13..e303e2c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -2,6 +2,7 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; @@ -9,11 +10,12 @@ import java.util.List; import java.util.Map; +@ToString @Getter @Setter @Component @Configuration -@ConfigurationProperties(prefix = "bot-setting.groups-settings") +@ConfigurationProperties(prefix = "groups-settings") public class GroupConfig { public List anniversaryNumbers; diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java index aec66d9..6cfa115 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java @@ -3,6 +3,7 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; import org.codewithoutus.tgbotusers.model.Group; import org.codewithoutus.tgbotusers.model.User; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -12,11 +13,12 @@ import java.util.List; import java.util.Map; +@ToString @Getter @Setter @Component @Configuration -@ConfigurationProperties(prefix = "bot-setting.message-templates") +@ConfigurationProperties(prefix = "message-templates") public class NotificationConfig { /*Default properties (specified by setting SpringApplication.setDefaultProperties). @@ -35,9 +37,9 @@ public class NotificationConfig { // final String format = "%-40s %s%n"; - private String JOIN_CONGRATULATION; - private String JOIN_ALERT; - private String JOIN_USER_INFO; + private String join_congratulation; + private String join_alert; + private String join_user_info; // public List anniversaryNumbers; // public String userName; diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index dc6ebb0..20d042f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -1,6 +1,9 @@ package org.codewithoutus.tgbotusers.controller; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.config.BotConfig; +import org.codewithoutus.tgbotusers.config.GroupConfig; +import org.codewithoutus.tgbotusers.config.NotificationConfig; import org.codewithoutus.tgbotusers.dto.BackendResponse; import org.codewithoutus.tgbotusers.services.BotService; import org.springframework.web.bind.annotation.GetMapping; @@ -12,6 +15,9 @@ @RequestMapping("/backend") @RequiredArgsConstructor public class BotController { + private final NotificationConfig notificationConfig; + private final GroupConfig groupConfig; + private final BotConfig botConfig; private final BotService botService; @@ -29,5 +35,11 @@ private BackendResponse stopBotBackend() { private BackendResponse sendMessage(@RequestParam String message) { return botService.sendMessage(message); } + + @GetMapping("/props") + private String getProps(){ + return notificationConfig+"\n"+groupConfig+"\n"+botConfig+"\n"; + + } } diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 2b05c28..813a932 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,6 +1,4 @@ -bot-setting: - - groups-settings: +groups-settings: rewrite-database-saved-settings: false moderator-groups: - id: 11111 @@ -24,7 +22,7 @@ bot-setting: - message-templates: +message-templates: join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} join-user-info: 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления} diff --git a/src/main/resources/application-main.yml b/src/main/resources/application-main.yml index 27d297a..3323dbc 100644 --- a/src/main/resources/application-main.yml +++ b/src/main/resources/application-main.yml @@ -14,7 +14,6 @@ logging: root: info bot-settings: - bot-setting: bot-user-name: ${BOT_USER_NAME} bot-token: ${BOT_TOKEN} long-polling-timeout: 1000 From 82c0793bf70c9723b12e05269009de509ed150fa Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Mon, 11 Jul 2022 19:12:01 +0700 Subject: [PATCH 11/50] edit prop.rev3 --- src/main/resources/application-bot.yml | 44 ++++++++++++------------- src/main/resources/application-main.yml | 6 ++-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 813a932..581613a 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,29 +1,29 @@ groups-settings: - rewrite-database-saved-settings: false - moderator-groups: - - id: 11111 - user-groups: - - 222 - - 333 - - 444 - - id: 22222 - user-groups: - - 444 - - 555 - - 666 - anniversary-numbers: - - 100 - - 500 - - 777 - - 1000 - - 5000 - - 10000 + rewrite-database-saved-settings: false + moderator-groups: + - id: 11111 + user-groups: + - 222 + - 333 + - 444 + - id: 22222 + user-groups: + - 444 + - 555 + - 666 + anniversary-numbers: + - 100 + - 500 + - 777 + - 1000 + - 5000 + - 10000 message-templates: - join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 - join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} - join-user-info: 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления} + join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 + join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} + join-user-info: 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления} diff --git a/src/main/resources/application-main.yml b/src/main/resources/application-main.yml index 3323dbc..f77f0ab 100644 --- a/src/main/resources/application-main.yml +++ b/src/main/resources/application-main.yml @@ -14,7 +14,7 @@ logging: root: info bot-settings: - bot-user-name: ${BOT_USER_NAME} - bot-token: ${BOT_TOKEN} - long-polling-timeout: 1000 + bot-user-name: ${BOT_USER_NAME} + bot-token: ${BOT_TOKEN} + long-polling-timeout: 1000 From 6169d9987962bfe40450cc29476ee2bbe55b30df Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Mon, 11 Jul 2022 19:51:50 +0700 Subject: [PATCH 12/50] edit prop.rev4 --- src/main/resources/application-bot.yml | 47 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 581613a..d4123fa 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,16 +1,16 @@ groups-settings: rewrite-database-saved-settings: false moderator-groups: - - id: 11111 - user-groups: - - 222 - - 333 - - 444 + - id: 11111 + user-groups: + - 222 + - 333 + - 444 - id: 22222 - user-groups: - - 444 - - 555 - - 666 + user-groups: + - 444 + - 555 + - 666 anniversary-numbers: - 100 - 500 @@ -18,12 +18,33 @@ groups-settings: - 1000 - 5000 - 10000 +#groups-settings: +# rewrite-database-saved-settings: false +# moderator-groups: +# - id: 11111 +# - user-groups: +# - 333 +# - id: 33333 +# - user-groups: +# - 444 +# - 555 +# - 666 +# user-groups: +# - 222 +# - 333 +# anniversary-numbers: +# - 100 +# - 500 +# - 777 +# - 1000 +# - 5000 +# - 10000 -message-templates: - join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 - join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} - join-user-info: 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления} +#message-templates: +# join-congratulation: "🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" +# join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления}" +# join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления}" From 1ff383999f60f5215e3238b3f76e4cfa59ed5a09 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Mon, 11 Jul 2022 23:31:20 +0700 Subject: [PATCH 13/50] edit prop.rev5.0 --- .../tgbotusers/TgBotUsersApplication.java | 1 + .../tgbotusers/config/BotConfig.java | 2 +- .../tgbotusers/config/GroupConfig.java | 12 ++++----- .../tgbotusers/config/NotificationConfig.java | 6 ++--- src/main/resources/application-bot.yml | 26 +++++++++---------- src/main/resources/application-main.yml | 26 +++++++++---------- src/main/resources/application.yml | 26 +++++++++++-------- 7 files changed, 51 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java index 9b6617e..3ddb1d7 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java +++ b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java @@ -3,6 +3,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; + @SpringBootApplication public class TgBotUsersApplication { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java index 6b14752..9d249dd 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java @@ -21,6 +21,6 @@ public class BotConfig { public int longPollingTimeout; public String botToken; - public String botName; + public String botUserName; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java index e303e2c..f42a0b0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java @@ -14,7 +14,7 @@ @Getter @Setter @Component -@Configuration +//@Configuration @ConfigurationProperties(prefix = "groups-settings") public class GroupConfig { @@ -23,11 +23,11 @@ public class GroupConfig { private Map> userGroups; - private List groups; // 24-28 строки были тут - public record ModeratorGroup(int id, List userGroups) { - } - public record UserGroup(int id) { - } +// private List groups; // 24-28 строки были тут +// public record ModeratorGroup(int id, List userGroups) { +// } +// public record UserGroup(int id) { +// } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java index 6cfa115..d1c7874 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java @@ -37,9 +37,9 @@ public class NotificationConfig { // final String format = "%-40s %s%n"; - private String join_congratulation; - private String join_alert; - private String join_user_info; + private String joinCongratulation; + private String joinAlert; + private String joinUserInfo; // public List anniversaryNumbers; // public String userName; diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index d4123fa..569fb3c 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,16 +1,14 @@ groups-settings: rewrite-database-saved-settings: false moderator-groups: - - id: 11111 - user-groups: - - 222 - - 333 - - 444 - - id: 22222 - user-groups: - - 444 - - 555 - - 666 + 11111: + - 222 + - 333 + - 444 + 22222: + - 444 + - 555 + - 666 anniversary-numbers: - 100 - 500 @@ -43,8 +41,8 @@ groups-settings: -#message-templates: -# join-congratulation: "🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" -# join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления}" -# join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления}" +message-templates: + join-congratulation: "🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" + join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления}" + join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления}" diff --git a/src/main/resources/application-main.yml b/src/main/resources/application-main.yml index f77f0ab..e7c49bb 100644 --- a/src/main/resources/application-main.yml +++ b/src/main/resources/application-main.yml @@ -1,17 +1,17 @@ -spring: - datasource: - url: ${DATASOURCE_URL} - username: ${DATASOURCE_USER} - password: ${DATASOURCE_PASS} - hikari: - maximum-pool-size: 2 - jpa: - hibernate: - ddl-auto: update +#spring: +# datasource: +# url: ${DATASOURCE_URL} +# username: ${DATASOURCE_USER} +# password: ${DATASOURCE_PASS} +# hikari: +# maximum-pool-size: 2 +# jpa: +# hibernate: +# ddl-auto: update -logging: - level: - root: info +#logging: +# level: +# root: info bot-settings: bot-user-name: ${BOT_USER_NAME} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 30805e3..1806136 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,21 +7,25 @@ spring: maximum-pool-size: 2 jpa: hibernate: - ddl-auto: update + ddl-auto: none + profiles: + include: + - main + - bot logging: level: root: info -telegram: +#telegram: # multiplicity: 500 # incremental-saves: 2 - long-polling-timeout: 1000 - - bot-user-name: ${BOT_USER_NAME} - bot-token: ${BOT_TOKEN} - - groups: - - moderator-group: -644481529 - user-groups: - - -606991893 +# long-polling-timeout: 1000 +# +# bot-user-name: ${BOT_USER_NAME} +# bot-token: ${BOT_TOKEN} +# +# groups: +# - moderator-group: -644481529 +# user-groups: +# - -606991893 From c5f184be54714df74fee7f813bba247db936973f Mon Sep 17 00:00:00 2001 From: Aleksei Date: Mon, 11 Jul 2022 21:35:36 +0400 Subject: [PATCH 14/50] added callback processing --- .../{GroupConfig.java => ChatConfig.java} | 2 +- .../exception/CommandNotFoundException.java | 7 ++ .../handler/CallbackQueryHandler.java | 60 ++++++++++++++- .../tgbotusers/model/ModeratorChat.java | 15 ++++ .../tgbotusers/model/UserChat.java | 15 ++++ .../tgbotusers/model/UserJoining.java | 7 ++ .../model/enums/CongratulateStatus.java | 7 ++ .../repository/UserJoiningRepository.java | 2 + .../service/BotInitializerService.java | 4 +- .../service/CallbackQueryService.java | 35 +++++++-- .../service/ChatJoinRequestService.java | 4 +- .../tgbotusers/service/MessageService.java | 3 +- .../service/ModeratorChatService.java | 12 +++ .../service/NotificationService.java | 73 ++++++++++++------- .../service/TelegramEntityService.java | 10 --- .../tgbotusers/service/UserChatService.java | 10 +++ .../service/UserJoiningService.java | 7 +- src/main/resources/application.yml | 4 +- 18 files changed, 220 insertions(+), 57 deletions(-) rename src/main/java/org/codewithoutus/tgbotusers/config/{GroupConfig.java => ChatConfig.java} (95%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/exception/CommandNotFoundException.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/enums/CongratulateStatus.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatConfig.java similarity index 95% rename from src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java rename to src/main/java/org/codewithoutus/tgbotusers/config/ChatConfig.java index 39f5c47..5019662 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatConfig.java @@ -12,7 +12,7 @@ @Setter @Configuration @ConfigurationProperties(prefix = "telegram") -public class GroupConfig { +public class ChatConfig { private List moderatorChats; diff --git a/src/main/java/org/codewithoutus/tgbotusers/exception/CommandNotFoundException.java b/src/main/java/org/codewithoutus/tgbotusers/exception/CommandNotFoundException.java new file mode 100644 index 0000000..cb84613 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/exception/CommandNotFoundException.java @@ -0,0 +1,7 @@ +package org.codewithoutus.tgbotusers.exception; + +public class CommandNotFoundException extends RuntimeException { + public CommandNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java index d632163..e3224b7 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java @@ -1,11 +1,26 @@ package org.codewithoutus.tgbotusers.handler; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import com.pengrad.telegrambot.model.CallbackQuery; import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.exception.CommandNotFoundException; +import org.codewithoutus.tgbotusers.model.ModeratorChat; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import org.codewithoutus.tgbotusers.service.CallbackQueryService; +import org.codewithoutus.tgbotusers.service.CongratulationService; +import org.codewithoutus.tgbotusers.service.ModeratorChatService; +import org.codewithoutus.tgbotusers.service.UserChatService; +import org.codewithoutus.tgbotusers.service.UserJoiningService; import org.springframework.stereotype.Component; +import java.lang.reflect.Type; import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; @Component @@ -13,16 +28,53 @@ public class CallbackQueryHandler implements Handler { private final CallbackQueryService callbackQueryService; + private final CongratulationService congratulationService; + private final UserJoiningService userJoiningService; + private final ModeratorChatService moderatorChatService; + private final UserChatService userChatService; @Override public boolean handle(Update update) { - // TODO implement logic + CallbackQuery callbackQuery = update.callbackQuery(); + if (callbackQuery == null) { + return false; + } - Map callbackQueryData = Map.of(); + String data = callbackQuery.data(); + if (data == null || data.isBlank()) { + return false; + } - callbackQueryService.process(callbackQueryData); + Type mapType = new TypeToken>() {}.getType(); + Gson gson = new Gson().fromJson(data, mapType); + Map callbackQueryData = gson.fromJson(data, mapType); - return true; + Long moderatorChatId = Long.valueOf(callbackQueryData.get("moderatorChatId")); + Long chatId = Long.valueOf(callbackQueryData.get("chatId")); + List moderatorChats = userChatService.getUserChats() + .get(chatId).getModeratorChats(); + boolean fromModeratorChat = moderatorChats.stream() + .anyMatch(chat -> chat.getChatId().equals(moderatorChatId)); + if (!fromModeratorChat) { + return false; + } + + Long userId = Long.valueOf(callbackQueryData.get("userId")); + UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); + String command = callbackQueryData.get("command"); + if (userJoining.getStatus().equals(CongratulateStatus.WAIT)) { + if (command.equals("congratulate")) { + callbackQueryService.process(userJoining, CongratulateStatus.CONGRATULATE); + return true; + } else if (command.equals("decline")) { + callbackQueryService.process(userJoining, CongratulateStatus.DECLINE); + return true; + } else { + throw new CommandNotFoundException("Wrong callback data. Callback data does not contain a command."); + } + } + + return false; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java index 37485fd..796e615 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java @@ -32,4 +32,19 @@ public class ModeratorChat { joinColumns = @JoinColumn(name = "moderator_chat_id"), inverseJoinColumns = @JoinColumn(name = "user_chat_id")) private List userChats; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ModeratorChat that = (ModeratorChat) o; + + return chatId.equals(that.chatId); + } + + @Override + public int hashCode() { + return chatId.hashCode(); + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java index 41e6567..33ad78e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java @@ -29,4 +29,19 @@ public class UserChat { @ManyToMany(mappedBy = "userChats") private List moderatorChats; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UserChat userChat = (UserChat) o; + + return chatId.equals(userChat.chatId); + } + + @Override + public int hashCode() { + return chatId.hashCode(); + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java index d1b7431..fa1541f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java @@ -2,9 +2,12 @@ import lombok.Getter; import lombok.Setter; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -30,5 +33,9 @@ public class UserJoining { @Column(nullable = false) private LocalDateTime joinTime; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private CongratulateStatus status; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/enums/CongratulateStatus.java b/src/main/java/org/codewithoutus/tgbotusers/model/enums/CongratulateStatus.java new file mode 100644 index 0000000..05f7e16 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/enums/CongratulateStatus.java @@ -0,0 +1,7 @@ +package org.codewithoutus.tgbotusers.model.enums; + +public enum CongratulateStatus { + WAIT, + CONGRATULATE, + DECLINE +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java index fcb10a7..58a5bd9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java @@ -6,5 +6,7 @@ @Repository public interface UserJoiningRepository extends CrudRepository { + + UserJoining findByUserIdAndChatId(Long userId, Long chatId); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java index 9e14994..dc609ce 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java @@ -5,14 +5,14 @@ import com.pengrad.telegrambot.request.GetMyDefaultAdministratorRights; import com.pengrad.telegrambot.request.SetMyDefaultAdministratorRights; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.config.GroupConfig; +import org.codewithoutus.tgbotusers.config.ChatConfig; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class BotInitializerService { - private final GroupConfig groupConfig; + private final ChatConfig chatConfig; private final TelegramBot bot; diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java index e69911e..011ddf9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java @@ -1,18 +1,41 @@ package org.codewithoutus.tgbotusers.service; +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.request.GetChatMember; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import org.springframework.stereotype.Service; -import java.util.Map; - @Service @RequiredArgsConstructor -public class CallbackQueryService implements TelegramEntityService { +public class CallbackQueryService { + + private final TelegramBot bot; + private final NotificationService notificationService; + private final UserJoiningService userJoiningService; - @Override - public void process(Map callbackQueryData) { - // TODO implement logic + public void process(UserJoining userJoining, CongratulateStatus settableStatus) { + + notificationService.removeKeyboardFromNotification(); + + if (settableStatus.equals(CongratulateStatus.CONGRATULATE)) { + Long chatId = userJoining.getChatId(); + Long userId = userJoining.getUserId(); + var getChatMember = new GetChatMember(chatId, userId); + var getChatMemberResponse = bot.execute(getChatMember); + User user = getChatMemberResponse.chatMember().user(); + String userName = user.firstName(); + Integer number = userJoining.getNumber(); + + notificationService.congratulateUser(chatId, userName, number); + } + + userJoining.setStatus(settableStatus); + userJoiningService.save(userJoining); } + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java index bda1274..e44c425 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import org.springframework.stereotype.Service; import java.time.Instant; @@ -10,7 +11,7 @@ @Service @RequiredArgsConstructor -public class ChatJoinRequestService implements TelegramEntityService { +public class ChatJoinRequestService { private final UserJoiningService userJoiningService; private final NotificationService notificationService; @@ -33,6 +34,7 @@ private UserJoining buildUserJoining(Map userData) { userJoining.setNumber((Integer) userData.get("count")); Instant time = Instant.ofEpochSecond((Long) userData.get("data")); userJoining.setJoinTime(LocalDateTime.from(time)); + userJoining.setStatus(CongratulateStatus.WAIT); return userJoining; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java index 4a7c103..4131097 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java @@ -7,9 +7,8 @@ @Service @RequiredArgsConstructor -public class MessageService implements TelegramEntityService { +public class MessageService { - @Override public void process(Map messageData) { // TODO implement logic diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java index f99b31a..9fee016 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java @@ -1,9 +1,21 @@ package org.codewithoutus.tgbotusers.service; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.codewithoutus.tgbotusers.model.ModeratorChat; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.Map; + @Service +@Getter +@Setter @RequiredArgsConstructor public class ModeratorChatService { + + private Map moderatorChats; + + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java index 3fe6be0..d82d383 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java @@ -9,7 +9,8 @@ import com.pengrad.telegrambot.request.GetChatMember; import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.config.GroupConfig; +import org.codewithoutus.tgbotusers.config.ChatConfig; +import org.codewithoutus.tgbotusers.model.ModeratorChat; import org.codewithoutus.tgbotusers.model.UserJoining; import org.codewithoutus.tgbotusers.service.enums.ButtonText; import org.jetbrains.annotations.NotNull; @@ -17,46 +18,60 @@ import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; +import java.util.List; import java.util.Map; @Service @RequiredArgsConstructor public class NotificationService { + // TODO load from config private static final String NOTIFICATION_TEMPLATE = "\uD83C\uDF89 В \"%s\" группу вступил юбилейный пользователь %s (%s), %s. \n" + "Время вступления %s"; + private static final String CONGRATULATION_TEMPLATE = + "\uD83C\uDF89 Поздравляю, %s, как же удачно попали в нужное время и в нужное время! " + + "Вы %d участник коммьюнити.\n" + + "Вас ждут плюшки и печенюшки!\uD83C\uDF89"; private static final String NO_NICK = "ника нет"; - private final GroupConfig groupConfig; + private final ChatConfig chatConfig; private final TelegramBot bot; + private final ModeratorChatService moderatorChatService; + private final UserChatService userChatService; public void notifyModerators(UserJoining userJoining) { - Map notification = createNotification(userJoining); - sendNotification(notification, userJoining); + String text = createTextNotification(userJoining); + sendNotification(text, userJoining); } - private Map createNotification(UserJoining userJoining) { - Map joiningData = getJoiningData(userJoining); + public void congratulateUser(Long chatId, String userName, Integer number) { + // TODO use 'number' from config - String text = String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), + String text = String.format(CONGRATULATION_TEMPLATE, userName, number); + bot.execute(new SendMessage(chatId, text)); + } + + private String createTextNotification(UserJoining userJoining) { + Map joiningData = getJoiningData(userJoining); + + return String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), joiningData.get("userName"), joiningData.get("nickName"), joiningData.get("number"), joiningData.get("joinTime")); - InlineKeyboardMarkup keyboard = createKeyboard(userJoining); - - return Map.of("text", text, "keyboard", keyboard); } // TODO organize mailing - private void sendNotification(Map notification, UserJoining userJoining) { - groupConfig.getModeratorChats(); - - var sendMessage = new SendMessage( - userJoining.getChatId(), - (String) notification.get("text")) - .replyMarkup((InlineKeyboardMarkup) notification.get("keyboard")); - - bot.execute(sendMessage); + private void sendNotification(String text, UserJoining userJoining) { + List moderatorChats = userChatService.getUserChats() + .get(userJoining.getChatId()).getModeratorChats(); + + for (ModeratorChat moderatorChat : moderatorChats) { + Long moderatorChatId = moderatorChat.getChatId(); + var sendMessage = new SendMessage(moderatorChatId, text) + .replyMarkup(createKeyboard(userJoining, moderatorChatId)); + + bot.execute(sendMessage); + } } private Map getJoiningData(UserJoining userJoining) { @@ -89,38 +104,42 @@ private void sendNotification(Map notification, UserJoining userJoini "joinTime", joinTime); } - private InlineKeyboardMarkup createKeyboard(UserJoining userJoining) { + private InlineKeyboardMarkup createKeyboard(UserJoining userJoining, Long moderatorChatId) { var inlineKeyboardMarkup = new InlineKeyboardMarkup(); InlineKeyboardButton[] buttons = new InlineKeyboardButton[]{ new InlineKeyboardButton(ButtonText.CONGRATULATE.getSolution()) - .callbackData(getCongratulateCallbackData(userJoining)), + .callbackData(getCongratulateCallbackData(userJoining, moderatorChatId)), new InlineKeyboardButton(ButtonText.DECLINE.getSolution()) - .callbackData(getDeclineCallbackData(userJoining))}; + .callbackData(getDeclineCallbackData(userJoining, moderatorChatId))}; inlineKeyboardMarkup.addRow(buttons); return inlineKeyboardMarkup; } - private String getCongratulateCallbackData(UserJoining userJoining) { - JsonObject jsonObject = getJsonObject(userJoining); + private String getCongratulateCallbackData(UserJoining userJoining, Long moderatorChatId) { + JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); jsonObject.addProperty("command", "congratulate"); return jsonObject.getAsString(); } - private String getDeclineCallbackData(UserJoining userJoining) { - JsonObject jsonObject = getJsonObject(userJoining); + private String getDeclineCallbackData(UserJoining userJoining, Long moderatorChatId) { + JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); jsonObject.addProperty("command", "decline"); return jsonObject.getAsString(); } @NotNull - private JsonObject getJsonObject(UserJoining userJoining) { + private JsonObject getJsonObject(UserJoining userJoining, Long moderatorChatId) { JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("moderatorChatId", moderatorChatId); jsonObject.addProperty("userId", userJoining.getUserId()); jsonObject.addProperty("chatId", userJoining.getChatId()); return jsonObject; } + public void removeKeyboardFromNotification() { + // TODO implement + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java b/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java deleted file mode 100644 index 6e142f5..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/TelegramEntityService.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import com.pengrad.telegrambot.model.Update; - -import java.util.Map; - -public interface TelegramEntityService { - - void process(Map data); -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java index 4597525..a99bb15 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java @@ -1,9 +1,19 @@ package org.codewithoutus.tgbotusers.service; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.codewithoutus.tgbotusers.model.UserChat; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.Map; + @Service +@Getter +@Setter @RequiredArgsConstructor public class UserChatService { + + private Map userChats; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java index b5816ec..6e5442c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java @@ -11,8 +11,11 @@ public class UserJoiningService { private final UserJoiningRepository userJoiningRepository; - public void save(UserJoining userJoining) { - userJoiningRepository.save(userJoining); + public UserJoining save(UserJoining userJoining) { + return userJoiningRepository.save(userJoining); } + public UserJoining findByUserIdAndChatId(Long userId, Long chatId) { + return userJoiningRepository.findByUserIdAndChatId(userId, chatId); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2a9e925..5d22fbb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,8 +14,8 @@ logging: root: info telegram: - multiplicity: 500 - incremental-saves: 2 + multiplicity: 1 + incremental-saves: 1 long-polling-timeout: 1000 bot-user-name: ${BOT_USER_NAME} From d4ea6ba883ef0efa24320a45029d6874c7f877a1 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Mon, 11 Jul 2022 22:43:57 +0400 Subject: [PATCH 15/50] Small refactoring --- .../tgbotusers/TgBotUsersApplication.java | 1 - .../tgbotusers/config/AppConfig.java | 25 +++------ .../{BotConfig.java => BotSettings.java} | 13 +---- .../tgbotusers/config/GroupConfig.java | 34 ------------- .../tgbotusers/config/GroupSettings.java | 33 ++++++++++++ .../tgbotusers/config/NotificationConfig.java | 51 ------------------- .../config/NotificationTemplates.java | 17 +++++++ .../tgbotusers/config/PropsFromFile.java | 4 -- .../tgbotusers/controller/BotController.java | 28 +++------- .../{services => service}/BotService.java | 22 +++----- src/main/resources/application-bot.yml | 27 +--------- src/main/resources/application-main.yml | 20 -------- src/main/resources/application.yml | 25 +++------ 13 files changed, 83 insertions(+), 217 deletions(-) rename src/main/java/org/codewithoutus/tgbotusers/config/{BotConfig.java => BotSettings.java} (60%) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java rename src/main/java/org/codewithoutus/tgbotusers/{services => service}/BotService.java (62%) delete mode 100644 src/main/resources/application-main.yml diff --git a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java index 3ddb1d7..9b6617e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java +++ b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java @@ -3,7 +3,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; - @SpringBootApplication public class TgBotUsersApplication { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java index cd56674..000a824 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java @@ -1,29 +1,18 @@ package org.codewithoutus.tgbotusers.config; import com.pengrad.telegrambot.TelegramBot; -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; -@Getter -@Setter -@Component @Configuration -@ConfigurationProperties(prefix = "telegram") +@RequiredArgsConstructor public class AppConfig { - - private int longPollingTimeout; - private int multiplicity; - private int incrementalSaves; - private String botUserName; - private String botToken; - + + private final BotSettings botSettings; + @Bean public TelegramBot telegramBot() { - return new TelegramBot(botToken); + return new TelegramBot(botSettings.getBotToken()); } - -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java similarity index 60% rename from src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java rename to src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java index 9d249dd..f84186f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java @@ -2,25 +2,16 @@ import lombok.Getter; import lombok.Setter; -import lombok.ToString; -import org.codewithoutus.tgbotusers.model.Group; -import org.codewithoutus.tgbotusers.model.User; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; -import java.util.Map; - -@ToString @Getter @Setter @Component -@Configuration @ConfigurationProperties(prefix = "bot-settings") -public class BotConfig { +public class BotSettings { public int longPollingTimeout; public String botToken; public String botUserName; - -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java deleted file mode 100644 index f42a0b0..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Map; - -@ToString -@Getter -@Setter -@Component -//@Configuration -@ConfigurationProperties(prefix = "groups-settings") -public class GroupConfig { - - public List anniversaryNumbers; - private Map> moderatorGroups; - private Map> userGroups; - - -// private List groups; // 24-28 строки были тут -// public record ModeratorGroup(int id, List userGroups) { -// } -// public record UserGroup(int id) { -// } - - -} - diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java new file mode 100644 index 0000000..4da9287 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java @@ -0,0 +1,33 @@ +package org.codewithoutus.tgbotusers.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import org.codewithoutus.tgbotusers.model.ModeratorGroup; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; + +@Getter +@Setter +@Component +@ConfigurationProperties(prefix = "groups-settings") +public class GroupSettings { + + private Boolean rewriteDatabaseSettingsOnStartup; + private List anniversaryNumbers; + + @Getter(AccessLevel.NONE) + private Map> moderatorGroupsData; // only used for loading from application-settings file + + @JsonIgnore + private List moderatorGroups; // real group-settings, mapped to DB Entities + + @PostConstruct + private void synchronizeDataBaseSettings() { + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java deleted file mode 100644 index d1c7874..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import org.codewithoutus.tgbotusers.model.Group; -import org.codewithoutus.tgbotusers.model.User; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Map; - -@ToString -@Getter -@Setter -@Component -@Configuration -@ConfigurationProperties(prefix = "message-templates") -public class NotificationConfig { - -/*Default properties (specified by setting SpringApplication.setDefaultProperties). - создать дефолтный проперти дял юсера,если пустой то тогда как нужно -@ConfigurationProperties это не только про загрузку свойств из application.properties файла. - Возможности Spring Boot Configuration гораздо шире — поддерживается 17 (!) - разных источников свойств в строгом приоритете. Можно определить дефолт в application.properties - и перекрыть его через переменную окружения, JVM properties, профиль, тестовые свойства и т.п. - Что дает очень мощные возможности для переконфигурирования приложения в нужном окружении - и сильно упрощает конфигурацию. - */ - -// join-congratulation: 🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉 -// join-alert: 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления} -// join-user-info: 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления} -// final String format = "%-40s %s%n"; - - - private String joinCongratulation; - private String joinAlert; - private String joinUserInfo; - -// public List anniversaryNumbers; -// public String userName; -// -// public String PATCH_IMG;//"\uD83C\uDF89";//🎉 - - - -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java new file mode 100644 index 0000000..7cc5360 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java @@ -0,0 +1,17 @@ +package org.codewithoutus.tgbotusers.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@Component +@ConfigurationProperties(prefix = "message-templates") +public class NotificationTemplates { + + private String joinCongratulation; + private String joinAlert; + private String joinUserInfo; +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java b/src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java deleted file mode 100644 index d247efc..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/PropsFromFile.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - -public class PropsFromFile { -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index 20d042f..2ebdf31 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -1,11 +1,8 @@ package org.codewithoutus.tgbotusers.controller; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.config.BotConfig; -import org.codewithoutus.tgbotusers.config.GroupConfig; -import org.codewithoutus.tgbotusers.config.NotificationConfig; import org.codewithoutus.tgbotusers.dto.BackendResponse; -import org.codewithoutus.tgbotusers.services.BotService; +import org.codewithoutus.tgbotusers.service.BotService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -15,31 +12,22 @@ @RequestMapping("/backend") @RequiredArgsConstructor public class BotController { - private final NotificationConfig notificationConfig; - private final GroupConfig groupConfig; - private final BotConfig botConfig; - + private final BotService botService; - + @GetMapping("/start") private BackendResponse startBotBackend() { return botService.start(); } - + @GetMapping("/stop") private BackendResponse stopBotBackend() { return botService.stop(); } - - @GetMapping("/sendMessage") - private BackendResponse sendMessage(@RequestParam String message) { - return botService.sendMessage(message); - } - - @GetMapping("/props") - private String getProps(){ - return notificationConfig+"\n"+groupConfig+"\n"+botConfig+"\n"; + @GetMapping("/sendMessage") + private String sendMessage(@RequestParam String message) { + botService.sendMessage(-644481529L, message); + return "Message send"; } - } diff --git a/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java similarity index 62% rename from src/main/java/org/codewithoutus/tgbotusers/services/BotService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/BotService.java index 42adc8b..b293efc 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/services/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.services; +package org.codewithoutus.tgbotusers.service; import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.UpdatesListener; @@ -6,25 +6,21 @@ import com.pengrad.telegrambot.request.GetUpdates; import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.config.AppConfig; -import org.codewithoutus.tgbotusers.config.GroupConfig; +import org.codewithoutus.tgbotusers.config.BotSettings; +import org.codewithoutus.tgbotusers.config.GroupSettings; import org.codewithoutus.tgbotusers.dto.BackendResponse; import org.codewithoutus.tgbotusers.dto.enums.BotStatus; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class BotService { - private final AppConfig appConfig; - private final GroupConfig groupConfig; + private final BotSettings botSettings; private final TelegramBot bot; - public BackendResponse start() { - - GetUpdates getUpdates = new GetUpdates().timeout(appConfig.getLongPollingTimeout()); + GetUpdates getUpdates = new GetUpdates().timeout(botSettings.getLongPollingTimeout()); bot.setUpdatesListener(updates -> { updates.forEach(this::process); return UpdatesListener.CONFIRMED_UPDATES_ALL; @@ -38,15 +34,11 @@ public BackendResponse stop() { return new BackendResponse(true, BotStatus.STOP); } - private void process(Update update) { } - - public BackendResponse sendMessage(String message) { - SendMessage sendMessage = new SendMessage(-644481529, message); - bot.execute(sendMessage); - return new BackendResponse(true, BotStatus.START); + public void sendMessage(Long chatId, String message) { + bot.execute(new SendMessage(chatId, message)); } } diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 569fb3c..4fdae57 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,5 +1,5 @@ groups-settings: - rewrite-database-saved-settings: false + rewrite-database-settings-on-startup: false moderator-groups: 11111: - 222 @@ -16,33 +16,8 @@ groups-settings: - 1000 - 5000 - 10000 -#groups-settings: -# rewrite-database-saved-settings: false -# moderator-groups: -# - id: 11111 -# - user-groups: -# - 333 -# - id: 33333 -# - user-groups: -# - 444 -# - 555 -# - 666 -# user-groups: -# - 222 -# - 333 -# anniversary-numbers: -# - 100 -# - 500 -# - 777 -# - 1000 -# - 5000 -# - 10000 - - - message-templates: join-congratulation: "🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления}" join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления}" - diff --git a/src/main/resources/application-main.yml b/src/main/resources/application-main.yml deleted file mode 100644 index e7c49bb..0000000 --- a/src/main/resources/application-main.yml +++ /dev/null @@ -1,20 +0,0 @@ -#spring: -# datasource: -# url: ${DATASOURCE_URL} -# username: ${DATASOURCE_USER} -# password: ${DATASOURCE_PASS} -# hikari: -# maximum-pool-size: 2 -# jpa: -# hibernate: -# ddl-auto: update - -#logging: -# level: -# root: info - -bot-settings: - bot-user-name: ${BOT_USER_NAME} - bot-token: ${BOT_TOKEN} - long-polling-timeout: 1000 - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1806136..47fe252 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,31 +1,22 @@ spring: datasource: - url: jdbc:postgresql://localhost:5432/tg-bot-users - username: postgres - password: testtest + url: ${DATASOURCE_URL} + username: ${DATASOURCE_USER} + password: ${DATASOURCE_PASS} hikari: maximum-pool-size: 2 jpa: hibernate: - ddl-auto: none + ddl-auto: validate profiles: include: - - main - bot logging: level: root: info -#telegram: -# multiplicity: 500 -# incremental-saves: 2 -# long-polling-timeout: 1000 -# -# bot-user-name: ${BOT_USER_NAME} -# bot-token: ${BOT_TOKEN} -# -# groups: -# - moderator-group: -644481529 -# user-groups: -# - -606991893 +bot-settings: + bot-user-name: ${BOT_USER_NAME} + bot-token: ${BOT_TOKEN} + long-polling-timeout: 1000 From ba617805e1afcfd94a66a3af66a9fa36bc67762a Mon Sep 17 00:00:00 2001 From: Aleksei Date: Tue, 12 Jul 2022 00:06:06 +0400 Subject: [PATCH 16/50] added luckyList command logic --- .../tgbotusers/handler/MessageHandler.java | 33 ++++++++++--- .../tgbotusers/model/UserChat.java | 3 ++ .../tgbotusers/model/UserJoining.java | 34 ++++++++++++-- .../repository/UserChatRepository.java | 2 + .../repository/UserJoiningRepository.java | 3 ++ .../tgbotusers/service/BotService.java | 1 - .../tgbotusers/service/MessageService.java | 46 ++++++++++++++++++- .../tgbotusers/service/UserChatService.java | 7 +++ .../service/UserJoiningService.java | 6 +++ .../tgbotusers/service/enums/BotCommand.java | 15 ++++++ 10 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/enums/BotCommand.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java index 3e3cd1c..d8235c9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java @@ -1,28 +1,49 @@ package org.codewithoutus.tgbotusers.handler; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.MessageEntity; import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.service.MessageService; +import org.codewithoutus.tgbotusers.service.ModeratorChatService; import org.springframework.stereotype.Component; +import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.Optional; @Component @RequiredArgsConstructor public class MessageHandler implements Handler { private final MessageService messageService; + private final ModeratorChatService moderatorChatService; @Override public boolean handle(Update update) { - // TODO implement logic - - Map messageData = Map.of(); - - messageService.process(messageData); - + Message message = update.message(); + MessageEntity[] entities = message.entities(); + if (entities == null || entities.length <= 0) { + return false; + } + + Optional botEntityOptional = Arrays.stream(message.entities()) + .filter(entity -> entity.type().equals(MessageEntity.Type.bot_command)).findFirst(); + if (botEntityOptional.isEmpty()) { + return false; + } + + Long chatId = message.chat().id(); + if (!moderatorChatService.getModeratorChats().containsKey(chatId)) { + return false; + } + + MessageEntity entity = botEntityOptional.get(); + + messageService.process(message.text(), entity); + return true; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java index 33ad78e..6145420 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java @@ -26,6 +26,9 @@ public class UserChat { @Column(nullable = false, unique = true) private Long chatId; + @Column(nullable = false, unique = true) + private String name; + @ManyToMany(mappedBy = "userChats") private List moderatorChats; diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java index fa1541f..d8b1ee8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java @@ -3,6 +3,7 @@ import lombok.Getter; import lombok.Setter; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.jetbrains.annotations.NotNull; import javax.persistence.Column; import javax.persistence.Entity; @@ -16,8 +17,8 @@ @Entity @Getter @Setter -public class UserJoining { - +public class UserJoining implements Comparable { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @@ -37,5 +38,32 @@ public class UserJoining { @Enumerated(EnumType.STRING) @Column(nullable = false) private CongratulateStatus status; - + + + @Override + public int compareTo(@NotNull UserJoining o) { + int result = this.number.compareTo(o.number); + if (result == 0) { + result = this.joinTime.compareTo(o.joinTime); + } + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UserJoining that = (UserJoining) o; + + if (!userId.equals(that.userId)) return false; + return chatId.equals(that.chatId); + } + + @Override + public int hashCode() { + int result = userId.hashCode(); + result = 31 * result + chatId.hashCode(); + return result; + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java index 8931a06..65b7acd 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java @@ -6,4 +6,6 @@ @Repository public interface UserChatRepository extends CrudRepository { + + UserChat findByName(String name); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java index 58a5bd9..befb9b3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java @@ -4,9 +4,12 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface UserJoiningRepository extends CrudRepository { UserJoining findByUserIdAndChatId(Long userId, Long chatId); + List findByChatId(Long chatId); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java index 4fb78f0..95c392f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java @@ -19,7 +19,6 @@ public class BotService { private final UpdateHandler updateHandler; private BotStatus status; - public BackendResponse start() { if (status != null && status.equals(BotStatus.START)) { return new BackendResponse(false, status); diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java index 4131097..2c76672 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java @@ -1,17 +1,59 @@ package org.codewithoutus.tgbotusers.service; +import com.pengrad.telegrambot.model.MessageEntity; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.UserChat; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.service.enums.BotCommand; import org.springframework.stereotype.Service; +import java.util.Comparator; +import java.util.List; import java.util.Map; @Service @RequiredArgsConstructor public class MessageService { - public void process(Map messageData) { + private final ModeratorChatService moderatorChatService; + private final UserChatService userChatService; + private final UserJoiningService userJoiningService; - // TODO implement logic + public void process(String text, MessageEntity entity) { + if (text.contains(BotCommand.LUCKY_LIST.getCommand())) { + performLuckyList(text, entity); + } + + + } + + @Deprecated + private String getCommand(String text, MessageEntity entity) { + int begin = entity.offset(); + int end = begin + entity.length(); + String command = text.substring(begin, end); + if (command.contains("@")) { + return command.split("@")[0]; + } + return command; + } + + private void performLuckyList(String text, MessageEntity entity) { + String chatName = parseParam(text, entity); + UserChat userChat = userChatService.findByName(chatName); + Long userChatId = userChat.getChatId(); + List userJoinings = userJoiningService.findByChatId(userChatId); + userJoinings.sort(Comparator.naturalOrder()); + + for (UserJoining userJoining : userJoinings) { + // TODO implement logic + } + + } + + private String parseParam(String text, MessageEntity entity) { + String param = text.substring(entity.offset() + entity.length()); + return param.trim(); } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java index a99bb15..fb6302f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; import org.codewithoutus.tgbotusers.model.UserChat; +import org.codewithoutus.tgbotusers.repository.UserChatRepository; import org.springframework.stereotype.Service; import java.util.List; @@ -15,5 +16,11 @@ @RequiredArgsConstructor public class UserChatService { + private final UserChatRepository userChatRepository; + private Map userChats; + + public UserChat findByName(String name) { + return userChatRepository.findByName(name); + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java index 6e5442c..993b097 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java @@ -5,6 +5,8 @@ import org.codewithoutus.tgbotusers.repository.UserJoiningRepository; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class UserJoiningService { @@ -18,4 +20,8 @@ public UserJoining save(UserJoining userJoining) { public UserJoining findByUserIdAndChatId(Long userId, Long chatId) { return userJoiningRepository.findByUserIdAndChatId(userId, chatId); } + + public List findByChatId(Long chatId) { + return userJoiningRepository.findByChatId(chatId); + } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/service/enums/BotCommand.java new file mode 100644 index 0000000..f049fd3 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/enums/BotCommand.java @@ -0,0 +1,15 @@ +package org.codewithoutus.tgbotusers.service.enums; + +import lombok.Getter; + +@Getter +public enum BotCommand { + + LUCKY_LIST("/luckyList"); + + private final String command; + + BotCommand(String command) { + this.command = command; + } +} From 59b339810f74bdec74b0585a375ebd0c8f9f7a34 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 12 Jul 2022 02:15:05 +0400 Subject: [PATCH 17/50] Small refactoring --- .../tgbotusers/TgBotUsersApplication.java | 3 +- .../org/codewithoutus/tgbotusers/bot/Bot.java | 38 +++ .../tgbotusers/bot/BotResponse.java | 4 + .../BotRightsService.java} | 33 +-- .../tgbotusers/bot/BotService.java | 42 +++ .../tgbotusers/bot/BotStatus.java | 6 + .../tgbotusers/bot/TelegramService.java | 60 +++++ .../tgbotusers/config/AppConfig.java | 33 ++- .../tgbotusers/config/BotConfig.java | 18 -- .../tgbotusers/config/ChatConfig.java | 28 -- .../tgbotusers/config/GroupSettings.java | 4 +- .../tgbotusers/config/NotificationConfig.java | 4 - .../tgbotusers/controller/BotController.java | 33 ++- .../tgbotusers/dto/BackendResponse.java | 6 - .../tgbotusers/dto/enums/BotStatus.java | 6 - .../handler/CallbackQueryHandler.java | 96 +++---- .../handler/ChatJoinRequestHandler.java | 57 ++--- .../tgbotusers/handler/Handler.java | 4 +- .../tgbotusers/handler/MessageHandler.java | 18 +- .../tgbotusers/handler/UpdateHandler.java | 25 -- .../CongratulationDecisionKeyboard.java | 16 ++ .../tgbotusers/model/Administrator.java | 5 +- .../tgbotusers/model/Congratulation.java | 1 - .../tgbotusers/model/ModeratorChat.java | 17 +- .../codewithoutus/tgbotusers/model/User.java | 5 +- .../tgbotusers/model/UserChat.java | 3 - .../tgbotusers/model/UserJoining.java | 1 - .../repository/AdministratorRepository.java | 2 +- .../repository/CongratulationRepository.java | 2 +- .../repository/ModeratorChatRepository.java | 2 +- .../repository/UserChatRepository.java | 2 +- .../repository/UserJoiningRepository.java | 5 +- .../tgbotusers/repository/UserRepository.java | 2 +- .../service/AdministratorService.java | 2 +- .../service/BotExecutorService.java | 27 -- .../tgbotusers/service/BotService.java | 53 ---- .../service/CallbackQueryService.java | 44 ++-- .../service/ChatJoinRequestService.java | 47 ++-- .../service/CongratulationService.java | 2 +- .../tgbotusers/service/MessageService.java | 17 -- .../service/ModeratorChatService.java | 21 -- ...UserService.java => ModeratorService.java} | 6 +- .../service/NotificationService.java | 240 ++++++++---------- .../tgbotusers/service/UserChatService.java | 19 -- .../service/UserJoiningService.java | 16 +- .../tgbotusers/service/enums/ButtonText.java | 17 -- src/main/resources/application.yml | 6 +- 47 files changed, 473 insertions(+), 625 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java rename src/main/java/org/codewithoutus/tgbotusers/{service/BotInitializerService.java => bot/BotRightsService.java} (72%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/BotService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/ChatConfig.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/BotService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java rename src/main/java/org/codewithoutus/tgbotusers/service/{UserService.java => ModeratorService.java} (51%) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/enums/ButtonText.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java index 9b6617e..631b092 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java +++ b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java @@ -9,5 +9,4 @@ public class TgBotUsersApplication { public static void main(String[] args) { SpringApplication.run(TgBotUsersApplication.class, args); } - -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java new file mode 100644 index 0000000..d415b4b --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java @@ -0,0 +1,38 @@ +package org.codewithoutus.tgbotusers.bot; + +import com.pengrad.telegrambot.TelegramBot; + +public class Bot extends TelegramBot { + + private final BotService botService; + private BotStatus status; + + public Bot(String botToken, BotService botService) { + super(botToken); + this.botService = botService; + } + + public BotStatus getStatus() { + return status; + } + + public boolean start() { + if (status == BotStatus.START) { + return false; + } + botService.start(); + + status = BotStatus.START; + return true; + } + + public boolean stop() { + if (status == BotStatus.STOP) { + return false; + } + botService.stop(); + + status = BotStatus.STOP; + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java new file mode 100644 index 0000000..52c4fac --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java @@ -0,0 +1,4 @@ +package org.codewithoutus.tgbotusers.bot; + +public record BotResponse(boolean ok, BotStatus status) { +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotRightsService.java similarity index 72% rename from src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/BotRightsService.java index dc609ce..a45c623 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/BotInitializerService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotRightsService.java @@ -1,52 +1,37 @@ -package org.codewithoutus.tgbotusers.service; +package org.codewithoutus.tgbotusers.bot; import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.model.ChatAdministratorRights; import com.pengrad.telegrambot.request.GetMyDefaultAdministratorRights; import com.pengrad.telegrambot.request.SetMyDefaultAdministratorRights; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.config.ChatConfig; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -public class BotInitializerService { - - private final ChatConfig chatConfig; +public class BotRightsService { + private final TelegramBot bot; - - - public void initialiseBot() { - initialiseAdministratorRights(); - initialiseGroups(); - } - - private void initialiseAdministratorRights() { + + public void initialiseAdministratorRights() { if (!administratorRightsIsCorrect()) { setAdministratorRights(); } } - + private boolean administratorRightsIsCorrect() { var getRights = new GetMyDefaultAdministratorRights(); var response = bot.execute(getRights); var rights = response.result(); return rights.canInviteUsers(); } - + private void setAdministratorRights() { var chatAdministratorRights = new ChatAdministratorRights(); // TODO add isAnonymous(boolean) chatAdministratorRights.canInviteUsers(true); - + var setMyDefaultAdministratorRights = new SetMyDefaultAdministratorRights(); setMyDefaultAdministratorRights.rights(chatAdministratorRights); bot.execute(setMyDefaultAdministratorRights); } - - - private void initialiseGroups() { - - } - - -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotService.java new file mode 100644 index 0000000..fe320bf --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotService.java @@ -0,0 +1,42 @@ +package org.codewithoutus.tgbotusers.bot; + +import com.pengrad.telegrambot.UpdatesListener; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.GetUpdates; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.config.BotSettings; +import org.codewithoutus.tgbotusers.handler.Handler; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.function.Consumer; + +@Service +@RequiredArgsConstructor +public class BotService { + + private final Bot bot; + private final BotSettings botSettings; + private final List updateHandlers; + + public void start() { + GetUpdates timeout = new GetUpdates().timeout(botSettings.getLongPollingTimeout()); + Consumer updateHandler = (update -> { + for (Handler handler : updateHandlers) { + if (handler.handle(update)) { + return; + } + } + }); + UpdatesListener updatesListener = (updates -> { + updates.forEach(updateHandler); + return UpdatesListener.CONFIRMED_UPDATES_ALL; + }); + + bot.setUpdatesListener(updatesListener, timeout); + } + + public void stop() { + bot.removeGetUpdatesListener(); + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java new file mode 100644 index 0000000..3b52ce1 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java @@ -0,0 +1,6 @@ +package org.codewithoutus.tgbotusers.bot; + +public enum BotStatus { + START, + STOP +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java new file mode 100644 index 0000000..247ffc1 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java @@ -0,0 +1,60 @@ +package org.codewithoutus.tgbotusers.bot; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.CallbackQuery; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.request.GetChatMember; +import com.pengrad.telegrambot.request.GetChatMemberCount; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +@Slf4j +public class TelegramService { + + private final TelegramBot bot; + private final ObjectMapper objectMapper; + + public void sendMessage(Long chatId, String message) { + bot.execute(new SendMessage(chatId, message)); + } + + public int getChatMembersCount(long chatId) { + var getChatMemberCount = new GetChatMemberCount(chatId); + var getChatMemberCountResponse = bot.execute(getChatMemberCount); + return getChatMemberCountResponse.count(); + } + + public User getUserInfo(long chatId, long userId) { + var getChatMember = new GetChatMember(chatId, userId); + var getChatMemberResponse = bot.execute(getChatMember); + return getChatMemberResponse.chatMember().user(); + } + + public Map getCallbackData(Update update) { + return Optional + .ofNullable(update.callbackQuery()) + .map(CallbackQuery::data) + .filter(data -> !data.isBlank()) + .map(data -> { + try { + return objectMapper.readValue(data, new TypeReference>() { + }); + } catch (JsonProcessingException e) { + log.error("CallbackQuery json parsing from update {}", this); + return null; + } + }) + .orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java index 000a824..3700bb6 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java @@ -1,18 +1,45 @@ package org.codewithoutus.tgbotusers.config; -import com.pengrad.telegrambot.TelegramBot; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.Bot; +import org.codewithoutus.tgbotusers.bot.BotService; +import org.codewithoutus.tgbotusers.handler.CallbackQueryHandler; +import org.codewithoutus.tgbotusers.handler.ChatJoinRequestHandler; +import org.codewithoutus.tgbotusers.handler.Handler; +import org.codewithoutus.tgbotusers.handler.MessageHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.List; + @Configuration @RequiredArgsConstructor public class AppConfig { private final BotSettings botSettings; + private final BotService botService; + + private final CallbackQueryHandler callbackQueryHandler; + private final ChatJoinRequestHandler chatJoinRequestHandler; + private final MessageHandler messageHandler; + + @Bean + public Bot telegramBot() { + return new Bot(botSettings.getBotToken(), botService); + } + + @Bean + public List telegramBotUpdateHandlers() { + // order FROM more frequent & less resource-intensive -> TO less frequent & more resource-intensive + return List.of( + messageHandler, + chatJoinRequestHandler, + callbackQueryHandler); + } @Bean - public TelegramBot telegramBot() { - return new TelegramBot(botSettings.getBotToken()); + public ObjectMapper objectMapper() { + return new ObjectMapper(); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java deleted file mode 100644 index 9b1018e..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/BotConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - -import com.pengrad.telegrambot.TelegramBot; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@RequiredArgsConstructor -public class BotConfig { - - private final AppConfig appConfig; - - @Bean - public TelegramBot telegramBot() { - return new TelegramBot(appConfig.getBotToken()); - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatConfig.java deleted file mode 100644 index 5019662..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/ChatConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - -import lombok.Getter; -import lombok.Setter; -import org.codewithoutus.tgbotusers.model.ModeratorChat; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -import java.util.List; - -@Getter -@Setter -@Configuration -@ConfigurationProperties(prefix = "telegram") -public class ChatConfig { - - private List moderatorChats; - - - public record ModeratorChatProp(long id, List userChats) { - - } - - public record UserChatProp(long id) { - - } - -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java index 4da9287..b7b204e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java @@ -4,7 +4,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; -import org.codewithoutus.tgbotusers.model.ModeratorGroup; +import org.codewithoutus.tgbotusers.model.ModeratorChat; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -25,7 +25,7 @@ public class GroupSettings { private Map> moderatorGroupsData; // only used for loading from application-settings file @JsonIgnore - private List moderatorGroups; // real group-settings, mapped to DB Entities + private List moderatorGroups; // real group-settings, mapped to DB Entities @PostConstruct private void synchronizeDataBaseSettings() { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java deleted file mode 100644 index 7b35b8d..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationConfig.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - -public class NotificationConfig { -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index 2ebdf31..db5761d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -1,33 +1,40 @@ package org.codewithoutus.tgbotusers.controller; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.dto.BackendResponse; -import org.codewithoutus.tgbotusers.service.BotService; +import org.codewithoutus.tgbotusers.bot.Bot; +import org.codewithoutus.tgbotusers.bot.BotResponse; +import org.codewithoutus.tgbotusers.bot.TelegramService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/backend") +@RequestMapping("/admin") @RequiredArgsConstructor public class BotController { - private final BotService botService; + private final Bot bot; + private final TelegramService telegramService; @GetMapping("/start") - private BackendResponse startBotBackend() { - return botService.start(); + private BotResponse startBotBackend() { + return new BotResponse(bot.start(), bot.getStatus()); } @GetMapping("/stop") - private BackendResponse stopBotBackend() { - return botService.stop(); + private BotResponse stopBotBackend() { + return new BotResponse(bot.stop(), bot.getStatus()); } - @GetMapping("/sendMessage") - private String sendMessage(@RequestParam String message) { - botService.sendMessage(-644481529L, message); - return "Message send"; + @GetMapping("/status") + private BotResponse getStatus() { + return new BotResponse(true, bot.getStatus()); } -} + + @GetMapping("/sendMessage") // chatId = -644481529L + private BotResponse sendMessage(@RequestParam Long chatId, @RequestParam String message) { + telegramService.sendMessage(chatId, message); + return new BotResponse(true, bot.getStatus()); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java b/src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java deleted file mode 100644 index 99a1dbf..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/dto/BackendResponse.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.codewithoutus.tgbotusers.dto; - -import org.codewithoutus.tgbotusers.dto.enums.BotStatus; - -public record BackendResponse(boolean ok, BotStatus status) { -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java b/src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java deleted file mode 100644 index 5fc7795..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/dto/enums/BotStatus.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.codewithoutus.tgbotusers.dto.enums; - -public enum BotStatus { - START, - STOP -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java index e3224b7..5b4f47c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java @@ -1,80 +1,52 @@ package org.codewithoutus.tgbotusers.handler; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; -import com.pengrad.telegrambot.model.CallbackQuery; import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.exception.CommandNotFoundException; -import org.codewithoutus.tgbotusers.model.ModeratorChat; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.codewithoutus.tgbotusers.service.CallbackQueryService; -import org.codewithoutus.tgbotusers.service.CongratulationService; -import org.codewithoutus.tgbotusers.service.ModeratorChatService; -import org.codewithoutus.tgbotusers.service.UserChatService; -import org.codewithoutus.tgbotusers.service.UserJoiningService; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.TelegramService; import org.springframework.stereotype.Component; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; @Component @RequiredArgsConstructor +@Slf4j public class CallbackQueryHandler implements Handler { - - private final CallbackQueryService callbackQueryService; - private final CongratulationService congratulationService; - private final UserJoiningService userJoiningService; - private final ModeratorChatService moderatorChatService; - private final UserChatService userChatService; - + + private final TelegramService telegramService; + @Override public boolean handle(Update update) { - - CallbackQuery callbackQuery = update.callbackQuery(); - if (callbackQuery == null) { - return false; - } - - String data = callbackQuery.data(); - if (data == null || data.isBlank()) { + Map callbackQueryData = telegramService.getCallbackData(update); + if (callbackQueryData == null) { return false; } - - Type mapType = new TypeToken>() {}.getType(); - Gson gson = new Gson().fromJson(data, mapType); - Map callbackQueryData = gson.fromJson(data, mapType); - - Long moderatorChatId = Long.valueOf(callbackQueryData.get("moderatorChatId")); - Long chatId = Long.valueOf(callbackQueryData.get("chatId")); - List moderatorChats = userChatService.getUserChats() - .get(chatId).getModeratorChats(); - boolean fromModeratorChat = moderatorChats.stream() - .anyMatch(chat -> chat.getChatId().equals(moderatorChatId)); - if (!fromModeratorChat) { - return false; - } - - Long userId = Long.valueOf(callbackQueryData.get("userId")); - UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); - String command = callbackQueryData.get("command"); - if (userJoining.getStatus().equals(CongratulateStatus.WAIT)) { - if (command.equals("congratulate")) { - callbackQueryService.process(userJoining, CongratulateStatus.CONGRATULATE); - return true; - } else if (command.equals("decline")) { - callbackQueryService.process(userJoining, CongratulateStatus.DECLINE); - return true; - } else { - throw new CommandNotFoundException("Wrong callback data. Callback data does not contain a command."); - } - } - + +// Long moderatorChatId = Long.valueOf(callbackQueryData.get("moderatorChatId")); +// Long chatId = Long.valueOf(callbackQueryData.get("chatId")); +// List moderatorChats = userChatService.getUserChats() +// .get(chatId).getModeratorChats(); +// boolean fromModeratorChat = moderatorChats.stream() +// .anyMatch(chat -> chat.getChatId().equals(moderatorChatId)); +// if (!fromModeratorChat) { +// return false; +// } +// +// Long userId = Long.valueOf(callbackQueryData.get("userId")); +// UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); +// String command = callbackQueryData.get("command"); +// if (userJoining.getStatus().equals(CongratulateStatus.WAIT)) { +// if (command.equals("congratulate")) { +// callbackQueryService.process(userJoining, CongratulateStatus.CONGRATULATE); +// return true; +// } else if (command.equals("decline")) { +// callbackQueryService.process(userJoining, CongratulateStatus.DECLINE); +// return true; +// } else { +// throw new CommandNotFoundException("Wrong callback data. Callback data does not contain a command."); +// } +// } +// return false; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java index 735997a..930fd2f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java @@ -2,55 +2,48 @@ import com.pengrad.telegrambot.model.ChatJoinRequest; import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.model.User; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.service.BotExecutorService; +import org.codewithoutus.tgbotusers.bot.TelegramService; import org.codewithoutus.tgbotusers.service.ChatJoinRequestService; import org.springframework.stereotype.Component; -import java.util.Map; - @Component @RequiredArgsConstructor public class ChatJoinRequestHandler implements Handler { - private static final int MULTIPLICITY = 500; // TODO replace on config - private static final int INCREMENTAL_SAVES = 2; // TODO replace on config - - private final BotExecutorService botExecutorService; + + private final TelegramService botExecutorService; private final ChatJoinRequestService chatJoinRequestService; @Override public boolean handle(Update update) { - ChatJoinRequest chatJoinRequest = update.chatJoinRequest(); if (chatJoinRequest == null) { return false; } - // TODO проверить из чата модераторов - - long chatId = chatJoinRequest.chat().id(); - int count = botExecutorService.getCount(chatId); - if (count % MULTIPLICITY > INCREMENTAL_SAVES) { - return false; - } - - User user = chatJoinRequest.from(); - if (user.isBot()) { - return false; - } - - int date = chatJoinRequest.date(); - - Map userData = Map.of( - "userId", user.id(), - "chatId", chatId, - "count", count, - "date", date); - - chatJoinRequestService.process(userData); +// // TODO проверить из чата модераторов +// +// long chatId = chatJoinRequest.chat().id(); +// int count = botExecutorService.getCount(chatId); +// if (count % MULTIPLICITY > INCREMENTAL_SAVES) { +// return false; +// } +// +// User user = chatJoinRequest.from(); +// if (user.isBot()) { +// return false; +// } +// +// int date = chatJoinRequest.date(); +// +// Map userData = Map.of( +// "userId", user.id(), +// "chatId", chatId, +// "count", count, +// "date", date); +// +// chatJoinRequestService.process(userData); return true; } - } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java index cc75264..ad8e376 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java @@ -2,8 +2,6 @@ import com.pengrad.telegrambot.model.Update; -import java.util.Map; - public interface Handler { boolean handle(Update update); -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java index 3e3cd1c..dc22682 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java @@ -2,27 +2,15 @@ import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.service.MessageService; import org.springframework.stereotype.Component; -import java.util.Collections; -import java.util.Map; - @Component @RequiredArgsConstructor public class MessageHandler implements Handler { - - private final MessageService messageService; - + @Override public boolean handle(Update update) { - - // TODO implement logic - - Map messageData = Map.of(); - - messageService.process(messageData); - - return true; +// messageService.process(messageData); + return false; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java deleted file mode 100644 index 77f7631..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/UpdateHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -import java.util.Map; - -@Component -@RequiredArgsConstructor -public class UpdateHandler implements Handler { - - private final ChatJoinRequestHandler chatJoinRequestHandler; - private final CallbackQueryHandler callbackQueryHandler; - private final MessageHandler messageHandler; - - @Override - public boolean handle(Update update) { - boolean chatJoinRequestHandlerResult = chatJoinRequestHandler.handle(update); - boolean callbackHandlerResult = callbackQueryHandler.handle(update); - boolean messageHandlerResult = messageHandler.handle(update); - - return chatJoinRequestHandlerResult || callbackHandlerResult || messageHandlerResult; - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java new file mode 100644 index 0000000..622f71d --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java @@ -0,0 +1,16 @@ +package org.codewithoutus.tgbotusers.keyboard; + +import lombok.Getter; + +@Getter +public enum CongratulationDecisionKeyboard { + + CONGRATULATE("Поздравить \uD83E\uDD73"), + DECLINE("Отклонить 🚫"); + + private final String solution; + + CongratulationDecisionKeyboard(String solution) { + this.solution = solution; + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java index e7d1f6f..2623de6 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java @@ -14,13 +14,12 @@ @Getter @Setter public class Administrator { - + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; - + @NaturalId @Column(nullable = false) Long userId; - } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java index 2600203..fa7c074 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java @@ -26,5 +26,4 @@ public class Congratulation { @Column(nullable = false) private Integer number; - } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java index 796e615..c34bc8c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java @@ -4,14 +4,7 @@ import lombok.Setter; import org.hibernate.annotations.NaturalId; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; +import javax.persistence.*; import java.util.List; @Entity @@ -32,19 +25,17 @@ public class ModeratorChat { joinColumns = @JoinColumn(name = "moderator_chat_id"), inverseJoinColumns = @JoinColumn(name = "user_chat_id")) private List userChats; - + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - ModeratorChat that = (ModeratorChat) o; - return chatId.equals(that.chatId); } - + @Override public int hashCode() { return chatId.hashCode(); } -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/User.java b/src/main/java/org/codewithoutus/tgbotusers/model/User.java index 72ec5db..38b3a87 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/User.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/User.java @@ -13,7 +13,7 @@ import javax.persistence.Table; @Entity -@Table(name = "_user") +@Table(name = "users") @Getter @Setter public class User { @@ -25,5 +25,4 @@ public class User { @NaturalId @Column(nullable = false) Long userId; - -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java index 33ad78e..01bcfe5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java @@ -29,14 +29,11 @@ public class UserChat { @ManyToMany(mappedBy = "userChats") private List moderatorChats; - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - UserChat userChat = (UserChat) o; - return chatId.equals(userChat.chatId); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java index fa1541f..e7d06b7 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java @@ -37,5 +37,4 @@ public class UserJoining { @Enumerated(EnumType.STRING) @Column(nullable = false) private CongratulateStatus status; - } diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java index 8b2467c..6344b2c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java @@ -6,4 +6,4 @@ @Repository public interface AdministratorRepository extends CrudRepository { -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java index 524065a..29af824 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java @@ -6,4 +6,4 @@ @Repository public interface CongratulationRepository extends CrudRepository { -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java index 7020e4a..420b6f9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java @@ -6,4 +6,4 @@ @Repository public interface ModeratorChatRepository extends CrudRepository { -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java index 8931a06..91cb3fd 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java @@ -6,4 +6,4 @@ @Repository public interface UserChatRepository extends CrudRepository { -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java index 58a5bd9..a32a39b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java @@ -6,7 +6,6 @@ @Repository public interface UserJoiningRepository extends CrudRepository { - - UserJoining findByUserIdAndChatId(Long userId, Long chatId); -} + UserJoining findByUserIdAndChatId(Long userId, Long chatId); +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java index bfdf1ed..257bdc5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java @@ -6,4 +6,4 @@ @Repository public interface UserRepository extends CrudRepository { -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java b/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java index b3ffade..4712ffc 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java @@ -6,4 +6,4 @@ @Service @RequiredArgsConstructor public class AdministratorService { -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java deleted file mode 100644 index da08287..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/BotExecutorService.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.model.User; -import com.pengrad.telegrambot.request.GetChatMember; -import com.pengrad.telegrambot.request.GetChatMemberCount; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class BotExecutorService { - private final TelegramBot bot; - - - public int getCount(long chatId) { - var getChatMemberCount = new GetChatMemberCount(chatId); - var getChatMemberCountResponse = bot.execute(getChatMemberCount); - return getChatMemberCountResponse.count(); - } - - public User getUser(long chatId, long userId) { - var getChatMember = new GetChatMember(chatId, userId); - var getChatMemberResponse = bot.execute(getChatMember); - return getChatMemberResponse.chatMember().user(); - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java deleted file mode 100644 index 9ca2ec6..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/BotService.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.UpdatesListener; -import com.pengrad.telegrambot.request.GetUpdates; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.config.BotSettings; -import org.codewithoutus.tgbotusers.config.GroupSettings; -import org.codewithoutus.tgbotusers.dto.BackendResponse; -import org.codewithoutus.tgbotusers.dto.enums.BotStatus; -import org.codewithoutus.tgbotusers.handler.UpdateHandler; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class BotService { - - private final BotSettings botSettings; - private final TelegramBot bot; - private final UpdateHandler updateHandler; - private BotStatus status; - - public BackendResponse start() { - if (status != null && status.equals(BotStatus.START)) { - return new BackendResponse(false, status); - } - - GetUpdates getUpdates = new GetUpdates().timeout(botSettings.getLongPollingTimeout()); - bot.setUpdatesListener(updates -> { - updates.forEach(updateHandler::handle); - return UpdatesListener.CONFIRMED_UPDATES_ALL; - }, - getUpdates); - status = BotStatus.START; - - return new BackendResponse(true, status); - } - - public BackendResponse stop() { - if (status == null || status.equals(BotStatus.STOP)) { - return new BackendResponse(false, status); - } - - bot.removeGetUpdatesListener(); - status = BotStatus.STOP; - - return new BackendResponse(true, status); - } - - public void sendMessage(Long chatId, String message) { - bot.execute(new SendMessage(chatId, message)); - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java index 011ddf9..780f952 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java @@ -16,26 +16,24 @@ public class CallbackQueryService { private final NotificationService notificationService; private final UserJoiningService userJoiningService; - - public void process(UserJoining userJoining, CongratulateStatus settableStatus) { - - notificationService.removeKeyboardFromNotification(); - - if (settableStatus.equals(CongratulateStatus.CONGRATULATE)) { - Long chatId = userJoining.getChatId(); - Long userId = userJoining.getUserId(); - var getChatMember = new GetChatMember(chatId, userId); - var getChatMemberResponse = bot.execute(getChatMember); - User user = getChatMemberResponse.chatMember().user(); - String userName = user.firstName(); - Integer number = userJoining.getNumber(); - - notificationService.congratulateUser(chatId, userName, number); - } - - userJoining.setStatus(settableStatus); - userJoiningService.save(userJoining); - - } - -} +// public void process(UserJoining userJoining, CongratulateStatus settableStatus) { +// +// notificationService.removeKeyboardFromNotification(); +// +// if (settableStatus.equals(CongratulateStatus.CONGRATULATE)) { +// Long chatId = userJoining.getChatId(); +// Long userId = userJoining.getUserId(); +// var getChatMember = new GetChatMember(chatId, userId); +// var getChatMemberResponse = bot.execute(getChatMember); +// User user = getChatMemberResponse.chatMember().user(); +// String userName = user.firstName(); +// Integer number = userJoining.getNumber(); +// +// notificationService.congratulateUser(chatId, userName, number); +// } +// +// userJoining.setStatus(settableStatus); +// userJoiningService.save(userJoining); +// +// } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java index e44c425..ac18ab8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java @@ -15,29 +15,24 @@ public class ChatJoinRequestService { private final UserJoiningService userJoiningService; private final NotificationService notificationService; - - - public void process(Map userData) { - UserJoining userJoining = buildUserJoining(userData); - - userJoiningService.save(userJoining); - - notificationService.notifyModerators(userJoining); - } - - private UserJoining buildUserJoining(Map userData) { - - UserJoining userJoining = new UserJoining(); - - userJoining.setUserId((Long) userData.get("userId")); - userJoining.setChatId((Long) userData.get("chatId")); - userJoining.setNumber((Integer) userData.get("count")); - Instant time = Instant.ofEpochSecond((Long) userData.get("data")); - userJoining.setJoinTime(LocalDateTime.from(time)); - userJoining.setStatus(CongratulateStatus.WAIT); - - return userJoining; - } - - -} + +// public void process(Map userData) { +// UserJoining userJoining = buildUserJoining(userData); +// userJoiningService.save(userJoining); +// notificationService.notifyModerators(userJoining); +// } + +// private UserJoining buildUserJoining(Map userData) { +// +// UserJoining userJoining = new UserJoining(); +// +// userJoining.setUserId((Long) userData.get("userId")); +// userJoining.setChatId((Long) userData.get("chatId")); +// userJoining.setNumber((Integer) userData.get("count")); +// Instant time = Instant.ofEpochSecond((Long) userData.get("data")); +// userJoining.setJoinTime(LocalDateTime.from(time)); +// userJoining.setStatus(CongratulateStatus.WAIT); +// +// return userJoining; +// } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java index 4f06cd1..a92d262 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java @@ -6,4 +6,4 @@ @Service @RequiredArgsConstructor public class CongratulationService { -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java deleted file mode 100644 index 4131097..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.Map; - -@Service -@RequiredArgsConstructor -public class MessageService { - - public void process(Map messageData) { - - // TODO implement logic - - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java deleted file mode 100644 index 9fee016..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorChatService.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import org.codewithoutus.tgbotusers.model.ModeratorChat; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; - -@Service -@Getter -@Setter -@RequiredArgsConstructor -public class ModeratorChatService { - - private Map moderatorChats; - - -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorService.java similarity index 51% rename from src/main/java/org/codewithoutus/tgbotusers/service/UserService.java rename to src/main/java/org/codewithoutus/tgbotusers/service/ModeratorService.java index d55a1a8..cee4ac9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorService.java @@ -1,7 +1,9 @@ package org.codewithoutus.tgbotusers.service; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service -public class UserService { -} +@RequiredArgsConstructor +public class ModeratorService { +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java index d82d383..53b56aa 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java @@ -1,145 +1,117 @@ package org.codewithoutus.tgbotusers.service; -import com.google.gson.JsonObject; import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.model.User; -import com.pengrad.telegrambot.model.request.InlineKeyboardButton; -import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; -import com.pengrad.telegrambot.request.GetChat; -import com.pengrad.telegrambot.request.GetChatMember; -import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.config.ChatConfig; -import org.codewithoutus.tgbotusers.model.ModeratorChat; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.service.enums.ButtonText; -import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; -import java.util.List; -import java.util.Map; - @Service @RequiredArgsConstructor public class NotificationService { - - // TODO load from config - private static final String NOTIFICATION_TEMPLATE = - "\uD83C\uDF89 В \"%s\" группу вступил юбилейный пользователь %s (%s), %s. \n" + "Время вступления %s"; - private static final String CONGRATULATION_TEMPLATE = - "\uD83C\uDF89 Поздравляю, %s, как же удачно попали в нужное время и в нужное время! " + - "Вы %d участник коммьюнити.\n" + - "Вас ждут плюшки и печенюшки!\uD83C\uDF89"; + private static final String NO_NICK = "ника нет"; - - private final ChatConfig chatConfig; + private final TelegramBot bot; - private final ModeratorChatService moderatorChatService; - private final UserChatService userChatService; - - - public void notifyModerators(UserJoining userJoining) { - String text = createTextNotification(userJoining); - sendNotification(text, userJoining); - } - - public void congratulateUser(Long chatId, String userName, Integer number) { - // TODO use 'number' from config - - String text = String.format(CONGRATULATION_TEMPLATE, userName, number); - bot.execute(new SendMessage(chatId, text)); - } - - private String createTextNotification(UserJoining userJoining) { - Map joiningData = getJoiningData(userJoining); - - return String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), - joiningData.get("userName"), joiningData.get("nickName"), - joiningData.get("number"), joiningData.get("joinTime")); - } - - // TODO organize mailing - private void sendNotification(String text, UserJoining userJoining) { - List moderatorChats = userChatService.getUserChats() - .get(userJoining.getChatId()).getModeratorChats(); - - for (ModeratorChat moderatorChat : moderatorChats) { - Long moderatorChatId = moderatorChat.getChatId(); - var sendMessage = new SendMessage(moderatorChatId, text) - .replyMarkup(createKeyboard(userJoining, moderatorChatId)); - - bot.execute(sendMessage); - } - } - - private Map getJoiningData(UserJoining userJoining) { - - Long chatId = userJoining.getChatId(); - var getChat = new GetChat(chatId); - var getChatResponse = bot.execute(getChat); - String chatName = getChatResponse.chat().title(); - - Long userId = userJoining.getUserId(); - var getChatMember = new GetChatMember(chatId, userId); - var getChatMemberResponse = bot.execute(getChatMember); - User user = getChatMemberResponse.chatMember().user(); - String firstName = user.firstName(); - String lastName = user.lastName(); - String userName = firstName + ((lastName == null) ? "" : (" " + lastName)); - - String nickName = user.username(); - nickName = nickName == null ? NO_NICK : nickName; - - Integer number = userJoining.getNumber(); - - DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT); - String joinTime = userJoining.getJoinTime().format(formatter); - - return Map.of("chatName", chatName, - "userName", userName, - "nickName", nickName, - "number", number, - "joinTime", joinTime); - } - - private InlineKeyboardMarkup createKeyboard(UserJoining userJoining, Long moderatorChatId) { - - var inlineKeyboardMarkup = new InlineKeyboardMarkup(); - - InlineKeyboardButton[] buttons = new InlineKeyboardButton[]{ - new InlineKeyboardButton(ButtonText.CONGRATULATE.getSolution()) - .callbackData(getCongratulateCallbackData(userJoining, moderatorChatId)), - new InlineKeyboardButton(ButtonText.DECLINE.getSolution()) - .callbackData(getDeclineCallbackData(userJoining, moderatorChatId))}; - inlineKeyboardMarkup.addRow(buttons); - - return inlineKeyboardMarkup; - } - - private String getCongratulateCallbackData(UserJoining userJoining, Long moderatorChatId) { - JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); - jsonObject.addProperty("command", "congratulate"); - return jsonObject.getAsString(); - } - - private String getDeclineCallbackData(UserJoining userJoining, Long moderatorChatId) { - JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); - jsonObject.addProperty("command", "decline"); - return jsonObject.getAsString(); - } - - @NotNull - private JsonObject getJsonObject(UserJoining userJoining, Long moderatorChatId) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("moderatorChatId", moderatorChatId); - jsonObject.addProperty("userId", userJoining.getUserId()); - jsonObject.addProperty("chatId", userJoining.getChatId()); - return jsonObject; - } - - public void removeKeyboardFromNotification() { - // TODO implement - } -} + +// public void notifyModerators(UserJoining userJoining) { +// String text = createTextNotification(userJoining); +// sendNotification(text, userJoining); +// } +// +// public void congratulateUser(Long chatId, String userName, Integer number) { +// // TODO use 'number' from config +// +// String text = String.format(CONGRATULATION_TEMPLATE, userName, number); +// bot.execute(new SendMessage(chatId, text)); +// } +// +// private String createTextNotification(UserJoining userJoining) { +// Map joiningData = getJoiningData(userJoining); +// +// return String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), +// joiningData.get("userName"), joiningData.get("nickName"), +// joiningData.get("number"), joiningData.get("joinTime")); +// } +// +// // TODO organize mailing +// private void sendNotification(String text, UserJoining userJoining) { +// List moderatorChats = userChatService.getUserChats() +// .get(userJoining.getChatId()).getModeratorChats(); +// +// for (ModeratorChat moderatorChat : moderatorChats) { +// Long moderatorChatId = moderatorChat.getChatId(); +// var sendMessage = new SendMessage(moderatorChatId, text) +// .replyMarkup(createKeyboard(userJoining, moderatorChatId)); +// +// bot.execute(sendMessage); +// } +// } +// +// private Map getJoiningData(UserJoining userJoining) { +// +// Long chatId = userJoining.getChatId(); +// var getChat = new GetChat(chatId); +// var getChatResponse = bot.execute(getChat); +// String chatName = getChatResponse.chat().title(); +// +// Long userId = userJoining.getUserId(); +// var getChatMember = new GetChatMember(chatId, userId); +// var getChatMemberResponse = bot.execute(getChatMember); +// User user = getChatMemberResponse.chatMember().user(); +// String firstName = user.firstName(); +// String lastName = user.lastName(); +// String userName = firstName + ((lastName == null) ? "" : (" " + lastName)); +// +// String nickName = user.username(); +// nickName = nickName == null ? NO_NICK : nickName; +// +// Integer number = userJoining.getNumber(); +// +// DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT); +// String joinTime = userJoining.getJoinTime().format(formatter); +// +// return Map.of("chatName", chatName, +// "userName", userName, +// "nickName", nickName, +// "number", number, +// "joinTime", joinTime); +// } +// +// private InlineKeyboardMarkup createKeyboard(UserJoining userJoining, Long moderatorChatId) { +// +// var inlineKeyboardMarkup = new InlineKeyboardMarkup(); +// +// InlineKeyboardButton[] buttons = new InlineKeyboardButton[]{ +// new InlineKeyboardButton(ButtonText.CONGRATULATE.getSolution()) +// .callbackData(getCongratulateCallbackData(userJoining, moderatorChatId)), +// new InlineKeyboardButton(ButtonText.DECLINE.getSolution()) +// .callbackData(getDeclineCallbackData(userJoining, moderatorChatId))}; +// inlineKeyboardMarkup.addRow(buttons); +// +// return inlineKeyboardMarkup; +// } +// +// private String getCongratulateCallbackData(UserJoining userJoining, Long moderatorChatId) { +// JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); +// jsonObject.addProperty("command", "congratulate"); +// return jsonObject.getAsString(); +// } +// +// private String getDeclineCallbackData(UserJoining userJoining, Long moderatorChatId) { +// JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); +// jsonObject.addProperty("command", "decline"); +// return jsonObject.getAsString(); +// } +// +// @NotNull +// private JsonObject getJsonObject(UserJoining userJoining, Long moderatorChatId) { +// JsonObject jsonObject = new JsonObject(); +// jsonObject.addProperty("moderatorChatId", moderatorChatId); +// jsonObject.addProperty("userId", userJoining.getUserId()); +// jsonObject.addProperty("chatId", userJoining.getChatId()); +// return jsonObject; +// } +// +// public void removeKeyboardFromNotification() { +// // TODO implement +// } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java deleted file mode 100644 index a99bb15..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import org.codewithoutus.tgbotusers.model.UserChat; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; - -@Service -@Getter -@Setter -@RequiredArgsConstructor -public class UserChatService { - - private Map userChats; -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java index 6e5442c..0fe8891 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java @@ -11,11 +11,11 @@ public class UserJoiningService { private final UserJoiningRepository userJoiningRepository; - public UserJoining save(UserJoining userJoining) { - return userJoiningRepository.save(userJoining); - } - - public UserJoining findByUserIdAndChatId(Long userId, Long chatId) { - return userJoiningRepository.findByUserIdAndChatId(userId, chatId); - } -} +// public UserJoining save(UserJoining userJoining) { +// return userJoiningRepository.save(userJoining); +// } +// +// public UserJoining findByUserIdAndChatId(Long userId, Long chatId) { +// return userJoiningRepository.findByUserIdAndChatId(userId, chatId); +// } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/enums/ButtonText.java b/src/main/java/org/codewithoutus/tgbotusers/service/enums/ButtonText.java deleted file mode 100644 index f39cdfc..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/enums/ButtonText.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.codewithoutus.tgbotusers.service.enums; - -import lombok.Getter; - -@Getter -public enum ButtonText { - - CONGRATULATE ("Поздравить \uD83E\uDD73"), - DECLINE ("Отклонить 🚫"); - - private final String solution; - - ButtonText(String solution) { - this.solution = solution; - } - -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 47fe252..1931735 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,4 +1,8 @@ spring: +# datasource: +# url: jdbc:postgresql://localhost:5432/tg-bot-users +# username: tg-admin +# password: 9en2w0oc datasource: url: ${DATASOURCE_URL} username: ${DATASOURCE_USER} @@ -7,7 +11,7 @@ spring: maximum-pool-size: 2 jpa: hibernate: - ddl-auto: validate + ddl-auto: none profiles: include: - bot From 862bae5c196613ef86f689bba28eb87ffe273095 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 12 Jul 2022 22:00:18 +0400 Subject: [PATCH 18/50] Small refactoring =) --- pom.xml | 26 +++++--- .../org/codewithoutus/tgbotusers/bot/Bot.java | 45 +++++++++++--- .../tgbotusers/bot/BotConfig.java | 36 +++++++++++ .../tgbotusers/bot/BotResponse.java | 7 ++- .../tgbotusers/bot/BotRightsService.java | 37 ------------ .../tgbotusers/bot/RightsService.java | 33 +++++++++++ .../tgbotusers/bot/TelegramService.java | 25 +------- .../tgbotusers/bot/UpdateService.java | 54 +++++++++++++++++ .../tgbotusers/config/AppConfig.java | 23 +------- .../tgbotusers/config/BotSettings.java | 9 +-- .../tgbotusers/config/GroupSettings.java | 6 +- .../config/NotificationTemplates.java | 3 +- .../config/UpdateHandlersConfig.java | 2 - .../handler/CallbackQueryHandler.java | 5 +- .../handler/ChatJoinRequestHandler.java | 4 +- .../tgbotusers/handler/MessageHandler.java | 49 --------------- .../handler/PrivateMessageHandler.java | 50 ++++++++++++++++ .../tgbotusers/service/MessageService.java | 58 ++++++++++++++++++ .../tgbotusers/service/ModeratorService.java | 59 ------------------- src/main/resources/application-bot.yml | 2 +- src/main/resources/application.yml | 19 +++--- 21 files changed, 325 insertions(+), 227 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/BotRightsService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/RightsService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/UpdateHandlersConfig.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/ModeratorService.java diff --git a/pom.xml b/pom.xml index 82bef94..4e3f7d6 100644 --- a/pom.xml +++ b/pom.xml @@ -2,45 +2,53 @@ 4.0.0 + org.springframework.boot spring-boot-starter-parent 2.7.1 - + + org.codewithoutus tg-bot-users 0.0.1-SNAPSHOT + tg-bot-users Demo project for Spring Boot + 17 + - org.springframework.boot - spring-boot-starter-data-jpa + com.github.pengrad + java-telegram-bot-api + 6.1.0 + org.springframework.boot spring-boot-starter-web + - com.github.pengrad - java-telegram-bot-api - 6.1.0 + org.springframework.boot + spring-boot-starter-data-jpa - org.postgresql postgresql runtime + org.projectlombok lombok - true + provided + org.springframework.boot spring-boot-starter-test @@ -65,4 +73,4 @@ - + \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java index d415b4b..ea4b1f1 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java @@ -1,27 +1,37 @@ package org.codewithoutus.tgbotusers.bot; import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.UpdatesListener; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.GetUpdates; +import org.codewithoutus.tgbotusers.handler.Handler; + +import java.util.List; public class Bot extends TelegramBot { - private final BotService botService; + private final int longPollingTimeout; + private List updateHandlers; private BotStatus status; - public Bot(String botToken, BotService botService) { + public Bot(String botToken, int longPollingTimeout) { super(botToken); - this.botService = botService; + this.longPollingTimeout = longPollingTimeout; } public BotStatus getStatus() { return status; } + public void setUpdateHandlers(List updateHandlers) { + this.updateHandlers = updateHandlers; + } + public boolean start() { if (status == BotStatus.START) { return false; } - botService.start(); - + startUpdatePolling(); status = BotStatus.START; return true; } @@ -30,9 +40,30 @@ public boolean stop() { if (status == BotStatus.STOP) { return false; } - botService.stop(); - + stopUpdatePolling(); status = BotStatus.STOP; return true; } + + private void startUpdatePolling() { + GetUpdates timeout = new GetUpdates().timeout(longPollingTimeout); + UpdatesListener updatesListener = (updates -> { + updates.forEach(this::handleUpdate); + return UpdatesListener.CONFIRMED_UPDATES_ALL; + }); + + setUpdatesListener(updatesListener, timeout); + } + + private void stopUpdatePolling() { + removeGetUpdatesListener(); + } + + private void handleUpdate(Update update) { + for (Handler handler : updateHandlers) { + if (handler.handle(update)) { + return; + } + } + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java new file mode 100644 index 0000000..c125f99 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java @@ -0,0 +1,36 @@ +package org.codewithoutus.tgbotusers.bot; + +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.Bot; +import org.codewithoutus.tgbotusers.handler.CallbackQueryHandler; +import org.codewithoutus.tgbotusers.handler.ChatJoinRequestHandler; +import org.codewithoutus.tgbotusers.handler.Handler; +import org.codewithoutus.tgbotusers.handler.PrivateMessageHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.List; + +@Configuration +@RequiredArgsConstructor +public class BotConfig { + + private final Bot bot; + + private final CallbackQueryHandler callbackQueryHandler; + private final ChatJoinRequestHandler chatJoinRequestHandler; + private final PrivateMessageHandler privateMessageHandler; + + + @PostConstruct + private void botStart() { + List updateHandlers = List.of( + privateMessageHandler, + chatJoinRequestHandler, + callbackQueryHandler); + + bot.setUpdateHandlers(updateHandlers); + bot.start(); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java index 52c4fac..fc52762 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java @@ -1,4 +1,9 @@ package org.codewithoutus.tgbotusers.bot; -public record BotResponse(boolean ok, BotStatus status) { +import lombok.Data; + +@Data +public final class BotResponse { + private final boolean ok; + private final BotStatus status; } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotRightsService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotRightsService.java deleted file mode 100644 index a45c623..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/BotRightsService.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.codewithoutus.tgbotusers.bot; - -import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.model.ChatAdministratorRights; -import com.pengrad.telegrambot.request.GetMyDefaultAdministratorRights; -import com.pengrad.telegrambot.request.SetMyDefaultAdministratorRights; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class BotRightsService { - - private final TelegramBot bot; - - public void initialiseAdministratorRights() { - if (!administratorRightsIsCorrect()) { - setAdministratorRights(); - } - } - - private boolean administratorRightsIsCorrect() { - var getRights = new GetMyDefaultAdministratorRights(); - var response = bot.execute(getRights); - var rights = response.result(); - return rights.canInviteUsers(); - } - - private void setAdministratorRights() { - var chatAdministratorRights = new ChatAdministratorRights(); // TODO add isAnonymous(boolean) - chatAdministratorRights.canInviteUsers(true); - - var setMyDefaultAdministratorRights = new SetMyDefaultAdministratorRights(); - setMyDefaultAdministratorRights.rights(chatAdministratorRights); - bot.execute(setMyDefaultAdministratorRights); - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/RightsService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/RightsService.java new file mode 100644 index 0000000..5d14b69 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/RightsService.java @@ -0,0 +1,33 @@ +package org.codewithoutus.tgbotusers.bot; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class RightsService { + +// private final TelegramBot bot; +// +// public void initialiseAdministratorRights() { +// if (!administratorRightsIsCorrect()) { +// setAdministratorRights(); +// } +// } +// +// private boolean administratorRightsIsCorrect() { +// var getRights = new GetMyDefaultAdministratorRights(); +// var response = bot.execute(getRights); +// var rights = response.result(); +// return rights.canInviteUsers(); +// } +// +// private void setAdministratorRights() { +// var chatAdministratorRights = new ChatAdministratorRights(); // TODO add isAnonymous(boolean) +// chatAdministratorRights.canInviteUsers(true); +// +// var setMyDefaultAdministratorRights = new SetMyDefaultAdministratorRights(); +// setMyDefaultAdministratorRights.rights(chatAdministratorRights); +// bot.execute(setMyDefaultAdministratorRights); +// } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java index 247ffc1..8edf7a8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.model.CallbackQuery; import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.User; @@ -22,11 +21,10 @@ @Slf4j public class TelegramService { - private final TelegramBot bot; - private final ObjectMapper objectMapper; + private final Bot bot; - public void sendMessage(Long chatId, String message) { - bot.execute(new SendMessage(chatId, message)); + public void sendMessage(Long chatId, String text) { + bot.execute(new SendMessage(chatId, text)); } public int getChatMembersCount(long chatId) { @@ -40,21 +38,4 @@ public User getUserInfo(long chatId, long userId) { var getChatMemberResponse = bot.execute(getChatMember); return getChatMemberResponse.chatMember().user(); } - - public Map getCallbackData(Update update) { - return Optional - .ofNullable(update.callbackQuery()) - .map(CallbackQuery::data) - .filter(data -> !data.isBlank()) - .map(data -> { - try { - return objectMapper.readValue(data, new TypeReference>() { - }); - } catch (JsonProcessingException e) { - log.error("CallbackQuery json parsing from update {}", this); - return null; - } - }) - .orElse(null); - } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java new file mode 100644 index 0000000..4a226a2 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java @@ -0,0 +1,54 @@ +package org.codewithoutus.tgbotusers.bot; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pengrad.telegrambot.model.CallbackQuery; +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +@Slf4j +public class UpdateService { + + private final ObjectMapper objectMapper; + + public Boolean isPrivateMessage(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::chat) + .map(chat -> chat.type() == Chat.Type.Private) + .orElse(Boolean.FALSE); + } + + public Long getChatId(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::chat) + .map(Chat::id) + .orElse(null); + } + + public Map getCallbackData(Update update) { + return Optional + .ofNullable(update.callbackQuery()) + .map(CallbackQuery::data) + .filter(data -> !data.isBlank()) + .map(data -> { + try { + return objectMapper.readValue(data, new TypeReference>() { + }); + } catch (JsonProcessingException e) { + log.error("CallbackQuery json parsing from update {}", this); + return null; + } + }) + .orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java index 3700bb6..b023a2e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java @@ -3,39 +3,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.Bot; -import org.codewithoutus.tgbotusers.bot.BotService; -import org.codewithoutus.tgbotusers.handler.CallbackQueryHandler; -import org.codewithoutus.tgbotusers.handler.ChatJoinRequestHandler; -import org.codewithoutus.tgbotusers.handler.Handler; -import org.codewithoutus.tgbotusers.handler.MessageHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.List; - @Configuration @RequiredArgsConstructor public class AppConfig { private final BotSettings botSettings; - private final BotService botService; - - private final CallbackQueryHandler callbackQueryHandler; - private final ChatJoinRequestHandler chatJoinRequestHandler; - private final MessageHandler messageHandler; @Bean public Bot telegramBot() { - return new Bot(botSettings.getBotToken(), botService); - } - - @Bean - public List telegramBotUpdateHandlers() { - // order FROM more frequent & less resource-intensive -> TO less frequent & more resource-intensive - return List.of( - messageHandler, - chatJoinRequestHandler, - callbackQueryHandler); + return new Bot(botSettings.getBotToken(), botSettings.getLongPollingTimeout()); } @Bean diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java index f84186f..98ae4f1 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java @@ -3,15 +3,16 @@ import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; @Getter @Setter -@Component +@Configuration @ConfigurationProperties(prefix = "bot-settings") public class BotSettings { - public int longPollingTimeout; - public String botToken; - public String botUserName; + private int longPollingTimeout; + private String botToken; + private String botUserName; } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java index b7b204e..b20400d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java @@ -1,11 +1,13 @@ package org.codewithoutus.tgbotusers.config; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import org.codewithoutus.tgbotusers.model.ModeratorChat; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @@ -14,7 +16,7 @@ @Getter @Setter -@Component +@Configuration @ConfigurationProperties(prefix = "groups-settings") public class GroupSettings { @@ -25,7 +27,7 @@ public class GroupSettings { private Map> moderatorGroupsData; // only used for loading from application-settings file @JsonIgnore - private List moderatorGroups; // real group-settings, mapped to DB Entities + private List moderatorsGroups; // real group-settings, mapped to DB Entities @PostConstruct private void synchronizeDataBaseSettings() { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java index 7cc5360..10367ac 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java @@ -3,11 +3,12 @@ import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; @Getter @Setter -@Component +@Configuration @ConfigurationProperties(prefix = "message-templates") public class NotificationTemplates { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/UpdateHandlersConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/UpdateHandlersConfig.java deleted file mode 100644 index ef08567..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/UpdateHandlersConfig.java +++ /dev/null @@ -1,2 +0,0 @@ -package org.codewithoutus.tgbotusers.config;public class UpdateHandlersSettings { -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java index 5b4f47c..900fec9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.codewithoutus.tgbotusers.bot.TelegramService; +import org.codewithoutus.tgbotusers.bot.UpdateService; import org.springframework.stereotype.Component; import java.util.Map; @@ -14,10 +15,10 @@ public class CallbackQueryHandler implements Handler { private final TelegramService telegramService; - + private final UpdateService updateService; @Override public boolean handle(Update update) { - Map callbackQueryData = telegramService.getCallbackData(update); + Map callbackQueryData = updateService.getCallbackData(update); if (callbackQueryData == null) { return false; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java index 930fd2f..23bba2c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java @@ -11,7 +11,7 @@ @RequiredArgsConstructor public class ChatJoinRequestHandler implements Handler { - private final TelegramService botExecutorService; + private final TelegramService telegramService; private final ChatJoinRequestService chatJoinRequestService; @Override @@ -24,7 +24,7 @@ public boolean handle(Update update) { // // TODO проверить из чата модераторов // // long chatId = chatJoinRequest.chat().id(); -// int count = botExecutorService.getCount(chatId); +// int count = telegramService.getCount(chatId); // if (count % MULTIPLICITY > INCREMENTAL_SAVES) { // return false; // } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java deleted file mode 100644 index d8235c9..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/MessageHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.MessageEntity; -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.service.MessageService; -import org.codewithoutus.tgbotusers.service.ModeratorChatService; -import org.springframework.stereotype.Component; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; - -@Component -@RequiredArgsConstructor -public class MessageHandler implements Handler { - - private final MessageService messageService; - private final ModeratorChatService moderatorChatService; - - @Override - public boolean handle(Update update) { - - Message message = update.message(); - MessageEntity[] entities = message.entities(); - if (entities == null || entities.length <= 0) { - return false; - } - - Optional botEntityOptional = Arrays.stream(message.entities()) - .filter(entity -> entity.type().equals(MessageEntity.Type.bot_command)).findFirst(); - if (botEntityOptional.isEmpty()) { - return false; - } - - Long chatId = message.chat().id(); - if (!moderatorChatService.getModeratorChats().containsKey(chatId)) { - return false; - } - - MessageEntity entity = botEntityOptional.get(); - - messageService.process(message.text(), entity); - - return true; - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java new file mode 100644 index 0000000..6aa8cd2 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java @@ -0,0 +1,50 @@ +package org.codewithoutus.tgbotusers.handler; + +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.MessageEntity; +import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.TelegramService; +import org.codewithoutus.tgbotusers.bot.UpdateService; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class PrivateMessageHandler implements Handler { + + private static final String SORRY = "Sorry, functionality not implemented"; + private final TelegramService telegramService; + private final UpdateService updateService; + + @Override + public boolean handle(Update update) { + if (!updateService.isPrivateMessage(update)) { + return false; + } + telegramService.sendMessage(updateService.getChatId(update), SORRY); + return true; + +// Message message = update.message(); +// MessageEntity[] entities = message.entities(); +// if (entities == null || entities.length <= 0) { +// return false; +// } +// +// Optional botEntityOptional = Arrays.stream(message.entities()) +// .filter(entity -> entity.type().equals(MessageEntity.Type.bot_command)).findFirst(); +// if (botEntityOptional.isEmpty()) { +// return false; +// } +// +// Long chatId = message.chat().id(); +// if (!moderatorChatService.getModeratorChats().containsKey(chatId)) { +// return false; +// } +// +// MessageEntity entity = botEntityOptional.get(); +// +// messageService.process(message.text(), entity); +// +// return true; + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java new file mode 100644 index 0000000..d622644 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java @@ -0,0 +1,58 @@ +package org.codewithoutus.tgbotusers.service; + +import com.pengrad.telegrambot.model.MessageEntity; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.UserChat; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.service.enums.BotCommand; +import org.springframework.stereotype.Service; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class MessageService { + + private final UserChatService userChatService; + private final UserJoiningService userJoiningService; + +// public void process(String text, MessageEntity entity) { +// +// if (text.contains(BotCommand.LUCKY_LIST.getCommand())) { +// performLuckyList(text, entity); +// } +// +// +// } +// +// @Deprecated +// private String getCommand(String text, MessageEntity entity) { +// int begin = entity.offset(); +// int end = begin + entity.length(); +// String command = text.substring(begin, end); +// if (command.contains("@")) { +// return command.split("@")[0]; +// } +// return command; +// } +// +// private void performLuckyList(String text, MessageEntity entity) { +// String chatName = parseParam(text, entity); +// UserChat userChat = userChatService.findByName(chatName); +// Long userChatId = userChat.getChatId(); +// List userJoinings = userJoiningService.findByChatId(userChatId); +// userJoinings.sort(Comparator.naturalOrder()); +// +// for (UserJoining userJoining : userJoinings) { +// // TODO implement logic +// } +// +// } +// +// private String parseParam(String text, MessageEntity entity) { +// String param = text.substring(entity.offset() + entity.length()); +// return param.trim(); +// } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorService.java deleted file mode 100644 index 2c76672..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ModeratorService.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import com.pengrad.telegrambot.model.MessageEntity; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.UserChat; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.service.enums.BotCommand; -import org.springframework.stereotype.Service; - -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -@Service -@RequiredArgsConstructor -public class MessageService { - - private final ModeratorChatService moderatorChatService; - private final UserChatService userChatService; - private final UserJoiningService userJoiningService; - - public void process(String text, MessageEntity entity) { - - if (text.contains(BotCommand.LUCKY_LIST.getCommand())) { - performLuckyList(text, entity); - } - - - } - - @Deprecated - private String getCommand(String text, MessageEntity entity) { - int begin = entity.offset(); - int end = begin + entity.length(); - String command = text.substring(begin, end); - if (command.contains("@")) { - return command.split("@")[0]; - } - return command; - } - - private void performLuckyList(String text, MessageEntity entity) { - String chatName = parseParam(text, entity); - UserChat userChat = userChatService.findByName(chatName); - Long userChatId = userChat.getChatId(); - List userJoinings = userJoiningService.findByChatId(userChatId); - userJoinings.sort(Comparator.naturalOrder()); - - for (UserJoining userJoining : userJoinings) { - // TODO implement logic - } - - } - - private String parseParam(String text, MessageEntity entity) { - String param = text.substring(entity.offset() + entity.length()); - return param.trim(); - } -} diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 4fdae57..170a596 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,6 +1,6 @@ groups-settings: rewrite-database-settings-on-startup: false - moderator-groups: + moderator-groups-data: 11111: - 222 - 333 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1931735..eebb24a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,17 +1,22 @@ spring: -# datasource: -# url: jdbc:postgresql://localhost:5432/tg-bot-users -# username: tg-admin -# password: 9en2w0oc + # datasource: + # url: ${DATASOURCE_URL} + # username: ${DATASOURCE_USER} + # password: ${DATASOURCE_PASS} datasource: - url: ${DATASOURCE_URL} - username: ${DATASOURCE_USER} - password: ${DATASOURCE_PASS} + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/tg-bot-users + username: tg-admin + password: 9en2w0oc hikari: maximum-pool-size: 2 + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + show_sql: true hibernate: ddl-auto: none + profiles: include: - bot From d1958f7beb5f174156520107dc082f8c2ec0bbdf Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 12 Jul 2022 22:18:35 +0400 Subject: [PATCH 19/50] variables --- src/main/resources/application.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eebb24a..0d49e0a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,13 +1,9 @@ spring: - # datasource: - # url: ${DATASOURCE_URL} - # username: ${DATASOURCE_USER} - # password: ${DATASOURCE_PASS} datasource: driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://localhost:5432/tg-bot-users - username: tg-admin - password: 9en2w0oc + url: ${DATASOURCE_URL} + username: ${DATASOURCE_USER} + password: ${DATASOURCE_PASS} hikari: maximum-pool-size: 2 From 5b222de76f488cbc5ce0b0d59bae97128b47cbef Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Wed, 13 Jul 2022 00:13:27 +0400 Subject: [PATCH 20/50] some developing --- .../org/codewithoutus/tgbotusers/bot/Bot.java | 1 + .../tgbotusers/bot/BotConfig.java | 1 - .../tgbotusers/bot/BotResponse.java | 1 + .../tgbotusers/bot/BotStatus.java | 6 --- .../enums/BotCommands.java} | 6 +-- .../tgbotusers/bot/enums/BotStatus.java | 6 +++ .../enums/CongratulationDecisionKeyboard.java | 43 +++++++++++++++++++ .../tgbotusers/bot/keyboards/KeyData.java | 4 ++ .../tgbotusers/bot/keyboards/Keyboard.java | 5 +++ .../service/ChatJoinRequestService.java | 9 +--- .../service/CongratulationService.java} | 10 ++--- .../{ => bot}/service/MessageService.java | 12 ++---- .../service/NotificationService.java | 11 +++-- .../tgbotusers/config/GroupSettings.java | 1 + .../handler/CallbackQueryHandler.java | 21 ++++++++- .../handler/ChatJoinRequestHandler.java | 2 +- .../CongratulationDecisionKeyboard.java | 16 ------- .../tgbotusers/service/UserChatService.java | 11 ++--- .../service/UserJoiningService.java | 7 ++- 19 files changed, 110 insertions(+), 63 deletions(-) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java rename src/main/java/org/codewithoutus/tgbotusers/{service/enums/BotCommand.java => bot/enums/BotCommands.java} (57%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotStatus.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java rename src/main/java/org/codewithoutus/tgbotusers/{ => bot}/service/ChatJoinRequestService.java (81%) rename src/main/java/org/codewithoutus/tgbotusers/{service/CallbackQueryService.java => bot/service/CongratulationService.java} (84%) rename src/main/java/org/codewithoutus/tgbotusers/{ => bot}/service/MessageService.java (81%) rename src/main/java/org/codewithoutus/tgbotusers/{ => bot}/service/NotificationService.java (94%) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java index ea4b1f1..263d96c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java @@ -4,6 +4,7 @@ import com.pengrad.telegrambot.UpdatesListener; import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.request.GetUpdates; +import org.codewithoutus.tgbotusers.bot.enums.BotStatus; import org.codewithoutus.tgbotusers.handler.Handler; import java.util.List; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java index c125f99..ee61b10 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java @@ -22,7 +22,6 @@ public class BotConfig { private final ChatJoinRequestHandler chatJoinRequestHandler; private final PrivateMessageHandler privateMessageHandler; - @PostConstruct private void botStart() { List updateHandlers = List.of( diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java index fc52762..e698cdd 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java @@ -1,6 +1,7 @@ package org.codewithoutus.tgbotusers.bot; import lombok.Data; +import org.codewithoutus.tgbotusers.bot.enums.BotStatus; @Data public final class BotResponse { diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java deleted file mode 100644 index 3b52ce1..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/BotStatus.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.codewithoutus.tgbotusers.bot; - -public enum BotStatus { - START, - STOP -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java similarity index 57% rename from src/main/java/org/codewithoutus/tgbotusers/service/enums/BotCommand.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java index f049fd3..78c1ad3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/enums/BotCommand.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java @@ -1,15 +1,15 @@ -package org.codewithoutus.tgbotusers.service.enums; +package org.codewithoutus.tgbotusers.bot.enums; import lombok.Getter; @Getter -public enum BotCommand { +public enum BotCommands { LUCKY_LIST("/luckyList"); private final String command; - BotCommand(String command) { + BotCommands(String command) { this.command = command; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotStatus.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotStatus.java new file mode 100644 index 0000000..3b912d3 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotStatus.java @@ -0,0 +1,6 @@ +package org.codewithoutus.tgbotusers.bot.enums; + +public enum BotStatus { + START, + STOP +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java new file mode 100644 index 0000000..7099aab --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java @@ -0,0 +1,43 @@ +package org.codewithoutus.tgbotusers.bot.enums; + +import com.google.gson.JsonObject; +import lombok.Getter; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; + +import java.util.Arrays; +import java.util.Map; +import java.util.Optional; + +@Getter +public enum CongratulationDecisionKeyboard { + + CONGRATULATE("/congratulate", "Поздравить \uD83E\uDD73", CongratulateStatus.CONGRATULATE), + DECLINE("/decline", "Отклонить 🚫", CongratulateStatus.DECLINE); + + private final String command; + private final String representation; + private final CongratulateStatus status; + + CongratulationDecisionKeyboard(String command, String representation, CongratulateStatus status) { + this.command = command; + this.representation = representation; + this.status = status; + } + + public String buildJsonKeyData(UserJoining userJoining, Long moderatorChatId) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("moderatorChatId", moderatorChatId); + jsonObject.addProperty("userId", userJoining.getUserId()); + jsonObject.addProperty("chatId", userJoining.getChatId()); + jsonObject.addProperty("command", command); + return jsonObject.getAsString(); + } + + public static Optional defineKey(Map callbackQueryData) { + String command = callbackQueryData.get("command"); + return Arrays.stream(CongratulationDecisionKeyboard.values()) + .filter(value -> value.getCommand().equals(command)) + .findFirst(); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java new file mode 100644 index 0000000..2f66661 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java @@ -0,0 +1,4 @@ +package org.codewithoutus.tgbotusers.bot.keyboards; + +public interface KeyData { +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java new file mode 100644 index 0000000..810cf31 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java @@ -0,0 +1,5 @@ +package org.codewithoutus.tgbotusers.bot.keyboards; + +public interface Keyboard { + public String buildJsonKeyData(KeyData keyData); +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/ChatJoinRequestService.java similarity index 81% rename from src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/service/ChatJoinRequestService.java index ac18ab8..c173045 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/ChatJoinRequestService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/ChatJoinRequestService.java @@ -1,14 +1,9 @@ -package org.codewithoutus.tgbotusers.service; +package org.codewithoutus.tgbotusers.bot.service; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.codewithoutus.tgbotusers.service.UserJoiningService; import org.springframework.stereotype.Service; -import java.time.Instant; -import java.time.LocalDateTime; -import java.util.Map; - @Service @RequiredArgsConstructor public class ChatJoinRequestService { diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/CongratulationService.java similarity index 84% rename from src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/service/CongratulationService.java index 780f952..7d1f2f4 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CallbackQueryService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/CongratulationService.java @@ -1,19 +1,17 @@ -package org.codewithoutus.tgbotusers.service; +package org.codewithoutus.tgbotusers.bot.service; -import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.request.GetChatMember; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.model.UserJoining; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.codewithoutus.tgbotusers.service.UserJoiningService; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -public class CallbackQueryService { - - private final TelegramBot bot; - private final NotificationService notificationService; +public class CongratulationService { + private final UserJoiningService userJoiningService; // public void process(UserJoining userJoining, CongratulateStatus settableStatus) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/MessageService.java similarity index 81% rename from src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/service/MessageService.java index d622644..2b958d5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/MessageService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/MessageService.java @@ -1,16 +1,10 @@ -package org.codewithoutus.tgbotusers.service; +package org.codewithoutus.tgbotusers.bot.service; -import com.pengrad.telegrambot.model.MessageEntity; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.UserChat; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.service.enums.BotCommand; +import org.codewithoutus.tgbotusers.service.UserChatService; +import org.codewithoutus.tgbotusers.service.UserJoiningService; import org.springframework.stereotype.Service; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - @Service @RequiredArgsConstructor public class MessageService { diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java similarity index 94% rename from src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index 53b56aa..cf334ac 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -1,7 +1,10 @@ -package org.codewithoutus.tgbotusers.service; +package org.codewithoutus.tgbotusers.bot.service; +import com.google.gson.JsonObject; import com.pengrad.telegrambot.TelegramBot; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; @Service @@ -111,7 +114,7 @@ public class NotificationService { // return jsonObject; // } // -// public void removeKeyboardFromNotification() { -// // TODO implement -// } +//// public void removeKeyboardFromNotification() { +//// // TODO implement +//// } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java index b20400d..c6b413d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java @@ -31,5 +31,6 @@ public class GroupSettings { @PostConstruct private void synchronizeDataBaseSettings() { + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java index 900fec9..b8db98a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java @@ -5,9 +5,13 @@ import lombok.extern.slf4j.Slf4j; import org.codewithoutus.tgbotusers.bot.TelegramService; import org.codewithoutus.tgbotusers.bot.UpdateService; +import org.codewithoutus.tgbotusers.bot.enums.CongratulationDecisionKeyboard; +import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.service.UserJoiningService; import org.springframework.stereotype.Component; import java.util.Map; +import java.util.Optional; @Component @RequiredArgsConstructor @@ -16,6 +20,8 @@ public class CallbackQueryHandler implements Handler { private final TelegramService telegramService; private final UpdateService updateService; + private final UserJoiningService userJoiningService; + @Override public boolean handle(Update update) { Map callbackQueryData = updateService.getCallbackData(update); @@ -23,6 +29,19 @@ public boolean handle(Update update) { return false; } + // Congratulation decision + Optional key = CongratulationDecisionKeyboard.defineKey(callbackQueryData); + if (key.isPresent()) { + CongratulationDecisionKeyboard decision = key.get(); +// UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); +// userJoining.setStatus(decision.getStatus()); +// userJoiningService.save(userJoining); + if (decision == CongratulationDecisionKeyboard.CONGRATULATE) { + // send message + } + return true; + } +// // Long moderatorChatId = Long.valueOf(callbackQueryData.get("moderatorChatId")); // Long chatId = Long.valueOf(callbackQueryData.get("chatId")); // List moderatorChats = userChatService.getUserChats() @@ -47,7 +66,7 @@ public boolean handle(Update update) { // throw new CommandNotFoundException("Wrong callback data. Callback data does not contain a command."); // } // } -// + return false; } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java index 23bba2c..19a13ef 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java @@ -4,7 +4,7 @@ import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.TelegramService; -import org.codewithoutus.tgbotusers.service.ChatJoinRequestService; +import org.codewithoutus.tgbotusers.bot.service.ChatJoinRequestService; import org.springframework.stereotype.Component; @Component diff --git a/src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java deleted file mode 100644 index 622f71d..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/keyboard/CongratulationDecisionKeyboard.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.codewithoutus.tgbotusers.keyboard; - -import lombok.Getter; - -@Getter -public enum CongratulationDecisionKeyboard { - - CONGRATULATE("Поздравить \uD83E\uDD73"), - DECLINE("Отклонить 🚫"); - - private final String solution; - - CongratulationDecisionKeyboard(String solution) { - this.solution = solution; - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java index fb6302f..1d6db21 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java @@ -7,19 +7,14 @@ import org.codewithoutus.tgbotusers.repository.UserChatRepository; import org.springframework.stereotype.Service; -import java.util.List; -import java.util.Map; - -@Service @Getter @Setter +@Service @RequiredArgsConstructor public class UserChatService { - + private final UserChatRepository userChatRepository; - - private Map userChats; - + public UserChat findByName(String name) { return userChatRepository.findByName(name); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java index 993b097..c2c1d80 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import org.codewithoutus.tgbotusers.repository.UserJoiningRepository; import org.springframework.stereotype.Service; @@ -12,7 +13,11 @@ public class UserJoiningService { private final UserJoiningRepository userJoiningRepository; - + +// public void setCongratulateStatus(UserJoining userJoining, CongratulateStatus settableStatus) { +// userJoining +// } + public UserJoining save(UserJoining userJoining) { return userJoiningRepository.save(userJoining); } From 3a5a28c0e94a53baa3c88db93ec1cf73adcdb2f1 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Thu, 14 Jul 2022 02:49:24 +0400 Subject: [PATCH 21/50] Application rethinking, change architecture, refactoring, renaming --- .../org/codewithoutus/tgbotusers/bot/Bot.java | 70 ++----- .../tgbotusers/bot/BotConfig.java | 35 ---- .../tgbotusers/bot/TelegramService.java | 41 ---- .../tgbotusers/bot/UpdateService.java | 54 ------ .../tgbotusers/bot/enums/BotCommands.java | 18 +- .../enums/CongratulationDecisionKeyboard.java | 43 ----- .../CallbackDataMappingException.java | 8 + .../exception/CommandNotFoundException.java | 5 +- .../TelegramSendMessageException.java | 8 + .../bot/handler/CallbackQueryHandler.java | 115 +++++++++++ .../bot/handler/ChatJoinRequestHandler.java | 61 ++++++ .../tgbotusers/bot/handler/Handler.java | 19 ++ .../bot/handler/PrivateMessageHandler.java | 47 +++++ .../tgbotusers/bot/handler/UpdateUtils.java | 62 ++++++ .../CongratulationDecisionKeyboard.java | 19 ++ .../tgbotusers/bot/keyboard/Keyboard.java | 9 + .../bot/keyboard/KeyboardUtils.java | 29 +++ .../tgbotusers/bot/keyboards/KeyData.java | 4 - .../tgbotusers/bot/keyboards/Keyboard.java | 5 - .../tgbotusers/bot/service/BotService.java | 73 +++++++ .../bot/service/ChatJoinRequestService.java | 33 ---- .../bot/service/CongratulationService.java | 37 ---- .../bot/service/MessageService.java | 52 ----- .../bot/service/NotificationService.java | 181 +++++++----------- .../bot/{ => service}/RightsService.java | 2 +- .../bot/service/TelegramService.java | 55 ++++++ .../tgbotusers/config/AppConfig.java | 24 --- .../tgbotusers/config/AppStaticContext.java | 9 + .../tgbotusers/config/BotSettings.java | 1 - .../tgbotusers/config/ChatSettings.java | 83 ++++++++ .../tgbotusers/config/GroupSettings.java | 36 ---- .../config/NotificationTemplates.java | 1 - .../tgbotusers/controller/BotController.java | 23 ++- .../{bot => controller/dto}/BotResponse.java | 2 +- .../handler/CallbackQueryHandler.java | 72 ------- .../handler/ChatJoinRequestHandler.java | 49 ----- .../tgbotusers/handler/Handler.java | 7 - .../handler/PrivateMessageHandler.java | 50 ----- .../tgbotusers/model/Congratulation.java | 29 --- .../codewithoutus/tgbotusers/model/User.java | 28 --- .../tgbotusers/model/UserChat.java | 47 ----- .../model/{ => entity}/Administrator.java | 8 +- .../ChatModerator.java} | 31 ++- .../tgbotusers/model/entity/ChatUser.java | 33 ++++ .../model/entity/TelegramCallbackData.java | 20 ++ .../model/{ => entity}/UserJoining.java | 30 +-- .../model/entity/UserJoiningNotification.java | 31 +++ .../repository/AdministratorRepository.java | 4 +- .../repository/ChatModeratorRepository.java | 13 ++ .../model/repository/ChatUserRepository.java | 15 ++ .../TelegramCallbackDataRepository.java | 11 ++ .../UserJoiningNotificationRepository.java | 13 ++ .../repository/UserJoiningRepository.java | 8 +- .../service/AdministratorService.java | 2 +- .../model/service/ChatModeratorService.java | 34 ++++ .../model/service/ChatUserService.java | 35 ++++ .../service/TelegramCallbackDataService.java | 41 ++++ .../UserJoiningNotificationService.java | 23 +++ .../service/UserJoiningService.java | 19 +- .../repository/CongratulationRepository.java | 9 - .../repository/ModeratorChatRepository.java | 9 - .../repository/UserChatRepository.java | 11 -- .../tgbotusers/repository/UserRepository.java | 9 - .../service/CongratulationService.java | 9 - .../tgbotusers/service/UserChatService.java | 21 -- src/main/resources/application-bot.yml | 5 +- src/main/resources/application.yml | 2 +- 67 files changed, 1020 insertions(+), 972 deletions(-) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/exception/CallbackDataMappingException.java rename src/main/java/org/codewithoutus/tgbotusers/{ => bot}/exception/CommandNotFoundException.java (73%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/exception/TelegramSendMessageException.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/Handler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/service/ChatJoinRequestService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/service/CongratulationService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/service/MessageService.java rename src/main/java/org/codewithoutus/tgbotusers/bot/{ => service}/RightsService.java (95%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/AppStaticContext.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java rename src/main/java/org/codewithoutus/tgbotusers/{bot => controller/dto}/BotResponse.java (77%) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/User.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java rename src/main/java/org/codewithoutus/tgbotusers/model/{ => entity}/Administrator.java (57%) rename src/main/java/org/codewithoutus/tgbotusers/model/{ModeratorChat.java => entity/ChatModerator.java} (52%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java rename src/main/java/org/codewithoutus/tgbotusers/model/{ => entity}/UserJoining.java (52%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java rename src/main/java/org/codewithoutus/tgbotusers/{ => model}/repository/AdministratorRepository.java (64%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java rename src/main/java/org/codewithoutus/tgbotusers/{ => model}/repository/UserJoiningRepository.java (74%) rename src/main/java/org/codewithoutus/tgbotusers/{ => model}/service/AdministratorService.java (75%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java rename src/main/java/org/codewithoutus/tgbotusers/{ => model}/service/UserJoiningService.java (62%) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java index 263d96c..61daefa 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/Bot.java @@ -1,70 +1,22 @@ package org.codewithoutus.tgbotusers.bot; import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.UpdatesListener; -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.request.GetUpdates; +import lombok.Getter; +import lombok.Setter; import org.codewithoutus.tgbotusers.bot.enums.BotStatus; -import org.codewithoutus.tgbotusers.handler.Handler; - -import java.util.List; +import org.codewithoutus.tgbotusers.config.BotSettings; +import org.springframework.stereotype.Component; +@Getter +@Setter +@Component public class Bot extends TelegramBot { - private final int longPollingTimeout; - private List updateHandlers; + private final BotSettings botSettings; private BotStatus status; - public Bot(String botToken, int longPollingTimeout) { - super(botToken); - this.longPollingTimeout = longPollingTimeout; - } - - public BotStatus getStatus() { - return status; - } - - public void setUpdateHandlers(List updateHandlers) { - this.updateHandlers = updateHandlers; - } - - public boolean start() { - if (status == BotStatus.START) { - return false; - } - startUpdatePolling(); - status = BotStatus.START; - return true; - } - - public boolean stop() { - if (status == BotStatus.STOP) { - return false; - } - stopUpdatePolling(); - status = BotStatus.STOP; - return true; - } - - private void startUpdatePolling() { - GetUpdates timeout = new GetUpdates().timeout(longPollingTimeout); - UpdatesListener updatesListener = (updates -> { - updates.forEach(this::handleUpdate); - return UpdatesListener.CONFIRMED_UPDATES_ALL; - }); - - setUpdatesListener(updatesListener, timeout); - } - - private void stopUpdatePolling() { - removeGetUpdatesListener(); - } - - private void handleUpdate(Update update) { - for (Handler handler : updateHandlers) { - if (handler.handle(update)) { - return; - } - } + public Bot(BotSettings botSettings) { + super(botSettings.getBotToken()); + this.botSettings = botSettings; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java b/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java deleted file mode 100644 index ee61b10..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/BotConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.codewithoutus.tgbotusers.bot; - -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.Bot; -import org.codewithoutus.tgbotusers.handler.CallbackQueryHandler; -import org.codewithoutus.tgbotusers.handler.ChatJoinRequestHandler; -import org.codewithoutus.tgbotusers.handler.Handler; -import org.codewithoutus.tgbotusers.handler.PrivateMessageHandler; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import javax.annotation.PostConstruct; -import java.util.List; - -@Configuration -@RequiredArgsConstructor -public class BotConfig { - - private final Bot bot; - - private final CallbackQueryHandler callbackQueryHandler; - private final ChatJoinRequestHandler chatJoinRequestHandler; - private final PrivateMessageHandler privateMessageHandler; - - @PostConstruct - private void botStart() { - List updateHandlers = List.of( - privateMessageHandler, - chatJoinRequestHandler, - callbackQueryHandler); - - bot.setUpdateHandlers(updateHandlers); - bot.start(); - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java deleted file mode 100644 index 8edf7a8..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/TelegramService.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.codewithoutus.tgbotusers.bot; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.pengrad.telegrambot.model.CallbackQuery; -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.model.User; -import com.pengrad.telegrambot.request.GetChatMember; -import com.pengrad.telegrambot.request.GetChatMemberCount; -import com.pengrad.telegrambot.request.SendMessage; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.Map; -import java.util.Optional; - -@Service -@RequiredArgsConstructor -@Slf4j -public class TelegramService { - - private final Bot bot; - - public void sendMessage(Long chatId, String text) { - bot.execute(new SendMessage(chatId, text)); - } - - public int getChatMembersCount(long chatId) { - var getChatMemberCount = new GetChatMemberCount(chatId); - var getChatMemberCountResponse = bot.execute(getChatMemberCount); - return getChatMemberCountResponse.count(); - } - - public User getUserInfo(long chatId, long userId) { - var getChatMember = new GetChatMember(chatId, userId); - var getChatMemberResponse = bot.execute(getChatMember); - return getChatMemberResponse.chatMember().user(); - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java deleted file mode 100644 index 4a226a2..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateService.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.codewithoutus.tgbotusers.bot; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.pengrad.telegrambot.model.CallbackQuery; -import com.pengrad.telegrambot.model.Chat; -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.Map; -import java.util.Optional; - -@Service -@RequiredArgsConstructor -@Slf4j -public class UpdateService { - - private final ObjectMapper objectMapper; - - public Boolean isPrivateMessage(Update update) { - return Optional.ofNullable(update.message()) - .map(Message::chat) - .map(chat -> chat.type() == Chat.Type.Private) - .orElse(Boolean.FALSE); - } - - public Long getChatId(Update update) { - return Optional.ofNullable(update.message()) - .map(Message::chat) - .map(Chat::id) - .orElse(null); - } - - public Map getCallbackData(Update update) { - return Optional - .ofNullable(update.callbackQuery()) - .map(CallbackQuery::data) - .filter(data -> !data.isBlank()) - .map(data -> { - try { - return objectMapper.readValue(data, new TypeReference>() { - }); - } catch (JsonProcessingException e) { - log.error("CallbackQuery json parsing from update {}", this); - return null; - } - }) - .orElse(null); - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java index 78c1ad3..b68de62 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java @@ -4,12 +4,14 @@ @Getter public enum BotCommands { - - LUCKY_LIST("/luckyList"); - - private final String command; - - BotCommands(String command) { - this.command = command; + + LUCKY_LIST("/luckyList"), + CONGRATULATE("/congratulate"), + DECLINE("/decline"); + + private final String text; + + BotCommands(String text) { + this.text = text; } -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java deleted file mode 100644 index 7099aab..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/CongratulationDecisionKeyboard.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.enums; - -import com.google.gson.JsonObject; -import lombok.Getter; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; - -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; - -@Getter -public enum CongratulationDecisionKeyboard { - - CONGRATULATE("/congratulate", "Поздравить \uD83E\uDD73", CongratulateStatus.CONGRATULATE), - DECLINE("/decline", "Отклонить 🚫", CongratulateStatus.DECLINE); - - private final String command; - private final String representation; - private final CongratulateStatus status; - - CongratulationDecisionKeyboard(String command, String representation, CongratulateStatus status) { - this.command = command; - this.representation = representation; - this.status = status; - } - - public String buildJsonKeyData(UserJoining userJoining, Long moderatorChatId) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("moderatorChatId", moderatorChatId); - jsonObject.addProperty("userId", userJoining.getUserId()); - jsonObject.addProperty("chatId", userJoining.getChatId()); - jsonObject.addProperty("command", command); - return jsonObject.getAsString(); - } - - public static Optional defineKey(Map callbackQueryData) { - String command = callbackQueryData.get("command"); - return Arrays.stream(CongratulationDecisionKeyboard.values()) - .filter(value -> value.getCommand().equals(command)) - .findFirst(); - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/exception/CallbackDataMappingException.java b/src/main/java/org/codewithoutus/tgbotusers/bot/exception/CallbackDataMappingException.java new file mode 100644 index 0000000..56d727c --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/exception/CallbackDataMappingException.java @@ -0,0 +1,8 @@ +package org.codewithoutus.tgbotusers.bot.exception; + +public class CallbackDataMappingException extends RuntimeException { + + public CallbackDataMappingException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/exception/CommandNotFoundException.java b/src/main/java/org/codewithoutus/tgbotusers/bot/exception/CommandNotFoundException.java similarity index 73% rename from src/main/java/org/codewithoutus/tgbotusers/exception/CommandNotFoundException.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/exception/CommandNotFoundException.java index cb84613..f429f4e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/exception/CommandNotFoundException.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/exception/CommandNotFoundException.java @@ -1,7 +1,8 @@ -package org.codewithoutus.tgbotusers.exception; +package org.codewithoutus.tgbotusers.bot.exception; public class CommandNotFoundException extends RuntimeException { + public CommandNotFoundException(String message) { super(message); } -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/exception/TelegramSendMessageException.java b/src/main/java/org/codewithoutus/tgbotusers/bot/exception/TelegramSendMessageException.java new file mode 100644 index 0000000..7ae5575 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/exception/TelegramSendMessageException.java @@ -0,0 +1,8 @@ +package org.codewithoutus.tgbotusers.bot.exception; + +public class TelegramSendMessageException extends RuntimeException { + + public TelegramSendMessageException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java new file mode 100644 index 0000000..1a5c604 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -0,0 +1,115 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.pengrad.telegrambot.model.Update; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.exception.CommandNotFoundException; +import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; +import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; +import org.codewithoutus.tgbotusers.bot.service.NotificationService; +import org.codewithoutus.tgbotusers.config.AppStaticContext; +import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.codewithoutus.tgbotusers.model.service.TelegramCallbackDataService; +import org.codewithoutus.tgbotusers.model.service.UserJoiningService; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CallbackQueryHandler extends Handler { + + private final NotificationService notificationService; + private final UserJoiningService userJoiningService; + private final TelegramCallbackDataService telegramCallbackDataService; + + @Override + protected boolean handle(Update update) { + Map callbackQueryData = UpdateUtils.getCallbackQueryDataAsMap(update); + if (callbackQueryData == null || callbackQueryData.isEmpty()) { + return false; + } + String command = (String) callbackQueryData.get("command"); + if (command == null || command.isBlank()) { + return false; + } + + if (handleCongratulationDecision(command, callbackQueryData) + || handleProvidingAnniversaryStatistic(command, callbackQueryData)) { + return true; + + } else { + log.error("Unhandled command: {}", callbackQueryData); + throw new CommandNotFoundException("Unhandled command: " + callbackQueryData); + } + } + + private boolean handleCongratulationDecision(String command, Map callbackQueryData) { + // TODO: доделать реакцию на команду модераторов "поздравить" или "отклонить" + Optional key = KeyboardUtils.defineKey(CongratulationDecisionKeyboard.class, command); + if (key.isEmpty()) { + return false; + } + CongratulationDecisionKeyboard decision = key.get(); + Map callbackData = getCallbackDataFromDatabase(callbackQueryData); + + // TODO: проверить, что данные есть в мапе callbackData + // TODO: все данные определены в NotificationService.notifyModeratorsAboutUserJoining() + Long chatId = (Long) callbackData.get("chatId"); + Long userId = (Long) callbackData.get("userId"); + Integer anniversaryNumber = (Integer) callbackData.get("anniversaryNumber"); + + // TODO: проверить, что уже не поздравили кого-тотме, иначе отмена + // TODO: проверить, что кого хотим поздравить в статусе WAIT, иначе отмена + + CongratulateStatus newStatus = (decision == CongratulationDecisionKeyboard.CONGRATULATE) + ? CongratulateStatus.CONGRATULATE + : CongratulateStatus.DECLINE; + UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); + userJoining.setStatus(newStatus); + userJoiningService.save(userJoining); + + if (decision == CongratulationDecisionKeyboard.CONGRATULATE) { + // TODO: удалить кнопки у всех неудачников =) + // notificationService.deleteKeyboardFromJoiningNotifications(chatId, LIST, anniversaryNumber); + notificationService.notifyUserAboutJoining(chatId, userId, anniversaryNumber); + + } else { + notificationService.deleteKeyboardFromJoiningNotification(chatId, userId, anniversaryNumber); + } + return true; + } + + private boolean handleProvidingAnniversaryStatistic(String command, Map callbackQueryData) { + // TODO: реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" + // TODO: уже поздравленные имеют корону, можно добавить в шаблоны и вставлять в начало, неудачники не показываются + // TODO: если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard + // TODO: если поздравленного нет, то отклоненные тоже выводятся + // TODO: все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись + // TODO: можно попробовать выводить все-все чаты, а потом доработать -- на будущее + return false; + } + + private Map getCallbackDataFromDatabase(Map callbackQueryData) { + Integer id = (Integer) callbackQueryData.get(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD); + if (id == null) { + return Collections.emptyMap(); + } + try { + TelegramCallbackData telegramCallbackData = telegramCallbackDataService.findById(id); + if (telegramCallbackData != null) { + return AppStaticContext.OBJECT_MAPPER.readValue(telegramCallbackData.getData(), new TypeReference<>() { + }); + } + } catch (Exception ex) { + log.error("CallbackData with id {} deserialize error", id); + } + return Collections.emptyMap(); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java new file mode 100644 index 0000000..fe3934d --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java @@ -0,0 +1,61 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.ChatJoinRequest; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.User; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.service.NotificationService; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.config.ChatSettings; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.codewithoutus.tgbotusers.model.service.UserJoiningService; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.LocalDateTime; + +@Component +@RequiredArgsConstructor +public class ChatJoinRequestHandler extends Handler { + + private final ChatSettings chatSettings; + private final ChatUserService chatUserService; + private final TelegramService telegramService; + private final NotificationService notificationService; + private final UserJoiningService userJoiningService; + + @Override + protected boolean handle(Update update) { + ChatJoinRequest chatJoinRequest = update.chatJoinRequest(); + if (chatJoinRequest == null) { + return false; + } + + // TODO: если присоединяется сразу несколько в чат -- на будущее + // TODO: если присоединяющийся пользователь бот и юбилейный -- на далёкое будущее + // TODO: юбилейные номера могут быть разные для каждого чата -- на далёкое будущее + + User user = chatJoinRequest.from(); + long chatId = chatJoinRequest.chat().id(); + int joinNumber = telegramService.getChatMembersCount(chatId); + int anniversaryNumber = chatSettings.getAnniversaryJoinNumber(chatId, joinNumber); + + if (anniversaryNumber == 0 || !chatUserService.isChatUser(chatId)) { + return false; + } + + UserJoining userJoining = new UserJoining(); + userJoining.setChatId(chatId); + userJoining.setUserId(user.id()); + userJoining.setNumber(joinNumber); + userJoining.setJoinTime(LocalDateTime.from(Instant.ofEpochSecond(chatJoinRequest.date()))); + userJoining = userJoiningService.save(userJoining); + + // TODO: оповещение модераторов + // проверяем, что ещё никто не был поздравлен в этой группе с этим юбилейным номером, иначе ничего не делаем + // notificationService.notifyModeratorsAboutUserJoining(); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/Handler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/Handler.java new file mode 100644 index 0000000..23fb737 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/Handler.java @@ -0,0 +1,19 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.Update; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public abstract class Handler { + + public boolean tryHandle(Update update) { + try { + return handle(update); + } catch (Exception ex) { + log.error(ex.getMessage()); + return false; + } + } + + abstract protected boolean handle(Update update); +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java new file mode 100644 index 0000000..7363f20 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -0,0 +1,47 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.request.InlineKeyboardButton; +import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class PrivateMessageHandler extends Handler { + + private static final String SORRY = "Sorry, functionality not implemented"; + private final TelegramService telegramService; + + @Override + protected boolean handle(Update update) { + if (!UpdateUtils.isPrivateMessage(update)) { + return false; + } + + String text = UpdateUtils.getMessageText(update); + Chat chat = UpdateUtils.getChat(update); + + if (text.startsWith("/test ")) { + telegramService.sendMessage(new SendMessage(chat.id(), "Идёт тест...")); + + if (text.startsWith("/test InlineKeyboard")) { + String callbackData = text.substring("/test InlineKeyboard".length() - 1).trim(); + if (!callbackData.isBlank()) { + InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( + new InlineKeyboardButton("callback_data").callbackData(callbackData)); + telegramService.sendMessage(new SendMessage(chat.id(), "InlineKeyboard test").replyMarkup(keyboard)); + return true; + } + } + } + + // TODO: реализовать админку: добавление и удаление чатов + + telegramService.sendMessage(new SendMessage(chat.id(), SORRY)); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java new file mode 100644 index 0000000..611aabd --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java @@ -0,0 +1,62 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.pengrad.telegrambot.model.*; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.exception.CallbackDataMappingException; +import org.codewithoutus.tgbotusers.config.AppStaticContext; + +import java.util.Map; +import java.util.Optional; + +@Slf4j +public class UpdateUtils { + + public static Boolean isPrivateMessage(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::chat) + .map(chat -> chat.type() == Chat.Type.Private) + .orElse(Boolean.FALSE); + } + + public static String getMessageText(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::text) + .orElse(""); + } + + public static Chat getChat(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::chat) + .orElse(null); + } + + public static User getFromUser(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::from) + .orElse(null); + } + + public static String getCallbackQueryData(Update update) { + return Optional + .ofNullable(update.callbackQuery()) + .map(CallbackQuery::data) + .filter(data -> !data.isBlank()) + .orElse("{}"); + } + + public static Map getCallbackQueryDataAsMap(Update update) { + return getCallbackQueryDataAsMap(getCallbackQueryData(update)); + } + + public static Map getCallbackQueryDataAsMap(String callbackData) { + try { + return AppStaticContext.OBJECT_MAPPER.readValue(callbackData, new TypeReference<>() { + }); + } catch (JsonProcessingException e) { + log.error("CallbackQuery json parsing to map error {}", callbackData); + throw new CallbackDataMappingException(callbackData); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java new file mode 100644 index 0000000..bd80f33 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java @@ -0,0 +1,19 @@ +package org.codewithoutus.tgbotusers.bot.keyboard; + +import lombok.Getter; +import org.codewithoutus.tgbotusers.bot.enums.BotCommands; + +@Getter +public enum CongratulationDecisionKeyboard implements Keyboard { + + CONGRATULATE(BotCommands.CONGRATULATE, "Поздравить \uD83E\uDD73"), + DECLINE(BotCommands.DECLINE, "Отклонить 🚫"); + + private final BotCommands botCommand; + private final String representation; + + CongratulationDecisionKeyboard(BotCommands botCommand, String representation) { + this.botCommand = botCommand; + this.representation = representation; + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java new file mode 100644 index 0000000..1cc835d --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java @@ -0,0 +1,9 @@ +package org.codewithoutus.tgbotusers.bot.keyboard; + +import org.codewithoutus.tgbotusers.bot.enums.BotCommands; + +public interface Keyboard { + + BotCommands getBotCommand(); + String getRepresentation(); +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java new file mode 100644 index 0000000..50b0b39 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java @@ -0,0 +1,29 @@ +package org.codewithoutus.tgbotusers.bot.keyboard; + +import com.pengrad.telegrambot.model.request.InlineKeyboardButton; +import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import org.codewithoutus.tgbotusers.config.AppStaticContext; + +import java.util.Arrays; +import java.util.Optional; + +public class KeyboardUtils { + + public static InlineKeyboardMarkup createKeyboard(Class enumClass, String callbackDataId) { + return new InlineKeyboardMarkup(Arrays + .stream(enumClass.getEnumConstants()) + .map(value -> new InlineKeyboardButton(value.getRepresentation()).callbackData( + AppStaticContext.OBJECT_MAPPER + .createObjectNode() + .put("command", value.getBotCommand().getText()) + .put(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD, callbackDataId) + .toString())) + .toArray(InlineKeyboardButton[]::new)); + } + + public static Optional defineKey(Class enumClass, String command) { + return Arrays.stream(enumClass.getEnumConstants()) + .filter(value -> value.getBotCommand().getText().equals(command)) + .findFirst(); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java deleted file mode 100644 index 2f66661..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/KeyData.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.keyboards; - -public interface KeyData { -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java deleted file mode 100644 index 810cf31..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboards/Keyboard.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.keyboards; - -public interface Keyboard { - public String buildJsonKeyData(KeyData keyData); -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java new file mode 100644 index 0000000..67bb0c2 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java @@ -0,0 +1,73 @@ +package org.codewithoutus.tgbotusers.bot.service; + +import com.pengrad.telegrambot.UpdatesListener; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.GetUpdates; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.Bot; +import org.codewithoutus.tgbotusers.bot.enums.BotStatus; +import org.codewithoutus.tgbotusers.bot.handler.CallbackQueryHandler; +import org.codewithoutus.tgbotusers.bot.handler.ChatJoinRequestHandler; +import org.codewithoutus.tgbotusers.bot.handler.PrivateMessageHandler; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +@Service +@RequiredArgsConstructor +public class BotService { + + private final Bot bot; + private final CallbackQueryHandler callbackQueryHandler; + private final ChatJoinRequestHandler chatJoinRequestHandler; + private final PrivateMessageHandler privateMessageHandler; + + @PostConstruct + private void botStart() { + start(); + } + + public boolean start() { + if (bot.getStatus() == BotStatus.START) { + return false; + } + startUpdatePolling(); + bot.setStatus(BotStatus.START); + return true; + } + + public boolean stop() { + if (bot.getStatus() == BotStatus.STOP) { + return false; + } + stopUpdatePolling(); + bot.setStatus(BotStatus.STOP); + return true; + } + + public BotStatus getStatus() { + return bot.getStatus(); + } + + private void startUpdatePolling() { + GetUpdates timeout = new GetUpdates().timeout(bot.getBotSettings().getLongPollingTimeout()); + UpdatesListener updatesListener = (updates -> { + updates.forEach(this::handleUpdate); + return UpdatesListener.CONFIRMED_UPDATES_ALL; + }); + + bot.setUpdatesListener(updatesListener, timeout); + } + + private void stopUpdatePolling() { + bot.removeGetUpdatesListener(); + } + + private void handleUpdate(Update update) { + if (privateMessageHandler.tryHandle(update) + || callbackQueryHandler.tryHandle(update) + || chatJoinRequestHandler.tryHandle(update)) { + // update handled, return + } + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/ChatJoinRequestService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/ChatJoinRequestService.java deleted file mode 100644 index c173045..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/ChatJoinRequestService.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.service; - -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.service.UserJoiningService; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class ChatJoinRequestService { - - private final UserJoiningService userJoiningService; - private final NotificationService notificationService; - -// public void process(Map userData) { -// UserJoining userJoining = buildUserJoining(userData); -// userJoiningService.save(userJoining); -// notificationService.notifyModerators(userJoining); -// } - -// private UserJoining buildUserJoining(Map userData) { -// -// UserJoining userJoining = new UserJoining(); -// -// userJoining.setUserId((Long) userData.get("userId")); -// userJoining.setChatId((Long) userData.get("chatId")); -// userJoining.setNumber((Integer) userData.get("count")); -// Instant time = Instant.ofEpochSecond((Long) userData.get("data")); -// userJoining.setJoinTime(LocalDateTime.from(time)); -// userJoining.setStatus(CongratulateStatus.WAIT); -// -// return userJoining; -// } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/CongratulationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/CongratulationService.java deleted file mode 100644 index 7d1f2f4..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/CongratulationService.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.service; - -import com.pengrad.telegrambot.model.User; -import com.pengrad.telegrambot.request.GetChatMember; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.codewithoutus.tgbotusers.service.UserJoiningService; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class CongratulationService { - - private final UserJoiningService userJoiningService; - -// public void process(UserJoining userJoining, CongratulateStatus settableStatus) { -// -// notificationService.removeKeyboardFromNotification(); -// -// if (settableStatus.equals(CongratulateStatus.CONGRATULATE)) { -// Long chatId = userJoining.getChatId(); -// Long userId = userJoining.getUserId(); -// var getChatMember = new GetChatMember(chatId, userId); -// var getChatMemberResponse = bot.execute(getChatMember); -// User user = getChatMemberResponse.chatMember().user(); -// String userName = user.firstName(); -// Integer number = userJoining.getNumber(); -// -// notificationService.congratulateUser(chatId, userName, number); -// } -// -// userJoining.setStatus(settableStatus); -// userJoiningService.save(userJoining); -// -// } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/MessageService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/MessageService.java deleted file mode 100644 index 2b958d5..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/MessageService.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.service; - -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.service.UserChatService; -import org.codewithoutus.tgbotusers.service.UserJoiningService; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class MessageService { - - private final UserChatService userChatService; - private final UserJoiningService userJoiningService; - -// public void process(String text, MessageEntity entity) { -// -// if (text.contains(BotCommand.LUCKY_LIST.getCommand())) { -// performLuckyList(text, entity); -// } -// -// -// } -// -// @Deprecated -// private String getCommand(String text, MessageEntity entity) { -// int begin = entity.offset(); -// int end = begin + entity.length(); -// String command = text.substring(begin, end); -// if (command.contains("@")) { -// return command.split("@")[0]; -// } -// return command; -// } -// -// private void performLuckyList(String text, MessageEntity entity) { -// String chatName = parseParam(text, entity); -// UserChat userChat = userChatService.findByName(chatName); -// Long userChatId = userChat.getChatId(); -// List userJoinings = userJoiningService.findByChatId(userChatId); -// userJoinings.sort(Comparator.naturalOrder()); -// -// for (UserJoining userJoining : userJoinings) { -// // TODO implement logic -// } -// -// } -// -// private String parseParam(String text, MessageEntity entity) { -// String param = text.substring(entity.offset() + entity.length()); -// return param.trim(); -// } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index cf334ac..01bed80 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -1,120 +1,85 @@ package org.codewithoutus.tgbotusers.bot.service; -import com.google.gson.JsonObject; -import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import com.pengrad.telegrambot.request.EditMessageReplyMarkup; +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.response.SendResponse; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.jetbrains.annotations.NotNull; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; +import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; +import org.codewithoutus.tgbotusers.model.entity.UserJoiningNotification; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.TelegramCallbackDataService; +import org.codewithoutus.tgbotusers.model.service.UserJoiningNotificationService; import org.springframework.stereotype.Service; +import javax.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + @Service @RequiredArgsConstructor +@Slf4j public class NotificationService { - private static final String NO_NICK = "ника нет"; + private final TelegramService telegramService; + private final TelegramCallbackDataService telegramCallbackDataService; + private final ChatModeratorService chatModeratorService; + private final UserJoiningNotificationService userJoiningNotificationService; + + @Transactional + public void notifyModeratorsAboutUserJoining(Long chatId, Long userId, LocalDateTime joinTime, int number, int anniversaryNumber) { + TelegramCallbackData callbackData = telegramCallbackDataService.saveMap(Map.of( + "chatId", chatId, + "userId", userId, + "joinTime", joinTime, + "number", number, + "anniversaryNumber", anniversaryNumber)); + + InlineKeyboardMarkup keyboard = KeyboardUtils.createKeyboard( + CongratulationDecisionKeyboard.class, + String.valueOf(callbackData.getId())); + + String notificationText = callbackData.toString(); // TODO: подтянуть шаблон + + List moderators = chatModeratorService.findByChatUsersId(chatId); + for (ChatModerator moderator : moderators) { + SendMessage message = new SendMessage(moderator.getChatId(), notificationText).replyMarkup(keyboard); + SendResponse response = telegramService.sendMessage(message); + + UserJoiningNotification notification = new UserJoiningNotification(); + notification.setChatId(chatId); + notification.setUserId(userId); + notification.setAnniversaryNumber(anniversaryNumber); + notification.setSentMessageChatId(moderator.getChatId()); + notification.setSentMessageId(response.message().messageId()); + userJoiningNotificationService.save(notification); + } + } + + @Transactional + public void notifyUserAboutJoining(Long chatId, Long userId, int anniversaryNumber) { + // TODO: реализовать оповещение и всю запись в базу + } - private final TelegramBot bot; + public void deleteKeyboardFromJoiningNotification(Long chatId, Long userId, int anniversaryNumber) { + deleteKeyboardFromJoiningNotifications(chatId, List.of(userId), anniversaryNumber); + } -// public void notifyModerators(UserJoining userJoining) { -// String text = createTextNotification(userJoining); -// sendNotification(text, userJoining); -// } -// -// public void congratulateUser(Long chatId, String userName, Integer number) { -// // TODO use 'number' from config -// -// String text = String.format(CONGRATULATION_TEMPLATE, userName, number); -// bot.execute(new SendMessage(chatId, text)); -// } -// -// private String createTextNotification(UserJoining userJoining) { -// Map joiningData = getJoiningData(userJoining); -// -// return String.format(NOTIFICATION_TEMPLATE, joiningData.get("chatName"), -// joiningData.get("userName"), joiningData.get("nickName"), -// joiningData.get("number"), joiningData.get("joinTime")); -// } -// -// // TODO organize mailing -// private void sendNotification(String text, UserJoining userJoining) { -// List moderatorChats = userChatService.getUserChats() -// .get(userJoining.getChatId()).getModeratorChats(); -// -// for (ModeratorChat moderatorChat : moderatorChats) { -// Long moderatorChatId = moderatorChat.getChatId(); -// var sendMessage = new SendMessage(moderatorChatId, text) -// .replyMarkup(createKeyboard(userJoining, moderatorChatId)); -// -// bot.execute(sendMessage); -// } -// } -// -// private Map getJoiningData(UserJoining userJoining) { -// -// Long chatId = userJoining.getChatId(); -// var getChat = new GetChat(chatId); -// var getChatResponse = bot.execute(getChat); -// String chatName = getChatResponse.chat().title(); -// -// Long userId = userJoining.getUserId(); -// var getChatMember = new GetChatMember(chatId, userId); -// var getChatMemberResponse = bot.execute(getChatMember); -// User user = getChatMemberResponse.chatMember().user(); -// String firstName = user.firstName(); -// String lastName = user.lastName(); -// String userName = firstName + ((lastName == null) ? "" : (" " + lastName)); -// -// String nickName = user.username(); -// nickName = nickName == null ? NO_NICK : nickName; -// -// Integer number = userJoining.getNumber(); -// -// DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT); -// String joinTime = userJoining.getJoinTime().format(formatter); -// -// return Map.of("chatName", chatName, -// "userName", userName, -// "nickName", nickName, -// "number", number, -// "joinTime", joinTime); -// } -// -// private InlineKeyboardMarkup createKeyboard(UserJoining userJoining, Long moderatorChatId) { -// -// var inlineKeyboardMarkup = new InlineKeyboardMarkup(); -// -// InlineKeyboardButton[] buttons = new InlineKeyboardButton[]{ -// new InlineKeyboardButton(ButtonText.CONGRATULATE.getSolution()) -// .callbackData(getCongratulateCallbackData(userJoining, moderatorChatId)), -// new InlineKeyboardButton(ButtonText.DECLINE.getSolution()) -// .callbackData(getDeclineCallbackData(userJoining, moderatorChatId))}; -// inlineKeyboardMarkup.addRow(buttons); -// -// return inlineKeyboardMarkup; -// } -// -// private String getCongratulateCallbackData(UserJoining userJoining, Long moderatorChatId) { -// JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); -// jsonObject.addProperty("command", "congratulate"); -// return jsonObject.getAsString(); -// } -// -// private String getDeclineCallbackData(UserJoining userJoining, Long moderatorChatId) { -// JsonObject jsonObject = getJsonObject(userJoining, moderatorChatId); -// jsonObject.addProperty("command", "decline"); -// return jsonObject.getAsString(); -// } -// -// @NotNull -// private JsonObject getJsonObject(UserJoining userJoining, Long moderatorChatId) { -// JsonObject jsonObject = new JsonObject(); -// jsonObject.addProperty("moderatorChatId", moderatorChatId); -// jsonObject.addProperty("userId", userJoining.getUserId()); -// jsonObject.addProperty("chatId", userJoining.getChatId()); -// return jsonObject; -// } -// -//// public void removeKeyboardFromNotification() { -//// // TODO implement -//// } + @Transactional + public void deleteKeyboardFromJoiningNotifications(Long chatId, List userIdList, int anniversaryNumber) { + // TODO: надо проверить + userJoiningNotificationService.findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber) + .stream() + .filter(notification -> userIdList.contains(notification.getUserId())) + .forEach(notification -> { + telegramService.editMessageReplyMarkup( + new EditMessageReplyMarkup(chatId, notification.getSentMessageId()) + .replyMarkup(new InlineKeyboardMarkup())); + }); + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/RightsService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java similarity index 95% rename from src/main/java/org/codewithoutus/tgbotusers/bot/RightsService.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java index 5d14b69..caf7b6c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/RightsService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.bot; +package org.codewithoutus.tgbotusers.bot.service; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java new file mode 100644 index 0000000..85d2992 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java @@ -0,0 +1,55 @@ +package org.codewithoutus.tgbotusers.bot.service; + +import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.request.*; +import com.pengrad.telegrambot.response.BaseResponse; +import com.pengrad.telegrambot.response.SendResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.Bot; +import org.codewithoutus.tgbotusers.bot.exception.TelegramSendMessageException; +import org.codewithoutus.tgbotusers.config.AppStaticContext; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class TelegramService { + + private final Bot bot; + + private , R extends BaseResponse> R sendRequest(BaseRequest request) { + R response = bot.execute(request); + if (!response.isOk()) { + String requestAsString; + try { + requestAsString = AppStaticContext.OBJECT_MAPPER.createObjectNode() + .put("parameters", request.getParameters().toString()) + .toString(); + + } catch (Exception ex) { + requestAsString = request.toString(); + } + log.error("Request {} -- get error response {}", requestAsString, response); + throw new TelegramSendMessageException(requestAsString); + } + return response; + } + + public SendResponse sendMessage(SendMessage message) { + return sendRequest(message); + } + + + public void editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup) { + sendRequest(editMessageReplyMarkup); + } + + public int getChatMembersCount(long chatId) { + return sendRequest(new GetChatMemberCount(chatId)).count(); + } + + public User getUser(long chatId, long userId) { + return sendRequest(new GetChatMember(chatId, userId)).chatMember().user(); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java deleted file mode 100644 index b023a2e..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/AppConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.Bot; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@RequiredArgsConstructor -public class AppConfig { - - private final BotSettings botSettings; - - @Bean - public Bot telegramBot() { - return new Bot(botSettings.getBotToken(), botSettings.getLongPollingTimeout()); - } - - @Bean - public ObjectMapper objectMapper() { - return new ObjectMapper(); - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppStaticContext.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppStaticContext.java new file mode 100644 index 0000000..667e92e --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppStaticContext.java @@ -0,0 +1,9 @@ +package org.codewithoutus.tgbotusers.config; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class AppStaticContext { + + public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + public static final String CALLBACK_QUERY_DATA_ID_FIELD = "dataId"; +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java index 98ae4f1..af6828f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/BotSettings.java @@ -4,7 +4,6 @@ import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; @Getter @Setter diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java new file mode 100644 index 0000000..35b5c99 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java @@ -0,0 +1,83 @@ +package org.codewithoutus.tgbotusers.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import javax.transaction.Transactional; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Getter +@Setter +@Configuration +@ConfigurationProperties(prefix = "chats-settings") +@RequiredArgsConstructor +@Slf4j +public class ChatSettings { + + private final ChatModeratorService chatModeratorService; + private final ChatUserService chatUserService; + + private Boolean rewriteDatabaseSettingsOnStartup; + private Integer anniversaryNumbersDelta; + private Set anniversaryNumbers; + + @Getter(AccessLevel.NONE) + private Map> chatsSettingsData; // only used for loading from application-settings file + + @JsonIgnore + private List moderatorsChats; // real group-settings, mapped to DB Entities + + public int getAnniversaryJoinNumber(long chatId, int joinNumber) { + return anniversaryNumbers.stream() + .filter(anniversaryNumber -> joinNumber >= anniversaryNumber) + .filter(anniversaryNumber -> joinNumber <= anniversaryNumber + anniversaryNumbersDelta) + .findFirst() + .orElse(0); + } + + @PostConstruct + private void synchronizeDataBaseSettings() { + moderatorsChats = chatModeratorService.findAll(); + if (rewriteDatabaseSettingsOnStartup || moderatorsChats.isEmpty()) { + rewriteDataBaseSettings(); + } + moderatorsChats = chatModeratorService.findAll(); + } + + @Transactional + private void rewriteDataBaseSettings() { + chatModeratorService.deleteAll(); + chatUserService.deleteAll(); + + for (Map.Entry> moderatorData : chatsSettingsData.entrySet()) { + ChatModerator chatModerator = new ChatModerator(); + chatModerator.setChatId(moderatorData.getKey()); + + for (Long userData : moderatorData.getValue()) { + ChatUser chatUser = chatUserService.findByChatId(userData); + if (chatUser == null) { + chatUser = new ChatUser(); + chatUser.setChatId(userData); + chatUser = chatUserService.save(chatUser); + } + chatUser.addChatModerator(chatModerator); + chatModerator.addChatUser(chatUser); + chatModerator = chatModeratorService.save(chatModerator); + } + } + log.info("Group settings was written to DB"); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java deleted file mode 100644 index c6b413d..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/config/GroupSettings.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.codewithoutus.tgbotusers.config; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import org.codewithoutus.tgbotusers.model.ModeratorChat; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; - -@Getter -@Setter -@Configuration -@ConfigurationProperties(prefix = "groups-settings") -public class GroupSettings { - - private Boolean rewriteDatabaseSettingsOnStartup; - private List anniversaryNumbers; - - @Getter(AccessLevel.NONE) - private Map> moderatorGroupsData; // only used for loading from application-settings file - - @JsonIgnore - private List moderatorsGroups; // real group-settings, mapped to DB Entities - - @PostConstruct - private void synchronizeDataBaseSettings() { - - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java index 10367ac..76db341 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java @@ -4,7 +4,6 @@ import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; @Getter @Setter diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index db5761d..f4cf498 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -1,9 +1,11 @@ package org.codewithoutus.tgbotusers.controller; +import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.Bot; -import org.codewithoutus.tgbotusers.bot.BotResponse; -import org.codewithoutus.tgbotusers.bot.TelegramService; +import org.codewithoutus.tgbotusers.bot.service.BotService; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.config.ChatSettings; +import org.codewithoutus.tgbotusers.controller.dto.BotResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -14,27 +16,28 @@ @RequiredArgsConstructor public class BotController { - private final Bot bot; + private final ChatSettings chatSettings; + private final BotService botService; private final TelegramService telegramService; @GetMapping("/start") private BotResponse startBotBackend() { - return new BotResponse(bot.start(), bot.getStatus()); + return new BotResponse(botService.start(), botService.getStatus()); } @GetMapping("/stop") private BotResponse stopBotBackend() { - return new BotResponse(bot.stop(), bot.getStatus()); + return new BotResponse(botService.stop(), botService.getStatus()); } @GetMapping("/status") private BotResponse getStatus() { - return new BotResponse(true, bot.getStatus()); + return new BotResponse(true, botService.getStatus()); } - @GetMapping("/sendMessage") // chatId = -644481529L + @GetMapping("/sendMessage") private BotResponse sendMessage(@RequestParam Long chatId, @RequestParam String message) { - telegramService.sendMessage(chatId, message); - return new BotResponse(true, bot.getStatus()); + telegramService.sendMessage(new SendMessage(chatId, message)); + return new BotResponse(true, botService.getStatus()); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java b/src/main/java/org/codewithoutus/tgbotusers/controller/dto/BotResponse.java similarity index 77% rename from src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java rename to src/main/java/org/codewithoutus/tgbotusers/controller/dto/BotResponse.java index e698cdd..cc9630e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/BotResponse.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/dto/BotResponse.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.bot; +package org.codewithoutus.tgbotusers.controller.dto; import lombok.Data; import org.codewithoutus.tgbotusers.bot.enums.BotStatus; diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java deleted file mode 100644 index b8db98a..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/CallbackQueryHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.codewithoutus.tgbotusers.bot.TelegramService; -import org.codewithoutus.tgbotusers.bot.UpdateService; -import org.codewithoutus.tgbotusers.bot.enums.CongratulationDecisionKeyboard; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.service.UserJoiningService; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.Optional; - -@Component -@RequiredArgsConstructor -@Slf4j -public class CallbackQueryHandler implements Handler { - - private final TelegramService telegramService; - private final UpdateService updateService; - private final UserJoiningService userJoiningService; - - @Override - public boolean handle(Update update) { - Map callbackQueryData = updateService.getCallbackData(update); - if (callbackQueryData == null) { - return false; - } - - // Congratulation decision - Optional key = CongratulationDecisionKeyboard.defineKey(callbackQueryData); - if (key.isPresent()) { - CongratulationDecisionKeyboard decision = key.get(); -// UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); -// userJoining.setStatus(decision.getStatus()); -// userJoiningService.save(userJoining); - if (decision == CongratulationDecisionKeyboard.CONGRATULATE) { - // send message - } - return true; - } -// -// Long moderatorChatId = Long.valueOf(callbackQueryData.get("moderatorChatId")); -// Long chatId = Long.valueOf(callbackQueryData.get("chatId")); -// List moderatorChats = userChatService.getUserChats() -// .get(chatId).getModeratorChats(); -// boolean fromModeratorChat = moderatorChats.stream() -// .anyMatch(chat -> chat.getChatId().equals(moderatorChatId)); -// if (!fromModeratorChat) { -// return false; -// } -// -// Long userId = Long.valueOf(callbackQueryData.get("userId")); -// UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); -// String command = callbackQueryData.get("command"); -// if (userJoining.getStatus().equals(CongratulateStatus.WAIT)) { -// if (command.equals("congratulate")) { -// callbackQueryService.process(userJoining, CongratulateStatus.CONGRATULATE); -// return true; -// } else if (command.equals("decline")) { -// callbackQueryService.process(userJoining, CongratulateStatus.DECLINE); -// return true; -// } else { -// throw new CommandNotFoundException("Wrong callback data. Callback data does not contain a command."); -// } -// } - - return false; - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java deleted file mode 100644 index 19a13ef..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/ChatJoinRequestHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -import com.pengrad.telegrambot.model.ChatJoinRequest; -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.TelegramService; -import org.codewithoutus.tgbotusers.bot.service.ChatJoinRequestService; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class ChatJoinRequestHandler implements Handler { - - private final TelegramService telegramService; - private final ChatJoinRequestService chatJoinRequestService; - - @Override - public boolean handle(Update update) { - ChatJoinRequest chatJoinRequest = update.chatJoinRequest(); - if (chatJoinRequest == null) { - return false; - } - -// // TODO проверить из чата модераторов -// -// long chatId = chatJoinRequest.chat().id(); -// int count = telegramService.getCount(chatId); -// if (count % MULTIPLICITY > INCREMENTAL_SAVES) { -// return false; -// } -// -// User user = chatJoinRequest.from(); -// if (user.isBot()) { -// return false; -// } -// -// int date = chatJoinRequest.date(); -// -// Map userData = Map.of( -// "userId", user.id(), -// "chatId", chatId, -// "count", count, -// "date", date); -// -// chatJoinRequestService.process(userData); - - return true; - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java deleted file mode 100644 index ad8e376..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/Handler.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -import com.pengrad.telegrambot.model.Update; - -public interface Handler { - boolean handle(Update update); -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java deleted file mode 100644 index 6aa8cd2..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/handler/PrivateMessageHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.codewithoutus.tgbotusers.handler; - -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.MessageEntity; -import com.pengrad.telegrambot.model.Update; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.TelegramService; -import org.codewithoutus.tgbotusers.bot.UpdateService; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class PrivateMessageHandler implements Handler { - - private static final String SORRY = "Sorry, functionality not implemented"; - private final TelegramService telegramService; - private final UpdateService updateService; - - @Override - public boolean handle(Update update) { - if (!updateService.isPrivateMessage(update)) { - return false; - } - telegramService.sendMessage(updateService.getChatId(update), SORRY); - return true; - -// Message message = update.message(); -// MessageEntity[] entities = message.entities(); -// if (entities == null || entities.length <= 0) { -// return false; -// } -// -// Optional botEntityOptional = Arrays.stream(message.entities()) -// .filter(entity -> entity.type().equals(MessageEntity.Type.bot_command)).findFirst(); -// if (botEntityOptional.isEmpty()) { -// return false; -// } -// -// Long chatId = message.chat().id(); -// if (!moderatorChatService.getModeratorChats().containsKey(chatId)) { -// return false; -// } -// -// MessageEntity entity = botEntityOptional.get(); -// -// messageService.process(message.text(), entity); -// -// return true; - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java b/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java deleted file mode 100644 index fa7c074..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Congratulation.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.codewithoutus.tgbotusers.model; - -import lombok.Getter; -import lombok.Setter; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.OneToOne; - -@Entity -@Getter -@Setter -public class Congratulation { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @OneToOne(optional = false, orphanRemoval = true) - @JoinColumn(name = "user_joining_id", nullable = false) - private UserJoining userJoining; - - @Column(nullable = false) - private Integer number; -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/User.java b/src/main/java/org/codewithoutus/tgbotusers/model/User.java deleted file mode 100644 index 38b3a87..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/User.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.codewithoutus.tgbotusers.model; - -import lombok.Getter; -import lombok.Setter; -import org.hibernate.annotations.NaturalId; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.MappedSuperclass; -import javax.persistence.Table; - -@Entity -@Table(name = "users") -@Getter -@Setter -public class User { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @NaturalId - @Column(nullable = false) - Long userId; -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java deleted file mode 100644 index 478c9c7..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserChat.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.codewithoutus.tgbotusers.model; - -import lombok.Getter; -import lombok.Setter; -import org.hibernate.annotations.NaturalId; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.ManyToMany; -import javax.persistence.OneToMany; -import java.util.List; - -@Entity -@Getter -@Setter -public class UserChat { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @NaturalId - @Column(nullable = false, unique = true) - private Long chatId; - - @Column(nullable = false, unique = true) - private String name; - - @ManyToMany(mappedBy = "userChats") - private List moderatorChats; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - UserChat userChat = (UserChat) o; - return chatId.equals(userChat.chatId); - } - - @Override - public int hashCode() { - return chatId.hashCode(); - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/Administrator.java similarity index 57% rename from src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java rename to src/main/java/org/codewithoutus/tgbotusers/model/entity/Administrator.java index 2623de6..6800fa9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/Administrator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/Administrator.java @@ -1,14 +1,10 @@ -package org.codewithoutus.tgbotusers.model; +package org.codewithoutus.tgbotusers.model.entity; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.NaturalId; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; +import javax.persistence.*; @Entity @Getter diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java similarity index 52% rename from src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java rename to src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index c34bc8c..dda70e4 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/ModeratorChat.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -1,41 +1,36 @@ -package org.codewithoutus.tgbotusers.model; +package org.codewithoutus.tgbotusers.model.entity; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.NaturalId; import javax.persistence.*; +import java.util.ArrayList; import java.util.List; @Entity @Getter @Setter -public class ModeratorChat { - +public class ChatModerator { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; - + @NaturalId @Column(nullable = false, unique = true) private Long chatId; - - @ManyToMany + + @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "moderators2users", joinColumns = @JoinColumn(name = "moderator_chat_id"), inverseJoinColumns = @JoinColumn(name = "user_chat_id")) - private List userChats; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ModeratorChat that = (ModeratorChat) o; - return chatId.equals(that.chatId); - } + private List chatUsers; - @Override - public int hashCode() { - return chatId.hashCode(); + public void addChatUser(ChatUser chatUser) { + if (chatUsers == null) { + chatUsers = new ArrayList<>(); + } + chatUsers.add(chatUser); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java new file mode 100644 index 0000000..85b88bd --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -0,0 +1,33 @@ +package org.codewithoutus.tgbotusers.model.entity; + +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.NaturalId; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Setter +public class ChatUser { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @NaturalId + @Column(nullable = false, unique = true) + private Long chatId; + + @ManyToMany(mappedBy = "chatUsers", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) + private List chatModerators; + + public void addChatModerator(ChatModerator chatModerator) { + if (chatModerators == null) { + chatModerators = new ArrayList<>(); + } + chatModerators.add(chatModerator); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java new file mode 100644 index 0000000..7e1d4b7 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java @@ -0,0 +1,20 @@ +package org.codewithoutus.tgbotusers.model.entity; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Entity +@Getter +@Setter +public class TelegramCallbackData { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(nullable = false) + String data; +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java similarity index 52% rename from src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java rename to src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java index 878b83c..77890d2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java @@ -1,46 +1,34 @@ -package org.codewithoutus.tgbotusers.model; +package org.codewithoutus.tgbotusers.model.entity; import lombok.Getter; import lombok.Setter; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.jetbrains.annotations.NotNull; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; + +import javax.persistence.*; import java.time.LocalDateTime; @Entity @Getter @Setter -public class UserJoining implements Comparable { +public class UserJoining { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; - + @Column(nullable = false) private Long userId; - + @Column(nullable = false) private Long chatId; - + @Column(nullable = false) private Integer number; - + @Column(nullable = false) private LocalDateTime joinTime; - + @Enumerated(EnumType.STRING) @Column(nullable = false) private CongratulateStatus status; - - @Override - public int compareTo(UserJoining o) { - return o == null ? 1 : number.compareTo(o.getNumber()); - } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java new file mode 100644 index 0000000..ba0a8c3 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java @@ -0,0 +1,31 @@ +package org.codewithoutus.tgbotusers.model.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Entity +@Getter +@Setter +public class UserJoiningNotification { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(nullable = false) + private Integer sentMessageId; + + @Column(nullable = false) + private Long sentMessageChatId; + + @Column(nullable = false) + private Long userId; + + @Column(nullable = false) + private Long chatId; + + @Column(nullable = false) + private Integer anniversaryNumber; +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/AdministratorRepository.java similarity index 64% rename from src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java rename to src/main/java/org/codewithoutus/tgbotusers/model/repository/AdministratorRepository.java index 6344b2c..b1efcb8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/AdministratorRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/AdministratorRepository.java @@ -1,6 +1,6 @@ -package org.codewithoutus.tgbotusers.repository; +package org.codewithoutus.tgbotusers.model.repository; -import org.codewithoutus.tgbotusers.model.Administrator; +import org.codewithoutus.tgbotusers.model.entity.Administrator; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java new file mode 100644 index 0000000..e537183 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java @@ -0,0 +1,13 @@ +package org.codewithoutus.tgbotusers.model.repository; + +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ChatModeratorRepository extends CrudRepository { + + List findByChatUsers_ChatId(Long chatId); +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java new file mode 100644 index 0000000..6669c05 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java @@ -0,0 +1,15 @@ +package org.codewithoutus.tgbotusers.model.repository; + +import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ChatUserRepository extends CrudRepository { + + ChatUser findByChatId(long chatId); + + List findByChatModeratorsNotEmpty(); +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java new file mode 100644 index 0000000..d786712 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java @@ -0,0 +1,11 @@ +package org.codewithoutus.tgbotusers.model.repository; + +import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TelegramCallbackDataRepository extends CrudRepository { + + TelegramCallbackData findById(int id); +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java new file mode 100644 index 0000000..583e7ff --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java @@ -0,0 +1,13 @@ +package org.codewithoutus.tgbotusers.model.repository; + +import org.codewithoutus.tgbotusers.model.entity.UserJoiningNotification; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface UserJoiningNotificationRepository extends CrudRepository { + + List findByChatIdAndAnniversaryNumber(Long chatId, Integer anniversaryNumber); +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java similarity index 74% rename from src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java rename to src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java index befb9b3..f81b146 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java @@ -1,6 +1,6 @@ -package org.codewithoutus.tgbotusers.repository; +package org.codewithoutus.tgbotusers.model.repository; -import org.codewithoutus.tgbotusers.model.UserJoining; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @@ -8,8 +8,8 @@ @Repository public interface UserJoiningRepository extends CrudRepository { - + UserJoining findByUserIdAndChatId(Long userId, Long chatId); + List findByChatId(Long chatId); } - diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/AdministratorService.java similarity index 75% rename from src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java rename to src/main/java/org/codewithoutus/tgbotusers/model/service/AdministratorService.java index 4712ffc..a664841 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/AdministratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/AdministratorService.java @@ -1,4 +1,4 @@ -package org.codewithoutus.tgbotusers.service; +package org.codewithoutus.tgbotusers.model.service; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java new file mode 100644 index 0000000..4ba4013 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -0,0 +1,34 @@ +package org.codewithoutus.tgbotusers.model.service; + +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.repository.ChatModeratorRepository; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ChatModeratorService { + + private final ChatModeratorRepository chatModeratorRepository; + + public void deleteAll() { + chatModeratorRepository.deleteAll(); + } + + public ChatModerator save(ChatModerator entity) { + return chatModeratorRepository.save(entity); + } + + public List findAll() { + List result = new ArrayList<>(); + chatModeratorRepository.findAll().forEach(result::add); + return result; + } + + public List findByChatUsersId(Long chatId) { + return chatModeratorRepository.findByChatUsers_ChatId(chatId); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java new file mode 100644 index 0000000..b3eeb33 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -0,0 +1,35 @@ +package org.codewithoutus.tgbotusers.model.service; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.codewithoutus.tgbotusers.model.repository.ChatUserRepository; +import org.springframework.stereotype.Service; + +@Getter +@Setter +@Service +@RequiredArgsConstructor +public class ChatUserService { + + private final ChatUserRepository chatUserRepository; + + public void deleteAll() { + chatUserRepository.deleteAll(); + } + + public ChatUser save(ChatUser entity) { + return chatUserRepository.save(entity); + } + + public ChatUser findByChatId(long chatId) { + return chatUserRepository.findByChatId(chatId); + } + + public boolean isChatUser(long id) { + return chatUserRepository.findByChatModeratorsNotEmpty() + .stream() + .anyMatch(chatUser -> chatUser.getChatId().equals(id)); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java new file mode 100644 index 0000000..c6aa4d4 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java @@ -0,0 +1,41 @@ +package org.codewithoutus.tgbotusers.model.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.exception.CallbackDataMappingException; +import org.codewithoutus.tgbotusers.config.AppStaticContext; +import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; +import org.codewithoutus.tgbotusers.model.repository.TelegramCallbackDataRepository; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +@RequiredArgsConstructor +@Slf4j +public class TelegramCallbackDataService { + + private final TelegramCallbackDataRepository telegramCallbackDataRepository; + + public TelegramCallbackData save(TelegramCallbackData telegramCallbackData) { + return telegramCallbackDataRepository.save(telegramCallbackData); + } + + public TelegramCallbackData findById(int id) { + return telegramCallbackDataRepository.findById(id); + } + + public TelegramCallbackData saveMap(Map callbackData) { + try { + String json = AppStaticContext.OBJECT_MAPPER.writeValueAsString(callbackData); + TelegramCallbackData entity = new TelegramCallbackData(); + entity.setData(json); + return save(entity); + + } catch (JsonProcessingException e) { + log.error("Serializing error of Map {}", callbackData); + throw new CallbackDataMappingException(callbackData.toString()); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java new file mode 100644 index 0000000..5f36b99 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java @@ -0,0 +1,23 @@ +package org.codewithoutus.tgbotusers.model.service; + +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.model.entity.UserJoiningNotification; +import org.codewithoutus.tgbotusers.model.repository.UserJoiningNotificationRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class UserJoiningNotificationService { + + private final UserJoiningNotificationRepository userJoiningNotificationRepository; + + public UserJoiningNotification save(UserJoiningNotification entity) { + return userJoiningNotificationRepository.save(entity); + } + + public List findByChatIdAndAnniversaryNumber(Long chatId, Integer anniversaryNumber) { + return userJoiningNotificationRepository.findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java similarity index 62% rename from src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java rename to src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java index c2c1d80..6ef7ee2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java @@ -1,9 +1,8 @@ -package org.codewithoutus.tgbotusers.service; +package org.codewithoutus.tgbotusers.model.service; import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.model.UserJoining; -import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.codewithoutus.tgbotusers.repository.UserJoiningRepository; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.repository.UserJoiningRepository; import org.springframework.stereotype.Service; import java.util.List; @@ -11,22 +10,18 @@ @Service @RequiredArgsConstructor public class UserJoiningService { - - private final UserJoiningRepository userJoiningRepository; -// public void setCongratulateStatus(UserJoining userJoining, CongratulateStatus settableStatus) { -// userJoining -// } + private final UserJoiningRepository userJoiningRepository; public UserJoining save(UserJoining userJoining) { return userJoiningRepository.save(userJoining); } - + public UserJoining findByUserIdAndChatId(Long userId, Long chatId) { return userJoiningRepository.findByUserIdAndChatId(userId, chatId); } - + public List findByChatId(Long chatId) { return userJoiningRepository.findByChatId(chatId); } -} +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java deleted file mode 100644 index 29af824..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/CongratulationRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.codewithoutus.tgbotusers.repository; - -import org.codewithoutus.tgbotusers.model.Congratulation; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface CongratulationRepository extends CrudRepository { -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java deleted file mode 100644 index 420b6f9..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/ModeratorChatRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.codewithoutus.tgbotusers.repository; - -import org.codewithoutus.tgbotusers.model.ModeratorChat; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ModeratorChatRepository extends CrudRepository { -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java deleted file mode 100644 index 65b7acd..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserChatRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.codewithoutus.tgbotusers.repository; - -import org.codewithoutus.tgbotusers.model.UserChat; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface UserChatRepository extends CrudRepository { - - UserChat findByName(String name); -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java deleted file mode 100644 index 257bdc5..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/repository/UserRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.codewithoutus.tgbotusers.repository; - -import org.codewithoutus.tgbotusers.model.User; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface UserRepository extends CrudRepository { -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java b/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java deleted file mode 100644 index a92d262..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/CongratulationService.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class CongratulationService { -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java b/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java deleted file mode 100644 index 1d6db21..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/service/UserChatService.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.codewithoutus.tgbotusers.service; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import org.codewithoutus.tgbotusers.model.UserChat; -import org.codewithoutus.tgbotusers.repository.UserChatRepository; -import org.springframework.stereotype.Service; - -@Getter -@Setter -@Service -@RequiredArgsConstructor -public class UserChatService { - - private final UserChatRepository userChatRepository; - - public UserChat findByName(String name) { - return userChatRepository.findByName(name); - } -} diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 170a596..bfca316 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,6 +1,6 @@ -groups-settings: +chats-settings: rewrite-database-settings-on-startup: false - moderator-groups-data: + chats-settings-data: 11111: - 222 - 333 @@ -9,6 +9,7 @@ groups-settings: - 444 - 555 - 666 + anniversary-numbers-delta: 3 anniversary-numbers: - 100 - 500 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0d49e0a..ea25542 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,7 +11,7 @@ spring: database-platform: org.hibernate.dialect.PostgreSQLDialect show_sql: true hibernate: - ddl-auto: none + ddl-auto: create profiles: include: From ab4e5454021203326801d23a82db779700956090 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Sat, 16 Jul 2022 01:31:17 +0400 Subject: [PATCH 22/50] CallbackData entity remove. Now transmitted the ID of UserJoining entity to keyboard buttons. TODO with assigned developer. Refactoring. --- .../bot/handler/CallbackQueryHandler.java | 69 +++++++------------ .../bot/handler/ChatJoinRequestHandler.java | 14 ++-- .../bot/handler/PrivateMessageHandler.java | 44 +++++++++--- .../tgbotusers/bot/handler/UpdateUtils.java | 12 ++-- .../bot/service/NotificationService.java | 56 ++++++--------- .../tgbotusers/bot/service/RightsService.java | 4 +- .../bot/service/TelegramService.java | 17 +++-- .../tgbotusers/config/ChatSettings.java | 8 +-- .../config/NotificationTemplates.java | 8 +++ .../model/entity/ChatModerator.java | 2 +- .../tgbotusers/model/entity/ChatUser.java | 2 +- .../model/entity/TelegramCallbackData.java | 20 ------ .../tgbotusers/model/entity/UserJoining.java | 9 +++ .../model/entity/UserJoiningNotification.java | 12 +--- .../TelegramCallbackDataRepository.java | 11 --- .../UserJoiningNotificationRepository.java | 4 +- .../repository/UserJoiningRepository.java | 5 +- .../service/TelegramCallbackDataService.java | 41 ----------- .../UserJoiningNotificationService.java | 6 +- .../model/service/UserJoiningService.java | 12 +++- 20 files changed, 151 insertions(+), 205 deletions(-) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index 1a5c604..8dccbe6 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -1,6 +1,5 @@ package org.codewithoutus.tgbotusers.bot.handler; -import com.fasterxml.jackson.core.type.TypeReference; import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -9,14 +8,12 @@ import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; import org.codewithoutus.tgbotusers.bot.service.NotificationService; import org.codewithoutus.tgbotusers.config.AppStaticContext; -import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.codewithoutus.tgbotusers.model.service.TelegramCallbackDataService; import org.codewithoutus.tgbotusers.model.service.UserJoiningService; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; -import java.util.Collections; import java.util.Map; import java.util.Optional; @@ -27,7 +24,6 @@ public class CallbackQueryHandler extends Handler { private final NotificationService notificationService; private final UserJoiningService userJoiningService; - private final TelegramCallbackDataService telegramCallbackDataService; @Override protected boolean handle(Update update) { @@ -50,66 +46,51 @@ protected boolean handle(Update update) { } } + @Transactional private boolean handleCongratulationDecision(String command, Map callbackQueryData) { - // TODO: доделать реакцию на команду модераторов "поздравить" или "отклонить" Optional key = KeyboardUtils.defineKey(CongratulationDecisionKeyboard.class, command); if (key.isEmpty()) { return false; } CongratulationDecisionKeyboard decision = key.get(); - Map callbackData = getCallbackDataFromDatabase(callbackQueryData); + Integer userJoiningId = (Integer) callbackQueryData.get(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD); + UserJoining userJoining = userJoiningService.findById(userJoiningId) + .orElseThrow(() -> new IllegalStateException("CallbackQueryData with no exist user joining ID" + userJoiningId)); - // TODO: проверить, что данные есть в мапе callbackData - // TODO: все данные определены в NotificationService.notifyModeratorsAboutUserJoining() - Long chatId = (Long) callbackData.get("chatId"); - Long userId = (Long) callbackData.get("userId"); - Integer anniversaryNumber = (Integer) callbackData.get("anniversaryNumber"); + Long chatId = userJoining.getChatId(); + Long userId = userJoining.getUserId(); + Integer anniversaryNumber = userJoining.getAnniversaryNumber(); - // TODO: проверить, что уже не поздравили кого-тотме, иначе отмена - // TODO: проверить, что кого хотим поздравить в статусе WAIT, иначе отмена + // TODO: Pavel (подумать) -- если из списка, то можно поздравить отклоненного раннее, а иначе нельзя + if (userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { + return true; + } CongratulateStatus newStatus = (decision == CongratulationDecisionKeyboard.CONGRATULATE) ? CongratulateStatus.CONGRATULATE : CongratulateStatus.DECLINE; - UserJoining userJoining = userJoiningService.findByUserIdAndChatId(userId, chatId); userJoining.setStatus(newStatus); - userJoiningService.save(userJoining); +// userJoiningService.save(userJoining); // TODO: Алекс -- check, that entity was updated without save() method if (decision == CongratulationDecisionKeyboard.CONGRATULATE) { - // TODO: удалить кнопки у всех неудачников =) - // notificationService.deleteKeyboardFromJoiningNotifications(chatId, LIST, anniversaryNumber); - notificationService.notifyUserAboutJoining(chatId, userId, anniversaryNumber); + notificationService.deleteKeyboardFromAllJoiningNotifications(chatId, anniversaryNumber); + notificationService.notifyUserAboutAnniversaryJoining(userJoining); - } else { - notificationService.deleteKeyboardFromJoiningNotification(chatId, userId, anniversaryNumber); + } else if (decision == CongratulationDecisionKeyboard.DECLINE) { + notificationService.deleteKeyboardFromJoiningNotification(userId, chatId, anniversaryNumber); } return true; } + @Transactional private boolean handleProvidingAnniversaryStatistic(String command, Map callbackQueryData) { - // TODO: реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" - // TODO: уже поздравленные имеют корону, можно добавить в шаблоны и вставлять в начало, неудачники не показываются - // TODO: если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard - // TODO: если поздравленного нет, то отклоненные тоже выводятся - // TODO: все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись - // TODO: можно попробовать выводить все-все чаты, а потом доработать -- на будущее - return false; - } + // TODO: Алекс -- реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" - private Map getCallbackDataFromDatabase(Map callbackQueryData) { - Integer id = (Integer) callbackQueryData.get(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD); - if (id == null) { - return Collections.emptyMap(); - } - try { - TelegramCallbackData telegramCallbackData = telegramCallbackDataService.findById(id); - if (telegramCallbackData != null) { - return AppStaticContext.OBJECT_MAPPER.readValue(telegramCallbackData.getData(), new TypeReference<>() { - }); - } - } catch (Exception ex) { - log.error("CallbackData with id {} deserialize error", id); - } - return Collections.emptyMap(); + // TODO: Алекс -- уже поздравленные имеют корону, можно добавить в шаблоны и вставлять в начало, неудачники не показываются + // TODO: Алекс -- если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard + // TODO: Алекс -- если поздравленного нет, то отклоненные тоже выводятся + // TODO: Алекс -- все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись + // TODO: Алекс -- можно попробовать выводить все-все чаты, а потом доработать -- на будущее + return false; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java index fe3934d..c07a5dc 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java @@ -32,15 +32,14 @@ protected boolean handle(Update update) { return false; } - // TODO: если присоединяется сразу несколько в чат -- на будущее - // TODO: если присоединяющийся пользователь бот и юбилейный -- на далёкое будущее - // TODO: юбилейные номера могут быть разные для каждого чата -- на далёкое будущее + // TODO: Pavel (подумать) -- если присоединяется сразу несколько в чат (на будущее) + // TODO: Pavel (подумать) -- если присоединяющийся пользователь бот и юбилейный (на далёкое будущее) + // TODO: Pavel (подумать) -- юбилейные номера могут быть разные для каждого чата (на далёкое будущее) User user = chatJoinRequest.from(); long chatId = chatJoinRequest.chat().id(); int joinNumber = telegramService.getChatMembersCount(chatId); int anniversaryNumber = chatSettings.getAnniversaryJoinNumber(chatId, joinNumber); - if (anniversaryNumber == 0 || !chatUserService.isChatUser(chatId)) { return false; } @@ -52,10 +51,9 @@ protected boolean handle(Update update) { userJoining.setJoinTime(LocalDateTime.from(Instant.ofEpochSecond(chatJoinRequest.date()))); userJoining = userJoiningService.save(userJoining); - // TODO: оповещение модераторов - // проверяем, что ещё никто не был поздравлен в этой группе с этим юбилейным номером, иначе ничего не делаем - // notificationService.notifyModeratorsAboutUserJoining(); - + if (!userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { + notificationService.notifyModeratorsAboutUserJoining(userJoining); + } return true; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 7363f20..0a2891b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -2,6 +2,7 @@ import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.request.InlineKeyboardButton; import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; import com.pengrad.telegrambot.request.SendMessage; @@ -23,25 +24,50 @@ protected boolean handle(Update update) { } String text = UpdateUtils.getMessageText(update); - Chat chat = UpdateUtils.getChat(update); + Long chatId = UpdateUtils.getChat(update).id(); + if (handleForwardMessage(chatId, update) + || handleAdminRequest(chatId, text) + || handleTestRequest(chatId, text)) { + return true; + } + telegramService.sendMessage(new SendMessage(chatId, SORRY)); + return false; + } + + private boolean handleForwardMessage(Long chatId, Update update) { + if (!UpdateUtils.isForwardMessage(update)) { + return false; + } + Integer messageId = update.message().messageId(); + User user = telegramService.getUser(chatId, chatId); + String userName = user.firstName().isBlank() ? user.username() : user.firstName(); + String text = userName + ", сплетничать не хорошо"; + telegramService.sendMessage(new SendMessage(chatId, text).replyToMessageId(messageId)); + return true; + } + + private boolean handleAdminRequest(Long chatId, String text) { + // TODO: Макс -- реализовать админку (добавление и удаление чатов) + // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки + // TODO: Макс -- изменения настроек должны сохраняться в базе + return false; + } + + private boolean handleTestRequest(Long chatId, String text) { if (text.startsWith("/test ")) { - telegramService.sendMessage(new SendMessage(chat.id(), "Идёт тест...")); + telegramService.sendMessage(new SendMessage(chatId, "Идёт тест...")); if (text.startsWith("/test InlineKeyboard")) { - String callbackData = text.substring("/test InlineKeyboard".length() - 1).trim(); + String callbackData = text.substring("/test InlineKeyboard".length() + 1).trim(); if (!callbackData.isBlank()) { InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( new InlineKeyboardButton("callback_data").callbackData(callbackData)); - telegramService.sendMessage(new SendMessage(chat.id(), "InlineKeyboard test").replyMarkup(keyboard)); + telegramService.sendMessage(new SendMessage(chatId, "InlineKeyboard test").replyMarkup(keyboard)); return true; } } } - - // TODO: реализовать админку: добавление и удаление чатов - - telegramService.sendMessage(new SendMessage(chat.id(), SORRY)); - return true; + return false; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java index 611aabd..5177a2b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java @@ -20,6 +20,12 @@ public static Boolean isPrivateMessage(Update update) { .orElse(Boolean.FALSE); } + public static boolean isForwardMessage(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::forwardDate) + .isPresent(); + } + public static String getMessageText(Update update) { return Optional.ofNullable(update.message()) .map(Message::text) @@ -32,12 +38,6 @@ public static Chat getChat(Update update) { .orElse(null); } - public static User getFromUser(Update update) { - return Optional.ofNullable(update.message()) - .map(Message::from) - .orElse(null); - } - public static String getCallbackQueryData(Update update) { return Optional .ofNullable(update.callbackQuery()) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index 01bed80..8429dad 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -1,7 +1,6 @@ package org.codewithoutus.tgbotusers.bot.service; import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; -import com.pengrad.telegrambot.request.EditMessageReplyMarkup; import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.response.SendResponse; import lombok.RequiredArgsConstructor; @@ -9,17 +8,14 @@ import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; -import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.entity.UserJoiningNotification; import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; -import org.codewithoutus.tgbotusers.model.service.TelegramCallbackDataService; import org.codewithoutus.tgbotusers.model.service.UserJoiningNotificationService; import org.springframework.stereotype.Service; import javax.transaction.Transactional; -import java.time.LocalDateTime; import java.util.List; -import java.util.Map; @Service @RequiredArgsConstructor @@ -27,34 +23,28 @@ public class NotificationService { private final TelegramService telegramService; - private final TelegramCallbackDataService telegramCallbackDataService; private final ChatModeratorService chatModeratorService; private final UserJoiningNotificationService userJoiningNotificationService; @Transactional - public void notifyModeratorsAboutUserJoining(Long chatId, Long userId, LocalDateTime joinTime, int number, int anniversaryNumber) { - TelegramCallbackData callbackData = telegramCallbackDataService.saveMap(Map.of( - "chatId", chatId, - "userId", userId, - "joinTime", joinTime, - "number", number, - "anniversaryNumber", anniversaryNumber)); + public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { + List moderators = chatModeratorService.findByChatUsersId(userJoining.getChatId()); + if (moderators.isEmpty()) { + log.warn(""); + return; + } - InlineKeyboardMarkup keyboard = KeyboardUtils.createKeyboard( - CongratulationDecisionKeyboard.class, - String.valueOf(callbackData.getId())); + InlineKeyboardMarkup keyboard = KeyboardUtils + .createKeyboard(CongratulationDecisionKeyboard.class, String.valueOf(userJoining.getId())); - String notificationText = callbackData.toString(); // TODO: подтянуть шаблон + String notificationText = userJoining.toString(); // TODO: Алекс -- подтянуть шаблон - List moderators = chatModeratorService.findByChatUsersId(chatId); for (ChatModerator moderator : moderators) { SendMessage message = new SendMessage(moderator.getChatId(), notificationText).replyMarkup(keyboard); SendResponse response = telegramService.sendMessage(message); UserJoiningNotification notification = new UserJoiningNotification(); - notification.setChatId(chatId); - notification.setUserId(userId); - notification.setAnniversaryNumber(anniversaryNumber); + notification.setUserJoining(userJoining); notification.setSentMessageChatId(moderator.getChatId()); notification.setSentMessageId(response.message().messageId()); userJoiningNotificationService.save(notification); @@ -62,24 +52,22 @@ public void notifyModeratorsAboutUserJoining(Long chatId, Long userId, LocalDate } @Transactional - public void notifyUserAboutJoining(Long chatId, Long userId, int anniversaryNumber) { - // TODO: реализовать оповещение и всю запись в базу + public void notifyUserAboutAnniversaryJoining(UserJoining userJoining) { + String notificationText = userJoining.toString(); // TODO: Алекс -- подтянуть шаблон + + SendMessage message = new SendMessage(userJoining.getChatId(), notificationText); + telegramService.sendMessage(message); } - public void deleteKeyboardFromJoiningNotification(Long chatId, Long userId, int anniversaryNumber) { - deleteKeyboardFromJoiningNotifications(chatId, List.of(userId), anniversaryNumber); + @Transactional + public void deleteKeyboardFromJoiningNotification(Long userId, Long chatId, int anniversaryNumber) { + userJoiningNotificationService.findByUserIdAndChatIdAndAnniversaryNumber(userId, chatId, anniversaryNumber) + .forEach(notification -> telegramService.removeKeyboardFromMessage(chatId, notification.getSentMessageId())); } @Transactional - public void deleteKeyboardFromJoiningNotifications(Long chatId, List userIdList, int anniversaryNumber) { - // TODO: надо проверить + public void deleteKeyboardFromAllJoiningNotifications(Long chatId, int anniversaryNumber) { userJoiningNotificationService.findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber) - .stream() - .filter(notification -> userIdList.contains(notification.getUserId())) - .forEach(notification -> { - telegramService.editMessageReplyMarkup( - new EditMessageReplyMarkup(chatId, notification.getSentMessageId()) - .replyMarkup(new InlineKeyboardMarkup())); - }); + .forEach(notification -> telegramService.removeKeyboardFromMessage(chatId, notification.getSentMessageId())); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java index caf7b6c..3fc269c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java @@ -7,6 +7,8 @@ @RequiredArgsConstructor public class RightsService { + // TODO: Алекс -- нам это нужно? + // private final TelegramBot bot; // // public void initialiseAdministratorRights() { @@ -23,7 +25,7 @@ public class RightsService { // } // // private void setAdministratorRights() { -// var chatAdministratorRights = new ChatAdministratorRights(); // TODO add isAnonymous(boolean) +// var chatAdministratorRights = new ChatAdministratorRights(); // chatAdministratorRights.canInviteUsers(true); // // var setMyDefaultAdministratorRights = new SetMyDefaultAdministratorRights(); diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java index 85d2992..d392d1b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java @@ -1,6 +1,8 @@ package org.codewithoutus.tgbotusers.bot.service; +import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; import com.pengrad.telegrambot.request.*; import com.pengrad.telegrambot.response.BaseResponse; import com.pengrad.telegrambot.response.SendResponse; @@ -40,16 +42,19 @@ public SendResponse sendMessage(SendMessage message) { return sendRequest(message); } + public void removeKeyboardFromMessage(long chatId, int messageId) { + sendRequest(new EditMessageReplyMarkup(chatId, messageId).replyMarkup(new InlineKeyboardMarkup())); + } - public void editMessageReplyMarkup(EditMessageReplyMarkup editMessageReplyMarkup) { - sendRequest(editMessageReplyMarkup); + public User getUser(long chatId, long userId) { + return sendRequest(new GetChatMember(chatId, userId)).chatMember().user(); } - public int getChatMembersCount(long chatId) { - return sendRequest(new GetChatMemberCount(chatId)).count(); + public Chat getChat(long chatId) { + return sendRequest(new GetChat(chatId)).chat(); } - public User getUser(long chatId, long userId) { - return sendRequest(new GetChatMember(chatId, userId)).chatMember().user(); + public int getChatMembersCount(long chatId) { + return sendRequest(new GetChatMemberCount(chatId)).count(); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java index 35b5c99..c62fafc 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java @@ -1,6 +1,5 @@ package org.codewithoutus.tgbotusers.config; -import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -37,9 +36,6 @@ public class ChatSettings { @Getter(AccessLevel.NONE) private Map> chatsSettingsData; // only used for loading from application-settings file - @JsonIgnore - private List moderatorsChats; // real group-settings, mapped to DB Entities - public int getAnniversaryJoinNumber(long chatId, int joinNumber) { return anniversaryNumbers.stream() .filter(anniversaryNumber -> joinNumber >= anniversaryNumber) @@ -50,11 +46,9 @@ public int getAnniversaryJoinNumber(long chatId, int joinNumber) { @PostConstruct private void synchronizeDataBaseSettings() { - moderatorsChats = chatModeratorService.findAll(); - if (rewriteDatabaseSettingsOnStartup || moderatorsChats.isEmpty()) { + if (rewriteDatabaseSettingsOnStartup || chatModeratorService.findAll().isEmpty()) { rewriteDataBaseSettings(); } - moderatorsChats = chatModeratorService.findAll(); } @Transactional diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java index 76db341..2e11b96 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java @@ -5,6 +5,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import javax.annotation.PostConstruct; + @Getter @Setter @Configuration @@ -14,4 +16,10 @@ public class NotificationTemplates { private String joinCongratulation; private String joinAlert; private String joinUserInfo; + + @PostConstruct + private void formatTemplates() { + // TODO: Алекс -- сделать шаблоны +// joinCongratulation = joinCongratulation.replaceAll("{\*}", "%s"); + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index dda70e4..6214796 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -11,7 +11,7 @@ @Entity @Getter @Setter -public class ChatModerator { +public class ChatModerator { // TODO: Pavel -- переименовать сущность и сопутствующие переменные (после задачи Макса) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index 85b88bd..d7541e3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -11,7 +11,7 @@ @Entity @Getter @Setter -public class ChatUser { +public class ChatUser { // TODO: Pavel -- переименовать сущность и сопутствующие переменные (после задачи Макса) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java deleted file mode 100644 index 7e1d4b7..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/TelegramCallbackData.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.codewithoutus.tgbotusers.model.entity; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import javax.persistence.*; - -@Entity -@Getter -@Setter -public class TelegramCallbackData { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @Column(nullable = false) - String data; -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java index 77890d2..b7a00ca 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java @@ -2,14 +2,17 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import javax.persistence.*; import java.time.LocalDateTime; +import java.util.List; @Entity @Getter @Setter +@ToString public class UserJoining { @Id @@ -25,10 +28,16 @@ public class UserJoining { @Column(nullable = false) private Integer number; + @Column(nullable = false) + private Integer anniversaryNumber; + @Column(nullable = false) private LocalDateTime joinTime; @Enumerated(EnumType.STRING) @Column(nullable = false) private CongratulateStatus status; + + @OneToMany(mappedBy = "userJoining") + private List notifications; } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java index ba0a8c3..83cf17b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java @@ -20,12 +20,6 @@ public class UserJoiningNotification { @Column(nullable = false) private Long sentMessageChatId; - @Column(nullable = false) - private Long userId; - - @Column(nullable = false) - private Long chatId; - - @Column(nullable = false) - private Integer anniversaryNumber; -} + @ManyToOne + private UserJoining userJoining; +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java deleted file mode 100644 index d786712..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/TelegramCallbackDataRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.codewithoutus.tgbotusers.model.repository; - -import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface TelegramCallbackDataRepository extends CrudRepository { - - TelegramCallbackData findById(int id); -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java index 583e7ff..6ab6c4b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java @@ -9,5 +9,7 @@ @Repository public interface UserJoiningNotificationRepository extends CrudRepository { - List findByChatIdAndAnniversaryNumber(Long chatId, Integer anniversaryNumber); + List findByUserJoining_ChatIdAndUserJoining_AnniversaryNumber(Long chatId, Integer anniversaryNumber); + + List findByUserJoining_UserIdAndUserJoining_ChatIdAndUserJoining_AnniversaryNumber(Long userId, Long chatId, Integer anniversaryNumber); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java index f81b146..5ae1e5a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java @@ -1,6 +1,7 @@ package org.codewithoutus.tgbotusers.model.repository; import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @@ -9,7 +10,7 @@ @Repository public interface UserJoiningRepository extends CrudRepository { - UserJoining findByUserIdAndChatId(Long userId, Long chatId); - List findByChatId(Long chatId); + + boolean existsByChatIdAndAnniversaryNumberAndStatus(Long chatId, Integer anniversaryNumber, CongratulateStatus congratulateStatus); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java deleted file mode 100644 index c6aa4d4..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/TelegramCallbackDataService.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.codewithoutus.tgbotusers.model.service; - -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.codewithoutus.tgbotusers.bot.exception.CallbackDataMappingException; -import org.codewithoutus.tgbotusers.config.AppStaticContext; -import org.codewithoutus.tgbotusers.model.entity.TelegramCallbackData; -import org.codewithoutus.tgbotusers.model.repository.TelegramCallbackDataRepository; -import org.springframework.stereotype.Service; - -import java.util.Map; - -@Service -@RequiredArgsConstructor -@Slf4j -public class TelegramCallbackDataService { - - private final TelegramCallbackDataRepository telegramCallbackDataRepository; - - public TelegramCallbackData save(TelegramCallbackData telegramCallbackData) { - return telegramCallbackDataRepository.save(telegramCallbackData); - } - - public TelegramCallbackData findById(int id) { - return telegramCallbackDataRepository.findById(id); - } - - public TelegramCallbackData saveMap(Map callbackData) { - try { - String json = AppStaticContext.OBJECT_MAPPER.writeValueAsString(callbackData); - TelegramCallbackData entity = new TelegramCallbackData(); - entity.setData(json); - return save(entity); - - } catch (JsonProcessingException e) { - log.error("Serializing error of Map {}", callbackData); - throw new CallbackDataMappingException(callbackData.toString()); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java index 5f36b99..b907116 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java @@ -18,6 +18,10 @@ public UserJoiningNotification save(UserJoiningNotification entity) { } public List findByChatIdAndAnniversaryNumber(Long chatId, Integer anniversaryNumber) { - return userJoiningNotificationRepository.findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber); + return userJoiningNotificationRepository.findByUserJoining_ChatIdAndUserJoining_AnniversaryNumber(chatId, anniversaryNumber); + } + + public List findByUserIdAndChatIdAndAnniversaryNumber(Long userId, Long chatId, Integer anniversaryNumber) { + return userJoiningNotificationRepository.findByUserJoining_UserIdAndUserJoining_ChatIdAndUserJoining_AnniversaryNumber(userId, chatId, anniversaryNumber); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java index 6ef7ee2..b44929c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java @@ -2,10 +2,12 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; import org.codewithoutus.tgbotusers.model.repository.UserJoiningRepository; import org.springframework.stereotype.Service; import java.util.List; +import java.util.Optional; @Service @RequiredArgsConstructor @@ -17,11 +19,15 @@ public UserJoining save(UserJoining userJoining) { return userJoiningRepository.save(userJoining); } - public UserJoining findByUserIdAndChatId(Long userId, Long chatId) { - return userJoiningRepository.findByUserIdAndChatId(userId, chatId); + public Optional findById(int id) { + return userJoiningRepository.findById(id); } public List findByChatId(Long chatId) { return userJoiningRepository.findByChatId(chatId); } -} \ No newline at end of file + + public boolean existCongratulatedUser(long chatId, int anniversaryNumber) { + return userJoiningRepository.existsByChatIdAndAnniversaryNumberAndStatus(chatId, anniversaryNumber, CongratulateStatus.CONGRATULATE); + } +} From 7d13d8184c6ac45384582141c9fcfaf8e0e2e057 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sat, 16 Jul 2022 16:39:22 +0700 Subject: [PATCH 23/50] need help with "update" after merge --- .../bot/handler/PrivateMessageHandler.java | 44 ++++++++++++++++++- .../tgbotusers/controller/BotController.java | 2 + .../repository/ChatModeratorRepository.java | 9 ++++ .../model/service/ChatModeratorService.java | 9 ++++ src/main/resources/application.yml | 18 +++++--- 5 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 0a2891b..07f6546 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -8,14 +8,23 @@ import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.model.repository.ChatModeratorRepository; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class PrivateMessageHandler extends Handler { + //список команд + private static final String ADD_MODER_CHAT = "/addModerChat"; + private static final String ADD = "Cool, moder chat № %s , is add in DB"; + private static final String NOT = "Oh, moder chat № %s ,is already in the database"; + private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; + private static final String SORRY = "Sorry, bot does not know this \"%s\" command"; + - private static final String SORRY = "Sorry, functionality not implemented"; private final TelegramService telegramService; + private final ChatModeratorService chatModeratorService; @Override protected boolean handle(Update update) { @@ -68,6 +77,37 @@ private boolean handleTestRequest(Long chatId, String text) { } } } - return false; + //мой id=161855902 /11725 + //TODO проверка пришла ли команда от админа или от левого,если от админа дальше иначе брек + //TODO получить номер группы + //TODO добавить группу в БД + else if (text.startsWith(ADD_MODER_CHAT)) {//парсим команду + long chatID = Integer.parseInt(text.substring(ADD_MODER_CHAT.length()).trim());//парсим id группы + Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту + if (chatID > 0) {//проверяем на валидность номер чата + //вариант от Алексея +// if(!chatModeratorService.findByIDModerChatInDatabase(chatID)){//проверяем есть ли этот ид уже в базе +// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD,chatID))); + ChatModeratorRepository rep = chatModeratorService.getChatModeratorRepository(); + if ((rep.findByChatId(chatID).toString().matches("Optional.empty"))) { + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD, chatID))); + System.out.println("--------list empty, add ID-----------"); + System.out.println(rep.findByChatId(chatID).toString()); + return true; + } else { + System.out.println("--------list NOT empty,NOT add ID-----------"); + System.out.println(rep.findByChatId(chatID).toString()); + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT, chatID))); + return true; + } + } + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ERROR, chatID))); + return true; + } + + // TODO: реализовать админку: добавление и удаление чатов + + telegramService.sendMessage(new SendMessage(chat.id(), String.format(SORRY, text))); + return true; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index f4cf498..94bd741 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -40,4 +40,6 @@ private BotResponse sendMessage(@RequestParam Long chatId, @RequestParam String telegramService.sendMessage(new SendMessage(chatId, message)); return new BotResponse(true, botService.getStatus()); } + + } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java index e537183..9984eea 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java @@ -5,9 +5,18 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface ChatModeratorRepository extends CrudRepository { List findByChatUsers_ChatId(Long chatId); + + @Override + Optional findById(Integer integer); + + + boolean existsByChatId(Long chatId); + + OptionalfindByChatId(Long chatId); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java index 4ba4013..0583b9d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -31,4 +31,13 @@ public List findAll() { public List findByChatUsersId(Long chatId) { return chatModeratorRepository.findByChatUsers_ChatId(chatId); } + + public ChatModeratorRepository getChatModeratorRepository() { + return chatModeratorRepository; + } + public boolean findByIDModerChatInDatabase(Long chatId){ + return chatModeratorRepository.existsByChatId(chatId); + } + + } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ea25542..3ed718b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,9 +1,12 @@ spring: datasource: driver-class-name: org.postgresql.Driver - url: ${DATASOURCE_URL} - username: ${DATASOURCE_USER} - password: ${DATASOURCE_PASS} +# url: ${DATASOURCE_URL} + url: jdbc:postgresql://localhost:5432/tg-bot-users +# username: ${DATASOURCE_USER} + username: postgres +# password: ${DATASOURCE_PASS} + password: testtest hikari: maximum-pool-size: 2 @@ -22,6 +25,11 @@ logging: root: info bot-settings: - bot-user-name: ${BOT_USER_NAME} - bot-token: ${BOT_TOKEN} +# bot-user-name: ${BOT_USER_NAME} + bot-user-name: skillTestBoxBot +# bot-token: ${BOT_TOKEN} + bot-token: 5566542855:AAHQ8Ge8VD8lVv5sw21f1ztrslroFVo1upI long-polling-timeout: 1000 + + +server.port: 8090 From 1d8c92874ef4128732d72af582714dce5a0f8388 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sat, 16 Jul 2022 20:14:27 +0700 Subject: [PATCH 24/50] repear"update" after merge --- .../bot/handler/PrivateMessageHandler.java | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 07f6546..b2ed169 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -26,6 +26,7 @@ public class PrivateMessageHandler extends Handler { private final TelegramService telegramService; private final ChatModeratorService chatModeratorService; + @Override protected boolean handle(Update update) { if (!UpdateUtils.isPrivateMessage(update)) { @@ -36,10 +37,11 @@ protected boolean handle(Update update) { Long chatId = UpdateUtils.getChat(update).id(); if (handleForwardMessage(chatId, update) - || handleAdminRequest(chatId, text) + || handleAdminRequest(update, chatId, text) || handleTestRequest(chatId, text)) { return true; } + telegramService.sendMessage(new SendMessage(chatId, SORRY)); return false; } @@ -56,40 +58,26 @@ private boolean handleForwardMessage(Long chatId, Update update) { return true; } - private boolean handleAdminRequest(Long chatId, String text) { + private boolean handleAdminRequest(Update update, Long chatId, String text) { // TODO: Макс -- реализовать админку (добавление и удаление чатов) // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки // TODO: Макс -- изменения настроек должны сохраняться в базе - return false; - } - private boolean handleTestRequest(Long chatId, String text) { - if (text.startsWith("/test ")) { - telegramService.sendMessage(new SendMessage(chatId, "Идёт тест...")); - - if (text.startsWith("/test InlineKeyboard")) { - String callbackData = text.substring("/test InlineKeyboard".length() + 1).trim(); - if (!callbackData.isBlank()) { - InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( - new InlineKeyboardButton("callback_data").callbackData(callbackData)); - telegramService.sendMessage(new SendMessage(chatId, "InlineKeyboard test").replyMarkup(keyboard)); - return true; - } - } - } //мой id=161855902 /11725 //TODO проверка пришла ли команда от админа или от левого,если от админа дальше иначе брек //TODO получить номер группы //TODO добавить группу в БД - else if (text.startsWith(ADD_MODER_CHAT)) {//парсим команду + + if (text.startsWith(ADD_MODER_CHAT)) {//парсим команду long chatID = Integer.parseInt(text.substring(ADD_MODER_CHAT.length()).trim());//парсим id группы Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту + if (chatID > 0) {//проверяем на валидность номер чата //вариант от Алексея // if(!chatModeratorService.findByIDModerChatInDatabase(chatID)){//проверяем есть ли этот ид уже в базе // telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD,chatID))); ChatModeratorRepository rep = chatModeratorService.getChatModeratorRepository(); - if ((rep.findByChatId(chatID).toString().matches("Optional.empty"))) { + if ((rep.findByChatId(chatID).isEmpty())) { telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD, chatID))); System.out.println("--------list empty, add ID-----------"); System.out.println(rep.findByChatId(chatID).toString()); @@ -105,9 +93,24 @@ else if (text.startsWith(ADD_MODER_CHAT)) {//парсим команду return true; } - // TODO: реализовать админку: добавление и удаление чатов + return false; + } - telegramService.sendMessage(new SendMessage(chat.id(), String.format(SORRY, text))); + private boolean handleTestRequest(Long chatId, String text) { + if (text.startsWith("/test ")) { + telegramService.sendMessage(new SendMessage(chatId, "Идёт тест...")); + + if (text.startsWith("/test InlineKeyboard")) { + String callbackData = text.substring("/test InlineKeyboard".length() + 1).trim(); + if (!callbackData.isBlank()) { + InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( + new InlineKeyboardButton("callback_data").callbackData(callbackData)); + telegramService.sendMessage(new SendMessage(chatId, "InlineKeyboard test").replyMarkup(keyboard)); + return true; + } + } + } + telegramService.sendMessage(new SendMessage(chatId, String.format(SORRY, text))); return true; } } \ No newline at end of file From 6fec8523988e46a82c178c151851fef46c6dce87 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 14:42:45 +0700 Subject: [PATCH 25/50] done addModerChat --- .../bot/handler/PrivateMessageHandler.java | 108 +++++++++++++----- .../model/entity/ChatModerator.java | 8 ++ 2 files changed, 88 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index b2ed169..4947b1e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -8,19 +8,29 @@ import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.codewithoutus.tgbotusers.model.repository.ChatModeratorRepository; import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + @Component @RequiredArgsConstructor public class PrivateMessageHandler extends Handler { //список команд + private static final String HELP = "/help"; private static final String ADD_MODER_CHAT = "/addModerChat"; + private static final String ADD_USER_CHAT = "/addUserChat"; + private static final String ADD_USER_CHAT_IN_MODER_CHAT = "/addUserChatInModerChat"; private static final String ADD = "Cool, moder chat № %s , is add in DB"; private static final String NOT = "Oh, moder chat № %s ,is already in the database"; private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; private static final String SORRY = "Sorry, bot does not know this \"%s\" command"; + List listCommand= Arrays.asList(HELP,ADD_MODER_CHAT,ADD_USER_CHAT,ADD_USER_CHAT_IN_MODER_CHAT); private final TelegramService telegramService; @@ -58,6 +68,23 @@ private boolean handleForwardMessage(Long chatId, Update update) { return true; } + private boolean handleTestRequest(Long chatId, String text) { + if (text.startsWith("/test ")) { + telegramService.sendMessage(new SendMessage(chatId, "Идёт тест...")); + if (text.startsWith("/test InlineKeyboard")) { + String callbackData = text.substring("/test InlineKeyboard".length() + 1).trim(); + if (!callbackData.isBlank()) { + InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( + new InlineKeyboardButton("callback_data").callbackData(callbackData)); + telegramService.sendMessage(new SendMessage(chatId, "InlineKeyboard test").replyMarkup(keyboard)); + return true; + } + } + } + telegramService.sendMessage(new SendMessage(chatId, String.format(SORRY, text))); + return true; + } + private boolean handleAdminRequest(Update update, Long chatId, String text) { // TODO: Макс -- реализовать админку (добавление и удаление чатов) // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки @@ -67,50 +94,75 @@ private boolean handleAdminRequest(Update update, Long chatId, String text) { //TODO проверка пришла ли команда от админа или от левого,если от админа дальше иначе брек //TODO получить номер группы //TODO добавить группу в БД + Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту + String command = text.split(" ")[0]; + switch (command) { + case HELP -> { + telegramService.sendMessage(new SendMessage(chatFrom.id(), "Список доступных команд :\n"+listCommand.toString())); + return true; + } + case ADD_MODER_CHAT -> { + addModerChat(update, chatId, text); + + return true; + } + case ADD_USER_CHAT -> { + telegramService.sendMessage(new SendMessage(chatFrom.id(), "добавили юсер чат")); + return true; + } + case ADD_USER_CHAT_IN_MODER_CHAT -> { + telegramService.sendMessage(new SendMessage(chatFrom.id(), "добавили юсер чат в модер чат")); + return true; + } + + + } + return false; + } + private boolean addModerChat(Update update, Long chatId, String text) { if (text.startsWith(ADD_MODER_CHAT)) {//парсим команду - long chatID = Integer.parseInt(text.substring(ADD_MODER_CHAT.length()).trim());//парсим id группы + long chatIdForAdd = Integer.parseInt(text.substring(ADD_MODER_CHAT.length()).trim());//парсим id группы Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту - if (chatID > 0) {//проверяем на валидность номер чата - //вариант от Алексея -// if(!chatModeratorService.findByIDModerChatInDatabase(chatID)){//проверяем есть ли этот ид уже в базе -// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD,chatID))); + if (chatIdForAdd > 0) {//проверяем на валидность номер чата ChatModeratorRepository rep = chatModeratorService.getChatModeratorRepository(); - if ((rep.findByChatId(chatID).isEmpty())) { - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD, chatID))); - System.out.println("--------list empty, add ID-----------"); - System.out.println(rep.findByChatId(chatID).toString()); + if ((rep.findByChatId(chatIdForAdd).isEmpty())) { + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD, chatIdForAdd))); + rep.save(new ChatModerator(chatIdForAdd, new ArrayList<>()));//сохраняем группу до перезагрузки бота return true; } else { - System.out.println("--------list NOT empty,NOT add ID-----------"); - System.out.println(rep.findByChatId(chatID).toString()); - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT, chatID))); + System.out.println(rep.findByChatId(chatIdForAdd).toString()); + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT, chatIdForAdd))); return true; } } - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ERROR, chatID))); + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ERROR, chatIdForAdd))); return true; } - return false; } - private boolean handleTestRequest(Long chatId, String text) { - if (text.startsWith("/test ")) { - telegramService.sendMessage(new SendMessage(chatId, "Идёт тест...")); + private boolean addUserChat() { + + return true; + } + + private boolean addUserChatInModerChat(int chatModerator, int chatUser) { + ChatModerator chat = new ChatModerator(); + chat.addChatUser(new ChatUser()); + return true; + } + private boolean deleteUserChat() { + + return true; + } + private boolean deleteModerChat() { + + return true; + } + private boolean deleteUserChatFromModerChat() { - if (text.startsWith("/test InlineKeyboard")) { - String callbackData = text.substring("/test InlineKeyboard".length() + 1).trim(); - if (!callbackData.isBlank()) { - InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( - new InlineKeyboardButton("callback_data").callbackData(callbackData)); - telegramService.sendMessage(new SendMessage(chatId, "InlineKeyboard test").replyMarkup(keyboard)); - return true; - } - } - } - telegramService.sendMessage(new SendMessage(chatId, String.format(SORRY, text))); return true; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index 6214796..0b3f7a2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -33,4 +33,12 @@ public void addChatUser(ChatUser chatUser) { } chatUsers.add(chatUser); } + + public ChatModerator(Long chatId, List chatUsers) { + this.chatId = chatId; + this.chatUsers = chatUsers; + } + + public ChatModerator() { + } } \ No newline at end of file From 287bf1e6de31b35497583b544c88e04f7ec49836 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 17:59:42 +0700 Subject: [PATCH 26/50] need help bindUserChatToModer --- .../bot/handler/PrivateMessageHandler.java | 82 ++++++++++++++----- .../tgbotusers/model/entity/ChatUser.java | 10 +++ .../model/repository/ChatUserRepository.java | 6 ++ .../model/service/ChatUserService.java | 4 + 4 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 4947b1e..2061b1d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -11,30 +11,39 @@ import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.codewithoutus.tgbotusers.model.repository.ChatModeratorRepository; +import org.codewithoutus.tgbotusers.model.repository.ChatUserRepository; import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; @Component @RequiredArgsConstructor public class PrivateMessageHandler extends Handler { //список команд + //todo надо потом сделать привествие бота,что бы он сразу присал что если что есть help private static final String HELP = "/help"; private static final String ADD_MODER_CHAT = "/addModerChat"; private static final String ADD_USER_CHAT = "/addUserChat"; - private static final String ADD_USER_CHAT_IN_MODER_CHAT = "/addUserChatInModerChat"; - private static final String ADD = "Cool, moder chat № %s , is add in DB"; - private static final String NOT = "Oh, moder chat № %s ,is already in the database"; + private static final String BIND_USER_CHAT_TO_MODER = "/bindUserChatToModer"; + private static final String OK_BIND = "Cool, user chat № %s , is add in moder chat № %s"; + private static final String OK_MODER = "Cool, moder chat № %s , is add in DB"; + private static final String OK_USER = "Cool, user chat № %s , is add in DB"; + private static final String NOT_MODER = "Oh, moder chat № %s ,is already in the database"; + private static final String NOT_USER = "Oh, user chat № %s ,is already in the database"; private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; private static final String SORRY = "Sorry, bot does not know this \"%s\" command"; - List listCommand= Arrays.asList(HELP,ADD_MODER_CHAT,ADD_USER_CHAT,ADD_USER_CHAT_IN_MODER_CHAT); + List listCommandAdd = Arrays.asList(HELP, ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); + List listCommandDelete = Arrays.asList(ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); private final TelegramService telegramService; private final ChatModeratorService chatModeratorService; + private final ChatUserService chatUserService; @Override @@ -92,30 +101,27 @@ private boolean handleAdminRequest(Update update, Long chatId, String text) { //мой id=161855902 /11725 //TODO проверка пришла ли команда от админа или от левого,если от админа дальше иначе брек - //TODO получить номер группы - //TODO добавить группу в БД + Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту String command = text.split(" ")[0]; switch (command) { case HELP -> { - telegramService.sendMessage(new SendMessage(chatFrom.id(), "Список доступных команд :\n"+listCommand.toString())); + telegramService.sendMessage(new SendMessage(chatFrom.id(), "Список доступных команд :\n" + + listCommandAdd.toString() + "\n" + listCommandDelete)); return true; } case ADD_MODER_CHAT -> { addModerChat(update, chatId, text); - return true; } case ADD_USER_CHAT -> { - telegramService.sendMessage(new SendMessage(chatFrom.id(), "добавили юсер чат")); + addUserChat(update, chatId, text); return true; } - case ADD_USER_CHAT_IN_MODER_CHAT -> { - telegramService.sendMessage(new SendMessage(chatFrom.id(), "добавили юсер чат в модер чат")); + case BIND_USER_CHAT_TO_MODER -> { + bindUserChatToModer(update, text); return true; } - - } return false; } @@ -128,12 +134,11 @@ private boolean addModerChat(Update update, Long chatId, String text) { if (chatIdForAdd > 0) {//проверяем на валидность номер чата ChatModeratorRepository rep = chatModeratorService.getChatModeratorRepository(); if ((rep.findByChatId(chatIdForAdd).isEmpty())) { - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ADD, chatIdForAdd))); + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_MODER, chatIdForAdd))); rep.save(new ChatModerator(chatIdForAdd, new ArrayList<>()));//сохраняем группу до перезагрузки бота return true; } else { - System.out.println(rep.findByChatId(chatIdForAdd).toString()); - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT, chatIdForAdd))); + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_MODER, chatIdForAdd))); return true; } } @@ -143,24 +148,57 @@ private boolean addModerChat(Update update, Long chatId, String text) { return false; } - private boolean addUserChat() { - - return true; + private boolean addUserChat(Update update, Long chatId, String text) { + long chatIdForAdd = Integer.parseInt(text.substring(ADD_USER_CHAT.length()).trim());//парсим id группы + Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту + if (chatIdForAdd > 0) { + ChatUserRepository rep = chatUserService.getChatUserRepository(); + if (rep.findByChatId(chatIdForAdd) == null) { + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_USER, chatIdForAdd))); + rep.save(new ChatUser(chatIdForAdd, new ArrayList<>()));//сохраняем группу до перезагрузки бота + return true; + } else { + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_USER, chatIdForAdd))); + return true; + } + } + return false; } - private boolean addUserChatInModerChat(int chatModerator, int chatUser) { - ChatModerator chat = new ChatModerator(); - chat.addChatUser(new ChatUser()); + private boolean bindUserChatToModer(Update update, String text) { + Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту + int count = text.split(" ").length; + if (count == 3) { + ChatUserRepository repUser = chatUserService.getChatUserRepository(); + int idUser = Integer.parseInt(text.split(" ")[1]); + if (repUser.findByChatId(idUser) != null) { + ChatModeratorRepository repModer = chatModeratorService.getChatModeratorRepository(); + long idModer = Integer.parseInt(text.split(" ")[2]); + if (!repModer.findByChatId(idModer).isEmpty()) { + repModer.findByChatId(idModer).get().addChatUser(repUser.findByChatId(idUser)); + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_BIND, idUser, idModer))); + return true; + } + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_MODER, idModer))); + return true; + } + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_USER, idUser))); + return true; + } + telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(SORRY, text + "-" + count))); return true; } + private boolean deleteUserChat() { return true; } + private boolean deleteModerChat() { return true; } + private boolean deleteUserChatFromModerChat() { return true; diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index d7541e3..05588ec 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -30,4 +30,14 @@ public void addChatModerator(ChatModerator chatModerator) { } chatModerators.add(chatModerator); } + + public ChatUser(Long chatId, List chatModerators) { + this.chatId = chatId; + this.chatModerators = chatModerators; + } + + public ChatUser() { + } + + } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java index 6669c05..cd8d798 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java @@ -1,10 +1,12 @@ package org.codewithoutus.tgbotusers.model.repository; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface ChatUserRepository extends CrudRepository { @@ -12,4 +14,8 @@ public interface ChatUserRepository extends CrudRepository { ChatUser findByChatId(long chatId); List findByChatModeratorsNotEmpty(); + + boolean existsByChatId(Long chatId); + + OptionalfindByChatId(Long chatId); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index b3eeb33..a48d13a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -3,10 +3,13 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.codewithoutus.tgbotusers.model.repository.ChatUserRepository; import org.springframework.stereotype.Service; +import java.util.List; + @Getter @Setter @Service @@ -27,6 +30,7 @@ public ChatUser findByChatId(long chatId) { return chatUserRepository.findByChatId(chatId); } + public boolean isChatUser(long id) { return chatUserRepository.findByChatModeratorsNotEmpty() .stream() From 112c1ba81309581472469b07df48f5c551ec95ab Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 18:05:01 +0700 Subject: [PATCH 27/50] need help bindUserChatToModer now --- .../tgbotusers/bot/handler/PrivateMessageHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 2061b1d..496bbe1 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -176,6 +176,7 @@ private boolean bindUserChatToModer(Update update, String text) { long idModer = Integer.parseInt(text.split(" ")[2]); if (!repModer.findByChatId(idModer).isEmpty()) { repModer.findByChatId(idModer).get().addChatUser(repUser.findByChatId(idUser)); + repUser.findByChatId(idUser).addChatModerator(repModer.findByChatId(idModer).get()); telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_BIND, idUser, idModer))); return true; } From 8c1c4f2ea7640da9ab921245d54a452e79a9d60a Mon Sep 17 00:00:00 2001 From: Aleksei Date: Sun, 17 Jul 2022 15:29:51 +0400 Subject: [PATCH 28/50] added command handler. Project not start! --- .../{BotCommands.java => BotCommand.java} | 4 +- .../bot/handler/CallbackQueryHandler.java | 45 ++--- .../bot/handler/ChatJoinRequestHandler.java | 1 + .../bot/handler/MessageHandler.java | 159 ++++++++++++++++++ .../bot/handler/PrivateMessageHandler.java | 1 - .../tgbotusers/bot/handler/UpdateUtils.java | 3 +- .../CongratulationDecisionKeyboard.java | 10 +- .../tgbotusers/bot/keyboard/Keyboard.java | 4 +- .../tgbotusers/bot/service/BotService.java | 25 +-- .../bot/service/NotificationService.java | 10 +- .../tgbotusers/config/ChatSettings.java | 7 + .../tgbotusers/model/entity/ChatUser.java | 3 + .../tgbotusers/model/entity/UserJoining.java | 32 +++- .../repository/ChatModeratorRepository.java | 8 +- .../model/repository/ChatUserRepository.java | 5 + .../model/service/ChatModeratorService.java | 9 + .../model/service/ChatUserService.java | 10 ++ src/main/resources/application-bot.yml | 2 +- 18 files changed, 287 insertions(+), 51 deletions(-) rename src/main/java/org/codewithoutus/tgbotusers/bot/enums/{BotCommands.java => BotCommand.java} (80%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java similarity index 80% rename from src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java index b68de62..777de4d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommands.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java @@ -3,7 +3,7 @@ import lombok.Getter; @Getter -public enum BotCommands { +public enum BotCommand { LUCKY_LIST("/luckyList"), CONGRATULATE("/congratulate"), @@ -11,7 +11,7 @@ public enum BotCommands { private final String text; - BotCommands(String text) { + BotCommand(String text) { this.text = text; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index 8dccbe6..feaa6cb 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -1,8 +1,11 @@ package org.codewithoutus.tgbotusers.bot.handler; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.MessageEntity; import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; import org.codewithoutus.tgbotusers.bot.exception.CommandNotFoundException; import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; @@ -10,10 +13,14 @@ import org.codewithoutus.tgbotusers.config.AppStaticContext; import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; import org.codewithoutus.tgbotusers.model.service.UserJoiningService; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -24,20 +31,23 @@ public class CallbackQueryHandler extends Handler { private final NotificationService notificationService; private final UserJoiningService userJoiningService; + private final ChatModeratorService chatModeratorService; @Override protected boolean handle(Update update) { + // есть ли callbackQuery в update Map callbackQueryData = UpdateUtils.getCallbackQueryDataAsMap(update); if (callbackQueryData == null || callbackQueryData.isEmpty()) { return false; } + + // есть ли команда в callbackQuery String command = (String) callbackQueryData.get("command"); if (command == null || command.isBlank()) { return false; } - - if (handleCongratulationDecision(command, callbackQueryData) - || handleProvidingAnniversaryStatistic(command, callbackQueryData)) { + + if (handleCongratulationDecision(command, callbackQueryData)) { return true; } else { @@ -48,14 +58,18 @@ protected boolean handle(Update update) { @Transactional private boolean handleCongratulationDecision(String command, Map callbackQueryData) { - Optional key = KeyboardUtils.defineKey(CongratulationDecisionKeyboard.class, command); - if (key.isEmpty()) { + // есть ли команда в callbackQuery + Optional decisionOptional = + KeyboardUtils.defineKey(CongratulationDecisionKeyboard.class, command); + if (decisionOptional.isEmpty()) { return false; } - CongratulationDecisionKeyboard decision = key.get(); + + // есть ли поздравленные в чате с таким порядковым номером Integer userJoiningId = (Integer) callbackQueryData.get(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD); UserJoining userJoining = userJoiningService.findById(userJoiningId) - .orElseThrow(() -> new IllegalStateException("CallbackQueryData with no exist user joining ID" + userJoiningId)); + .orElseThrow(() -> new IllegalStateException( + "CallbackQueryData with no exist user joining ID" + userJoiningId)); Long chatId = userJoining.getChatId(); Long userId = userJoining.getUserId(); @@ -65,7 +79,9 @@ private boolean handleCongratulationDecision(String command, Map if (userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { return true; } - + + + CongratulationDecisionKeyboard decision = decisionOptional.get(); CongratulateStatus newStatus = (decision == CongratulationDecisionKeyboard.CONGRATULATE) ? CongratulateStatus.CONGRATULATE : CongratulateStatus.DECLINE; @@ -81,16 +97,5 @@ private boolean handleCongratulationDecision(String command, Map } return true; } - - @Transactional - private boolean handleProvidingAnniversaryStatistic(String command, Map callbackQueryData) { - // TODO: Алекс -- реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" - - // TODO: Алекс -- уже поздравленные имеют корону, можно добавить в шаблоны и вставлять в начало, неудачники не показываются - // TODO: Алекс -- если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard - // TODO: Алекс -- если поздравленного нет, то отклоненные тоже выводятся - // TODO: Алекс -- все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись - // TODO: Алекс -- можно попробовать выводить все-все чаты, а потом доработать -- на будущее - return false; - } + } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java index c07a5dc..67239b2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java @@ -51,6 +51,7 @@ protected boolean handle(Update update) { userJoining.setJoinTime(LocalDateTime.from(Instant.ofEpochSecond(chatJoinRequest.date()))); userJoining = userJoiningService.save(userJoining); + // если не было поздравненных в чате с таким порядковым номером if (!userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { notificationService.notifyModeratorsAboutUserJoining(userJoining); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java new file mode 100644 index 0000000..622569c --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java @@ -0,0 +1,159 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.MessageEntity; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.User; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.codewithoutus.tgbotusers.model.service.UserJoiningService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@Component("UpdateMessageHandler") +@RequiredArgsConstructor +public class MessageHandler extends Handler { + + private final ChatModeratorService chatModeratorService; + private final ChatUserService chatUserService; + private final UserJoiningService userJoiningService; + private final TelegramService telegramService; + + @Override + protected boolean handle(Update update) { + // TODO: Алекс -- реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" + + // TODO: Алекс -- уже поздравленные имеют корону, можно добавить в шаблоны и вставлять в начало, неудачники не показываются + // TODO: Алекс -- если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard + // TODO: Алекс -- если поздравленного нет, то отклоненные тоже выводятся + // TODO: Алекс -- все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись + // TODO: Алекс -- можно попробовать выводить все-все чаты, а потом доработать -- на будущее + + + // получить команду из update + // получить moderatorChatId + // получить все подчинённые chatId + // получить все userJoining из этих чатов + // сформироваь список -> сформировать текст сообщения + // оправить сообщение + + Message message = update.message(); + Optional botEntityOptional = getBotCommandEntity(message); + if (botEntityOptional.isEmpty()) { + return false; + } + + Long chatId = message.chat().id(); + if (!chatModeratorService.existsByChatId(chatId)) { + return false; + } + + MessageEntity entity = botEntityOptional.get(); + + handleBotCommand(message.text(), entity, chatId); + + + + + return false; + } + + + private Optional getBotCommandEntity(Message message) { + + if (message == null) { + return Optional.empty(); + } + + MessageEntity[] entities = message.entities(); + if (entities == null || entities.length <= 0) { + return Optional.empty(); + } + + return Arrays.stream(message.entities()) + .filter(entity -> entity.type().equals(MessageEntity.Type.bot_command)) + .findFirst(); + } + + private void handleBotCommand(String messageText, MessageEntity entity, Long moderatorChatId) { + + if (messageText.contains(BotCommand.LUCKY_LIST.getText())) { + performLuckyList(messageText, entity, moderatorChatId); + } + + + } + + private void performLuckyList(String messageText, MessageEntity entity, Long moderatorChatId) { + String luckyListText = ""; + String param = parseParam(messageText, entity); + if (!param.isEmpty()) { + Optional chatUserOptional = chatUserService.findByName(param); + if(chatUserOptional.isPresent()) { + luckyListText = createLuckyListText(List.of(chatUserOptional.get())); + } + } else { + ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorChatId) + .orElseThrow(() -> new IllegalArgumentException( + "DB was changed after check exists with moderatorChatId=" + moderatorChatId)); + luckyListText = createLuckyListText(chatModerator.getChatUsers()); + } + + sendLuckyList(luckyListText, moderatorChatId); + } + + private String parseParam(String text, MessageEntity entity) { + String param = text.substring(entity.offset() + entity.length()); + return param.trim(); + } + + private String createLuckyListText(List chatUsers) { + + // получить все объекты userJoining из базы + List userJoinings = new ArrayList<>(); + for (ChatUser chatUser : chatUsers) { + List joinings = userJoiningService.findByChatId(chatUser.getChatId()); + userJoinings.addAll(joinings); + } + + userJoinings.sort(Comparator.naturalOrder()); + + // получить актуальные данные по каждому объекту + // собрать список в строку + return userJoinings.stream() + .map(joining -> { + User user = telegramService.getUser(joining.getChatId(), joining.getUserId()); + return buildMember(user); + }) + .reduce((member1, member2) -> { + if (!member1.isEmpty()) { + member1.append("\n\n"); + } + member1.append(member2); + return member1; + }) + .orElse(new StringBuilder()) + .toString(); + } + + private StringBuilder buildMember(User user) { + + return null; + } + + private void sendLuckyList(String luckyListText, Long moderatorChatId) { + + } + +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 0a2891b..8e27d37 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -1,6 +1,5 @@ package org.codewithoutus.tgbotusers.bot.handler; -import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.request.InlineKeyboardButton; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java index 5177a2b..53d3ab0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java @@ -39,8 +39,7 @@ public static Chat getChat(Update update) { } public static String getCallbackQueryData(Update update) { - return Optional - .ofNullable(update.callbackQuery()) + return Optional.ofNullable(update.callbackQuery()) .map(CallbackQuery::data) .filter(data -> !data.isBlank()) .orElse("{}"); diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java index bd80f33..2e7a96f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/CongratulationDecisionKeyboard.java @@ -1,18 +1,18 @@ package org.codewithoutus.tgbotusers.bot.keyboard; import lombok.Getter; -import org.codewithoutus.tgbotusers.bot.enums.BotCommands; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; @Getter public enum CongratulationDecisionKeyboard implements Keyboard { - CONGRATULATE(BotCommands.CONGRATULATE, "Поздравить \uD83E\uDD73"), - DECLINE(BotCommands.DECLINE, "Отклонить 🚫"); + CONGRATULATE(BotCommand.CONGRATULATE, "Поздравить \uD83E\uDD73"), + DECLINE(BotCommand.DECLINE, "Отклонить 🚫"); - private final BotCommands botCommand; + private final BotCommand botCommand; private final String representation; - CongratulationDecisionKeyboard(BotCommands botCommand, String representation) { + CongratulationDecisionKeyboard(BotCommand botCommand, String representation) { this.botCommand = botCommand; this.representation = representation; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java index 1cc835d..deb17c5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Keyboard.java @@ -1,9 +1,9 @@ package org.codewithoutus.tgbotusers.bot.keyboard; -import org.codewithoutus.tgbotusers.bot.enums.BotCommands; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; public interface Keyboard { - BotCommands getBotCommand(); + BotCommand getBotCommand(); String getRepresentation(); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java index 67bb0c2..3dc999c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java @@ -8,6 +8,7 @@ import org.codewithoutus.tgbotusers.bot.enums.BotStatus; import org.codewithoutus.tgbotusers.bot.handler.CallbackQueryHandler; import org.codewithoutus.tgbotusers.bot.handler.ChatJoinRequestHandler; +import org.codewithoutus.tgbotusers.bot.handler.MessageHandler; import org.codewithoutus.tgbotusers.bot.handler.PrivateMessageHandler; import org.springframework.stereotype.Service; @@ -16,17 +17,18 @@ @Service @RequiredArgsConstructor public class BotService { - + private final Bot bot; private final CallbackQueryHandler callbackQueryHandler; private final ChatJoinRequestHandler chatJoinRequestHandler; private final PrivateMessageHandler privateMessageHandler; - + private final MessageHandler messageHandler; + @PostConstruct private void botStart() { start(); } - + public boolean start() { if (bot.getStatus() == BotStatus.START) { return false; @@ -35,7 +37,7 @@ public boolean start() { bot.setStatus(BotStatus.START); return true; } - + public boolean stop() { if (bot.getStatus() == BotStatus.STOP) { return false; @@ -44,29 +46,30 @@ public boolean stop() { bot.setStatus(BotStatus.STOP); return true; } - + public BotStatus getStatus() { return bot.getStatus(); } - + private void startUpdatePolling() { GetUpdates timeout = new GetUpdates().timeout(bot.getBotSettings().getLongPollingTimeout()); UpdatesListener updatesListener = (updates -> { updates.forEach(this::handleUpdate); return UpdatesListener.CONFIRMED_UPDATES_ALL; }); - + bot.setUpdatesListener(updatesListener, timeout); } - + private void stopUpdatePolling() { bot.removeGetUpdatesListener(); } - + private void handleUpdate(Update update) { - if (privateMessageHandler.tryHandle(update) + if (chatJoinRequestHandler.tryHandle(update) || callbackQueryHandler.tryHandle(update) - || chatJoinRequestHandler.tryHandle(update)) { + || privateMessageHandler.tryHandle(update) + || messageHandler.tryHandle(update)) { // update handled, return } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index 8429dad..92db719 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -28,8 +28,8 @@ public class NotificationService { @Transactional public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { - List moderators = chatModeratorService.findByChatUsersId(userJoining.getChatId()); - if (moderators.isEmpty()) { + List moderatorChats = chatModeratorService.findByChatUsersId(userJoining.getChatId()); + if (moderatorChats.isEmpty()) { log.warn(""); return; } @@ -39,13 +39,13 @@ public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { String notificationText = userJoining.toString(); // TODO: Алекс -- подтянуть шаблон - for (ChatModerator moderator : moderators) { - SendMessage message = new SendMessage(moderator.getChatId(), notificationText).replyMarkup(keyboard); + for (ChatModerator moderatorChat : moderatorChats) { + SendMessage message = new SendMessage(moderatorChat.getChatId(), notificationText).replyMarkup(keyboard); SendResponse response = telegramService.sendMessage(message); UserJoiningNotification notification = new UserJoiningNotification(); notification.setUserJoining(userJoining); - notification.setSentMessageChatId(moderator.getChatId()); + notification.setSentMessageChatId(moderatorChat.getChatId()); notification.setSentMessageId(response.message().messageId()); userJoiningNotificationService.save(notification); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java index c62fafc..83ed969 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java @@ -56,6 +56,13 @@ private void rewriteDataBaseSettings() { chatModeratorService.deleteAll(); chatUserService.deleteAll(); + // TODO : Павел - необходимо: + // 1. при запуске приложения проверять настройки в базе, + // если в базе пусто проверять конфигурацию и записывать в базу + // 2. при каждом запуске запуске актуализировать названия юзерских чатов (поле name) + // (нужно для получения возможности получения списка юбилейных с параметром названия группы, + // пример команды: [/luckyList@UsersTgBot Java разработчик]) + for (Map.Entry> moderatorData : chatsSettingsData.entrySet()) { ChatModerator chatModerator = new ChatModerator(); chatModerator.setChatId(moderatorData.getKey()); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index d7541e3..471ab4f 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -20,6 +20,9 @@ public class ChatUser { // TODO: Pavel -- переименовать сущно @NaturalId @Column(nullable = false, unique = true) private Long chatId; + + @Column(nullable = false) + private String name; @ManyToMany(mappedBy = "chatUsers", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) private List chatModerators; diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java index b7a00ca..03ca0af 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java @@ -4,6 +4,7 @@ import lombok.Setter; import lombok.ToString; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.jetbrains.annotations.NotNull; import javax.persistence.*; import java.time.LocalDateTime; @@ -13,7 +14,7 @@ @Getter @Setter @ToString -public class UserJoining { +public class UserJoining implements Comparable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -39,5 +40,34 @@ public class UserJoining { private CongratulateStatus status; @OneToMany(mappedBy = "userJoining") + @ToString.Exclude private List notifications; + + + @Override + public int compareTo(@NotNull UserJoining o) { + int result = number.compareTo(o.number); + if (result == 0) { + result = joinTime.compareTo(o.joinTime); + } + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UserJoining that = (UserJoining) o; + + if (!userId.equals(that.userId)) return false; + return chatId.equals(that.chatId); + } + + @Override + public int hashCode() { + int result = userId.hashCode(); + result = 31 * result + chatId.hashCode(); + return result; + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java index e537183..81c1dfd 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java @@ -5,9 +5,15 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface ChatModeratorRepository extends CrudRepository { - + + boolean existsByChatId(Long chatId); + List findByChatUsers_ChatId(Long chatId); + + Optional findByChatId(Long chatId); + } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java index 6669c05..e996b1b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java @@ -5,11 +5,16 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface ChatUserRepository extends CrudRepository { + + boolean existsByName(String name); ChatUser findByChatId(long chatId); + + Optional findByName(String name); List findByChatModeratorsNotEmpty(); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java index 4ba4013..d6c65e9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Service @RequiredArgsConstructor @@ -31,4 +32,12 @@ public List findAll() { public List findByChatUsersId(Long chatId) { return chatModeratorRepository.findByChatUsers_ChatId(chatId); } + + public boolean existsByChatId(Long chatId) { + return chatModeratorRepository.existsByChatId(chatId); + } + + public Optional findByChatId(Long chatId) { + return chatModeratorRepository.findByChatId(chatId); + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index b3eeb33..b7b1022 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -7,6 +7,8 @@ import org.codewithoutus.tgbotusers.model.repository.ChatUserRepository; import org.springframework.stereotype.Service; +import java.util.Optional; + @Getter @Setter @Service @@ -18,6 +20,10 @@ public class ChatUserService { public void deleteAll() { chatUserRepository.deleteAll(); } + + public boolean existsByName(String name) { + return chatUserRepository.existsByName(name); + } public ChatUser save(ChatUser entity) { return chatUserRepository.save(entity); @@ -27,6 +33,10 @@ public ChatUser findByChatId(long chatId) { return chatUserRepository.findByChatId(chatId); } + public Optional findByName(String name) { + return chatUserRepository.findByName(name); + } + public boolean isChatUser(long id) { return chatUserRepository.findByChatModeratorsNotEmpty() .stream() diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index bfca316..cbb6b0a 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -19,6 +19,6 @@ chats-settings: - 10000 message-templates: - join-congratulation: "🎉 Поздравляю, {Никита}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" + join-congratulation: "🎉 Поздравляю, {ИмяУчастника}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления}" join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления}" From 1079479f56494854ce8580e2e047c4ca9d0f627f Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 21:12:36 +0700 Subject: [PATCH 29/50] done all add --- .../bot/handler/PrivateMessageHandler.java | 80 ++++++++++++------- .../model/entity/ChatModerator.java | 13 ++- .../tgbotusers/model/entity/ChatUser.java | 1 + .../repository/ChatModeratorRepository.java | 1 - .../model/service/ChatModeratorService.java | 5 +- .../model/service/ChatUserService.java | 3 +- 6 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 496bbe1..8d21f81 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -1,6 +1,5 @@ package org.codewithoutus.tgbotusers.bot.handler; -import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.request.InlineKeyboardButton; @@ -10,8 +9,7 @@ import org.codewithoutus.tgbotusers.bot.service.TelegramService; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; -import org.codewithoutus.tgbotusers.model.repository.ChatModeratorRepository; -import org.codewithoutus.tgbotusers.model.repository.ChatUserRepository; +import org.codewithoutus.tgbotusers.model.service.AdministratorService; import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; import org.codewithoutus.tgbotusers.model.service.ChatUserService; import org.springframework.stereotype.Component; @@ -31,12 +29,14 @@ public class PrivateMessageHandler extends Handler { private static final String ADD_USER_CHAT = "/addUserChat"; private static final String BIND_USER_CHAT_TO_MODER = "/bindUserChatToModer"; private static final String OK_BIND = "Cool, user chat № %s , is add in moder chat № %s"; + private static final String NOT_BIND = "Oh, user chat № %s , is already in moder chat № %s"; private static final String OK_MODER = "Cool, moder chat № %s , is add in DB"; private static final String OK_USER = "Cool, user chat № %s , is add in DB"; private static final String NOT_MODER = "Oh, moder chat № %s ,is already in the database"; private static final String NOT_USER = "Oh, user chat № %s ,is already in the database"; private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; - private static final String SORRY = "Sorry, bot does not know this \"%s\" command"; + private static final String ANKNOW_COMMAND = "Sorry, bot does not know this \"%s\" command"; + private static final String SORRY = "Sorry"; List listCommandAdd = Arrays.asList(HELP, ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); List listCommandDelete = Arrays.asList(ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); @@ -60,8 +60,7 @@ protected boolean handle(Update update) { || handleTestRequest(chatId, text)) { return true; } - - telegramService.sendMessage(new SendMessage(chatId, SORRY)); + telegramService.sendMessage(new SendMessage(chatId, SORRY + update.message() + update.toString() + update)); return false; } @@ -90,7 +89,7 @@ private boolean handleTestRequest(Long chatId, String text) { } } } - telegramService.sendMessage(new SendMessage(chatId, String.format(SORRY, text))); + telegramService.sendMessage(new SendMessage(chatId, String.format(ANKNOW_COMMAND, text))); return true; } @@ -98,6 +97,7 @@ private boolean handleAdminRequest(Update update, Long chatId, String text) { // TODO: Макс -- реализовать админку (добавление и удаление чатов) // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки // TODO: Макс -- изменения настроек должны сохраняться в базе + // TODO сохраняем в бд группы с + а нужно с - //мой id=161855902 /11725 //TODO проверка пришла ли команда от админа или от левого,если от админа дальше иначе брек @@ -165,29 +165,55 @@ private boolean addUserChat(Update update, Long chatId, String text) { return false; } - private boolean bindUserChatToModer(Update update, String text) { - Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту - int count = text.split(" ").length; - if (count == 3) { - ChatUserRepository repUser = chatUserService.getChatUserRepository(); - int idUser = Integer.parseInt(text.split(" ")[1]); - if (repUser.findByChatId(idUser) != null) { - ChatModeratorRepository repModer = chatModeratorService.getChatModeratorRepository(); - long idModer = Integer.parseInt(text.split(" ")[2]); - if (!repModer.findByChatId(idModer).isEmpty()) { - repModer.findByChatId(idModer).get().addChatUser(repUser.findByChatId(idUser)); - repUser.findByChatId(idUser).addChatModerator(repModer.findByChatId(idModer).get()); - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_BIND, idUser, idModer))); - return true; - } - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_MODER, idModer))); - return true; - } - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_USER, idUser))); + private boolean bindUserChatToModer(Update update, Long chatId, String text) { + Matcher matcher = BIND_USER_CHAT_REGEX.matcher(text); + if (!matcher.matches()) { + return false; + } + long userGroupId = Long.parseLong(matcher.group(1)); + long moderatorGroupId = Long.parseLong(matcher.group(2)); + + ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); + if (chatUser == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_USER, userGroupId))); + return true; + } + ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); + if (chatModerator == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, moderatorGroupId))); return true; } - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(SORRY, text + "-" + count))); + if (chatModerator.getChatUsers().contains(chatUser)) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_BIND, userGroupId, moderatorGroupId))); + return true; + } + chatModerator.addChatUser(chatUser); + chatModeratorService.save(chatModerator); + telegramService.sendMessage(new SendMessage(chatId, String.format(OK_BIND, userGroupId, moderatorGroupId))); return true; + +// int count = text.split(" ").length; +// if (count == 3) { +// //ChatUserRepository repUser = chatUserService.getChatUserRepository(); +// int idUser = Integer.parseInt(text.split(" ")[1]); +// if (repUser.findByChatId(idUser) != null) { +// //ChatModeratorRepository repModer = chatModeratorService.getChatModeratorRepository(); +// +// long idModer = Integer.parseInt(text.split(" ")[2]); +// if (!repModer.findByChatId(idModer).isEmpty()) { +// repModer.findByChatId(idModer).get().addChatUser(repUser.findByChatId(idUser)); +// repUser.findByChatId(idUser).addChatModerator(repModer.findByChatId(idModer).get()); +// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_BIND, idUser, idModer))); +// return true; +// } +// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_MODER, idModer))); +// return true; +// } +// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_USER, idUser))); +// return true; +// } +// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(SORRY, text))); +// return true; } private boolean deleteUserChat() { diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index 0b3f7a2..40f8929 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -7,6 +7,7 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +import java.util.Objects; @Entity @Getter @@ -39,6 +40,16 @@ public ChatModerator(Long chatId, List chatUsers) { this.chatUsers = chatUsers; } - public ChatModerator() { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChatModerator that = (ChatModerator) o; + return chatId.equals(that.chatId); + } + + @Override + public int hashCode() { + return Objects.hash(chatId); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index 05588ec..a9ac479 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -7,6 +7,7 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +import java.util.Objects; @Entity @Getter diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java index 9984eea..7170576 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java @@ -15,7 +15,6 @@ public interface ChatModeratorRepository extends CrudRepository findById(Integer integer); - boolean existsByChatId(Long chatId); OptionalfindByChatId(Long chatId); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java index 0583b9d..8ecaa8a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Service @RequiredArgsConstructor @@ -32,8 +33,8 @@ public List findByChatUsersId(Long chatId) { return chatModeratorRepository.findByChatUsers_ChatId(chatId); } - public ChatModeratorRepository getChatModeratorRepository() { - return chatModeratorRepository; + public Optional findByChatId(Long chatId){ + return chatModeratorRepository.findByChatId(chatId); } public boolean findByIDModerChatInDatabase(Long chatId){ return chatModeratorRepository.existsByChatId(chatId); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index a48d13a..110992c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Service; import java.util.List; +import java.util.Optional; @Getter @Setter @@ -26,7 +27,7 @@ public ChatUser save(ChatUser entity) { return chatUserRepository.save(entity); } - public ChatUser findByChatId(long chatId) { + public Optional findByChatId(long chatId) { return chatUserRepository.findByChatId(chatId); } From d75664fd50d6c89094e86a8ad0c175d15994f460 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 22:14:59 +0700 Subject: [PATCH 30/50] done delete user & moder chat. need help unbind --- .../bot/handler/PrivateMessageHandler.java | 31 +++++++++++++++---- .../model/service/ChatModeratorService.java | 9 ++++++ .../model/service/ChatUserService.java | 4 +++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 8d21f81..96eef48 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -28,15 +28,20 @@ public class PrivateMessageHandler extends Handler { private static final String ADD_MODER_CHAT = "/addModerChat"; private static final String ADD_USER_CHAT = "/addUserChat"; private static final String BIND_USER_CHAT_TO_MODER = "/bindUserChatToModer"; + private static final String UNBIND_USER_CHAT_TO_MODER = "/unbindUserChatToModer"; + private static final String OK_BIND = "Cool, user chat № %s , is add in moder chat № %s"; private static final String NOT_BIND = "Oh, user chat № %s , is already in moder chat № %s"; + private static final String NOT_UNBIND = "Oh, user chat № %s , is not bind in moder chat № %s"; + private static final String OK_UNBIND = "Cool, user chat № %s , is unbind from moder chat № %s"; private static final String OK_MODER = "Cool, moder chat № %s , is add in DB"; private static final String OK_USER = "Cool, user chat № %s , is add in DB"; private static final String NOT_MODER = "Oh, moder chat № %s ,is already in the database"; private static final String NOT_USER = "Oh, user chat № %s ,is already in the database"; private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; - private static final String ANKNOW_COMMAND = "Sorry, bot does not know this \"%s\" command"; + private static final String UNKNOW_COMMAND = "Sorry, bot does not know this \"%s\" command"; private static final String SORRY = "Sorry"; + //todo вынести команды бота в бот сервис/в бот команд List listCommandAdd = Arrays.asList(HELP, ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); List listCommandDelete = Arrays.asList(ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); @@ -89,14 +94,12 @@ private boolean handleTestRequest(Long chatId, String text) { } } } - telegramService.sendMessage(new SendMessage(chatId, String.format(ANKNOW_COMMAND, text))); + telegramService.sendMessage(new SendMessage(chatId, String.format(UNKNOW_COMMAND, text))); return true; } private boolean handleAdminRequest(Update update, Long chatId, String text) { - // TODO: Макс -- реализовать админку (добавление и удаление чатов) // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки - // TODO: Макс -- изменения настроек должны сохраняться в базе // TODO сохраняем в бд группы с + а нужно с - //мой id=161855902 /11725 @@ -216,9 +219,25 @@ private boolean bindUserChatToModer(Update update, Long chatId, String text) { // return true; } - private boolean deleteUserChat() { - + private boolean deleteUserChat(Update update, Long chatId,String text) { + Matcher matcher = DELETE_USER_CHAT_REGEX.matcher(text); + if (!matcher.matches()) { + return false; + } + long userGroupId = Long.parseLong(matcher.group(1)); + ChatUser chatUser= chatUserService.findByChatId(userGroupId).orElse(null); + if (chatUser == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, userGroupId))); + return true; + } + if(!chatUser.getChatModerators().isEmpty()){ + telegramService.sendMessage(new SendMessage(chatId, "Есть связные сущности у группы "+ userGroupId)); + return true; + } + chatUserService.deleteById(chatUser.getId()); + telegramService.sendMessage(new SendMessage(chatId, "Удалили группу юзеров № "+ userGroupId)); return true; + } private boolean deleteModerChat() { diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java index 8ecaa8a..a4700a8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.codewithoutus.tgbotusers.model.repository.ChatModeratorRepository; import org.springframework.stereotype.Service; @@ -18,11 +19,19 @@ public class ChatModeratorService { public void deleteAll() { chatModeratorRepository.deleteAll(); } + public void deleteById(Integer id) { + chatModeratorRepository.deleteById(id); + } public ChatModerator save(ChatModerator entity) { return chatModeratorRepository.save(entity); } + public void delete(ChatModerator chatModerator,ChatUser chatUser){ + + + } + public List findAll() { List result = new ArrayList<>(); chatModeratorRepository.findAll().forEach(result::add); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index 110992c..f8d1da9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -22,11 +22,15 @@ public class ChatUserService { public void deleteAll() { chatUserRepository.deleteAll(); } + public void deleteById(Integer id) { + chatUserRepository.deleteById(id); + } public ChatUser save(ChatUser entity) { return chatUserRepository.save(entity); } + public Optional findByChatId(long chatId) { return chatUserRepository.findByChatId(chatId); } From 1698ccd6fd60e4d21a1e01f64e73fd739fc7e73d Mon Sep 17 00:00:00 2001 From: Aleksei Date: Sun, 17 Jul 2022 19:16:54 +0400 Subject: [PATCH 31/50] added class Template, added creation of notification text for joinAlert, joinCongratulate and joinUserInfo (+ printout congratulated users with crown) --- .../tgbotusers/bot/enums/TemplatePattern.java | 20 ++++ .../bot/handler/MessageHandler.java | 43 +++------ .../tgbotusers/bot/keyboard/Template.java | 96 +++++++++++++++++++ .../bot/service/NotificationService.java | 11 ++- .../tgbotusers/config/ChatSettings.java | 3 +- .../config/NotificationTemplates.java | 10 +- .../tgbotusers/model/entity/ChatUser.java | 2 +- 7 files changed, 143 insertions(+), 42 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java new file mode 100644 index 0000000..6c2dc78 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java @@ -0,0 +1,20 @@ +package org.codewithoutus.tgbotusers.bot.enums; + +public enum TemplatePattern { + + GROUP_NAME("НазваниеГруппы"), + USER_NAME("ИмяУчастника"), + NICK_NAME("НикУчастника"), + JOINING_NUMBER("порядковыйНомерВступления"), + JOIN_TIME("ВремяВступления"); + + private final String pattern; + + TemplatePattern(String pattern) { + this.pattern = pattern; + } + + public String getPattern() { + return '{' + pattern + '}'; + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java index 622569c..5d4808d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java @@ -3,9 +3,10 @@ import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.MessageEntity; import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.enums.BotCommand; +import org.codewithoutus.tgbotusers.bot.keyboard.Template; import org.codewithoutus.tgbotusers.bot.service.TelegramService; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; @@ -29,24 +30,16 @@ public class MessageHandler extends Handler { private final ChatUserService chatUserService; private final UserJoiningService userJoiningService; private final TelegramService telegramService; + private final Template template; @Override protected boolean handle(Update update) { // TODO: Алекс -- реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" - - // TODO: Алекс -- уже поздравленные имеют корону, можно добавить в шаблоны и вставлять в начало, неудачники не показываются + // TODO: Алекс -- если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard // TODO: Алекс -- если поздравленного нет, то отклоненные тоже выводятся // TODO: Алекс -- все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись // TODO: Алекс -- можно попробовать выводить все-все чаты, а потом доработать -- на будущее - - - // получить команду из update - // получить moderatorChatId - // получить все подчинённые chatId - // получить все userJoining из этих чатов - // сформироваь список -> сформировать текст сообщения - // оправить сообщение Message message = update.message(); Optional botEntityOptional = getBotCommandEntity(message); @@ -62,9 +55,6 @@ protected boolean handle(Update update) { MessageEntity entity = botEntityOptional.get(); handleBotCommand(message.text(), entity, chatId); - - - return false; } @@ -87,12 +77,9 @@ private Optional getBotCommandEntity(Message message) { } private void handleBotCommand(String messageText, MessageEntity entity, Long moderatorChatId) { - if (messageText.contains(BotCommand.LUCKY_LIST.getText())) { performLuckyList(messageText, entity, moderatorChatId); } - - } private void performLuckyList(String messageText, MessageEntity entity, Long moderatorChatId) { @@ -132,28 +119,24 @@ private String createLuckyListText(List chatUsers) { // получить актуальные данные по каждому объекту // собрать список в строку return userJoinings.stream() - .map(joining -> { - User user = telegramService.getUser(joining.getChatId(), joining.getUserId()); - return buildMember(user); - }) - .reduce((member1, member2) -> { - if (!member1.isEmpty()) { - member1.append("\n\n"); + .map(this::buildUserInfo) + .reduce((userInfo1, userInfo2) -> { + if (!userInfo1.isEmpty()) { + userInfo1.append("\n\n"); } - member1.append(member2); - return member1; + userInfo1.append(userInfo2); + return userInfo1; }) .orElse(new StringBuilder()) .toString(); } - private StringBuilder buildMember(User user) { - - return null; + private StringBuilder buildUserInfo(UserJoining userJoining) { + return new StringBuilder(template.getUserInfoText(userJoining)); } private void sendLuckyList(String luckyListText, Long moderatorChatId) { - + telegramService.sendMessage(new SendMessage(moderatorChatId, luckyListText)); } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java new file mode 100644 index 0000000..0624a7b --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java @@ -0,0 +1,96 @@ +package org.codewithoutus.tgbotusers.bot.keyboard; + +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.User; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.enums.TemplatePattern; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.config.NotificationTemplates; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; + +// TODO: Pavel - подумать куда определить этот класс или перенести функционал по кдругим классам +@Component +@RequiredArgsConstructor +public class Template { + private static final String CROWN = "👑👑👑"; + private static final String SUB_CROWN = "🎉"; + private static final String NO_NICK = "ника нет"; + + private final NotificationTemplates notificationTemplates; + private final TelegramService telegramService; + + + public String getCongratulateText(UserJoining userJoining) { + String congratulationTemplate = notificationTemplates.getJoinCongratulation(); + return prepareDataForCongratulate(congratulationTemplate, userJoining); + } + + public String getAlertText(UserJoining userJoining) { + String joinAlertTemplate = notificationTemplates.getJoinAlert(); + return prepareDataForAlertAndUserInfo(joinAlertTemplate, userJoining); + } + + public String getUserInfoText(UserJoining userJoining) { + String userInfoTemplate = notificationTemplates.getJoinUserInfo(); + + boolean congratulateStatus = userJoining.getStatus().equals(CongratulateStatus.CONGRATULATE); + + return prepareDataForAlertAndUserInfo(userInfoTemplate, userJoining) + .replaceFirst(SUB_CROWN, congratulateStatus ? CROWN : SUB_CROWN); + } + + private String prepareDataForCongratulate(String configTemplate, UserJoining userJoining) { + + User user = telegramService.getUser(userJoining.getChatId(), userJoining.getUserId()); + String firstName = user.firstName(); + String lastName = user.lastName(); + String userName = firstName + (lastName == null ? "" : (" " + lastName)); + + int joiningNumber = userJoining.getNumber(); + + + return getText(configTemplate, "", userName, "", joiningNumber, ""); + } + + private String prepareDataForAlertAndUserInfo(String configTemplate, UserJoining userJoining) { + + Chat chat = telegramService.getChat(userJoining.getChatId()); + String groupName = chat.title(); + + User user = telegramService.getUser(userJoining.getChatId(), userJoining.getUserId()); + String firstName = user.firstName(); + String lastName = user.lastName(); + String userName = firstName + (lastName == null ? "" : (" " + lastName)); + + String nickName = user.username(); + nickName = nickName == null ? NO_NICK : nickName; + + int joiningNumber = userJoining.getNumber(); + + DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( + FormatStyle.SHORT, FormatStyle.SHORT); + String joinTime = userJoining.getJoinTime().format(formatter); + + String crown = userJoining.getStatus().equals(CongratulateStatus.CONGRATULATE) ? CROWN : SUB_CROWN; + + return getText(configTemplate, groupName, userName, nickName, joiningNumber, joinTime); + } + + @NotNull + private String getText(String configTemplate, String groupName, String userName, + String nickName, int joiningNumber, String joinTime) { + + return configTemplate + .replaceFirst(TemplatePattern.GROUP_NAME.getPattern(), groupName) + .replaceFirst(TemplatePattern.USER_NAME.getPattern(), userName) + .replaceFirst(TemplatePattern.NICK_NAME.getPattern(), nickName) + .replaceFirst(TemplatePattern.JOINING_NUMBER.getPattern(), String.valueOf(joiningNumber)) + .replaceFirst(TemplatePattern.JOIN_TIME.getPattern(), joinTime); + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index 92db719..eb85ee7 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; +import org.codewithoutus.tgbotusers.bot.keyboard.Template; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.entity.UserJoiningNotification; @@ -21,7 +22,8 @@ @RequiredArgsConstructor @Slf4j public class NotificationService { - + + private final Template template; private final TelegramService telegramService; private final ChatModeratorService chatModeratorService; private final UserJoiningNotificationService userJoiningNotificationService; @@ -36,8 +38,9 @@ public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { InlineKeyboardMarkup keyboard = KeyboardUtils .createKeyboard(CongratulationDecisionKeyboard.class, String.valueOf(userJoining.getId())); - - String notificationText = userJoining.toString(); // TODO: Алекс -- подтянуть шаблон + + + String notificationText = template.getAlertText(userJoining); for (ChatModerator moderatorChat : moderatorChats) { SendMessage message = new SendMessage(moderatorChat.getChatId(), notificationText).replyMarkup(keyboard); @@ -53,7 +56,7 @@ public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { @Transactional public void notifyUserAboutAnniversaryJoining(UserJoining userJoining) { - String notificationText = userJoining.toString(); // TODO: Алекс -- подтянуть шаблон + String notificationText = template.getCongratulateText(userJoining); SendMessage message = new SendMessage(userJoining.getChatId(), notificationText); telegramService.sendMessage(message); diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java index 83ed969..2ca6d03 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java @@ -11,6 +11,7 @@ import org.codewithoutus.tgbotusers.model.service.ChatUserService; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; import javax.annotation.PostConstruct; import javax.transaction.Transactional; @@ -60,7 +61,7 @@ private void rewriteDataBaseSettings() { // 1. при запуске приложения проверять настройки в базе, // если в базе пусто проверять конфигурацию и записывать в базу // 2. при каждом запуске запуске актуализировать названия юзерских чатов (поле name) - // (нужно для получения возможности получения списка юбилейных с параметром названия группы, + // (нужно для возможности получения списка юбилейных с параметром названия группы, // пример команды: [/luckyList@UsersTgBot Java разработчик]) for (Map.Entry> moderatorData : chatsSettingsData.entrySet()) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java index 2e11b96..e01fb10 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java @@ -2,10 +2,13 @@ import lombok.Getter; import lombok.Setter; +import org.codewithoutus.tgbotusers.bot.enums.TemplatePattern; +import org.jetbrains.annotations.NotNull; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; +import java.time.LocalDateTime; @Getter @Setter @@ -16,10 +19,5 @@ public class NotificationTemplates { private String joinCongratulation; private String joinAlert; private String joinUserInfo; - - @PostConstruct - private void formatTemplates() { - // TODO: Алекс -- сделать шаблоны -// joinCongratulation = joinCongratulation.replaceAll("{\*}", "%s"); - } + } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index 471ab4f..6f44d66 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -21,7 +21,7 @@ public class ChatUser { // TODO: Pavel -- переименовать сущно @Column(nullable = false, unique = true) private Long chatId; - @Column(nullable = false) + @Column(nullable = true) // TODO: Pavel - подумать nullable = true или false? private String name; @ManyToMany(mappedBy = "chatUsers", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) From 1e49a73e1070bebb86a761d464f5bbda58d155d3 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 22:24:47 +0700 Subject: [PATCH 32/50] need help unbind(deleteChatUser) --- .../codewithoutus/tgbotusers/model/entity/ChatModerator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index 40f8929..1b3f6a3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -34,6 +34,11 @@ public void addChatUser(ChatUser chatUser) { } chatUsers.add(chatUser); } + public void deleteChatUser(ChatUser chatUser) { + chatUsers.remove(chatUser); + chatUsers.remove(chatUser.getChatId()); + + } public ChatModerator(Long chatId, List chatUsers) { this.chatId = chatId; From b78f65eee97fa5e9da9614751d951382752cba52 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 22:41:22 +0700 Subject: [PATCH 33/50] done add all,bind,unbind --- .../tgbotusers/model/entity/ChatModerator.java | 12 ++++-------- .../model/service/ChatModeratorService.java | 1 + .../tgbotusers/model/service/ChatUserService.java | 3 +++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index 1b3f6a3..4a76917 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -34,15 +34,11 @@ public void addChatUser(ChatUser chatUser) { } chatUsers.add(chatUser); } - public void deleteChatUser(ChatUser chatUser) { - chatUsers.remove(chatUser); - chatUsers.remove(chatUser.getChatId()); - } - - public ChatModerator(Long chatId, List chatUsers) { - this.chatId = chatId; - this.chatUsers = chatUsers; + public void removeChatUser(ChatUser chatUser) { + if (chatUsers != null) { + chatUsers.remove(chatUser); + } } @Override diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java index a4700a8..6646925 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -32,6 +32,7 @@ public void delete(ChatModerator chatModerator,ChatUser chatUser){ } + public List findAll() { List result = new ArrayList<>(); chatModeratorRepository.findAll().forEach(result::add); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index f8d1da9..1c9a1c8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -41,4 +41,7 @@ public boolean isChatUser(long id) { .stream() .anyMatch(chatUser -> chatUser.getChatId().equals(id)); } + public void delete(ChatUser entity) { + chatUserRepository.delete(entity); + } } \ No newline at end of file From aafc49dd953ee41f37c4dd020f0cc9fbf4fcd420 Mon Sep 17 00:00:00 2001 From: "gladiator.maksim1992@mail.ru" Date: Sun, 17 Jul 2022 22:49:09 +0700 Subject: [PATCH 34/50] commit dor Pavel --- .../bot/handler/PrivateMessageHandler.java | 25 +------------------ .../tgbotusers/config/ChatSettings.java | 16 ++++++------ .../model/entity/ChatModerator.java | 13 +--------- .../tgbotusers/model/entity/ChatUser.java | 9 ++----- 4 files changed, 12 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 96eef48..060fea5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -190,33 +190,10 @@ private boolean bindUserChatToModer(Update update, Long chatId, String text) { telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_BIND, userGroupId, moderatorGroupId))); return true; } - chatModerator.addChatUser(chatUser); + chatModerator.getChatUsers().add(chatUser); chatModeratorService.save(chatModerator); telegramService.sendMessage(new SendMessage(chatId, String.format(OK_BIND, userGroupId, moderatorGroupId))); return true; - -// int count = text.split(" ").length; -// if (count == 3) { -// //ChatUserRepository repUser = chatUserService.getChatUserRepository(); -// int idUser = Integer.parseInt(text.split(" ")[1]); -// if (repUser.findByChatId(idUser) != null) { -// //ChatModeratorRepository repModer = chatModeratorService.getChatModeratorRepository(); -// -// long idModer = Integer.parseInt(text.split(" ")[2]); -// if (!repModer.findByChatId(idModer).isEmpty()) { -// repModer.findByChatId(idModer).get().addChatUser(repUser.findByChatId(idUser)); -// repUser.findByChatId(idUser).addChatModerator(repModer.findByChatId(idModer).get()); -// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_BIND, idUser, idModer))); -// return true; -// } -// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_MODER, idModer))); -// return true; -// } -// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_USER, idUser))); -// return true; -// } -// telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(SORRY, text))); -// return true; } private boolean deleteUserChat(Update update, Long chatId,String text) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java index c62fafc..42119c3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java @@ -61,14 +61,14 @@ private void rewriteDataBaseSettings() { chatModerator.setChatId(moderatorData.getKey()); for (Long userData : moderatorData.getValue()) { - ChatUser chatUser = chatUserService.findByChatId(userData); - if (chatUser == null) { - chatUser = new ChatUser(); - chatUser.setChatId(userData); - chatUser = chatUserService.save(chatUser); - } - chatUser.addChatModerator(chatModerator); - chatModerator.addChatUser(chatUser); + ChatUser chatUser = chatUserService.findByChatId(userData).orElseGet(() -> { + ChatUser newEntity = new ChatUser(); + newEntity.setChatId(userData); + newEntity = chatUserService.save(newEntity); + return newEntity; + }); + chatUser.getChatModerators().add(chatModerator); + chatModerator.getChatUsers().add(chatUser); chatModerator = chatModeratorService.save(chatModerator); } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index 4a76917..1d880f0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -26,20 +26,9 @@ public class ChatModerator { // TODO: Pavel -- переименовать сущ @JoinTable(name = "moderators2users", joinColumns = @JoinColumn(name = "moderator_chat_id"), inverseJoinColumns = @JoinColumn(name = "user_chat_id")) - private List chatUsers; + private List chatUsers=new ArrayList<>(); - public void addChatUser(ChatUser chatUser) { - if (chatUsers == null) { - chatUsers = new ArrayList<>(); - } - chatUsers.add(chatUser); - } - public void removeChatUser(ChatUser chatUser) { - if (chatUsers != null) { - chatUsers.remove(chatUser); - } - } @Override public boolean equals(Object o) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index a9ac479..a92293c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -23,14 +23,9 @@ public class ChatUser { // TODO: Pavel -- переименовать сущно private Long chatId; @ManyToMany(mappedBy = "chatUsers", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) - private List chatModerators; + private List chatModerators=new ArrayList<>(); + - public void addChatModerator(ChatModerator chatModerator) { - if (chatModerators == null) { - chatModerators = new ArrayList<>(); - } - chatModerators.add(chatModerator); - } public ChatUser(Long chatId, List chatModerators) { this.chatId = chatId; From be6711c7a23d241d726ba2574bffbe33d2d626d3 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Mon, 18 Jul 2022 20:36:29 +0400 Subject: [PATCH 35/50] added logging --- .../bot/handler/CallbackQueryHandler.java | 6 ------ .../tgbotusers/bot/keyboard/Template.java | 2 -- .../tgbotusers/bot/service/BotService.java | 4 ++++ .../tgbotusers/bot/service/NotificationService.java | 9 +++++++-- .../tgbotusers/bot/service/TelegramService.java | 13 ++++++++++--- src/main/resources/application.yml | 2 ++ 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index feaa6cb..0485f63 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -1,11 +1,8 @@ package org.codewithoutus.tgbotusers.bot.handler; -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.MessageEntity; import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.codewithoutus.tgbotusers.bot.enums.BotCommand; import org.codewithoutus.tgbotusers.bot.exception.CommandNotFoundException; import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; @@ -18,9 +15,6 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; import java.util.Map; import java.util.Optional; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java index 0624a7b..95c73a0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java @@ -77,8 +77,6 @@ private String prepareDataForAlertAndUserInfo(String configTemplate, UserJoining FormatStyle.SHORT, FormatStyle.SHORT); String joinTime = userJoining.getJoinTime().format(formatter); - String crown = userJoining.getStatus().equals(CongratulateStatus.CONGRATULATE) ? CROWN : SUB_CROWN; - return getText(configTemplate, groupName, userName, nickName, joiningNumber, joinTime); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java index 3dc999c..78afc58 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java @@ -4,6 +4,7 @@ import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.request.GetUpdates; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.codewithoutus.tgbotusers.bot.Bot; import org.codewithoutus.tgbotusers.bot.enums.BotStatus; import org.codewithoutus.tgbotusers.bot.handler.CallbackQueryHandler; @@ -16,6 +17,7 @@ @Service @RequiredArgsConstructor +@Slf4j public class BotService { private final Bot bot; @@ -35,6 +37,7 @@ public boolean start() { } startUpdatePolling(); bot.setStatus(BotStatus.START); + log.info("Bot running!"); return true; } @@ -44,6 +47,7 @@ public boolean stop() { } stopUpdatePolling(); bot.setStatus(BotStatus.STOP); + log.info("Bot stopped!"); return true; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index eb85ee7..ab18007 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -39,12 +39,13 @@ public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { InlineKeyboardMarkup keyboard = KeyboardUtils .createKeyboard(CongratulationDecisionKeyboard.class, String.valueOf(userJoining.getId())); - String notificationText = template.getAlertText(userJoining); for (ChatModerator moderatorChat : moderatorChats) { SendMessage message = new SendMessage(moderatorChat.getChatId(), notificationText).replyMarkup(keyboard); SendResponse response = telegramService.sendMessage(message); + log.debug("ModerChat(id={}) notified about user(id={}) joining. Status={}", + response.message().chat().id(), userJoining.getUserId(), response.isOk()); UserJoiningNotification notification = new UserJoiningNotification(); notification.setUserJoining(userJoining); @@ -59,18 +60,22 @@ public void notifyUserAboutAnniversaryJoining(UserJoining userJoining) { String notificationText = template.getCongratulateText(userJoining); SendMessage message = new SendMessage(userJoining.getChatId(), notificationText); - telegramService.sendMessage(message); + SendResponse response = telegramService.sendMessage(message); + log.debug("User(id={}, chatId={}) notified. Status={}", + userJoining.getUserId(), response.message().chat().id(), response.isOk()); } @Transactional public void deleteKeyboardFromJoiningNotification(Long userId, Long chatId, int anniversaryNumber) { userJoiningNotificationService.findByUserIdAndChatIdAndAnniversaryNumber(userId, chatId, anniversaryNumber) .forEach(notification -> telegramService.removeKeyboardFromMessage(chatId, notification.getSentMessageId())); + } @Transactional public void deleteKeyboardFromAllJoiningNotifications(Long chatId, int anniversaryNumber) { userJoiningNotificationService.findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber) .forEach(notification -> telegramService.removeKeyboardFromMessage(chatId, notification.getSentMessageId())); + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java index d392d1b..2608009 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java @@ -44,17 +44,24 @@ public SendResponse sendMessage(SendMessage message) { public void removeKeyboardFromMessage(long chatId, int messageId) { sendRequest(new EditMessageReplyMarkup(chatId, messageId).replyMarkup(new InlineKeyboardMarkup())); + log.debug("Keyboard removed from message(id={}) moderChat(id={})", messageId, chatId); } public User getUser(long chatId, long userId) { - return sendRequest(new GetChatMember(chatId, userId)).chatMember().user(); + User user = sendRequest(new GetChatMember(chatId, userId)).chatMember().user(); + log.debug("Receive response: user(id={}, chatId={})", userId, chatId); + return user; } public Chat getChat(long chatId) { - return sendRequest(new GetChat(chatId)).chat(); + Chat chat = sendRequest(new GetChat(chatId)).chat(); + log.debug("Receive response: chat(id={})", chatId); + return chat; } public int getChatMembersCount(long chatId) { - return sendRequest(new GetChatMemberCount(chatId)).count(); + int count = sendRequest(new GetChatMemberCount(chatId)).count(); + log.debug("Receive response: count={} for chat(id={})", count, chatId); + return count; } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ea25542..1d4fdbd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,6 +20,8 @@ spring: logging: level: root: info + web: info + sql: info bot-settings: bot-user-name: ${BOT_USER_NAME} From 3ebae0605b51ebc97c59bfbe2bd0ceea514ba58c Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 19 Jul 2022 03:39:16 +0400 Subject: [PATCH 36/50] Refactoring --- .../bot/{handler => }/UpdateUtils.java | 19 ++- .../tgbotusers/bot/enums/BotCommand.java | 20 ++- .../tgbotusers/bot/enums/TemplatePattern.java | 20 --- .../bot/handler/CallbackQueryHandler.java | 2 +- .../bot/handler/LuckyListCommandHandler.java | 111 ++++++++++++++ .../bot/handler/MessageHandler.java | 142 ------------------ .../bot/handler/PrivateMessageHandler.java | 1 + .../tgbotusers/bot/keyboard/Template.java | 94 ------------ .../tgbotusers/bot/service/BotService.java | 8 +- .../bot/service/NotificationService.java | 47 ++++-- .../bot/service/TelegramService.java | 17 ++- .../bot/service/TemplateEngine.java | 89 +++++++++++ .../tgbotusers/config/ChatSettings.java | 19 ++- .../config/NotificationTemplates.java | 15 +- .../model/entity/ChatModerator.java | 19 ++- .../tgbotusers/model/entity/ChatUser.java | 25 +-- .../tgbotusers/model/entity/UserJoining.java | 24 +-- .../model/repository/ChatUserRepository.java | 6 +- .../repository/UserJoiningRepository.java | 3 + .../model/service/ChatUserService.java | 18 +-- .../model/service/UserJoiningService.java | 4 + src/main/resources/application-bot.yml | 16 +- 22 files changed, 367 insertions(+), 352 deletions(-) rename src/main/java/org/codewithoutus/tgbotusers/bot/{handler => }/UpdateUtils.java (72%) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java b/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateUtils.java similarity index 72% rename from src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/UpdateUtils.java index 53d3ab0..f6c105d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/UpdateUtils.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateUtils.java @@ -1,12 +1,14 @@ -package org.codewithoutus.tgbotusers.bot.handler; +package org.codewithoutus.tgbotusers.bot; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.pengrad.telegrambot.model.*; import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; import org.codewithoutus.tgbotusers.bot.exception.CallbackDataMappingException; import org.codewithoutus.tgbotusers.config.AppStaticContext; +import java.util.Arrays; import java.util.Map; import java.util.Optional; @@ -20,6 +22,10 @@ public static Boolean isPrivateMessage(Update update) { .orElse(Boolean.FALSE); } + public static boolean isPrivateMessageFromAdmin(Update update) { + return isPrivateMessage(update); + } + public static boolean isForwardMessage(Update update) { return Optional.ofNullable(update.message()) .map(Message::forwardDate) @@ -32,6 +38,17 @@ public static String getMessageText(Update update) { .orElse(""); } + public static BotCommand getBotCommand(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::entities) + .flatMap(entities -> Arrays.stream(entities) + .filter(entity -> entity.type().equals(MessageEntity.Type.bot_command)) + .findFirst() + .map(e -> getMessageText(update).substring(e.offset(), e.length())) + .map(BotCommand::getByCommandText)) + .orElse(null); + } + public static Chat getChat(Update update) { return Optional.ofNullable(update.message()) .map(Message::chat) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java index 777de4d..93a08e8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java @@ -2,16 +2,28 @@ import lombok.Getter; +import java.util.Arrays; +import java.util.regex.Pattern; + @Getter public enum BotCommand { - LUCKY_LIST("/luckyList"), - CONGRATULATE("/congratulate"), - DECLINE("/decline"); + LUCKY_LIST("/luckyList", "^/luckyList( (\\-?\\d*{18}))?"), + CONGRATULATE("/congratulate", "^/congratulate$"), + DECLINE("/decline", "^/decline"); private final String text; + private final Pattern regex; - BotCommand(String text) { + BotCommand(String text, String regex) { this.text = text; + this.regex = Pattern.compile(regex); + } + + public static BotCommand getByCommandText(String command) { + return Arrays.stream(values()) + .filter(value -> command.startsWith(value.getText())) + .findFirst() + .orElse(null); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java deleted file mode 100644 index 6c2dc78..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/TemplatePattern.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.enums; - -public enum TemplatePattern { - - GROUP_NAME("НазваниеГруппы"), - USER_NAME("ИмяУчастника"), - NICK_NAME("НикУчастника"), - JOINING_NUMBER("порядковыйНомерВступления"), - JOIN_TIME("ВремяВступления"); - - private final String pattern; - - TemplatePattern(String pattern) { - this.pattern = pattern; - } - - public String getPattern() { - return '{' + pattern + '}'; - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index 0485f63..3be5579 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -3,6 +3,7 @@ import com.pengrad.telegrambot.model.Update; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.UpdateUtils; import org.codewithoutus.tgbotusers.bot.exception.CommandNotFoundException; import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; @@ -91,5 +92,4 @@ private boolean handleCongratulationDecision(String command, Map } return true; } - } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java new file mode 100644 index 0000000..7d9551f --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java @@ -0,0 +1,111 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.UpdateUtils; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.bot.service.TemplateEngine; +import org.codewithoutus.tgbotusers.config.NotificationTemplates; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.codewithoutus.tgbotusers.model.service.UserJoiningService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +@Slf4j +public class LuckyListCommandHandler extends Handler { + + private final ChatModeratorService chatModeratorService; + private final ChatUserService chatUserService; + private final UserJoiningService userJoiningService; + private final TelegramService telegramService; + private final NotificationTemplates notificationTemplates; + private final TemplateEngine templateEngine; + + @Override + protected boolean handle(Update update) { + // TODO: Алекс -- реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" + + // TODO: Алекс -- если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard + // TODO: Алекс -- если поздравленного нет, то отклоненные тоже выводятся + // TODO: Алекс -- все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись + // TODO: Алекс -- можно попробовать выводить все-все чаты, а потом доработать -- на будущее + + BotCommand command = UpdateUtils.getBotCommand(update); + if (command != BotCommand.LUCKY_LIST) { + return false; + } + + Long moderatorChatId = UpdateUtils.getChat(update).id(); + if (!UpdateUtils.isPrivateMessageFromAdmin(update) && !chatModeratorService.existsByChatId(moderatorChatId)) { + return false; + } + + String commandText = UpdateUtils.getMessageText(update); + Matcher matcher = command.getRegex().matcher(commandText); + if (!matcher.matches()) { + log.warn("Wrong command to bot {}", commandText); + return false; + } + + String chatId = matcher.group(2); + List luckyChats = getLuckyChats(chatId, moderatorChatId); + if (luckyChats.isEmpty()) { + telegramService.sendMessage(new SendMessage(moderatorChatId, "Нет данных о чатах пользователей")); + return true; + } + + telegramService.sendMessage(new SendMessage(moderatorChatId, createLuckyListText(luckyChats))); + return true; + } + + private List getLuckyChats(String chatId, Long moderatorChatId) { + if (chatId == null) { + return chatModeratorService + .findByChatId(moderatorChatId) + .map(ChatModerator::getChatUsers) + .orElseGet(ArrayList::new); + } else { + return chatUserService + .findByChatId(Long.parseLong(chatId)) + .stream() + .toList(); + } + } + + private String createLuckyListText(List luckyChats) { + List chatsIds = luckyChats.stream() + .map(ChatUser::getChatId) + .toList(); + + StringBuilder resultBuilder = new StringBuilder(); + String template = notificationTemplates.getJoinUserInfo(); + userJoiningService.findByChatIds(chatsIds) + .stream() + .collect(Collectors.groupingBy(x -> String.valueOf(x.getChatId()))) + .forEach((groupId, joiningList) -> { + resultBuilder + .append("==================================================").append(System.lineSeparator()) + .append("Группа: ").append(groupId).append(System.lineSeparator()); + joiningList.forEach(joining -> { + resultBuilder + .append(templateEngine.buildFromTemplate(template, joining, true)) + .append(System.lineSeparator()); + }); + resultBuilder.append(System.lineSeparator()); + }); + + return resultBuilder.isEmpty() ? "Пока ещё нет счастливчиков" : resultBuilder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java deleted file mode 100644 index 5d4808d..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/MessageHandler.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.handler; - -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.MessageEntity; -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.request.SendMessage; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.enums.BotCommand; -import org.codewithoutus.tgbotusers.bot.keyboard.Template; -import org.codewithoutus.tgbotusers.bot.service.TelegramService; -import org.codewithoutus.tgbotusers.model.entity.ChatModerator; -import org.codewithoutus.tgbotusers.model.entity.ChatUser; -import org.codewithoutus.tgbotusers.model.entity.UserJoining; -import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; -import org.codewithoutus.tgbotusers.model.service.ChatUserService; -import org.codewithoutus.tgbotusers.model.service.UserJoiningService; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; - -@Component("UpdateMessageHandler") -@RequiredArgsConstructor -public class MessageHandler extends Handler { - - private final ChatModeratorService chatModeratorService; - private final ChatUserService chatUserService; - private final UserJoiningService userJoiningService; - private final TelegramService telegramService; - private final Template template; - - @Override - protected boolean handle(Update update) { - // TODO: Алекс -- реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" - - // TODO: Алекс -- если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard - // TODO: Алекс -- если поздравленного нет, то отклоненные тоже выводятся - // TODO: Алекс -- все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись - // TODO: Алекс -- можно попробовать выводить все-все чаты, а потом доработать -- на будущее - - Message message = update.message(); - Optional botEntityOptional = getBotCommandEntity(message); - if (botEntityOptional.isEmpty()) { - return false; - } - - Long chatId = message.chat().id(); - if (!chatModeratorService.existsByChatId(chatId)) { - return false; - } - - MessageEntity entity = botEntityOptional.get(); - - handleBotCommand(message.text(), entity, chatId); - - return false; - } - - - private Optional getBotCommandEntity(Message message) { - - if (message == null) { - return Optional.empty(); - } - - MessageEntity[] entities = message.entities(); - if (entities == null || entities.length <= 0) { - return Optional.empty(); - } - - return Arrays.stream(message.entities()) - .filter(entity -> entity.type().equals(MessageEntity.Type.bot_command)) - .findFirst(); - } - - private void handleBotCommand(String messageText, MessageEntity entity, Long moderatorChatId) { - if (messageText.contains(BotCommand.LUCKY_LIST.getText())) { - performLuckyList(messageText, entity, moderatorChatId); - } - } - - private void performLuckyList(String messageText, MessageEntity entity, Long moderatorChatId) { - String luckyListText = ""; - String param = parseParam(messageText, entity); - if (!param.isEmpty()) { - Optional chatUserOptional = chatUserService.findByName(param); - if(chatUserOptional.isPresent()) { - luckyListText = createLuckyListText(List.of(chatUserOptional.get())); - } - } else { - ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorChatId) - .orElseThrow(() -> new IllegalArgumentException( - "DB was changed after check exists with moderatorChatId=" + moderatorChatId)); - luckyListText = createLuckyListText(chatModerator.getChatUsers()); - } - - sendLuckyList(luckyListText, moderatorChatId); - } - - private String parseParam(String text, MessageEntity entity) { - String param = text.substring(entity.offset() + entity.length()); - return param.trim(); - } - - private String createLuckyListText(List chatUsers) { - - // получить все объекты userJoining из базы - List userJoinings = new ArrayList<>(); - for (ChatUser chatUser : chatUsers) { - List joinings = userJoiningService.findByChatId(chatUser.getChatId()); - userJoinings.addAll(joinings); - } - - userJoinings.sort(Comparator.naturalOrder()); - - // получить актуальные данные по каждому объекту - // собрать список в строку - return userJoinings.stream() - .map(this::buildUserInfo) - .reduce((userInfo1, userInfo2) -> { - if (!userInfo1.isEmpty()) { - userInfo1.append("\n\n"); - } - userInfo1.append(userInfo2); - return userInfo1; - }) - .orElse(new StringBuilder()) - .toString(); - } - - private StringBuilder buildUserInfo(UserJoining userJoining) { - return new StringBuilder(template.getUserInfoText(userJoining)); - } - - private void sendLuckyList(String luckyListText, Long moderatorChatId) { - telegramService.sendMessage(new SendMessage(moderatorChatId, luckyListText)); - } - -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 8e27d37..55b06a3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -6,6 +6,7 @@ import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.UpdateUtils; import org.codewithoutus.tgbotusers.bot.service.TelegramService; import org.springframework.stereotype.Component; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java deleted file mode 100644 index 95c73a0..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/Template.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.keyboard; - -import com.pengrad.telegrambot.model.Chat; -import com.pengrad.telegrambot.model.User; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.enums.TemplatePattern; -import org.codewithoutus.tgbotusers.bot.service.TelegramService; -import org.codewithoutus.tgbotusers.config.NotificationTemplates; -import org.codewithoutus.tgbotusers.model.entity.UserJoining; -import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.jetbrains.annotations.NotNull; -import org.springframework.stereotype.Component; - -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; - -// TODO: Pavel - подумать куда определить этот класс или перенести функционал по кдругим классам -@Component -@RequiredArgsConstructor -public class Template { - private static final String CROWN = "👑👑👑"; - private static final String SUB_CROWN = "🎉"; - private static final String NO_NICK = "ника нет"; - - private final NotificationTemplates notificationTemplates; - private final TelegramService telegramService; - - - public String getCongratulateText(UserJoining userJoining) { - String congratulationTemplate = notificationTemplates.getJoinCongratulation(); - return prepareDataForCongratulate(congratulationTemplate, userJoining); - } - - public String getAlertText(UserJoining userJoining) { - String joinAlertTemplate = notificationTemplates.getJoinAlert(); - return prepareDataForAlertAndUserInfo(joinAlertTemplate, userJoining); - } - - public String getUserInfoText(UserJoining userJoining) { - String userInfoTemplate = notificationTemplates.getJoinUserInfo(); - - boolean congratulateStatus = userJoining.getStatus().equals(CongratulateStatus.CONGRATULATE); - - return prepareDataForAlertAndUserInfo(userInfoTemplate, userJoining) - .replaceFirst(SUB_CROWN, congratulateStatus ? CROWN : SUB_CROWN); - } - - private String prepareDataForCongratulate(String configTemplate, UserJoining userJoining) { - - User user = telegramService.getUser(userJoining.getChatId(), userJoining.getUserId()); - String firstName = user.firstName(); - String lastName = user.lastName(); - String userName = firstName + (lastName == null ? "" : (" " + lastName)); - - int joiningNumber = userJoining.getNumber(); - - - return getText(configTemplate, "", userName, "", joiningNumber, ""); - } - - private String prepareDataForAlertAndUserInfo(String configTemplate, UserJoining userJoining) { - - Chat chat = telegramService.getChat(userJoining.getChatId()); - String groupName = chat.title(); - - User user = telegramService.getUser(userJoining.getChatId(), userJoining.getUserId()); - String firstName = user.firstName(); - String lastName = user.lastName(); - String userName = firstName + (lastName == null ? "" : (" " + lastName)); - - String nickName = user.username(); - nickName = nickName == null ? NO_NICK : nickName; - - int joiningNumber = userJoining.getNumber(); - - DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( - FormatStyle.SHORT, FormatStyle.SHORT); - String joinTime = userJoining.getJoinTime().format(formatter); - - return getText(configTemplate, groupName, userName, nickName, joiningNumber, joinTime); - } - - @NotNull - private String getText(String configTemplate, String groupName, String userName, - String nickName, int joiningNumber, String joinTime) { - - return configTemplate - .replaceFirst(TemplatePattern.GROUP_NAME.getPattern(), groupName) - .replaceFirst(TemplatePattern.USER_NAME.getPattern(), userName) - .replaceFirst(TemplatePattern.NICK_NAME.getPattern(), nickName) - .replaceFirst(TemplatePattern.JOINING_NUMBER.getPattern(), String.valueOf(joiningNumber)) - .replaceFirst(TemplatePattern.JOIN_TIME.getPattern(), joinTime); - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java index 78afc58..e7bf785 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java @@ -9,7 +9,7 @@ import org.codewithoutus.tgbotusers.bot.enums.BotStatus; import org.codewithoutus.tgbotusers.bot.handler.CallbackQueryHandler; import org.codewithoutus.tgbotusers.bot.handler.ChatJoinRequestHandler; -import org.codewithoutus.tgbotusers.bot.handler.MessageHandler; +import org.codewithoutus.tgbotusers.bot.handler.LuckyListCommandHandler; import org.codewithoutus.tgbotusers.bot.handler.PrivateMessageHandler; import org.springframework.stereotype.Service; @@ -24,7 +24,7 @@ public class BotService { private final CallbackQueryHandler callbackQueryHandler; private final ChatJoinRequestHandler chatJoinRequestHandler; private final PrivateMessageHandler privateMessageHandler; - private final MessageHandler messageHandler; + private final LuckyListCommandHandler luckyListCommandHandler; @PostConstruct private void botStart() { @@ -72,8 +72,8 @@ private void stopUpdatePolling() { private void handleUpdate(Update update) { if (chatJoinRequestHandler.tryHandle(update) || callbackQueryHandler.tryHandle(update) - || privateMessageHandler.tryHandle(update) - || messageHandler.tryHandle(update)) { + || luckyListCommandHandler.tryHandle(update) + || privateMessageHandler.tryHandle(update)) { // update handled, return } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index ab18007..4621b1d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; -import org.codewithoutus.tgbotusers.bot.keyboard.Template; +import org.codewithoutus.tgbotusers.config.NotificationTemplates; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.entity.UserJoiningNotification; @@ -22,60 +22,75 @@ @RequiredArgsConstructor @Slf4j public class NotificationService { - - private final Template template; + + private final NotificationTemplates notificationTemplates; + private final TemplateEngine templateEngine; private final TelegramService telegramService; private final ChatModeratorService chatModeratorService; private final UserJoiningNotificationService userJoiningNotificationService; @Transactional public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { + log.debug("Start notifying about user(id={}, number={}) joining", userJoining.getUserId(), userJoining.getNumber()); List moderatorChats = chatModeratorService.findByChatUsersId(userJoining.getChatId()); if (moderatorChats.isEmpty()) { - log.warn(""); + log.warn("No binding moderators to user chat {}", userJoining.getChatId()); return; } InlineKeyboardMarkup keyboard = KeyboardUtils .createKeyboard(CongratulationDecisionKeyboard.class, String.valueOf(userJoining.getId())); - - String notificationText = template.getAlertText(userJoining); + + String notificationText = templateEngine + .buildFromTemplate(notificationTemplates.getJoinAlert(), userJoining, false); for (ChatModerator moderatorChat : moderatorChats) { SendMessage message = new SendMessage(moderatorChat.getChatId(), notificationText).replyMarkup(keyboard); SendResponse response = telegramService.sendMessage(message); log.debug("ModerChat(id={}) notified about user(id={}) joining. Status={}", - response.message().chat().id(), userJoining.getUserId(), response.isOk()); + moderatorChat.getChatId(), userJoining.getUserId(), response.isOk()); UserJoiningNotification notification = new UserJoiningNotification(); notification.setUserJoining(userJoining); notification.setSentMessageChatId(moderatorChat.getChatId()); notification.setSentMessageId(response.message().messageId()); userJoiningNotificationService.save(notification); + log.debug("Saved to DB notifying ModerChat(id={}) about user(id={}) joining", + moderatorChat.getChatId(), userJoining.getUserId()); } + log.debug("Finish notifying about user(id={}) joining", userJoining.getUserId()); } @Transactional public void notifyUserAboutAnniversaryJoining(UserJoining userJoining) { - String notificationText = template.getCongratulateText(userJoining); + log.debug("Start congratulate user(id={}, number={}) joining", userJoining.getUserId(), userJoining.getNumber()); + String notificationText = templateEngine + .buildFromTemplate(notificationTemplates.getJoinCongratulation(), userJoining, false); SendMessage message = new SendMessage(userJoining.getChatId(), notificationText); SendResponse response = telegramService.sendMessage(message); - log.debug("User(id={}, chatId={}) notified. Status={}", - userJoining.getUserId(), response.message().chat().id(), response.isOk()); + log.debug("Finish congratulate user(id={}). Status={}", userJoining.getUserId(), response.isOk()); } @Transactional public void deleteKeyboardFromJoiningNotification(Long userId, Long chatId, int anniversaryNumber) { - userJoiningNotificationService.findByUserIdAndChatIdAndAnniversaryNumber(userId, chatId, anniversaryNumber) - .forEach(notification -> telegramService.removeKeyboardFromMessage(chatId, notification.getSentMessageId())); - + log.debug("Start keyboard deleting about user(id={}, number={}) joining", userId, anniversaryNumber); + userJoiningNotificationService + .findByUserIdAndChatIdAndAnniversaryNumber(userId, chatId, anniversaryNumber) + .forEach(this::deleteKeyboard); + log.debug("Finish keyboard deleting about user(id={}, number={}) joining", userId, anniversaryNumber); } @Transactional public void deleteKeyboardFromAllJoiningNotifications(Long chatId, int anniversaryNumber) { - userJoiningNotificationService.findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber) - .forEach(notification -> telegramService.removeKeyboardFromMessage(chatId, notification.getSentMessageId())); - + log.debug("Start keyboard deleting about chat(id={}) {} joining", chatId, anniversaryNumber); + userJoiningNotificationService + .findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber) + .forEach(this::deleteKeyboard); + log.debug("Finish keyboard deleting about chat(id={}) {} joining", chatId, anniversaryNumber); + } + + private void deleteKeyboard(UserJoiningNotification notification) { + telegramService.removeKeyboardFromMessage(notification.getSentMessageChatId(), notification.getSentMessageId()); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java index 2608009..3948501 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java @@ -39,29 +39,32 @@ private , R extends BaseResponse> R sendRequest(Base } public SendResponse sendMessage(SendMessage message) { - return sendRequest(message); + SendResponse response = sendRequest(message); + log.debug("Sent message parameters={}. Status={}", message.getParameters().toString(), response.isOk()); + return response; } - public void removeKeyboardFromMessage(long chatId, int messageId) { - sendRequest(new EditMessageReplyMarkup(chatId, messageId).replyMarkup(new InlineKeyboardMarkup())); - log.debug("Keyboard removed from message(id={}) moderChat(id={})", messageId, chatId); + public BaseResponse removeKeyboardFromMessage(long chatId, int messageId) { + BaseResponse response = sendRequest(new EditMessageReplyMarkup(chatId, messageId).replyMarkup(new InlineKeyboardMarkup())); + log.debug("Keyboard removed from message(id={}) in chat(id={}). Status={}", messageId, chatId, response.isOk()); + return response; } public User getUser(long chatId, long userId) { User user = sendRequest(new GetChatMember(chatId, userId)).chatMember().user(); - log.debug("Receive response: user(id={}, chatId={})", userId, chatId); + log.debug("Receive getUser: user(id={}, chatId={})", userId, chatId); return user; } public Chat getChat(long chatId) { Chat chat = sendRequest(new GetChat(chatId)).chat(); - log.debug("Receive response: chat(id={})", chatId); + log.debug("Receive getChat: chat(id={})", chatId); return chat; } public int getChatMembersCount(long chatId) { int count = sendRequest(new GetChatMemberCount(chatId)).count(); - log.debug("Receive response: count={} for chat(id={})", count, chatId); + log.debug("Receive getChatMembersCount: count={} for chat(id={})", count, chatId); return count; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java new file mode 100644 index 0000000..1894bd3 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java @@ -0,0 +1,89 @@ +package org.codewithoutus.tgbotusers.bot.service; + +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.User; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.config.NotificationTemplates; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +@Component +@RequiredArgsConstructor +public class TemplateEngine { + + private final NotificationTemplates notificationTemplates; + private final TelegramService telegramService; + + public String buildFromTemplate(String template, UserJoining userJoining, boolean showCrown) { + Chat chat = telegramService.getChat(userJoining.getChatId()); + User user = telegramService.getUser(userJoining.getChatId(), userJoining.getUserId()); + + return buildFromTemplate(template) + .withJoinDate(userJoining.getJoinTime()) + .withJoinNumber(userJoining.getAnniversaryNumber()) + .withUserName(user) + .withUserNickname(user) + .withChatName(chat) + .withWinnerCrown(showCrown && userJoining.getStatus() == CongratulateStatus.CONGRATULATE) + .done(); + } + + public Builder buildFromTemplate(String template) { + return new Builder(template, notificationTemplates); + } + + public static class Builder { + private final StringBuilder result; + private final NotificationTemplates temp; + + private Builder(String template, NotificationTemplates templates) { + result = new StringBuilder(template); + temp = templates; + } + + private void replace(String target, String replacement) { + for (int i = result.lastIndexOf(target); i > -1; i = result.lastIndexOf(target, i)) { + result.replace(i, i + target.length() - 1, replacement); + } + } + + public Builder withChatName(Chat chat) { + replace(temp.getVariables().get("chat-name"), chat.title()); + return this; + } + + public Builder withUserName(User user) { + String name = user.firstName() + (user.lastName() == null ? "" : (" " + user.lastName())); + replace(temp.getVariables().get("user-name"), name); + return this; + } + + public Builder withUserNickname(User user) { + String nickname = user.username() == null ? temp.getPlugs().get("no-nick") : user.username(); + replace(temp.getVariables().get("user-nickname"), nickname); + return this; + } + + public Builder withJoinDate(LocalDateTime dateTime) { + replace(temp.getVariables().get("join-date"), temp.getDateTimeFormatter().format(dateTime)); + return this; + } + + public Builder withJoinNumber(int number) { + replace(temp.getVariables().get("join-number"), String.valueOf(number)); + return this; + } + + public Builder withWinnerCrown(boolean isWinner) { + result.insert(0, isWinner ? temp.getPlugs().get("winner-crown") : ""); + return this; + } + + public String done() { + return result.toString(); + } + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java index 2ca6d03..d6a8277 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java @@ -11,7 +11,6 @@ import org.codewithoutus.tgbotusers.model.service.ChatUserService; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; import javax.annotation.PostConstruct; import javax.transaction.Transactional; @@ -63,20 +62,20 @@ private void rewriteDataBaseSettings() { // 2. при каждом запуске запуске актуализировать названия юзерских чатов (поле name) // (нужно для возможности получения списка юбилейных с параметром названия группы, // пример команды: [/luckyList@UsersTgBot Java разработчик]) - + for (Map.Entry> moderatorData : chatsSettingsData.entrySet()) { ChatModerator chatModerator = new ChatModerator(); chatModerator.setChatId(moderatorData.getKey()); for (Long userData : moderatorData.getValue()) { - ChatUser chatUser = chatUserService.findByChatId(userData); - if (chatUser == null) { - chatUser = new ChatUser(); - chatUser.setChatId(userData); - chatUser = chatUserService.save(chatUser); - } - chatUser.addChatModerator(chatModerator); - chatModerator.addChatUser(chatUser); + ChatUser chatUser = chatUserService.findByChatId(userData).orElseGet(() -> { + ChatUser newEntity = new ChatUser(); + newEntity.setChatId(userData); + newEntity = chatUserService.save(newEntity); + return newEntity; + }); + chatUser.getChatModerators().add(chatModerator); + chatModerator.getChatUsers().add(chatUser); chatModerator = chatModeratorService.save(chatModerator); } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java index e01fb10..464ec60 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/NotificationTemplates.java @@ -2,13 +2,12 @@ import lombok.Getter; import lombok.Setter; -import org.codewithoutus.tgbotusers.bot.enums.TemplatePattern; -import org.jetbrains.annotations.NotNull; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; -import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; @Getter @Setter @@ -16,8 +15,16 @@ @ConfigurationProperties(prefix = "message-templates") public class NotificationTemplates { + private Map variables; + private Map plugs; + private DateTimeFormatter dateTimeFormatter; + private String joinCongratulation; private String joinAlert; private String joinUserInfo; - + + @PostConstruct + private void postConstruct() { + dateTimeFormatter = DateTimeFormatter.ofPattern(plugs.get("join-date-format")); + } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index 6214796..d1583c0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -7,6 +7,7 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +import java.util.Objects; @Entity @Getter @@ -25,12 +26,18 @@ public class ChatModerator { // TODO: Pavel -- переименовать сущ @JoinTable(name = "moderators2users", joinColumns = @JoinColumn(name = "moderator_chat_id"), inverseJoinColumns = @JoinColumn(name = "user_chat_id")) - private List chatUsers; + private List chatUsers=new ArrayList<>(); - public void addChatUser(ChatUser chatUser) { - if (chatUsers == null) { - chatUsers = new ArrayList<>(); - } - chatUsers.add(chatUser); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChatModerator that = (ChatModerator) o; + return chatId.equals(that.chatId); + } + + @Override + public int hashCode() { + return Objects.hash(chatId); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index 6f44d66..5a4f421 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -7,6 +7,7 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +import java.util.Objects; @Entity @Getter @@ -20,17 +21,23 @@ public class ChatUser { // TODO: Pavel -- переименовать сущно @NaturalId @Column(nullable = false, unique = true) private Long chatId; - - @Column(nullable = true) // TODO: Pavel - подумать nullable = true или false? - private String name; + +// @Column(nullable = true) // TODO: Pavel - подумать nullable = true или false? +// private String name; @ManyToMany(mappedBy = "chatUsers", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) - private List chatModerators; + private List chatModerators = new ArrayList<>(); + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChatUser that = (ChatUser) o; + return chatId.equals(that.chatId); + } - public void addChatModerator(ChatModerator chatModerator) { - if (chatModerators == null) { - chatModerators = new ArrayList<>(); - } - chatModerators.add(chatModerator); + @Override + public int hashCode() { + return Objects.hash(chatId); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java index 03ca0af..5cbc826 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java @@ -2,9 +2,7 @@ import lombok.Getter; import lombok.Setter; -import lombok.ToString; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.jetbrains.annotations.NotNull; import javax.persistence.*; import java.time.LocalDateTime; @@ -13,8 +11,7 @@ @Entity @Getter @Setter -@ToString -public class UserJoining implements Comparable { +public class UserJoining { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -40,30 +37,19 @@ public class UserJoining implements Comparable { private CongratulateStatus status; @OneToMany(mappedBy = "userJoining") - @ToString.Exclude private List notifications; - - - @Override - public int compareTo(@NotNull UserJoining o) { - int result = number.compareTo(o.number); - if (result == 0) { - result = joinTime.compareTo(o.joinTime); - } - return result; - } - + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - + UserJoining that = (UserJoining) o; - + if (!userId.equals(that.userId)) return false; return chatId.equals(that.chatId); } - + @Override public int hashCode() { int result = userId.hashCode(); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java index e996b1b..189574a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java @@ -10,11 +10,11 @@ @Repository public interface ChatUserRepository extends CrudRepository { - boolean existsByName(String name); +// boolean existsByName(String name); - ChatUser findByChatId(long chatId); + Optional findByChatId(long chatId); - Optional findByName(String name); +// Optional findByName(String name); List findByChatModeratorsNotEmpty(); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java index 5ae1e5a..cc2f6b2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java @@ -5,6 +5,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.List; @Repository @@ -12,5 +13,7 @@ public interface UserJoiningRepository extends CrudRepository findByChatId(Long chatId); + List findDistinctByChatIdInOrderByChatIdAscNumberAsc(Collection chatIds); + boolean existsByChatIdAndAnniversaryNumberAndStatus(Long chatId, Integer anniversaryNumber, CongratulateStatus congratulateStatus); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index b7b1022..b0855ed 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -20,23 +20,23 @@ public class ChatUserService { public void deleteAll() { chatUserRepository.deleteAll(); } - - public boolean existsByName(String name) { - return chatUserRepository.existsByName(name); - } + +// public boolean existsByName(String name) { +// return chatUserRepository.existsByName(name); +// } public ChatUser save(ChatUser entity) { return chatUserRepository.save(entity); } - public ChatUser findByChatId(long chatId) { + public Optional findByChatId(long chatId) { return chatUserRepository.findByChatId(chatId); } - public Optional findByName(String name) { - return chatUserRepository.findByName(name); - } - +// public Optional findByName(String name) { +// return chatUserRepository.findByName(name); +// } + public boolean isChatUser(long id) { return chatUserRepository.findByChatModeratorsNotEmpty() .stream() diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java index b44929c..64b5feb 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java @@ -27,6 +27,10 @@ public List findByChatId(Long chatId) { return userJoiningRepository.findByChatId(chatId); } + public List findByChatIds(List chatIds) { + return userJoiningRepository.findDistinctByChatIdInOrderByChatIdAscNumberAsc(chatIds); + } + public boolean existCongratulatedUser(long chatId, int anniversaryNumber) { return userJoiningRepository.existsByChatIdAndAnniversaryNumberAndStatus(chatId, anniversaryNumber, CongratulateStatus.CONGRATULATE); } diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index cbb6b0a..0d5e606 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -19,6 +19,16 @@ chats-settings: - 10000 message-templates: - join-congratulation: "🎉 Поздравляю, {ИмяУчастника}, как же удачно попали в нужное время и в нужное время!%nВы {500} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" - join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{порядковыйНомерВступления}.%nВремя вступления {ВремяВступления}" - join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {порядковыйНомерВступления} 🕐{ВремяВступления}" + variables: + chat-name: "{НазваниеГруппы}" + user-name: "{ИмяУчастника}" + user-nickname: "{НикУчастника}" + join-date: "{ВремяВступления}" + join-number: "{НомерВступления}" + plugs: + no-nick: "ника нет" + winner-crown: "👑👑👑" + join-date-format: "HH:mm" + join-congratulation: "🎉 Поздравляю, {ИмяУчастника}, как же удачно попали в нужное время и в нужное время!%nВы {НомерВступления} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" + join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{НомерВступления}.%nВремя вступления {ВремяВступления}" + join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {НомерВступления} 🕐{ВремяВступления}" From e001d06f5b11c875d657ab0c3f66729b22121306 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 19 Jul 2022 05:01:01 +0400 Subject: [PATCH 37/50] Refactoring --- .../tgbotusers/bot/enums/BotCommand.java | 26 ++- .../bot/handler/AdminMessageHandler.java | 183 ++++++++++++++++++ .../bot/handler/CallbackQueryHandler.java | 17 +- .../bot/handler/PrivateMessageHandler.java | 173 +---------------- .../bot/keyboard/AdminKeyboard.java | 27 +++ .../tgbotusers/bot/service/BotService.java | 13 +- .../model/entity/ChatModerator.java | 2 +- .../tgbotusers/model/entity/ChatUser.java | 2 +- src/main/resources/application.yml | 32 +-- 9 files changed, 266 insertions(+), 209 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java index 93a08e8..7970de4 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java @@ -8,16 +8,32 @@ @Getter public enum BotCommand { - LUCKY_LIST("/luckyList", "^/luckyList( (\\-?\\d*{18}))?"), - CONGRATULATE("/congratulate", "^/congratulate$"), - DECLINE("/decline", "^/decline"); + CONGRATULATE("/congratulate", "T"), + DECLINE("/decline", "T"), - private final String text; + LUCKY_LIST("/luckyList", "T( (ID))?"), + + ADD_MODER_CHAT("/addModerChat", "T (ID)"), + ADD_USER_CHAT("/addUserChat", "T (ID)"), + DELETE_MODER_CHAT("/deleteModerChat", "T (ID)"), + DELETE_USER_CHAT("/deleteUserChat", "T (ID)"), + BIND_USER_CHAT_TO_MODER("/bindUserChatToModer", "T (ID) (ID)"), + UNBIND_USER_CHAT_FROM_MODER("/unbindUserChatFromModer", "T (ID) (ID)"), + + HELP("/help", "T"); + + private static final String ID_REGEX = "(\\-?\\d*{18})"; private final Pattern regex; + private final String text; + private final String help; BotCommand(String text, String regex) { this.text = text; - this.regex = Pattern.compile(regex); + this.help = regex.replace("T", text); + this.regex = Pattern.compile("^" + regex + .replace("T", text) + .replace("(ID)", ID_REGEX) + + "$"); } public static BotCommand getByCommandText(String command) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java new file mode 100644 index 0000000..ef336d4 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java @@ -0,0 +1,183 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.UpdateUtils; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; +import org.codewithoutus.tgbotusers.bot.keyboard.AdminKeyboard; +import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.springframework.stereotype.Component; + +import java.util.regex.Matcher; + +@Component +@RequiredArgsConstructor +public class AdminMessageHandler extends Handler { + + private final TelegramService telegramService; + private final ChatModeratorService chatModeratorService; + private final ChatUserService chatUserService; + + //список команд + //todo надо потом сделать привествие бота,что бы он сразу присал что если что есть help +// private static final String HELP = "/help"; +// private static final String ADD_MODER_CHAT = "/addModerChat"; +// private static final String ADD_USER_CHAT = "/addUserChat"; +// private static final String BIND_USER_CHAT_TO_MODER = "/bindUserChatToModer"; +// private static final String UNBIND_USER_CHAT_TO_MODER = "/unbindUserChatToModer"; + + private static final String OK_BIND = "Cool, user chat № %s , is add in moder chat № %s"; + private static final String NOT_BIND = "Oh, user chat № %s , is already in moder chat № %s"; + private static final String NOT_UNBIND = "Oh, user chat № %s , is not bind in moder chat № %s"; + private static final String OK_UNBIND = "Cool, user chat № %s , is unbind from moder chat № %s"; + private static final String OK_MODER = "Cool, moder chat № %s , is add in DB"; + private static final String OK_USER = "Cool, user chat № %s , is add in DB"; + private static final String NOT_MODER = "Oh, moder chat № %s ,is already in the database"; + private static final String NOT_USER = "Oh, user chat № %s ,is already in the database"; + private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; + private static final String UNKNOW_COMMAND = "Sorry, bot does not know this \"%s\" command"; + //todo вынести команды бота в бот сервис/в бот команд +// List listCommandAdd = Arrays.asList(HELP, ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); +// List listCommandDelete = Arrays.asList(ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); + + @Override + protected boolean handle(Update update) { + if (!UpdateUtils.isPrivateMessageFromAdmin(update)) { + return false; + } + + String text = UpdateUtils.getMessageText(update); + Long chatId = UpdateUtils.getChat(update).id(); + + // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки + // TODO сохраняем в бд группы с + а нужно с - + //мой id=161855902 /11725 + + AdminKeyboard command = KeyboardUtils.defineKey(AdminKeyboard.class, text).orElse(null); + if (command == null) { + return false; + } + return switch (command) { + case HELP -> showHelp(chatId); + case ADD_MODER_CHAT -> addModerChat(update, chatId, text); + case ADD_USER_CHAT -> addUserChat(update, chatId, text); + case BIND_USER_CHAT_TO_MODER -> bindUserChatToModer(update, chatId, text); + default -> false; + }; + } + + private boolean showHelp(Long chatId) { + telegramService.sendMessage(new SendMessage(chatId, + "Список доступных команд :\n" + "listCommandAdd" + "\n" + "listCommandDelete")); + return true; + } + + + private boolean addModerChat(Update update, Long chatId, String text) { + Matcher matcher = BotCommand.ADD_MODER_CHAT.getRegex().matcher(text); + if (!matcher.matches()) { + return false; + } + + long moderatorGroupId = Long.parseLong(matcher.group(2)); + ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); + if (chatModerator != null) { + telegramService.sendMessage(new SendMessage(chatId, "Такой модератор уже есть")); + return true; + } + + ChatModerator newEntity = new ChatModerator(); + newEntity.setChatId(moderatorGroupId); + chatModeratorService.save(newEntity); + return true; + } + + private boolean addUserChat(Update update, Long chatId, String text) { + Matcher matcher = BotCommand.ADD_USER_CHAT.getRegex().matcher(text); + if (!matcher.matches()) { + return false; + } + + long userGroupId = Long.parseLong(matcher.group(1)); + ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); + if (chatUser != null) { + telegramService.sendMessage(new SendMessage(chatId, "Такая группа уже есть")); + return true; + } + + ChatUser newEntity = new ChatUser(); + newEntity.setChatId(userGroupId); + chatUserService.save(newEntity); + return true; + } + + private boolean bindUserChatToModer(Update update, Long chatId, String text) { + Matcher matcher = BotCommand.BIND_USER_CHAT_TO_MODER.getRegex().matcher(text); + if (!matcher.matches()) { + return false; + } + + long userGroupId = Long.parseLong(matcher.group(1)); + ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); + if (chatUser == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_USER, userGroupId))); + return true; + } + + long moderatorGroupId = Long.parseLong(matcher.group(2)); + ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); + if (chatModerator == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, moderatorGroupId))); + return true; + } + + if (chatModerator.getChatUsers().contains(chatUser)) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_BIND, userGroupId, moderatorGroupId))); + return true; + } + + chatModerator.getChatUsers().add(chatUser); + chatModeratorService.save(chatModerator); + telegramService.sendMessage(new SendMessage(chatId, String.format(OK_BIND, userGroupId, moderatorGroupId))); + return true; + } + + private boolean deleteUserChat(Update update, Long chatId,String text) { + Matcher matcher = BotCommand.DELETE_USER_CHAT.getRegex().matcher(text); + if (!matcher.matches()) { + return false; + } + + long userGroupId = Long.parseLong(matcher.group(1)); + ChatUser chatUser= chatUserService.findByChatId(userGroupId).orElse(null); + if (chatUser == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, userGroupId))); + return true; + } + + if(!chatUser.getChatModerators().isEmpty()){ + telegramService.sendMessage(new SendMessage(chatId, "Есть связные сущности у группы "+ userGroupId)); + return true; + } + + chatUserService.deleteById(chatUser.getId()); + telegramService.sendMessage(new SendMessage(chatId, "Удалили группу юзеров № "+ userGroupId)); + return true; + } + + private boolean deleteModerChat() { + + return false; + } + + private boolean deleteUserChatFromModerChat() { + + return false; + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index 3be5579..110b079 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -17,7 +17,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Map; -import java.util.Optional; @Component @RequiredArgsConstructor @@ -35,13 +34,13 @@ protected boolean handle(Update update) { if (callbackQueryData == null || callbackQueryData.isEmpty()) { return false; } - + // есть ли команда в callbackQuery String command = (String) callbackQueryData.get("command"); if (command == null || command.isBlank()) { return false; } - + if (handleCongratulationDecision(command, callbackQueryData)) { return true; @@ -54,12 +53,12 @@ protected boolean handle(Update update) { @Transactional private boolean handleCongratulationDecision(String command, Map callbackQueryData) { // есть ли команда в callbackQuery - Optional decisionOptional = - KeyboardUtils.defineKey(CongratulationDecisionKeyboard.class, command); - if (decisionOptional.isEmpty()) { + CongratulationDecisionKeyboard decision = KeyboardUtils + .defineKey(CongratulationDecisionKeyboard.class, command).orElse(null); + if (decision == null) { return false; } - + // есть ли поздравленные в чате с таким порядковым номером Integer userJoiningId = (Integer) callbackQueryData.get(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD); UserJoining userJoining = userJoiningService.findById(userJoiningId) @@ -74,9 +73,7 @@ private boolean handleCongratulationDecision(String command, Map if (userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { return true; } - - - CongratulationDecisionKeyboard decision = decisionOptional.get(); + CongratulateStatus newStatus = (decision == CongratulationDecisionKeyboard.CONGRATULATE) ? CongratulateStatus.CONGRATULATE : CongratulateStatus.DECLINE; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index 65e83a0..a391d06 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -8,49 +8,14 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.UpdateUtils; import org.codewithoutus.tgbotusers.bot.service.TelegramService; -import org.codewithoutus.tgbotusers.model.entity.ChatModerator; -import org.codewithoutus.tgbotusers.model.entity.ChatUser; -import org.codewithoutus.tgbotusers.model.service.AdministratorService; -import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; -import org.codewithoutus.tgbotusers.model.service.ChatUserService; import org.springframework.stereotype.Component; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - @Component @RequiredArgsConstructor public class PrivateMessageHandler extends Handler { - //список команд - //todo надо потом сделать привествие бота,что бы он сразу присал что если что есть help - private static final String HELP = "/help"; - private static final String ADD_MODER_CHAT = "/addModerChat"; - private static final String ADD_USER_CHAT = "/addUserChat"; - private static final String BIND_USER_CHAT_TO_MODER = "/bindUserChatToModer"; - private static final String UNBIND_USER_CHAT_TO_MODER = "/unbindUserChatToModer"; - - private static final String OK_BIND = "Cool, user chat № %s , is add in moder chat № %s"; - private static final String NOT_BIND = "Oh, user chat № %s , is already in moder chat № %s"; - private static final String NOT_UNBIND = "Oh, user chat № %s , is not bind in moder chat № %s"; - private static final String OK_UNBIND = "Cool, user chat № %s , is unbind from moder chat № %s"; - private static final String OK_MODER = "Cool, moder chat № %s , is add in DB"; - private static final String OK_USER = "Cool, user chat № %s , is add in DB"; - private static final String NOT_MODER = "Oh, moder chat № %s ,is already in the database"; - private static final String NOT_USER = "Oh, user chat № %s ,is already in the database"; - private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; - private static final String UNKNOW_COMMAND = "Sorry, bot does not know this \"%s\" command"; - private static final String SORRY = "Sorry"; - //todo вынести команды бота в бот сервис/в бот команд - List listCommandAdd = Arrays.asList(HELP, ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); - List listCommandDelete = Arrays.asList(ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); - private final TelegramService telegramService; - private final ChatModeratorService chatModeratorService; - private final ChatUserService chatUserService; - + private static final String SORRY = "Sorry"; @Override protected boolean handle(Update update) { @@ -61,9 +26,7 @@ protected boolean handle(Update update) { String text = UpdateUtils.getMessageText(update); Long chatId = UpdateUtils.getChat(update).id(); - if (handleForwardMessage(chatId, update) - || handleAdminRequest(update, chatId, text) - || handleTestRequest(chatId, text)) { + if (handleForwardMessage(chatId, update) || handleTestRequest(chatId, text)) { return true; } telegramService.sendMessage(new SendMessage(chatId, SORRY + update.message() + update.toString() + update)); @@ -91,140 +54,12 @@ private boolean handleTestRequest(Long chatId, String text) { InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( new InlineKeyboardButton("callback_data").callbackData(callbackData)); telegramService.sendMessage(new SendMessage(chatId, "InlineKeyboard test").replyMarkup(keyboard)); - return true; - } - } - } - telegramService.sendMessage(new SendMessage(chatId, String.format(UNKNOW_COMMAND, text))); - return true; - } - - private boolean handleAdminRequest(Update update, Long chatId, String text) { - // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки - // TODO сохраняем в бд группы с + а нужно с - - - //мой id=161855902 /11725 - //TODO проверка пришла ли команда от админа или от левого,если от админа дальше иначе брек - - Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту - String command = text.split(" ")[0]; - switch (command) { - case HELP -> { - telegramService.sendMessage(new SendMessage(chatFrom.id(), "Список доступных команд :\n" - + listCommandAdd.toString() + "\n" + listCommandDelete)); - return true; - } - case ADD_MODER_CHAT -> { - addModerChat(update, chatId, text); - return true; - } - case ADD_USER_CHAT -> { - addUserChat(update, chatId, text); - return true; - } - case BIND_USER_CHAT_TO_MODER -> { - bindUserChatToModer(update, text); - return true; - } - } - return false; - } - - private boolean addModerChat(Update update, Long chatId, String text) { - if (text.startsWith(ADD_MODER_CHAT)) {//парсим команду - long chatIdForAdd = Integer.parseInt(text.substring(ADD_MODER_CHAT.length()).trim());//парсим id группы - Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту - - if (chatIdForAdd > 0) {//проверяем на валидность номер чата - ChatModeratorRepository rep = chatModeratorService.getChatModeratorRepository(); - if ((rep.findByChatId(chatIdForAdd).isEmpty())) { - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_MODER, chatIdForAdd))); - rep.save(new ChatModerator(chatIdForAdd, new ArrayList<>()));//сохраняем группу до перезагрузки бота - return true; - } else { - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_MODER, chatIdForAdd))); - return true; } - } - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(ERROR, chatIdForAdd))); - return true; - } - return false; - } - - private boolean addUserChat(Update update, Long chatId, String text) { - long chatIdForAdd = Integer.parseInt(text.substring(ADD_USER_CHAT.length()).trim());//парсим id группы - Chat chatFrom = UpdateUtils.getChat(update);//получаем чат из которого писали боту - if (chatIdForAdd > 0) { - ChatUserRepository rep = chatUserService.getChatUserRepository(); - if (rep.findByChatId(chatIdForAdd) == null) { - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(OK_USER, chatIdForAdd))); - rep.save(new ChatUser(chatIdForAdd, new ArrayList<>()));//сохраняем группу до перезагрузки бота - return true; } else { - telegramService.sendMessage(new SendMessage(chatFrom.id(), String.format(NOT_USER, chatIdForAdd))); - return true; + telegramService.sendMessage(new SendMessage(chatId, String.format(SORRY, text))); } - } - return false; - } - - private boolean bindUserChatToModer(Update update, Long chatId, String text) { - Matcher matcher = BIND_USER_CHAT_REGEX.matcher(text); - if (!matcher.matches()) { - return false; - } - long userGroupId = Long.parseLong(matcher.group(1)); - long moderatorGroupId = Long.parseLong(matcher.group(2)); - - ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); - if (chatUser == null) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_USER, userGroupId))); - return true; - } - ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); - if (chatModerator == null) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, moderatorGroupId))); - return true; - } - if (chatModerator.getChatUsers().contains(chatUser)) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_BIND, userGroupId, moderatorGroupId))); - return true; - } - chatModerator.getChatUsers().add(chatUser); - chatModeratorService.save(chatModerator); - telegramService.sendMessage(new SendMessage(chatId, String.format(OK_BIND, userGroupId, moderatorGroupId))); - return true; - } - - private boolean deleteUserChat(Update update, Long chatId,String text) { - Matcher matcher = DELETE_USER_CHAT_REGEX.matcher(text); - if (!matcher.matches()) { - return false; - } - long userGroupId = Long.parseLong(matcher.group(1)); - ChatUser chatUser= chatUserService.findByChatId(userGroupId).orElse(null); - if (chatUser == null) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, userGroupId))); - return true; - } - if(!chatUser.getChatModerators().isEmpty()){ - telegramService.sendMessage(new SendMessage(chatId, "Есть связные сущности у группы "+ userGroupId)); return true; } - chatUserService.deleteById(chatUser.getId()); - telegramService.sendMessage(new SendMessage(chatId, "Удалили группу юзеров № "+ userGroupId)); - return true; - - } - - private boolean deleteModerChat() { - - return true; - } - - private boolean deleteUserChatFromModerChat() { - - return true; + return false; } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java new file mode 100644 index 0000000..ed2d238 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java @@ -0,0 +1,27 @@ +package org.codewithoutus.tgbotusers.bot.keyboard; + +import lombok.Getter; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; + +@Getter +public enum AdminKeyboard implements Keyboard { + + ADD_MODER_CHAT(BotCommand.ADD_MODER_CHAT, "Добавить чат модераторов"), + DELETE_MODER_CHAT(BotCommand.DELETE_MODER_CHAT, "Удалить чат модераторов"), + + ADD_USER_CHAT(BotCommand.ADD_USER_CHAT, "Добавить чат пользователей"), + DELETE_USER_CHAT(BotCommand.DELETE_USER_CHAT, "Удалить чат пользователей"), + + BIND_USER_CHAT_TO_MODER(BotCommand.BIND_USER_CHAT_TO_MODER, "Привязать чат пользователей к модераторам"), + UNBIND_USER_CHAT_FROM_MODER(BotCommand.UNBIND_USER_CHAT_FROM_MODER, "Отвязать чат пользователей к модераторам"), + + HELP(BotCommand.HELP, "Помощь"); + + private final BotCommand botCommand; + private final String representation; + + AdminKeyboard(BotCommand botCommand, String representation) { + this.botCommand = botCommand; + this.representation = representation; + } +} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java index e7bf785..573bea1 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java @@ -7,10 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.codewithoutus.tgbotusers.bot.Bot; import org.codewithoutus.tgbotusers.bot.enums.BotStatus; -import org.codewithoutus.tgbotusers.bot.handler.CallbackQueryHandler; -import org.codewithoutus.tgbotusers.bot.handler.ChatJoinRequestHandler; -import org.codewithoutus.tgbotusers.bot.handler.LuckyListCommandHandler; -import org.codewithoutus.tgbotusers.bot.handler.PrivateMessageHandler; +import org.codewithoutus.tgbotusers.bot.handler.*; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; @@ -21,6 +18,7 @@ public class BotService { private final Bot bot; + private final AdminMessageHandler adminMessageHandler; private final CallbackQueryHandler callbackQueryHandler; private final ChatJoinRequestHandler chatJoinRequestHandler; private final PrivateMessageHandler privateMessageHandler; @@ -70,10 +68,11 @@ private void stopUpdatePolling() { } private void handleUpdate(Update update) { - if (chatJoinRequestHandler.tryHandle(update) + if (adminMessageHandler.tryHandle(update) + || privateMessageHandler.tryHandle(update) + || chatJoinRequestHandler.tryHandle(update) || callbackQueryHandler.tryHandle(update) - || luckyListCommandHandler.tryHandle(update) - || privateMessageHandler.tryHandle(update)) { + || luckyListCommandHandler.tryHandle(update)) { // update handled, return } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java index d1583c0..8d399d5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatModerator.java @@ -26,7 +26,7 @@ public class ChatModerator { // TODO: Pavel -- переименовать сущ @JoinTable(name = "moderators2users", joinColumns = @JoinColumn(name = "moderator_chat_id"), inverseJoinColumns = @JoinColumn(name = "user_chat_id")) - private List chatUsers=new ArrayList<>(); + private List chatUsers = new ArrayList<>(); @Override public boolean equals(Object o) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index 669f7d0..5a4f421 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -26,7 +26,7 @@ public class ChatUser { // TODO: Pavel -- переименовать сущно // private String name; @ManyToMany(mappedBy = "chatUsers", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) - private List chatModerators; + private List chatModerators = new ArrayList<>(); @Override public boolean equals(Object o) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fb6fb02..38f2088 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,12 +1,22 @@ +server: + port: 8090 + +bot-settings: + # bot-user-name: skillTestBoxBot + # bot-token: 5566542855:AAHQ8Ge8VD8lVv5sw21f1ztrslroFVo1upI + bot-user-name: ${BOT_USER_NAME} + bot-token: ${BOT_TOKEN} + long-polling-timeout: 1000 + spring: datasource: +# url: jdbc:postgresql://localhost:5432/tg-bot-users +# username: postgres +# password: testtest driver-class-name: org.postgresql.Driver -# url: ${DATASOURCE_URL} - url: jdbc:postgresql://localhost:5432/tg-bot-users -# username: ${DATASOURCE_USER} - username: postgres -# password: ${DATASOURCE_PASS} - password: testtest + url: ${DATASOURCE_URL} + username: ${DATASOURCE_USER} + password: ${DATASOURCE_PASS} hikari: maximum-pool-size: 2 @@ -25,13 +35,3 @@ logging: root: info web: info sql: info - -bot-settings: -# bot-user-name: ${BOT_USER_NAME} - bot-user-name: skillTestBoxBot -# bot-token: ${BOT_TOKEN} - bot-token: 5566542855:AAHQ8Ge8VD8lVv5sw21f1ztrslroFVo1upI - long-polling-timeout: 1000 - - -server.port: 8090 From 845c9ad70fc3942cd9c36c809937f71403eb393b Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 19 Jul 2022 05:13:25 +0400 Subject: [PATCH 38/50] Refactoring+ --- .../bot/handler/AdminMessageHandler.java | 20 +++++++++++-------- .../bot/handler/PrivateMessageHandler.java | 6 +++--- .../bot/service/TelegramService.java | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java index ef336d4..f8ae9f4 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java @@ -14,7 +14,9 @@ import org.codewithoutus.tgbotusers.model.service.ChatUserService; import org.springframework.stereotype.Component; +import java.util.Arrays; import java.util.regex.Matcher; +import java.util.stream.Collectors; @Component @RequiredArgsConstructor @@ -73,12 +75,14 @@ protected boolean handle(Update update) { } private boolean showHelp(Long chatId) { - telegramService.sendMessage(new SendMessage(chatId, - "Список доступных команд :\n" + "listCommandAdd" + "\n" + "listCommandDelete")); + String text = "Список доступных команд: " + System.lineSeparator(); + text += Arrays.stream(AdminKeyboard.values()) + .map(command -> command.getBotCommand().getHelp()) + .collect(Collectors.joining(System.lineSeparator())); + telegramService.sendMessage(new SendMessage(chatId, text)); return true; } - private boolean addModerChat(Update update, Long chatId, String text) { Matcher matcher = BotCommand.ADD_MODER_CHAT.getRegex().matcher(text); if (!matcher.matches()) { @@ -148,26 +152,26 @@ private boolean bindUserChatToModer(Update update, Long chatId, String text) { return true; } - private boolean deleteUserChat(Update update, Long chatId,String text) { + private boolean deleteUserChat(Update update, Long chatId, String text) { Matcher matcher = BotCommand.DELETE_USER_CHAT.getRegex().matcher(text); if (!matcher.matches()) { return false; } long userGroupId = Long.parseLong(matcher.group(1)); - ChatUser chatUser= chatUserService.findByChatId(userGroupId).orElse(null); + ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); if (chatUser == null) { telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, userGroupId))); return true; } - if(!chatUser.getChatModerators().isEmpty()){ - telegramService.sendMessage(new SendMessage(chatId, "Есть связные сущности у группы "+ userGroupId)); + if (!chatUser.getChatModerators().isEmpty()) { + telegramService.sendMessage(new SendMessage(chatId, "Есть связные сущности у группы " + userGroupId)); return true; } chatUserService.deleteById(chatUser.getId()); - telegramService.sendMessage(new SendMessage(chatId, "Удалили группу юзеров № "+ userGroupId)); + telegramService.sendMessage(new SendMessage(chatId, "Удалили группу юзеров № " + userGroupId)); return true; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java index a391d06..9d27eb3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/PrivateMessageHandler.java @@ -15,7 +15,7 @@ public class PrivateMessageHandler extends Handler { private final TelegramService telegramService; - private static final String SORRY = "Sorry"; + private static final String SORRY = "Sorry, wrong or unknown command"; @Override protected boolean handle(Update update) { @@ -29,7 +29,7 @@ protected boolean handle(Update update) { if (handleForwardMessage(chatId, update) || handleTestRequest(chatId, text)) { return true; } - telegramService.sendMessage(new SendMessage(chatId, SORRY + update.message() + update.toString() + update)); + telegramService.sendMessage(new SendMessage(chatId, SORRY)); return false; } @@ -56,7 +56,7 @@ private boolean handleTestRequest(Long chatId, String text) { telegramService.sendMessage(new SendMessage(chatId, "InlineKeyboard test").replyMarkup(keyboard)); } } else { - telegramService.sendMessage(new SendMessage(chatId, String.format(SORRY, text))); + telegramService.sendMessage(new SendMessage(chatId, SORRY)); } return true; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java index 3948501..2c2438c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java @@ -40,7 +40,7 @@ private , R extends BaseResponse> R sendRequest(Base public SendResponse sendMessage(SendMessage message) { SendResponse response = sendRequest(message); - log.debug("Sent message parameters={}. Status={}", message.getParameters().toString(), response.isOk()); + log.debug("Sent message text={}. Status={}", message.getParameters().get("text"), response.isOk()); return response; } From 6f49a22b02acb8744fe962c56bfee923ca10eddb Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 19 Jul 2022 14:29:11 +0400 Subject: [PATCH 39/50] First test --- .../bot/handler/AdminMessageHandler.java | 2 +- .../bot/keyboard/KeyboardUtils.java | 2 +- .../bot/handler/AdminMessageHandlerTest.java | 81 +++++++++++++++++++ .../mocks/answer/AnswerChatModerator.java | 33 ++++++++ .../mocks/answer/AnswerSendResponse.java | 21 +++++ .../tgbotusers/mocks/dto/CallbackQuery.java | 8 ++ .../tgbotusers/mocks/dto/Chat.java | 25 ++++++ .../tgbotusers/mocks/dto/ChatJoinRequest.java | 27 +++++++ .../tgbotusers/mocks/dto/Message.java | 50 ++++++++++++ .../tgbotusers/mocks/dto/MessageEntity.java | 29 +++++++ .../tgbotusers/mocks/dto/Update.java | 25 ++++++ .../tgbotusers/mocks/dto/User.java | 41 ++++++++++ .../mocks/response/ResponseUtils.java | 12 +++ 13 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandlerTest.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerChatModerator.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerSendResponse.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/dto/CallbackQuery.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Chat.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/dto/ChatJoinRequest.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Message.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/dto/MessageEntity.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Update.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/dto/User.java create mode 100644 src/test/java/org/codewithoutus/tgbotusers/mocks/response/ResponseUtils.java diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java index f8ae9f4..2cdbbd9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java @@ -89,7 +89,7 @@ private boolean addModerChat(Update update, Long chatId, String text) { return false; } - long moderatorGroupId = Long.parseLong(matcher.group(2)); + long moderatorGroupId = Long.parseLong(matcher.group(1)); ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); if (chatModerator != null) { telegramService.sendMessage(new SendMessage(chatId, "Такой модератор уже есть")); diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java index 50b0b39..8e842d9 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/KeyboardUtils.java @@ -23,7 +23,7 @@ public static InlineKeyboardMarkup createKeyboard(Class public static Optional defineKey(Class enumClass, String command) { return Arrays.stream(enumClass.getEnumConstants()) - .filter(value -> value.getBotCommand().getText().equals(command)) + .filter(value -> command.startsWith(value.getBotCommand().getText())) .findFirst(); } } \ No newline at end of file diff --git a/src/test/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandlerTest.java b/src/test/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandlerTest.java new file mode 100644 index 0000000..6b7e9b2 --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandlerTest.java @@ -0,0 +1,81 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.request.SendMessage; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.mocks.answer.AnswerChatModerator; +import org.codewithoutus.tgbotusers.mocks.answer.AnswerSendResponse; +import org.codewithoutus.tgbotusers.mocks.dto.Chat; +import org.codewithoutus.tgbotusers.mocks.dto.Message; +import org.codewithoutus.tgbotusers.mocks.dto.Update; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class AdminMessageHandlerTest { + @Mock + private TelegramService telegramService; + @Mock + private ChatModeratorService chatModeratorService; + @Mock + private ChatUserService chatUserService; + + @InjectMocks + private AdminMessageHandler adminMessageHandler; + + @DisplayName("Тест добавления чата модераторов: добавление уже имеющегося в базе ID") + @Test + public void testAddModerChat_existId() { + // when + ChatModerator entity = new ChatModerator(); + when(chatModeratorService.findByChatId(any(Long.class))).thenReturn(Optional.of(entity)); + when(telegramService.sendMessage(any(SendMessage.class))).then(AnswerSendResponse.sendToConsole()); + Mockito.lenient().when(chatModeratorService.save(any(ChatModerator.class))).thenThrow(new IllegalStateException("Записи в БД не должно быть")); + + // then + String commandText = BotCommand.ADD_MODER_CHAT.getText() + " 123"; // "/addModerChat 123" + com.pengrad.telegrambot.model.Update update = getPrivateMessageUpdateWithText(commandText); + + assertTrue(adminMessageHandler.handle(update)); + assertNull(entity.getId()); + } + + @DisplayName("Тест добавления чата модераторов: добавление нового ID") + @Test + public void testAddModerChat_newId() { + // when + ChatModerator entity = new ChatModerator(); + when(chatModeratorService.findByChatId(any(Long.class))).thenReturn(Optional.empty()); + when(chatModeratorService.save(any(ChatModerator.class))).then(AnswerChatModerator.save(entity)); + + // when + String commandText = BotCommand.ADD_MODER_CHAT.getText() + " 123"; // "/addModerChat 123" + com.pengrad.telegrambot.model.Update update = getPrivateMessageUpdateWithText(commandText); + + assertTrue(adminMessageHandler.handle(update)); + assertNotNull(entity.getId()); + } + + private Update getPrivateMessageUpdateWithText(String text) { + return Update.builder() + .message(Message.builder() + .text(text) + .chat(Chat.builder().type(com.pengrad.telegrambot.model.Chat.Type.Private).build()) + .build()) + .build(); + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerChatModerator.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerChatModerator.java new file mode 100644 index 0000000..61165d8 --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerChatModerator.java @@ -0,0 +1,33 @@ +package org.codewithoutus.tgbotusers.mocks.answer; + +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.concurrent.atomic.AtomicInteger; + +public class AnswerChatModerator { + + private static final AtomicInteger sequence = new AtomicInteger(); + + public static Answer newEntity() { + return new Answer() { + @Override + public ChatModerator answer(InvocationOnMock invocation) throws Throwable { + ChatModerator entity = new ChatModerator(); + entity.setId(sequence.getAndIncrement()); + return entity; + } + }; + } + + public static Answer save(ChatModerator entity) { + return new Answer() { + @Override + public ChatModerator answer(InvocationOnMock invocation) throws Throwable { + entity.setId(sequence.getAndIncrement()); + return entity; + } + }; + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerSendResponse.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerSendResponse.java new file mode 100644 index 0000000..1ff915d --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/answer/AnswerSendResponse.java @@ -0,0 +1,21 @@ +package org.codewithoutus.tgbotusers.mocks.answer; + +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.response.SendResponse; +import org.codewithoutus.tgbotusers.mocks.response.ResponseUtils; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class AnswerSendResponse { + + public static Answer sendToConsole() { + return new Answer() { + @Override + public SendResponse answer(InvocationOnMock invocationOnMock) throws Throwable { + SendMessage sendMessage = (SendMessage) invocationOnMock.getArgument(0); + System.out.println(sendMessage.getParameters().get("text")); + return null; + } + }; + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/CallbackQuery.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/CallbackQuery.java new file mode 100644 index 0000000..4849b4c --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/CallbackQuery.java @@ -0,0 +1,8 @@ +package org.codewithoutus.tgbotusers.mocks.dto; + +import lombok.Builder; + +@Builder +public class CallbackQuery extends com.pengrad.telegrambot.model.CallbackQuery { + private String data; +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Chat.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Chat.java new file mode 100644 index 0000000..c84dff4 --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Chat.java @@ -0,0 +1,25 @@ +package org.codewithoutus.tgbotusers.mocks.dto; + +import lombok.Builder; + +@Builder +public class Chat extends com.pengrad.telegrambot.model.Chat { + private Long id; + private Type type; + private String title; + + @Override + public Long id() { + return id; + } + + @Override + public Type type() { + return type; + } + + @Override + public String title() { + return title; + } +} \ No newline at end of file diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/ChatJoinRequest.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/ChatJoinRequest.java new file mode 100644 index 0000000..ab113bf --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/ChatJoinRequest.java @@ -0,0 +1,27 @@ +package org.codewithoutus.tgbotusers.mocks.dto; + +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.User; +import lombok.Builder; + +@Builder +public class ChatJoinRequest extends com.pengrad.telegrambot.model.ChatJoinRequest { + private Chat chat; + private User from; + private Integer date; + + @Override + public Chat chat() { + return chat; + } + + @Override + public User from() { + return from; + } + + @Override + public Integer date() { + return date; + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Message.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Message.java new file mode 100644 index 0000000..13ef193 --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Message.java @@ -0,0 +1,50 @@ +package org.codewithoutus.tgbotusers.mocks.dto; + +import lombok.Builder; +import lombok.Getter; + +@Builder +public class Message extends com.pengrad.telegrambot.model.Message { + private Integer message_id; + private User from; + private Integer date; + private Chat chat; + private Integer forward_date; + private String text; + private MessageEntity[] entities; + + @Override + public Integer messageId() { + return message_id; + } + + @Override + public com.pengrad.telegrambot.model.User from() { + return from; + } + + @Override + public Integer date() { + return date; + } + + @Override + public com.pengrad.telegrambot.model.Chat chat() { + return chat; + } + + @Override + public Integer forwardDate() { + return forward_date; + } + + @Override + public String text() { + return text; + } + + @Override + public com.pengrad.telegrambot.model.MessageEntity[] entities() { + return entities; + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/MessageEntity.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/MessageEntity.java new file mode 100644 index 0000000..f3813a7 --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/MessageEntity.java @@ -0,0 +1,29 @@ +package org.codewithoutus.tgbotusers.mocks.dto; + +import lombok.Builder; + +public class MessageEntity extends com.pengrad.telegrambot.model.MessageEntity { + private Type type; + private Integer offset; + private Integer length; + + @Builder + public MessageEntity(Type type, Integer offset, Integer length) { + super(type, offset, length); + } + + @Override + public Type type() { + return type; + } + + @Override + public Integer offset() { + return offset; + } + + @Override + public Integer length() { + return length; + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Update.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Update.java new file mode 100644 index 0000000..b997d22 --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/Update.java @@ -0,0 +1,25 @@ +package org.codewithoutus.tgbotusers.mocks.dto; + +import lombok.Builder; + +@Builder +public class Update extends com.pengrad.telegrambot.model.Update { + private Message message; + private CallbackQuery callback_query; + private ChatJoinRequest chat_join_request; + + @Override + public com.pengrad.telegrambot.model.Message message() { + return message; + } + + @Override + public com.pengrad.telegrambot.model.CallbackQuery callbackQuery() { + return callback_query; + } + + @Override + public com.pengrad.telegrambot.model.ChatJoinRequest chatJoinRequest() { + return chat_join_request; + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/User.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/User.java new file mode 100644 index 0000000..ed1a2f4 --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/dto/User.java @@ -0,0 +1,41 @@ +package org.codewithoutus.tgbotusers.mocks.dto; + +import lombok.Builder; + +public class User extends com.pengrad.telegrambot.model.User { + private Long id; + private Boolean is_bot; + private String first_name; + private String last_name; + private String username; + + @Builder + public User(Long id) { + super(id); + } + + @Override + public Long id() { + return id; + } + + @Override + public Boolean isBot() { + return is_bot; + } + + @Override + public String firstName() { + return first_name; + } + + @Override + public String lastName() { + return last_name; + } + + @Override + public String username() { + return username; + } +} diff --git a/src/test/java/org/codewithoutus/tgbotusers/mocks/response/ResponseUtils.java b/src/test/java/org/codewithoutus/tgbotusers/mocks/response/ResponseUtils.java new file mode 100644 index 0000000..e49db8e --- /dev/null +++ b/src/test/java/org/codewithoutus/tgbotusers/mocks/response/ResponseUtils.java @@ -0,0 +1,12 @@ +package org.codewithoutus.tgbotusers.mocks.response; + +import com.pengrad.telegrambot.response.SendResponse; +import lombok.SneakyThrows; + +public class ResponseUtils { + + @SneakyThrows + public static SendResponse newSendResponse() { + return SendResponse.class.getConstructor().newInstance(); + } +} From ad284db92fc69ab7dc3fdf059412c4d6236ae830 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 19 Jul 2022 21:31:57 +0400 Subject: [PATCH 40/50] First test --- src/main/resources/application-bot.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index 0d5e606..d671fc2 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,10 +1,9 @@ chats-settings: rewrite-database-settings-on-startup: false chats-settings-data: - 11111: - - 222 - - 333 - - 444 + -1001523814996: + - -1001523814996 + - -1001781082670 22222: - 444 - 555 From 4d3425bcb932c8e2f58c2b45d49f7ce4801d9601 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Wed, 20 Jul 2022 13:22:51 +0400 Subject: [PATCH 41/50] README + admin's ID list + minor config yml changes --- README.md | 92 +++++++++++++++++++ .../tgbotusers/bot/UpdateUtils.java | 31 ++++++- .../tgbotusers/bot/enums/BotCommand.java | 16 ++-- .../bot/handler/AdminMessageHandler.java | 8 +- .../bot/handler/CallbackQueryHandler.java | 6 +- .../bot/handler/ChatJoinMessageHandler.java | 69 ++++++++++++++ .../bot/handler/LuckyListCommandHandler.java | 14 ++- .../bot/keyboard/AdminKeyboard.java | 10 +- .../tgbotusers/bot/service/BotService.java | 6 +- .../tgbotusers/config/ChatSettings.java | 13 ++- .../model/entity/Administrator.java | 21 ----- .../tgbotusers/model/entity/UserJoining.java | 2 +- .../repository/AdministratorRepository.java | 9 -- .../repository/UserJoiningRepository.java | 2 + .../model/service/AdministratorService.java | 9 -- .../model/service/ChatUserService.java | 4 + .../model/service/UserJoiningService.java | 4 + src/main/resources/application-bot.yml | 24 +++-- src/main/resources/application.yml | 2 +- 19 files changed, 259 insertions(+), 83 deletions(-) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinMessageHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/entity/Administrator.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/repository/AdministratorRepository.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/model/service/AdministratorService.java diff --git a/README.md b/README.md index 1164b0e..285f1c4 100644 --- a/README.md +++ b/README.md @@ -1 +1,93 @@ # tg-bot-users 🤖 + +--- +## Общие сведения +Приложение является телеграм ботом + + +Основные функции: + * Бот отслеживает вступление в группу новых пользователей. + * В случае юбилейного вступления пользователя, присылает уведомление в группу модераторов для принятия решения о поздравлении. + * При нажатии кнопки "Поздравить" приходит автоматическое поздравление вступившего пользователя. + * При нажатии кнопки "Отклонить" поздравления не происходит. + + +Дополнительные функции: + * Помимо самого юбилейного вступления, отслеживается несколько последующих - для ситуаций, если юбилейным оказался бот или модератор. Уведомление присылается на каждое подобное вступление, если решение о поздравлении ещё не было принято. + * Модератор может запросить у бота список всех юбилейных вступлений. Если в юбилейном списке есть ожидающие поздравления, то модератор может принять решение, пользуясь полученным списком. При этом раннее отклоненные решения могут быть изменены. + + +Менять настройки групп могут только администраторы. + + +--- +## Команды бота + +### Команды администраторов +_Доступны администраторам в приватном чате_ + +| Команда | Описание | +|------------------------------------|---------------------------------------------------------| +| /help | вывод списка доступных команд | +| /addModerChat {id} | добавление чата модераторов | +| /addUserChat {id} | добавление чата пользователей | +| /deleteModerChat {id} | удаление чата модераторов | +| /deleteUserChat {id} | удаление чата пользователей | +| /bindUserChatToModer {id} {id} | привязка чата пользователей к чату модераторов | +| /unbindUserChatFromModer {id} {id} | удаление привязки чата пользователей к чату модераторов | + + +### Команды модераторов +_Доступны в чатах модераторов_ + +| Команда | Описание | +|-----------------|---------------------------------------------------------------------------| +| /luckyList | вывод списка юбилейных вступлений во всех привязанных чатах пользователей | +| /luckyList {id} | вывод списка юбилейных вступлений в конкретном чате | + + +--- +## Настройки приложения + +Конфигурационные файлы приложения разделены на две части: +### Настройки для разработчиков: + * Spring + * Database _(environment vars)_ + * Bot token _(environment vars)_ + + + +### Настройки функций бота: +Настройки групп в приоритете берутся из базы данных. Из файла конфигурации эти настройки подтягиваются только в случае отсутствия таковых в БД, либо если включен флаг перезаписи настроек. + + * Настройки для чатов `chats-settings`: + * `administrators` - список ID администраторов + * `anniversary-numbers` - список юбилейных номеров + * `anniversary-numbers-delta` - количество дополнительно отслеживаемых вступлений + * `chats-settings` - настройки групп. Настройки прописываются иерархически: к каждому ID группы модераторов прописывается список ID групп пользователей. + * `rewrite-chats-settings-in-database-on-startup` - перезапись настроек групп в базе данных на настройки из конфигурационного файла. + + + * Настройки шаблонов `message-templates`: + * `variables` - названия переменных шаблонов + * `plugs` - дополнительные моменты + * `join-congratulation` - шаблон сообщения поздравления пользователя + * `join-alert` - шаблон уведомления модераторов о юбилейном вступлении пользователя + * `join-user-info` - шаблон данных пользователя при использовании команды `/luckyList` + + +--- +## Примеры сообщений + +Уведомление модераторов + + 🎉 “Java разработчик” 👤 Василий (ника нет), + 🔢 500 🕐 26.06.22 10:56 + [ПОЗДРАВИТЬ] [ОТКЛОНИТЬ] + +Поздравление пользователя + + 🎉 Поздравляю, Никита, + как же удачно попали в нужное время и в нужное время! + Вы 500 участник коммьюнити. + Вас ждут плюшки и печенюшки!🎉 diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateUtils.java b/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateUtils.java index f6c105d..f1cc36b 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateUtils.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/UpdateUtils.java @@ -7,6 +7,7 @@ import org.codewithoutus.tgbotusers.bot.enums.BotCommand; import org.codewithoutus.tgbotusers.bot.exception.CallbackDataMappingException; import org.codewithoutus.tgbotusers.config.AppStaticContext; +import org.codewithoutus.tgbotusers.config.ChatSettings; import java.util.Arrays; import java.util.Map; @@ -22,8 +23,8 @@ public static Boolean isPrivateMessage(Update update) { .orElse(Boolean.FALSE); } - public static boolean isPrivateMessageFromAdmin(Update update) { - return isPrivateMessage(update); + public static boolean isPrivateMessageFromAdmin(Update update, ChatSettings chatSettings) { + return isPrivateMessage(update) && chatSettings.isAdminId(getUserId(update)); } public static boolean isForwardMessage(Update update) { @@ -38,6 +39,12 @@ public static String getMessageText(Update update) { .orElse(""); } + public static User[] getNewChatMembers(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::newChatMembers) + .orElse(null); + } + public static BotCommand getBotCommand(Update update) { return Optional.ofNullable(update.message()) .map(Message::entities) @@ -55,6 +62,22 @@ public static Chat getChat(Update update) { .orElse(null); } + public static Long getChatId(Update update) { + Chat chat = getChat(update); + return chat == null ? null : chat.id(); + } + + public static User getUser(Update update) { + return Optional.ofNullable(update.message()) + .map(Message::from) + .orElse(null); + } + + public static Long getUserId(Update update) { + User user = getUser(update); + return user == null ? null : user.id(); + } + public static String getCallbackQueryData(Update update) { return Optional.ofNullable(update.callbackQuery()) .map(CallbackQuery::data) @@ -62,11 +85,11 @@ public static String getCallbackQueryData(Update update) { .orElse("{}"); } - public static Map getCallbackQueryDataAsMap(Update update) { + public static Map getCallbackQueryDataAsMap(Update update) { return getCallbackQueryDataAsMap(getCallbackQueryData(update)); } - public static Map getCallbackQueryDataAsMap(String callbackData) { + public static Map getCallbackQueryDataAsMap(String callbackData) { try { return AppStaticContext.OBJECT_MAPPER.readValue(callbackData, new TypeReference<>() { }); diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java index 7970de4..c77e86e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java @@ -11,14 +11,14 @@ public enum BotCommand { CONGRATULATE("/congratulate", "T"), DECLINE("/decline", "T"), - LUCKY_LIST("/luckyList", "T( (ID))?"), + LUCKY_LIST("/luckyList", "T( {id}})?"), - ADD_MODER_CHAT("/addModerChat", "T (ID)"), - ADD_USER_CHAT("/addUserChat", "T (ID)"), - DELETE_MODER_CHAT("/deleteModerChat", "T (ID)"), - DELETE_USER_CHAT("/deleteUserChat", "T (ID)"), - BIND_USER_CHAT_TO_MODER("/bindUserChatToModer", "T (ID) (ID)"), - UNBIND_USER_CHAT_FROM_MODER("/unbindUserChatFromModer", "T (ID) (ID)"), + ADD_MODER_CHAT("/addModerChat", "T {id}"), + ADD_USER_CHAT("/addUserChat", "T {id}"), + DELETE_MODER_CHAT("/deleteModerChat", "T {id}"), + DELETE_USER_CHAT("/deleteUserChat", "T {id}"), + BIND_USER_CHAT_TO_MODER("/bindUserChatToModer", "T {id} {id}"), + UNBIND_USER_CHAT_FROM_MODER("/unbindUserChatFromModer", "T {id} {id}"), HELP("/help", "T"); @@ -32,7 +32,7 @@ public enum BotCommand { this.help = regex.replace("T", text); this.regex = Pattern.compile("^" + regex .replace("T", text) - .replace("(ID)", ID_REGEX) + .replace("{id}", ID_REGEX) + "$"); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java index 2cdbbd9..8a4e563 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java @@ -1,6 +1,7 @@ package org.codewithoutus.tgbotusers.bot.handler; import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.UpdateUtils; @@ -8,6 +9,7 @@ import org.codewithoutus.tgbotusers.bot.keyboard.AdminKeyboard; import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.config.ChatSettings; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; @@ -23,6 +25,8 @@ public class AdminMessageHandler extends Handler { private final TelegramService telegramService; + private final ChatSettings chatSettings; + private final ChatModeratorService chatModeratorService; private final ChatUserService chatUserService; @@ -50,7 +54,8 @@ public class AdminMessageHandler extends Handler { @Override protected boolean handle(Update update) { - if (!UpdateUtils.isPrivateMessageFromAdmin(update)) { + User user = UpdateUtils.getUser(update); + if (user == null || !chatSettings.isAdminId(user.id()) || !UpdateUtils.isPrivateMessage(update)) { return false; } @@ -59,7 +64,6 @@ protected boolean handle(Update update) { // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки // TODO сохраняем в бд группы с + а нужно с - - //мой id=161855902 /11725 AdminKeyboard command = KeyboardUtils.defineKey(AdminKeyboard.class, text).orElse(null); if (command == null) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index 110b079..6c83e6c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -30,7 +30,7 @@ public class CallbackQueryHandler extends Handler { @Override protected boolean handle(Update update) { // есть ли callbackQuery в update - Map callbackQueryData = UpdateUtils.getCallbackQueryDataAsMap(update); + Map callbackQueryData = UpdateUtils.getCallbackQueryDataAsMap(update); if (callbackQueryData == null || callbackQueryData.isEmpty()) { return false; } @@ -51,7 +51,7 @@ protected boolean handle(Update update) { } @Transactional - private boolean handleCongratulationDecision(String command, Map callbackQueryData) { + private boolean handleCongratulationDecision(String command, Map callbackQueryData) { // есть ли команда в callbackQuery CongratulationDecisionKeyboard decision = KeyboardUtils .defineKey(CongratulationDecisionKeyboard.class, command).orElse(null); @@ -60,7 +60,7 @@ private boolean handleCongratulationDecision(String command, Map } // есть ли поздравленные в чате с таким порядковым номером - Integer userJoiningId = (Integer) callbackQueryData.get(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD); + Integer userJoiningId = Integer.parseInt(callbackQueryData.get(AppStaticContext.CALLBACK_QUERY_DATA_ID_FIELD)); UserJoining userJoining = userJoiningService.findById(userJoiningId) .orElseThrow(() -> new IllegalStateException( "CallbackQueryData with no exist user joining ID" + userJoiningId)); diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinMessageHandler.java new file mode 100644 index 0000000..ce2f55c --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinMessageHandler.java @@ -0,0 +1,69 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.User; +import lombok.RequiredArgsConstructor; +import org.codewithoutus.tgbotusers.bot.UpdateUtils; +import org.codewithoutus.tgbotusers.bot.service.NotificationService; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.config.ChatSettings; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; +import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.codewithoutus.tgbotusers.model.service.UserJoiningService; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; + +@Component +@RequiredArgsConstructor +public class ChatJoinMessageHandler extends Handler { + + private final ChatSettings chatSettings; + private final ChatUserService chatUserService; + private final TelegramService telegramService; + private final NotificationService notificationService; + private final UserJoiningService userJoiningService; + + @Override + protected boolean handle(Update update) { + User[] users = UpdateUtils.getNewChatMembers(update); + if (users == null || users.length == 0) { + return false; + } + long chatId = UpdateUtils.getChat(update).id(); + if (!chatUserService.existByChatId(chatId)) { + return false; + } + + Instant instant = Instant.ofEpochSecond(update.message().date()); + LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + int chatNumbersCount = telegramService.getChatMembersCount(chatId); + + for (int i = 0; i < users.length; i++) { + User user = users[i]; + int joinNumber = chatNumbersCount - users.length + 1 + i; + int anniversaryNumber = chatSettings.getAnniversaryJoinNumber(chatId, joinNumber); + if (anniversaryNumber == 0 || userJoiningService.userWasAlreadyJoinedToChat(chatId, user.id())) { + return false; + } + + UserJoining userJoining = new UserJoining(); + userJoining.setChatId(chatId); + userJoining.setUserId(user.id()); + userJoining.setJoinTime(dateTime); + userJoining.setNumber(joinNumber); + userJoining.setAnniversaryNumber(anniversaryNumber); + userJoining.setStatus(CongratulateStatus.WAIT); + userJoining = userJoiningService.save(userJoining); + + // если не было поздравненных в чате с таким порядковым номером + if (!userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { + notificationService.notifyModeratorsAboutUserJoining(userJoining); + } + } + return true; + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java index 7d9551f..e8c1de6 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java @@ -8,6 +8,7 @@ import org.codewithoutus.tgbotusers.bot.enums.BotCommand; import org.codewithoutus.tgbotusers.bot.service.TelegramService; import org.codewithoutus.tgbotusers.bot.service.TemplateEngine; +import org.codewithoutus.tgbotusers.config.ChatSettings; import org.codewithoutus.tgbotusers.config.NotificationTemplates; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; @@ -26,12 +27,14 @@ @Slf4j public class LuckyListCommandHandler extends Handler { + private final ChatSettings chatSettings; + private final NotificationTemplates notificationTemplates; + private final TelegramService telegramService; + private final TemplateEngine templateEngine; + private final ChatModeratorService chatModeratorService; private final ChatUserService chatUserService; private final UserJoiningService userJoiningService; - private final TelegramService telegramService; - private final NotificationTemplates notificationTemplates; - private final TemplateEngine templateEngine; @Override protected boolean handle(Update update) { @@ -47,8 +50,9 @@ protected boolean handle(Update update) { return false; } - Long moderatorChatId = UpdateUtils.getChat(update).id(); - if (!UpdateUtils.isPrivateMessageFromAdmin(update) && !chatModeratorService.existsByChatId(moderatorChatId)) { + Long moderatorChatId = UpdateUtils.getChatId(update); + if (!UpdateUtils.isPrivateMessageFromAdmin(update, chatSettings) + && !chatModeratorService.existsByChatId(moderatorChatId)) { return false; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java index ed2d238..1ecb3d2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java @@ -6,16 +6,16 @@ @Getter public enum AdminKeyboard implements Keyboard { - ADD_MODER_CHAT(BotCommand.ADD_MODER_CHAT, "Добавить чат модераторов"), - DELETE_MODER_CHAT(BotCommand.DELETE_MODER_CHAT, "Удалить чат модераторов"), + HELP(BotCommand.HELP, "Помощь"), + ADD_MODER_CHAT(BotCommand.ADD_MODER_CHAT, "Добавить чат модераторов"), ADD_USER_CHAT(BotCommand.ADD_USER_CHAT, "Добавить чат пользователей"), + + DELETE_MODER_CHAT(BotCommand.DELETE_MODER_CHAT, "Удалить чат модераторов"), DELETE_USER_CHAT(BotCommand.DELETE_USER_CHAT, "Удалить чат пользователей"), BIND_USER_CHAT_TO_MODER(BotCommand.BIND_USER_CHAT_TO_MODER, "Привязать чат пользователей к модераторам"), - UNBIND_USER_CHAT_FROM_MODER(BotCommand.UNBIND_USER_CHAT_FROM_MODER, "Отвязать чат пользователей к модераторам"), - - HELP(BotCommand.HELP, "Помощь"); + UNBIND_USER_CHAT_FROM_MODER(BotCommand.UNBIND_USER_CHAT_FROM_MODER, "Отвязать чат пользователей к модераторам"); private final BotCommand botCommand; private final String representation; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java index 573bea1..d5fcb11 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java @@ -20,10 +20,11 @@ public class BotService { private final Bot bot; private final AdminMessageHandler adminMessageHandler; private final CallbackQueryHandler callbackQueryHandler; + private final ChatJoinMessageHandler chatJoinMessageHandler; private final ChatJoinRequestHandler chatJoinRequestHandler; - private final PrivateMessageHandler privateMessageHandler; private final LuckyListCommandHandler luckyListCommandHandler; - + private final PrivateMessageHandler privateMessageHandler; + @PostConstruct private void botStart() { start(); @@ -70,6 +71,7 @@ private void stopUpdatePolling() { private void handleUpdate(Update update) { if (adminMessageHandler.tryHandle(update) || privateMessageHandler.tryHandle(update) + || chatJoinMessageHandler.tryHandle(update) || chatJoinRequestHandler.tryHandle(update) || callbackQueryHandler.tryHandle(update) || luckyListCommandHandler.tryHandle(update)) { diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java index d6a8277..2582909 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java +++ b/src/main/java/org/codewithoutus/tgbotusers/config/ChatSettings.java @@ -29,12 +29,17 @@ public class ChatSettings { private final ChatModeratorService chatModeratorService; private final ChatUserService chatUserService; - private Boolean rewriteDatabaseSettingsOnStartup; + private Set administrators; private Integer anniversaryNumbersDelta; private Set anniversaryNumbers; @Getter(AccessLevel.NONE) - private Map> chatsSettingsData; // only used for loading from application-settings file + private Map> chatsSettings; // only used for loading from application-settings file + private Boolean rewriteChatsSettingsInDatabaseOnStartup; + + public boolean isAdminId(Long id) { + return id != null && administrators.contains(id); + } public int getAnniversaryJoinNumber(long chatId, int joinNumber) { return anniversaryNumbers.stream() @@ -46,7 +51,7 @@ public int getAnniversaryJoinNumber(long chatId, int joinNumber) { @PostConstruct private void synchronizeDataBaseSettings() { - if (rewriteDatabaseSettingsOnStartup || chatModeratorService.findAll().isEmpty()) { + if (rewriteChatsSettingsInDatabaseOnStartup || chatModeratorService.findAll().isEmpty()) { rewriteDataBaseSettings(); } } @@ -63,7 +68,7 @@ private void rewriteDataBaseSettings() { // (нужно для возможности получения списка юбилейных с параметром названия группы, // пример команды: [/luckyList@UsersTgBot Java разработчик]) - for (Map.Entry> moderatorData : chatsSettingsData.entrySet()) { + for (Map.Entry> moderatorData : chatsSettings.entrySet()) { ChatModerator chatModerator = new ChatModerator(); chatModerator.setChatId(moderatorData.getKey()); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/Administrator.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/Administrator.java deleted file mode 100644 index 6800fa9..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/Administrator.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.codewithoutus.tgbotusers.model.entity; - -import lombok.Getter; -import lombok.Setter; -import org.hibernate.annotations.NaturalId; - -import javax.persistence.*; - -@Entity -@Getter -@Setter -public class Administrator { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @NaturalId - @Column(nullable = false) - Long userId; -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java index 5cbc826..f4d9ea4 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java @@ -33,7 +33,7 @@ public class UserJoining { private LocalDateTime joinTime; @Enumerated(EnumType.STRING) - @Column(nullable = false) + @Column(nullable = false)//, columnDefinition = "varchar(15) default 'WAIT'") private CongratulateStatus status; @OneToMany(mappedBy = "userJoining") diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/AdministratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/AdministratorRepository.java deleted file mode 100644 index b1efcb8..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/AdministratorRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.codewithoutus.tgbotusers.model.repository; - -import org.codewithoutus.tgbotusers.model.entity.Administrator; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface AdministratorRepository extends CrudRepository { -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java index cc2f6b2..a92650d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java @@ -16,4 +16,6 @@ public interface UserJoiningRepository extends CrudRepository findDistinctByChatIdInOrderByChatIdAscNumberAsc(Collection chatIds); boolean existsByChatIdAndAnniversaryNumberAndStatus(Long chatId, Integer anniversaryNumber, CongratulateStatus congratulateStatus); + + boolean existsByChatIdAndUserId(Long chatId, Long userId); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/AdministratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/AdministratorService.java deleted file mode 100644 index a664841..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/AdministratorService.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.codewithoutus.tgbotusers.model.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class AdministratorService { -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index f42c6f6..a436304 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -36,6 +36,10 @@ public Optional findByChatId(long chatId) { return chatUserRepository.findByChatId(chatId); } + public boolean existByChatId(long chatId) { + return chatUserRepository.existsByChatId(chatId); + } + public boolean isChatUser(long id) { return chatUserRepository.findByChatModeratorsNotEmpty() .stream() diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java index 64b5feb..7af2d10 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java @@ -34,4 +34,8 @@ public List findByChatIds(List chatIds) { public boolean existCongratulatedUser(long chatId, int anniversaryNumber) { return userJoiningRepository.existsByChatIdAndAnniversaryNumberAndStatus(chatId, anniversaryNumber, CongratulateStatus.CONGRATULATE); } + + public boolean userWasAlreadyJoinedToChat(Long chatId, Long userId) { + return userJoiningRepository.existsByChatIdAndUserId(chatId, userId); + } } diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml index d671fc2..0b9ebb9 100644 --- a/src/main/resources/application-bot.yml +++ b/src/main/resources/application-bot.yml @@ -1,21 +1,27 @@ chats-settings: - rewrite-database-settings-on-startup: false - chats-settings-data: - -1001523814996: - - -1001523814996 - - -1001781082670 - 22222: - - 444 - - 555 - - 666 + administrators: + - 1039061325 # pavel + - 161855902 # max + - 1004758635 # alex anniversary-numbers-delta: 3 anniversary-numbers: + - 5 + - 50 - 100 - 500 - 777 - 1000 - 5000 - 10000 + chats-settings: + -1001523814996: #Хакатон и телеграм бот (Skillbox) + - -1001523814996 # Хакатон и телеграм бот (Skillbox) + - -1001781082670 # Java-диплом с нуля (Skillbox) + 22222: + - 444 + - 555 + - 666 + rewrite-chats-settings-in-database-on-startup: true message-templates: variables: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 38f2088..2d7e0c3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -24,7 +24,7 @@ spring: database-platform: org.hibernate.dialect.PostgreSQLDialect show_sql: true hibernate: - ddl-auto: create + ddl-auto: validate profiles: include: From e11e3f88f6ff945e6b5d48914adb0941b197541e Mon Sep 17 00:00:00 2001 From: Aleksei Date: Wed, 20 Jul 2022 21:32:15 +0400 Subject: [PATCH 42/50] fix config settings, added application-test.yml, added Dockerfile and docker-compose.yml. For starting set properties in application-bot.yml and .env, in command line input "mvn clean package" and "docker-compose.yml", for stop CTRL+C --- .dockerignore | 4 +++ .env | 6 ++++ .mvn/wrapper/maven-wrapper.jar | Bin 58727 -> 59925 bytes .mvn/wrapper/maven-wrapper.properties | 20 ++++++++++-- Dockerfile | 4 +++ ...application-bot.yml => application-bot.yml | 0 docker-compose.yml | 30 ++++++++++++++++++ pom.xml | 7 ++++ src/main/resources/application.yml | 13 ++++---- .../TgBotUsersApplicationTests.java | 2 ++ src/test/resources/application-test.yml | 30 ++++++++++++++++++ 11 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 .dockerignore create mode 100644 .env create mode 100644 Dockerfile rename src/main/resources/application-bot.yml => application-bot.yml (100%) create mode 100644 docker-compose.yml create mode 100644 src/test/resources/application-test.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9d82e02 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.git +.idea +.mvn +Dockerfile \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..c14d4c2 --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +BOT_USER_NAME=GroupControlSkillboxBot +BOT_TOKEN=5566628073:AAHyZTYOh62Fb7_zVGNmHMqzwJUQtBvGWuc +DATASOURCE_DB=tg-bot-users +DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5431/ +DATASOURCE_USER=tg-admin +DATASOURCE_PASS=9en2w0oc \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index c1dd12f17644411d6e840bd5a10c6ecda0175f18..bf82ff01c6cdae4a1bb754a6e062954d77ac5c11 100644 GIT binary patch delta 26989 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 w3X+E*U-p8ah&^pYiiN7iT4~3Z~R{#J2 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 22f219d..70e554c 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,18 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..039a84f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:17-alpine +COPY target/tg-bot-users-0.0.1-SNAPSHOT.jar tg-bot-users.jar +COPY application-bot.yml . +CMD java -jar tg-bot-users.jar \ No newline at end of file diff --git a/src/main/resources/application-bot.yml b/application-bot.yml similarity index 100% rename from src/main/resources/application-bot.yml rename to application-bot.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d91d1dc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +version: "3.9" +services: + postgres: + container_name: postgres + image: postgres:alpine + restart: always + environment: + POSTGRES_USER: ${DATASOURCE_USER} + POSTGRES_PASSWORD: ${DATASOURCE_PASS} + POSTGRES_DB: ${DATASOURCE_DB} + ports: + - "5431:5432" + volumes: + - C:\docker\postgres:/var/lib/postgres + tg-bot-users: + container_name: tg-bot-users + image: tg-bot-users + build: + context: . + ports: + - "8080:8080" + environment: + BOT_USER_NAME: ${BOT_USER_NAME} + BOT_TOKEN: ${BOT_TOKEN} + DATASOURCE_URL: ${DATASOURCE_URL}${DATASOURCE_DB} + DATASOURCE_USER: ${DATASOURCE_USER} + DATASOURCE_PASS: ${DATASOURCE_PASS} + depends_on: + postgres: + condition: service_started \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4e3f7d6..39578dc 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,13 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + false + + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2d7e0c3..02323e8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,12 +1,12 @@ -server: - port: 8090 +#server: +# port: 8090 bot-settings: # bot-user-name: skillTestBoxBot # bot-token: 5566542855:AAHQ8Ge8VD8lVv5sw21f1ztrslroFVo1upI bot-user-name: ${BOT_USER_NAME} bot-token: ${BOT_TOKEN} - long-polling-timeout: 1000 + long-polling-timeout: 75000 spring: datasource: @@ -24,7 +24,7 @@ spring: database-platform: org.hibernate.dialect.PostgreSQLDialect show_sql: true hibernate: - ddl-auto: validate + ddl-auto: update profiles: include: @@ -33,5 +33,6 @@ spring: logging: level: root: info - web: info - sql: info + org.codewithoutus.tgbotusers: debug + web: off + sql: off diff --git a/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java b/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java index c65754d..fa2407b 100644 --- a/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java +++ b/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java @@ -2,8 +2,10 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; @SpringBootTest +@ActiveProfiles({"test", "bot"}) class TgBotUsersApplicationTests { @Test diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..06c1456 --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,30 @@ +#server: +# port: 8090 + +bot-settings: +# bot-user-name: GroupControlSkillboxBot +# bot-token: 5566628073:AAHyZTYOh62Fb7_zVGNmHMqzwJUQtBvGWuc + long-polling-timeout: 75000 + +spring: + datasource: + url: jdbc:postgresql://localhost:5432/tg-bot-users + username: tg-admin + password: 9en2w0oc +# driver-class-name: org.postgresql.Driver + hikari: + maximum-pool-size: 2 + + jpa: +# database-platform: org.hibernate.dialect.PostgreSQLDialect + show_sql: false + hibernate: + ddl-auto: update + + +logging: + level: + root: info + org.codewithoutus.tgbotusers: debug + web: off + sql: off From dccdc29c5293c93341c30ab302e5e3d208270f06 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Wed, 20 Jul 2022 22:56:54 +0400 Subject: [PATCH 43/50] added chooseLucky command. Need refactoring! --- docker-compose.yml | 1 + .../tgbotusers/bot/enums/BotCommand.java | 1 + .../handler/ChooseLuckyCommandHandler.java | 115 ++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java diff --git a/docker-compose.yml b/docker-compose.yml index d91d1dc..d68ef05 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,7 @@ services: tg-bot-users: container_name: tg-bot-users image: tg-bot-users + restart: always build: context: . ports: diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java index c77e86e..d3f8d22 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java @@ -12,6 +12,7 @@ public enum BotCommand { DECLINE("/decline", "T"), LUCKY_LIST("/luckyList", "T( {id}})?"), + CHOOSE_LUCKY("/chooseLucky", "T( {id})"), ADD_MODER_CHAT("/addModerChat", "T {id}"), ADD_USER_CHAT("/addUserChat", "T {id}"), diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java new file mode 100644 index 0000000..19ef1e9 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java @@ -0,0 +1,115 @@ +package org.codewithoutus.tgbotusers.bot.handler; + +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.SendMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.UpdateUtils; +import org.codewithoutus.tgbotusers.bot.enums.BotCommand; +import org.codewithoutus.tgbotusers.bot.service.NotificationService; +import org.codewithoutus.tgbotusers.bot.service.TelegramService; +import org.codewithoutus.tgbotusers.bot.service.TemplateEngine; +import org.codewithoutus.tgbotusers.config.ChatSettings; +import org.codewithoutus.tgbotusers.config.NotificationTemplates; +import org.codewithoutus.tgbotusers.model.entity.ChatModerator; +import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; +import org.codewithoutus.tgbotusers.model.service.ChatUserService; +import org.codewithoutus.tgbotusers.model.service.UserJoiningService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +@Slf4j +public class ChooseLuckyCommandHandler extends Handler { + + private final ChatSettings chatSettings; + private final NotificationService notificationService; + private final TelegramService telegramService; + + private final ChatModeratorService chatModeratorService; + private final ChatUserService chatUserService; + private final UserJoiningService userJoiningService; + + @Override + protected boolean handle(Update update) { + + // TODO: Алекс: + // 1. если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard + // 2. если поздравленного нет, то отклоненные тоже выводятся + // 3. все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись + // 4. можно попробовать выводить все-все чаты, а потом доработать -- на будущее + // ИТОГ: реализовать (внедрить) команду /chooseLucky + + BotCommand command = UpdateUtils.getBotCommand(update); + if (command != BotCommand.LUCKY_LIST) { + return false; + } + + Long moderatorChatId = UpdateUtils.getChatId(update); + if (!UpdateUtils.isPrivateMessageFromAdmin(update, chatSettings) + && !chatModeratorService.existsByChatId(moderatorChatId)) { + return false; + } + + String commandText = UpdateUtils.getMessageText(update); + Matcher matcher = command.getRegex().matcher(commandText); + if (!matcher.matches()) { + log.warn("Wrong command to bot {}", commandText); + return false; + } + + String chatId = matcher.group(2); + List luckyChats = getLuckyChats(chatId, moderatorChatId); + if (luckyChats.isEmpty()) { + telegramService.sendMessage(new SendMessage(moderatorChatId, "Нет данных о чатах пользователей")); + return true; + } + + sendNotificationsAgain(moderatorChatId, luckyChats); + + return true; + } + + + private List getLuckyChats(String chatId, Long moderatorChatId) { + if (chatId == null) { + return chatModeratorService + .findByChatId(moderatorChatId) + .map(ChatModerator::getChatUsers) + .orElseGet(ArrayList::new); + } else { + return chatUserService + .findByChatId(Long.parseLong(chatId)) + .stream() + .toList(); + } + } + + private void sendNotificationsAgain(Long moderatorChatId, List luckyChats) { + List chatsIds = luckyChats.stream() + .map(ChatUser::getChatId) + .toList(); + + userJoiningService.findByChatIds(chatsIds) + .stream() + .collect(Collectors.groupingBy(x -> String.valueOf(x.getChatId()))) + .forEach((groupId, joiningList) -> { + + telegramService.sendMessage(new SendMessage(moderatorChatId, getGroupSeparator(groupId))); + + joiningList.forEach(notificationService::notifyModeratorsAboutUserJoining); + }); + } + + private String getGroupSeparator(String groupId) { + return "==================================================" + System.lineSeparator() + + "Группа: " + groupId + System.lineSeparator() + + "=================================================="; + } +} From 553a963420c706ee86e236f76e1adc3269330bc2 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Thu, 21 Jul 2022 23:36:08 +0400 Subject: [PATCH 44/50] added delete user chat and moderator chat, added new command "/currentSettings" for administrators --- .../tgbotusers/bot/enums/BotCommand.java | 5 +- .../bot/handler/AdminMessageHandler.java | 254 ++++++++++++------ .../bot/keyboard/AdminKeyboard.java | 1 + .../model/service/ChatUserService.java | 4 + 4 files changed, 183 insertions(+), 81 deletions(-) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java index d3f8d22..076b709 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java @@ -21,15 +21,18 @@ public enum BotCommand { BIND_USER_CHAT_TO_MODER("/bindUserChatToModer", "T {id} {id}"), UNBIND_USER_CHAT_FROM_MODER("/unbindUserChatFromModer", "T {id} {id}"), - HELP("/help", "T"); + HELP("/help", "T"), + CURRENT_SETTINGS("/currentSettings", "T"); private static final String ID_REGEX = "(\\-?\\d*{18})"; private final Pattern regex; private final String text; + private final String params; private final String help; BotCommand(String text, String regex) { this.text = text; + this.params = regex.replace("T", ""); this.help = regex.replace("T", text); this.regex = Pattern.compile("^" + regex .replace("T", text) diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java index 8a4e563..5f5f6d5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java @@ -2,6 +2,7 @@ import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.UpdateUtils; @@ -17,175 +18,268 @@ import org.springframework.stereotype.Component; import java.util.Arrays; +import java.util.List; +import java.util.Optional; import java.util.regex.Matcher; -import java.util.stream.Collectors; @Component @RequiredArgsConstructor public class AdminMessageHandler extends Handler { - + + private static final String OK_ADD_MODER = "OK add moder chat № %s "; + private static final String OK_ADD_USER = "OK add user chat № %s"; + private static final String OK_BIND = "OK bind user chat № %s and moder chat № %s "; + private static final String OK_UNBIND = "OK unbind user chat № %s and moder chat № %s "; + private static final String OK_DEL_MODER = "OK delete moder chat № %s "; + private static final String OK_DEL_USER = "OK delete user chat № %s "; + + private static final String MODER_EXISTS = "Moder chat with id = %s exists"; + private static final String USER_EXISTS = "User chat with id = %s exists"; + private static final String MODER_NOT_FOUND = "Moder chat with id = %s not found"; + private static final String USER_NOT_FOUND = "User chat with id = %s not found"; + + private static final String FAIL_ADD_MODER = "FAIL add moder chat № %s "; + private static final String FAIL_ADD_USER = "FAIL add user chat № %s "; + private static final String ALREADY_BIND = "ALREADY bind user chat № %s and moder chat № %s "; + private static final String ALREADY_UNBIND = "ALREADY unbind user chat № %s and moder chat № %s "; + private static final String FAIL_DEL_MODER = "FAIL delete moder chat № %s "; + private static final String FAIL_DEL_USER = "FAIL delete user chat № %s "; + + private final TelegramService telegramService; private final ChatSettings chatSettings; - + private final ChatModeratorService chatModeratorService; private final ChatUserService chatUserService; - - //список команд - //todo надо потом сделать привествие бота,что бы он сразу присал что если что есть help -// private static final String HELP = "/help"; -// private static final String ADD_MODER_CHAT = "/addModerChat"; -// private static final String ADD_USER_CHAT = "/addUserChat"; -// private static final String BIND_USER_CHAT_TO_MODER = "/bindUserChatToModer"; -// private static final String UNBIND_USER_CHAT_TO_MODER = "/unbindUserChatToModer"; - - private static final String OK_BIND = "Cool, user chat № %s , is add in moder chat № %s"; - private static final String NOT_BIND = "Oh, user chat № %s , is already in moder chat № %s"; - private static final String NOT_UNBIND = "Oh, user chat № %s , is not bind in moder chat № %s"; - private static final String OK_UNBIND = "Cool, user chat № %s , is unbind from moder chat № %s"; - private static final String OK_MODER = "Cool, moder chat № %s , is add in DB"; - private static final String OK_USER = "Cool, user chat № %s , is add in DB"; - private static final String NOT_MODER = "Oh, moder chat № %s ,is already in the database"; - private static final String NOT_USER = "Oh, user chat № %s ,is already in the database"; - private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; - private static final String UNKNOW_COMMAND = "Sorry, bot does not know this \"%s\" command"; - //todo вынести команды бота в бот сервис/в бот команд -// List listCommandAdd = Arrays.asList(HELP, ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); -// List listCommandDelete = Arrays.asList(ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); - + + @Override protected boolean handle(Update update) { User user = UpdateUtils.getUser(update); if (user == null || !chatSettings.isAdminId(user.id()) || !UpdateUtils.isPrivateMessage(update)) { return false; } - + String text = UpdateUtils.getMessageText(update); Long chatId = UpdateUtils.getChat(update).id(); - - // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки - // TODO сохраняем в бд группы с + а нужно с - - + AdminKeyboard command = KeyboardUtils.defineKey(AdminKeyboard.class, text).orElse(null); if (command == null) { return false; } return switch (command) { case HELP -> showHelp(chatId); - case ADD_MODER_CHAT -> addModerChat(update, chatId, text); - case ADD_USER_CHAT -> addUserChat(update, chatId, text); - case BIND_USER_CHAT_TO_MODER -> bindUserChatToModer(update, chatId, text); + case CURRENT_SETTINGS -> showCurrentSettings(chatId); + case ADD_MODER_CHAT -> addModerChat(chatId, text); + case ADD_USER_CHAT -> addUserChat(chatId, text); + case BIND_USER_CHAT_TO_MODER -> bindUserChatToModer(chatId, text); + case UNBIND_USER_CHAT_FROM_MODER -> unbindUserChatFromModer(chatId, text); + case DELETE_USER_CHAT -> deleteUserChat(chatId, text); + case DELETE_MODER_CHAT -> deleteModerChat(chatId, text); default -> false; }; } - + private boolean showHelp(Long chatId) { - String text = "Список доступных команд: " + System.lineSeparator(); - text += Arrays.stream(AdminKeyboard.values()) - .map(command -> command.getBotCommand().getHelp()) - .collect(Collectors.joining(System.lineSeparator())); - telegramService.sendMessage(new SendMessage(chatId, text)); + StringBuilder builder = new StringBuilder("Список доступных команд: ") + .append(System.lineSeparator()); + Arrays.stream(AdminKeyboard.values()) + .forEach(command -> { + String text = command.getBotCommand().getText(); + String params = command.getBotCommand().getParams(); + builder.append("").append(text).append("").append(params) + .append(System.lineSeparator()); + }); + telegramService.sendMessage(new SendMessage(chatId, builder.toString()).parseMode(ParseMode.HTML)); return true; } - - private boolean addModerChat(Update update, Long chatId, String text) { + + private boolean showCurrentSettings(Long chatId) { + StringBuilder settings = new StringBuilder("Текущие настройки групп:") + .append(System.lineSeparator()); + + List chatModerators = chatModeratorService.findAll(); + List chatUsers = chatUserService.findAll(); + + chatModerators.forEach(moderChat -> { + settings.append(" ").append(moderChat.getChatId()).append(':').append(System.lineSeparator()); + moderChat.getChatUsers().forEach(userChat -> { + settings.append(" ").append(userChat.getChatId()).append(System.lineSeparator()); + chatUsers.remove(userChat); + }); + }); + + if (!chatUsers.isEmpty()) { + settings.append("Не связанные группы:").append(System.lineSeparator()); + chatUsers.forEach(chatUser -> { + settings.append(" ").append(chatUser.getChatId()).append(System.lineSeparator()); + }); + } + + telegramService.sendMessage(new SendMessage(chatId, settings.toString())); + return true; + } + + + private boolean addModerChat(Long chatId, String text) { Matcher matcher = BotCommand.ADD_MODER_CHAT.getRegex().matcher(text); if (!matcher.matches()) { return false; } - + long moderatorGroupId = Long.parseLong(matcher.group(1)); - ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); - if (chatModerator != null) { - telegramService.sendMessage(new SendMessage(chatId, "Такой модератор уже есть")); + Optional chatModeratorOptional = chatModeratorService.findByChatId(moderatorGroupId); + if (chatModeratorOptional.isPresent()) { + telegramService.sendMessage(new SendMessage(chatId, String.format(MODER_EXISTS, moderatorGroupId))); return true; } - + + Optional chatUserOptional = chatUserService.findByChatId(moderatorGroupId); + if (chatUserOptional.isPresent()) { + telegramService.sendMessage(new SendMessage(chatId, String.format(USER_EXISTS, moderatorGroupId))); + return true; + } + ChatModerator newEntity = new ChatModerator(); newEntity.setChatId(moderatorGroupId); chatModeratorService.save(newEntity); + telegramService.sendMessage(new SendMessage(chatId, String.format(OK_ADD_MODER, moderatorGroupId))); return true; } - - private boolean addUserChat(Update update, Long chatId, String text) { + + private boolean addUserChat(Long chatId, String text) { Matcher matcher = BotCommand.ADD_USER_CHAT.getRegex().matcher(text); if (!matcher.matches()) { return false; } - + long userGroupId = Long.parseLong(matcher.group(1)); - ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); - if (chatUser != null) { - telegramService.sendMessage(new SendMessage(chatId, "Такая группа уже есть")); + Optional chatUserOptional = chatUserService.findByChatId(userGroupId); + if (chatUserOptional.isPresent()) { + telegramService.sendMessage(new SendMessage(chatId, String.format(USER_EXISTS, userGroupId))); return true; } - + + Optional chatModeratorOptional = chatModeratorService.findByChatId(userGroupId); + if (chatModeratorOptional.isPresent()) { + telegramService.sendMessage(new SendMessage(chatId, String.format(MODER_EXISTS, userGroupId))); + return true; + } + ChatUser newEntity = new ChatUser(); newEntity.setChatId(userGroupId); chatUserService.save(newEntity); + telegramService.sendMessage(new SendMessage(chatId, String.format(OK_ADD_USER, userGroupId))); return true; } - - private boolean bindUserChatToModer(Update update, Long chatId, String text) { + + private boolean bindUserChatToModer(Long chatId, String text) { Matcher matcher = BotCommand.BIND_USER_CHAT_TO_MODER.getRegex().matcher(text); if (!matcher.matches()) { return false; } - + long userGroupId = Long.parseLong(matcher.group(1)); ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); if (chatUser == null) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_USER, userGroupId))); + telegramService.sendMessage(new SendMessage(chatId, String.format(USER_NOT_FOUND, userGroupId))); return true; } - + long moderatorGroupId = Long.parseLong(matcher.group(2)); ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); if (chatModerator == null) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, moderatorGroupId))); + telegramService.sendMessage(new SendMessage(chatId, String.format(MODER_NOT_FOUND, moderatorGroupId))); return true; } - + if (chatModerator.getChatUsers().contains(chatUser)) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_BIND, userGroupId, moderatorGroupId))); + telegramService.sendMessage(new SendMessage(chatId, String.format(ALREADY_BIND, userGroupId, moderatorGroupId))); return true; } - + chatModerator.getChatUsers().add(chatUser); chatModeratorService.save(chatModerator); telegramService.sendMessage(new SendMessage(chatId, String.format(OK_BIND, userGroupId, moderatorGroupId))); return true; } - - private boolean deleteUserChat(Update update, Long chatId, String text) { + + + private boolean unbindUserChatFromModer(Long chatId, String text) { + Matcher matcher = BotCommand.UNBIND_USER_CHAT_FROM_MODER.getRegex().matcher(text); + if (!matcher.matches()) { + return false; + } + + long userGroupId = Long.parseLong(matcher.group(1)); + ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); + if (chatUser == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(USER_NOT_FOUND, userGroupId))); + return true; + } + + long moderatorGroupId = Long.parseLong(matcher.group(2)); + ChatModerator chatModerator = chatModeratorService.findByChatId(moderatorGroupId).orElse(null); + if (chatModerator == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(MODER_NOT_FOUND, moderatorGroupId))); + return true; + } + + if (!chatModerator.getChatUsers().contains(chatUser)) { + telegramService.sendMessage(new SendMessage(chatId, String.format(ALREADY_UNBIND, userGroupId, moderatorGroupId))); + return true; + } + + chatModerator.getChatUsers().remove(chatUser); + chatModeratorService.save(chatModerator); + telegramService.sendMessage(new SendMessage(chatId, String.format(OK_UNBIND, userGroupId, moderatorGroupId))); + return true; + } + + private boolean deleteUserChat(Long chatId, String text) { Matcher matcher = BotCommand.DELETE_USER_CHAT.getRegex().matcher(text); if (!matcher.matches()) { return false; } - + long userGroupId = Long.parseLong(matcher.group(1)); ChatUser chatUser = chatUserService.findByChatId(userGroupId).orElse(null); if (chatUser == null) { - telegramService.sendMessage(new SendMessage(chatId, String.format(NOT_MODER, userGroupId))); + telegramService.sendMessage(new SendMessage(chatId, String.format(USER_NOT_FOUND, userGroupId))); return true; } - + if (!chatUser.getChatModerators().isEmpty()) { - telegramService.sendMessage(new SendMessage(chatId, "Есть связные сущности у группы " + userGroupId)); + telegramService.sendMessage(new SendMessage(chatId, String.format(FAIL_DEL_USER, userGroupId))); return true; } - + chatUserService.deleteById(chatUser.getId()); - telegramService.sendMessage(new SendMessage(chatId, "Удалили группу юзеров № " + userGroupId)); + telegramService.sendMessage(new SendMessage(chatId, String.format(OK_DEL_USER, userGroupId))); return true; } - - private boolean deleteModerChat() { - - return false; - } - - private boolean deleteUserChatFromModerChat() { - - return false; + + private boolean deleteModerChat(Long chatId, String text) { + Matcher matcher = BotCommand.DELETE_MODER_CHAT.getRegex().matcher(text); + if (!matcher.matches()) { + return false; + } + + long moderGroupId = Long.parseLong(matcher.group(1)); + ChatModerator chatModerator = chatModeratorService.findByChatId(moderGroupId).orElse(null); + if (chatModerator == null) { + telegramService.sendMessage(new SendMessage(chatId, String.format(USER_NOT_FOUND, moderGroupId))); + return true; + } + + if (!chatModerator.getChatUsers().isEmpty()) { + telegramService.sendMessage(new SendMessage(chatId, String.format(FAIL_DEL_MODER, moderGroupId))); + return true; + } + + chatModeratorService.deleteById(chatModerator.getId()); + telegramService.sendMessage(new SendMessage(chatId, String.format(OK_DEL_MODER, moderGroupId))); + return true; } + } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java index 1ecb3d2..f837344 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/keyboard/AdminKeyboard.java @@ -7,6 +7,7 @@ public enum AdminKeyboard implements Keyboard { HELP(BotCommand.HELP, "Помощь"), + CURRENT_SETTINGS(BotCommand.CURRENT_SETTINGS, "Текущие настройки"), ADD_MODER_CHAT(BotCommand.ADD_MODER_CHAT, "Добавить чат модераторов"), ADD_USER_CHAT(BotCommand.ADD_USER_CHAT, "Добавить чат пользователей"), diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index a436304..ed69cb6 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -32,6 +32,10 @@ public ChatUser save(ChatUser entity) { return chatUserRepository.save(entity); } + public List findAll() { + return (List) chatUserRepository.findAll(); + } + public Optional findByChatId(long chatId) { return chatUserRepository.findByChatId(chatId); } From 284087d502db8fabb1aea607037a1653cbf3123b Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Fri, 22 Jul 2022 00:31:24 +0400 Subject: [PATCH 45/50] + luckyList + chooseLucky --- README.md | 27 +++++--- application-bot.yml | 36 +++++++++++ .../tgbotusers/TgBotUsersApplication.java | 4 +- .../tgbotusers/bot/enums/BotCommand.java | 1 + .../bot/handler/AdminMessageHandler.java | 20 ------ .../bot/handler/CallbackQueryHandler.java | 6 +- .../bot/handler/ChatJoinRequestHandler.java | 60 ------------------ ...dHandler.java => LuckyCommandHandler.java} | 63 +++++++++++++++---- .../tgbotusers/bot/service/BotService.java | 12 +--- .../bot/service/NotificationService.java | 43 ++++++++----- .../bot/service/TelegramService.java | 2 +- .../bot/service/TemplateEngine.java | 4 +- .../tgbotusers/config/AppListener.java | 21 +++++++ .../tgbotusers/controller/BotController.java | 6 +- .../tgbotusers/model/entity/ChatUser.java | 3 - .../tgbotusers/model/entity/UserJoining.java | 2 +- .../model/entity/UserJoiningNotification.java | 3 + .../repository/ChatModeratorRepository.java | 7 +-- .../model/repository/ChatUserRepository.java | 7 --- .../UserJoiningNotificationRepository.java | 4 +- .../repository/UserJoiningRepository.java | 10 +++ .../model/service/ChatModeratorService.java | 12 ++-- .../model/service/ChatUserService.java | 14 +---- .../UserJoiningNotificationService.java | 8 +-- .../model/service/UserJoiningService.java | 8 +-- src/main/resources/application-bot.yml | 39 ------------ src/main/resources/application.yml | 12 ++-- .../TgBotUsersApplicationTests.java | 2 + src/test/resources/application-test.yml | 30 +++++++++ 29 files changed, 232 insertions(+), 234 deletions(-) create mode 100644 application-bot.yml delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java rename src/main/java/org/codewithoutus/tgbotusers/bot/handler/{LuckyListCommandHandler.java => LuckyCommandHandler.java} (62%) create mode 100644 src/main/java/org/codewithoutus/tgbotusers/config/AppListener.java delete mode 100644 src/main/resources/application-bot.yml create mode 100644 src/test/resources/application-test.yml diff --git a/README.md b/README.md index 285f1c4..917790b 100644 --- a/README.md +++ b/README.md @@ -40,24 +40,37 @@ _Доступны администраторам в приватном чате_ ### Команды модераторов _Доступны в чатах модераторов_ -| Команда | Описание | -|-----------------|---------------------------------------------------------------------------| -| /luckyList | вывод списка юбилейных вступлений во всех привязанных чатах пользователей | -| /luckyList {id} | вывод списка юбилейных вступлений в конкретном чате | +| Команда | Описание | +|---------------------|---------------------------------------------------------------------------------| +| /luckyList | вывод списка юбилейных вступлений во всех привязанных чатах пользователей | +| /luckyList {id} | вывод списка юбилейных вступлений в конкретном чате | +| /chooseLucky | вывод списка **ожидающих поздравления** во всех привязанных чатах пользователей | +| /chooseLucky {id} | вывод списка **ожидающих поздравления** в конкретном чате | + + +--- +## Web API +На всякий случай для бота сделан небольшой API.
По умолчанию используется порт 8090. + +| Endpoint | Описание | +|------------------|-------------------------------------------------------------------------------------------------| +| /api/start | Ручной старт бота | +| /api/stop | Ручная остановка бота | +| /api/status | Текущий статус бота | +| /api/sendMessage | Отправка сообщения от бота
`chatId` ID чата, куда отправить
`message` текст сообщения | --- ## Настройки приложения Конфигурационные файлы приложения разделены на две части: -### Настройки для разработчиков: +### Настройки для разработчиков _(application.yml)_: * Spring * Database _(environment vars)_ * Bot token _(environment vars)_ - -### Настройки функций бота: +### Настройки функций бота _(application-bot.yml)_: Настройки групп в приоритете берутся из базы данных. Из файла конфигурации эти настройки подтягиваются только в случае отсутствия таковых в БД, либо если включен флаг перезаписи настроек. * Настройки для чатов `chats-settings`: diff --git a/application-bot.yml b/application-bot.yml new file mode 100644 index 0000000..a7723b7 --- /dev/null +++ b/application-bot.yml @@ -0,0 +1,36 @@ +chats-settings: + administrators: + - 1039061325 # pavel + - 161855902 # max + - 1004758635 # alex + anniversary-numbers-delta: 3 + anniversary-numbers: + - 5 + - 50 + - 100 + - 500 + - 777 + - 1000 + - 5000 + - 10000 + chats-settings: + -1001523814996: #Хакатон и телеграм бот (Skillbox) + - -1001523814996 # Хакатон и телеграм бот (Skillbox) + - -1001781082670 # Java-диплом с нуля (Skillbox) + + rewrite-chats-settings-in-database-on-startup: false + +message-templates: + variables: + chat-name: "{НазваниеГруппы}" + user-name: "{ИмяУчастника}" + user-nickname: "{НикУчастника}" + join-date: "{ВремяВступления}" + join-number: "{НомерВступления}" + plugs: + no-nick: "ника нет" + winner-crown: "👑👑👑" + join-date-format: "dd.MM.yy HH:mm" + join-congratulation: "🎉 Поздравляю, *{ИмяУчастника}*, как же удачно попали в нужное время и в нужное время!\nВы *{НомерВступления}* участник коммьюнити.\nВас ждут плюшки и печенюшки!🎉" + join-alert: " 🎉 В *{НазваниеГруппы}* группу вступил юбилейный пользователь\n{ИмяУчастника} (*{НикУчастника}*),\n 🔢 *{НомерВступления}* 🕐 {ВремяВступления}" + join-user-info: " 👤{ИмяУчастника} ({НикУчастника}),\n 🔢 *{НомерВступления}* 🕐 {ВремяВступления}" diff --git a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java index 631b092..dace981 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java +++ b/src/main/java/org/codewithoutus/tgbotusers/TgBotUsersApplication.java @@ -2,11 +2,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class TgBotUsersApplication { public static void main(String[] args) { - SpringApplication.run(TgBotUsersApplication.class, args); + ConfigurableApplicationContext context = SpringApplication.run(TgBotUsersApplication.class, args); + context.start(); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java index c77e86e..9a0702c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/enums/BotCommand.java @@ -12,6 +12,7 @@ public enum BotCommand { DECLINE("/decline", "T"), LUCKY_LIST("/luckyList", "T( {id}})?"), + CHOOSE_LUCKY("/chooseLucky", "T( {id})?"), ADD_MODER_CHAT("/addModerChat", "T {id}"), ADD_USER_CHAT("/addUserChat", "T {id}"), diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java index 8a4e563..5d5e19a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java @@ -30,27 +30,10 @@ public class AdminMessageHandler extends Handler { private final ChatModeratorService chatModeratorService; private final ChatUserService chatUserService; - //список команд - //todo надо потом сделать привествие бота,что бы он сразу присал что если что есть help -// private static final String HELP = "/help"; -// private static final String ADD_MODER_CHAT = "/addModerChat"; -// private static final String ADD_USER_CHAT = "/addUserChat"; -// private static final String BIND_USER_CHAT_TO_MODER = "/bindUserChatToModer"; -// private static final String UNBIND_USER_CHAT_TO_MODER = "/unbindUserChatToModer"; - private static final String OK_BIND = "Cool, user chat № %s , is add in moder chat № %s"; private static final String NOT_BIND = "Oh, user chat № %s , is already in moder chat № %s"; - private static final String NOT_UNBIND = "Oh, user chat № %s , is not bind in moder chat № %s"; - private static final String OK_UNBIND = "Cool, user chat № %s , is unbind from moder chat № %s"; - private static final String OK_MODER = "Cool, moder chat № %s , is add in DB"; - private static final String OK_USER = "Cool, user chat № %s , is add in DB"; private static final String NOT_MODER = "Oh, moder chat № %s ,is already in the database"; private static final String NOT_USER = "Oh, user chat № %s ,is already in the database"; - private static final String ERROR = "Sorry, \" %s\" ,is invalid format ID"; - private static final String UNKNOW_COMMAND = "Sorry, bot does not know this \"%s\" command"; - //todo вынести команды бота в бот сервис/в бот команд -// List listCommandAdd = Arrays.asList(HELP, ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); -// List listCommandDelete = Arrays.asList(ADD_MODER_CHAT, ADD_USER_CHAT, BIND_USER_CHAT_TO_MODER); @Override protected boolean handle(Update update) { @@ -62,9 +45,6 @@ protected boolean handle(Update update) { String text = UpdateUtils.getMessageText(update); Long chatId = UpdateUtils.getChat(update).id(); - // TODO: Макс -- вначале строковыми командами, а потом можно попробовать кнопки - // TODO сохраняем в бд группы с + а нужно с - - AdminKeyboard command = KeyboardUtils.defineKey(AdminKeyboard.class, text).orElse(null); if (command == null) { return false; diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index 6c83e6c..f04f0a0 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -25,7 +25,6 @@ public class CallbackQueryHandler extends Handler { private final NotificationService notificationService; private final UserJoiningService userJoiningService; - private final ChatModeratorService chatModeratorService; @Override protected boolean handle(Update update) { @@ -36,14 +35,13 @@ protected boolean handle(Update update) { } // есть ли команда в callbackQuery - String command = (String) callbackQueryData.get("command"); + String command = callbackQueryData.get("command"); if (command == null || command.isBlank()) { return false; } if (handleCongratulationDecision(command, callbackQueryData)) { return true; - } else { log.error("Unhandled command: {}", callbackQueryData); throw new CommandNotFoundException("Unhandled command: " + callbackQueryData); @@ -78,7 +76,7 @@ private boolean handleCongratulationDecision(String command, Map ? CongratulateStatus.CONGRATULATE : CongratulateStatus.DECLINE; userJoining.setStatus(newStatus); -// userJoiningService.save(userJoining); // TODO: Алекс -- check, that entity was updated without save() method + userJoiningService.save(userJoining); if (decision == CongratulationDecisionKeyboard.CONGRATULATE) { notificationService.deleteKeyboardFromAllJoiningNotifications(chatId, anniversaryNumber); diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java deleted file mode 100644 index 67239b2..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChatJoinRequestHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.handler; - -import com.pengrad.telegrambot.model.ChatJoinRequest; -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.model.User; -import lombok.RequiredArgsConstructor; -import org.codewithoutus.tgbotusers.bot.service.NotificationService; -import org.codewithoutus.tgbotusers.bot.service.TelegramService; -import org.codewithoutus.tgbotusers.config.ChatSettings; -import org.codewithoutus.tgbotusers.model.entity.UserJoining; -import org.codewithoutus.tgbotusers.model.service.ChatUserService; -import org.codewithoutus.tgbotusers.model.service.UserJoiningService; -import org.springframework.stereotype.Component; - -import java.time.Instant; -import java.time.LocalDateTime; - -@Component -@RequiredArgsConstructor -public class ChatJoinRequestHandler extends Handler { - - private final ChatSettings chatSettings; - private final ChatUserService chatUserService; - private final TelegramService telegramService; - private final NotificationService notificationService; - private final UserJoiningService userJoiningService; - - @Override - protected boolean handle(Update update) { - ChatJoinRequest chatJoinRequest = update.chatJoinRequest(); - if (chatJoinRequest == null) { - return false; - } - - // TODO: Pavel (подумать) -- если присоединяется сразу несколько в чат (на будущее) - // TODO: Pavel (подумать) -- если присоединяющийся пользователь бот и юбилейный (на далёкое будущее) - // TODO: Pavel (подумать) -- юбилейные номера могут быть разные для каждого чата (на далёкое будущее) - - User user = chatJoinRequest.from(); - long chatId = chatJoinRequest.chat().id(); - int joinNumber = telegramService.getChatMembersCount(chatId); - int anniversaryNumber = chatSettings.getAnniversaryJoinNumber(chatId, joinNumber); - if (anniversaryNumber == 0 || !chatUserService.isChatUser(chatId)) { - return false; - } - - UserJoining userJoining = new UserJoining(); - userJoining.setChatId(chatId); - userJoining.setUserId(user.id()); - userJoining.setNumber(joinNumber); - userJoining.setJoinTime(LocalDateTime.from(Instant.ofEpochSecond(chatJoinRequest.date()))); - userJoining = userJoiningService.save(userJoining); - - // если не было поздравненных в чате с таким порядковым номером - if (!userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { - notificationService.notifyModeratorsAboutUserJoining(userJoining); - } - return true; - } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyCommandHandler.java similarity index 62% rename from src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java rename to src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyCommandHandler.java index e8c1de6..aec0036 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyListCommandHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/LuckyCommandHandler.java @@ -1,17 +1,23 @@ package org.codewithoutus.tgbotusers.bot.handler; import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.request.SendMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.codewithoutus.tgbotusers.bot.UpdateUtils; import org.codewithoutus.tgbotusers.bot.enums.BotCommand; +import org.codewithoutus.tgbotusers.bot.keyboard.CongratulationDecisionKeyboard; +import org.codewithoutus.tgbotusers.bot.keyboard.KeyboardUtils; +import org.codewithoutus.tgbotusers.bot.service.NotificationService; import org.codewithoutus.tgbotusers.bot.service.TelegramService; import org.codewithoutus.tgbotusers.bot.service.TemplateEngine; import org.codewithoutus.tgbotusers.config.ChatSettings; import org.codewithoutus.tgbotusers.config.NotificationTemplates; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; +import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; import org.codewithoutus.tgbotusers.model.service.ChatUserService; import org.codewithoutus.tgbotusers.model.service.UserJoiningService; @@ -25,10 +31,11 @@ @Component @RequiredArgsConstructor @Slf4j -public class LuckyListCommandHandler extends Handler { +public class LuckyCommandHandler extends Handler { private final ChatSettings chatSettings; private final NotificationTemplates notificationTemplates; + private final NotificationService notificationService; private final TelegramService telegramService; private final TemplateEngine templateEngine; @@ -38,15 +45,8 @@ public class LuckyListCommandHandler extends Handler { @Override protected boolean handle(Update update) { - // TODO: Алекс -- реализовать вывод списка счастивчиков "/списокЮбилейный" или "/luckyList" - - // TODO: Алекс -- если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard - // TODO: Алекс -- если поздравленного нет, то отклоненные тоже выводятся - // TODO: Алекс -- все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись - // TODO: Алекс -- можно попробовать выводить все-все чаты, а потом доработать -- на будущее - BotCommand command = UpdateUtils.getBotCommand(update); - if (command != BotCommand.LUCKY_LIST) { + if (command != BotCommand.LUCKY_LIST && command != BotCommand.CHOOSE_LUCKY) { return false; } @@ -63,15 +63,47 @@ protected boolean handle(Update update) { return false; } + switch (command) { + case LUCKY_LIST -> handleLuckyList(matcher, moderatorChatId); + case CHOOSE_LUCKY -> handleChooseLucky(matcher, moderatorChatId); + } + return true; + } + + private void handleChooseLucky(Matcher matcher, Long moderatorChatId) { + String chatId = matcher.group(2); + List luckyChatsIds = getLuckyChats(chatId, moderatorChatId) + .stream() + .map(ChatUser::getChatId) + .toList(); + + List luckyOnes = userJoiningService.findNotCongratulatedByChatIds(luckyChatsIds); + if (luckyOnes.isEmpty()) { + telegramService.sendMessage(new SendMessage(moderatorChatId, "Пока ещё нет счастливчиков")); + return; + } + + for (UserJoining userJoining : luckyOnes) { + InlineKeyboardMarkup keyboard = KeyboardUtils + .createKeyboard(CongratulationDecisionKeyboard.class, String.valueOf(userJoining.getId())); + + String notificationText = templateEngine + .buildFromTemplate(notificationTemplates.getJoinUserInfo(), userJoining, false); + + notificationService.sendModeratorNotification(moderatorChatId, userJoining, notificationText, keyboard); + } + } + + private void handleLuckyList(Matcher matcher, Long moderatorChatId) { String chatId = matcher.group(2); List luckyChats = getLuckyChats(chatId, moderatorChatId); if (luckyChats.isEmpty()) { telegramService.sendMessage(new SendMessage(moderatorChatId, "Нет данных о чатах пользователей")); - return true; + return; } - telegramService.sendMessage(new SendMessage(moderatorChatId, createLuckyListText(luckyChats))); - return true; + telegramService.sendMessage(new SendMessage(moderatorChatId, createLuckyListText(luckyChats)) + .parseMode(ParseMode.Markdown)); } private List getLuckyChats(String chatId, Long moderatorChatId) { @@ -101,10 +133,15 @@ private String createLuckyListText(List luckyChats) { .forEach((groupId, joiningList) -> { resultBuilder .append("==================================================").append(System.lineSeparator()) - .append("Группа: ").append(groupId).append(System.lineSeparator()); + .append("Группа: *") + .append(telegramService.getChat(Long.parseLong(groupId)).title()) + .append("*") + .append(System.lineSeparator()) + .append("==================================================").append(System.lineSeparator()); joiningList.forEach(joining -> { resultBuilder .append(templateEngine.buildFromTemplate(template, joining, true)) + .append(System.lineSeparator()) .append(System.lineSeparator()); }); resultBuilder.append(System.lineSeparator()); diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java index d5fcb11..7c54265 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/BotService.java @@ -10,8 +10,6 @@ import org.codewithoutus.tgbotusers.bot.handler.*; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; - @Service @RequiredArgsConstructor @Slf4j @@ -21,14 +19,8 @@ public class BotService { private final AdminMessageHandler adminMessageHandler; private final CallbackQueryHandler callbackQueryHandler; private final ChatJoinMessageHandler chatJoinMessageHandler; - private final ChatJoinRequestHandler chatJoinRequestHandler; - private final LuckyListCommandHandler luckyListCommandHandler; + private final LuckyCommandHandler luckyListCommandHandler; private final PrivateMessageHandler privateMessageHandler; - - @PostConstruct - private void botStart() { - start(); - } public boolean start() { if (bot.getStatus() == BotStatus.START) { @@ -72,10 +64,8 @@ private void handleUpdate(Update update) { if (adminMessageHandler.tryHandle(update) || privateMessageHandler.tryHandle(update) || chatJoinMessageHandler.tryHandle(update) - || chatJoinRequestHandler.tryHandle(update) || callbackQueryHandler.tryHandle(update) || luckyListCommandHandler.tryHandle(update)) { - // update handled, return } } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java index 4621b1d..dbc5d45 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/NotificationService.java @@ -1,6 +1,7 @@ package org.codewithoutus.tgbotusers.bot.service; import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.response.SendResponse; import lombok.RequiredArgsConstructor; @@ -45,29 +46,37 @@ public void notifyModeratorsAboutUserJoining(UserJoining userJoining) { .buildFromTemplate(notificationTemplates.getJoinAlert(), userJoining, false); for (ChatModerator moderatorChat : moderatorChats) { - SendMessage message = new SendMessage(moderatorChat.getChatId(), notificationText).replyMarkup(keyboard); - SendResponse response = telegramService.sendMessage(message); - log.debug("ModerChat(id={}) notified about user(id={}) joining. Status={}", - moderatorChat.getChatId(), userJoining.getUserId(), response.isOk()); - - UserJoiningNotification notification = new UserJoiningNotification(); - notification.setUserJoining(userJoining); - notification.setSentMessageChatId(moderatorChat.getChatId()); - notification.setSentMessageId(response.message().messageId()); - userJoiningNotificationService.save(notification); - log.debug("Saved to DB notifying ModerChat(id={}) about user(id={}) joining", - moderatorChat.getChatId(), userJoining.getUserId()); - } + sendModeratorNotification(moderatorChat.getChatId(), userJoining, notificationText, keyboard); + } log.debug("Finish notifying about user(id={}) joining", userJoining.getUserId()); } + @Transactional + public void sendModeratorNotification(Long moderatorChatId, UserJoining userJoining, String notificationText, InlineKeyboardMarkup keyboard) { + SendMessage message = new SendMessage(moderatorChatId, notificationText) + .replyMarkup(keyboard) + .parseMode(ParseMode.Markdown); + SendResponse response = telegramService.sendMessage(message); + log.debug("ModerChat(id={}) notified about user(id={}) joining. Status={}", + moderatorChatId, userJoining.getUserId(), response.isOk()); + + UserJoiningNotification notification = new UserJoiningNotification(); + notification.setUserJoining(userJoining); + notification.setSentMessageChatId(response.message().chat().id()); + notification.setSentMessageId(response.message().messageId()); + notification.setHasKeyboard(true); + userJoiningNotificationService.save(notification); + log.debug("Saved to DB notifying ModerChat(id={}) about user(id={}) joining", + moderatorChatId, userJoining.getUserId()); + } + @Transactional public void notifyUserAboutAnniversaryJoining(UserJoining userJoining) { log.debug("Start congratulate user(id={}, number={}) joining", userJoining.getUserId(), userJoining.getNumber()); String notificationText = templateEngine .buildFromTemplate(notificationTemplates.getJoinCongratulation(), userJoining, false); - SendMessage message = new SendMessage(userJoining.getChatId(), notificationText); + SendMessage message = new SendMessage(userJoining.getChatId(), notificationText).parseMode(ParseMode.Markdown); SendResponse response = telegramService.sendMessage(message); log.debug("Finish congratulate user(id={}). Status={}", userJoining.getUserId(), response.isOk()); } @@ -76,7 +85,7 @@ public void notifyUserAboutAnniversaryJoining(UserJoining userJoining) { public void deleteKeyboardFromJoiningNotification(Long userId, Long chatId, int anniversaryNumber) { log.debug("Start keyboard deleting about user(id={}, number={}) joining", userId, anniversaryNumber); userJoiningNotificationService - .findByUserIdAndChatIdAndAnniversaryNumber(userId, chatId, anniversaryNumber) + .findByChatIdAndUserIdAndKeyboardStatus(chatId, userId, true) .forEach(this::deleteKeyboard); log.debug("Finish keyboard deleting about user(id={}, number={}) joining", userId, anniversaryNumber); } @@ -85,12 +94,14 @@ public void deleteKeyboardFromJoiningNotification(Long userId, Long chatId, int public void deleteKeyboardFromAllJoiningNotifications(Long chatId, int anniversaryNumber) { log.debug("Start keyboard deleting about chat(id={}) {} joining", chatId, anniversaryNumber); userJoiningNotificationService - .findByChatIdAndAnniversaryNumber(chatId, anniversaryNumber) + .findByChatIdAndAnniversaryNumberAndKeyboardStatus(chatId, anniversaryNumber, true) .forEach(this::deleteKeyboard); log.debug("Finish keyboard deleting about chat(id={}) {} joining", chatId, anniversaryNumber); } + @Transactional private void deleteKeyboard(UserJoiningNotification notification) { telegramService.removeKeyboardFromMessage(notification.getSentMessageChatId(), notification.getSentMessageId()); + notification.setHasKeyboard(false); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java index 2c2438c..c9a0fc3 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TelegramService.java @@ -40,7 +40,7 @@ private , R extends BaseResponse> R sendRequest(Base public SendResponse sendMessage(SendMessage message) { SendResponse response = sendRequest(message); - log.debug("Sent message text={}. Status={}", message.getParameters().get("text"), response.isOk()); + log.debug("Sent message text=''{}''. Status={}", message.getParameters().get("text"), response.isOk()); return response; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java index 1894bd3..08b4bb2 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/service/TemplateEngine.java @@ -41,12 +41,12 @@ public static class Builder { private Builder(String template, NotificationTemplates templates) { result = new StringBuilder(template); - temp = templates; + this.temp = templates; } private void replace(String target, String replacement) { for (int i = result.lastIndexOf(target); i > -1; i = result.lastIndexOf(target, i)) { - result.replace(i, i + target.length() - 1, replacement); + result.replace(i, i + target.length(), replacement); } } diff --git a/src/main/java/org/codewithoutus/tgbotusers/config/AppListener.java b/src/main/java/org/codewithoutus/tgbotusers/config/AppListener.java new file mode 100644 index 0000000..11ad9a7 --- /dev/null +++ b/src/main/java/org/codewithoutus/tgbotusers/config/AppListener.java @@ -0,0 +1,21 @@ +package org.codewithoutus.tgbotusers.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.codewithoutus.tgbotusers.bot.service.BotService; +import org.springframework.context.event.ContextStartedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class AppListener { + + private final BotService botService; + + @EventListener + public void handleContextStartedEvent(ContextStartedEvent ctxStartEvt) { + botService.start(); + } +} diff --git a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java index 94bd741..c652baf 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java +++ b/src/main/java/org/codewithoutus/tgbotusers/controller/BotController.java @@ -4,7 +4,6 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.bot.service.BotService; import org.codewithoutus.tgbotusers.bot.service.TelegramService; -import org.codewithoutus.tgbotusers.config.ChatSettings; import org.codewithoutus.tgbotusers.controller.dto.BotResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -12,11 +11,10 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/admin") +@RequestMapping("c") @RequiredArgsConstructor public class BotController { - private final ChatSettings chatSettings; private final BotService botService; private final TelegramService telegramService; @@ -40,6 +38,4 @@ private BotResponse sendMessage(@RequestParam Long chatId, @RequestParam String telegramService.sendMessage(new SendMessage(chatId, message)); return new BotResponse(true, botService.getStatus()); } - - } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index 5a4f421..057a3d8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -22,9 +22,6 @@ public class ChatUser { // TODO: Pavel -- переименовать сущно @Column(nullable = false, unique = true) private Long chatId; -// @Column(nullable = true) // TODO: Pavel - подумать nullable = true или false? -// private String name; - @ManyToMany(mappedBy = "chatUsers", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) private List chatModerators = new ArrayList<>(); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java index f4d9ea4..5cbc826 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoining.java @@ -33,7 +33,7 @@ public class UserJoining { private LocalDateTime joinTime; @Enumerated(EnumType.STRING) - @Column(nullable = false)//, columnDefinition = "varchar(15) default 'WAIT'") + @Column(nullable = false) private CongratulateStatus status; @OneToMany(mappedBy = "userJoining") diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java index 83cf17b..9b22100 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/UserJoiningNotification.java @@ -20,6 +20,9 @@ public class UserJoiningNotification { @Column(nullable = false) private Long sentMessageChatId; + @Column + private boolean hasKeyboard; + @ManyToOne private UserJoining userJoining; } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java index 7170576..98bc665 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatModeratorRepository.java @@ -10,12 +10,9 @@ @Repository public interface ChatModeratorRepository extends CrudRepository { - List findByChatUsers_ChatId(Long chatId); + OptionalfindByChatId(Long chatId); - @Override - Optional findById(Integer integer); + List findByChatUsers_ChatId(Long chatId); boolean existsByChatId(Long chatId); - - OptionalfindByChatId(Long chatId); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java index be1357a..57abd43 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java @@ -1,6 +1,5 @@ package org.codewithoutus.tgbotusers.model.repository; -import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @@ -10,16 +9,10 @@ @Repository public interface ChatUserRepository extends CrudRepository { - -// boolean existsByName(String name); Optional findByChatId(long chatId); - -// Optional findByName(String name); List findByChatModeratorsNotEmpty(); boolean existsByChatId(Long chatId); - - OptionalfindByChatId(Long chatId); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java index 6ab6c4b..ce32a3a 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningNotificationRepository.java @@ -9,7 +9,7 @@ @Repository public interface UserJoiningNotificationRepository extends CrudRepository { - List findByUserJoining_ChatIdAndUserJoining_AnniversaryNumber(Long chatId, Integer anniversaryNumber); + List findByUserJoining_ChatIdAndUserJoining_AnniversaryNumberAndHasKeyboard(Long chatId, Integer anniversaryNumber, boolean hasKeyboard); - List findByUserJoining_UserIdAndUserJoining_ChatIdAndUserJoining_AnniversaryNumber(Long userId, Long chatId, Integer anniversaryNumber); + List findByUserJoining_ChatIdAndUserJoining_UserIdAndHasKeyboard(Long chatId, Long userId, boolean hasKeyboard); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java index a92650d..40b044e 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java @@ -2,6 +2,7 @@ import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @@ -18,4 +19,13 @@ public interface UserJoiningRepository extends CrudRepository findByChatIdAndNotStatus(List chatIds, CongratulateStatus status); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java index ca2f8b9..8084410 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -2,7 +2,6 @@ import lombok.RequiredArgsConstructor; import org.codewithoutus.tgbotusers.model.entity.ChatModerator; -import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.codewithoutus.tgbotusers.model.repository.ChatModeratorRepository; import org.springframework.stereotype.Service; @@ -19,9 +18,6 @@ public class ChatModeratorService { public void deleteAll() { chatModeratorRepository.deleteAll(); } - public void deleteById(Integer id) { - chatModeratorRepository.deleteById(id); - } public ChatModerator save(ChatModerator entity) { return chatModeratorRepository.save(entity); @@ -33,6 +29,10 @@ public List findAll() { return result; } + public Optional findByChatId(Long chatId) { + return chatModeratorRepository.findByChatId(chatId); + } + public List findByChatUsersId(Long chatId) { return chatModeratorRepository.findByChatUsers_ChatId(chatId); } @@ -40,8 +40,4 @@ public List findByChatUsersId(Long chatId) { public boolean existsByChatId(Long chatId) { return chatModeratorRepository.existsByChatId(chatId); } - - public Optional findByChatId(Long chatId) { - return chatModeratorRepository.findByChatId(chatId); - } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index a436304..1a744c6 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -3,16 +3,12 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; -import org.codewithoutus.tgbotusers.model.entity.ChatModerator; import org.codewithoutus.tgbotusers.model.entity.ChatUser; import org.codewithoutus.tgbotusers.model.repository.ChatUserRepository; import org.springframework.stereotype.Service; import java.util.Optional; -import java.util.List; -import java.util.Optional; - @Getter @Setter @Service @@ -24,6 +20,7 @@ public class ChatUserService { public void deleteAll() { chatUserRepository.deleteAll(); } + public void deleteById(Integer id) { chatUserRepository.deleteById(id); } @@ -39,13 +36,4 @@ public Optional findByChatId(long chatId) { public boolean existByChatId(long chatId) { return chatUserRepository.existsByChatId(chatId); } - - public boolean isChatUser(long id) { - return chatUserRepository.findByChatModeratorsNotEmpty() - .stream() - .anyMatch(chatUser -> chatUser.getChatId().equals(id)); - } - public void delete(ChatUser entity) { - chatUserRepository.delete(entity); - } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java index b907116..f4c79d8 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningNotificationService.java @@ -17,11 +17,11 @@ public UserJoiningNotification save(UserJoiningNotification entity) { return userJoiningNotificationRepository.save(entity); } - public List findByChatIdAndAnniversaryNumber(Long chatId, Integer anniversaryNumber) { - return userJoiningNotificationRepository.findByUserJoining_ChatIdAndUserJoining_AnniversaryNumber(chatId, anniversaryNumber); + public List findByChatIdAndAnniversaryNumberAndKeyboardStatus(Long chatId, Integer anniversaryNumber, boolean hasKeyboard) { + return userJoiningNotificationRepository.findByUserJoining_ChatIdAndUserJoining_AnniversaryNumberAndHasKeyboard(chatId, anniversaryNumber, hasKeyboard); } - public List findByUserIdAndChatIdAndAnniversaryNumber(Long userId, Long chatId, Integer anniversaryNumber) { - return userJoiningNotificationRepository.findByUserJoining_UserIdAndUserJoining_ChatIdAndUserJoining_AnniversaryNumber(userId, chatId, anniversaryNumber); + public List findByChatIdAndUserIdAndKeyboardStatus(Long chatId, Long userId, boolean hasKeyboard) { + return userJoiningNotificationRepository.findByUserJoining_ChatIdAndUserJoining_UserIdAndHasKeyboard(chatId, userId, hasKeyboard); } } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java index 7af2d10..a5d6894 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/UserJoiningService.java @@ -23,14 +23,14 @@ public Optional findById(int id) { return userJoiningRepository.findById(id); } - public List findByChatId(Long chatId) { - return userJoiningRepository.findByChatId(chatId); - } - public List findByChatIds(List chatIds) { return userJoiningRepository.findDistinctByChatIdInOrderByChatIdAscNumberAsc(chatIds); } + public List findNotCongratulatedByChatIds(List chatIds) { + return userJoiningRepository.findByChatIdAndNotStatus(chatIds, CongratulateStatus.CONGRATULATE); + } + public boolean existCongratulatedUser(long chatId, int anniversaryNumber) { return userJoiningRepository.existsByChatIdAndAnniversaryNumberAndStatus(chatId, anniversaryNumber, CongratulateStatus.CONGRATULATE); } diff --git a/src/main/resources/application-bot.yml b/src/main/resources/application-bot.yml deleted file mode 100644 index 0b9ebb9..0000000 --- a/src/main/resources/application-bot.yml +++ /dev/null @@ -1,39 +0,0 @@ -chats-settings: - administrators: - - 1039061325 # pavel - - 161855902 # max - - 1004758635 # alex - anniversary-numbers-delta: 3 - anniversary-numbers: - - 5 - - 50 - - 100 - - 500 - - 777 - - 1000 - - 5000 - - 10000 - chats-settings: - -1001523814996: #Хакатон и телеграм бот (Skillbox) - - -1001523814996 # Хакатон и телеграм бот (Skillbox) - - -1001781082670 # Java-диплом с нуля (Skillbox) - 22222: - - 444 - - 555 - - 666 - rewrite-chats-settings-in-database-on-startup: true - -message-templates: - variables: - chat-name: "{НазваниеГруппы}" - user-name: "{ИмяУчастника}" - user-nickname: "{НикУчастника}" - join-date: "{ВремяВступления}" - join-number: "{НомерВступления}" - plugs: - no-nick: "ника нет" - winner-crown: "👑👑👑" - join-date-format: "HH:mm" - join-congratulation: "🎉 Поздравляю, {ИмяУчастника}, как же удачно попали в нужное время и в нужное время!%nВы {НомерВступления} участник коммьюнити.%nВас ждут плюшки и печенюшки!🎉" - join-alert: " 🎉 В {НазваниеГруппы} группу вступил юбилейный пользователь%n{ИмяУчастника} ({НикУчастника}),%n{НомерВступления}.%nВремя вступления {ВремяВступления}" - join-user-info: " 🎉 {НазваниеГруппы} 👤{ИмяУчастника} ({НикУчастника}),%n🔢 {НомерВступления} 🕐{ВремяВступления}" diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2d7e0c3..5be62fa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,17 +2,12 @@ server: port: 8090 bot-settings: - # bot-user-name: skillTestBoxBot - # bot-token: 5566542855:AAHQ8Ge8VD8lVv5sw21f1ztrslroFVo1upI bot-user-name: ${BOT_USER_NAME} bot-token: ${BOT_TOKEN} long-polling-timeout: 1000 spring: datasource: -# url: jdbc:postgresql://localhost:5432/tg-bot-users -# username: postgres -# password: testtest driver-class-name: org.postgresql.Driver url: ${DATASOURCE_URL} username: ${DATASOURCE_USER} @@ -24,7 +19,7 @@ spring: database-platform: org.hibernate.dialect.PostgreSQLDialect show_sql: true hibernate: - ddl-auto: validate + ddl-auto: update profiles: include: @@ -33,5 +28,6 @@ spring: logging: level: root: info - web: info - sql: info + org.codewithoutus.tgbotusers: debug + web: off + sql: off diff --git a/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java b/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java index c65754d..fa2407b 100644 --- a/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java +++ b/src/test/java/org/codewithoutus/tgbotusers/TgBotUsersApplicationTests.java @@ -2,8 +2,10 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; @SpringBootTest +@ActiveProfiles({"test", "bot"}) class TgBotUsersApplicationTests { @Test diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..06c1456 --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,30 @@ +#server: +# port: 8090 + +bot-settings: +# bot-user-name: GroupControlSkillboxBot +# bot-token: 5566628073:AAHyZTYOh62Fb7_zVGNmHMqzwJUQtBvGWuc + long-polling-timeout: 75000 + +spring: + datasource: + url: jdbc:postgresql://localhost:5432/tg-bot-users + username: tg-admin + password: 9en2w0oc +# driver-class-name: org.postgresql.Driver + hikari: + maximum-pool-size: 2 + + jpa: +# database-platform: org.hibernate.dialect.PostgreSQLDialect + show_sql: false + hibernate: + ddl-auto: update + + +logging: + level: + root: info + org.codewithoutus.tgbotusers: debug + web: off + sql: off From 209ec32318588cfc1c00aff5e0ddd67e0e452a47 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 22 Jul 2022 00:39:56 +0400 Subject: [PATCH 46/50] update readme and docker-compose --- .env | 4 ---- README.md | 22 +++++++++++++++++++++- application-bot.yml | 2 +- docker-compose.yml | 14 +++++++------- pom.xml | 2 +- src/main/resources/application.yml | 2 +- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/.env b/.env index c14d4c2..65c5e90 100644 --- a/.env +++ b/.env @@ -1,6 +1,2 @@ BOT_USER_NAME=GroupControlSkillboxBot BOT_TOKEN=5566628073:AAHyZTYOh62Fb7_zVGNmHMqzwJUQtBvGWuc -DATASOURCE_DB=tg-bot-users -DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5431/ -DATASOURCE_USER=tg-admin -DATASOURCE_PASS=9en2w0oc \ No newline at end of file diff --git a/README.md b/README.md index 285f1c4..f59c2c9 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ _Доступны администраторам в приватном чате_ | Команда | Описание | |------------------------------------|---------------------------------------------------------| | /help | вывод списка доступных команд | +| /currentSettings | вывод текущих настроек чатов | | /addModerChat {id} | добавление чата модераторов | | /addUserChat {id} | добавление чата пользователей | | /deleteModerChat {id} | удаление чата модераторов | @@ -50,7 +51,7 @@ _Доступны в чатах модераторов_ ## Настройки приложения Конфигурационные файлы приложения разделены на две части: -### Настройки для разработчиков: +### Настройки для администраторов: * Spring * Database _(environment vars)_ * Bot token _(environment vars)_ @@ -76,6 +77,25 @@ _Доступны в чатах модераторов_ * `join-user-info` - шаблон данных пользователя при использовании команды `/luckyList` +--- +## Запуск + Проект настроен так, чтобы администратору приходилось выполнять минимум действий, поэтому сконфигурированное приложение запускается лишь парой команд. Нет необходимости настраивать базу данных, так как она запускается в docker-контейнере рядом с контейнером приложения. И так для запуска приложения вам потребуется выполнить следующие действия: + + Все команды выполняются в терминале в корне проекта! + * Сделать клонирование репозитория командой `git clone {адрес репозитория}` + * Настроить соответствующие файлы конфигураций: + * .env - установить следующие значения: + * `BOT_USER_NAME` - Имя бота + * `BOT_TOKEN` - Токен бота (необходимо создать нового бота по [инструкции](https://core.telegram.org/bots#3-how-do-i-create-a-bot)) + * `DATASOURCE_DB` - Название базы данных (опционально) + * `DATASOURCE_URL` - url базы данных со слэшом в конце "/" (опционально) + * `DATASOURCE_USER` - Имя пользователя базы данных (опционально) + * `DATASOURCE_PASS` - Пароль пользователя базы данных (опционально) + * application-bot.yml - установить значения для начальной настройки администраторов, чатов пользователей и модераторов и т.д. + * С помощью Maven собрать проект командой `mvn clean package` + * Установить и запустить Docker, затем выполнить команду `docker-compose up` или `docker-compose up -d` (для запуска в фоновом режиме) + + --- ## Примеры сообщений diff --git a/application-bot.yml b/application-bot.yml index 0b9ebb9..026146f 100644 --- a/application-bot.yml +++ b/application-bot.yml @@ -21,7 +21,7 @@ chats-settings: - 444 - 555 - 666 - rewrite-chats-settings-in-database-on-startup: true + rewrite-chats-settings-in-database-on-startup: false message-templates: variables: diff --git a/docker-compose.yml b/docker-compose.yml index d68ef05..e9c13ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,12 +4,12 @@ services: container_name: postgres image: postgres:alpine restart: always - environment: - POSTGRES_USER: ${DATASOURCE_USER} - POSTGRES_PASSWORD: ${DATASOURCE_PASS} - POSTGRES_DB: ${DATASOURCE_DB} ports: - "5431:5432" + environment: + POSTGRES_DB: ${DATASOURCE_DB:-tg-bot-users} + POSTGRES_USER: ${DATASOURCE_USER:-tg-admin} + POSTGRES_PASSWORD: ${DATASOURCE_PASS:-postgres} volumes: - C:\docker\postgres:/var/lib/postgres tg-bot-users: @@ -23,9 +23,9 @@ services: environment: BOT_USER_NAME: ${BOT_USER_NAME} BOT_TOKEN: ${BOT_TOKEN} - DATASOURCE_URL: ${DATASOURCE_URL}${DATASOURCE_DB} - DATASOURCE_USER: ${DATASOURCE_USER} - DATASOURCE_PASS: ${DATASOURCE_PASS} + DATASOURCE_URL: ${DATASOURCE_URL:-jdbc:postgresql://host.docker.internal:5431/}${DATASOURCE_DB:-tg-bot-users} + DATASOURCE_USER: ${DATASOURCE_USER:-tg-admin} + DATASOURCE_PASS: ${DATASOURCE_PASS:-postgres} depends_on: postgres: condition: service_started \ No newline at end of file diff --git a/pom.xml b/pom.xml index 39578dc..6e3e8e1 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ org.apache.maven.plugins maven-surefire-plugin - false + true diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 02323e8..a1f7bd5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,7 +32,7 @@ spring: logging: level: - root: info + root: off org.codewithoutus.tgbotusers: debug web: off sql: off From ed49b7ff7d3396c020af2ce385f8254bfe8ffbc4 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Fri, 22 Jul 2022 00:44:13 +0400 Subject: [PATCH 47/50] + pre-final --- README.md | 2 +- .../bot/handler/AdminMessageHandler.java | 1 - .../bot/handler/CallbackQueryHandler.java | 2 - .../handler/ChooseLuckyCommandHandler.java | 115 ------------------ .../tgbotusers/bot/service/RightsService.java | 35 ------ .../tgbotusers/model/entity/ChatUser.java | 2 +- .../model/repository/ChatUserRepository.java | 2 - .../repository/UserJoiningRepository.java | 2 - .../model/service/ChatModeratorService.java | 4 + .../model/service/ChatUserService.java | 1 + src/main/resources/application.yml | 3 - 11 files changed, 7 insertions(+), 162 deletions(-) delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java delete mode 100644 src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java diff --git a/README.md b/README.md index 917790b..367f30c 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ _Доступны в чатах модераторов_ --- ## Web API -На всякий случай для бота сделан небольшой API.
По умолчанию используется порт 8090. +На всякий случай для бота сделан небольшой API.
По умолчанию используется порт 8080. | Endpoint | Описание | |------------------|-------------------------------------------------------------------------------------------------| diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java index 5f5f6d5..e00054d 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/AdminMessageHandler.java @@ -281,5 +281,4 @@ private boolean deleteModerChat(Long chatId, String text) { telegramService.sendMessage(new SendMessage(chatId, String.format(OK_DEL_MODER, moderGroupId))); return true; } - } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java index f04f0a0..cd9fbc5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java +++ b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/CallbackQueryHandler.java @@ -11,7 +11,6 @@ import org.codewithoutus.tgbotusers.config.AppStaticContext; import org.codewithoutus.tgbotusers.model.entity.UserJoining; import org.codewithoutus.tgbotusers.model.enums.CongratulateStatus; -import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; import org.codewithoutus.tgbotusers.model.service.UserJoiningService; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -67,7 +66,6 @@ private boolean handleCongratulationDecision(String command, Map Long userId = userJoining.getUserId(); Integer anniversaryNumber = userJoining.getAnniversaryNumber(); - // TODO: Pavel (подумать) -- если из списка, то можно поздравить отклоненного раннее, а иначе нельзя if (userJoiningService.existCongratulatedUser(chatId, anniversaryNumber)) { return true; } diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java b/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java deleted file mode 100644 index 19ef1e9..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/handler/ChooseLuckyCommandHandler.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.handler; - -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.request.SendMessage; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.codewithoutus.tgbotusers.bot.UpdateUtils; -import org.codewithoutus.tgbotusers.bot.enums.BotCommand; -import org.codewithoutus.tgbotusers.bot.service.NotificationService; -import org.codewithoutus.tgbotusers.bot.service.TelegramService; -import org.codewithoutus.tgbotusers.bot.service.TemplateEngine; -import org.codewithoutus.tgbotusers.config.ChatSettings; -import org.codewithoutus.tgbotusers.config.NotificationTemplates; -import org.codewithoutus.tgbotusers.model.entity.ChatModerator; -import org.codewithoutus.tgbotusers.model.entity.ChatUser; -import org.codewithoutus.tgbotusers.model.service.ChatModeratorService; -import org.codewithoutus.tgbotusers.model.service.ChatUserService; -import org.codewithoutus.tgbotusers.model.service.UserJoiningService; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.stream.Collectors; - -@Component -@RequiredArgsConstructor -@Slf4j -public class ChooseLuckyCommandHandler extends Handler { - - private final ChatSettings chatSettings; - private final NotificationService notificationService; - private final TelegramService telegramService; - - private final ChatModeratorService chatModeratorService; - private final ChatUserService chatUserService; - private final UserJoiningService userJoiningService; - - @Override - protected boolean handle(Update update) { - - // TODO: Алекс: - // 1. если на anniversary number не было поздравления, то выводятся все претенденты с кнопками CongratulationDecisionKeyboard - // 2. если поздравленного нет, то отклоненные тоже выводятся - // 3. все новые кнопки нужно записывать в таблицу UserJoiningNotification, чтобы потом также удалялись - // 4. можно попробовать выводить все-все чаты, а потом доработать -- на будущее - // ИТОГ: реализовать (внедрить) команду /chooseLucky - - BotCommand command = UpdateUtils.getBotCommand(update); - if (command != BotCommand.LUCKY_LIST) { - return false; - } - - Long moderatorChatId = UpdateUtils.getChatId(update); - if (!UpdateUtils.isPrivateMessageFromAdmin(update, chatSettings) - && !chatModeratorService.existsByChatId(moderatorChatId)) { - return false; - } - - String commandText = UpdateUtils.getMessageText(update); - Matcher matcher = command.getRegex().matcher(commandText); - if (!matcher.matches()) { - log.warn("Wrong command to bot {}", commandText); - return false; - } - - String chatId = matcher.group(2); - List luckyChats = getLuckyChats(chatId, moderatorChatId); - if (luckyChats.isEmpty()) { - telegramService.sendMessage(new SendMessage(moderatorChatId, "Нет данных о чатах пользователей")); - return true; - } - - sendNotificationsAgain(moderatorChatId, luckyChats); - - return true; - } - - - private List getLuckyChats(String chatId, Long moderatorChatId) { - if (chatId == null) { - return chatModeratorService - .findByChatId(moderatorChatId) - .map(ChatModerator::getChatUsers) - .orElseGet(ArrayList::new); - } else { - return chatUserService - .findByChatId(Long.parseLong(chatId)) - .stream() - .toList(); - } - } - - private void sendNotificationsAgain(Long moderatorChatId, List luckyChats) { - List chatsIds = luckyChats.stream() - .map(ChatUser::getChatId) - .toList(); - - userJoiningService.findByChatIds(chatsIds) - .stream() - .collect(Collectors.groupingBy(x -> String.valueOf(x.getChatId()))) - .forEach((groupId, joiningList) -> { - - telegramService.sendMessage(new SendMessage(moderatorChatId, getGroupSeparator(groupId))); - - joiningList.forEach(notificationService::notifyModeratorsAboutUserJoining); - }); - } - - private String getGroupSeparator(String groupId) { - return "==================================================" + System.lineSeparator() + - "Группа: " + groupId + System.lineSeparator() + - "=================================================="; - } -} diff --git a/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java b/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java deleted file mode 100644 index 3fc269c..0000000 --- a/src/main/java/org/codewithoutus/tgbotusers/bot/service/RightsService.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.codewithoutus.tgbotusers.bot.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class RightsService { - - // TODO: Алекс -- нам это нужно? - -// private final TelegramBot bot; -// -// public void initialiseAdministratorRights() { -// if (!administratorRightsIsCorrect()) { -// setAdministratorRights(); -// } -// } -// -// private boolean administratorRightsIsCorrect() { -// var getRights = new GetMyDefaultAdministratorRights(); -// var response = bot.execute(getRights); -// var rights = response.result(); -// return rights.canInviteUsers(); -// } -// -// private void setAdministratorRights() { -// var chatAdministratorRights = new ChatAdministratorRights(); -// chatAdministratorRights.canInviteUsers(true); -// -// var setMyDefaultAdministratorRights = new SetMyDefaultAdministratorRights(); -// setMyDefaultAdministratorRights.rights(chatAdministratorRights); -// bot.execute(setMyDefaultAdministratorRights); -// } -} \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java index 057a3d8..3622522 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/entity/ChatUser.java @@ -12,7 +12,7 @@ @Entity @Getter @Setter -public class ChatUser { // TODO: Pavel -- переименовать сущность и сопутствующие переменные (после задачи Макса) +public class ChatUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java index 57abd43..3dfc7e5 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/ChatUserRepository.java @@ -12,7 +12,5 @@ public interface ChatUserRepository extends CrudRepository { Optional findByChatId(long chatId); - List findByChatModeratorsNotEmpty(); - boolean existsByChatId(Long chatId); } \ No newline at end of file diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java index 40b044e..b51c4e6 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/repository/UserJoiningRepository.java @@ -12,8 +12,6 @@ @Repository public interface UserJoiningRepository extends CrudRepository { - List findByChatId(Long chatId); - List findDistinctByChatIdInOrderByChatIdAscNumberAsc(Collection chatIds); boolean existsByChatIdAndAnniversaryNumberAndStatus(Long chatId, Integer anniversaryNumber, CongratulateStatus congratulateStatus); diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java index 8084410..878a9fe 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatModeratorService.java @@ -19,6 +19,10 @@ public void deleteAll() { chatModeratorRepository.deleteAll(); } + public void deleteById(Integer id) { + chatModeratorRepository.deleteById(id); + } + public ChatModerator save(ChatModerator entity) { return chatModeratorRepository.save(entity); } diff --git a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java index 81ac3de..561124c 100644 --- a/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java +++ b/src/main/java/org/codewithoutus/tgbotusers/model/service/ChatUserService.java @@ -7,6 +7,7 @@ import org.codewithoutus.tgbotusers.model.repository.ChatUserRepository; import org.springframework.stereotype.Service; +import java.util.List; import java.util.Optional; @Getter diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 52ee0de..c8a5cf6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,3 @@ -#server: -# port: 8090 - bot-settings: bot-user-name: ${BOT_USER_NAME} bot-token: ${BOT_TOKEN} From 84738a4890a102df09fd7daba9fa1f7d7cb69610 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Fri, 22 Jul 2022 00:52:49 +0400 Subject: [PATCH 48/50] + final --- .env | 6 +----- README.md | 20 +++++++++++++++++++- docker-compose.yml | 14 +++++++------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/.env b/.env index c14d4c2..9806346 100644 --- a/.env +++ b/.env @@ -1,6 +1,2 @@ BOT_USER_NAME=GroupControlSkillboxBot -BOT_TOKEN=5566628073:AAHyZTYOh62Fb7_zVGNmHMqzwJUQtBvGWuc -DATASOURCE_DB=tg-bot-users -DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5431/ -DATASOURCE_USER=tg-admin -DATASOURCE_PASS=9en2w0oc \ No newline at end of file +BOT_TOKEN=5566628073:AAHyZTYOh62Fb7_zVGNmHMqzwJUQtBvGWuc \ No newline at end of file diff --git a/README.md b/README.md index 367f30c..43ef6af 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,23 @@ Менять настройки групп могут только администраторы. +--- +## Запуск +Проект настроен так, чтобы администратору приходилось выполнять минимум действий, поэтому сконфигурированное приложение запускается лишь парой команд. Нет необходимости настраивать базу данных, так как она запускается в docker-контейнере рядом с контейнером приложения. И так для запуска приложения вам потребуется выполнить следующие действия: + +Все команды выполняются в терминале в корне проекта! +* Сделать клонирование репозитория командой `git clone {адрес репозитория}` +* Настроить соответствующие файлы конфигураций: + * .env - установить следующие значения: + * `BOT_USER_NAME` - Имя бота + * `BOT_TOKEN` - Токен бота (необходимо создать нового бота по [инструкции](https://core.telegram.org/bots#3-how-do-i-create-a-bot)) + * `DATASOURCE_DB` - Название базы данных (опционально) + * `DATASOURCE_URL` - url базы данных со слэшом в конце "/" (опционально) + * `DATASOURCE_USER` - Имя пользователя базы данных (опционально) + * `DATASOURCE_PASS` - Пароль пользователя базы данных (опционально) + * application-bot.yml - установить значения для начальной настройки администраторов, чатов пользователей и модераторов и т.д. +* С помощью Maven собрать проект командой `mvn clean package` +* Установить и запустить Docker, затем выполнить команду `docker-compose up` или `docker-compose up -d` (для запуска в фоновом режиме) --- ## Команды бота @@ -29,6 +46,7 @@ _Доступны администраторам в приватном чате_ | Команда | Описание | |------------------------------------|---------------------------------------------------------| | /help | вывод списка доступных команд | +| /currentSettings | вывод текущих настроек чатов | | /addModerChat {id} | добавление чата модераторов | | /addUserChat {id} | добавление чата пользователей | | /deleteModerChat {id} | удаление чата модераторов | @@ -64,7 +82,7 @@ _Доступны в чатах модераторов_ ## Настройки приложения Конфигурационные файлы приложения разделены на две части: -### Настройки для разработчиков _(application.yml)_: +### Настройки для администраторов _(application.yml)_: * Spring * Database _(environment vars)_ * Bot token _(environment vars)_ diff --git a/docker-compose.yml b/docker-compose.yml index d68ef05..e9c13ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,12 +4,12 @@ services: container_name: postgres image: postgres:alpine restart: always - environment: - POSTGRES_USER: ${DATASOURCE_USER} - POSTGRES_PASSWORD: ${DATASOURCE_PASS} - POSTGRES_DB: ${DATASOURCE_DB} ports: - "5431:5432" + environment: + POSTGRES_DB: ${DATASOURCE_DB:-tg-bot-users} + POSTGRES_USER: ${DATASOURCE_USER:-tg-admin} + POSTGRES_PASSWORD: ${DATASOURCE_PASS:-postgres} volumes: - C:\docker\postgres:/var/lib/postgres tg-bot-users: @@ -23,9 +23,9 @@ services: environment: BOT_USER_NAME: ${BOT_USER_NAME} BOT_TOKEN: ${BOT_TOKEN} - DATASOURCE_URL: ${DATASOURCE_URL}${DATASOURCE_DB} - DATASOURCE_USER: ${DATASOURCE_USER} - DATASOURCE_PASS: ${DATASOURCE_PASS} + DATASOURCE_URL: ${DATASOURCE_URL:-jdbc:postgresql://host.docker.internal:5431/}${DATASOURCE_DB:-tg-bot-users} + DATASOURCE_USER: ${DATASOURCE_USER:-tg-admin} + DATASOURCE_PASS: ${DATASOURCE_PASS:-postgres} depends_on: postgres: condition: service_started \ No newline at end of file From ac93703b8109037f517a5e798bdfb91c521f6e05 Mon Sep 17 00:00:00 2001 From: Pavel Novikov <83977511+nokerokuta@users.noreply.github.com> Date: Fri, 22 Jul 2022 01:07:26 +0400 Subject: [PATCH 49/50] Update pom.xml --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 39578dc..d1fd30b 100644 --- a/pom.xml +++ b/pom.xml @@ -74,10 +74,10 @@ org.apache.maven.plugins maven-surefire-plugin - false + true - \ No newline at end of file + From 4d59b6361dadc9f93cf11c974b668aacfdd6c536 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Fri, 22 Jul 2022 02:40:21 +0400 Subject: [PATCH 50/50] minor README message example addon --- README.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 43ef6af..7684da0 100644 --- a/README.md +++ b/README.md @@ -112,13 +112,27 @@ _Доступны в чатах модераторов_ Уведомление модераторов - 🎉 “Java разработчик” 👤 Василий (ника нет), - 🔢 500 🕐 26.06.22 10:56 + 🎉 “Java разработчик” 👤 Василий (ника нет), + 🔢 500 🕐 26.06.22 10:56 [ПОЗДРАВИТЬ] [ОТКЛОНИТЬ] Поздравление пользователя - 🎉 Поздравляю, Никита, + 🎉 Поздравляю, Никита, как же удачно попали в нужное время и в нужное время! Вы 500 участник коммьюнити. Вас ждут плюшки и печенюшки!🎉 + +Вывод списка счастливчиков + + ================================================== + Группа: “Java разработчик” + ================================================== + 👑👑👑 👤Василий (ника нет), + 🔢 500 🕐 20.07.22 22:00 + + 👤GroupSkillboxBot (GroupSkillboxBot), + 🔢 501 🕐 20.07.22 23:06 + + 👑👑👑 👤Никита (nikita), + 🔢 1000 🕐 22.07.22 01:00 \ No newline at end of file