mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2026-11-08 12:53:09 +00:00
Add Bot API 10.2 subscription updates support
New BotSubscriptionUpdated payload (user, invoicePayload, typed state: Canceled/Active/Failed/Unknown) in dev.inmo.tgbotapi.types.payments and the BotSubscriptionUpdatedUpdate for the new subscription update (UPDATE_SUBSCRIPTION), FlowsUpdatesFilter.botSubscriptionUpdatedUpdatesFlow, onBotSubscriptionUpdated trigger, waitBotSubscriptionUpdated expectation and class casts. Notes the Telegram-side Mini App method-origin security hardening. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0c329326f7
commit
3d47034f54
15 files changed
+351
No files matched your search
@@ -15851,6 +15851,7 @@ public final class dev/inmo/tgbotapi/types/UpdateTypesKt {
|
||||
public static final field UPDATE_PURCHASED_PAID_MEDIA Ljava/lang/String;
|
||||
public static final field UPDATE_REMOVE_CHAT_BOOST Ljava/lang/String;
|
||||
public static final field UPDATE_SHIPPING_QUERY Ljava/lang/String;
|
||||
public static final field UPDATE_SUBSCRIPTION Ljava/lang/String;
|
||||
public static final fun getALL_UPDATES_LIST ()Ljava/util/List;
|
||||
public static final fun getALL_UPDATES_LIST_WITHOUT_REACTIONS ()Ljava/util/List;
|
||||
}
|
||||
@@ -32796,6 +32797,103 @@ public final class dev/inmo/tgbotapi/types/payments/AffiliateInfo$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated : dev/inmo/tgbotapi/abstracts/WithUser {
|
||||
public static final field Companion Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$Companion;
|
||||
public fun <init> (Ldev/inmo/tgbotapi/types/chat/User;Ljava/lang/String;Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State;)V
|
||||
public final fun component1 ()Ldev/inmo/tgbotapi/types/chat/User;
|
||||
public final fun component2 ()Ljava/lang/String;
|
||||
public final fun component3 ()Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State;
|
||||
public final fun copy (Ldev/inmo/tgbotapi/types/chat/User;Ljava/lang/String;Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State;)Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;
|
||||
public static synthetic fun copy$default (Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;Ldev/inmo/tgbotapi/types/chat/User;Ljava/lang/String;Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State;ILjava/lang/Object;)Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getInvoicePayload ()Ljava/lang/String;
|
||||
public final fun getState ()Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State;
|
||||
public fun getUser ()Ldev/inmo/tgbotapi/types/chat/User;
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final synthetic class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$$serializer;
|
||||
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;)V
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public abstract interface class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State {
|
||||
public static final field Companion Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Companion;
|
||||
public abstract fun getName ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Active : dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State {
|
||||
public static final field INSTANCE Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Active;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Canceled : dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State {
|
||||
public static final field INSTANCE Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Canceled;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Failed : dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State {
|
||||
public static final field INSTANCE Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Failed;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Unknown : dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State {
|
||||
public static final field Companion Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Unknown$Companion;
|
||||
public static final synthetic fun box-impl (Ljava/lang/String;)Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Unknown;
|
||||
public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
|
||||
public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public static fun hashCode-impl (Ljava/lang/String;)I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
|
||||
public final synthetic fun unbox-impl ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final synthetic class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Unknown$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Unknown$$serializer;
|
||||
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public final fun deserialize-56ZYDkg (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/String;
|
||||
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public final fun serialize-OHdutMU (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/String;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated$State$Unknown$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/payments/CurrenciedKt {
|
||||
public static final fun javaCurrency (Ldev/inmo/tgbotapi/types/payments/abstracts/Currencied;)Ljava/util/Currency;
|
||||
}
|
||||
@@ -38618,6 +38716,36 @@ public final class dev/inmo/tgbotapi/types/stories/StoryAreaType$Weather$Compani
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate : dev/inmo/tgbotapi/types/update/abstracts/Update {
|
||||
public static final field Companion Ldev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate$Companion;
|
||||
public synthetic fun <init> (JLdev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun component1-4k5XoGU ()J
|
||||
public final fun component2 ()Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;
|
||||
public final fun copy-VElHuNg (JLdev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;)Ldev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate;
|
||||
public static synthetic fun copy-VElHuNg$default (Ldev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate;JLdev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;ILjava/lang/Object;)Ldev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public fun getData ()Ldev/inmo/tgbotapi/types/payments/BotSubscriptionUpdated;
|
||||
public synthetic fun getData ()Ljava/lang/Object;
|
||||
public fun getUpdateId-4k5XoGU ()J
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final synthetic class dev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Ldev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate$$serializer;
|
||||
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ldev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Ldev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate;)V
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/update/BotSubscriptionUpdatedUpdate$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class dev/inmo/tgbotapi/types/update/BusinessConnectionUpdate : dev/inmo/tgbotapi/types/update/abstracts/Update {
|
||||
public static final field Companion Ldev/inmo/tgbotapi/types/update/BusinessConnectionUpdate$Companion;
|
||||
public synthetic fun <init> (JLdev/inmo/tgbotapi/types/business_connection/BusinessConnection;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
@@ -39245,6 +39373,7 @@ public final class dev/inmo/tgbotapi/types/webapps/query/SentWebAppMessage$Compa
|
||||
public abstract class dev/inmo/tgbotapi/updateshandlers/AbstractFlowsUpdatesFilter : dev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter {
|
||||
public fun <init> ()V
|
||||
public fun getAllowedUpdates ()Ljava/util/List;
|
||||
public fun getBotSubscriptionUpdatedUpdatesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
public fun getBusinessConnectionUpdatesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
public fun getBusinessMessageUpdatesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
public fun getCallbackQueriesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
@@ -39288,6 +39417,7 @@ public final class dev/inmo/tgbotapi/updateshandlers/DefaultFlowsUpdatesFilter :
|
||||
public abstract interface class dev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter : dev/inmo/tgbotapi/updateshandlers/UpdatesFilter {
|
||||
public abstract fun getAllUpdatesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
public fun getAllowedUpdates ()Ljava/util/List;
|
||||
public abstract fun getBotSubscriptionUpdatedUpdatesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
public abstract fun getBusinessConnectionUpdatesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
public abstract fun getBusinessMessageUpdatesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
public abstract fun getCallbackQueriesFlow ()Lkotlinx/coroutines/flow/Flow;
|
||||
|
||||
@@ -28,6 +28,8 @@ const val UPDATE_GUEST_MESSAGE = "guest_message"
|
||||
|
||||
const val UPDATE_MANAGED_BOT = "managed_bot"
|
||||
|
||||
const val UPDATE_SUBSCRIPTION = "subscription"
|
||||
|
||||
val ALL_UPDATES_LIST_WITHOUT_REACTIONS = listOf(
|
||||
UPDATE_MESSAGE,
|
||||
UPDATE_EDITED_MESSAGE,
|
||||
@@ -65,6 +67,8 @@ val ALL_UPDATES_LIST_WITHOUT_REACTIONS = listOf(
|
||||
UPDATE_REMOVE_CHAT_BOOST,
|
||||
|
||||
UPDATE_MANAGED_BOT,
|
||||
|
||||
UPDATE_SUBSCRIPTION,
|
||||
)
|
||||
|
||||
val ALL_UPDATES_LIST = ALL_UPDATES_LIST_WITHOUT_REACTIONS + listOf(
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
@file:OptIn(ExperimentalSerializationApi::class)
|
||||
|
||||
package dev.inmo.tgbotapi.types.payments
|
||||
|
||||
import dev.inmo.tgbotapi.abstracts.WithUser
|
||||
import dev.inmo.tgbotapi.types.chat.User
|
||||
import dev.inmo.tgbotapi.types.invoicePayloadField
|
||||
import dev.inmo.tgbotapi.types.stateField
|
||||
import dev.inmo.tgbotapi.types.userField
|
||||
import kotlinx.serialization.EncodeDefault
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
/**
|
||||
* Information about changes to a user payment subscription toward the current bot
|
||||
*
|
||||
* @param user User who subscribed for payments toward the bot
|
||||
* @param invoicePayload Bot-specified invoice payload
|
||||
* @param state New state of the subscription
|
||||
*/
|
||||
@Serializable
|
||||
data class BotSubscriptionUpdated(
|
||||
@SerialName(userField)
|
||||
override val user: User,
|
||||
@SerialName(invoicePayloadField)
|
||||
val invoicePayload: String,
|
||||
@SerialName(stateField)
|
||||
val state: State
|
||||
) : WithUser {
|
||||
@Serializable(StateSerializer::class)
|
||||
sealed interface State {
|
||||
val name: String
|
||||
|
||||
@Serializable
|
||||
data object Canceled : State {
|
||||
@EncodeDefault
|
||||
override val name = "canceled"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data object Active : State {
|
||||
@EncodeDefault
|
||||
override val name = "active"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data object Failed : State {
|
||||
@EncodeDefault
|
||||
override val name = "failed"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class Unknown(override val name: String) : State
|
||||
}
|
||||
|
||||
private object StateSerializer : KSerializer<State> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
|
||||
serialName = "dev.inmo.tgbotapi.types.payments.BotSubscriptionUpdated.State",
|
||||
kind = PrimitiveKind.STRING
|
||||
)
|
||||
|
||||
override fun deserialize(decoder: Decoder): State {
|
||||
val value = decoder.decodeString()
|
||||
return when (value) {
|
||||
State.Canceled.name -> State.Canceled
|
||||
State.Active.name -> State.Active
|
||||
State.Failed.name -> State.Failed
|
||||
else -> State.Unknown(value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: State) {
|
||||
encoder.encodeString(value.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package dev.inmo.tgbotapi.types.update
|
||||
|
||||
import dev.inmo.tgbotapi.types.UpdateId
|
||||
import dev.inmo.tgbotapi.types.payments.BotSubscriptionUpdated
|
||||
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BotSubscriptionUpdatedUpdate(
|
||||
override val updateId: UpdateId,
|
||||
override val data: BotSubscriptionUpdated
|
||||
) : Update
|
||||
@@ -15,6 +15,7 @@ import dev.inmo.tgbotapi.types.chat.member.ChatMemberUpdated
|
||||
import dev.inmo.tgbotapi.types.managed_bots.ManagedBotUpdated
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.*
|
||||
import dev.inmo.tgbotapi.types.message.payments.PaidMediaPurchased
|
||||
import dev.inmo.tgbotapi.types.payments.BotSubscriptionUpdated
|
||||
import dev.inmo.tgbotapi.types.payments.PreCheckoutQuery
|
||||
import dev.inmo.tgbotapi.types.payments.ShippingQuery
|
||||
import dev.inmo.tgbotapi.types.polls.Poll
|
||||
@@ -69,6 +70,7 @@ internal data class RawUpdate constructor(
|
||||
private val guest_message: RequestGuestContentMessage<*>? = null,
|
||||
private val purchased_paid_media: PaidMediaPurchased? = null,
|
||||
private val managed_bot: ManagedBotUpdated? = null,
|
||||
private val subscription: BotSubscriptionUpdated? = null,
|
||||
) {
|
||||
@Transient
|
||||
private var initedUpdate: Update? = null
|
||||
@@ -107,6 +109,7 @@ internal data class RawUpdate constructor(
|
||||
guest_message != null -> GuestMessageUpdate(updateId, guest_message)
|
||||
purchased_paid_media != null -> PaidMediaPurchasedUpdate(updateId, purchased_paid_media)
|
||||
managed_bot != null -> ManagedBotUpdate(updateId, managed_bot)
|
||||
subscription != null -> BotSubscriptionUpdatedUpdate(updateId, subscription)
|
||||
else -> UnknownUpdate(
|
||||
updateId,
|
||||
raw
|
||||
|
||||
+2
@@ -47,6 +47,7 @@ interface FlowsUpdatesFilter : UpdatesFilter {
|
||||
val guestMessageUpdatesFlow: Flow<GuestMessageUpdate>
|
||||
val paidMediaPurchasedUpdatesFlow: Flow<PaidMediaPurchasedUpdate>
|
||||
val managedBotUpdatesFlow: Flow<ManagedBotUpdate>
|
||||
val botSubscriptionUpdatedUpdatesFlow: Flow<BotSubscriptionUpdatedUpdate>
|
||||
val unknownUpdatesFlow: Flow<UnknownUpdate>
|
||||
}
|
||||
|
||||
@@ -77,6 +78,7 @@ abstract class AbstractFlowsUpdatesFilter : FlowsUpdatesFilter {
|
||||
override val guestMessageUpdatesFlow: Flow<GuestMessageUpdate> by lazy { allUpdatesFlow.filterIsInstance() }
|
||||
override val paidMediaPurchasedUpdatesFlow: Flow<PaidMediaPurchasedUpdate> by lazy { allUpdatesFlow.filterIsInstance() }
|
||||
override val managedBotUpdatesFlow: Flow<ManagedBotUpdate> by lazy { allUpdatesFlow.filterIsInstance() }
|
||||
override val botSubscriptionUpdatedUpdatesFlow: Flow<BotSubscriptionUpdatedUpdate> by lazy { allUpdatesFlow.filterIsInstance() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user