basic bot
This commit is contained in:
parent
777eca8572
commit
643b2de45b
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
# Stores the Bot token
|
||||
TOKEN
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
217
src/bot.py
Normal file
217
src/bot.py
Normal 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
16
src/main.py
Normal 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())
|
Loading…
x
Reference in New Issue
Block a user