mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
5 Commits
c014e85faa
...
1.1.0
Author | SHA1 | Date | |
---|---|---|---|
042f4bd330 | |||
9aef71b09f | |||
9cbe755520 | |||
df02f9f872 | |||
5cbb695a66 |
@ -14,7 +14,10 @@ import moe.fuqiuluo.shamrock.tools.toast
|
|||||||
import moe.fuqiuluo.shamrock.xposed.helper.AppTalker
|
import moe.fuqiuluo.shamrock.xposed.helper.AppTalker
|
||||||
import mqq.app.MobileQQ
|
import mqq.app.MobileQQ
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Calendar
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import java.util.Timer
|
||||||
|
import java.util.TimerTask
|
||||||
|
|
||||||
internal enum class Level(
|
internal enum class Level(
|
||||||
val id: Byte
|
val id: Byte
|
||||||
@ -31,7 +34,29 @@ internal object LogCenter {
|
|||||||
// 格式化时间
|
// 格式化时间
|
||||||
SimpleDateFormat("yyyy-MM-dd").format(Date())
|
SimpleDateFormat("yyyy-MM-dd").format(Date())
|
||||||
}_"
|
}_"
|
||||||
private val LogFile = MobileQQ.getContext().getExternalFilesDir(null)!!
|
private var LogFile = generateLogFile()
|
||||||
|
|
||||||
|
private val format = SimpleDateFormat("[HH:mm:ss] ")
|
||||||
|
private val timer = Timer()
|
||||||
|
|
||||||
|
init {
|
||||||
|
val now = Calendar.getInstance()
|
||||||
|
val tomorrowMidnight = Calendar.getInstance().apply {
|
||||||
|
add(Calendar.DAY_OF_YEAR, 1)
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}
|
||||||
|
val delay = tomorrowMidnight.timeInMillis - now.timeInMillis
|
||||||
|
timer.scheduleAtFixedRate(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
LogFile = generateLogFile()
|
||||||
|
}
|
||||||
|
}, delay, 24 * 60 * 60 * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateLogFile() = MobileQQ.getContext().getExternalFilesDir(null)!!
|
||||||
.parentFile!!.resolve("Tencent/Shamrock/log").also {
|
.parentFile!!.resolve("Tencent/Shamrock/log").also {
|
||||||
if (it.exists()) it.delete()
|
if (it.exists()) it.delete()
|
||||||
it.mkdirs()
|
it.mkdirs()
|
||||||
@ -49,8 +74,6 @@ internal object LogCenter {
|
|||||||
return@let result
|
return@let result
|
||||||
}
|
}
|
||||||
|
|
||||||
private val format = SimpleDateFormat("[HH:mm:ss] ")
|
|
||||||
|
|
||||||
fun log(string: String, level: Level = Level.INFO, toast: Boolean = false) {
|
fun log(string: String, level: Level = Level.INFO, toast: Boolean = false) {
|
||||||
if (!ShamrockConfig[DebugMode] && level == Level.DEBUG) {
|
if (!ShamrockConfig[DebugMode] && level == Level.DEBUG) {
|
||||||
return
|
return
|
||||||
|
@ -39,7 +39,7 @@ class AntiDetection: IAction {
|
|||||||
if (ShamrockConfig[AntiJvmTrace])
|
if (ShamrockConfig[AntiJvmTrace])
|
||||||
antiTrace()
|
antiTrace()
|
||||||
antiMemoryWalking()
|
antiMemoryWalking()
|
||||||
antiO3Report()
|
//antiO3Report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun antiO3Report() {
|
private fun antiO3Report() {
|
||||||
|
@ -9,11 +9,13 @@ import qq.service.QQInterfaces
|
|||||||
|
|
||||||
object SwitchStatus: IInteract, QQInterfaces() {
|
object SwitchStatus: IInteract, QQInterfaces() {
|
||||||
override fun invoke(intent: Intent) {
|
override fun invoke(intent: Intent) {
|
||||||
AppTalker.talk("switch_status") {
|
if (app.isLogin) {
|
||||||
put("account", app.currentAccountUin)
|
AppTalker.talk("switch_status") {
|
||||||
put("nickname", if (app is QQAppInterface) app.currentNickname else "unknown")
|
put("account", app.currentAccountUin)
|
||||||
put("voice", NativeLoader.isVoiceLoaded)
|
put("nickname", if (app is QQAppInterface) (app.currentNickname ?: "unknown") else "unknown")
|
||||||
put("core_version", ShamrockVersion)
|
put("voice", NativeLoader.isVoiceLoaded)
|
||||||
|
put("core_version", ShamrockVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,13 @@
|
|||||||
package qq.service.msg
|
package qq.service.msg
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString
|
||||||
import com.tencent.mobileqq.qroute.QRoute
|
import com.tencent.mobileqq.qroute.QRoute
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||||
import com.tencent.qqnt.msg.api.IMsgService
|
import com.tencent.qqnt.msg.api.IMsgService
|
||||||
import io.kritor.common.*
|
import io.kritor.common.*
|
||||||
|
import io.kritor.common.Element.ElementType
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import moe.fuqiuluo.shamrock.helper.ActionMsgException
|
import moe.fuqiuluo.shamrock.helper.ActionMsgException
|
||||||
@ -55,11 +57,13 @@ private object MsgConvertor {
|
|||||||
val text = element.textElement
|
val text = element.textElement
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
if (text.atType != MsgConstant.ATTYPEUNKNOWN) {
|
if (text.atType != MsgConstant.ATTYPEUNKNOWN) {
|
||||||
|
elem.type = ElementType.AT
|
||||||
elem.setAt(AtElement.newBuilder().apply {
|
elem.setAt(AtElement.newBuilder().apply {
|
||||||
this.uid = text.atNtUid
|
this.uid = text.atNtUid
|
||||||
this.uin = ContactHelper.getUinByUidAsync(text.atNtUid).toLong()
|
this.uin = ContactHelper.getUinByUidAsync(text.atNtUid).toLong()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
elem.type = ElementType.TEXT
|
||||||
elem.setText(TextElement.newBuilder().apply {
|
elem.setText(TextElement.newBuilder().apply {
|
||||||
this.text = text.content
|
this.text = text.content
|
||||||
})
|
})
|
||||||
@ -71,6 +75,7 @@ private object MsgConvertor {
|
|||||||
val face = element.faceElement
|
val face = element.faceElement
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
if (face.faceType == 5) {
|
if (face.faceType == 5) {
|
||||||
|
elem.type = ElementType.POKE
|
||||||
elem.setPoke(PokeElement.newBuilder().apply {
|
elem.setPoke(PokeElement.newBuilder().apply {
|
||||||
this.id = face.vaspokeId
|
this.id = face.vaspokeId
|
||||||
this.type = face.pokeType
|
this.type = face.pokeType
|
||||||
@ -78,28 +83,43 @@ private object MsgConvertor {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
when (face.faceIndex) {
|
when (face.faceIndex) {
|
||||||
114 -> elem.setBasketball(BasketballElement.newBuilder().apply {
|
114 -> {
|
||||||
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
|
elem.type = ElementType.BASKETBALL
|
||||||
})
|
elem.setBasketball(BasketballElement.newBuilder().apply {
|
||||||
|
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
358 -> elem.setDice(DiceElement.newBuilder().apply {
|
358 -> {
|
||||||
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
|
elem.type = ElementType.DICE
|
||||||
})
|
elem.setDice(DiceElement.newBuilder().apply {
|
||||||
|
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
359 -> elem.setRps(RpsElement.newBuilder().apply {
|
359 -> {
|
||||||
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
|
elem.type = ElementType.RPS
|
||||||
})
|
elem.setRps(RpsElement.newBuilder().apply {
|
||||||
|
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
394 -> elem.setFace(FaceElement.newBuilder().apply {
|
394 -> {
|
||||||
this.id = face.faceIndex
|
elem.type = ElementType.FACE
|
||||||
this.isBig = face.faceType == 3
|
elem.setFace(FaceElement.newBuilder().apply {
|
||||||
this.result = face.resultId.ifNullOrEmpty { "1" }?.toInt() ?: 1
|
this.id = face.faceIndex
|
||||||
})
|
this.isBig = face.faceType == 3
|
||||||
|
this.result = face.resultId.ifNullOrEmpty { "1" }?.toInt() ?: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
else -> elem.setFace(FaceElement.newBuilder().apply {
|
else -> {
|
||||||
this.id = face.faceIndex
|
elem.type = ElementType.FACE
|
||||||
this.isBig = face.faceType == 3
|
elem.setFace(FaceElement.newBuilder().apply {
|
||||||
})
|
this.id = face.faceIndex
|
||||||
|
this.isBig = face.faceType == 3
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success(elem.build())
|
return Result.success(elem.build())
|
||||||
@ -134,8 +154,9 @@ private object MsgConvertor {
|
|||||||
LogCenter.log({ "receive image: $image" }, Level.DEBUG)
|
LogCenter.log({ "receive image: $image" }, Level.DEBUG)
|
||||||
|
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
|
elem.type = ElementType.IMAGE
|
||||||
elem.setImage(ImageElement.newBuilder().apply {
|
elem.setImage(ImageElement.newBuilder().apply {
|
||||||
this.fileMd5 = md5
|
this.file = ByteString.copyFromUtf8(md5)
|
||||||
this.fileUrl = when (record.chatType) {
|
this.fileUrl = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
|
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
|
||||||
originalUrl = originalUrl,
|
originalUrl = originalUrl,
|
||||||
@ -190,6 +211,7 @@ private object MsgConvertor {
|
|||||||
ptt.fileName.substring(5)
|
ptt.fileName.substring(5)
|
||||||
else ptt.md5HexStr
|
else ptt.md5HexStr
|
||||||
|
|
||||||
|
elem.type = ElementType.VOICE
|
||||||
elem.setVoice(VoiceElement.newBuilder().apply {
|
elem.setVoice(VoiceElement.newBuilder().apply {
|
||||||
this.fileUrl = when (record.chatType) {
|
this.fileUrl = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid)
|
||||||
@ -201,7 +223,7 @@ private object MsgConvertor {
|
|||||||
|
|
||||||
else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}")
|
else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}")
|
||||||
}
|
}
|
||||||
this.fileMd5 = md5
|
this.file = ByteString.copyFromUtf8(md5)
|
||||||
this.magic = ptt.voiceChangeType != MsgConstant.KPTTVOICECHANGETYPENONE
|
this.magic = ptt.voiceChangeType != MsgConstant.KPTTVOICECHANGETYPENONE
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -218,8 +240,9 @@ private object MsgConvertor {
|
|||||||
it[it.size - 2].hex2ByteArray()
|
it[it.size - 2].hex2ByteArray()
|
||||||
}
|
}
|
||||||
} else video.fileName.split(".")[0].hex2ByteArray()
|
} else video.fileName.split(".")[0].hex2ByteArray()
|
||||||
|
elem.type = ElementType.VIDEO
|
||||||
elem.setVideo(VideoElement.newBuilder().apply {
|
elem.setVideo(VideoElement.newBuilder().apply {
|
||||||
this.fileMd5 = md5.toHexString()
|
this.file = ByteString.copyFromUtf8(md5.toHexString())
|
||||||
this.fileUrl = when (record.chatType) {
|
this.fileUrl = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
|
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
|
||||||
@ -233,6 +256,7 @@ private object MsgConvertor {
|
|||||||
suspend fun convertMarketFace(record: MsgRecord, element: MsgElement): Result<Element> {
|
suspend fun convertMarketFace(record: MsgRecord, element: MsgElement): Result<Element> {
|
||||||
val marketFace = element.marketFaceElement
|
val marketFace = element.marketFaceElement
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
|
elem.type = ElementType.MARKET_FACE
|
||||||
elem.setMarketFace(MarketFaceElement.newBuilder().apply {
|
elem.setMarketFace(MarketFaceElement.newBuilder().apply {
|
||||||
this.id = marketFace.emojiId.lowercase()
|
this.id = marketFace.emojiId.lowercase()
|
||||||
})
|
})
|
||||||
@ -245,6 +269,7 @@ private object MsgConvertor {
|
|||||||
when (data["app"].asString) {
|
when (data["app"].asString) {
|
||||||
"com.tencent.multimsg" -> {
|
"com.tencent.multimsg" -> {
|
||||||
val info = data["meta"].asJsonObject["detail"].asJsonObject
|
val info = data["meta"].asJsonObject["detail"].asJsonObject
|
||||||
|
elem.type = ElementType.FORWARD
|
||||||
elem.setForward(ForwardElement.newBuilder().apply {
|
elem.setForward(ForwardElement.newBuilder().apply {
|
||||||
this.resId = info["resid"].asString
|
this.resId = info["resid"].asString
|
||||||
this.uniseq = info["uniseq"].asString
|
this.uniseq = info["uniseq"].asString
|
||||||
@ -257,6 +282,7 @@ private object MsgConvertor {
|
|||||||
|
|
||||||
"com.tencent.troopsharecard" -> {
|
"com.tencent.troopsharecard" -> {
|
||||||
val info = data["meta"].asJsonObject["contact"].asJsonObject
|
val info = data["meta"].asJsonObject["contact"].asJsonObject
|
||||||
|
elem.type = ElementType.CONTACT
|
||||||
elem.setContact(ContactElement.newBuilder().apply {
|
elem.setContact(ContactElement.newBuilder().apply {
|
||||||
this.scene = Scene.GROUP
|
this.scene = Scene.GROUP
|
||||||
this.peer = info["jumpUrl"].asString.split("group_code=")[1]
|
this.peer = info["jumpUrl"].asString.split("group_code=")[1]
|
||||||
@ -265,6 +291,7 @@ private object MsgConvertor {
|
|||||||
|
|
||||||
"com.tencent.contact.lua" -> {
|
"com.tencent.contact.lua" -> {
|
||||||
val info = data["meta"].asJsonObject["contact"].asJsonObject
|
val info = data["meta"].asJsonObject["contact"].asJsonObject
|
||||||
|
elem.type = ElementType.CONTACT
|
||||||
elem.setContact(ContactElement.newBuilder().apply {
|
elem.setContact(ContactElement.newBuilder().apply {
|
||||||
this.scene = Scene.FRIEND
|
this.scene = Scene.FRIEND
|
||||||
this.peer = info["jumpUrl"].asString.split("uin=")[1]
|
this.peer = info["jumpUrl"].asString.split("uin=")[1]
|
||||||
@ -273,6 +300,7 @@ private object MsgConvertor {
|
|||||||
|
|
||||||
"com.tencent.map" -> {
|
"com.tencent.map" -> {
|
||||||
val info = data["meta"].asJsonObject["Location.Search"].asJsonObject
|
val info = data["meta"].asJsonObject["Location.Search"].asJsonObject
|
||||||
|
elem.type = ElementType.LOCATION
|
||||||
elem.setLocation(LocationElement.newBuilder().apply {
|
elem.setLocation(LocationElement.newBuilder().apply {
|
||||||
this.lat = info["lat"].asString.toFloat()
|
this.lat = info["lat"].asString.toFloat()
|
||||||
this.lon = info["lng"].asString.toFloat()
|
this.lon = info["lng"].asString.toFloat()
|
||||||
@ -281,9 +309,12 @@ private object MsgConvertor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> elem.setJson(JsonElement.newBuilder().apply {
|
else -> {
|
||||||
this.json = data.toString()
|
elem.type = ElementType.JSON
|
||||||
})
|
elem.setJson(JsonElement.newBuilder().apply {
|
||||||
|
this.json = data.toString()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Result.success(elem.build())
|
return Result.success(elem.build())
|
||||||
}
|
}
|
||||||
@ -291,6 +322,7 @@ private object MsgConvertor {
|
|||||||
suspend fun convertReply(record: MsgRecord, element: MsgElement): Result<Element> {
|
suspend fun convertReply(record: MsgRecord, element: MsgElement): Result<Element> {
|
||||||
val reply = element.replyElement
|
val reply = element.replyElement
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
|
elem.type = ElementType.REPLY
|
||||||
elem.setReply(ReplyElement.newBuilder().apply {
|
elem.setReply(ReplyElement.newBuilder().apply {
|
||||||
val msgSeq = reply.replayMsgSeq
|
val msgSeq = reply.replayMsgSeq
|
||||||
val contact = MessageHelper.generateContact(record)
|
val contact = MessageHelper.generateContact(record)
|
||||||
@ -332,6 +364,7 @@ private object MsgConvertor {
|
|||||||
else -> RichProtoSvc.getGroupFileDownUrl(record.peerUin, fileId, bizId)
|
else -> RichProtoSvc.getGroupFileDownUrl(record.peerUin, fileId, bizId)
|
||||||
}
|
}
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
|
elem.type = ElementType.FILE
|
||||||
elem.setFile(FileElement.newBuilder().apply {
|
elem.setFile(FileElement.newBuilder().apply {
|
||||||
this.name = fileName
|
this.name = fileName
|
||||||
this.size = fileSize
|
this.size = fileSize
|
||||||
@ -347,6 +380,7 @@ private object MsgConvertor {
|
|||||||
suspend fun convertMarkdown(record: MsgRecord, element: MsgElement): Result<Element> {
|
suspend fun convertMarkdown(record: MsgRecord, element: MsgElement): Result<Element> {
|
||||||
val markdown = element.markdownElement
|
val markdown = element.markdownElement
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
|
elem.type = ElementType.MARKDOWN
|
||||||
elem.setMarkdown(MarkdownElement.newBuilder().apply {
|
elem.setMarkdown(MarkdownElement.newBuilder().apply {
|
||||||
this.markdown = markdown.content
|
this.markdown = markdown.content
|
||||||
})
|
})
|
||||||
@ -356,6 +390,7 @@ private object MsgConvertor {
|
|||||||
suspend fun convertBubbleFace(record: MsgRecord, element: MsgElement): Result<Element> {
|
suspend fun convertBubbleFace(record: MsgRecord, element: MsgElement): Result<Element> {
|
||||||
val bubbleFace = element.faceBubbleElement
|
val bubbleFace = element.faceBubbleElement
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
|
elem.type = ElementType.BUBBLE_FACE
|
||||||
elem.setBubbleFace(BubbleFaceElement.newBuilder().apply {
|
elem.setBubbleFace(BubbleFaceElement.newBuilder().apply {
|
||||||
this.id = bubbleFace.yellowFaceInfo.index
|
this.id = bubbleFace.yellowFaceInfo.index
|
||||||
this.count = bubbleFace.faceCount ?: 1
|
this.count = bubbleFace.faceCount ?: 1
|
||||||
@ -366,6 +401,7 @@ private object MsgConvertor {
|
|||||||
suspend fun convertInlineKeyboard(record: MsgRecord, element: MsgElement): Result<Element> {
|
suspend fun convertInlineKeyboard(record: MsgRecord, element: MsgElement): Result<Element> {
|
||||||
val inlineKeyboard = element.inlineKeyboardElement
|
val inlineKeyboard = element.inlineKeyboardElement
|
||||||
val elem = Element.newBuilder()
|
val elem = Element.newBuilder()
|
||||||
|
elem.type = ElementType.BUTTON
|
||||||
elem.setButton(ButtonElement.newBuilder().apply {
|
elem.setButton(ButtonElement.newBuilder().apply {
|
||||||
inlineKeyboard.rows.forEach { row ->
|
inlineKeyboard.rows.forEach { row ->
|
||||||
this.addRows(ButtonRow.newBuilder().apply {
|
this.addRows(ButtonRow.newBuilder().apply {
|
||||||
|
Reference in New Issue
Block a user