Better Futures :)

This commit is contained in:
ekardnam 2019-07-06 11:45:10 +02:00
parent 73f3cd22c5
commit c1902ff3eb
2 changed files with 58 additions and 83 deletions

View file

@ -24,10 +24,11 @@ import com.bot4s.telegram.api.RequestHandler
import com.bot4s.telegram.api.declarative.{Commands, whenOrElse} import com.bot4s.telegram.api.declarative.{Commands, whenOrElse}
import com.bot4s.telegram.clients.ScalajHttpClient import com.bot4s.telegram.clients.ScalajHttpClient
import com.bot4s.telegram.future.{Polling, TelegramBot} 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 slogging.{LogLevel, LoggerConfig, PrintLoggerFactory}
import scala.concurrent.Future import scala.concurrent.Future
import scala.util.{Failure, Success}
class Bot(val token: String) extends TelegramBot class Bot(val token: String) extends TelegramBot
with Polling with Polling
@ -43,68 +44,45 @@ class Bot(val token: String) extends TelegramBot
def getLogger() = logger def getLogger() = logger
/** def fromAdmin(msg: Message) : Boolean = msg.from.fold {
* Ensures a message is sent from a user and not from the chat // option is not present
* @param msg the message false
* @return a boolean that answers the question: is it an actual user? } {u =>
*/ val user = new BotUser(u, msg.chat, this)
def fromActualUser(msg: Message): Boolean = // we should wait for the user to be ready
msg.from.isDefined user.syncAfterInit { user =>
/**
* 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)
user.isAdmin || user.isCreator user.isAdmin || user.isCreator
} else { } { _ => false }
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
} }
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 => whenOrElse(onCommand("ban"), canBanUsers) { implicit msg =>
msg.replyToMessage match { msg.replyToMessage.fold {
case Some(message) => { reply(localizer.getString("ban.reply_to_a_message")).void
message.from match { }
case Some(u) => { { message =>
val user = new BotUser(u, message.chat, this) message.from.fold {
reply(localizer.getString("ban.invalid_user")).void
if (user.initSuccessful()) { }
{ u =>
if (!user.isAdmin && !user.isCreator) { Future {
user.ban val user = new BotUser(u, message.chat, this)
user.init() onComplete {
case Success(_) => {
if (!user.isAdmin) {
user.ban()
reply(localizer.getString("ban.successful")).void reply(localizer.getString("ban.successful")).void
} else reply(localizer.getString("ban.failed_ban_admin")).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
} }
} }
} }

View file

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