Skip to content

Commit f069e3f

Browse files
authored
Merge pull request #23 from bullet-true/KTB-27-dictionary-loading
KTB-27 create DictionaryDataSource class, move table initialization to DatabaseUserDictionary
2 parents 8cff2f6 + f277789 commit f069e3f

File tree

12 files changed

+287
-88
lines changed

12 files changed

+287
-88
lines changed

src/main/kotlin/ru/ifedorov/telegrambot/console/Main.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import ru.ifedorov.telegrambot.trainer.LearnWordsTrainer
66

77
fun main() {
88
val chatId = 0L
9+
val username = "from console trainer"
910

1011
Runtime.getRuntime().addShutdownHook(Thread {
1112
DatabaseConnection.close()
1213
})
1314

1415
val trainer = try {
15-
LearnWordsTrainer(DatabaseUserDictionary(chatId))
16+
LearnWordsTrainer(DatabaseUserDictionary(chatId, username))
1617
} catch (e: Exception) {
1718
println("Невозможно подключиться к БД. $e")
1819
return

src/main/kotlin/ru/ifedorov/telegrambot/data/db/DatabaseConnection.kt

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,17 @@ import java.sql.DriverManager
66
const val DATABASE_NAME = "data.db"
77

88
object DatabaseConnection {
9-
val connection: Connection = DriverManager.getConnection("jdbc:sqlite:${DATABASE_NAME}").apply {
10-
createStatement().use { statement ->
11-
statement.execute("PRAGMA journal_mode=WAL;")
12-
statement.execute("PRAGMA foreign_keys=ON;")
13-
statement.execute("PRAGMA busy_timeout=10000;")
14-
}
15-
}
16-
17-
init {
18-
connection.createStatement().use { statement ->
19-
statement.executeUpdate(
20-
"""
21-
CREATE TABLE IF NOT EXISTS words (
22-
id INTEGER PRIMARY KEY AUTOINCREMENT,
23-
text TEXT UNIQUE,
24-
translate TEXT
25-
);
26-
""".trimIndent()
27-
)
28-
29-
statement.executeUpdate(
30-
"""
31-
CREATE TABLE IF NOT EXISTS users (
32-
id INTEGER PRIMARY KEY AUTOINCREMENT,
33-
username TEXT,
34-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
35-
chat_id INTEGER UNIQUE
36-
);
37-
""".trimIndent()
38-
)
39-
40-
statement.executeUpdate(
41-
"""
42-
CREATE TABLE IF NOT EXISTS user_answers (
43-
user_id INTEGER,
44-
word_id INTEGER,
45-
correct_answer_count INTEGER,
46-
updated_at TIMESTAMP,
47-
FOREIGN KEY(user_id) REFERENCES users(id),
48-
FOREIGN KEY(word_id) REFERENCES words(id),
49-
UNIQUE(user_id, word_id)
50-
);
51-
""".trimIndent()
52-
)
9+
val connection: Connection = try {
10+
DriverManager.getConnection("jdbc:sqlite:${DATABASE_NAME}").apply {
11+
createStatement().use { statement ->
12+
statement.execute("PRAGMA journal_mode=WAL;")
13+
statement.execute("PRAGMA foreign_keys=ON;")
14+
statement.execute("PRAGMA busy_timeout=10000;")
15+
}
5316
}
17+
} catch (e: Exception) {
18+
println("Не удалось подключиться к БД: ${e.message}")
19+
throw IllegalStateException("БД недоступна. $e")
5420
}
5521

5622
fun close() {

src/main/kotlin/ru/ifedorov/telegrambot/data/db/DatabaseUserDictionary.kt

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,84 @@ package ru.ifedorov.telegrambot.data.db
22

33
import ru.ifedorov.telegrambot.trainer.IUserDictionary
44
import ru.ifedorov.telegrambot.trainer.model.Word
5+
import java.io.File
56
import java.sql.Connection
67
import java.sql.ResultSet
78

89
const val DEFAULT_LEARNING_THRESHOLD = 3
910

1011
class DatabaseUserDictionary(
1112
private val chatId: Long,
13+
private val username: String,
1214
private val learningThreshold: Int = DEFAULT_LEARNING_THRESHOLD
1315
) : IUserDictionary {
1416

1517
private val connection: Connection = DatabaseConnection.connection
18+
private val dictionaryDataSource = DictionaryDataSource()
19+
20+
init {
21+
createTablesIfNotExists()
22+
23+
try {
24+
dictionaryDataSource.updateDictionary()
25+
} catch (e: Exception) {
26+
println("Ошибка загрузки словаря. ${e.message}")
27+
}
28+
}
29+
30+
fun updateDictionaryFromFile(file: File) {
31+
dictionaryDataSource.updateDictionary(file)
32+
}
33+
34+
private fun createTablesIfNotExists() {
35+
DatabaseConnection.connection.createStatement().use { statement ->
36+
statement.executeUpdate(
37+
"""
38+
CREATE TABLE IF NOT EXISTS words (
39+
id INTEGER PRIMARY KEY AUTOINCREMENT,
40+
text TEXT UNIQUE,
41+
translate TEXT
42+
);
43+
""".trimIndent()
44+
)
45+
46+
statement.executeUpdate(
47+
"""
48+
CREATE TABLE IF NOT EXISTS users (
49+
id INTEGER PRIMARY KEY AUTOINCREMENT,
50+
username TEXT,
51+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
52+
chat_id INTEGER UNIQUE
53+
);
54+
""".trimIndent()
55+
)
56+
57+
statement.executeUpdate(
58+
"""
59+
CREATE TABLE IF NOT EXISTS user_answers (
60+
user_id INTEGER,
61+
word_id INTEGER,
62+
correct_answer_count INTEGER,
63+
updated_at TIMESTAMP,
64+
FOREIGN KEY(user_id) REFERENCES users(id),
65+
FOREIGN KEY(word_id) REFERENCES words(id),
66+
UNIQUE(user_id, word_id)
67+
);
68+
""".trimIndent()
69+
)
70+
}
71+
}
1672

1773
private fun getUserId(): Int {
1874
connection.prepareStatement(
1975
"""
20-
INSERT INTO users (chat_id, created_at)
21-
VALUES (?, CURRENT_TIMESTAMP)
76+
INSERT INTO users (username, chat_id, created_at)
77+
VALUES (?, ?, CURRENT_TIMESTAMP)
2278
ON CONFLICT(chat_id) DO NOTHING
2379
""".trimIndent()
2480
).use { ps ->
25-
ps.setLong(1, chatId)
81+
ps.setString(1, username)
82+
ps.setLong(2, chatId)
2683
ps.executeUpdate()
2784
}
2885

@@ -84,7 +141,6 @@ class DatabaseUserDictionary(
84141
return learnedWords
85142
}
86143

87-
88144
override fun getUnlearnedWords(): List<Word> {
89145
val userId = getUserId()
90146
val unlearnedWords = mutableListOf<Word>()

src/main/kotlin/ru/ifedorov/telegrambot/data/db/DictionaryDataSource.kt

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,55 @@
11
package ru.ifedorov.telegrambot.data.db
22

3+
import ru.ifedorov.telegrambot.data.DICTIONARY_FILE
34
import java.io.File
45

5-
fun main() {
6-
val wordsFile = File("words.txt")
7-
updateDictionary(wordsFile)
8-
}
6+
class DictionaryDataSource() {
97

10-
fun updateDictionary(wordsFile: File) {
11-
if (!wordsFile.exists()) {
12-
throw IllegalStateException("Файл словаря ${wordsFile.name} не найден")
13-
}
8+
fun updateDictionary(wordsFile: File = File(DICTIONARY_FILE)) {
9+
if (!wordsFile.exists()) {
10+
throw IllegalStateException("Файл словаря ${wordsFile.name} не найден")
11+
}
1412

15-
val connection = DatabaseConnection.connection
16-
connection.createStatement().use { statement ->
17-
statement.executeUpdate(
18-
"""
13+
val connection = DatabaseConnection.connection
14+
connection.createStatement().use { statement ->
15+
statement.executeUpdate(
16+
"""
1917
CREATE TABLE IF NOT EXISTS words (
2018
id INTEGER PRIMARY KEY AUTOINCREMENT,
2119
text TEXT UNIQUE,
2220
translate TEXT
2321
);
2422
""".trimIndent()
25-
)
23+
)
2624

27-
val wordsList = wordsFile.readLines()
28-
if (wordsList.isEmpty()) {
29-
throw IllegalStateException("Файл словаря пустой: ${wordsFile.absolutePath}")
30-
}
25+
val wordsList = wordsFile.readLines()
26+
.map { it.trim() }
27+
.filter { it.isNotEmpty() }
3128

32-
wordsList.forEach {
33-
try {
34-
val line = it.split("|")
35-
val original = line[0].trim()
36-
val translate = line[1].trim()
29+
if (wordsList.isEmpty()) {
30+
throw IllegalStateException("Файл словаря пустой: ${wordsFile.absolutePath}")
31+
}
32+
33+
wordsList.forEach {
34+
try {
35+
val line = it.split("|")
36+
val original = line[0].trim()
37+
val translate = line[1].trim()
3738

38-
connection.prepareStatement(
39-
"""
39+
connection.prepareStatement(
40+
"""
4041
INSERT INTO words (text, translate) VALUES (?, ?)
4142
ON CONFLICT(text) DO UPDATE SET translate = excluded.translate;
4243
""".trimIndent()
43-
).use { ps ->
44-
ps.setString(1, original)
45-
ps.setString(2, translate)
46-
ps.executeUpdate()
44+
).use { ps ->
45+
ps.setString(1, original)
46+
ps.setString(2, translate)
47+
ps.executeUpdate()
48+
}
49+
50+
} catch (e: IndexOutOfBoundsException) {
51+
throw IllegalStateException("Некорректное содержание файла словаря $wordsFile. $e")
4752
}
48-
49-
} catch (e: IndexOutOfBoundsException) {
50-
throw IllegalStateException("Некорректное содержание файла словаря $wordsFile. $e")
5153
}
5254
}
5355
}

src/main/kotlin/ru/ifedorov/telegrambot/telegram/Telegram.kt

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package ru.ifedorov.telegrambot.telegram
33
import ru.ifedorov.telegrambot.data.db.DatabaseConnection
44
import ru.ifedorov.telegrambot.data.db.DatabaseUserDictionary
55
import ru.ifedorov.telegrambot.telegram.service.*
6+
import ru.ifedorov.telegrambot.telegram.service.entity.GetFileResponse
67
import ru.ifedorov.telegrambot.telegram.service.entity.Update
78
import ru.ifedorov.telegrambot.trainer.LearnWordsTrainer
9+
import java.io.File
810

911
fun main(args: Array<String>) {
1012
val botToken = args[0]
@@ -34,11 +36,14 @@ fun handleUpdate(update: Update, trainers: HashMap<Long, LearnWordsTrainer>, bot
3436
?: update.callbackQuery?.message?.chat?.id
3537
?: return
3638

39+
val username = update.message?.from?.username ?: ""
3740
val message = update.message?.text
3841
val data = update.callbackQuery?.data
42+
val document = update.message?.document
3943

44+
val dictionary = DatabaseUserDictionary(chatId, username)
4045
val trainer = trainers.getOrPut(chatId) {
41-
LearnWordsTrainer(DatabaseUserDictionary(chatId))
46+
LearnWordsTrainer(dictionary)
4247
}
4348

4449
if (message == COMMAND_START || data == MENU_CALLBACK) {
@@ -57,12 +62,53 @@ fun handleUpdate(update: Update, trainers: HashMap<Long, LearnWordsTrainer>, bot
5762
botService.checkNextQuestionAndSend(trainer, chatId)
5863
}
5964

60-
data?.takeIf { it.startsWith(CALLBACK_DATA_ANSWER_PREFIX) }?.let {
61-
botService.checkAnswerAndSend(trainer, chatId, it)
65+
if (data == LOAD_NEW_WORDS_CALLBACK) {
66+
botService.sendMessage(
67+
chatId,
68+
"""
69+
Для загрузки новых слов с словарь отправьте в чат бота
70+
текстовый файл формата txt, который содержит
71+
"слово|перевод", с разделителем "|" например:
72+
73+
cat|кошка
74+
dog|собака
75+
76+
Можно добавлять несколько слов, каждое с новой строки.
77+
""".trimIndent()
78+
)
6279
}
6380

6481
if (data == RESET_CLICKED) {
6582
trainer.resetProgress()
6683
botService.sendMessage(chatId, "Прогресс сброшен")
6784
}
85+
86+
data?.takeIf { it.startsWith(CALLBACK_DATA_ANSWER_PREFIX) }?.let {
87+
botService.checkAnswerAndSend(trainer, chatId, it)
88+
}
89+
90+
document?.let { document ->
91+
val fileInfoResponse: GetFileResponse? = botService.getFileInfoFromTelegram(document.fileId)
92+
93+
if (fileInfoResponse?.ok == true && fileInfoResponse.result != null) {
94+
val filePath = fileInfoResponse.result.filePath
95+
val fileName = document.fileName
96+
97+
botService.saveTelegramFileLocally(filePath, fileName)
98+
99+
try {
100+
dictionary.updateDictionaryFromFile(File(fileName))
101+
botService.sendMessage(chatId, "Словарь успешно обновлен из файла $fileName")
102+
} catch (e: Exception) {
103+
botService.sendMessage(
104+
chatId,
105+
"Ошибка при обновлении словаря из файла. Проверьте формат файла и его содержание"
106+
)
107+
println("Не удалось обновить словарь из файла $fileName. Ошибка: ${e.message}")
108+
}
109+
110+
} else {
111+
botService.sendMessage(chatId, "Ошибка при сохранении файла")
112+
}
113+
}
68114
}

0 commit comments

Comments
 (0)