From d447e7c71eaf1e424b6223677f544f7c5957d998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=9D=E3=82=AD?= Date: Sat, 7 Jun 2025 11:27:23 -0700 Subject: [PATCH] feat: add initial songbox telegram bot implementation - Implement bot with song download and metadata display functionality - Add configuration files and dependencies - Include gitignore for Python development environment --- .gitignore | 179 +++++++++++++++++++++++++++++++++++++++++++++ config.json.sample | 4 + main.py | 74 +++++++++++++++++++ requirements.txt | 5 ++ 4 files changed, 262 insertions(+) create mode 100644 .gitignore create mode 100644 config.json.sample create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf67ce8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,179 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python + +*.session +config.json \ No newline at end of file diff --git a/config.json.sample b/config.json.sample new file mode 100644 index 0000000..afe9fdd --- /dev/null +++ b/config.json.sample @@ -0,0 +1,4 @@ +{ + "botToken": "1234:1231231231231123", + "songboxToken": "jwt_token_here" +} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..87f4219 --- /dev/null +++ b/main.py @@ -0,0 +1,74 @@ +from pyrogram import Client, filters, types +import re +import httpx +import os +from songbox import SongboxClient +import json + + +async def download_file(url, filename): + async with httpx.AsyncClient() as client: + response = await client.get(url) + response.raise_for_status() + with open(filename, 'wb') as f: + f.write(response.content) + return filename + + +with open("config.json", "r") as f: + config = json.load(f) + +songbox = SongboxClient() +songbox.set_token(config['songboxToken']) + +app = Client("songbox", api_id=6, api_hash="eb06d4abfb49dc3eeb1aeb98ae0f581e", bot_token=config['botToken']) + + +@app.on_message(filters.command('start')) +async def start(_, message: types.Message): + await message.reply("Hello!") + + +@app.on_message(filters.text) +async def echo(_, message: filters.Message): + match = re.search(r'/((songs|albums|artists))/(\d+)', message.text) + if match: + content_type = match.group(1) + content_id = match.group(3) + if content_type == 'songs': + song = songbox.music.get_song(content_id) + artwork_file = f"{song['name']}_artwork.jpg" + await download_file(song['artwork_url'], artwork_file) + duration_seconds = int(song['duration']) + await message.reply_photo( + photo=artwork_file, + caption=( + f"**Album:** {song['album_name']}\n" + f"**Artist:** {song['artist_name']}\n" + f"**Song:** {song['name']}\n" + f"**Duration:** {duration_seconds // 60}:{duration_seconds % 60:02}" + ) + ) + audio_file = songbox.music.download_song(song['url_original']) + await message.reply_audio( + audio=audio_file, + performer=song['artist_name'], + title=song['name'], + thumb=artwork_file, + ) + if os.path.exists(audio_file): + os.remove(audio_file) + if os.path.exists(artwork_file): + os.remove(artwork_file) + + elif content_type =='albums': + await message.reply("downloading from albums does not support yet") + elif content_type =='artists': + await message.reply("downloading from artists does not support yet") + new_token = songbox.auth.refresh_token() + config['songboxToken'] = new_token + with open("config.json", "w") as f: + json.dump(config, f, indent=4) + + +app.run() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6d02d8a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +httpx==0.28.1 +pyrofork==2.3.64 +tgcrypto==1.2.5 +--index-url https://git.cubable.date/api/packages/CustomIcon/pypi/simple/ +songbox==1.0.0 \ No newline at end of file