All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.simbot.component.mirai.MiraiApplication.kt Maven / Gradle / Ivy

There is a newer version: 1.11.0-1.17-Final
Show newest version
/*
 * Copyright (c) 2020. ForteScarlet All rights reserved.
 * Project  component-mirai (Codes other than Mirai)
 * File     MiraiApplication.kt (Codes other than Mirai)
 *
 * You can contact the author through the following channels:
 * github https://github.com/ForteScarlet
 * gitee  https://gitee.com/ForteScarlet
 * email  [email protected]
 * QQ     1149159218
 *
 * The Mirai code is copyrighted by mamoe-mirai
 * you can see mirai at https://github.com/mamoe/mirai
 *
 *
 */

package com.simbot.component.mirai

import com.forte.qqrobot.*
import com.forte.qqrobot.beans.messages.msgget.MsgGet
import com.forte.qqrobot.bot.BotInfo
import com.forte.qqrobot.bot.BotManager
import com.forte.qqrobot.depend.DependCenter
import com.forte.qqrobot.exception.BotVerifyException
import com.forte.qqrobot.listener.invoker.AtDetection
import com.forte.qqrobot.listener.invoker.ListenerFilter
import com.forte.qqrobot.listener.invoker.ListenerManager
import com.forte.qqrobot.log.QQLog
import com.forte.qqrobot.sender.senderlist.RootSenderList
import com.simbot.component.mirai.messages.*
import com.simbot.component.mirai.utils.ListenRegisterUtil
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.message.data.At
import net.mamoe.mirai.message.data.firstIsInstanceOrNull
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.function.Function
import kotlin.reflect.KClass

/**
 * mirai的context
 */
class MiraiContext(
        sender: MiraiBotSender,
        setter: MiraiBotSender,
        getter: MiraiBotSender,
        manager: BotManager,
        msgParser: MsgParser,
        processor: MsgProcessor,
        dependCenter: DependCenter,
        config: MiraiConfiguration,
        application: MiraiApplication
) : SimpleRobotContext(
        sender,
        setter,
        getter,
        manager,
        msgParser,
        processor,
        dependCenter,
        config,
        application
)


/**
 * mirai app
 */
interface MiraiApp: Application {
    override fun before(configuration: MiraiConfiguration)
}


/**
 *
 * Mirai组件启动器
 *
 * @author ForteScarlet <\[email][email protected]>
 * @since JDK1.8
 **/
class MiraiApplication : BaseApplication() {

    private lateinit var botThread: Thread;

    @Deprecated("just see getRootSenderFunction", ReplaceWith("null"))
    override fun getSetter(msgGet: MsgGet?, botManager: BotManager?): MiraiBotSender? = null

    @Deprecated("just see getRootSenderFunction", ReplaceWith("null"))
    override fun getSender(msgGet: MsgGet?, botManager: BotManager?): MiraiBotSender? = null

    @Deprecated("just see getRootSenderFunction", ReplaceWith("null"))
    override fun getGetter(msgGet: MsgGet?, botManager: BotManager?): MiraiBotSender? = null

    /** 配置类实例 */
    private val config: MiraiConfiguration = MiraiConfiguration()

    /**
     * 是否注册额外监听成功
     */
    private var registeredSpecialListenerSuccess: Boolean = false

    /**
     * 开发者实现的获取Config对象实例的方法
     * 此方法将会最先被执行,并会将值保存,使用时可使用[.getConf] 方法获取
     */
    override fun getConfiguration(): MiraiConfiguration = config


    /**
     * 开发者实现的资源初始化
     * 此方法将会在所有的初始化方法最后执行
     * 增加一个参数
     * 此资源配置将会在配置之后执行
     */
    override fun resourceInit(config: MiraiConfiguration) {
        registerMiraiEvent()
    }

    /**
     * 开发者实现的资源初始化
     * 此方法将会在所有的无配置初始化方法最后执行
     * 将会在用户配置之前执行
     */
    override fun resourceInit() {
        registerMiraiAtFilter()
    }

