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 38d7761..3858c7e 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 @@ -9,6 +9,8 @@ import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId import com.pengrad.telegrambot.model.request.* import com.pengrad.telegrambot.request.{SendMediaGroup, SendMessage} +import scala.collection.mutable.ListBuffer + /** Model of social networks' status. for example twitter tweet or * weibo status. * @@ -61,9 +63,12 @@ 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. + val results = ListBuffer[InlineQueryUnit[?]]() + + // It has multi medias, and the mosaic version is provided, + // uses the mosaic version to provide an image+text result. + if (medias.length > 1) && (medias_mosaic nonEmpty) && (medias_mosaic.get.t == Photo) && medias_mosaic.get.isInstanceOf[SocialMediaWithUrl] then + results += InlineQueryUnit(InlineQueryResultPhoto( s"[$id_head/photo/mosaic]$id_param", medias_mosaic.get.asInstanceOf[SocialMediaWithUrl].url, @@ -71,49 +76,73 @@ case class SocialContent ( ).title( s"$name $title" ).description( - s"Pictures are combined. $description" + s"Medias 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 $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 $title", - InputTextMessageContent(text_withPicPlaceholder).parseMode(ParseMode.HTML) - ).description( - s"Plain text only. $description" - )) :: Nil - else - // There are never any medias. + ).parseMode(ParseMode.HTML)) + + // It has medias, and the first media is URL formatted, then provide the first media with content, + // uses the first media to provide an image1+text result. + else if (medias nonEmpty) && medias.head.isInstanceOf[SocialMediaWithUrl] then + val media = medias.head.asInstanceOf[SocialMediaWithUrl] + results += + InlineQueryUnit(InlineQueryResultPhoto( + s"[$id_head/photo/0/contented]$id_param", + media.url, + thumbnailOrElse(media.url) + ).title( + s"$name $title" + ).description( + s"Pic 1/${medias.length}, with content. $description" + ).caption( + text_html + ).parseMode(ParseMode.HTML)) + + // It has medias, and all the previous method failed, + // then a plain text version will be provided. + else if medias.nonEmpty then + results += + InlineQueryUnit(InlineQueryResultArticle( + s"[$id_head/text_only]$id_param", + s"$name $title", + InputTextMessageContent(text_withPicPlaceholder).parseMode(ParseMode.HTML) + ).description( + s"Plain text. $description" + )) + + // The medias is provided, iterate all the medias and provide a media-only result. + // Note that the media are not URL formatted will be ignored. + val resultsMediaOnly: IndexedSeq[InlineQueryUnit[?]] = if medias nonEmpty then for mediaId <- medias.indices yield { + val media = medias(mediaId) + media match + case media_url: SocialMediaWithUrl => + InlineQueryUnit(InlineQueryResultPhoto( + s"[$id_head/photo/$mediaId/bare]$id_param", + media_url.url, + thumbnailOrElse(media_url.url) + ).title( + s"$name $title" + ).description( + s"Pic ${mediaId+1}/${medias.length}, pic only. $description" + ).caption( + media_url.sourceUrl + )) + case _ => null + } else Nil.toIndexedSeq + results ++= resultsMediaOnly + + // If there are no any medias, use the plain text mode. + if results isEmpty then + results += InlineQueryUnit(InlineQueryResultArticle( s"[$id_head/text]$id_param", s"$name $title", InputTextMessageContent(text_html).parseMode(ParseMode.HTML) ).description( description - )) :: Nil - ) ::: Nil + )) + + results.toList } } @@ -123,16 +152,16 @@ object SocialContent { enum SocialMediaType: case Photo case Video - sealed trait SocialMedia(val t: SocialMediaType) { + sealed trait SocialMedia(val t: SocialMediaType, val sourceUrl: String) { def genTelegramInputMedia: InputMedia[?] } - case class SocialMediaWithUrl (url: String)(t: SocialMediaType) extends SocialMedia(t) { + case class SocialMediaWithUrl (url: String)(t: SocialMediaType, sourceUrl: String) extends SocialMedia(t, sourceUrl) { override def genTelegramInputMedia: InputMedia[_] = t match case Photo => InputMediaPhoto(url) case Video => InputMediaVideo(url) } - case class SocialMediaWithBytesData (data: Array[Byte])(t: SocialMediaType) extends SocialMedia(t) { + case class SocialMediaWithBytesData (data: Array[Byte])(t: SocialMediaType, sourceUrl: String) extends SocialMedia(t, sourceUrl) { override def genTelegramInputMedia: InputMedia[_] = t match case Photo => InputMediaPhoto(data) 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 8bf591f..f238a26 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 @@ -51,18 +51,18 @@ object SocialTwitterParser { ( media.photos match case None => List.empty - case Some(photos) => for i <- photos yield SocialMediaWithUrl(i.url)(Photo) + case Some(photos) => for i <- photos yield SocialMediaWithUrl(i.url)(Photo, tweet.url) ) ::: ( media.videos match case None => List.empty - case Some(videos) => for i <- videos yield SocialMediaWithUrl(i.url)(Video) + case Some(videos) => for i <- videos yield SocialMediaWithUrl(i.url)(Video, tweet.url) ) val thumbnail = if media.videos.nonEmpty then - Some(SocialMediaWithUrl(media.videos.get.head.thumbnail_url)(Photo)) + Some(SocialMediaWithUrl(media.videos.get.head.thumbnail_url)(Photo, tweet.url)) else None val mediaMosaic = media.mosaic match - case Some(mosaic) => Some(SocialMediaWithUrl(mosaic.formats.jpeg)(Photo)) + case Some(mosaic) => Some(SocialMediaWithUrl(mosaic.formats.jpeg)(Photo, tweet.url)) case None => None SocialContent( if title.nonEmpty then title else 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 96ee634..871f365 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 @@ -27,27 +27,28 @@ object SocialWeiboParser { @throws[HttpError[?] | SttpClientException | ParsingFailure | DecodingFailure] def parseMStatus (api: MApi[MStatus])(originUrl: String): SocialContent = { + val statusUrl: String = genWeiboStatusUrl(StatusUrlInfo(api.data.user.id.toString, api.data.id)) val content = // language=html s"""🔸${h(api.data.user.screen_name)} | |${ch(api.data.text)} |${parseMStatus_forRetweeted(api.data)} - |${h(api.data.created_at)}""".stripMargin + |${h(api.data.created_at)}""".stripMargin val content_withPicPlaceholder = // language=html s"""🔸${h(api.data.user.screen_name)} | |${ch(api.data.text)}${parseMStatus_forPicPreview(api.data)} |${parseMStatus_forRetweeted(api.data)} - |${h(api.data.created_at)}""".stripMargin + |${h(api.data.created_at)}""".stripMargin val title = api.data.text.ensureNotExceed(35) val description: String = originUrl api.data.pics match case None => SocialContent(title, description, content, content_withPicPlaceholder, Nil) case Some(pics) => - val mediaGroup = pics.map(f => SocialMediaWithBytesData(MApi.Fetch.pic(f.large.url))(Photo)) + val mediaGroup = pics.map(f => SocialMediaWithBytesData(MApi.Fetch.pic(f.large.url))(Photo, statusUrl)) SocialContent( if title.nonEmpty then title else s"from ${api.data.user.screen_name}",