diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala index e8f4069..4d92f83 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala @@ -28,6 +28,8 @@ class MornyOnInlineQuery (using queryManager: MornyQueries) (using coeur: MornyC if (r isPersonal) isPersonal = true resultAnswers += r.result } + cacheTime = 1 + logger debug "Inline Query remote caches is DISABLED, you may received duplicate queries logs." if (results isEmpty) return; diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnGetSocial.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnGetSocial.scala index 807da28..7463a46 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnGetSocial.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnGetSocial.scala @@ -66,11 +66,11 @@ object OnGetSocial { case Left(texts) => weibo.guessWeiboStatusUrl(texts.trim) case Right(url) => - weibo.parseWeiboStatusUrl(url.trim).toList - }.map(f => { + weibo.parseWeiboStatusUrl(url.trim).map(url -> _).toList + }.map { (url, status) => succeed += 1 - tryFetchSocialOfWeibo(f) - }) + tryFetchSocialOfWeibo(status, url) + } { val bilibiliVideos: List[BiliVideoId] = text match @@ -124,13 +124,13 @@ object OnGetSocial { "Error on requesting FixTweet API\n" + exceptionLog(e) coeur.daemons.reporter.exception(e, "Error on requesting FixTweet API") - private def tryFetchSocialOfWeibo (url: weibo.StatusUrlInfo)(using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur) = + private def tryFetchSocialOfWeibo (url: weibo.StatusUrlInfo, rawUrl: String)(using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur) = import io.circe.{DecodingFailure, ParsingFailure} import sttp.client3.{HttpError, SttpClientException} import weibo.MApi try { val api = MApi.Fetch.statuses_show(url.id) - SocialWeiboParser.parseMStatus(api).outputToTelegram + SocialWeiboParser.parseMStatus(api)(rawUrl).outputToTelegram } catch case e: HttpError[?] => coeur.account exec SendMessage( diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolSocialContent.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolSocialContent.scala index 6e20066..3006c65 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolSocialContent.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolSocialContent.scala @@ -52,17 +52,17 @@ class ShareToolSocialContent extends ITelegramQuery { SocialTwitterParser.parseFXTweet(FXApi.Fetch.status(Some(tweet.statusPath), tweet.statusId)) .genInlineQueryResults(using "morny/share/tweet/content", tweet.statusId, - "Twitter Tweet Content" + "[Twitter]" ) } } private def weiboStatus (query: String): List[InlineQueryUnit[_]] = { - weibo.guessWeiboStatusUrl(query).flatMap { status => - SocialWeiboParser.parseMStatus(MApi.Fetch.statuses_show(status.id)) + weibo.guessWeiboStatusUrl(query).flatMap { (url, status) => + SocialWeiboParser.parseMStatus(MApi.Fetch.statuses_show(status.id))(url) .genInlineQueryResults(using "morny/share/weibo/status/content", status.id, - "Weibo Content" + "[Weibo]" ) } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala index a950f86..330568f 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala @@ -29,11 +29,15 @@ class ShareToolTwitter extends ITelegramQuery { getQueryTweetId(ID_PREFIX_FX, tweet), getTweetName(TITLE_FX, tweet), s"https://fxtwitter.com/${tweet.statusPath}" + ).description( + "URL only, and Fix-Tweet web preview available." )), InlineQueryUnit(InlineQueryResultArticle( getQueryTweetId(ID_PREFIX_VX, tweet), getTweetName(TITLE_VX, tweet), s"https://vxtwitter.com/${tweet.statusPath}" + ).description( + "URL only, and VxTwitter web preview available." )) ) ) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolXhs.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolXhs.scala index a91ea76..c461d65 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolXhs.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolXhs.scala @@ -32,7 +32,7 @@ class ShareToolXhs extends ITelegramQuery { getTitle(xhsLink), xhsLink.link ).description( - "URL only." + (if maybeFromShare.nonEmpty then s" from $maybeFromShare" else "") + "URL only." + (if maybeFromShare.nonEmpty then s" from ${maybeFromShare.get}" else "") )) ) diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialContent.scala b/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialContent.scala index 0c153d2..38d7761 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialContent.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialContent.scala @@ -30,11 +30,13 @@ import com.pengrad.telegrambot.request.{SendMediaGroup, SendMessage} * parser/formatter implementation. */ case class SocialContent ( + title: String, + description: String, text_html: String, text_withPicPlaceholder: String, medias: List[SocialMedia], medias_mosaic: Option[SocialMedia] = None, - thumbnail: Option[SocialMedia] = None + thumbnail: Option[SocialMedia] = None, ) { def thumbnailOrElse[T] (orElse: T): String | T = @@ -61,31 +63,55 @@ case class SocialContent ( def genInlineQueryResults (using id_head: String, id_param: Any, name: String): List[InlineQueryUnit[?]] = { ( if (medias_mosaic nonEmpty) && (medias_mosaic.get.t == Photo) && medias_mosaic.get.isInstanceOf[SocialMediaWithUrl] then + // It has multi medias, and the mosaic version is provided. InlineQueryUnit(InlineQueryResultPhoto( s"[$id_head/photo/mosaic]$id_param", medias_mosaic.get.asInstanceOf[SocialMediaWithUrl].url, thumbnailOrElse(medias_mosaic.get.asInstanceOf[SocialMediaWithUrl].url) - ).title(s"$name").caption(text_html).parseMode(ParseMode.HTML)) :: Nil + ).title( + s"$name $title" + ).description( + s"Pictures are combined. $description" + ).caption( + text_html + ).parseMode(ParseMode.HTML)) :: Nil else if (medias nonEmpty) && (medias.head.t == Photo) then val media = medias.head media match case media_url: SocialMediaWithUrl => + // the medias is provided, and the first one is in URL format. + // it may still contain multiple medias. + // although in only two implementations, the Twitter implementation will always give a mosaic + // pic; and the Weibo implementation never uses URL formatted medias. InlineQueryUnit(InlineQueryResultPhoto( s"[$id_head/photo/0]$id_param", media_url.url, thumbnailOrElse(media_url.url) - ).title(s"$name").caption(text_html).parseMode(ParseMode.HTML)) :: Nil + ).title( + s"$name $title" + ).description( + s"Pic 1. $description" + ).caption( + text_html + ).parseMode(ParseMode.HTML)) :: Nil case _ => + // the medias are provided but are not in URL format. + // in this case, the plain text version will be used. InlineQueryUnit(InlineQueryResultArticle( s"[$id_head/text_only]$id_param", - s"$name (text only)", + s"$name $title", InputTextMessageContent(text_withPicPlaceholder).parseMode(ParseMode.HTML) + ).description( + s"Plain text only. $description" )) :: Nil else + // There are never any medias. InlineQueryUnit(InlineQueryResultArticle( s"[$id_head/text]$id_param", - s"$name", + s"$name $title", InputTextMessageContent(text_html).parseMode(ParseMode.HTML) + ).description( + description )) :: Nil ) ::: Nil } diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialTwitterParser.scala b/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialTwitterParser.scala index b8cef11..8bf591f 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialTwitterParser.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialTwitterParser.scala @@ -5,6 +5,8 @@ import cc.sukazyo.cono.morny.data.social.SocialContent.SocialMediaType.{Photo, V import cc.sukazyo.cono.morny.extra.twitter.{FXApi, FXTweet} import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h +import cc.sukazyo.cono.morny.util.StringEnsure.ensureNotExceed + object SocialTwitterParser { def parseFXTweet_forMediaPlaceholderInContent (tweet: FXTweet): String = @@ -21,7 +23,7 @@ object SocialTwitterParser { // language=html s"""❌ Fix-Tweet ${api.code} |${h(api.message)}""".stripMargin - SocialContent(content, content, Nil) + SocialContent("ERROR", "ERROR", content, content, Nil) case Some(tweet) => val content: String = // language=html @@ -39,9 +41,11 @@ object SocialTwitterParser { | |💬${tweet.replies} 🔗${tweet.retweets} ❤️${tweet.likes} |${h(tweet.created_at)}""".stripMargin + val title: String = tweet.text.ensureNotExceed(35) + val description: String = tweet.url tweet.media match case None => - SocialContent(content, content_withMediasPlaceholder, Nil) + SocialContent(title, description, content, content_withMediasPlaceholder, Nil) case Some(media) => val mediaGroup: List[SocialMedia] = ( @@ -60,7 +64,11 @@ object SocialTwitterParser { val mediaMosaic = media.mosaic match case Some(mosaic) => Some(SocialMediaWithUrl(mosaic.formats.jpeg)(Photo)) case None => None - SocialContent(content, content_withMediasPlaceholder, mediaGroup, mediaMosaic, thumbnail) + SocialContent( + if title.nonEmpty then title else + s"from ${tweet.author.name}", + description, content, content_withMediasPlaceholder, mediaGroup, mediaMosaic, thumbnail + ) } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialWeiboParser.scala b/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialWeiboParser.scala index 7acaa08..96ee634 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialWeiboParser.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/social/SocialWeiboParser.scala @@ -7,6 +7,8 @@ import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.{cleanupH import io.circe.{DecodingFailure, ParsingFailure} import sttp.client3.{HttpError, SttpClientException} +import cc.sukazyo.cono.morny.util.StringEnsure.ensureNotExceed + object SocialWeiboParser { def parseMStatus_forPicPreview (status: MStatus): String = @@ -24,7 +26,7 @@ object SocialWeiboParser { case None => "" @throws[HttpError[?] | SttpClientException | ParsingFailure | DecodingFailure] - def parseMStatus (api: MApi[MStatus]): SocialContent = { + def parseMStatus (api: MApi[MStatus])(originUrl: String): SocialContent = { val content = // language=html s"""🔸${h(api.data.user.screen_name)} @@ -39,12 +41,18 @@ object SocialWeiboParser { |${ch(api.data.text)}${parseMStatus_forPicPreview(api.data)} |${parseMStatus_forRetweeted(api.data)} |${h(api.data.created_at)}""".stripMargin + val title = api.data.text.ensureNotExceed(35) + val description: String = originUrl api.data.pics match case None => - SocialContent(content, content_withPicPlaceholder, Nil) + SocialContent(title, description, content, content_withPicPlaceholder, Nil) case Some(pics) => val mediaGroup = pics.map(f => SocialMediaWithBytesData(MApi.Fetch.pic(f.large.url))(Photo)) - SocialContent(content, content_withPicPlaceholder, mediaGroup) + SocialContent( + if title.nonEmpty then title else + s"from ${api.data.user.screen_name}", + description, content, content_withPicPlaceholder, mediaGroup + ) } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/extra/weibo/package.scala b/src/main/scala/cc/sukazyo/cono/morny/extra/weibo/package.scala index abf4e30..b63e929 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/extra/weibo/package.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/extra/weibo/package.scala @@ -30,9 +30,9 @@ package object weibo { case REGEX_WEIBO_STATUS_URL(_, uid, id, _) => Some(StatusUrlInfo(uid, id)) case _ => None - def guessWeiboStatusUrl (text: String): List[StatusUrlInfo] = + def guessWeiboStatusUrl (text: String): List[(String, StatusUrlInfo)] = REGEX_WEIBO_STATUS_URL.findAllMatchIn(text).map(matches => { - StatusUrlInfo(matches.group(2), matches.group(3)) + matches.group(0) -> StatusUrlInfo(matches.group(2), matches.group(3)) }).toList def genWeiboStatusUrl (url: StatusUrlInfo): String = diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala b/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala index 3fd795e..cfdbf72 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala @@ -12,6 +12,11 @@ object StringEnsure { } else str } + def ensureNotExceed (size: Int, ellipsis: String = "..."): String = { + if (str.length <= size) str + else str.take(size) + ellipsis + } + def deSensitive (keepStart: Int = 2, keepEnd: Int = 4, sensitive_cover: Char = '*'): String = (str take keepStart) + (sensitive_cover.toString*(str.length-keepStart-keepEnd)) + (str takeRight keepEnd)