Skip to content

Commit

Permalink
📦 0.12.2 (#586)
Browse files Browse the repository at this point in the history
* 📦 rewrote `k!img`

`k!img` is one of the most popular Killua commands and was using more than 50% of my pxlapi requests.

Replacing it with my own code should save me lots of tokens

* 📦 Add opt-out option of action commands

This new command (k!settings) allows users to opt out of any of the action commands so that they cannot be chosen as targets for them anymore

* 📦 improved structure

In this commit the structure is improved by removing some things from classes.py and putting it in different files plus also improving the new k!settings command a bit

* 🔧 tried to increase speed

Spoiler: did not help much

* 🤖 fixing lgtm alerts
  • Loading branch information
Kile authored Mar 25, 2022
1 parent ba0eda2 commit e84ae70
Show file tree
Hide file tree
Showing 22 changed files with 445 additions and 241 deletions.
5 changes: 3 additions & 2 deletions killua/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import discord
import aiohttp
import asyncio
import getopt, sys, os
import getopt, sys
from random import randint, choice
from discord.ext import commands, ipc
from datetime import date
Expand All @@ -11,7 +11,8 @@

from .webhook.api import app
from .utils.help import MyHelp
from .utils.classes import Category, Guild
from .utils.classes import Guild
from .static.enums import Category
from .static.constants import TOKEN, IPC_TOKEN, presence, TIPS, PORT

class Bot(commands.Bot):
Expand Down
115 changes: 112 additions & 3 deletions killua/cogs/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,36 @@
from typing import List, Union

from killua.utils.checks import check
from killua.utils.classes import Category
from killua.utils.classes import User
from killua.static.enums import Category
from killua.utils.interactions import View
from killua.static.constants import ACTIONS

class SettingsSelect(discord.ui.Select):
"""Creates a select menu to change action settings"""
def __init__(self, options, **kwargs):
super().__init__(
options=options,
**kwargs
)

async def callback(self, interaction: discord.Interaction):
self.view.values = interaction.data["values"]
for opt in self.options:
if opt.value in self.view.values:
opt.default = True

class SettingsButton(discord.ui.Button):

def __init__(self, **kwargs):
super().__init__(**kwargs)

async def callback(self, interaction: discord.Interaction):
if not hasattr(self.view, "values"):
return await interaction.response.send_message("You have not changed any settings", ephemeral=True)
self.view.timed_out = False
self.view.stop()

class Actions(commands.Cog):

def __init__(self, client):
Expand Down Expand Up @@ -55,7 +82,7 @@ def generate_users(self, members:list, title:str) -> str:
memberlist = memberlist + f', {member.display_name}'
return memberlist

async def action_embed(self, endpoint:str, author, members:List[discord.Member]) -> discord.Embed:
async def action_embed(self, endpoint:str, author, members:List[discord.Member], disabled:int = 0) -> discord.Embed:
if endpoint == 'hug':
image = {"link": random.choice(ACTIONS[endpoint]["images"])} # This might eventually be deprecated for copyright reasons
else:
Expand All @@ -71,6 +98,9 @@ async def action_embed(self, endpoint:str, author, members:List[discord.Member])
"image": {"url": image["link"]},
"color": 0x1400ff
})

if disabled > 0:
embed.set_footer(text=f"{disabled} user{'s' if disabled > 1 else ''} disabled being targetted with this action")
return embed

