Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .buttons import BotLinks as BotLinks
from .commands_export import build_commands_payload as build_commands_payload
from .commands_export import export_commands as export_commands
from .emojis import Emoji
from .log import (
log_app_command_error as log_app_command_error,
)
Expand All @@ -26,4 +27,5 @@
"TimeToString",
"build_commands_payload",
"export_commands",
"Emoji",
]
14 changes: 7 additions & 7 deletions api/buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from discord.ext import commands
from discord.ui import Button, View

from .emojis import Emoji


class Links:
"""Simple utility for creating Discord link buttons."""
Expand Down Expand Up @@ -46,9 +48,7 @@ class BotLinks:

def __init__(self, support_url: str | None = None, invite_url: str | None = None) -> None:
self.support_url = support_url or "https://discord.gg/x4kaVDcubT"
self.invite_url = (
invite_url or "https://discord.com/oauth2/authorize?client_id=1455170105666306113"
)
self.invite_url = invite_url or "https://discord.com/oauth2/authorize?client_id=1455170105666306113"

def support(self, label: str = "Support Server", emoji: str = "🎗️") -> View:
"""Get support server link view."""
Expand All @@ -73,8 +73,8 @@ def __init__(
self,
author_id: int,
*,
confirm_label: str = "Confirm",
cancel_label: str = "Cancel",
confirm_label: str = f"{Emoji.DELETE.value} Confirm",
cancel_label: str = f"{Emoji.CROSS.value} Cancel",
confirm_style: discord.ButtonStyle = discord.ButtonStyle.danger,
cancel_style: discord.ButtonStyle = discord.ButtonStyle.secondary,
timeout: float = 30.0,
Expand Down Expand Up @@ -133,8 +133,8 @@ async def confirm_action(
*,
timeout: float = 30.0,
ephemeral: bool = True,
confirm_label: str = "Confirm",
cancel_label: str = "Cancel",
confirm_label: str = f"{Emoji.DELETE.value} Confirm",
cancel_label: str = f"{Emoji.CROSS.value} Cancel",
confirm_style: discord.ButtonStyle = discord.ButtonStyle.danger,
cancel_style: discord.ButtonStyle = discord.ButtonStyle.secondary,
confirm_message: str | None = None,
Expand Down
36 changes: 36 additions & 0 deletions api/emojis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from enum import Enum


class Emoji(Enum):
AT = "<:__:1518549811207340135>" # @
ADD = "<:add:1518549814516781197>"
CROSS = "<:cross:1518549817981276180>"
CRYPTO = "<:crypto:1518549821517201498>"
DELETE = "<:delete:1518549825728155739>"
DISLIKE = "<:dislike:1518549829842636981>"
DOWNLOAD = "<:download:1518549833869295646>"
ENCRYPTION = "<:encryption:1518549837703020554>"
FILE = "<:file:1518549840399958126>"
HIDE = "<:hide:1518549844380094494>"
HOUSE = "<:house:1518549847400251632>"
INVITE = "<:invite:1518549850264830072>"
LEAF = "<:leaf:1518549852915761152>"
LIKE = "<:like:1518549855398662214>"
PING = "<:ping:1518549857923760230>"
PYTHON = "<:python:1518549861451042817>"
SEARCH = "<:search:1518549864667938857>"
SHINE = "<:shine:1518549868287758419>"
TIME = "<:time:1518549871710310500>"
X = "<:x_:1518549879402532905>"
YOUTUBE = "<:youtube:1518549883165085826>"
GIVEAWAY = "<:giveaway:1518974855855472740>"
HASHTAG = "<:hashtag:1518974868920471743>"
SECURITY = "<:security:1518974879896965252>"
SHUFFLE = "<:shuffle:1518974886616502313>"
TADA = "<:tada:1518999195573289201>"
WARNING = "<:warning:1519002977354907658>"
COMMAND = "<:command:1519019447283617823>"
UTILITIES = "<:utilities:1519019450282541196>"
DOWNSHIFT = "<:downchart:1519019453721870587>"
UPSHIFT = "<:upchart:1519019457530564740>"
CROWN = "<:crown:1519019461464817858>"
28 changes: 6 additions & 22 deletions api/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ async def _send_exception_webhook(exception: Exception, hook: str | None = None)
return

error = getattr(exception, "original", exception)
traceback_text = "".join(
traceback.format_exception(type(error), error, error.__traceback__)
)
traceback_text = "".join(traceback.format_exception(type(error), error, error.__traceback__))
traceback_text = _truncate_text(traceback_text, 3800)

embed = discord.Embed(
Expand Down Expand Up @@ -157,9 +155,7 @@ async def log_command_error(ctx: commands.Context, exception: Exception) -> None
"""
try:
error = getattr(exception, "original", exception)
traceback_text = "".join(
traceback.format_exception(type(error), error, error.__traceback__)
)
traceback_text = "".join(traceback.format_exception(type(error), error, error.__traceback__))
traceback_text = _truncate_text(traceback_text, 3800)

embed = discord.Embed(
Expand Down Expand Up @@ -212,9 +208,7 @@ def _get_app_command_name(command: app_commands.Command | None) -> str:
return name or str(command)


async def log_app_command_usage(
interaction: discord.Interaction, command: app_commands.Command | None
) -> None:
async def log_app_command_usage(interaction: discord.Interaction, command: app_commands.Command | None) -> None:
"""
Log app command usage for on_app_command_completion event.

Expand Down Expand Up @@ -248,11 +242,7 @@ async def log_app_command_usage(
)
embed.add_field(
name="Server",
value=(
f"{interaction.guild.name} [`{interaction.guild.id}`]"
if interaction.guild
else "DM"
),
value=(f"{interaction.guild.name} [`{interaction.guild.id}`]" if interaction.guild else "DM"),
inline=False,
)
embed.add_field(
Expand Down Expand Up @@ -283,9 +273,7 @@ async def log_app_command_error(interaction: discord.Interaction, exception: Exc
"""
try:
error = getattr(exception, "original", exception)
traceback_text = "".join(
traceback.format_exception(type(error), error, error.__traceback__)
)
traceback_text = "".join(traceback.format_exception(type(error), error, error.__traceback__))
traceback_text = _truncate_text(traceback_text, 3800)

user = interaction.user
Expand Down Expand Up @@ -315,11 +303,7 @@ async def log_app_command_error(interaction: discord.Interaction, exception: Exc
)
embed.add_field(
name="Server",
value=(
f"{interaction.guild.name} [`{interaction.guild.id}`]"
if interaction.guild
else "DM"
),
value=(f"{interaction.guild.name} [`{interaction.guild.id}`]" if interaction.guild else "DM"),
inline=False,
)
embed.add_field(
Expand Down
Binary file added assets/emoji/@.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/add.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/command.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/cross.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/crown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/crypto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/delete.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/dislike.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/downchart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/download.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/encryption.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/file.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/giveaway.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/hashtag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/hide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/house.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/invite.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/leaf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/like.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/ping.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/python.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/search.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/security.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/shine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/emoji/shuffle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/emoji/source.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generated from https://ray.so/ & custom made
Binary file added assets/emoji/tada.png
Binary file added assets/emoji/time.png
Binary file added assets/emoji/upchart.png
Binary file added assets/emoji/utilities.png
Binary file added assets/emoji/warning.png
Binary file added assets/emoji/x.png
Binary file added assets/emoji/youtube.png
68 changes: 20 additions & 48 deletions cogs/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from discord import app_commands
from discord.ext import commands

from api.emojis import Emoji
from api.log import log_exception
from core.amenity import Amenity
from core.cache import cache
Expand Down Expand Up @@ -168,18 +169,19 @@ async def _get_coin_id(self, coin: str) -> str | None:
if not coins:
return None

# Prefer exact ID match
for entry in coins:
if entry.get("id") == query:
return entry.get("id")

# Then name match
for entry in coins:
name = str(entry.get("name") or "").lower()
if name == query:
if str(entry.get("name") or "").lower() == query:
return entry.get("id")

# Then symbol match
for entry in coins:
symbol = str(entry.get("symbol") or "").lower()
if symbol == query:
if str(entry.get("symbol") or "").lower() == query:
return entry.get("id")

return None
Expand Down Expand Up @@ -207,9 +209,7 @@ async def _get_ltc_price_usd(self) -> float | None:
if isinstance(cached, (int, float)):
return float(cached)

coingecko_url = (
"https://api.coingecko.com/api/v3/simple/price?ids=litecoin&vs_currencies=usd"
)
coingecko_url = "https://api.coingecko.com/api/v3/simple/price?ids=litecoin&vs_currencies=usd"
data = await self._fetch_json(coingecko_url)
price = None
if data and isinstance(data.get("litecoin"), dict):
Expand All @@ -233,9 +233,7 @@ async def _get_price_usd(self, coin_id: str, cache_key: str) -> float | None:
if isinstance(cached, (int, float)):
return float(cached)

coingecko_url = (
f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd"
)
coingecko_url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd"
data = await self._fetch_json(coingecko_url)
price = None
if data and isinstance(data.get(coin_id), dict):
Expand Down Expand Up @@ -296,9 +294,7 @@ async def ltc_balance(self, ctx: commands.Context, address: str) -> None:
await ctx.send("⚠️ Rate limited by BlockCypher. Please try again later.")
return
else:
await ctx.send(
f"Error fetching balance from BlockCypher (Status: {response.status})."
)
await ctx.send(f"Error fetching balance from BlockCypher (Status: {response.status}).")
return
except Exception as exc:
log_exception(exc)
Expand Down Expand Up @@ -364,9 +360,7 @@ async def eth_balance(self, ctx: commands.Context, address: str) -> None:
await ctx.send("⚠️ Rate limited by BlockCypher. Please try again later.")
return
else:
await ctx.send(
f"Error fetching balance from BlockCypher (Status: {response.status})."
)
await ctx.send(f"Error fetching balance from BlockCypher (Status: {response.status}).")
return
except Exception as exc:
log_exception(exc)
Expand Down Expand Up @@ -438,9 +432,7 @@ async def sol_balance(self, ctx: commands.Context, address: str) -> None:
if response.status == 200:
data = await response.json()
else:
await ctx.send(
f"Error fetching balance from Solana RPC (Status: {response.status})."
)
await ctx.send(f"Error fetching balance from Solana RPC (Status: {response.status}).")
return
except Exception as exc:
log_exception(exc)
Expand Down Expand Up @@ -530,7 +522,7 @@ def format_line(symbol: str, value: float | int | None) -> str | None:
change_str = f"\n\n{emoji} **24h Change:** {change_24h:+.2f}%"

embed = discord.Embed(
title=f"💰 Price of {coin.upper()}",
title=f"{Emoji.CRYPTO.value} Price of {coin.upper()}",
description=" | ".join(lines) + change_str,
color=discord.Color.green()
if (isinstance(change_24h, (int, float)) and change_24h >= 0)
Expand Down Expand Up @@ -578,19 +570,14 @@ async def crypto_convert(

try:
if from_is_fiat and to_is_fiat:
url = (
"https://api.frankfurter.app/latest"
f"?amount={amount}&from={fromm.upper()}&to={to.upper()}"
)
url = f"https://api.frankfurter.app/latest?amount={amount}&from={fromm.upper()}&to={to.upper()}"
data, status = await self._fetch_json_status(url)
rates = data.get("rates", {}) if isinstance(data, dict) else {}
result = rates.get(to.upper())
if result is not None:
embed = discord.Embed(
title="Currency Conversion",
description=(
f"**{amount:,.2f} {fromm.upper()}** = **{result:,.2f} {to.upper()}**"
),
description=(f"**{amount:,.2f} {fromm.upper()}** = **{result:,.2f} {to.upper()}**"),
color=discord.Color.blue(),
)
embed.set_footer(text="Exchange rates from Frankfurter (ECB)")
Expand All @@ -607,10 +594,7 @@ async def crypto_convert(
await ctx.send(f"Could not find crypto: `{fromm}`")
return

url = (
"https://api.coingecko.com/api/v3/simple/price"
f"?ids={coin_id}&vs_currencies={to}"
)
url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies={to}"
data, status = await self._fetch_json_status(url)
if status == 429:
await ctx.send("⚠️ Rate limited. Please try again later.")
Expand All @@ -620,9 +604,7 @@ async def crypto_convert(
result = amount * rate
embed = discord.Embed(
title="Crypto to Fiat",
description=(
f"**{amount:,.8g} {fromm.upper()}** = **{result:,.2f} {to.upper()}**"
),
description=(f"**{amount:,.8g} {fromm.upper()}** = **{result:,.2f} {to.upper()}**"),
color=discord.Color.gold(),
)
embed.set_footer(text="Data from CoinGecko")
Expand All @@ -637,10 +619,7 @@ async def crypto_convert(
await ctx.send(f"Could not find crypto: `{to}`")
return

url = (
"https://api.coingecko.com/api/v3/simple/price"
f"?ids={coin_id}&vs_currencies={fromm}"
)
url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies={fromm}"
data, status = await self._fetch_json_status(url)
if status == 429:
await ctx.send("⚠️ Rate limited. Please try again later.")
Expand All @@ -654,9 +633,7 @@ async def crypto_convert(
result_str = f"{result:,.6f}" if result >= 1 else f"{result:.8f}"
embed = discord.Embed(
title="Fiat to Crypto",
description=(
f"**{amount:,.2f} {fromm.upper()}** = **{result_str} {to.upper()}**"
),
description=(f"**{amount:,.2f} {fromm.upper()}** = **{result_str} {to.upper()}**"),
color=discord.Color.gold(),
)
embed.set_footer(text="Data from CoinGecko")
Expand All @@ -676,10 +653,7 @@ async def crypto_convert(
await ctx.send(f"Could not find crypto: `{to}`")
return

url = (
"https://api.coingecko.com/api/v3/simple/price"
f"?ids={from_coin_id},{to_coin_id}&vs_currencies=usd"
)
url = f"https://api.coingecko.com/api/v3/simple/price?ids={from_coin_id},{to_coin_id}&vs_currencies=usd"
data, status = await self._fetch_json_status(url)
if status == 429:
await ctx.send("⚠️ Rate limited. Please try again later.")
Expand All @@ -692,9 +666,7 @@ async def crypto_convert(
result_str = f"{result:,.6f}" if result >= 1 else f"{result:.8f}"
embed = discord.Embed(
title="Crypto to Crypto",
description=(
f"**{amount:,.8g} {fromm.upper()}** = **{result_str} {to.upper()}**"
),
description=(f"**{amount:,.8g} {fromm.upper()}** = **{result_str} {to.upper()}**"),
color=discord.Color.purple(),
)
embed.add_field(
Expand Down
Loading