feat(scripting): module updater

This commit is contained in:
rhunk
2024-05-22 22:39:41 +02:00
parent adf2eff024
commit 97aed78894
8 changed files with 173 additions and 149 deletions

View File

@@ -5,9 +5,8 @@ import android.os.ParcelFileDescriptor
import me.rhunk.snapenhance.bridge.scripting.IScripting
import me.rhunk.snapenhance.common.BuildConfig
import me.rhunk.snapenhance.common.logger.AbstractLogger
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
import me.rhunk.snapenhance.common.scripting.type.readModuleInfo
import org.mozilla.javascript.ScriptableObject
import java.io.BufferedReader
import java.io.InputStream
open class ScriptRuntime(
@@ -35,45 +34,6 @@ open class ScriptRuntime(
return modules.values.find { it.moduleInfo.name == name }
}
fun readModuleInfo(reader: BufferedReader): ModuleInfo {
val header = reader.readLine()
if (!header.startsWith("// ==SE_module==")) {
throw Exception("Invalid module header")
}
val properties = mutableMapOf<String, String>()
while (true) {
val line = reader.readLine()
if (line.startsWith("// ==/SE_module==")) {
break
}
val split = line.replaceFirst("//", "").split(":")
if (split.size != 2) {
throw Exception("Invalid module property")
}
properties[split[0].trim()] = split[1].trim()
}
return ModuleInfo(
name = properties["name"]?.also {
if (!it.matches(Regex("[a-z_]+"))) {
throw Exception("Invalid module name : Only lowercase letters and underscores are allowed")
}
} ?: throw Exception("Missing module name"),
version = properties["version"] ?: throw Exception("Missing module version"),
displayName = properties["displayName"],
description = properties["description"],
author = properties["author"],
minSnapchatVersion = properties["minSnapchatVersion"]?.toLongOrNull(),
minSEVersion = properties["minSEVersion"]?.toLongOrNull(),
grantedPermissions = properties["permissions"]?.split(",")?.map { it.trim() } ?: emptyList(),
)
}
fun getModuleInfo(inputStream: InputStream): ModuleInfo {
return readModuleInfo(inputStream.bufferedReader())
}
fun removeModule(scriptPath: String) {
modules.remove(scriptPath)
}
@@ -94,7 +54,7 @@ open class ScriptRuntime(
fun load(scriptPath: String, content: InputStream): JSModule {
logger.info("Loading module $scriptPath")
val bufferedReader = content.bufferedReader()
val moduleInfo = readModuleInfo(bufferedReader)
val moduleInfo = bufferedReader.readModuleInfo()
if (moduleInfo.minSEVersion != null && moduleInfo.minSEVersion > BuildConfig.VERSION_CODE) {
throw Exception("Module requires a newer version of SnapEnhance (min version: ${moduleInfo.minSEVersion})")

View File

@@ -1,28 +1,57 @@
package me.rhunk.snapenhance.common.scripting.type
import java.io.BufferedReader
data class ModuleInfo(
val name: String,
val version: String,
val displayName: String? = null,
val description: String? = null,
val updateUrl: String? = null,
val author: String? = null,
val minSnapchatVersion: Long? = null,
val minSEVersion: Long? = null,
val grantedPermissions: List<String>,
) {
override fun equals(other: Any?): Boolean {
if (other !is ModuleInfo) return false
if (other === this) return true
return name == other.name &&
version == other.version &&
displayName == other.displayName &&
description == other.description &&
author == other.author
}
fun ensurePermissionGranted(permission: Permissions) {
if (!grantedPermissions.contains(permission.key)) {
throw AssertionError("Permission $permission is not granted")
}
}
}
}
fun BufferedReader.readModuleInfo(): ModuleInfo {
val header = readLine()
if (!header.startsWith("// ==SE_module==")) {
throw Exception("Invalid module header")
}
val properties = mutableMapOf<String, String>()
while (true) {
val line = readLine()
if (line.startsWith("// ==/SE_module==")) {
break
}
val split = line.replaceFirst("//", "").split(":", limit = 2)
if (split.size != 2) {
throw Exception("Invalid module property")
}
properties[split[0].trim()] = split[1].trim()
}
return ModuleInfo(
name = properties["name"]?.also {
if (!it.matches(Regex("[a-z_]+"))) {
throw Exception("Invalid module name : Only lowercase letters and underscores are allowed")
}
} ?: throw Exception("Missing module name"),
version = properties["version"] ?: throw Exception("Missing module version"),
displayName = properties["displayName"],
description = properties["description"],
updateUrl = properties["updateUrl"],
author = properties["author"],
minSnapchatVersion = properties["minSnapchatVersion"]?.toLongOrNull(),
minSEVersion = properties["minSEVersion"]?.toLongOrNull(),
grantedPermissions = properties["permissions"]?.split(",")?.map { it.trim() } ?: emptyList(),
)
}

View File

@@ -61,7 +61,7 @@ fun <T> rememberAsyncMutableState(
defaultValue: T,
updateDispatcher: AsyncUpdateDispatcher? = null,
keys: Array<*> = emptyArray<Any>(),
getter: () -> T,
getter: suspend () -> T,
): MutableState<T> {
return rememberCommonState(
initialState = { mutableStateOf(defaultValue) },
@@ -82,7 +82,7 @@ fun <T> rememberAsyncMutableStateList(
defaultValue: List<T>,
updateDispatcher: AsyncUpdateDispatcher? = null,
keys: Array<*> = emptyArray<Any>(),
getter: () -> List<T>,
getter: suspend () -> List<T>,
): SnapshotStateList<T> {
return rememberCommonState(
initialState = { mutableStateListOf<T>().apply {

View File

@@ -0,0 +1,29 @@
package me.rhunk.snapenhance.common.util.ktx
import kotlinx.coroutines.CompletionHandler
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import okio.IOException
import kotlin.coroutines.resumeWithException
suspend inline fun Call.await(): Response {
return suspendCancellableCoroutine { continuation ->
val callback = object: CompletionHandler, Callback {
override fun invoke(cause: Throwable?) {
runCatching { cancel() }
}
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
continuation.resumeWith(runCatching { response })
}
}
enqueue(callback)
continuation.invokeOnCancellation(callback)
}
}