mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Extract Java implementations to java package. Update docs
This commit is contained in:
parent
64296c5ae0
commit
f69c41f64e
@ -14,6 +14,7 @@ package net.mamoe.mirai.console.command
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
|
||||
import net.mamoe.mirai.console.command.java.JCommand
|
||||
import net.mamoe.mirai.console.internal.command.isValidSubName
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
|
||||
@ -25,15 +26,19 @@ import net.mamoe.mirai.message.data.SingleMessage
|
||||
* @see RawCommand 无参数解析, 接收原生参数的指令
|
||||
* @see CompositeCommand 复合指令
|
||||
* @see SimpleCommand 简单的, 支持参数自动解析的指令
|
||||
*
|
||||
* @see JCommand 为 Java 用户添加协程帮助的 [Command]
|
||||
*/
|
||||
public interface Command {
|
||||
/**
|
||||
* 指令名. 需要至少有一个元素. 所有元素都不能带有空格
|
||||
*
|
||||
* @see Command.primaryName 获取主要指令名
|
||||
*/
|
||||
public val names: Array<out String>
|
||||
|
||||
/**
|
||||
* 用法说明, 用于发送给用户
|
||||
* 用法说明, 用于发送给用户. 一般 [usage] 包含 [description].
|
||||
*/
|
||||
public val usage: String
|
||||
|
||||
|
@ -82,6 +82,12 @@ public abstract class CompositeCommand(
|
||||
overrideContext: CommandArgumentContext = EmptyCommandArgumentContext
|
||||
) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional),
|
||||
CommandArgumentContextAware {
|
||||
|
||||
/**
|
||||
* 自动根据带有 [SubCommand] 注解的函数签名生成 [usage]. 也可以被覆盖.
|
||||
*/
|
||||
public override val usage: String get() = super.usage
|
||||
|
||||
/**
|
||||
* [CommandArgumentParser] 的环境
|
||||
*/
|
||||
@ -126,71 +132,3 @@ public abstract class CompositeCommand(
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 复合指令. 指令注册时候会通过反射构造指令解析器.
|
||||
*
|
||||
* 示例:
|
||||
* ```
|
||||
* public final class MyCompositeCommand extends CompositeCommand {
|
||||
* public static final MyCompositeCommand INSTANCE = new MyCompositeCommand();
|
||||
*
|
||||
* public MyCompositeCommand() {
|
||||
* super(MyPluginMain.INSTANCE, "manage") // "manage" 是主指令名
|
||||
* }
|
||||
*
|
||||
* // [参数智能解析]
|
||||
* //
|
||||
* //
|
||||
* // 在控制台执行 "/manage <群号>.<群员> <持续时间>",
|
||||
* // 或在聊天群内发送 "/manage <@一个群员> <持续时间>",
|
||||
* // 或在聊天群内发送 "/manage <目标群员的群名> <持续时间>",
|
||||
* // 或在聊天群内发送 "/manage <目标群员的账号> <持续时间>"
|
||||
* // 时调用这个函数
|
||||
* @SubCommand
|
||||
* public void mute(CommandSender sender, Member target, int duration) { // 通过 /manage mute <target> <duration> 调用.
|
||||
* sender.sendMessage("/manage mute 被调用了, 参数为: " + target + ", " + duration);
|
||||
*
|
||||
*
|
||||
* String result;
|
||||
* try {
|
||||
* result = target.mute(duration).toString();
|
||||
* } catch(Exception e) {
|
||||
* result = ExceptionsKt.stackTraceToString(e);
|
||||
* }
|
||||
*
|
||||
* sender.sendMessage("结果: " + result)
|
||||
* }
|
||||
*
|
||||
* @SubCommand
|
||||
* public void list(CommandSender sender) { // 执行 "/manage list" 时调用这个方法
|
||||
* sender.sendMessage("/manage list 被调用了")
|
||||
* }
|
||||
*
|
||||
* // 支持 Image 类型, 需在聊天中执行此指令.
|
||||
* @SubCommand
|
||||
* public void test(CommandSender sender, Image image) { // 执行 "/manage test <一张图片>" 时调用这个方法
|
||||
* sender.sendMessage("/manage image 被调用了, 图片是 " + image.imageId)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see buildCommandArgumentContext
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public abstract class JCompositeCommand(
|
||||
owner: CommandOwner,
|
||||
vararg names: String
|
||||
) : CompositeCommand(owner, *names) {
|
||||
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */
|
||||
public final override var description: String = "<no descriptions given>"
|
||||
protected set
|
||||
|
||||
/** 指令权限 */
|
||||
public final override var permission: CommandPermission = CommandPermission.Default
|
||||
protected set
|
||||
|
||||
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
|
||||
public final override var prefixOptional: Boolean = false
|
||||
protected set
|
||||
|
||||
}
|
@ -11,10 +11,9 @@
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.java.JRawCommand
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
|
||||
/**
|
||||
@ -56,69 +55,3 @@ public abstract class RawCommand(
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 供 Java 用户继承
|
||||
*
|
||||
* 请在构造时设置相关属性.
|
||||
*
|
||||
* ```java
|
||||
* public final class MyCommand extends JRawCommand {
|
||||
* public static final MyCommand INSTANCE = new MyCommand();
|
||||
* private MyCommand () {
|
||||
* super(MyPluginMain.INSTANCE, "test")
|
||||
* // 可选设置如下属性
|
||||
* setUsage("/test")
|
||||
* setDescription("这是一个测试指令")
|
||||
* setPermission(CommandPermission.Operator.INSTANCE)
|
||||
* setPrefixOptional(true)
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void onCommand(@NotNull CommandSender sender, @NotNull args: Object[]) {
|
||||
* // 处理指令
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see RawCommand
|
||||
*/
|
||||
public abstract class JRawCommand(
|
||||
/**
|
||||
* 指令拥有者.
|
||||
* @see CommandOwner
|
||||
*/
|
||||
public override val owner: CommandOwner,
|
||||
/** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */
|
||||
public override vararg val names: String
|
||||
) : Command {
|
||||
/** 用法说明, 用于发送给用户 */
|
||||
public override var usage: String = "<no usages given>"
|
||||
protected set
|
||||
|
||||
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */
|
||||
public final override var description: String = "<no descriptions given>"
|
||||
protected set
|
||||
|
||||
/** 指令权限 */
|
||||
public final override var permission: CommandPermission = CommandPermission.Default
|
||||
protected set
|
||||
|
||||
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
|
||||
public final override var prefixOptional: Boolean = false
|
||||
protected set
|
||||
|
||||
/**
|
||||
* 在指令被执行时调用.
|
||||
*
|
||||
* @param args 指令参数. 数组元素类型可能是 [SingleMessage] 或 [String]. 且已经以 ' ' 分割.
|
||||
*
|
||||
* @see CommandManager.execute 查看更多信息
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
@JvmName("onCommand")
|
||||
public abstract fun onCommand(sender: CommandSender, args: Array<out Any>)
|
||||
|
||||
public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
|
||||
withContext(Dispatchers.IO) { onCommand(this@onCommand, args) }
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ package net.mamoe.mirai.console.command
|
||||
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.description.*
|
||||
import net.mamoe.mirai.console.command.java.JSimpleCommand
|
||||
import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
|
||||
import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
|
||||
|
||||
@ -55,14 +56,19 @@ public abstract class SimpleCommand(
|
||||
) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional),
|
||||
CommandArgumentContextAware {
|
||||
|
||||
public override val usage: String
|
||||
get() = super.usage
|
||||
/**
|
||||
* 自动根据带有 [Handler] 注解的函数签名生成 [usage]. 也可以被覆盖.
|
||||
*/
|
||||
public override val usage: String get() = super.usage
|
||||
|
||||
/**
|
||||
* 标注指令处理器
|
||||
*/
|
||||
protected annotation class Handler
|
||||
|
||||
/**
|
||||
* 指令参数环境. 默认为 [CommandArgumentContext.Builtins] `+` `overrideContext`
|
||||
*/
|
||||
public override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext
|
||||
|
||||
public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
|
||||
@ -81,39 +87,3 @@ public abstract class SimpleCommand(
|
||||
get() = SimpleCommandSubCommandAnnotationResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* Java 实现:
|
||||
* ```java
|
||||
* public final class MySimpleCommand extends JSimpleCommand {
|
||||
* private MySimpleCommand() {
|
||||
* super(MyPlugin.INSTANCE, "tell")
|
||||
* // 可选设置如下属性
|
||||
* setDescription("这是一个测试指令")
|
||||
* setUsage("/tell <target> <message>") // 如不设置则自动根据带有 @Handler 的方法生成
|
||||
* setPermission(CommandPermission.Operator.INSTANCE)
|
||||
* setPrefixOptional(true)
|
||||
* }
|
||||
*
|
||||
* @Handler
|
||||
* public void onCommand(CommandSender sender, User target, String message) {
|
||||
* target.sendMessage(message)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see SimpleCommand
|
||||
* @see [CommandManager.executeCommand]
|
||||
*/
|
||||
public abstract class JSimpleCommand(
|
||||
owner: CommandOwner,
|
||||
vararg names: String
|
||||
) : SimpleCommand(owner, *names) {
|
||||
public override var description: String = super.description
|
||||
protected set
|
||||
public override var permission: CommandPermission = super.permission
|
||||
protected set
|
||||
public override var prefixOptional: Boolean = super.prefixOptional
|
||||
protected set
|
||||
public override var context: CommandArgumentContext = super.context
|
||||
protected set
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.command.java
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.console.command.Command
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
|
||||
/**
|
||||
* 为 Java 用户添加协程帮助的 [Command].
|
||||
*
|
||||
* 注意, [JSimpleCommand], [JCompositeCommand], [JRawCommand] 都不实现这个接口. [JCommand] 只设计为 Java 使用者自己实现 [Command] 相关内容.
|
||||
*
|
||||
* @see Command
|
||||
*/
|
||||
public interface JCommand : Command {
|
||||
public override suspend fun CommandSender.onCommand(args: Array<out Any>) {
|
||||
withContext(Dispatchers.IO) { onCommand(this@onCommand, args) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指令被执行时调用.
|
||||
*
|
||||
* @param args 指令参数. 数组元素类型可能是 [SingleMessage] 或 [String]. 且已经以 ' ' 分割.
|
||||
*
|
||||
* @see CommandManager.executeCommand 查看更多信息
|
||||
*/
|
||||
public fun onCommand(sender: CommandSender, args: Array<out Any>) // overrides bridge
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.command.java
|
||||
|
||||
import net.mamoe.mirai.console.command.CommandOwner
|
||||
import net.mamoe.mirai.console.command.CommandPermission
|
||||
import net.mamoe.mirai.console.command.CompositeCommand
|
||||
import net.mamoe.mirai.console.command.description.buildCommandArgumentContext
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
|
||||
/**
|
||||
* 复合指令. 指令注册时候会通过反射构造指令解析器.
|
||||
*
|
||||
* 示例:
|
||||
* ```
|
||||
* public final class MyCompositeCommand extends CompositeCommand {
|
||||
* public static final MyCompositeCommand INSTANCE = new MyCompositeCommand();
|
||||
*
|
||||
* public MyCompositeCommand() {
|
||||
* super(MyPluginMain.INSTANCE, "manage") // "manage" 是主指令名
|
||||
* }
|
||||
*
|
||||
* // [参数智能解析]
|
||||
* //
|
||||
* //
|
||||
* // 在控制台执行 "/manage <群号>.<群员> <持续时间>",
|
||||
* // 或在聊天群内发送 "/manage <@一个群员> <持续时间>",
|
||||
* // 或在聊天群内发送 "/manage <目标群员的群名> <持续时间>",
|
||||
* // 或在聊天群内发送 "/manage <目标群员的账号> <持续时间>"
|
||||
* // 时调用这个函数
|
||||
* @SubCommand
|
||||
* public void mute(CommandSender sender, Member target, int duration) { // 通过 /manage mute <target> <duration> 调用.
|
||||
* sender.sendMessage("/manage mute 被调用了, 参数为: " + target + ", " + duration);
|
||||
*
|
||||
*
|
||||
* String result;
|
||||
* try {
|
||||
* result = target.mute(duration).toString();
|
||||
* } catch(Exception e) {
|
||||
* result = ExceptionsKt.stackTraceToString(e);
|
||||
* }
|
||||
*
|
||||
* sender.sendMessage("结果: " + result)
|
||||
* }
|
||||
*
|
||||
* @SubCommand
|
||||
* public void list(CommandSender sender) { // 执行 "/manage list" 时调用这个方法
|
||||
* sender.sendMessage("/manage list 被调用了")
|
||||
* }
|
||||
*
|
||||
* // 支持 Image 类型, 需在聊天中执行此指令.
|
||||
* @SubCommand
|
||||
* public void test(CommandSender sender, Image image) { // 执行 "/manage test <一张图片>" 时调用这个方法
|
||||
* sender.sendMessage("/manage image 被调用了, 图片是 " + image.imageId)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see buildCommandArgumentContext
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public abstract class JCompositeCommand(
|
||||
owner: CommandOwner,
|
||||
vararg names: String
|
||||
) : CompositeCommand(owner, *names) {
|
||||
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */
|
||||
public final override var description: String = "<no descriptions given>"
|
||||
protected set
|
||||
|
||||
/** 指令权限 */
|
||||
public final override var permission: CommandPermission = CommandPermission.Default
|
||||
protected set
|
||||
|
||||
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
|
||||
public final override var prefixOptional: Boolean = false
|
||||
protected set
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.command.java
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.console.command.Command
|
||||
import net.mamoe.mirai.console.command.CommandOwner
|
||||
import net.mamoe.mirai.console.command.CommandPermission
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
|
||||
/**
|
||||
* 供 Java 用户继承
|
||||
*
|
||||
* 请在构造时设置相关属性.
|
||||
*
|
||||
* ```java
|
||||
* public final class MyCommand extends JRawCommand {
|
||||
* public static final MyCommand INSTANCE = new MyCommand();
|
||||
* private MyCommand() {
|
||||
* super(MyPluginMain.INSTANCE, "test")
|
||||
* // 可选设置如下属性
|
||||
* setUsage("/test")
|
||||
* setDescription("这是一个测试指令")
|
||||
* setPermission(CommandPermission.Operator.INSTANCE)
|
||||
* setPrefixOptional(true)
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void onCommand(@NotNull CommandSender sender, @NotNull args: Object[]) {
|
||||
* // 处理指令
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see JRawCommand
|
||||
*/
|
||||
public abstract class JRawCommand(
|
||||
/**
|
||||
* 指令拥有者.
|
||||
* @see CommandOwner
|
||||
*/
|
||||
public override val owner: CommandOwner,
|
||||
/** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */
|
||||
public override vararg val names: String
|
||||
) : Command {
|
||||
/** 用法说明, 用于发送给用户 */
|
||||
public override var usage: String = "<no usages given>"
|
||||
protected set
|
||||
|
||||
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */
|
||||
public final override var description: String = "<no descriptions given>"
|
||||
protected set
|
||||
|
||||
/** 指令权限 */
|
||||
public final override var permission: CommandPermission = CommandPermission.Default
|
||||
protected set
|
||||
|
||||
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
|
||||
public final override var prefixOptional: Boolean = false
|
||||
protected set
|
||||
|
||||
/**
|
||||
* 在指令被执行时调用.
|
||||
*
|
||||
* @param args 指令参数. 数组元素类型可能是 [SingleMessage] 或 [String]. 且已经以 ' ' 分割.
|
||||
*
|
||||
* @see CommandManager.execute 查看更多信息
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
@JvmName("onCommand")
|
||||
public abstract fun onCommand(sender: CommandSender, args: Array<out Any>)
|
||||
|
||||
public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
|
||||
withContext(Dispatchers.IO) { onCommand(this@onCommand, args) }
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.command.java
|
||||
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.CommandOwner
|
||||
import net.mamoe.mirai.console.command.CommandPermission
|
||||
import net.mamoe.mirai.console.command.SimpleCommand
|
||||
import net.mamoe.mirai.console.command.description.CommandArgumentContext
|
||||
|
||||
/**
|
||||
* Java 实现:
|
||||
* ```java
|
||||
* public final class MySimpleCommand extends JSimpleCommand {
|
||||
* private MySimpleCommand() {
|
||||
* super(MyPlugin.INSTANCE, "tell")
|
||||
* // 可选设置如下属性
|
||||
* setDescription("这是一个测试指令")
|
||||
* setUsage("/tell <target> <message>") // 如不设置则自动根据带有 @Handler 的方法生成
|
||||
* setPermission(CommandPermission.Operator.INSTANCE)
|
||||
* setPrefixOptional(true)
|
||||
* }
|
||||
*
|
||||
* @Handler
|
||||
* public void onCommand(CommandSender sender, User target, String message) {
|
||||
* target.sendMessage(message)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see SimpleCommand
|
||||
* @see [CommandManager.executeCommand]
|
||||
*/
|
||||
public abstract class JSimpleCommand(
|
||||
owner: CommandOwner,
|
||||
vararg names: String
|
||||
) : SimpleCommand(owner, *names) {
|
||||
public override var description: String = super.description
|
||||
protected set
|
||||
public override var permission: CommandPermission = super.permission
|
||||
protected set
|
||||
public override var prefixOptional: Boolean = super.prefixOptional
|
||||
protected set
|
||||
public override var context: CommandArgumentContext = super.context
|
||||
protected set
|
||||
}
|
Loading…
Reference in New Issue
Block a user