mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-25 20:47:38 +08:00
Compare commits
3 Commits
3d1699ea1d
...
ad65ab7a73
Author | SHA1 | Date | |
---|---|---|---|
ad65ab7a73 | |||
20c9916535 | |||
79d41d5e72 |
@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur
|
|||||||
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
||||||
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
||||||
|
|
||||||
VERSION = 1.3.0-dev11.1
|
VERSION = 1.3.0-dev14
|
||||||
|
|
||||||
USE_DELTA = false
|
USE_DELTA = false
|
||||||
VERSION_DELTA =
|
VERSION_DELTA =
|
||||||
|
@ -2,16 +2,10 @@ package cc.sukazyo.cono.morny.bot.command
|
|||||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
||||||
import cc.sukazyo.cono.morny.MornyCoeur
|
import cc.sukazyo.cono.morny.MornyCoeur
|
||||||
import cc.sukazyo.cono.morny.extra.{twitter, weibo}
|
import cc.sukazyo.cono.morny.bot.event.OnGetSocial
|
||||||
import cc.sukazyo.cono.morny.extra.twitter.{FXApi, TweetUrlInformation}
|
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||||
import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
|
|
||||||
import cc.sukazyo.cono.morny.extra.weibo.StatusUrlInfo
|
|
||||||
import com.pengrad.telegrambot.model.Update
|
import com.pengrad.telegrambot.model.Update
|
||||||
import com.pengrad.telegrambot.model.request.{InputMedia, InputMediaPhoto, InputMediaVideo, ParseMode}
|
import com.pengrad.telegrambot.request.SendSticker
|
||||||
import com.pengrad.telegrambot.request.{SendMediaGroup, SendMessage, SendSticker}
|
|
||||||
import io.circe.{DecodingFailure, ParsingFailure}
|
|
||||||
import sttp.client3.{HttpError, SttpClientException}
|
|
||||||
|
|
||||||
class GetSocial (using coeur: MornyCoeur) extends ITelegramCommand {
|
class GetSocial (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||||
|
|
||||||
@ -30,111 +24,8 @@ class GetSocial (using coeur: MornyCoeur) extends ITelegramCommand {
|
|||||||
|
|
||||||
if command.args.length < 1 then { do404(); return }
|
if command.args.length < 1 then { do404(); return }
|
||||||
|
|
||||||
var succeed = 0
|
if !OnGetSocial.tryFetchSocial(command.args(0))(using event.message.chat.id, event.message.messageId) then
|
||||||
twitter.parseTweetUrl(command.args(0)) match
|
do404()
|
||||||
case None =>
|
|
||||||
case Some(TweetUrlInformation(_, _, screenName, statusId, _, _)) =>
|
|
||||||
succeed += 1
|
|
||||||
try {
|
|
||||||
val api = FXApi.Fetch.status(Some(screenName), statusId)
|
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
|
|
||||||
api.tweet match
|
|
||||||
case None =>
|
|
||||||
coeur.account exec SendMessage(
|
|
||||||
event.message.chat.id,
|
|
||||||
// language=html
|
|
||||||
s"""❌ Fix-Tweet <code>${api.code}</code>
|
|
||||||
|<i>${h(api.message)}</i>""".stripMargin
|
|
||||||
).replyToMessageId(event.message.messageId).parseMode(ParseMode.HTML)
|
|
||||||
case Some(tweet) =>
|
|
||||||
val content: String =
|
|
||||||
// language=html
|
|
||||||
s"""⚪️ <b>${h(tweet.author.name)} <a href="${tweet.author.url}">@${h(tweet.author.screen_name)}</a></b>
|
|
||||||
|
|
|
||||||
|${h(tweet.text)}
|
|
||||||
|
|
|
||||||
|<i>💬${tweet.replies} 🔗${tweet.retweets} ❤️${tweet.likes}</i>
|
|
||||||
|<i><a href="${tweet.url}">${h(tweet.created_at)}</a></i>""".stripMargin
|
|
||||||
tweet.media match
|
|
||||||
case None =>
|
|
||||||
coeur.account exec SendMessage(
|
|
||||||
event.message.chat.id,
|
|
||||||
content
|
|
||||||
).replyToMessageId(event.message.messageId).parseMode(ParseMode.HTML)
|
|
||||||
case Some(media) =>
|
|
||||||
val mediaGroup: List[InputMedia[?]] =
|
|
||||||
(
|
|
||||||
media.photos match
|
|
||||||
case None => List.empty
|
|
||||||
case Some(photos) => for i <- photos yield InputMediaPhoto(i.url)
|
|
||||||
) ::: (
|
|
||||||
media.videos match
|
|
||||||
case None => List.empty
|
|
||||||
case Some(videos) => for i <- videos yield InputMediaVideo(i.url)
|
|
||||||
)
|
|
||||||
mediaGroup.head.caption(content)
|
|
||||||
mediaGroup.head.parseMode(ParseMode.HTML)
|
|
||||||
coeur.account exec SendMediaGroup(
|
|
||||||
event.message.chat.id,
|
|
||||||
mediaGroup:_*
|
|
||||||
).replyToMessageId(event.message.messageId)
|
|
||||||
} catch case e: (SttpClientException|ParsingFailure|DecodingFailure) =>
|
|
||||||
coeur.account exec SendSticker(
|
|
||||||
event.message.chat.id,
|
|
||||||
TelegramStickers.ID_NETWORK_ERR
|
|
||||||
).replyToMessageId(event.message.messageId)
|
|
||||||
logger error
|
|
||||||
"Error on requesting FixTweet API\n" + exceptionLog(e)
|
|
||||||
coeur.daemons.reporter.exception(e, "Error on requesting FixTweet API")
|
|
||||||
|
|
||||||
weibo.parseWeiboStatusUrl(command.args(0)) match
|
|
||||||
case None =>
|
|
||||||
case Some(StatusUrlInfo(_, id)) =>
|
|
||||||
succeed += 1
|
|
||||||
try {
|
|
||||||
val api = weibo.MApi.Fetch.statuses_show(id)
|
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.{cleanupHtml as ch, escapeHtml as h}
|
|
||||||
val content =
|
|
||||||
// language=html
|
|
||||||
s"""🔸<b><a href="${api.data.user.profile_url}">${h(api.data.user.screen_name)}</a></b>
|
|
||||||
|
|
|
||||||
|${ch(api.data.text)}
|
|
||||||
|
|
|
||||||
|<i><a href="${weibo.genWeiboStatusUrl(StatusUrlInfo(api.data.user.id.toString, api.data.id))}">${h(api.data.created_at)}</a></i>""".stripMargin
|
|
||||||
api.data.pics match
|
|
||||||
case None =>
|
|
||||||
coeur.account exec SendMessage(
|
|
||||||
event.message.chat.id,
|
|
||||||
content
|
|
||||||
).replyToMessageId(event.message.messageId).parseMode(ParseMode.HTML)
|
|
||||||
case Some(pics) =>
|
|
||||||
// val mediaGroup = pics.map(f =>
|
|
||||||
// InputMediaPhoto(weibo.PicUrl(weibo.randomPicCdn, "large", f.pid).toUrl))
|
|
||||||
val mediaGroup = pics.map(f => InputMediaPhoto(weibo.MApi.Fetch.pic(f.large.url)))
|
|
||||||
mediaGroup.head.caption(content)
|
|
||||||
mediaGroup.head.parseMode(ParseMode.HTML)
|
|
||||||
coeur.account exec SendMediaGroup(
|
|
||||||
event.message.chat.id,
|
|
||||||
mediaGroup:_*
|
|
||||||
).replyToMessageId(event.message.messageId)
|
|
||||||
} catch
|
|
||||||
case e: HttpError[?] =>
|
|
||||||
coeur.account exec SendMessage(
|
|
||||||
event.message.chat.id,
|
|
||||||
// language=html
|
|
||||||
s"""Weibo Request Error <code>${e.statusCode}</code>
|
|
||||||
|<pre><code>${e.body}</code></pre>""".stripMargin
|
|
||||||
).replyToMessageId(event.message.messageId).parseMode(ParseMode.HTML)
|
|
||||||
case e: (SttpClientException|ParsingFailure|DecodingFailure) =>
|
|
||||||
coeur.account exec SendSticker(
|
|
||||||
event.message.chat.id,
|
|
||||||
TelegramStickers.ID_NETWORK_ERR
|
|
||||||
).replyToMessageId(event.message.messageId)
|
|
||||||
logger error
|
|
||||||
"Error on requesting Weibo m.API\n" + exceptionLog(e)
|
|
||||||
coeur.daemons.reporter.exception(e, "Error on requesting Weibo m.API")
|
|
||||||
|
|
||||||
if succeed == 0 then do404()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ class MornyEventListeners (using manager: EventListenerManager) (using coeur: Mo
|
|||||||
OnUserSlashAction(),
|
OnUserSlashAction(),
|
||||||
OnCallMe(),
|
OnCallMe(),
|
||||||
OnCallMsgSend(),
|
OnCallMsgSend(),
|
||||||
|
OnGetSocial(),
|
||||||
OnMedicationNotifyApply(),
|
OnMedicationNotifyApply(),
|
||||||
OnEventHackHandle()
|
OnEventHackHandle()
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
package cc.sukazyo.cono.morny.bot.event
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.MornyCoeur
|
||||||
|
import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener}
|
||||||
|
import cc.sukazyo.cono.morny.bot.event.OnGetSocial.tryFetchSocial
|
||||||
|
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||||
|
import cc.sukazyo.cono.morny.extra.{twitter, weibo}
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||||
|
import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
|
||||||
|
import cc.sukazyo.cono.morny.data.social.{SocialTwitterParser, SocialWeiboParser}
|
||||||
|
import com.pengrad.telegrambot.model.Chat
|
||||||
|
import com.pengrad.telegrambot.model.request.ParseMode
|
||||||
|
import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
|
||||||
|
|
||||||
|
class OnGetSocial (using coeur: MornyCoeur) extends EventListener {
|
||||||
|
|
||||||
|
override def onMessage (using event: EventEnv): Unit = {
|
||||||
|
import event.update.message as messageEvent
|
||||||
|
|
||||||
|
if messageEvent.chat.`type` != Chat.Type.Private then return;
|
||||||
|
if messageEvent.text == null then return;
|
||||||
|
|
||||||
|
if tryFetchSocial(messageEvent.text)(using messageEvent.chat.id, messageEvent.messageId) then
|
||||||
|
event.setEventOk
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object OnGetSocial {
|
||||||
|
|
||||||
|
/** Try fetch from url from input and output fetched social content.
|
||||||
|
*
|
||||||
|
* @param text input text, maybe a social url.
|
||||||
|
* @param replyChat chat that should be output to.
|
||||||
|
* @param replyToMessage message that should be reply to.
|
||||||
|
* @param coeur [[MornyCoeur]] instance for executing Telegram function.
|
||||||
|
* @return [[true]] if fetched social content and sent something out.
|
||||||
|
*/
|
||||||
|
def tryFetchSocial (text: String)(using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur): Boolean = {
|
||||||
|
val _text = text.trim
|
||||||
|
|
||||||
|
var succeed = 0
|
||||||
|
|
||||||
|
import io.circe.{DecodingFailure, ParsingFailure}
|
||||||
|
import sttp.client3.{HttpError, SttpClientException}
|
||||||
|
import twitter.{FXApi, TweetUrlInformation}
|
||||||
|
import weibo.{MApi, StatusUrlInfo}
|
||||||
|
twitter.parseTweetUrl(_text) match
|
||||||
|
case None =>
|
||||||
|
case Some(TweetUrlInformation(_, _, screenName, statusId, _, _)) =>
|
||||||
|
succeed += 1
|
||||||
|
try {
|
||||||
|
val api = FXApi.Fetch.status(Some(screenName), statusId)
|
||||||
|
SocialTwitterParser.parseFXTweet(api).outputToTelegram
|
||||||
|
} catch case e: (SttpClientException | ParsingFailure | DecodingFailure) =>
|
||||||
|
coeur.account exec SendSticker(
|
||||||
|
replyChat,
|
||||||
|
TelegramStickers.ID_NETWORK_ERR
|
||||||
|
).replyToMessageId(replyToMessage)
|
||||||
|
logger error
|
||||||
|
"Error on requesting FixTweet API\n" + exceptionLog(e)
|
||||||
|
coeur.daemons.reporter.exception(e, "Error on requesting FixTweet API")
|
||||||
|
|
||||||
|
weibo.parseWeiboStatusUrl(_text) match
|
||||||
|
case None =>
|
||||||
|
case Some(StatusUrlInfo(_, id)) =>
|
||||||
|
succeed += 1
|
||||||
|
try {
|
||||||
|
val api = MApi.Fetch.statuses_show(id)
|
||||||
|
SocialWeiboParser.parseMStatus(api).outputToTelegram
|
||||||
|
} catch
|
||||||
|
case e: HttpError[?] =>
|
||||||
|
coeur.account exec SendMessage(
|
||||||
|
replyChat,
|
||||||
|
// language=html
|
||||||
|
s"""Weibo Request Error <code>${e.statusCode}</code>
|
||||||
|
|<pre><code>${e.body}</code></pre>""".stripMargin
|
||||||
|
).replyToMessageId(replyToMessage).parseMode(ParseMode.HTML)
|
||||||
|
case e: (SttpClientException | ParsingFailure | DecodingFailure) =>
|
||||||
|
coeur.account exec SendSticker(
|
||||||
|
replyChat,
|
||||||
|
TelegramStickers.ID_NETWORK_ERR
|
||||||
|
).replyToMessageId(replyToMessage)
|
||||||
|
logger error
|
||||||
|
"Error on requesting Weibo m.API\n" + exceptionLog(e)
|
||||||
|
coeur.daemons.reporter.exception(e, "Error on requesting Weibo m.API")
|
||||||
|
|
||||||
|
succeed > 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,7 +12,8 @@ class MornyQueries (using MornyCoeur) {
|
|||||||
RawText(),
|
RawText(),
|
||||||
MyInformation(),
|
MyInformation(),
|
||||||
ShareToolTwitter(),
|
ShareToolTwitter(),
|
||||||
ShareToolBilibili()
|
ShareToolBilibili(),
|
||||||
|
ShareToolSocialContent()
|
||||||
)
|
)
|
||||||
|
|
||||||
def query (event: Update): List[InlineQueryUnit[_]] = {
|
def query (event: Update): List[InlineQueryUnit[_]] = {
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package cc.sukazyo.cono.morny.bot.query
|
||||||
|
import cc.sukazyo.cono.morny.data.social.{SocialTwitterParser, SocialWeiboParser}
|
||||||
|
import cc.sukazyo.cono.morny.extra.{twitter, weibo}
|
||||||
|
import cc.sukazyo.cono.morny.extra.twitter.{FXApi, TweetUrlInformation}
|
||||||
|
import cc.sukazyo.cono.morny.extra.weibo.{MApi, StatusUrlInfo}
|
||||||
|
import com.pengrad.telegrambot.model.Update
|
||||||
|
|
||||||
|
class ShareToolSocialContent extends ITelegramQuery {
|
||||||
|
|
||||||
|
override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
|
||||||
|
|
||||||
|
val _queryRaw = event.inlineQuery.query
|
||||||
|
val query =
|
||||||
|
_queryRaw.trim match
|
||||||
|
case _startsWithTag if _startsWithTag startsWith "get " =>
|
||||||
|
(_startsWithTag drop 4)trim
|
||||||
|
case _endsWithTag if _endsWithTag endsWith " get" =>
|
||||||
|
(_endsWithTag dropRight 4)trim
|
||||||
|
case _ => return null
|
||||||
|
|
||||||
|
(
|
||||||
|
twitter.parseTweetUrl(query) match
|
||||||
|
case Some(TweetUrlInformation(_, statusPath, _, statusId, _, _)) =>
|
||||||
|
SocialTwitterParser.parseFXTweet(FXApi.Fetch.status(Some(statusPath), statusId))
|
||||||
|
.genInlineQueryResults(using
|
||||||
|
"morny/share/tweet/content", statusId,
|
||||||
|
"Twitter Tweet Content"
|
||||||
|
)
|
||||||
|
case None => Nil
|
||||||
|
) ::: (
|
||||||
|
weibo.parseWeiboStatusUrl(query) match
|
||||||
|
case Some(StatusUrlInfo(_, id)) =>
|
||||||
|
SocialWeiboParser.parseMStatus(MApi.Fetch.statuses_show(id))
|
||||||
|
.genInlineQueryResults(using
|
||||||
|
"morny/share/weibo/status/content", id,
|
||||||
|
"Weibo Content"
|
||||||
|
)
|
||||||
|
case None => Nil
|
||||||
|
) ::: Nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
package cc.sukazyo.cono.morny.data.social
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.data.social.SocialContent.{SocialMedia, SocialMediaType, SocialMediaWithUrl}
|
||||||
|
import cc.sukazyo.cono.morny.data.social.SocialContent.SocialMediaType.{Photo, Video}
|
||||||
|
import cc.sukazyo.cono.morny.MornyCoeur
|
||||||
|
import cc.sukazyo.cono.morny.bot.query.InlineQueryUnit
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId
|
||||||
|
import com.pengrad.telegrambot.model.request.*
|
||||||
|
import com.pengrad.telegrambot.request.{SendMediaGroup, SendMessage}
|
||||||
|
|
||||||
|
/** Model of social networks' status. for example twitter tweet or
|
||||||
|
* weibo status.
|
||||||
|
*
|
||||||
|
* Can be output to Telegram.
|
||||||
|
*
|
||||||
|
* @param text_html Formatted HTML output of the status that can be output
|
||||||
|
* directly to Telegram. Normally will contains metadata
|
||||||
|
* like status' author or like count etc.
|
||||||
|
* @param text_withPicPlaceholder same with [[text_html]], but contains more
|
||||||
|
* placeholder texts of medias. can be used
|
||||||
|
* when medias cannot be output.
|
||||||
|
* @param medias Status attachment medias.
|
||||||
|
* @param medias_mosaic Mosaic version of status medias. Will be used when
|
||||||
|
* the output API doesn't support multiple medias like
|
||||||
|
* Telegram inline API. This value is depends on the specific
|
||||||
|
* backend parser/formatter implementation.
|
||||||
|
* @param thumbnail Medias' thumbnail. Will be used when the output API required
|
||||||
|
* a thumbnail. This value is depends on the specific backend
|
||||||
|
* parser/formatter implementation.
|
||||||
|
*/
|
||||||
|
case class SocialContent (
|
||||||
|
text_html: String,
|
||||||
|
text_withPicPlaceholder: String,
|
||||||
|
medias: List[SocialMedia],
|
||||||
|
medias_mosaic: Option[SocialMedia] = None,
|
||||||
|
thumbnail: Option[SocialMedia] = None
|
||||||
|
) {
|
||||||
|
|
||||||
|
def thumbnailOrElse[T] (orElse: T): String | T =
|
||||||
|
thumbnail match
|
||||||
|
case Some(x) if x.isInstanceOf[SocialMediaWithUrl] && x.t == Photo =>
|
||||||
|
x.asInstanceOf[SocialMediaWithUrl].url
|
||||||
|
case _ => orElse
|
||||||
|
|
||||||
|
def outputToTelegram (using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur): Unit = {
|
||||||
|
if medias isEmpty then
|
||||||
|
coeur.account exec
|
||||||
|
SendMessage(replyChat, text_html)
|
||||||
|
.parseMode(ParseMode.HTML)
|
||||||
|
.replyToMessageId(replyToMessage)
|
||||||
|
else
|
||||||
|
val mediaGroup = medias.map(f => f.genTelegramInputMedia)
|
||||||
|
mediaGroup.head.caption(text_html)
|
||||||
|
mediaGroup.head.parseMode(ParseMode.HTML)
|
||||||
|
coeur.account exec
|
||||||
|
SendMediaGroup(replyChat, mediaGroup: _*)
|
||||||
|
.replyToMessageId(replyToMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
InlineQueryUnit(InlineQueryResultPhoto(
|
||||||
|
inlineQueryId(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
|
||||||
|
else if (medias nonEmpty) && (medias.head.t == Photo) then
|
||||||
|
val media = medias.head
|
||||||
|
media match
|
||||||
|
case media_url: SocialMediaWithUrl =>
|
||||||
|
InlineQueryUnit(InlineQueryResultPhoto(
|
||||||
|
inlineQueryId(s"[$id_head/photo/0]$id_param"),
|
||||||
|
media_url.url,
|
||||||
|
thumbnailOrElse(media_url.url)
|
||||||
|
).title(s"$name").caption(text_html).parseMode(ParseMode.HTML)) :: Nil
|
||||||
|
case _ =>
|
||||||
|
InlineQueryUnit(InlineQueryResultArticle(
|
||||||
|
inlineQueryId(s"[$id_head/text_only]$id_param"), s"$name (text only)",
|
||||||
|
InputTextMessageContent(text_withPicPlaceholder).parseMode(ParseMode.HTML)
|
||||||
|
)) :: Nil
|
||||||
|
else
|
||||||
|
InlineQueryUnit(InlineQueryResultArticle(
|
||||||
|
inlineQueryId(s"[$id_head/text]$id_param"), s"$name",
|
||||||
|
InputTextMessageContent(text_html).parseMode(ParseMode.HTML)
|
||||||
|
)) :: Nil
|
||||||
|
) ::: Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object SocialContent {
|
||||||
|
|
||||||
|
enum SocialMediaType:
|
||||||
|
case Photo
|
||||||
|
case Video
|
||||||
|
sealed trait SocialMedia(val t: SocialMediaType) {
|
||||||
|
def genTelegramInputMedia: InputMedia[?]
|
||||||
|
}
|
||||||
|
case class SocialMediaWithUrl (url: String)(t: SocialMediaType) extends SocialMedia(t) {
|
||||||
|
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) {
|
||||||
|
override def genTelegramInputMedia: InputMedia[_] =
|
||||||
|
t match
|
||||||
|
case Photo => InputMediaPhoto(data)
|
||||||
|
case Video => InputMediaVideo(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package cc.sukazyo.cono.morny.data.social
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.data.social.SocialContent.{SocialMedia, SocialMediaWithUrl}
|
||||||
|
import cc.sukazyo.cono.morny.data.social.SocialContent.SocialMediaType.{Photo, Video}
|
||||||
|
import cc.sukazyo.cono.morny.extra.twitter.{FXApi, FXTweet}
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
|
||||||
|
|
||||||
|
object SocialTwitterParser {
|
||||||
|
|
||||||
|
def parseFXTweet_forMediaPlaceholderInContent (tweet: FXTweet): String =
|
||||||
|
tweet.media match
|
||||||
|
case None => ""
|
||||||
|
case Some(media) =>
|
||||||
|
"\n" + (media.photos.getOrElse(Nil).map(* => "🖼️") ::: media.videos.getOrElse(Nil).map(* => "🎞️"))
|
||||||
|
.mkString(" ")
|
||||||
|
|
||||||
|
def parseFXTweet (api: FXApi): SocialContent = {
|
||||||
|
api.tweet match
|
||||||
|
case None =>
|
||||||
|
val content =
|
||||||
|
// language=html
|
||||||
|
s"""❌ Fix-Tweet <code>${api.code}</code>
|
||||||
|
|<i>${h(api.message)}</i>""".stripMargin
|
||||||
|
SocialContent(content, content, Nil)
|
||||||
|
case Some(tweet) =>
|
||||||
|
val content: String =
|
||||||
|
// language=html
|
||||||
|
s"""⚪️ <b>${h(tweet.author.name)} <a href="${tweet.author.url}">@${h(tweet.author.screen_name)}</a></b>
|
||||||
|
|
|
||||||
|
|${h(tweet.text)}
|
||||||
|
|
|
||||||
|
|<i>💬${tweet.replies} 🔗${tweet.retweets} ❤️${tweet.likes}</i>
|
||||||
|
|<i><a href="${tweet.url}">${h(tweet.created_at)}</a></i>""".stripMargin
|
||||||
|
val content_withMediasPlaceholder: String =
|
||||||
|
// language=html
|
||||||
|
s"""⚪️ <b>${h(tweet.author.name)} <a href="${tweet.author.url}">@${h(tweet.author.screen_name)}</a></b>
|
||||||
|
|
|
||||||
|
|${h(tweet.text)}${parseFXTweet_forMediaPlaceholderInContent(tweet)}
|
||||||
|
|
|
||||||
|
|<i>💬${tweet.replies} 🔗${tweet.retweets} ❤️${tweet.likes}</i>
|
||||||
|
|<i><a href="${tweet.url}">${h(tweet.created_at)}</a></i>""".stripMargin
|
||||||
|
tweet.media match
|
||||||
|
case None =>
|
||||||
|
SocialContent(content, content_withMediasPlaceholder, Nil)
|
||||||
|
case Some(media) =>
|
||||||
|
val mediaGroup: List[SocialMedia] =
|
||||||
|
(
|
||||||
|
media.photos match
|
||||||
|
case None => List.empty
|
||||||
|
case Some(photos) => for i <- photos yield SocialMediaWithUrl(i.url)(Photo)
|
||||||
|
) ::: (
|
||||||
|
media.videos match
|
||||||
|
case None => List.empty
|
||||||
|
case Some(videos) => for i <- videos yield SocialMediaWithUrl(i.url)(Video)
|
||||||
|
)
|
||||||
|
val thumbnail =
|
||||||
|
if media.videos.nonEmpty then
|
||||||
|
Some(SocialMediaWithUrl(media.videos.get.head.thumbnail_url)(Photo))
|
||||||
|
else None
|
||||||
|
val mediaMosaic = media.mosaic match
|
||||||
|
case Some(mosaic) => Some(SocialMediaWithUrl(mosaic.formats.jpeg)(Photo))
|
||||||
|
case None => None
|
||||||
|
SocialContent(content, content_withMediasPlaceholder, mediaGroup, mediaMosaic, thumbnail)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package cc.sukazyo.cono.morny.data.social
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.data.social.SocialContent.SocialMediaType.Photo
|
||||||
|
import cc.sukazyo.cono.morny.data.social.SocialContent.SocialMediaWithBytesData
|
||||||
|
import cc.sukazyo.cono.morny.extra.weibo.{genWeiboStatusUrl, MApi, MStatus, StatusUrlInfo}
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.{cleanupHtml as ch, escapeHtml as h}
|
||||||
|
import io.circe.{DecodingFailure, ParsingFailure}
|
||||||
|
import sttp.client3.{HttpError, SttpClientException}
|
||||||
|
|
||||||
|
object SocialWeiboParser {
|
||||||
|
|
||||||
|
def parseMStatus_forPicPreview (status: MStatus): String =
|
||||||
|
if status.pic_ids.isEmpty then "" else
|
||||||
|
"\n" + (for (pic <- status.pic_ids) yield "🖼️").mkString(" ")
|
||||||
|
|
||||||
|
def parseMStatus_forRetweeted (originalStatus: MStatus): String =
|
||||||
|
originalStatus.retweeted_status match
|
||||||
|
case Some(status) =>
|
||||||
|
// language=html
|
||||||
|
s"""
|
||||||
|
|<i>//<a href="https://weibo.com/${status.user.id}/${status.id}">${h(status.user.screen_name)}</a>:</i>
|
||||||
|
|${ch(status.text)}${parseMStatus_forPicPreview(status)}
|
||||||
|
|""".stripMargin
|
||||||
|
case None => ""
|
||||||
|
|
||||||
|
@throws[HttpError[?] | SttpClientException | ParsingFailure | DecodingFailure]
|
||||||
|
def parseMStatus (api: MApi[MStatus]): SocialContent = {
|
||||||
|
val content =
|
||||||
|
// language=html
|
||||||
|
s"""🔸<b><a href="${api.data.user.profile_url}">${h(api.data.user.screen_name)}</a></b>
|
||||||
|
|
|
||||||
|
|${ch(api.data.text)}
|
||||||
|
|${parseMStatus_forRetweeted(api.data)}
|
||||||
|
|<i><a href="${genWeiboStatusUrl(StatusUrlInfo(api.data.user.id.toString, api.data.id))}">${h(api.data.created_at)}</a></i>""".stripMargin
|
||||||
|
val content_withPicPlaceholder =
|
||||||
|
// language=html
|
||||||
|
s"""🔸<b><a href="${api.data.user.profile_url}">${h(api.data.user.screen_name)}</a></b>
|
||||||
|
|
|
||||||
|
|${ch(api.data.text)}${parseMStatus_forPicPreview(api.data)}
|
||||||
|
|${parseMStatus_forRetweeted(api.data)}
|
||||||
|
|<i><a href="${genWeiboStatusUrl(StatusUrlInfo(api.data.user.id.toString, api.data.id))}">${h(api.data.created_at)}</a></i>""".stripMargin
|
||||||
|
api.data.pics match
|
||||||
|
case None =>
|
||||||
|
SocialContent(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user