diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt index ee70e7c..fe62d2e 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/GroupSvc.kt @@ -16,8 +16,18 @@ import com.tencent.qqnt.kernel.nativeinterface.MemberInfo import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import friendlist.stUinInfo import io.ktor.client.call.body +import io.ktor.client.plugins.onUpload +import io.ktor.client.request.forms.formData +import io.ktor.client.request.forms.submitForm +import io.ktor.client.request.forms.submitFormWithBinaryData import io.ktor.client.request.get import io.ktor.client.request.header +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import io.ktor.http.headers +import io.ktor.http.parameters import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay @@ -48,13 +58,13 @@ import moe.fuqiuluo.shamrock.tools.GlobalClient import moe.fuqiuluo.shamrock.tools.asInt import moe.fuqiuluo.shamrock.tools.asJsonArrayOrNull import moe.fuqiuluo.shamrock.tools.asJsonObject -import moe.fuqiuluo.shamrock.tools.asJsonObjectOrNull import moe.fuqiuluo.shamrock.tools.asLong import moe.fuqiuluo.shamrock.tools.asString import moe.fuqiuluo.shamrock.tools.asStringOrNull import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty import moe.fuqiuluo.shamrock.tools.putBuf32Long import moe.fuqiuluo.shamrock.tools.slice +import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import tencent.im.oidb.cmd0x899.oidb_0x899 @@ -818,4 +828,82 @@ internal object GroupSvc: BaseSvc() { return Result.failure(Exception(body.jsonObject["em"].asStringOrNull)) } } + + @OptIn(ExperimentalSerializationApi::class) + suspend fun uploadImageTroopNotice(image: String): Result { + // 图片上传有问题 + val file = FileUtils.parseAndSave(image) + val cookie = TicketSvc.getCookie("qun.qq.com") + val bkn = TicketSvc.getBkn(TicketSvc.getRealSkey(TicketSvc.getUin())) + val response = GlobalClient.submitFormWithBinaryData( + url = "https://web.qun.qq.com/cgi-bin/announce/upload_img", + formData = formData { + append("filename", "001.png") + append("source", "troopNotice") + append("bkn", bkn) + append("m", "0") + append("pic_up", file.readBytes(), Headers.build { + append(HttpHeaders.ContentType, "image/png") + append(HttpHeaders.ContentDisposition, "filename=\"001.png\"") + }) + }, + block = { + headers { + header("Cookie", cookie) + } + } + ) + val body = Json.decodeFromStream(response.body()) + if (body.jsonObject["ec"].asInt == 0) { + var idJsonStr = body.jsonObject["id"].asStringOrNull + return if (idJsonStr != null) { + idJsonStr = idJsonStr.replace(""", "\"") + val idJson = Json.decodeFromString(idJsonStr) + LogCenter.log(idJson.toString()) + Result.success(GroupAnnouncementMessageImage( + height = idJson.asJsonObject["h"].asString, + width = idJson.asJsonObject["w"].asString, + id = idJson.asJsonObject["id"].asString, + )) + } else { + Result.failure(Exception("图片上传失败")) + } + } else { + return Result.failure(Exception(body.jsonObject["em"].asStringOrNull)) + } + } + + @OptIn(ExperimentalSerializationApi::class) + suspend fun addQunNotice(groupId: Long, text: String, image: GroupAnnouncementMessageImage?) : Result { + val cookie = TicketSvc.getCookie("qun.qq.com") + val bkn = TicketSvc.getBkn(TicketSvc.getRealSkey(TicketSvc.getUin())) + val response = GlobalClient.submitForm( + url = "https://web.qun.qq.com/cgi-bin/announce/add_qun_notice", + formParameters = parameters { + append("qid", groupId.toString()) + append("bkn", bkn) + append("text", text) + append("pinned", "0") + append("type", "1") + // todo allow custom settings + append("settings", "{\"is_show_edit_card:\"1,\"tip_window_type\":1,\"confirm_required\":1}") + if (null != image) { + append("pic", image.id) + append("imgWidth", image.width) + append("imgHeight", image.height) + } + }, + block = { + headers { + header("Cookie", cookie) + } + } + ) + val body = Json.decodeFromStream(response.body()) + return if (body.jsonObject["ec"].asInt == 0) { + Result.success(true) + } else { + Result.failure(Exception(body.jsonObject["em"].asStringOrNull)) + } + } } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt index 779774c..23c0ad4 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt @@ -31,7 +31,7 @@ internal object ActionManager { // GroupActions ModifyTroopName, LeaveTroop, KickTroopMember, BanTroopMember, SetGroupWholeBan, SetGroupAdmin, ModifyTroopMemberName, SetGroupUnique, GetTroopHonor, GroupPoke, SetEssenceMessage, DeleteEssenceMessage, - GetGroupSystemMsg, GetProhibitedMemberList, GetEssenceMessageList, GetGroupNotice, + GetGroupSystemMsg, GetProhibitedMemberList, GetEssenceMessageList, GetGroupNotice, SendGroupNotice, // MSG ACTIONS SendMessage, DeleteMessage, GetMsg, GetForwardMsg, SendGroupForwardMsg, SendGroupMessage, SendPrivateMessage, diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendGroupNotice.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendGroupNotice.kt new file mode 100644 index 0000000..5ba74d0 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendGroupNotice.kt @@ -0,0 +1,37 @@ +package moe.fuqiuluo.shamrock.remote.action.handlers + +import com.tencent.qqnt.kernel.nativeinterface.MsgConstant +import kotlinx.serialization.json.JsonElement +import moe.fuqiuluo.qqinterface.servlet.GroupSvc +import moe.fuqiuluo.qqinterface.servlet.MsgSvc +import moe.fuqiuluo.shamrock.helper.Level +import moe.fuqiuluo.shamrock.helper.LogCenter +import moe.fuqiuluo.shamrock.remote.action.ActionSession +import moe.fuqiuluo.shamrock.remote.action.IActionHandler +import moe.fuqiuluo.shamrock.tools.EmptyJsonString + +internal object SendGroupNotice: IActionHandler() { + override suspend fun internalHandle(session: ActionSession): String { + val groupId = session.getLong("group_id") + val text = session.getString("text") + val image = session.getStringOrNull("image") + return invoke(groupId, text, image, session.echo) + } + + suspend operator fun invoke(groupId: Long, text: String, image: String?, echo: JsonElement = EmptyJsonString): String { + val groupAnnouncementMessageImage = if (image != null) { + GroupSvc.uploadImageTroopNotice(image).onFailure { + LogCenter.log("上传群公告图片失败:${it.message}", Level.WARN) + }.getOrNull() + } else null + val announcements = GroupSvc.addQunNotice(groupId, text, groupAnnouncementMessageImage) + if (announcements.isSuccess) { + return ok(announcements.getOrNull(), echo) + } + return logic(announcements.exceptionOrNull()?.message ?: "", echo) + + } + + override val alias: Array = arrayOf("send_group_notice") + override fun path(): String = "_send_group_notice" +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt index dcdcacc..286fc9b 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/GroupAction.kt @@ -133,4 +133,11 @@ fun Routing.troopAction() { call.respondText(GetGroupNotice(groupId), ContentType.Application.Json) } + getOrPost("/_send_group_notice") { + val groupId = fetchOrThrow("group_id").toLong() + val text = fetchOrThrow("text") + val image = fetchOrNull("image") + call.respondText(SendGroupNotice(groupId, text, image), ContentType.Application.Json) + } + } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/GroupAnnouncement.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/GroupAnnouncement.kt index 17e7602..402ef96 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/GroupAnnouncement.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/GroupAnnouncement.kt @@ -21,4 +21,5 @@ internal data class GroupAnnouncementMessageImage ( @SerialName("height") val height: String, @SerialName("width") val width: String, @SerialName("id") val id: String, -) \ No newline at end of file +) +