Jelajahi Sumber

changed city name with city id in database

Vadik Sirekanyan 5 tahun lalu
induk
melakukan
efb0db9fe9

+ 1 - 0
.gitignore

@@ -2,3 +2,4 @@
 /.idea
 /build
 /bot.properties
+/bot.properties.debug

+ 28 - 11
src/main/kotlin/com/sirekanyan/andersrobot/AndersRobot.kt

@@ -12,7 +12,10 @@ import org.telegram.telegrambots.meta.api.objects.Update
 import org.telegram.telegrambots.meta.generics.LongPollingBot
 import org.telegram.telegrambots.util.WebhookUtils
 
+private const val DEFAULT_CITY_ID = 524901L // Moscow
+
 val botName = Config[BOT_USERNAME]
+val adminId = Config[ADMIN_ID].toLong()
 
 class AndersRobot : DefaultAbsSender(DefaultBotOptions()), LongPollingBot {
 
@@ -24,11 +27,20 @@ class AndersRobot : DefaultAbsSender(DefaultBotOptions()), LongPollingBot {
     override fun getBotToken(): String = Config[BOT_TOKEN]
 
     override fun onUpdateReceived(update: Update) {
+        try {
+            onUpdate(update)
+        } catch (exception: Exception) {
+            logError("Cannot handle update", exception)
+            logError(update)
+        }
+    }
+
+    private fun onUpdate(update: Update) {
         val message = update.message
         val chatId = message.chatId
         val language = message.from?.languageCode?.takeIf { it in supportedLanguages }
         println("${message.from?.id} (chat $chatId) => ${message.text}")
-        val isBetterAccuracy = message.chatId == 314085103L || message.chatId == 106547051L
+        val isBetterAccuracy = message.chatId == 314085103L || message.chatId == adminId
         val accuracy = if (isBetterAccuracy) 1 else 0
         val cityCommand = getCityCommand(message.text)
         val addCityCommand = getAddCityCommand(message.text)
@@ -53,15 +65,16 @@ class AndersRobot : DefaultAbsSender(DefaultBotOptions()), LongPollingBot {
                 if (temperature == null) {
                     sendText(chatId, "Не знаю такого города")
                 } else {
-                    repository.putCity(chatId, addCityCommand)
+                    repository.putCity(chatId, temperature.id)
                     showWeather(chatId, accuracy, language)
                 }
             }
             !delCityCommand.isNullOrEmpty() -> {
-                if (repository.deleteCity(chatId, delCityCommand)) {
-                    sendText(chatId, "Удалено")
-                } else {
-                    sendText(chatId, "Нет такого города")
+                val temperature = weather.getTemperature(delCityCommand, language)
+                when {
+                    temperature == null -> sendText(chatId, "Не знаю такого города")
+                    repository.deleteCity(chatId, temperature.id) -> sendText(chatId, "Удалено")
+                    else -> sendText(chatId, "Нет такого города")
                 }
             }
             isCelsiusCommand(message.text) -> {
@@ -75,15 +88,19 @@ class AndersRobot : DefaultAbsSender(DefaultBotOptions()), LongPollingBot {
 
     private fun showWeather(chatId: Long, accuracy: Int, language: String?) {
         val dbCities = repository.getCities(chatId)
-        val cities = if (dbCities.isEmpty()) listOf("Moscow") else dbCities
-        val temperatures = weather.getTemperatures(cities, accuracy, language)
-        if (temperatures.isNotEmpty()) {
-            sendText(chatId, temperatures.joinToString("\n"))
-        }
+        val cities = if (dbCities.isEmpty()) listOf(DEFAULT_CITY_ID) else dbCities
+        val temperatures = weather.getTemperatures(cities, language)
+        check(temperatures.isNotEmpty())
+        sendText(chatId, temperatures.joinToString("\n") { it.format(accuracy) })
     }
 
     override fun clearWebhook() {
+        logInfo("Cleared.")
         WebhookUtils.clearWebhook(this)
     }
 
+    override fun onClosing() {
+        logInfo("Closed.")
+    }
+
 }

+ 1 - 0
src/main/kotlin/com/sirekanyan/andersrobot/api/Weather.kt

@@ -6,6 +6,7 @@ import java.io.File
 @Serializable
 data class Weather(
     val main: MainInfo,
+    val id: Long,
     val name: String,
     val sys: System,
     val weather: List<Condition>

+ 11 - 10
src/main/kotlin/com/sirekanyan/andersrobot/api/WeatherApi.kt

@@ -18,25 +18,26 @@ class WeatherApi {
     private val comparator = compareBy<Weather> { it.sys.country.toLowerCase().replace("ru", "aa") }.thenBy { it.name }
 
     fun getTemperature(city: String, language: String?): Weather? = runBlocking {
-        getWeather(city, language)
+        println("getting $city")
+        getWeather("q" to city, "lang" to language)
     }
 
-    fun getTemperatures(cities: List<String>, accuracy: Int, language: String?): List<String> = runBlocking {
-        cities.map { city -> async { getWeather(city, language) } }
+    fun getTemperatures(cities: List<Long>, language: String?): List<Weather> = runBlocking {
+        println("getting $cities")
+        cities.map { city -> async { getWeather("id" to city, "lang" to language) } }
             .mapNotNull { it.await() }
             .sortedWith(comparator)
-            .map { it.format(accuracy) }
     }
 
-    private suspend fun getWeather(city: String, language: String?): Weather? =
+    private suspend fun getWeather(vararg params: Pair<String, Any?>): Weather? =
         try {
-            println("getting $city")
             val response: String = httpClient.get(WEATHER_URL) {
-                parameter("q", city)
-                parameter("units", "metric")
-                if (language != null) {
-                    parameter("lang", language)
+                params.forEach { (k, v) ->
+                    if (v != null) {
+                        parameter(k, v)
+                    }
                 }
+                parameter("units", "metric")
                 parameter("appid", apiKey)
             }
             val json = Json { ignoreUnknownKeys = true }

+ 2 - 1
src/main/kotlin/com/sirekanyan/andersrobot/config/Config.kt

@@ -3,7 +3,8 @@ package com.sirekanyan.andersrobot.config
 import java.io.File
 import java.util.*
 
-private const val CONFIG_FILE = "bot.properties"
+private const val DEBUG_SUFFIX = ""
+private const val CONFIG_FILE = "bot.properties$DEBUG_SUFFIX"
 
 object Config {
 

+ 34 - 0
src/main/kotlin/com/sirekanyan/andersrobot/extensions/Logger.kt

@@ -0,0 +1,34 @@
+package com.sirekanyan.andersrobot.extensions
+
+import com.sirekanyan.andersrobot.adminId
+import org.telegram.telegrambots.meta.api.methods.send.SendDocument
+import org.telegram.telegrambots.meta.api.methods.send.SendMessage
+import org.telegram.telegrambots.meta.api.objects.InputFile
+import org.telegram.telegrambots.meta.api.objects.Update
+import org.telegram.telegrambots.meta.bots.AbsSender
+import java.io.ByteArrayOutputStream
+import java.io.PrintStream
+
+fun AbsSender.logInfo(text: String) {
+    execute(SendMessage(adminId, text))
+}
+
+fun AbsSender.logError(text: String, throwable: Throwable) {
+    try {
+        val stream = ByteArrayOutputStream()
+        throwable.printStackTrace(PrintStream(stream))
+        execute(SendMessage(adminId, "$text\n\n$stream"))
+    } catch (exception: Exception) {
+        exception.printStackTrace()
+    }
+}
+
+fun AbsSender.logError(update: Update) {
+    try {
+        val input = update.toString().byteInputStream()
+        val document = InputFile(input, "update.txt")
+        execute(SendDocument().setChatId(adminId).setDocument(document))
+    } catch (exception: Exception) {
+        exception.printStackTrace()
+    }
+}

+ 14 - 14
src/main/kotlin/com/sirekanyan/andersrobot/repository/CityRepository.kt

@@ -6,21 +6,21 @@ import java.sql.ResultSet
 private const val CREATE_TABLE = """
     create table if not exists cities (
         chat bigint,
-        name varchar(200),
-        primary key (chat, name)
+        city bigint,
+        primary key (chat, city)
     );
     """
 private const val SELECT_CITIES =
-    "select name from cities where chat = ?"
+    "select city from cities where chat = ?"
 private const val INSERT_CITY =
-    "insert into cities (chat, name) values (?, ?) on conflict do nothing"
+    "insert into cities (chat, city) values (?, ?) on conflict do nothing"
 private const val DELETE_CITY =
-    "delete from cities where chat = ? and name = ?"
+    "delete from cities where chat = ? and city = ?"
 
 interface CityRepository {
-    fun getCities(chat: Long): List<String>
-    fun putCity(chat: Long, name: String): Boolean
-    fun deleteCity(chat: Long, name: String): Boolean
+    fun getCities(chat: Long): List<Long>
+    fun putCity(chat: Long, city: Long): Boolean
+    fun deleteCity(chat: Long, city: Long): Boolean
 }
 
 class CityRepositoryImpl(url: String) : CityRepository {
@@ -31,29 +31,29 @@ class CityRepositoryImpl(url: String) : CityRepository {
         }
     }
 
-    override fun getCities(chat: Long): List<String> =
+    override fun getCities(chat: Long): List<Long> =
         connection.prepareStatement(SELECT_CITIES)
             .run {
                 setLong(1, chat)
                 executeQuery()
             }
             .map {
-                getString(1)
+                getLong(1)
             }
 
-    override fun putCity(chat: Long, name: String): Boolean =
+    override fun putCity(chat: Long, city: Long): Boolean =
         connection.prepareStatement(INSERT_CITY)
             .run {
                 setLong(1, chat)
-                setString(2, name)
+                setLong(2, city)
                 executeUpdate() == 1
             }
 
-    override fun deleteCity(chat: Long, name: String): Boolean =
+    override fun deleteCity(chat: Long, city: Long): Boolean =
         connection.prepareStatement(DELETE_CITY)
             .run {
                 setLong(1, chat)
-                setString(2, name)
+                setLong(2, city)
                 executeUpdate() == 1
             }