プッシュ購読の状況を確認する

This commit is contained in:
tateisu 2018-05-14 07:12:49 +09:00
parent ea918f1b3c
commit 6f5fb65b26
9 changed files with 219 additions and 128 deletions

View File

@ -1711,7 +1711,7 @@ class Column(
if(with_attachment && ! with_highlight) path += "&only_media=1"
if(instance != null
&& instance.isEnoughVersion(version_1_6)
&& instance.versionGE(version_1_6)
// 将来的に正しく判定できる見込みがないので、Pleroma条件でのフィルタは行わない
// && instance.instanceType != TootInstance.InstanceType.Pleroma
) {

View File

@ -180,7 +180,7 @@ class TootApiClient(
}
fun getScopeString(ti : TootInstance) = when {
ti.isEnoughVersion(TootInstance.VERSION_2_4) -> "read+write+follow+push"
ti.versionGE(TootInstance.VERSION_2_4_0) -> "read+write+follow+push"
else -> "read+write+follow"
}

View File

@ -5,12 +5,13 @@ import jp.juggler.subwaytooter.util.*
import org.json.JSONObject
import java.util.regex.Pattern
class TootInstance(parser:TootParser,src : JSONObject) {
class TootInstance(parser : TootParser, src : JSONObject) {
companion object {
val rePleroma = Pattern.compile("\\bpleroma\\b",Pattern.CASE_INSENSITIVE)
private val rePleroma = Pattern.compile("\\bpleroma\\b", Pattern.CASE_INSENSITIVE)
val VERSION_2_4 = VersionString("2.4")
val VERSION_2_4_0 = VersionString("2.4.0")
val VERSION_2_4_1 = VersionString("2.4.1")
}
@ -49,11 +50,13 @@ class TootInstance(parser:TootParser,src : JSONObject) {
val max_toot_chars : Int?
// インスタンスの種別
enum class InstanceType{
enum class InstanceType {
Mastodon,
Pleroma
}
val instanceType : InstanceType
private val instanceType : InstanceType
// XXX: urls をパースしてない。使ってないから…
@ -68,22 +71,22 @@ class TootInstance(parser:TootParser,src : JSONObject) {
this.thumbnail = src.parseString("thumbnail")
this.max_toot_chars = src.parseInt("max_toot_chars")
this.instanceType = when{
rePleroma.matcher(version ?:"").find() -> InstanceType.Pleroma
else->InstanceType.Mastodon
this.instanceType = when {
rePleroma.matcher(version ?: "").find() -> InstanceType.Pleroma
else -> InstanceType.Mastodon
}
languages = src.optJSONArray("languages")?.toStringArrayList()
val parser2 = TootParser(
parser.context,
object:LinkHelper {
override val host :String
get()= uri ?: "?"
object : LinkHelper {
override val host : String
get() = uri ?: "?"
}
)
contact_account = parseItem(::TootAccount,parser2,src.optJSONObject("contact_account"))
contact_account = parseItem(::TootAccount, parser2, src.optJSONObject("contact_account"))
}
class Stats(src : JSONObject) {
@ -98,10 +101,15 @@ class TootInstance(parser:TootParser,src : JSONObject) {
}
}
fun isEnoughVersion(check : VersionString) : Boolean {
fun versionGE(check : VersionString) : Boolean {
if(decoded_version.isEmpty || check.isEmpty) return false
val i = VersionString.compare(decoded_version, check)
return i >= 0
}
fun versionEquals(check : VersionString) : Boolean {
if(decoded_version.isEmpty || check.isEmpty) return false
val i = VersionString.compare(decoded_version, check)
return i == 0
}
}

View File

@ -0,0 +1,25 @@
package jp.juggler.subwaytooter.api.entity
import jp.juggler.subwaytooter.util.parseLong
import org.json.JSONObject
import jp.juggler.subwaytooter.util.parseString
class TootPushSubscription(src : JSONObject){
val id: Long?
val endpoint : String?
val alerts= HashMap<String,Boolean>()
val server_key : String?
init{
id = src.parseLong("id")
endpoint = src.parseString("endpoint")
server_key = src.parseString("server_key")
src.optJSONObject("alerts")?.let{
for( k in it.keys() ){
alerts[k] = it.optBoolean(k)
}
}
}
}

View File

@ -205,7 +205,7 @@ class PostHelper(
instance = instance_tmp ?: return r2
account.instance = instance
}
visibility_checked = if(instance.isEnoughVersion(version_1_6)) {
visibility_checked = if(instance.versionGE(version_1_6)) {
null
} else {
val r2 = getCredential(client)

View File

@ -6,6 +6,8 @@ import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.api.TootApiResult
import jp.juggler.subwaytooter.api.entity.TootInstance
import jp.juggler.subwaytooter.api.entity.TootPushSubscription
import jp.juggler.subwaytooter.api.entity.parseItem
import jp.juggler.subwaytooter.table.SavedAccount
import okhttp3.Request
import okhttp3.RequestBody
@ -36,9 +38,11 @@ class WebPushSubscription(
private val sb = StringBuilder()
private fun addLog(s : String) {
if(sb.isNotEmpty()) sb.append('\n')
sb.append(s)
private fun addLog(s : String?) {
if(s?.isNotEmpty() == true) {
if(sb.isNotEmpty()) sb.append('\n')
sb.append(s)
}
}
private fun updateSubscription_sub(client : TootApiClient) : TootApiResult? {
@ -52,7 +56,7 @@ class WebPushSubscription(
// インスタンスバージョンの確認
var r = client.getInstanceInformation2()
val ti = r?.data as? TootInstance ?: return r
if(! ti.isEnoughVersion(TootInstance.VERSION_2_4)) {
if(! ti.versionGE(TootInstance.VERSION_2_4_0)) {
return TootApiResult(
error = context.getString(
R.string.instance_does_not_support_push_api,
@ -93,9 +97,45 @@ class WebPushSubscription(
enabled = true
// TODO 現在の購読状態を取得できれば良いのに…
// 現在の購読状態を取得
// https://github.com/tootsuite/mastodon/issues/7468
// https://github.com/tootsuite/mastodon/pull/7471
// https://github.com/tootsuite/mastodon/pull/7472
r = client.request("/api/v1/push/subscription")
res = r?.response ?: return r // cancelled or missing response
var subscriptionNotRegistered = false
when(res.code()) {
200 -> {
// 購読が存在する
}
404 -> {
if(ti.versionGE(TootInstance.VERSION_2_4_1)
|| ti.versionEquals(TootInstance.VERSION_2_4_0)
) {
// 2.4.0正式版と2.4.1以降では購読が存在しないと解釈できる
subscriptionNotRegistered = true
} else {
// 2.4.0rc の #7472 以後は購読が存在しないことを示す
// 2.4.0rc の #7472 未満はAPIがないことを示す
// コミット単位でバージョン比較する方法はないので、2.4.0正式ではない2.4.0xxxでは存在確認はできない
}
}
else -> {
addLog("${res.request()}")
addLog("${res.code()} ${res.message()}")
}
}
if(flags == 0) {
// 通知設定が全てカラなので、購読を取り消したい
if(subscriptionNotRegistered) {
if(verbose) addLog(context.getString(R.string.push_subscription_not_exists))
return TootApiResult()
}
// delete subscription
r = client.request(
"/api/v1/push/subscription",
@ -104,116 +144,127 @@ class WebPushSubscription(
res = r?.response ?: return r
when(res.code()) {
return when(res.code()) {
200 -> {
// バックグラウンド実行時はログ出力しない
if(! verbose) return TootApiResult()
addLog(context.getString(R.string.push_subscription_deleted))
return r
if(! verbose) {
TootApiResult()
} else {
addLog(context.getString(R.string.push_subscription_deleted))
r
}
}
404 -> {
enabled = false
// バックグラウンド実行時はログ出力しない
if(! verbose) return TootApiResult()
addLog(context.getString(R.string.missing_push_api))
return r
if(! verbose) {
TootApiResult()
} else {
addLog(context.getString(R.string.missing_push_api))
r
}
}
403 -> {
enabled = false
// バックグラウンド実行時はログ出力しない
if(! verbose) return TootApiResult()
addLog(context.getString(R.string.missing_push_scope))
return r
if(! verbose) {
TootApiResult()
} else {
addLog(context.getString(R.string.missing_push_scope))
r
}
}
else -> {
addLog("${res.request()}")
addLog("${res.code()} ${res.message()}")
r
}
}
addLog("${res.request()}")
addLog("${res.code()} ${res.message()}")
val json = r?.jsonObject
if(json != null) {
addLog(json.toString())
} else {
// 通知設定が空ではないので購読を行いたい
// FCM経由での配信に必要なパラメータをendpoint URLに埋め込む
val endpoint =
"${PollingWorker.APP_SERVER}/webpushcallback/${device_id.encodePercent()}/${account.acct.encodePercent()}/$flags"
// 既に登録済みで、endpointも一致しているなら何もしない
val oldSubscription = parseItem(::TootPushSubscription, r?.jsonObject)
if(oldSubscription?.endpoint == endpoint) {
subscribed = true
if(verbose) addLog(context.getString(R.string.push_subscription_already_exists))
return TootApiResult()
}
return r
}
// FCM経由での配信に必要なパラメータ
val endpoint =
"${PollingWorker.APP_SERVER}/webpushcallback/${device_id.encodePercent()}/${account.acct.encodePercent()}/$flags"
// プッシュ通知の登録
var json : JSONObject? = JSONObject().also {
it.put("subscription", JSONObject().also {
it.put("endpoint", endpoint)
it.put("keys", JSONObject().also {
it.put(
"p256dh",
"BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE"
// プッシュ通知の登録
val json = JSONObject().apply {
put("subscription", JSONObject().apply {
put("endpoint", endpoint)
put("keys", JSONObject().apply {
put(
"p256dh",
"BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE"
)
put("auth", "iRdmDrOS6eK6xvG1H6KshQ")
})
})
put("data", JSONObject().apply {
put("alerts", JSONObject().apply {
put("follow", account.notification_follow)
put("favourite", account.notification_favourite)
put("reblog", account.notification_boost)
put("mention", account.notification_mention)
})
})
}
r = client.request(
"/api/v1/push/subscription",
Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_JSON,
json.toString()
)
it.put("auth", "iRdmDrOS6eK6xvG1H6KshQ")
})
})
it.put("data", JSONObject().also {
it.put("alerts", JSONObject().also {
it.put("follow", account.notification_follow)
it.put("favourite", account.notification_favourite)
it.put("reblog", account.notification_boost)
it.put("mention", account.notification_mention)
})
})
}
r = client.request(
"/api/v1/push/subscription",
Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_JSON,
json.toString()
)
)
)
res = r?.response ?: return r
when(res.code()) {
404 -> {
// バックグラウンド実行時はログ出力しない
if(! verbose) return TootApiResult()
addLog(context.getString(R.string.missing_push_api))
return r
}
403 -> {
// バックグラウンド実行時はログ出力しない
if(! verbose) return TootApiResult()
addLog(context.getString(R.string.missing_push_scope))
return r
}
res = r?.response ?: return r
200 -> {
subscribed = true
return when(res.code()) {
404 -> {
if(! verbose) {
TootApiResult()
} else {
addLog(context.getString(R.string.missing_push_api))
r
}
}
// バックグラウンド実行時はログ出力しない
if(! verbose) return TootApiResult()
addLog(context.getString(R.string.push_subscription_updated))
return r
403 -> {
if(! verbose) {
TootApiResult()
} else {
addLog(context.getString(R.string.missing_push_scope))
r
}
}
200 -> {
subscribed = true
if(! verbose) {
TootApiResult()
} else {
addLog(context.getString(R.string.push_subscription_updated))
r
}
}
else -> {
addLog(r?.jsonObject?.toString())
r
}
}
}
json = r?.jsonObject
if(json != null) {
addLog(json.toString())
}
return r
} catch(ex : Throwable) {
return TootApiResult(error = ex.withCaption("error."))
}

View File

@ -659,16 +659,19 @@
<string name="available_mastodon_2_4_later">(available in Mastodon 2.4 or later)</string>
<string name="confirm_boost_private_from">Boost private status from %1$s ? It\'s shown to all followers.</string>
<string name="boost_private_toot_not_allowed">You can\'t boost private toot by another person.</string>
<string name="update_push_subscription">Update push subscription (Mastodon 2.4 or later)</string>
<string name="pseudo_account_not_supported">Pseudo account can\'t use notifications.</string>
<string name="instance_does_not_support_push_api">Instance version %1$s too old. that does not support Push API.</string>
<string name="missing_fcm_device_id">Can\'t prepare FCM device ID. please retry later.</string>
<string name="missing_install_id">Can\'t prepare install ID. please retry later.</string>
<string name="token_exported">Due to exporting application data or restoring from backup, this access token may be used by other devices as well. Because access tokens that are not used by other devices are required to use push notifications, we recommend updating the access token.</string>
<string name="push_subscription_deleted">Push subscription deleted.</string>
<string name="missing_push_api">The instance has no Push API endpoint.</string>
<string name="missing_push_scope">Your access token does not contains push scope. updating access token is recommended.</string>
<string name="pseudo_account_not_supported">Pseudo account can\'t use notifications.</string>
<string name="instance_does_not_support_push_api">Instance version %1$s too old. that does not support push subscription.</string>
<string name="missing_fcm_device_id">Can\'t prepare FCM device ID. please retry later.</string>
<string name="missing_install_id">Can\'t prepare install ID. please retry later.</string>
<string name="token_exported">Due to exporting application data or restoring from backup, this access token may be used by other devices as well. Because access tokens that are not used by other devices are required to use push notifications, we recommend updating the access token.</string>
<string name="missing_push_api">The instance has no push subscription API.</string>
<string name="missing_push_scope">Your access token does not contains push scope. updating access token is recommended.</string>
<string name="push_subscription_deleted">Push subscription deleted.</string>
<string name="push_subscription_updated">Push subscription updated.</string>
<string name="push_subscription_not_exists">Push subscription is not required.</string>
<string name="push_subscription_already_exists">Push subscription is up to date.</string>
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->

View File

@ -938,15 +938,17 @@
<string name="available_mastodon_2_4_later">(マストドン2.4以降で利用可能)</string>
<string name="confirm_boost_private_from">非公開トゥートを %1$s からブーストしますか? 全てのフォロワーに公開されます</string>
<string name="boost_private_toot_not_allowed">非公開トゥートをブーストできるのは本人だけです</string>
<string name="update_push_subscription">プッシュ通知購読の更新 (Mastodon2.4以降)</string>
<string name="update_push_subscription">プッシュ購読の更新 (Mastodon2.4以降)</string>
<string name="pseudo_account_not_supported">疑似アカウントでは通知を利用できません。</string>
<string name="instance_does_not_support_push_api">タンスのバージョン %1$s は古くてプッシュAPIを利用できません</string>
<string name="instance_does_not_support_push_api">タンスのバージョン %1$s は古くてプッシュ購読を利用できません</string>
<string name="missing_fcm_device_id">FCMのデバイスIDを準備できません。しばらく後に試してください。</string>
<string name="missing_install_id">インストールIDを準備できません。しばらく後に試してください。</string>
<string name="token_exported">アプリデータのエクスポート、インポート、バックアップからの復元などでアクセストークンが他のデバイスでも使われている可能性があります。アクセストークンの更新をおすすめします。</string>
<string name="push_subscription_deleted">プッシュ通知の購読を取り消しました。</string>
<string name="missing_push_api">このタンスはなぜかプッシュAPIがありません。</string>
<string name="missing_push_scope">このアクセストークンは古いのでプッシュAPIを利用する権限がありません。アクセストークンの更新をおすすめします。</string>
<string name="push_subscription_updated">プッシュ通知の購読を更新しました。</string>
<string name="push_subscription_deleted">プッシュ購読を取り消しました。</string>
<string name="missing_push_api">このタンスにはプッシュ購読APIがありません。</string>
<string name="missing_push_scope">このアクセストークンは古いのでプッシュ購読の権限がありません。アクセストークンの更新をおすすめします。</string>
<string name="push_subscription_updated">プッシュ購読を更新しました。</string>
<string name="push_subscription_not_exists">プッシュ購読は必要ありません。</string>
<string name="push_subscription_already_exists">プッシュ購読は既に最新の状態です。</string>
</resources>

View File

@ -647,12 +647,14 @@
<string name="boost_private_toot_not_allowed">You can\'t boost private toot by another person.</string>
<string name="update_push_subscription">Update push subscription (Mastodon 2.4 or later)</string>
<string name="pseudo_account_not_supported">Pseudo account can\'t use notifications.</string>
<string name="instance_does_not_support_push_api">Instance version %1$s too old. that does not support Push API.</string>
<string name="instance_does_not_support_push_api">Instance version %1$s too old. that does not support push subscription.</string>
<string name="missing_fcm_device_id">Can\'t prepare FCM device ID. please retry later.</string>
<string name="missing_install_id">Can\'t prepare install ID. please retry later.</string>
<string name="token_exported">Due to exporting application data or restoring from backup, this access token may be used by other devices as well. Because access tokens that are not used by other devices are required to use push notifications, we recommend updating the access token.</string>
<string name="push_subscription_deleted">Push subscription deleted.</string>
<string name="missing_push_api">The instance has no Push API endpoint.</string>
<string name="missing_push_api">The instance has no push subscription API.</string>
<string name="missing_push_scope">Your access token does not contains push scope. updating access token is recommended.</string>
<string name="push_subscription_deleted">Push subscription deleted.</string>
<string name="push_subscription_updated">Push subscription updated.</string>
<string name="push_subscription_not_exists">Push subscription is not required.</string>
<string name="push_subscription_already_exists">Push subscription is up to date.</string>
</resources>