Skip to content

Commit c02aa53

Browse files
committed
Fix #31: Publish articles
1 parent 2344129 commit c02aa53

File tree

9 files changed

+150
-4
lines changed

9 files changed

+150
-4
lines changed

herald/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ application {
1111
dependencies {
1212
implementation(libs.kotlinx.serialization.core)
1313
implementation(libs.kaml)
14+
implementation(libs.tgbotapi.extensions.api)
15+
implementation(project.projects.votes.dynamodb)
16+
implementation(project.projects.votes.tgbotapiExtensions)
17+
implementation(project.projects.votes.dynamodb)
1418

1519
testImplementation(libs.junit.jupiter.api)
1620
testImplementation(libs.junit.jupiter.params)

herald/src/main/kotlin/by/jprof/telegram/bot/herald/App.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package by.jprof.telegram.bot.herald
22

33
import by.jprof.telegram.bot.herald.impl.post
44
import by.jprof.telegram.bot.herald.impl.postFile
5+
import by.jprof.telegram.bot.herald.impl.send
56

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

1314
println("Parsed the post: $post")
15+
16+
send(post)
17+
18+
println("Done")
1419
}
Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package by.jprof.telegram.bot.herald.impl
22

3+
import by.jprof.telegram.bot.herald.model.Frontmatter
34
import by.jprof.telegram.bot.herald.model.Post
45
import com.charleskorn.kaml.Yaml
56
import kotlinx.serialization.decodeFromString
67
import java.nio.file.Path
8+
import kotlin.io.path.Path
9+
import kotlin.io.path.absolutePathString
10+
import kotlin.io.path.nameWithoutExtension
711
import kotlin.io.path.readText
12+
import kotlin.io.path.relativeTo
813
import kotlin.text.RegexOption.DOT_MATCHES_ALL
914
import kotlin.text.RegexOption.MULTILINE
1015

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

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

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

21-
Post(Yaml.default.decodeFromString(frontmatter), content)
28+
Post(
29+
relativePath.parent.toString() + "/" + relativePath.nameWithoutExtension,
30+
Yaml.default.decodeFromString<Frontmatter>(frontmatter).run {
31+
if (this.image == null) {
32+
this
33+
} else {
34+
this.copy(
35+
image = path.resolveSibling(this.image).absolutePathString()
36+
)
37+
}
38+
},
39+
content,
40+
)
2241
}
2342
}

herald/src/main/kotlin/by/jprof/telegram/bot/herald/impl/postFile.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import java.nio.file.Path
44
import java.time.LocalDate
55
import java.time.format.DateTimeFormatter
66
import kotlin.io.path.Path
7+
import kotlin.io.path.absolute
78
import kotlin.io.path.exists
89

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

