From 9cc8b49459a28214d39024b443c03891173c2509 Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Thu, 15 Feb 2024 18:33:17 +0800 Subject: [PATCH] scaladoc update, and added Update.sourceTime --- project/MornyConfiguration.scala | 2 +- project/plugins.sbt | 1 + .../sukazyo/cono/morny/core/MornyCoeur.scala | 4 +- .../morny/core/bot/api/EventListener.scala | 4 +- .../core/bot/api/EventListenerManager.scala | 2 +- .../MornyOnUpdateTimestampOffsetLock.scala | 24 ++- .../cono/morny/core/http/ServiceUI.scala | 1 - .../cono/morny/reporter/MornyReport.scala | 6 +- .../social_share/external/twitter/FXApi.scala | 16 +- .../event_hack/HackerEventHandler.scala | 4 +- .../cono/morny/util/CommonFormat.scala | 2 +- .../morny/util/schedule/RoutineTask.scala | 2 +- .../cono/morny/util/schedule/Scheduler.scala | 4 +- .../morny/util/tgapi/TelegramExtensions.scala | 199 +++++++++++++++++- 14 files changed, 237 insertions(+), 34 deletions(-) diff --git a/project/MornyConfiguration.scala b/project/MornyConfiguration.scala index ff2184c..e24db4e 100644 --- a/project/MornyConfiguration.scala +++ b/project/MornyConfiguration.scala @@ -8,7 +8,7 @@ object MornyConfiguration { val MORNY_CODE_STORE = "https://github.com/Eyre-S/Coeur-Morny-Cono" val MORNY_COMMIT_PATH = "https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s" - val VERSION = "2.0.0-alpha12" + val VERSION = "2.0.0-alpha13" val VERSION_DELTA: Option[String] = None val CODENAME = "guanggu" diff --git a/project/plugins.sbt b/project/plugins.sbt index ab8db93..bfa5bed 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,3 +3,4 @@ addSbtPlugin("no.arktekk.sbt" % "aether-deploy" % "0.29.1") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.1") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.5") +//addSbtPlugin("com.glngn" % "sbt-alldocs" % "0.3.0") diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala index 2a48181..8a75a02 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala @@ -24,7 +24,7 @@ object MornyCoeur { /** A tag that shows current [[MornyCoeur!]] is running under * test mode. * - * @see [[MornyCoeur.testRun]] for more introduction for test mode. + * @see *Test Mode* in [[MornyCoeur]] for more introduction for test mode. * @since 2.0.0 */ object TestRun @@ -88,7 +88,7 @@ object MornyCoeur { * * ## Lifecycle * - * todo... + * todo * * ## Test Mode * diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListener.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListener.scala index a01e9ce..44f2d4c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListener.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListener.scala @@ -4,7 +4,7 @@ trait EventListener () { /** Determine if this event listener should be processed. * - * Default implementation is it only be [[true]] when the event + * Default implementation is it only be `true` when the event * is not ok yet (when [[EventEnv.isEventOk]] is false). * * Notice that: You should not override this method to filter some @@ -13,7 +13,7 @@ trait EventListener () { * method is just for event low-level controls. * * @param env The [[EventEnv event variable]]. - * @return [[true]] if this event listener should run; [[false]] + * @return `true` if this event listener should run; `false` * if it should not run. */ def executeFilter (using env: EventEnv): Boolean = diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala index 78466a7..6909829 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala @@ -11,7 +11,7 @@ import com.pengrad.telegrambot.UpdatesListener import scala.collection.mutable import scala.language.postfixOps -/** Contains a [[mutable.Queue]] of [[EventListener]], and delivery telegram [[Update]]. +/** Contains a [[scala.collection.mutable.Queue]] of [[EventListener]], and delivery telegram [[Update]]. * * Implemented [[process]] in [[UpdatesListener]] so it can directly used in [[com.pengrad.telegrambot.TelegramBot.setupListener]]. * diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala index 8f1c015..eacdb3a 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala @@ -2,16 +2,26 @@ package cc.sukazyo.cono.morny.core.bot.event import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Update.sourceTime +import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener { - private def checkOutdated (timestamp: Int)(using event: EventEnv): Unit = - if coeur.config.eventIgnoreOutdated && (timestamp < (coeur.coeurStartTimestamp/1000)) then - event.setEventCanceled + override def executeFilter (using env: EventEnv): Boolean = + if ( + (env.update.message != null) || + (env.update.editedMessage != null) || + (env.update.channelPost != null) || + (env.update.editedChannelPost != null) + ) + true + else false - override def onMessage (using event: EventEnv): Unit = checkOutdated(event.update.message.date) - override def onEditedMessage (using event: EventEnv): Unit = checkOutdated(event.update.editedMessage.date) - override def onChannelPost (using event: EventEnv): Unit = checkOutdated(event.update.channelPost.date) - override def onEditedChannelPost (using event: EventEnv): Unit = checkOutdated(event.update.editedChannelPost.date) + override def on (using event: EventEnv): Unit = + event.update.sourceTime match + case Some(timestamp) => + if coeur.config.eventIgnoreOutdated && (EpochMillis.fromEpochSeconds(timestamp) < coeur.coeurStartTimestamp) then + event.setEventCanceled + case None => } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/http/ServiceUI.scala b/src/main/scala/cc/sukazyo/cono/morny/core/http/ServiceUI.scala index c05dc79..404cfcd 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/http/ServiceUI.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/http/ServiceUI.scala @@ -4,7 +4,6 @@ import cats.effect.IO import cc.sukazyo.cono.morny.core.http.api.HttpService4Api import cc.sukazyo.cono.morny.data.TelegramImages import org.http4s.{HttpRoutes, MediaType} -import org.http4s.dsl.impl./ import org.http4s.dsl.io.* import org.http4s.headers.`Content-Type` diff --git a/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala b/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala index 1d26cdb..74a0c4f 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala @@ -11,7 +11,7 @@ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtm import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec import cc.sukazyo.cono.morny.util.EpochDateTime.DurationMillis import cc.sukazyo.cono.morny.util.schedule.CronTask -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Update.{extractSourceChat, extractSourceUser} +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Update.{sourceChat, sourceUser} import cc.sukazyo.cono.morny.util.CommonEncrypt.hashId import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex import com.cronutils.builder.CronBuilder @@ -206,9 +206,9 @@ class MornyReport (using coeur: MornyCoeur) { override def atEventPost (using event: EventEnv): Unit = { import event.* eventTotal += 1 - event.update.extractSourceChat match + event.update.sourceChat match case None => - event.update.extractSourceUser match + event.update.sourceUser match case None => case Some(user) => event_from_user_action << hashId(user.id).toHex diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/twitter/FXApi.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/twitter/FXApi.scala index 1b73a8e..d748b93 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/twitter/FXApi.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/twitter/FXApi.scala @@ -11,18 +11,18 @@ import io.circe.{DecodingFailure, ParsingFailure} * * @see [[https://github.com/FixTweet/FixTweet/wiki/Status-Fetch-API]] * - * @param code Status code, normally be [[200]], but can be 401 - * or [[404]] or [[500]] due to different reasons. + * @param code Status code, normally be `200`, but can be 401 + * or `404` or `500` due to different reasons. * * Related to [[message]] * @param message Status message. * - * - When [[code]] is [[200]], it should be `OK` - * - When [[code]] is [[401]], it should be `PRIVATE_TWEET`, + * - When [[code]] is `200`, it should be `OK` + * - When [[code]] is `401`, it should be `PRIVATE_TWEET`, * while in practice, it seems PRIVATE_TWEET will - * just return [[404]]. - * - When [[code]] is [[404]], it should be `NOT_FOUND` - * - When [[code]] is [[500]], it should be `API_FILE` + * just return `404`. + * - When [[code]] is `404`, it should be `NOT_FOUND` + * - When [[code]] is `500`, it should be `API_FILE` * @param tweet [[FXTweet]] content. * @since 1.3.0 * @version 2023.11.21 @@ -86,7 +86,7 @@ object FXApi { * @throws DecodingFailure When cannot decode the API response to a [[FXApi]] * object. It might be some wrong with the [[FXApi]] * model, or the remote API spec changes. - * @return a [[FXApi]] response object, with [[200]] or any other response code. + * @return a [[FXApi]] response object, with `200` or any other response code. */ @throws[SttpClientException|ParsingFailure|DecodingFailure] def status (screen_name: Option[String], id: String, translate_to: Option[String] = None): FXApi = diff --git a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/HackerEventHandler.scala b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/HackerEventHandler.scala index e6f46eb..21e5f07 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/HackerEventHandler.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/HackerEventHandler.scala @@ -12,8 +12,8 @@ class HackerEventHandler (using hacker: EventHacker)(using coeur: MornyCoeur) ex override def on (using event: EventEnv): Unit = given update: Update = event.update if hacker.trigger( - update.extractSourceChat.map[Long](_.id).getOrElse(0), - update.extractSourceUser.map[Long](_.id).getOrElse(0) + update.sourceChat.map[Long](_.id).getOrElse(0), + update.sourceUser.map[Long](_.id).getOrElse(0) ) then event.setEventOk diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/CommonFormat.scala b/src/main/scala/cc/sukazyo/cono/morny/util/CommonFormat.scala index d562d58..90ad990 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/CommonFormat.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/CommonFormat.scala @@ -33,7 +33,7 @@ object CommonFormat { * @param utcOffset the hour offset of the time zone, the time-zone controls * which local time describe will use. * - * for example, timestamp [[0]] describes 1970-1-1 00:00:00 in + * for example, timestamp `0` describes 1970-1-1 00:00:00 in * UTC+0, so, use the `timestamp` `0` and `utfOffset` `0` will * returns `"1970-1-1 00:00:00:000"`; however, at the same time, * in UTC+8, the local time is 1970-1-1 08:00:00:000, so use diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/schedule/RoutineTask.scala b/src/main/scala/cc/sukazyo/cono/morny/util/schedule/RoutineTask.scala index d7904ab..a2ae0f3 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/schedule/RoutineTask.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/schedule/RoutineTask.scala @@ -43,7 +43,7 @@ trait RoutineTask extends Task { * * @param previousRoutineScheduledTimeMillis The previous task routine's * scheduled time. - * @return The next task routine's scheduled time, or [[null]] means end + * @return The next task routine's scheduled time, or `null` means end * of the task. */ def nextRoutineTimeMillis (previousRoutineScheduledTimeMillis: EpochMillis): EpochMillis|Null diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala b/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala index 4a07a24..87d4aac 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala @@ -151,7 +151,7 @@ class Scheduler { schedule(task) this /** Add one task to scheduler task queue. - * @return [[true]] if the task is added. + * @return `true` if the task is added. */ def schedule (task: Task): Boolean = taskList.synchronized: @@ -175,7 +175,7 @@ class Scheduler { * If the removal task is running, the method will wait for the current run * complete (and current run post effect complete), then do remove. * - * @return [[true]] if the task is in task queue or is running, and have been + * @return `true` if the task is in task queue or is running, and have been * succeed removed from task queue. */ def cancel (task: Task): Boolean = diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala index 5d48f76..1449c3e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala @@ -1,6 +1,7 @@ package cc.sukazyo.cono.morny.util.tgapi import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException +import cc.sukazyo.cono.morny.util.EpochDateTime.{EpochMillis, EpochSeconds} import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.model.* import com.pengrad.telegrambot.request.{BaseRequest, GetChatMember} @@ -12,8 +13,31 @@ object TelegramExtensions { object Bot { extension (bot: TelegramBot) { + /** Try sync execute a [[BaseRequest request]], and throws [[EventRuntimeException]] + * when it fails. + * + * It will use [[Telegram.execute]] to execute the request, and check if the response is failed. + * + * If the request returned a [[BaseResponse response]], the [[BaseResponse.isOk]] will be checked. + * if it is false, the method will ended with a [[EventRuntimeException.ActionFailed]], which + * message is param [[onError_message]](or [[BaseResponse.errorCode]] if it is empty). + * + * If the request failed in the client (that means [[TelegramBot.execute]] call failed with Exceptions), + * this method will ended with a [[EventRuntimeException.ClientFailed]]. + * + * @param request The request needed to be run. + * @param onError_message The exception message that will be thrown when the [[request]]'s + * [[BaseResponse response]] is not ok([[BaseResponse.isOk isOk()]] == false) + * @tparam T Type of the request + * @tparam R Type of the response that request should returns. + * @throws EventRuntimeException Whenever the request's response is not ok, or the request does not + * return a response. See above for more info. + * @return The succeed response (which is returned by [[TelegramBot.execute]]) as is. + * + * @since 1.0.0 + */ @throws[EventRuntimeException] - def exec [T <: BaseRequest[T, R], R <: BaseResponse] (request: T, onError_message: String = ""): R = { + def exec [T <: BaseRequest[T, R], R <: BaseResponse] (request: BaseRequest[T, R], onError_message: String = ""): R = { try { val response = bot execute request if response isOk then return response @@ -31,7 +55,41 @@ object TelegramExtensions { object Update { extension (update: Update) { - def extractSourceChat: Option[Chat] = + /** Get the [[Chat]] that the update comes from. + * + * For the update types that is non-chat related, or the update types that framework does not + * supported yet, this method will return [[None]]. + * + * ### Supported Update Types + * + * Belows are the supported update types that will return a valid [[Chat]]. + * + * - [[Update.message]]: Chat that the message belongs. + * - [[Update.editedMessage]]: Chat that the edited message belongs. + * - [[Update.channelPost]]: Chat that the channel post message belongs. + * - [[Update.editedChannelPost]]: Chat that the edited channel post message belongs. + * - [[Update.callbackQuery]]: Chat that the callback query's source message (a callback query + * is triggered from a inline message button, that inline message is the source message) belongs. + * - [[Update.myChatMember]]: Chat that where member status of current bot changed there. + * - [[Update.chatMember]]: Chat that any one user's member status changed there. + * - [[Update.chatJoinRequest]]: Chat that the join request comes from. + * + * Belows are the known update types that is not chat related, so there's only a [[None]] returned. + * + * - [[Update.inlineQuery]] + * - [[Update.chosenInlineResult]] + * - [[Update.shippingQuery]] + * - [[Update.preCheckoutQuery]] + * - [[Update.poll]] + * - [[Update.pollAnswer]] + * + * Supported up to Telegram Bot API 6.2 + * + * @return An [[Option]] either contains a [[Chat]] that the update comes from, or [[None]] if there's + * no user or update type is unsupported. + * @since 2.0.0 + */ + def sourceChat: Option[Chat] = if (update.message != null) Some(update.message.chat) else if (update.editedMessage != null) Some(update.editedMessage.chat) else if (update.channelPost != null) Some(update.channelPost.chat) @@ -48,7 +106,42 @@ object TelegramExtensions { else if (update.chatJoinRequest != null) Some(update.chatJoinRequest.chat) else None - def extractSourceUser: Option[User] = + /** Get the [[User]] that the update comes from. + * + * For the update types that is non-user related, or the update types that framework does not + * supported yet, this method will return [[None]]. + * + * ### Supported Update Types + * + * Belows are the supported update types that will return a valid [[User]]. + * + * - [[Update.message]]: User that sent the message. + * - [[Update.editedMessage]]: User that sent the message. Notice that is the user who ORIGINAL + * SENT the message but NOT EDITED the message due to the API limitation, while in current Bot + * API version (v7.0), editing other's message is not allowed so it should have no problem. + * - [[Update.inlineQuery]]: User that executing the inline query. + * - [[Update.chosenInlineResult]]: User that chosen the inline result of a inline query. + * - [[Update.callbackQuery]]: User that triggered the callback query. + * - [[Update.shippingQuery]]: User that sent the shipping query. + * - [[Update.preCheckoutQuery]]: User that sent the pre-checkout query. + * - [[Update.pollAnswer]]: User that answered a un-anonymous poll. + * - [[Update.myChatMember]]: Current bot that my member status changed. + * - [[Update.chatMember]]: User that their member status changed. + * - [[Update.chatJoinRequest]]: User that sent a join request. + * + * Belows are the known update types that is not user related, so there's only a [[None]] returned. + * + * - [[Update.channelPost]] + * - [[Update.editedChannelPost]] + * - [[Update.poll]] (odd, but it is no sender in the latest Bot API 7.0) + * + * Supported up to Telegram Bot API 6.2 + * + * @return An [[Option]] either contains a [[User]] that the update comes from, or [[None]] if there's + * no user or update type is unsupported. + * @since 2.0.0 + */ + def sourceUser: Option[User] = if (update.message != null) Some(update.message.from) else if (update.editedMessage != null) Some(update.editedMessage.from) else if (update.channelPost != null) None @@ -65,13 +158,113 @@ object TelegramExtensions { else if (update.chatJoinRequest != null) Some(update.chatJoinRequest.from) else None + /** Date time of the update. + * + * This date-time is guessed from the update content -- it may not response the accurate or even real + * time when the update happens. Some update types contains no related time information, so it can only + * returns [[None]]. + * + * Telegram uses UNIX seconds as the time unit, and Java Telegram Bot API (which this project + * dependents this) use [[Int]] to store it. so the returned time will be formatted to [[EpochSeconds]] + * which also uses UNIX seconds as the time unit, and stored in type [[Int]]. + * + * ### Supported Update Types + * + * Belows are the supported update types that will return a valid time. + * + * - [[Update.message]]: Time that the message is sent. Should be fixed and accurate. + * - [[Update.editedMessage]]: Time that the message is edited. Can be changed when the message is + * edited again, but should be the fixed editing time in the specific update. + * - [[Update.channelPost]]: Time that the channel post message is sent. Should be fixed and accurate. + * - [[Update.editedChannelPost]]: Time that the channel post message is edited. Same with + * [[Update.editedMessage]] + * - [[Update.myChatMember]]: Time that the member status of current bot changes done. + * - [[Update.chatMember]]: Time that one user's member status changes done. + * - [[Update.chatJoinRequest]]: Time that the join request is sent. + * + * Belows have no any time information, so there's only a [[None]] returned. + * + * - [[Update.inlineQuery]] + * - [[Update.chosenInlineResult]] + * - [[Update.callbackQuery]] + * - [[Update.shippingQuery]] + * - [[Update.preCheckoutQuery]] + * - [[Update.poll]] + * - [[Update.pollAnswer]] + * + * Supported up to Telegram Bot API 6.2 + * + * @return An [[Option]] either contains a [[EpochSeconds]] that may be the time when update happens, + * or [[None]] if there's unsupported. + * @since 2.0.0 + */ + def sourceTime: Option[EpochSeconds] = + if (update.message != null) Some(update.message.date) + else if (update.editedMessage != null) Some(update.editedMessage.editDate) + else if (update.channelPost != null) Some(update.channelPost.date) + else if (update.editedChannelPost != null) Some(update.editedChannelPost.editDate) + else if (update.inlineQuery != null) None + else if (update.chosenInlineResult != null) None + else if (update.callbackQuery != null) None + else if (update.shippingQuery != null) None + else if (update.preCheckoutQuery != null) None + else if (update.poll != null) None + else if (update.pollAnswer != null) None + else if (update.myChatMember != null) Some(update.myChatMember.date) + else if (update.chatMember != null) Some(update.chatMember.date) + else if (update.chatJoinRequest != null) Some(update.chatJoinRequest.date) + else None + }} object Chat { extension (chat: Chat) { + /** Check if a user is a member of this chat. + * + * It is equivalent to `memberHasPermission(user, ChatMember.Status.member)`. + * + * It checks if the user's member status is [[ChatMember.Status.member]], so if the member is in + * this chat but they are been [[ChatMember.Status.restricted]], it will be treated as *not a member*. + * + * It needs to execute a request getting chat member, so it requires a live [[TelegramBot]] instance, + * and the bot needs to be a member of this chat so that it can read the chat member. + * + * **Notice:** This method will execute a request to the Telegram Bot API, so it may be slow. + * + * @see [[memberHasPermission]] + * + * @param user The user that want to check if they are a member of this chat. + * @return [[true]] when the user is a member of this chat, [[false]] otherwise. + * + * @since 1.0.0 + */ def hasMember (user: User) (using TelegramBot): Boolean = memberHasPermission(user, ChatMember.Status.member) + /** Check if a user has a permission level in this chat. + * + * It checks if the user's member status is equal or greater than the required permission level. + * + * Due to API does not implemented a permission level system, so inside this method, there's a + * permission level table: + * + * | [[ChatMember.Status]] | Permission Level | + * |----------------------|-----------------:| + * | [[ChatMember.Status.creator]] | 10 | + * | [[ChatMember.Status.administrator]] | 3 | + * | [[ChatMember.Status.member]] | 1 | + * | [[ChatMember.Status.restricted]] | -1 | + * | [[ChatMember.Status.left]] | -3 | + * | [[ChatMember.Status.kicked]] | -5 | + * + * **Notice:** This method will execute a request to the Telegram Bot API, so it may be slow. + * + * @param user The user that wanted to check if they have the permission. + * @param permission The required permission level. + * @param bot A live [[TelegramBot]] that will be used to execute the getChatMember request. It + * should be a member of this chat so that can read the chat member for this method works. + * @return [[true]] if the user have the permission or higher permission, [[false]] otherwise. + */ def memberHasPermission (user: User, permission: ChatMember.Status) (using bot: TelegramBot): Boolean = { //noinspection ScalaUnusedSymbol