From c1902ff3ebdd1ccef98fe5ca59d8b280dffad852 Mon Sep 17 00:00:00 2001 From: ekardnam Date: Sat, 6 Jul 2019 11:45:10 +0200 Subject: [PATCH] Better Futures :) --- .../congressodeiradicali/karlmarx/Bot.scala | 92 +++++++------------ .../karlmarx/BotUser.scala | 49 +++++----- 2 files changed, 58 insertions(+), 83 deletions(-) diff --git a/src/main/scala/org/congressodeiradicali/karlmarx/Bot.scala b/src/main/scala/org/congressodeiradicali/karlmarx/Bot.scala index 2717d6a..ff97285 100644 --- a/src/main/scala/org/congressodeiradicali/karlmarx/Bot.scala +++ b/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 .").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 } } } diff --git a/src/main/scala/org/congressodeiradicali/karlmarx/BotUser.scala b/src/main/scala/org/congressodeiradicali/karlmarx/BotUser.scala index d7933ea..3c42428 100644 --- a/src/main/scala/org/congressodeiradicali/karlmarx/BotUser.scala +++ b/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,36 +37,34 @@ 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 + 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) - } + 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 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 initSuccessful() : Boolean = chatMember != null - def isCreator: Boolean = if (chatMember != null) chatMember.status == MemberStatus.Creator else false def isAdmin : Boolean = if (chatMember != null) chatMember.status == MemberStatus.Administrator else false def isMember : Boolean = if (chatMember != null) chatMember.status == MemberStatus.Member 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 + }