14-
return cwd.resolve(today.format(formatter) + ".md").takeIf { it.exists() }
15+
return cwd.resolve(today.format(formatter) + ".md").absolute().takeIf { it.exists() }
1516
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package by.jprof.telegram.bot.herald.impl
2+
3+
import by.jprof.telegram.bot.herald.model.Post
4+
import by.jprof.telegram.bot.votes.dynamodb.dao.VotesDAO
5+
import by.jprof.telegram.bot.votes.model.Votes
6+
import by.jprof.telegram.bot.votes.tgbotapi_extensions.toInlineKeyboardMarkup
7+
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
8+
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
9+
import dev.inmo.tgbotapi.extensions.api.send.media.sendPhoto
10+
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
11+
import dev.inmo.tgbotapi.requests.abstracts.InputFile
12+
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
13+
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2ParseMode
14+
import dev.inmo.tgbotapi.types.chat.abstracts.UsernameChat
15+
import dev.inmo.tgbotapi.types.toChatId
16+
import dev.inmo.tgbotapi.utils.StorageFile
17+
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
18+
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider
19+
import software.amazon.awssdk.regions.Region
20+
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient
21+
import kotlin.io.path.Path
22+
import kotlin.io.path.absolute
23+
import kotlin.io.path.isRegularFile
24+
import kotlin.system.exitProcess
25+
26+
suspend fun send(post: Post) {
27+
val image = post.image()
28+
val votes = post.votes()
29+
30+
post.frontmatter.chats.forEach { chat ->
31+
when {
32+
image != null -> {
33+
println("Sending image to $chat")
34+
35+
bot.sendPhoto(
36+
chatId = chat.toChatId(),
37+
fileId = image,
38+
text = post.content.forChat(chat),
39+
parseMode = MarkdownV2ParseMode,
40+
replyMarkup = votes?.toInlineKeyboardMarkup(),
41+
)
42+
}
43+
else -> {
44+
println("Sending text to $chat")
45+
46+
bot.sendMessage(
47+
chatId = chat.toChatId(),
48+
text = post.content.forChat(chat),
49+
parseMode = MarkdownV2ParseMode,
50+
replyMarkup = votes?.toInlineKeyboardMarkup(),
51+
disableWebPagePreview = true,
52+
)
53+
}
54+
}
55+
}
56+
}
57+
58+
private val bot = telegramBot(System.getenv("TOKEN_TELEGRAM_BOT") ?: run {
59+
println("TOKEN_TELEGRAM_BOT is not set!")
60+
exitProcess(0)
61+
})
62+
63+
private val votesDAO = VotesDAO(
64+
DynamoDbAsyncClient
65+
.builder()
66+
.region(Region.US_EAST_1)
67+
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
68+
.build(),
69+
System.getenv("TABLE_VOTES") ?: run {
70+
println("TABLE_VOTES is not set!")
71+
exitProcess(0)
72+
}
73+
)
74+
75+
private fun Post.image(): InputFile? {
76+
val cwd = Path("").absolute()
77+
val imagePath = this.frontmatter.image?.let { cwd.resolve(it) }
78+
79+
return if (imagePath != null && imagePath.isRegularFile()) {
80+
MultipartFile(StorageFile(imagePath.toFile()))
81+
} else {
82+
null
83+
}
84+
}
85+
86+
private suspend fun Post.votes(): Votes? {
87+
return if (this.frontmatter.votes == null) {
88+
null
89+
} else {
90+
val votesId = "HERALD-${this.id}"
91+
92+
votesDAO.get(votesId) ?: Votes(votesId, this.frontmatter.votes)
93+
}
94+
}
95+
96+
private suspend fun String.forChat(chatId: Long): String {
97+
val chat = (bot.getChat(chatId.toChatId()) as? UsernameChat)?.username?.username ?: return this
98+
99+
return "${this.trimEnd()}\n\n${chat.escapeMarkdownV2Common()}"
100+
}

herald/src/main/kotlin/by/jprof/telegram/bot/herald/model/Frontmatter.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ import kotlinx.serialization.Serializable
66
data class Frontmatter(
77
val chats: List<Long>,
88
val image: String? = null,
9-
val disableWebPagePreview: Boolean = false,
109
val votes: List<String>? = null,
1110
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package by.jprof.telegram.bot.herald.model
22

33
data class Post(
4+
val id: String,
45
val frontmatter: Frontmatter,
56
val content: String,
67
)

herald/src/test/kotlin/by/jprof/telegram/bot/herald/impl/PostTest.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import org.junit.jupiter.params.provider.Arguments
99
import org.junit.jupiter.params.provider.MethodSource
1010
import java.nio.file.Path
1111
import java.util.stream.Stream
12+
import kotlin.io.path.Path
13+
import kotlin.io.path.absolutePathString
14+
import kotlin.io.path.nameWithoutExtension
15+
import kotlin.io.path.relativeTo
1216

1317
internal class PostTest {
1418
@ParameterizedTest(name = "{0}")
@@ -26,20 +30,33 @@ internal class PostTest {
2630
Arguments.of(
2731
Named.of("001", "001".testResourcePath),
2832
Post(
33+
"001".id,
2934
Frontmatter(chats = listOf(-1001146107319, -1001585354456)),
3035
"This is content.\n"
3136
)
3237
),
3338
Arguments.of(
3439
Named.of("002", "002".testResourcePath),
3540
Post(
36-
Frontmatter(chats = listOf(-1001146107319, -1001585354456), image = "002.png"),
41+
"002".id,
42+
Frontmatter(chats = listOf(-1001146107319, -1001585354456), image = "002".image),
3743
"This is a\nmultiline content!\n"
3844
)
3945
),
4046
)
4147

4248
private val String.testResourcePath: Path
4349
get() = Path.of(this@Companion::class.java.classLoader.getResource("posts/$this.md").toURI())
50+
51+
private val String.id: String
52+
get() {
53+
val cwd = Path("").toAbsolutePath()
54+
val resourcePath = Path.of(this@Companion::class.java.classLoader.getResource("posts/$this.md").toURI()).relativeTo(cwd)
55+
56+
return resourcePath.parent.toString() + "/" + resourcePath.nameWithoutExtension
57+
}
58+
59+
private val String.image: String
60+
get() = Path.of(this@Companion::class.java.classLoader.getResource("posts/$this.png").toURI()).absolutePathString()
4461
}
4562
}

herald/src/test/resources/posts/002.png

Loading

0 commit comments

Comments
 (0)