MisskeyのMentionでホスト名が省略されていた場合は閲覧者ではなく投稿者のドメインを補う

This commit is contained in:
tateisu 2022-05-31 05:30:52 +09:00
parent e182127494
commit f57ac32c78
20 changed files with 87 additions and 74 deletions

View File

@ -23,8 +23,11 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import jp.juggler.subwaytooter.Styler.defaultColorIcon
import jp.juggler.subwaytooter.action.accountRemove
import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.api.TootApiResult
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.api.runApiTask
import jp.juggler.subwaytooter.databinding.ActAccountSettingBinding
import jp.juggler.subwaytooter.dialog.ActionsDialog
import jp.juggler.subwaytooter.notification.NotificationHelper
@ -34,7 +37,10 @@ import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.PrefS
import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.*
import jp.juggler.subwaytooter.util.DecodeOptions
import jp.juggler.subwaytooter.util.EmojiDecoder
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
import jp.juggler.subwaytooter.util.openBrowser
import jp.juggler.util.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
@ -45,9 +51,11 @@ import okhttp3.RequestBody
import okio.BufferedSink
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.textColor
import java.io.*
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.max
class ActAccountSetting : AppCompatActivity(), View.OnClickListener,
@ -942,7 +950,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener,
linkHelper = account,
emojiMapProfile = src.profile_emojis,
emojiMapCustom = src.custom_emojis,
mentionDefaultHostDomain = account
authorDomain = account,
)
val displayName = src.display_name

View File

@ -358,7 +358,7 @@ fun ActPost.initializeFromRedraftStatus(account: SavedAccount, jsonText: String)
this,
mentionFullAcct = true,
mentions = baseStatus.mentions,
mentionDefaultHostDomain = account
linkHelper = account,
)
var text: CharSequence = if (account.isMisskey) {
@ -459,7 +459,7 @@ fun ActPost.initializeFromEditStatus(account: SavedAccount, jsonText: String) {
this,
mentionFullAcct = true,
mentions = baseStatus.mentions,
mentionDefaultHostDomain = account
linkHelper = account,
)
var text: CharSequence = if (account.isMisskey) {

View File

@ -24,7 +24,7 @@ fun ActPost.appendContentText(
val svEmoji = DecodeOptions(
context = this,
decodeEmoji = true,
mentionDefaultHostDomain = account ?: unknownHostAndDomain
authorDomain = account ?: unknownHostAndDomain,
).decodeEmoji(src)
if (svEmoji.isEmpty()) return

View File

@ -35,7 +35,6 @@ fun ActPost.showReplyTo() {
linkHelper = account,
short = true,
decodeEmoji = true,
mentionDefaultHostDomain = account ?: unknownHostAndDomain
).decodeHTML(states.inReplyToText)
views.ivReply.setImageUrl(Styler.calcIconRound(views.ivReply.layoutParams),
states.inReplyToImage)

View File

@ -441,7 +441,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
context,
emojiMapProfile = profile_emojis,
emojiMapCustom = custom_emojis,
mentionDefaultHostDomain = this
authorDomain = this
).decodeEmoji(sv)
}
@ -539,7 +539,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
emojiMapProfile = profile_emojis,
emojiMapCustom = custom_emojis,
unwrapEmojiImageTag = true,
mentionDefaultHostDomain = this,
authorDomain = this,
).decodeHTML(note)
.replaceAllEx(reNoteLineFeed, " ")
.trimEx()

View File

@ -28,7 +28,7 @@ class TootAccountRef(parser: TootParser, account: TootAccount) : TimelineItem()
emojiMapProfile = account.profile_emojis,
emojiMapCustom = account.custom_emojis,
unwrapEmojiImageTag = true,
mentionDefaultHostDomain = account,
authorDomain = account,
).decodeHTML(account.note)
}

View File

@ -61,7 +61,6 @@ class TootAnnouncement(parser: TootParser, src: JsonObject) {
// attachmentList = media_attachments,
highlightTrie = parser.highlightTrie,
mentions = mentions,
mentionDefaultHostDomain = parser.linkHelper
)
this.content = src.string("content") ?: ""

