Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions herald/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ application {
dependencies {
implementation(libs.kotlinx.serialization.core)
implementation(libs.kaml)
implementation(libs.tgbotapi.extensions.api)
implementation(project.projects.votes.dynamodb)
implementation(project.projects.votes.tgbotapiExtensions)
implementation(project.projects.votes.dynamodb)

testImplementation(libs.junit.jupiter.api)
testImplementation(libs.junit.jupiter.params)
Expand Down
3 changes: 3 additions & 0 deletions herald/processor/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= Herald / Processor

link:../../votes/voting-processor/src/main/kotlin/by/jprof/telegram/bot/votes/voting_processor/VotingProcessor.kt[`VotingProcessor`] implementation for this scheduled messages poster.
10 changes: 10 additions & 0 deletions herald/processor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins {
kotlin("jvm")
}

dependencies {
api(project.projects.core)
api(project.projects.votes)
api(project.projects.votes.votingProcessor)
implementation(libs.log4j.api)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package by.jprof.telegram.bot.herald.processor

import by.jprof.telegram.bot.core.UpdateProcessor
import by.jprof.telegram.bot.votes.dao.VotesDAO
import by.jprof.telegram.bot.votes.voting_processor.VotingProcessor
import dev.inmo.tgbotapi.bot.RequestsExecutor
import dev.inmo.tgbotapi.types.update.CallbackQueryUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import org.apache.logging.log4j.LogManager

class HeraldVoteUpdateProcessor(
votesDAO: VotesDAO,
bot: RequestsExecutor,
) : VotingProcessor(
"HERALD",
votesDAO,
{ throw UnsupportedOperationException("Votes should be constructed elsewhere!") },
bot,
), UpdateProcessor {
companion object {
private val logger = LogManager.getLogger(HeraldVoteUpdateProcessor::class.java)!!
}

override suspend fun process(update: Update) {
when (update) {
is CallbackQueryUpdate -> processCallbackQuery(update.data)
}
}
}
5 changes: 5 additions & 0 deletions herald/src/main/kotlin/by/jprof/telegram/bot/herald/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package by.jprof.telegram.bot.herald

import by.jprof.telegram.bot.herald.impl.post
import by.jprof.telegram.bot.herald.impl.postFile
import by.jprof.telegram.bot.herald.impl.send

suspend fun main(args: Array<String>) {
val postFile = postFile() ?: run { println("No post for today"); return }
Expand All @@ -11,4 +12,8 @@ suspend fun main(args: Array<String>) {
val post = post(postFile) ?: run { println("Cannot parse the file"); return }

println("Parsed the post: $post")

send(post)

println("Done")
}
21 changes: 20 additions & 1 deletion herald/src/main/kotlin/by/jprof/telegram/bot/herald/impl/post.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package by.jprof.telegram.bot.herald.impl

import by.jprof.telegram.bot.herald.model.Frontmatter
import by.jprof.telegram.bot.herald.model.Post
import com.charleskorn.kaml.Yaml
import kotlinx.serialization.decodeFromString
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.readText
import kotlin.io.path.relativeTo
import kotlin.text.RegexOption.DOT_MATCHES_ALL
import kotlin.text.RegexOption.MULTILINE

fun post(path: Path): Post? {
val cwd = Path("")
val text = path.readText()
val regex = Regex("-{3,}\\R(?<frontmatter>.*)\\R-{3,}\\s+(?<content>.*)", setOf(MULTILINE, DOT_MATCHES_ALL))

Expand All @@ -17,7 +23,20 @@ fun post(path: Path): Post? {

val frontmatter = groups["frontmatter"]?.value ?: return null
val content = groups["content"]?.value ?: return null
val relativePath = path.relativeTo(cwd.toAbsolutePath())

Post(Yaml.default.decodeFromString(frontmatter), content)
Post(
relativePath.parent.toString() + "/" + relativePath.nameWithoutExtension,
Yaml.default.decodeFromString<Frontmatter>(frontmatter).run {
if (this.image == null) {
this
} else {
this.copy(
image = path.resolveSibling(this.image).absolutePathString()
)
}
},
content,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import java.nio.file.Path
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import kotlin.io.path.Path
import kotlin.io.path.absolute
import kotlin.io.path.exists

fun postFile(): Path? {
val cwd = Path("")
val today = LocalDate.now()
val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd")

return cwd.resolve(today.format(formatter) + ".md").takeIf { it.exists() }
return cwd.resolve(today.format(formatter) + ".md").absolute().takeIf { it.exists() }
}
100 changes: 100 additions & 0 deletions herald/src/main/kotlin/by/jprof/telegram/bot/herald/impl/send.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package by.jprof.telegram.bot.herald.impl

import by.jprof.telegram.bot.herald.model.Post
import by.jprof.telegram.bot.votes.dynamodb.dao.VotesDAO
import by.jprof.telegram.bot.votes.model.Votes
import by.jprof.telegram.bot.votes.tgbotapi_extensions.toInlineKeyboardMarkup
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.media.sendPhoto
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
import dev.inmo.tgbotapi.requests.abstracts.InputFile
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2ParseMode
import dev.inmo.tgbotapi.types.chat.abstracts.UsernameChat
import dev.inmo.tgbotapi.types.toChatId
import dev.inmo.tgbotapi.utils.StorageFile
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient
import kotlin.io.path.Path
import kotlin.io.path.absolute
import kotlin.io.path.isRegularFile
import kotlin.system.exitProcess

suspend fun send(post: Post) {
val image = post.image()
val votes = post.votes()

post.frontmatter.chats.forEach { chat ->
when {
image != null -> {
println("Sending image to $chat")

bot.sendPhoto(
chatId = chat.toChatId(),
fileId = image,
text = post.content.forChat(chat),
parseMode = MarkdownV2ParseMode,
replyMarkup = votes?.toInlineKeyboardMarkup(),
)
}
else -> {
println("Sending text to $chat")

bot.sendMessage(
chatId = chat.toChatId(),
text = post.content.forChat(chat),
parseMode = MarkdownV2ParseMode,
replyMarkup = votes?.toInlineKeyboardMarkup(),
disableWebPagePreview = true,
)
}
}
}
}

private val bot = telegramBot(System.getenv("TOKEN_TELEGRAM_BOT") ?: run {
println("TOKEN_TELEGRAM_BOT is not set!")
exitProcess(0)
})

private val votesDAO = VotesDAO(
DynamoDbAsyncClient
.builder()
.region(Region.US_EAST_1)
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.build(),
System.getenv("TABLE_VOTES") ?: run {
println("TABLE_VOTES is not set!")
exitProcess(0)
}
)

private fun Post.image(): InputFile? {
val cwd = Path("").absolute()
val imagePath = this.frontmatter.image?.let { cwd.resolve(it) }

return if (imagePath != null && imagePath.isRegularFile()) {
MultipartFile(StorageFile(imagePath.toFile()))
} else {
null
}
}

private suspend fun Post.votes(): Votes? {
return if (this.frontmatter.votes == null) {
null
} else {
val votesId = "HERALD-${this.id}"

votesDAO.get(votesId) ?: Votes(votesId, this.frontmatter.votes)
}
}

private suspend fun String.forChat(chatId: Long): String {
val chat = (bot.getChat(chatId.toChatId()) as? UsernameChat)?.username?.username ?: return this

return "${this.trimEnd()}\n\n${chat.escapeMarkdownV2Common()}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ import kotlinx.serialization.Serializable
data class Frontmatter(
val chats: List<Long>,
val image: String? = null,
val disableWebPagePreview: Boolean = false,
val votes: List<String>? = null,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package by.jprof.telegram.bot.herald.model

data class Post(
val id: String,
val frontmatter: Frontmatter,
val content: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import java.nio.file.Path
import java.util.stream.Stream
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.relativeTo

internal class PostTest {
@ParameterizedTest(name = "{0}")
Expand All @@ -26,20 +30,33 @@ internal class PostTest {
Arguments.of(
Named.of("001", "001".testResourcePath),
Post(
"001".id,
Frontmatter(chats = listOf(-1001146107319, -1001585354456)),
"This is content.\n"
)
),
Arguments.of(
Named.of("002", "002".testResourcePath),
Post(
Frontmatter(chats = listOf(-1001146107319, -1001585354456), image = "002.png"),
"002".id,
Frontmatter(chats = listOf(-1001146107319, -1001585354456), image = "002".image),
"This is a\nmultiline content!\n"
)
),
)

private val String.testResourcePath: Path
get() = Path.of(this@Companion::class.java.classLoader.getResource("posts/$this.md").toURI())

private val String.id: String
get() {
val cwd = Path("").toAbsolutePath()
val resourcePath = Path.of(this@Companion::class.java.classLoader.getResource("posts/$this.md").toURI()).relativeTo(cwd)

return resourcePath.parent.toString() + "/" + resourcePath.nameWithoutExtension
}

private val String.image: String
get() = Path.of(this@Companion::class.java.classLoader.getResource("posts/$this.png").toURI()).absolutePathString()
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions runners/lambda/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ dependencies {
implementation(project.projects.pins.dynamodb)
implementation(project.projects.pins.sfn)
implementation(project.projects.currencies)
implementation(project.projects.herald.processor)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package by.jprof.telegram.bot.runners.lambda.config
import by.jprof.telegram.bot.core.UpdateProcessingPipeline
import by.jprof.telegram.bot.core.UpdateProcessor
import by.jprof.telegram.bot.currencies.CurrenciesUpdateProcessor
import by.jprof.telegram.bot.currencies.parser.MonetaryAmountParsingPipeline
import by.jprof.telegram.bot.eval.EvalUpdateProcessor
import by.jprof.telegram.bot.herald.processor.HeraldVoteUpdateProcessor
import by.jprof.telegram.bot.jep.JEPUpdateProcessor
import by.jprof.telegram.bot.jep.JsoupJEPSummary
import by.jprof.telegram.bot.kotlin.KotlinMentionsUpdateProcessor
Expand Down Expand Up @@ -123,4 +123,11 @@ val pipelineModule = module {
bot = get(),
)
}

single<UpdateProcessor>(named("HeraldVoteUpdateProcessor")) {
HeraldVoteUpdateProcessor(
votesDAO = get(),
bot = get(),
)
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ include(":pins:sfn")
include(":runners:lambda")
include(":currencies")
include(":herald")
include(":herald:processor")