-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
433 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,29 @@ | ||
# Spoyt | ||
|
||
Spotify to YouTube Discord link converter. | ||
Spotify to YouTube; Discord and Guilded link converter. | ||
|
||
## Usage | ||
|
||
Just send a message with share link from Spotify. Bot will automatically find | ||
the track in Spotify database, and search its name and artists in YouTube. | ||
If possible, it will try to delete your message, but you can disable it | ||
by permitting it by permissions. | ||
by permitting permissions. | ||
|
||
## Guilded | ||
Invite the bot by one of following links: | ||
- Discord: https://discord.com/api/oauth2/authorize?client_id=948274806325903410&permissions=3072&scope=bot | ||
- Guilded: https://www.guilded.gg/b/93177486-3a1d-4464-a202-1ddd6354844b | ||
|
||
Guilded version: [AnonymousX86/spoyt-guilded](https://github.com/AnonymousX86/spoyt-guilded) | ||
## Support | ||
|
||
You can join one of my servers (or both): | ||
|
||
- Discord: [discord.gg/SRdmrPpf2z](https://discord.gg/SRdmrPpf2z) | ||
- Guilded: [guilded.gg/Anonymous-Canteen](https://guilded.gg/Anonymous-Canteen) | ||
|
||
## How to run | ||
|
||
Make sure you have Python `>=3.8` installed. | ||
``` | ||
[py|python|python3] -(O|OO)m Spoyt.[Discord|Guilded] | ||
``` | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# -*- coding: utf-8 -*- | ||
from logging import INFO, basicConfig | ||
|
||
from discord import Activity, ActivityType, Client, Intents | ||
from rich.logging import RichHandler | ||
|
||
from Spoyt.logging import log | ||
from Spoyt.wrapper import main | ||
|
||
if __name__ == '__main__': | ||
basicConfig( | ||
level=INFO, | ||
format='%(message)s', | ||
datefmt='[%x]', | ||
handlers=[RichHandler(rich_tracebacks=True)] | ||
) | ||
log.info('Starting Discord bot') | ||
intents = Intents.default() | ||
intents.message_content = True | ||
client = Client( | ||
max_messages=None, | ||
intents=intents, | ||
activity=Activity( | ||
name='Spotify & YouTube', | ||
type=ActivityType.listening | ||
) | ||
) | ||
main(client, __package__) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# -*- coding: utf-8 -*- | ||
from logging import INFO, basicConfig | ||
|
||
from guilded import Client | ||
from rich.logging import RichHandler | ||
|
||
from Spoyt.logging import log | ||
from Spoyt.wrapper import main | ||
|
||
if __name__ == '__main__': | ||
basicConfig( | ||
level=INFO, | ||
format='%(message)s', | ||
datefmt='[%x]', | ||
handlers=[RichHandler(rich_tracebacks=True)] | ||
) | ||
log.info('Starting Guilded bot') | ||
client = Client() | ||
main(client, __package__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,17 @@ | ||
# -*- coding: utf-8 -*- | ||
from os import getenv | ||
from logging import INFO, basicConfig | ||
|
||
from discord import Client, Intents, Message, Embed, Color, \ | ||
ActivityType, Activity, NotFound, Forbidden | ||
|
||
from Spoyt.spotify_api import search_spotify, model_track, TrackEmbed | ||
from Spoyt.youtube_api import find_youtube_id | ||
|
||
|
||
def main(): | ||
intents = Intents.default() | ||
intents.message_content = True | ||
|
||
client = Client(intents=intents) | ||
client.youtube = None | ||
|
||
@client.event | ||
async def on_ready(): | ||
print(f'Logged in as {client.user}') | ||
await client.change_presence( | ||
activity=Activity( | ||
name='Spotify & YouTube', | ||
type=ActivityType.listening | ||
) | ||
) | ||
|
||
@client.event | ||
async def on_message(message: Message): | ||
if not message.content.startswith('https://open.spotify.com/track/'): | ||
return | ||
|
||
msg = await message.channel.send(embed=Embed( | ||
title=':hourglass_flowing_sand: Spotify link found!', | ||
description='Connecting to super secret database...', | ||
color=Color.green() | ||
)) | ||
|
||
track_id = message.content.split('?')[0].split('&')[0].split('/')[-1] | ||
spotify_query = search_spotify(track_id) | ||
|
||
if not spotify_query: | ||
await msg.edit(embed=Embed( | ||
title='Oh no', | ||
description='Spotify is out of service', | ||
color=Color.red() | ||
)) | ||
return | ||
|
||
track = model_track(spotify_query) | ||
track_embed = TrackEmbed(track) | ||
|
||
try: | ||
await message.delete() | ||
except Forbidden or NotFound: | ||
pass | ||
else: | ||
track_embed.add_author(message.author) | ||
|
||
await msg.edit(embeds=[track_embed, Embed( | ||
title=':hourglass_flowing_sand: Searching YouTube', | ||
color=Color.blurple() | ||
)]) | ||
|
||
video_id_found, result = find_youtube_id( | ||
query='{} {}'.format(track.name, ' '.join(track.artists)) | ||
) | ||
|
||
if not video_id_found: | ||
await msg.edit(embeds=[track_embed, Embed( | ||
title='Video not found', | ||
description=result, | ||
color=Color.dark_red() | ||
)]) | ||
return | ||
|
||
await msg.edit(embeds=[track_embed, Embed( | ||
title='Best YouTube result', | ||
color=Color.blurple() | ||
)]) | ||
|
||
await message.channel.send( | ||
content=f'https://www.youtube.com/watch?v={result}' | ||
) | ||
|
||
client.run(getenv('BOT_TOKEN')) | ||
from rich.logging import RichHandler | ||
|
||
from Spoyt.logging import log | ||
|
||
if __name__ == '__main__': | ||
main() | ||
basicConfig( | ||
level=INFO, | ||
format='%(message)s', | ||
datefmt='[%x]', | ||
handlers=[RichHandler(rich_tracebacks=True)] | ||
) | ||
log.info('This is general Spoyt module.') | ||
log.info('To run specific bot please run "Discord" or "Guilded" module.') | ||
log.info('Remember to set "BOT_TOKEN" environment variables.') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# -*- coding: utf-8 -*- | ||
from discord import Color as DiscordColor | ||
from guilded import Color as GuildedColor | ||
|
||
from Spoyt.env_check import is_discord, is_guilded | ||
|
||
if is_discord(): | ||
DEFAULT = DiscordColor.blurple() | ||
elif is_guilded(): | ||
DEFAULT = GuildedColor.gilded() | ||
else: | ||
DEFAULT = 0xCCCCCC | ||
|
||
GREEN = 0x2ECC71 | ||
RED = 0xFF0000 | ||
DARK_RED = 0x992D22 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# -*- coding: utf-8 -*- | ||
from Spoyt.embeds.definitions import BaseDiscordEmbed, BaseGuildedEmbed, EmbedDict | ||
from Spoyt.env_check import current_platform, is_discord, is_guilded | ||
from Spoyt.logging import log | ||
|
||
|
||
def create_embed(data: EmbedDict) -> BaseDiscordEmbed or BaseGuildedEmbed: | ||
embed = BaseDiscordEmbed if is_discord() else BaseGuildedEmbed if is_guilded else None | ||
if type(embed) is None: | ||
log.critical(f'Platform is "{current_platform()}" which is not good.') | ||
return | ||
|
||
embed = embed( | ||
title=data.title, | ||
description=data.description, | ||
color=data.color | ||
) | ||
for field in data.fields: | ||
embed.add_field( | ||
name=field.name, | ||
value=field.value, | ||
inline=field.inline | ||
) | ||
if url := data.thumbnail_url: | ||
embed.set_thumbnail(url=url) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
# -*- coding: utf-8 -*- | ||
from discord import Embed as DiscordEmbed, Color as DiscordColor | ||
from guilded import Embed as GuiledEmbed, Color as GuildedColor | ||
|
||
from Spoyt.embeds.color import DEFAULT, DARK_RED, GREEN, RED | ||
from Spoyt.env_check import is_discord, is_guilded | ||
from Spoyt.spotify_api import Track | ||
from Spoyt.youtube_api import YouTubeResult | ||
|
||
|
||
class BaseDiscordEmbed(DiscordEmbed): | ||
def __init__(self, *args, **kwargs) -> None: | ||
super().__init__(*args, **kwargs) | ||
if 'color' not in kwargs.keys(): | ||
self.color = DiscordColor.blurple() | ||
|
||
|
||
class BaseGuildedEmbed(GuiledEmbed): | ||
def __init__(self, *args, **kwargs) -> None: | ||
super().__init__(*args, **kwargs) | ||
if 'color' not in kwargs.keys(): | ||
self.color = GuildedColor.gilded() | ||
|
||
|
||
class EmbedField: | ||
def __init__( | ||
self, | ||
name: str, | ||
value: str, | ||
inline: bool = True | ||
) -> None: | ||
self.name = name | ||
self.value = value | ||
self.inline = inline | ||
|
||
|
||
class EmbedDict: | ||
def __init__( | ||
self, | ||
title: str = None, | ||
description: str = None, | ||
color: int = DEFAULT, | ||
fields: list[EmbedField] = [], | ||
thumbnail_url: str = None | ||
) -> None: | ||
self.title = title | ||
self.description = description | ||
self.color = color | ||
self.fields = fields | ||
self.thumbnail_url = thumbnail_url | ||
|
||
|
||
def markdown_url(url: str) -> str: | ||
return ( | ||
'<{0}>' if is_discord() else | ||
'[{0}]({0})' if is_guilded() else | ||
'{0}' | ||
).format(url) | ||
|
||
|
||
def track_to_embed(track: Track) -> EmbedDict: | ||
return EmbedDict( | ||
title=track.name, | ||
description=markdown_url(track.track_url), | ||
color=GREEN, | ||
fields=[ | ||
EmbedField( | ||
name='Artist{}'.format('' if track.is_single_artist else 's'), | ||
value=', '.join(track.artists), | ||
inline=track.is_single_artist | ||
), | ||
EmbedField( | ||
name='Released', | ||
value=track.release_date | ||
) | ||
], | ||
thumbnail_url=track.album_url | ||
) | ||
|
||
|
||
def video_to_embed(video: YouTubeResult) -> EmbedDict: | ||
return EmbedDict( | ||
title=video.title, | ||
description=markdown_url(video.video_link), | ||
fields=[ | ||
EmbedField( | ||
name='Description', | ||
value=video.description, | ||
inline=False | ||
), | ||
EmbedField( | ||
name='Published', | ||
value=video.published_date | ||
) | ||
], | ||
thumbnail_url=video.video_thumbnail | ||
) | ||
|
||
|
||
LINK_FOUND = EmbedDict( | ||
title='\u23f3 Spotify link found!', | ||
description='Connecting to super secret database\u2026', | ||
color=GREEN | ||
), | ||
|
||
SPOTIFY_UNREACHABLE = EmbedDict( | ||
title='Oh no', | ||
description='Spotify is out of service', | ||
color=RED | ||
), | ||
|
||
SEARCHING_YOUTUBE = EmbedDict( | ||
title='\u23f3 Searching YouTube' | ||
) | ||
|
||
VIDEO_NOT_FOUND = EmbedDict( | ||
title='Video not found', | ||
color=DARK_RED | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# -*- coding: utf-8 -*- | ||
from os import environ, getenv | ||
|
||
from Spoyt.logging import log | ||
|
||
|
||
def current_platform() -> str: | ||
return getenv('PLATFORM', 'unknown') | ||
|
||
|
||
def is_discord() -> bool: | ||
return current_platform() == 'discord' | ||
|
||
|
||
def is_guilded() -> bool: | ||
return current_platform() == 'guilded' | ||
|
||
|
||
def check_platform(source: str, platform: str) -> bool: | ||
if source.lower().endswith(platform.lower()): | ||
environ['PLATFORM'] = platform | ||
log.info(f'Automatically set "PLATFORM" to "{platform}"') | ||
return True | ||
return False | ||
|
||
|
||
def auto_set_platform(source: str) -> bool: | ||
for platform in ['guilded', 'discord']: | ||
if check_platform(source, platform): | ||
return True | ||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# -*- coding: utf-8 -*- | ||
from logging import getLogger | ||
|
||
log = getLogger('rich') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# -*- coding: utf-8 -*- | ||
from os import getenv | ||
|
||
|
||
def bot_token(): | ||
return getenv('BOT_TOKEN') |
Oops, something went wrong.