From 06fa1412106d7d153f0ef2f5ac80ecd008c068f3 Mon Sep 17 00:00:00 2001 From: Kevin Rode Date: Thu, 15 Jul 2021 00:36:37 -0400 Subject: [PATCH] First commit --- .gitignore | 7 ++ README.md | 5 ++ discordbot.py | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 discordbot.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f74aedb --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.cache* +config.ini +*.json +test.py +app.py +.flask_session/ +__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..86f94e2 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Collabify +## Description +Collabify is a discord bot designed to allow users to create collaborative playlists from a discord channel. Playlists are created and hosted on a central Spotify account as private collaborative playlists. There is a fork of this code that allows users to add songs from their personal accounts however this is not currently supported by the Spotify API. +## Installation +Collabify can be added to your server using the below link or run yourself. Ifd you are choosing to host your own Collabify you will need a spotify developer account. Once you have this an application can be created and the proper keys can be added to config.ini. An example layout of config.ini can be found in example.ini. diff --git a/discordbot.py b/discordbot.py new file mode 100644 index 0000000..01fe349 --- /dev/null +++ b/discordbot.py @@ -0,0 +1,177 @@ +from discord.ext import commands +from discord.utils import get +import spotipy, urllib, re, discord, json, os, asyncio, configparser +from spotipy.oauth2 import SpotifyOAuth +from spotipy import util + +client = discord.Client() +bot = commands.Bot(command_prefix='>') +bot.mapping = {} +bot.playlist = {} + +config = configparser.ConfigParser() +config.read('config.ini') + +def authenticate(): + scope = 'playlist-modify playlist-modify-private' + token = util.prompt_for_user_token( + username=config['spotify']['username'], + scope=scope, + client_id=config['spotify']['client_id'], + client_secret=config['spotify']['client_secret'], + redirect_uri=config['spotify']['redirect_uri'] + ) + return token + + +def addToPlaylist(authToken, username, playlistName, songName): + if authToken: + spotifyObject = spotipy.Spotify(authToken) + spotifyObject.trace = False + spotifyObject.user_playlist_add_tracks(username, playlistName, songName) + + +def lookupSong(authToken, *s): + s = s[0] + if authToken: + song_uri_list = [] + spotifyObject = spotipy.Spotify(authToken) + spotifyObject.trace = False + if 'https://open.spotify.com' in s: + song_uri = re.split('\w+:\/\/\w+.\w+.\w+\/\w+\/(\w+)',s) + song_uri_list.append(song_uri[1]) + else: + s_encode = urllib.parse.quote_plus(s) + ret = spotifyObject.search(s_encode, limit=5) + ret = ret['tracks']['items'] + if ret == []: + ret = spotifyObject.search(s_encode, limit=5, market='GB') + ret = ret['tracks']['items'] + for i in ret: + song_url = ret[ret.index(i)]['external_urls']['spotify'] + song_uri = re.split('\w+:\/\/\w+.\w+.\w+\/\w+\/(\w+)',song_url) + song_uri_list.append(song_uri[1]) + return song_uri_list + + +def createPlaylist(authToken, username, server): + sp = spotipy.Spotify(auth=authToken) + sp.user_playlist_create(username, name=str(server)) + + +def setPlaylist(authToken,server,username): + playlist_id = '' + sp = spotipy.Spotify(authToken) + playlists = sp.user_playlists(username) + for playlist in playlists['items']: + if playlist['name'] == server: + playlist_id = playlist['id'] + sp.playlist_change_details(playlist_id,public=False, collaborative=True) + return playlist_id + + +def deletePlaylist(authToken, playlist): + sp = spotipy.Spotify(authToken) + sp.user_playlist_unfollow(config['spotify']['username'],playlist) + + +@bot.event +async def on_ready(): + print('Logged in as') + print(bot.user.name) + print(bot.user.id) + print('-----') + activity = discord.Activity(name="type '>help' for help", type=discord.ActivityType.listening) + await bot.change_presence(activity=activity) + if os.path.isfile('playlist.json'): + j = open('playlist.json', "r") + bot.playlist = json.load(j) + + +@bot.command(brief='>resync',description='Forces the bot to resync with the local storage of users and playlists') +async def resync(ctx): + if os.path.isfile('playlist.json'): + f = open('playlist.json', "r") + bot.playlist = json.load(f) + + +@bot.command(brief='>removePlaylist', description='Removes the currently created playlist') +@commands.has_guild_permissions(administrator=True) +async def removePlaylist(ctx): + token = authenticate() + try: + deletePlaylist(token,bot.playlist[str(ctx.author.guild)]) + except KeyError: + pass + try: + del bot.playlist[str(ctx.author.guild)] + if os.path.isfile('playlist.json'): + with open('playlist.json',"w") as outfile: + json.dump(bot.playlist,outfile) + await ctx.send('Playlist deleted!') + except KeyError: + await ctx.send('This server does not have a playlist') + + +@bot.command(brief='>playlist',description='Use to specify the playlist that songs should be added to') +@commands.has_guild_permissions(administrator=True) +async def playlist(ctx): + try: + bot.playlist[str(ctx.author.guild)] + except KeyError: + token = authenticate() + createPlaylist(token, config['spotify']['username'],str(ctx.author.guild)) + bot.playlist[str(ctx.author.guild)] = setPlaylist(token,str(ctx.author.guild),config['spotify']['username']) + with open('playlist.json',"w") as outfile: + json.dump(bot.playlist,outfile) + await ctx.send('Playlist https://open.spotify.com/playlist/' + bot.playlist[str(ctx.author.guild)]+' has been created!') + return + await ctx.send('This server already has a playlist') + + +@bot.command(brief='>addsong [song_name]',description='Allows a user to add a song to the playlist') +async def addsong(ctx, *song): + if len(song) > 1: + song = ' '.join(song) + else: + song = song[0] + def check(m: discord.Message): # m = discord.Message. + return m.author.id == ctx.author.id and m.channel.id == ctx.channel.id + try: + authToken = authenticate() + except KeyError: + await ctx.send('You need to connect your account first with `>connect [account name]`') + return + uri_list = lookupSong(authToken,song) + i = 1 + for uri in uri_list: + await ctx.send(str(i)+': https://open.spotify.com/track/'+uri) + i+=1 + await ctx.send(f"***Send the number of the song to add to the playlist***") + try: + msg = await bot.wait_for('message', check=lambda message: message.author == ctx.author, timeout = 60.0) + except asyncio.TimeoutError: + # at this point, the check didn't become True, let's handle it. + await ctx.send(f"**{ctx.author}**, you didn't send any message that meets the check in this channel for 60 seconds..") + return + else: + if int(msg.content) == 1: + uri = uri_list[0] + elif int(msg.content) == 2: + uri = uri_list[1] + elif int(msg.content) == 3: + uri = uri_list[2] + elif int(msg.content) == 4: + uri = uri_list[3] + elif int(msg.content) == 5: + uri = uri_list[4] + try: + addToPlaylist(authToken, config['spotify']['username'], bot.playlist[str(ctx.author.guild)], [uri]) + except KeyError: + await ctx.send('Playlist must be set with `>playlist [spotify_url]`') + return + playlist_url = 'https://open.spotify.com/playlist/'+bot.playlist[str(ctx.author.guild)] + await ctx.send('Playlist '+playlist_url+' has been updated!') + + +bot.run(config['discord']['bot_token'])