basic bot

This commit is contained in:
Corban-Lee 2023-06-12 21:13:30 +01:00
parent 777eca8572
commit 643b2de45b
3 changed files with 236 additions and 0 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# Stores the Bot token
TOKEN
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

217
src/bot.py Normal file
View File

@ -0,0 +1,217 @@
"""The discord bot for the application"""
import time
from datetime import datetime
import aiohttp
import discord
from bs4 import BeautifulSoup as bs4
from discord import Intents, Interaction, app_commands
from discord.ext import commands
from bbc_feeds import news
class DiscordBot(commands.Bot):
def __init__(self):
super().__init__(command_prefix="-", intents=Intents.all())
async def sync_app_commands(self):
"""Sync application commands"""
await self.wait_until_ready()
await self.tree.sync()
print("app commands synced")
async def on_ready(self):
"""When the bot is ready"""
await self.add_cog(CommandsCog(self))
await self.add_cog(ErrorCog(self))
await self.sync_app_commands()
class CommandsCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
super().__init__()
async def story_to_embed(self, story) -> discord.Embed:
"""
Returns a discord.Embed object representing the given story.
Parameters
----------
story : _type_
_description_
Returns
-------
discord.Embed
_description_
"""
async with aiohttp.ClientSession() as session:
async with session.get(story.link) as response:
html = await response.text()
soup = bs4(html, "html.parser")
image_src = soup.select_one("picture > img").get("src")
embed = discord.Embed(
colour=discord.Colour.from_rgb(255, 0, 0),
title=story.title,
description=story.summary,
url=story.link,
timestamp=datetime.fromtimestamp(time.mktime(story.published_parsed)),
)
embed.set_image(url=image_src)
return embed
group = app_commands.Group(
name="news",
description="BBC News"
)
@group.command(name="latest")
async def get_latest_news(self, inter: Interaction, limit: int = 1):
"""
Provides the latest developments from BBC News.
"""
await inter.response.defer()
stories = news().all(limit=limit)
for story in stories:
embed = await self.story_to_embed(story)
await inter.followup.send(embed=embed)
class ErrorCog(commands.Cog):
"""Error handling cog."""
__slots__ = ()
default_err_msg = "I'm sorry, but I've encountered an " \
"error while processing your command."
def __init__(self, bot):
super().__init__()
self.bot = bot
# Register the error handler
bot.tree.error(coro = self._dispatch_to_app_command_handler)
def trace_error(self, error: Exception):
print(f"{type(error).__name__} {error}")
raise error
async def _dispatch_to_app_command_handler(
self,
inter: Interaction,
error: app_commands.AppCommandError
):
"""Dispatches the error to the app command handler"""
self.bot.dispatch("app_command_error", inter, error)
async def _respond_to_interaction(self, inter: Interaction) -> bool:
"""Respond to an interaction with an error message"""
try:
await inter.response.send_message(
self.default_err_msg,
ephemeral=True
)
except discord.InteractionResponded:
return
@commands.Cog.listener("on_app_command_error")
async def get_app_command_error(
self,
inter: Interaction,
error: app_commands.AppCommandError
):
"""Handles the application command error.
Responds with the appropriate error message.
"""
try:
# Send the default error message and create an edit
# shorthand to add more details to the message once
# we've figured out what the error is.
print(error.with_traceback(None))
await self._respond_to_interaction(inter)
edit = lambda x: inter.edit_original_response(content=x)
raise error
except app_commands.CommandInvokeError as _err:
# The interaction has already been responded to.
if isinstance(
_err.original,
discord.InteractionResponded
):
await edit(_err.original)
return
# Some other error occurred while invoking the command.
await edit(
f"`{type(_err.original).__name__}` " \
f": {_err.original}"
)
except app_commands.CheckFailure as _err:
# The command is still on cooldown.
if isinstance(
_err,
app_commands.CommandOnCooldown
):
await edit(
f"Woah, slow down! This command is on cooldown, " \
f"wait `{str(_err).split(' ')[7]}` !"
)
return
if isinstance(
_err,
app_commands.MissingPermissions
):
await edit(
"You don't have the required permissions to " \
"run this command!"
)
return
if isinstance(
_err,
app_commands.BotMissingPermissions
):
await edit(
"I don't have the required permissions to " \
"run this command! Please ask an admin to " \
"grant me the required permissions."
)
return
# A different check has failed.
await edit(f"`{type(_err).__name__}` : {_err}")
except app_commands.CommandNotFound:
# The command could not be found.
await edit(
f"I couldn't find the command you were looking for... "
"\nThis is probably a discord bug related to " \
"desynchronization between my commands and discord's " \
"servers. Please try again later."
)
except Exception as _err:
# Caught here:
# app_commands.TransformerError
# app_commands.CommandLimitReached
# app_commands.CommandAlreadyRegistered
# app_commands.CommandSignatureMismatch
self.trace_error(_err)

16
src/main.py Normal file
View File

@ -0,0 +1,16 @@
"""Entry point for the application."""
import asyncio
from bot import DiscordBot
async def main():
with open("TOKEN", "r") as token_file:
token = token_file.read()
await DiscordBot().start(token)
if __name__ == "__main__":
asyncio.run(main())