Add spotify search (only search), default menu
This commit is contained in:
@@ -4,10 +4,11 @@ from rich import print
|
|||||||
async def runner():
|
async def runner():
|
||||||
from .common import dp, bot
|
from .common import dp, bot
|
||||||
|
|
||||||
from . import handlers
|
from . import handlers, callbacks
|
||||||
|
|
||||||
dp.include_router(
|
dp.include_routers(
|
||||||
handlers.router
|
handlers.router,
|
||||||
|
callbacks.router,
|
||||||
)
|
)
|
||||||
|
|
||||||
await bot.delete_webhook(drop_pending_updates=True)
|
await bot.delete_webhook(drop_pending_updates=True)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from aiogram import Router
|
||||||
|
from . import (
|
||||||
|
full_menu,
|
||||||
|
)
|
||||||
|
from bot.middlewares import PrivateButtonMiddleware
|
||||||
|
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
router.callback_query.middleware(PrivateButtonMiddleware())
|
||||||
|
|
||||||
|
router.include_routers(
|
||||||
|
full_menu.router,
|
||||||
|
)
|
||||||
|
|||||||
6
bot/callbacks/factories/full_menu.py
Normal file
6
bot/callbacks/factories/full_menu.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from typing import Literal
|
||||||
|
from aiogram.filters.callback_data import CallbackData
|
||||||
|
|
||||||
|
|
||||||
|
class FullMenuCallback(CallbackData, prefix='full_menu'):
|
||||||
|
action: Literal['settings']
|
||||||
12
bot/callbacks/full_menu.py
Normal file
12
bot/callbacks/full_menu.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from aiogram import Router, Bot, F
|
||||||
|
from aiogram.types import (
|
||||||
|
CallbackQuery
|
||||||
|
)
|
||||||
|
from .factories.full_menu import FullMenuCallback
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(FullMenuCallback.filter(F.action == 'settings'))
|
||||||
|
async def on_close(callback_query: CallbackQuery, bot: Bot):
|
||||||
|
await callback_query.answer('Settings are not available yet')
|
||||||
@@ -1,9 +1,20 @@
|
|||||||
from aiogram import Router
|
from aiogram import Router
|
||||||
from . import initialize
|
from . import (
|
||||||
|
initialize,
|
||||||
|
inline_default,
|
||||||
|
inline_empty,
|
||||||
|
on_chosen,
|
||||||
|
)
|
||||||
|
|
||||||
|
from bot.middlewares import SaveChosenMiddleware
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
router.chosen_inline_result.outer_middleware(SaveChosenMiddleware())
|
||||||
|
|
||||||
router.include_routers(
|
router.include_routers(
|
||||||
initialize.router,
|
initialize.router,
|
||||||
|
inline_default.router,
|
||||||
|
inline_empty.router,
|
||||||
|
on_chosen.router,
|
||||||
)
|
)
|
||||||
|
|||||||
1
bot/handlers/inline_default/__init__.py
Normal file
1
bot/handlers/inline_default/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .on_inline_default import router
|
||||||
38
bot/handlers/inline_default/on_inline_default.py
Normal file
38
bot/handlers/inline_default/on_inline_default.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from aiogram import Router, Bot, F
|
||||||
|
from aiogram.types import (
|
||||||
|
InlineQuery, InlineQueryResultDocument, InlineQueryResultCachedAudio,
|
||||||
|
InlineKeyboardMarkup, InlineKeyboardButton,
|
||||||
|
)
|
||||||
|
|
||||||
|
from bot.modules.spotify import spotify
|
||||||
|
from bot.modules.database import db
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@router.inline_query(F.query != '')
|
||||||
|
async def default_inline_query(inline_query: InlineQuery, bot: Bot):
|
||||||
|
await inline_query.answer(
|
||||||
|
[
|
||||||
|
InlineQueryResultDocument(
|
||||||
|
id='spot::' + audio.id,
|
||||||
|
title=audio.name,
|
||||||
|
description=audio.all_artists,
|
||||||
|
thumb_url=audio.thumbnail,
|
||||||
|
document_url=audio.preview_url,
|
||||||
|
mime_type='application/zip',
|
||||||
|
reply_markup=InlineKeyboardMarkup(
|
||||||
|
inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text='Downloading...', callback_data='.')]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
) if audio.id not in list(db.spotify.keys()) else
|
||||||
|
InlineQueryResultCachedAudio(
|
||||||
|
id='spotc::' + audio.id,
|
||||||
|
audio_file_id=db.spotify[audio.id],
|
||||||
|
)
|
||||||
|
for audio in spotify.songs.search(inline_query.query)
|
||||||
|
],
|
||||||
|
cache_time=0,
|
||||||
|
is_personal=True
|
||||||
|
)
|
||||||
1
bot/handlers/inline_empty/__init__.py
Normal file
1
bot/handlers/inline_empty/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .on_inline_empty import router
|
||||||
23
bot/handlers/inline_empty/on_inline_empty.py
Normal file
23
bot/handlers/inline_empty/on_inline_empty.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from aiogram import Router, Bot, F
|
||||||
|
from aiogram.types import (
|
||||||
|
InlineQuery, InputTextMessageContent, InlineQueryResultArticle
|
||||||
|
)
|
||||||
|
from bot.keyboards.inline.full_menu import get_full_menu_kb
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@router.inline_query(F.query == '')
|
||||||
|
async def empty_inline_query(inline_query: InlineQuery, bot: Bot):
|
||||||
|
await inline_query.answer(
|
||||||
|
[
|
||||||
|
InlineQueryResultArticle(
|
||||||
|
id='show_menu',
|
||||||
|
title='⚙️ Open menu',
|
||||||
|
input_message_content=InputTextMessageContent(
|
||||||
|
message_text='⚙️ Menu'
|
||||||
|
),
|
||||||
|
reply_markup=get_full_menu_kb()
|
||||||
|
)
|
||||||
|
], cache_time=0
|
||||||
|
)
|
||||||
1
bot/handlers/on_chosen/__init__.py
Normal file
1
bot/handlers/on_chosen/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .spotify import router
|
||||||
35
bot/handlers/on_chosen/spotify.py
Normal file
35
bot/handlers/on_chosen/spotify.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
from aiogram import Router, Bot, F
|
||||||
|
from aiogram.types import (
|
||||||
|
FSInputFile, URLInputFile, InputMediaAudio,
|
||||||
|
ChosenInlineResult,
|
||||||
|
)
|
||||||
|
|
||||||
|
from bot.modules.spotify import spotify
|
||||||
|
from rich import print
|
||||||
|
from bot.utils.config import config
|
||||||
|
from bot.modules.database import db
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@router.chosen_inline_result(F.result_id.startswith('spot::'))
|
||||||
|
async def on_new_chosen(chosen_result: ChosenInlineResult, bot: Bot):
|
||||||
|
print('TEST: DOWNLOADING NEW')
|
||||||
|
song = spotify.songs.from_id(chosen_result.result_id.removeprefix('spot::'))
|
||||||
|
|
||||||
|
audio = await bot.send_audio(
|
||||||
|
chat_id=config.telegram.files_chat,
|
||||||
|
audio=FSInputFile('tests/test.mp3'),
|
||||||
|
thumbnail=URLInputFile(song.thumbnail),
|
||||||
|
performer=song.all_artists,
|
||||||
|
title=song.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.spotify[song.id] = audio.audio.file_id
|
||||||
|
await db.occasionally_write()
|
||||||
|
|
||||||
|
await bot.edit_message_media(
|
||||||
|
inline_message_id=chosen_result.inline_message_id,
|
||||||
|
media=InputMediaAudio(media=audio.audio.file_id),
|
||||||
|
reply_markup=None
|
||||||
|
)
|
||||||
22
bot/keyboards/inline/full_menu.py
Normal file
22
bot/keyboards/inline/full_menu.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from aiogram.utils.keyboard import (InlineKeyboardMarkup, InlineKeyboardButton,
|
||||||
|
InlineKeyboardBuilder)
|
||||||
|
from bot.callbacks.factories.full_menu import FullMenuCallback
|
||||||
|
|
||||||
|
|
||||||
|
def get_full_menu_kb() -> InlineKeyboardMarkup:
|
||||||
|
buttons = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text='⚙️ Settings',
|
||||||
|
callback_data=FullMenuCallback(
|
||||||
|
action='settings'
|
||||||
|
).pack()
|
||||||
|
),
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text='🎵 Search in SoundCloud',
|
||||||
|
switch_inline_query_current_chat='sc::'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
return InlineKeyboardBuilder(buttons).as_markup()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
from .private_button import PrivateButtonMiddleware
|
||||||
|
from .save_chosen import SaveChosenMiddleware
|
||||||
|
|||||||
19
bot/middlewares/private_button.py
Normal file
19
bot/middlewares/private_button.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
||||||
|
from aiogram.types import CallbackQuery
|
||||||
|
|
||||||
|
from typing import Any, Awaitable, Callable, Dict
|
||||||
|
|
||||||
|
from bot.modules.database import db
|
||||||
|
|
||||||
|
|
||||||
|
class PrivateButtonMiddleware(BaseMiddleware):
|
||||||
|
async def __call__(
|
||||||
|
self,
|
||||||
|
handler: Callable[[CallbackQuery, Dict[str, Any]], Awaitable[Any]],
|
||||||
|
event: CallbackQuery,
|
||||||
|
data: Dict[str, Any],
|
||||||
|
):
|
||||||
|
if event.from_user.id == db.inline[event.inline_message_id].from_user.id:
|
||||||
|
return await handler(event, data)
|
||||||
|
else:
|
||||||
|
await event.answer('This button is not for you')
|
||||||
46
bot/middlewares/save_chosen.py
Normal file
46
bot/middlewares/save_chosen.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
||||||
|
from aiogram.types import ChosenInlineResult
|
||||||
|
|
||||||
|
from typing import Any, Awaitable, Callable, Dict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from bot.modules.database import db
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SavedUser:
|
||||||
|
id: int
|
||||||
|
first_name: str
|
||||||
|
last_name: str | None
|
||||||
|
username: str | None
|
||||||
|
language_code: str | None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SavedResult:
|
||||||
|
result_id: str
|
||||||
|
from_user: SavedUser
|
||||||
|
query: str
|
||||||
|
inline_message_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class SaveChosenMiddleware(BaseMiddleware):
|
||||||
|
async def __call__(
|
||||||
|
self,
|
||||||
|
handler: Callable[[ChosenInlineResult, Dict[str, Any]], Awaitable[Any]],
|
||||||
|
event: ChosenInlineResult,
|
||||||
|
data: Dict[str, Any],
|
||||||
|
):
|
||||||
|
db.inline[event.inline_message_id] = SavedResult(
|
||||||
|
result_id=event.result_id,
|
||||||
|
from_user=SavedUser(
|
||||||
|
id=event.from_user.id,
|
||||||
|
first_name=event.from_user.first_name,
|
||||||
|
last_name=event.from_user.last_name,
|
||||||
|
username=event.from_user.username,
|
||||||
|
language_code=event.from_user.language_code
|
||||||
|
),
|
||||||
|
query=event.query,
|
||||||
|
inline_message_id=event.inline_message_id
|
||||||
|
)
|
||||||
|
return await handler(event, data)
|
||||||
@@ -2,6 +2,8 @@ from .db_model import DBDict
|
|||||||
import os.path
|
import os.path
|
||||||
from bot.utils.config import config
|
from bot.utils.config import config
|
||||||
|
|
||||||
|
from random import randint
|
||||||
|
|
||||||
DB = os.path.join(config.local.db_path, 'db')
|
DB = os.path.join(config.local.db_path, 'db')
|
||||||
|
|
||||||
if not os.path.isfile(DB):
|
if not os.path.isfile(DB):
|
||||||
@@ -12,6 +14,12 @@ class Db(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.fsm = DBDict('fsm')
|
self.fsm = DBDict('fsm')
|
||||||
self.config = DBDict('config')
|
self.config = DBDict('config')
|
||||||
|
self.inline = DBDict('inline')
|
||||||
|
self.spotify = DBDict('spotify')
|
||||||
|
|
||||||
async def write(self):
|
async def write(self):
|
||||||
await self.config.write()
|
await self.config.write()
|
||||||
|
|
||||||
|
async def occasionally_write(self, chance: int = 5):
|
||||||
|
if randint(1, chance) == 1:
|
||||||
|
await self.write()
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ class SongItem:
|
|||||||
def all_artists(self):
|
def all_artists(self):
|
||||||
return ', '.join(self.artists)
|
return ', '.join(self.artists)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def full_name(self):
|
||||||
|
return f"{self.all_artists} - {self.name}"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{', '.join(self.artists)} - {self.name}"
|
return f"{', '.join(self.artists)} - {self.name}"
|
||||||
|
|
||||||
@@ -39,3 +43,11 @@ class Songs(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
return [SongItem.from_spotify(item) for item in r['tracks']['items']]
|
return [SongItem.from_spotify(item) for item in r['tracks']['items']]
|
||||||
|
|
||||||
|
def from_id(self, song_id: str) -> SongItem | None:
|
||||||
|
r = self.spotify.track(song_id)
|
||||||
|
|
||||||
|
if r is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return SongItem.from_spotify(r)
|
||||||
|
|||||||
0
bot/modules/youtube/__init__.py
Normal file
0
bot/modules/youtube/__init__.py
Normal file
Reference in New Issue
Block a user