Compare commits

...

11 commits

Author SHA1 Message Date
425f921be7 add album charts 2025-10-08 05:35:56 -03:00
bd54c8c9cd add my pyzxz fork 2025-10-08 03:57:19 -03:00
31718549d9 start 0x0. implementation 2025-10-08 02:46:01 -03:00
80846d5c73 update todo list 2025-10-08 02:17:37 -03:00
321b2f5d39 undo/comment out async catbox library 2025-10-08 02:17:23 -03:00
d649de8fb8 add catbox uploader and collage generator to bot class 2025-10-08 01:16:33 -03:00
7be4fee669 remove redundant if in setfm 2025-10-08 00:39:47 -03:00
94f43336f7 add set as an alias 2025-10-08 00:36:29 -03:00
7dfb910667 remove a couple redundant prints 2025-10-07 23:59:23 -03:00
bfc9197333 minor todo for post removal 2025-10-07 23:58:27 -03:00
4ad65c6b1d add server count command 2025-10-07 23:58:10 -03:00
9 changed files with 144 additions and 21 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "pyzxz"]
path = pyzxz
url = https://git.yuki.k4w411.net/yuki/pyzxz

View file

@ -12,5 +12,7 @@ dead simple last.fm bot
- add database username checker to utils.py
- add collage generation
- fix attachment generation and upload
- temporary fix: upload and link to catbox urls
- permanent fix: fork and update nerimity library
- ~~**temporary fix**: upload and link to catbox urls~~ the asynchronous catbox library does not accept byte arrays
- ~~**temporary fix**: construct the attachment object from scratch and upload manually(?)~~ its still broken
- **temporary fix:** use 0x0.st
- **permanent fix**: fork and update nerimity library

25
bot.py
View file

@ -1,13 +1,20 @@
import os
import importlib
import aiosqlite
import nerimity
import pylast
import aiosqlite
from pyzxz.pyzxz.pyzxz import ZeroXZero # :sob:
from lastfmcollagegenerator.collage_generator import CollageGenerator
# from catbox_async_uploader.catbox_async_uploader.core import CatboxAsyncUploader
class Bot(nerimity.Client):
"""Extended client class for extra functionality."""
def __init__(self, lastfm_api_key: str, lastfm_api_secret: str, owner_id: int = None, db_file: str = "database.db", *args, **kwargs):
def __init__(self, lastfm_api_key: str, lastfm_api_secret: str,
owner_id: int = None, db_file: str = "database.db",
*args, **kwargs):
super().__init__(*args, **kwargs)
self.owner_id = owner_id
@ -18,10 +25,22 @@ class Bot(nerimity.Client):
api_secret = lastfm_api_secret
)
# initialize collage generator
self.collage_generator = CollageGenerator(
lastfm_api_key=lastfm_api_key,
lastfm_api_secret=lastfm_api_secret
)
# initialize catbox client
# self.catbox_uploader = CatboxAsyncUploader(userhash=catbox_hash)
# initialize 0x0 client
self.zxz = ZeroXZero("https://x0.at")
# database
self.db = None
self.db_file = db_file
def load_commands(self, commands_dir: str):
for filename in os.listdir(commands_dir):
if filename.endswith(".py") and not filename.startswith("_"):

View file

@ -6,7 +6,6 @@ import utils as u
def setup(bot: bot.Bot):
@bot.command(name="echo")
async def echo(ctx: nerimity.Context, *text: str):
print(bot.is_owner(ctx.author))
if not bot.is_owner(ctx.author):
print("returned")
return
@ -19,9 +18,19 @@ def setup(bot: bot.Bot):
print(e)
await ctx.send(u.error_msg(f"Unknown error:\n`{e}`"))
@bot.command(name="servercount", aliases=["sc"])
async def servercount(ctx: nerimity.Context):
if not bot.is_owner(ctx.author):
print("returned")
return
try: await ctx.send(f"I'm in {len(bot.servers)} servers!!")
except Exception as e:
print(e)
await ctx.send(u.error_msg(f"Unknown error:\n`{e}`"))
@bot.command(name="post")
async def post(ctx: nerimity.Context, *text: str):
print(bot.is_owner(ctx.author))
if not bot.is_owner(ctx.author):
print("returned")
return
@ -31,6 +40,7 @@ def setup(bot: bot.Bot):
try:
await ctx.send(f"Creating post:\n```markdown\n{" ".join(text)}\n```")
# TODO: post removal command :p
# post = nerimity.Post.create_post(content=" ".join(text))
# print(post)
await ctx.send(u.good_msg(f"Post created!!"))

64
commands/collage.py Normal file
View file

