Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b3fe0218a | ||
|
|
17ac242cce | ||
|
|
c78f7e8b21 | ||
|
|
eadf478714 | ||
|
|
df554b9c4a | ||
|
|
872bb7c6bc | ||
|
|
4e7a77f270 | ||
|
|
86d7009288 | ||
|
|
0de7ed51bb | ||
|
|
e3fa64c85e | ||
|
|
329a88d12b | ||
|
|
c5ada7df8a | ||
|
|
fe3097a928 | ||
|
|
672c231b8a | ||
|
|
a06a80b268 | ||
|
|
df45fd9548 | ||
|
|
7039596bcf | ||
|
|
676d9e27b3 | ||
|
|
581824f5ff | ||
|
|
898dae5e6f | ||
|
|
11b7eb2e1f | ||
|
|
807a4715ab | ||
|
|
8f119435da | ||
|
|
70f52f8832 | ||
|
|
65b5b1a064 | ||
|
|
b90bb77be9 | ||
|
|
d19c0c2826 | ||
|
|
12ca45123b | ||
|
|
1e549fd96c | ||
|
|
c64252de6e | ||
|
|
ccad084b2b | ||
|
|
5076b4a1b7 | ||
|
|
869f4bb8da | ||
|
|
7f7357796e | ||
|
|
c3d8bb8d05 | ||
|
|
a1ca7dff60 | ||
|
|
ef12d46745 | ||
|
|
60274065f2 | ||
|
|
a05bf4ede4 | ||
|
|
85dee0ad8b | ||
|
|
82c33359b1 | ||
|
|
a888776a6c | ||
|
|
8fc1ff176f | ||
|
|
632aceaf89 | ||
|
|
780858e6d8 | ||
|
|
1b46f00812 | ||
|
|
80e5095ebc | ||
|
|
decf472b6c | ||
|
|
50f0ad6099 | ||
|
|
e8db484870 | ||
|
|
729dce1ae2 | ||
|
|
5d87c3dcdc | ||
|
|
6c6cdf1a20 | ||
|
|
d94fc1b596 | ||
|
|
e6181a704b | ||
|
|
3d68a3af31 | ||
|
|
ddff041974 | ||
|
|
b3012f117e | ||
|
|
46494784e4 | ||
|
|
9d803c4710 | ||
|
|
a74c15e73a | ||
|
|
c020dc5e89 | ||
|
|
00eddecbd9 | ||
|
|
fdecc629c5 | ||
|
|
3a7d27fbb3 | ||
|
|
2e9000160e | ||
|
|
0fadb07e48 | ||
|
|
2f52012b33 | ||
|
|
d4a70d5782 | ||
|
|
d57ba2d78a | ||
|
|
1d0b23c03f | ||
|
|
e8e58ce9e3 | ||
|
|
a7fb88bad2 | ||
|
|
f296c65ef2 | ||
|
|
ed1aa25490 | ||
|
|
24903d720c | ||
|
|
a79b59ee85 | ||
|
|
17d6a597a7 | ||
|
|
831740f18c |
20
README.md
20
README.md
@@ -5,12 +5,18 @@ adventure - Originally by Locastan. My version is a collaboration between Trusty
|
||||
|
||||
antiphoneclapper - Detects and removes bad GIFs posted in chat that have malformed frames. Thanks to Sitryk for all of the code that actually mattered for detection in this cog. Config stuff tacked on by me. Later iterations will include emoji recognizing and possibly link detection for these bad images.
|
||||
|
||||
away - Originally by Paddo, written for v3 by Axas, final tests by aikaterna. Set and unset a user as being "away".
|
||||
away - Originally by Paddo, written for v3 by Axas, final tests by aikaterna, and large improvements by TrustyJAID. Set and unset a user as being "away".
|
||||
|
||||
blurplefy - Make an avatar or an image upload blurple for Discord's 3rd anniversary.
|
||||
|
||||
cah - Cards Against Humanity, played in DM's. This can rate limit large bots via the sheer number of messages sent. Install and use with caution on larger bots.
|
||||
|
||||
chatchart - Generates a pie chart to display chat activity over the last 5000 messages. Requested by violetnyte.
|
||||
|
||||
dadjokes - Another UltimatePancake cog. Get some dad jokes on command.
|
||||
|
||||
dictonary - I stole this from UltimatePancake's v2 cogs, defines words via a dictonary. Only the "define" command for now until the dependency owner updates their files with a pending PR.
|
||||
|
||||
dungeon - New users with new accounts will be shuffled off to a locked channel on-join to help mitigate raiders. Please see the dungeon_readme.md file on this repo for more information.
|
||||
|
||||
imgwelcome - The repo can be found at: https://github.com/aikaterna/imgwelcome
|
||||
@@ -19,6 +25,8 @@ inspirobot - Fetch "inspirational" messages from inspirobot.me with [p]inspireme
|
||||
|
||||
leveler - A v3 port of Stevy's v2 leveler, originally by Fixator and modified by me. Add the repo at: https://github.com/aikaterna/Fixator10-Cogs
|
||||
|
||||
noflippedtables - A v3 port of irdumb's v2 cog with a little extra surprise included. Unflip all the tables.
|
||||
|
||||
nolinks - A very blunt hammer to remove anything that looks like a link. Roles can be whitelisted and it can watch multiple channels.
|
||||
|
||||
otherbot - Alert a role when bot(s) go offline.
|
||||
@@ -27,10 +35,18 @@ partycrash - A port of Will's partycrash command from the v2 Admin cog. This cog
|
||||
|
||||
pingtime - Show all shards' pingtimes.
|
||||
|
||||
pressf - A port/rewrite of NekoTony's v2 pressf cog. Pay your respects by pressing F.
|
||||
|
||||
pupper - A cog for Ryan5374. A pet that comes around on an on_message listener and waits for someone to pet it (react with a standard wave emoji), and rewards with credits. Many attributes are configurable.
|
||||
|
||||
region - A command to change the voice region of a server. Requires the guild admin or mod role or guild administrator.
|
||||
|
||||
retrosign - A v3 port of Anismash's retrosign cog: https://github.com/Anismash/Ani-Cogs/tree/master/retrosign
|
||||
|
||||
rndstatus - A v3 port of Twentysix's rndstatus cog with a couple extra settings.
|
||||
|
||||
snacktime - A v3 port of irdumb's snacktime cog. Now with friends!
|
||||
|
||||
timezone - A v3 port of Fishyfing's timezone cog with a few improvements.
|
||||
|
||||
trickortreat - A trick or treat-based competitive candy eating game with a leaderboard and other fun commands like stealing candy from guildmates.
|
||||
@@ -39,4 +55,6 @@ tools - A collection of mod and admin tools, ported from my v2 version. Sitryk i
|
||||
|
||||
wolfram - A v3 port of Paddo's abandoned Wolfram Alpha cog.
|
||||
|
||||
youtube - A v3 port of Paddo's youtube search cog for v2.
|
||||
|
||||
Are you looking for the v3 lavalink music cog? It's been added to Red v3 as the audio module. Install from develop and ask in the Red support server for help if you need it, or join my server. https://discord.gg/th6eS3T
|
||||
|
||||
@@ -7,6 +7,12 @@ from redbot.core import commands, checks, Config
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
listener = getattr(commands.Cog, "listener", None) # Trusty + Sinbad
|
||||
if listener is None:
|
||||
|
||||
def listener(name=None):
|
||||
return lambda x: x
|
||||
|
||||
|
||||
class AntiPhoneClapper(BaseCog):
|
||||
"""This cog deletes bad GIFs that will crash phone clients."""
|
||||
@@ -67,6 +73,7 @@ class AntiPhoneClapper(BaseCog):
|
||||
tile_sizes.append(im.tile[0][1][2:])
|
||||
return any([x[0] > limit[0] or x[1] > limit[1] for x in tile_sizes])
|
||||
|
||||
@listener()
|
||||
async def on_message(self, m):
|
||||
if not m.attachments:
|
||||
return
|
||||
@@ -106,5 +113,5 @@ class AntiPhoneClapper(BaseCog):
|
||||
else:
|
||||
return
|
||||
|
||||
def __unload(self):
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self.session.close())
|
||||
|
||||
@@ -8,6 +8,12 @@ IMAGE_LINKS = re.compile(r"(http[s]?:\/\/[^\"\']*\.(?:png|jpg|jpeg|gif|png))")
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
listener = getattr(commands.Cog, "listener", None) # Trusty + Sinbad
|
||||
if listener is None:
|
||||
|
||||
def listener(name=None):
|
||||
return lambda x: x
|
||||
|
||||
|
||||
class Away(BaseCog):
|
||||
"""Le away cog"""
|
||||
@@ -126,7 +132,7 @@ class Away(BaseCog):
|
||||
for word in message.split():
|
||||
match = re.search(r"<@!?([0-9]+)>", word)
|
||||
if match:
|
||||
user = await self.bot.get_user_info(int(match.group(1)))
|
||||
user = await self.bot.fetch_user(int(match.group(1)))
|
||||
message = re.sub(match.re, "@" + user.name, message)
|
||||
return message
|
||||
|
||||
@@ -183,6 +189,7 @@ class Away(BaseCog):
|
||||
return True
|
||||
return False
|
||||
|
||||
@listener()
|
||||
async def on_message(self, message):
|
||||
tmp = {}
|
||||
guild = message.guild
|
||||
|
||||
@@ -20,9 +20,7 @@ darkblurple = (78, 93, 148)
|
||||
white = (255, 255, 255)
|
||||
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
class Blurplefy(BaseCog):
|
||||
class Blurplefy(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
"""Blurplefy images and check content of images."""
|
||||
self.bot = bot
|
||||
@@ -93,7 +91,7 @@ class Blurplefy(BaseCog):
|
||||
else:
|
||||
picture = user.avatar_url
|
||||
try:
|
||||
async with self.session.request("GET", picture) as r:
|
||||
async with self.session.request("GET", str(picture)) as r:
|
||||
response = await r.read()
|
||||
except ValueError:
|
||||
await ctx.send("{}, please link a valid image URL.".format(ctx.author.display_name))
|
||||
@@ -120,7 +118,7 @@ class Blurplefy(BaseCog):
|
||||
role_check = False
|
||||
|
||||
try:
|
||||
async with self.session.request("GET", picture) as r:
|
||||
async with self.session.request("GET", str(picture)) as r:
|
||||
response = await r.read()
|
||||
except ValueError:
|
||||
await ctx.send("{}, please link a valid image URL.".format(ctx.author.display_name))
|
||||
@@ -226,7 +224,7 @@ class Blurplefy(BaseCog):
|
||||
else:
|
||||
picture = user.avatar_url
|
||||
try:
|
||||
async with self.session.request("GET", picture) as r:
|
||||
async with self.session.request("GET", str(picture)) as r:
|
||||
response = await r.read()
|
||||
except ValueError:
|
||||
await ctx.send("{}, please link a valid image URL.".format(ctx.author.display_name))
|
||||
@@ -398,21 +396,21 @@ class Blurplefy(BaseCog):
|
||||
|
||||
@commands.command()
|
||||
async def countdown(self, ctx):
|
||||
"""Countdown to Discord's 4th Anniversary."""
|
||||
"""Countdown to Discord's 5th Anniversary."""
|
||||
embed = discord.Embed(name="", colour=0x7289da)
|
||||
timeleft = (
|
||||
datetime.datetime(2018, 5, 13)
|
||||
datetime.datetime(2019, 5, 13)
|
||||
+ datetime.timedelta(hours=7)
|
||||
- datetime.datetime.utcnow()
|
||||
)
|
||||
embed.set_author(name="Time left until Discord's 4th Anniversary")
|
||||
embed.set_author(name="Time left until Discord's 5th Anniversary")
|
||||
if int(timeleft.total_seconds()) < 0:
|
||||
timeleft = (
|
||||
datetime.datetime(2019, 5, 13)
|
||||
datetime.datetime(2020, 5, 13)
|
||||
+ datetime.timedelta(hours=7)
|
||||
- datetime.datetime.utcnow()
|
||||
)
|
||||
embed.set_author(name="Time left until Discord's 4th Anniversary")
|
||||
embed.set_author(name="Time left until Discord's 5th Anniversary")
|
||||
embed.add_field(
|
||||
name="Countdown to midnight, May 13, California time (UTC-7):",
|
||||
value=("{}".format(self._dynamic_time(int(timeleft.total_seconds())))),
|
||||
@@ -437,5 +435,5 @@ class Blurplefy(BaseCog):
|
||||
msg = ""
|
||||
return msg.format(d, h, m, s)
|
||||
|
||||
def __unload(self):
|
||||
self.session.close()
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self.session.close())
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
from redbot.core import commands, data_manager
|
||||
from .cah import CardsAgainstHumanity
|
||||
|
||||
|
||||
def setup(bot: commands.Bot):
|
||||
n = CardsAgainstHumanity(bot)
|
||||
data_manager.load_bundled_data(n, __file__)
|
||||
bot.add_cog(n)
|
||||
def setup(bot):
|
||||
bot.add_cog(CardsAgainstHumanity(bot))
|
||||
|
||||
@@ -71,11 +71,18 @@ class CardsAgainstHumanity(BaseCog):
|
||||
return member
|
||||
# No member yet - try ID
|
||||
memID = "".join(list(filter(str.isdigit, name)))
|
||||
newMem = memberForID(memID, server)
|
||||
newMem = self.memberforid(memID, server)
|
||||
if newMem:
|
||||
return newMem
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def memberforid(checkid, server):
|
||||
for member in server.members:
|
||||
if str(member.id) == str(checkid):
|
||||
return member
|
||||
return None
|
||||
|
||||
def getreadabletimebetween(self, first, last):
|
||||
# A helper function to make a readable string between two times
|
||||
timeBetween = int(last - first)
|
||||
|
||||
18
cah/info.json
Normal file
18
cah/info.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"author": [
|
||||
"aikaterna"
|
||||
],
|
||||
"description": "Cards Against Humanity, played in DMs.",
|
||||
"install_msg": "Warning: This cog can send a lot of DMs, and the bot can become rate limited. Be warned that installing this cog on a bot with a lot of users or servers might be detrimental to the bot's performance. Thanks for installing, have fun.",
|
||||
"permissions" : [
|
||||
"embed_links",
|
||||
"manage_roles"
|
||||
],
|
||||
"short": "Cards Against Humanity.",
|
||||
"tags": [
|
||||
"CAH",
|
||||
"cards",
|
||||
"games"
|
||||
],
|
||||
"type": "COG"
|
||||
}
|
||||
5
dadjokes/__init__.py
Normal file
5
dadjokes/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .dadjokes import DadJokes
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(DadJokes(bot))
|
||||
16
dadjokes/dadjokes.py
Normal file
16
dadjokes/dadjokes.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from redbot.core import commands
|
||||
import aiohttp
|
||||
|
||||
class DadJokes(commands.Cog):
|
||||
"""Random dad jokes from icanhazdadjoke.com"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command()
|
||||
async def dadjoke(self, ctx):
|
||||
"""Gets a random dad joke."""
|
||||
api = 'https://icanhazdadjoke.com/'
|
||||
async with aiohttp.request('GET', api, headers={'Accept': 'text/plain'}) as r:
|
||||
result = await r.text()
|
||||
await ctx.send(f"`{result}`")
|
||||
10
dadjokes/info.json
Normal file
10
dadjokes/info.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"author": [
|
||||
"UltimatePancake"
|
||||
],
|
||||
"description": "Gets a random dad joke from icanhazdadjoke.com",
|
||||
"install_msg": "Gets a random dad joke from icanhazdadjoke.com. Thanks for installing.",
|
||||
"short": "Random dad jokes",
|
||||
"tags": ["jokes", "dad", "dadjokes"],
|
||||
"type": "COG"
|
||||
}
|
||||
5
dictionary/__init__.py
Normal file
5
dictionary/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .dictionary import Dictionary
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Dictionary(bot))
|
||||
117
dictionary/dictionary.py
Normal file
117
dictionary/dictionary.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import aiohttp
|
||||
from bs4 import BeautifulSoup
|
||||
import re
|
||||
from redbot.core import commands
|
||||
|
||||
|
||||
class Dictionary(commands.Cog):
|
||||
"""Word, yo
|
||||
Parts of this cog are adapted from the PyDictionary library."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.session = aiohttp.ClientSession()
|
||||
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self.session.close())
|
||||
|
||||
async def _get_soup_object(self, url):
|
||||
try:
|
||||
async with self.session.request("GET", url) as response:
|
||||
return BeautifulSoup(await response.text(), "html.parser")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
@commands.command()
|
||||
async def antonym(self, ctx, *, word: str):
|
||||
"""Displays antonyms for a given word."""
|
||||
search_msg = await ctx.send("Searching...")
|
||||
search_term = word.split(" ", 1)[0]
|
||||
result = await self._antonym(ctx, search_term)
|
||||
if not result:
|
||||
return await search_msg.edit(content="This word is not in the dictionary.")
|
||||
|
||||
result_text = "*, *".join(result)
|
||||
await search_msg.edit(content=f"Antonyms for **{search_term}**: *{result_text}*")
|
||||
|
||||
async def _antonym(self, ctx, word):
|
||||
data = await self._get_soup_object(f"http://www.thesaurus.com/browse/{word}")
|
||||
section = data.find_all("ul", {"class": "css-1lc0dpe et6tpn80"})
|
||||
try:
|
||||
section[1]
|
||||
except IndexError:
|
||||
return
|
||||
spans = section[1].findAll("li")
|
||||
antonyms = [span.text for span in spans[:50]]
|
||||
return antonyms
|
||||
|
||||
@commands.command()
|
||||
async def define(self, ctx, *, word: str):
|
||||
"""Displays definitions of a given word."""
|
||||
search_msg = await ctx.send("Searching...")
|
||||
search_term = word.split(" ", 1)[0]
|
||||
result = await self._definition(ctx, search_term)
|
||||
str_buffer = ""
|
||||
if not result:
|
||||
return await search_msg.edit(content="This word is not in the dictionary.")
|
||||
for key in result:
|
||||
str_buffer += f"\n**{key}**: \n"
|
||||
counter = 1
|
||||
j = False
|
||||
for val in result[key]:
|
||||
if val.startswith("("):
|
||||
str_buffer += f"{str(counter)}. *{val})* "
|
||||
counter += 1
|
||||
j = True
|
||||
else:
|
||||
if j:
|
||||
str_buffer += f"{val}\n"
|
||||
j = False
|
||||
else:
|
||||
str_buffer += f"{str(counter)}. {val}\n"
|
||||
counter += 1
|
||||
await search_msg.edit(content=str_buffer)
|
||||
|
||||
async def _definition(self, ctx, word):
|
||||
html = await self._get_soup_object(f"http://wordnetweb.princeton.edu/perl/webwn?s={word}")
|
||||
types = html.findAll("h3")
|
||||
length = len(types)
|
||||
lists = html.findAll("ul")
|
||||
out = {}
|
||||
if not lists:
|
||||
return
|
||||
for a in types:
|
||||
reg = str(lists[types.index(a)])
|
||||
meanings = []
|
||||
for x in re.findall(r">\s\((.*?)\)\s<", reg):
|
||||
if "often followed by" in x:
|
||||
pass
|
||||
elif len(x) > 5 or " " in str(x):
|
||||
meanings.append(x)
|
||||
name = a.text
|
||||
out[name] = meanings
|
||||
return out
|
||||
|
||||
async def _synonym(self, ctx, word):
|
||||
data = await self._get_soup_object(f"http://www.thesaurus.com/browse/{word}")
|
||||
section = data.find_all("ul", {"class": "css-1lc0dpe et6tpn80"})
|
||||
try:
|
||||
section[1]
|
||||
except IndexError:
|
||||
return
|
||||
spans = section[0].findAll("li")
|
||||
synonyms = [span.text for span in spans[:50]]
|
||||
return synonyms
|
||||
|
||||
@commands.command()
|
||||
async def synonym(self, ctx, *, word: str):
|
||||
"""Displays synonyms for a given word."""
|
||||
search_msg = await ctx.send("Searching...")
|
||||
search_term = word.split(" ", 1)[0]
|
||||
result = await self._synonym(ctx, search_term)
|
||||
if not result:
|
||||
return await search_msg.edit(content="This word is not in the dictionary.")
|
||||
|
||||
result_text = "*, *".join(result)
|
||||
await search_msg.edit(content=f"Synonyms for **{search_term}**: *{result_text}*")
|
||||
15
dictionary/info.json
Normal file
15
dictionary/info.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"author": [
|
||||
"UltimatePancake", "aikaterna"
|
||||
],
|
||||
"description": "Gets definitions, antonyms, or synonyms for given words",
|
||||
"install_msg": "After loading the cog with `[p]load dictionary`, use [p]help Dictionary to view commands.",
|
||||
"short": "Gets definitions, antonyms, or synonyms for given words",
|
||||
"tags": [
|
||||
"dictionary"
|
||||
],
|
||||
"requirements": [
|
||||
"beautifulsoup4"
|
||||
],
|
||||
"type": "COG"
|
||||
}
|
||||
@@ -2,12 +2,9 @@ import asyncio
|
||||
import datetime
|
||||
import discord
|
||||
from redbot.core import Config, commands, checks, modlog
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
class Dungeon(BaseCog):
|
||||
class Dungeon(commands.Cog):
|
||||
"""Auto-quarantine suspicious users."""
|
||||
|
||||
def __init__(self, bot):
|
||||
@@ -348,6 +345,7 @@ class Dungeon(BaseCog):
|
||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, description=msg)
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_join(self, member):
|
||||
default_avatar = False
|
||||
toggle = await self.config.guild(member.guild).toggle()
|
||||
|
||||
@@ -3,9 +3,7 @@ import discord
|
||||
from redbot.core import commands
|
||||
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
class Inspirobot(BaseCog):
|
||||
class Inspirobot(commands.Cog):
|
||||
"""Posts images generated by https://inspirobot.me"""
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
@@ -25,5 +23,5 @@ class Inspirobot(BaseCog):
|
||||
except Exception as e:
|
||||
await ctx.send(f"Oops, there was a problem: {e}")
|
||||
|
||||
def __unload(self):
|
||||
self.session.close()
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self.session.close())
|
||||
|
||||
5
noflippedtables/__init__.py
Normal file
5
noflippedtables/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .noflippedtables import NoFlippedTables
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(NoFlippedTables(bot))
|
||||
9
noflippedtables/info.json
Normal file
9
noflippedtables/info.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name" : "NoFlippedTables",
|
||||
"author" : ["irdumb", "aikaterna"],
|
||||
"short" : "Unflip some tables.",
|
||||
"description" : "Unflip all the flipped tables.",
|
||||
"install_msg" : "Usage: [p]help tableset",
|
||||
"tags" : ["noflippedtables", "no flip", "tables"],
|
||||
"disabled" : false
|
||||
}
|
||||
131
noflippedtables/noflippedtables.py
Normal file
131
noflippedtables/noflippedtables.py
Normal file
@@ -0,0 +1,131 @@
|
||||
import asyncio
|
||||
from random import uniform as randfloat
|
||||
import re
|
||||
from redbot.core import commands, checks, Config
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
|
||||
|
||||
class NoFlippedTables(commands.Cog):
|
||||
"""For the table sympathizers"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, 2712290002, force_registration=True)
|
||||
|
||||
default_guild = {
|
||||
"ALL_TABLES": True,
|
||||
"BOT_EXEMPT": False,
|
||||
"SNACKBEAR": False,
|
||||
"TOGGLE": False,
|
||||
}
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
self.flippedTables = {}
|
||||
|
||||
@checks.mod_or_permissions(manage_guild=True)
|
||||
@commands.group()
|
||||
async def tableset(self, ctx):
|
||||
"""Got some nice settings for my UNflipped tables"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
settings = await self.config.guild(ctx.guild).all()
|
||||
msg = "[Current Settings]\n"
|
||||
for k, v in settings.items():
|
||||
msg += str(k) + ": " + str(v) + "\n"
|
||||
await ctx.send(box(msg, lang="ini"))
|
||||
|
||||
@tableset.command()
|
||||
async def flipall(self, ctx):
|
||||
"""Enables/disables right all unflipped tables in a message"""
|
||||
settings = await self.config.guild(ctx.guild).ALL_TABLES()
|
||||
await self.config.guild(ctx.guild).ALL_TABLES.set(not settings)
|
||||
if not settings:
|
||||
await ctx.send("All tables will now be unflipped.")
|
||||
else:
|
||||
await ctx.send("Now only one table unflipped per message.")
|
||||
|
||||
@tableset.command()
|
||||
async def flipbot(self, ctx):
|
||||
"""Enables/disables allowing bot to flip tables"""
|
||||
settings = await self.config.guild(ctx.guild).BOT_EXEMPT()
|
||||
await self.config.guild(ctx.guild).BOT_EXEMPT.set(not settings)
|
||||
if not settings:
|
||||
await ctx.send("Bot is now allowed to leave its own tables flipped.")
|
||||
else:
|
||||
await ctx.send("Bot must now unflip tables that itself flips.")
|
||||
|
||||
@tableset.command()
|
||||
async def snackbear(self, ctx):
|
||||
"""Snackburr is unflipping tables!"""
|
||||
settings = await self.config.guild(ctx.guild).SNACKBEAR()
|
||||
await self.config.guild(ctx.guild).SNACKBEAR.set(not settings)
|
||||
if not settings:
|
||||
await ctx.send("Snackburr will now unflip tables.")
|
||||
else:
|
||||
await ctx.send("Snackburr is heading off for his errands!")
|
||||
|
||||
@tableset.command()
|
||||
async def toggle(self, ctx):
|
||||
"""Toggle the unflipping on or off."""
|
||||
settings = await self.config.guild(ctx.guild).TOGGLE()
|
||||
await self.config.guild(ctx.guild).TOGGLE.set(not settings)
|
||||
if not settings:
|
||||
await ctx.send("No table shall be left unflipped in this server.")
|
||||
else:
|
||||
await ctx.send("No more unflipping here.")
|
||||
|
||||
@commands.Cog.listener()
|
||||
# so much fluff just for this OpieOP
|
||||
async def on_message(self, message):
|
||||
channel = message.channel
|
||||
user = message.author
|
||||
if not message.guild:
|
||||
return
|
||||
if hasattr(user, "bot") and user.bot is True:
|
||||
return
|
||||
toggle = await self.config.guild(message.guild).TOGGLE()
|
||||
if not toggle:
|
||||
return
|
||||
if channel.id not in self.flippedTables:
|
||||
self.flippedTables[channel.id] = {}
|
||||
# ┬─┬ ┬┬ ┻┻ ┻━┻ ┬───┬ ┻━┻ will leave 3 tables left flipped
|
||||
# count flipped tables
|
||||
for m in re.finditer("┻━*┻|┬─*┬", message.content):
|
||||
t = m.group()
|
||||
bot_exempt = await self.config.guild(message.guild).BOT_EXEMPT()
|
||||
if "┻" in t and not (message.author.id == self.bot.user.id and bot_exempt):
|
||||
if t in self.flippedTables[channel.id]:
|
||||
self.flippedTables[channel.id][t] += 1
|
||||
else:
|
||||
self.flippedTables[channel.id][t] = 1
|
||||
all_tables = await self.config.guild(message.guild).ALL_TABLES()
|
||||
if not all_tables:
|
||||
break
|
||||
else:
|
||||
f = t.replace("┬", "┻").replace("─", "━")
|
||||
if f in self.flippedTables[channel.id]:
|
||||
if self.flippedTables[channel.id][f] <= 0:
|
||||
del self.flippedTables[channel.id][f]
|
||||
else:
|
||||
self.flippedTables[channel.id][f] -= 1
|
||||
# wait random time. some tables may be unflipped by now.
|
||||
await asyncio.sleep(randfloat(0, 1.5))
|
||||
tables = ""
|
||||
|
||||
deleteTables = []
|
||||
# unflip tables in self.flippedTables[channel.id]
|
||||
for t, n in self.flippedTables[channel.id].items():
|
||||
snackburr = await self.config.guild(message.guild).SNACKBEAR()
|
||||
if snackburr:
|
||||
unflipped = t.replace("┻", "┬").replace("━", "─") + " ノʕ •ᴥ•ノʔ" + "\n"
|
||||
else:
|
||||
unflipped = t.replace("┻", "┬").replace("━", "─") + " ノ( ゜-゜ノ)" + "\n"
|
||||
for i in range(0, n):
|
||||
tables += unflipped
|
||||
# in case being processed in parallel
|
||||
self.flippedTables[channel.id][t] -= 1
|
||||
deleteTables.append(t)
|
||||
for t in deleteTables:
|
||||
del self.flippedTables[channel.id][t]
|
||||
if tables != "":
|
||||
await channel.send(tables)
|
||||
@@ -5,6 +5,12 @@ from redbot.core import Config, commands, checks
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
listener = getattr(commands.Cog, "listener", None) # Trusty + Sinbad
|
||||
if listener is None:
|
||||
|
||||
def listener(name=None):
|
||||
return lambda x: x
|
||||
|
||||
class NoLinks(BaseCog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
@@ -104,6 +110,7 @@ class NoLinks(BaseCog):
|
||||
await self.config.guild(ctx.guild).watching.set(channel_list)
|
||||
await ctx.send(f"{self.bot.get_channel(channel.id).mention} will not have links removed.")
|
||||
|
||||
@listener()
|
||||
async def on_message(self, message):
|
||||
if isinstance(message.channel, discord.abc.PrivateChannel):
|
||||
return
|
||||
|
||||
@@ -4,6 +4,11 @@ from redbot.core import commands, checks, Config
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
listener = getattr(commands.Cog, "listener", None) # Trusty + Sinbad
|
||||
if listener is None:
|
||||
|
||||
def listener(name=None):
|
||||
return lambda x: x
|
||||
|
||||
class Otherbot(BaseCog):
|
||||
def __init__(self, bot):
|
||||
@@ -23,7 +28,13 @@ class Otherbot(BaseCog):
|
||||
|
||||
@otherbot.command()
|
||||
async def channel(self, ctx, channel: discord.TextChannel = None):
|
||||
"""Sets the channel to report in."""
|
||||
"""
|
||||
Sets the channel to report in.
|
||||
|
||||
Default to the current one.
|
||||
"""
|
||||
if not channel:
|
||||
channel = ctx.channel
|
||||
await self.config.guild(ctx.guild).reporting.set(channel.id)
|
||||
await ctx.send(f"Reporting channel set to: {channel.mention}.")
|
||||
|
||||
@@ -56,7 +67,7 @@ class Otherbot(BaseCog):
|
||||
msg += "None."
|
||||
if not bot_user:
|
||||
for saved_bot_id in data["watching"]:
|
||||
bot_user = await self.bot.get_user_info(saved_bot_id)
|
||||
bot_user = await self.bot.fetch_user(saved_bot_id)
|
||||
if len(bot_user.name) > 16:
|
||||
bot_name = f"{bot_user.name:16}...#{bot_user.discriminator}"
|
||||
else:
|
||||
@@ -75,7 +86,10 @@ class Otherbot(BaseCog):
|
||||
f"Reporting channel set to: {ctx.message.channel.mention}. Use `{ctx.prefix}otherbot channel` to change this."
|
||||
)
|
||||
|
||||
@listener()
|
||||
async def on_member_update(self, before, after):
|
||||
if after.guild is None or not after.bot:
|
||||
return
|
||||
data = await self.config.guild(after.guild).all()
|
||||
if after.status == discord.Status.offline and (after.id in data["watching"]):
|
||||
channel_object = self.bot.get_channel(data["reporting"])
|
||||
|
||||
4
pressf/__init__.py
Normal file
4
pressf/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .pressf import PressF
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(PressF(bot))
|
||||
13
pressf/info.json
Normal file
13
pressf/info.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"author": [
|
||||
"aikaterna"
|
||||
],
|
||||
"description": "Pay respects to a thing or user by pressing f.",
|
||||
"install_msg": "Thanks for installing, have fun.",
|
||||
"short": "Press f to pay respects.",
|
||||
"tags": [
|
||||
"pressf",
|
||||
"respects"
|
||||
],
|
||||
"type": "COG"
|
||||
}
|
||||
66
pressf/pressf.py
Normal file
66
pressf/pressf.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import asyncio
|
||||
import discord
|
||||
from redbot.core import commands
|
||||
from redbot.core.utils.common_filters import filter_mass_mentions
|
||||
|
||||
|
||||
class PressF(commands.Cog):
|
||||
"""Pay some respects."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.channels = {}
|
||||
|
||||
@commands.command()
|
||||
@commands.bot_has_permissions(add_reactions=True)
|
||||
async def pressf(self, ctx, *, user: discord.User = None):
|
||||
"""Pay respects by pressing F"""
|
||||
if str(ctx.channel.id) in self.channels:
|
||||
return await ctx.send(
|
||||
"Oops! I'm still paying respects in this channel, you'll have to wait until I'm done."
|
||||
)
|
||||
self.channels[str(ctx.channel.id)] = {}
|
||||
|
||||
if user:
|
||||
answer = user.display_name
|
||||
else:
|
||||
await ctx.send("What do you want to pay respects to?")
|
||||
|
||||
def check(m):
|
||||
return m.author == ctx.author and m.channel == ctx.channel
|
||||
|
||||
try:
|
||||
pressf = await ctx.bot.wait_for("message", timeout=120.0, check=check)
|
||||
except asyncio.TimeoutError:
|
||||
del self.channels[str(ctx.channel.id)]
|
||||
return await ctx.send("You took too long to reply.")
|
||||
|
||||
answer = pressf.content[:1900]
|
||||
|
||||
message = await ctx.send(
|
||||
f"Everyone, let's pay respects to **{filter_mass_mentions(answer)}**! Press the f reaction on the this message to pay respects."
|
||||
)
|
||||
await message.add_reaction("\U0001f1eb")
|
||||
self.channels[str(ctx.channel.id)] = {'msg_id': message.id, 'reacted': []}
|
||||
await asyncio.sleep(120)
|
||||
try:
|
||||
await message.delete()
|
||||
except (discord.errors.NotFound, discord.errors.Forbidden):
|
||||
pass
|
||||
amount = len(self.channels[str(ctx.channel.id)]['reacted'])
|
||||
word = "person has" if amount == 1 else "people have"
|
||||
await ctx.send(f"**{amount}** {word} paid respects to **{filter_mass_mentions(answer)}**.")
|
||||
del self.channels[str(ctx.channel.id)]
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_reaction_add(self, reaction, user):
|
||||
if str(reaction.message.channel.id) not in self.channels:
|
||||
return
|
||||
if self.channels[str(reaction.message.channel.id)]['msg_id'] != reaction.message.id:
|
||||
return
|
||||
if user.id == self.bot.user.id:
|
||||
return
|
||||
if user.id not in self.channels[str(reaction.message.channel.id)]['reacted']:
|
||||
if str(reaction.emoji) == "\U0001f1eb":
|
||||
await reaction.message.channel.send(f"**{user.name}** has paid their respects.")
|
||||
self.channels[str(reaction.message.channel.id)]['reacted'].append(user.id)
|
||||
5
pupper/__init__.py
Normal file
5
pupper/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .pupper import Pupper
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
bot.add_cog(Pupper(bot))
|
||||
12
pupper/info.json
Normal file
12
pupper/info.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"author": [
|
||||
"aikaterna"
|
||||
],
|
||||
"description": "Pupper is a configurable pet that can hand out credits.",
|
||||
"install_msg": "This cog and its countdowns are triggered by messages. If your bot doesn't see any messages posted anywhere in any server, the pet will never come around. This pet helps to keep chat active but it needs to be slightly active in the first place. If you see a message asking for pats (or whatever other greeting message you set), the first person to react with a wave emoji will get a random amount of credits. This cog also deletes the messages involved in the game, which can lead to channels showing an unread message when there really isn't anything there any more. Use `[p]help pets` to get started. If this command doesn't work for you, the `pets` command is only usable by people with the bot's Admin or Mod role, or if the user has administrative perms on the Discord server.",
|
||||
"short": "Pet the dog.",
|
||||
"tags": [
|
||||
"pets"
|
||||
],
|
||||
"type": "COG"
|
||||
}
|
||||
225
pupper/pupper.py
Normal file
225
pupper/pupper.py
Normal file
@@ -0,0 +1,225 @@
|
||||
import asyncio
|
||||
import datetime
|
||||
import discord
|
||||
import random
|
||||
from redbot.core import commands, checks, Config, bank
|
||||
from redbot.core.utils.chat_formatting import box, humanize_list
|
||||
|
||||
|
||||
class Pupper(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, 2767241393, force_registration=True)
|
||||
self.pets = {}
|
||||
|
||||
default_guild = {
|
||||
"borf_msg": "borf! (thank for pats h00man, have a doggocoin)",
|
||||
"channel": [],
|
||||
"cooldown": 3600,
|
||||
"credits": [100, 500],
|
||||
"hello_msg": "Hi! Can someone pet me?",
|
||||
"last_pet": "2019-08-01 00:00:00.000001",
|
||||
"toggle": False,
|
||||
}
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@commands.group()
|
||||
async def pets(self, ctx):
|
||||
"""Manage your pet."""
|
||||
pass
|
||||
|
||||
@pets.command()
|
||||
async def toggle(self, ctx):
|
||||
"""Toggle pets on the server."""
|
||||
toggle = await self.config.guild(ctx.guild).toggle()
|
||||
msg = f"Pets active: {not toggle}.\n"
|
||||
await self.config.guild(ctx.guild).toggle.set(not toggle)
|
||||
await ctx.send(msg)
|
||||
|
||||
@pets.command()
|
||||
async def cooldown(self, ctx, seconds: int = None):
|
||||
"""Set the pet appearance cooldown in seconds.
|
||||
|
||||
300s/5 minute minimum. Default is 3600s/1 hour."""
|
||||
|
||||
if not seconds:
|
||||
seconds = 3600
|
||||
if seconds < 300:
|
||||
seconds = 300
|
||||
await self.config.guild(ctx.guild).cooldown.set(seconds)
|
||||
await ctx.send(f"Pet appearance cooldown set to {seconds}.")
|
||||
|
||||
@pets.command()
|
||||
async def credits(self, ctx, min_amt: int, max_amt: int):
|
||||
"""Set the pet credits range on successful petting."""
|
||||
if min_amt > max_amt:
|
||||
return await ctx.send("Min must be less than max.")
|
||||
if min_amt < 1 or max_amt < 1:
|
||||
return await ctx.send("Min and max amounts must be greater than 1.")
|
||||
await self.config.guild(ctx.guild).credits.set([min_amt, max_amt])
|
||||
await ctx.send(f"Pet credit range set to {min_amt} - {max_amt}.")
|
||||
|
||||
@pets.command()
|
||||
async def hello(self, ctx, *, message: str = None):
|
||||
"""Set the pet greeting message."""
|
||||
if not message:
|
||||
hello = await self.config.guild(ctx.guild).hello_msg()
|
||||
return await ctx.send(
|
||||
f"Current greeting message: `{hello}`\nUse this command with the message you would like to set."
|
||||
)
|
||||
if len(message) > 1000:
|
||||
return await ctx.send("That dog sure likes to talk a lot. Try a shorter message.")
|
||||
await self.config.guild(ctx.guild).hello_msg.set(message)
|
||||
await ctx.send(f"Pet hello message set to: `{message}`.")
|
||||
|
||||
@pets.command()
|
||||
async def thanks(self, ctx, *, message: str = None):
|
||||
"""Set the pet thanks message."""
|
||||
if not message:
|
||||
bye = await self.config.guild(ctx.guild).borf_msg()
|
||||
return await ctx.send(
|
||||
f"Current thanks message: `{bye}`\nUse this command with the message you would like to set."
|
||||
)
|
||||
if len(message) > 1000:
|
||||
return await ctx.send("That dog sure likes to talk a lot. Try a shorter message.")
|
||||
await self.config.guild(ctx.guild).borf_msg.set(message)
|
||||
await ctx.send(f"Pet thanks message set to: `{message}`.")
|
||||
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@pets.group(invoke_without_command=True)
|
||||
async def channel(self, ctx):
|
||||
"""Channel management for pet appearance."""
|
||||
await ctx.send_help()
|
||||
channel_list = await self.config.guild(ctx.guild).channel()
|
||||
channel_msg = "Petting Channels:\n"
|
||||
if not channel_list:
|
||||
channel_msg += "None."
|
||||
for chan in channel_list:
|
||||
channel_obj = self.bot.get_channel(chan)
|
||||
channel_msg += f"{channel_obj.name}\n"
|
||||
await ctx.send(box(channel_msg))
|
||||
|
||||
@channel.command()
|
||||
async def add(self, ctx, channel: discord.TextChannel):
|
||||
"""Add a text channel for pets."""
|
||||
channel_list = await self.config.guild(ctx.guild).channel()
|
||||
if channel.id not in channel_list:
|
||||
channel_list.append(channel.id)
|
||||
await self.config.guild(ctx.guild).channel.set(channel_list)
|
||||
await ctx.send(f"{channel.mention} added to the valid petting channels.")
|
||||
else:
|
||||
await ctx.send(f"{channel.mention} is already in the list of petting channels.")
|
||||
|
||||
@channel.command()
|
||||
async def addall(self, ctx):
|
||||
"""Add all valid channels for the guild that the bot can speak in."""
|
||||
bot_text_channels = [
|
||||
c
|
||||
for c in ctx.guild.text_channels
|
||||
if c.permissions_for(ctx.guild.me).send_messages is True
|
||||
]
|
||||
channel_list = await self.config.guild(ctx.guild).channel()
|
||||
channels_appended = []
|
||||
channels_in_list = []
|
||||
|
||||
for text_channel in bot_text_channels:
|
||||
if text_channel.id not in channel_list:
|
||||
channel_list.append(text_channel.id)
|
||||
channels_appended.append(text_channel.mention)
|
||||
else:
|
||||
channels_in_list.append(text_channel.mention)
|
||||
pass
|
||||
|
||||
first_msg = ""
|
||||
second_msg = ""
|
||||
await self.config.guild(ctx.guild).channel.set(channel_list)
|
||||
if len(channels_appended) > 0:
|
||||
first_msg = (
|
||||
f"{humanize_list(channels_appended)} added to the valid petting channels.\n"
|
||||
)
|
||||
if len(channels_in_list) > 0:
|
||||
second_msg = (
|
||||
f"{humanize_list(channels_in_list)}: already in the list of petting channels."
|
||||
)
|
||||
await ctx.send(f"{first_msg}{second_msg}")
|
||||
|
||||
@channel.command()
|
||||
async def remove(self, ctx, channel: discord.TextChannel):
|
||||
"""Remove a text channel from petting."""
|
||||
channel_list = await self.config.guild(ctx.guild).channel()
|
||||
if channel.id in channel_list:
|
||||
channel_list.remove(channel.id)
|
||||
else:
|
||||
return await ctx.send(f"{channel.mention} not in the active channel list.")
|
||||
await self.config.guild(ctx.guild).channel.set(channel_list)
|
||||
await ctx.send(f"{channel.mention} removed from the list of petting channels.")
|
||||
|
||||
@channel.command()
|
||||
async def removeall(self, ctx):
|
||||
"""Remove all petting channels from the list."""
|
||||
await self.config.guild(ctx.guild).channel.set([])
|
||||
await ctx.send("All channels have been removed from the list of petting channels.")
|
||||
|
||||
def _pet_lock(self, guild_id, tf):
|
||||
if tf:
|
||||
self.pets[guild_id] = True
|
||||
else:
|
||||
self.pets[guild_id] = False
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if isinstance(message.channel, discord.abc.PrivateChannel):
|
||||
return
|
||||
if message.author.bot:
|
||||
return
|
||||
guild_data = await self.config.guild(message.guild).all()
|
||||
if not guild_data["toggle"]:
|
||||
return
|
||||
if not guild_data["channel"]:
|
||||
return
|
||||
self.pets.setdefault(message.guild.id, False)
|
||||
if self.pets[message.guild.id]:
|
||||
return
|
||||
|
||||
last_time = datetime.datetime.strptime(str(guild_data["last_pet"]), "%Y-%m-%d %H:%M:%S.%f")
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
now = now.replace(tzinfo=None)
|
||||
if (
|
||||
int((now - last_time).total_seconds())
|
||||
> await self.config.guild(message.guild).cooldown()
|
||||
):
|
||||
self._pet_lock(message.guild.id, True)
|
||||
rando_channel = random.choice(guild_data["channel"])
|
||||
await asyncio.sleep(random.randint(60, 480))
|
||||
rando_channel_obj = self.bot.get_channel(rando_channel)
|
||||
borf_msg = await rando_channel_obj.send(guild_data["hello_msg"])
|
||||
pets = "👋"
|
||||
pets_action = {"veryfastpats": "👋"}
|
||||
|
||||
def check(r, u):
|
||||
return r.message.id == borf_msg.id and any(e in str(r.emoji) for e in pets)
|
||||
|
||||
try:
|
||||
r, u = await self.bot.wait_for("reaction_add", check=check, timeout=300.0)
|
||||
except asyncio.TimeoutError:
|
||||
return await borf_msg.delete()
|
||||
|
||||
reacts = {v: k for k, v in pets_action.items()}
|
||||
react = reacts[r.emoji]
|
||||
if react == "veryfastpats":
|
||||
await borf_msg.delete()
|
||||
deposit = random.randint(guild_data["credits"][0], guild_data["credits"][1])
|
||||
await bank.deposit_credits(u, deposit)
|
||||
credits_name = await bank.get_currency_name(message.guild)
|
||||
await rando_channel_obj.send(
|
||||
content=f"{guild_data['borf_msg']} (`+{deposit}` {credits_name})",
|
||||
delete_after=10,
|
||||
)
|
||||
else:
|
||||
pass
|
||||
self._pet_lock(message.guild.id, False)
|
||||
await self.config.guild(message.guild).last_pet.set(str(now))
|
||||
5
region/__init__.py
Normal file
5
region/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .region import Region
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Region())
|
||||
9
region/info.json
Normal file
9
region/info.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name" : "region",
|
||||
"author" : ["aikaterna"],
|
||||
"short" : "Change the Discord server's region.",
|
||||
"description" : "Change the Discord server's region with a command.",
|
||||
"install_msg" : "Thanks for installing.",
|
||||
"tags" : ["voice region", "region"],
|
||||
"disabled" : false
|
||||
}
|
||||
49
region/region.py
Normal file
49
region/region.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import discord
|
||||
from redbot.core import checks, commands
|
||||
from redbot.core.utils.chat_formatting import humanize_list
|
||||
|
||||
|
||||
class Region(commands.Cog):
|
||||
"""Change the guild voice region."""
|
||||
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@commands.cooldown(1, 60, discord.ext.commands.BucketType.guild)
|
||||
@commands.command()
|
||||
async def region(self, ctx, *, region: str):
|
||||
"""Set the current guild's voice region."""
|
||||
regions = [
|
||||
"japan",
|
||||
"singapore",
|
||||
"eu-central",
|
||||
"europe",
|
||||
"india",
|
||||
"us-central",
|
||||
"london",
|
||||
"eu-west",
|
||||
"amsterdam",
|
||||
"brazil",
|
||||
"dubai",
|
||||
"us-west",
|
||||
"hongkong",
|
||||
"us-south",
|
||||
"southafrica",
|
||||
"us-east",
|
||||
"sydney",
|
||||
"frankfurt",
|
||||
"russia",
|
||||
]
|
||||
region = region.replace(" ", "-")
|
||||
if region not in regions:
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
return await ctx.send(
|
||||
f"`{region}` was not found in the list of Discord voice regions. Valid regions are: {humanize_list(regions)}."
|
||||
)
|
||||
try:
|
||||
await ctx.guild.edit(region=region)
|
||||
except discord.errors.Forbidden:
|
||||
return await ctx.send("I don't have permissions to edit this guild's settings.")
|
||||
except discord.errors.HTTPException:
|
||||
return await ctx.send(f"Error: An invalid server region was passed: `{region}`")
|
||||
await ctx.send(
|
||||
f"The voice server region for `{ctx.guild.name}` has been changed to `{region}`."
|
||||
)
|
||||
@@ -12,9 +12,7 @@ import re
|
||||
import unicodedata
|
||||
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
class Retrosign(BaseCog):
|
||||
class Retrosign(commands.Cog):
|
||||
"""Make an 80s retro sign. Originally by Anismash"""
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
@@ -67,7 +65,7 @@ class Retrosign(BaseCog):
|
||||
|
||||
async with ctx.channel.typing():
|
||||
async with self.session.post(
|
||||
"http://photofunia.com/effects/retro-wave", data=data
|
||||
"https://photofunia.com/effects/retro-wave", data=data
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
soup = bs(await response.text(), "html.parser")
|
||||
@@ -79,5 +77,5 @@ class Retrosign(BaseCog):
|
||||
image = discord.File(fp=temp_image, filename="image.png")
|
||||
await ctx.channel.send(file=image)
|
||||
|
||||
def __unload(self):
|
||||
self.session.detach()
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self.session.close())
|
||||
|
||||
@@ -2,6 +2,4 @@ from .rndstatus import RndStatus
|
||||
|
||||
|
||||
def setup(bot):
|
||||
n = RndStatus(bot)
|
||||
bot.add_listener(n.switch_status, "on_message")
|
||||
bot.add_cog(n)
|
||||
bot.add_cog(RndStatus(bot))
|
||||
|
||||
@@ -4,9 +4,7 @@ from random import choice as rndchoice
|
||||
import time
|
||||
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
class RndStatus(BaseCog):
|
||||
class RndStatus(commands.Cog):
|
||||
"""Cycles random statuses or displays bot stats.
|
||||
|
||||
If a custom status is already set, it won't change it until
|
||||
@@ -28,9 +26,9 @@ class RndStatus(BaseCog):
|
||||
"Python",
|
||||
"with your heart.",
|
||||
],
|
||||
"type": "1",
|
||||
"streamer": "rndstatusstreamer",
|
||||
"type": 1,
|
||||
}
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
|
||||
@commands.group(autohelp=True)
|
||||
@@ -57,6 +55,19 @@ class RndStatus(BaseCog):
|
||||
"Done. Redo this command with no parameters to see the current list of statuses."
|
||||
)
|
||||
|
||||
@rndstatus.command(name="streamer")
|
||||
async def _streamer(self, ctx: commands.Context, *, streamer=None):
|
||||
"""Set the streamer needed for streaming statuses.
|
||||
"""
|
||||
|
||||
saved_streamer = await self.config.streamer()
|
||||
if streamer == None:
|
||||
return await ctx.send("Current Streamer: " + saved_streamer)
|
||||
await self.config.streamer.set(streamer)
|
||||
await ctx.send(
|
||||
"Done. Redo this command with no parameters to see the current streamer."
|
||||
)
|
||||
|
||||
@rndstatus.command()
|
||||
async def botstats(self, ctx, *statuses: str):
|
||||
"""Toggle for a bot stats status instead of random messages."""
|
||||
@@ -91,7 +102,8 @@ class RndStatus(BaseCog):
|
||||
else:
|
||||
await ctx.send("Type must be between 0 and 3.")
|
||||
|
||||
async def switch_status(self, message):
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
try:
|
||||
current_game = str(message.guild.me.activity.name)
|
||||
except AttributeError:
|
||||
@@ -99,6 +111,8 @@ class RndStatus(BaseCog):
|
||||
statuses = await self.config.statuses()
|
||||
botstats = await self.config.botstats()
|
||||
prefix = await self.bot.db.prefix()
|
||||
streamer = await self.config.streamer()
|
||||
url = "https://www.twitch.tv/" + streamer
|
||||
|
||||
if botstats:
|
||||
total_users = sum(len(s.members) for s in self.bot.guilds)
|
||||
@@ -106,30 +120,44 @@ class RndStatus(BaseCog):
|
||||
botstatus = f"{prefix[0]}help | {total_users} users | {servers} servers"
|
||||
if self.last_change == None:
|
||||
type = await self.config.type()
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Activity(name=botstatus, type=type)
|
||||
)
|
||||
if type == 1:
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Streaming(name=botstatus, url=url)
|
||||
)
|
||||
else:
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Activity(name=botstatus, type=type)
|
||||
)
|
||||
self.last_change = int(time.perf_counter())
|
||||
if message.author.id == self.bot.user.id:
|
||||
return
|
||||
delay = await self.config.delay()
|
||||
if abs(self.last_change - int(time.perf_counter())) < int(delay):
|
||||
return
|
||||
self.last_change = int(time.perf_counter())
|
||||
if (current_game != str(botstatus)) or current_game == None:
|
||||
type = await self.config.type()
|
||||
return await self.bot.change_presence(
|
||||
activity=discord.Activity(name=botstatus, type=type)
|
||||
)
|
||||
if type == 1:
|
||||
return await self.bot.change_presence(
|
||||
activity=discord.Streaming(name=botstatus, url=url)
|
||||
)
|
||||
else:
|
||||
return await self.bot.change_presence(
|
||||
activity=discord.Activity(name=botstatus, type=type)
|
||||
)
|
||||
|
||||
if self.last_change == None:
|
||||
self.last_change = int(time.perf_counter())
|
||||
if len(statuses) > 0 and (current_game in statuses or current_game == None):
|
||||
new_status = self.random_status(message, statuses)
|
||||
self.last_change = int(time.perf_counter())
|
||||
type = await self.config.type()
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Activity(name=new_status, type=type)
|
||||
)
|
||||
if type == 1:
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Streaming(name=new_status, url=url)
|
||||
)
|
||||
else:
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Activity(name=new_status, type=type)
|
||||
)
|
||||
|
||||
if message.author.id != self.bot.user.id:
|
||||
delay = await self.config.delay()
|
||||
@@ -139,9 +167,14 @@ class RndStatus(BaseCog):
|
||||
if current_game != new_status:
|
||||
if current_game in statuses or current_game == None:
|
||||
type = await self.config.type()
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Activity(name=new_status, type=type)
|
||||
)
|
||||
if type == 1:
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Streaming(name=new_status, url=url)
|
||||
)
|
||||
else:
|
||||
await self.bot.change_presence(
|
||||
activity=discord.Activity(name=new_status, type=type)
|
||||
)
|
||||
|
||||
def random_status(self, msg, statuses):
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from .seen import Seen
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Seen(bot))
|
||||
async def setup(bot):
|
||||
cog = Seen(bot)
|
||||
await cog.initialize()
|
||||
bot.add_cog(cog)
|
||||
|
||||
178
seen/seen.py
178
seen/seen.py
@@ -1,36 +1,100 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
import datetime
|
||||
from typing import Union
|
||||
|
||||
import discord
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from redbot.core import Config, commands
|
||||
|
||||
_SCHEMA_VERSION = 2
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
class Seen(BaseCog):
|
||||
"""Shows last time a user was seen in chat"""
|
||||
class Seen(commands.Cog):
|
||||
"""Shows last time a user was seen in chat."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, 2784481001, force_registration=True)
|
||||
|
||||
default_member = {"member_seen": []}
|
||||
default_global = dict(schema_version=1)
|
||||
default_member = dict(seen=None)
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_member(**default_member)
|
||||
|
||||
self._cache = {}
|
||||
self._task = self.bot.loop.create_task(self._save_to_config())
|
||||
|
||||
async def initialize(self):
|
||||
asyncio.ensure_future(
|
||||
self._migrate_config(
|
||||
from_version=await self.config.schema_version(), to_version=_SCHEMA_VERSION
|
||||
)
|
||||
)
|
||||
|
||||
async def _migrate_config(self, from_version: int, to_version: int):
|
||||
if from_version == to_version:
|
||||
return
|
||||
elif from_version < to_version:
|
||||
all_guild_data = await self.config.all_members()
|
||||
users_data = {}
|
||||
for guild_id, guild_data in all_guild_data.items():
|
||||
for user_id, user_data in guild_data.items():
|
||||
for _, v in user_data.items():
|
||||
if not v:
|
||||
v = None
|
||||
if user_id not in users_data:
|
||||
users_data[guild_id][user_id] = {"seen": v}
|
||||
else:
|
||||
if (v and not users_data[guild_id][user_id]["seen"]) or (
|
||||
v
|
||||
and users_data[guild_id][user_id]["seen"]
|
||||
and v > users_data[guild_id][user_id]["seen"]
|
||||
):
|
||||
users_data[guild_id][user_id] = {"seen": v}
|
||||
|
||||
group = self.config._get_base_group(self.config.MEMBER) # Bulk update to new scope
|
||||
async with group.all() as new_data:
|
||||
for guild_id, member_data in users_data.items():
|
||||
new_data[guild_id] = member_data
|
||||
|
||||
# new schema is now in place
|
||||
await self.config.schema_version.set(_SCHEMA_VERSION)
|
||||
|
||||
# migration done, now let's delete all the old stuff
|
||||
await self.config.clear_all_members()
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command(name="seen")
|
||||
@commands.bot_has_permissions(embed_links=True)
|
||||
async def _seen(self, ctx, author: discord.Member):
|
||||
"""Shows last time a user was seen in chat"""
|
||||
member_seen = await self.config.member(author).member_seen()
|
||||
now = int(time.time())
|
||||
try:
|
||||
time_elapsed = int(now - member_seen)
|
||||
except TypeError:
|
||||
"""Shows last time a user was seen in chat."""
|
||||
member_seen_config = await self.config.member(author).seen()
|
||||
member_seen_cache = self._cache.get(author.guild.id, {}).get(author.id, None)
|
||||
|
||||
if not member_seen_cache and not member_seen_config:
|
||||
embed = discord.Embed(
|
||||
colour=discord.Color.red(), title="I haven't seen that user yet."
|
||||
)
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
if not member_seen_cache:
|
||||
member_seen = member_seen_config
|
||||
elif not member_seen_config:
|
||||
member_seen = member_seen_cache
|
||||
elif member_seen_cache > member_seen_config:
|
||||
member_seen = member_seen_cache
|
||||
elif member_seen_config > member_seen_cache:
|
||||
member_seen = member_seen_config
|
||||
else:
|
||||
member_seen = member_seen_cache or member_seen_config
|
||||
|
||||
now = int(time.time())
|
||||
time_elapsed = int(now - member_seen)
|
||||
output = self._dynamic_time(time_elapsed)
|
||||
|
||||
if output[2] < 1:
|
||||
ts = "just now"
|
||||
else:
|
||||
@@ -48,26 +112,88 @@ class Seen(BaseCog):
|
||||
elif output[2] > 1:
|
||||
ts += "{} minutes ago".format(output[2])
|
||||
em = discord.Embed(colour=discord.Color.green())
|
||||
avatar = author.avatar_url if author.avatar else author.default_avatar_url
|
||||
avatar = author.avatar_url or author.default_avatar_url
|
||||
em.set_author(name="{} was seen {}".format(author.display_name, ts), icon_url=avatar)
|
||||
await ctx.send(embed=em)
|
||||
|
||||
def _dynamic_time(self, time_elapsed):
|
||||
@staticmethod
|
||||
def _dynamic_time(time_elapsed):
|
||||
m, s = divmod(time_elapsed, 60)
|
||||
h, m = divmod(m, 60)
|
||||
d, h = divmod(h, 24)
|
||||
return (d, h, m)
|
||||
return d, h, m
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if (
|
||||
not isinstance(message.channel, discord.abc.PrivateChannel)
|
||||
and self.bot.user.id != message.author.id
|
||||
):
|
||||
prefixes = await self.bot.get_prefix(message)
|
||||
if not any(message.content.startswith(n) for n in prefixes):
|
||||
author = message.author
|
||||
ts = int(time.time())
|
||||
try:
|
||||
await self.config.member(author).member_seen.set(ts)
|
||||
except AttributeError:
|
||||
pass
|
||||
if getattr(message, "guild", None):
|
||||
if message.guild.id not in self._cache:
|
||||
self._cache[message.guild.id] = {}
|
||||
self._cache[message.guild.id][message.author.id] = int(time.time())
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_typing(
|
||||
self,
|
||||
channel: discord.abc.Messageable,
|
||||
user: Union[discord.User, discord.Member],
|
||||
when: datetime.datetime,
|
||||
):
|
||||
if getattr(user, "guild", None):
|
||||
if user.guild.id not in self._cache:
|
||||
self._cache[user.guild.id] = {}
|
||||
self._cache[user.guild.id][user.id] = int(time.time())
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message_edit(self, before: discord.Message, after: discord.Message):
|
||||
if getattr(after, "guild", None):
|
||||
if after.guild.id not in self._cache:
|
||||
self._cache[after.guild.id] = {}
|
||||
self._cache[after.guild.id][after.author.id] = int(time.time())
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_reaction_remove(
|
||||
self, reaction: discord.Reaction, user: Union[discord.Member, discord.User]
|
||||
):
|
||||
if getattr(user, "guild", None):
|
||||
if user.guild.id not in self._cache:
|
||||
self._cache[user.guild.id] = {}
|
||||
self._cache[user.guild.id][user.id] = int(time.time())
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_reaction_add(
|
||||
self, reaction: discord.Reaction, user: Union[discord.Member, discord.User]
|
||||
):
|
||||
if getattr(user, "guild", None):
|
||||
if user.guild.id not in self._cache:
|
||||
self._cache[user.guild.id] = {}
|
||||
self._cache[user.guild.id][user.id] = int(time.time())
|
||||
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self._clean_up())
|
||||
|
||||
async def _clean_up(self):
|
||||
if self._task:
|
||||
self._task.cancel()
|
||||
if self._cache:
|
||||
group = self.config._get_base_group(self.config.MEMBER) # Bulk update to config
|
||||
async with group.all() as new_data:
|
||||
for guild_id, member_data in self._cache.items():
|
||||
if str(guild_id) not in new_data:
|
||||
new_data[str(guild_id)] = {}
|
||||
for member_id, seen in member_data.items():
|
||||
new_data[str(guild_id)][str(member_id)] = {"seen": seen}
|
||||
|
||||
async def _save_to_config(self):
|
||||
await self.bot.wait_until_ready()
|
||||
with contextlib.suppress(asyncio.CancelledError):
|
||||
while True:
|
||||
users_data = self._cache.copy()
|
||||
self._cache = {}
|
||||
group = self.config._get_base_group(self.config.MEMBER) # Bulk update to config
|
||||
async with group.all() as new_data:
|
||||
for guild_id, member_data in users_data.items():
|
||||
if str(guild_id) not in new_data:
|
||||
new_data[str(guild_id)] = {}
|
||||
for member_id, seen in member_data.items():
|
||||
new_data[str(guild_id)][str(member_id)] = {"seen": seen}
|
||||
|
||||
await asyncio.sleep(60)
|
||||
|
||||
5
snacktime/__init__.py
Executable file
5
snacktime/__init__.py
Executable file
@@ -0,0 +1,5 @@
|
||||
from .snacktime import Snacktime
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
bot.add_cog(Snacktime(bot))
|
||||
9
snacktime/info.json
Normal file
9
snacktime/info.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name" : "Snacktime",
|
||||
"author" : ["irdumb", "aikaterna"],
|
||||
"short" : "ʕ •ᴥ•ʔ < It's snacktime, who wants snacks?",
|
||||
"description" : "snackburr will come around every-so-often if you've asked him to.\nI hear snackburr likes to come around more often when people are partyin.",
|
||||
"install_msg" : "A snack delivery bear has arrived ʕ•ᴥ• ʔ",
|
||||
"tags" : ["snack", "snacktime", "snackburr", "party", "party time"],
|
||||
"disabled" : false
|
||||
}
|
||||
73
snacktime/phrases.py
Normal file
73
snacktime/phrases.py
Normal file
@@ -0,0 +1,73 @@
|
||||
FRIENDS = {
|
||||
"Snackburr": "ʕ •ᴥ•ʔ <",
|
||||
"Pancakes": "₍⸍⸌̣ʷ̣̫⸍̣⸌₎ <",
|
||||
"Mr Pickles": "(=`ェ´=) <",
|
||||
"Satin": "▼・ᴥ・▼ <",
|
||||
"Thunky": "ᘛ⁐̤ᕐᐷ <",
|
||||
"Jingle": "꒰∗´꒳`꒱ <",
|
||||
"FluffButt": "/ᐠ。ꞈ。ᐟ\ <",
|
||||
"Staplefoot": "( ̄(エ) ̄) <",
|
||||
}
|
||||
|
||||
|
||||
SNACKBURR_PHRASES = {
|
||||
"SNACKTIME": [
|
||||
"It's snack time!",
|
||||
"I'm back with s'more snacks! Who wants!?",
|
||||
"I'm back errbody! Who wants some snacks!?",
|
||||
"Woo man those errands are crazy! Anyways, anybody want some snacks?",
|
||||
"I got snacks! If nobody wants em, I'm gonna eat em all!!",
|
||||
"Hey, I'm back! Anybody in the mood for some snacks?!",
|
||||
"Heyyaaayayyyaya! I say Hey, I got snacks!",
|
||||
"Heyyaaayayyyaya! I say Hey, What's goin on?... I uh.. I got snacks.",
|
||||
"If anybody has reason why these snacks and my belly should not be wed, speak now or forever hold your peace!",
|
||||
"Got another snack delivery guys!",
|
||||
"Did somebody say snacks?!?! o/",
|
||||
"Choo Choo! it's the pb train! Come on over guys!",
|
||||
"Snacks are here! Dig in! Who wants a plate?",
|
||||
"Pstt.. I got the snacks you were lookin for. <.<",
|
||||
"I hope you guys are hungry! Cause i'm loaded to the brim with snacks!!!",
|
||||
"I was hungry on the way over so I kinda started without you guys :3 Who wants snacks!?!",
|
||||
"Beep beep! I got a snack delivery comin in! Who wants snacks!",
|
||||
"Guess what time it is?! It's snacktime!! Who wants?!",
|
||||
"Hey check out this sweet stach o' snacks I found! Who wants a cut?",
|
||||
"Who's ready to gobble down some snacks!?",
|
||||
"So who's gonna help me eat all these snacks? :3",
|
||||
],
|
||||
"OUT": [
|
||||
"I'm out of snacks! I'll be back with more soon.",
|
||||
"I'm out of snacks :( I'll be back soon with more!",
|
||||
"Aight, I gotta head out! I'll be back with more, don worry :3",
|
||||
"Alright, I gotta get back to my errands. I'll see you guys soon!",
|
||||
],
|
||||
"LONELY": ["I guess you guys don't like snacktimes.. I'll stop comin around."],
|
||||
"NO_TAKERS": [
|
||||
"I guess nobody wants snacks... more for me!",
|
||||
"Guess nobody's here.. I'll just head out then",
|
||||
"I don't see anybody.. <.< ... >.> ... All the snacks for me!!",
|
||||
"I guess nobody wants snacks huh.. Well, I'll come back later",
|
||||
"I guess i'll just come back later..",
|
||||
],
|
||||
"GIVE": [
|
||||
"Here ya go, {0}, here's {1} pb!",
|
||||
"Alright here ya go, {0}, {1} pb for you!",
|
||||
"Yeah! Here you go, {0}! {1} pb!",
|
||||
"Of course {0}! Here's {1} pb!",
|
||||
"Ok {0}, here's {1} pb for you. Anyone else want some?",
|
||||
"Alllright, {1} pb for {0}!",
|
||||
"Hold your horses {0}! Alright, {1} pb for you :)",
|
||||
],
|
||||
"LAST_SECOND": [
|
||||
"Fine fine, {0}, I'll give you {1} of my on-the-road pb.. Cya!",
|
||||
"Oh! {0}, you caught me right before I left! Alright, i'll give you {1} of my own pb",
|
||||
],
|
||||
"GREEDY": [
|
||||
"Don't be greedy now! you already got some pb {0}!",
|
||||
"You already got your snacks {0}!",
|
||||
"Come on {0}, you already got your snacks! We gotta make sure there's some for errbody!",
|
||||
],
|
||||
"ENABLE": [
|
||||
"Oh you guys want snacks?! Aight, I'll come around every so often to hand some out!"
|
||||
],
|
||||
"DISABLE": ["You guys don't want snacks anymore? Alright, I'll stop comin around."],
|
||||
}
|
||||
466
snacktime/snacktime.py
Normal file
466
snacktime/snacktime.py
Normal file
@@ -0,0 +1,466 @@
|
||||
import asyncio
|
||||
import discord
|
||||
import logging
|
||||
from random import randint
|
||||
from random import choice as randchoice
|
||||
|
||||
from redbot.core import bank, checks, commands, Config
|
||||
from redbot.core.utils.chat_formatting import box, humanize_list, pagify
|
||||
|
||||
from .phrases import FRIENDS, SNACKBURR_PHRASES
|
||||
|
||||
|
||||
log = logging.getLogger("red.aikaterna.snacktime")
|
||||
|
||||
|
||||
class Snacktime(commands.Cog):
|
||||
"""Snackburr's passing out pb jars!"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, 2712291001, force_registration=True)
|
||||
|
||||
self.snackSchedule = {}
|
||||
self.snacktimePrediction = {}
|
||||
self.previousSpeaker = {}
|
||||
self.snackInProgress = {}
|
||||
self.acceptInput = {}
|
||||
self.alreadySnacked = {}
|
||||
self.msgsPassed = {}
|
||||
self.startLock = {}
|
||||
self.snacktimeCheckLock = {}
|
||||
self.lockRequests = {}
|
||||
self.channel_persona = {}
|
||||
|
||||
default_guild = {
|
||||
"DELIVER_CHANNELS": [],
|
||||
"FRIENDS": False,
|
||||
"EVENT_START_DELAY": 1800,
|
||||
"EVENT_START_DELAY_VARIANCE": 900,
|
||||
"SNACK_DURATION": 240,
|
||||
"SNACK_DURATION_VARIANCE": 120,
|
||||
"MSGS_BEFORE_EVENT": 8,
|
||||
"SNACK_AMOUNT": 200,
|
||||
}
|
||||
|
||||
default_channel = {"repeatMissedSnacktimes": 0}
|
||||
|
||||
self.config.register_guild(**default_guild)
|
||||
self.config.register_channel(**default_channel)
|
||||
|
||||
async def persona_choice(self, msg):
|
||||
invite_friends = await self.config.guild(msg.guild).FRIENDS()
|
||||
personas = FRIENDS
|
||||
if not invite_friends:
|
||||
return "Snackburr"
|
||||
elif invite_friends is True:
|
||||
del personas["Snackburr"]
|
||||
return randchoice(list(personas.keys()))
|
||||
|
||||
async def get_response(self, msg, phrase_type):
|
||||
scid = f"{msg.guild.id}-{msg.channel.id}"
|
||||
persona = self.channel_persona[scid]
|
||||
persona_phrase = FRIENDS.get(persona)
|
||||
phrase = randchoice(SNACKBURR_PHRASES[phrase_type])
|
||||
return f"`{persona_phrase} {phrase}`"
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.group()
|
||||
@checks.mod_or_permissions(manage_guild=True)
|
||||
async def snackset(self, ctx):
|
||||
"""snack stuff"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
guild_data = await self.config.guild(ctx.guild).all()
|
||||
if not guild_data["DELIVER_CHANNELS"]:
|
||||
channel_names = ["No channels set."]
|
||||
else:
|
||||
channel_names = []
|
||||
for channel_id in guild_data["DELIVER_CHANNELS"]:
|
||||
channel_obj = self.bot.get_channel(channel_id)
|
||||
channel_names.append(channel_obj.name)
|
||||
|
||||
if guild_data["FRIENDS"] is True:
|
||||
invite_friends = "Friends only"
|
||||
elif guild_data["FRIENDS"] is False:
|
||||
invite_friends = "Snackburr only"
|
||||
else:
|
||||
invite_friends = "Everyone's invited!"
|
||||
|
||||
msg = f"[Delivering in]: {humanize_list(channel_names)}\n"
|
||||
msg += f"[Event start delay]: {guild_data['EVENT_START_DELAY']} seconds\n"
|
||||
msg += (
|
||||
f"[Event start variance]: {guild_data['EVENT_START_DELAY_VARIANCE']} seconds\n"
|
||||
)
|
||||
msg += f"[Friends status]: {invite_friends}\n"
|
||||
msg += f"[Messages before event]: {guild_data['MSGS_BEFORE_EVENT']}\n"
|
||||
msg += f"[Snack amount limit]: {guild_data['SNACK_AMOUNT']} pb\n"
|
||||
msg += f"[Snack duration]: {guild_data['SNACK_DURATION']} seconds\n"
|
||||
msg += f"[Snack duration variance]: {guild_data['SNACK_DURATION_VARIANCE']} seconds\n"
|
||||
|
||||
for page in pagify(msg, delims=["\n"]):
|
||||
await ctx.send(box(page, lang="ini"))
|
||||
|
||||
@snackset.command()
|
||||
async def errandtime(self, ctx, seconds: int):
|
||||
"""How long snackburr needs to be out doin errands.. more or less."""
|
||||
event_start_delay_variance = await self.config.guild(
|
||||
ctx.guild
|
||||
).EVENT_START_DELAY_VARIANCE()
|
||||
if seconds <= event_start_delay_variance:
|
||||
await ctx.send("errandtime must be greater than errandvariance!")
|
||||
elif seconds <= 0:
|
||||
await ctx.send("errandtime must be greater than 0")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).EVENT_START_DELAY.set(seconds)
|
||||
await ctx.send(
|
||||
f"snackburr's errands will now take around {round(seconds/60, 2)} minutes!"
|
||||
)
|
||||
|
||||
@snackset.command()
|
||||
async def errandvariance(self, ctx, seconds: int):
|
||||
"""How early or late snackburr might be to snacktime"""
|
||||
event_start_delay = await self.config.guild(ctx.guild).EVENT_START_DELAY()
|
||||
if seconds >= event_start_delay:
|
||||
await ctx.send("errandvariance must be less than errandtime!")
|
||||
elif seconds < 0:
|
||||
await ctx.send("errandvariance must be 0 or greater!")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).EVENT_START_DELAY_VARIANCE.set(seconds)
|
||||
await ctx.send(
|
||||
f"snackburr now might be {round(seconds/60, 2)} minutes early or late to snacktime"
|
||||
)
|
||||
|
||||
@snackset.command(name="snacktime")
|
||||
async def snacktimetime(self, ctx, seconds: int):
|
||||
"""How long snackburr will hang out giving out snacks!.. more or less."""
|
||||
snack_duration_variance = await self.config.guild(ctx.guild).SNACK_DURATION_VARIANCE()
|
||||
if seconds <= snack_duration_variance:
|
||||
await ctx.send("snacktime must be greater than snackvariance!")
|
||||
elif seconds <= 0:
|
||||
await ctx.send("snacktime must be greater than 0")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).SNACK_DURATION.set(seconds)
|
||||
await ctx.send(f"snacktimes will now last around {round(seconds/60, 2)} minutes!")
|
||||
|
||||
@snackset.command(name="snackvariance")
|
||||
async def snacktimevariance(self, ctx, seconds: int):
|
||||
"""How early or late snackburr might have to leave for errands"""
|
||||
snack_duration = await self.config.guild(ctx.guild).SNACK_DURATION()
|
||||
if seconds >= snack_duration:
|
||||
await ctx.send("snackvariance must be less than snacktime!")
|
||||
elif seconds < 0:
|
||||
await ctx.send("snackvariance must be 0 or greater!")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).SNACK_DURATION_VARIANCE.set(seconds)
|
||||
await ctx.send(
|
||||
f"snackburr now may have to leave snacktime {round(seconds/60, 2)} minutes early or late"
|
||||
)
|
||||
|
||||
@snackset.command()
|
||||
async def msgsneeded(self, ctx, amt: int):
|
||||
"""How many messages must pass in a conversation before a snacktime can start"""
|
||||
if amt <= 0:
|
||||
await ctx.send("msgsneeded must be greater than 0")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).MSGS_BEFORE_EVENT.set(amt)
|
||||
await ctx.send(
|
||||
f"snackburr will now wait until {amt} messages pass until he comes with snacks"
|
||||
)
|
||||
|
||||
@snackset.command()
|
||||
async def amount(self, ctx, amt: int):
|
||||
"""How much pb max snackburr should give out to each person per snacktime"""
|
||||
|
||||
if amt <= 0:
|
||||
await ctx.send("amount must be greater than 0")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).SNACK_AMOUNT.set(amt)
|
||||
await ctx.send(f"snackburr will now give out {amt} pb max per person per snacktime.")
|
||||
|
||||
@snackset.command(name="friends")
|
||||
async def snackset_friends(self, ctx, choice: int):
|
||||
"""snackburr's friends wanna know what all the hub-bub's about!
|
||||
|
||||
Do you want to
|
||||
1: invite them to the party,
|
||||
2: only allow snackburr to chillax with you guys, or
|
||||
3: kick snackburr out on the curb in favor of his obviously cooler friends?
|
||||
"""
|
||||
|
||||
if choice not in (1, 2, 3):
|
||||
return await ctx.send_help()
|
||||
|
||||
choices = {
|
||||
1: ("both", "Everybody's invited!"),
|
||||
2: (False, "You chose to not invite snackburr's friends."),
|
||||
3: (True, "You kick snackburr out in favor of his friends! Ouch. Harsh..."),
|
||||
}
|
||||
choice = choices[choice]
|
||||
|
||||
await self.config.guild(ctx.guild).FRIENDS.set(choice[0])
|
||||
await ctx.send(choice[1])
|
||||
|
||||
@snackset.command()
|
||||
async def deliver(self, ctx):
|
||||
"""Asks snackburr to start delivering to this channel"""
|
||||
deliver_channels = await self.config.guild(ctx.guild).DELIVER_CHANNELS()
|
||||
if not deliver_channels:
|
||||
deliver_channels = []
|
||||
if ctx.channel.id not in deliver_channels:
|
||||
deliver_channels.append(ctx.channel.id)
|
||||
await self.config.guild(ctx.guild).DELIVER_CHANNELS.set(deliver_channels)
|
||||
await ctx.send("snackburr will start delivering here!")
|
||||
else:
|
||||
deliver_channels.remove(ctx.channel.id)
|
||||
await self.config.guild(ctx.guild).DELIVER_CHANNELS.set(deliver_channels)
|
||||
await ctx.send("snackburr will stop delivering here!")
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command()
|
||||
async def snacktime(self, ctx):
|
||||
"""Man i'm hungry! When's snackburr gonna get back with more snacks?"""
|
||||
scid = f"{ctx.message.guild.id}-{ctx.message.channel.id}"
|
||||
if self.snacktimePrediction.get(scid, None) == None:
|
||||
if self.acceptInput.get(scid, False):
|
||||
return
|
||||
else:
|
||||
phrases = [
|
||||
"Don't look at me. I donno where snackburr's at ¯\_(ツ)_/¯",
|
||||
"I hear snackburr likes parties. *wink wink",
|
||||
"I hear snackburr is attracted to channels with active conversations",
|
||||
"If you party, snackburr will come! 〈( ^o^)ノ",
|
||||
]
|
||||
await ctx.send(randchoice(phrases))
|
||||
return
|
||||
seconds = self.snacktimePrediction[scid] - self.bot.loop.time()
|
||||
if self.snacktimeCheckLock.get(scid, False):
|
||||
if randint(1, 4) == 4:
|
||||
await ctx.send("Hey, snackburr's on errands. I ain't his keeper Kappa")
|
||||
return
|
||||
self.snacktimeCheckLock[scid] = True
|
||||
if seconds < 0:
|
||||
await ctx.send(
|
||||
f"I'm not sure where snackburr is.. He's already {round(abs(seconds/60), 2)} minutes late!"
|
||||
)
|
||||
else:
|
||||
await ctx.send(
|
||||
f"snackburr's out on errands! I think he'll be back in {round(seconds/60, 2)} minutes"
|
||||
)
|
||||
await asyncio.sleep(40)
|
||||
self.snacktimeCheckLock[scid] = False
|
||||
|
||||
async def startSnack(self, message):
|
||||
scid = f"{message.guild.id}-{message.channel.id}"
|
||||
if self.acceptInput.get(scid, False):
|
||||
return
|
||||
self.channel_persona[scid] = await self.persona_choice(message)
|
||||
await message.channel.send(await self.get_response(message, "SNACKTIME"))
|
||||
|
||||
self.acceptInput[scid] = True
|
||||
self.alreadySnacked[scid] = []
|
||||
|
||||
guild_data = await self.config.guild(message.guild).all()
|
||||
|
||||
duration = guild_data["SNACK_DURATION"] + randint(
|
||||
-guild_data["SNACK_DURATION_VARIANCE"], guild_data["SNACK_DURATION_VARIANCE"]
|
||||
)
|
||||
await asyncio.sleep(duration)
|
||||
# sometimes fails sending messages and stops all future snacktimes. Hopefully this fixes it.
|
||||
try:
|
||||
# list isn't empty
|
||||
if self.alreadySnacked.get(scid, False):
|
||||
await message.channel.send(await self.get_response(message, "OUT"))
|
||||
await self.config.channel(message.channel).repeatMissedSnacktimes.set(0)
|
||||
else:
|
||||
await message.channel.send(await self.get_response(message, "NO_TAKERS"))
|
||||
repeat_missed_snacktimes = await self.config.channel(
|
||||
message.channel
|
||||
).repeatMissedSnacktimes()
|
||||
await self.config.channel(message.channel).repeatMissedSnacktimes.set(
|
||||
repeat_missed_snacktimes + 1
|
||||
)
|
||||
await asyncio.sleep(2)
|
||||
if (repeat_missed_snacktimes + 1) > 9: # move to a setting
|
||||
await message.channel.send(await self.get_response(message, "LONELY"))
|
||||
deliver_channels = await self.config.guild(message.guild).DELIVER_CHANNELS()
|
||||
new_deliver_channels = deliver_channels.remove(message.channel.id)
|
||||
await self.config.guild(message.guild).DELIVER_CHANNELS.set(
|
||||
new_deliver_channels
|
||||
)
|
||||
await self.config.channel(message.channel).repeatMissedSnacktimes.set(0)
|
||||
except:
|
||||
log.error("Snacktime: Failed to send message in startSnack")
|
||||
self.acceptInput[scid] = False
|
||||
self.snackInProgress[scid] = False
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if not message.guild:
|
||||
return
|
||||
if message.author.bot:
|
||||
return
|
||||
if not message.channel.permissions_for(message.guild.me).send_messages:
|
||||
return
|
||||
|
||||
deliver_channels = await self.config.guild(message.guild).DELIVER_CHANNELS()
|
||||
if not deliver_channels:
|
||||
return
|
||||
if message.channel.id not in deliver_channels:
|
||||
return
|
||||
scid = f"{message.guild.id}-{message.channel.id}"
|
||||
if message.author.id != self.bot.user.id:
|
||||
# if nobody has said anything since start
|
||||
if self.previousSpeaker.get(scid, None) == None:
|
||||
self.previousSpeaker[scid] = message.author.id
|
||||
# if new speaker
|
||||
elif self.previousSpeaker[scid] != message.author.id:
|
||||
self.previousSpeaker[scid] = message.author.id
|
||||
msgTime = self.bot.loop.time()
|
||||
# if there's a scheduled snack
|
||||
if self.snackSchedule.get(scid, None) != None:
|
||||
# if it's time for a snack
|
||||
if msgTime > self.snackSchedule[scid]:
|
||||
# 1 schedule at a time, so remove schedule
|
||||
self.snackSchedule[scid] = None
|
||||
self.snackInProgress[scid] = True
|
||||
|
||||
# wait to make it more natural
|
||||
naturalWait = randint(30, 240)
|
||||
log.debug(f"Snacktime: snack trigger msg: {message.content}")
|
||||
log.debug(f"Snacktime: Waiting {str(naturalWait)} seconds")
|
||||
await asyncio.sleep(naturalWait)
|
||||
# start snacktime
|
||||
await self.startSnack(message)
|
||||
# if no snack coming, schedule one
|
||||
elif self.snackInProgress.get(scid, False) == False and not self.startLock.get(
|
||||
scid, False
|
||||
):
|
||||
self.msgsPassed[scid] = self.msgsPassed.get(scid, 0) + 1
|
||||
# check for collisions
|
||||
msgs_before_event = await self.config.guild(message.guild).MSGS_BEFORE_EVENT()
|
||||
if self.msgsPassed[scid] > msgs_before_event:
|
||||
self.startLock[scid] = True
|
||||
if self.lockRequests.get(scid, None) == None:
|
||||
self.lockRequests[scid] = []
|
||||
self.lockRequests[scid].append(message)
|
||||
await asyncio.sleep(1)
|
||||
log.debug(
|
||||
f"Snacktime: :-+-|||||-+-: Lock request: {str(self.lockRequests[scid][0] == message)}"
|
||||
)
|
||||
if self.lockRequests[scid][0] == message:
|
||||
await asyncio.sleep(5)
|
||||
log.debug(f"Snacktime: {message.author.name} - I got the Lock")
|
||||
self.lockRequests[scid] = []
|
||||
# someone got through already
|
||||
if self.msgsPassed[
|
||||
scid
|
||||
] < msgs_before_event or self.snackInProgress.get(scid, False):
|
||||
log.debug("Snacktime: Lock: someone got through already.")
|
||||
return
|
||||
else:
|
||||
log.debug(
|
||||
"Snacktime: Lock: looks like i'm in the clear. lifting lock. If someone comes now, they should get the lock"
|
||||
)
|
||||
self.msgsPassed[scid] = msgs_before_event
|
||||
self.startLock[scid] = False
|
||||
else:
|
||||
log.debug(f"Snacktime: {message.author.name} Failed lock")
|
||||
return
|
||||
if self.msgsPassed[scid] == msgs_before_event:
|
||||
# schedule a snack
|
||||
log.debug(f"Snacktime: activity: {message.content}")
|
||||
guild_data = await self.config.guild(message.guild).all()
|
||||
timeTillSnack = guild_data["EVENT_START_DELAY"] + randint(
|
||||
-guild_data["EVENT_START_DELAY_VARIANCE"],
|
||||
guild_data["EVENT_START_DELAY_VARIANCE"],
|
||||
)
|
||||
log.debug(f"Snacktime: {str(timeTillSnack)} seconds till snacktime")
|
||||
self.snacktimePrediction[scid] = msgTime + guild_data["EVENT_START_DELAY"]
|
||||
self.snackSchedule[scid] = msgTime + timeTillSnack
|
||||
self.msgsPassed[scid] = 0
|
||||
|
||||
# it's snacktime! who want's snacks?
|
||||
if self.acceptInput.get(scid, False):
|
||||
if message.author.id not in self.alreadySnacked.get(scid, []):
|
||||
agree_phrases = [
|
||||
"holds out hand",
|
||||
"im ready",
|
||||
"i'm ready",
|
||||
"hit me up",
|
||||
"hand over",
|
||||
"hand me",
|
||||
"kindly",
|
||||
"i want",
|
||||
"i'll have",
|
||||
"ill have",
|
||||
"yes",
|
||||
"pls",
|
||||
"plz",
|
||||
"please",
|
||||
"por favor",
|
||||
"can i",
|
||||
"i'd like",
|
||||
"i would",
|
||||
"may i",
|
||||
"in my mouth",
|
||||
"in my belly",
|
||||
"snack me",
|
||||
"gimme",
|
||||
"give me",
|
||||
"i'll take",
|
||||
"ill take",
|
||||
"i am",
|
||||
"about me",
|
||||
"me too",
|
||||
"of course",
|
||||
]
|
||||
userWants = False
|
||||
for agreePhrase in agree_phrases:
|
||||
# no one word answers
|
||||
if (
|
||||
agreePhrase in message.content.lower()
|
||||
and len(message.content.split()) > 1
|
||||
):
|
||||
userWants = True
|
||||
break
|
||||
if userWants:
|
||||
if self.alreadySnacked.get(scid, None) == None:
|
||||
self.alreadySnacked[scid] = []
|
||||
self.alreadySnacked[scid].append(message.author.id)
|
||||
await asyncio.sleep(randint(1, 6))
|
||||
snack_amount = await self.config.guild(message.guild).SNACK_AMOUNT()
|
||||
snackAmt = randint(1, snack_amount)
|
||||
try:
|
||||
if self.acceptInput.get(scid, False):
|
||||
resp = await self.get_response(message, "GIVE")
|
||||
resp = resp.format(message.author.name, snackAmt)
|
||||
await message.channel.send(resp)
|
||||
else:
|
||||
resp = await self.get_response(message, "LAST_SECOND")
|
||||
resp = resp.format(message.author.name, snackAmt)
|
||||
await message.channel.send(resp)
|
||||
await bank.deposit_credits(message.author, snackAmt)
|
||||
except:
|
||||
log.info(
|
||||
f"Snacktime: Failed to send pb message. {message.author.name} didn't get pb"
|
||||
)
|
||||
|
||||
else:
|
||||
more_phrases = [
|
||||
"more pl",
|
||||
"i have some more",
|
||||
"i want more",
|
||||
"i have another",
|
||||
"i have more",
|
||||
"more snack",
|
||||
]
|
||||
userWants = False
|
||||
for morePhrase in more_phrases:
|
||||
if morePhrase in message.content.lower():
|
||||
userWants = True
|
||||
break
|
||||
if userWants:
|
||||
await asyncio.sleep(randint(1, 6))
|
||||
if self.acceptInput.get(scid, False):
|
||||
resp = await self.get_response(message, "GREEDY")
|
||||
await message.channel.send(resp.format(message.author.name))
|
||||
@@ -137,7 +137,7 @@ class Timezone(commands.Cog):
|
||||
time = time.strftime(fmt)
|
||||
await ctx.send(
|
||||
f"{user.name}'s current timezone is: **{usertime}**\n"
|
||||
f"They current time is: {str(time)}"
|
||||
f"The current time is: {str(time)}"
|
||||
)
|
||||
else:
|
||||
await ctx.send("That user hasn't set their timezone.")
|
||||
|
||||
@@ -11,10 +11,8 @@ from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||
from tabulate import tabulate
|
||||
from contextlib import suppress as sps
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class Tools(BaseCog):
|
||||
class Tools(commands.Cog):
|
||||
"""Mod and Admin tools."""
|
||||
|
||||
def __init__(self, bot):
|
||||
@@ -181,7 +179,7 @@ class Tools(BaseCog):
|
||||
bancount = len(banlist)
|
||||
ban_list = []
|
||||
if bancount == 0:
|
||||
banlist = "No users are banned from this server."
|
||||
msg = "No users are banned from this server."
|
||||
else:
|
||||
msg = ""
|
||||
for user_obj in banlist:
|
||||
@@ -359,7 +357,7 @@ class Tools(BaseCog):
|
||||
)
|
||||
await awaiter.edit(embed=embed)
|
||||
|
||||
@commands.command(name='listguilds', aliases=['listservers', 'guildlist', 'serverlist'])
|
||||
@commands.command(name="listguilds", aliases=["listservers", "guildlist", "serverlist"])
|
||||
@checks.mod_or_permissions()
|
||||
async def listguilds(self, ctx):
|
||||
"""
|
||||
@@ -367,22 +365,32 @@ class Tools(BaseCog):
|
||||
"""
|
||||
asciidoc = lambda m: "```asciidoc\n{}\n```".format(m)
|
||||
guilds = sorted(self.bot.guilds, key=lambda g: -g.member_count)
|
||||
header = ("```\n"
|
||||
"The bot is in the following {} server{}\n"
|
||||
"```").format(len(guilds), 's' if len(guilds) > 1 else '')
|
||||
header = ("```\n" "The bot is in the following {} server{}:\n" "```").format(
|
||||
len(guilds), "s" if len(guilds) > 1 else ""
|
||||
)
|
||||
|
||||
max_zpadding = max([len(str(g.member_count)) for g in guilds])
|
||||
form = "{gid} :: {mems:0{zpadding}} :: {name}"
|
||||
all_forms = [form.format(gid=g.id, mems=g.member_count, name=g.name, zpadding=max_zpadding) for g in guilds]
|
||||
final = '\n'.join(all_forms)
|
||||
all_forms = [
|
||||
form.format(
|
||||
gid=g.id, mems=g.member_count, name=cf.escape(g.name), zpadding=max_zpadding
|
||||
)
|
||||
for g in guilds
|
||||
]
|
||||
final = "\n".join(all_forms)
|
||||
|
||||
await ctx.send(header)
|
||||
for page in cf.pagify(final, delims=['\n'], shorten_by=16):
|
||||
await ctx.send(asciidoc(page))
|
||||
page_list = []
|
||||
for page in cf.pagify(final, delims=["\n"], page_length=1000):
|
||||
page_list.append(asciidoc(page))
|
||||
|
||||
if len(page_list) == 1:
|
||||
return await ctx.send(asciidoc(page))
|
||||
await menu(ctx, page_list, DEFAULT_CONTROLS)
|
||||
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(manage_channels=True)
|
||||
@commands.command(name='listchannel', aliases=['channellist'])
|
||||
@commands.command(name="listchannel", aliases=["channellist"])
|
||||
async def listchannel(self, ctx):
|
||||
"""
|
||||
List the channels of the current server
|
||||
@@ -391,15 +399,17 @@ class Tools(BaseCog):
|
||||
channels = ctx.guild.channels
|
||||
top_channels, category_channels = self.sort_channels(ctx.guild.channels)
|
||||
|
||||
topChannels_formed = '\n'.join(self.channels_format(top_channels))
|
||||
categories_formed = '\n\n'.join([self.category_format(tup) for tup in category_channels])
|
||||
topChannels_formed = "\n".join(self.channels_format(top_channels))
|
||||
categories_formed = "\n\n".join([self.category_format(tup) for tup in category_channels])
|
||||
|
||||
await ctx.send(f"{ctx.guild.name} has {len(channels)} channel{'s' if len(channels) > 1 else ''}.")
|
||||
await ctx.send(
|
||||
f"{ctx.guild.name} has {len(channels)} channel{'s' if len(channels) > 1 else ''}."
|
||||
)
|
||||
|
||||
for page in cf.pagify(topChannels_formed, delims=['\n'], shorten_by=16):
|
||||
for page in cf.pagify(topChannels_formed, delims=["\n"], shorten_by=16):
|
||||
await ctx.send(asciidoc(page))
|
||||
|
||||
for page in cf.pagify(categories_formed, delims=['\n\n'], shorten_by=16):
|
||||
for page in cf.pagify(categories_formed, delims=["\n\n"], shorten_by=16):
|
||||
await ctx.send(asciidoc(page))
|
||||
|
||||
@commands.guild_only()
|
||||
@@ -569,6 +579,29 @@ class Tools(BaseCog):
|
||||
)
|
||||
)
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command(aliases=["listroles"])
|
||||
@checks.mod_or_permissions(manage_guild=True)
|
||||
async def rolelist(self, ctx):
|
||||
"""Displays the server's roles."""
|
||||
form = "`{rpos:0{zpadding}}` - `{rid}` - `{rcolor}` - {rment} "
|
||||
max_zpadding = max([len(str(r.position)) for r in ctx.guild.roles])
|
||||
rolelist = [
|
||||
form.format(rpos=r.position, zpadding=max_zpadding, rid=r.id, rment=r.mention, rcolor=r.color)
|
||||
for r in ctx.guild.roles
|
||||
]
|
||||
|
||||
rolelist = sorted(rolelist, reverse=True)
|
||||
rolelist = "\n".join(rolelist)
|
||||
embed_list = []
|
||||
for page in cf.pagify(rolelist, shorten_by=1400):
|
||||
embed = discord.Embed(
|
||||
description=f"**Total roles:** {len(ctx.guild.roles)}\n\n{page}",
|
||||
colour=await ctx.embed_colour(),
|
||||
)
|
||||
embed_list.append(embed)
|
||||
await menu(ctx, embed_list, DEFAULT_CONTROLS)
|
||||
|
||||
@commands.command(hidden=True)
|
||||
async def sharedservers(self, ctx, user: discord.Member = None):
|
||||
"""Shows shared server info. Defaults to author."""
|
||||
@@ -809,7 +842,10 @@ class Tools(BaseCog):
|
||||
channels.pop(channels.index(c))
|
||||
temp[c.category].append(c)
|
||||
|
||||
category_channels = sorted([(cat, sorted(chans, key=lambda c: c.position)) for cat, chans in temp.items()], key=lambda t: t[0].position)
|
||||
category_channels = sorted(
|
||||
[(cat, sorted(chans, key=lambda c: c.position)) for cat, chans in temp.items()],
|
||||
key=lambda t: t[0].position,
|
||||
)
|
||||
return channels, category_channels
|
||||
|
||||
def channels_format(self, channels: list):
|
||||
@@ -824,8 +860,14 @@ class Tools(BaseCog):
|
||||
name_justify = max([len(c.name[:24]) for c in channels])
|
||||
type_justify = max([len(type_name(c)) for c in channels])
|
||||
|
||||
return [channel_form.format(name=c.name[:24].ljust(name_justify), ctype=type_name(c).ljust(type_justify), cid=c.id) for c in channels]
|
||||
|
||||
return [
|
||||
channel_form.format(
|
||||
name=c.name[:24].ljust(name_justify),
|
||||
ctype=type_name(c).ljust(type_justify),
|
||||
cid=c.id,
|
||||
)
|
||||
for c in channels
|
||||
]
|
||||
|
||||
def category_format(self, cat_chan_tuple: tuple):
|
||||
|
||||
@@ -834,7 +876,7 @@ class Tools(BaseCog):
|
||||
|
||||
chfs = self.channels_format(chs)
|
||||
if chfs != []:
|
||||
ch_forms = ['\t' + f for f in chfs]
|
||||
return '\n'.join([f'{cat.name} :: {cat.id}'] + ch_forms)
|
||||
ch_forms = ["\t" + f for f in chfs]
|
||||
return "\n".join([f"{cat.name} :: {cat.id}"] + ch_forms)
|
||||
else:
|
||||
return '\n'.join([f'{cat.name} :: {cat.id}'] + ['\tNo Channels'])
|
||||
return "\n".join([f"{cat.name} :: {cat.id}"] + ["\tNo Channels"])
|
||||
|
||||
@@ -2,15 +2,20 @@ import asyncio
|
||||
import datetime
|
||||
import discord
|
||||
import random
|
||||
import math
|
||||
from typing import Union # <- Remove this at 3.2
|
||||
from babel.numbers import format_decimal # <- Remove this at 3.2
|
||||
from redbot.core import commands, checks, Config, bank
|
||||
from redbot.core.utils.chat_formatting import box, pagify
|
||||
from redbot.core.utils.chat_formatting import box, pagify #, humanize_number <- Will be for 3.2
|
||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||
|
||||
__version__ = "0.0.5"
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
__version__ = "0.0.6a"
|
||||
|
||||
def humanize_number(val: Union[int, float]): # <- Remove this at 3.2
|
||||
return format_decimal(val, locale="en_US")
|
||||
|
||||
|
||||
class TrickOrTreat(BaseCog):
|
||||
class TrickOrTreat(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, 2710311393, force_registration=True)
|
||||
@@ -39,8 +44,10 @@ class TrickOrTreat(BaseCog):
|
||||
pick = await self.config.guild(ctx.guild).pick()
|
||||
if not candy_type:
|
||||
candy_type = "candies"
|
||||
if number <= 0:
|
||||
number == 1
|
||||
if number < 0:
|
||||
return await ctx.send("That doesn't sound fun.")
|
||||
if number == 0:
|
||||
return await ctx.send("You pretend to eat a candy.")
|
||||
if candy_type in ["candies", "candy"]:
|
||||
candy_type = "candies"
|
||||
if candy_type in ["lollipops", "lollipop"]:
|
||||
@@ -127,7 +134,9 @@ class TrickOrTreat(BaseCog):
|
||||
)
|
||||
|
||||
pluralcandy = "candy" if number == 1 else "candies"
|
||||
await ctx.send(f"{random.choice(eat_phrase)} {number} {pluralcandy}.")
|
||||
await ctx.send(
|
||||
f"{random.choice(eat_phrase)} {number} {pluralcandy}. (Total eaten: `{humanize_number(await self.config.user(ctx.author).eaten() + number)}` \N{CANDY})"
|
||||
)
|
||||
await self.config.user(ctx.author).sickness.set(userdata["sickness"] + (number * 2))
|
||||
await self.config.user(ctx.author).candies.set(userdata["candies"] - number)
|
||||
await self.config.user(ctx.author).eaten.set(userdata["eaten"] + number)
|
||||
@@ -154,7 +163,7 @@ class TrickOrTreat(BaseCog):
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@commands.command()
|
||||
async def balance(self, ctx):
|
||||
async def totbalance(self, ctx):
|
||||
"""Check how many candies are 'on the ground' in the guild."""
|
||||
pick = await self.config.guild(ctx.guild).pick()
|
||||
await ctx.send(f"The guild is currently holding: {pick} \N{CANDY}")
|
||||
@@ -179,68 +188,111 @@ class TrickOrTreat(BaseCog):
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command()
|
||||
@commands.bot_has_permissions(embed_links=True, add_reactions=True)
|
||||
async def cboard(self, ctx):
|
||||
"""Show the candy eating leaderboard."""
|
||||
userinfo = await self.config._all_from_scope(scope="USER")
|
||||
if not userinfo:
|
||||
return await ctx.send("No one has any candy.")
|
||||
message = await ctx.send("Populating leaderboard...")
|
||||
sorted_acc = sorted(userinfo.items(), key=lambda x: x[1]["eaten"], reverse=True)
|
||||
msg = "{name:33}{score:19}\n".format(name="Name", score="Candies Eaten")
|
||||
for i, account in enumerate(sorted_acc):
|
||||
async with ctx.typing():
|
||||
sorted_acc = sorted(userinfo.items(), key=lambda x: x[1]["eaten"], reverse=True)
|
||||
# Leaderboard logic from https://github.com/Cog-Creators/Red-DiscordBot/blob/V3/develop/redbot/cogs/economy/economy.py#L445
|
||||
pound_len = len(str(len(sorted_acc)))
|
||||
score_len = 10
|
||||
header = "{pound:{pound_len}}{score:{score_len}}{name:2}\n".format(
|
||||
pound="#",
|
||||
pound_len=pound_len + 3,
|
||||
score="Candies Eaten",
|
||||
score_len=score_len + 6,
|
||||
name="\N{THIN SPACE}" + "Name"
|
||||
if not str(ctx.author.mobile_status) in ["online", "idle", "dnd"]
|
||||
else "Name",
|
||||
)
|
||||
temp_msg = header
|
||||
for pos, account in enumerate(sorted_acc):
|
||||
if account[1]["eaten"] == 0:
|
||||
continue
|
||||
user_idx = i + 1
|
||||
user_obj = await self.bot.get_user_info(account[0])
|
||||
try:
|
||||
if account[0] in [member.id for member in ctx.guild.members]:
|
||||
user_obj = ctx.guild.get_member(account[0])
|
||||
else:
|
||||
user_obj = await self.bot.fetch_user(account[0])
|
||||
except AttributeError:
|
||||
user_obj = await self.bot.fetch_user(account[0])
|
||||
|
||||
user_name = f"{user_obj.name}#{user_obj.discriminator}"
|
||||
if len(user_name) > 28:
|
||||
user_name = f"{user_obj.name[:19]}...#{user_obj.discriminator}"
|
||||
user_idx = pos + 1
|
||||
if user_obj == ctx.author:
|
||||
name = f"{user_idx}. <<{user_name}>>"
|
||||
temp_msg += (
|
||||
f"{f'{user_idx}.': <{pound_len + 2}} "
|
||||
f"{humanize_number(account[1]['eaten']) + ' 🍬': <{score_len + 4}} <<{user_name}>>\n"
|
||||
)
|
||||
else:
|
||||
name = f"{user_idx}. {user_name}"
|
||||
msg += f"{name:33}{account[1]['eaten']}\N{CANDY}\n"
|
||||
temp_msg += (
|
||||
f"{f'{user_idx}.': <{pound_len + 2}} "
|
||||
f"{humanize_number(account[1]['eaten']) + ' 🍬': <{score_len + 4}} {user_name}\n"
|
||||
)
|
||||
|
||||
page_list = []
|
||||
for page in pagify(msg, delims=["\n"], page_length=1000):
|
||||
pages = 1
|
||||
for page in pagify(temp_msg, delims=["\n"], page_length=1000):
|
||||
embed = discord.Embed(
|
||||
colour=await ctx.embed_colour(), description=(box(page, lang="md"))
|
||||
colour=0xF4731C,
|
||||
description=box(f"\N{CANDY} Global leaderboard \N{CANDY}", lang="prolog")
|
||||
+ (box(page, lang="md")),
|
||||
)
|
||||
embed.set_footer(
|
||||
text=f"Page {humanize_number(pages)}/{humanize_number(math.ceil(len(temp_msg) / 1500))}"
|
||||
)
|
||||
pages += 1
|
||||
page_list.append(embed)
|
||||
await message.edit(content=box(f"\N{CANDY} Global Leaderboard \N{CANDY}", lang="prolog"))
|
||||
await menu(ctx, page_list, DEFAULT_CONTROLS)
|
||||
return await menu(ctx, page_list, DEFAULT_CONTROLS)
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command()
|
||||
@commands.bot_has_permissions(embed_links=True)
|
||||
async def cinventory(self, ctx):
|
||||
"""Check your inventory."""
|
||||
userdata = await self.config.user(ctx.author).all()
|
||||
msg = f"{ctx.author.mention}'s Candy Bag:\n{userdata['candies']} \N{CANDY}"
|
||||
sickness = userdata["sickness"]
|
||||
msg = f"{ctx.author.mention}'s Candy Bag:"
|
||||
em = discord.Embed(color=await ctx.embed_color())
|
||||
em.description = f"{userdata['candies']} \N{CANDY}"
|
||||
if userdata["lollipops"]:
|
||||
msg += f"\n{userdata['lollipops']} \N{LOLLIPOP}"
|
||||
em.description += f"\n{userdata['lollipops']} \N{LOLLIPOP}"
|
||||
if userdata["stars"]:
|
||||
msg += f"\n{userdata['stars']} \N{WHITE MEDIUM STAR}"
|
||||
if userdata["sickness"] in range(40, 54):
|
||||
msg += "\n\n**Sickness is over 40/100**\n*You don't feel so good...*"
|
||||
if userdata["sickness"] in range(55, 70):
|
||||
msg += "\n\n**Sickness is over 55/100**\n*You don't feel so good...*"
|
||||
if userdata["sickness"] in range(71, 84):
|
||||
msg += "\n\n**Sickness is over 70/100**\n*You really don't feel so good...*"
|
||||
if userdata["sickness"] in range(85, 100):
|
||||
msg += "\n\n**Sickness is over 85/100**\n*The thought of more sugar makes you feel awful...*"
|
||||
if userdata["sickness"] > 100:
|
||||
msg += "\n\n**Sickness is over 100/100**\n*Better wait a while for more candy...*"
|
||||
await ctx.send(msg)
|
||||
em.description += f"\n{userdata['stars']} \N{WHITE MEDIUM STAR}"
|
||||
if sickness in range(41, 56):
|
||||
em.description += f"\n\n**Sickness is over 40/100**\n*You don't feel so good...*"
|
||||
elif sickness in range(56, 71):
|
||||
em.description += f"\n\n**Sickness is over 55/100**\n*You don't feel so good...*"
|
||||
elif sickness in range(71, 86):
|
||||
em.description += (
|
||||
f"\n\n**Sickness is over 70/100**\n*You really don't feel so good...*"
|
||||
)
|
||||
elif sickness in range(86, 101):
|
||||
em.description += f"\n\n**Sickness is over 85/100**\n*The thought of more sugar makes you feel awful...*"
|
||||
elif sickness > 100:
|
||||
em.description += (
|
||||
f"\n\n**Sickness is over 100/100**\n*Better wait a while for more candy...*"
|
||||
)
|
||||
await ctx.send(msg, embed=em)
|
||||
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@commands.command()
|
||||
async def cooldown(self, ctx, cooldown_time: int = 0):
|
||||
async def totcooldown(self, ctx, cooldown_time: int = 0):
|
||||
"""Set the cooldown time for trick or treating on the server."""
|
||||
cooldown = await self.config.guild(ctx.guild).cooldown()
|
||||
if not cooldown_time:
|
||||
if cooldown_time < 0:
|
||||
return await ctx.send("Nice try.")
|
||||
if cooldown_time == 0:
|
||||
await self.config.guild(ctx.guild).cooldown.set(300)
|
||||
await ctx.send("Trick or treating cooldown time reset to 5m.")
|
||||
return await ctx.send("Trick or treating cooldown time reset to 5m.")
|
||||
elif 1 <= cooldown_time <= 30:
|
||||
await self.config.guild(ctx.guild).cooldown.set(30)
|
||||
return await ctx.send("Trick or treating cooldown time set to the minimum of 30s.")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).cooldown.set(cooldown_time)
|
||||
await ctx.send(f"Trick or treating cooldown time set to {cooldown_time}s.")
|
||||
@@ -420,9 +472,10 @@ class TrickOrTreat(BaseCog):
|
||||
async def totversion(self, ctx):
|
||||
"""Trick or Treat version."""
|
||||
await ctx.send(
|
||||
f"Trick or Treat, version {__version__}\n\n*0.0.5 updates:*\n**Save values before waiting on messages:\nQuick commands will not overwrite other values**\n\n*0.0.4 updates:*\n**+2% star chance on trick or treat (6% total)\n+5% lollipop chance on trick or treat (25% total)\nMore RP messages\nFix for steal mechanic freezing\n**"
|
||||
f"Trick or Treat, version {__version__}\n\n*0.0.6 updates:*\n**cooldown -> totcooldown\nGeneral cleanup for 2019**\n\n*0.0.5 updates:*\n**Save values before waiting on messages:\nQuick commands will not overwrite other values**\n\n*0.0.4 updates:*\n**+2% star chance on trick or treat (6% total)\n+5% lollipop chance on trick or treat (25% total)\nMore RP messages\nFix for steal mechanic freezing\n**"
|
||||
)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if isinstance(message.channel, discord.abc.PrivateChannel):
|
||||
return
|
||||
@@ -440,7 +493,7 @@ class TrickOrTreat(BaseCog):
|
||||
new_sickness = 0
|
||||
await self.config.user(message.author).sickness.set(new_sickness)
|
||||
|
||||
if "trick or treat" not in content:
|
||||
if not content.startswith("trick or treat"):
|
||||
return
|
||||
toggle = await self.config.guild(message.guild).toggle()
|
||||
if not toggle:
|
||||
@@ -468,7 +521,7 @@ class TrickOrTreat(BaseCog):
|
||||
"The wind starts to pick up as you look for the next house...",
|
||||
]
|
||||
return await message.channel.send(random.choice(messages))
|
||||
|
||||
await self.config.user(message.author).last_tot.set(str(now))
|
||||
candy = random.randint(1, 25)
|
||||
lollipop = random.randint(0, 100)
|
||||
star = random.randint(0, 100)
|
||||
@@ -526,4 +579,3 @@ class TrickOrTreat(BaseCog):
|
||||
win_message += "\n**BONUS**: 1 \N{WHITE MEDIUM STAR}"
|
||||
|
||||
await message.channel.send(win_message)
|
||||
await self.config.user(message.author).last_tot.set(str(now))
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
"tags": [
|
||||
"wolfram"
|
||||
],
|
||||
"requirements": [
|
||||
"pillow"
|
||||
],
|
||||
"type": "COG"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import aiohttp
|
||||
import os
|
||||
from redbot.core import Config, commands, checks
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
import asyncio
|
||||
import discord
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
import xml.etree.ElementTree as ET
|
||||
import urllib.parse
|
||||
|
||||
from redbot.core import Config, commands, checks
|
||||
from redbot.core.utils.chat_formatting import box, pagify
|
||||
|
||||
|
||||
BaseCog = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class Wolfram(BaseCog):
|
||||
class Wolfram(commands.Cog):
|
||||
"""Ask Wolfram Alpha any question."""
|
||||
|
||||
def __init__(self, bot):
|
||||
@@ -23,29 +25,102 @@ class Wolfram(BaseCog):
|
||||
@commands.command(name="wolfram", aliases=["ask"])
|
||||
async def _wolfram(self, ctx, *question: str):
|
||||
"""Ask Wolfram Alpha any question."""
|
||||
|
||||
api_key = await self.config.WOLFRAM_API_KEY()
|
||||
if not api_key:
|
||||
return await ctx.send(
|
||||
"No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/"
|
||||
)
|
||||
|
||||
if api_key:
|
||||
url = "http://api.wolframalpha.com/v2/query?"
|
||||
query = " ".join(question)
|
||||
payload = {"input": query, "appid": api_key}
|
||||
headers = {"user-agent": "Red-cog/2.0.0"}
|
||||
async with self.session.get(url, params=payload, headers=headers) as r:
|
||||
result = await r.text()
|
||||
root = ET.fromstring(result)
|
||||
a = []
|
||||
for pt in root.findall(".//plaintext"):
|
||||
if pt.text:
|
||||
a.append(pt.text.capitalize())
|
||||
if len(a) < 1:
|
||||
message = "There is as yet insufficient data for a meaningful answer."
|
||||
else:
|
||||
message = "\n".join(a[0:3])
|
||||
url = "http://api.wolframalpha.com/v2/query?"
|
||||
query = " ".join(question)
|
||||
payload = {"input": query, "appid": api_key}
|
||||
headers = {"user-agent": "Red-cog/2.0.0"}
|
||||
async with self.session.get(url, params=payload, headers=headers) as r:
|
||||
result = await r.text()
|
||||
root = ET.fromstring(result)
|
||||
a = []
|
||||
for pt in root.findall(".//plaintext"):
|
||||
if pt.text:
|
||||
a.append(pt.text.capitalize())
|
||||
if len(a) < 1:
|
||||
message = "There is as yet insufficient data for a meaningful answer."
|
||||
else:
|
||||
message = "No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/"
|
||||
message = "\n".join(a[0:3])
|
||||
|
||||
await ctx.send(box(message))
|
||||
|
||||
@commands.command(name="wolframimage")
|
||||
async def _image(self, ctx, *arguments: str):
|
||||
"""Ask Wolfram Alpha any question. Returns an image."""
|
||||
if not arguments:
|
||||
return await ctx.send_help()
|
||||
api_key = await self.config.WOLFRAM_API_KEY()
|
||||
if not api_key:
|
||||
return await ctx.send(
|
||||
"No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/"
|
||||
)
|
||||
|
||||
width = 800
|
||||
font_size = 30
|
||||
layout = "labelbar"
|
||||
background = "193555"
|
||||
foreground = "white"
|
||||
units = "metric"
|
||||
query = " ".join(arguments)
|
||||
query = urllib.parse.quote(query)
|
||||
url = f"http://api.wolframalpha.com/v1/simple?appid={api_key}&i={query}%3F&width={width}&fontsize={font_size}&layout={layout}&background={background}&foreground={foreground}&units={units}"
|
||||
|
||||
async with ctx.channel.typing():
|
||||
async with self.session.request("GET", url) as r:
|
||||
img = await r.content.read()
|
||||
if len(img) == 43:
|
||||
# img = b'Wolfram|Alpha did not understand your input'
|
||||
return await ctx.send(
|
||||
"There is as yet insufficient data for a meaningful answer."
|
||||
)
|
||||
wolfram_img = BytesIO(img)
|
||||
try:
|
||||
await ctx.channel.send(
|
||||
file=discord.File(wolfram_img, f"wolfram{ctx.author.id}.png")
|
||||
)
|
||||
except Exception as e:
|
||||
await ctx.send(f"Oops, there was a problem: {e}")
|
||||
|
||||
@commands.command(name="wolframsolve")
|
||||
async def _solve(self, ctx, *, query: str):
|
||||
"""Ask Wolfram Alpha any math question. Returns step by step answers."""
|
||||
api_key = await self.config.WOLFRAM_API_KEY()
|
||||
if not api_key:
|
||||
return await ctx.send(
|
||||
"No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/"
|
||||
)
|
||||
|
||||
url = f"http://api.wolframalpha.com/v2/query"
|
||||
params = {
|
||||
"appid": api_key,
|
||||
"input": query,
|
||||
"podstate": "Step-by-step solution",
|
||||
"format": "plaintext",
|
||||
}
|
||||
msg = ""
|
||||
|
||||
async with ctx.channel.typing():
|
||||
async with self.session.request("GET", url, params=params) as r:
|
||||
text = await r.content.read()
|
||||
root = ET.fromstring(text)
|
||||
for pod in root.findall(".//pod"):
|
||||
if pod.attrib["title"] == "Number line":
|
||||
continue
|
||||
msg += f"{pod.attrib['title']}\n"
|
||||
for pt in pod.findall(".//plaintext"):
|
||||
if pt.text:
|
||||
strip = pt.text.replace(" | ", " ").replace("| ", " ")
|
||||
msg += f"- {strip}\n\n"
|
||||
if len(msg) < 1:
|
||||
msg = "There is as yet insufficient data for a meaningful answer."
|
||||
for text in pagify(msg):
|
||||
await ctx.send(box(text))
|
||||
|
||||
@checks.is_owner()
|
||||
@commands.command(name="setwolframapi", aliases=["setwolfram"])
|
||||
async def _setwolframapi(self, ctx, key: str):
|
||||
@@ -55,5 +130,5 @@ class Wolfram(BaseCog):
|
||||
await self.config.WOLFRAM_API_KEY.set(key)
|
||||
await ctx.send("Key set.")
|
||||
|
||||
def __unload(self):
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self.session.close())
|
||||
|
||||
5
youtube/__init__.py
Normal file
5
youtube/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .youtube import YouTube
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(YouTube(bot))
|
||||
12
youtube/info.json
Normal file
12
youtube/info.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"author": [
|
||||
"aikaterna"
|
||||
],
|
||||
"description": "Search youtube for videos, originally by Paddo. This version also includes a ytsearch command to look through multiple results.",
|
||||
"install_msg": "Thanks for installing, have fun.",
|
||||
"short": "Search youtube for videos.",
|
||||
"tags": [
|
||||
"youtube"
|
||||
],
|
||||
"type": "COG"
|
||||
}
|
||||
47
youtube/youtube.py
Normal file
47
youtube/youtube.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import aiohttp
|
||||
import re
|
||||
from redbot.core import commands
|
||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||
|
||||
|
||||
class YouTube(commands.Cog):
|
||||
"""Search YouTube for videos."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.session = aiohttp.ClientSession()
|
||||
|
||||
async def _youtube_results(self, query: str):
|
||||
try:
|
||||
search_url = "https://www.youtube.com/results?"
|
||||
payload = {"search_query": "".join(query)}
|
||||
headers = {"user-agent": "Red-cog/3.0"}
|
||||
async with self.session.get(search_url, params=payload, headers=headers) as r:
|
||||
result = await r.text()
|
||||
yt_find = re.findall(r"href=\"\/watch\?v=(.{11})", result)
|
||||
|
||||
url_list = []
|
||||
for track in yt_find:
|
||||
url = f"https://www.youtube.com/watch?v={track}"
|
||||
if url not in url_list:
|
||||
url_list.append(url)
|
||||
|
||||
except Exception as e:
|
||||
url_list = [f"Something went terribly wrong! [{e}]"]
|
||||
|
||||
return url_list
|
||||
|
||||
@commands.command()
|
||||
async def youtube(self, ctx, *, query: str):
|
||||
"""Search on Youtube."""
|
||||
result = await self._youtube_results(query)
|
||||
await ctx.send(result[0])
|
||||
|
||||
@commands.command()
|
||||
async def ytsearch(self, ctx, *, query: str):
|
||||
"""Search on Youtube, multiple results."""
|
||||
result = await self._youtube_results(query)
|
||||
await menu(ctx, result, DEFAULT_CONTROLS)
|
||||
|
||||
def cog_unload(self):
|
||||
self.bot.loop.create_task(self.session.close())
|
||||
Reference in New Issue
Block a user