mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 13:12:17 +08:00
Shamrock
: 开源开发许可证
Signed-off-by: 白池 <whitechi73@outlook.com>
This commit is contained in:
parent
87629666f2
commit
4d5c054bc4
BIN
.github/jetbrains-variant-3.png
vendored
Normal file
BIN
.github/jetbrains-variant-3.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 178 KiB |
@ -69,6 +69,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
[![][contrib-image]][contrib-link]
|
[![][contrib-image]][contrib-link]
|
||||||
|
|
||||||
|
## 鸣谢
|
||||||
|
|
||||||
|
感谢[**JetBrains**](https://www.jetbrains.com/zh-cn/community/opensource/#support)提供的开源开发许可证,JetBrains 通过为核心项目贡献者免费提供一套一流的开发者工具来支持非商业开源项目。
|
||||||
|
|
||||||
|
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/zh-cn/community/opensource/#support)
|
||||||
|
|
||||||
[banner]: https://socialify.git.ci/whitechi73/OpenShamrock/image?description=1&forks=1&issues=1&logo=https%3A%2F%2Fwhitechi73.github.io%2FOpenShamrock%2Fshamrock.jpg&pattern=Plus&pulls=1&stargazers=1&theme=Auto
|
[banner]: https://socialify.git.ci/whitechi73/OpenShamrock/image?description=1&forks=1&issues=1&logo=https%3A%2F%2Fwhitechi73.github.io%2FOpenShamrock%2Fshamrock.jpg&pattern=Plus&pulls=1&stargazers=1&theme=Auto
|
||||||
|
|
||||||
[actions]: https://img.shields.io/github/actions/workflow/status/whitechi73/OpenShamrock/build-apk.yml?style=for-the-badge
|
[actions]: https://img.shields.io/github/actions/workflow/status/whitechi73/OpenShamrock/build-apk.yml?style=for-the-badge
|
||||||
@ -102,3 +108,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
[contrib-image]: https://contrib.rocks/image?repo=whitechi73/OpenShamrock
|
[contrib-image]: https://contrib.rocks/image?repo=whitechi73/OpenShamrock
|
||||||
|
|
||||||
[contrib-link]: https://github.com/whitechi73/OpenShamrock/graphs/contributors
|
[contrib-link]: https://github.com/whitechi73/OpenShamrock/graphs/contributors
|
||||||
|
|
||||||
|
@ -31,10 +31,24 @@ public class oidb_cmd0xb77 {
|
|||||||
// public final PBUInt32Field service_id = PBField.initUInt32(0);
|
// public final PBUInt32Field service_id = PBField.initUInt32(0);
|
||||||
// public final PBStringField xml = PBField.initString("");
|
// public final PBStringField xml = PBField.initString("");
|
||||||
//};
|
//};
|
||||||
//public oidb_cmd0xb77$MiniAppMsgBody mini_app_msg_body = new oidb_cmd0xb77$MiniAppMsgBody();
|
public MiniAppMsgBody mini_app_msg_body = new MiniAppMsgBody();
|
||||||
public final PBUInt64Field recv_guild_id = PBField.initUInt64(0);
|
public final PBUInt64Field recv_guild_id = PBField.initUInt64(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class MiniAppMsgBody extends MessageMicro<MiniAppMsgBody> {
|
||||||
|
//static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(
|
||||||
|
// new int[]{8, 18, 26, 32, 42, 50, 82}, new String[]{"mini_app_appid", "mini_app_path", "web_page_url", "mini_app_type", "title", "desc", "json_str"}
|
||||||
|
|
||||||
|
//, new Object[]{0L, "", "", 0, "", "", ""}, oidb_cmd0xb77$MiniAppMsgBody.class);
|
||||||
|
public final PBUInt64Field mini_app_appid = PBField.initUInt64(0);
|
||||||
|
public final PBStringField mini_app_path = PBField.initString("");
|
||||||
|
public final PBStringField web_page_url = PBField.initString("");
|
||||||
|
public final PBUInt32Field mini_app_type = PBField.initUInt32(0);
|
||||||
|
public final PBStringField title = PBField.initString("");
|
||||||
|
public final PBStringField desc = PBField.initString("");
|
||||||
|
public final PBStringField json_str = PBField.initString("");
|
||||||
|
}
|
||||||
|
|
||||||
public static class ArkMsgBody extends MessageMicro<ArkMsgBody> {
|
public static class ArkMsgBody extends MessageMicro<ArkMsgBody> {
|
||||||
public final PBStringField app = PBField.initString("");
|
public final PBStringField app = PBField.initString("");
|
||||||
public final PBStringField view = PBField.initString("");
|
public final PBStringField view = PBField.initString("");
|
||||||
|
@ -80,9 +80,12 @@ internal object PacketSvc: BaseSvc() {
|
|||||||
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, ProtoBuf.encodeToByteArray(msgPush))
|
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, ProtoBuf.encodeToByteArray(msgPush))
|
||||||
return withTimeoutOrNull(5000L) {
|
return withTimeoutOrNull(5000L) {
|
||||||
suspendCancellableCoroutine {
|
suspendCancellableCoroutine {
|
||||||
AioListener.messageLessListenerMap[msgSeq] = {
|
AioListener.registerTemporaryMsgListener(msgSeq) {
|
||||||
it.resume(this.msgId)
|
it.resume(this.msgId)
|
||||||
}
|
}
|
||||||
|
it.invokeOnCancellation {
|
||||||
|
AioListener.unregisterTemporaryMsgListener(msgSeq)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ?: -1L
|
} ?: -1L
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package moe.fuqiuluo.qqinterface.servlet.ark
|
||||||
|
sealed class ArkAppInfo(
|
||||||
|
val appId: Long,
|
||||||
|
val version: String,
|
||||||
|
val packageName: String,
|
||||||
|
val signature: String,
|
||||||
|
val miniAppId: Long = 0
|
||||||
|
) {
|
||||||
|
data object QQMusic: ArkAppInfo(
|
||||||
|
appId = 100497308,
|
||||||
|
version = "0.0.0",
|
||||||
|
packageName = "com.tencent.qqmusic",
|
||||||
|
signature = "cbd27cd7c861227d013a25b2d10f0799"
|
||||||
|
)
|
||||||
|
data object NetEaseMusic: ArkAppInfo(
|
||||||
|
appId = 100495085,
|
||||||
|
version = "0.0.0",
|
||||||
|
packageName = "com.netease.cloudmusic",
|
||||||
|
signature = "da6b069da1e2982db3e386233f68d76d"
|
||||||
|
)
|
||||||
|
|
||||||
|
data object DanMaKu: ArkAppInfo(
|
||||||
|
appId = 100951776,
|
||||||
|
version = "0.0.0",
|
||||||
|
packageName = "tv.danmaku.bili",
|
||||||
|
signature = "7194d531cbe7960a22007b9f6bdaa38b",
|
||||||
|
miniAppId = 1109937557
|
||||||
|
)
|
||||||
|
}
|
@ -1,18 +1,16 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.ark
|
package moe.fuqiuluo.qqinterface.servlet.ark
|
||||||
|
|
||||||
|
import com.tencent.mobileqq.pb.ByteStringMicro
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
||||||
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
|
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
|
||||||
import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77
|
import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77
|
||||||
|
import kotlin.coroutines.resume
|
||||||
sealed class ArkAppInfo(
|
import kotlin.time.Duration.Companion.seconds
|
||||||
val appId: Long,
|
|
||||||
val version: String,
|
|
||||||
val packageName: String,
|
|
||||||
val signature: String
|
|
||||||
) {
|
|
||||||
object QQMusic: ArkAppInfo(100497308, "0.0.0", "com.tencent.qqmusic", "cbd27cd7c861227d013a25b2d10f0799")
|
|
||||||
object NeteaseMusic: ArkAppInfo(100495085, "0.0.0", "com.netease.cloudmusic", "da6b069da1e2982db3e386233f68d76d")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object ArkMsgSvc: BaseSvc() {
|
internal object ArkMsgSvc: BaseSvc() {
|
||||||
fun tryShareMusic(
|
fun tryShareMusic(
|
||||||
@ -54,4 +52,49 @@ internal object ArkMsgSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
sendOidb("OidbSvc.0xb77_9", 0xb77, 9, req.toByteArray())
|
sendOidb("OidbSvc.0xb77_9", 0xb77, 9, req.toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun tryShareJsonMessage(
|
||||||
|
jsonString: String,
|
||||||
|
arkAppInfo: ArkAppInfo = ArkAppInfo.DanMaKu,
|
||||||
|
): Result<String> {
|
||||||
|
val msgSeq = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEC2C).qqMsgId
|
||||||
|
val req = oidb_cmd0xb77.ReqBody()
|
||||||
|
req.appid.set(arkAppInfo.appId)
|
||||||
|
req.app_type.set(1)
|
||||||
|
req.msg_style.set(10)
|
||||||
|
req.client_info.set(oidb_cmd0xb77.ClientInfo().also {
|
||||||
|
it.platform.set(1)
|
||||||
|
it.sdk_version.set(arkAppInfo.version)
|
||||||
|
it.android_package_name.set(arkAppInfo.packageName)
|
||||||
|
it.android_signature.set(arkAppInfo.signature)
|
||||||
|
})
|
||||||
|
req.ext_info.set(oidb_cmd0xb77.ExtInfo().also {
|
||||||
|
it.tag_name.set(ByteStringMicro.copyFromUtf8("shamrock"))
|
||||||
|
it.msg_seq.set(msgSeq)
|
||||||
|
})
|
||||||
|
req.send_type.set(0)
|
||||||
|
req.recv_uin.set(TicketSvc.getLongUin())
|
||||||
|
req.mini_app_msg_body.set(oidb_cmd0xb77.MiniAppMsgBody().also {
|
||||||
|
it.mini_app_appid.set(arkAppInfo.miniAppId)
|
||||||
|
it.mini_app_path.set("pages")
|
||||||
|
it.web_page_url.set("https://im.qq.com/index/")
|
||||||
|
it.title.set("title")
|
||||||
|
it.desc.set("desc")
|
||||||
|
it.json_str.set(jsonString)
|
||||||
|
})
|
||||||
|
sendOidb("OidbSvc.0xb77_9", 0xb77, 9, req.toByteArray())
|
||||||
|
val signedJson: String = withTimeoutOrNull(5.seconds) {
|
||||||
|
suspendCancellableCoroutine {
|
||||||
|
AioListener.registerTemporaryMsgListener(msgSeq) {
|
||||||
|
it.resume(elements.first {
|
||||||
|
it.elementType == MsgConstant.KELEMTYPEARKSTRUCT
|
||||||
|
}.arkElement.bytesData)
|
||||||
|
}
|
||||||
|
it.invokeOnCancellation {
|
||||||
|
AioListener.unregisterTemporaryMsgListener(msgSeq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: return Result.failure(Exception("unable to sign json"))
|
||||||
|
return Result.success(signedJson)
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package moe.fuqiuluo.qqinterface.servlet.ark
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
internal data class Region(
|
||||||
|
val adcode: Int,
|
||||||
|
val province: String?,
|
||||||
|
val city: String?
|
||||||
|
)
|
@ -15,13 +15,6 @@ import moe.fuqiuluo.shamrock.helper.LogCenter
|
|||||||
import moe.fuqiuluo.shamrock.tools.*
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
@Serializable
|
|
||||||
internal data class Region(
|
|
||||||
val adcode: Int,
|
|
||||||
val province: String?,
|
|
||||||
val city: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
internal object WeatherSvc {
|
internal object WeatherSvc {
|
||||||
suspend fun fetchWeatherCard(code: Int): Result<JsonObject> {
|
suspend fun fetchWeatherCard(code: Int): Result<JsonObject> {
|
||||||
val cookie = TicketSvc.getCookie("mp.qq.com")
|
val cookie = TicketSvc.getCookie("mp.qq.com")
|
||||||
|
@ -29,7 +29,7 @@ internal object MusicHelper {
|
|||||||
chatType,
|
chatType,
|
||||||
peerId,
|
peerId,
|
||||||
msgId,
|
msgId,
|
||||||
ArkAppInfo.NeteaseMusic,
|
ArkAppInfo.NetEaseMusic,
|
||||||
title.ifBlank { name },
|
title.ifBlank { name },
|
||||||
singerName,
|
singerName,
|
||||||
jumpUrl,
|
jumpUrl,
|
||||||
|
@ -149,6 +149,14 @@ internal class ActionSession {
|
|||||||
return params[key].asBooleanOrNull ?: default as Boolean
|
return params[key].asBooleanOrNull ?: default as Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getJsonElement(key: String): JsonElement {
|
||||||
|
return params[key]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getJsonElementOrNull(key: String): JsonElement? {
|
||||||
|
return params[key]
|
||||||
|
}
|
||||||
|
|
||||||
fun getObject(key: String): JsonObject {
|
fun getObject(key: String): JsonObject {
|
||||||
return params[key].asJsonObject
|
return params[key].asJsonObject
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.ark.ArkMsgSvc
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
|
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||||
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
|
||||||
|
@OneBotHandler("sign_ark_message")
|
||||||
|
internal object SignArkMessage: IActionHandler() {
|
||||||
|
override suspend fun internalHandle(session: ActionSession): String {
|
||||||
|
val json = if(session.isString("json")) session.getString("json")
|
||||||
|
else session.getJsonElement("json").toString()
|
||||||
|
return invoke(json, session.echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend operator fun invoke(json: String, echo: JsonElement = EmptyJsonString): String {
|
||||||
|
/*
|
||||||
|
ArkMsgSvc.tryShareJsonMessage(json).onSuccess {
|
||||||
|
return ok(SignArkMessageResult(it), echo = echo)
|
||||||
|
}.onFailure {
|
||||||
|
return error(it.message ?: it.toString(), echo)
|
||||||
|
}*/
|
||||||
|
return logic("logic error", echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SignArkMessageResult(
|
||||||
|
val result: String
|
||||||
|
)
|
||||||
|
}
|
@ -17,11 +17,13 @@ import moe.fuqiuluo.shamrock.tools.fetchGetOrThrow
|
|||||||
import moe.fuqiuluo.shamrock.tools.fetchOrNull
|
import moe.fuqiuluo.shamrock.tools.fetchOrNull
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchOrThrow
|
import moe.fuqiuluo.shamrock.tools.fetchOrThrow
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchPostJsonArray
|
import moe.fuqiuluo.shamrock.tools.fetchPostJsonArray
|
||||||
|
import moe.fuqiuluo.shamrock.tools.fetchPostJsonElement
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject
|
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchPostJsonString
|
import moe.fuqiuluo.shamrock.tools.fetchPostJsonString
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchPostOrNull
|
import moe.fuqiuluo.shamrock.tools.fetchPostOrNull
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchPostOrThrow
|
import moe.fuqiuluo.shamrock.tools.fetchPostOrThrow
|
||||||
import moe.fuqiuluo.shamrock.tools.getOrPost
|
import moe.fuqiuluo.shamrock.tools.getOrPost
|
||||||
|
import moe.fuqiuluo.shamrock.tools.isJsonArray
|
||||||
import moe.fuqiuluo.shamrock.tools.isJsonData
|
import moe.fuqiuluo.shamrock.tools.isJsonData
|
||||||
import moe.fuqiuluo.shamrock.tools.isJsonObject
|
import moe.fuqiuluo.shamrock.tools.isJsonObject
|
||||||
import moe.fuqiuluo.shamrock.tools.isJsonString
|
import moe.fuqiuluo.shamrock.tools.isJsonString
|
||||||
@ -29,6 +31,19 @@ import moe.fuqiuluo.shamrock.tools.jsonArray
|
|||||||
import moe.fuqiuluo.shamrock.tools.respond
|
import moe.fuqiuluo.shamrock.tools.respond
|
||||||
|
|
||||||
fun Routing.messageAction() {
|
fun Routing.messageAction() {
|
||||||
|
route("/sign_ark_message") {
|
||||||
|
get {
|
||||||
|
val json = fetchGetOrThrow("json")
|
||||||
|
call.respondText(SignArkMessage(json), ContentType.Application.Json)
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
val json = if (isJsonData() && (isJsonObject("json") || isJsonArray("json")))
|
||||||
|
fetchPostJsonElement("json").toString()
|
||||||
|
else fetchPostOrThrow("json")
|
||||||
|
call.respondText(SignArkMessage(json), ContentType.Application.Json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
route("/send_group_forward_(msg|message)".toRegex()) {
|
route("/send_group_forward_(msg|message)".toRegex()) {
|
||||||
post {
|
post {
|
||||||
val groupId = fetchPostOrNull("group_id")
|
val groupId = fetchPostOrNull("group_id")
|
||||||
|
@ -25,7 +25,7 @@ import kotlin.collections.HashMap
|
|||||||
|
|
||||||
internal object AioListener : IKernelMsgListener {
|
internal object AioListener : IKernelMsgListener {
|
||||||
// 通过MSG SEQ临时监听器
|
// 通过MSG SEQ临时监听器
|
||||||
internal val messageLessListenerMap = Collections.synchronizedMap(HashMap<Long, MsgRecord.() -> Unit>())
|
private val tempMessageListenerMap = Collections.synchronizedMap(HashMap<Long, suspend MsgRecord.() -> Unit>())
|
||||||
|
|
||||||
override fun onRecvMsg(msgList: ArrayList<MsgRecord>) {
|
override fun onRecvMsg(msgList: ArrayList<MsgRecord>) {
|
||||||
if (msgList.isEmpty()) return
|
if (msgList.isEmpty()) return
|
||||||
@ -37,13 +37,26 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun registerTemporaryMsgListener(
|
||||||
|
msgSeq: Long,
|
||||||
|
listener: suspend MsgRecord.() -> Unit
|
||||||
|
) {
|
||||||
|
LogCenter.log({ "注册临时消息监听器: $msgSeq" }, Level.DEBUG)
|
||||||
|
tempMessageListenerMap[msgSeq] = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unregisterTemporaryMsgListener(msgSeq: Long) {
|
||||||
|
tempMessageListenerMap.remove(msgSeq)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun handleMsg(record: MsgRecord) {
|
private suspend fun handleMsg(record: MsgRecord) {
|
||||||
try {
|
try {
|
||||||
messageLessListenerMap.firstNotNullOfOrNull {
|
tempMessageListenerMap.firstNotNullOfOrNull {
|
||||||
if (it.key == record.msgSeq) it else null
|
if (it.key == record.msgSeq) it else null
|
||||||
}?.let {
|
}?.let {
|
||||||
it.value(record)
|
it.value(record)
|
||||||
messageLessListenerMap.remove(it.key)
|
tempMessageListenerMap.remove(it.key)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (record.msgSeq < 0) return
|
if (record.msgSeq < 0) return
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user