View File

@ -56,7 +56,7 @@ class TootCard(
DecodeOptions(
context = parser.context,
decodeEmoji = true,
mentionDefaultHostDomain = src.account
authorDomain = src.account,
).decodeHTML(src.content ?: "").toString()
},
image = src.media_attachments

View File

@ -110,7 +110,7 @@ class TootPolls(
emojiMapCustom = status.custom_emojis,
emojiMapProfile = status.profile_emojis,
mentions = status.mentions,
mentionDefaultHostDomain = status.account
authorDomain = status.account
).decodeHTML(this.question ?: "?")
}
@ -128,7 +128,7 @@ class TootPolls(
emojiMapCustom = status.custom_emojis,
emojiMapProfile = status.profile_emojis,
mentions = status.mentions,
mentionDefaultHostDomain = status.account
authorDomain = status.account
).decodeHTML(this.question ?: "?")
this.items = parseChoiceListMastodon(
@ -193,7 +193,7 @@ class TootPolls(
emojiMapCustom = status.custom_emojis,
emojiMapProfile = status.profile_emojis,
mentions = status.mentions,
mentionDefaultHostDomain = status.account
authorDomain = status.account
).decodeHTML(this.question ?: "?")
this.items = parseChoiceListFriendsNico(
@ -222,7 +222,7 @@ class TootPolls(
emojiMapCustom = status.custom_emojis,
emojiMapProfile = status.profile_emojis,
mentions = status.mentions,
mentionDefaultHostDomain = status.account,
authorDomain = status.account,
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
).decodeHTML(this.question ?: "?")
@ -315,7 +315,7 @@ class TootPolls(
emojiMapCustom = status.custom_emojis,
emojiMapProfile = status.profile_emojis,
decodeEmoji = true,
mentionDefaultHostDomain = status.account
authorDomain = status.account
)
for (o in objectArray) {
val text = reWhitespace
@ -349,7 +349,7 @@ class TootPolls(
emojiMapCustom = status.custom_emojis,
emojiMapProfile = status.profile_emojis,
decodeEmoji = true,
mentionDefaultHostDomain = status.account
authorDomain = status.account
)
for (o in objectArray) {
val text = reWhitespace
@ -383,7 +383,7 @@ class TootPolls(
emojiMapCustom = status.custom_emojis,
emojiMapProfile = status.profile_emojis,
decodeEmoji = true,
mentionDefaultHostDomain = status.account
authorDomain = status.account
)
for (i in 0 until size) {
val text = reWhitespace

View File

@ -315,7 +315,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
attachmentList = media_attachments,
highlightTrie = parser.highlightTrie,
mentions = null, // MisskeyはMFMをパースし終わるまでメンションが分からない
mentionDefaultHostDomain = account
authorDomain = account
)
this.decoded_content = options.decodeHTML(content)
@ -348,7 +348,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
attachmentList = media_attachments,
highlightTrie = parser.highlightTrie,
mentions = null, // MisskeyはMFMをパースし終わるまでメンションが分からない
mentionDefaultHostDomain = account
authorDomain = account
)
this.decoded_spoiler_text = options.decodeHTML(spoiler_text)
@ -522,7 +522,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
attachmentList = media_attachments,
highlightTrie = parser.highlightTrie,
mentions = mentions,
mentionDefaultHostDomain = account,
authorDomain = account,
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
)
@ -545,7 +545,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
emojiMapProfile = profile_emojis,
highlightTrie = parser.highlightTrie,
mentions = mentions,
mentionDefaultHostDomain = account,
authorDomain = account,
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
)
@ -750,7 +750,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
attachmentList = media_attachments,
highlightTrie = parser.highlightTrie,
mentions = mentions,
mentionDefaultHostDomain = account
authorDomain = account
)
this.decoded_content = options.decodeHTML(content)
@ -772,7 +772,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
emojiMapProfile = profile_emojis,
highlightTrie = parser.highlightTrie,
mentions = mentions,
mentionDefaultHostDomain = account
authorDomain = account
)
this.decoded_spoiler_text = options.decodeEmoji(spoiler_text)

