Advertisement
Guest User

LyricsCommands

a guest
Sep 17th, 2018
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 18.96 KB | None | 0 0
  1. from .utils.chat_formatting import pagify
  2. from discord.ext import commands
  3. from tabulate import tabulate
  4. from .utils.dataIO import dataIO
  5. try:
  6.     from bs4 import BeautifulSoup
  7.     soupAvailable = True
  8. except:
  9.     soupAvailable = False
  10. import discord
  11. import aiohttp
  12. import requests
  13. import asyncio
  14. import os
  15.  
  16.  
  17. class Genius:
  18.  
  19.     def __init__(self, bot):
  20.         self.bot = bot
  21.         self.JSON = "data/Sitryk-Cogs/genius/settings.json"
  22.         self.settings = dataIO.load_json(self.JSON)
  23.  
  24.     def save_settings(self):
  25.         dataIO.save_json(self.JSON, self.settings)
  26.  
  27.  
  28.     async def _update_event(self, method: str, ctx, data):
  29.         self.bot.dispatch('genius_event', method, ctx, data)
  30.  
  31.     def _get_settings(self, ctx):
  32.         server = ctx.message.server
  33.         channel = ctx.message.channel
  34.         if server.id not in self.settings:
  35.             return DEFAULT
  36.         else:
  37.             return {"CHANNEL": self.settings[server.id]}
  38.  
  39.     def _data_check(self, ctx):
  40.         server = ctx.message.server
  41.         channel = ctx.message.channel
  42.         if server.id not in self.settings:
  43.             self.settings[server.id] = DEFAULT
  44.             self.save_settings()
  45.  
  46. # Setting related commands
  47.  
  48.     @commands.group(pass_context=True)
  49.     async def lyricset(self, ctx):
  50.         """Change lyric related settings"""
  51.         if ctx.invoked_subcommand is None:
  52.             self._data_check(ctx)
  53.             await self.bot.send_cmd_help(ctx)
  54.  
  55.     @lyricset.command(pass_context=True)
  56.     async def channel(self, ctx, *, channel_name):
  57.         """Set the channel for lyrics to be sent to
  58.       Note: to reset default channel to DMs enter dms
  59.       """
  60.         self._data_check(ctx)
  61.         server = ctx.message.server
  62.         channel = discord.utils.find(lambda c: c.name.lower() == channel_name.lower(), server.channels)
  63.         if channel:
  64.             self.settings[server.id] = channel.id
  65.         elif not channel and channel_name.lower() == "dms":
  66.             self.settings[server.id] = None
  67.         else:
  68.             return
  69.         self.save_settings()
  70.  
  71.  
  72.     # @lyricset.command(pass_context=True, no_pm=True)
  73.     # async def autolyrics(self, ctx):
  74.     #     """Toggle the autolyrics feature"""
  75.     #     self._data_check(ctx)
  76.     #     server = ctx.message.server
  77.     #     channel = ctx.message.channel
  78.     #     AudioCog = self.bot.get_cog('Audio')
  79.     #     if not AudioCog:
  80.     #         self.settings[server.id]["AUTOLYRICS"] = False
  81.     #         self.save_settings()
  82.     #         await self.bot.say("You do not have the audio cog loaded.\n"
  83.     #                            "Load the audio cog to enable this setting.")
  84.     #         return
  85.     #     else:
  86.     #         self.settings[server.id]["AUTOLYRICS"] = not self.settings[server.id]["AUTOLYRICS"]
  87.     #         self.save_settings()
  88.     #         if self.settings[server.id]["AUTOLYRICS"]:
  89.     #             await self.bot.say("I will now recommend lyrics depending on what is playing"
  90.     #                                " from the audio cog")
  91.     #         else:
  92.     #             await self.bot.say("I will no longer recommend lyrics when audio is playing")
  93.  
  94. # Base commands start
  95.  
  96.     @commands.command(pass_context=True)
  97.     async def lyrics(self, ctx, *, query: str):
  98.         """Used to fetch lyrics from a search query
  99.       Usage: [p]lyrics white ferrari
  100.              [p]lyrics syrup sandwiches
  101.       Tip: You can use '[p]lyrics now playing' to
  102.       search for what's currently playing in audio
  103.       """
  104.  
  105.         server = ctx.message.server
  106.         author = ctx.message.author
  107.         self._data_check(ctx)
  108.  
  109.         AudioCog = self.bot.get_cog('Audio')
  110.         if AudioCog:
  111.             if query in ("now playing", "audio", "current", "--p") and AudioCog.is_playing(server):
  112.                 query = AudioCog._get_queue_nowplaying(server).title
  113.  
  114.         data = await genius_search(query)
  115.  
  116.         if len(data) < 1:
  117.             desc = "There were no results for {}".format(query)
  118.             e = discord.Embed(description=desc, colour=discord.Colour.dark_red())
  119.             await self.bot.say(embed=e)
  120.             return
  121.  
  122.  
  123.         items = ""
  124.         for item in data:
  125.             items += "**{}.** {} - {}\n\n".format(item,
  126.                                                   data[item]['title'],
  127.                                                   data[item]['artist']['name']
  128.                                                   )
  129.  
  130.         authdesc = "Genius"
  131.         footdesc = "Results based on search for: {}".format(query)
  132.  
  133.         choices = discord.Embed(description= items,
  134.                                 colour= discord.Color.green()
  135.                                 )
  136.         choices.set_author(name=authdesc, icon_url=geniusicon)
  137.         choices.set_footer(text=footdesc)
  138.  
  139.         try:
  140.             sent = await self.bot.say(embed=choices)
  141.         except discord.errors.Forbidden:
  142.             await self.bot.say("I need the `Embed Messages` Permission")
  143.             return
  144.  
  145.  
  146.         def check(msg):
  147.             content = msg.content
  148.             if content.isdigit() and int(content) in range(0, len(items)+1):
  149.                 return msg
  150.  
  151.         choice = await self.bot.wait_for_message(timeout= 20, author= author,
  152.                                                  check= check, channel= sent.channel)
  153.  
  154.         if choice is None or choice.content == '0':
  155.             e = discord.Embed(description= "Cancelled", colour= discord.Colour.dark_red())
  156.             await self.bot.edit_message(sent, embed=e)
  157.             del(e)
  158.             return
  159.         else:
  160.             choice = int(choice.content)
  161.  
  162.             destination = self.bot.get_channel(self._get_settings(ctx)["CHANNEL"])
  163.             if destination is None:
  164.                 destination = author
  165.  
  166.             song = data[choice]['url']
  167.             lyrics = await lyrics_from_path(song)
  168.             lyrics = pagify(lyrics)
  169.  
  170.  
  171.             t = data[choice]['title']
  172.             a = data[choice]['artist']['name']
  173.  
  174.             e = discord.Embed(colour=16776960) # Aesthetics
  175.             e.set_author(name="Requested lyrics for {} - {}".format(t, a), icon_url=loadgif)
  176.             await self.bot.edit_message(sent, embed=e)
  177.             del(e)
  178.  
  179.             e = discord.Embed(colour=discord.Colour.green()) # Aesthetics
  180.             e.set_author(name="Here are the lyrics for {} - {}".format(t, a), icon_url=geniusicon)
  181.             await self.bot.send_message(destination, embed=e)
  182.             del(e)
  183.  
  184.             for page in lyrics: # Send the lyrics
  185.                 await self.bot.send_message(destination, page)
  186.  
  187.             e = discord.Embed(colour=discord.Colour.green()) # Aesthetics
  188.             e.set_author(name="Sent lyrics for {} - {}".format(t, a), icon_url=greentick)
  189.             await self.bot.edit_message(sent, embed=e)
  190.             del(e)
  191.  
  192.     @commands.command(pass_context=True)
  193.     async def genius(self, ctx, *, query: str):
  194.         """Used to fetch items from a search query
  195.       Usage: [p]genius Childish Gambino
  196.              [p]genius Kendrick Lamar
  197.       """
  198.         channel = ctx.message.channel
  199.         server = ctx.message.server
  200.         author = ctx.message.author
  201.         self._data_check(ctx)
  202.  
  203.         bool_convert = {True: 'Yes',
  204.                         False: 'No'
  205.                         }
  206.  
  207.         AudioCog = self.bot.get_cog('Audio')
  208.         if AudioCog:
  209.             if query in ("now playing", "audio", "playing", "current") and AudioCog.isplaying(server):
  210.                 query = AudioCog._get_queue_nowplaying(server).title
  211.  
  212.         data = await genius_search(query)
  213.         embeds = []
  214.  
  215.         song_selection = ""
  216.         for item in data:
  217.  
  218.             stats = data[item]['stats']
  219.             artist = data[item]['artist']
  220.  
  221.             iq = artist['iq']
  222.             views = stats['views']
  223.             artist_name = artist['name']
  224.             song_type = data[item]['type'].title()
  225.             title = data[item]['full title']
  226.             hot = bool_convert[stats['hot']]
  227.             verified = bool_convert[artist['verified']]
  228.  
  229.  
  230.             # text = ("**Primary Artist:**  {}\n"
  231.             #         "**Title:**                    {}\n" # I know this is super ugly but it deals with embed spacing issues
  232.             #         "**IQ:**                         {}\n"
  233.             #         "**Verified:**              {}\n"
  234.             #         "**Views:**                  {}\n"
  235.             #         "**Hot:**                       {}\n"
  236.             #         "**Type:**                    {}".format(artist_name, title, iq, verified, views, hot, song_type))
  237.  
  238.             e = discord.Embed(colour=discord.Colour.green())
  239.             e.add_field(name="Title", value=title, inline=True)
  240.             e.add_field(name="Primary Artist", value=artist_name, inline=True)
  241.             e.add_field(name="IQ", value=iq, inline=True)
  242.             e.add_field(name="Verified", value=verified, inline=True)
  243.             e.add_field(name="Views", value=views, inline=True)
  244.             e.add_field(name="Hot", value=hot, inline=True)
  245.             e.add_field(name="Text Type", value=song_type, inline=True)
  246.             e.set_thumbnail(url=data[item]['song art'])
  247.             e.set_footer(text="Page {} - Search: {}".format(item, query))
  248.             embeds.append(e)
  249.  
  250.         await self.genius_menu(ctx, query_list=embeds, extra_data=data)
  251.  
  252. # Lunars menu control
  253.  
  254.     async def genius_menu(self, ctx, query_list: list, extra_data: dict,
  255.                           message: discord.Message=None,
  256.                           page=0, timeout: int=30):
  257.         """
  258.       Menu control logic for this credited to
  259.       https://github.com/Lunar-Dust/Dusty-Cogs/blob/master/menu/menu.py
  260.       """
  261.  
  262.         key = page+1
  263.         title = extra_data[key]['title']
  264.         artist = extra_data[key]['artist']['name']
  265.         author = ctx.message.author
  266.         channel = ctx.message.channel
  267.         server = ctx.message.server
  268.  
  269.         query = query_list[page]
  270.  
  271.         if not message:
  272.             message = await self.bot.send_message(channel, embed=query)
  273.             await self.bot.add_reaction(message, "?")
  274.             await self.bot.add_reaction(message, "??")
  275.             await self.bot.add_reaction(message, "?")
  276.             #await self.bot.add_reaction(message, "?")
  277.             await self.bot.add_reaction(message, "?")
  278.         else:
  279.             message = await self.bot.edit_message(message, embed=query)
  280.  
  281.         react = await self.bot.wait_for_reaction(message=message,
  282.                                                  user=ctx.message.author,
  283.                                                  timeout=timeout,
  284.                                                  emoji=["?", "?", "?", "??", "?"]
  285.                                                  )
  286.         if react is None:
  287.             try:
  288.                 try:
  289.                     await self.bot.clear_reactions(message)
  290.                 except:
  291.                     await self.bot.remove_reaction(message, "?", self.bot.user)
  292.                     await self.bot.remove_reaction(message, "??", self.bot.user)
  293.                     await self.bot.remove_reaction(message, "?", self.bot.user)
  294.                     #await self.bot.remove_reaction(message, "?", self.bot.user)
  295.                     await self.bot.remove_reaction(message, "?", self.bot.user)
  296.             except:
  297.                 pass
  298.             return None
  299.  
  300.         reacts = {v: k for k, v in numbs.items()}
  301.         react = reacts[react.reaction.emoji]
  302.  
  303.         if react == "next":
  304.             page += 1
  305.             next_page = page % len(query_list)
  306.             try:
  307.                 await self.bot.remove_reaction(message, "?", author)
  308.             except:
  309.                 pass
  310.  
  311.             return await self.genius_menu(ctx, query_list, extra_data, message=message,
  312.                                           page=next_page, timeout=timeout)
  313.  
  314.         elif react == "back":
  315.             page -= 1
  316.             next_page = page % len(query_list)
  317.             try:
  318.                 await self.bot.remove_reaction(message, "?", author)
  319.             except:
  320.                 pass
  321.  
  322.             return await self.genius_menu(ctx, query_list, extra_data, message=message,
  323.                                           page=next_page, timeout=timeout)
  324.  
  325.         elif react == "request lyrics":
  326.             try:
  327.                 try:
  328.                     await self.bot.clear_reactions(message)
  329.                 except:
  330.                     await self.bot.remove_reaction(message, "?", self.bot.user)
  331.                     await self.bot.remove_reaction(message, "??", self.bot.user)
  332.                     await self.bot.remove_reaction(message, "?", self.bot.user)
  333.                     await self.bot.remove_reaction(message, "?", self.bot.user)
  334.                     await self.bot.remove_reaction(message, "?", self.bot.user)
  335.             except:
  336.                 pass
  337.  
  338.             e = discord.Embed(colour=16776960)
  339.             e.set_author(name="Requested lyrics for {} - {}".format(artist, title), icon_url=loadgif)
  340.             await self.bot.edit_message(message, embed= e)
  341.  
  342.             destination = self.bot.get_channel(self._get_settings(ctx)["CHANNEL"])
  343.             if destination is None:
  344.                 destination = author
  345.  
  346.             lyrics = await lyrics_from_path(extra_data[page+1]['url'])
  347.             lyrics = pagify(lyrics)
  348.             for p in lyrics:
  349.                 await self.bot.send_message(destination, p)
  350.  
  351.             e = discord.Embed(colour=discord.Colour.green())
  352.             e.set_author(name="Sent lyrics for {} - {}".format(artist, title), icon_url=greentick)
  353.             await self.bot.edit_message(message, embed=e)
  354.  
  355.         # elif react == "queue in audio":
  356.         #     AudioCog = self.bot.get_cog('Audio')
  357.         #     if not AudioCog:
  358.         #         e = discord.Embed(description="ERROR: Audio module not loaded", colour=discord.Colour.red())
  359.         #         await self.bot.edit_message(message, embed=e)
  360.         #         await self.bot.delete_message(message)
  361.         #         return
  362.  
  363.         #     search = extra_data[page+1]['full title']
  364.  
  365.         #     e = discord.Embed(colour=16776960)
  366.         #     e.set_author(name="Searching youtube for {}".format(search), icon_url=loadgif)
  367.         #     await self.bot.edit_message(message, embed= e)
  368.  
  369.  
  370.         #     try:
  371.         #         await self.bot.remove_reaction(message, "?", author)
  372.         #     except:
  373.         #         pass
  374.         #     try:
  375.         #         try:
  376.         #             await self.bot.clear_reactions(message)
  377.         #         except:
  378.         #             await self.bot.remove_reaction(message, "?", self.bot.user)
  379.         #             await self.bot.remove_reaction(message, "??", self.bot.user)
  380.         #             await self.bot.remove_reaction(message, "?", self.bot.user)
  381.         #             await self.bot.remove_reaction(message, "?", self.bot.user)
  382.         #             await self.bot.remove_reaction(message, "?", self.bot.user)
  383.         #     except:
  384.         #         pass
  385.         #     await ctx.invoke(AudioCog.play, ctx=ctx, url_or_search_terms=search)
  386.  
  387.         #     e = discord.Embed(colour=16776960)
  388.         #     e.set_author(name="Queued <youtube title> {}".format(search), icon_url=loadgif)
  389.         #     await self.bot.edit_message(message, embed= e)
  390.  
  391.         else:
  392.             return await self.bot.delete_message(message)
  393.  
  394.  
  395.  
  396. # Constants
  397.  
  398. numbs = {
  399. "next": "?",
  400. "request lyrics" : "??",
  401. "queue in audio" : "?",
  402. "back": "?",
  403. "exit": "?"
  404.         }
  405.  
  406. DEFAULT = {"CHANNEL": None,
  407.            "AUTOLYRICS": False}
  408.  
  409. loadgif = "https://i.pinimg.com/originals/58/4b/60/584b607f5c2ff075429dc0e7b8d142ef.gif"
  410. greentick = "https://www.smhow.org.au/wp/wp-content/uploads/tick.png"
  411. geniusicon = "https://images.genius.com/8ed669cadd956443e29c70361ec4f372.1000x1000x1.png"
  412.  
  413. headers = {'Authorization': 'Bearer 2wjXkB5_rWzVnEFOKwFMWhJOwvNPAlFDTywyaRK0jc3gtrCZjx8CsaXjzcE-2_4j'}
  414. api_url = "https://api.genius.com"
  415.  
  416.  
  417. # Genius related functions
  418.  
  419. async def lyrics_from_path(path):
  420.     """Gets the lyrics from a song path"""
  421.  
  422.     with requests.get(path) as page:
  423.         html = BeautifulSoup(page.text, "html.parser")
  424.         [h.extract() for h in html('script')]
  425.         lyrics = html.find("div", class_="lyrics").get_text()
  426.         return lyrics
  427.  
  428.  
  429.  
  430.  
  431. async def genius_search(query:str):
  432.     """Get the data from the genius api"""
  433.  
  434.     search_url = api_url + "/search"
  435.     data = {'q': query}
  436.     json = None
  437.     async with aiohttp.get(search_url, data=data, headers=headers) as r:
  438.         json = await r.json()
  439.  
  440.     the_dict = {}
  441.     for index, hit in enumerate(json['response']['hits']):
  442.  
  443.         try:
  444.             iq = str(hit['result']['primary_artist']['iq'])
  445.         except KeyError:
  446.             iq = "0"
  447.         try:
  448.             views = str(hit['result']['stats']['pageviews'])
  449.         except KeyError:
  450.             views = "0"
  451.  
  452.  
  453.         the_dict[index+1] = {
  454.                             'type' : hit['type'],
  455.                             'api path' : hit['result']['api_path'],
  456.                             'annotations' : hit['result']['annotation_count'],
  457.                             'title' : hit['result']['title'],
  458.                             'full title' : hit['result']['full_title'],
  459.                             'header image' : hit['result']['header_image_url'],
  460.                             'url' : hit['result']['url'],
  461.                             'song art' : hit['result']['song_art_image_thumbnail_url'],
  462.                             'artist' : {'name' : hit['result']['primary_artist']['name'],
  463.                                         'url' : hit['result']['primary_artist']['url'],
  464.                                         'iq' : iq,
  465.                                         'meme verified' : hit['result']['primary_artist']['is_meme_verified'],
  466.                                         'verified' : hit['result']['primary_artist']['is_verified'],
  467.                                         'profile picture' : hit['result']['primary_artist']['image_url']
  468.                                         },
  469.                             'stats' : {
  470.                                        'hot' : hit['result']['stats']['hot'],
  471.                                        'views' : views
  472.                                         }
  473.                             }
  474.     return the_dict
  475.  
  476. # Cog setup
  477.  
  478. def check_folders():
  479.     path = "data/Sitryk-Cogs/genius"
  480.     if not os.path.exists(path):
  481.         print("Creating {} folder...".format(path))
  482.         os.makedirs(path)
  483.  
  484. def check_files():
  485.  
  486.     f = "data/Sitryk-Cogs/genius/settings.json"
  487.     if not dataIO.is_valid_json(f):
  488.         print("Creating default settings.json...")
  489.         dataIO.save_json(f, {})
  490.  
  491.  
  492. def setup(bot):
  493.     if soupAvailable:
  494.         check_folders()
  495.         check_files()
  496.         n = Genius(bot)
  497.         bot.add_cog(n)
  498.     else:
  499.         raise RuntimeError("You need to run `pip3 install beautifulsoup4`")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement