aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/__main__.py1
-rw-r--r--bot/cogs/free.py29
-rw-r--r--bot/cogs/help.py6
-rw-r--r--bot/cogs/moderation.py51
-rw-r--r--bot/cogs/token_remover.py6
-rw-r--r--bot/constants.py12
-rw-r--r--bot/decorators.py14
-rw-r--r--bot/resources/stars.json82
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"
+}