async def no_argument(self, ctx) -> Union[None, discord.Embed]:
Expand All @@ -92,7 +122,16 @@ async def do_action(self, ctx, members:List[discord.Member]=None) -> Union[disco
elif ctx.author == members[0]:
return await ctx.send("Sorry... you can\'t use this command on yourself")
else:
embed = await self.action_embed(ctx.command.name, ctx.author, members)
if len(members) == 1 and not User(members[0].id).action_settings[ctx.command.name]:
return await ctx.send(f"**{members[0].display_name}** has disabled this action", allowed_mentions=discord.AllowedMentions.none())

disabled = 0
for member in members:
if not User(member.id).action_settings[ctx.command.name]:
disabled+=1
members.remove(member)

embed = await self.action_embed(ctx.command.name, ctx.author, members, disabled)

if isinstance(embed, str):
return await ctx.send(embed)
Expand Down Expand Up @@ -165,6 +204,76 @@ async def cuddle(self, ctx, members: commands.Greedy[discord.Member]=None):
"""Snuggle up to a user and cuddle them with this command"""
return await self.do_action(ctx, members)

def _get_view(self, id:int, current: dict) -> View:
options = [discord.SelectOption(label=k, value=k, default=v) for k, v in current.items()]
select = SettingsSelect(options, min_values=0, max_values=len(current))
button = SettingsButton(label="Save", style=discord.ButtonStyle.green, emoji="\U0001f4be")
view = View(user_id=id, timeout=100)
view.timed_out = True

view.add_item(select)
view.add_item(button)

return view

@check()
@commands.command(extras={"category": Category.ACTIONS}, usage="settings")
async def settings(self, ctx):
"""Change the settings that control who can use what action on you"""

embed = discord.Embed.from_dict({
"title": "Settings",
"description": "By unticking a box users will no longer able to use that action on you",
"color": 0x1400ff
})

user = User(ctx.author.id)
current = user.action_settings

for action in ACTIONS.keys():
if action in current:
embed.add_field(name=action, value="✅" if current[action] else "❌", inline=False)
else:
embed.add_field(name=action, value="✅", inline=False)
current[action] = True

view = self._get_view(ctx.author.id, current)

msg = await ctx.send(embed=embed, view=view)

await view.wait()

if view.timed_out:
return await view.disable(msg)

for val in view.values:
current[val] = True

while True:
embed.clear_fields()

for action in ACTIONS.keys():
if action in view.values:
current[action] = True
embed.add_field(name=action, value="✅", inline=False)
else:
current[action] = False
embed.add_field(name=action, value="❌", inline=False)

user.set_action_settings(current)
view = self._get_view(ctx.author.id, current)

await msg.edit(embed=embed, view=view)

await view.wait()

if view.timed_out:
return await view.disable(msg)

for val in view.values:
current[val] = True


Cog = Actions

def setup(client):
Expand Down
31 changes: 21 additions & 10 deletions killua/cogs/cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

from killua.utils.checks import check
from killua.utils.paginator import Paginator
from killua.utils.classes import User, CardNotFound, Category, CheckFailure, Book, ConfirmButton, NoMatches
from killua.utils.classes import User, CardNotFound, CheckFailure, Book, NoMatches
from killua.static.enums import Category
from killua.utils.interactions import ConfirmButton
from killua.static.cards import Card
from killua.static.constants import ALLOWED_AMOUNT_MULTIPLE, FREE_SLOTS, DEF_SPELLS, VIEW_DEF_SPELLS, PRICES, BOOK_PAGES, items, LOOTBOXES

Expand All @@ -17,23 +19,32 @@ class Cards(commands.Cog):

def __init__(self, client):
self.client = client
self.reward_cache = {
"item": [x['_id'] for x in items.find({'type': 'normal', 'rank': { "$in": ['A', 'B', 'C']}})],
"spell": [x['_id'] for x in items.find({'type': 'spell', 'rank': { "$in": ['B', 'C']}})],
"monster": {
"E": [x['_id'] for x in items.find({'type': 'monster', 'rank': {"$in": ['E', 'G', 'H']}})],
"D": [x['_id'] for x in items.find({'type': 'monster', 'rank': {"$in": ['D', 'E', 'F']}})],
"C": [x['_id'] for x in items.find({'type': 'monster', 'rank': {"$in": ['C', 'D', 'E']}})]
}
}

def _get_single_reward(self, score:int) -> Tuple[int, int]:
if score == 1:
if random.randint(1, 10) < 5:
return 1, random.choice([x['_id'] for x in items.find({'type': 'normal', 'rank': { "$in": ['A', 'B', 'C']}})])
return 1, random.choice(self.reward_cache["item"])
else:
return 1, random.choice([x['_id'] for x in items.find({'type': 'spell', 'rank': {"$in": ['B', 'C']}})])
return 1, random.choice(self.reward_cache["spell"])

if score < 3000/10000:
rarities = ['E', 'G', 'H']
rarities = "E"
elif score < 7000/10000:
rarities = ['D', 'E', 'F']
rarities = "D"
else:
rarities = ['C', 'D', 'E']
rarities = "C"

amount = random.randint(1, math.ceil(score*10))
card = random.choice([x['_id'] for x in items.find({'type': 'monster', 'rank': {"$in": rarities}})])
card = random.choice(self.reward_cache["monster"][rarities])
return amount, card

def _construct_rewards(self, score:int) -> List[Tuple[int, int]]:
Expand All @@ -44,14 +55,14 @@ def _construct_rewards(self, score:int) -> List[Tuple[int, int]]:
rewards.append(self._get_single_reward(1))
score = 0.7

for i in range(math.ceil(random.randint(1, math.ceil(score*10))/1.6)):
for _ in range(math.ceil(random.randint(1, math.ceil(score*10))/1.6)):
r = self._get_single_reward(score)
rewards.append(r)

final_rewards: List[Tuple[int, int]] = []
for reward in rewards:
# This avoid duplicates e.g. 4xPaladins Necklace, 2xPaladins Necklace => 6xPaladins Necklace
if reward[1] in (l:= [y for x, y in final_rewards]):
if reward[1] in (l:= [y for _, y in final_rewards]):
index = l.index(reward[1])
final_rewards[index] = (final_rewards[index][0]+reward[0], final_rewards[index][1])
else:
Expand All @@ -64,7 +75,7 @@ def _format_rewards(self, rewards:List[Tuple[int, int]], user:User, score:float)
formatted_text: List[str] = []

for reward in rewards:
for i in range(reward[0]):
for _ in range(reward[0]):
if len([*user.fs_cards,*[x for x in rewards if x[1] > 99 and not user.has_rs_card(x[0])]]) >= 40 and (reward[1] > 99 or (not user.has_rs_card(reward[1]) and reward[1] < 100)):
return formatted_rewards, formatted_text, True # if the free slots are full the process stops
formatted_rewards.append([reward[1], {"fake": False, "clone": False}])
Expand Down
3 changes: 2 additions & 1 deletion killua/cogs/devstuff.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import re

from killua.utils.checks import check
from killua.utils.classes import User, Category #lgtm [py/unused-import]
from killua.utils.classes import User #lgtm [py/unused-import]
from killua.static.enums import Category
from killua.static.cards import Card #lgtm [py/unused-import]
from killua.static.constants import teams, guilds, blacklist, presence as pr, items, updates, UPDATE_CHANNEL #lgtm [py/unused-import]

Expand Down
7 changes: 4 additions & 3 deletions killua/cogs/economy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
from random import randint

from killua.utils.checks import check
from killua.utils.paginator import View
from killua.utils.help import Select
from killua.utils.classes import User, Guild, Category, LootBox
from killua.utils.interactions import View
from killua.utils.interactions import Select
from killua.utils.classes import User, Guild, LootBox
from killua.static.enums import Category
from killua.static.constants import USER_FLAGS, KILLUA_BADGES, GUILD_BADGES, teams, LOOTBOXES, PREMIUM_ALIASES

class Economy(commands.Cog):
Expand Down
5 changes: 3 additions & 2 deletions killua/cogs/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from discord.ext import commands, tasks
from PIL import Image

from killua.utils.classes import Guild, Book, PrintColors
from killua.utils.classes import Guild, Book
from killua.static.enums import PrintColors
from killua.static.constants import TOPGG_TOKEN, DBL_TOKEN, items, teams, PatreonBanner, stats

class Events(commands.Cog):
Expand Down Expand Up @@ -123,7 +124,7 @@ async def on_guild_remove(self, guild):
@commands.Cog.listener()
async def on_command_error(self, ctx, error):

if ctx.channel.permissions_for(ctx.me).send_messages: # we don't want to raise an error inside the error handler when Killua can't send the error because that does not trigger `on_command_error`
if ctx.channel.permissions_for(ctx.me).send_messages and not self.client.is_dev: # we don't want to raise an error inside the error handler when Killua can't send the error because that does not trigger `on_command_error`
return

if ctx.command:
Expand Down
32 changes: 27 additions & 5 deletions killua/cogs/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,31 @@
import math

from killua.utils.paginator import View
from killua.utils.classes import User, ConfirmButton, Category
from killua.utils.classes import User
from killua.utils.interactions import ConfirmButton
from killua.static.enums import Category
from killua.utils.checks import blcheck, check
from killua.utils.help import Select
from killua.utils.interactions import Select


class RpsSelect(discord.ui.Select):
"""Creates a select menu to confirm an rps choice"""
def __init__(self, options, **kwargs):
super().__init__(
min_values=1,
max_values=1,
options=options,
**kwargs
)

async def callback(self, interaction: discord.Interaction):
self.view.value = int(interaction.data["values"][0])
for opt in self.options:
if opt.value == str(self.view.value):
opt.default = True
self.disabled = True
await interaction.response.edit_message(view=self.view)
self.view.stop()

class Trivia:
"""Handles a trivia game"""
Expand Down Expand Up @@ -135,7 +157,7 @@ async def _send_rps_embed(self) -> discord.Message:
async def _timeout(self, players:list, data:List[Tuple[discord.Message, discord.ui.View]]) -> None:
"""A way to handle a timeout of not responding to Killua in dms"""
for x in players:
if x.id in [v.user.id for m, v in data]:
if x.id in [v.user.id for _, v in data]:
await x.send('Sadly the other player has not responded in time')
else:
await x.send('Too late, time to respond is up!')
Expand All @@ -144,13 +166,13 @@ async def _wait_for_response(self, users:List[discord.Member]) -> Union[None, Li
data = []
for u in users:
view = View(user_id=u.id, timeout=None)
select = Select(options=self._get_options())
select = RpsSelect(options=self._get_options())
view.add_item(select)
view.user = u
msg = await u.send("You chose to play Rock Paper Scissors, what\'s your choice Hunter?", view=view)
data.append((msg, view))

done, pending = await asyncio.wait([v.wait() for m, v in data], return_when=asyncio.ALL_COMPLETED, timeout=100)
done, pending = await asyncio.wait([v.wait() for _, v in data], return_when=asyncio.ALL_COMPLETED, timeout=100)

for m, v in data:
await v.disable(m)
Expand Down
19 changes: 1 addition & 18 deletions killua/cogs/image_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

from killua.utils.gif import save_transparent_gif
from killua.utils.checks import check
from killua.utils.classes import Category
from killua.utils.paginator import Paginator
from killua.static.enums import Category
from killua.static.constants import NOKIA_CODE, PXLAPI

class ImageManipulation(commands.Cog):
Expand Down Expand Up @@ -258,22 +257,6 @@ async def google(self, ctx, *, query:str):
return await self.client.send_message(ctx, embed=embed)
return await ctx.send(':x: '+r.error)

@check(4)
@commands.command(aliases=['image'], extras={"category":Category.FUN}, usage="img <query>")
async def img(self, ctx, *,query:str):
"""Search for any image you want"""

r = await self.pxl.image_search(query=query)
if r.success:
def make_embed(page, embed, pages):
embed.title = "Results for query " + query
embed.set_image(url=pages[page-1])
return embed

return await Paginator(ctx, r.data, func=make_embed).start()
else:
return await ctx.send(':x: '+r.error, allowed_mentions=discord.AllowedMentions.none())

@check(30) # long check because this is exhausting for the poor computer
@commands.command(alises=["s"], extras={"category": Category.FUN}, usage="spin <user/url>")
async def spin(self, ctx, args:Union[discord.Member, discord.PartialEmoji, str]=None):
Expand Down
3 changes: 2 additions & 1 deletion killua/cogs/moderation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from discord.ext import commands

from killua.utils.checks import check
from killua.utils.classes import Category, Guild
from killua.utils.classes import Guild
from killua.static.enums import Category

class Moderation(commands.Cog):

Expand Down
Loading

0 comments on commit e84ae70

Please sign in to comment.