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

net.mamoe.mirai.api.http.context.session.session.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Mamoe Technologies and contributors.
 *
 * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
 * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
 *
 * https://github.com/mamoe/mirai/blob/master/LICENSE
 */

package net.mamoe.mirai.api.http.context.session

import kotlinx.atomicfu.atomic
import kotlinx.coroutines.*
import net.mamoe.mirai.Bot
import net.mamoe.mirai.api.http.adapter.common.NotVerifiedSessionException
import net.mamoe.mirai.api.http.context.session.manager.SessionManager
import net.mamoe.mirai.api.http.spi.persistence.Persistence
import net.mamoe.mirai.event.Listener
import net.mamoe.mirai.event.events.BotEvent
import kotlin.coroutines.CoroutineContext

/**
 * 提供默认 Session 标准实现
 */
class StandardSession constructor(
    override val key: String,
    override val manager: SessionManager,
) : AbstractSession() {
    private val supervisorJob = SupervisorJob()
    override val coroutineContext: CoroutineContext = supervisorJob + CoroutineName("session-$key")
    private val lifeCounter = atomic(0)

    private lateinit var _bot: Bot
    private lateinit var _cache: Persistence
    @Volatile private var _isAuthed = false
    @Volatile private var _closed = false
    @Volatile private var _closing = false

    override val bot: Bot get() = if (isAuthed) _bot else throw NotVerifiedSessionException
    override val sourceCache: Persistence get() = if (isAuthed) _cache else throw NotVerifiedSessionException
    override val isAuthed get() = _isAuthed
    override val isClosed get() = _closed

    override fun authWith(bot: Bot, sourceCache: Persistence) {
        if(isAuthed) {
            return
        }

        _isAuthed = true
        _bot = bot
        _cache = sourceCache
    }

    override fun ref() {
        lifeCounter.incrementAndGet()
    }

    override fun getRefCount(): Int {
        return lifeCounter.value
    }

    override fun close() {
        if (_closing) {
            return
        }

        _closing = true
        if (!isClosed && lifeCounter.decrementAndGet() <= 0) {
            _closed = true
            manager.closeSession(key)
            supervisorJob.cancel()
        }
        _closing = false
    }
}

/**
 * 提供而外 Job 执行的 Session 包装类
 * 1. 提供一个可用的 expired 计时器
 * 2. 提供一个用于 Bot 事件监听的监听器
 */
private typealias BotEventHandler = (Session, BotEvent)->Unit
class ListenableSessionWrapper(val session: Session) : Session by session {

    companion object Key {
        val expiredJob = object : Session.ExtKey{}
        val listenerJob = object : Session.ExtKey>{}
        val botEventHandler = object : Session.ExtKey{}
    }

    /**
     * 代理方法
     * 正常执行认证流程后, 直接开启
     */
    override fun authWith(bot: Bot, sourceCache: Persistence) {
        session.authWith(bot, sourceCache)
        val job = getExtElement(listenerJob)
        if (job == null) {
            startBotEventListener()
        }
    }

    /**
     * 代理方法
     * 正常执行关闭流程后, 如果 Session 已关闭, 则停止默认提供的两个 Job
     */
    override fun close() {
        session.close()
        if (session.isClosed) {
            getExtElement(expiredJob)?.cancel()
            getExtElement(listenerJob)?.cancel()
        }
    }

    fun startExpiredCountdown(expired: Long, callback: (() -> Unit)? = null) {
        val element = launch {
            delay(expired)
            if (!isAuthed) {
                close()
                callback?.invoke()
            }
        }
        putExtElement(expiredJob, element)
    }

    fun startBotEventListener(botEventHandler: BotEventHandler? = null) {
        check(isAuthed) { "Session is not authed" }

        val handler = botEventHandler ?: getExtElement(Key.botEventHandler)
        val element = bot.eventChannel.subscribeAlways(BotEvent::class, coroutineContext) { event ->
            handler?.invoke(session, event)
        }

        putExtElement(listenerJob, element)
    }
}

/**
 * 提供 Session 的默认实现
 */
abstract class AbstractSession : Session {
    private val extElement = mutableMapOf, Any?>()

    override fun  getExtElement(key: Session.ExtKey): T? {
        @Suppress("unchecked_cast")
        return extElement[key] as T?
    }

    override fun  putExtElement(key: Session.ExtKey, element: T) {
        extElement[key] = element
    }

    override fun removeExtElement(key: Session.ExtKey) {
        extElement.remove(key)
    }
}

/**
 * 通用 Session. 一个 Session 只与一个 Bot 实例绑定.
 * Session 可以被多次复用, 通过 ref 开启引用计数器, 并在 close 时检查引用数量
 */
interface Session : CoroutineScope {
    val key: String
    val bot: Bot
    val manager: SessionManager

    val isAuthed: Boolean
    val isClosed: Boolean
    val sourceCache: Persistence

    /**
     * 通过 Bot 和 cache 完成 Session 的认证过程, 执行 AuthedSession 初始化
     */
    fun authWith(bot: Bot, sourceCache: Persistence)

    /**
     * 引用 Session, 可以使得 Session 在关闭时先检查引用计数
     */
    fun ref()

    /**
     * 获取引用计数的数量
     */
    fun getRefCount(): Int

    /**
     * 关闭当前 Session
     */
    fun close()

    fun  getExtElement(key: ExtKey): T?
    fun  putExtElement(key: ExtKey, element: T)
    fun removeExtElement(key: ExtKey)

    interface ExtKey
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy