Add onion feed to cogs
All checks were successful
Build and Publish / build_and_publish (push) Successful in 1m3s
All checks were successful
Build and Publish / build_and_publish (push) Successful in 1m3s
This commit is contained in:
parent
00b80a929c
commit
d856c10260
1 changed files with 100 additions and 0 deletions
100
src/cogs/onion_feed.py
Normal file
100
src/cogs/onion_feed.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
"""
|
||||||
|
Scrapes the onion RSS feed once every hour and posts any new articles to the desired channel
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import dataclasses
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands, tasks
|
||||||
|
import httpx
|
||||||
|
from conf import CONFIG
|
||||||
|
import redis
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class RSSItem:
|
||||||
|
title: str
|
||||||
|
link: str
|
||||||
|
description: str
|
||||||
|
pubDate: datetime.datetime
|
||||||
|
guid: str
|
||||||
|
thumbnail: str
|
||||||
|
|
||||||
|
|
||||||
|
class OnionFeed(commands.Cog):
|
||||||
|
SOURCE = "https://www.theonion.com/rss"
|
||||||
|
EPOCH = datetime.datetime(2024, 7, 7, tzinfo=datetime.timezone.utc)
|
||||||
|
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot: commands.Bot = bot
|
||||||
|
self.log = logging.getLogger("jimmy.cogs.onion_feed")
|
||||||
|
self.check_onion_feed.start()
|
||||||
|
self.redis = redis.Redis(**CONFIG["redis"])
|
||||||
|
|
||||||
|
def cog_unload(self) -> None:
|
||||||
|
self.check_onion_feed.cancel()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_item(item: BeautifulSoup) -> RSSItem:
|
||||||
|
kwargs = {
|
||||||
|
"title": item.title.get_text(strip=True).strip(),
|
||||||
|
"link": item.link.get_text(strip=True).strip(),
|
||||||
|
"pubDate": datetime.datetime.strptime(
|
||||||
|
item.pubDate.get_text(strip=True).strip(), "%a, %d %b %Y %H:%M:%S %Z"
|
||||||
|
),
|
||||||
|
"guid": item.guid.get_text(strip=True).strip(),
|
||||||
|
"description": BeautifulSoup(item.description.get_text()).p.get_text(strip=True).strip()[:-1],
|
||||||
|
"thumbnail": item.find("media:thumbnail")["url"],
|
||||||
|
}
|
||||||
|
return RSSItem(**kwargs)
|
||||||
|
|
||||||
|
def parse_feed(self, text: str) -> list[RSSItem]:
|
||||||
|
soup = BeautifulSoup(text, "xml")
|
||||||
|
return [self.parse_item(item) for item in soup.find_all("item")]
|
||||||
|
|
||||||
|
@tasks.loop(hours=1)
|
||||||
|
async def check_onion_feed(self):
|
||||||
|
if not self.bot.is_ready():
|
||||||
|
await self.bot.wait_until_ready()
|
||||||
|
|
||||||
|
guild = self.bot.get_guild(994710566612500550)
|
||||||
|
if not guild:
|
||||||
|
return self.log.error("Nonsense guild not found. Can't do onion feed.")
|
||||||
|
channel = discord.utils.get(guild.text_channels, name="spam")
|
||||||
|
if not channel:
|
||||||
|
return self.log.error("Spam channel not found.")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.get(self.SOURCE)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return self.log.error(f"Failed to fetch onion feed: {response.status_code}")
|
||||||
|
items: list[RSSItem] = await asyncio.to_thread(self.parse_feed, response.text)
|
||||||
|
for item in items:
|
||||||
|
if self.redis.get("onion-" + item.guid):
|
||||||
|
continue
|
||||||
|
embed = discord.Embed(
|
||||||
|
title=item.title,
|
||||||
|
url=item.link,
|
||||||
|
description=item.description + f"... [Read More]({item.link})",
|
||||||
|
color=0x00DF78,
|
||||||
|
timestamp=item.pubDate,
|
||||||
|
)
|
||||||
|
embed.set_thumbnail(url=item.thumbnail)
|
||||||
|
try:
|
||||||
|
msg = await channel.send(embed=embed)
|
||||||
|
# noinspection PyAsyncCall
|
||||||
|
self.redis.set("onion-" + item.guid, str(msg.id))
|
||||||
|
except discord.HTTPException:
|
||||||
|
self.log.exception(f"Failed to send onion feed message: {item.title}")
|
||||||
|
else:
|
||||||
|
self.log.debug(f"Sent onion feed message: {item.title}")
|
||||||
|
|
||||||
|
@check_onion_feed.before_loop
|
||||||
|
async def before_check_onion_feed(self):
|
||||||
|
await self.bot.wait_until_ready()
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(OnionFeed(bot))
|
Loading…
Reference in a new issue