All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dymelt.rss2discord_3.0.0.5-5-0a6000.source-code.Main.scala Maven / Gradle / Ivy

package io.github.windymelt.rss2discord

import cats.effect.IO
import cats.effect.IOApp
import com.amazonaws.services.lambda.runtime.Context
import com.github.nscala_time.time.Imports._
import com.rometools.rome.feed._
import com.rometools.rome.feed.synd.SyndEntry
import com.rometools.rome.io.SyndFeedInput
import com.rometools.rome.io.XmlReader
import io.github.windymelt.rss2discord.endpoint.Discord
import org.http4s.blaze.client.BlazeClientBuilder
import sttp.tapir.client.http4s.Http4sClientInterpreter

import java.io.InputStream
import java.io.OutputStream
import java.net.URL
import scala.concurrent.duration.FiniteDuration

val feedUrlEnv: Option[String] = sys.env.get("RSS_URL")
val webhookUrl: String = sys.env("WEBHOOK_URL")
val tzOffset: Int = sys.env
  .get("TZ_OFFSET")
  .map(_.toInt)
  .getOrElse(
    FiniteDuration(
      DateTime.now().zone.getOffset(DateTime.now()),
      "milliseconds"
    ).toHours.toInt
  )

extension (e: SyndEntry)
  def publishedDateTime: Option[DateTime] = Option(e.getPublishedDate).map {
    _.toLocalDateTime
      .toDateTime(DateTimeZone.forOffsetHours(tzOffset))
  }
  def updatedDateTime: Option[DateTime] = Option(e.getUpdatedDate).map {
    _.toLocalDateTime
      .toDateTime(DateTimeZone.forOffsetHours(tzOffset))
  }

object Rss2Discord extends IOApp.Simple {
  val feedUrl = feedUrlEnv.get
  def handler(in: InputStream, out: OutputStream, ctx: Context): Unit = main(
    Array()
  )
  private def entriesToPost(feedUrl: String, timeAfter: DateTime) = IO.delay {
    import scala.collection.JavaConverters._
    val feed = new SyndFeedInput().build(new XmlReader(new URL(feedUrl)))
    feed
      .getEntries()
      .asScala
      .filter {
        case e if (e.publishedDateTime orElse e.updatedDateTime).isDefined =>
          val dt = (e.publishedDateTime orElse e.updatedDateTime).get
          dt.isAfter(timeAfter)
        case _ => false
      }
  }
  def run: IO[Unit] = {
    import cats.implicits._
    for {
      dt <- IO(DateTime.now())
      _ <- IO.println(s"finding entries ${dt.minusMinutes(31)} .. $dt")
      entries <- entriesToPost(feedUrl, timeAfter = dt.minusMinutes(31))
      _ <- IO.println(s"entriesToPost: ${entries.map(_.getTitle())}")
      _ <- entries.toSeq.traverse(e => post(formatEntry(e)))
    } yield ()
  }

  def formatEntry(entry: SyndEntry): String = {
    s"""[${entry.getTitle()}](${entry.getLink()})
Author: ${entry.getAuthor()}
"""
  }

  def post(content: String): IO[Unit] = {
    val (req, res) =
      Http4sClientInterpreter[IO]()
        .toRequest(
          endpoint.Discord.webhook,
          Some(org.http4s.Uri.unsafeFromString(webhookUrl))
        )
        .apply(endpoint.WebhookInput(content))

    val clientResource = BlazeClientBuilder[IO].resource
    val parsedResult =
      clientResource.flatMap(_.run(req)).use(res)

    parsedResult.debug("DEBUG").flatTap(_ => IO.println("posted")).void
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy