Skip to content

Commit

Permalink
🏷️ added /echo, /ping, and /youtube (#47)
Browse files Browse the repository at this point in the history
* finished misc cog
* added /echo, /ping, and /youtube
* fixed bugs
  • Loading branch information
v0idzdev authored Apr 29, 2022
2 parents 5b0fbcc + c4c33e2 commit 3e92b3d
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 12 deletions.
10 changes: 5 additions & 5 deletions ext/commands/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async def joined(self, interaction: discord.Interaction, *, member: Optional[dis
.add_field(name="📅 Date", value=f"{date}.") \
.set_author(icon_url=member.avatar.url or None, name=member.name)

await interaction.response.send_message(embed=joined_embed, ephemeral=True)
await interaction.response.send_message(embed=joined_embed)

@app_commands.command()
@app_commands.describe(member="❓ The member to view the top role of.")
Expand All @@ -61,7 +61,7 @@ async def toprole(self, interaction: discord.Interaction, *, member: Optional[di
.add_field(name="🏷️ Role", value=f"*@{member.top_role.name}*") \
.set_author(icon_url=member.avatar.url or None, name=member.name)

await interaction.response.send_message(embed=top_role_embed, ephemeral=True)
await interaction.response.send_message(embed=top_role_embed)

@app_commands.command()
@app_commands.describe(member="❓ The member to view the permissions of.")
Expand All @@ -84,7 +84,7 @@ async def perms(self, interaction: discord.Interaction, *, member: Optional[disc
value="\u200b".join(f"`{perm}` " for perm, value in member.guild_permissions if value) or "..."
)

await interaction.response.send_message(embed=perms_embed, ephemeral=True)
await interaction.response.send_message(embed=perms_embed)

@app_commands.command()
@app_commands.describe(member="❓ The member to view the avatar of.")
Expand All @@ -103,7 +103,7 @@ async def avatar(self, interaction: discord.Interaction, *, member: Optional[dis
) \
.set_image(url=member.avatar.url or None)

await interaction.response.send_message(embed=avatar_embed, ephemeral=True)
await interaction.response.send_message(embed=avatar_embed)

@app_commands.command()
async def servericon(self, interaction: discord.Interaction) -> None:
Expand All @@ -118,7 +118,7 @@ async def servericon(self, interaction: discord.Interaction) -> None:
) \
.set_image(url=interaction.guild.icon.url)

await interaction.response.send_message(embed=server_icon_embed, ephemeral=True)
await interaction.response.send_message(embed=server_icon_embed)


async def setup(client: core.DiscordClient) -> None:
Expand Down
112 changes: 107 additions & 5 deletions ext/commands/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
information commands for BB.Bot.
"""
import datetime
import random
import discord
import humanize
import twitchio
Expand All @@ -12,6 +13,8 @@
from discord import app_commands
from discord.ext import commands

from ext.views.youtube_view import YoutubeView


class Misc(commands.Cog, name="Miscellaneous"):
"""
Expand All @@ -31,7 +34,7 @@ async def twitch(self, interaction: discord.Interaction, *, broadcaster: str) ->

if not streams:
error_embed = utils.create_error_embed(f"The streamer **`{broadcaster}`** is not currently live.")
return await interaction.response.send_message(embed=error_embed)
return await interaction.response.send_message(embed=error_embed, ephemeral=True)

stream: twitchio.Stream = streams[0]
current_time = datetime.datetime.utcnow()
Expand All @@ -55,6 +58,30 @@ async def twitch(self, interaction: discord.Interaction, *, broadcaster: str) ->
.add_field(name="❓ Category", value=stream.game_name, inline=False)

await interaction.response.send_message(embed=stream_embed)

@app_commands.command()
@app_commands.describe(choices="❓ The choices to choose from, separated by commas.")
async def choose(self, interaction: discord.Interaction, *, choices: str):
"""
🎲 Chooses a random option from a list of choices.
"""
choices = [x.strip() for x in choices.split(",")]

if len(choices) <= 1:
error_embed = utils.create_error_embed("You need to give me at least 2 choices.")
return await interaction.response.send_message(embed=error_embed, ephemeral=True)

numbered_choices = [f"**`{i + 1}`** — {x}" for i, x in enumerate(choices)]

choice_embed = discord.Embed(
title="🎲 My Choice",
description=f"**`{random.choice(choices)}`**",
timestamp=datetime.datetime.utcnow(),
color=self.client.theme,
) \
.add_field(name="❗ Options Given", value="\n".join(numbered_choices))

await interaction.response.send_message(embed=choice_embed)

@app_commands.command()
async def meme(self, interaction: discord.Interaction):
Expand All @@ -63,14 +90,15 @@ async def meme(self, interaction: discord.Interaction):
"""
response = await self.client.session.get("https://meme-api.herokuapp.com/gimme")
data = await response.json()
url = data['url']

meme_embed = discord.Embed(
title="🎲 Found a Meme",
description=f"**`{data['title']}`**",
description=f"**[{data['title']}]({url})**",
timestamp=datetime.datetime.utcnow(),
color=self.client.theme,
color=self.client.theme,
) \
.set_image(url=f"{data['url']}") \
.set_image(url=f"{url}") \
.set_footer(text="❓ Try again? Use /meme.")

await interaction.response.send_message(embed=meme_embed)
Expand All @@ -81,19 +109,93 @@ async def poll(self, interaction: discord.Interaction, *, question: str):
"""
🎲 Creates a simple yes or no poll for users to vote on.
"""
if message is None:
error_embed = utils.create_error_embed("You need to ask a question.")
return await interaction.response.send_message(embed=error_embed)

poll_embed = discord.Embed(
title="🎲 Poll",
description=f"**`{question}`**",
timestamp=datetime.datetime.utcnow(),
color=self.client.theme,
) \
.set_author(name=interaction.user.name, url=interaction.user.avatar.url) \
.set_author(name=interaction.user.name, icon_url=interaction.user.avatar.url) \
.set_footer(text="Vote ✔️ Yes or ❌ No.")

message = await interaction.channel.send(embed=poll_embed)
await message.add_reaction("✔️")
await message.add_reaction("❌")

@app_commands.command()
@app_commands.describe(message="❓ The phrase you want the bot to repeat.")
async def echo(self, interaction: discord.Interaction, *, message: str):
"""
🎲 Repeats what you say.
"""
if message is None:
error_embed = utils.create_error_embed("You need to tell me what to say.")
return await interaction.response.send_message(embed=error_embed)

echo_embed = discord.Embed(
title=f"🎲 Message",
description=f"**`{message}`**",
timestamp=datetime.datetime.utcnow(),
color=self.client.theme,
) \
.set_author(name=interaction.user.name, icon_url=interaction.user.avatar.url)

await interaction.response.send_message(embed=echo_embed)

@app_commands.command()
async def ping(self, interaction: discord.Interaction):
"""
🎲 Shows the bot's current websocket latency.
"""
await interaction.response.defer()

embed = discord.Embed(
title="🏓 Pong!",
description=f"⌛ Your ping is **{round(self.client.latency * 1000)}**ms.",
timestamp=datetime.datetime.utcnow(),
color=self.client.theme,
)

await interaction.followup.send(embed=embed)

@app_commands.command()
@app_commands.describe(search="❓ The YouTube video to search for.")
async def youtube(self, interaction: discord.Interaction, *, search: str):
"""
🎲 Searches for a video on youtube and sends the link.
"""
if search is None:
error_embed = utils.create_error_embed("You need to specify a search term.")
return await interaction.response.send_message(embed=error_embed)

url = await utils.youtube_search_to_url(search)
title = await utils.youtube_url_to_title(url)
thumbnail = await utils.youtube_url_to_thumbnail(url)

embed = discord.Embed(
title="🎲 Found a Video",
description=f"🔗 [{title}]({url})",
timestamp=datetime.datetime.utcnow(),
color=self.client.theme,
) \
.set_thumbnail(url=thumbnail) \
.set_footer(text=f"❓ Follow the link above to view the video.")

await interaction.response.send_message(embed=embed)

embed = discord.Embed(
title="🎲 View in Discord?",
description="❓ Click View In Discord to view the video in this text channel.",
timestamp=datetime.datetime.utcnow(),
color=self.client.theme,
)

view = YoutubeView(url, interaction)
view.message = await interaction.followup.send(embed=embed, view=view)


async def setup(client: core.DiscordClient) -> None:
Expand Down
63 changes: 61 additions & 2 deletions ext/utils/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@
Module `functions` contains standalone utility functions to be
used in BB.Bot extensions.
"""
import functools
from typing import Any, Callable, List
import discord
import datetime
import requests
import re

from bs4 import BeautifulSoup
from urllib import (
parse,
request
)


def create_error_embed(message: str) -> discord.Embed:
Expand All @@ -21,5 +31,54 @@ def create_error_embed(message: str) -> discord.Embed:
return discord.Embed(
title=f"❌ An Error Occurred",
description=f"🏷️ {message}",
timestamp=datetime.datetime.utcnow()
)
timestamp=datetime.datetime.utcnow(),
color=discord.Color.red(),
)


async def youtube_search_to_url(search_query: str) -> str:
"""
Returns the first result in a Youtube search for a given
query.
Params:
- search_query (str): The search terms to use.
Returns:
- The URL of the first result found.
"""
query = parse.urlencode({"search_query": search_query})
content = request.urlopen("http://www.youtube.com/results?" + query)
results = re.findall(r"watch\?v=(\S{11})", content.read().decode())
return "https://www.youtube.com/watch?v=" + results[0]


async def youtube_url_to_title(url: str) -> str:
"""
Returns the title of a Youtube video, given a video's URL.
Params:
- url (str): The URL of the Youtube video.
Returns:
- The title of the Youtube video.
"""
reqs = requests.get(url)
soup = BeautifulSoup(reqs.text, "html.parser")

return "".join([t for t in soup.find("title")])


async def youtube_url_to_thumbnail(url: str) -> str:
"""
Returns the thumbnail of a Youtube video, given a video's URL.
Params:
- url (str): The URL of the Youtube video.
Returns:
- A URL for the thumbnail of the Youtube video.
"""
exp = r"^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*"
s = re.findall(exp, url)[0][-1]
return f"https://i.ytimg.com/vi/{s}/maxresdefault.jpg"
4 changes: 4 additions & 0 deletions ext/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""
Module `views` contains user interface components used
in BB.Bot's extensions (commands or otherwise).
"""
65 changes: 65 additions & 0 deletions ext/views/base_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
Module `base_view` contains the `BaseView` class, which
provides common functionality.
"""
import discord

from ext import utils
from typing import Any

from discord import (
ui,
app_commands
)


class BaseView(ui.View):
"""
Class `BaseView` is a subclass of `discord.ui.View` that
implements `interaction_check` and `disable_all_buttons`.
"""
def __init__(self, interaction: discord.Interaction, **kwargs: Any) -> None:
"""
Creates an instance of `discord.ui.View` that implements
an `interaction_check` and a `disable_all_buttons` method.
Params:
- interaction (discord.Interaction): The interaction the command was invoked with.
- message (discord.Message): Pass the message the view is sent in.
- **kwargs (Any): Any keyword arguments that `discord.ui.View` accepts.
"""
super().__init__()
self.command_author_id = interaction.user.id
self.message: discord.Message = None

async def interaction_check(self, interaction: discord.Interaction) -> bool:
"""
Prevents users who weren't the command sender from interacting
with the view component.
Params:
- interaction (discord.Interaction): The interaction used with the view.
Returns:
- A `bool`. If false, the view will not execute any callbacks.
"""
if interaction.user.id == self.command_author_id:
return True

error_embed = utils.create_error_embed("This isn't your interaction.")
await interaction.response.send_message(embed=error_embed, ephemeral=True)
return False

async def disable_all_buttons(self) -> None:
"""
Disables all buttons that are attached to the view component. This
is usually done when a view should be closed, to prevent errors.
Params:
- interaction (discord.Interaction): The interaction used with the view.
"""
for child in self.children:
child.disabled = True

await self.message.edit(view=self)
self.stop()
Loading

0 comments on commit 3e92b3d

Please sign in to comment.