mirror of
https://github.com/stijndcl/didier.git
synced 2026-04-07 15:48:29 +02:00
Initial commit
Transfer Didier to this repo
This commit is contained in:
commit
f1138c3b56
116 changed files with 353825 additions and 0 deletions
226
cogs/birthdays.py
Normal file
226
cogs/birthdays.py
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
from data import constants
|
||||
import datetime
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import timeFormatters, stringFormatters
|
||||
from functions.database import birthdays
|
||||
|
||||
|
||||
class Birthdays(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Birthday", aliases=["Bd", "Birthdays"], case_insensitive=True, invoke_without_command=True)
|
||||
@help.Category(Category.Other)
|
||||
async def birthday(self, ctx, member: discord.Member = None):
|
||||
"""
|
||||
Command to check the birthday of yourself/another person.
|
||||
:param ctx: Discord Context
|
||||
:param member: The member to check
|
||||
"""
|
||||
if member is not None:
|
||||
# A member was tagged
|
||||
nameStr = "**{}**'s".format(member.display_name)
|
||||
res = birthdays.get_user(member.id)
|
||||
else:
|
||||
# No member passed -> check the user's birthday
|
||||
nameStr = "Jouw"
|
||||
res = birthdays.get_user(ctx.author.id)
|
||||
|
||||
if not res:
|
||||
# Nothing found in the db for this member
|
||||
return await ctx.send("{} verjaardag zit nog niet in de database.".format(nameStr))
|
||||
|
||||
# Create a datetime object of the upcoming birthday,
|
||||
# and a formatted string displaying the date
|
||||
dayDatetime, timeString = self.dmToDatetime(res[0][0], res[0][1])
|
||||
|
||||
# Find the weekday related to this day
|
||||
weekday = timeFormatters.intToWeekday(dayDatetime.weekday()).lower()
|
||||
|
||||
return await ctx.send("{} verjaardag staat ingesteld op **{} {}**.".format(
|
||||
nameStr, weekday, timeString
|
||||
))
|
||||
|
||||
@birthday.command(name="Today", aliases=["Now"])
|
||||
async def today(self, ctx):
|
||||
"""
|
||||
Command that lists all birthdays of the day.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
# Create a datetime object for today
|
||||
dt = timeFormatters.dateTimeNow()
|
||||
await ctx.send(self.getBirthdayOnDate(dt))
|
||||
|
||||
@birthday.command(name="Tomorrow", aliases=["Tm", "Tmw"])
|
||||
async def tomorrow(self, ctx):
|
||||
"""
|
||||
Command that lists all birthdays of tomorrow.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
# Create a datetime object for tomorrow
|
||||
dt = timeFormatters.dateTimeNow() + datetime.timedelta(days=1)
|
||||
await ctx.send(self.getBirthdayOnDate(dt).replace("Vandaag", "Morgen").replace("vandaag", "morgen"))
|
||||
|
||||
@birthday.command(name="Week")
|
||||
async def week(self, ctx):
|
||||
"""
|
||||
Command that lists all birthdays for the coming week.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
# Dict of all birthdays this week
|
||||
this_week = {}
|
||||
|
||||
# Create a datetime object starting yesterday so the first line
|
||||
# of the loop can add a day every time,
|
||||
# as premature returning would prevent this from happening
|
||||
# & get the day stuck
|
||||
dt = timeFormatters.dateTimeNow() - datetime.timedelta(days=1)
|
||||
|
||||
# Create an embed
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Verjaardagen deze week")
|
||||
|
||||
# Add all people of the coming week
|
||||
for dayCounter in range(7):
|
||||
dt += datetime.timedelta(days=1)
|
||||
res = birthdays.get_users_on_date(dt.day, dt.month)
|
||||
|
||||
# No birthdays on this day
|
||||
if not res:
|
||||
continue
|
||||
|
||||
# Add everyone from this day into the dict
|
||||
this_week[str(dayCounter)] = {"day": dt.day, "month": dt.month, "users": []}
|
||||
|
||||
for user in res:
|
||||
this_week[str(dayCounter)]["users"].append(user[0])
|
||||
|
||||
# No one found
|
||||
if not this_week:
|
||||
embed.description = "Deze week is er niemand jarig."
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
COC = self.client.get_guild(int(constants.CallOfCode))
|
||||
|
||||
# For every day, add the list of users into the embed
|
||||
for day, value in this_week.items():
|
||||
|
||||
dayDatetime, timeString = self.dmToDatetime(int(value["day"]), int(value["month"]))
|
||||
weekday = timeFormatters.intToWeekday(dayDatetime.weekday())
|
||||
|
||||
embed.add_field(name="{} {}".format(weekday, timeString),
|
||||
value=", ".join(COC.get_member(user).mention for user in value["users"]),
|
||||
inline=False)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
def getBirthdayOnDate(self, dt):
|
||||
"""
|
||||
Function to get all birthdays on a certain date.
|
||||
Returns a string right away to avoid more code duplication.
|
||||
:param dt: the date (Python datetime instance)
|
||||
:return: A formatted string containing all birthdays on [dt]
|
||||
"""
|
||||
res = birthdays.get_users_on_date(dt.day, dt.month)
|
||||
|
||||
# Nobody's birthday
|
||||
if not res:
|
||||
return "Vandaag is er niemand jarig."
|
||||
|
||||
COC = self.client.get_guild(int(constants.CallOfCode))
|
||||
|
||||
# Create a list of member objects of the people that have a birthday on this date
|
||||
people = [COC.get_member(int(user[0])) for user in res]
|
||||
|
||||
if len(people) == 1:
|
||||
return "Vandaag is **{}** jarig.".format(people[0].display_name)
|
||||
return "Vandaag zijn {} en {} jarig.".format(
|
||||
", ".join("**" + user.display_name + "**" for user in people[:-1]),
|
||||
people[-1].display_name
|
||||
)
|
||||
|
||||
def dmToDatetime(self, day, month):
|
||||
"""
|
||||
Converts a day + month to a datetime instance.
|
||||
:param day: the day in the date
|
||||
:param month: the month in the date
|
||||
:return: a datetime instance representing the next time this date occurs,
|
||||
and a formatted string for this date
|
||||
"""
|
||||
now = timeFormatters.dateTimeNow()
|
||||
year = now.year
|
||||
|
||||
# Add an extra year to the date in case it has already passed
|
||||
if month < now.month or (month == now.month and day < now.day):
|
||||
year += 1
|
||||
|
||||
# Create a datetime object for this birthday
|
||||
timeString = "{}/{}/{}".format(
|
||||
stringFormatters.leadingZero(str(day)),
|
||||
stringFormatters.leadingZero(str(month)),
|
||||
year
|
||||
)
|
||||
|
||||
dayDatetime = datetime.datetime.strptime(timeString, "%d/%m/%Y")
|
||||
return dayDatetime, timeString
|
||||
|
||||
@birthday.command(name="Set", usage="[DD/MM/YYYY]")
|
||||
async def set(self, ctx, date=None, member: discord.Member = None):
|
||||
"""
|
||||
Command to add your birthday into the database.
|
||||
:param ctx: Discord Context
|
||||
:param date: the date of your birthday
|
||||
:param member: another member whose birthday has to be added/changed
|
||||
"""
|
||||
# No date passed
|
||||
if date is None:
|
||||
return await ctx.send("Geef een datum op.")
|
||||
|
||||
# Invalid format used
|
||||
if date.count("/") != 2:
|
||||
return await ctx.send("Ongeldig formaat (gebruik DD/MM/YYYY).")
|
||||
|
||||
# Check if anything is wrong with the date
|
||||
try:
|
||||
day = int(date.split("/")[0])
|
||||
month = int(date.split("/")[1])
|
||||
year = int(date.split("/")[2])
|
||||
|
||||
# This is not used, but creating an invalid datetime object throws a ValueError
|
||||
# so it prevents invalid dates like 69/420/360
|
||||
dt = datetime.datetime(year=year, month=month, day=day)
|
||||
|
||||
# Assume no one in the Discord is more than 5 years younger, or 10 years older
|
||||
# (which are also virtually impossible, but just to be sure)
|
||||
if year >= timeFormatters.dateTimeNow().year - 15 or year < 1990:
|
||||
raise ValueError
|
||||
|
||||
except ValueError:
|
||||
return await ctx.send("Dit is geen geldige datum.")
|
||||
|
||||
# A member was tagged, check if I did it
|
||||
if member is not None:
|
||||
if str(ctx.author.id) != str(constants.myId):
|
||||
return await ctx.send("Je kan andere mensen hun verjaardag niet instellen, {}.".format(ctx.author.display_name))
|
||||
else:
|
||||
birthdays.add_user(member.id, day, month, year)
|
||||
return await ctx.message.add_reaction("✅")
|
||||
|
||||
# Birthday is already added
|
||||
if birthdays.get_user(ctx.author.id) and str(ctx.author.id) != constants.myId:
|
||||
return await ctx.send("Je verjaardag zit al in de database.")
|
||||
|
||||
# Add into the db
|
||||
birthdays.add_user(ctx.author.id, day, month, year)
|
||||
return await ctx.send("Je verjaardag is toegevoegd aan de database.")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Birthdays(client))
|
||||
149
cogs/bitcoin.py
Normal file
149
cogs/bitcoin.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
from converters.numbers import Abbreviated
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, timeFormatters
|
||||
from functions.database import currency
|
||||
import requests
|
||||
|
||||
|
||||
class Bitcoin(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Bitcoin", aliases=["Bc"], case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def bc(self, ctx):
|
||||
"""
|
||||
Command that shows your Bitcoin bank.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
price = self.getPrice()
|
||||
bc = float(currency.getOrAddUser(ctx.author.id)[8])
|
||||
|
||||
currentTime = timeFormatters.dateTimeNow()
|
||||
currentTimeFormatted = currentTime.strftime('%m/%d/%Y om %H:%M:%S')
|
||||
|
||||
# Create the embed
|
||||
embed = discord.Embed(colour=discord.Colour.gold())
|
||||
embed.set_author(name="Bitcoin Bank van {}".format(ctx.author.display_name))
|
||||
embed.add_field(name="Aantal Bitcoins:", value="{:,}".format(round(bc, 8)), inline=False)
|
||||
embed.add_field(name="Huidige waarde:", value="{:,} Didier Dink{}"
|
||||
.format(round(bc * price, 8), checks.pluralS(bc * price)), inline=False)
|
||||
embed.set_footer(text="Huidige Bitcoin prijs: €{:,} ({})".format(price, str(currentTimeFormatted)))
|
||||
|
||||
# Add the Bitcoin icon to the embed
|
||||
file = discord.File("files/images/bitcoin.png", filename="icon.png")
|
||||
embed.set_thumbnail(url="attachment://icon.png")
|
||||
|
||||
await ctx.send(embed=embed, file=file)
|
||||
|
||||
@bc.command(name="Price")
|
||||
async def price(self, ctx):
|
||||
"""
|
||||
Command that shows the current Bitcoin price.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
price = self.getPrice()
|
||||
currentTime = timeFormatters.dateTimeNow()
|
||||
currentTimeFormatted = currentTime.strftime('%m/%d/%Y om %H:%M:%S')
|
||||
await ctx.send(
|
||||
"Huidige Bitcoin prijs: **€{:,}** ({}).".format(price, str(currentTimeFormatted)))
|
||||
|
||||
@bc.command(name="Buy", usage="[Aantal]")
|
||||
async def buy(self, ctx, amount: Abbreviated):
|
||||
"""
|
||||
Command to buy Bitcoins.
|
||||
:param ctx: Discord Context
|
||||
:param amount: the amount of Bitcoins the user wants to buy
|
||||
"""
|
||||
|
||||
resp = checks.isValidAmount(ctx, amount)
|
||||
|
||||
# Not a valid amount: send the appropriate error message
|
||||
if not resp[0]:
|
||||
return await ctx.send(resp[1])
|
||||
|
||||
if amount == "all":
|
||||
amount = resp[1]
|
||||
|
||||
# Calculate the amount of Bitcoins the user can buy with [amount] of Didier Dinks
|
||||
price = self.getPrice()
|
||||
purchased = round(float(amount) / price, 8)
|
||||
|
||||
# Update the db
|
||||
currency.update(ctx.author.id, "dinks", float(currency.dinks(ctx.author.id)) - float(amount))
|
||||
currency.update(ctx.author.id, "bitcoins",
|
||||
float(currency.getOrAddUser(ctx.author.id)[8]) + float(purchased))
|
||||
|
||||
await ctx.send("**{}** heeft **{:,}** Bitcoin{} gekocht voor **{:,}** Didier Dink{}!"
|
||||
.format(ctx.author.display_name, purchased, checks.pluralS(purchased),
|
||||
round(float(amount)), checks.pluralS(amount)))
|
||||
|
||||
@bc.command(name="Sell", usage="[Aantal]")
|
||||
async def sell(self, ctx, amount: Abbreviated):
|
||||
"""
|
||||
Command to sell Bitcoins.
|
||||
:param ctx: Discord Context
|
||||
:param amount: the amount of Bitcoins the user wants to sell
|
||||
"""
|
||||
if amount == "all":
|
||||
amount = float(currency.getOrAddUser(ctx.author.id)[8])
|
||||
|
||||
try:
|
||||
amount = float(amount)
|
||||
if amount <= 0:
|
||||
raise ValueError
|
||||
|
||||
bc = float(currency.getOrAddUser(ctx.author.id)[8])
|
||||
|
||||
if bc == 0.0:
|
||||
# User has no Bitcoins
|
||||
await ctx.send("Je hebt geen Bitcoins, **{}**".format(ctx.author.display_name))
|
||||
elif amount > bc:
|
||||
# User is trying to sell more Bitcoins that he has
|
||||
await ctx.send("Je hebt niet genoeg Bitcoins om dit te doen, **{}**"
|
||||
.format(ctx.author.display_name))
|
||||
else:
|
||||
price = self.getPrice()
|
||||
dinks = float(currency.dinks(ctx.author.id))
|
||||
|
||||
currency.update(ctx.author.id, "bitcoins", bc - amount)
|
||||
currency.update(ctx.author.id, "dinks", dinks + (price * amount))
|
||||
|
||||
await ctx.send("**{}** heeft **{:,}** Bitcoin{} verkocht voor **{:,}** Didier Dink{}!"
|
||||
.format(ctx.author.display_name, round(amount, 8), checks.pluralS(amount),
|
||||
round((price * amount), 8), checks.pluralS(price * amount)))
|
||||
except ValueError:
|
||||
# Can't be parsed to float -> random string OR smaller than 0
|
||||
await ctx.send("Geef een geldig bedrag op.")
|
||||
|
||||
@bc.command(aliases=["Lb", "Leaderboards"], hidden=True)
|
||||
@help.Category(category=Category.Other)
|
||||
async def leaderboard(self, ctx):
|
||||
"""
|
||||
Command that shows the Bitcoin Leaderboard.
|
||||
Alias for Lb Bc.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
# Call the appropriate leaderboard function
|
||||
await self.client.get_cog("Leaderboards").callLeaderboard("bitcoin", ctx)
|
||||
|
||||
def getPrice(self):
|
||||
"""
|
||||
Function to get the current Bitcoin price.
|
||||
:return: the current Bitcoin price (float)
|
||||
"""
|
||||
result = requests.get("https://api.coindesk.com/v1/bpi/currentprice.json").json()
|
||||
currentPrice = result["bpi"]["EUR"]["rate_float"]
|
||||
return float(currentPrice)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Bitcoin(client))
|
||||
296
cogs/corona.py
Normal file
296
cogs/corona.py
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, timeFormatters
|
||||
import requests
|
||||
|
||||
|
||||
class Corona(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
# Gets the information & calls other functions if necessary
|
||||
@commands.group(name="Corona", usage="[Land]*", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def corona(self, ctx, country: str = "Belgium"):
|
||||
"""
|
||||
Command that shows the corona stats for a certain country.
|
||||
:param ctx: Discord Context
|
||||
:param country: the country to show the stats for
|
||||
"""
|
||||
dic = await self.getCountryStats(country)
|
||||
if dic is None:
|
||||
# Country was not found
|
||||
await self.sendError(ctx)
|
||||
return
|
||||
|
||||
await self.sendEmbed(ctx, dic)
|
||||
|
||||
@corona.command(aliases=["lb", "leaderboards"], hidden=True)
|
||||
async def leaderboard(self, ctx):
|
||||
"""
|
||||
Command that shows the Corona Leaderboard.
|
||||
Alias for Lb Corona.
|
||||
:param ctx: Discord Context
|
||||
:return: y
|
||||
"""
|
||||
await self.client.get_cog("Leaderboards").callLeaderboard("corona", ctx)
|
||||
|
||||
async def sendEmbed(self, ctx, dic):
|
||||
"""
|
||||
Function that sends a Corona embed from a dictionary.
|
||||
:param ctx: Discord Context
|
||||
:param dic: the dictionary corresponding to this country
|
||||
"""
|
||||
embed = discord.Embed(colour=discord.Colour.red(), title="Coronatracker {}".format(dic["today"]["country"]))
|
||||
embed.set_thumbnail(url="https://i.imgur.com/aWnDuBt.png")
|
||||
|
||||
# Total
|
||||
embed.add_field(name="Totale Gevallen (Vandaag):",
|
||||
value="{:,} **(+{:,})** {}".format(
|
||||
dic["today"]["cases"],
|
||||
dic["today"]["todayCases"],
|
||||
self.trendIndicator(dic, "todayCases")
|
||||
),
|
||||
inline=False)
|
||||
|
||||
# Active
|
||||
embed.add_field(name="Actieve Gevallen (Vandaag):",
|
||||
value="{:,} **(+{:,})** {}".format(
|
||||
dic["today"]["activeCases"],
|
||||
dic["today"]["activeCases"] - dic["yesterday"]["activeCases"],
|
||||
self.activeTrendIndicator(dic)
|
||||
),
|
||||
inline=False)
|
||||
|
||||
# Deaths
|
||||
embed.add_field(name="Sterfgevallen (Vandaag):",
|
||||
value="{:,} **(+{:,})** {}".format(
|
||||
dic["today"]["deaths"],
|
||||
dic["today"]["todayDeaths"],
|
||||
self.trendIndicator(dic, "todayDeaths")
|
||||
),
|
||||
inline=False)
|
||||
|
||||
# Recovered
|
||||
embed.add_field(name="Hersteld (Vandaag):",
|
||||
value="{:,} **(+{:,}) {}**".format(
|
||||
dic["today"]["recovered"],
|
||||
dic["today"]["todayRecovered"],
|
||||
self.trendIndicator(dic, "todayRecovered")
|
||||
),
|
||||
inline=False)
|
||||
|
||||
# Test Cases
|
||||
embed.add_field(name="Aantal uitgevoerde tests:",
|
||||
value="{:,}".format(dic["today"]["tests"]),
|
||||
inline=False)
|
||||
|
||||
# Timestamp of last update
|
||||
timeFormatted = timeFormatters.epochToDate(dic["today"]["updated"])
|
||||
embed.set_footer(text="Laatst geüpdatet op {} ({} geleden)".format(
|
||||
timeFormatted["date"], timeFormatted["timeAgo"]))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="Trends", aliases=["Ct"], usage="[Land]*")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def trends(self, ctx, country: str = "Belgium"):
|
||||
"""
|
||||
Command that gives more precise stats & changes.
|
||||
:param ctx: Discord Context
|
||||
:param country: the country to get the stats for
|
||||
"""
|
||||
dic = await self.getCountryStats(country)
|
||||
if dic is None:
|
||||
await self.sendError(ctx)
|
||||
return
|
||||
|
||||
# Get the distribution for this country
|
||||
distribution = self.distribution(dic)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.red(), title="Coronatrends {}".format(dic["today"]["country"]))
|
||||
embed.set_thumbnail(url="https://i.imgur.com/aWnDuBt.png")
|
||||
|
||||
# Calculate the trends & add them into the fields
|
||||
embed.add_field(name="Totale Gevallen\n({:,})".format(dic["today"]["cases"]),
|
||||
value=self.trend(dic, "cases"),
|
||||
inline=True)
|
||||
|
||||
embed.add_field(name="Sterfgevallen\n({:,})".format(dic["today"]["deaths"]),
|
||||
value=self.trend(dic, "deaths"),
|
||||
inline=True)
|
||||
|
||||
embed.add_field(name="Hersteld\n({:,})".format(dic["today"]["recovered"]),
|
||||
value=self.trend(dic, "recovered"))
|
||||
|
||||
embed.add_field(name="Totale Gevallen\nVandaag ({:,})".format(dic["today"]["todayCases"]),
|
||||
value=self.trend(dic, "todayCases"),
|
||||
inline=True)
|
||||
|
||||
embed.add_field(name="Sterfgevallen\nVandaag ({:,})".format(dic["today"]["todayDeaths"]),
|
||||
value=self.trend(dic, "todayDeaths"),
|
||||
inline=True)
|
||||
|
||||
embed.add_field(name="Hersteld\nVandaag ({:,})".format(dic["today"]["todayRecovered"]),
|
||||
value=self.trend(dic, "todayRecovered"))
|
||||
|
||||
embed.add_field(name="Verdeling", value="Actief: {} | Overleden: {} | Hersteld: {}".format(
|
||||
distribution[0], distribution[1], distribution[2]), inline=False)
|
||||
|
||||
# Timestamp of last update
|
||||
timeFormatted = timeFormatters.epochToDate(dic["today"]["updated"])
|
||||
embed.set_footer(text="Laatst geüpdatet op {} ({} geleden)".format(
|
||||
timeFormatted["date"], timeFormatted["timeAgo"]))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
async def getCountryStats(self, country):
|
||||
"""
|
||||
Function that gets the stats for a specific country.
|
||||
:param country: the country to get the stats for
|
||||
:return: a dictionary containing the info for today & yesterday
|
||||
"""
|
||||
# Check if Global or a country was passed
|
||||
if country.lower() == "global":
|
||||
country = "all?"
|
||||
else:
|
||||
country = "countries/{}?strict=false&".format(country)
|
||||
|
||||
today = requests.get("https://disease.sh/v3/covid-19/{}yesterday=false&allowNull=false".format(country)).json()
|
||||
|
||||
# Send error message
|
||||
if "message" in today:
|
||||
return None
|
||||
|
||||
yesterday = requests.get("https://disease.sh/v3/covid-19/{}yesterday=true&allowNull=false".format(country)) \
|
||||
.json()
|
||||
|
||||
# Divide into today & yesterday to be able to calculate the changes
|
||||
dic = {
|
||||
"today": {
|
||||
"country": today["country"] if country != "all?" else "Global",
|
||||
"cases": today["cases"],
|
||||
"activeCases": today["active"],
|
||||
"todayCases": today["todayCases"],
|
||||
"deaths": today["deaths"],
|
||||
"todayDeaths": today["todayDeaths"],
|
||||
"recovered": today["recovered"],
|
||||
"todayRecovered": today["todayRecovered"],
|
||||
"tests": today["tests"],
|
||||
"updated": today["updated"]
|
||||
},
|
||||
"yesterday": {
|
||||
"cases": yesterday["cases"],
|
||||
"activeCases": yesterday["active"],
|
||||
"todayCases": yesterday["todayCases"],
|
||||
"deaths": yesterday["deaths"],
|
||||
"todayDeaths": yesterday["todayDeaths"],
|
||||
"recovered": yesterday["recovered"],
|
||||
"todayRecovered": yesterday["todayRecovered"],
|
||||
"tests": yesterday["tests"],
|
||||
"updated": yesterday["updated"]
|
||||
}
|
||||
}
|
||||
return dic
|
||||
|
||||
def distribution(self, dic):
|
||||
"""
|
||||
Calculates the percentage distribution for every key & shows an indicator.
|
||||
:param dic: the today/yesterday dictionary for this country
|
||||
:return: a list containing the distribution + indicator for active, recovered & deaths
|
||||
"""
|
||||
totalToday = dic["today"]["cases"] if dic["today"]["cases"] != 0 else 1
|
||||
totalYesterday = dic["yesterday"]["cases"] if dic["yesterday"]["cases"] != 0 else 1
|
||||
|
||||
tap = round(100 * dic["today"]["activeCases"]/totalToday, 2) # Today Active Percentage
|
||||
trp = round(100 * dic["today"]["recovered"]/totalToday, 2) # Today Recovered Percentage
|
||||
tdp = round(100 * dic["today"]["deaths"]/totalToday, 2) # Today Deaths Percentage
|
||||
yap = round(100 * dic["yesterday"]["activeCases"] / totalYesterday, 2) # Yesterday Active Percentage
|
||||
yrp = round(100 * dic["yesterday"]["recovered"] / totalYesterday, 2) # Yesterday Recovered Percentage
|
||||
ydp = round(100 * dic["yesterday"]["deaths"] / totalYesterday, 2) # Yesterday Deaths Percentage
|
||||
|
||||
return ["{}% {}".format(tap, self.indicator(tap, yap)),
|
||||
"{}% {}".format(tdp, self.indicator(tdp, ydp)),
|
||||
"{}% {}".format(trp, self.indicator(trp, yrp))]
|
||||
|
||||
async def sendError(self, ctx):
|
||||
"""
|
||||
Function that sends an error embed when an invalid country was passed.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
embed = discord.Embed(colour=discord.Colour.red())
|
||||
embed.add_field(name="Error", value="Dit land staat niet in de database.", inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
# Returns a number and a percentage of rise/decline
|
||||
def trend(self, dic, key):
|
||||
"""
|
||||
Function that creates a string representing a number & percentage of
|
||||
rise & decline for a certain key of the dict.
|
||||
:param dic: the today/yesterday dictionary for this country
|
||||
:param key: the key to compare
|
||||
:return: a string showing the increase in numbers & percentages
|
||||
"""
|
||||
# Difference vs yesterday
|
||||
change = dic["today"][key] - dic["yesterday"][key]
|
||||
|
||||
# Don't divide by 0
|
||||
yesterday = dic["yesterday"][key] if dic["yesterday"][key] != 0 else 1
|
||||
|
||||
# Percentage
|
||||
perc = round(100 * change/yesterday, 2)
|
||||
|
||||
# Sign to add to the number
|
||||
sign = "+" if change >= 0 else ""
|
||||
|
||||
return "{}{:,} ({}{:,}%)".format(sign, change, sign, perc)
|
||||
|
||||
# Requires a bit of math so this is a separate function
|
||||
def activeTrendIndicator(self, dic):
|
||||
"""
|
||||
Function that returns a rise/decline indicator for the active cases of the day.
|
||||
This is a separate function as it requires some math to get right.
|
||||
New cases have to take into account the deaths & recovered cases being
|
||||
subtracted as well.
|
||||
:param dic: the today/yesterday dictionary for this country
|
||||
:return: a triangle emoji or empty string
|
||||
"""
|
||||
todayNew = dic["today"]["todayCases"] - dic["today"]["todayDeaths"] - dic["today"]["todayRecovered"]
|
||||
yesterdayNew = dic["yesterday"]["todayCases"] - dic["yesterday"]["todayDeaths"] - dic["yesterday"]["todayRecovered"]
|
||||
|
||||
return ":small_red_triangle:" if todayNew > yesterdayNew else \
|
||||
(":small_red_triangle_down:" if todayNew < yesterdayNew else "")
|
||||
|
||||
# Returns an arrow indicating rise or decline
|
||||
def trendIndicator(self, dic, key):
|
||||
"""
|
||||
Function that returns a rise/decline indicator for the target key.
|
||||
:param dic: the today/yesterday dictionary for this country
|
||||
:param key: the key to get the indicator for
|
||||
:return: a triangle emoji or empty string
|
||||
"""
|
||||
return ":small_red_triangle:" if dic["today"][key] > dic["yesterday"][key] else \
|
||||
(":small_red_triangle_down:" if dic["today"][key] < dic["yesterday"][key] else "")
|
||||
|
||||
# Also returns an indicator, but compares instead of pulling it out of the dic (for custom numbers)
|
||||
def indicator(self, today, yesterday):
|
||||
"""
|
||||
Function that also returns an indicator but for two numbers
|
||||
instead of comparing values out of the dictionary.
|
||||
:param today: the number representing today
|
||||
:param yesterday: the number representing yesterday
|
||||
:return: a triangle emoji or empty string
|
||||
"""
|
||||
return ":small_red_triangle:" if today > yesterday else \
|
||||
(":small_red_triangle_down:" if yesterday > today else "")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Corona(client))
|
||||
113
cogs/define.py
Normal file
113
cogs/define.py
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import os
|
||||
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
import requests
|
||||
|
||||
|
||||
class Define(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Define", aliases=["UrbanDictionary", "Ud"], usage="[Woord]")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def define(self, ctx, *words):
|
||||
"""
|
||||
Command that looks up the definition of a word in the Urban Dictionary.
|
||||
:param ctx: Discord Context
|
||||
:param words: Word(s) to look up
|
||||
"""
|
||||
words = list(words)
|
||||
if len(words) == 0:
|
||||
return await ctx.send("Controleer je argumenten.")
|
||||
|
||||
query = " ".join(words)
|
||||
answer = self.lookup(query)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.from_rgb(220, 255, 0))
|
||||
embed.set_author(name="Urban Dictionary")
|
||||
|
||||
embed.add_field(name="Woord", value=answer["word"], inline=True)
|
||||
embed.add_field(name="Auteur", value=answer["author"], inline=True)
|
||||
embed.add_field(name="Definitie", value=self.cleanString(answer["definition"]), inline=False)
|
||||
embed.add_field(name="Voorbeeld", value=self.cleanString(answer["example"]), inline=False)
|
||||
embed.add_field(name="Rating", value=str(round(self.ratio(answer), 2)) + "%")
|
||||
embed.add_field(name="Link naar de volledige definitie",
|
||||
value="[Urban Dictionary]({})".format(str(answer["link"])))
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
def lookup(self, word):
|
||||
"""
|
||||
Function that sends the API request to get the definition.
|
||||
:param word: the woord to look up
|
||||
:return: a dictionary representing the info of this word
|
||||
"""
|
||||
url = "https://mashape-community-urban-dictionary.p.rapidapi.com/define"
|
||||
|
||||
querystring = {"term": word}
|
||||
|
||||
headers = {
|
||||
'x-rapidapi-host': "mashape-community-urban-dictionary.p.rapidapi.com",
|
||||
'x-rapidapi-key': os.getenv("URBANDICTIONARY")
|
||||
}
|
||||
|
||||
try:
|
||||
if word.lower() == "didier":
|
||||
raise Exception
|
||||
|
||||
response = requests.request("GET", url, headers=headers, params=querystring).json()["list"]
|
||||
|
||||
if len(response) > 0:
|
||||
return {"word": response[0]["word"], "definition": response[0]["definition"],
|
||||
"example": response[0]["example"], "thumbs_up": response[0]["thumbs_up"],
|
||||
"thumbs_down": response[0]["thumbs_down"], "link": response[0]["permalink"],
|
||||
"author": response[0]["author"]}
|
||||
|
||||
# No valid response
|
||||
return self.defineDidier()
|
||||
except Exception:
|
||||
return self.defineDidier()
|
||||
|
||||
def cleanString(self, text: str):
|
||||
"""
|
||||
Function that cuts off definitions that are too long & strips out UD markdown
|
||||
from an input string.
|
||||
:param text: the input string to clean up
|
||||
:return: the edited version of the string
|
||||
"""
|
||||
text = text.replace("[", "")
|
||||
text = text.replace("]", "")
|
||||
return text if len(text) < 1024 else text[:1021] + "..."
|
||||
|
||||
def ratio(self, dic):
|
||||
"""
|
||||
Function that alculates the upvote/downvote ratio of the definition.
|
||||
:param dic: the dictionary representing the definition
|
||||
:return: the upvote/downvote ratio (float)
|
||||
"""
|
||||
return (100 * int(dic["thumbs_up"])) / (int(dic["thumbs_up"]) + int(dic["thumbs_down"])) \
|
||||
if int(dic["thumbs_down"]) != 0 else 100.0
|
||||
|
||||
def defineDidier(self):
|
||||
"""
|
||||
Function that returns a stock dictionary to define Didier
|
||||
in case people call it, or no definition was found.
|
||||
:return: a dictionary that defines Didier
|
||||
"""
|
||||
return {"word": "Didier", "definition": "Didier", "example": "1: Didier\n2: Hmm?", "thumbs_up": 69420,
|
||||
"thumbs_down": 0, "author": "Didier",
|
||||
"link": "https://upload.wikimedia.org/wikipedia/commons/a/a5"
|
||||
"/Didier_Reynders_in_Iranian_Parliament_02.jpg"}
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Define(client))
|
||||
567
cogs/dinks.py
Normal file
567
cogs/dinks.py
Normal file
|
|
@ -0,0 +1,567 @@
|
|||
from converters.numbers import Abbreviated, abbreviated
|
||||
from data import constants
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from enums.numbers import Numbers
|
||||
from functions import checks
|
||||
from functions.database import currency, prison, stats
|
||||
from functions.numbers import getRep
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
|
||||
|
||||
def calcCapacity(level):
|
||||
"""
|
||||
Function that calculates the rob capacity for a given level.
|
||||
:param level: the level of the user
|
||||
:return: the capacity the user can rob (float)
|
||||
"""
|
||||
cap = 200
|
||||
for x in range(level):
|
||||
cap *= (math.pow(1.03, x))
|
||||
return round(cap)
|
||||
|
||||
|
||||
class Dinks(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Award", aliases=["Reward"], usage="[@Persoon] [Aantal]", hidden=True)
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def award(self, ctx, user: discord.User, amount: Abbreviated):
|
||||
"""
|
||||
Command that awards a user a certain amount of Didier Dinks.
|
||||
:param ctx: Discord Context
|
||||
:param user: the user to give the Didier Dinks to
|
||||
:param amount: the amount of Didier Dinks to award [user]
|
||||
"""
|
||||
# No amount was passed
|
||||
if amount is None:
|
||||
return
|
||||
|
||||
# Update the db
|
||||
currency.update(user.id, "dinks", float(currency.dinks(user.id)) + float(amount))
|
||||
|
||||
# Gets the abbreviated representation of the amount
|
||||
rep = getRep(amount, Numbers.t.value)
|
||||
|
||||
await ctx.send("**{}** heeft **{}** zowaar **{}** Didier Dink{} beloond!"
|
||||
.format(ctx.author.display_name, self.utilsCog.getDisplayName(ctx, user.id), rep, checks.pluralS(amount)))
|
||||
|
||||
@commands.group(name="Dinks", aliases=["Cash"], case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def dinks(self, ctx):
|
||||
"""
|
||||
Command that shows the user's Didier Dinks & Platinum Dinks
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
dinks = currency.dinksAll(ctx.author.id)
|
||||
|
||||
answer = "**{}** heeft **{:,}** Didier Dink{}"\
|
||||
.format(ctx.author.display_name, math.floor(dinks["dinks"]), checks.pluralS(dinks["dinks"]))
|
||||
|
||||
if dinks["platinum"] > 0:
|
||||
answer += " en **{}** Platinum Dink{}".format(dinks["platinum"], checks.pluralS(dinks["platinum"]))
|
||||
|
||||
await ctx.send(answer + "!")
|
||||
|
||||
@dinks.command(aliases=["Lb", "Leaderboards"], hidden=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
async def leaderboard(self, ctx):
|
||||
"""
|
||||
Command that shows the Didier Dinks Leaderboard.
|
||||
Alias for Lb Dinks.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
await self.client.get_cog("Leaderboards").callLeaderboard("dinks", ctx)
|
||||
|
||||
@commands.command(name="Nightly")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def nightly(self, ctx):
|
||||
"""
|
||||
Command to claim daily Didier Dinks.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
response = currency.nightly(int(ctx.author.id))
|
||||
if response[0]:
|
||||
# Claim successful
|
||||
await ctx.send("Je hebt je dagelijkse **{:,}** Didier Dinks geclaimt. :fire:**{}**".format(
|
||||
response[1], response[2]))
|
||||
else:
|
||||
# Already claimed today, react PIPO
|
||||
await ctx.send("Je kan dit niet meerdere keren per dag doen.")
|
||||
reactCog = self.client.get_cog("ReactWord")
|
||||
await reactCog.react(ctx, "pipo")
|
||||
|
||||
@commands.command(name="Give", aliases=["Gift"], usage="[@Persoon] [Aantal]")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def give(self, ctx, person: discord.Member, amount: Abbreviated):
|
||||
"""
|
||||
Command that gives your Didier Dinks to another user.
|
||||
:param ctx: Discord Context
|
||||
:param person: user to give the Didier Dinks to
|
||||
:param amount: the amount of Didier Dinks to give
|
||||
"""
|
||||
# Invalid amount
|
||||
if amount is None:
|
||||
return
|
||||
|
||||
valid = checks.isValidAmount(ctx, amount)
|
||||
if not valid[0]:
|
||||
return await ctx.send(valid[1])
|
||||
|
||||
authorDinks = float(currency.dinks(ctx.author.id))
|
||||
if amount == "all":
|
||||
amount = authorDinks
|
||||
|
||||
amount = float(amount)
|
||||
|
||||
currency.update(person.id, "dinks", float(currency.dinks(person.id)) + amount)
|
||||
currency.update(ctx.author.id, "dinks", authorDinks - amount)
|
||||
|
||||
rep = getRep(math.floor(amount), Numbers.t.value)
|
||||
|
||||
await ctx.send("**{}** heeft **{}** zowaar **{}** Didier Dink{} geschonken!"
|
||||
.format(ctx.author.display_name, person.display_name,
|
||||
rep, checks.pluralS(amount)))
|
||||
|
||||
@commands.group(name="Bank", aliases=["B"], case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def bank(self, ctx):
|
||||
"""
|
||||
Command that shows the user's Didier Bank.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
# 0 1 2 3 4 5 6 7 8 9 10
|
||||
# ID dinks level investedamount investeddays profit defense offense bc nightly streak
|
||||
response = currency.getOrAddUser(ctx.author.id)
|
||||
|
||||
# Calculate the cost to level your bank
|
||||
interestLevelPrice = round(math.pow(1.28, int(response[2])) * 300)
|
||||
ratio = round(float(1 * (1 + (int(response[2]) * 0.01))), 4)
|
||||
|
||||
# Calculate the amount of levels the user can purchase
|
||||
counter = 0
|
||||
sumPrice = float(math.pow(1.28, int(response[2])) * 300)
|
||||
while float(response[1]) + float(response[3]) + float(response[5]) > sumPrice:
|
||||
counter += 1
|
||||
sumPrice += round(float(math.pow(1.28, int(response[2]) + counter) * 300), 4)
|
||||
maxLevels = "" if counter == 0 else " (+{})".format(str(counter))
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Bank van {}".format(ctx.author.display_name))
|
||||
embed.set_thumbnail(url=str(ctx.author.avatar_url))
|
||||
embed.add_field(name="Level:", value=str(response[2]) + maxLevels, inline=True)
|
||||
embed.add_field(name="Ratio:", value=str(ratio), inline=True)
|
||||
embed.add_field(name="Prijs voor volgend level:", value="{:,}".format(interestLevelPrice), inline=False)
|
||||
embed.add_field(name="Momenteel geïnvesteerd:", value="{:,}".format(math.floor(float(response[3]))), inline=False)
|
||||
embed.add_field(name="Aantal dagen geïnvesteerd:", value=str(response[4]), inline=True)
|
||||
embed.add_field(name="Huidige winst na claim:", value="{:,}".format(math.floor(response[5])), inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@bank.command(name="Stats")
|
||||
async def stats(self, ctx):
|
||||
"""
|
||||
Command that shows the user's bank stats.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
response = currency.getOrAddUser(ctx.author.id)
|
||||
|
||||
# Calculate the prices to level stats up
|
||||
defense = int(response[6])
|
||||
defenseLevelPrice = math.floor(math.pow(1.4, defense) * 365) if defense < 38 else 5 * calcCapacity(defense - 6)
|
||||
offense = int(response[7])
|
||||
capacity = calcCapacity(offense)
|
||||
offenseLevelPrice = math.floor(math.pow(1.5, offense) * 369) if offense < 32 else 5 * capacity
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Bank van {}".format(ctx.author.display_name))
|
||||
embed.add_field(name="Offense:", value=str(offense), inline=True)
|
||||
embed.add_field(name="Prijs voor volgend level:", value="{:,}".format(int(offenseLevelPrice)), inline=True)
|
||||
embed.add_field(name="Capaciteit:", value="{:,}".format(int(capacity)), inline=True)
|
||||
embed.add_field(name="Security:", value=str(defense), inline=True)
|
||||
embed.add_field(name="Prijs voor volgend level:", value="{:,}".format(int(defenseLevelPrice)), inline=True)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@bank.group(name="Upgrade", aliases=["U"], case_insensitive=True, usage="[Categorie]", invoke_without_command=True)
|
||||
async def upgrade(self, ctx):
|
||||
"""
|
||||
Command group to upgrade bank stats,
|
||||
calling the group itself does nothing.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
pass
|
||||
|
||||
@upgrade.command(name="Level", aliases=["L"], hidden=True)
|
||||
async def level(self, ctx):
|
||||
"""
|
||||
Command that upgrades the user's bank level,
|
||||
increasing interest.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
response = currency.getOrAddUser(ctx.author.id)
|
||||
interestLevelPrice = float(math.pow(1.28, int(response[2])) * 300)
|
||||
|
||||
# Check if user has enough Didier Dinks to do this
|
||||
if float(response[1]) >= interestLevelPrice:
|
||||
currency.update(ctx.author.id, "dinks", float(response[1]) - interestLevelPrice)
|
||||
currency.update(ctx.author.id, "banklevel", int(response[2]) + 1)
|
||||
await ctx.send("**{}** heeft zijn bank geüpgradet naar level **{}**!"
|
||||
.format(ctx.author.display_name, str(int(response[2]) + 1)))
|
||||
else:
|
||||
await ctx.send("Je hebt niet genoeg Didier Dinks om dit te doen, **{}**."
|
||||
.format(ctx.author.display_name))
|
||||
|
||||
@upgrade.command(aliases=["Cap", "Capacity", "O", "Offence"], hidden=True)
|
||||
async def offense(self, ctx):
|
||||
"""
|
||||
Command that upgrades the user's bank offense,
|
||||
increasing capacity & rob chances.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
response = currency.getOrAddUser(ctx.author.id)
|
||||
|
||||
offense = int(response[7])
|
||||
capacity = calcCapacity(offense)
|
||||
offenseLevelPrice = math.floor(math.pow(1.5, offense) * 369) if offense < 32 else 5 * capacity
|
||||
|
||||
# Check if user has enough Didier Dinks to do this
|
||||
if float(response[1]) >= offenseLevelPrice:
|
||||
currency.update(ctx.author.id, "dinks", float(response[1]) - offenseLevelPrice)
|
||||
currency.update(ctx.author.id, "offense", int(response[7]) + 1)
|
||||
await ctx.send("**{}** heeft de offense van zijn bank geüpgradet naar level **{}**!"
|
||||
.format(ctx.author.display_name, int(response[7]) + 1))
|
||||
else:
|
||||
await ctx.send("Je hebt niet genoeg Didier Dinks om dit te doen, **{}**."
|
||||
.format(ctx.author.display_name))
|
||||
|
||||
@upgrade.command(aliases=["D", "Defence", "Def", "Security"], hidden=True)
|
||||
async def defense(self, ctx):
|
||||
"""
|
||||
Command that upgrades the user's bank defense,
|
||||
increasing chance of failed robs by others.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
response = currency.getOrAddUser(ctx.author.id)
|
||||
defense = int(response[6])
|
||||
defenseLevelPrice = math.floor(math.pow(1.4, defense) * 365) if defense < 38 else 5 * calcCapacity(defense - 6)
|
||||
|
||||
# Check if user has enough Didier Dinks to do this
|
||||
if float(response[1]) >= defenseLevelPrice:
|
||||
currency.update(ctx.author.id, "dinks", float(response[1]) - defenseLevelPrice)
|
||||
currency.update(ctx.author.id, "defense", int(response[6]) + 1)
|
||||
await ctx.send("**{}** heeft de security van zijn bank geüpgradet naar level **{}**!"
|
||||
.format(ctx.author.display_name, int(response[6]) + 1))
|
||||
else:
|
||||
await ctx.send("Je hebt niet genoeg Didier Dinks om dit te doen, **{}**."
|
||||
.format(ctx.author.display_name))
|
||||
|
||||
@commands.command(name="Invest", aliases=["Deposit"], usage="[Aantal]")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def invest(self, ctx, *amount: Abbreviated):
|
||||
"""
|
||||
Command that invests Didier Dinks into the user's bank.
|
||||
:param ctx: Discord Context
|
||||
:param amount: the amount of Didier Dinks to invest
|
||||
"""
|
||||
# Tuples don't support assignment
|
||||
amount = list(amount)
|
||||
|
||||
if len(amount) != 1:
|
||||
await ctx.send("Geef een geldig bedrag op.")
|
||||
elif not checks.isValidAmount(ctx, amount[0])[0]:
|
||||
await ctx.send(checks.isValidAmount(ctx, amount[0])[1])
|
||||
else:
|
||||
user = currency.getOrAddUser(ctx.author.id)
|
||||
if amount[0] == "all":
|
||||
amount[0] = user[1]
|
||||
|
||||
amount[0] = float(amount[0])
|
||||
currency.update(ctx.author.id, "investedamount", float(user[3]) + amount[0])
|
||||
currency.update(ctx.author.id, "dinks", float(user[1]) - amount[0])
|
||||
await ctx.send("**{}** heeft **{:,}** Didier Dink{} geïnvesteerd!"
|
||||
.format(ctx.author.display_name, math.floor(amount[0]), checks.pluralS(amount[0])))
|
||||
|
||||
@commands.command(name="Claim", usage="[Aantal]*")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def claim(self, ctx, *args):
|
||||
"""
|
||||
Command that claims profit out of the user's Didier Bank.
|
||||
:param ctx:
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
user = currency.getOrAddUser(ctx.author.id)
|
||||
args = list(args)
|
||||
claimAll = False
|
||||
|
||||
if len(args) == 0:
|
||||
args.append("all")
|
||||
if args[0] == "all":
|
||||
args[0] = float(user[5])
|
||||
claimAll = True
|
||||
|
||||
if not claimAll:
|
||||
args[0] = abbreviated(str(args[0]))
|
||||
if args[0] is None:
|
||||
return await ctx.send("Dit is geen geldig bedrag.")
|
||||
|
||||
try:
|
||||
# Checks if it can be parsed to int
|
||||
_ = int(args[0])
|
||||
args[0] = float(args[0])
|
||||
# Can't claim more than you have (or negative amounts)
|
||||
if args[0] < 0 or args[0] > float(user[5]):
|
||||
raise ValueError
|
||||
|
||||
currency.update(ctx.author.id, "profit", float(user[5]) - args[0])
|
||||
currency.update(ctx.author.id, "dinks", float(user[1]) + args[0])
|
||||
s = stats.getOrAddUser(ctx.author.id)
|
||||
stats.update(ctx.author.id, "profit", float(s[7]) + args[0])
|
||||
|
||||
# If you claim everything, you get your invest back as well & your days reset
|
||||
if claimAll:
|
||||
currency.update(ctx.author.id, "dinks", float(user[1]) + float(user[3]) + float(user[5]))
|
||||
currency.update(ctx.author.id, "investedamount", 0.0)
|
||||
currency.update(ctx.author.id, "investeddays", 0)
|
||||
await ctx.send("**{}** heeft **{:,}** Didier Dink{} geclaimt!"
|
||||
.format(ctx.author.display_name, math.floor(args[0] + float(user[3])),
|
||||
checks.pluralS(math.floor(args[0] + float(user[3])))))
|
||||
else:
|
||||
await ctx.send("**{}** heeft **{:,}** Didier Dink{} geclaimt!".format(
|
||||
ctx.author.display_name, math.floor(args[0]), checks.pluralS(math.floor(args[0]))))
|
||||
|
||||
except ValueError:
|
||||
await ctx.send("Geef een geldig bedrag op.")
|
||||
|
||||
@commands.group(name="Rob", usage="[@Persoon]", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def rob(self, ctx, target: discord.User):
|
||||
"""
|
||||
Command to rob another user.
|
||||
:param ctx: Discord Context
|
||||
:param target: the target victim to be robbed
|
||||
:return:
|
||||
"""
|
||||
canRob, caller, target = await self.canRob(ctx, target)
|
||||
|
||||
if not canRob:
|
||||
return
|
||||
|
||||
threshold = 50 + round(int(target[6]) * 0.7)
|
||||
rg = random.randint(0 + int(caller[7]), 100)
|
||||
stat = stats.getOrAddUser(ctx.author.id)
|
||||
|
||||
# Rob succeeded
|
||||
if rg > threshold:
|
||||
capacity = float(calcCapacity(caller[7]))
|
||||
remaining = capacity
|
||||
|
||||
# Try robbing out of invest first, then Dinks pouch
|
||||
amount = capacity if float(target[3]) >= capacity else float(target[3])
|
||||
remaining -= amount
|
||||
currency.update(target[0], "investedamount", float(target[3]) - amount)
|
||||
|
||||
# Rob out of Dinks pouch
|
||||
if amount != capacity and not float(target[1]) < 1:
|
||||
if float(target[1]) >= remaining:
|
||||
amount += remaining
|
||||
currency.update(target[0], "dinks", float(target[1]) - remaining)
|
||||
else:
|
||||
amount += float(target[1])
|
||||
currency.update(target[0], "dinks", 0.0)
|
||||
|
||||
# Update db
|
||||
currency.update(caller[0], "dinks", float(caller[1]) + amount)
|
||||
await ctx.send("**{}** heeft **{:,}** Didier Dink{} gestolen van **{}**!".format(
|
||||
ctx.author.display_name, math.floor(amount), checks.pluralS(math.floor(amount)),
|
||||
self.utilsCog.getDisplayName(ctx, target[0])
|
||||
))
|
||||
|
||||
stats.update(ctx.author.id, "robs_success", int(stat[2]) + 1)
|
||||
stats.update(ctx.author.id, "robs_total", float(stat[4]) + amount)
|
||||
else:
|
||||
# Rob failed
|
||||
|
||||
# Calculate what happens
|
||||
fate = random.randint(1, 10)
|
||||
|
||||
# Leave Dinks behind instead of robbing
|
||||
if fate < 8:
|
||||
punishment = float(calcCapacity(caller[7]))/2
|
||||
prisoned = round(float(caller[1])) < round(punishment)
|
||||
|
||||
# Doesn't have enough Dinks -> prison
|
||||
if prisoned:
|
||||
diff = round(punishment - float(caller[1]))
|
||||
punishment = round(float(caller[1]))
|
||||
days = 1 + round(int(caller[7]) // 10)
|
||||
prison.imprison(caller[0], diff, days,
|
||||
round(round(diff)//days))
|
||||
|
||||
# Update db
|
||||
currency.update(target[0], "dinks", float(target[1]) + punishment)
|
||||
currency.update(caller[0], "dinks", float(caller[1]) - punishment)
|
||||
await ctx.send("**{}** was zo vriendelijk om **{}** zowaar **{:,}** Didier Dink{} te geven!"
|
||||
.format(ctx.author.display_name,
|
||||
self.utilsCog.getDisplayName(ctx, target[0]),
|
||||
math.floor(punishment), checks.pluralS(math.floor(punishment))))
|
||||
|
||||
# Can't put this in the previous if- because the value of Punishment changes
|
||||
if prisoned:
|
||||
await ctx.send("Je bent naar de gevangenis verplaatst omdat je niet genoeg Didier Dinks had.")
|
||||
elif fate == 9:
|
||||
# Prison
|
||||
totalSum = round(calcCapacity(caller[7]))
|
||||
days = 1 + (int(caller[7])//10)
|
||||
|
||||
prison.imprison(caller[0], totalSum, days, totalSum/days)
|
||||
await ctx.send("**{}** niet stelen, **{}** niet stelen!\nJe bent naar de gevangenis verplaatst.".format(
|
||||
ctx.author.display_name, ctx.author.display_name
|
||||
))
|
||||
else:
|
||||
# Escape
|
||||
await ctx.send("Je poging is mislukt, maar je kon nog net op tijd vluchten, **{}**.".format(
|
||||
ctx.author.display_name))
|
||||
|
||||
stats.update(ctx.author.id, "robs_failed", int(stat[3]) + 1)
|
||||
|
||||
@rob.command(name="Leaderboard", aliases=["Lb", "Leaderboards"], hidden=True)
|
||||
async def rob_leaderboard(self, ctx):
|
||||
"""
|
||||
Command that shows the Rob Leaderboard.
|
||||
Alias for Lb Rob.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
await self.client.get_cog("Leaderboards").callLeaderboard("rob", ctx)
|
||||
|
||||
@rob.command(name="Stats", hidden=True)
|
||||
async def rob_stats(self, ctx):
|
||||
"""
|
||||
Command that shows the user's rob stats.
|
||||
Alias for Stats Rob.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
await self.client.get_cog("Stats").callStats("rob", ctx)
|
||||
|
||||
@commands.command(name="Prison", aliases=["Jail"])
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Currency)
|
||||
async def prison(self, ctx):
|
||||
"""
|
||||
Command that shows how long you have to sit in prison for.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
user = prison.getUser(ctx.author.id)
|
||||
|
||||
if len(user) == 0:
|
||||
await ctx.send("Je zit niet in de gevangenis, **{}**.".format(ctx.author.display_name))
|
||||
return
|
||||
|
||||
user = user[0]
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="De Gevangenis")
|
||||
embed.add_field(name="Borgsom:", value="{:,}".format(math.floor(user[1])), inline=False)
|
||||
embed.add_field(name="Resterende dagen:", value="{}".format((user[2])), inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="Bail")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Currency)
|
||||
async def bail(self, ctx):
|
||||
"""
|
||||
Command to bail yourself out of prison.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
user = prison.getUser(ctx.author.id)
|
||||
if len(user) == 0:
|
||||
return await ctx.send("Je zit niet in de gevangenis, **{}**.".format(ctx.author.display_name))
|
||||
|
||||
user = user[0]
|
||||
|
||||
# Check if user can afford this
|
||||
valid = checks.isValidAmount(ctx, math.floor(user[1]))
|
||||
if not valid[0]:
|
||||
return await ctx.send(valid[1])
|
||||
|
||||
dinks = currency.dinks(ctx.author.id)
|
||||
prison.remove(ctx.author.id)
|
||||
currency.update(ctx.author.id, "dinks", float(dinks) - float(user[1]))
|
||||
await ctx.send("**{}** heeft zichzelf vrijgekocht!".format(ctx.author.display_name))
|
||||
|
||||
# Update the user's stats
|
||||
s = stats.getOrAddUser(ctx.author.id)
|
||||
stats.update(ctx.author.id, "bails", int(s[10]) + 1)
|
||||
|
||||
# Increase the bail in the stats file
|
||||
with open("files/stats.json", "r") as fp:
|
||||
s = json.load(fp)
|
||||
|
||||
s["rob"]["bail_paid"] += float(user[1])
|
||||
|
||||
with open("files/stats.json", "w") as fp:
|
||||
json.dump(s, fp)
|
||||
|
||||
async def canRob(self, ctx, target):
|
||||
"""
|
||||
Function that performs checks to see if a user can rob another user.
|
||||
In case the rob is not possible, it already sends an error message to show this.
|
||||
Returns the database dictionaries corresponding to these two users as they are
|
||||
needed in this function anyways, so it prevents an unnecessary database call
|
||||
in the rob command.
|
||||
:param ctx: Discord Context
|
||||
:param target: the target victim to be robbed
|
||||
:return: success: boolean, user1 ("Caller"): tuple, user2 ("Target"): tuple
|
||||
"""
|
||||
# Can't rob in DM's
|
||||
if str(ctx.channel.type) == "private":
|
||||
await ctx.send("Dat doe je niet, {}.".format(ctx.author.display_name))
|
||||
return False, None, None
|
||||
|
||||
# Can't rob bots
|
||||
if str(ctx.author.id) in constants.botIDs:
|
||||
await ctx.send("Nee.")
|
||||
|
||||
# Can't rob in prison
|
||||
if len(prison.getUser(ctx.author.id)) != 0:
|
||||
await ctx.send("Je kan niemand bestelen als je in de gevangenis zit.")
|
||||
return False, None, None
|
||||
|
||||
# Check the database for these users
|
||||
user1 = currency.getOrAddUser(ctx.author.id)
|
||||
user2 = currency.getOrAddUser(target.id)
|
||||
|
||||
# Can't rob without Didier Dinks
|
||||
if float(user1[1]) < 1.0:
|
||||
await ctx.send("Mensen zonder Didier Dinks kunnen niet stelen.")
|
||||
return False, None, None
|
||||
|
||||
# Target has no Didier Dinks to rob
|
||||
if float(user2[1]) < 1.0 and float(user2[3]) < 1.0:
|
||||
await ctx.send("Deze persoon heeft geen Didier Dinks om te stelen.")
|
||||
return False, None, None
|
||||
|
||||
# Passed all tests
|
||||
return True, user1, user2
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Dinks(client))
|
||||
336
cogs/events.py
Normal file
336
cogs/events.py
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
from data import constants
|
||||
import datetime
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from functions import checks, easterEggResponses
|
||||
from functions.database import stats, muttn
|
||||
import pytz
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
class Events(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
self.failedChecksCog = self.client.get_cog("FailedChecks")
|
||||
self.lastFeatureRequest = 0
|
||||
self.lastBugReport = 0
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_connect(self):
|
||||
"""
|
||||
Function called when the bot connects to Discord.
|
||||
"""
|
||||
print("Connected")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
"""
|
||||
Function called when the bot is ready & done leading.
|
||||
"""
|
||||
# Change status
|
||||
with open("files/status.txt", "r") as statusFile:
|
||||
status = statusFile.readline()
|
||||
|
||||
await self.client.change_presence(status=discord.Status.online, activity=discord.Game(str(status)))
|
||||
|
||||
# Print a message in the terminal to show that he's ready
|
||||
with open("files/readyMessage.txt", "r") as readyFile:
|
||||
readyMessage = readyFile.readline()
|
||||
|
||||
print(readyMessage)
|
||||
|
||||
# Add constants to the client as a botvar
|
||||
self.client.constants = constants.Live if "zandbak" not in readyMessage else constants.Zandbak
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
"""
|
||||
Function called when someone sends a message the bot can see.
|
||||
:param message: the discord.Message instance of the message
|
||||
"""
|
||||
# Check if the server is locked, if so only allow me (to unlock) & Didier (to send the message) to talk
|
||||
if self.client.locked \
|
||||
and message.guild is not None \
|
||||
and str(message.author.id) != constants.myId \
|
||||
and str(message.author.id) != constants.didierId:
|
||||
# Auto unlock when someone sends a message past the current time
|
||||
if time.time() > self.client.lockedUntil:
|
||||
return await self.unlock(message.channel)
|
||||
|
||||
return await self.utilsCog.removeMessage(message)
|
||||
|
||||
# If FreeGamesCheck failed, remove the message & send the user a DM
|
||||
if not checks.freeGamesCheck(message):
|
||||
await self.failedChecksCog.freeGames(message)
|
||||
|
||||
# Log commands in terminal
|
||||
if any(message.content.lower().startswith(pre) for pre in self.client.prefixes):
|
||||
DM = message.guild is None
|
||||
print("{} in {}: {}".format(message.author.display_name,
|
||||
"DM" if DM else "{} ({})".format(message.channel.name, message.guild.name),
|
||||
message.content))
|
||||
|
||||
# Boos React to people that call him Dider
|
||||
if "dider" in message.content.lower() and str(message.author.id) not in [constants.myId, constants.didierId]:
|
||||
await message.add_reaction("<:boos:629603785840263179>")
|
||||
|
||||
# Check for other easter eggs
|
||||
eER = easterEggResponses.control(message)
|
||||
if eER:
|
||||
await message.channel.send(eER)
|
||||
|
||||
# Earn XP & Message count
|
||||
stats.sentMessage(message)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_command_error(self, ctx, err):
|
||||
"""
|
||||
Function called when a command throws an error.
|
||||
:param ctx: Discord Context
|
||||
:param err: the error thrown
|
||||
"""
|
||||
# Don't handle commands that have their own custom error handler
|
||||
if hasattr(ctx.command, 'on_error'):
|
||||
return
|
||||
# Someone just mentioned Didier without calling a real command,
|
||||
# don't care about this error
|
||||
if isinstance(err, (commands.CommandNotFound, commands.CheckFailure, commands.TooManyArguments), ):
|
||||
pass
|
||||
# Someone used a command that was on cooldown
|
||||
elif isinstance(err, commands.CommandOnCooldown):
|
||||
await ctx.send("Je kan dit commando niet (meer) spammen.", delete_after=10)
|
||||
# Someone forgot an argument or passed an invalid argument
|
||||
elif isinstance(err, (commands.BadArgument, commands.MissingRequiredArgument)):
|
||||
await ctx.send("Controleer je argumenten.")
|
||||
else:
|
||||
# Remove the InvokeCommandError because it's useless information
|
||||
x = traceback.format_exception(type(err), err, err.__traceback__)
|
||||
errorString = ""
|
||||
for line in x:
|
||||
if "direct cause of the following" in line:
|
||||
break
|
||||
errorString += line.replace("*", "") + "\n" if line.strip() != "" else ""
|
||||
await self.sendErrorEmbed(ctx, err, errorString)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_add(self, react):
|
||||
"""
|
||||
Function called when someone adds a reaction to a message.
|
||||
:param react: the RawReactionEvent associated with the reaction
|
||||
"""
|
||||
# Ignore RPS adding reacts
|
||||
if self.client.get_user(react.user_id).bot:
|
||||
return
|
||||
# Feature request
|
||||
if str(react.emoji) == "➕":
|
||||
await self.sendReactEmbed(react, "Feature Request")
|
||||
# Bug report
|
||||
elif str(react.emoji) == "🐛":
|
||||
await self.sendReactEmbed(react, "Bug Report")
|
||||
# Muttn react
|
||||
elif str(react.emoji) == "<:Muttn:761551956346798111>":
|
||||
await self.addMuttn(react)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_remove(self, react):
|
||||
"""
|
||||
Function called when someone removes a reaction from a message.
|
||||
:param react: the RawReactionEvent associated with the reaction
|
||||
"""
|
||||
# Decrease Muttn counter
|
||||
if str(react.emoji) == "<:Muttn:761551956346798111>":
|
||||
await self.removeMuttn(react)
|
||||
|
||||
async def removeMuttn(self, react):
|
||||
"""
|
||||
Function that decreases the Muttn counter for someone.
|
||||
:param react: the RawReactionEvent associated with the reaction
|
||||
"""
|
||||
# Get the Message instance of the message
|
||||
channel = self.client.get_channel(react.channel_id)
|
||||
message = await channel.fetch_message(react.message_id)
|
||||
muttn.removeMuttn(message)
|
||||
|
||||
async def addMuttn(self, react):
|
||||
"""
|
||||
Function that checks the Muttn counter for a message.
|
||||
:param react: the RawReactionEvent associated with the reaction
|
||||
"""
|
||||
count = -1
|
||||
# Get the Message instance of the message
|
||||
channel = self.client.get_channel(react.channel_id)
|
||||
message = await channel.fetch_message(react.message_id)
|
||||
|
||||
# Get the amount of reacts on this message
|
||||
for reaction in message.reactions:
|
||||
if str(reaction.emoji) == "<:Muttn:761551956346798111>":
|
||||
count = reaction.count
|
||||
for user in await reaction.users().flatten():
|
||||
# Remove bot reacts
|
||||
if user.bot:
|
||||
count -= 1
|
||||
break
|
||||
|
||||
# React was removed in the milliseconds the fetch_message needs to get the info
|
||||
if count <= 0:
|
||||
return
|
||||
|
||||
# Update the db
|
||||
muttn.muttn(message.author.id, count, message.id)
|
||||
|
||||
def reactCheck(self, react, msg):
|
||||
"""
|
||||
Function that checks if feature requests/bug reports have been sent already.
|
||||
:param react: the RawReactionEvent associated with the reaction
|
||||
:param msg: the message this react was placed on
|
||||
"""
|
||||
# Blacklist NinjaJay after spamming
|
||||
if react.user_id in [153162010576551946]:
|
||||
return False
|
||||
|
||||
# Don't spam DM's when something has already been reported
|
||||
# Check if the react's count is 1
|
||||
for reaction in msg.reactions:
|
||||
if reaction.emoji == react.emoji.name:
|
||||
return reaction.count == 1
|
||||
|
||||
async def sendReactEmbed(self, react, messageType):
|
||||
"""
|
||||
Function that sends a message in Zandbak with what's going on.
|
||||
:param react: the RawReactionEvent associated with the reaction
|
||||
:param messageType: the type of message to send
|
||||
"""
|
||||
channel = self.client.get_channel(react.channel_id)
|
||||
msg = await channel.fetch_message(react.message_id)
|
||||
|
||||
# Didn't pass the check, ignore it
|
||||
if not self.reactCheck(react, msg):
|
||||
return
|
||||
|
||||
typeChannels = {"Feature Request": int(constants.FeatureRequests), "Bug Report": int(constants.BugReports)}
|
||||
|
||||
# Add a 10 second cooldown to requests/reports to avoid spam
|
||||
# even tho apparently the people don't care
|
||||
if round(time.time()) - (
|
||||
self.lastFeatureRequest if messageType == "Feature Request" else self.lastBugReport) < 10:
|
||||
await channel.send("Je moet even wachten vooraleer je nog een {} maakt.".format(messageType.lower()))
|
||||
await msg.add_reaction("🕐")
|
||||
return
|
||||
# Report on an empty message
|
||||
elif msg.content == "":
|
||||
await channel.send("Dit bericht bevat geen tekst.")
|
||||
await msg.add_reaction("❌")
|
||||
return
|
||||
|
||||
# Update the variables
|
||||
if messageType == "Feature Request":
|
||||
self.lastFeatureRequest = round(time.time())
|
||||
else:
|
||||
self.lastBugReport = round(time.time())
|
||||
|
||||
# Ignore people reacting on Didier's messages
|
||||
if str(msg.author.id) != constants.didierId:
|
||||
# Get the user's User instance & the channel to send the message to
|
||||
COC = self.client.get_guild(int(constants.CallOfCode))
|
||||
user = COC.get_member(react.user_id)
|
||||
targetChannel = self.client.get_channel(typeChannels[messageType])
|
||||
|
||||
await targetChannel.send("{} door **{}** in **#{}** ({}):\n``{}``\n{}".format(
|
||||
messageType,
|
||||
user.display_name,
|
||||
channel.name if str(channel.type) != "private" else "DM",
|
||||
channel.guild.name if str(channel.type) != "private" else "DM",
|
||||
msg.content, msg.jump_url
|
||||
))
|
||||
await msg.add_reaction("✅")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message_edit(self, before, after):
|
||||
"""
|
||||
Function called when a message is edited,
|
||||
so people can't edit messages in FreeGames to cheat the system.
|
||||
:param before: the message before it was edited
|
||||
:param after: the message after it was edited
|
||||
"""
|
||||
# Run the message through the checks again
|
||||
if not checks.freeGamesCheck(after):
|
||||
await self.failedChecksCog.freeGames(after)
|
||||
|
||||
async def sendErrorEmbed(self, ctx, error: Exception, trace):
|
||||
"""
|
||||
Function that sends an error embed in #ErrorLogs.
|
||||
:param ctx: Discord Context
|
||||
:param error: the error thrown
|
||||
:param trace: the stacktrace of the error
|
||||
"""
|
||||
embed = discord.Embed(colour=discord.Colour.red())
|
||||
embed.set_author(name="Error")
|
||||
embed.add_field(name="Command:", value="{} in {}: {}".format(ctx.author.display_name,
|
||||
ctx.channel.name if str(
|
||||
ctx.channel.type) != "private" else "DM",
|
||||
ctx.message.content), inline=False)
|
||||
embed.add_field(name="Error:", value=str(error)[:1024], inline=False)
|
||||
embed.add_field(name="Message:", value=str(trace)[:1024], inline=False)
|
||||
|
||||
# Add remaining parts in extra fields
|
||||
# (embed field limits)
|
||||
if len(str(trace)) < 5500:
|
||||
trace_split = [str(trace)[i:i + 1024] for i in range(1024, len(str(trace)), 1024)]
|
||||
for spl in trace_split:
|
||||
embed.add_field(name="\u200b", value=spl, inline=False)
|
||||
|
||||
errorChannel = self.client.get_channel(762668505455132722)
|
||||
await errorChannel.send(embed=embed)
|
||||
|
||||
@commands.command(hidden=True)
|
||||
@commands.check(checks.isMe)
|
||||
async def lock(self, ctx, until=None):
|
||||
"""
|
||||
Command that locks the server during online exams.
|
||||
:param ctx: Discord Context
|
||||
:param until: the timestamp until which to lock (HH:MM)
|
||||
"""
|
||||
# No timestamp passed
|
||||
if until is None:
|
||||
return
|
||||
|
||||
until = until.split(":")
|
||||
|
||||
# Create timestamps
|
||||
now = datetime.datetime.now()
|
||||
untilTimestamp = time.time()
|
||||
|
||||
# Gets the current amount of minutes into the day
|
||||
nowMinuteCount = (now.hour * 60) + now.minute
|
||||
|
||||
# Gets the target amount of minutes into the day
|
||||
untilMinuteCount = (int(until[0]) * 60) + int(until[1])
|
||||
|
||||
# Adds the remaining seconds onto the current time to calculate the end of the lock
|
||||
untilTimestamp += (60 * (untilMinuteCount - nowMinuteCount)) - now.second
|
||||
|
||||
self.client.locked = True
|
||||
self.client.lockedUntil = round(untilTimestamp)
|
||||
|
||||
await ctx.send("De server wordt gelocked tot **{}**.".format(
|
||||
datetime.datetime.fromtimestamp(untilTimestamp,
|
||||
pytz.timezone("Europe/Brussels")
|
||||
).strftime('%H:%M:%S')))
|
||||
|
||||
@commands.command(hidden=True)
|
||||
@commands.check(checks.isMe)
|
||||
async def unlock(self, ctx):
|
||||
"""
|
||||
Command to unlock the server manually before the timer is over.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
self.client.locked = False
|
||||
self.client.lockedUntil = -1
|
||||
await ctx.send("De server is niet langer gelocked.")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Events(client))
|
||||
28
cogs/failedchecks.py
Normal file
28
cogs/failedchecks.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from data import constants
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
# Cog that handles failure of checks
|
||||
# Has to be a Cog to have access to the Client
|
||||
class FailedChecks(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# User posted in #FreeGames without being allowed to do so
|
||||
async def freeGames(self, ctx):
|
||||
content = ctx.content
|
||||
errorChannel = self.client.get_channel(int(constants.ErrorLogs))
|
||||
|
||||
await self.utilsCog.removeMessage(ctx)
|
||||
await self.utilsCog.sendDm(ctx.author.id,
|
||||
"Je bericht \n`{}`\n werd verwijderd uit #FreeGames omdat het geen link "
|
||||
"bevatte.\nPost AUB enkel links in dit kanaal.\n*Als je bericht onterecht "
|
||||
"verwijderd werd, stuur dan een DM naar DJ STIJN.*".format(content))
|
||||
await errorChannel.send("`{}`\nDoor **{}** werd verwijderd uit #FreeGames.".format(content,
|
||||
ctx.author.display_name))
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(FailedChecks(client))
|
||||
170
cogs/faq.py
Normal file
170
cogs/faq.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import stringFormatters, checks
|
||||
from functions.database import faq
|
||||
|
||||
|
||||
class Faq(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="FAQ", usage="[Categorie]* [@Personen]*", case_insensitive=True, invoke_without_command=True)
|
||||
@help.Category(category=Category.Other)
|
||||
async def faq(self, ctx, *args):
|
||||
"""
|
||||
Command group that controls the FAQ commands.
|
||||
When this command is invoked, it sends a list of valid categories.
|
||||
:param ctx: Discord Context
|
||||
:param args: args passed
|
||||
"""
|
||||
# A category was requested
|
||||
# This is not the cleanest but 80 subcommands is a bit much
|
||||
if len(args) != 0 and any("@" not in arg for arg in args):
|
||||
return await self.faqCategory(ctx, args)
|
||||
|
||||
# List of all categories with the first letter capitalized
|
||||
resp = [stringFormatters.titleCase(cat[0]) for cat in faq.getCategories()]
|
||||
|
||||
# Sort alphabetically
|
||||
resp.sort()
|
||||
|
||||
# Create an embed with all the categories
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="FAQ Categorieën")
|
||||
embed.description = "\n".join(resp)
|
||||
|
||||
# Check if the embed has to be sent to the user
|
||||
# or if the user tagged anyone
|
||||
if len(ctx.message.mentions) == 0:
|
||||
await ctx.author.send(embed=embed)
|
||||
else:
|
||||
embed.set_footer(text="Doorgestuurd door {}".format(ctx.author.display_name))
|
||||
# Send it to everyone that was mentioned
|
||||
for person in ctx.message.mentions:
|
||||
if not person.bot:
|
||||
await person.send(embed=embed)
|
||||
|
||||
@faq.command(hidden=True, name="Add", usage="[Category] [Question]* [Answer]*")
|
||||
@commands.check(checks.isMe)
|
||||
async def add(self, ctx, category, question=None, answer=None, answer_markdown=None):
|
||||
"""
|
||||
Command to add a FAQ to the database
|
||||
:param ctx: Discord Context
|
||||
:param category: the category to add the FAQ to
|
||||
:param question: the question
|
||||
:param answer: the answer
|
||||
:param answer_markdown: a version of the answer with markdown applied
|
||||
"""
|
||||
# Add a new category
|
||||
if question is None or answer is None:
|
||||
faq.addCategory(category)
|
||||
await ctx.send("**{}** is toegevoegd.".format(category))
|
||||
else:
|
||||
# Add a new question/answer couple to a category
|
||||
faq.addQuestion(category, question, answer, answer_markdown)
|
||||
await ctx.send("``{}\n{}`` is toegevoegd in {}.".format(question, answer, category))
|
||||
|
||||
# Quotes a specific line of the fac instead of DM'ing the entire thing
|
||||
@faq.command(name="Quote", aliases=["Q"], usage="[Categorie] [Index]")
|
||||
@help.Category(category=Category.Other)
|
||||
async def quote(self, ctx, category, index):
|
||||
"""
|
||||
Command that quotes 1 line of the FAQ into the current channel.
|
||||
:param ctx: Discord Context
|
||||
:param category: the category of the FAQ
|
||||
:param index: the index in the list to quote
|
||||
:return:y
|
||||
"""
|
||||
# Check if a (valid) number was passed
|
||||
try:
|
||||
index = int(index)
|
||||
if index < 1:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
await ctx.send("Dit is geen geldig getal.")
|
||||
|
||||
# Create a list of categories
|
||||
categories = [t[0] for t in faq.getCategories()]
|
||||
|
||||
# Check if a valid category was passed
|
||||
if category.lower() not in categories:
|
||||
return await ctx.send("Dit is geen geldige categorie.")
|
||||
|
||||
resp = faq.getCategory(category.lower())
|
||||
|
||||
# Check if this index exists in this category
|
||||
if len(resp) < index:
|
||||
return await ctx.send("Dit is geen geldig getal.")
|
||||
|
||||
# Sort by entry Id
|
||||
resp.sort(key=lambda x: int(x[0]))
|
||||
|
||||
await ctx.send("**{}**\n{}".format(resp[index - 1][2], resp[index - 1][3]))
|
||||
|
||||
async def faqCategory(self, ctx, args):
|
||||
"""
|
||||
Function that sends everything from a category.
|
||||
:param ctx: Discord Context
|
||||
:param args: the args passed
|
||||
"""
|
||||
|
||||
# Create a list of categories
|
||||
categories = [t[0] for t in faq.getCategories()]
|
||||
|
||||
# Random word was passed as a category
|
||||
if not any(arg.lower() in categories for arg in args):
|
||||
return await self.sendErrorEmbed(ctx, "Dit is geen geldige categorie.")
|
||||
elif len(args) - len(ctx.message.mentions) != 1:
|
||||
# Multiple categories were requested, which is not allowed
|
||||
return await self.sendErrorEmbed(ctx, "Controleer je argumenten.")
|
||||
|
||||
category = ""
|
||||
|
||||
# Find the category the user requested
|
||||
for word in args:
|
||||
if word.lower() in categories:
|
||||
category = word
|
||||
break
|
||||
|
||||
resp = faq.getCategory(category.lower())
|
||||
|
||||
# Sort by entry Id
|
||||
resp.sort(key=lambda x: int(x[0]))
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="FAQ {}".format(stringFormatters.titleCase(category)))
|
||||
|
||||
# Add everything into the embed
|
||||
for i, pair in enumerate(resp):
|
||||
# Add custom markdown if it exists
|
||||
embed.add_field(name="#{}: {}".format(str(i + 1), pair[2]), value=pair[3] if pair[4] is None else pair[4], inline=False)
|
||||
|
||||
# Check if anyone was tagged to send the embed to
|
||||
if len(ctx.message.mentions) == 0:
|
||||
await ctx.author.send(embed=embed)
|
||||
else:
|
||||
embed.set_footer(text="Doorgestuurd door {}".format(ctx.author.display_name))
|
||||
# Author tagged some people to send it to
|
||||
for person in ctx.message.mentions:
|
||||
await person.send(embed=embed)
|
||||
|
||||
async def sendErrorEmbed(self, ctx, message: str):
|
||||
"""
|
||||
Function that sends an error embed.
|
||||
:param ctx: Discord Context
|
||||
:param message: the message to put in the embed
|
||||
"""
|
||||
embed = discord.Embed(colour=discord.Colour.red())
|
||||
embed.set_author(name="Error")
|
||||
embed.description = message
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Faq(client))
|
||||
89
cogs/football.py
Normal file
89
cogs/football.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
from bs4 import BeautifulSoup
|
||||
import datetime
|
||||
from decorators import help
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, config
|
||||
import requests
|
||||
import tabulate
|
||||
|
||||
|
||||
class Football(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return checks.isMe(ctx) and not self.client.locked
|
||||
|
||||
@commands.group(name="Jpl", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Sports)
|
||||
async def jpl(self, ctx, *args):
|
||||
pass
|
||||
|
||||
@jpl.command(name="Matches", aliases=["M"], usage="[Week]*")
|
||||
async def matches(self, ctx, *args):
|
||||
args = list(args)
|
||||
if not args:
|
||||
args = [str(config.get("jpl_day"))]
|
||||
if all(letter.isdigit() for letter in args[0]):
|
||||
current_day = requests.get("https://api.sporza.be/web/soccer/matchdays/161733/{}".format(args[0])).json()
|
||||
current_day = current_day["groupedMatches"][0]["matches"]
|
||||
|
||||
# Create dictionaries for every match
|
||||
matches_formatted = {}
|
||||
for i, match in enumerate(current_day):
|
||||
matchDic = {"home": match["homeTeam"]["name"], "away": match["awayTeam"]["name"]}
|
||||
|
||||
# Add date
|
||||
matchDate = datetime.datetime.strptime(match["startDateTime"].split("+")[0], "%Y-%m-%dT%H:%M:%S.%f")
|
||||
matchDic["date"] = matchDate.strftime("%d/%m")
|
||||
matchDic["day"] = self.get_weekday(matchDate.weekday())
|
||||
|
||||
# TODO check back when there's active games (to find the key in the dict) & add the current time if not over
|
||||
# Add scores
|
||||
if match["status"] == "END": # Status != [not_yet_started] whatever it is
|
||||
matchDic["score"] = "{} - {}".format(match["homeScore"], match["awayScore"])
|
||||
else:
|
||||
# If there's no score, show when the match starts
|
||||
matchDic["score"] = "{}:{}".format(
|
||||
("" if len(str(matchDate.hour)) == 2 else "0") + str(matchDate.hour), # Leading Zero
|
||||
("" if len(str(matchDate.minute)) == 2 else "0") + str(matchDate.minute)) # Leading Zero
|
||||
|
||||
matches_formatted[i] = matchDic
|
||||
|
||||
# Put every formatted version of the matches in a list
|
||||
matchList = list([self.format_match(matches_formatted[match]) for match in matches_formatted])
|
||||
await ctx.send("```Jupiler Pro League - Speeldag {}\n\n{}```".format(args[0], tabulate.tabulate(matchList, headers=["Dag", "Datum", "Thuis", "Stand", "Uit", "Tijd"])))
|
||||
else:
|
||||
return await ctx.send("Dit is geen geldige speeldag.")
|
||||
|
||||
# TODO check back when there's active games & add the timestamp instead of EINDE
|
||||
def format_match(self, match):
|
||||
return [match["day"], match["date"], match["home"], match["score"], match["away"], "Einde"]
|
||||
|
||||
def get_weekday(self, day: int):
|
||||
days = ["Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"]
|
||||
return days[day]
|
||||
|
||||
@jpl.command(name="Table", aliases=["Ranking", "Rankings", "Ranks", "T"])
|
||||
async def table(self, ctx, *args):
|
||||
page_html = requests.get("https://sporza.be/nl/categorie/voetbal/jupiler-pro-league/").text
|
||||
bs_parsed = BeautifulSoup(page_html, "html.parser")
|
||||
rows = bs_parsed.find(summary="algemeen klassement").find_all("tr")[1:]
|
||||
rowsFormatted = []
|
||||
for row in rows:
|
||||
rowsFormatted.append(self.createRowList(row))
|
||||
await ctx.send("```Jupiler Pro League Klassement\n\n{}```".format(tabulate.tabulate(rowsFormatted, headers=["#", "Ploeg", "Punten", "M", "M+", "M-", "M="])))
|
||||
|
||||
# Formats the row into an list that can be passed to Tabulate
|
||||
def createRowList(self, row):
|
||||
scoresArray = list([td.renderContents().decode("utf-8") for td in row.find_all("td")])[:6]
|
||||
# Insert the team name into the list
|
||||
scoresArray.insert(1, row.find_all("a")[0].renderContents().decode("utf-8").split("<!--")[0])
|
||||
return scoresArray
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Football(client))
|
||||
182
cogs/fun.py
Normal file
182
cogs/fun.py
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, mock, stringFormatters
|
||||
from functions.database import memes, trump, dadjoke
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import requests
|
||||
|
||||
|
||||
class Fun(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Dadjoke", aliases=["Dj", "Dad"])
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Fun)
|
||||
async def dadjoke(self, ctx):
|
||||
"""
|
||||
Command that sends a random dad joke.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
await ctx.send(dadjoke.getRandomJoke())
|
||||
|
||||
@commands.command(name="Stalin", aliases=["Ss", "StalinMotivation", "Motivate"])
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Quotes)
|
||||
async def stalin(self, ctx):
|
||||
"""
|
||||
Command that sends a random Stalin quote.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
with open("files/StalinMotivation.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
await ctx.send(file[random.randint(1, len(file))])
|
||||
|
||||
@commands.command(name="Satan", aliases=["S8n", "SatanQuote"])
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Quotes)
|
||||
async def satan(self, ctx):
|
||||
"""
|
||||
Command that sends a random Satan quote.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
with open("files/SatanQuotes.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
await ctx.send(file[random.randint(1, len(file))])
|
||||
|
||||
@commands.command(name="Trump")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Quotes)
|
||||
async def trump(self, ctx):
|
||||
"""
|
||||
Command that sends a random Trump quote.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
quote = trump.getRandomQuote()
|
||||
await ctx.send("**\"{}**\"\n{} - {}".format(quote[1], quote[2], quote[3]))
|
||||
|
||||
@commands.command(name="8-Ball", aliases=["8b", "8Ball"], ignore_extra=True)
|
||||
@help.Category(category=Category.Quotes)
|
||||
async def eightball(self, ctx):
|
||||
"""
|
||||
Command that sends a random 8-ball response.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
with open("files/eightball.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
await ctx.send(file[random.randint(0, len(file) - 1)])
|
||||
|
||||
@commands.command(name="Memegen", usage="[Meme] [Velden]")
|
||||
@commands.cooldown(1, 5, commands.BucketType.guild)
|
||||
@help.Category(category=Category.Fun)
|
||||
async def memegen(self, ctx, name="", *fields):
|
||||
"""
|
||||
Command that generates memes.
|
||||
:param ctx: Discord Context
|
||||
:param name: the name of the meme
|
||||
:param fields: the fields to add to the meme
|
||||
"""
|
||||
if len(fields) == 0:
|
||||
return await ctx.send("Controleer je argumenten.")
|
||||
|
||||
# Get the meme info that corresponds to this name
|
||||
result = memes.getMeme(name)
|
||||
|
||||
# No meme found
|
||||
if not result[0]:
|
||||
return await ctx.send(result[1])
|
||||
|
||||
# Convert to list to support item assignment
|
||||
fields = list(fields)
|
||||
|
||||
# If there's only one field, the user isn't required to use quotes
|
||||
if result[1][2] == 1:
|
||||
fields = [" ".join(fields)]
|
||||
|
||||
# Apply mock to mocking spongebob memes
|
||||
if result[1][1] == "mocking spongebob":
|
||||
fields = list(map(mock.mock, fields))
|
||||
|
||||
# X, X everywhere only takes X as an argument
|
||||
if result[1][1] == "x, x everywhere":
|
||||
fields[0] = " ".join(fields)
|
||||
fields.append(fields[0] + " everywhere")
|
||||
|
||||
# List of fields to send to the API
|
||||
boxes = [{"text": ""}, {"text": ""}, {"text": ""}, {"text": ""}]
|
||||
|
||||
# Add all fields required & ignore the excess ones
|
||||
for i in range(len(fields)):
|
||||
if i > 3:
|
||||
break
|
||||
boxes[i]["text"] = fields[i]
|
||||
|
||||
# Check server status
|
||||
req = requests.get('https://api.imgflip.com/get_memes').json()
|
||||
|
||||
if req["success"]:
|
||||
caption = {
|
||||
"template_id": result[1][0],
|
||||
"username": os.getenv("IMGFLIPNAME"),
|
||||
"password": os.getenv("IMGFLIPPASSWORD"),
|
||||
"boxes[0][text]": boxes[0]["text"],
|
||||
"boxes[1][text]": boxes[1]["text"],
|
||||
"boxes[2][text]": boxes[2]["text"],
|
||||
"boxes[3][text]": boxes[3]["text"]
|
||||
}
|
||||
|
||||
# Send the POST to the API
|
||||
memeReply = requests.post('https://api.imgflip.com/caption_image', caption).json()
|
||||
|
||||
if memeReply['success']:
|
||||
await ctx.send(str(memeReply['data']['url']))
|
||||
await self.utilsCog.removeMessage(ctx.message)
|
||||
else:
|
||||
await ctx.send(
|
||||
"Error! Controleer of je de juiste syntax hebt gebruikt. Gebruik het commando "
|
||||
"\"memes\" voor een lijst aan geaccepteerde meme-namen.")
|
||||
else:
|
||||
await ctx.send("Er is een fout opgetreden.")
|
||||
|
||||
@commands.command(name="Memes")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Fun)
|
||||
async def memes(self, ctx):
|
||||
"""
|
||||
Command that shows a list of memes in the database.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
memeList = memes.getAllMemes()
|
||||
|
||||
# Turn the list into a list of [Name: fields]
|
||||
memeList = [": ".join([stringFormatters.titleCase(meme[1]),
|
||||
str(meme[2])]) for meme in sorted(memeList, key=lambda x: x[1])]
|
||||
|
||||
# Add the fields into the embed
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.add_field(name="Meme: aantal velden", value="\n".join(memeList), inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="Pjoke")
|
||||
@help.Category(category=Category.Fun)
|
||||
async def pjoke(self, ctx):
|
||||
"""
|
||||
Command that sends a random programming joke.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
r = requests.get("https://official-joke-api.appspot.com/jokes/programming/random").json()
|
||||
await ctx.send(r[0]["setup"] + "\n" + r[0]["punchline"])
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Fun(client))
|
||||
257
cogs/games.py
Normal file
257
cogs/games.py
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
from converters.numbers import Abbreviated, abbreviated
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, dinks
|
||||
from functions.database import currency, stats
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
|
||||
|
||||
class Games(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Coinflip", aliases=["Cf"], usage="[Inzet]* [Aantal]*", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Gamble)
|
||||
async def coinflip(self, ctx, *args):
|
||||
"""
|
||||
Command to flip a coin, optionally for Didier Dinks.
|
||||
:param ctx: Discord Context
|
||||
:param args: bet & wager
|
||||
"""
|
||||
args = list(args)
|
||||
choices = ["Kop", "Munt"]
|
||||
result = random.choice(choices)
|
||||
|
||||
# No choice made & no wager
|
||||
if len(args) == 0:
|
||||
await ctx.send("**{}**!".format(result))
|
||||
self.updateStats("cf", "h" if result == "Kop" else "t")
|
||||
return
|
||||
|
||||
# Check for invalid args
|
||||
if len(args) == 1 or args[0][0].lower() not in "kmht":
|
||||
return await ctx.send("Controleer je argumenten.")
|
||||
|
||||
args[1] = abbreviated(args[1])
|
||||
valid = checks.isValidAmount(ctx, args[1])
|
||||
|
||||
# Invalid amount
|
||||
if not valid[0]:
|
||||
return await ctx.send(valid[1])
|
||||
|
||||
# Allow full words, abbreviations, and English alternatives
|
||||
args[0] = "k" if args[0][0].lower() == "k" or args[0][0].lower() == "h" else "m"
|
||||
won = await self.gamble(ctx, args[0], result, valid[1], 2)
|
||||
|
||||
if won:
|
||||
s = stats.getOrAddUser(ctx.author.id)
|
||||
stats.update(ctx.author.id, "cf_wins", int(s[8]) + 1)
|
||||
stats.update(ctx.author.id, "cf_profit", float(s[9]) + float(valid[1]))
|
||||
|
||||
self.updateStats("cf", "h" if result == "Kop" else "t")
|
||||
|
||||
@coinflip.command(name="Stats", hidden=True)
|
||||
async def cf_stats(self, ctx):
|
||||
return await self.client.get_cog("Stats").callStats("cf", ctx)
|
||||
|
||||
@commands.group(name="Dice", aliases=["Roll"], usage="[Inzet]* [Aantal]*", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Gamble)
|
||||
async def dice(self, ctx, *args):
|
||||
"""
|
||||
Command to roll a dice, optionally for Didier Dinks.
|
||||
:param ctx: Discord Context
|
||||
:param args: bet & wager
|
||||
"""
|
||||
args = list(args)
|
||||
result = random.randint(1, 6)
|
||||
|
||||
# No choice made & no wager
|
||||
if len(args) == 0:
|
||||
self.updateStats("dice", result)
|
||||
return await ctx.send(":game_die: **{}**!".format(result))
|
||||
|
||||
# Check for invalid args
|
||||
if len(args) == 1 or not args[0].isdigit() or not 0 < int(args[0]) < 7:
|
||||
return await ctx.send("Controleer je argumenten.")
|
||||
|
||||
args[1] = abbreviated(args[1])
|
||||
valid = checks.isValidAmount(ctx, args[1])
|
||||
|
||||
# Invalid amount
|
||||
if not valid[0]:
|
||||
return await ctx.send(valid[1])
|
||||
|
||||
await self.gamble(ctx, args[0], str(result), valid[1], 6, ":game_die: ")
|
||||
self.updateStats("dice", result)
|
||||
|
||||
@dice.command(name="Stats", hidden=True)
|
||||
async def dice_stats(self, ctx):
|
||||
await self.client.get_cog("Stats").callStats("dice", ctx)
|
||||
|
||||
async def gamble(self, ctx, bet, result, wager, factor, pre="", post=""):
|
||||
"""
|
||||
Function for gambling because it's the same thing every time.
|
||||
:param ctx: Discord Context
|
||||
:param bet: the option the user bet on
|
||||
:param result: randomly generated result
|
||||
:param wager: size of the bet of the user
|
||||
:param factor: the factor by which the person's wager is amplified
|
||||
:param pre: any string that might have to be pre-pended to the output string
|
||||
:param post: any string that might have to be appended to the output string
|
||||
:return: a boolean indicating whether or not the user has won
|
||||
"""
|
||||
# Code no longer first removes your bet to then add profit,
|
||||
# resulting in triple coinflip profit (@Clement).
|
||||
# Subtract one off of the factor to compensate for the initial wager
|
||||
factor -= 1
|
||||
answer = "**{}**! ".format(result)
|
||||
won = False
|
||||
|
||||
# Check if won
|
||||
if result[0].lower() == bet[0].lower():
|
||||
won = True
|
||||
answer += "Je wint **{:,}** Didier Dink{}"
|
||||
currency.update(ctx.author.id, "dinks", float(currency.dinks(ctx.author.id)) + (float(wager) * factor))
|
||||
else:
|
||||
answer += "Je hebt je inzet (**{:,}** Didier Dink{}) verloren"
|
||||
currency.update(ctx.author.id, "dinks", float(currency.dinks(ctx.author.id)) - float(wager))
|
||||
self.loseDinks(round(float(wager)))
|
||||
|
||||
# If won -> multiple dinkS, if lost, it's possible that the user only bet on 1 dinK
|
||||
await ctx.send(pre + answer.format(round(float(wager) * factor if won else float(wager)),
|
||||
checks.pluralS(float(wager) * factor if won else float(wager))) +
|
||||
", **{}**!".format(ctx.author.display_name))
|
||||
return won
|
||||
|
||||
@commands.group(name="Slots", usage="[Aantal]", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Gamble)
|
||||
async def slots(self, ctx, wager: Abbreviated = None):
|
||||
"""
|
||||
Command to play slot machines.
|
||||
:param ctx: Discord Context
|
||||
:param wager: the amount of Didier Dinks to bet with
|
||||
"""
|
||||
valid = checks.isValidAmount(ctx, wager)
|
||||
# Invalid amount
|
||||
if not valid[0]:
|
||||
return await ctx.send(valid[1])
|
||||
|
||||
ratios = dinks.getRatios()
|
||||
|
||||
def randomKey():
|
||||
return random.choice(list(ratios.keys()))
|
||||
|
||||
def generateResults():
|
||||
return [randomKey(), randomKey(), randomKey()]
|
||||
|
||||
# Generate the result
|
||||
result = generateResults()
|
||||
|
||||
textFormatted = "{}\n{}\n:yellow_square:{}:yellow_square:\n:arrow_right:{}:arrow_left::part_alternation_mark:\n" \
|
||||
":yellow_square:{}:yellow_square: :red_circle:\n{}\n{}".format(
|
||||
dinks.slotsHeader, dinks.slotsEmptyRow,
|
||||
"".join(generateResults()), "".join(result), "".join(generateResults()),
|
||||
dinks.slotsEmptyRow, dinks.slotsFooter)
|
||||
|
||||
await ctx.send(textFormatted)
|
||||
|
||||
# Everything different -> no profit
|
||||
if len(set(result)) == 3:
|
||||
await ctx.send("Je hebt je inzet (**{:,}** Didier Dinks) verloren, **{}**.".format(
|
||||
math.floor(float(valid[1])), ctx.author.display_name
|
||||
))
|
||||
currency.update(ctx.author.id, "dinks", float(currency.dinks(ctx.author.id)) - math.floor(float(valid[1])))
|
||||
return
|
||||
|
||||
# Calculate the profit multiplier
|
||||
multiplier = 1.0
|
||||
for symbol in set(result):
|
||||
multiplier *= ratios[symbol][result.count(symbol) - 1]
|
||||
|
||||
await ctx.send(":moneybag: Je wint **{:,}** Didier Dinks, **{}**! :moneybag:".format(
|
||||
round(float(valid[1]) * multiplier, 2), ctx.author.display_name
|
||||
))
|
||||
currency.update(ctx.author.id, "dinks",
|
||||
float(currency.dinks(ctx.author.id)) + (float(valid[1]) * multiplier) - math.floor(
|
||||
float(valid[1])))
|
||||
# Current Dinks - wager + profit
|
||||
|
||||
# Returns list of profits
|
||||
@slots.command(name="Chart", aliases=["Symbols", "Profit"])
|
||||
async def chart(self, ctx):
|
||||
"""
|
||||
Command to show the profit distributions for the Didier Slotmachines.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Slots Profit Chart")
|
||||
ratios = dinks.getRatios()
|
||||
|
||||
# Add every symbol into the embed
|
||||
for ratio in ratios:
|
||||
embed.add_field(name=ratio, value="1: x{}\n2: x{}\n3: x{}".format(
|
||||
str(ratios[ratio][0]), str(ratios[ratio][1]), str(ratios[ratio][2])
|
||||
))
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.group(name="Lost", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Gamble)
|
||||
async def lost(self, ctx):
|
||||
"""
|
||||
Command that shows the amount of Didier Dinks that have been lost due to gambling.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
await ctx.send("Er zijn al **{:,}** Didier Dinks verloren sinds 13/03/2020."
|
||||
.format(dinks.lost()))
|
||||
|
||||
@lost.command(name="Today")
|
||||
async def today(self, ctx):
|
||||
"""
|
||||
Command that shows the amount of Didier Dinks lost today.
|
||||
:param ctx: Discord Context
|
||||
"""
|
||||
await ctx.send("Er zijn vandaag al **{:,}** Didier Dinks verloren."
|
||||
.format(dinks.lostToday()))
|
||||
|
||||
def updateStats(self, game, key):
|
||||
"""
|
||||
Function to update the stats file for a game.
|
||||
:param game: the game to change the stats for
|
||||
:param key: the key in the game's dict to update
|
||||
"""
|
||||
with open("files/stats.json", "r") as fp:
|
||||
s = json.load(fp)
|
||||
|
||||
s[game][str(key)] += 1
|
||||
|
||||
with open("files/stats.json", "w") as fp:
|
||||
json.dump(s, fp)
|
||||
|
||||
def loseDinks(self, amount):
|
||||
"""
|
||||
Function that adds Didier Dinks to the lost file.
|
||||
:param amount: the amount of Didier Dinks lost
|
||||
"""
|
||||
with open("files/lost.json", "r") as fp:
|
||||
fc = json.load(fp)
|
||||
fc["lost"] = fc["lost"] + round(amount)
|
||||
fc["today"] = fc["today"] + round(amount)
|
||||
with open("files/lost.json", "w") as fp:
|
||||
json.dump(fc, fp)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Games(client))
|
||||
170
cogs/hangman.py
Normal file
170
cogs/hangman.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
from decorators import help
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
|
||||
|
||||
def randomWord():
|
||||
lineb = random.randrange(os.stat("files/words-dutch.txt").st_size)
|
||||
with open("files/words-dutch.txt", encoding="latin-1") as file:
|
||||
file.seek(lineb)
|
||||
file.readline()
|
||||
return file.readline().rstrip().upper()
|
||||
|
||||
|
||||
class Hangman(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Hangman", aliases=["Hm"], usage="[Letter]*", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Games)
|
||||
async def hangman(self, ctx, letter=None):
|
||||
if letter and letter.strip():
|
||||
greek = "αβΓγΔδεζηΘθικΛλμνΞξΠπρΣσςτυΦφχΨψΩω"
|
||||
if len(letter) == 1 and (letter.isalpha() or letter.isdigit()) and letter not in greek:
|
||||
await self.guessLetter(ctx, letter)
|
||||
else:
|
||||
await ctx.send("Dit is geen geldige letter.")
|
||||
return
|
||||
await self.gameStatus(ctx)
|
||||
|
||||
async def guessLetter(self, ctx, letter):
|
||||
with open("files/hangman.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
|
||||
if letter.upper() in file["guessed"]:
|
||||
await ctx.send("Deze letter is al eens geprobeerd.")
|
||||
return
|
||||
|
||||
file["guessed"] += letter.upper()
|
||||
|
||||
if letter.upper() not in file["word"]:
|
||||
file["guesses"] += 1
|
||||
|
||||
if int(file["guesses"] >= 9):
|
||||
await ctx.send(self.endGame(file["word"]))
|
||||
return
|
||||
|
||||
with open("files/hangman.json", "w") as fp:
|
||||
json.dump(file, fp)
|
||||
|
||||
await self.gameStatus(ctx)
|
||||
|
||||
@hangman.command(name="Start", usage="[Woord]*")
|
||||
async def start(self, ctx, *, word=None):
|
||||
with open("files/hangman.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
|
||||
# Can't play two games at once
|
||||
if file["word"]:
|
||||
await ctx.send("Er is al een spel bezig.")
|
||||
return
|
||||
|
||||
if word:
|
||||
if not all(letter.isalpha() or letter.isdigit() or letter in [" "] for letter in word):
|
||||
await ctx.send("Dit is geen geldig woord.")
|
||||
return
|
||||
|
||||
# Can only supply your own words in DM
|
||||
if str(ctx.channel.type) != "private":
|
||||
await ctx.message.delete()
|
||||
await ctx.author.send("Het is niet slim om hangman games aan te maken waar iedereen het kan zien."
|
||||
"\nGebruik dit commando hier zodat niemand het woord op voorhand weet.")
|
||||
return
|
||||
|
||||
# Choose a random word when none were passed
|
||||
if not word:
|
||||
word = randomWord()
|
||||
|
||||
with open("files/hangman.json", "w") as fp:
|
||||
json.dump({"guessed": [], "guesses": 0, "word": word}, fp)
|
||||
|
||||
await self.gameStatus(ctx)
|
||||
|
||||
async def gameStatus(self, ctx):
|
||||
with open("files/hangman.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
|
||||
if not file["word"]:
|
||||
await ctx.send("Er is geen spel bezig.")
|
||||
return
|
||||
|
||||
guessed = " ".join(letter for letter in file["guessed"] if letter not in file["word"])
|
||||
filled = self.fillWord(file)
|
||||
|
||||
if filled.replace(" ", "") == file["word"]:
|
||||
self.clearGame()
|
||||
await ctx.send("**Het woord is geraden.**")
|
||||
await ctx.message.add_reaction("✅")
|
||||
return
|
||||
|
||||
await ctx.send("{}\n{}\nFoute letters: {}".format(filled, self.hangManString(file["guesses"]), guessed))
|
||||
|
||||
@hangman.command(name="Guess", usage="[Woord]")
|
||||
async def guess(self, ctx, *, word=None):
|
||||
if not word:
|
||||
await ctx.send("Geef een woord op.")
|
||||
return
|
||||
|
||||
with open("files/hangman.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
|
||||
if not file["word"]:
|
||||
await ctx.send("Er is geen spel bezig.")
|
||||
return
|
||||
|
||||
if word.upper() == file["word"].upper():
|
||||
self.clearGame()
|
||||
await ctx.send("**Het woord is geraden**")
|
||||
await ctx.message.add_reaction("✅")
|
||||
else:
|
||||
file["guesses"] += 1
|
||||
await ctx.send("**{}** is een foute gok.".format(word))
|
||||
await ctx.message.add_reaction("❌")
|
||||
if file["guesses"] >= 9:
|
||||
await ctx.send(self.endGame(file["word"]))
|
||||
else:
|
||||
with open("files/hangman.json", "w") as fp:
|
||||
json.dump(file, fp)
|
||||
await self.gameStatus(ctx)
|
||||
|
||||
# Create a representation of the word by filling in letters that have been guessed, and dots otherwise
|
||||
def fillWord(self, file):
|
||||
return "**" + " ".join(
|
||||
letter if letter in file["guessed"] else "." if letter.isalpha() or letter.isdigit() else letter for letter
|
||||
in file["word"]) + "**"
|
||||
|
||||
def endGame(self, word):
|
||||
self.clearGame()
|
||||
return self.hangManString(9) + "\nHet woord was **{}**.".format(word)
|
||||
|
||||
def clearGame(self):
|
||||
with open("files/hangman.json", "w") as fp:
|
||||
json.dump({"guessed": [], "guesses": 0, "word": ""}, fp)
|
||||
|
||||
def hangManString(self, number):
|
||||
dic = {
|
||||
0: "\n \n \n \n",
|
||||
1: "\n \n \n \n ===",
|
||||
2: "\n |\n |\n |\n ===",
|
||||
3: "--+---+\n |\n |\n |\n ===",
|
||||
4: "--+---+\n | O\n |\n |\n ===",
|
||||
5: "--+---+\n | O\n | |\n |\n ===",
|
||||
6: "--+---+\n | O\n | /|\n |\n ===",
|
||||
7: "--+---+\n | O\n | /|\\\n |\n ===",
|
||||
8: "--+---+\n | O\n | /|\\\n | /\n ===",
|
||||
9: "--+---+\n | O\n | /|\\\n | / \\\n ===",
|
||||
}
|
||||
return dic[number]
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Hangman(client))
|
||||
197
cogs/help.py
Normal file
197
cogs/help.py
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
from data import constants
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import categories, getCategory, Category
|
||||
import json
|
||||
|
||||
|
||||
class HelpCommand(commands.MinimalHelpCommand):
|
||||
def __init__(self, **options):
|
||||
super().__init__(**options)
|
||||
self.ctx = None
|
||||
self.target = None
|
||||
self.is_mod = False
|
||||
|
||||
# Slightly modifying the original callback in order to
|
||||
# allow sending help to DM's & checking for Enums
|
||||
# while removing cog help & checking for mentions
|
||||
async def command_callback(self, ctx, *, command=None):
|
||||
self.ctx = ctx
|
||||
self.is_mod = str(ctx.author.id) == constants.myId
|
||||
bot = ctx.bot
|
||||
if ctx.bot.locked:
|
||||
return
|
||||
|
||||
if len(ctx.message.mentions) > 5:
|
||||
return await ctx.send("Je kan Help maar naar maximaal 5 mensen doorsturen.")
|
||||
|
||||
# Send help categories
|
||||
if command is None:
|
||||
return await self.send_bot_help(self.get_bot_mapping())
|
||||
|
||||
# Check if command is a category
|
||||
if command.lower() == "mod" and not self.is_mod:
|
||||
return await self.send_error_message("Je hebt geen toegang tot deze commando's.")
|
||||
category = getCategory(command, self.is_mod)
|
||||
if category:
|
||||
return await self.send_category_help(category)
|
||||
|
||||
# Cut the mentions out & split based on subcommand
|
||||
spl = command.split(" ")
|
||||
spl = spl[:len(spl) - len(self.ctx.message.mentions)]
|
||||
|
||||
# Turn dic to lowercase to allow proper name searching
|
||||
all_commands = dict((k.lower(), v) for k, v in bot.all_commands.items())
|
||||
|
||||
if spl[0].lower() not in all_commands:
|
||||
return await self.send_error_message(await self.command_not_found(spl[0]))
|
||||
|
||||
cmd = all_commands[spl[0].lower()]
|
||||
|
||||
# Check if the entered command path exists
|
||||
for key in spl[1:]:
|
||||
try:
|
||||
all_commands = dict((k.lower(), v) for k, v in cmd.all_commands.items())
|
||||
if key.lower() not in all_commands:
|
||||
raise AttributeError
|
||||
found = all_commands[key.lower()]
|
||||
except AttributeError:
|
||||
return await self.send_error_message(await self.subcommand_not_found(cmd, key))
|
||||
cmd = found
|
||||
|
||||
# Subcommands should have the parent command's category
|
||||
temp = cmd
|
||||
while temp.parent is not None:
|
||||
temp = temp.parent
|
||||
|
||||
# Don't allow non-mods to see mod commands
|
||||
try:
|
||||
if temp.callback.category == Category.Mod and not self.is_mod:
|
||||
return await self.send_error_message("Je hebt geen toegang tot dit commando.")
|
||||
except AttributeError:
|
||||
return await self.send_error_message("Dit is geen (openbaar) commando.")
|
||||
|
||||
if isinstance(cmd, commands.Group):
|
||||
return await self.send_group_help(cmd)
|
||||
else:
|
||||
return await self.send_command_help(cmd)
|
||||
|
||||
def get_bot_mapping(self):
|
||||
return categories(self.is_mod)
|
||||
|
||||
# Sends list of commands in a category
|
||||
async def send_category_help(self, category):
|
||||
# Get a list of all commands in this category
|
||||
category_commands = [command.name if not command.callback.unpack else command
|
||||
for command in self.ctx.bot.commands
|
||||
if hasattr(command.callback, "category") and command.callback.category == category]
|
||||
|
||||
# Unpack any groups that have to be unpacked
|
||||
for command in list(category_commands):
|
||||
if not isinstance(command, str):
|
||||
category_commands.remove(command)
|
||||
category_commands.extend([self.get_name(c) for c in self.unpack_group(command)])
|
||||
|
||||
embed = self.create_help_embed(category.value)
|
||||
embed.add_field(name="Commands", value="\n".join(sorted(category_commands)))
|
||||
for person in await self.get_destination():
|
||||
await person.send(embed=embed)
|
||||
|
||||
async def send_bot_help(self, mapping):
|
||||
embed = self.create_help_embed("Help")
|
||||
embed.add_field(name="Categorieën", value="\n".join(sorted(mapping)))
|
||||
await self.ctx.send(embed=embed)
|
||||
|
||||
async def send_command_help(self, command):
|
||||
with open("files/help.json", "r") as fp:
|
||||
helpFile = json.load(fp)
|
||||
|
||||
try:
|
||||
helpDescription = helpFile[self.get_name(command).lower()]
|
||||
except KeyError:
|
||||
helpDescription = "Indien je dit leest is DJ STIJN vergeten om dit commando in de help page te zetten. Stuur hem een DM om hem eraan te herinneren."
|
||||
|
||||
embed = self.create_help_embed("Help")
|
||||
embed.add_field(name=await self.get_command_signature(command),
|
||||
value=await self.add_aliases_formatting(sorted(command.aliases)) + helpDescription)
|
||||
for person in await self.get_destination():
|
||||
await person.send(embed=embed)
|
||||
|
||||
async def send_group_help(self, group):
|
||||
with open("files/help.json", "r") as fp:
|
||||
helpFile = json.load(fp)
|
||||
|
||||
embed = self.create_help_embed(group.name + " Commando's")
|
||||
|
||||
try:
|
||||
helpDescription = helpFile[self.get_name(group).lower()]
|
||||
except KeyError:
|
||||
helpDescription = "Indien je dit leest is DJ STIJN vergeten om dit commando in de help page te zetten. Stuur hem een DM om hem eraan te herinneren."
|
||||
|
||||
embed.add_field(name=await self.get_command_signature(group),
|
||||
value=await self.add_aliases_formatting(sorted(group.aliases)) + helpDescription,
|
||||
inline=False)
|
||||
|
||||
# Signature: Aliases - Usage
|
||||
for subcommand in self.unpack_group(group):
|
||||
embed.add_field(name=await self.get_command_signature(subcommand),
|
||||
value=await self.add_aliases_formatting(sorted(subcommand.aliases)) +
|
||||
helpFile[self.get_name(subcommand).lower()], inline=False)
|
||||
|
||||
for person in await self.get_destination():
|
||||
await person.send(embed=embed)
|
||||
|
||||
# Allow mentioning people to send it to them instead
|
||||
async def get_destination(self):
|
||||
if self.ctx.message.mentions:
|
||||
return set(mention for mention in self.ctx.message.mentions if not mention.bot)
|
||||
return [self.ctx.author]
|
||||
|
||||
async def command_not_found(self, string):
|
||||
return "Er bestaat geen commando met de naam **{}**".format(string)
|
||||
|
||||
async def subcommand_not_found(self, command, string):
|
||||
return "**{}** heeft geen subcommando met de naam **{}**.".format(command.name, string)
|
||||
|
||||
async def get_command_signature(self, command):
|
||||
return "{} {}".format(self.get_name(command), command.usage if command.usage is not None else "")
|
||||
|
||||
async def add_aliases_formatting(self, aliases):
|
||||
return "*Alias: {}*\n".format(", ".join(aliases)) if aliases else ""
|
||||
|
||||
async def send_error_message(self, error):
|
||||
embed = discord.Embed(colour=discord.Colour.red())
|
||||
embed.set_author(name="Help")
|
||||
embed.add_field(name="Error", value=error)
|
||||
await self.ctx.author.send(embed=embed)
|
||||
|
||||
def unpack_group(self, group):
|
||||
# Create a list of all command objects in this group, in case they aren't hidden, sorted by name
|
||||
subcommands = [group.all_commands.get(command) for command in group.all_commands]
|
||||
subcommands.sort(key=lambda x: x.name)
|
||||
subcommands = filter(lambda x: not x.hidden, subcommands)
|
||||
return list(set(subcommands))
|
||||
|
||||
def get_name(self, command):
|
||||
return command.qualified_name if command.parents else command.name
|
||||
|
||||
def create_help_embed(self, title):
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name=title)
|
||||
embed.set_footer(text="Syntax: Didier Help [Categorie] of Didier Help [Commando]")
|
||||
return embed
|
||||
|
||||
|
||||
class Help(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self._original_help_command = client.help_command
|
||||
client.help_command = HelpCommand(command_attrs={"aliases": ["rtfm"]})
|
||||
client.help_command.cog = self
|
||||
|
||||
def cog_unload(self):
|
||||
self.client.help_command = self._original_help_command
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Help(client))
|
||||
54
cogs/launch.py
Normal file
54
cogs/launch.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
from datetime import datetime
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
import pytz
|
||||
import requests
|
||||
|
||||
|
||||
class Launch(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Launch", aliases=["SpaceX"])
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def launch(self, ctx, *args):
|
||||
resp = self.getNextLaunch()
|
||||
resp: dict = resp[list(resp.keys())[0]]
|
||||
embed = discord.Embed(
|
||||
colour=discord.Colour.blue()
|
||||
)
|
||||
embed.set_author(name="🚀 Volgende SpaceX lancering 🚀")
|
||||
embed.add_field(name="Naam:", value=resp["name"], inline=False)
|
||||
embed.add_field(name="Tijdstip:", value=resp["time"])
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
def getNextLaunch(self):
|
||||
resp = requests.get("https://launchlibrary.net/1.3/launch?next=1&name=falcon").json()
|
||||
if "status" in resp and (resp["status"] == "fail" or resp["status"] == "error"):
|
||||
return {"error": "fail" if resp["status"].lower() == "fail" else "none found"}
|
||||
resp = resp["launches"]
|
||||
ret = {}
|
||||
for launch in resp:
|
||||
ret[launch["id"]] = {
|
||||
"name": launch["name"],
|
||||
"time": self.parseDate(launch["net"][:-4]) if launch["tbdtime"] == 0 else "TBA",
|
||||
"TBA": launch["tbdtime"] == "0"
|
||||
}
|
||||
return ret
|
||||
|
||||
def parseDate(self, timestr):
|
||||
d = datetime.strptime(timestr, "%B %d, %Y %H:%M:%S").timestamp()
|
||||
return str(
|
||||
datetime.fromtimestamp(int(d) + 7200, pytz.timezone("Europe/Brussels")).strftime('%B %d %Y om %H:%M:%S'))
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Launch(client))
|
||||
204
cogs/leaderboards.py
Normal file
204
cogs/leaderboards.py
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
from data import paginatedLeaderboard
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from enums.numbers import Numbers
|
||||
from functions import checks, xp
|
||||
from functions.database import currency, stats, poke, muttn
|
||||
import math
|
||||
import requests
|
||||
|
||||
|
||||
|
||||
# TODO some sort of general leaderboard because all of them are the same
|
||||
class Leaderboards(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Leaderboard", aliases=["Lb", "Leaderboards"], case_insensitive=True, usage="[Categorie]*",
|
||||
invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def leaderboard(self, ctx):
|
||||
categories = ["Bitcoin", "Corona", "Dinks", "Messages", "Poke", "Rob", "XP"]
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Leaderboard Categorieën")
|
||||
embed.description = "\n".join(categories)
|
||||
await ctx.channel.send(embed=embed)
|
||||
|
||||
@leaderboard.command(name="Dinks", aliases=["Cash"], hidden=True)
|
||||
async def dinks(self, ctx):
|
||||
entries = currency.getAllRows()
|
||||
platDinks = currency.getAllPlatDinks()
|
||||
|
||||
# Take platinum dinks into account
|
||||
for i, user in enumerate(entries):
|
||||
if str(user[0]) in platDinks:
|
||||
# Tuples don't support assignment, cast to list
|
||||
user = list(user)
|
||||
user[1] += platDinks[str(user[0])] * Numbers.q.value
|
||||
entries[i] = user
|
||||
|
||||
boardTop = []
|
||||
for i, user in enumerate(sorted(entries, key=lambda x: (float(x[1]) + float(x[3])), reverse=True)):
|
||||
if i == 0 and float(user[1]) + float(user[3]) == 0.0:
|
||||
return await self.emptyLeaderboard(ctx, "Dinks Leaderboard", "Er zijn nog geen personen met Didier Dinks.")
|
||||
elif float(user[1]) + float(user[3]) > 0.0:
|
||||
|
||||
# Get the username in this guild
|
||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
||||
|
||||
if int(user[0]) == int(ctx.author.id):
|
||||
boardTop.append("**{} ({:,})**".format(name, math.floor(float(user[1]) + float(user[3]))))
|
||||
else:
|
||||
boardTop.append("{} ({:,})".format(name, math.floor(float(user[1]) + float(user[3]))))
|
||||
|
||||
await self.startPaginated(ctx, boardTop, "Dinks Leaderboard")
|
||||
|
||||
@leaderboard.command(name="Corona", hidden=True)
|
||||
async def corona(self, ctx):
|
||||
result = requests.get("http://corona.lmao.ninja/v2/countries").json()
|
||||
result.sort(key=lambda x: int(x["cases"]), reverse=True)
|
||||
board = []
|
||||
for land in result:
|
||||
|
||||
if land["country"] == "Belgium":
|
||||
board.append("**{} ({:,})**".format(land["country"], land["cases"]))
|
||||
else:
|
||||
board.append("{} ({:,})".format(land["country"], land["cases"]))
|
||||
|
||||
await self.startPaginated(ctx, board, "Corona Leaderboard", discord.Colour.red())
|
||||
|
||||
@leaderboard.command(name="Bitcoin", aliases=["Bc"], hidden=True)
|
||||
async def bitcoin(self, ctx):
|
||||
users = currency.getAllRows()
|
||||
boardTop = []
|
||||
for i, user in enumerate(sorted(users, key=lambda x: x[8], reverse=True)):
|
||||
# Don't create an empty leaderboard
|
||||
if i == 0 and float(user[8]) == 0.0:
|
||||
return await self.emptyLeaderboard(ctx, "Bitcoin Leaderboard", "Er zijn nog geen personen met Bitcoins.")
|
||||
elif float(user[8]) > 0.0:
|
||||
# Only add people with more than 0
|
||||
# Get the username in this guild
|
||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
||||
if int(user[0]) == int(ctx.author.id):
|
||||
boardTop.append("**{} ({:,})**".format(name, round(user[8], 8)))
|
||||
else:
|
||||
boardTop.append("{} ({:,})".format(name, round(user[8], 8)))
|
||||
|
||||
await self.startPaginated(ctx, boardTop, "Bitcoin Leaderboard")
|
||||
|
||||
@leaderboard.command(name="Rob", hidden=True)
|
||||
async def rob(self, ctx):
|
||||
users = list(stats.getAllRows())
|
||||
boardTop = []
|
||||
for i, user in enumerate(sorted(users, key=lambda x: x[4], reverse=True)):
|
||||
# Don't create an empty leaderboard
|
||||
if i == 0 and float(user[4]) == 0.0:
|
||||
return await self.emptyLeaderboard(ctx, "Rob Leaderboard", "Er heeft nog niemand Didier Dinks gestolen.")
|
||||
elif float(user[4]) > 0.0:
|
||||
# Only add people with more than 0
|
||||
# Get the username in this guild
|
||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
||||
if int(user[0]) == int(ctx.author.id):
|
||||
boardTop.append("**{} ({:,})**".format(name, math.floor(float(user[4]))))
|
||||
else:
|
||||
boardTop.append("{} ({:,})".format(name, math.floor(float(user[4]))))
|
||||
await self.startPaginated(ctx, boardTop, "Rob Leaderboard")
|
||||
|
||||
@leaderboard.command(name="Poke", hidden=True)
|
||||
async def poke(self, ctx):
|
||||
s = stats.getAllRows()
|
||||
blacklist = poke.getAllBlacklistedUsers()
|
||||
boardTop = []
|
||||
for i, user in enumerate(sorted(s, key=lambda x: x[1], reverse=True)):
|
||||
if i == 0 and int(user[1]) == 0:
|
||||
return await self.emptyLeaderboard(ctx, "Poke Leaderboard", "Er is nog niemand getikt.")
|
||||
|
||||
elif int(user[1]) == 0:
|
||||
break
|
||||
# Don't include blacklisted users
|
||||
elif str(user[0]) not in blacklist:
|
||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
||||
if int(user[0]) == int(ctx.author.id):
|
||||
boardTop.append("**{} ({:,})**".format(name, round(int(user[1]))))
|
||||
else:
|
||||
boardTop.append("{} ({:,})".format(name, round(int(user[1]))))
|
||||
await self.startPaginated(ctx, boardTop, "Poke Leaderboard")
|
||||
|
||||
@leaderboard.command(name="Xp", aliases=["Level"], hidden=True)
|
||||
async def xp(self, ctx):
|
||||
s = stats.getAllRows()
|
||||
boardTop = []
|
||||
for i, user in enumerate(sorted(s, key=lambda x: x[12], reverse=True)):
|
||||
if int(user[12]) == 0:
|
||||
break
|
||||
|
||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
||||
if int(user[0]) == int(ctx.author.id):
|
||||
boardTop.append("**{} (Level {:,} | {:,} XP)**".format(name,
|
||||
xp.calculate_level(round(int(user[12]))),
|
||||
round(int(user[12]))))
|
||||
else:
|
||||
boardTop.append("{} (Level {:,} | {:,} XP)".format(name,
|
||||
xp.calculate_level(round(int(user[12]))),
|
||||
round(int(user[12]))))
|
||||
await self.startPaginated(ctx, boardTop, "XP Leaderboard")
|
||||
|
||||
@leaderboard.command(name="Messages", aliases=["Mc", "Mess"], hidden=True)
|
||||
async def messages(self, ctx):
|
||||
s = stats.getAllRows()
|
||||
boardTop = []
|
||||
for i, user in enumerate(sorted(s, key=lambda x: x[11], reverse=True)):
|
||||
if int(user[11]) == 0:
|
||||
break
|
||||
|
||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
||||
if int(user[0]) == int(ctx.author.id):
|
||||
boardTop.append("**{} ({:,})**".format(name, round(int(user[11]))))
|
||||
else:
|
||||
boardTop.append("{} ({:,})".format(name, round(int(user[11]))))
|
||||
await self.startPaginated(ctx, boardTop, "Messages Leaderboard")
|
||||
|
||||
@leaderboard.command(name="Muttn", aliases=["M", "Mutn", "Mutten"], hidden=True)
|
||||
async def muttn(self, ctx):
|
||||
users = muttn.getAllRows()
|
||||
boardTop = []
|
||||
for i, user in enumerate(sorted(users, key=lambda x: x[1], reverse=True)):
|
||||
if i == 0 and int(user[1]) == 0:
|
||||
return await self.emptyLeaderboard(ctx, "Muttn Leaderboard", "Der zittn nog geen muttns in de server.")
|
||||
|
||||
if float(user[1]) == 0:
|
||||
break
|
||||
|
||||
name = self.utilsCog.getDisplayName(ctx, user[0])
|
||||
if int(user[0]) == int(ctx.author.id):
|
||||
boardTop.append("**{} ({})%**".format(name, round(float(user[1]), 2)))
|
||||
else:
|
||||
boardTop.append("{} ({}%)".format(name, round(float(user[1]), 2)))
|
||||
await self.startPaginated(ctx, boardTop, "Muttn Leaderboard")
|
||||
|
||||
async def callLeaderboard(self, name, ctx):
|
||||
await [command for command in self.leaderboard.commands if command.name.lower() == name.lower()][0](ctx)
|
||||
|
||||
async def startPaginated(self, ctx, source, name, colour=discord.Colour.blue()):
|
||||
pages = paginatedLeaderboard.Pages(source=paginatedLeaderboard.Source(source, name, colour),
|
||||
clear_reactions_after=True)
|
||||
await pages.start(ctx)
|
||||
|
||||
async def emptyLeaderboard(self, ctx, name, message, colour=discord.Colour.blue()):
|
||||
embed = discord.Embed(colour=colour)
|
||||
embed.set_author(name=name)
|
||||
embed.description = message
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Leaderboards(client))
|
||||
156
cogs/minesweeper.py
Normal file
156
cogs/minesweeper.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
from decorators import help
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
import itertools
|
||||
import random
|
||||
|
||||
|
||||
class Minesweeper(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Minesweeper", aliases=["Ms"], usage="[Niveau]*")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Games)
|
||||
async def minesweeper(self, ctx, difficulty="m"):
|
||||
if difficulty[0].lower() not in "emh":
|
||||
await ctx.send("Geef een geldige moeilijkheidsgraad op.")
|
||||
return
|
||||
|
||||
await ctx.send(self.createGame(difficulty[0].lower()))
|
||||
|
||||
def createGame(self, difficutly):
|
||||
|
||||
# [X, Y, BombCount]
|
||||
dimensions = {
|
||||
"e": [5, 5, 4],
|
||||
"m": [10, 10, 20],
|
||||
"h": [13, 13, 35]
|
||||
}
|
||||
|
||||
numbers = {
|
||||
0: "||:zero:||",
|
||||
1: "||:one:||",
|
||||
2: "||:two:||",
|
||||
3: "||:three:||",
|
||||
4: "||:four:||",
|
||||
5: "||:five:||",
|
||||
6: "||:six:||",
|
||||
7: "||:seven:||",
|
||||
8: "||:eight:||",
|
||||
}
|
||||
|
||||
# Initialize an empty grid
|
||||
grid = [[" " for _ in range(dimensions[difficutly][0])] for _ in range(dimensions[difficutly][1])]
|
||||
|
||||
# Generate every possible position on the grid
|
||||
positions = set(itertools.product([x for x in range(len(grid))], repeat=2))
|
||||
|
||||
# Place the bombs in the grid randomly
|
||||
for i in range(dimensions[difficutly][2]):
|
||||
bombPosition = random.choice(list(positions))
|
||||
positions.remove(bombPosition)
|
||||
grid[bombPosition[0]][bombPosition[1]] = "||:boom:||"
|
||||
|
||||
# Add in the numbers representing the amount of bombs nearby
|
||||
for i, row in enumerate(grid):
|
||||
for j, cell in enumerate(row):
|
||||
if cell == " ":
|
||||
grid[i][j] = numbers[self.countBombs(grid, [i, j])]
|
||||
|
||||
# Reveal the biggest empty space to the player
|
||||
self.revealSpaces(grid, self.findBiggestEmptySpace(grid))
|
||||
|
||||
# Join the grid into a string
|
||||
li = [" ".join(row) for row in grid]
|
||||
return "\n".join(li)
|
||||
|
||||
# Counts the amount of bombs near a given cell
|
||||
def countBombs(self, grid, cell):
|
||||
positions = [
|
||||
[1, -1], [1, 0], [1, 1],
|
||||
[0, -1], [0, 1],
|
||||
[-1, -1], [-1, 0], [-1, 1]
|
||||
]
|
||||
|
||||
count = 0
|
||||
for position in positions:
|
||||
if 0 <= cell[0] + position[0] < len(grid) and 0 <= cell[1] + position[1] < len(grid[0]):
|
||||
if "boom" in grid[cell[0] + position[0]][cell[1] + position[1]]:
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
# Finds the biggest spot of 0's on the grid to reveal at the start
|
||||
def findBiggestEmptySpace(self, grid):
|
||||
spaces = []
|
||||
biggest = []
|
||||
|
||||
# Floodfill
|
||||
for i, row in enumerate(grid):
|
||||
for j, cell in enumerate(row):
|
||||
# Only check cells that aren't part of a space yet
|
||||
if not any(cell in space for space in spaces) and "zero" in cell:
|
||||
li = [[i, j]]
|
||||
changed = True
|
||||
while changed:
|
||||
changed = False
|
||||
for added in li:
|
||||
neighb = self.neighbours(grid, added)
|
||||
# Add all neighbours that have not yet been added to this space
|
||||
for neighbour in neighb:
|
||||
if neighbour not in li:
|
||||
li.append(neighbour)
|
||||
changed = True
|
||||
spaces.append(li)
|
||||
|
||||
# If it's bigger than the current biggest, make it the new biggest
|
||||
if len(li) > len(biggest):
|
||||
biggest = li
|
||||
return biggest
|
||||
|
||||
# Returns all neighbouring cells containing a 0
|
||||
def neighbours(self, grid, cell):
|
||||
positions = [
|
||||
[1, 0],
|
||||
[0, -1], [0, 1],
|
||||
[-1, 0]
|
||||
]
|
||||
|
||||
neighb = []
|
||||
|
||||
for position in positions:
|
||||
if 0 <= cell[0] + position[0] < len(grid) and 0 <= cell[1] + position[1] < len(grid[0]):
|
||||
if "zero" in grid[cell[0] + position[0]][cell[1] + position[1]]:
|
||||
neighb.append([cell[0] + position[0], cell[1] + position[1]])
|
||||
return neighb
|
||||
|
||||
# Take away the spoiler marks from the biggest empty space to help the player start
|
||||
def revealSpaces(self, grid, emptySpaces):
|
||||
positions = [
|
||||
[1, -1], [1, 0], [1, 1],
|
||||
[0, -1], [0, 1],
|
||||
[-1, -1], [-1, 0], [-1, 1]
|
||||
]
|
||||
|
||||
for space in emptySpaces:
|
||||
grid[space[0]][space[1]] = ":zero:"
|
||||
# Reveal all spaces around this one
|
||||
for position in positions:
|
||||
# Check if the space is not zero & is contained inside the grid & the space hasn't been cut before
|
||||
if 0 <= space[0] + position[0] < len(grid) and \
|
||||
0 <= space[1] + position[1] < len(grid[0]) and \
|
||||
"zero" not in grid[space[0] + position[0]][space[1] + position[1]] and \
|
||||
"||" in grid[space[0] + position[0]][space[1] + position[1]]:
|
||||
# Cut the spoiler from this cell
|
||||
grid[space[0] + position[0]][space[1] + position[1]] = grid[space[0] + position[0]][
|
||||
space[1] + position[1]][2:-2]
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Minesweeper(client))
|
||||
160
cogs/modCommands.py
Normal file
160
cogs/modCommands.py
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
from data import constants
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, config
|
||||
from functions.database import memes, githubs, twitch, dadjoke
|
||||
import json
|
||||
import os
|
||||
|
||||
|
||||
class ModCommands(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog('Utils')
|
||||
|
||||
@commands.command(name="Remove", aliases=["Rm"], hidden=True)
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def remove(self, ctx, message: str):
|
||||
spl = message.split("/")
|
||||
channel = self.client.get_channel(int(spl[-2]))
|
||||
message = await channel.fetch_message(int(spl[-1]))
|
||||
await message.delete()
|
||||
|
||||
# Load a cog
|
||||
@commands.group(name="Load", usage="[Cog]", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def load(self, ctx, extension: str):
|
||||
try:
|
||||
self.client.load_extension("cogs.{}".format(extension))
|
||||
await self.sendDm(constants.myId, "Loaded **{}**".format(extension))
|
||||
except discord.ext.commands.errors.ExtensionAlreadyLoaded:
|
||||
await self.sendDm(constants.myId, "**{}** has already been loaded".format(extension))
|
||||
|
||||
@commands.command(name="Config", aliases=["Setup", "Set"], case_insensitive=True, usage="[Categorie] [Value]",
|
||||
invoke_without_commands=True)
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(Category.Mod)
|
||||
async def set(self, ctx, category, value):
|
||||
if config.config(category, value):
|
||||
await ctx.message.add_reaction("✅")
|
||||
|
||||
# Load all cogs except for modCommands
|
||||
@load.command(name="All")
|
||||
async def loadAll(self, ctx):
|
||||
for file in os.listdir("./cogs"):
|
||||
if file.endswith(".py") and not file == "modCommands.py":
|
||||
await self.load(ctx, file[:-3])
|
||||
|
||||
# Unload a cog
|
||||
@commands.group(name="Unload", usage="[Cog]", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def unload(self, ctx, extension: str):
|
||||
try:
|
||||
self.client.unload_extension("cogs.{}".format(extension))
|
||||
await self.sendDm(constants.myId, "Unloaded **{}**".format(extension))
|
||||
except discord.ext.commands.errors.ExtensionNotLoaded:
|
||||
await self.sendDm(constants.myId, "**{}** has already been unloaded".format(extension))
|
||||
|
||||
# Unload all cogs except for modCommands
|
||||
@unload.command(name="All")
|
||||
async def unloadAll(self, ctx):
|
||||
for file in os.listdir("./cogs"):
|
||||
if file.endswith(".py") and not file == "modCommands.py":
|
||||
await self.unload(ctx, file[:-3])
|
||||
|
||||
# Reloads a cog
|
||||
@commands.command(name="Reload", aliases=["Update"], usage="[Cog]")
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def reload(self, ctx, cog):
|
||||
await self.unload(ctx, cog)
|
||||
await self.load(ctx, cog)
|
||||
await ctx.message.add_reaction("✅")
|
||||
|
||||
# Repeat what was said
|
||||
@commands.command(name="Repeat", usage="[Text]")
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def repeat(self, ctx, *text):
|
||||
await self.utilsCog.removeMessage(ctx.message)
|
||||
await ctx.send(" ".join(text))
|
||||
|
||||
# Add a reaction to a message
|
||||
@commands.command(name="Reac", aliases=["Reacc"], usage="[Emoji] [Id]")
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def reac(self, ctx, emoji, messageId):
|
||||
channel = ctx.channel
|
||||
|
||||
# Check if the URL or the Id was passed
|
||||
if messageId.count("/") > 3:
|
||||
spl = messageId.split("/")
|
||||
channel = self.client.get_channel(int(spl[-2]))
|
||||
if channel is None:
|
||||
return await ctx.send("Ik kan geen kanaal zien met dit Id.")
|
||||
messageId = int(spl[-1])
|
||||
|
||||
await self.utilsCog.removeMessage(ctx.message)
|
||||
message = await channel.fetch_message(messageId)
|
||||
if message is None:
|
||||
return await ctx.send("Ik kan geen bericht zien met dit Id.")
|
||||
await message.add_reaction(emoji)
|
||||
|
||||
# Adds stuff into their databases
|
||||
@commands.group(name="Add", usage="[Category] [Args]", case_insensitive=True, invoke_without_command=False)
|
||||
@commands.check(checks.isMe)
|
||||
@help.Category(category=Category.Mod)
|
||||
async def add(self, ctx):
|
||||
pass
|
||||
|
||||
@add.command(name="Dadjoke", aliases=["Dj", "Dad"], usage="[Joke]")
|
||||
async def dadjoke(self, ctx, *, joke):
|
||||
dadjoke.addJoke(joke)
|
||||
await ctx.send("Added ``{}``.".format(joke))
|
||||
await ctx.message.add_reaction("✅")
|
||||
|
||||
@add.command(name="8-Ball", aliases=["8b", "Eightball", "8Ball"], usage="[Response]")
|
||||
async def eightball(self, ctx, message):
|
||||
with open("files/eightball.json", "r") as fp:
|
||||
file = json.load(fp)
|
||||
file.append(message)
|
||||
with open("files/eightball.json", "w") as fp:
|
||||
json.dump(file, fp)
|
||||
|
||||
# Adds a meme into the database
|
||||
@add.command(name="Meme", aliases=["Mem"], usage="[Id] [Name] [Aantal Velden]")
|
||||
async def meme(self, ctx, memeid, meme, fields):
|
||||
await ctx.send(memes.insert(memeid, meme, fields)[1])
|
||||
|
||||
# Adds a person's GitHub into the database
|
||||
@add.command(name="GitHub", aliases=["Gh", "Git"], usage="[Id] [Link]")
|
||||
async def github(self, ctx, userid, link):
|
||||
# Allow tagging to work as well
|
||||
if len(ctx.message.mentions) == 1:
|
||||
userid = ctx.message.mentions[0].id
|
||||
githubs.add(userid, link)
|
||||
await ctx.send("{}'s GitHub is toegevoegd aan de database.".format(self.utilsCog.getDisplayName(ctx, userid)))
|
||||
|
||||
# Adds a person's Twitch into the database
|
||||
@add.command(name="Twitch", aliases=["Stream", "Streamer", "Tw"])
|
||||
async def twitch(self, ctx, userid, link):
|
||||
# Allow tagging to work as well
|
||||
if len(ctx.message.mentions) == 1:
|
||||
userid = ctx.message.mentions[0].id
|
||||
twitch.add(userid, link)
|
||||
await ctx.send("{}'s Twitch is toegevoegd aan de database.".format(self.utilsCog.getDisplayName(ctx, userid)))
|
||||
|
||||
# Send a DM to a user -- Can't re-use Utils cog in (un)load because the cog might not be loaded
|
||||
async def sendDm(self, userid, message: str):
|
||||
user = self.client.get_user(int(userid))
|
||||
await user.send(message)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(ModCommands(client))
|
||||
37
cogs/muttn.py
Normal file
37
cogs/muttn.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import discord
|
||||
from discord.ext import commands
|
||||
from decorators import help
|
||||
from enums.help_categories import Category
|
||||
from functions.database import muttn
|
||||
|
||||
|
||||
class Muttn(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Muttn", aliases=["HowMuttn", "M", "Mutn", "Mutten"], usage="[@Persoon]", case_insensitive=True, invoke_without_command=True)
|
||||
@help.Category(Category.Fun)
|
||||
async def muttn(self, ctx, member: discord.Member = None):
|
||||
if member is None:
|
||||
member = ctx.author
|
||||
|
||||
user = muttn.getOrAddUser(member.id)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue(), title=member.display_name)
|
||||
embed.set_author(name="Muttn-O'-Meter")
|
||||
embed.add_field(name="Percentage", value="{}%".format(round(float(user[1]), 2)))
|
||||
|
||||
embed.add_field(name="Aantal {}'s".format("<:Muttn:761551956346798111>"), value=str(user[2]))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@muttn.command(name="Leaderboard", aliases=["Lb"], hidden=True)
|
||||
async def lb(self, ctx):
|
||||
await self.client.get_cog("Leaderboards").callLeaderboard("muttn", ctx)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Muttn(client))
|
||||
153
cogs/oneliners.py
Normal file
153
cogs/oneliners.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
from data import constants
|
||||
import datetime
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, clap, mock, sunrise, timeFormatters
|
||||
import pytz
|
||||
import time
|
||||
import urllib.parse
|
||||
|
||||
|
||||
# Random things that are usually oneliners & don't belong in any other categories
|
||||
class Oneliners(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog('Utils')
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Age", usage="[Formaat]*")
|
||||
@help.Category(category=Category.Didier)
|
||||
async def age(self, ctx, specification=None):
|
||||
allowedSpecifications = ["d", "days", "m", "months", "w", "weeks", "y", "years"]
|
||||
|
||||
if specification is not None and specification.lower() not in allowedSpecifications:
|
||||
await ctx.send("**{}** is geen geldig formaat.".format(specification))
|
||||
return
|
||||
|
||||
if specification is None:
|
||||
timeString = timeFormatters.diffYearBasisString(constants.creationDate)
|
||||
else:
|
||||
ageSeconds = round(time.time()) - constants.creationDate
|
||||
timeFormat = timeFormatters.getFormat(specification)
|
||||
timeString = str(timeFormatters.timeIn(ageSeconds, timeFormat)[0])
|
||||
timeString += " " + timeFormatters.getPlural(int(timeString), timeFormat)
|
||||
await ctx.send("Didier is **{}** oud.".format(timeString))
|
||||
|
||||
@commands.command(name="Clap", usage="[Tekst]")
|
||||
@help.Category(category=Category.Other)
|
||||
async def clap(self, ctx, *args):
|
||||
await ctx.send(clap.clap("".join(args)))
|
||||
await self.utilsCog.removeMessage(ctx.message)
|
||||
|
||||
@commands.command(name="Reverse", aliases=["Rev"], usage="[Tekst]")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def reverse(self, ctx, *args):
|
||||
await ctx.send(" ".join(args)[::-1])
|
||||
|
||||
@commands.command(name="Government", aliases=["Gov", "Regering"])
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def government(self, ctx):
|
||||
now = timeFormatters.dateTimeNow()
|
||||
newGov = datetime.datetime.fromtimestamp(1601539200, tz=pytz.timezone("Europe/Brussels"))
|
||||
delta = now - newGov
|
||||
zin = "Na **494** dagen is er weer een regering, **47** dagen te vroeg om het record te breken. Very sad times.\nMAAR hoelang denk je dat de nieuwe regering het gaat volhouden? Place your bets! Momenteel zitten we aan **{}** dag{}.".format(
|
||||
delta.days, "en" if delta.days != 1 else ""
|
||||
)
|
||||
# now = int(time.time())
|
||||
# valVorige = 1545350400
|
||||
# verkiezingen = 1558828800
|
||||
# valDiff = now - valVorige
|
||||
# verkiezingenDiff = now - verkiezingen
|
||||
# zin = (
|
||||
# "We zitten al **%d** dagen zonder regering, en proberen al **%d** dagen een nieuwe te vormen.\nHet "
|
||||
# "huidige wereldrecord is "
|
||||
# "**541** dagen, dus nog **%d** dagen tot we het gebroken hebben." %
|
||||
# (valDiff // 86400, verkiezingenDiff // 86400, 541 - int(verkiezingenDiff // 86400)))
|
||||
await ctx.send(zin)
|
||||
|
||||
@commands.command()
|
||||
async def inject(self, ctx):
|
||||
await ctx.send("**{}** heeft wat code geïnjecteerd.".format(ctx.author.display_name))
|
||||
|
||||
@commands.command(name="Mock", usage="[Tekst]")
|
||||
@help.Category(category=Category.Other)
|
||||
async def mock(self, ctx, *text):
|
||||
await ctx.channel.send("{} - **{}**".format(mock.mock(" ".join(text)), ctx.author.display_name))
|
||||
await self.utilsCog.removeMessage(ctx.message)
|
||||
|
||||
@commands.command(name="Molest", usage="[@Persoon]")
|
||||
async def molest(self, ctx):
|
||||
if constants.didierId in ctx.message.content:
|
||||
await ctx.send("Nee.")
|
||||
elif str(ctx.author.id) in ctx.message.content or ctx.message.content == "molest me":
|
||||
await ctx.send("I didn't know you swing that way, " + ctx.author.display_name)
|
||||
elif "171671190631481345" in ctx.message.content:
|
||||
await ctx.send("Nee")
|
||||
else:
|
||||
await ctx.send("https://imgur.com/a/bwA6Exn")
|
||||
|
||||
@commands.command(name="Changelog", aliases=["Cl", "Change", "Changes"])
|
||||
@help.Category(category=Category.Didier)
|
||||
async def changelog(self, ctx, *args):
|
||||
await ctx.send("V2.0: <https://docs.google.com/document/d/1oa-9oc9yFnZ0X5sLJTWfdahtaL0vF8acLl-xMXA3a40/edit#>\n"
|
||||
"V2.1: https://docs.google.com/document/d/1ezdJBTnKWoog4q9yJrgwfF4iGOn-PZMoBZgSNVYPtqg/edit#")
|
||||
|
||||
@commands.command(name="Todo", aliases=["List", "Td"])
|
||||
@help.Category(category=Category.Didier)
|
||||
async def todo(self, ctx, *args):
|
||||
await ctx.send("https://trello.com/b/PdtsAJea/didier-to-do-list")
|
||||
|
||||
@commands.command(name="LMGTFY", aliases=["Dsfr", "Gtfm", "Google"], usage="[Query]")
|
||||
@help.Category(category=Category.Other)
|
||||
async def lmgtfy(self, ctx, *, query=None):
|
||||
if query:
|
||||
await ctx.send("https://lmgtfy.com/?q={}&iie=1".format(urllib.parse.quote(query)))
|
||||
|
||||
@commands.command(name="Neck", aliases=["Necc"], usage="[Lengte]*")
|
||||
@help.Category(category=Category.Fun)
|
||||
async def neck(self, ctx, size=None):
|
||||
if not size:
|
||||
size = 1
|
||||
try:
|
||||
size = int(size)
|
||||
if not 0 < size < 16:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
return await ctx.send("Geef een geldig getal op.")
|
||||
|
||||
await ctx.send("<:WhatDidYou:744476950654877756>" + ("<:DoTo:744476965951504414>" * size) + "<:MyDrink:744476979939508275>")
|
||||
|
||||
@commands.command()
|
||||
async def open(self, ctx):
|
||||
# await ctx.send(file=discord.File("files/images/open_source_bad.jpg"))
|
||||
await ctx.send("Soon:tm:")
|
||||
|
||||
@commands.command()
|
||||
async def sc(self, ctx, *args):
|
||||
await ctx.send("http://take-a-screenshot.org/")
|
||||
|
||||
@commands.command(aliases=["src", "os"])
|
||||
async def source(self, ctx):
|
||||
# await ctx.send("<https://bit.ly/31z3BuH>")
|
||||
await ctx.send("Soon:tm:")
|
||||
|
||||
@commands.command(aliases=["sunrise", "sunshine"])
|
||||
async def sun(self, ctx):
|
||||
s = sunrise.Sun()
|
||||
await ctx.send(":sunny:: **{}**\n:crescent_moon:: **{}**".format(s.sunrise(), s.sunset()))
|
||||
|
||||
@commands.command(name="Tias", aliases=["TryIt"])
|
||||
async def tias(self, ctx, *args):
|
||||
await ctx.send("***Try it and see***")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Oneliners(client))
|
||||
113
cogs/poke.py
Normal file
113
cogs/poke.py
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
from data import constants
|
||||
import datetime
|
||||
from decorators import help
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, timeFormatters
|
||||
from functions.database import poke, stats
|
||||
|
||||
|
||||
class Poke(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Poke", usage="[@Persoon]", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Games)
|
||||
async def poke(self, ctx, member=None):
|
||||
if not await self.pokeChecks(ctx):
|
||||
return
|
||||
|
||||
member = ctx.message.mentions[0]
|
||||
await ctx.send("**{}** heeft **{}** getikt. **{}** is hem!".format(
|
||||
ctx.author.display_name, member.display_name, member.display_name))
|
||||
|
||||
# Add into the database
|
||||
poke.update(ctx.author.id, member.id)
|
||||
stats.update(member.id, "poked", int(stats.getOrAddUser(member.id)[1]) + 1)
|
||||
|
||||
@poke.command(name="Blacklist", aliases=["Bl"])
|
||||
async def blacklist(self, ctx):
|
||||
if poke.blacklisted(ctx.author.id):
|
||||
await ctx.send("Je hebt jezelf al geblacklisted, {}.".format(ctx.author.display_name))
|
||||
return
|
||||
if str(poke.get()[0]) == str(ctx.author.id):
|
||||
await ctx.send("Je kan jezelf niet blacklisten als je aan de beurt bent, {}.".format(
|
||||
ctx.author.display_name))
|
||||
return
|
||||
poke.blacklist(ctx.author.id)
|
||||
await ctx.send("**{}** heeft zichzelf geblacklisted en kan niet meer getikt worden.".format(
|
||||
ctx.author.display_name))
|
||||
|
||||
@poke.command(aliases=["wl"], hidden=True)
|
||||
async def whitelist(self, ctx, *user):
|
||||
user = ctx.message.mentions[0].id
|
||||
if not poke.blacklisted(user):
|
||||
await ctx.send("Deze persoon is niet geblacklisted.")
|
||||
return
|
||||
|
||||
poke.blacklist(user, False)
|
||||
await ctx.send("**{}** heeft {} gewhitelisted.".format(
|
||||
ctx.author.display_name, self.utilsCog.getDisplayName(ctx, user)))
|
||||
|
||||
@poke.command(name="Current")
|
||||
async def current(self, ctx):
|
||||
p = poke.get()
|
||||
pokedTimeStamp = datetime.datetime.fromtimestamp(int(p[1]))
|
||||
timeString = timeFormatters.diffDayBasisString(pokedTimeStamp)
|
||||
|
||||
await ctx.send("Het is al **{}** aan **{}**.".format(timeString, self.utilsCog.getDisplayName(ctx, p[0])))
|
||||
|
||||
@poke.command(hidden=True)
|
||||
async def me(self, ctx):
|
||||
await ctx.send("Liever niet.")
|
||||
|
||||
@poke.command(hidden=True)
|
||||
@commands.check(checks.isMe)
|
||||
async def reset(self, ctx):
|
||||
new = poke.reset()
|
||||
await ctx.send("Poke is gereset. <@!{}> is hem!".format(str(new)))
|
||||
|
||||
@poke.command(aliases=["Lb", "Leaderboards"], hidden=True)
|
||||
async def leaderboard(self, ctx):
|
||||
await self.client.get_cog("Leaderboards").callLeaderboard("poke", ctx)
|
||||
|
||||
async def pokeChecks(self, ctx):
|
||||
if len(ctx.message.mentions) == 0:
|
||||
await ctx.send("Dit is geen geldige persoon.")
|
||||
return False
|
||||
if len(ctx.message.mentions) > 1:
|
||||
await ctx.send("Je kan maar 1 persoon tegelijk tikken.")
|
||||
return False
|
||||
if ctx.message.mentions[0].id == ctx.author.id:
|
||||
await ctx.send("Je kan jezelf niet tikken, {}.".format(ctx.author.display_name))
|
||||
return False
|
||||
if str(ctx.message.mentions[0].id) == constants.didierId:
|
||||
await ctx.send("Je kan me niet tikken, {}.".format(ctx.author.display_name))
|
||||
return False
|
||||
if str(ctx.message.mentions[0].id) in constants.botIDs:
|
||||
await ctx.send("Je kan geen bots tikken, {}.".format(ctx.author.display_name))
|
||||
return False
|
||||
|
||||
# Check database things
|
||||
p = poke.get()
|
||||
if str(p[0]) != str(ctx.author.id):
|
||||
await ctx.send("Het is niet jouw beurt, {}.".format(ctx.author.display_name))
|
||||
return False
|
||||
if str(ctx.message.mentions[0].id) == str(p[2]):
|
||||
await ctx.send("Je mag niet terugtikken, {}.".format(ctx.author.display_name))
|
||||
return False
|
||||
if poke.blacklisted(ctx.message.mentions[0].id):
|
||||
await ctx.send("Deze persoon heeft zichzelf geblacklisted en kan niet meer getikt worden.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Poke(client))
|
||||
44
cogs/qr.py
Normal file
44
cogs/qr.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
import requests
|
||||
import urllib.parse
|
||||
|
||||
|
||||
class QR(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="QR", usage="[Tekst]")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def QR(self, ctx, *link):
|
||||
if len(link) != 1:
|
||||
await ctx.send(file=discord.File("files/images/ngguuqr.png"))
|
||||
await self.client.get_cog("Utils").removeMessage(ctx.message)
|
||||
else:
|
||||
self.generate("".join(link))
|
||||
await ctx.send(file=discord.File("files/images/qrcode.png"))
|
||||
self.remove()
|
||||
await self.client.get_cog("Utils").removeMessage(ctx.message)
|
||||
|
||||
def generate(self, link):
|
||||
fileContent = requests.get(
|
||||
"https://image-charts.com/chart?chs=999x999&cht=qr&chl={}&choe=UTF-8&chof=.png".format(
|
||||
urllib.parse.quote(link))).content
|
||||
with open("files/images/qrcode.png", "wb+") as fp:
|
||||
fp.write(fileContent)
|
||||
|
||||
def remove(self):
|
||||
import os
|
||||
os.remove("files/images/qrcode.png")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(QR(client))
|
||||
139
cogs/random.py
Normal file
139
cogs/random.py
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import colours
|
||||
import random
|
||||
import requests
|
||||
import json
|
||||
|
||||
|
||||
class Random(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
# Creates an alias
|
||||
@commands.command(name="Choice", aliases=["Choose"], usage="[Argumenten]")
|
||||
async def choose(self, ctx, *options):
|
||||
await self.choice(ctx, options)
|
||||
|
||||
@commands.command(name="Shuffle", usage="[Argumenten]")
|
||||
async def _shuffle(self, ctx, *options):
|
||||
await self.shuffle(ctx, options)
|
||||
|
||||
@commands.group(name="Random", aliases=["R", "Rand", "RNG"], case_insensitive=True, invoke_without_command=True)
|
||||
@help.Category(category=Category.Random, unpack=True)
|
||||
async def random(self, ctx):
|
||||
pass
|
||||
|
||||
@random.command(name="Choice", usage="[Argumenten]")
|
||||
async def choice(self, ctx, *options):
|
||||
if not options:
|
||||
await ctx.send("Geef een geldige reeks op.")
|
||||
return
|
||||
|
||||
# Allows choose alias to pass in it's args too
|
||||
if isinstance(options[0], tuple):
|
||||
options = options[0]
|
||||
|
||||
await ctx.send(random.choice(options))
|
||||
|
||||
@random.command(name="Number", aliases=["Int"], usage="[Van]* [Tot]*")
|
||||
async def number(self, ctx, to=100, start=1):
|
||||
# This allows number(to) to work, as well as number(start, to)
|
||||
if start > to:
|
||||
start, to = to, start
|
||||
|
||||
await ctx.send(random.randint(start, to))
|
||||
|
||||
@number.error
|
||||
async def on_number_error(self, ctx, error):
|
||||
if isinstance(error, discord.ext.commands.BadArgument):
|
||||
await ctx.send("Dit is geen geldig getal.")
|
||||
else:
|
||||
raise error
|
||||
|
||||
@random.command(name="Name")
|
||||
async def name(self, ctx, *args):
|
||||
try:
|
||||
name = requests.get("https://randomuser.me/api/").json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
await ctx.send("Er ging iets mis. Probeer het opnieuw.")
|
||||
return
|
||||
|
||||
name = name["results"][0]["name"]
|
||||
await ctx.send("{} {} {}".format(name["title"], name["first"], name["last"]))
|
||||
|
||||
@random.command(name="Identity", aliases=["Id"])
|
||||
async def identity(self, ctx, *args):
|
||||
try:
|
||||
identity = requests.get("https://randomuser.me/api/").json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
await ctx.send("Er ging iets mis. Probeer het opnieuw.")
|
||||
return
|
||||
|
||||
identity = identity["results"][0]
|
||||
name = identity["name"]
|
||||
name = "{} {} {}".format(name["title"], name["first"], name["last"])
|
||||
|
||||
gender = identity["gender"]
|
||||
street = "{} {}".format(identity["location"]["street"]["number"], identity["location"]["street"]["name"])
|
||||
location = "{}, {}, {}, {}".format(street, identity["location"]["city"],
|
||||
identity["location"]["state"], identity["location"]["country"])
|
||||
age = identity["dob"]["age"]
|
||||
|
||||
await ctx.send("{}\n{}, {}\n{}".format(name, age, gender, location))
|
||||
|
||||
@random.command(name="Shuffle", aliases=["Order"], usage="[Argumenten]")
|
||||
async def shuffle(self, ctx, *args):
|
||||
if not args:
|
||||
await ctx.send("Geef een geldige reeks op.")
|
||||
return
|
||||
|
||||
# Allows shuffle alias to pass in it's args too
|
||||
if isinstance(args[0], tuple):
|
||||
args = args[0]
|
||||
|
||||
args = list(args)
|
||||
|
||||
random.shuffle(args)
|
||||
|
||||
await ctx.send(" - ".join(args))
|
||||
|
||||
@random.command(name="Colour", aliases=["Color"])
|
||||
async def colour(self, ctx, *args):
|
||||
r, g, b = colours.randomRGB()
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.from_rgb(r, g, b))
|
||||
embed.set_author(name="Random Colour")
|
||||
embed.add_field(name="RGB", value="{}, {}, {}".format(r, g, b), inline=False)
|
||||
embed.add_field(name="HEX", value=colours.RGBToHEX(r, g, b), inline=False)
|
||||
embed.add_field(name="HSL", value="{}°, {}%, {}%".format(*colours.RGBToHSL(r, g, b)), inline=False)
|
||||
embed.add_field(name="HSV", value="{}°, {}%, {}%".format(*colours.RGBToHSV(r, g, b)), inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@random.command(name="Timestamp", aliases=["Time", "Ts"])
|
||||
async def timestamp(self, ctx, *args):
|
||||
hour = str(random.randint(0, 23))
|
||||
hour = ("0" if len(hour) == 1 else "") + hour
|
||||
minutes = str(random.randint(0, 23))
|
||||
minutes = ("0" if len(minutes) == 1 else "") + minutes
|
||||
await ctx.send("{}:{}".format(hour, minutes))
|
||||
|
||||
@random.command(name="Fact", aliases=["Knowledge"])
|
||||
async def fact(self, ctx, *args):
|
||||
randomFact = requests.get("https://uselessfacts.jsph.pl/random.json?language=en").json()
|
||||
await ctx.send(randomFact["text"])
|
||||
|
||||
@commands.command(name="Yes/No", aliases=["Yn"])
|
||||
@help.Category(Category.Random)
|
||||
async def yesno(self, ctx, *args):
|
||||
await ctx.send(random.choice(["Ja.", "Nee."]))
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Random(client))
|
||||
56
cogs/reactword.py
Normal file
56
cogs/reactword.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import reactWord
|
||||
|
||||
|
||||
class ReactWord(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="React", usage="[Tekst] [Message id/url]*")
|
||||
@help.Category(category=Category.Other)
|
||||
async def react(self, ctx, *words):
|
||||
words = list(words)
|
||||
target = False
|
||||
channel = ctx.channel
|
||||
|
||||
# Check if the URL or the Id was passed
|
||||
if str(words[-1]).count("/") > 3:
|
||||
spl = str(words[-1]).split("/")
|
||||
channel = self.client.get_channel(int(spl[-2]))
|
||||
if channel is None:
|
||||
return await ctx.send("Ik kan geen kanaal zien met dit id.")
|
||||
words[-1] = spl[-1]
|
||||
|
||||
# Get the message object if an Id was passed, otherwise react to the message itself
|
||||
try:
|
||||
message = await channel.fetch_message(words[-1])
|
||||
if message is None:
|
||||
return await ctx.send("Ik kan geen bericht zien met dit id.")
|
||||
target = True
|
||||
except discord.HTTPException:
|
||||
message = ctx.message
|
||||
|
||||
# Reactions that were added before this command was executed
|
||||
previousReactions = ([x.emoji for x in message.reactions]) if len(message.reactions) != 0 else []
|
||||
eligible, arr = reactWord.check(list(words), previousReactions)
|
||||
|
||||
if not eligible:
|
||||
await ctx.send(arr[0])
|
||||
else:
|
||||
if target:
|
||||
await self.utilsCog.removeMessage(ctx.message)
|
||||
for reac in arr:
|
||||
await message.add_reaction(reac)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(ReactWord(client))
|
||||
138
cogs/release.py
Normal file
138
cogs/release.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import datetime
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
import requests
|
||||
import time
|
||||
|
||||
|
||||
class Release(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
# Gets upcoming game releases
|
||||
@commands.group(name="Releases", usage="[Pagina]*", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Games)
|
||||
async def releases(self, ctx, page="1"):
|
||||
for char in page:
|
||||
if not char.isdigit():
|
||||
await ctx.send("Geef een geldige pagina op.")
|
||||
return
|
||||
|
||||
dates = self.getDates()
|
||||
resp = requests.get("https://api.rawg.io/api/games?dates={},{}&page_size=25&page={}&ordering=released".format(
|
||||
dates[0], dates[1], page
|
||||
)).json()
|
||||
|
||||
try:
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Volgende Game Releases | Pagina {}".format(page))
|
||||
|
||||
embed.description = "\n".join(
|
||||
["{} (#{}): {}".format(result["name"], result["id"], self.rewriteDate(result["released"]))
|
||||
for result in resp["results"]])
|
||||
embed.set_footer(text="Voor gedetailleerde info: Didier Game Info [id]")
|
||||
except KeyError:
|
||||
embed = discord.Embed(colour=discord.Colour.red())
|
||||
embed.set_author(name="Game Releases")
|
||||
embed.add_field(name="Error", value="Er ging iets fout.")
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
def getDates(self):
|
||||
today = datetime.datetime.fromtimestamp(time.time())
|
||||
nextMonth = datetime.datetime.fromtimestamp(time.time() + 2629743)
|
||||
return ["-".join([str(today.year), self.leadingZero(str(today.month)), self.leadingZero(str(today.day))]),
|
||||
"-".join([str(nextMonth.year),
|
||||
self.leadingZero(str(nextMonth.month)), self.leadingZero(str(nextMonth.day))])]
|
||||
|
||||
def leadingZero(self, num):
|
||||
return num if len(num) == 2 else "0" + num
|
||||
|
||||
# Shows more detailed information for a game
|
||||
@releases.command(name="Info", aliases=["Details"], usage="[Game Id]")
|
||||
async def info(self, ctx, *, game_id):
|
||||
game_id = self.create_slug(game_id)
|
||||
resp = requests.get("https://api.rawg.io/api/games/{}".format(str(game_id))).json()
|
||||
if "redirect" in resp:
|
||||
resp = requests.get("https://api.rawg.io/api/games/{}".format(resp["slug"])).json()
|
||||
if "Not found." in resp.values():
|
||||
embed = discord.Embed(colour=discord.Colour.red())
|
||||
embed.set_author(name="Game Info")
|
||||
embed.description = "Er is geen game gevonden met deze id of naam."
|
||||
else:
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Game Info")
|
||||
embed.add_field(name="Naam", value=resp["name"])
|
||||
embed.add_field(name="Id", value=resp["id"])
|
||||
embed.add_field(name="Datum", value="TBA" if resp["tba"] else self.rewriteDate(resp["released"]))
|
||||
embed.add_field(name="Platforms", value=", ".join(self.getPlatforms(resp)))
|
||||
embed.add_field(name="Stores", value=", ".join(self.getStores(resp)))
|
||||
embed.add_field(name="Genres", value=", ".join(self.getGenres(resp)))
|
||||
embed.add_field(name="Tags", value=self.getTags(resp), inline=False)
|
||||
embed.add_field(name="Description", value=self.writeDescription(resp["description_raw"]), inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
# Turns name into a slug
|
||||
def create_slug(self, game_id):
|
||||
try:
|
||||
# Check if it's a number
|
||||
game_id = int(game_id)
|
||||
return str(game_id)
|
||||
except ValueError:
|
||||
game_id = game_id.lower().replace(" ", "-").replace(":", "").replace("'", "")
|
||||
return game_id
|
||||
|
||||
def rewriteDate(self, date):
|
||||
date = date.split("-")
|
||||
return "-".join(reversed(date))
|
||||
|
||||
def getGenres(self, release):
|
||||
return sorted([genre["name"] for genre in release["genres"]])
|
||||
|
||||
# Returns a list of all platforms this game is available on
|
||||
def getPlatforms(self, release):
|
||||
return sorted([platform["platform"]["name"] for platform in release["platforms"]])
|
||||
|
||||
# Returns a list of all stores this game is available on
|
||||
def getStores(self, release):
|
||||
return sorted(store["store"]["name"] for store in release["stores"])
|
||||
|
||||
# Returns a list of all tags associated with this game
|
||||
def getTags(self, release):
|
||||
if len(release["tags"]) == 0:
|
||||
return "N/A"
|
||||
li = sorted([tag["name"] for tag in release["tags"]])
|
||||
st = li[0]
|
||||
for tag in li[1:]:
|
||||
if len(st) + 2 + len(tag) > 1024:
|
||||
break
|
||||
st += ", " + tag
|
||||
return st if st else "N/A"
|
||||
|
||||
# Truncates the description if necessary
|
||||
def writeDescription(self, description):
|
||||
if len(description) > 700:
|
||||
return description[:697] + "..."
|
||||
return description if description else "N/A"
|
||||
|
||||
@info.error
|
||||
async def info_on_error(self, ctx, error):
|
||||
if isinstance(error, commands.BadArgument):
|
||||
await ctx.send("Geef een geldig getal op.")
|
||||
elif isinstance(error, commands.MissingRequiredArgument):
|
||||
await ctx.send("Controleer je argumenten.")
|
||||
else:
|
||||
raise error
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Release(client))
|
||||
304
cogs/school.py
Normal file
304
cogs/school.py
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
from data import constants
|
||||
import datetime
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.courses import years
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, eten, timeFormatters, config, stringFormatters
|
||||
from functions.numbers import clamp
|
||||
import json
|
||||
|
||||
|
||||
class School(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Eten", aliases=["Food", "Menu"], usage="[Dag]*")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.School)
|
||||
async def eten(self, ctx, *day):
|
||||
day = self.getWeekDay(None if len(day) == 0 else day)[1]
|
||||
|
||||
# Create embed
|
||||
menu = eten.etenScript(day)
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Menu voor {}".format(day))
|
||||
if "gesloten" in menu[0].lower():
|
||||
embed.description = "Restaurant gesloten"
|
||||
else:
|
||||
embed.add_field(name="Soep:", value=menu[0], inline=False)
|
||||
embed.add_field(name="Hoofdgerechten:", value=menu[1], inline=False)
|
||||
|
||||
if menu[2]:
|
||||
embed.add_field(name="Groenten:", value=menu[2], inline=False)
|
||||
|
||||
embed.set_footer(text="Omwille van de coronamaatregelen is er een beperkter aanbod, en kan je enkel nog eten afhalen. Ter plaatse eten is niet meer mogelijk.")
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="Les", aliases=["Sched", "Schedule", "Class"], usage="[Jaargang]* [Dag]*")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.School)
|
||||
async def les(self, ctx, *day):
|
||||
semester = int(config.get("semester"))
|
||||
year = int(config.get("year"))
|
||||
years_counter = int(config.get("years"))
|
||||
# Check if a schedule or a day was called
|
||||
if len(day) == 0:
|
||||
day = []
|
||||
else:
|
||||
# Only either of them was passed
|
||||
if len(day) == 1:
|
||||
# Called a schedule
|
||||
if day[0].isdigit():
|
||||
if 0 < int(day[0]) < years_counter + 1:
|
||||
year = int(day[0])
|
||||
day = []
|
||||
else:
|
||||
return await ctx.send("Dit is geen geldige jaargang.")
|
||||
# elif: calling a weekday is automatically handled below,
|
||||
# so checking is obsolete
|
||||
else:
|
||||
# Both were passed
|
||||
if day[0].isdigit():
|
||||
if 0 < int(day[0]) < years_counter + 1:
|
||||
year = int(day[0])
|
||||
day = []
|
||||
else:
|
||||
return await ctx.send("Dit is geen geldige jaargang.")
|
||||
# Cut the schedule from the string
|
||||
day = day[1:]
|
||||
|
||||
day = self.getWeekDay(None if len(day) == 0 else day)[1]
|
||||
dayDatetime = self.findDate(timeFormatters.weekdayToInt(day))
|
||||
|
||||
schedule = self.customizeSchedule(ctx, year, semester)
|
||||
|
||||
# Create a date object to check the current week
|
||||
startDate = 1600041600
|
||||
currentTime = dayDatetime.timestamp()
|
||||
week = clamp(timeFormatters.timeIn(currentTime - startDate, "weeks")[0], 1, 13)
|
||||
|
||||
title, week = self.getTitle(day, dayDatetime, week)
|
||||
|
||||
# Add all courses & their corresponding times + locations of today
|
||||
courses, extras, prev, online = self.getCourses(schedule, day, week)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue(), title=title)
|
||||
embed.set_author(name="Lessenrooster voor {}{} Bachelor".format(year, "ste" if year == 1 else "de"))
|
||||
|
||||
if len(courses) == 0:
|
||||
embed.add_field(name="Geen Les", value="Geen Les", inline=False)
|
||||
else:
|
||||
courseString = self.createCourseString(courses)
|
||||
courseString += "\nGroep {} heeft vandaag online les.".format(1 if week % 2 == 0 else 2)
|
||||
embed.description = courseString
|
||||
|
||||
if prev:
|
||||
embed.add_field(name="Vakken uit vorige jaren", value=self.createCourseString(prev), inline=False)
|
||||
|
||||
if extras:
|
||||
embed.add_field(name="Extra", value="\n".join(self.getExtras(extra) for extra in extras), inline=False)
|
||||
|
||||
# Add online links if not commnet
|
||||
if online:
|
||||
embed.add_field(name="Online Links", value="\n".join(self.getLink(onlineClass) for onlineClass in online))
|
||||
|
||||
embed.set_footer(text="Semester {} | Lesweek {}".format(semester, round(week)))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
def getLink(self, onlineClass):
|
||||
return "{}: **[{}]({})**".format(onlineClass["course"], onlineClass["online"], onlineClass["link"])
|
||||
|
||||
def createCourseString(self, courses):
|
||||
courseString = ""
|
||||
for course in sorted(courses, key=lambda item: item["slot"]["time"][1]):
|
||||
# Add a ":" to the hour + add a leading "0" if needed
|
||||
start = timeFormatters.timeFromInt(course["slot"]["time"][1])
|
||||
end = timeFormatters.timeFromInt(course["slot"]["time"][2])
|
||||
courseString += "{} - {}: {} {}\n".format(start, end,
|
||||
str(course["course"]), self.getLocation(course["slot"]))
|
||||
return courseString
|
||||
|
||||
# Returns the day of the week, while keeping track of weekends
|
||||
def getWeekDay(self, day=None):
|
||||
weekDays = ["maandag", "dinsdag", "woensdag", "donderdag", "vrijdag"]
|
||||
|
||||
# Get current day of the week
|
||||
dayNumber = datetime.datetime.today().weekday()
|
||||
# If a day or a modifier was passed, show that day instead
|
||||
if day is not None:
|
||||
if day[0] == "morgen":
|
||||
dayNumber += 1
|
||||
elif day[0] == "overmorgen":
|
||||
dayNumber += 2
|
||||
else:
|
||||
for i in range(5):
|
||||
if weekDays[i].startswith(day):
|
||||
dayNumber = i
|
||||
# Weekends should be skipped
|
||||
dayNumber = dayNumber % 7
|
||||
if dayNumber > 4:
|
||||
dayNumber = 0
|
||||
|
||||
# Get daystring
|
||||
return dayNumber, weekDays[dayNumber]
|
||||
|
||||
def getLocation(self, slot):
|
||||
if "canceled" in slot:
|
||||
return None
|
||||
|
||||
if "online" in slot:
|
||||
return "online @ **[{}]({})**".format(slot["online"], slot["zoom"] if slot["online"] == "ZOOM" else slot["bongo"])
|
||||
|
||||
# Check for courses in multiple locations
|
||||
if "locations" in slot:
|
||||
# Language - 'en' for the last one
|
||||
return ", ".join(self.getLocation(location) for location in slot["locations"][:-1]) \
|
||||
+ " en " + self.getLocation(slot["locations"][-1])
|
||||
return "in {} {} {}".format(slot["campus"], slot["building"], slot["room"])
|
||||
|
||||
def getCourses(self, schedule, day, week):
|
||||
# Add all courses & their corresponding times + locations of today
|
||||
courses = []
|
||||
|
||||
extras = []
|
||||
|
||||
prev = []
|
||||
|
||||
onlineLinks = []
|
||||
|
||||
for course in schedule:
|
||||
for slot in course["slots"]:
|
||||
if day in slot["time"]:
|
||||
classDic = {"course": course["course"], "slot": slot}
|
||||
|
||||
# Class was canceled
|
||||
if "canceled" in slot and "weeks" in slot and week in slot["weeks"]:
|
||||
extras.append(classDic)
|
||||
continue
|
||||
|
||||
# Add online links for those at home
|
||||
if not any(el["course"] == course["course"] for el in onlineLinks):
|
||||
if "bongo" in course:
|
||||
onlineDic = {"course": course["course"], "online": "Bongo Virtual Classroom", "link": course["bongo"]}
|
||||
onlineLinks.append(onlineDic)
|
||||
elif "zoom" in course:
|
||||
onlineDic = {"course": course["course"], "online": "Zoom", "link": course["zoom"]}
|
||||
onlineLinks.append(onlineDic)
|
||||
|
||||
# Add this class' bongo & zoom links
|
||||
if "bongo" in course:
|
||||
classDic["slot"]["bongo"] = course["bongo"]
|
||||
|
||||
if "zoom" in course:
|
||||
classDic["slot"]["zoom"] = course["zoom"]
|
||||
|
||||
if "custom" in course:
|
||||
prev.append(classDic)
|
||||
|
||||
# Check for special classes
|
||||
if "weeks" in slot and "online" not in slot:
|
||||
if week in slot["weeks"]:
|
||||
if "custom" not in course:
|
||||
courses.append(classDic)
|
||||
extras.append(classDic)
|
||||
elif "weeks" in slot and "online" in slot and "group" not in slot:
|
||||
if week in slot["weeks"]:
|
||||
if "custom" not in course:
|
||||
courses.append(classDic)
|
||||
extras.append(classDic)
|
||||
|
||||
else:
|
||||
if "custom" not in course:
|
||||
courses.append(classDic)
|
||||
|
||||
# Filter out normal courses that are replaced with special courses
|
||||
for extra in extras:
|
||||
for course in courses:
|
||||
if course["slot"]["time"] == extra["slot"]["time"] and course != extra:
|
||||
courses.remove(course)
|
||||
break
|
||||
|
||||
# Sort online links alphabetically
|
||||
onlineLinks.sort(key=lambda x: x["course"])
|
||||
|
||||
# Remove links of canceled classes
|
||||
for element in onlineLinks:
|
||||
if not any(c["course"] == element["course"] for c in courses):
|
||||
onlineLinks.remove(element)
|
||||
|
||||
return courses, extras, prev, onlineLinks
|
||||
|
||||
def getTitle(self, day, dayDT, week):
|
||||
# now = timeFormatters.dateTimeNow()
|
||||
# if timeFormatters.weekdayToInt(day) < now.weekday():
|
||||
# week += 1
|
||||
|
||||
day = day[0].upper() + day[1:].lower()
|
||||
|
||||
titleString = "{} {}/{}/{}".format(day, stringFormatters.leadingZero(dayDT.day),
|
||||
stringFormatters.leadingZero(dayDT.month), dayDT.year)
|
||||
return titleString, week
|
||||
|
||||
def getExtras(self, extra):
|
||||
start = timeFormatters.timeFromInt(extra["slot"]["time"][1])
|
||||
end = timeFormatters.timeFromInt(extra["slot"]["time"][2])
|
||||
|
||||
location = self.getLocation(extra["slot"])
|
||||
|
||||
if "canceled" in extra["slot"]:
|
||||
return "De les **{}** van **{}** tot **{}** gaat vandaag uitzonderlijk **niet** door.".format(
|
||||
extra["course"], start, end
|
||||
)
|
||||
|
||||
if "group" in extra["slot"]:
|
||||
return "**Groep {}** heeft vandaag uitzonderlijk **{}** **{}** van **{} tot {}**.".format(
|
||||
extra["slot"]["group"], extra["course"], location,
|
||||
start, end
|
||||
)
|
||||
elif "online" in extra["slot"]:
|
||||
return "**{}** gaat vandaag uitzonderlijk online door {} van **{} tot {}**.".format(
|
||||
extra["course"], location[7:],
|
||||
start, end
|
||||
)
|
||||
else:
|
||||
return "**{}** vindt vandaag uitzonderlijk plaats **{}** van **{} tot {}**.".format(
|
||||
extra["course"], location,
|
||||
start, end
|
||||
)
|
||||
|
||||
def findDate(self, targetWeekday):
|
||||
now = timeFormatters.dateTimeNow()
|
||||
while now.weekday() != targetWeekday:
|
||||
now = now + datetime.timedelta(days=1)
|
||||
return now
|
||||
|
||||
# Add all the user's courses
|
||||
def customizeSchedule(self, ctx, year, semester):
|
||||
with open("files/schedules/{}{}.json".format(year, semester), "r") as fp:
|
||||
schedule = json.load(fp)
|
||||
return schedule
|
||||
member = self.client.get_guild(int(constants.CallOfCode)).get_member(ctx.author.id)
|
||||
for role in member.roles:
|
||||
for univYear in years:
|
||||
for course in univYear:
|
||||
if course.value["year"] < year and course.value["id"] == role.id and course.value["semester"] == semester:
|
||||
with open("files/schedules/{}{}.json".format(course.value["year"], course.value["semester"]),
|
||||
"r") as fp:
|
||||
sched2 = json.load(fp)
|
||||
|
||||
for val in sched2:
|
||||
if val["course"] == course.value["name"]:
|
||||
val["custom"] = course.value["year"]
|
||||
schedule.append(val)
|
||||
return schedule
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(School(client))
|
||||
102
cogs/selfpromo.py
Normal file
102
cogs/selfpromo.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
from functions.database import githubs, twitch
|
||||
|
||||
|
||||
class SelfPromo(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.utilsCog = self.client.get_cog("Utils")
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="GitHub", aliases=["Git", "GitHubs", "Gh"], case_insensitive=True, usage="[@Persoon]*", invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def github(self, ctx, member: discord.Member = None):
|
||||
# Get a specific member's GitHub
|
||||
if member:
|
||||
user_git = githubs.get_user(member.id)
|
||||
if not user_git:
|
||||
return await ctx.send("**{}** heeft zijn GitHub link nog niet doorgegeven.".format(member.display_name))
|
||||
|
||||
return await self.createPersonalPromo(ctx, member, user_git[0][0], discord.Colour.from_rgb(250, 250, 250), "GitHub")
|
||||
|
||||
l = githubs.getAll()
|
||||
await self.createPromoEmbed(ctx, l, discord.Colour.from_rgb(250, 250, 250), "GitHub", "files/images/github.png")
|
||||
|
||||
@github.command(name="Add", aliases=["Insert", "Register", "Set"], usage="[Link]")
|
||||
async def githubadd(self, ctx, link):
|
||||
if "github.com" not in link.lower() and "github.ugent.be" not in link.lower() and "gitlab.com" not in link.lower():
|
||||
link = "https://github.com/{}".format(link)
|
||||
|
||||
githubs.add(ctx.author.id, link)
|
||||
await ctx.message.add_reaction("✅")
|
||||
|
||||
@commands.group(name="Twitch", aliases=["Streams"], case_insensitive=True, usage="[@Persoon]", invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def twitch(self, ctx, member: discord.Member = None):
|
||||
# Get a specific member's GitHub
|
||||
if member:
|
||||
user_twitch = twitch.get_user(member.id)
|
||||
if not user_twitch:
|
||||
return await ctx.send("**{}** heeft zijn Twitch link nog niet doorgegeven.".format(member.display_name))
|
||||
|
||||
return await self.createPersonalPromo(ctx, member, user_twitch[0][0], discord.Colour.from_rgb(100, 65, 165), "Twitch")
|
||||
|
||||
l = twitch.getAll()
|
||||
await self.createPromoEmbed(ctx, l, discord.Colour.from_rgb(100, 65, 165), "Twitch", "files/images/twitch.png")
|
||||
|
||||
@twitch.command(name="Add", aliases=["Insert", "Register", "Set"], usage="[Link]")
|
||||
async def twitchadd(self, ctx, link):
|
||||
if "twitch.tv" not in link.lower():
|
||||
link = "https://www.twitch.tv/{}".format(link)
|
||||
|
||||
twitch.add(ctx.author.id, link)
|
||||
await ctx.message.add_reaction("✅")
|
||||
|
||||
# Creates embed with everyone's links & a fancy image
|
||||
async def createPromoEmbed(self, ctx, users, colour, type, imageUrl=None):
|
||||
# Image file
|
||||
file = None
|
||||
|
||||
# Sort users by Discord name
|
||||
users = [[self.utilsCog.getMember(ctx, user[0]), user[1]] for user in users if self.utilsCog.getMember(ctx, user[0]) is not None]
|
||||
users.sort(key=lambda x: x[0].name)
|
||||
|
||||
embed = discord.Embed(colour=colour)
|
||||
if imageUrl is not None:
|
||||
# Link
|
||||
if "https" in imageUrl:
|
||||
embed.set_thumbnail(url=imageUrl)
|
||||
else:
|
||||
# Local file
|
||||
file = discord.File(imageUrl, filename="icon.png")
|
||||
embed.set_thumbnail(url="attachment://icon.png")
|
||||
embed.set_author(name="{} Links".format(type))
|
||||
for user in users:
|
||||
embed.add_field(name="{} ({})".format(
|
||||
user[0].display_name, user[0].name
|
||||
), value=user[1], inline=False)
|
||||
embed.set_footer(text="Wil je je eigen {0} hierin? Gebruik {0} Add [Link] of stuur een DM naar DJ STIJN.".format(type))
|
||||
if file is not None:
|
||||
await ctx.send(embed=embed, file=file)
|
||||
else:
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
async def createPersonalPromo(self, ctx, user, link, colour, type):
|
||||
embed = discord.Embed(colour=colour)
|
||||
embed.set_author(name="{} Links".format(type), icon_url=user.avatar_url)
|
||||
embed.add_field(name="{} link van {}".format(type, user.display_name), value=link)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(SelfPromo(client))
|
||||
128
cogs/stats.py
Normal file
128
cogs/stats.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
from functions.database import stats
|
||||
import json
|
||||
|
||||
|
||||
class Stats(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Stats", usage="[Categorie]*", case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(category=Category.Other)
|
||||
async def stats(self, ctx):
|
||||
s = stats.getOrAddUser(ctx.author.id)
|
||||
|
||||
# Calculate the percentages
|
||||
robAttempts = int(s[2]) + int(s[3]) if int(s[2]) + int(s[3]) != 0 else 1
|
||||
robSuccessPercent = round(100 * int(s[2]) / robAttempts, 2)
|
||||
robFailedPercent = round(100 * int(s[3]) / robAttempts, 2)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="{}'s Stats".format(ctx.author.display_name))
|
||||
embed.add_field(name="Geslaagde Rob Pogingen", value="{} ({})%".format(s[2], robSuccessPercent))
|
||||
embed.add_field(name="Gefaalde Rob Pogingen", value="{} ({})%".format(s[3], robFailedPercent))
|
||||
embed.add_field(name="Aantal Dinks Gestolen", value="{:,}".format(round(s[4])))
|
||||
embed.add_field(name="Aantal Nightlies", value=str(s[6]))
|
||||
embed.add_field(name="Langste Nightly Streak", value=str(s[5]))
|
||||
embed.add_field(name="Totale Profit", value="{:,}".format(round(s[7])))
|
||||
embed.add_field(name="Aantal keer gepoked", value=str(s[1]))
|
||||
embed.add_field(name="Aantal Gewonnen Coinflips", value=str(s[8]))
|
||||
embed.add_field(name="Totale winst uit Coinflips", value="{:,}".format(round(s[9])))
|
||||
embed.add_field(name="Aantal Bails", value="{:,}".format(int(s[10])))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@stats.command(aliases=["Coinflip"], hidden=True)
|
||||
async def cf(self, ctx):
|
||||
with open("files/stats.json", "r") as fp:
|
||||
s = json.load(fp)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Coinflip Stats")
|
||||
embed.description = "**Kop**: {:,} ({}%)\n**Munt**: {:,} ({}%)".format(
|
||||
s["cf"]["h"], self.percent(s["cf"], "h"), s["cf"]["t"], self.percent(s["cf"], "t"))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@stats.command(aliases=["Roll"], hidden=True)
|
||||
async def dice(self, ctx):
|
||||
with open("files/stats.json", "r") as fp:
|
||||
s = json.load(fp)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Dice Stats")
|
||||
embed.description = "\n".join(["**{}**: {:,} ({}%)".format(
|
||||
i, s["dice"][i], self.percent(s["dice"], i)) for i in sorted(s["dice"].keys())])
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@stats.command(hidden=True)
|
||||
async def rob(self, ctx):
|
||||
with open("files/stats.json", "r") as fp:
|
||||
s = json.load(fp)["rob"]
|
||||
totalAttempts = s["robs_success"] + s["robs_failed"]
|
||||
successPercent = round(100 * s["robs_success"] / totalAttempts, 2)
|
||||
failedPercent = round(100 * s["robs_failed"] / totalAttempts, 2)
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Rob Stats")
|
||||
embed.description = "**Geslaagd**: {:,} ({}%)\n**Gefaald**: {:,} ({}%)\n**Borg betaald**: {:,}".format(
|
||||
s["robs_success"], successPercent, s["robs_failed"], failedPercent, round(s["bail_paid"])
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@stats.command(name="Channels", aliases=["C", "CA"], usage="[#Channel]*", hidden=True)
|
||||
@commands.check(checks.isMod)
|
||||
async def channels(self, ctx, channel: discord.TextChannel = None):
|
||||
res = stats.channel_activity(channel)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
|
||||
if channel:
|
||||
embed.set_author(name="Channel Activity - {}".format(channel.name))
|
||||
channel_instance = self.client.get_channel(int(res[0][0]))
|
||||
embed.add_field(name="Aantal berichten", value="{:,}".format(round(float(res[0][1]), 2)), inline=False)
|
||||
|
||||
try:
|
||||
last_message = await channel_instance.fetch_message(channel_instance.last_message_id)
|
||||
except discord.NotFound:
|
||||
last_message = None
|
||||
|
||||
if last_message is None:
|
||||
embed.add_field(name="Laatste bericht", value="[Verwijderd]", inline=False)
|
||||
else:
|
||||
embed.add_field(name="Laatste bericht", value="[Jump URL]({})".format(last_message.jump_url), inline=False)
|
||||
elif ctx.guild:
|
||||
embed.set_author(name="Channel Activity - {}".format(ctx.guild))
|
||||
|
||||
description = ""
|
||||
for c in res:
|
||||
channel_instance = self.client.get_channel(int(c[0]))
|
||||
|
||||
description += "{}: {:,}\n".format(channel_instance.mention, round(float(c[1]), 2))
|
||||
|
||||
embed.description = description
|
||||
else:
|
||||
return await ctx.send("Dit commando werkt niet in DM's.")
|
||||
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
async def callStats(self, name, ctx):
|
||||
await [command for command in self.stats.commands if command.name == name][0](ctx)
|
||||
|
||||
def percent(self, dic, stat):
|
||||
total = sum([int(dic[s]) for s in dic])
|
||||
if total == 0:
|
||||
total = 1
|
||||
|
||||
return round(100 * int(dic[stat]) / total, 2)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Stats(client))
|
||||
120
cogs/store.py
Normal file
120
cogs/store.py
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
from converters.numbers import Abbreviated
|
||||
from data import storePages
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from enums.numbers import Numbers
|
||||
from functions import checks
|
||||
from functions.database import store, currency
|
||||
from functions.numbers import getRep
|
||||
|
||||
|
||||
class Store(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.group(name="Store", aliases=["Shop"], case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Currency)
|
||||
async def store(self, ctx):
|
||||
entries = store.getAllItems()
|
||||
await storePages.Pages(source=storePages.Source(entries), clear_reactions_after=True).start(ctx)
|
||||
|
||||
@store.command(name="Buy", aliases=["Get"], hidden=True)
|
||||
async def storeBuy(self, ctx, item, amount: Abbreviated = 1):
|
||||
if amount is None:
|
||||
return
|
||||
|
||||
await self.buy(ctx, item, amount)
|
||||
|
||||
@commands.command(name="Buy", aliases=["Get"], usage="[Item id] [Aantal]*")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Currency)
|
||||
async def buy(self, ctx, item, amount: Abbreviated = 1):
|
||||
if amount is None:
|
||||
return
|
||||
|
||||
try:
|
||||
item = int(item)
|
||||
except ValueError:
|
||||
return await ctx.send("Dit is geen geldig id.")
|
||||
|
||||
success, message = store.buy(ctx, ctx.author.id, item, amount)
|
||||
if not success:
|
||||
return await ctx.send(message)
|
||||
|
||||
rep = getRep(message["price"], Numbers.t.value)
|
||||
|
||||
return await ctx.send("**{}** heeft **{} {}{}** gekocht voor **{}** Didier Dink{}.".format(
|
||||
ctx.author.display_name, amount, message["name"], checks.pluralS(amount),
|
||||
rep, checks.pluralS(message["price"])
|
||||
))
|
||||
|
||||
@store.command(name="Sell", hidden=True)
|
||||
async def storeSell(self, ctx, itemid, amount: Abbreviated = 1):
|
||||
if amount is None:
|
||||
return
|
||||
await self.sell(ctx, itemid, amount)
|
||||
|
||||
@commands.command(name="Sell", usage="[Item id] [Aantal]")
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Currency)
|
||||
async def sell(self, ctx, itemid, amount: Abbreviated = 1):
|
||||
if amount is None:
|
||||
return
|
||||
|
||||
try:
|
||||
itemid = int(itemid)
|
||||
except ValueError:
|
||||
return await ctx.send("Dit is geen geldig id.")
|
||||
|
||||
inv = store.inventory(ctx.author.id)
|
||||
|
||||
if not inv or not any(int(item[0]) == itemid for item in inv):
|
||||
return await ctx.send("Je hebt geen item met dit id.")
|
||||
|
||||
item_tuple = None
|
||||
for item in inv:
|
||||
if item[0] == itemid:
|
||||
item_tuple = item
|
||||
break
|
||||
|
||||
if int(item_tuple[2]) < amount:
|
||||
return await ctx.send("Je hebt niet zoveel {}s.".format(item_tuple[1]))
|
||||
|
||||
store.sell(int(ctx.author.id), itemid, int(amount), int(item_tuple[2]))
|
||||
price = int(store.getItemPrice(itemid)[0])
|
||||
returnValue = round(0.8 * (price * amount))
|
||||
|
||||
currency.update(ctx.author.id, "dinks", currency.dinks(ctx.author.id) + returnValue)
|
||||
|
||||
await ctx.send("**{}** heeft **{} {}{}** verkocht voor **{}** Didier Dinks!".format(
|
||||
ctx.author.display_name, amount, item_tuple[1], "s" if amount != 1 else "",
|
||||
getRep(returnValue, Numbers.t.value)
|
||||
))
|
||||
|
||||
@commands.command(name="Inventory", aliases=["Inv", "Items"])
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Currency)
|
||||
async def inventory(self, ctx, *args):
|
||||
inv = store.inventory(ctx.author.id)
|
||||
inv = sorted(inv, key=lambda x: x[1])
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Inventory van {}".format(ctx.author.display_name))
|
||||
embed.set_thumbnail(url=str(ctx.author.avatar_url))
|
||||
if len(inv) == 0:
|
||||
embed.description = "Je hebt nog niets gekocht!\n" \
|
||||
"Koop iets in de Store wanneer DJ STIJN niet langer te lui is om er iets in te steken."
|
||||
else:
|
||||
embed.description = "\n".join("#{} {}: {}".format(item[0], item[1], item[2]) for item in inv)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Store(client))
|
||||
167
cogs/tasks.py
Normal file
167
cogs/tasks.py
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
from data import constants
|
||||
from discord.ext import commands, tasks
|
||||
from enums.numbers import Numbers
|
||||
from functions import timeFormatters
|
||||
from functions.database import currency, poke, prison, birthdays, stats
|
||||
import json
|
||||
import requests
|
||||
import time
|
||||
|
||||
|
||||
class Tasks(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.bankInterest.start()
|
||||
self.resetPrison.start()
|
||||
self.resetLost.start()
|
||||
# self.resetPoke.start()
|
||||
self.checkBirthdays.start()
|
||||
self.updateMessageCounts.start()
|
||||
|
||||
@tasks.loop(hours=1.0)
|
||||
async def bankInterest(self):
|
||||
# Don't do it multiple times a day if bot dc's, ...
|
||||
with open("files/lastTasks.json", "r") as fp:
|
||||
lastTasks = json.load(fp)
|
||||
if int(self.getCurrentHour()) == 0 and int(time.time()) - int(lastTasks["interest"]) > 10000:
|
||||
users = currency.getAllRows()
|
||||
bitcoinPrice = self.getCurrentBitcoinPrice()
|
||||
for user in users:
|
||||
# People in prison don't get interest
|
||||
if len(prison.getUser(int(user[0]))) != 0:
|
||||
continue
|
||||
|
||||
if float(user[3]) != 0.0:
|
||||
currency.update(user[0], "investeddays", int(user[4]) + 1)
|
||||
profit = ((float(user[3]) + float(user[5])) * (1 + (float(user[2]) * 0.01))) - float(user[3])
|
||||
# Can't exceed 1 quadrillion
|
||||
# Check BC as well so they can't put everything into BC to cheat the system
|
||||
if float(user[1]) + float(user[3]) + float(user[5]) + profit + (float(user[8]) * bitcoinPrice) > Numbers.q.value:
|
||||
# In case adding profit would exceed 1q, only add the difference
|
||||
profit = Numbers.q.value - float(user[1]) - float(user[3]) - float(user[5]) - (float(user[8]) * bitcoinPrice)
|
||||
# Don't reduce the current profit if Dinks were gained some other way (rob, bc, ...)
|
||||
if profit > 0:
|
||||
currency.update(user[0], "profit", float(user[5]) + profit)
|
||||
|
||||
await self.client.get_user(int(user[0])).send("Je hebt de invest-limiet van 1Q Didier Dinks bereikt.\nIndien je nog meer Didier Dinks wil sparen, kan je 1q Didier Dinks omruilen voor een Platinum Dink in de shop.")
|
||||
|
||||
else:
|
||||
currency.update(user[0], "profit", float(user[5]) + profit)
|
||||
lastTasks["interest"] = int(round(time.time()))
|
||||
with open("files/lastTasks.json", "w") as fp:
|
||||
json.dump(lastTasks, fp)
|
||||
|
||||
@bankInterest.before_loop
|
||||
async def beforeBankInterest(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@tasks.loop(hours=1.0)
|
||||
async def resetLost(self):
|
||||
# Don't do it multiple times a day if bot dc's, ...
|
||||
with open("files/lastTasks.json", "r") as fp:
|
||||
lastTasks = json.load(fp)
|
||||
|
||||
if int(self.getCurrentHour()) == 0 and int(time.time()) - int(lastTasks["lost"]) > 10000:
|
||||
with open("files/lost.json", "r") as fp:
|
||||
fc = json.load(fp)
|
||||
fc["today"] = 0
|
||||
with open("files/lost.json", "w") as fp:
|
||||
json.dump(fc, fp)
|
||||
|
||||
lastTasks["lost"] = round(time.time())
|
||||
with open("files/lastTasks.json", "w") as fp:
|
||||
json.dump(lastTasks, fp)
|
||||
|
||||
@resetLost.before_loop
|
||||
async def beforeResetLost(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@tasks.loop(hours=6.0)
|
||||
async def resetPoke(self):
|
||||
if int(time.time()) - int(poke.get()[1]) > 259200:
|
||||
await self.client.get_guild(int(self.client.constants.CallOfCode))\
|
||||
.get_channel(int(self.client.constants.DidierPosting))\
|
||||
.send("Poke is gereset door inactiviteit. <@!{}> is hem!".format(int(poke.reset())))
|
||||
|
||||
@resetPoke.before_loop
|
||||
async def beforeResetPoke(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@tasks.loop(hours=1.0)
|
||||
async def resetPrison(self):
|
||||
# Don't do it multiple times a day if bot dc's, ...
|
||||
with open("files/lastTasks.json", "r") as fp:
|
||||
lastTasks = json.load(fp)
|
||||
|
||||
if int(self.getCurrentHour()) == 0 and int(time.time()) - int(lastTasks["prison"]) > 10000:
|
||||
prison.dailyLowers()
|
||||
|
||||
with open("files/lastTasks.json", "w") as fp:
|
||||
lastTasks["prison"] = round(time.time())
|
||||
json.dump(lastTasks, fp)
|
||||
|
||||
@resetPrison.before_loop
|
||||
async def beforeResetPrison(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@tasks.loop(hours=1.0)
|
||||
async def checkBirthdays(self):
|
||||
# Don't do it multiple times a day if bot dc's, ...
|
||||
with open("files/lastTasks.json", "r") as fp:
|
||||
lastTasks = json.load(fp)
|
||||
if int(self.getCurrentHour()) == 6 and int(time.time()) - int(lastTasks["birthdays"]) > 10000:
|
||||
dt = timeFormatters.dateTimeNow()
|
||||
res = birthdays.get_users_on_date(dt.day, dt.month)
|
||||
|
||||
COC = self.client.get_guild(int(constants.CallOfCode))
|
||||
people = [COC.get_member(int(user[0])) for user in res]
|
||||
general = COC.get_channel(int(constants.CoCGeneral))
|
||||
|
||||
lastTasks["birthdays"] = round(time.time())
|
||||
with open("files/lastTasks.json", "w") as fp:
|
||||
json.dump(lastTasks, fp)
|
||||
|
||||
if not people:
|
||||
return
|
||||
|
||||
if len(people) == 1:
|
||||
return await general.send("Gelukkige verjaardag {}!".format(people[0].mention))
|
||||
return await general.send("Gelukkige verjaardag {} en {}!".format(
|
||||
", ".join(user.mention for user in people[:-1]),
|
||||
people[-1].mention
|
||||
))
|
||||
|
||||
@checkBirthdays.before_loop
|
||||
async def beforecheckBirthdays(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@tasks.loop(hours=1.0)
|
||||
async def updateMessageCounts(self):
|
||||
# Don't do it multiple times a day if bot dc's, ...
|
||||
with open("files/lastTasks.json", "r") as fp:
|
||||
lastTasks = json.load(fp)
|
||||
if int(self.getCurrentHour()) == 0 and int(time.time()) - int(lastTasks["channels"]) > 10000:
|
||||
channels = stats.channel_activity()
|
||||
for channel in channels:
|
||||
stats.lower_channel(int(channel[0]), 0.95 * float(channel[1]))
|
||||
|
||||
with open("files/lastTasks.json", "w") as fp:
|
||||
lastTasks["channels"] = round(time.time())
|
||||
json.dump(lastTasks, fp)
|
||||
|
||||
@updateMessageCounts.before_loop
|
||||
async def beforeupdateMessageCounts(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
def getCurrentHour(self):
|
||||
return timeFormatters.dateTimeNow().hour
|
||||
|
||||
def getCurrentBitcoinPrice(self):
|
||||
result = requests.get("https://api.coindesk.com/v1/bpi/currentprice.json").json()
|
||||
currentPrice = result["bpi"]["EUR"]["rate_float"]
|
||||
return float(currentPrice)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Tasks(client))
|
||||
20
cogs/testCog.py
Normal file
20
cogs/testCog.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from discord.ext import commands
|
||||
from functions import checks
|
||||
|
||||
|
||||
class TestCog(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# All commands in this Cog should only be accessible to me
|
||||
def cog_check(self, ctx):
|
||||
return checks.isMe(ctx)
|
||||
|
||||
@commands.command()
|
||||
async def test(self, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(TestCog(client))
|
||||
128
cogs/train.py
Normal file
128
cogs/train.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
from data import paginatedLeaderboard
|
||||
import datetime
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands, menus
|
||||
from enums.help_categories import Category
|
||||
from functions import checks
|
||||
import requests
|
||||
|
||||
|
||||
class Train(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Train", aliases=["Trein"], usage="[Vertrek]* [Bestemming]")
|
||||
@help.Category(category=Category.School)
|
||||
async def train(self, ctx, *args):
|
||||
if not args or len(args) > 2:
|
||||
await ctx.send("Controleer je argumenten.")
|
||||
return
|
||||
destination = args[-1]
|
||||
departure = args[0] if len(args) > 1 else "Gent Sint-Pieters"
|
||||
|
||||
req = requests.get(
|
||||
"http://api.irail.be/connections/?from={}&to={}&alerts=true&lang=nl&format=json".format(departure,
|
||||
destination)).json()
|
||||
if "error" in req:
|
||||
embed = discord.Embed(colour=discord.Colour.red())
|
||||
embed.set_author(name="Treinen van {} naar {}".format(
|
||||
self.formatCity(departure), self.formatCity(destination)))
|
||||
embed.add_field(name="Error", value="Er ging iets fout, probeer het later opnieuw.", inline=False)
|
||||
await self.sendEmbed(ctx, embed)
|
||||
return
|
||||
|
||||
pages = paginatedLeaderboard.Pages(source=TrainPagination(self.formatConnections(req["connection"]),
|
||||
self.formatCity(departure),
|
||||
self.formatCity(destination)),
|
||||
clear_reactions_after=True)
|
||||
await pages.start(ctx)
|
||||
|
||||
def formatConnections(self, connections):
|
||||
response = []
|
||||
for connection in sorted(connections, key=lambda con: con["departure"]["time"]):
|
||||
conn = {}
|
||||
if connection["departure"]["canceled"] != "0" or connection["arrival"]["canceled"] != "0":
|
||||
conn = {"Canceled": "Afgeschaft"}
|
||||
dep = connection["departure"]
|
||||
arr = connection["arrival"]
|
||||
conn["depStation"] = self.formatCity(dep["station"])
|
||||
conn["depTime"] = self.formatTime(dep["time"])
|
||||
conn["delay"] = self.formatDelay(dep["delay"])
|
||||
conn["track"] = dep["platform"]
|
||||
conn["arrStation"] = self.formatCity(arr["station"])
|
||||
conn["direction"] = self.formatCity(dep["direction"]["name"])
|
||||
conn["arrTime"] = self.formatTime(arr["time"])
|
||||
conn["duration"] = self.formatTime(connection["duration"])
|
||||
response.append(conn)
|
||||
return response
|
||||
|
||||
def formatTime(self, timestamp):
|
||||
if int(timestamp) <= 86400:
|
||||
minutes = int(timestamp) // 60
|
||||
if minutes < 60:
|
||||
return str(minutes) + "m"
|
||||
return "{}h{:02}m".format(minutes // 60, minutes % 60)
|
||||
else:
|
||||
return datetime.datetime.fromtimestamp(int(timestamp)).strftime("%H:%M")
|
||||
|
||||
def formatDelay(self, seconds):
|
||||
seconds = int(seconds)
|
||||
return self.sign(seconds) + self.formatTime(abs(seconds)) if seconds != 0 else ""
|
||||
|
||||
def sign(self, number):
|
||||
return "-" if int(number) < 0 else "+"
|
||||
|
||||
def formatCity(self, city):
|
||||
city = city[0].upper() + city[1:]
|
||||
arr = []
|
||||
for i, letter in enumerate(city):
|
||||
if (i > 0 and (city[i - 1] == " " or city[i - 1] == "-")) or i == 0:
|
||||
arr.append(letter.upper())
|
||||
else:
|
||||
arr.append(letter.lower())
|
||||
return "".join(arr)
|
||||
|
||||
async def sendEmbed(self, ctx, embed):
|
||||
if checks.allowedChannels(ctx):
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
await ctx.author.send(embed=embed)
|
||||
|
||||
|
||||
class TrainPagination(menus.ListPageSource):
|
||||
def __init__(self, data, departure, destination):
|
||||
super().__init__(data, per_page=3)
|
||||
self.departure = departure
|
||||
self.destination = destination
|
||||
|
||||
async def format_page(self, menu: menus.MenuPages, entries):
|
||||
offset = menu.current_page * self.per_page
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Treinen van {} naar {}".format(self.departure, self.destination))
|
||||
embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages()))
|
||||
|
||||
for i, connection in enumerate(entries, start=offset):
|
||||
afgeschaft = "Canceled" in connection
|
||||
embed.add_field(name="Van", value=str(connection["depStation"]), inline=True)
|
||||
embed.add_field(name="Om", value=str(connection["depTime"]), inline=True)
|
||||
embed.add_field(name="Spoor", value=str(connection["track"]), inline=True)
|
||||
embed.add_field(name="Richting", value=str(connection["direction"]), inline=True)
|
||||
embed.add_field(name="Aankomst", value=(str(connection["arrTime"])
|
||||
if not afgeschaft else "**AFGESCHAFT**"), inline=True)
|
||||
embed.add_field(name="Vertraging", value=str(connection["delay"]) if connection["delay"] != "" else "0",
|
||||
inline=True)
|
||||
|
||||
# White space
|
||||
if i - offset < 2:
|
||||
embed.add_field(name="\u200b", value="\u200b", inline=False)
|
||||
return embed
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Train(client))
|
||||
114
cogs/translate.py
Normal file
114
cogs/translate.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions.stringFormatters import titleCase as tc
|
||||
from googletrans import Translator, LANGUAGES
|
||||
import re
|
||||
|
||||
|
||||
class Translate(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Translate", aliases=["Tl", "Trans"], usage="[Tekst] [Van]* [Naar]*")
|
||||
@help.Category(Category.Words)
|
||||
async def translate(self, ctx, query=None, to="nl", fr="auto"):
|
||||
if query is None:
|
||||
return await ctx.send("Controleer je argumenten.")
|
||||
|
||||
success, query = await self.getQuery(ctx, query)
|
||||
if not success:
|
||||
return await ctx.send(query)
|
||||
|
||||
translator = Translator()
|
||||
|
||||
# From & To were provided, swap them
|
||||
if fr != "auto":
|
||||
temp = fr
|
||||
fr = to
|
||||
to = temp
|
||||
|
||||
try:
|
||||
translation = translator.translate(query, to, fr)
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Didier Translate")
|
||||
|
||||
if fr == "auto":
|
||||
language = translation.extra_data["original-language"]
|
||||
embed.add_field(name="Gedetecteerde taal", value=tc(LANGUAGES[language]))
|
||||
embed.add_field(name="Zekerheid", value="{}%".format(translation.extra_data["confidence"] * 100))
|
||||
|
||||
embed.add_field(name="Origineel ({})".format(translation.src.upper()), value=query, inline=False)
|
||||
embed.add_field(name="Vertaling ({})".format(to.upper()), value=translation.text)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
except ValueError as e:
|
||||
message = str(e)
|
||||
if "destination" in message:
|
||||
return await ctx.send("{} is geen geldige taal.".format(tc(to)))
|
||||
|
||||
if "source" in message:
|
||||
return await ctx.send("{} is geen geldige taal.".format(tc(fr)))
|
||||
|
||||
raise e
|
||||
|
||||
@commands.command(name="Detect", aliases=["Ld"], usage="[Tekst]")
|
||||
@help.Category(Category.Words)
|
||||
async def detect(self, ctx, query=None):
|
||||
if query is None:
|
||||
return await ctx.send("Controleer je argumenten.")
|
||||
|
||||
success, query = await self.getQuery(ctx, query)
|
||||
if not success:
|
||||
return await ctx.send(query)
|
||||
|
||||
translator = Translator()
|
||||
language = translator.detect(query)
|
||||
|
||||
confidence = language.confidence * 100
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name="Language Detection")
|
||||
embed.add_field(name="Zin", value=query, inline=False)
|
||||
embed.add_field(name="Gedetecteerde taal", value=tc(LANGUAGES[language.lang]))
|
||||
embed.add_field(name="Zekerheid", value="{}%".format(confidence))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
async def getQuery(self, ctx, query):
|
||||
# Check if it's a link to a message
|
||||
if re.match(r"^https://discord.com/channels/[0-9A-Za-z@]+/[0-9]+/[0-9]+$", query):
|
||||
spl = query.split("/")
|
||||
channel = self.client.get_channel(int(spl[-2]))
|
||||
if channel is None:
|
||||
return False, "Ik kan geen kanaal zien met dit id."
|
||||
|
||||
message = await channel.fetch_message(spl[-1])
|
||||
if message is None:
|
||||
return False, "Ik kan geen bericht zien met dit id."
|
||||
|
||||
query = message.content
|
||||
|
||||
else:
|
||||
try:
|
||||
# An id was passed instead
|
||||
query = int(query)
|
||||
message = await ctx.channel.fetch_message(query)
|
||||
if message is None:
|
||||
return False, "Ik kan geen bericht zien met dit id."
|
||||
query = message.content
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if not query:
|
||||
return False, "Dit is geen geldig bericht."
|
||||
|
||||
return True, query
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Translate(client))
|
||||
67
cogs/utils.py
Normal file
67
cogs/utils.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
from data import constants
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from decorators import help
|
||||
from enums.help_categories import Category
|
||||
|
||||
|
||||
class Utils(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.client.locked = False
|
||||
self.client.lockedUntil = -1
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
# Marco Polo to check if bot is running & delay
|
||||
@commands.command(name="Marco")
|
||||
@help.Category(category=Category.Didier)
|
||||
async def marco(self, ctx):
|
||||
await ctx.send("Polo! {}ms".format(round(self.client.latency * 1000)))
|
||||
|
||||
async def removeMessage(self, message):
|
||||
try:
|
||||
await message.delete()
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
|
||||
# Send a DM to a user
|
||||
async def sendDm(self, userid, message: str):
|
||||
user = self.client.get_user(int(userid))
|
||||
await user.send(message)
|
||||
|
||||
# Send an Embed to a user
|
||||
async def sendEmbed(self, userid, embed):
|
||||
await discord.utils.get(self.client.get_all_members(), id=int(userid)).send(embed=embed)
|
||||
|
||||
# Returns a member object of a user
|
||||
def getMember(self, ctx, memberid):
|
||||
if str(ctx.channel.type) == "private" or ctx.guild.get_member(int(memberid)) is None:
|
||||
if str(memberid) == str(ctx.author.id):
|
||||
return ctx.author
|
||||
COC = self.client.get_guild(int(constants.CallOfCode))
|
||||
return COC.get_member(int(memberid))
|
||||
|
||||
return ctx.guild.get_member(int(memberid))
|
||||
|
||||
# Returns a user's display name if he's in this server, else COC
|
||||
def getDisplayName(self, ctx, memberid):
|
||||
# Checks if this is a DM, or the user is not in the guild
|
||||
if str(ctx.channel.type) == "private" or ctx.guild.get_member(int(memberid)) is None:
|
||||
if str(memberid) == str(ctx.author.id):
|
||||
return ctx.author.display_name
|
||||
COC = self.client.get_guild(int(constants.CallOfCode))
|
||||
member = COC.get_member(int(memberid))
|
||||
if member is not None:
|
||||
return member.display_name
|
||||
return "[Persoon die de server misschien geleaved is]"
|
||||
|
||||
mem = ctx.guild.get_member(int(memberid))
|
||||
return mem.display_name
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Utils(client))
|
||||
52
cogs/words.py
Normal file
52
cogs/words.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
from decorators import help
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
import requests
|
||||
|
||||
|
||||
class Words(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
# Don't allow any commands to work when locked
|
||||
def cog_check(self, ctx):
|
||||
return not self.client.locked
|
||||
|
||||
@commands.command(name="Adjective", aliases=["Adj", "Adjectives"], usage="[Woord]")
|
||||
@help.Category(category=Category.Words)
|
||||
async def adjective(self, ctx, word=None):
|
||||
await self.getData(ctx, word, "rel_jjb")
|
||||
|
||||
@commands.command(name="Synonym", aliases=["Syn", "Synonyms"], usage="[Woord]")
|
||||
@help.Category(category=Category.Words)
|
||||
async def synonym(self, ctx, word=None):
|
||||
await self.getData(ctx, word, "rel_syn")
|
||||
|
||||
@commands.command(name="Antonym", aliases=["Ant", "Antonyms", "Opp", "Opposite"], usage="[Woord]")
|
||||
@help.Category(category=Category.Words)
|
||||
async def antonym(self, ctx, word=None):
|
||||
await self.getData(ctx, word, "rel_ant")
|
||||
|
||||
@commands.command(name="Rhyme", aliases=["Rhymes"], usage="[Woord]")
|
||||
@help.Category(category=Category.Words)
|
||||
async def rhyme(self, ctx, word=None):
|
||||
await self.getData(ctx, word, "rel_rhy")
|
||||
|
||||
# Contacts the API & returns the response, as these commands all do the same anyways
|
||||
async def getData(self, ctx, word, relation):
|
||||
if not word:
|
||||
await ctx.send("Geef een woord op.")
|
||||
return
|
||||
|
||||
res = requests.get("https://api.datamuse.com/words?{}={}".format(relation, word)).json()
|
||||
|
||||
# Only show top 20 results
|
||||
res = res if len(res) <= 15 else res[:15]
|
||||
|
||||
# Pull the words out of the dicts
|
||||
res = [word["word"] for word in res]
|
||||
await ctx.send(", ".join(res) if len(res) > 0 else "Geen resultaten gevonden.")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Words(client))
|
||||
41
cogs/xp.py
Normal file
41
cogs/xp.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from data import constants
|
||||
from decorators import help
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from enums.help_categories import Category
|
||||
from functions import checks, xp
|
||||
from functions.database import stats
|
||||
|
||||
|
||||
class Xp(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.group(name="Xp", aliases=["Level", "Mc", "Mess", "Messages"], case_insensitive=True, invoke_without_command=True)
|
||||
@commands.check(checks.allowedChannels)
|
||||
@help.Category(Category.Other)
|
||||
async def xp(self, ctx, user: discord.Member = None):
|
||||
if user is not None and str(ctx.author.id) != constants.myId:
|
||||
return await ctx.send("Je hebt geen toegang tot dit commando.")
|
||||
|
||||
target = user if user is not None else ctx.author
|
||||
|
||||
target_stats = stats.getOrAddUser(target.id)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
embed.set_author(name=target.display_name, icon_url=target.avatar_url)
|
||||
embed.add_field(name="Aantal Berichten", value="{}".format(int(target_stats[11])))
|
||||
embed.add_field(name="Level", value=str(xp.calculate_level(target_stats[12])))
|
||||
embed.add_field(name="XP", value="{:,}".format(int(target_stats[12])))
|
||||
embed.set_footer(text="*Sinds Didier 2.0 Launch")
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@xp.command(name="Leaderboard", aliases=["Lb"], hidden=True)
|
||||
async def xpleaderboard(self, ctx, *args):
|
||||
if any(alias in ctx.message.content for alias in ["mc", "mess", "messages"]):
|
||||
return await self.client.get_cog("Leaderboards").callLeaderboard("Messages", ctx)
|
||||
await self.client.get_cog("Leaderboards").callLeaderboard("Xp", ctx)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Xp(client))
|
||||
Loading…
Add table
Add a link
Reference in a new issue