古いプッシュ購読のURL中の識別子を見て自動移行を行う。アプリデータのインポート時に、使わなくなったカラムのデータを無視する。

This commit is contained in:
tateisu 2023-02-09 06:31:03 +09:00
parent 2d7fceb238
commit 7e7f726e6a
5 changed files with 73 additions and 7 deletions

View File

@ -15,8 +15,10 @@ sub cmd($){
} }
} }
cmd "./gradlew --stop";
cmd "./gradlew clean"; cmd "./gradlew clean";
cmd "./gradlew assembleNoFcmRelease"; cmd "./gradlew assembleNoFcmRelease";
cmd "./gradlew assembleFcmRelease"; cmd "./gradlew assembleFcmRelease";
cmd "./gradlew --stop";
cmd "mv app/build/outputs/apk/SubwayTooter*.apk app/"; cmd "mv app/build/outputs/apk/SubwayTooter*.apk app/";
cmd " ls -1t app/SubwayTooter*.apk |head -n 5"; cmd " ls -1t app/SubwayTooter*.apk |head -n 5";

View File

@ -182,6 +182,7 @@ object AppDataExporter {
cv.clear() cv.clear()
reader.beginObject() reader.beginObject()
val keys = ArrayList<String>()
while (reader.hasNext()) { while (reader.hasNext()) {
val name = reader.nextName() val name = reader.nextName()
if (name == null) { if (name == null) {
@ -205,6 +206,7 @@ object AppDataExporter {
"last_notification_error", "last_notification_error",
"last_subscription_error", "last_subscription_error",
"last_push_endpoint", "last_push_endpoint",
"sound_uri",
-> { -> {
reader.skipValue() reader.skipValue()
continue continue
@ -217,20 +219,35 @@ object AppDataExporter {
JsonToken.NULL -> { JsonToken.NULL -> {
reader.skipValue() reader.skipValue()
cv.putNull(name) cv.putNull(name)
keys.add(name)
} }
JsonToken.BOOLEAN -> cv.put(name, if (reader.nextBoolean()) 1 else 0) JsonToken.BOOLEAN -> {
cv.put(name, if (reader.nextBoolean()) 1 else 0)
keys.add(name)
}
JsonToken.NUMBER -> cv.put(name, reader.nextLong()) JsonToken.NUMBER -> {
cv.put(name, reader.nextLong())
keys.add(name)
}
JsonToken.STRING -> cv.put(name, reader.nextString()) JsonToken.STRING -> {
cv.put(name, reader.nextString())
keys.add(name)
}
else -> reader.skipValue() else -> {
reader.skipValue()
log.w("skip $table.$name")
}
} }
} }
reader.endObject() reader.endObject()
val new_id = db.replace(table, null, cv) val new_id = db.replace(table, null, cv)
if (new_id == -1L) error("importTable: invalid row_id") if (new_id == -1L) {
error("importTable: replace failed. table=$table, names=${keys.joinToString(",")}")
}
idMap?.put(old_id, new_id) idMap?.put(old_id, new_id)
} }
reader.endArray() reader.endArray()

View File

@ -18,6 +18,7 @@ import jp.juggler.util.data.*
import jp.juggler.util.log.LogCategory import jp.juggler.util.log.LogCategory
import jp.juggler.util.time.parseTimeIso8601 import jp.juggler.util.time.parseTimeIso8601
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.security.Provider import java.security.Provider
import java.security.SecureRandom import java.security.SecureRandom
import java.security.interfaces.ECPublicKey import java.security.interfaces.ECPublicKey
@ -88,11 +89,16 @@ class PushMastodon(
} }
} }
} }
if (params["dh"] != deviceHash) { if (params["dh"] != deviceHash && !isOldSubscription(account, oldEndpointUrl)) {
// この端末で作成した購読ではない。 // この端末で作成した購読ではない。
// TODO: 古い形式のURLを移行できないか // TODO: 古い形式のURLを移行できないか
log.w("deviceHash not match. keep it for other devices. ${account.acct} $oldEndpointUrl") log.w("deviceHash not match. keep it for other devices. ${account.acct} $oldEndpointUrl")
subLog.e(R.string.push_subscription_exists_but_not_created_by_this_device) subLog.e(R.string.push_subscription_exists_but_not_created_by_this_device)
daoAccountNotificationStatus.updateSubscriptionError(
account.acct,
context.getString(R.string.push_subscription_exists_but_not_created_by_this_device)
)
return return
} }
} }
@ -175,6 +181,22 @@ class PushMastodon(
} }
} }
private fun isOldSubscription(account: SavedAccount, url: String): Boolean {
// https://mastodon-msg.juggler.jp
// /webpushcallback
// /{ deviceId(FCM token) }
// /{ acct }
// /{flags }
// /{ client identifier}
val clientIdentifierOld = url.toHttpUrlOrNull()?.pathSegments?.elementAtOrNull(4)
?: return false
val installId = prefDevice.installIdV1?.notEmpty() ?: return false
val accessToken = account.bearerAccessToken?.notEmpty() ?: return false
val clientIdentifier = "$accessToken$installId".digestSHA256Base64Url()
return clientIdentifier == clientIdentifierOld
}
private suspend fun isSameAlerts( private suspend fun isSameAlerts(
subLog: SubscriptionLogger, subLog: SubscriptionLogger,
account: SavedAccount, account: SavedAccount,
@ -362,7 +384,7 @@ class PushMastodon(
// - notification.user のfull acct がないのでふぁぼ魔ミュートは行えない // - notification.user のfull acct がないのでふぁぼ魔ミュートは行えない
// - テキスト本文のミュートは…部分的には可能 // - テキスト本文のミュートは…部分的には可能
if(pm.textExpand?.let{TootStatus.muted_word?.matchShort(it)}==true){ if (pm.textExpand?.let { TootStatus.muted_word?.matchShort(it) } == true) {
error("muted by text word.") error("muted by text word.")
} }

View File

@ -18,6 +18,7 @@ import jp.juggler.subwaytooter.pref.prefDevice
import jp.juggler.subwaytooter.table.* import jp.juggler.subwaytooter.table.*
import jp.juggler.util.data.* import jp.juggler.util.data.*
import jp.juggler.util.log.LogCategory import jp.juggler.util.log.LogCategory
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.security.Provider import java.security.Provider
import java.security.SecureRandom import java.security.SecureRandom
import java.security.interfaces.ECPublicKey import java.security.interfaces.ECPublicKey
@ -99,6 +100,10 @@ class PushMisskey(
subLog.i(R.string.push_subscription_app_server_hash_missing_but_ok) subLog.i(R.string.push_subscription_app_server_hash_missing_but_ok)
} else { } else {
subLog.e(R.string.push_subscription_app_server_hash_missing_error) subLog.e(R.string.push_subscription_app_server_hash_missing_error)
daoAccountNotificationStatus.updateSubscriptionError(
account.acct,
context.getString(R.string.push_subscription_app_server_hash_missing_error)
)
} }
return return
} else if (willRemoveSubscription) { } else if (willRemoveSubscription) {
@ -160,6 +165,22 @@ class PushMisskey(
subLog.i(R.string.push_subscription_completed) subLog.i(R.string.push_subscription_completed)
} }
private fun isOldSubscription(account: SavedAccount, url: String): Boolean {
// https://mastodon-msg.juggler.jp
// /webpushcallback
// /{ deviceId(FCM token) }
// /{ acct }
// /{flags }
// /{ client identifier}
val clientIdentifierOld = url.toHttpUrlOrNull()?.pathSegments?.elementAtOrNull(4)
?: return false
val installId = prefDevice.installIdV1?.notEmpty() ?: return false
val accessToken = account.misskeyApiToken?.notEmpty() ?: return false
val clientIdentifier = "$accessToken$installId".digestSHA256Base64Url()
return clientIdentifier == clientIdentifierOld
}
/* /*
https://github.com/syuilo/misskey/blob/master/src/services/create-notification.ts#L46 https://github.com/syuilo/misskey/blob/master/src/services/create-notification.ts#L46
Misskeyは通知に既読の概念がありイベント発生後2秒たっても未読の時だけプッシュ通知が発生する Misskeyは通知に既読の概念がありイベント発生後2秒たっても未読の時だけプッシュ通知が発生する

View File

@ -357,6 +357,10 @@ class PushRepo(
) )
} catch (ex: Throwable) { } catch (ex: Throwable) {
subLog.e(ex, "updateSubscription failed.") subLog.e(ex, "updateSubscription failed.")
daoAccountNotificationStatus.updateSubscriptionError(
account.acct,
ex.withCaption()
)
} }
} }
prefDevice.timeLastEndpointRegister = System.currentTimeMillis() prefDevice.timeLastEndpointRegister = System.currentTimeMillis()