Browse Source

Better Futures :)

ekardnam 4 years ago
parent
commit
c1902ff3eb

+ 35 - 57
src/main/scala/org/congressodeiradicali/karlmarx/Bot.scala

@@ -24,10 +24,11 @@ import com.bot4s.telegram.api.RequestHandler
 import com.bot4s.telegram.api.declarative.{Commands, whenOrElse}
 import com.bot4s.telegram.clients.ScalajHttpClient
 import com.bot4s.telegram.future.{Polling, TelegramBot}
-import com.bot4s.telegram.models.{ChatType, Message}
+import com.bot4s.telegram.models.Message
 import slogging.{LogLevel, LoggerConfig, PrintLoggerFactory}
 
 import scala.concurrent.Future
+import scala.util.{Failure, Success}
 
 class Bot(val token: String) extends TelegramBot
   with Polling
@@ -43,68 +44,45 @@ class Bot(val token: String) extends TelegramBot
 
   def getLogger() = logger
 
-  /**
-    * Ensures a message is sent from a user and not from the chat
-    * @param msg the message
-    * @return a boolean that answers the question: is it an actual user?
-    */
-  def fromActualUser(msg: Message): Boolean =
-    msg.from.isDefined
-
-  /**
-    * Ensures a message is from a group or super group
-    * @param msg the message
-    * @return answer to the question: is it a group?
-    */
-  def fromGroup(msg: Message): Boolean =
-    msg.chat.`type` == ChatType.Group || msg.chat.`type` == ChatType.Supergroup
-
-  /**
-    * Ensures a message is from a chat administrator
-    * @param msg the message
-    * @return a boolean that answers the question: is it an administrator?
-    */
-  def fromAdmin(msg: Message): Boolean =
-    if (fromActualUser(msg) && fromGroup(msg)) {
-
-      val user = new BotUser(msg.from.get, msg.chat, this)
-
+  def fromAdmin(msg: Message) : Boolean = msg.from.fold {
+    // option is not present
+    false
+  } {u =>
+    val user = new BotUser(u, msg.chat, this)
+    // we should wait for the user to be ready
+    user.syncAfterInit { user =>
       user.isAdmin || user.isCreator
-    } else {
-      false
-    }
-
-
-  def canBan(msg: Message): Boolean =
-    if (fromActualUser(msg) && fromGroup(msg)) {
-
-      val user = new BotUser(msg.from.get, msg.chat, this)
-
-      user.canBanUsers
-    } else {
-      false
-    }
-
-  onCommand("license") { implicit msg =>
-    reply("    karl-marx is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    karl-marx is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with karl-marx.  If not, see <https://www.gnu.org/licenses/>.").void
+    } { _ => false }
   }
 
+  def canBanUsers(msg: Message) : Boolean = msg.from.fold {
+    // option is not present
+    false
+  } {u =>
+    val user = new BotUser(u, msg.chat, this)
+    // we should wait for the user to be ready
+    user.syncAfterInit{user => user.canBanUsers}{_ => false}
+  }
 
-  whenOrElse(onCommand("ban"), canBan) { implicit msg =>
-    msg.replyToMessage match {
-      case Some(message) => {
-        message.from match {
-          case Some(u) => {
-            val user = new BotUser(u, message.chat, this)
-
-            if (user.initSuccessful()) {
-
-              if (!user.isAdmin && !user.isCreator) {
-                user.ban
+  whenOrElse(onCommand("ban"), canBanUsers) { implicit msg =>
+    msg.replyToMessage.fold {
+      reply(localizer.getString("ban.reply_to_a_message")).void
+    }
+    { message =>
+      message.from.fold {
+        reply(localizer.getString("ban.invalid_user")).void
+      }
+      { u =>
+        Future {
+          val user = new BotUser(u, message.chat, this)
+          user.init() onComplete {
+            case Success(_) => {
+              if (!user.isAdmin) {
+                user.ban()
                 reply(localizer.getString("ban.successful")).void
               } else reply(localizer.getString("ban.failed_ban_admin")).void
-
-            } else reply(localizer.getString("ban.failed")).void
+            }
+            case Failure(_) => reply(localizer.getString("ban.failed")).void
           }
         }
       }

+ 24 - 27
src/main/scala/org/congressodeiradicali/karlmarx/BotUser.scala

@@ -20,12 +20,10 @@ package org.congressodeiradicali.karlmarx
 import com.bot4s.telegram.methods.{GetChatMember, KickChatMember, UnbanChatMember}
 import com.bot4s.telegram.models.{Chat, ChatId, ChatMember, MemberStatus, User}
 
-import scala.concurrent.Await
 import scala.concurrent.duration.Duration
+import scala.concurrent.{Await, ExecutionContext, Future, blocking}
 import scala.util.{Failure, Success, Try}
 
-import util.control.Breaks._
-
 class BotUser(private val user: User, private val chat: Chat, private val bot: Bot) {
 
   def ban() : Unit = {
@@ -39,35 +37,33 @@ class BotUser(private val user: User, private val chat: Chat, private val bot: B
     bot.request(UnbanChatMember(ChatId.fromChat(chat.id), user.id))
   }
 
-  private var chatMember : ChatMember = {
-    val maxRetryTimes: Int = 10
-    var chatMember: ChatMember = null
-
-    breakable {
-      for (_ <- 0 to maxRetryTimes) {
-        Await.ready(bot.request(GetChatMember(ChatId.fromChat(chat.id), user.id)), Duration.Inf).value.get match {
-          case Success(cm) => {
-            chatMember = cm
-            break
-          }
-          case Failure(err) => {
-            bot.getLogger().error(s"Error when requesting ChatMember data for user ${user.id}")
-            bot.getLogger().error(err.getLocalizedMessage)
-          }
+  var chatMember: ChatMember = null
+
+  def init() : Future[Try[Unit]] = Future {
+    implicit val ec: ExecutionContext = ExecutionContext.global
+
+    blocking {
+      Await.ready(bot.request(GetChatMember(ChatId.fromChat(chat.id), user.id)), Duration.Inf).value.get match {
+        case Success(cm) => {
+          chatMember = cm
+          Success()
+        }
+        case Failure(err) => {
+          bot.getLogger().error(s"Could not retrieve information about user ${user.id}")
+          bot.getLogger().error(err.getLocalizedMessage)
+          Failure(err)
         }
       }
     }
+  }(ExecutionContext.global)
 
-    if (chatMember == null) {
-      // after all retries it still has failed the request
-      // probably there is a problem with the network or the Telegram API is down
-      // this should be notified
-      bot.getLogger().error(s"Could not request ChatMember data after ${maxRetryTimes} requests\n Is network up?")
-      null
-    } else chatMember
-  }
 
-  def initSuccessful() : Boolean = chatMember != null
+  def syncAfterInit[T](callback: BotUser => T)(failed: Throwable => T) : T = {
+    Await.ready(init(), Duration.Inf).value.get match {
+      case Success(_) => callback(this)
+      case Failure(err) => failed(err)
+    }
+  }
 
   def isCreator: Boolean = if (chatMember != null) chatMember.status == MemberStatus.Creator else false
   def isAdmin : Boolean = if (chatMember != null) chatMember.status == MemberStatus.Administrator else false
@@ -76,4 +72,5 @@ class BotUser(private val user: User, private val chat: Chat, private val bot: B
 
   def canBanUsers : Boolean = if (chatMember != null) chatMember.canRestrictMembers.getOrElse(false) else false
 
+
 }