    /**
     * 注册Mirai的at判断,追加当为[MiraiMessageGet]的时候的判断
     */
    private fun registerMiraiAtFilter(){
        ListenerFilter.updateAtDetectionFunction { old ->
            Function { msg ->
                if(msg is MiraiMessageGet<*>){
                    AtDetection {
                        (msg.message.find { msg -> msg is At } as? At)?.target == msg.event.bot.id
                    }
                }else{
                    old.apply(msg)
                }
            }
        }
    }

    /**
     * 注册mirai可提供的额外事件
     */
    private fun registerMiraiEvent(){
        val specialListeners: Map> = mapOf(
                // 头像变更事件
                MiraiEvents.friendAvatarChangedEvent to FriendAvatarChanged::class,
                // 昵称变更事件
                MiraiEvents.friendNicknameChangedEvent to FriendNicknameChanged::class,
                // 输入状态变更事件
                MiraiEvents.friendInputStatusChangedEvent to FriendInputStatusChanged::class,
                // bot离线事件
                MiraiEvents.botOfflineEvent to BotOffline::class,
                // bot重新登录事件
                MiraiEvents.botReloginEvent to BotRelogin::class,
                // 群名称变更事件
                MiraiEvents.groupNameChangedEvent to GroupNameChanged::class,
                // 群员群昵称变更事件
                MiraiEvents.memberRemarkChangedEvent to MemberRemarkChanged::class,
                // 群员群头衔变更事件
                MiraiEvents.memberSpecialTitleChangedEvent to MemberSpecialTitleChanged::class,
        )

        if(!ListenRegisterUtil.usable){
            val show = specialListeners.values.joinToString(",\n", transform = {
                it.simpleName as CharSequence
            })
            QQLog.warning("mirai.event.register.unavailable", show)
            QQLog.debug("mirai.event.register.unavailable", ListenRegisterUtil.cause, "")
            return
        }
        registeredSpecialListenerSuccess = true

        // 注册额外的事件
        specialListeners.forEach {
            ListenRegisterUtil.registerListen(it.key, it.value)
        }
    }

    /**
     * 根据 [getSender], [getSetter], [getGetter] 三个函数构建一个RootSenderList
     * 参数分别为一个[BotManager]和一个[MsgGet]对象
     * 如果组件不是分为三个部分而构建,则可以考虑重写此函数
     * 此函数最终会被送入组件实现的[runServer]中
     *
     * @return RootSenderList构建函数
     */
    override fun getRootSenderFunction(botManager: BotManager): Function {
        val cacheMaps = dependCenter.get(CacheMaps::class.java)
        val senderRunner = dependCenter.get(SenderRunner::class.java)
        return Function {
            //            var bot: Bot
            var contact: Contact? = null
            if (it is MiraiMessageGet<*>) {
                contact = it.contact
            }
            MultipleMiraiBotSender(contact, it, botManager, cacheMaps, senderRunner, registeredSpecialListenerSuccess, conf)
        }
    }

    /**
     * 获取一个组件专属的SimpleRobotContext对象
     * @param defaultSenders 函数[.getDefaultSenders]的最终返回值
     * @param manager       botManager对象
     * @param msgParser     消息字符串转化函数
     * @param processor     消息处理器
     * @param dependCenter  依赖中心
     * @return 组件的Context对象实例
     */
    override fun getComponentContext(defaultSenders: DefaultSenders,
                                     manager: BotManager,
                                     msgParser: MsgParser, processor: MsgProcessor,
                                     dependCenter: DependCenter, config: MiraiConfiguration): MiraiContext
            = MiraiContext(defaultSenders.sender, defaultSenders.getter, defaultSenders.setter, manager, msgParser, processor, dependCenter, config, this)


    @Deprecated("just see getDefaultSenders", ReplaceWith("null"))
    override fun getDefaultSetter(botManager: BotManager?): MiraiBotSender? = null

