diff options
| author | 2019-02-08 07:04:56 +1000 | |
|---|---|---|
| committer | 2019-02-08 07:04:56 +1000 | |
| commit | 77b64074568910819cdb18306f70481d489417ca (patch) | |
| tree | a7f5cab8ca801878db4ef8b1641cdc6d63c01af1 | |
| parent | Merge branch 'master' into newline-antispam (diff) | |
| parent | Merge pull request #306 from beardfist/some_branch (diff) | |
Merge branch 'master' into newline-antispam
| -rw-r--r-- | bot/__main__.py | 1 | ||||
| -rw-r--r-- | bot/cogs/free.py | 29 | ||||
| -rw-r--r-- | bot/cogs/help.py | 6 | ||||
| -rw-r--r-- | bot/cogs/moderation.py | 51 | ||||
| -rw-r--r-- | bot/cogs/token_remover.py | 6 | ||||
| -rw-r--r-- | bot/constants.py | 12 | ||||
| -rw-r--r-- | bot/decorators.py | 14 | ||||
| -rw-r--r-- | bot/resources/stars.json | 82 | 
8 files changed, 141 insertions, 60 deletions
| diff --git a/bot/__main__.py b/bot/__main__.py index 581fa5c8e..c9d6fd61e 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -29,6 +29,7 @@ bot.http_session = ClientSession(  )  log.info("Waiting for RabbitMQ...") +  has_rmq = wait_for_rmq()  if has_rmq: diff --git a/bot/cogs/free.py b/bot/cogs/free.py index 620449f7e..fd6009bb8 100644 --- a/bot/cogs/free.py +++ b/bot/cogs/free.py @@ -2,10 +2,10 @@ import logging  from datetime import datetime  from discord import Colour, Embed, Member, utils -from discord.ext import commands -from discord.ext.commands import BucketType, Context, command, cooldown +from discord.ext.commands import Context, command -from bot.constants import Categories, Free, Roles +from bot.constants import Categories, Channels, Free, STAFF_ROLES +from bot.decorators import redirect_output  log = logging.getLogger(__name__) @@ -21,7 +21,7 @@ class Free:      PYTHON_HELP_ID = Categories.python_help      @command(name="free", aliases=('f',)) -    @cooldown(RATE, PER, BucketType.channel) +    @redirect_output(destination_channel=Channels.bot, bypass_roles=STAFF_ROLES)      async def free(self, ctx: Context, user: Member = None, seek: int = 2):          """          Lists free help channels by likeliness of availability. @@ -100,27 +100,6 @@ class Free:          await ctx.send(embed=embed) -    @free.error -    async def free_error(self, ctx: Context, error): -        """ -        If error raised is CommandOnCooldown, and the -        user who invoked has the helper role, reset -        the cooldown and reinvoke the command. - -        Otherwise log the error. -        """ -        helpers = ctx.guild.get_role(Roles.helpers) - -        if isinstance(error, commands.CommandOnCooldown): -            if helpers in ctx.author.roles: -                # reset cooldown so second invocation -                # doesn't bring us back here. -                ctx.command.reset_cooldown(ctx) -                # return to avoid needlessly logging the error -                return await ctx.reinvoke() - -        log.exception(error)  # Don't ignore other errors -  def setup(bot):      bot.add_cog(Free()) diff --git a/bot/cogs/help.py b/bot/cogs/help.py index 2168e2a60..20ed08f07 100644 --- a/bot/cogs/help.py +++ b/bot/cogs/help.py @@ -10,7 +10,7 @@ from discord.ext.commands import CheckFailure  from fuzzywuzzy import fuzz, process  from bot import constants -from bot.constants import Roles +from bot.constants import Channels, STAFF_ROLES  from bot.decorators import redirect_output  from bot.pagination import (      DELETE_EMOJI, FIRST_EMOJI, LAST_EMOJI, @@ -25,7 +25,7 @@ REACTIONS = {      LAST_EMOJI: 'end',      DELETE_EMOJI: 'stop'  } -STAFF_ROLES = Roles.helpers, Roles.moderator, Roles.admin, Roles.owner +  Cog = namedtuple('Cog', ['name', 'description', 'commands']) @@ -654,7 +654,7 @@ class Help:      Custom Embed Pagination Help feature      """      @commands.command('help') -    @redirect_output(constants.Channels.bot, STAFF_ROLES) +    @redirect_output(destination_channel=Channels.bot, bypass_roles=STAFF_ROLES)      async def new_help(self, ctx, *commands):          """          Shows Command Help. diff --git a/bot/cogs/moderation.py b/bot/cogs/moderation.py index 6b90d43ab..3c7f80a7a 100644 --- a/bot/cogs/moderation.py +++ b/bot/cogs/moderation.py @@ -43,6 +43,9 @@ def proxy_user(user_id: str) -> Object:      return user +UserTypes = Union[Member, User, proxy_user] + +  class Moderation(Scheduler):      """      Server moderation tools. @@ -74,7 +77,7 @@ class Moderation(Scheduler):      @with_role(*MODERATION_ROLES)      @command() -    async def warn(self, ctx: Context, user: Union[User, proxy_user], *, reason: str = None): +    async def warn(self, ctx: Context, user: UserTypes, *, reason: str = None):          """          Create a warning infraction in the database for a user. @@ -184,7 +187,7 @@ class Moderation(Scheduler):      @with_role(*MODERATION_ROLES)      @command() -    async def ban(self, ctx: Context, user: Union[User, proxy_user], *, reason: str = None): +    async def ban(self, ctx: Context, user: UserTypes, *, reason: str = None):          """          Create a permanent ban infraction in the database for a user. @@ -192,8 +195,7 @@ class Moderation(Scheduler):          **`reason`:** The reason for the ban.          """ -        member = ctx.guild.get_member(user.id) -        if not await self.respect_role_hierarchy(ctx, member, 'ban'): +        if not await self.respect_role_hierarchy(ctx, user, 'ban'):              # Ensure ctx author has a higher top role than the target user              # Warning is sent to ctx by the helper method              return @@ -371,7 +373,7 @@ class Moderation(Scheduler):      @with_role(*MODERATION_ROLES)      @command()      async def tempban( -        self, ctx: Context, user: Union[User, proxy_user], duration: str, *, reason: str = None +        self, ctx: Context, user: UserTypes, duration: str, *, reason: str = None      ):          """          Create a temporary ban infraction in the database for a user. @@ -381,8 +383,7 @@ class Moderation(Scheduler):          **`reason`:** The reason for the temporary ban.          """ -        member = ctx.guild.get_member(user.id) -        if not await self.respect_role_hierarchy(ctx, member, 'tempban'): +        if not await self.respect_role_hierarchy(ctx, user, 'tempban'):              # Ensure ctx author has a higher top role than the target user              # Warning is sent to ctx by the helper method              return @@ -450,7 +451,7 @@ class Moderation(Scheduler):      @with_role(*MODERATION_ROLES)      @command(hidden=True, aliases=['shadowwarn', 'swarn', 'shadow_warn']) -    async def note(self, ctx: Context, user: Union[User, proxy_user], *, reason: str = None): +    async def note(self, ctx: Context, user: UserTypes, *, reason: str = None):          """          Create a private infraction note in the database for a user. @@ -537,7 +538,7 @@ class Moderation(Scheduler):      @with_role(*MODERATION_ROLES)      @command(hidden=True, aliases=['shadowban', 'sban']) -    async def shadow_ban(self, ctx: Context, user: Union[User, proxy_user], *, reason: str = None): +    async def shadow_ban(self, ctx: Context, user: UserTypes, *, reason: str = None):          """          Create a permanent ban infraction in the database for a user. @@ -545,8 +546,7 @@ class Moderation(Scheduler):          **`reason`:** The reason for the ban.          """ -        member = ctx.guild.get_member(user.id) -        if not await self.respect_role_hierarchy(ctx, member, 'shadowban'): +        if not await self.respect_role_hierarchy(ctx, user, 'shadowban'):              # Ensure ctx author has a higher top role than the target user              # Warning is sent to ctx by the helper method              return @@ -680,7 +680,7 @@ class Moderation(Scheduler):      @with_role(*MODERATION_ROLES)      @command(hidden=True, aliases=["shadowtempban, stempban"])      async def shadow_tempban( -            self, ctx: Context, user: Union[User, proxy_user], duration: str, *, reason: str = None +            self, ctx: Context, user: UserTypes, duration: str, *, reason: str = None      ):          """          Create a temporary ban infraction in the database for a user. @@ -690,8 +690,7 @@ class Moderation(Scheduler):          **`reason`:** The reason for the temporary ban.          """ -        member = ctx.guild.get_member(user.id) -        if not await self.respect_role_hierarchy(ctx, member, 'shadowtempban'): +        if not await self.respect_role_hierarchy(ctx, user, 'shadowtempban'):              # Ensure ctx author has a higher top role than the target user              # Warning is sent to ctx by the helper method              return @@ -827,7 +826,7 @@ class Moderation(Scheduler):      @with_role(*MODERATION_ROLES)      @command() -    async def unban(self, ctx: Context, user: Union[User, proxy_user]): +    async def unban(self, ctx: Context, user: UserTypes):          """          Deactivates the active ban infraction for a user. @@ -1368,25 +1367,31 @@ class Moderation(Scheduler):              if User in error.converters:                  await ctx.send(str(error.errors[0])) -    async def respect_role_hierarchy(self, ctx: Context, target: Member, infraction_type: str) -> bool: +    async def respect_role_hierarchy(self, ctx: Context, target: UserTypes, infr_type: str) -> bool:          """ -        Check if the highest role of the invoking member is greater than that of the target member +        Check if the highest role of the invoking member is greater than that of the target member. +        If this check fails, a warning is sent to the invoking ctx. -        If this check fails, a warning is sent to the invoking ctx +        Returns True always if target is not a discord.Member instance. -        Implement as a method rather than a check in order to avoid having to reimplement parameter -        checks & conversions in a dedicated check decorater +        :param ctx: The command context when invoked. +        :param target: The target of the infraction. +        :param infr_type: The type of infraction.          """ +        if not isinstance(target, Member): +            return True +          actor = ctx.author          target_is_lower = target.top_role < actor.top_role          if not target_is_lower:              log.info( -                f"{actor} ({actor.id}) attempted to {infraction_type} " -                f"{target} ({target.id}), who has an equal or higher top role" +                f"{actor} ({actor.id}) attempted to {infr_type} " +                f"{target} ({target.id}), who has an equal or higher top role."              )              await ctx.send( -                f":x: {actor.mention}, you may not {infraction_type} someone with an equal or higher top role" +                f":x: {actor.mention}, you may not {infr_type} " +                "someone with an equal or higher top role."              )          return target_is_lower diff --git a/bot/cogs/token_remover.py b/bot/cogs/token_remover.py index c1a0e18ba..05298a2ff 100644 --- a/bot/cogs/token_remover.py +++ b/bot/cogs/token_remover.py @@ -17,9 +17,9 @@ log = logging.getLogger(__name__)  DELETION_MESSAGE_TEMPLATE = (      "Hey {mention}! I noticed you posted a seemingly valid Discord API "      "token in your message and have removed your message. " -    "We **strongly recommend** regenerating your token as it's probably " -    "been compromised. You can do that here: " -    "<https://discordapp.com/developers/applications/me>\n" +    "This means that your token has been **compromised**. " +    "Please change your token **immediately** at: " +    "<https://discordapp.com/developers/applications/me>\n\n"      "Feel free to re-post it with the token removed. "      "If you believe this was a mistake, please let us know!"  ) diff --git a/bot/constants.py b/bot/constants.py index f23696e4d..ab62cd79d 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -496,6 +496,9 @@ DEBUG_MODE = True if 'local' in os.environ.get("SITE_URL", "local") else False  BOT_DIR = os.path.dirname(__file__)  PROJECT_ROOT = os.path.abspath(os.path.join(BOT_DIR, os.pardir)) +# Default role combinations +STAFF_ROLES = Roles.helpers, Roles.moderator, Roles.admin, Roles.owner +  # Bot replies  NEGATIVE_REPLIES = [      "Noooooo!!", @@ -512,7 +515,9 @@ NEGATIVE_REPLIES = [      "Not in a million years.",      "Fat chance.",      "Certainly not.", -    "NEGATORY." +    "NEGATORY.", +    "Nuh-uh.", +    "Not in my house!",  ]  POSITIVE_REPLIES = [ @@ -532,7 +537,7 @@ POSITIVE_REPLIES = [      "ROGER THAT",      "Of course!",      "Aye aye, cap'n!", -    "I'll allow it." +    "I'll allow it.",  ]  ERROR_REPLIES = [ @@ -544,7 +549,8 @@ ERROR_REPLIES = [      "You blew it.",      "You're bad at computers.",      "Are you trying to kill me?", -    "Noooooo!!" +    "Noooooo!!", +    "I can't believe you've done this",  ] diff --git a/bot/decorators.py b/bot/decorators.py index ce54f28cd..1ba2cd59e 100644 --- a/bot/decorators.py +++ b/bot/decorators.py @@ -2,10 +2,12 @@ import logging  import random  import typing  from asyncio import Lock, sleep +from contextlib import suppress  from functools import wraps  from weakref import WeakValueDictionary  from discord import Colour, Embed +from discord.errors import NotFound  from discord.ext import commands  from discord.ext.commands import CheckFailure, Context @@ -122,7 +124,7 @@ def redirect_output(destination_channel: int, bypass_roles: typing.Container[int              redirect_channel = ctx.guild.get_channel(destination_channel)              old_channel = ctx.channel -            log.trace(f"Redirecting output of {ctx.author}'s command '{ctx.command.name}'' to {redirect_channel.name}") +            log.trace(f"Redirecting output of {ctx.author}'s command '{ctx.command.name}' to {redirect_channel.name}")              ctx.channel = redirect_channel              await ctx.channel.send(f"Here's the output of your command, {ctx.author.mention}")              await func(self, ctx, *args, **kwargs) @@ -134,7 +136,13 @@ def redirect_output(destination_channel: int, bypass_roles: typing.Container[int              if RedirectOutput.delete_invocation:                  await sleep(RedirectOutput.delete_delay) -                await message.delete() -                await ctx.message.delete() + +                with suppress(NotFound): +                    await message.delete() +                    log.trace("Redirect output: Deleted user redirection message") + +                with suppress(NotFound): +                    await ctx.message.delete() +                    log.trace("Redirect output: Deleted invocation message")          return inner      return wrap diff --git a/bot/resources/stars.json b/bot/resources/stars.json new file mode 100644 index 000000000..8071b9626 --- /dev/null +++ b/bot/resources/stars.json @@ -0,0 +1,82 @@ +{ +  "Adele": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Adele_2016.jpg/220px-Adele_2016.jpg", +  "Steven Tyler": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Steven_Tyler_by_Gage_Skidmore_3.jpg/220px-Steven_Tyler_by_Gage_Skidmore_3.jpg", +  "Alex Van Halen": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Alex_Van_Halen_-_Van_Halen_Live.jpg/220px-Alex_Van_Halen_-_Van_Halen_Live.jpg", +  "Aretha Franklin": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c6/Aretha_Franklin_1968.jpg/220px-Aretha_Franklin_1968.jpg", +  "Ayumi Hamasaki": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Ayumi_Hamasaki_2007.jpg/220px-Ayumi_Hamasaki_2007.jpg", +  "Koshi Inaba": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/af/B%27Z_at_Best_Buy_Theater_NYC_-_9-30-12_-_18.jpg/220px-B%27Z_at_Best_Buy_Theater_NYC_-_9-30-12_-_18.jpg", +  "Barbra Streisand": "https://upload.wikimedia.org/wikipedia/en/thumb/a/a3/Barbra_Streisand_-_1966.jpg/220px-Barbra_Streisand_-_1966.jpg", +  "Barry Manilow": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/BarryManilow.jpg/220px-BarryManilow.jpg", +  "Barry White": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Barry_White%2C_Bestanddeelnr_927-0099.jpg/220px-Barry_White%2C_Bestanddeelnr_927-0099.jpg", +  "Beyonce": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Beyonce_-_The_Formation_World_Tour%2C_at_Wembley_Stadium_in_London%2C_England.jpg/220px-Beyonce_-_The_Formation_World_Tour%2C_at_Wembley_Stadium_in_London%2C_England.jpg", +  "Billy Joel": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Billy_Joel_Shankbone_NYC_2009.jpg/220px-Billy_Joel_Shankbone_NYC_2009.jpg", +  "Bob Dylan": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Bob_Dylan_-_Azkena_Rock_Festival_2010_2.jpg/220px-Bob_Dylan_-_Azkena_Rock_Festival_2010_2.jpg", +  "Bob Marley": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Bob-Marley.jpg/220px-Bob-Marley.jpg", +  "Bob Seger": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/Bob_Seger_2013.jpg/220px-Bob_Seger_2013.jpg", +  "Jon Bon Jovi": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/49/Jon_Bon_Jovi_at_the_2009_Tribeca_Film_Festival_3.jpg/220px-Jon_Bon_Jovi_at_the_2009_Tribeca_Film_Festival_3.jpg", +  "Britney Spears": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Britney_Spears_2013_%28Straighten_Crop%29.jpg/200px-Britney_Spears_2013_%28Straighten_Crop%29.jpg", +  "Bruce Springsteen": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Bruce_Springsteen_-_Roskilde_Festival_2012.jpg/210px-Bruce_Springsteen_-_Roskilde_Festival_2012.jpg", +  "Bruno Mars": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/BrunoMars24KMagicWorldTourLive_%28cropped%29.jpg/220px-BrunoMars24KMagicWorldTourLive_%28cropped%29.jpg", +  "Bryan Adams": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7e/Bryan_Adams_Hamburg_MG_0631_flickr.jpg/300px-Bryan_Adams_Hamburg_MG_0631_flickr.jpg", +  "Celine Dion": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/Celine_Dion_Concert_Singing_Taking_Chances_2008.jpg/220px-Celine_Dion_Concert_Singing_Taking_Chances_2008.jpg", +  "Cher": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Cher_-_Casablanca.jpg/220px-Cher_-_Casablanca.jpg", +  "Christina Aguilera": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Christina_Aguilera_in_2016.jpg/220px-Christina_Aguilera_in_2016.jpg", +  "David Bowie": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/David-Bowie_Chicago_2002-08-08_photoby_Adam-Bielawski-cropped.jpg/220px-David-Bowie_Chicago_2002-08-08_photoby_Adam-Bielawski-cropped.jpg", +  "David Lee Roth": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fb/David_Lee_Roth_-_Van_Halen.jpg/220px-David_Lee_Roth_-_Van_Halen.jpg", +  "Donna Summer": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Nobel_Peace_Price_Concert_2009_Donna_Summer3.jpg/220px-Nobel_Peace_Price_Concert_2009_Donna_Summer3.jpg", +  "Drake": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/81/Drake_at_the_Velvet_Underground_-_2017_%2835986086223%29_%28cropped%29.jpg/220px-Drake_at_the_Velvet_Underground_-_2017_%2835986086223%29_%28cropped%29.jpg", +  "Ed Sheeran": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Ed_Sheeran_2013.jpg/220px-Ed_Sheeran_2013.jpg", +  "Eddie Van Halen": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Eddie_Van_Halen.jpg/300px-Eddie_Van_Halen.jpg", +  "Elton John": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Elton_John_2011_Shankbone_2.JPG/220px-Elton_John_2011_Shankbone_2.JPG", +  "Elvis Presley": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/99/Elvis_Presley_promoting_Jailhouse_Rock.jpg/220px-Elvis_Presley_promoting_Jailhouse_Rock.jpg", +  "Eminem": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Eminem_-_Concert_for_Valor_in_Washington%2C_D.C._Nov._11%2C_2014_%282%29_%28Cropped%29.jpg/220px-Eminem_-_Concert_for_Valor_in_Washington%2C_D.C._Nov._11%2C_2014_%282%29_%28Cropped%29.jpg", +  "Enya": "https://enya.com/wp-content/themes/enya%20full%20site/images/enya-about.jpg", +  "Flo Rida": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Flo_Rida_%286924266548%29.jpg/220px-Flo_Rida_%286924266548%29.jpg", +  "Frank Sinatra": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/af/Frank_Sinatra_%2757.jpg/220px-Frank_Sinatra_%2757.jpg", +  "Garth Brooks": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Garth_Brooks_on_World_Tour_%28crop%29.png/220px-Garth_Brooks_on_World_Tour_%28crop%29.png", +  "George Michael": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f2/George_Michael.jpeg/220px-George_Michael.jpeg", +  "George Strait": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/George_Strait_2014_1.jpg/220px-George_Strait_2014_1.jpg", +  "James Taylor": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/James_Taylor_-_Columbia.jpg/220px-James_Taylor_-_Columbia.jpg", +  "Janet Jackson": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/JanetJacksonUnbreakableTourSanFran2015.jpg/220px-JanetJacksonUnbreakableTourSanFran2015.jpg", +  "Jay-Z": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Jay-Z.png/220px-Jay-Z.png", +  "Johnny Cash": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f2/JohnnyCash1969.jpg/220px-JohnnyCash1969.jpg", +  "Johnny Hallyday": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Johnny_Hallyday_Cannes.jpg/220px-Johnny_Hallyday_Cannes.jpg", +  "Julio Iglesias": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Julio_Iglesias09.jpg/220px-Julio_Iglesias09.jpg", +  "Justin Bieber": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Justin_Bieber_in_2015.jpg/220px-Justin_Bieber_in_2015.jpg", +  "Justin Timberlake": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Justin_Timberlake_by_Gage_Skidmore_2.jpg/220px-Justin_Timberlake_by_Gage_Skidmore_2.jpg", +  "Kanye West": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Kanye_West_at_the_2009_Tribeca_Film_Festival.jpg/220px-Kanye_West_at_the_2009_Tribeca_Film_Festival.jpg", +  "Katy Perry": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Katy_Perry_at_Madison_Square_Garden_%2837436531092%29_%28cropped%29.jpg/220px-Katy_Perry_at_Madison_Square_Garden_%2837436531092%29_%28cropped%29.jpg", +  "Kenny G": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/KennyGHWOFMay2013.jpg/220px-KennyGHWOFMay2013.jpg", +  "Kenny Rogers": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/KennyRogers.jpg/220px-KennyRogers.jpg", +  "Lady Gaga": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Lady_Gaga_interview_2016.jpg/220px-Lady_Gaga_interview_2016.jpg", +  "Lil Wayne": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Lil_Wayne_%2823513397583%29.jpg/220px-Lil_Wayne_%2823513397583%29.jpg", +  "Linda Ronstadt": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/LindaRonstadtPerforming.jpg/220px-LindaRonstadtPerforming.jpg", +  "Lionel Richie": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Lionel_Richie_2017.jpg/220px-Lionel_Richie_2017.jpg", +  "Madonna": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Madonna_Rebel_Heart_Tour_2015_-_Stockholm_%2823051472299%29_%28cropped_2%29.jpg/220px-Madonna_Rebel_Heart_Tour_2015_-_Stockholm_%2823051472299%29_%28cropped_2%29.jpg", +  "Mariah Carey": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Mariah_Carey_WBLS_2018_Interview_4.jpg/220px-Mariah_Carey_WBLS_2018_Interview_4.jpg", +  "Meat Loaf": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Meat_Loaf.jpg/220px-Meat_Loaf.jpg", +  "Michael Jackson": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/31/Michael_Jackson_in_1988.jpg/220px-Michael_Jackson_in_1988.jpg", +  "Neil Diamond": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Neil_Diamond_HWOF_Aug_2012_other_%28levels_adjusted_and_cropped%29.jpg/220px-Neil_Diamond_HWOF_Aug_2012_other_%28levels_adjusted_and_cropped%29.jpg", +  "Nicki Minaj": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Nicki_Minaj_MTV_VMAs_4.jpg/250px-Nicki_Minaj_MTV_VMAs_4.jpg", +  "Olivia Newton-John": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Olivia_Newton-John_2.jpg/220px-Olivia_Newton-John_2.jpg", +  "Paul McCartney": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Paul_McCartney_-_Out_There_Concert_-_140420-5941-jikatu_%2813950091384%29.jpg/220px-Paul_McCartney_-_Out_There_Concert_-_140420-5941-jikatu_%2813950091384%29.jpg", +  "Phil Collins": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/1_collins.jpg/220px-1_collins.jpg", +  "Pink": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/P%21nk_Live_2013.jpg/220px-P%21nk_Live_2013.jpg", +  "Prince": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Prince_1983_1st_Avenue.jpg/220px-Prince_1983_1st_Avenue.jpg", +  "Reba McEntire": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Reba_McEntire_by_Gage_Skidmore.jpg/220px-Reba_McEntire_by_Gage_Skidmore.jpg", +  "Rihanna": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Rihanna_concert_in_Washington_DC_%282%29.jpg/250px-Rihanna_concert_in_Washington_DC_%282%29.jpg", +  "Robbie Williams": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/21/Robbie_Williams.jpg/220px-Robbie_Williams.jpg", +  "Rod Stewart": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/Rod_stewart_05111976_12_400.jpg/220px-Rod_stewart_05111976_12_400.jpg", +  "Carlos Santana": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Santana_2010.jpg/220px-Santana_2010.jpg", +  "Shania Twain": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/ShaniaTwainJunoAwardsMar2011.jpg/220px-ShaniaTwainJunoAwardsMar2011.jpg", +  "Stevie Wonder": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Stevie_Wonder_1973.JPG/220px-Stevie_Wonder_1973.JPG", +  "Tak Matsumoto": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/B%27Z_at_Best_Buy_Theater_NYC_-_9-30-12_-_22.jpg/220px-B%27Z_at_Best_Buy_Theater_NYC_-_9-30-12_-_22.jpg", +  "Taylor Swift": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/25/Taylor_Swift_112_%2818119055110%29_%28cropped%29.jpg/220px-Taylor_Swift_112_%2818119055110%29_%28cropped%29.jpg", +  "Tim McGraw": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/Tim_McGraw_October_24_2015.jpg/220px-Tim_McGraw_October_24_2015.jpg", +  "Tina Turner": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Tina_turner_21021985_01_350.jpg/250px-Tina_turner_21021985_01_350.jpg", +  "Tom Petty": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Tom_Petty_Live_in_Horsens_%28cropped2%29.jpg/220px-Tom_Petty_Live_in_Horsens_%28cropped2%29.jpg", +  "Tupac Shakur": "https://upload.wikimedia.org/wikipedia/en/thumb/b/b5/Tupac_Amaru_Shakur2.jpg/220px-Tupac_Amaru_Shakur2.jpg", +  "Usher": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Usher_Cannes_2016_retusche.jpg/220px-Usher_Cannes_2016_retusche.jpg", +  "Whitney Houston": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Whitney_Houston_Welcome_Home_Heroes_1_cropped.jpg/220px-Whitney_Houston_Welcome_Home_Heroes_1_cropped.jpg", +  "Wolfgang Van Halen": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Wolfgang_Van_Halen_Different_Kind_of_Truth_2012.jpg/220px-Wolfgang_Van_Halen_Different_Kind_of_Truth_2012.jpg" +} | 