View File

@ -329,7 +329,7 @@ private fun ColumnViewHolder.showReactions(
column.accessInfo,
decodeEmoji = true,
enlargeEmoji = 1.5f,
mentionDefaultHostDomain = column.accessInfo
authorDomain = column.accessInfo
)
val actMain = activity

View File

@ -141,7 +141,7 @@ internal class ViewHolderHeaderInstance(
activity,
accessInfo,
decodeEmoji = true,
mentionDefaultHostDomain = accessInfo
authorDomain = accessInfo
)
tvShortDescription.text = options

View File

@ -664,7 +664,7 @@ internal class ViewHolderHeaderProfile(
short = true,
emojiMapCustom = who.custom_emojis,
emojiMapProfile = who.profile_emojis,
mentionDefaultHostDomain = who
authorDomain = who
)
val nameTypeface = ActMain.timeline_font_bold

View File

@ -34,7 +34,7 @@ internal class ViewHolderHeaderSearch(
tvSearchDesc.textColor = column.getContentColor()
tvSearchDesc.text = DecodeOptions(
activity, accessInfo, decodeEmoji = true,
mentionDefaultHostDomain = accessInfo
authorDomain = accessInfo
)
.decodeHTML(column.getHeaderDesc())
}

View File

@ -68,7 +68,7 @@ fun ItemViewHolder.showPreviewCard(status: TootStatus) {
val text = DecodeOptions(
activity, accessInfo,
forceHtml = true,
mentionDefaultHostDomain = status.account
authorDomain = status.account
).decodeHTML(sb.toString())
if (text.isNotEmpty()) {

View File

@ -157,8 +157,8 @@ class SpanOutputEnv(
)
),
url = url,
options.authorDomain,
options.linkHelper,
options.mentionDefaultHostDomain,
)
}
@ -196,20 +196,23 @@ class SpanOutputEnv(
// ユーザが記述したacct
val rawAcct = Acct.parse(username, strHost)
val linkHelper = linkHelper
if (linkHelper == null) {
// 長いacctを生成する
val fullAcct = getFullAcctOrNull(
rawAcct,
null,
options.authorDomain,
linkHelper
)
if( fullAcct==null){
appendText("@${rawAcct.pretty}")
return
}
// 長いacct
// MFMでは投稿者のドメインを補うのはサーバ側の仕事の筈なので、options.mentionDefault…は見ない
val fullAcct = rawAcct.followHost(linkHelper.apDomain)
// mentionsメタデータに含まれるacct
// ユーザの記述に因らず、サーバのホスト名同じなら短い、そうでなければ長いメンション
val shortAcct = when {
linkHelper.matchHost(fullAcct.host) -> Acct.parse(username)
val shortAcct = when (fullAcct.host) {
linkHelper?.apDomain, linkHelper?.apiHost -> Acct.parse(username)
else -> fullAcct
}

View File

@ -4,12 +4,14 @@ import android.content.Context
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.ReplacementSpan
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.api.entity.HostAndDomain
import jp.juggler.subwaytooter.api.entity.NicoProfileEmoji
import jp.juggler.subwaytooter.api.entity.TootAttachmentLike
import jp.juggler.subwaytooter.api.entity.TootMention
import jp.juggler.subwaytooter.emoji.CustomEmoji
import jp.juggler.subwaytooter.table.HighlightWord
import jp.juggler.util.WordTrieTree
import org.jetbrains.anko.collections.forEachReversedByIndex
import java.util.*
class DecodeOptions(
val context: Context? = null,
@ -24,11 +26,13 @@ class DecodeOptions(
var unwrapEmojiImageTag: Boolean = false,
var enlargeCustomEmoji: Float = 1f,
var enlargeEmoji: Float = 1f,
var forceHtml: Boolean = false, // force use HTML instead of Misskey Markdown
// force use HTML instead of Misskey Markdown
var forceHtml: Boolean = false,
var mentionFullAcct: Boolean = false,
var mentions: ArrayList<TootMention>? = null,
// Account.note などmentionsがない状況でメンションリンクをfull acct化するにはアカウント等からapDomainを補う必要がある
var mentionDefaultHostDomain: HostAndDomain = linkHelper ?: unknownHostAndDomain,
// MFMはメンションのホスト名を補うのに閲覧者ではなく投稿作者のホスト名を必要とする
var authorDomain: HostAndDomain? = null,
) {
internal fun isMediaAttachment(url: String?): Boolean =

View File

@ -1087,8 +1087,8 @@ object HTMLDecoder {
getFullAcctOrNull(
mention.acct,
href,
options.authorDomain,
options.linkHelper,
options.mentionDefaultHostDomain
)?.let { afterFullAcctResolved(it) }
} else {
@ -1110,8 +1110,8 @@ object HTMLDecoder {
getFullAcctOrNull(
rawAcct,
href,
options.authorDomain,
options.linkHelper,
options.mentionDefaultHostDomain
)?.let { fullAcct ->
// mentionメタデータを捏造する

View File

@ -60,36 +60,36 @@ fun LinkHelper.matchHost(src: String?) = apiHost.match(src) || apDomain.match(sr
fun LinkHelper.matchHost(src: Host?) = apiHost == src || apDomain == src
fun LinkHelper.matchHost(src: LinkHelper) =
apiHost == src.apiHost || apDomain == src.apDomain ||
apDomain == src.apiHost || apiHost == src.apDomain
apDomain == src.apiHost || apiHost == src.apDomain
fun LinkHelper.matchHost(src: TootAccount) =
apiHost == src.apiHost || apDomain == src.apDomain ||
apDomain == src.apiHost || apiHost == src.apDomain
apDomain == src.apiHost || apiHost == src.apDomain
// user や user@host から user@host を返す
fun getFullAcctOrNull(
rawAcct: Acct,
url: String,
url: String?,
hostDomain1: HostAndDomain? = null,
hostDomain2: HostAndDomain? = null
) =
if (rawAcct.isValidFull) {
// 最初から有効なfull acctがあればそれを使う
rawAcct
} else {
// URL中のホスト名を使うが、引数でホストとドメインの対応が提供されていればドメインへの変換を試みる
val host = TootAccount.reHostInUrl.matcher(url)
.findOrNull()?.groupEx(1)?.let { Host.parse(it) }
if (host == null) {
null
} else {
Acct.parse(
rawAcct.username,
when (host) {
hostDomain1?.apiHost -> hostDomain1.apDomain
hostDomain2?.apiHost -> hostDomain2.apDomain
else -> host
}
).validFull() // apDomainが ? だった場合など
}
hostDomain2: HostAndDomain? = null,
): Acct? {
// 最初から有効なfull acctがあればそれを使う
if (rawAcct.isValidFull) return rawAcct
// (MFMのみ)URLがなければ引数から適当に補う
if (url == null) {
return (hostDomain1?.apDomain?.valid()
?: hostDomain2?.apDomain?.valid())
?.let { Acct.parse(rawAcct.username, it) }
}
var host = TootAccount.reHostInUrl.matcher(url).findOrNull()
?.groupEx(1)?.let { Host.parse(it).valid() }
// URL中のホスト名が引数で指定されたドメインなら、APIホストとAPドメインの変換を行う
when (host) {
null -> return null
hostDomain1?.apiHost -> host = hostDomain1.apDomain
hostDomain2?.apiHost -> host = hostDomain2.apDomain
}
return Acct.parse(rawAcct.username, host).validFull()
}

View File

@ -72,7 +72,7 @@ object TootTextEncoder {
context,
accessInfo,
mentions = status.mentions,
mentionDefaultHostDomain = status.account
authorDomain = status.account,
).decodeHTML(status.content)
)
@ -103,7 +103,7 @@ object TootTextEncoder {
context,
accessInfo,
mentions = status.mentions,
mentionDefaultHostDomain = status.account
authorDomain = status.account,
).decodeHTML(status.content)
)
@ -274,7 +274,7 @@ object TootTextEncoder {
DecodeOptions(
context,
accessInfo,
mentionDefaultHostDomain = who
authorDomain = who,
).decodeHTML(who.note)
)