    @Deprecated("just see getDefaultSenders", ReplaceWith("null"))
    override fun getDefaultGetter(botManager: BotManager?): MiraiBotSender? = null

    @Deprecated("just see getDefaultSenders", ReplaceWith("null"))
    override fun getDefaultSender(botManager: BotManager?): MiraiBotSender? = null

    /**
     * 构建一个默认的RootSenderList
     * 参数分别为一个BotManager和一个MsgGet对象
     * 如果组件不是分为三个部分而构建,则可以考虑重写此函数
     * @return RootSenderList构建函数
     */
    override fun getDefaultSenders(botManager: BotManager): DefaultSenders {
        val cacheMaps = dependCenter.get(CacheMaps::class.java)
        val senderRunner = dependCenter.get(SenderRunner::class.java)
        val defaultSender = DefaultMiraiBotSender(null, cacheMaps, senderRunner, registeredSpecialListenerSuccess, botManager, conf)
        return DefaultSenders(defaultSender, defaultSender, defaultSender)
    }

    /**
     * 启动一个服务,这有可能是http或者是ws的监听服务
     * @param dependCenter   依赖中心
     * @param manager        监听管理器
     * @param msgProcessor   送信解析器
     * @return
     */
    override fun runServer(dependCenter: DependCenter, manager: ListenerManager, msgProcessor: MsgProcessor, msgParser: MsgParser): String {
        val cacheMaps = dependCenter.get(CacheMaps::class.java)
        // 启动服务,即注册监听
        MiraiBots.startListen(msgProcessor, cacheMaps, registeredSpecialListenerSuccess)

        if(config.autoRelogin) {
            // 如果要自动重启,此处注册自动重启事件
            MiraiBots.forEach {
                _, info ->
                val id = info.bot.id
                // 注册被动下线事件
                info.bot.subscribeAlways {
                    if(this.bot.id == id && !isClosed){
                        QQLog.debug("mirai.relogin.off", this.cause, id)
                        GlobalScope.launch {
                            bot.login()
                            QQLog.debug("mirai.relogin.on")
                        }
                    }
                }
            }
        }


        // 在一条新线程中执行挂起,防止程序终止
        botThread = Thread({
            MiraiBots.joinAll()
            QQLog.debug("bots all shutdown.")
        }, "Mirai-bot-join")
        botThread.start()
        return "mirai bot server"
    }


    /**
     * 验证账号
     * @param code 用户账号,不可为null
     * @param info 用于验证的bot,使用启动的path作为密码来执行登录
     */
    override fun verifyBot(code: String, info: BotInfo): BotInfo {
        val cacheMaps = dependCenter.get(CacheMaps::class.java)
        val senderRunner = dependCenter.get(SenderRunner::class.java)
        // 验证账号, 构建一个BotInfo
        // 如果验证失败,会抛出异常的
        try {
            QQLog.debug("验证账号$code...")
            return MiraiBotInfo(code, info.path, conf.botConfiguration(code), cacheMaps, senderRunner, registeredSpecialListenerSuccess)
        } catch (e: Exception) {
            throw BotVerifyException("failed", e, code, e.localizedMessage)
        }
    }


    /**
     * 字符串转化为MsgGet的方法,最终会被转化为[MsgParser]函数,
     * 此函数返回值永远为null
     */
    @Deprecated("no, just null", ReplaceWith("null"))
    override fun msgParse(str: String?): MsgGet? = null


    /**
     * 当关闭的时候执行的方法,会退出所有的bot,然后执行父类的close
     */
    override fun doClose() {
        MiraiBots.closeAll()
        // join 10 seconds
        QQLog.info("mirai.bot.thread.shutdown")
        // 等待1分钟
        botThread.join(TimeUnit.MINUTES.toMillis(1))
        if(botThread.isAlive){
            QQLog.warning("mirai.bot.thread.mandatoryTermination")
            // 强制终止
            @Suppress("DEPRECATION")
            botThread.stop()
        }
    }

}













© 2015 - 2025 Weber Informatics LLC | Privacy Policy