· 6 years ago · Nov 28, 2019, 02:14 AM
1import io
2import draw
3import typing
4import asyncio
5import aiohttp
6import requests
7from functions import *
8from discord.ext import commands
9from fuzzywuzzy import process
10from datetime import datetime
11
12bot = commands.Bot(command_prefix="!", case_insensitive=True, help_command=None)
13
14def get_command_help(command):
15 help = f"`{bot.command_prefix}{command.name} "
16 for alias in sorted(command.aliases):
17 help = help + f"or {bot.command_prefix}{alias} "
18 if command.signature != "":
19 help = help + f"{command.signature} "
20 help = help + f"` - {command.help}"
21 return help
22
23def is_super_channel(ctx):
24 if ctx.channel.name not in config["channels"]["super_waifu"]:
25 raise commands.NoPrivateMessage
26 return True
27
28def has_role(member, role_name):
29 for role in member.roles:
30 if role.name.lower() == role_name.lower():
31 return True
32 return False
33
34def is_silly_channel(ctx):
35 if isinstance(ctx.channel, discord.TextChannel):
36 if ctx.channel.name not in config["channels"]["serious"]:
37 return True
38 raise commands.NoPrivateMessage
39
40def get_guild():
41 guild_id = config["discord"]["guild_id"]
42 return bot.get_guild(guild_id)
43
44def get_channel(name):
45 for channel in get_guild().channels:
46 if channel.name == name:
47 return channel
48 return None
49
50def get_category(name):
51 for category in get_guild().categories:
52 if category.name.lower() == name.lower():
53 return category
54 return None
55
56def get_channel_by_topic(topic):
57 for channel in get_guild().text_channels:
58 if channel.topic == topic:
59 return channel
60 return None
61
62def get_role(name):
63 for role in get_guild().roles:
64 if role.name.lower() == name.lower():
65 return role
66 return None
67
68def get_joinable_roles():
69 joinable = []
70 role_colors = [discord.Color.orange(), discord.Color.blue(), discord.Color.from_rgb(54, 57, 63)]
71 for role in get_guild().roles:
72 if role.name not in config['roles']['forbidden'] and role.color in role_colors:
73 joinable.append(role)
74 return joinable
75
76def get_members_by_role(name):
77 return get_role(name).members
78
79async def detect_reposts(message):
80 if message.channel.name in config['channels']['ignore_reposts']:
81 return
82 guild = get_guild()
83 title = "**REPOST DETECTED :recycle:**"
84 author = message.author
85 description = f"Reposter: {author.mention}"
86 embed = discord.Embed(title=title, description=description, color=waifu_pink)
87 if len(message.content.split(" ")) > 3:
88 value = ""
89 file = io.BytesIO(message.content.encode("utf-8"))
90 bytes_hash = sha_256(file)
91 channel_category = message.channel.category.name
92 previous_hashes = get_hashes(bytes_hash, channel_category)
93 count = len(previous_hashes)
94 if count > 0:
95 date_time = date_time_from_str(previous_hashes[0][2])
96 delta = format_delta(time_since(date_time))
97 channel = get_channel(previous_hashes[0][5])
98 author = guild.get_member(previous_hashes[0][3])
99 name = "Message"
100 value = value + f"\"{message.content}\"\n"
101 if count == 1:
102 value = value + f"Previously posted: {count} time\n"
103 else:
104 value = value + f"Previously posted: {count} times\n"
105 value = value + f"Last posted: {delta} ago\n"
106 value = value + f"In: {channel.mention}\n"
107 value = value + f"By: {author.mention}\n"
108 embed.add_field(name=name, value=value, inline=False)
109 store_hash(bytes_hash, message)
110 for attachment in message.attachments:
111 value = ""
112 file = io.BytesIO()
113 await attachment.save(file)
114 bytes_hash = sha_256(file)
115 channel_category = message.channel.category.name
116 previous_hashes = get_hashes(bytes_hash, channel_category)
117 count = len(previous_hashes)
118 if count > 0:
119 date_time = date_time_from_str(previous_hashes[0][2])
120 delta = format_delta(time_since(date_time))
121 channel = get_channel(previous_hashes[0][5])
122 author = guild.get_member(previous_hashes[0][3])
123 name = "Attachment"
124 value = value + f"Filename: {attachment.filename}\n"
125 if count == 1:
126 value = value + f"Previously posted: {count} time\n"
127 else:
128 value = value + f"Previously posted: {count} times\n"
129 value = value + f"Last posted: {delta} ago\n"
130 value = value + f"In: {channel.mention}\n"
131 value = value + f"By: {author.mention}\n"
132 embed.add_field(name=name, value=value, inline=False)
133 store_hash(bytes_hash, message)
134 if len(embed.fields) > 0:
135 await message.channel.send(embed=embed)
136 return
137
138async def rate_limiter(message):
139 if message.channel.name not in config['channels']['rate_limited']:
140 return
141 if len(message.attachments) == 0:
142 return
143 attachments = []
144 for attachment in message.attachments:
145 attachments.append(attachment)
146 history = await message.channel.history(limit=25).flatten()
147 for previous_message in history:
148 if previous_message.author == message.author:
149 if len(previous_message.attachments) != 0:
150 if seconds_since(previous_message.created_at) < 120:
151 for attachment in previous_message.attachments:
152 attachments.append(attachment)
153 if len(attachments) > 5:
154 title = "**DUMP DETECTED :poo:**"
155 author = message.author
156 description = f"Dumper: {author.mention}\n\n"
157 description = description + "You're not breaking the rules, but you are being a scumbag."
158 embed = discord.Embed(title=title, description=description, color=waifu_pink)
159 await message.channel.send(embed=embed)
160 return
161
162async def yes_no_timeout(ctx, message):
163 await ctx.send(message)
164 def check(answer):
165 return answer.author == ctx.author and answer.channel == ctx.channel
166 try:
167 answer = await bot.wait_for("message", timeout=15, check=check)
168 if answer.content.lower() in config["answers"][True]:
169 reply = random.choice(strings["user_reply_yes"])
170 await ctx.send(reply)
171 return True
172 reply = random.choice(strings["user_reply_no"])
173 await ctx.send(reply)
174 return False
175 except asyncio.TimeoutError:
176 reply = random.choice(strings["user_reply_timeout"])
177 await ctx.send(reply)
178 return None
179
180async def reply_noob(message):
181 global block_noobs
182 if message.content.startswith("!"):
183 image_path = os.path.join(sys.path[0], "images", "power.gif")
184 file = discord.File(image_path)
185 reply = f"You don't have access to commands yet, noob."
186 await message.channel.send(reply, file=file)
187 file.close()
188 return
189 answer = re.sub("[^0-9a-zA-Z]+", "", message.clean_content).lower()
190 if answer == "dontbeadick":
191 reply = "Yup. Thanks! I'll grant you access. Just a sec..."
192 await message.channel.send(reply)
193 await asyncio.sleep(1)
194 block_noobs = True
195 await message.author.remove_roles(get_role("noob"))
196 await asyncio.sleep(1)
197 await message.channel.delete()
198 await asyncio.sleep(1)
199 block_noobs = False
200 general_chat = get_channel("general_chat")
201 if message.author not in general_chat.members:
202 for channel in get_guild().text_channels:
203 if "quarantine" in channel.name and message.author in channel.members:
204 reply = f"Hey everyone, {message.author.mention} just joined. {message.author.mention}, please introduce yourself. Thanks!"
205 await channel.send(reply)
206 reply = f"Hey everyone, {message.author.mention} just joined in {channel.mention}!"
207 await general_chat.send(reply)
208 return
209 reply = f"Hey everyone, {message.author.mention} just joined. {message.author.mention}, please introduce yourself. Thanks!"
210 await general_chat.send(reply)
211 return
212 else:
213 reply = "Not quite. Try again."
214 await message.channel.send(reply)
215 return
216
217async def always_sunny(message):
218 text = message.clean_content.replace("*", "")
219 text = text.replace("_", "")
220 text = ascii_only("\"" + text + "\"")
221 pending = await message.channel.send("Drawing some dumb shit...")
222 image = draw.sunny(text)
223 await pending.edit(content="Drawing is done. Sending now...")
224 file = discord.File(image)
225 await message.channel.send(file=file)
226 image.close()
227 file.close()
228 await pending.delete()
229 return
230
231@asyncio.coroutine
232async def change_status():
233 statuses = config["statuses"]
234 while True:
235 status = random.choice(statuses)
236 status_code = status[:1]
237 status_name = status[1:]
238 if status_code == "0":
239 status_code = discord.Status.online
240 elif status_code == "1":
241 status_code = discord.Status.idle
242 else:
243 status_code = discord.Status.dnd
244 game = discord.Game(status_name)
245 await bot.change_presence(status=status_code, activity=game)
246 await asyncio.sleep(random.randint(300, 600))
247
248@asyncio.coroutine
249async def monitor_noobs():
250 global block_noobs
251 guild = get_guild()
252 noob_role = get_role("noob")
253 super_waifu_chat = get_channel("super_waifu_chat")
254 while True:
255 for member in get_members_by_role("noob"):
256 if get_channel_by_topic(str(member.id)) == None:
257 if block_noobs:
258 await asyncio.sleep(5)
259 else:
260 await member.remove_roles(noob_role)
261 reply = f"No welcome_noob channel found for {member.mention}. Removing noob role."
262 await super_waifu_chat.send(reply)
263 for channel in guild.text_channels:
264 if channel.name == "welcome_noob":
265 id = int(channel.topic)
266 member = guild.get_member(id)
267 noobs = get_members_by_role("noob")
268 if member == None or member not in noobs:
269 if block_noobs:
270 await asyncio.sleep(5)
271 else:
272 await channel.delete()
273 reply = f"<@{id}> no longer has the noob role. Removing welcome_noob channel."
274 await super_waifu_chat.send(reply)
275 await asyncio.sleep(1)
276
277@asyncio.coroutine
278async def monitor_deletions():
279 guild = get_guild()
280 waifu_audit_log = {}
281 action = discord.AuditLogAction.message_delete
282 async for entry in guild.audit_logs(action=action, limit=25):
283 if entry.id not in waifu_audit_log:
284 if seconds_since(entry.created_at) < 3600:
285 waifu_audit_log[entry.id] = entry
286 while True:
287 message = await bot.wait_for("message_delete")
288 deleted_by = message.author
289 author = message.author
290 async for entry in guild.audit_logs(action=action, limit=5):
291 if entry.extra.channel == message.channel:
292 if entry.id not in waifu_audit_log:
293 if seconds_since(entry.created_at) < 60:
294 waifu_audit_log[entry.id] = entry
295 deleted_by = entry.user
296 elif waifu_audit_log[entry.id].extra.count != entry.extra.count:
297 waifu_audit_log[entry.id] = entry
298 deleted_by = entry.user
299 else:
300 if seconds_since(waifu_audit_log[entry.id].created_at) > 86400:
301 del waifu_audit_log[entry.id]
302 if author == bot.user and deleted_by == bot.user:
303 continue
304 if "deleted" in message.channel.name:
305 super_waifu_chat = get_channel("super_waifu_chat")
306 reply = f"Hey {deleted_by.mention}, it's best if you don't delete messages from {message.channel.mention}. Unless of course they're really bad."
307 await super_waifu_chat.send(reply)
308 continue
309 timestamp = message.created_at.strftime("%m/%d/%Y %H:%M")
310 title = f"ID: {message.id}"
311 description = f"Author: {author.mention}\nDeleted by: {deleted_by.mention}*\nChannel: {message.channel.mention}\nUTC: {timestamp}"
312 embed = discord.Embed(title=title, description=description, color=discord.Color.red())
313 deleted_embeds = message.embeds
314 if len(message.content) > 0:
315 value = f"\"{message.content}\""
316 embed.add_field(name="Message", value=value, inline=False)
317 if len(message.attachments) > 0:
318 name = "Attachments"
319 value = ""
320 for attachment in message.attachments:
321 value = value + "<{}>\n".format(attachment.proxy_url)
322 embed.add_field(name=name, value=value, inline=False)
323 if len(message.embeds) > 0:
324 name = "Embeds"
325 value = f"{len(message.embeds)} found. See below:"
326 embed.add_field(name=name, value=value, inline=False)
327 if message.channel.name in config["channels"]["female_only"]:
328 channel = get_channel("deleted_thots")
329 else:
330 channel = get_channel("deleted_text")
331 await channel.send(embed=embed)
332 for index, embed in enumerate(deleted_embeds):
333 embed.color = waifu_pink
334 reply = f"**Embed {index + 1} of {len(deleted_embeds)}**"
335 await channel.send(reply, embed=embed)
336
337@asyncio.coroutine
338async def monitor_joins():
339 global block_noobs
340 guild = get_guild()
341 super_waifu_chat = get_channel("super_waifu_chat")
342 title = f"**NOOB DETECTED :airplane_arriving:**"
343 previous_invites = await guild.invites()
344 while True:
345 try:
346 member = await bot.wait_for("member_join", timeout=3)
347 except asyncio.TimeoutError:
348 previous_invites = await guild.invites()
349 continue
350 super_waifu_role = get_role("super_waifu")
351 overwrites = {
352 guild.default_role: discord.PermissionOverwrite(read_messages=False),
353 guild.me: discord.PermissionOverwrite(read_messages=True),
354 super_waifu_role: discord.PermissionOverwrite(read_messages=True),
355 member: discord.PermissionOverwrite(read_messages=True)
356 }
357 block_noobs = True
358 noob_channel = await guild.create_text_channel("welcome_noob", topic=str(member.id), overwrites=overwrites)
359 await asyncio.sleep(1)
360 await member.add_roles(get_role("noob"))
361 block_noobs = False
362 description = f"Noob: {member.mention}\n"
363 embed = discord.Embed(title=title, description=description, color=waifu_pink)
364 invite_found = None
365 invites = await guild.invites()
366 for invite in invites:
367 for previous_invite in previous_invites:
368 if invite.id == previous_invite.id:
369 if invite.uses != previous_invite.uses:
370 invite_found = invite
371 previous_invites = invites
372 if invite_found is not None:
373 invite_details = get_invite_details(invite_found)
374 url = invite_found.url
375 invite_channel = invite_found.channel
376 if invite_found.max_uses == 2 and invite_found.uses == 1 and invite_details is not None:
377 event_name = None
378 invited_by = guild.get_member(int(invite_details[3])).mention
379 reason = invite_details[7]
380 await invite_found.delete()
381 update_invite_details(invite_found, member)
382 elif invite_found.max_uses == 100 and invite_details is not None:
383 invited_by = guild.get_member(int(invite_details[3])).mention
384 reason = invite_details[7]
385 event_name = invite_details[8].replace("_chat", "")
386 event_role = get_role(event_name)
387 await member.add_roles(event_role)
388 await member.add_roles(get_role("quarantine"))
389 else:
390 event_name = "UNOFFICIAL"
391 invited_by = invite_found.inviter.mention
392 reason = f"Fuck if I know. Ask {invited_by}."
393 value = f"Event: {event_name}\nCreated by: {invited_by}\nChannel: {invite_channel.mention}\nReason: {reason}\nURL: {url}\n"
394 else:
395 value = "ERROR: NO MATCHING INVITE FOUND"
396 embed.add_field(name="Invite", value=value, inline=False)
397 await super_waifu_chat.send(embed=embed)
398 reply = f"Hey {member.mention}, welcome to Waifus_4_Lifu! I'm WaifuBot, I manage various things here. Here is a basic outline of our rules:"
399 await noob_channel.send(reply)
400 reply = "1. Don't be a dick. We all like to have fun and mess around but let's try and keep it playful! On that note, please try and keep negativity to a minimum. All it does is bring everyone else down and we don't want that! This is intended to be a fun environment.\n\n"
401 reply = reply + "2. Introduce yourself before you start posting! Everybody is welcome, we just want to know who you are and what you are into!\n\n"
402 reply = reply + "3. If you want to post something NSFW (or just shitpost memes) then we have a channel for that! Just remember if its illegal we don't want to see it and you will be immediately banned without question. The shitposting channel has it's own special rules, please read them if you decide to join it. To gain access to the channel, join the shithead role in #role_call.\n\n"
403 reply = reply + "4. Speaking of, we have a bot! If you want a list of commands just type `!wtf` or `!help` and WaifuBot will explain!\n\n"
404 reply = reply + "5. If you have a problem of some sort, tag a Super Waifu. They are here to help!\n\n"
405 reply = reply + "6. We have voice channels for specific games and for general conversation! Please try and use the appropriate channel based on what you are playing or doing.\n\n"
406 reply = reply + "7. We don't have rules for all types of behaviors and actions. That being said, if a Super Waifu or Admin contacts you regarding something you have said or done, please be willing to comply. We try our hardest to make sure everybody here is having a good time. On that same note, if you have some sort of issue or concern with something that has been said or done then please bring it to a Super Waifu or Admin's attention. Your concern will be reviewed and addressed appropriately.\n\n"
407 reply = reply + "8. Have fun! That is why we made this server!\n\n**Before we continue, what's rule #1?**"
408 await noob_channel.send(reply)
409
410@asyncio.coroutine
411async def update_countdowns():
412 guild = get_guild()
413 while True:
414 for channel in guild.text_channels:
415 topic = channel.topic
416 if topic is not None:
417 if topic.startswith("countdown to "):
418 timestamp = topic.replace("countdown to ", "")
419 try:
420 date_time = date_time_from_str(timestamp)
421 except:
422 topic = f"INVALID FORMAT: '{channel.topic}' try: 'countdown to YYYYMMDDHHMMSS'"
423 await channel.edit(topic=topic)
424 await channel.edit(name="countdown_error")
425 continue
426 delta = time_until(date_time)
427 if delta.total_seconds() < 50:
428 await channel.delete()
429 continue
430 formatted_delta = format_countdown(delta)
431 if channel.name != formatted_delta:
432 try:
433 await channel.edit(name=formatted_delta)
434 except:
435 pass
436 await asyncio.sleep(1)
437
438@bot.event
439async def on_ready():
440 log.info(f"Logged on as {bot.user}")
441 loop = asyncio.get_event_loop()
442 change_status_task = loop.create_task(change_status())
443 monitor_noobs_task = loop.create_task(monitor_noobs())
444 monitor_deletions_task = loop.create_task(monitor_deletions())
445 monitor_joins_task = loop.create_task(monitor_joins())
446 update_countdowns_task = loop.create_task(update_countdowns())
447
448@bot.event
449async def on_command_error(ctx, error):
450 error_text = str(error)
451 if isinstance(error, commands.UserInputError):
452 if error_text != "":
453 if error_text[-1] != ".":
454 error_text = error_text + "."
455 error_text = sentence_case(error_text)
456 error_text = error_text + "\n" + get_command_help(ctx.command)
457 reply = f"{ctx.author.mention}, you sure are creative when it comes to syntax.\n{error_text}"
458 await ctx.send(reply)
459 elif isinstance(error, commands.MissingRole):
460 image_path = os.path.join(sys.path[0], "images", "dennis.gif")
461 file = discord.File(image_path)
462 reply = f"{ctx.author.mention}, just who do you think you are?"
463 await ctx.send(reply, file=file)
464 file.close()
465 elif isinstance(error, commands.NoPrivateMessage):
466 image_path = os.path.join(sys.path[0], "images", "power.gif")
467 file = discord.File(image_path)
468 reply = f"Say, uh {ctx.author.mention}, let's find a better channel for this."
469 await ctx.send(reply, file=file)
470 file.close()
471 elif isinstance(error, commands.errors.CommandNotFound):
472 reply = f"{ctx.author.mention}, that's not a valid command. Maybe try `!wtf`."
473 await ctx.send(reply)
474 elif isinstance(error, commands.errors.CommandInvokeError):
475 raise error.original
476 log_msg = f"[{ctx.author}] - [{ctx.channel}]\n[{error.__class__}]\n{ctx.message.content}"
477 log.error(log_msg)
478 return
479
480
481@bot.event
482async def on_raw_reaction_add(payload):
483 guild = get_guild()
484 user_id = payload.user_id
485 member = guild.get_member(user_id)
486 if member == bot.user:
487 return
488 role_call = get_channel('role_call')
489 channel_id = payload.channel_id
490 message_id = payload.message_id
491 emoji = payload.emoji.name
492 if role_call.id == channel_id:
493 messages = await role_call.history(limit=300).flatten()
494 for message in messages:
495 if message.id == message_id:
496 for reaction in message.reactions:
497 users = await reaction.users().flatten()
498 for user in users:
499 if user != bot.user:
500 await reaction.remove(user)
501 role_name = message.content.split(' - ')[0]
502 if role_name not in config["roles"]["forbidden"]:
503 role = get_role(role_name)
504 if emoji == '?':
505 if role not in member.roles:
506 await member.add_roles(role)
507 msg = f'You have been added to {role_name}'
508 await member.send(msg)
509 else:
510 msg = f'You are already a member of {role_name}'
511 await member.send(msg)
512 elif emoji == '?':
513 if role in member.roles:
514 await member.remove_roles(role)
515 msg = f'You have been removed from {role_name}'
516 await member.send(msg)
517 else:
518 msg = f'You are not a member of {role_name}'
519 await member.send(msg)
520 return
521
522@bot.event
523async def on_message(message):
524 if message.author == bot.user:
525 return
526 lower = message.clean_content.lower()
527 if isinstance(message.channel, discord.TextChannel):
528 if message.channel.name == "welcome_noob" and message.channel.topic == str(message.author.id):
529 await reply_noob(message)
530 return
531 if message.content.startswith("!"):
532 await bot.process_commands(message)
533 return
534 if "thank" in lower and "waifubot" in lower:
535 reply = random.choice(strings["no_problem"])
536 await message.channel.send(reply)
537 if "fuck" in lower and "waifubot" in lower:
538 reply = "Fuck me yourself, coward."
539 await message.channel.send(reply)
540 if "hungry" in message.content.lower().replace(" ", ""):
541 if chance(config['chance']['hungry']):
542 reply = "No, <@221162619497611274> is hungry."
543 file = discord.File(os.path.join(sys.path[0], 'images', 'dennis.gif'))
544 await message.channel.send(reply, file=file)
545 file.close()
546 if lower.startswith("*the gang") or lower.startswith("_the gang"):
547 await always_sunny(message)
548 if isinstance(message.channel, discord.TextChannel):
549 await detect_reposts(message)
550 await rate_limiter(message)
551 return
552
553@bot.command(aliases=["help"])
554@commands.guild_only()
555async def wtf(ctx):
556 """Display this help message."""
557 reply = "I understand the following commands:\n\n"
558 for command in sorted(bot.commands, key=lambda x: x.name):
559 if not command.hidden:
560 reply = reply + get_command_help(command) + "\n"
561 reply = reply + "\nIf I'm not working correctly, go fuck yourself, you aren't my boss."
562 await ctx.send(reply)
563
564@bot.command(aliases=["players"])
565@commands.guild_only()
566async def members(ctx, *, role):
567 """Show a list of members who have signed up for a role/game."""
568 role_colors = [discord.Color.orange(), discord.Color.blue(), discord.Color.from_rgb(54, 57, 63)]
569 role = get_role(role)
570 if role is None:
571 reply = f"{ctx.author.mention}, that is not a valid role/game."
572 await ctx.send(reply)
573 return
574 if role.name in config['roles']['forbidden'] or role.color not in role_colors:
575 reply = f"{ctx.author.mention}, this is a Forbidden Role:tm:. If you can't figure this out a different way, you don't deserve to know."
576 await ctx.send(reply)
577 return
578 if role.color in [discord.Color.orange(), discord.Color.from_rgb(54, 57, 63)]:
579 member_type = "members"
580 else:
581 member_type = "players"
582 title = f"**{member_type.upper()} OF {role.name.upper()}**"
583 description = ""
584 for member in role.members:
585 description = description + f"{member.mention}\n"
586 if description == "":
587 description = f"No {member_type.lower()} found."
588 color = role.color
589 pages = paginate(description)
590 for index, page in enumerate(pages):
591 if index == 0:
592 embed = discord.Embed(title=title, description=page, color=color)
593 else:
594 embed.add_field(name="Continued", value=page, inline=False)
595 await ctx.send(embed=embed)
596 return
597
598@bot.command()
599@commands.check(is_silly_channel)
600@commands.guild_only()
601async def magic8ball(ctx, question: str):
602 """Ask the magic 8 ball a question."""
603 answer = random.choice(strings['eight_ball'])
604 reply = f"{ctx.author.mention}, the magic 8 ball has spoken: \"{answer}\"."
605 await ctx.send(reply)
606 return
607
608@bot.command(name="color")
609@commands.guild_only()
610async def _color(ctx):
611 """Get the hex and RGB values for Waifu Pink:tm:."""
612 if get_role("colorblind_fucks") in ctx.author.roles:
613 reply = f"{ctx.author.mention}, you're not authorized to see in color."
614 await ctx.send(reply)
615 return
616 reply = f"{ctx.author.mention}, Waifu Pink:tm: is hex: `#ff3fb4`, RGB: `(255, 63, 180)`."
617 await ctx.send(reply)
618 return
619
620@bot.command(name="random")
621@commands.guild_only()
622async def _random(ctx):
623 """Request a random number, chosen by fair dice roll."""
624 await ctx.send(f"{ctx.author.mention}: 4")
625 def check(answer):
626 if answer.channel == ctx.channel:
627 if answer.author == bot.user:
628 return False
629 is_not = ["n't", "not", "no", "crypto"]
630 for word in is_not:
631 if (word in answer.content.lower() and "random" in answer.content.lower()) or answer.content.lower().startswith("!random"):
632 return True
633 return False
634 try:
635 answer = await bot.wait_for("message", timeout=300, check=check)
636 if answer.content.lower().startswith("!random"):
637 return
638 reply = f"{answer.author.mention}, I disagree:\nhttps://xkcd.com/221/"
639 await ctx.send(reply)
640 return
641 except asyncio.TimeoutError:
642 return
643
644# TODO: Fix plural replacements to use proper regex
645@bot.command()
646@commands.check(is_silly_channel)
647@commands.guild_only()
648async def catfact(ctx):
649 """Kinda self-explanatory."""
650 async with aiohttp.ClientSession() as session:
651 async with session.get('https://catfact.ninja/fact') as resp:
652 if resp.status != 200:
653 reply = f"Error {resp.status}: I cannot read the sacred texts."
654 await ctx.send(reply)
655 return
656 fact = (await resp.json())['fact']
657 if chance(config['chance']['catfact']):
658 guild = get_guild()
659 name = random.choice(guild.members).display_name
660 fact = replace_ignore_case(fact, " cat ", " " + name + " ")
661 fact = replace_ignore_case(fact, " cats ", " " + name + "s ")
662 fact = replace_ignore_case(fact, " cat's ", " " + name + "'s ")
663 namess = name.lower() + "s"
664 if namess[-2:] == "ss":
665 fact = replace_ignore_case(fact, namess, name)
666 fact = fact.replace("s's", "s'")
667 await ctx.send(fact)
668 return
669
670@bot.command()
671@commands.check(is_silly_channel)
672@commands.guild_only()
673async def sponge(ctx, target: typing.Optional[typing.Union[discord.Member, discord.Message, str]]):
674 """Mock a fellow member even though you're not clever."""
675 messages = []
676 async for message in ctx.channel.history(limit=20):
677 messages.append(message)
678 messages.pop(0)
679 if target == None:
680 for message in messages:
681 if len(message.content) != 0:
682 target = message
683 break
684 elif isinstance(target, discord.Member):
685 for message in messages:
686 if (message.author == target) and len(message.content) != 0:
687 target = message
688 break
689 if not isinstance(target, discord.Message):
690 reply = f"{ctx.author.mention}, I don't see any matching messages in this channel."
691 await ctx.send(reply)
692 return
693 pending = await ctx.send("Drawing some dumb shit...")
694 image = draw.spongebob(ctx, target)
695 await pending.edit(content="Drawing is done. Sending now...")
696 file = discord.File(image)
697 await ctx.send(file=file)
698 image.close()
699 file.close()
700 await pending.delete()
701 return
702
703@bot.command()
704@commands.check(is_silly_channel)
705@commands.guild_only()
706async def quoth(ctx, target: typing.Optional[typing.Union[discord.Member, discord.Message, str]]):
707 """Save message to inspirational quotes database."""
708 messages = []
709 async for message in ctx.channel.history(limit=20):
710 messages.append(message)
711 messages.pop(0)
712 if target == None:
713 for message in messages:
714 if len(message.content) != 0:
715 target = message
716 break
717 elif isinstance(target, discord.Member):
718 for message in messages:
719 if (message.author == target) and len(message.content) != 0:
720 target = message
721 break
722 if not isinstance(target, discord.Message):
723 reply = f"{ctx.author.mention}, I don't see any matching messages in this channel."
724 await ctx.send(reply)
725 return
726 if target.author == ctx.author:
727 reply = f"Nice try {ctx.author.mention}, you cannot quote yourself. Just how conceited are you?"
728 await ctx.send(reply)
729 return
730 nevermore = get_role('nevermore')
731 if nevermore in target.author.roles:
732 image_path = os.path.join(sys.path[0], "images", "raven.gif")
733 file = discord.File(image_path)
734 reply = f"{ctx.author.mention}, try tapping on someone else's chamber door."
735 await ctx.send(reply, file=file)
736 return
737 if len(target.content) == 0:
738 reply = f"{ctx.author.mention}, that quote is too short."
739 await ctx.send(reply)
740 return
741 if quote_exists(target.id):
742 reply = f"Can't do that, {ctx.author.mention}. That would be a duplicate quote."
743 await ctx.send(reply)
744 return
745 if ctx.channel.name in config["channels"]["sensitive"]:
746 reply = f"Hey uh, {ctx.author.mention}, this is a sensitive_channel™.\nAre you sure you want to do this?"
747 answer = await yes_no_timeout(ctx, reply)
748 if answer == False or answer == None:
749 return
750 if ctx.channel != target.channel:
751 reply = f"Nice try {ctx.author.mention}. Though you are being a dick."
752 await ctx.send(reply)
753 reply = f"{ctx.author.mention} tried to cross-quoth. Pretty dickish if you ask me."
754 await get_channel("super_waifu_chat").send(reply)
755 return
756 clean_content = store_quote(target, ctx)
757 title = "**QUOTE STORED :floppy_disk:**"
758 description = f"Author: {target.author.mention}\nStored by: {ctx.author.mention}\n"
759 embed = discord.Embed(title=title, description=description, color=waifu_pink)
760 value = f"\"{clean_content}\""
761 embed.add_field(name="Quote", value=value, inline=False)
762 await ctx.send(embed=embed)
763 return
764
765@bot.command()
766@commands.check(is_silly_channel)
767@commands.guild_only()
768async def inspire(ctx, *, phrase: typing.Optional[str]):
769 """Request a random inspirational work of art."""
770 if ctx.author.id == 247943708371189761:
771 phrase = None
772 quote = get_quote(ctx.channel, phrase)
773 if quote == None:
774 quote = get_quote(ctx.channel, None)
775 id = quote[0]
776 member = ctx.guild.get_member(quote[3])
777 if member:
778 name = member.display_name
779 else:
780 name = quote[4]
781 text = quote[7]
782 query = None
783 if phrase != None:
784 phrase = phrase.split(" ")
785 query = random.choice(phrase).lower()
786 pending = await ctx.send("Drawing some dumb shit...")
787 comical = has_role(ctx.author, "comical")
788 image = draw.inspiration(id, text, name, query, comical)
789 await pending.edit(content="Drawing is done. Sending now...")
790 if image == None:
791 reply = "I seem to be unable to draw today."
792 await ctx.send(reply)
793 await pending.delete()
794 return
795 file = discord.File(image)
796 await ctx.send(file=file)
797 image.close()
798 file.close()
799 await pending.delete()
800 return
801
802@bot.command()
803@commands.check(is_silly_channel)
804@commands.guild_only()
805async def shake(ctx, *, target: typing.Optional[typing.Union[discord.Member, discord.Message, str]]):
806 """Create a shaky GIF or GIF of text or image attachments."""
807 text = ""
808 attachments = []
809 messages = []
810 async for message in ctx.channel.history(limit=20):
811 messages.append(message)
812 messages.pop(0)
813 if target == None and len(ctx.message.attachments) == 0:
814 text = messages[0].clean_content
815 attachments = messages[0].attachments
816 elif isinstance(target, discord.Member):
817 for message in messages:
818 if (message.author == target):
819 text = message.clean_content
820 attachments = message.attachments
821 break
822 elif isinstance(target, discord.Message):
823 text = target.clean_content
824 attachments = target.attachments
825 else:
826 text = target
827 attachments = ctx.message.attachments
828 pending = await ctx.send("Drawing some dumb shit...")
829 if text != 0 and text != None:
830 image = draw.shaky_text(text)
831 file = discord.File(image)
832 await ctx.send(file=file)
833 image.close()
834 file.close()
835 for attachment in attachments:
836 file = io.BytesIO()
837 await attachment.save(file)
838 image = draw.shaky_image(file)
839 if image == "format":
840 reply = f"{ctx.author.mention}, attachment '{attachment.filename}' isn't in a valid format. How would you like it if I force-fed you garbage?"
841 await ctx.send(reply)
842 continue
843 if image == "memory":
844 reply = f"{ctx.author.mention}, attachment '{attachment.filename}' is way too big. I ran out of memory!"
845 await ctx.send(reply)
846 continue
847 actual_size = image.getbuffer().nbytes
848 if actual_size > 8000000:
849 reply = f"{ctx.author.mention}, attachment '{attachment.filename}' is too big."
850 await ctx.send(reply)
851 image.close()
852 file.close()
853 else:
854 file = discord.File(image)
855 try:
856 await ctx.send(file=file)
857 except discord.errors.HTTPException:
858 reply = f"{ctx.author.mention}, attachment '{attachment.filename}' failed and it's your fault."
859 await ctx.send(reply)
860 finally:
861 image.close()
862 file.close()
863 await pending.delete()
864 return
865
866@bot.command(hidden=True)
867@commands.has_role("super_waifu")
868@commands.guild_only()
869async def resetroles(ctx):
870 """Use in #role_call to reset the channel"""
871 channel = get_channel('role_call')
872 await channel.purge(limit=300)
873 msg = f'Right-click your name in the member list to see your roles.'
874 await channel.send(msg)
875 guild = get_guild()
876 roles = []
877 games = []
878 role_colors = [discord.Color.orange(), discord.Color.from_rgb(54, 57, 63)]
879 game_colors = [discord.Color.blue()]
880 for role in guild.roles:
881 if role.name not in config["roles"]["forbidden"]:
882 if role.color in role_colors:
883 roles.append(role)
884 elif role.color in game_colors:
885 games.append(role)
886 msg = '**ROLES:**'
887 await channel.send(msg)
888 for role in roles:
889 #TODO: write up descriptions and create db table
890 #description = 'This is a test description:'
891 #msg = role.name + ' - ' + description
892 msg = role.name
893 msg = await channel.send(msg)
894 await asyncio.sleep(1)
895 await msg.add_reaction('?')
896 await asyncio.sleep(1)
897 await msg.add_reaction('?')
898 await asyncio.sleep(1)
899 msg = '**GAMES:**'
900 await channel.send(msg)
901 for role in games:
902 #description = 'This is a test description:'
903 #msg = role.name + ' - ' + description
904 msg = role.name
905 msg = await channel.send(msg)
906 await asyncio.sleep(1)
907 await msg.add_reaction('?')
908 await asyncio.sleep(1)
909 await msg.add_reaction('?')
910 await asyncio.sleep(1)
911
912@bot.command(hidden=True, aliases=['creategame'])
913@commands.has_role("super_waifu")
914@commands.check(is_super_channel)
915@commands.guild_only()
916async def createrole(ctx, *, role):
917 """Create a mentionable role."""
918 role_colors = [discord.Color.orange(), discord.Color.from_rgb(54, 57, 63)]
919 guild = get_guild()
920 role_name = ascii_only(role).lower().replace(" ", "_")
921 role = get_role(role_name)
922 if role != None:
923 if role.color in role_colors:
924 role_type = "game"
925 else:
926 role_type = "role"
927 reply = f"{ctx.author.mention}, that {role_type} already exists."
928 await ctx.send(reply)
929 return
930 if ctx.invoked_with.lower() == "createrole":
931 role_type = "role"
932 emoji = ":passport_control:"
933 color = discord.Color.orange()
934 else:
935 role_type = "game"
936 emoji = ":video_game:"
937 color = discord.Color.blue()
938 role = await guild.create_role(name=role_name, color=color, mentionable=True)
939 title = f"**{role_type.upper()} CREATED {emoji}**"
940 description = f"{role_type.capitalize()}: {role.mention}\nCreated by: {ctx.author.mention}\n"
941 embed = discord.Embed(title=title, description=description, color=color)
942 await ctx.send(embed=embed)
943 return
944
945@bot.command(hidden=True, aliases=['deletegame'])
946@commands.has_role("super_waifu")
947@commands.check(is_super_channel)
948@commands.guild_only()
949async def deleterole(ctx, role: discord.Role):
950 """Delete a mentionable role."""
951 if role.name in config['roles']['forbidden'] or role.color not in [discord.Color.orange(), discord.Color.blue()]:
952 reply = f"{ctx.author.mention}, that is a Forbidden Role:tm:."
953 await ctx.send(reply)
954 return
955 if role.color == discord.Color.orange():
956 role_type = "role"
957 else:
958 role_type = "game"
959 title = f"**{role_type.upper()} DELETED :fire:**"
960 description = f"{role_type.capitalize()}: {role.name}\nDeleted by: {ctx.author.mention}\n"
961 embed = discord.Embed(title=title, description=description, color=role.color)
962 await role.delete()
963 await ctx.send(embed=embed)
964 return
965
966@bot.command(hidden=True)
967@commands.has_role("super_waifu")
968@commands.check(is_super_channel)
969@commands.guild_only()
970async def superwtf(ctx):
971 """Display this help message."""
972 reply = "Oh shit it's a Super_Waifu, everyone pretend like you aren't fucking shit up!\n\n"
973 for command in sorted(bot.commands, key=lambda x: x.name):
974 if command.hidden:
975 reply = reply + get_command_help(command) + "\n"
976 reply = reply + "\nIf I'm not working correctly, go fuck yourself, you aren't my boss."
977 await ctx.send(reply)
978 return
979
980@bot.command(hidden=True)
981@commands.has_role("super_waifu")
982@commands.check(is_super_channel)
983@commands.guild_only()
984async def invite(ctx, *, reason: typing.Union[discord.CategoryChannel, str]):
985 """Create an invite for the specified reason."""
986 if isinstance(reason, str):
987 event_name = reason
988 event = get_category(event_name)
989 else:
990 event_name = reason.name
991 event = reason
992 event_role = get_role(event_name)
993 if event_name in config["roles"]["forbidden"]:
994 reply = f"{ctx.author.mention}, that is not a valid event/role."
995 await ctx.send(reply)
996 return
997 channel = get_channel("welcome_and_rules")
998 if event is None or event_role is None:
999 type = "USER"
1000 event_name = None
1001 invite = await channel.create_invite(max_age=86400, max_uses=2, temporary=False, unique=True, reason=reason)
1002 else:
1003 type = "EVENT"
1004 reason = None
1005 invite = await channel.create_invite(max_uses=100, temporary=False, unique=True, reason=reason)
1006 store_invite_details(invite, ctx.author, reason, event_name)
1007 title = f"**{type} INVITE CREATED :love_letter:**"
1008 description = f"Created by: {ctx.author.mention}\nChannel: {channel.mention}\nReason: {reason}\n"
1009 embed = discord.Embed(title=title, description=description, color=waifu_pink)
1010 value = f"URL: {invite.url}"
1011 embed.add_field(name="Invite", value=value, inline=False)
1012 await ctx.send(embed=embed)
1013 return
1014
1015@bot.command(hidden=True)
1016@commands.has_role("super_waifu")
1017@commands.check(is_super_channel)
1018@commands.guild_only()
1019async def deletequote(ctx, id: typing.Optional[typing.Union[int, str]]):
1020 """Delete a quote by ID or URL"""
1021 guild = get_guild()
1022 if id == None:
1023 history = await ctx.channel.history(limit=25).flatten()
1024 for message in history:
1025 if len(message.attachments) == 1:
1026 id = message.attachments[0].url
1027 break
1028 if isinstance(id, str):
1029 try:
1030 id = id.split("_")[-1]
1031 id = id.split(".")[0]
1032 id = int(id)
1033 except:
1034 raise commands.UserInputError
1035 if not quote_exists(id):
1036 reply = "I can't find that quote in the database."
1037 await ctx.send(reply)
1038 return
1039 quote = delete_quote(id)
1040 try:
1041 author_mention = guild.get_member(int(quote[3])).mention
1042 except:
1043 author_mention = quote[4]
1044 try:
1045 stored_by_mention = guild.get_member(int(quote[5])).mention
1046 except:
1047 stored_by_mention = quote[6]
1048 text = quote[7]
1049 if not quote_exists(id):
1050 title = "**QUOTE DELETED :fire:**"
1051 description = f"Author: {author_mention}\nStored by: {stored_by_mention}\nDeleted by: {ctx.author.mention}\n"
1052 embed = discord.Embed(title=title, description=description, color=waifu_pink)
1053 value = f"\"{text}\""
1054 embed.add_field(name="Quote", value=value, inline=False)
1055 await ctx.send(embed=embed)
1056 return
1057
1058@bot.command(hidden=True)
1059@commands.has_role("admin")
1060@commands.guild_only()
1061async def notice(ctx):
1062 members = get_members_by_role("secret_senpai")
1063 guild = get_guild()
1064 if len(members) < 2:
1065 reply = "YOU MUST CONSTRUCT ADDITIONAL WEEBS"
1066 await ctx.send(reply)
1067 else:
1068 for i in range(100):
1069 mismatch = False
1070 senpai = members.copy()
1071 kohai = members.copy()
1072
1073 random.shuffle(senpai)
1074 random.shuffle(kohai)
1075
1076 for i in range(len(members)):
1077 if senpai[i] == kohai[i]:
1078 mismatch = True
1079 if senpai[i].id in config["secret_senpai_exclusions"]:
1080 if config["secret_senpai_exclusions"][senpai[i].id] == kohai[i].id:
1081 mismatch = True
1082
1083 if not mismatch:
1084 for i in range(len(members)):
1085 message = f"{senpai[i].display_name}, your covert kohai is {kohai[i].display_name} ({kohai[i].name}). Get to noticing!"
1086 try:
1087 await senpai[i].send(message)
1088 except:
1089 log.error(f"Unable to send message to {senpai[i]}")
1090 bot_testing = get_channel("bot_testing")
1091 warning = f"{senpai[i]} won't accept DM's. SPOILER: ||'{message}'||"
1092 await bot_testing.send(warning)
1093 log.info(message)
1094 secret_senpai = get_role("secret_senpai")
1095 message = f"{secret_senpai.mention}, check your DMs."
1096 await ctx.send(message)
1097 return
1098
1099 reply = "Unable to match members. Please check exclusions and try again."
1100 await ctx.send(reply)
1101 return
1102
1103@bot.command(hidden=True)
1104@commands.has_role("admin")
1105@commands.check(is_super_channel)
1106@commands.guild_only()
1107async def createevent(ctx, event: typing.Union[discord.CategoryChannel, str], *, YYYYMMDDHHMMSS: typing.Optional[str]):
1108 """Create an event category with channels and optional countdown."""
1109 if isinstance(event, str):
1110 event_name = event
1111 event = get_category(event_name)
1112 else:
1113 event_name = event.name
1114 event_role = get_role(event_name)
1115 if event is not None or event_role is not None:
1116 reply = f"{ctx.author.mention}, that event/role already exists."
1117 await ctx.send(reply)
1118 return
1119 if YYYYMMDDHHMMSS is not None:
1120 try:
1121 date_time_from_str(YYYYMMDDHHMMSS)
1122 except:
1123 reply = f"{ctx.author.mention}, '{YYYYMMDDHHMMSS}' is an invalid date format. Try: 'YYYYMMDDHHMMSS'"
1124 await ctx.send(reply)
1125 return
1126 name = ascii_only(event_name).replace(" ", "_").upper()
1127 guild = get_guild()
1128 noob_role = get_role("noob")
1129 quarantine_role = get_role("quarantine")
1130 event_role = await guild.create_role(name=name.lower(), mentionable=True, color=discord.Color.orange())
1131 countdown = {
1132 noob_role: discord.PermissionOverwrite(read_messages=False, send_messages=False, connect=False),
1133 guild.default_role: discord.PermissionOverwrite(send_messages=False, connect=False)
1134 }
1135 general = {
1136 noob_role: discord.PermissionOverwrite(read_messages=False, send_messages=False, connect=False),
1137 quarantine_role: discord.PermissionOverwrite(read_messages=False, send_messages=False, connect=False),
1138 event_role: discord.PermissionOverwrite(send_messages=True, connect=True),
1139 guild.default_role: discord.PermissionOverwrite(send_messages=False, connect=False)
1140 }
1141 quarantine = {
1142 noob_role: discord.PermissionOverwrite(read_messages=False, send_messages=False, connect=False),
1143 event_role: discord.PermissionOverwrite(send_messages=True, connect=True),
1144 guild.default_role: discord.PermissionOverwrite(send_messages=False, connect=False)
1145 }
1146 category = await guild.create_category_channel(name=name, overwrites=general)
1147 if YYYYMMDDHHMMSS is not None:
1148 topic = f"countdown to {YYYYMMDDHHMMSS}"
1149 await category.create_text_channel(name="countdown", overwrites=countdown, topic=topic)
1150 primary_channel = await category.create_text_channel(name=f"{name.lower()}_chat", overwrites=general)
1151 await category.create_voice_channel(name=f"{name.lower()}_voice", overwrites=general)
1152 await category.create_text_channel(name="questions", overwrites=general, slowmode_delay=300)
1153 await category.create_text_channel(name="looking_for_room", overwrites=general)
1154 await category.create_text_channel(name="quarantine_chat", overwrites=quarantine, slowmode_delay=10)
1155 await category.create_voice_channel(name="quarantine_voice", overwrites=quarantine)
1156 title = "**EVENT CREATED :confetti_ball:**"
1157 description = f"Event: {category.name}\nCreated by: {ctx.author.mention}\nPrimary channel: {primary_channel.mention}\nCountdown to: {YYYYMMDDHHMMSS}\n"
1158 embed = discord.Embed(title=title, description=description, color=waifu_pink)
1159 value = f"!invite {category.name} - Create 100 use event invite.\n!deleteevent {category.name} - Pretty self-explanatory.\n"
1160 embed.add_field(name="Commands", value=value, inline=False)
1161 await ctx.send(embed=embed)
1162 return
1163
1164@bot.command(hidden=True)
1165@commands.has_role("admin")
1166@commands.check(is_super_channel)
1167@commands.guild_only()
1168async def deleteevent(ctx, *, event: typing.Union[discord.CategoryChannel, str]):
1169 """Delete an event category and channels."""
1170 if isinstance(event, str):
1171 event_name = event.lower()
1172 event = get_category(event_name)
1173 else:
1174 event_name = event.name.lower()
1175 event_role = get_role(event_name)
1176 if event is None or event_role is None or event_name in config["roles"]["forbidden"]:
1177 reply = f"{ctx.author.mention}, that is not a valid event/role."
1178 await ctx.send(reply)
1179 return
1180 for channel in event.channels:
1181 await channel.delete()
1182 await event.delete()
1183 title = "**EVENT DELETED :cry:**"
1184 description = f"Event: {event.name}\nDeleted by: {ctx.author.mention}\n"
1185 embed = discord.Embed(title=title, description=description, color=waifu_pink)
1186 quarantined = get_members_by_role("quarantine")
1187 value = ""
1188 for member in quarantined:
1189 if event_role in member.roles:
1190 value = value + f"{member.mention}\n"
1191 if value != "":
1192 embed.add_field(name="Quarantined users", value=value, inline=False)
1193 await ctx.send(embed=embed)
1194 await event_role.delete()
1195 return
1196
1197@bot.command(hidden=True)
1198@commands.has_role("admin")
1199@commands.check(is_super_channel)
1200@commands.guild_only()
1201async def say(ctx, channel: discord.TextChannel, *, text: typing.Optional[str]):
1202 """Make me say something and/or post attachments."""
1203 if text == None and len(ctx.message.attachments) == 0:
1204 raise commands.UserInputError
1205 files = []
1206 file_paths = []
1207 for attachment in ctx.message.attachments:
1208 timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
1209 file_name = "{}_{}".format(timestamp, attachment.filename)
1210 file_path = os.path.join(sys.path[0], "tmp", file_name)
1211 await attachment.save(file_path)
1212 file = discord.File(file_path, filename=attachment.filename, spoiler=attachment.is_spoiler())
1213 files.append(file)
1214 file_paths.append(file_path)
1215 await channel.send(content=text, files=files)
1216 for file in files:
1217 file.close()
1218 for file_path in file_paths:
1219 os.remove(file_path)
1220 super_waifu_chat = get_channel("super_waifu_chat")
1221 reply = f"{ctx.author.mention} made me say something in {channel.mention}."
1222 await super_waifu_chat.send(reply)
1223 return
1224
1225@bot.command(hidden=True)
1226@commands.has_role("admin")
1227@commands.check(is_super_channel)
1228@commands.guild_only()
1229async def die(ctx):
1230 """Kill my currently running instance. I won't forget this."""
1231 reply = random.choice(strings['last_words'])
1232 await ctx.send(reply)
1233 exit(0)
1234 return
1235
1236
1237def api_get(query, key, is_image=None, num=1):
1238 url = ('https://www.googleapis.com/customsearch/v1?key=AIzaSyBTp9Xku6FdgfiCjTnhL-EMx0kXKWQhlc4&cx=012763604623577894851:r8w2tzy60qx'
1239 '&fields=items(title,link,snippet)&safe=off&nfpr=1' +
1240 ('&searchType=image' if is_image else ''))
1241 r = requests.get(url, params={'key': key, 'q': query, 'num': num})
1242 data = r.json()
1243
1244@bot.command(aliases=["search"])
1245@commands.guild_only()
1246async def g(ctx, inp, api_key=None):
1247 """Returns the first Google search result based on your query."""
1248 # google_api_key = config["api"]["google"]
1249
1250 parsed = api_get(inp, api_key)
1251 if 'items' not in parsed:
1252 return 'no results found'
1253
1254 out = '{link} -- \x02{title}\x02: "{snippet}"'.format(**parsed['items'][0])
1255 out = ' '.join(out.split())
1256 if len(out) > 300:
1257 out = out[:out.rfind(' ')] + '..."'
1258 reply = out
1259 await ctx.send(reply)
1260 return out
1261
1262global block_noobs
1263block_noobs = False
1264create_database()
1265token = config["discord"]["token"]
1266bot.run(token)