@ -0,0 +1,64 @@
import io
import nerimity
import bot
import utils as u
# from catbox_async_uploader.catbox_async_uploader.enums import LitterboxDuration
# from pyzxz import ZeroXZero
def setup(bot: bot.Bot):
@bot.command(name="chart", aliases=["c", "chartalbum", "albumchart", "collage"])
@bot.slash_command(name="chartalbum", description="Generate an album collage.")
async def chart_album(ctx: nerimity.Context, size: str = "5x5", timeframe: str = "7day", username: str = None):
if 'x' not in size:
await ctx.send(u.error_msg("Please provide a valid size.\nie `/chart album 5x5`"))
return
if not username:
try: username = await bot.get_lastfm(ctx.author.id)
except Exception as e:
print(e)
await ctx.send(u.error_msg(f"Unknown database error:\n{e}"))
if not username:
await ctx.send(u.error_msg("Please provide a Last.fm username (or set yours with `/setfm`)."))
print("returned")
return
try:
temp_msg = await ctx.send(f"Generating album chart for **{username}**...")
image = bot.collage_generator.generate(
entity = "album",
username = username,
rows = int(size.split(sep="x")[0]),
cols = int(size.split(sep="x")[1]),
period = timeframe
)
img_bytes = io.BytesIO()
image.save(fp=img_bytes, format="png")
# link = await bot.catbox_uploader.upload_to_litterbox(
# file_path_or_bytes = img_bytes,
# file_name = "collage.png",
# duration = LitterboxDuration.H12
# )
# attachment = await u.construct_attachment_from_bytes(
# filename = "chart",
# file_type = "png",
# bytes_arr = img_bytes,
# ).upload()
link = await bot.zxz.upload_from_bytes(img_bytes.getvalue(), "chart.png")
await ctx.send(u.good_msg(f"{size} Chart for {username} successfully generated:\n{link}"))
temp_msg.delete()
except Exception as e:
if temp_msg: temp_msg.delete()
print(e)
await ctx.send(u.error_msg(f"Unknown error:\n`{e}`"))

View file

@ -4,16 +4,17 @@ import bot
import utils as u
def setup(bot: bot.Bot):
@bot.command(name="setfm", aliases=["setuser", "setlastfm"])
@bot.command(name="setfm", aliases=["set", "setuser", "setlastfm"])
@bot.slash_command(name="setfm", description="Sets your Last.fm username.")
async def setfm(ctx: nerimity.Context, username: str = None):
if not username:
await ctx.send(u.error_msg("Please provide your Last.fm username.\n\
usage: `/fm <your last.fm username>`"))
else:
try:
await bot.set_lastfm(ctx.author.id, username)
await ctx.send(u.good_msg(f"Your Last.fm user has been set to **{username}**!"))
except Exception as e:
print(e)
await ctx.send(u.error_msg("Unknown database error."))
return
try:
await bot.set_lastfm(ctx.author.id, username)
await ctx.send(u.good_msg(f"Your Last.fm user has been set to **{username}**!"))
except Exception as e:
print(e)
await ctx.send(u.error_msg("Unknown database error."))

1
pyzxz Submodule

@ -0,0 +1 @@
Subproject commit 26559dd2707d92fc8f1740befb97ed0c32555e65

View file

@ -1,30 +1,38 @@
aiofiles==24.1.0
aiohappyeyeballs==2.6.1
aiohttp==3.13.0
aiohttp==3.10.10
aiosignal==1.4.0
aiosqlite==0.21.0
anyio==4.11.0
astroid==3.3.11
attrs==25.4.0
beautifulsoup4==4.12.3
certifi==2025.10.5
charset-normalizer==3.4.3
dill==0.4.0
frozenlist==1.8.0
h11==0.16.0
html5lib==1.1
httpcore==1.0.9
httpx==0.28.1
idna==3.10
isort==6.1.0
lastfmcollagegenerator==0.4.13
mccabe==0.7.0
multidict==6.7.0
nerimity==1.4.1
pillow==10.4.0
platformdirs==4.4.0
propcache==0.4.0
pylast==6.0.0
pylast==5.3.0
pylint==3.3.9
requests==2.32.5
requests==2.32.3
six==1.17.0
sniffio==1.3.1
soupsieve==2.8
tomlkit==0.13.3
typing_extensions==4.15.0
urllib3==2.5.0
webencodings==0.5.1
websockets==15.0.1
yarl==1.22.0

View file

@ -1,9 +1,10 @@
# import io
# import requests
# import mimetypes
import nerimity
# def construct_attachment_from_url(url: str = None):
# def construct_attachment_from_url(url: str = None) -> nerimity.Attachment | None:
# if not url: return None
# content = requests.get(url).content
@ -17,8 +18,22 @@ import nerimity
# return attachment
def error_msg(message: str):
# def construct_attachment_from_bytes(filename: str = "file", bytes_arr: io.BytesIO = None, file_type: str = None) -> nerimity.Attachment | None:
# if not bytes_arr: return None
# if not file_type: return None
# attachment = nerimity.Attachment()
# attachment.internal_type = nerimity.AttachmentTypes.OUTGOING
# attachment.data = bytes_arr.read()
# attachment.data_type = file_type
# attachment.size = len(attachment.data)
# attachment.name = filename
# return attachment
def error_msg(message: str) -> str:
return f"[#e5323b][Error] [#reset]{message}"
def good_msg(message: str):
def good_msg(message: str) -> str:
return f"[#52ff54][Success] [#reset]{message}"