一.标题没看懂是怎么回事
如果你的项目仅使用了Mirai-CoreAPI,但最近希望将其迁移进Android中,那么Seiko可以解决滑动验证,Bot保活等问题。
二.了解更多
前往Seiko的Github仓库预览源代码
前往Seiko的Realease下载Seiko
如果你的项目仅使用了Mirai-CoreAPI,但最近希望将其迁移进Android中,那么Seiko可以解决滑动验证,Bot保活等问题。
前往Seiko的Github仓库预览源代码
前往Seiko的Realease下载Seiko
补充:
System.getProperties().setProperty("overflow.timeout",1.minutes.inWholeMilliseconds.toString())
System.getProperties().setProperty("org.apache.tomcat.websocket.DEFAULT_BUFFER_SIZE","5242880")
适用于只使用overflow-core开发bot,并且使用spring自带的IoC能力的一套ws解决方案。可以避免早期overflow版本中反向websocket存在的各种问题。
class WSHandler : WebSocketHandler, IAdapter {
override val scope = CoroutineScope(Dispatchers.IO) + SupervisorJob()
override val logger: Logger = LoggerFactory.getLogger(ActionHandler::class.java)
override val actionHandler: ActionHandler = ActionHandler(scope.coroutineContext[Job], logger)
init {
top.mrxiaom.overflow.internal.Overflow.setup()
log.info("Youmu WebSocket 已接管默认Overflow Bot Handler,WSBufferSize: ${System.getProperty("org.apache.tomcat.websocket.DEFAULT_BUFFER_SIZE")}")
}
override fun afterConnectionEstablished(session: WebSocketSession) {
//在这里进行鉴权
scope.launch {
val botImpl = Bot(
SpringDelegatedWebSocket(session),
BotConfig(),
actionHandler
)
val versionInfo = botImpl.getVersionInfo()
if (botImpl.onebotVersion == 12) {
session.close(CloseStatus.PROTOCOL_ERROR.withReason("不支持的onebot版本:12"))
return@launch
}
net.mamoe.mirai.Bot.getInstanceOrNull(botImpl.id)?.getOriginChannel()
?.close(CloseStatus.NORMAL.withReason("当前连接下线"))
val bot = with(BotWrapper) {
val result = runCatching {
botImpl.wrap(
configuration = BotConfiguration {
botLoggerSupplier = {
LoggerFactory.getLogger("Bot#${botImpl.id}").asMiraiLogger()
}
networkLoggerSupplier = {
LoggerFactory.getLogger("Net#${botImpl.id}").asMiraiLogger()
}
}
)
}
if (result.isFailure) {
session.close(CloseStatus.PROTOCOL_ERROR.withReason("无法实例化bot"))
return@launch
}
result.getOrThrow()
}
log.info("Bot ${bot.id} 已连接,协议版本信息:$versionInfo")
net.mamoe.mirai.Bot._instances[botImpl.id] = bot
}
}
override fun handleMessage(session: WebSocketSession, message: WebSocketMessage<*>) {
if (message is TextMessage) {
val text = message.payload
onReceiveMessage(text)
}
}
override fun handleTransportError(session: WebSocketSession, exception: Throwable) {
}
@OptIn(MiraiInternalApi::class)
override fun afterConnectionClosed(session: WebSocketSession, closeStatus: CloseStatus) {
val bot = net.mamoe.mirai.Bot.instances.find {
it.getOriginChannel() == session
}
if (bot !== null) {
bot as BotWrapper
bot.eventDispatcher.broadcastAsync(BotOfflineEvent.Dropped(bot, cause = RuntimeException("连接断开")))
net.mamoe.mirai.Bot._instances.remove(bot.id)
log.info("${bot.id}断开连接")
}
}
override fun supportsPartialMessages(): Boolean = false
}
class SpringDelegatedWebSocket(val delegated: WebSocketSession) : WebSocket {
override fun close(p0: Int, p1: String?) {
delegated.close(CloseStatus(p0, p1))
}
override fun close(p0: Int) {
delegated.close(CloseStatus(p0))
}
override fun close() {
delegated.close()
}
override fun closeConnection(p0: Int, p1: String?) {
delegated.close(CloseStatus(p0, p1))
}
override fun send(p0: String?) {
delegated.sendMessage(TextMessage(p0!!))
}
override fun send(p0: ByteBuffer?) {
delegated.sendMessage(BinaryMessage(p0!!))
}
override fun send(p0: ByteArray?) {
delegated.sendMessage(BinaryMessage(p0!!))
}
override fun sendFrame(p0: Framedata?) {
TODO("Not yet implemented")
}
override fun sendFrame(p0: MutableCollection<Framedata>?) {
TODO("Not yet implemented")
}
override fun sendPing() {
delegated.sendMessage(PingMessage())
}
override fun sendFragmentedFrame(p0: Opcode?, p1: ByteBuffer?, p2: Boolean) {
TODO("Not yet implemented")
}
override fun hasBufferedData(): Boolean {
TODO("Not yet implemented")
}
override fun getRemoteSocketAddress(): InetSocketAddress = delegated.remoteAddress!!
override fun getLocalSocketAddress(): InetSocketAddress = delegated.localAddress!!
override fun isOpen(): Boolean = delegated.isOpen
override fun isClosing(): Boolean = !isOpen
override fun isFlushAndClose(): Boolean {
TODO("Not yet implemented")
}
override fun isClosed(): Boolean = !isOpen
override fun getDraft(): Draft {
TODO("Not yet implemented")
}
override fun getReadyState(): ReadyState {
TODO("Not yet implemented")
}
override fun getResourceDescriptor(): String {
TODO("Not yet implemented")
}
override fun <T : Any?> setAttachment(p0: T) {
TODO("Not yet implemented")
}
override fun <T : Any?> getAttachment(): T {
TODO("Not yet implemented")
}
override fun hasSSLSupport(): Boolean {
TODO("Not yet implemented")
}
override fun getSSLSession(): SSLSession {
TODO("Not yet implemented")
}
override fun getProtocol(): IProtocol {
TODO("Not yet implemented")
}
}
@Configuration
@EnableWebSocket
class WSConfig:WebSocketConfigurer {
override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
registry.addHandler(
WSHandler(),
"bot"
)
}
}
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package top.kagg886.youmu.backend.socket
import net.mamoe.mirai.Bot
import org.springframework.web.socket.WebSocketSession
import top.kagg886.youmu.bot.internal.spring.SpringDelegatedWebSocket
import top.mrxiaom.overflow.internal.contact.BotWrapper
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
fun Bot.getOriginChannel(): WebSocketSession {
val prop = BotWrapper::class.memberProperties.first { it.name == "implBot" } as KProperty1<BotWrapper, cn.evolvefield.onebot.client.core.Bot>
prop.isAccessible = true
val wrapper = prop.get(this as BotWrapper).channel
return (wrapper as SpringDelegatedWebSocket).delegated
}
最后附赠一套使用此方案部署在公网上的反向websocket bot:
wss://youmu.kagg886.top/bot
接入教程见此:部署bot
注:虽然名字叫舞萌开字母,但是曲库是可以自定义的。即自定义的曲库也可以是别的引诱甚至是混合的
https://github.com/kagg886/maimai-opening-song-game/releases
在群内发送:舞萌开字母
以开启一个新游戏。
<注>:一个群仅允许运行一个猜曲名实例。
在群内发送:开字母 一个字
以获得部分提示
<注>:这个字可以是数字可以是英文可以是标点符号甚至可以是日文
如果你知道了这是哪个曲子,那么请发送回答 id/别名/名字
以进行回答,回答成功后该曲目将会被完全解禁。
重复2,3步骤,直到所有曲目完全解禁。此时会提示游戏结束,并放出猜正确曲目的排行榜
实在猜不出来了怎么办呢?发送公布答案
以关闭这个游戏!
翻阅插件项目即可查看:项目地址
973510746
这个默认值是修改二维码状态刷新的时间
你改的越少,onIntervalLoop()就调用的越频繁
不过一般change的时候在CONFIRMED或CANCELLED的时候执行你的监听
至于你说的关闭二维码页面,可以为关闭方法注册一个副作用,在onIntervalLoop中检测副作用并抛出UnsupportedQRCodeCaptchaException即可
可以参考我写的扫码登录框架(
https://github.com/kagg886/Seiko/blob/master/app/src/main/java/com/kagg886/seiko/bot/AndroidSolver.java