diff --git a/requirements.in b/requirements.in index da9cb1f..28b2b20c 100644 --- a/requirements.in +++ b/requirements.in @@ -2,3 +2,4 @@ python-dotenv discord.py feedparser beautifulsoup4 +types-beautifulsoup4 diff --git a/requirements.txt b/requirements.txt index 0d72f01..f3d3012 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,5 +36,9 @@ sgmllib3k==1.0.0 # via feedparser soupsieve==2.5 # via beautifulsoup4 +types-beautifulsoup4==4.12.0.20240511 + # via -r requirements.in +types-html5lib==1.1.11.20240806 + # via types-beautifulsoup4 yarl==1.9.4 # via aiohttp diff --git a/setup.cfg b/setup.cfg index d3f12e2..a6569d5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -52,3 +52,6 @@ strict_equality = True scripts_are_modules = True warn_unused_configs = True + +[mypy-feedparser.*] +ignore_missing_imports = True diff --git a/src/bot.py b/src/bot.py index 2f8e77d..3f2f860 100644 --- a/src/bot.py +++ b/src/bot.py @@ -6,16 +6,19 @@ import discord from discord import app_commands +from discord.ext import tasks +from src.rss import check_posts from src.constants import ( SPECIAL_ROLE, VERIFIED_ROLE, CHANNEL_PREFIX, VOLUNTEER_ROLE, + FEED_CHANNEL_NAME, + FEED_CHECK_INTERVAL, ANNOUNCE_CHANNEL_NAME, WELCOME_CATEGORY_NAME, PASSWORDS_CHANNEL_NAME, - FEED_CHECK_INTERVAL, ) from src.commands.join import join from src.commands.team import ( @@ -27,10 +30,6 @@ create_team_channel, ) -from discord.ext import tasks - -from src.rss import check_posts - class BotClient(discord.Client): logger: logging.Logger @@ -41,6 +40,7 @@ class BotClient(discord.Client): welcome_category: discord.CategoryChannel announce_channel: discord.TextChannel passwords_channel: discord.TextChannel + feed_channel: discord.TextChannel def __init__( self, @@ -54,7 +54,7 @@ def __init__( self.tree = app_commands.CommandTree(self) guild_id = os.getenv('DISCORD_GUILD_ID') if guild_id is None or not guild_id.isnumeric(): - logger.error("Invalid guild ID") + self.logger.error("Invalid guild ID") exit(1) self.guild = discord.Object(id=int(guild_id)) team = Team() @@ -70,8 +70,6 @@ async def setup_hook(self) -> None: # This copies the global commands over to your guild. self.tree.copy_global_to(guild=self.guild) await self.tree.sync(guild=self.guild) - - async def setup_hook(self) -> None: self.check_for_new_blog_posts.start() async def on_ready(self) -> None: @@ -88,6 +86,7 @@ async def on_ready(self) -> None: welcome_category = discord.utils.get(guild.categories, name=WELCOME_CATEGORY_NAME) announce_channel = discord.utils.get(guild.text_channels, name=ANNOUNCE_CHANNEL_NAME) passwords_channel = discord.utils.get(guild.text_channels, name=PASSWORDS_CHANNEL_NAME) + feed_channel = discord.utils.get(guild.text_channels, name=FEED_CHANNEL_NAME) if ( verified_role is None @@ -96,6 +95,7 @@ async def on_ready(self) -> None: or welcome_category is None or announce_channel is None or passwords_channel is None + or feed_channel is None ): logging.error("Roles and channels are not set up") exit(1) @@ -106,6 +106,7 @@ async def on_ready(self) -> None: self.welcome_category = welcome_category self.announce_channel = announce_channel self.passwords_channel = passwords_channel + self.feed_channel = feed_channel async def on_member_join(self, member: discord.Member) -> None: name = member.display_name @@ -143,15 +144,14 @@ async def on_member_remove(self, member: discord.Member) -> None: self.logger.info(f"Deleted channel '{channel.name}', because it has no users.") @tasks.loop(seconds=FEED_CHECK_INTERVAL) - async def check_for_new_blog_posts(self): + async def check_for_new_blog_posts(self) -> None: self.logger.info("Checking for new blog posts") - await check_posts(self.get_guild(int(os.getenv('DISCORD_GUILD_ID')))) + await check_posts(self.feed_channel) @check_for_new_blog_posts.before_loop - async def before_check_for_new_blog_posts(self): + async def before_check_for_new_blog_posts(self) -> None: await self.wait_until_ready() - async def load_passwords(self) -> AsyncGenerator[Tuple[str, str], None]: """ Returns a mapping from role name to the password for that role. diff --git a/src/rss.py b/src/rss.py index 2acb5a3..90e4e33 100644 --- a/src/rss.py +++ b/src/rss.py @@ -6,7 +6,7 @@ from bs4 import BeautifulSoup from feedparser import FeedParserDict -from src.constants import FEED_URL, FEED_CHANNEL_NAME +from src.constants import FEED_URL def get_seen_posts() -> List[str]: @@ -22,9 +22,8 @@ def add_seen_post(post_id: str) -> None: f.write(post_id + '\n') -async def check_posts(guild: discord.Guild) -> None: +async def check_posts(channel: discord.TextChannel) -> None: feed = feedparser.parse(FEED_URL) - channel = discord.utils.get(guild.channels, name=FEED_CHANNEL_NAME) post = feed.entries[0] if post.id + "\n" not in get_seen_posts(): @@ -34,12 +33,16 @@ async def check_posts(guild: discord.Guild) -> None: def create_embed(post: FeedParserDict) -> discord.Embed: soup = BeautifulSoup(post.content[0].value, 'html.parser') + text = "" + + if soup.p: + text = soup.p.text embed = discord.Embed( title=post.title, type="article", url=post.link, - description=soup.p.text, + description=text, ) if len(post.media_thumbnail) > 0: