mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
* More changes
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed auto play defaulting to playlist
Signed-off-by: Guy <guyreis96@gmail.com>
* Localtrack fix
Signed-off-by: Guy <guyreis96@gmail.com>
* Updated deps .. since for some reason aiosqlite is not being auto installed for everyone
Signed-off-by: Guy <guyreis96@gmail.com>
* Yupo
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed a crash in [p]now
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed crash on playlist save
Signed-off-by: Guy <guyreis96@gmail.com>
* Debugging Commit
Signed-off-by: Guy <guyreis96@gmail.com>
* Yet more prints
Signed-off-by: Guy <guyreis96@gmail.com>
* Even more spammy debug
Signed-off-by: Guy <guyreis96@gmail.com>
* Debugging commit + NEw Dispatches
Signed-off-by: Guy <guyreis96@gmail.com>
* Debugging commit + NEw Dispatches
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed localpath checks
Signed-off-by: Guy <guyreis96@gmail.com>
* more fixes for Localpaths
Signed-off-by: Guy <guyreis96@gmail.com>
* Spelling mistake on method
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed Crash on event handler
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed Crash on local search
Signed-off-by: Guy <guyreis96@gmail.com>
* Reduced fuzzy match percentage threshold for local tracks to account for nested folders
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed a crash on queue end
Signed-off-by: Guy <guyreis96@gmail.com>
* Sigh ... Removed a duplicate dispatch
Signed-off-by: Guy <guyreis96@gmail.com>
* Sigh i removed this before ...
Signed-off-by: Guy <guyreis96@gmail.com>
* Reorder dispatch signatures so all 3 new dispatch have matching signature
Signed-off-by: Guy <guyreis96@gmail.com>
* Formatting
Signed-off-by: Guy <guyreis96@gmail.com>
* Edited Error Event to support localtracks
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix a Crash on track crash :awesome:
Signed-off-by: Guy <guyreis96@gmail.com>
* Yikes soo much spam
Signed-off-by: Guy <guyreis96@gmail.com>
* Remove spam and improve existance check
Signed-off-by: Guy <guyreis96@gmail.com>
* Repeat and Auto-play are mutually exclusive now
Signed-off-by: Guy <guyreis96@gmail.com>
* DEBUGS for Preda
Signed-off-by: Guy <guyreis96@gmail.com>
* Vimeo tracks can be from both these domains "vimeo.com", "beam.pro"
Signed-off-by: Guy <guyreis96@gmail.com>
* I mean Mixer can be from those 2 domains ....
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed `search sc` command
Signed-off-by: Guy <guyreis96@gmail.com>
* Run everything though lints.
rename localtracks module to dataclasses
Clear lock on errors
Signed-off-by: Draper <guyreis96@gmail.com>
* Try to speed up long playlist loading
Signed-off-by: Draper <guyreis96@gmail.com>
* Im an idiot
Signed-off-by: Draper <guyreis96@gmail.com>
* Im an idiot
Signed-off-by: Draper <guyreis96@gmail.com>
* Added logging for writes
Signed-off-by: Draper <guyreis96@gmail.com>
* Fix crash on cog reload
Signed-off-by: Draper <guyreis96@gmail.com>
* Fix for runtimewarning ?
Signed-off-by: Draper <guyreis96@gmail.com>
* Fix for Local Track cache
Signed-off-by: Draper <guyreis96@gmail.com>
* Remove broken tracks from queue on exception
Theoretically do not auto play if track stop reason is Stopped or cleanup
Signed-off-by: Draper <guyreis96@gmail.com>
* Previous commit was a fluke ... ignore it
Signed-off-by: Draper <guyreis96@gmail.com>
* Change from cleanup to Replaced
Signed-off-by: Draper <guyreis96@gmail.com>
* Fixed AttributeError: 'Track' object has no attribute 'info'.
[p]skip will only work for autoplay is there a track being played.
Fixed Console spam if query saving failed in the background while reloading bot.
Autoplay now respect [p]stop command
Signed-off-by: Guy <guyreis96@gmail.com>
* Black formatting
Fix Issue with auto play working when there is songs in the queue
Stop notifying queue ended if autoplay is on
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed a crash on track load timeout
Signed-off-by: Guy <guyreis96@gmail.com>
* [p]playlist start will now show the playlist name in embed body
Improved Logic for handling broken tracks when repeat is on.
Signed-off-by: Draper <guyreis96@gmail.com>
* Enqueue tracks as soon as we have the youtube URL ....
This basically changes how spotify urls are handled
Need to test saving spotify playlist
Need to test loading a spotify playlist from file
Need to test enqueuing a spotify playlist
Signed-off-by: Draper <guyreis96@gmail.com>
* Updated a track whrn enqueuing spotify playlist
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Debug
Signed-off-by: Draper <guyreis96@gmail.com>
* Revert spotify_enqueue changes
Signed-off-by: Draper <guyreis96@gmail.com>
* Revert spotify_enqueue changes
Signed-off-by: Draper <guyreis96@gmail.com>
* Allow to set Lavalink jar version from Environment vars
Signed-off-by: Draper <guyreis96@gmail.com>
* Allow to set Lavalink jar version from Environment vars
Signed-off-by: Draper <guyreis96@gmail.com>
* Fix for a crash on Equalizer, Merge Spotify_enqueue changes and revert manager changes
Signed-off-by: Draper <guyreis96@gmail.com>
* Break playlist enqueue after 10 consecutive failures
Signed-off-by: Draper <guyreis96@gmail.com>
* Auto DC, is not compatible with Auto Play
Signed-off-by: Draper <guyreis96@gmail.com>
* Make notifier aware of guild its being called for
Signed-off-by: Draper <guyreis96@gmail.com>
* Type checking
Signed-off-by: Draper <guyreis96@gmail.com>
* Remove lock from 2 exits that i didn't before
Signed-off-by: Draper <guyreis96@gmail.com>
* Fixed TypeError: spotify_enqueue() got an unexpected keyword argument 'notify'
Signed-off-by: Guy <guyreis96@gmail.com>
* Reorder toggles to alphabetical order
Signed-off-by: Guy <guyreis96@gmail.com>
* Update Query to handle spotify URIs
Signed-off-by: Guy <guyreis96@gmail.com>
* update database
Signed-off-by: Guy <guyreis96@gmail.com>
* Dont say tracks enqued on invalid link
Make autop lay a mod only setting
Signed-off-by: Draper <guyreis96@gmail.com>
* Dont say tracks enqued on invalid spotify link
Signed-off-by: Draper <guyreis96@gmail.com>
* Set default age to 365 days
Signed-off-by: Draper <guyreis96@gmail.com>
* Allow Audio mods to set auto play playlists.
Save playlists songs to cache when migrating
Signed-off-by: Guy <guyreis96@gmail.com>
* Black formatting
Signed-off-by: Guy <guyreis96@gmail.com>
* [p]eq cooldown is not triggered is player check fails (i.e if nothing is currently playing)
Adding and removing reaction is no longer a blocking action
Signed-off-by: Guy <guyreis96@gmail.com>
* changelog for non blocking reaction handles
Signed-off-by: Guy <guyreis96@gmail.com>
* Show auto dc and auto play settings by default
Signed-off-by: Guy <guyreis96@gmail.com>
* lint is being a bitch
Signed-off-by: Guy <guyreis96@gmail.com>
* lint changes
Signed-off-by: Draper <guyreis96@gmail.com>
* stop caching local tracks
Signed-off-by: Draper <guyreis96@gmail.com>
* List of Lavalink.Tracks natively added to Playlist Objects
Signed-off-by: Draper <guyreis96@gmail.com>
* Fix UX changes and should fix autoplay
Signed-off-by: Draper <guyreis96@gmail.com>
* Fixed Skip x number of tracks
Signed-off-by: Draper <guyreis96@gmail.com>
* Lint changes
Signed-off-by: Draper <guyreis96@gmail.com>
* Remvoe dead code
Signed-off-by: Draper <guyreis96@gmail.com>
* Update playlist embed formatting to reflect Preda's suggestions
Signed-off-by: Draper <guyreis96@gmail.com>
* Update change logs
Signed-off-by: Draper <guyreis96@gmail.com>
* Add `async with ctx.typing():` to queue and to local folder
Signed-off-by: Draper <guyreis96@gmail.com>
* Stop queuing now when queue is empty with [p]queue
Signed-off-by: Draper <guyreis96@gmail.com>
* fix ctx.typing()
Signed-off-by: Draper <guyreis96@gmail.com>
* fix ctx.typing()
Signed-off-by: Draper <guyreis96@gmail.com>
* Part 1
Signed-off-by: Draper <guyreis96@gmail.com>
* Dont check local track author and name if title is Unknown
Signed-off-by: Guy <guyreis96@gmail.com>
* Makes auto play more random
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixes local play
Fixed missing format
Signed-off-by: Guy <guyreis96@gmail.com>
* Query.process_input accept lavalink.Track objects
Signed-off-by: Draper <guyreis96@gmail.com>
* docstrings
Signed-off-by: Draper <guyreis96@gmail.com>
* Add TODO for timestamp support
Signed-off-by: Draper <guyreis96@gmail.com>
* Improve autoplay from cache logic (possibly slightly slower but more efficient overall)
Signed-off-by: Draper <guyreis96@gmail.com>
* Add My Lavalink PR as a dependency
Remember to remove this .... The PR will bump it to 0.3.2
Signed-off-by: Draper <guyreis96@gmail.com>
* Add My Lavalink PR as a dependency
Remember to remove this .... The PR will bump it to 0.3.2
Signed-off-by: Draper <guyreis96@gmail.com>
* Add My Lavalink PR as a dependency
Remember to remove this .... The PR will bump it to 0.3.2
Signed-off-by: Draper <guyreis96@gmail.com>
* Compile all regex at runtime
Signed-off-by: Draper <guyreis96@gmail.com>
* Fixes local play
Fixed missing format
Signed-off-by: Guy <guyreis96@gmail.com>
* Revert Dep error
Signed-off-by: Guy <guyreis96@gmail.com>
* black
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed attribute error
Signed-off-by: Guy <guyreis96@gmail.com>
* add `self.bot.dispatch("audio_disconnect", ctx.guild)` dispatch when the player is disconnected
Signed-off-by: Guy <guyreis96@gmail.com>
* Removed shuffle lock on skip
Signed-off-by: Guy <guyreis96@gmail.com>
* Better logic for auto seek (timestamps)
Signed-off-by: Guy <guyreis96@gmail.com>
* Better logic for auto seek (timestamps)
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixes timestamps on spotify tracks
Signed-off-by: Guy <guyreis96@gmail.com>
* Add ctx typing to playlist enqueue
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix Deps
Signed-off-by: Guy <guyreis96@gmail.com>
* Black formatting + Using new lavalink methods for shuffling
Signed-off-by: Guy <guyreis96@gmail.com>
* remove ctx.typing from playlist start
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixes typerror when enqueuing spotify playlists
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix keyerror
Signed-off-by: Guy <guyreis96@gmail.com>
* black formatting, + embed for [p]audioset cache as I forgot it before
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix Error on playlist upload
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix Text help for bump
Signed-off-by: Guy <guyreis96@gmail.com>
* Allow track bumping while shuffle is on
Signed-off-by: Guy <guyreis96@gmail.com>
* Edit bump embed to be consistent with other embed
Hyperlink tracks and removed dynamic title
Signed-off-by: Guy <guyreis96@gmail.com>
* Black
Signed-off-by: Guy <guyreis96@gmail.com>
* Errors not printing fix?
Signed-off-by: Guy <guyreis96@gmail.com>
* Errors not printing fix?
Signed-off-by: Guy <guyreis96@gmail.com>
* Track enqueued footer now shows correct track position when shuffle is on
Signed-off-by: Guy <guyreis96@gmail.com>
* Update changelogs
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix is_owner check in audioset settings
Signed-off-by: Guy <guyreis96@gmail.com>
* Changelogs
Signed-off-by: Guy <guyreis96@gmail.com>
* Dont store searches with no results in cache, fix malformated playlist to cache upon settings migration
Signed-off-by: Guy <guyreis96@gmail.com>
* _clear_lock_on_error > Needs to be reviewed to see if it has been done correctly
Signed-off-by: Guy <guyreis96@gmail.com>
* _clear_lock_on_error > Needs to be reviewed to see if it has been done correctly
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix Query search so that it works with absolute paths for localtracks
Signed-off-by: Guy <guyreis96@gmail.com>
* Extra error if lavalink is set to external and the query is a localtrack and nothing is found
Signed-off-by: Guy <guyreis96@gmail.com>
* Black
Signed-off-by: Guy <guyreis96@gmail.com>
* More detailed error message
Signed-off-by: Guy <guyreis96@gmail.com>
* [p]seek and [p]skip can be used by user if they are the song requester while DJ mode is enabled, if votes are disabled. , [p]queue shuffle can be used to shuffle the queue manually. and [p]queue clean self can be used to remove all songs you requested from the queue.
Signed-off-by: Guy <guyreis96@gmail.com>
* black
Signed-off-by: Guy <guyreis96@gmail.com>
* All the fixes + a `should_auto_play` dispatch for the tech savy peeps
Signed-off-by: Guy <guyreis96@gmail.com>
* Spellchecker + Pythonic changes
Signed-off-by: Guy <guyreis96@gmail.com>
* NO spam for logs
Signed-off-by: Guy <guyreis96@gmail.com>
* Pass Current voice channel to `red_audio_should_auto_play` dispatch
Signed-off-by: Guy <guyreis96@gmail.com>
* Black
Signed-off-by: Guy <guyreis96@gmail.com>
* playlist upload also updates cache in the background
Signed-off-by: Guy <guyreis96@gmail.com>
* playlist upload also updates cache in the background
Signed-off-by: Guy <guyreis96@gmail.com>
* Add scope to playlist picker
Signed-off-by: Guy <guyreis96@gmail.com>
* Delete Playlist picker message once something is selected
Signed-off-by: Guy <guyreis96@gmail.com>
* OCD Fix
Signed-off-by: Guy <guyreis96@gmail.com>
* Facepalm
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix a Potential crash
Signed-off-by: Guy <guyreis96@gmail.com>
* Update my stupidity
Signed-off-by: Guy <guyreis96@gmail.com>
* Auto Pause + Skip tracks already in playlist upon playlist append + a command to remove duplicated tracks from playlist
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix DJ mode when Role is deleted - Credits go to Neuro Assassin#4779
Fix an issue where auto play MAY not trigger
Signed-off-by: Guy <guyreis96@gmail.com>
* Change log to Neuro Assassin#4779 fix
Signed-off-by: Guy <guyreis96@gmail.com>
* Black
Signed-off-by: Guy <guyreis96@gmail.com>
* Dont auto pause manual pauses
Signed-off-by: Guy <guyreis96@gmail.com>
* Adds `[p]autoplay` that can be run by mods or higher
Signed-off-by: Guy <guyreis96@gmail.com>
* 🤦
Signed-off-by: Guy <guyreis96@gmail.com>
* 2x 🤦
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed wrong import
Signed-off-by: Guy <guyreis96@gmail.com>
* Added Autoplay notify
Signed-off-by: Guy <guyreis96@gmail.com>
* Added Autoplay notify
Signed-off-by: Guy <guyreis96@gmail.com>
* Black
Signed-off-by: Guy <guyreis96@gmail.com>
* Store Track object as prev song instead of URI
Signed-off-by: Guy <guyreis96@gmail.com>
* Black why do u hate me
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix command name
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix Autoplay notify
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix missing await and TypeError, Thanks Flame
Signed-off-by: Guy <guyreis96@gmail.com>
* Add a list of tracks to show as a menu
Signed-off-by: Guy <guyreis96@gmail.com>
* adds the `[p]genre` command which uses the Spotify and Youtube API
Signed-off-by: Guy <guyreis96@gmail.com>
* Enqueue Playlists from genre command
Signed-off-by: Guy <guyreis96@gmail.com>
* Pretify `[p]genre`
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix a Typo and correct jukebox charge order
Signed-off-by: Guy <guyreis96@gmail.com>
* Add genre command to error handling
Signed-off-by: Guy <guyreis96@gmail.com>
* Type checking
Signed-off-by: Guy <guyreis96@gmail.com>
* Update naming scheme for `[p]genre`
Signed-off-by: Guy <guyreis96@gmail.com>
* Black why do you hate me
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixed `[p]local start`
Playlist picker auto selects if theres just 1 playlist found
`[p]queue cleanself` added
Signed-off-by: Guy <guyreis96@gmail.com>
* *sigh* back compatibility with old localtrack paths
Signed-off-by: Guy <guyreis96@gmail.com>
* *sigh* back compatibility with old localtrack paths, even more
Signed-off-by: Guy <guyreis96@gmail.com>
* *sigh* back compatibility with old localtrack paths Even more
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixes localtracks in playlist info command
Signed-off-by: Guy <guyreis96@gmail.com>
* Debug Local Strings
Signed-off-by: Guy <guyreis96@gmail.com>
* Debug Local Strings
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixes `[p]playlist info` for local tracks + fixed error in `[p]remove`
Signed-off-by: Guy <guyreis96@gmail.com>
* Black
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixes formatting in `[p]playlist info`
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix an issue with User Scope playlists were not being deleted
Signed-off-by: Guy <guyreis96@gmail.com>
* Typechecking
Signed-off-by: Guy <guyreis96@gmail.com>
* Black
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix the logic of `delegate_autoplay`
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix a Crash on Load due to type hinting
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix a Crash on Load due to type hintingBlack + fix order of `red_audio_should_auto_play`
Signed-off-by: Guy <guyreis96@gmail.com>
* Add `red_audio_initialized` dispatch so that ownership of auto play can be maintained after a reload
Signed-off-by: Guy <guyreis96@gmail.com>
* Check if the current owner is loaded before raising an error
Signed-off-by: Guy <guyreis96@gmail.com>
* Fixes the Existence Check in `delegate_autoplay`
Signed-off-by: Guy <guyreis96@gmail.com>
* Turns `own_autoplay` in a property of Audio and improves `delegate_autoplay` Thanks Sinbad!
Signed-off-by: Guy <guyreis96@gmail.com>
* Fix for Localtracks playlists
Signed-off-by: Guy <guyreis96@gmail.com>
* When disconnecting send `Disconnecting...`
Fix Stop after a skip
Fix UX discrepancy on Playlist IDs
Fixed Exception when theres a track error
Signed-off-by: Guy <guyreis96@gmail.com>
* add `on_red_audio_unload` dispatch
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Fix a crash on track start where `player.current` can be none?
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Missing new line
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Allow `--author` for playlist to be used to filter playlist for an specific author.
Plus a few bugfixes for UX
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Rename `remdupe` to `dedupe`
Make global scope always be referenced as Global
add missing backwards quotes around the Playlist ID for 1 string
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Towncrier entries for dep changes
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Remove track index when shuffle is on
Fix Progress bar for livestreams
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Trigger autoplay on `QUEUE_END` event instead of `TRACK_END`
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Can't reproduce Ians bug but here a safeguard agaisnt it just in case
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Fixes 2 Messages that had the wrong formatting
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* standerdize playlist naming scheme
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Fix `[p]autoplay` message when Notify is enabled
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* y u h8 me black
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Fix an issue with `[p]audioset localpath` where the localtracks folder was incorrect
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Pythonic formatting
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Ugh
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Fix a typo
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Silently try to delete messages + fixes error Ian found with `[p]genre`
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* sigh black
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Add humanize_number usage correctly
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Bump RLL to 0.4.0
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Update changelog entries
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Change `bot.db` to new API's added by #2967
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Additional reformatting
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Remove PyCharm noise + Fixes a few Pycharm warnings
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Rework `index` parsing for youtube urls
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Addess Aika's review
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Fix a potential crash, saves guild ID to playlists to avoid an scheme change in the future
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Add handling for Python installs without sqlite3.
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Address Flame's review
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Fix ma stupidity
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Address Aika's latest review.
1. Update docstring for `[p]playlist rename`.
2. Fix punctuation for playlist matching.
3. `[p]playlist update` now respect playlist management perms
4. Playlist management errors now shows playlist name, id and scope where possible
5. Remove duplicated code and dead code.
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
* Pluralize string
Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
496 lines
19 KiB
Python
496 lines
19 KiB
Python
import argparse
|
||
import functools
|
||
from typing import Optional, Tuple, Union
|
||
|
||
import discord
|
||
|
||
from redbot.core import Config, commands
|
||
from redbot.core.bot import Red
|
||
from redbot.core.i18n import Translator
|
||
|
||
from .playlists import PlaylistScope, standardize_scope
|
||
|
||
_ = Translator("Audio", __file__)
|
||
|
||
__all__ = [
|
||
"ComplexScopeParser",
|
||
"PlaylistConverter",
|
||
"ScopeParser",
|
||
"LazyGreedyConverter",
|
||
"standardize_scope",
|
||
"get_lazy_converter",
|
||
"get_playlist_converter",
|
||
]
|
||
|
||
_config = None
|
||
_bot = None
|
||
|
||
_SCOPE_HELP = """
|
||
Scope must be a valid version of one of the following:
|
||
Global
|
||
Guild
|
||
User
|
||
"""
|
||
_USER_HELP = """
|
||
Author must be a valid version of one of the following:
|
||
User ID
|
||
User Mention
|
||
User Name#123
|
||
"""
|
||
_GUILD_HELP = """
|
||
Guild must be a valid version of one of the following:
|
||
Guild ID
|
||
Exact guild name
|
||
"""
|
||
|
||
|
||
def _pass_config_to_converters(config: Config, bot: Red):
|
||
global _config, _bot
|
||
if _config is None:
|
||
_config = config
|
||
if _bot is None:
|
||
_bot = bot
|
||
|
||
|
||
class PlaylistConverter(commands.Converter):
|
||
async def convert(self, ctx: commands.Context, arg: str) -> dict:
|
||
global_scope = await _config.custom(PlaylistScope.GLOBAL.value).all()
|
||
guild_scope = await _config.custom(PlaylistScope.GUILD.value).all()
|
||
user_scope = await _config.custom(PlaylistScope.USER.value).all()
|
||
user_matches = [
|
||
(uid, pid, pdata)
|
||
for uid, data in user_scope.items()
|
||
for pid, pdata in data.items()
|
||
if arg == pid or arg.lower() in pdata.get("name", "").lower()
|
||
]
|
||
guild_matches = [
|
||
(gid, pid, pdata)
|
||
for gid, data in guild_scope.items()
|
||
for pid, pdata in data.items()
|
||
if arg == pid or arg.lower() in pdata.get("name", "").lower()
|
||
]
|
||
global_matches = [
|
||
(None, pid, pdata)
|
||
for pid, pdata in global_scope.items()
|
||
if arg == pid or arg.lower() in pdata.get("name", "").lower()
|
||
]
|
||
if not user_matches and not guild_matches and not global_matches:
|
||
raise commands.BadArgument(_("Could not match '{}' to a playlist.").format(arg))
|
||
|
||
return {
|
||
PlaylistScope.GLOBAL.value: global_matches,
|
||
PlaylistScope.GUILD.value: guild_matches,
|
||
PlaylistScope.USER.value: user_matches,
|
||
"arg": arg,
|
||
}
|
||
|
||
|
||
class NoExitParser(argparse.ArgumentParser):
|
||
def error(self, message):
|
||
raise commands.BadArgument()
|
||
|
||
|
||
class ScopeParser(commands.Converter):
|
||
async def convert(
|
||
self, ctx: commands.Context, argument: str
|
||
) -> Tuple[str, discord.User, Optional[discord.Guild], bool]:
|
||
target_scope: str = PlaylistScope.GUILD.value
|
||
target_user: Optional[Union[discord.Member, discord.User]] = ctx.author
|
||
target_guild: Optional[discord.Guild] = ctx.guild
|
||
specified_user = False
|
||
|
||
argument = argument.replace("—", "--")
|
||
|
||
command, *arguments = argument.split(" -- ")
|
||
if arguments:
|
||
argument = " -- ".join(arguments)
|
||
else:
|
||
command = None
|
||
|
||
parser = NoExitParser(description="Playlist Scope Parsing.", add_help=False)
|
||
|
||
parser.add_argument("--scope", nargs="*", dest="scope", default=[])
|
||
parser.add_argument("--guild", nargs="*", dest="guild", default=[])
|
||
parser.add_argument("--server", nargs="*", dest="guild", default=[])
|
||
parser.add_argument("--author", nargs="*", dest="author", default=[])
|
||
parser.add_argument("--user", nargs="*", dest="author", default=[])
|
||
parser.add_argument("--member", nargs="*", dest="author", default=[])
|
||
|
||
if not command:
|
||
parser.add_argument("command", nargs="*")
|
||
|
||
try:
|
||
vals = vars(parser.parse_args(argument.split()))
|
||
except Exception as exc:
|
||
raise commands.BadArgument() from exc
|
||
|
||
if vals["scope"]:
|
||
scope_raw = " ".join(vals["scope"]).strip()
|
||
scope = scope_raw.upper().strip()
|
||
valid_scopes = PlaylistScope.list() + [
|
||
"GLOBAL",
|
||
"GUILD",
|
||
"AUTHOR",
|
||
"USER",
|
||
"SERVER",
|
||
"MEMBER",
|
||
"BOT",
|
||
]
|
||
if scope not in valid_scopes:
|
||
raise commands.ArgParserFailure("--scope", scope_raw, custom_help=_SCOPE_HELP)
|
||
target_scope = standardize_scope(scope)
|
||
elif "--scope" in argument and not vals["scope"]:
|
||
raise commands.ArgParserFailure("--scope", "Nothing", custom_help=_SCOPE_HELP)
|
||
|
||
is_owner = await ctx.bot.is_owner(ctx.author)
|
||
guild = vals.get("guild", None) or vals.get("server", None)
|
||
if is_owner and guild:
|
||
target_guild = None
|
||
guild_raw = " ".join(guild).strip()
|
||
if guild_raw.isnumeric():
|
||
guild_raw = int(guild_raw)
|
||
try:
|
||
target_guild = ctx.bot.get_guild(guild_raw)
|
||
except Exception:
|
||
target_guild = None
|
||
guild_raw = str(guild_raw)
|
||
if target_guild is None:
|
||
try:
|
||
target_guild = await commands.GuildConverter.convert(ctx, guild_raw)
|
||
except Exception:
|
||
target_guild = None
|
||
if target_guild is None:
|
||
try:
|
||
target_guild = await ctx.bot.fetch_guild(guild_raw)
|
||
except Exception:
|
||
target_guild = None
|
||
if target_guild is None:
|
||
raise commands.ArgParserFailure("--guild", guild_raw, custom_help=_GUILD_HELP)
|
||
elif not is_owner and (guild or any(x in argument for x in ["--guild", "--server"])):
|
||
raise commands.BadArgument("You cannot use `--guild`")
|
||
elif any(x in argument for x in ["--guild", "--server"]):
|
||
raise commands.ArgParserFailure("--guild", "Nothing", custom_help=_GUILD_HELP)
|
||
|
||
author = vals.get("author", None) or vals.get("user", None) or vals.get("member", None)
|
||
if author:
|
||
target_user = None
|
||
user_raw = " ".join(author).strip()
|
||
if user_raw.isnumeric():
|
||
user_raw = int(user_raw)
|
||
try:
|
||
target_user = ctx.bot.get_user(user_raw)
|
||
except Exception:
|
||
target_user = None
|
||
user_raw = str(user_raw)
|
||
if target_user is None:
|
||
member_converter = commands.MemberConverter()
|
||
user_converter = commands.UserConverter()
|
||
try:
|
||
target_user = await member_converter.convert(ctx, user_raw)
|
||
except Exception:
|
||
try:
|
||
target_user = await user_converter.convert(ctx, user_raw)
|
||
except Exception:
|
||
target_user = None
|
||
if target_user is None:
|
||
try:
|
||
target_user = await ctx.bot.fetch_user(user_raw)
|
||
except Exception:
|
||
target_user = None
|
||
if target_user is None:
|
||
raise commands.ArgParserFailure("--author", user_raw, custom_help=_USER_HELP)
|
||
else:
|
||
specified_user = True
|
||
elif any(x in argument for x in ["--author", "--user", "--member"]):
|
||
raise commands.ArgParserFailure("--scope", "Nothing", custom_help=_USER_HELP)
|
||
|
||
return target_scope, target_user, target_guild, specified_user
|
||
|
||
|
||
class ComplexScopeParser(commands.Converter):
|
||
async def convert(
|
||
self, ctx: commands.Context, argument: str
|
||
) -> Tuple[
|
||
str,
|
||
discord.User,
|
||
Optional[discord.Guild],
|
||
bool,
|
||
str,
|
||
discord.User,
|
||
Optional[discord.Guild],
|
||
bool,
|
||
]:
|
||
|
||
target_scope: str = PlaylistScope.GUILD.value
|
||
target_user: Optional[Union[discord.Member, discord.User]] = ctx.author
|
||
target_guild: Optional[discord.Guild] = ctx.guild
|
||
specified_target_user = False
|
||
|
||
source_scope: str = PlaylistScope.GUILD.value
|
||
source_user: Optional[Union[discord.Member, discord.User]] = ctx.author
|
||
source_guild: Optional[discord.Guild] = ctx.guild
|
||
specified_source_user = False
|
||
|
||
argument = argument.replace("—", "--")
|
||
|
||
command, *arguments = argument.split(" -- ")
|
||
if arguments:
|
||
argument = " -- ".join(arguments)
|
||
else:
|
||
command = None
|
||
|
||
parser = NoExitParser(description="Playlist Scope Parsing.", add_help=False)
|
||
|
||
parser.add_argument("--to-scope", nargs="*", dest="to_scope", default=[])
|
||
parser.add_argument("--to-guild", nargs="*", dest="to_guild", default=[])
|
||
parser.add_argument("--to-server", nargs="*", dest="to_server", default=[])
|
||
parser.add_argument("--to-author", nargs="*", dest="to_author", default=[])
|
||
parser.add_argument("--to-user", nargs="*", dest="to_user", default=[])
|
||
parser.add_argument("--to-member", nargs="*", dest="to_member", default=[])
|
||
|
||
parser.add_argument("--from-scope", nargs="*", dest="from_scope", default=[])
|
||
parser.add_argument("--from-guild", nargs="*", dest="from_guild", default=[])
|
||
parser.add_argument("--from-server", nargs="*", dest="from_server", default=[])
|
||
parser.add_argument("--from-author", nargs="*", dest="from_author", default=[])
|
||
parser.add_argument("--from-user", nargs="*", dest="from_user", default=[])
|
||
parser.add_argument("--from-member", nargs="*", dest="from_member", default=[])
|
||
|
||
if not command:
|
||
parser.add_argument("command", nargs="*")
|
||
|
||
try:
|
||
vals = vars(parser.parse_args(argument.split()))
|
||
except Exception as exc:
|
||
raise commands.BadArgument() from exc
|
||
|
||
is_owner = await ctx.bot.is_owner(ctx.author)
|
||
valid_scopes = PlaylistScope.list() + [
|
||
"GLOBAL",
|
||
"GUILD",
|
||
"AUTHOR",
|
||
"USER",
|
||
"SERVER",
|
||
"MEMBER",
|
||
"BOT",
|
||
]
|
||
|
||
if vals["to_scope"]:
|
||
to_scope_raw = " ".join(vals["to_scope"]).strip()
|
||
to_scope = to_scope_raw.upper().strip()
|
||
if to_scope not in valid_scopes:
|
||
raise commands.ArgParserFailure(
|
||
"--to-scope", to_scope_raw, custom_help=_SCOPE_HELP
|
||
)
|
||
target_scope = standardize_scope(to_scope)
|
||
elif "--to-scope" in argument and not vals["to_scope"]:
|
||
raise commands.ArgParserFailure("--to-scope", "Nothing", custom_help=_SCOPE_HELP)
|
||
|
||
if vals["from_scope"]:
|
||
from_scope_raw = " ".join(vals["from_scope"]).strip()
|
||
from_scope = from_scope_raw.upper().strip()
|
||
|
||
if from_scope not in valid_scopes:
|
||
raise commands.ArgParserFailure(
|
||
"--from-scope", from_scope_raw, custom_help=_SCOPE_HELP
|
||
)
|
||
source_scope = standardize_scope(from_scope)
|
||
elif "--from-scope" in argument and not vals["to_scope"]:
|
||
raise commands.ArgParserFailure("--to-scope", "Nothing", custom_help=_SCOPE_HELP)
|
||
|
||
to_guild = vals.get("to_guild", None) or vals.get("to_server", None)
|
||
if is_owner and to_guild:
|
||
target_guild = None
|
||
to_guild_raw = " ".join(to_guild).strip()
|
||
if to_guild_raw.isnumeric():
|
||
to_guild_raw = int(to_guild_raw)
|
||
try:
|
||
target_guild = ctx.bot.get_guild(to_guild_raw)
|
||
except Exception:
|
||
target_guild = None
|
||
to_guild_raw = str(to_guild_raw)
|
||
if target_guild is None:
|
||
try:
|
||
target_guild = await commands.GuildConverter.convert(ctx, to_guild_raw)
|
||
except Exception:
|
||
target_guild = None
|
||
if target_guild is None:
|
||
try:
|
||
target_guild = await ctx.bot.fetch_guild(to_guild_raw)
|
||
except Exception:
|
||
target_guild = None
|
||
if target_guild is None:
|
||
raise commands.ArgParserFailure(
|
||
"--to-guild", to_guild_raw, custom_help=_GUILD_HELP
|
||
)
|
||
elif not is_owner and (
|
||
to_guild or any(x in argument for x in ["--to-guild", "--to-server"])
|
||
):
|
||
raise commands.BadArgument("You cannot use `--to-server`")
|
||
elif any(x in argument for x in ["--to-guild", "--to-server"]):
|
||
raise commands.ArgParserFailure("--to-server", "Nothing", custom_help=_GUILD_HELP)
|
||
|
||
from_guild = vals.get("from_guild", None) or vals.get("from_server", None)
|
||
if is_owner and from_guild:
|
||
source_guild = None
|
||
from_guild_raw = " ".join(from_guild).strip()
|
||
if from_guild_raw.isnumeric():
|
||
from_guild_raw = int(from_guild_raw)
|
||
try:
|
||
source_guild = ctx.bot.get_guild(from_guild_raw)
|
||
except Exception:
|
||
source_guild = None
|
||
from_guild_raw = str(from_guild_raw)
|
||
if source_guild is None:
|
||
try:
|
||
source_guild = await commands.GuildConverter.convert(ctx, from_guild_raw)
|
||
except Exception:
|
||
source_guild = None
|
||
if source_guild is None:
|
||
try:
|
||
source_guild = await ctx.bot.fetch_guild(from_guild_raw)
|
||
except Exception:
|
||
source_guild = None
|
||
if source_guild is None:
|
||
raise commands.ArgParserFailure(
|
||
"--from-guild", from_guild_raw, custom_help=_GUILD_HELP
|
||
)
|
||
elif not is_owner and (
|
||
from_guild or any(x in argument for x in ["--from-guild", "--from-server"])
|
||
):
|
||
raise commands.BadArgument("You cannot use `--from-server`")
|
||
elif any(x in argument for x in ["--from-guild", "--from-server"]):
|
||
raise commands.ArgParserFailure("--from-server", "Nothing", custom_help=_GUILD_HELP)
|
||
|
||
to_author = (
|
||
vals.get("to_author", None) or vals.get("to_user", None) or vals.get("to_member", None)
|
||
)
|
||
if to_author:
|
||
target_user = None
|
||
to_user_raw = " ".join(to_author).strip()
|
||
if to_user_raw.isnumeric():
|
||
to_user_raw = int(to_user_raw)
|
||
try:
|
||
source_user = ctx.bot.get_user(to_user_raw)
|
||
except Exception:
|
||
source_user = None
|
||
to_user_raw = str(to_user_raw)
|
||
if target_user is None:
|
||
member_converter = commands.MemberConverter()
|
||
user_converter = commands.UserConverter()
|
||
try:
|
||
target_user = await member_converter.convert(ctx, to_user_raw)
|
||
except Exception:
|
||
try:
|
||
target_user = await user_converter.convert(ctx, to_user_raw)
|
||
except Exception:
|
||
target_user = None
|
||
if target_user is None:
|
||
try:
|
||
target_user = await ctx.bot.fetch_user(to_user_raw)
|
||
except Exception:
|
||
target_user = None
|
||
if target_user is None:
|
||
raise commands.ArgParserFailure("--to-author", to_user_raw, custom_help=_USER_HELP)
|
||
else:
|
||
specified_target_user = True
|
||
elif any(x in argument for x in ["--to-author", "--to-user", "--to-member"]):
|
||
raise commands.ArgParserFailure("--to-user", "Nothing", custom_help=_USER_HELP)
|
||
|
||
from_author = (
|
||
vals.get("from_author", None)
|
||
or vals.get("from_user", None)
|
||
or vals.get("from_member", None)
|
||
)
|
||
if from_author:
|
||
source_user = None
|
||
from_user_raw = " ".join(from_author).strip()
|
||
if from_user_raw.isnumeric():
|
||
from_user_raw = int(from_user_raw)
|
||
try:
|
||
target_user = ctx.bot.get_user(from_user_raw)
|
||
except Exception:
|
||
source_user = None
|
||
from_user_raw = str(from_user_raw)
|
||
if source_user is None:
|
||
member_converter = commands.MemberConverter()
|
||
user_converter = commands.UserConverter()
|
||
try:
|
||
source_user = await member_converter.convert(ctx, from_user_raw)
|
||
except Exception:
|
||
try:
|
||
source_user = await user_converter.convert(ctx, from_user_raw)
|
||
except Exception:
|
||
source_user = None
|
||
if source_user is None:
|
||
try:
|
||
source_user = await ctx.bot.fetch_user(from_user_raw)
|
||
except Exception:
|
||
source_user = None
|
||
if source_user is None:
|
||
raise commands.ArgParserFailure(
|
||
"--from-author", from_user_raw, custom_help=_USER_HELP
|
||
)
|
||
else:
|
||
specified_source_user = True
|
||
elif any(x in argument for x in ["--from-author", "--from-user", "--from-member"]):
|
||
raise commands.ArgParserFailure("--from-user", "Nothing", custom_help=_USER_HELP)
|
||
|
||
return (
|
||
source_scope,
|
||
source_user,
|
||
source_guild,
|
||
specified_source_user,
|
||
target_scope,
|
||
target_user,
|
||
target_guild,
|
||
specified_target_user,
|
||
)
|
||
|
||
|
||
class LazyGreedyConverter(commands.Converter):
|
||
def __init__(self, splitter: str):
|
||
self.splitter_Value = splitter
|
||
|
||
async def convert(self, ctx: commands.Context, argument: str) -> str:
|
||
full_message = ctx.message.content.partition(f" {argument} ")
|
||
if len(full_message) == 1:
|
||
full_message = (
|
||
(argument if argument not in full_message else "") + " " + full_message[0]
|
||
)
|
||
elif len(full_message) > 1:
|
||
full_message = (
|
||
(argument if argument not in full_message else "") + " " + full_message[-1]
|
||
)
|
||
greedy_output = (" " + full_message.replace("—", "--")).partition(
|
||
f" {self.splitter_Value}"
|
||
)[0]
|
||
return f"{greedy_output}".strip()
|
||
|
||
|
||
def get_lazy_converter(splitter: str) -> type:
|
||
"""
|
||
Returns a typechecking safe `LazyGreedyConverter` suitable for use with discord.py.
|
||
"""
|
||
|
||
class PartialMeta(type(LazyGreedyConverter)):
|
||
__call__ = functools.partialmethod(type(LazyGreedyConverter).__call__, splitter)
|
||
|
||
class ValidatedConverter(LazyGreedyConverter, metaclass=PartialMeta):
|
||
pass
|
||
|
||
return ValidatedConverter
|
||
|
||
|
||
def get_playlist_converter() -> type:
|
||
"""
|
||
Returns a typechecking safe `PlaylistConverter` suitable for use with discord.py.
|
||
"""
|
||
|
||
class PartialMeta(type(PlaylistConverter)):
|
||
__call__ = functools.partialmethod(type(PlaylistConverter).__call__)
|
||
|
||
class ValidatedConverter(PlaylistConverter, metaclass=PartialMeta):
|
||
pass
|
||
|
||
return ValidatedConverter
|