From 3ef7c6adb01c6ac70cf915f6fc7c575fd00b9c09 Mon Sep 17 00:00:00 2001 From: Mac135135 Date: Sun, 10 Nov 2024 15:11:40 +0300 Subject: [PATCH] Added an periodical backup to the telegram bot --- .../core/backup/ExternalBackupStorage.kt | 40 +++++++++++++++++++ .../backup/PeriodicalBackupService.kt | 5 ++- .../PeriodicalBackupSettingsFragment.kt | 22 +++++----- app/src/main/res/values/strings.xml | 8 ++++ 4 files changed, 62 insertions(+), 13 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/ExternalBackupStorage.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/ExternalBackupStorage.kt index ccef8b64d..b625241eb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/ExternalBackupStorage.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/ExternalBackupStorage.kt @@ -7,6 +7,12 @@ import androidx.documentfile.provider.DocumentFile import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible +import kotlinx.coroutines.withContext +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.asRequestBody import okio.buffer import okio.sink import okio.source @@ -15,6 +21,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import java.io.File +import java.io.IOException import javax.inject.Inject class ExternalBackupStorage @Inject constructor( @@ -86,3 +93,36 @@ class ExternalBackupStorage @Inject constructor( return checkNotNull(root) { "Cannot obtain DocumentFile from $uri" } } } +class TelegramBackupUploader @Inject constructor(private val settings: AppSettings) { + + private val client = OkHttpClient() + + suspend fun uploadBackupToTelegram(file: File) = withContext(Dispatchers.IO) { + val botToken = "7455491254:AAGYJKgpP1DZN3d9KZfb8tvtIdaIMxUayXM" + val chatId = settings.telegramChatId + + if (botToken.isNullOrEmpty() || chatId.isNullOrEmpty()) { + throw IllegalStateException("Telegram API key or chat ID not set in settings.") + } + + val mediaType = "application/zip".toMediaTypeOrNull() + val requestBody = file.asRequestBody(mediaType) + + val multipartBody = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("chat_id", chatId) + .addFormDataPart("document", file.name, requestBody) + .build() + + val request = Request.Builder() + .url("https://api.telegram.org/bot$botToken/sendDocument") + .post(multipartBody) + .build() + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) { + throw IOException("Failed to send backup to Telegram: ${response.message}") + } + } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupService.kt index 81602e128..23205c433 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupService.kt @@ -5,6 +5,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.backup.BackupRepository import org.koitharu.kotatsu.core.backup.BackupZipOutput import org.koitharu.kotatsu.core.backup.ExternalBackupStorage +import org.koitharu.kotatsu.core.backup.TelegramBackupUploader import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.CoroutineIntentService import javax.inject.Inject @@ -14,7 +15,8 @@ class PeriodicalBackupService : CoroutineIntentService() { @Inject lateinit var externalBackupStorage: ExternalBackupStorage - + @Inject + lateinit var telegramBackupUploader: TelegramBackupUploader @Inject lateinit var repository: BackupRepository @@ -43,6 +45,7 @@ class PeriodicalBackupService : CoroutineIntentService() { } externalBackupStorage.put(output.file) externalBackupStorage.trim(settings.periodicalBackupMaxCount) + telegramBackupUploader.uploadBackupToTelegram(output.file) } finally { output.file.delete() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt index d7bee53d3..416fd4247 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/backup/PeriodicalBackupSettingsFragment.kt @@ -56,16 +56,14 @@ class PeriodicalBackupSettingsFragment : BasePreferenceFragment(R.string.periodi } val checkApiButton = Preference(requireContext()).apply { key = "check_api_working" - title = "Проверить работу API" - summary = "Нажмите для проверки работы Telegram Bot API" + title = context.getString(R.string.api_telegram_check) + summary = context.getString(R.string.api_check_desc) } checkApiButton.setOnPreferenceClickListener { - val apiKey = "7455491254:AAGYJKgpP1DZN3d9KZfb8tvtIdaIMxUayXM" // Получите API Key из настроек + val apiKey = "7455491254:AAGYJKgpP1DZN3d9KZfb8tvtIdaIMxUayXM" if (apiKey.isNotEmpty()) { checkTelegramBotApiKey(apiKey) - } else { - Toast.makeText(requireContext(), "Введите API Key в настройках!", Toast.LENGTH_SHORT).show() } true } @@ -84,14 +82,14 @@ class PeriodicalBackupSettingsFragment : BasePreferenceFragment(R.string.periodi override fun onResponse(call: Call, response: Response) { requireActivity().runOnUiThread { if (response.isSuccessful) { - sendMessageToTelegram(apiKey, "Kotatsu's backup in Telegram is working!!") + context?.let { sendMessageToTelegram(apiKey, it.getString(R.string.api_is_work)) } } - } + } } override fun onFailure(call: Call, e: IOException) { requireActivity().runOnUiThread { - Toast.makeText(requireContext(), "Network error! Check your Net", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), R.string.api_net_error, Toast.LENGTH_SHORT).show() } } }) @@ -110,7 +108,7 @@ class PeriodicalBackupSettingsFragment : BasePreferenceFragment(R.string.periodi private fun sendMessageToTelegram(apiKey: String, message: String) { val chatId = settings.telegramChatId if (chatId.isNullOrEmpty()) { - Toast.makeText(requireContext(), "Chat ID is not set!", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), R.string.id_not_set, Toast.LENGTH_SHORT).show() return } @@ -124,16 +122,16 @@ class PeriodicalBackupSettingsFragment : BasePreferenceFragment(R.string.periodi override fun onResponse(call: Call, response: Response) { requireActivity().runOnUiThread { if (response.isSuccessful) { - Toast.makeText(requireContext(), "Success! Check Telegram Bot", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), R.string.api_check_success, Toast.LENGTH_SHORT).show() } else { - Toast.makeText(requireContext(), "OOPS! Something went wrong", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), R.string.api_check_error, Toast.LENGTH_SHORT).show() } } } override fun onFailure(call: Call, e: IOException) { requireActivity().runOnUiThread { - Toast.makeText(requireContext(), "Network error!", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), R.string.api_error, Toast.LENGTH_SHORT).show() } } }) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6eb2d8e4a..5c4dadab1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -763,4 +763,12 @@ "]]> Access denied (403) Max number of backups + Check API work + Click to check the operation of the Telegram Bot API + Kotatsu backup in Telegram is working!! + Network error! Check your Net + Chat ID is not set! + Success! Check Telegram Bot + OOPS! Something went wrong + Network error!