Compare commits

...

5 Commits

Author SHA1 Message Date
Alexander Buntakov b507a92de0 fix newline 2022-12-12 14:13:21 +03:00
Alexander Buntakov d9d98dceb8 fix newlines 2022-12-12 14:12:15 +03:00
Alexander Buntakov 02e0df53a3 add hasCommand 2022-11-18 01:33:45 +03:00
Alexander Buntakov d05e4fe4c5 remove unused dependencies 2022-11-14 01:39:23 +03:00
Alexander Buntakov 2282514dc7 add telegram bot 2022-11-14 00:43:14 +03:00
18 changed files with 311 additions and 5 deletions

View File

@ -19,12 +19,12 @@ object StringUtils {
}
nextUpperCase -> {
this.append(char.toUpperCase())
this.append(char.uppercase())
nextUpperCase = false
}
!nextUpperCase -> {
this.append(char.toLowerCase())
else -> {
this.append(char.lowercase())
}
}
}
@ -34,8 +34,8 @@ object StringUtils {
fun String.removeNonPrintableCharacters(): String {
return this
.transliterateCyrillic()
.replace("[\\p{Cntrl}&&[^\r\n\t]]".toRegex(), "")// erases all the ASCII control characters
.replace("\\p{C}".toRegex(), "")// removes non-printable characters from Unicode
.replace("\\p{Cntrl}&&[^\r\n\t]".toRegex(), "") // erases all the ASCII control characters
.replace("\\p{C}".toRegex(), "") // removes non-printable characters from Unicode
.trim()
}

View File

@ -61,3 +61,4 @@ include("server-info-spring-web")
include("geoip-core")
include("user-agent")
include("smart-migration")
include("telegram-bot-spring")

37
telegram-bot-spring/.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/

View File

@ -0,0 +1,35 @@
## Библиотека написания телеграмм-ботов
```kotlin
@Component
class MyTelegramBot(
private val messageHandlers: List<MessageHandler>,
) : TelegramLongPollingBot() {
override fun onUpdateReceived(update: Update?) {
// create MessageContext
// messageHandlers.takeWhile { it.process(ctx, this) }
}
}
@Component
class HelloMessageHandler: AbstractMessageHandler {
override fun isSupported(ctx: MessageContext): Booleat {
ctx.messageCommand.message.equals("hi")
}
override fun process(ctx: MessageContext, sender: AbsSender): Boolean {
val message = SendMessage().apply {
this.chatId = ctx.chatId
this.text = "Hello"
}
sender.execute(message)
return true
}
}
```

View File

@ -0,0 +1,18 @@
plugins {
id 'kotlin'
id 'kotlin-spring'
}
dependencies {
api 'org.telegram:telegrambots-spring-boot-starter:6.3.0'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
implementation 'org.springframework.boot:spring-boot'
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin")
testImplementation("org.mockito:mockito-inline")
testImplementation 'junit:junit'
}

View File

@ -0,0 +1,6 @@
package ru.touchin.spring.telegram.bot
import org.springframework.context.annotation.Import
@Import(value = [TelegramBotConfiguration::class])
annotation class EnableSpringTelegramBot

View File

@ -0,0 +1,12 @@
package ru.touchin.spring.telegram.bot
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@EnableConfigurationProperties
@ConfigurationPropertiesScan
@ComponentScan
class TelegramBotConfiguration

View File

@ -0,0 +1,22 @@
package ru.touchin.spring.telegram.bot.messages
import org.telegram.telegrambots.meta.bots.AbsSender
import ru.touchin.spring.telegram.bot.messages.dto.MessageContext
abstract class AbstractMessageHandler<C: MessageContext<U, S>, U, S>:
MessageHandler<C, U, S>,
MessageFilter<C, U, S>,
HelpMessage
{
override fun process(context: C, sender: AbsSender): Boolean {
if (!isSupported(context)) {
return false
}
return internalProcess(context, sender)
}
abstract fun internalProcess(context: C, sender: AbsSender): Boolean
}

View File

@ -0,0 +1,7 @@
package ru.touchin.spring.telegram.bot.messages
interface HelpMessage {
fun helpMessage(): String? = null
}

View File

@ -0,0 +1,9 @@
package ru.touchin.spring.telegram.bot.messages
import ru.touchin.spring.telegram.bot.messages.dto.MessageContext
interface MessageFilter<C: MessageContext<U, S>, U, S> {
fun isSupported(context: C): Boolean
}

View File

@ -0,0 +1,10 @@
package ru.touchin.spring.telegram.bot.messages
import org.telegram.telegrambots.meta.bots.AbsSender
import ru.touchin.spring.telegram.bot.messages.dto.MessageContext
interface MessageHandler<C: MessageContext<U, S>, U, S> {
fun process(context: C, sender: AbsSender): Boolean
}

View File

@ -0,0 +1,8 @@
package ru.touchin.spring.telegram.bot.messages
@Suppress("unused")
object Order {
const val HIGH_PRIORITY = 1024
const val NORMAL_PRIORITY = 2048
const val LOW_PRIORITY = 3072
}

View File

@ -0,0 +1,19 @@
package ru.touchin.spring.telegram.bot.messages.dto
data class MessageCommand(
val command: String,
val message: String
) {
fun hasCommand() = command != NO_COMMAND
companion object {
private const val NO_COMMAND = ""
fun createNoCommandMessage(message: String) = MessageCommand(
command = NO_COMMAND,
message = message
)
}
}

View File

@ -0,0 +1,17 @@
package ru.touchin.spring.telegram.bot.messages.dto
import org.telegram.telegrambots.meta.api.objects.Update
open class MessageContext<U, S>(
val origin: Update,
val user: U,
val messageCommand: MessageCommand,
val state: S
) {
fun hasCommand(command: String) = messageCommand.command == command
val chatId: String
get() = origin.message?.chatId?.toString()
?: origin.callbackQuery.message.chatId.toString()
}

View File

@ -0,0 +1,9 @@
package ru.touchin.spring.telegram.bot.messages.services
import ru.touchin.spring.telegram.bot.messages.dto.MessageCommand
interface TelegramMessageCommandResolver {
fun resolve(rawMessage: String): MessageCommand
}

View File

@ -0,0 +1,23 @@
package ru.touchin.spring.telegram.bot.messages.services.impl
import org.springframework.stereotype.Service
import ru.touchin.spring.telegram.bot.messages.dto.MessageCommand
import ru.touchin.spring.telegram.bot.messages.services.TelegramMessageCommandResolver
@Service
class TelegramMessageCommandResolverImpl : TelegramMessageCommandResolver {
override fun resolve(rawMessage: String): MessageCommand {
val message = rawMessage.trim()
if (message.isBlank() || !message.startsWith("/")) {
return MessageCommand.createNoCommandMessage(rawMessage)
}
return MessageCommand(
command = message.substringBefore(' ').trim().lowercase(),
message = message.substringAfter(' ', missingDelimiterValue = "")
)
}
}

View File

@ -0,0 +1,6 @@
package ru.touchin.spring.telegram.bot
import org.springframework.boot.autoconfigure.SpringBootApplication
@SpringBootApplication
open class TestApplication

View File

@ -0,0 +1,67 @@
package ru.touchin.spring.telegram.bot.messages.services.impl
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import ru.touchin.spring.telegram.bot.messages.services.TelegramMessageCommandResolver
@ActiveProfiles("test")
@SpringBootTest
internal class TelegramMessageCommandResolverImplTest {
@Autowired
private lateinit var telegramMessageCommandResolver: TelegramMessageCommandResolver
@Test
fun shouldBeEmptyCommand() {
telegramMessageCommandResolver.resolve("hello").also {
assertFalse(it.hasCommand())
assertEquals("hello", it.message)
}
telegramMessageCommandResolver.resolve("").also {
assertFalse(it.hasCommand())
assertEquals("", it.message)
}
telegramMessageCommandResolver.resolve(" ").also {
assertFalse(it.hasCommand())
assertEquals(" ", it.message)
}
telegramMessageCommandResolver.resolve("hello world").also {
assertFalse(it.hasCommand())
assertEquals("hello world", it.message)
}
}
@Test
fun shouldResolveCommand() {
telegramMessageCommandResolver.resolve("/hello").also {
assertTrue(it.hasCommand())
assertEquals("/hello", it.command)
assertEquals("", it.message)
}
telegramMessageCommandResolver.resolve("/hello world").also {
assertTrue(it.hasCommand())
assertEquals("/hello", it.command)
assertEquals("world", it.message)
}
telegramMessageCommandResolver.resolve("/hello /world").also {
assertTrue(it.hasCommand())
assertEquals("/hello", it.command)
assertEquals("/world", it.message)
}
telegramMessageCommandResolver.resolve(" /hello /world").also {
assertTrue(it.hasCommand())
assertEquals("/hello", it.command)
assertEquals(" /world", it.message)
}
}
}