experiment - slash commands

This commit is contained in:
esef committed 2026-06-29 15:54:52 +03:00
1 parent f05a4bbe5a
commit a578c143e7
3 files changed
+123 -52

No files matched your search

+5
View File
@@ -111,6 +111,11 @@ I will love, cherish, and protect Yuuka until my very last breath; my successors
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠛⣳⡾⢯⣿⣷⣶⣦⣤⣤⣤⣤⠤⡶⢶⣞⡛⠛⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀""" ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠛⣳⡾⢯⣿⣷⣶⣦⣤⣤⣤⣤⠤⡶⢶⣞⡛⠛⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀"""
await ctx.reply(file=discord.File(io.StringIO(pasta), filename="yuyuko.txt")) await ctx.reply(file=discord.File(io.StringIO(pasta), filename="yuyuko.txt"))
@commands.command()
async def anlaki(self, ctx):
pasta = r"""bro anlaki bro anlaki you guys are dogshit bro anlaki bro anlaki. You guys have; NO hands, NO brain, I'm so happy that I have monitor I have mouse I have keyboard uhh I have body I have hands I have arms... feet? Like bro..."""
await ctx.reply(pasta)
async def setup(bot): async def setup(bot):
await bot.add_cog(Copypasta(bot)) await bot.add_cog(Copypasta(bot))
+112 -52
View File
@@ -3,81 +3,141 @@ import time
import json import json
import aiohttp import aiohttp
import discord import discord
from discord import app_commands
from discord.ext import commands from discord.ext import commands
# load aliases from json # load aliases from json
with open(os.path.join(os.path.dirname(__file__), 'currency_aliases.json'), 'r', encoding='utf-8') as f: with open(os.path.join(os.path.dirname(__file__), 'currency_aliases.json'), 'r', encoding='utf-8') as f:
_ALIAS_MAP = json.load(f) _ALIAS_MAP = json.load(f)
# in‑memory cache for rates: {base: (timestamp, rates_dict)} # unique ISO codes available for autocomplete (preserves order, deduplicates)
_ISO_CODES = sorted(set(_ALIAS_MAP.values()))
# in-memory cache for rates: {base: (timestamp, rates_dict)}
_RATE_CACHE = {} _RATE_CACHE = {}
_CACHE_TTL = 3600 # seconds _CACHE_TTL = 3600 # seconds
def _norm(cur: str) -> str:
"""Normalize a currency string to its ISO code.
Returns upper-case ISO code or raises ValueError if unknown.
"""
cur = cur.strip().lower()
if cur in _ALIAS_MAP:
return _ALIAS_MAP[cur]
cur = cur.upper()
if len(cur) == 3 and cur.isalpha():
return cur
raise ValueError(f"unknown currency '{cur}'")
async def _get_rates(base: str) -> dict:
"""Fetch rates for *base* from cache or the Frankfurter API."""
now = time.time()
cached = _RATE_CACHE.get(base)
if cached and now - cached[0] < _CACHE_TTL:
return cached[1]
url = f"https://api.frankfurter.app/latest?base={base}"
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status != 200:
raise RuntimeError('currency API error')
data = await resp.json()
rates = data.get('rates', {})
_RATE_CACHE[base] = (now, rates)
return rates
def _make_embed(amount: float, from_code: str, to_code: str, rate: float) -> discord.Embed:
result = amount * rate
embed = discord.Embed(title='Currency conversion', color=0x5865F2)
embed.add_field(name='From', value=f"{amount:g} {from_code}", inline=True)
embed.add_field(name='To', value=f"{result:g} {to_code}", inline=True)
embed.add_field(name='Rate', value=f"1 {from_code} = {rate:g} {to_code}", inline=False)
embed.set_footer(text='Data from frankfurter.app')
return embed
async def _currency_autocomplete(
interaction: discord.Interaction,
current: str,
) -> list[app_commands.Choice[str]]:
"""Return up to 25 matching currency codes/aliases for autocomplete."""
current_lower = current.strip().lower()
results: list[app_commands.Choice[str]] = []
# exact alias matches first
for alias, code in _ALIAS_MAP.items():
if current_lower in alias and len(results) < 25:
results.append(app_commands.Choice(name=f"{code} ({alias})", value=code))
# fill remaining slots with ISO code prefix matches
for code in _ISO_CODES:
if code.startswith(current.upper()) and len(results) < 25:
if not any(c.value == code for c in results):
results.append(app_commands.Choice(name=code, value=code))
return results[:25]
class CurrencyConverter(commands.Cog): class CurrencyConverter(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
def _norm(self, cur: str) -> str: # ── slash command ────────────────────────────────────────────────────────
"""Normalize a currency string to its ISO code.
Returns upper‑case ISO code or raises ValueError if unknown.
"""
cur = cur.strip().lower()
if cur in _ALIAS_MAP:
return _ALIAS_MAP[cur]
cur = cur.upper()
if len(cur) == 3 and cur.isalpha():
return cur
raise ValueError(f"unknown currency '{cur}'")
async def _get_rates(self, base: str) -> dict: @app_commands.command(name='currency', description='Convert between currencies')
"""Fetch rates for *base* from cache or the Frankfurter API. @app_commands.describe(
Returns a dict mapping target ISO codes to float rates. from_cur='Currency to convert from (e.g. USD, dollar, $)',
""" to_cur='Currency to convert to (e.g. EUR, euro, €)',
now = time.time() amount='Amount to convert (default: 1)',
cached = _RATE_CACHE.get(base) )
if cached and now - cached[0] < _CACHE_TTL: @app_commands.autocomplete(from_cur=_currency_autocomplete, to_cur=_currency_autocomplete)
return cached[1] async def currency_slash(
url = f"https://api.frankfurter.app/latest?base={base}" self,
async with aiohttp.ClientSession() as session: interaction: discord.Interaction,
async with session.get(url) as resp: from_cur: str,
if resp.status != 200: to_cur: str,
raise RuntimeError('currency API error') amount: float = 1.0,
data = await resp.json() ):
rates = data.get('rates', {}) await interaction.response.defer()
_RATE_CACHE[base] = (now, rates) try:
return rates from_code = _norm(from_cur)
to_code = _norm(to_cur)
except ValueError as e:
return await interaction.followup.send(f"Unknown currency: {e}", ephemeral=True)
try:
rates = await _get_rates(from_code)
except Exception:
return await interaction.followup.send('Could not fetch conversion rates, try again later.', ephemeral=True)
rate = rates.get(to_code)
if rate is None:
return await interaction.followup.send(f"Unsupported conversion: {from_code} to {to_code}", ephemeral=True)
await interaction.followup.send(embed=_make_embed(amount, from_code, to_code, rate))
# ── prefix command (kept for backwards compat) ───────────────────────────
@commands.command(name='currency') @commands.command(name='currency')
async def currency(self, ctx, amount: float = 1, from_cur: str = None, to_cur: str = None): async def currency_prefix(self, ctx, amount: float = 1, from_cur: str = None, to_cur: str = None):
"""Convert *amount* from *from_cur* to *to_cur*. """Convert amount from from_cur to to_cur. Usage: !currency [amount] <from> <to>"""
If *amount* is omitted the command is parsed as ``!currency <from> <to>``.
"""
# discord.py passes arguments left‑to‑right; we need to support two signatures.
# When the user supplies only two strings, *amount* will be parsed as the first string.
if from_cur is None and to_cur is None: if from_cur is None and to_cur is None:
# only two arguments were given, treat them as from/to and default amount=1 to_cur = amount # type: ignore
to_cur = amount # type: ignore
from_cur = str(amount) # type: ignore from_cur = str(amount) # type: ignore
amount = 1.0 amount = 1.0
try: try:
from_code = self._norm(str(from_cur)) from_code = _norm(str(from_cur))
to_code = self._norm(str(to_cur)) to_code = _norm(str(to_cur))
except ValueError as e: except ValueError as e:
return await ctx.reply(f"❌ {e}") return await ctx.reply(f"Unknown currency: {e}")
try: try:
rates = await self._get_rates(from_code) rates = await _get_rates(from_code)
except Exception: except Exception:
return await ctx.reply('❌ could not fetch conversion rates, try again later') return await ctx.reply('Could not fetch conversion rates, try again later.')
rate = rates.get(to_code) rate = rates.get(to_code)
if rate is None: if rate is None:
return await ctx.reply(f"❌ unsupported conversion: {from_code} → {to_code}") return await ctx.reply(f"Unsupported conversion: {from_code} to {to_code}")
result = amount * rate await ctx.reply(embed=_make_embed(amount, from_code, to_code, rate))
embed = discord.Embed(title='Currency conversion', color=0x00ff00)
embed.add_field(name='From', value=f"{amount:g} {from_code}", inline=True)
embed.add_field(name='To', value=f"{result:g} {to_code}", inline=True)
embed.add_field(name='Rate', value=f"1 {from_code} = {rate:g} {to_code}", inline=False)
embed.set_footer(text='Data from frankfurter.app')
await ctx.reply(embed=embed)
async def setup(bot):
async def setup(bot: commands.Bot):
await bot.add_cog(CurrencyConverter(bot)) await bot.add_cog(CurrencyConverter(bot))
+6
View File
@@ -40,6 +40,12 @@ async def on_ready():
print(f'✅ {bot.user} is online!') print(f'✅ {bot.user} is online!')
asyncio.create_task(forwarder.start()) asyncio.create_task(forwarder.start())
asyncio.create_task(twitter.start()) asyncio.create_task(twitter.start())
try:
synced = await bot.tree.sync()
print(f'✅ synced {len(synced)} slash command(s)')
except Exception as e:
print(f'slash command sync failed: {e}')
async def main(): async def main():
async with bot: async with bot: