Merge branch 'master' into stealemoji-develop

pull/42/head
bobloy 5 years ago
commit a552362869

@ -16,7 +16,8 @@ Cog Function
| forcemention | **Alpha** | <details><summary>Mentions unmentionable roles</summary>Very simple cog, mention doesn't persist</details> | | forcemention | **Alpha** | <details><summary>Mentions unmentionable roles</summary>Very simple cog, mention doesn't persist</details> |
| hangman | **Alpha** | <details><summary>Play a game of hangman</summary>Some visual glitches and needs more customization</details> | | hangman | **Alpha** | <details><summary>Play a game of hangman</summary>Some visual glitches and needs more customization</details> |
| howdoi | **Incomplete** | <details><summary>Ask coding questions and get results from StackExchange</summary>Not yet functional</details> | | howdoi | **Incomplete** | <details><summary>Ask coding questions and get results from StackExchange</summary>Not yet functional</details> |
| leaver | **Alpha** | <details><summary>Send a message in a channel when a user leaves the server</summary>Just released, please report bugs</details> | | leaver | **Beta** | <details><summary>Send a message in a channel when a user leaves the server</summary>Seems to be functional, please report any bugs or suggestions</details> |
| infochannel | **Alpha** | <details><summary>Create a channel to display server info</summary>Just released, please report bugs</details> |
| lovecalculator | **Alpha** | <details><summary>Calculate the love between two users</summary>[Snap-Ons] Just updated to V3</details> | | lovecalculator | **Alpha** | <details><summary>Calculate the love between two users</summary>[Snap-Ons] Just updated to V3</details> |
| lseen | **Alpha** | <details><summary>Track when a member was last online</summary>Alpha release, please report bugs</details> | | lseen | **Alpha** | <details><summary>Track when a member was last online</summary>Alpha release, please report bugs</details> |
| nudity | **Incomplete** | <details><summary>Checks for NSFW images posted in non-NSFW channels</summary>Library this is based on has a bug, waiting for author to merge my PR</details> | | nudity | **Incomplete** | <details><summary>Checks for NSFW images posted in non-NSFW channels</summary>Library this is based on has a bug, waiting for author to merge my PR</details> |

@ -97,7 +97,7 @@ class AnnounceDaily(Cog):
if ctx.message.attachments: if ctx.message.attachments:
att_ = ctx.message.attachments[0] att_ = ctx.message.attachments[0]
try: try:
h = att_.height att_.height
except AttributeError: except AttributeError:
await ctx.send("You must attach an image, no other file will be accepted") await ctx.send("You must attach an image, no other file will be accepted")
return return
@ -140,7 +140,7 @@ class AnnounceDaily(Cog):
@_ad.command() @_ad.command()
async def listimg(self, ctx: commands.Context): async def listimg(self, ctx: commands.Context):
""" """
List all registered announcement immages List all registered announcement images
""" """
images = await self.config.images() images = await self.config.images()
for page in pagify("\n".join(images)): for page in pagify("\n".join(images)):
@ -192,7 +192,7 @@ class AnnounceDaily(Cog):
await self.config.time.set({"hour": h, "minute": m, "second": s}) await self.config.time.set({"hour": h, "minute": m, "second": s})
await ctx.send( await ctx.send(
"Announcements time has been set to {}::{}::{} every day\n" "Announcement time has been set to {}::{}::{} every day\n"
"**Changes will apply after next scheduled announcement or reload**".format(h, m, s) "**Changes will apply after next scheduled announcement or reload**".format(h, m, s)
) )
@ -229,7 +229,7 @@ class AnnounceDaily(Cog):
await channel.send(choice) await channel.send(choice)
async def check_day(self): async def check_day(self):
while self is self.bot.get_cog("AnnounceDaily"): while True:
tomorrow = datetime.now() + timedelta(days=1) tomorrow = datetime.now() + timedelta(days=1)
time = await self.config.time() time = await self.config.time()
h, m, s = time["hour"], time["minute"], time["second"] h, m, s = time["hour"], time["minute"], time["second"]

@ -10,7 +10,6 @@
"description": "Send daily announcements to all servers at a specified times", "description": "Send daily announcements to all servers at a specified times",
"hidden": true, "hidden": true,
"install_msg": "Thank you for installing AnnounceDaily! Get started with `[p]load announcedaily` and `[p]help AnnounceDaily`", "install_msg": "Thank you for installing AnnounceDaily! Get started with `[p]load announcedaily` and `[p]help AnnounceDaily`",
"requirements": [],
"short": "Send daily announcements", "short": "Send daily announcements",
"tags": [ "tags": [
"bobloy" "bobloy"

@ -5,20 +5,23 @@ from typing import List
import lavalink import lavalink
import yaml import yaml
from redbot.cogs.audio import Audio from redbot.cogs.audio import Audio
from redbot.cogs.audio.core.utilities import validation
from redbot.cogs.trivia import LOG from redbot.cogs.trivia import LOG
from redbot.cogs.trivia.trivia import InvalidListError, Trivia from redbot.cogs.trivia.trivia import InvalidListError, Trivia
from redbot.core import commands, Config, checks from redbot.core import commands, Config, checks
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.data_manager import cog_data_path from redbot.core.data_manager import cog_data_path
from redbot.core.utils.chat_formatting import box from redbot.core.utils.chat_formatting import box
# from redbot.cogs.audio.utils import userlimit
from .audiosession import AudioSession from .audiosession import AudioSession
class AudioTrivia(Trivia): class AudioTrivia(Trivia):
""" """
Custom commands Upgrade to the Trivia cog that enables audio trivia
Creates commands used to display text and adjust roles Replaces the Trivia cog
""" """
def __init__(self, bot: Red): def __init__(self, bot: Red):
@ -88,19 +91,24 @@ class AudioTrivia(Trivia):
if session is not None: if session is not None:
await ctx.send("There is already an ongoing trivia session in this channel.") await ctx.send("There is already an ongoing trivia session in this channel.")
return return
status = await self.audio.config.status() status = await self.audio.config.status()
notify = await self.audio.config.guild(ctx.guild).notify()
if status: if status:
await ctx.send( await ctx.send(
"I recommend disabling audio status with `{}audioset status`".format(ctx.prefix) "It is recommended to disable audio status with `{}audioset status`".format(ctx.prefix)
)
if notify:
await ctx.send(
"It is recommended to disable audio notify with `{}audioset notify`".format(ctx.prefix)
) )
if not self.audio._player_check(ctx): if not self.audio._player_check(ctx):
try: try:
if not ctx.author.voice.channel.permissions_for( if not ctx.author.voice.channel.permissions_for(
ctx.me ctx.me
).connect or self.audio._userlimit(ctx.author.voice.channel): ).connect or self.audio.is_vc_full(ctx.author.voice.channel):
return await ctx.send("I don't have permission to connect to your channel.") return await ctx.send("I don't have permission to connect to your channel.")
await lavalink.connect(ctx.author.voice.channel) await lavalink.connect(ctx.author.voice.channel)
lavaplayer = lavalink.get_player(ctx.guild.id) lavaplayer = lavalink.get_player(ctx.guild.id)
@ -110,9 +118,8 @@ class AudioTrivia(Trivia):
lavaplayer = lavalink.get_player(ctx.guild.id) lavaplayer = lavalink.get_player(ctx.guild.id)
lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno
lavaplayer.store("guild", ctx.guild.id)
await self.audio._data_check(ctx) await self.audio.set_player_settings(ctx)
if not ctx.author.voice or ctx.author.voice.channel != lavaplayer.channel: if not ctx.author.voice or ctx.author.voice.channel != lavaplayer.channel:
return await ctx.send( return await ctx.send(
@ -148,7 +155,8 @@ class AudioTrivia(Trivia):
"The trivia list was parsed successfully, however it appears to be empty!" "The trivia list was parsed successfully, however it appears to be empty!"
) )
return return
settings = await self.conf.guild(ctx.guild).all()
settings = await self.config.guild(ctx.guild).all()
audiosettings = await self.audioconf.guild(ctx.guild).all() audiosettings = await self.audioconf.guild(ctx.guild).all()
config = trivia_dict.pop("CONFIG", None) config = trivia_dict.pop("CONFIG", None)
if config and settings["allow_override"]: if config and settings["allow_override"]:
@ -196,7 +204,7 @@ class AudioTrivia(Trivia):
with path.open(encoding="utf-8") as file: with path.open(encoding="utf-8") as file:
try: try:
dict_ = yaml.load(file) dict_ = yaml.load(file, Loader=yaml.SafeLoader)
except yaml.error.YAMLError as exc: except yaml.error.YAMLError as exc:
raise InvalidListError("YAML parsing failed.") from exc raise InvalidListError("YAML parsing failed.") from exc
else: else:

File diff suppressed because it is too large Load Diff

@ -1,106 +0,0 @@
AUTHOR: bobloy
https://www.youtube.com/watch?v=nfjiy-NX5b0:
- flashbang
https://www.youtube.com/watch?v=mJCE7s4W4IE:
- starting round
- round start
- start round
https://www.youtube.com/watch?v=XfLGi4cPu0Y:
- select team
- team select
https://www.youtube.com/watch?v=b6ScVgFs-DQ:
- desert eagle
- deagle
https://www.youtube.com/watch?v=JnHm-rn199Y:
- planted bomb
- bomb planted
- bomb plant
- plant bomb
https://www.youtube.com/watch?v=3wztV24tbVU:
- defusing bomb
- defuse bomb
- bomb defuse
- bomb defusing
https://www.youtube.com/watch?v=mpY9poBVje4:
- lobby
https://www.youtube.com/watch?v=zMT4ovCN7gk:
- usp-s
- usp s
- usps
https://www.youtube.com/watch?v=oI5Ww7y2aUQ:
- gut knife
https://www.youtube.com/watch?v=Dqmyxnx-OaQ:
- ak47
- ak 47
https://www.youtube.com/watch?v=Ny4hGdziZP4:
- hitmarker
- hit
- hitmaker
- marker
https://www.youtube.com/watch?v=vYUynDKM1Yw:
- awp
https://www.youtube.com/watch?v=52etXKmbQRM:
- butterfly knife
https://www.youtube.com/watch?v=99o4eyq0SzY:
- won round
- round won
- win round
- round win
https://www.youtube.com/watch?v=V5tv1ZzqI_U:
- lost round
- round lost
- lose round
- round loss
https://www.youtube.com/watch?v=1hI25OPdim0:
- flashbang toss
- toss flashbang
- throwing flashbang
- throw flashbang
- flashbang throwing
- flashbang throw
- tossing flashbang
- flashbang tossing
https://www.youtube.com/watch?v=oML0z2Aj_D4:
- firegrenade toss
- toss firegrenade
- throwing firegrenade
- throw firegrenade
- firegrenade throwing
- firegrenade throw
- tossing firegrenade
- firegrenade tossing
- fire grenade toss
- toss fire grenade
- throwing fire grenade
- throw fire grenade
- fire grenade throwing
- fire grenade throw
- tossing fire grenade
- fire grenade tossing
https://www.youtube.com/watch?v=9otQ9OLfaQc:
- grenade out
https://www.youtube.com/watch?v=tFA-8Vc32Kg:
- famas
https://www.youtube.com/watch?v=MdI1u8oXKZw:
- awp zoom
- zoom awp
- awp scope
- scope awp
https://www.youtube.com/watch?v=6NiZhX4h32Q:
- c4
https://www.youtube.com/watch?v=3N0NxsyWPiY:
- planting c4
- c4 planting
- plant c4
- c4 plant
https://www.youtube.com/watch?v=XLaJIXZ5QUc:
- awp
https://www.youtube.com/watch?v=DmuK9Wml88E:
- P90
https://www.youtube.com/watch?v=t1Ky_TbDXHY:
- smoke
https://www.youtube.com/watch?v=sJvdTbejDRY:
- kill bonus
https://www.youtube.com/watch?v=DYWi8qdvWCk:
- AK47
- AK 47

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,4 +0,0 @@
https://www.youtube.com/watch?v=hfyE220BsD0:
- holiday
https://www.youtube.com/watch?v=Hh3U9iPKeXQ:
- sultans of swing

@ -1,4 +0,0 @@
https://www.youtube.com/watch?v=Hi1kUdreiWk:
- Jinx
https://www.youtube.com/watch?v=PNYHFluhOGI:
- Teemo

@ -0,0 +1,125 @@
AUTHOR: Lazar
https://youtu.be/6OejNXrGkK0:
- Anaheim Ducks
- Anaheim
- Ducks
https://youtu.be/RbUxSPoU9Yg:
- Arizona Coyotes
- Arizona
- Coyotes
https://youtu.be/DsI0PgWADks:
- Boston Bruins
- Boston
- Bruins
https://youtu.be/hjFTd3MJOHc:
- Buffalo Sabres
- Buffalo
- Sabres
https://youtu.be/sn1PliBCRDY:
- Calgary Flames
- Calgary
- Flames
https://youtu.be/3exZm6Frd18:
- Carolina Hurricanes
- Carolina
- Hurricanes
https://youtu.be/sBeXPMkqR80:
- Chicago Blackhawks
- Chicago
- Blackhawks
https://youtu.be/MARxzs_vCPI:
- Colorado Avalanche
- Colorado
- Avalanche
https://youtu.be/6yYbQfOWw4k:
- Columbus Blue Jackets
- Columbus
- Blue Jackets
https://youtu.be/Af8_9NP5lyw:
- Dallas
- Stars
- Dallas Stars
https://youtu.be/JflfvLvY7ks:
- Detroit Red Wings
- Detroit
- Red wings
https://youtu.be/xc422k5Tcqc:
- Edmonton Oilers
- Edmonton
- Oilers
https://youtu.be/Dm1bjUB9HLE:
- Florida Panthers
- Florida
- Panthers
https://youtu.be/jSgd3aIepY4:
- Los Angeles Kings
- Los Angeles
- Kings
https://youtu.be/4Pj8hWPR9VI:
- Minnesota Wild
- Minnesota
- Wild
https://youtu.be/rRGlUFWEBMk:
- Montreal Canadiens
- Montreal
- Canadiens
https://youtu.be/fHTehdlMwWQ:
- Nashville Predators
- Nashville
- Predators
https://youtu.be/4q0eNg-AbrQ:
- New Jersey Devils
- New Jersey
- Devils
https://youtu.be/ZC514zGrL80:
- New York
- Islanders
- New York Islanders
https://youtu.be/Zzfks2A2n38:
- New York Rangers
- New York
- Rangers
https://youtu.be/fHlWxPRNVBc:
- Ottawa Senators
- Ottawa
- Senators
https://youtu.be/0LsXpMiVD1E:
- Philadelphia Flyers
- Philadelphia
- Flyers
https://youtu.be/Llw3adcNuzI:
- Pittsburgh Penguins
- Pittsburgh
- Penguins
https://youtu.be/NZqSBkmpbLw:
- San Jose Sharks
- San Jose
- Sharks
https://youtu.be/Q23TDOJsY1s:
- St. Louis Blues
- St. Louis
- Blues
https://youtu.be/bdhDXxM20iM:
- Tampa Bay Lightning
- Tampa Bay
- Lightning
https://youtu.be/2cyekaemZgs:
- Toronto Maple Leafs
- Toronto
- Maple Leafs
https://youtu.be/CPozN-ZHpAo:
- Vancouver
- Canucks
- Vancouver Canucks
https://youtu.be/zheGI316WXg:
- Vegas Golden Knights
- Vegas
- Golden Knights
https://youtu.be/BH_CC1RxtfU:
- Washington Capitals
- Washington
- Capitals
https://youtu.be/3gcahU_i9WE:
- Winnipeg Jets
- Winnipeg
- Jets

@ -10,11 +10,11 @@
"description": "Start an Audio Trivia game", "description": "Start an Audio Trivia game",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing Audio trivia!\n You **MUST** unload trivia to use this (`[p]unload trivia`)\n Then you can get started with `[p]load audiotrivia` and `[p]help AudioTrivia`", "install_msg": "Thank you for installing Audio trivia!\n You **MUST** unload trivia to use this (`[p]unload trivia`)\n Then you can get started with `[p]load audiotrivia` and `[p]help AudioTrivia`",
"requirements": [],
"short": "Start an Audio Trivia game", "short": "Start an Audio Trivia game",
"tags": [ "tags": [
"fox", "fox",
"bobloy", "bobloy",
"games" "games",
"audio"
] ]
} }

@ -116,7 +116,7 @@ class CCRole(Cog):
# Selfrole # Selfrole
await ctx.send( await ctx.send(
"Is this a targeted command?(yes//no)\nNo will make this a selfrole command" "Is this a targeted command?(yes/no)\nNo will make this a selfrole command"
) )
try: try:
@ -236,6 +236,7 @@ class CCRole(Cog):
await ctx.author.send(box(page)) await ctx.author.send(box(page))
await ctx.send("Command list DM'd") await ctx.send("Command list DM'd")
@commands.Cog.listener()
async def on_message(self, message): async def on_message(self, message):
if len(message.content) < 2 or message.guild is None: if len(message.content) < 2 or message.guild is None:
return return
@ -293,7 +294,7 @@ class CCRole(Cog):
if cmd["targeted"]: if cmd["targeted"]:
try: try:
target = discord.utils.get( target = discord.utils.get(
message.guild.members, mention=message.content.split()[1] message.guild.members, mention=message.content.split(maxsplit=1)[1]
) )
except IndexError: # .split() return list of len<2 except IndexError: # .split() return list of len<2
target = None target = None

@ -10,7 +10,6 @@
"description": "[Incomplete] Creates custom commands to adjust roles and send custom messages", "description": "[Incomplete] Creates custom commands to adjust roles and send custom messages",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing Custom Commands w/ Roles. Get started with `[p]load ccrole` and `[p]help CCRole`", "install_msg": "Thank you for installing Custom Commands w/ Roles. Get started with `[p]load ccrole` and `[p]help CCRole`",
"requirements": [],
"short": "[Incomplete] Creates commands that adjust roles", "short": "[Incomplete] Creates commands that adjust roles",
"tags": [ "tags": [
"fox", "fox",

@ -7,10 +7,10 @@ from redbot.core import Config
from redbot.core import commands from redbot.core import commands
from redbot.core.data_manager import cog_data_path from redbot.core.data_manager import cog_data_path
from chatter.chatterbot import ChatBot from .chatterbot import ChatBot
from chatter.chatterbot.comparisons import levenshtein_distance from .chatterbot.comparisons import levenshtein_distance
from chatter.chatterbot.response_selection import get_first_response from .chatterbot.response_selection import get_first_response
from chatter.chatterbot.trainers import ListTrainer from .chatterbot.trainers import ListTrainer
from typing import Any from typing import Any
Cog: Any = getattr(commands, "Cog", object) Cog: Any = getattr(commands, "Cog", object)
@ -27,7 +27,7 @@ class Chatter(Cog):
default_global = {} default_global = {}
default_guild = {"whitelist": None, "days": 1} default_guild = {"whitelist": None, "days": 1}
path: pathlib.Path = cog_data_path(self) path: pathlib.Path = cog_data_path(self)
data_path = path / ("database.sqlite3") data_path = path / "database.sqlite3"
self.chatbot = ChatBot( self.chatbot = ChatBot(
"ChatterBot", "ChatterBot",
@ -80,7 +80,7 @@ class Chatter(Cog):
send_time = None send_time = None
try: try:
async for message in channel.history(limit=None, reverse=True, after=after): async for message in channel.history(limit=None, after=after):
# if message.author.bot: # Skip bot messages # if message.author.bot: # Skip bot messages
# continue # continue
if new_message(message, send_time, out[i]): if new_message(message, send_time, out[i]):
@ -152,6 +152,10 @@ class Chatter(Cog):
Trains the bot based on language in this guild Trains the bot based on language in this guild
""" """
await ctx.send("Warning: The cog may use significant RAM or CPU if trained on large data sets.\n"
"Additionally, large sets will use more disk space to save the trained data.\n\n"
"If you experience issues, clear your trained data and train again on a smaller scope.")
conversation = await self._get_conversation(ctx, channel) conversation = await self._get_conversation(ctx, channel)
if not conversation: if not conversation:
@ -176,20 +180,21 @@ class Chatter(Cog):
else: else:
await ctx.send("Error occurred :(") await ctx.send("Error occurred :(")
@commands.Cog.listener()
async def on_message(self, message: discord.Message): async def on_message(self, message: discord.Message):
""" """
Credit to https://github.com/Twentysix26/26-Cogs/blob/master/cleverbot/cleverbot.py Credit to https://github.com/Twentysix26/26-Cogs/blob/master/cleverbot/cleverbot.py
for on_message recognition of @bot for on_message recognition of @bot
""" """
author = message.author author = message.author
try:
guild: discord.Guild = message.guild guild: discord.Guild = message.guild
except AttributeError: # Not a guild message
return
channel: discord.TextChannel = message.channel channel: discord.TextChannel = message.channel
if author.id != self.bot.user.id: if author.id != self.bot.user.id:
if guild is None:
to_strip = "@" + channel.me.display_name + " "
else:
to_strip = "@" + guild.me.display_name + " " to_strip = "@" + guild.me.display_name + " "
text = message.clean_content text = message.clean_content
if not text.startswith(to_strip): if not text.startswith(to_strip):

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import logging import logging
from chatter.chatterbot import utils from . import utils
class ChatBot(object): class ChatBot(object):
@ -11,7 +11,7 @@ class ChatBot(object):
""" """
def __init__(self, name, **kwargs): def __init__(self, name, **kwargs):
from chatter.chatterbot.logic import MultiLogicAdapter from .logic import MultiLogicAdapter
self.name = name self.name = name
kwargs['name'] = name kwargs['name'] = name
@ -136,7 +136,7 @@ class ChatBot(object):
""" """
Learn that the statement provided is a valid response. Learn that the statement provided is a valid response.
""" """
from chatter.chatterbot.conversation import Response from .conversation import Response
if previous_statement: if previous_statement:
statement.add_response( statement.add_response(

@ -92,7 +92,7 @@ class SynsetDistance(Comparator):
""" """
Download required NLTK corpora if they have not already been downloaded. Download required NLTK corpora if they have not already been downloaded.
""" """
from chatter.chatterbot.utils import nltk_download_corpus from .utils import nltk_download_corpus
nltk_download_corpus('corpora/wordnet') nltk_download_corpus('corpora/wordnet')
@ -100,7 +100,7 @@ class SynsetDistance(Comparator):
""" """
Download required NLTK corpora if they have not already been downloaded. Download required NLTK corpora if they have not already been downloaded.
""" """
from chatter.chatterbot.utils import nltk_download_corpus from .utils import nltk_download_corpus
nltk_download_corpus('tokenizers/punkt') nltk_download_corpus('tokenizers/punkt')
@ -108,7 +108,7 @@ class SynsetDistance(Comparator):
""" """
Download required NLTK corpora if they have not already been downloaded. Download required NLTK corpora if they have not already been downloaded.
""" """
from chatter.chatterbot.utils import nltk_download_corpus from .utils import nltk_download_corpus
nltk_download_corpus('corpora/stopwords') nltk_download_corpus('corpora/stopwords')
@ -124,7 +124,7 @@ class SynsetDistance(Comparator):
""" """
from nltk.corpus import wordnet from nltk.corpus import wordnet
from nltk import word_tokenize from nltk import word_tokenize
from chatter.chatterbot import utils from . import utils
import itertools import itertools
tokens1 = word_tokenize(statement.text.lower()) tokens1 = word_tokenize(statement.text.lower())
@ -177,7 +177,7 @@ class SentimentComparison(Comparator):
Download the NLTK vader lexicon for sentiment analysis Download the NLTK vader lexicon for sentiment analysis
that is required for this algorithm to run. that is required for this algorithm to run.
""" """
from chatter.chatterbot.utils import nltk_download_corpus from .utils import nltk_download_corpus
nltk_download_corpus('sentiment/vader_lexicon') nltk_download_corpus('sentiment/vader_lexicon')
@ -252,7 +252,7 @@ class JaccardSimilarity(Comparator):
Download the NLTK wordnet corpora that is required for this algorithm Download the NLTK wordnet corpora that is required for this algorithm
to run only if the corpora has not already been downloaded. to run only if the corpora has not already been downloaded.
""" """
from chatter.chatterbot.utils import nltk_download_corpus from .utils import nltk_download_corpus
nltk_download_corpus('corpora/wordnet') nltk_download_corpus('corpora/wordnet')

@ -3,9 +3,9 @@ from sqlalchemy.ext.declarative import declared_attr, declarative_base
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.sql import func from sqlalchemy.sql import func
from chatter.chatterbot.constants import TAG_NAME_MAX_LENGTH, STATEMENT_TEXT_MAX_LENGTH from ...constants import TAG_NAME_MAX_LENGTH, STATEMENT_TEXT_MAX_LENGTH
from chatter.chatterbot.conversation import StatementMixin from ...conversation import StatementMixin
from chatter.chatterbot.ext.sqlalchemy_app.types import UnicodeString from .types import UnicodeString
class ModelBase(object): class ModelBase(object):
@ -72,8 +72,8 @@ class Statement(Base, StatementMixin):
return [tag.name for tag in self.tags] return [tag.name for tag in self.tags]
def get_statement(self): def get_statement(self):
from chatter.chatterbot.conversation import Statement as StatementObject from ...conversation import Statement as StatementObject
from chatter.chatterbot.conversation import Response as ResponseObject from ...conversation import Response as ResponseObject
statement = StatementObject( statement = StatementObject(
self.text, self.text,

@ -2,8 +2,8 @@ from __future__ import unicode_literals
from time import sleep from time import sleep
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.input import InputAdapter from . import InputAdapter
class Gitter(InputAdapter): class Gitter(InputAdapter):

@ -2,8 +2,8 @@ from __future__ import unicode_literals
from time import sleep from time import sleep
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.input import InputAdapter from . import InputAdapter
class HipChat(InputAdapter): class HipChat(InputAdapter):

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.adapters import Adapter from ..adapters import Adapter
class InputAdapter(Adapter): class InputAdapter(Adapter):

@ -2,8 +2,8 @@ from __future__ import unicode_literals
import datetime import datetime
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.input import InputAdapter from . import InputAdapter
class Mailgun(InputAdapter): class Mailgun(InputAdapter):

@ -2,8 +2,8 @@ from __future__ import unicode_literals
from time import sleep from time import sleep
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.input import InputAdapter from . import InputAdapter
class Microsoft(InputAdapter): class Microsoft(InputAdapter):

@ -1,8 +1,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.input import InputAdapter from . import InputAdapter
from chatter.chatterbot.utils import input_function from ..utils import input_function
class TerminalAdapter(InputAdapter): class TerminalAdapter(InputAdapter):

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.input import InputAdapter from . import InputAdapter
class VariableInputTypeAdapter(InputAdapter): class VariableInputTypeAdapter(InputAdapter):

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.logic import LogicAdapter from . import LogicAdapter
class BestMatch(LogicAdapter): class BestMatch(LogicAdapter):

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.adapters import Adapter from ..adapters import Adapter
from chatter.chatterbot.utils import import_module from ..utils import import_module
class LogicAdapter(Adapter): class LogicAdapter(Adapter):
@ -18,8 +18,8 @@ class LogicAdapter(Adapter):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(LogicAdapter, self).__init__(**kwargs) super(LogicAdapter, self).__init__(**kwargs)
from chatter.chatterbot.comparisons import levenshtein_distance from ..comparisons import levenshtein_distance
from chatter.chatterbot.response_selection import get_first_response from ..response_selection import get_first_response
# Import string module parameters # Import string module parameters
if 'statement_comparison_function' in kwargs: if 'statement_comparison_function' in kwargs:

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.logic import BestMatch from . import BestMatch
class LowConfidenceAdapter(BestMatch): class LowConfidenceAdapter(BestMatch):

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
from chatter.chatterbot.logic import LogicAdapter from . import LogicAdapter
class MathematicalEvaluation(LogicAdapter): class MathematicalEvaluation(LogicAdapter):

@ -2,8 +2,8 @@ from __future__ import unicode_literals
from collections import Counter from collections import Counter
from chatter.chatterbot import utils from .. import utils
from chatter.chatterbot.logic import LogicAdapter from . import LogicAdapter
class MultiLogicAdapter(LogicAdapter): class MultiLogicAdapter(LogicAdapter):

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.logic import LogicAdapter from . import LogicAdapter
class NoKnowledgeAdapter(LogicAdapter): class NoKnowledgeAdapter(LogicAdapter):

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.logic import LogicAdapter from . import LogicAdapter
class SpecificResponseAdapter(LogicAdapter): class SpecificResponseAdapter(LogicAdapter):
@ -16,7 +16,7 @@ class SpecificResponseAdapter(LogicAdapter):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(SpecificResponseAdapter, self).__init__(**kwargs) super(SpecificResponseAdapter, self).__init__(**kwargs)
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
self.input_text = kwargs.get('input_text') self.input_text = kwargs.get('input_text')

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from datetime import datetime from datetime import datetime
from chatter.chatterbot.logic import LogicAdapter from . import LogicAdapter
class TimeLogicAdapter(LogicAdapter): class TimeLogicAdapter(LogicAdapter):
@ -81,7 +81,7 @@ class TimeLogicAdapter(LogicAdapter):
return features return features
def process(self, statement): def process(self, statement):
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
now = datetime.now() now = datetime.now()

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.output import OutputAdapter from . import OutputAdapter
class Gitter(OutputAdapter): class Gitter(OutputAdapter):

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import json import json
from chatter.chatterbot.output import OutputAdapter from . import OutputAdapter
class HipChat(OutputAdapter): class HipChat(OutputAdapter):

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.output import OutputAdapter from . import OutputAdapter
class Mailgun(OutputAdapter): class Mailgun(OutputAdapter):

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import json import json
from chatter.chatterbot.output import OutputAdapter from . import OutputAdapter
class Microsoft(OutputAdapter): class Microsoft(OutputAdapter):

@ -1,4 +1,4 @@
from chatter.chatterbot.adapters import Adapter from ..adapters import Adapter
class OutputAdapter(Adapter): class OutputAdapter(Adapter):

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from chatter.chatterbot.output import OutputAdapter from . import OutputAdapter
class TerminalAdapter(OutputAdapter): class TerminalAdapter(OutputAdapter):

@ -1,4 +1,4 @@
from chatter.chatterbot.storage import StorageAdapter from . import StorageAdapter
class Query(object): class Query(object):
@ -119,7 +119,7 @@ class MongoDatabaseAdapter(StorageAdapter):
""" """
Return the class for the statement model. Return the class for the statement model.
""" """
from chatter.chatterbot.conversation import Statement from ..conversation import Statement
# Create a storage-aware statement # Create a storage-aware statement
statement = Statement statement = Statement
@ -131,7 +131,7 @@ class MongoDatabaseAdapter(StorageAdapter):
""" """
Return the class for the response model. Return the class for the response model.
""" """
from chatter.chatterbot.conversation import Response from ..conversation import Response
# Create a storage-aware response # Create a storage-aware response
response = Response response = Response

@ -1,8 +1,8 @@
from chatter.chatterbot.storage import StorageAdapter from . import StorageAdapter
def get_response_table(response): def get_response_table(response):
from chatter.chatterbot.ext.sqlalchemy_app.models import Response from ..ext.sqlalchemy_app.models import Response
return Response(text=response.text, occurrence=response.occurrence) return Response(text=response.text, occurrence=response.occurrence)
@ -86,28 +86,28 @@ class SQLStorageAdapter(StorageAdapter):
""" """
Return the statement model. Return the statement model.
""" """
from chatter.chatterbot.ext.sqlalchemy_app.models import Statement from ..ext.sqlalchemy_app.models import Statement
return Statement return Statement
def get_response_model(self): def get_response_model(self):
""" """
Return the response model. Return the response model.
""" """
from chatter.chatterbot.ext.sqlalchemy_app.models import Response from ..ext.sqlalchemy_app.models import Response
return Response return Response
def get_conversation_model(self): def get_conversation_model(self):
""" """
Return the conversation model. Return the conversation model.
""" """
from chatter.chatterbot.ext.sqlalchemy_app.models import Conversation from ..ext.sqlalchemy_app.models import Conversation
return Conversation return Conversation
def get_tag_model(self): def get_tag_model(self):
""" """
Return the conversation model. Return the conversation model.
""" """
from chatter.chatterbot.ext.sqlalchemy_app.models import Tag from ..ext.sqlalchemy_app.models import Tag
return Tag return Tag
def count(self): def count(self):
@ -379,14 +379,14 @@ class SQLStorageAdapter(StorageAdapter):
""" """
Drop the database attached to a given adapter. Drop the database attached to a given adapter.
""" """
from chatter.chatterbot.ext.sqlalchemy_app.models import Base from ..ext.sqlalchemy_app.models import Base
Base.metadata.drop_all(self.engine) Base.metadata.drop_all(self.engine)
def create(self): def create(self):
""" """
Populate the database with the tables. Populate the database with the tables.
""" """
from chatter.chatterbot.ext.sqlalchemy_app.models import Base from ..ext.sqlalchemy_app.models import Base
Base.metadata.create_all(self.engine) Base.metadata.create_all(self.engine)
def _session_finish(self, session, statement_text=None): def _session_finish(self, session, statement_text=None):

@ -2,8 +2,8 @@ import logging
import os import os
import sys import sys
from chatter.chatterbot import utils from . import utils
from chatter.chatterbot.conversation import Statement, Response from .conversation import Statement, Response
class Trainer(object): class Trainer(object):
@ -127,7 +127,7 @@ class ChatterBotCorpusTrainer(Trainer):
def __init__(self, storage, **kwargs): def __init__(self, storage, **kwargs):
super(ChatterBotCorpusTrainer, self).__init__(storage, **kwargs) super(ChatterBotCorpusTrainer, self).__init__(storage, **kwargs)
from chatter.chatterbot.corpus import Corpus from .corpus import Corpus
self.corpus = Corpus() self.corpus = Corpus()
@ -391,10 +391,8 @@ class UbuntuCorpusTrainer(Trainer):
'**', '**', '*.tsv' '**', '**', '*.tsv'
) )
file_kwargs = {}
# Specify the encoding in Python versions 3 and up # Specify the encoding in Python versions 3 and up
file_kwargs['encoding'] = 'utf-8' file_kwargs = {'encoding': 'utf-8'}
# WARNING: This might fail to read a unicode corpus file in Python 2.x # WARNING: This might fail to read a unicode corpus file in Python 2.x
for file in glob.iglob(extracted_corpus_path): for file in glob.iglob(extracted_corpus_path):

@ -11,8 +11,16 @@ def import_module(dotted_path):
import importlib import importlib
module_parts = dotted_path.split('.') module_parts = dotted_path.split('.')
if module_parts[:2] == ["chatter", "chatterbot"]:
# An import path starting with chatter.chatterbot means it comes from this
# package, and should be imported relatively.
package = __package__
module_parts = module_parts[2:]
module_parts[0] = "." + module_parts[0]
else:
package = None
module_path = '.'.join(module_parts[:-1]) module_path = '.'.join(module_parts[:-1])
module = importlib.import_module(module_path) module = importlib.import_module(module_path, package=package)
return getattr(module, module_parts[-1]) return getattr(module, module_parts[-1])
@ -46,7 +54,7 @@ def validate_adapter_class(validate_class, adapter_class):
:raises: Adapter.InvalidAdapterTypeException :raises: Adapter.InvalidAdapterTypeException
""" """
from chatter.chatterbot.adapters import Adapter from .adapters import Adapter
# If a dictionary was passed in, check if it has an import_path attribute # If a dictionary was passed in, check if it has an import_path attribute
if isinstance(validate_class, dict): if isinstance(validate_class, dict):
@ -128,7 +136,7 @@ def remove_stopwords(tokens, language):
Stop words are words like "is, the, a, ..." Stop words are words like "is, the, a, ..."
Be sure to download the required NLTK corpus before calling this function: Be sure to download the required NLTK corpus before calling this function:
- from chatter.chatterbot.utils import nltk_download_corpus - from chatterbot.utils import nltk_download_corpus
- nltk_download_corpus('corpora/stopwords') - nltk_download_corpus('corpora/stopwords')
""" """
from nltk.corpus import stopwords from nltk.corpus import stopwords

@ -75,7 +75,7 @@ class CogLint(Cog):
for c in code_blocks: for c in code_blocks:
is_python, code = c.split(None, 1) is_python, code = c.split(None, 1)
is_python = is_python.lower() == "python" is_python = is_python.lower() in ["python", "py"]
if is_python: # Then we're in business if is_python: # Then we're in business
linted, errors = await self.lint_code(code) linted, errors = await self.lint_code(code)
linted = linted.getvalue() linted = linted.getvalue()

@ -10,7 +10,7 @@
"description": "Lint python code posted in chat", "description": "Lint python code posted in chat",
"hidden": true, "hidden": true,
"install_msg": "Thank you for installing CogLint! Get started with `[p]load coglint` and `[p]help CogLint`", "install_msg": "Thank you for installing CogLint! Get started with `[p]load coglint` and `[p]help CogLint`",
"requirements": [], "requirements": ["pylint"],
"short": "Python cog linter", "short": "Python cog linter",
"tags": [ "tags": [
"bobloy", "bobloy",

@ -28,7 +28,7 @@ class Dad(Cog):
self.bot = bot self.bot = bot
self.config = Config.get_conf(self, identifier=6897100, force_registration=True) self.config = Config.get_conf(self, identifier=6897100, force_registration=True)
default_guild = {"enabled": False, "nickname": False, "cooldown": 240} default_guild = {"enabled": True, "nickname": False, "cooldown": 240}
self.config.register_guild(**default_guild) self.config.register_guild(**default_guild)
@ -76,6 +76,7 @@ class Dad(Cog):
await self.config.guild(ctx.guild).cooldown.set(cooldown) await self.config.guild(ctx.guild).cooldown.set(cooldown)
await ctx.send("Dad joke cooldown is now set to {}".format(cooldown)) await ctx.send("Dad joke cooldown is now set to {}".format(cooldown))
@commands.Cog.listener()
async def on_message(self, message: discord.Message): async def on_message(self, message: discord.Message):
guild: discord.Guild = message.guild guild: discord.Guild = message.guild
if guild is None: if guild is None:
@ -104,8 +105,10 @@ class Dad(Cog):
out = message.author.mention out = message.author.mention
else: else:
out = lower[4:] out = lower[4:]
try:
await message.channel.send("Hi {}, I'm {}!".format(out, guild.me.display_name)) await message.channel.send("Hi {}, I'm {}!".format(out, guild.me.display_name))
except discord.HTTPException:
return
self.cooldown[guild.id] = datetime.now() + timedelta( self.cooldown[guild.id] = datetime.now() + timedelta(
seconds=(await guild_config.cooldown()) seconds=(await guild_config.cooldown())

@ -10,7 +10,6 @@
"description": "Tell dad jokes and give out bad nicknames", "description": "Tell dad jokes and give out bad nicknames",
"hidden": true, "hidden": true,
"install_msg": "Thank you for installing Dad. Get started with `[p]load dad`, then `[p]help Dad`", "install_msg": "Thank you for installing Dad. Get started with `[p]load dad`, then `[p]help Dad`",
"requirements": [],
"short": "Dad joke bot", "short": "Dad joke bot",
"tags": [ "tags": [
"bobloy", "bobloy",

@ -9,8 +9,7 @@ Cog: Any = getattr(commands, "Cog", object)
class ExclusiveRole(Cog): class ExclusiveRole(Cog):
""" """
Custom commands Create roles that prevent all other roles from being added
Creates commands used to display text and adjust roles
""" """
def __init__(self, bot): def __init__(self, bot):
@ -20,7 +19,8 @@ class ExclusiveRole(Cog):
self.config.register_guild(**default_guild) self.config.register_guild(**default_guild)
@commands.group(no_pm=True, aliases=["exclusiverole"]) @commands.guild_only()
@commands.group(aliases=["exclusiverole"])
async def exclusive(self, ctx): async def exclusive(self, ctx):
"""Base command for managing exclusive roles""" """Base command for managing exclusive roles"""
@ -55,6 +55,21 @@ class ExclusiveRole(Cog):
await ctx.send("Exclusive role removed") await ctx.send("Exclusive role removed")
@exclusive.command(name="list")
@checks.mod_or_permissions(administrator=True)
async def exclusive_list(self, ctx):
"""List current exclusive roles"""
role_list = await self.config.guild(ctx.guild).role_list()
guild: discord.Guild = ctx.guild
role_list = [guild.get_role(role_id) for role_id in role_list]
out = "**Exclusive roles**\n\n"
for role in role_list:
out += "{}\n".format(role)
await ctx.send(out)
async def check_guild(self, guild: discord.Guild): async def check_guild(self, guild: discord.Guild):
role_set = set(await self.config.guild(guild).role_list()) role_set = set(await self.config.guild(guild).role_list())
for member in guild.members: for member in guild.members:
@ -74,6 +89,7 @@ class ExclusiveRole(Cog):
to_remove = [discord.utils.get(member.guild.roles, id=id) for id in to_remove] to_remove = [discord.utils.get(member.guild.roles, id=id) for id in to_remove]
await member.remove_roles(*to_remove, reason="Exclusive roles") await member.remove_roles(*to_remove, reason="Exclusive roles")
@commands.Cog.listener()
async def on_member_update(self, before: discord.Member, after: discord.Member): async def on_member_update(self, before: discord.Member, after: discord.Member):
if before.roles == after.roles: if before.roles == after.roles:
return return

@ -10,7 +10,6 @@
"description": "Assign roles to be exclusive, preventing other roles from being added", "description": "Assign roles to be exclusive, preventing other roles from being added",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing ExclusiveRole. Get started with `[p]load exclusiverole` and `[p]help ExclusiveRole`", "install_msg": "Thank you for installing ExclusiveRole. Get started with `[p]load exclusiverole` and `[p]help ExclusiveRole`",
"requirements": [],
"short": "Set roles to be exclusive", "short": "Set roles to be exclusive",
"tags": [ "tags": [
"fox", "fox",

@ -72,25 +72,22 @@ class Flag(Cog):
"""Flag a member""" """Flag a member"""
guild = ctx.guild guild = ctx.guild
await self._check_flags(guild) await self._check_flags(guild)
# clashroyale = self.bot.get_cog('clashroyale')
# if clashroyale is None:
# await ctx.send("Requires clashroyale cog installed")
# return
flag = self._flag_template() flag = self._flag_template()
expiredate = date.today() expire_date = date.today() + timedelta(days=await self.config.guild(guild).days())
expiredate += timedelta(days=await self.config.guild(guild).days())
flag["reason"] = reason flag["reason"] = reason
flag["expireyear"] = expiredate.year flag["expireyear"] = expire_date.year
flag["expiremonth"] = expiredate.month flag["expiremonth"] = expire_date.month
flag["expireday"] = expiredate.day flag["expireday"] = expire_date.day
# flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[]) # flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[])
# flags.append(flag) # flags.append(flag)
# await self.config.guild(guild).flags.set_raw(str(member.id), value=flags) # await self.config.guild(guild).flags.set_raw(str(member.id), value=flags)
async with self.config.guild(guild).flags() as flags: async with self.config.guild(guild).flags() as flags:
if str(member.id) not in flags:
flags[str(member.id)] = []
flags[str(member.id)].append(flag) flags[str(member.id)].append(flag)
outembed = await self._list_flags(member) outembed = await self._list_flags(member)
@ -98,7 +95,10 @@ class Flag(Cog):
if outembed: if outembed:
await ctx.send(embed=outembed) await ctx.send(embed=outembed)
if await self.config.guild(guild).dm(): if await self.config.guild(guild).dm():
try:
await member.send(embed=outembed) await member.send(embed=outembed)
except discord.Forbidden:
await ctx.send("DM-ing user failed")
else: else:
await ctx.send("This member has no flags.. somehow..") await ctx.send("This member has no flags.. somehow..")
@ -173,7 +173,7 @@ class Flag(Cog):
async def _check_flags(self, guild: discord.Guild): async def _check_flags(self, guild: discord.Guild):
"""Updates and removes expired flags""" """Updates and removes expired flags"""
flag_data = await self.config.guild(guild).flags() flag_data = await self.config.guild(guild).flags()
flag_d = {} # flag_d = {}
for memberid, flags in flag_data.items(): for memberid, flags in flag_data.items():
# for member in guild.members: # for member in guild.members:
# flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[]) # flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[])

@ -10,7 +10,6 @@
"description": "Add expiring flags on members to track warnings or incidents", "description": "Add expiring flags on members to track warnings or incidents",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing Flag! Get started with `[p]load flag` and `[p]help Flag`", "install_msg": "Thank you for installing Flag! Get started with `[p]load flag` and `[p]help Flag`",
"requirements": [],
"short": "Add expiring flags to members", "short": "Add expiring flags to members",
"tags": [ "tags": [
"bobloy", "bobloy",

@ -1,12 +1,18 @@
from discord.utils import get from discord.utils import get
from redbot import VersionInfo, version_info
from redbot.core import Config, checks, commands from redbot.core import Config, checks, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from typing import Any from typing import Any
import asyncio
Cog: Any = getattr(commands, "Cog", object) Cog: Any = getattr(commands, "Cog", object)
if version_info < VersionInfo.from_str("3.4.0"):
SANITIZE_ROLES_KWARG = {}
else:
SANITIZE_ROLES_KWARG = {"sanitize_roles": False}
class ForceMention(Cog): class ForceMention(Cog):
""" """
@ -35,7 +41,8 @@ class ForceMention(Cog):
if not role_obj.mentionable: if not role_obj.mentionable:
await role_obj.edit(mentionable=True) await role_obj.edit(mentionable=True)
await ctx.send("{}\n{}".format(role_obj.mention, message)) await ctx.send("{}\n{}".format(role_obj.mention, message), **SANITIZE_ROLES_KWARG)
await asyncio.sleep(5)
await role_obj.edit(mentionable=False) await role_obj.edit(mentionable=False)
else: else:
await ctx.send("{}\n{}".format(role_obj.mention, message)) await ctx.send("{}\n{}".format(role_obj.mention, message), **SANITIZE_ROLES_KWARG)

@ -10,7 +10,6 @@
"description": "Mentions roles that are unmentionable", "description": "Mentions roles that are unmentionable",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing ForceMention! Get started with `[p]load forcemention`, then `[p]forcemention`", "install_msg": "Thank you for installing ForceMention! Get started with `[p]load forcemention`, then `[p]forcemention`",
"requirements": [],
"short": "Mention unmentionables", "short": "Mention unmentionables",
"tags": [ "tags": [
"bobloy", "bobloy",

@ -4,6 +4,6 @@ from redbot.core import data_manager
def setup(bot): def setup(bot):
n = Hangman(bot) n = Hangman(bot)
data_manager.load_bundled_data(n, __file__) data_manager.bundled_data_path(n)
bot.add_cog(n) bot.add_cog(n)
bot.add_listener(n.on_react, "on_reaction_add") bot.add_listener(n.on_react, "on_reaction_add")

@ -3,7 +3,7 @@ from random import randint
import discord import discord
from redbot.core import Config, checks, commands from redbot.core import Config, checks, commands
from redbot.core.data_manager import cog_data_path from redbot.core.data_manager import bundled_data_path
from typing import Any from typing import Any
Cog: Any = getattr(commands, "Cog", object) Cog: Any = getattr(commands, "Cog", object)
@ -31,9 +31,11 @@ class Hangman(Cog):
"answer": "", "answer": "",
} }
) )
self.path = str(cog_data_path(self)).replace("\\", "/") # self.path = str(cog_data_path(self)).replace("\\", "/")
self.answer_path = self.path + "/bundled_data/hanganswers.txt" # self.answer_path = self.path + "/bundled_data/hanganswers.txt"
self.answer_path = bundled_data_path(self) / "hanganswers.txt"
self.winbool = defaultdict(lambda: False) self.winbool = defaultdict(lambda: False)
@ -137,21 +139,25 @@ class Hangman(Cog):
HANGMAN""", HANGMAN""",
) )
@commands.group(aliases=["sethang"], pass_context=True) @commands.group(aliases=["sethang"])
@checks.mod_or_permissions(administrator=True) @checks.mod_or_permissions(administrator=True)
async def hangset(self, ctx): async def hangset(self, ctx):
"""Adjust hangman settings""" """Adjust hangman settings"""
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
pass pass
@hangset.command(pass_context=True) @hangset.command()
async def face(self, ctx: commands.Context, theface): async def face(self, ctx: commands.Context, theface):
"""Set the face of the hangman""" """Set the face of the hangman"""
message = ctx.message message = ctx.message
# Borrowing FlapJack's emoji validation # Borrowing FlapJack's emoji validation
# (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py) # (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py)
if theface[:2] == "<:": if theface[:2] == "<:":
theface = [r for r in self.bot.emojis if r.id == theface.split(":")[2][:-1]][0] theface = self.bot.get_emoji(int(theface.split(":")[2][:-1]))
if theface is None:
await ctx.send("I could not find that emoji")
return
try: try:
# Use the face as reaction to see if it's valid (THANKS FLAPJACK <3) # Use the face as reaction to see if it's valid (THANKS FLAPJACK <3)
@ -160,11 +166,11 @@ class Hangman(Cog):
await ctx.send("That's not an emoji I recognize.") await ctx.send("That's not an emoji I recognize.")
return return
await self.config.guild(ctx.guild).theface.set(theface) await self.config.guild(ctx.guild).theface.set(str(theface))
await self._update_hanglist() await self._update_hanglist()
await ctx.send("Face has been updated!") await ctx.send("Face has been updated!")
@hangset.command(pass_context=True) @hangset.command()
async def toggleemoji(self, ctx: commands.Context): async def toggleemoji(self, ctx: commands.Context):
"""Toggles whether to automatically react with the alphabet""" """Toggles whether to automatically react with the alphabet"""
@ -172,7 +178,7 @@ class Hangman(Cog):
await self.config.guild(ctx.guild).emojis.set(not current) await self.config.guild(ctx.guild).emojis.set(not current)
await ctx.send("Emoji Letter reactions have been set to {}".format(not current)) await ctx.send("Emoji Letter reactions have been set to {}".format(not current))
@commands.command(aliases=["hang"], pass_context=True) @commands.command(aliases=["hang"])
async def hangman(self, ctx, guess: str = None): async def hangman(self, ctx, guess: str = None):
"""Play a game of hangman against the bot!""" """Play a game of hangman against the bot!"""
if guess is None: if guess is None:
@ -270,6 +276,7 @@ class Hangman(Cog):
await self._reprintgame(message) await self._reprintgame(message)
@commands.Cog.listener()
async def on_react(self, reaction, user): async def on_react(self, reaction, user):
""" Thanks to flapjack reactpoll for guidelines """ Thanks to flapjack reactpoll for guidelines
https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py""" https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py"""
@ -339,11 +346,14 @@ class Hangman(Cog):
await message.add_reaction(self.navigate[0]) await message.add_reaction(self.navigate[0])
def _make_say(self, guild): async def _make_say(self, guild):
c_say = "Guess this: " + str(self._hideanswer(guild)) + "\n" c_say = "Guess this: " + str(self._hideanswer(guild)) + "\n"
c_say += "Used Letters: " + str(self._guesslist(guild)) + "\n" c_say += "Used Letters: " + str(self._guesslist(guild)) + "\n"
c_say += self.hanglist[guild][self.the_data[guild]["hangman"]] + "\n" c_say += self.hanglist[guild][self.the_data[guild]["hangman"]] + "\n"
c_say += self.navigate[0] + " for A-M, " + self.navigate[-1] + " for N-Z" if await self.config.guild(guild).emojis():
c_say += "{} for A-M, {} for N-Z".format(self.navigate[0], self.navigate[-1])
else:
c_say += "React with {} - {} to guess".format(self.letters[0], self.letters[-1])
return c_say return c_say
@ -351,7 +361,7 @@ class Hangman(Cog):
if message.guild not in self.hanglist: if message.guild not in self.hanglist:
await self._update_hanglist() await self._update_hanglist()
c_say = self._make_say(message.guild) c_say = await self._make_say(message.guild)
await message.edit(content=c_say) await message.edit(content=c_say)
self.the_data[message.guild]["trackmessage"] = message.id self.the_data[message.guild]["trackmessage"] = message.id
@ -363,7 +373,7 @@ class Hangman(Cog):
if channel.guild not in self.hanglist: if channel.guild not in self.hanglist:
await self._update_hanglist() await self._update_hanglist()
c_say = self._make_say(channel.guild) c_say = await self._make_say(channel.guild)
message = await channel.send(c_say) message = await channel.send(c_say)

@ -0,0 +1,5 @@
from .infochannel import InfoChannel
def setup(bot):
bot.add_cog(InfoChannel(bot))

@ -0,0 +1,18 @@
{
"author": [
"Bobloy"
],
"bot_version": [
3,
0,
0
],
"description": "Create a channel with updating server info",
"hidden": false,
"install_msg": "Thank you for installing InfoChannel. Get started with `[p]load infochannel`, then `[p]help InfoChannel`",
"short": "Updating server info channel",
"tags": [
"bobloy",
"utils"
]
}

@ -0,0 +1,215 @@
from typing import Any
import discord
from redbot.core import Config, commands, checks
from redbot.core.bot import Red
Cog: Any = getattr(commands, "Cog", object)
listener = getattr(commands.Cog, "listener", None) # Trusty + Sinbad
if listener is None:
def listener(name=None):
return lambda x: x
class InfoChannel(Cog):
"""
Create a channel with updating server info
Less important information about the cog
"""
def __init__(self, bot: Red):
self.bot = bot
self.config = Config.get_conf(
self, identifier=731101021116710497110110101108, force_registration=True
)
default_guild = {
"channel_id": None,
"botchannel_id": None,
"onlinechannel_id": None,
"member_count": True,
"bot_count": False,
"online_count": False,
}
self.config.register_guild(**default_guild)
@commands.command()
@checks.admin()
async def infochannel(self, ctx: commands.Context):
"""
Toggle info channel for this server
"""
def check(m):
return (
m.content.upper() in ["Y", "YES", "N", "NO"]
and m.channel == ctx.channel
and m.author == ctx.author
)
guild: discord.Guild = ctx.guild
channel_id = await self.config.guild(guild).channel_id()
if channel_id is not None:
channel: discord.VoiceChannel = guild.get_channel(channel_id)
else:
channel: discord.VoiceChannel = None
if channel_id is not None and channel is None:
await ctx.send("Info channel has been deleted, recreate it?")
elif channel_id is None:
await ctx.send("Enable info channel on this server?")
else:
await ctx.send("Do you wish to delete current info channels?")
msg = await self.bot.wait_for("message", check=check)
if msg.content.upper() in ["N", "NO"]:
await ctx.send("Cancelled")
return
if channel is None:
await self.make_infochannel(guild)
else:
await self.delete_infochannel(guild, channel)
if not await ctx.tick():
await ctx.send("Done!")
@commands.group()
@checks.admin()
async def infochannelset(self, ctx: commands.Context):
"""
Toggle different types of infochannels
"""
@infochannelset.command(name="botcount")
async def _infochannelset_botcount(self, ctx: commands.Context, enabled: bool = None):
"""
Toggle an infochannel that shows the amount of bots in the server
"""
guild = ctx.guild
if enabled is None:
enabled = not await self.config.guild(guild).bot_count()
await self.config.guild(guild).bot_count.set(enabled)
if enabled:
await ctx.send("InfoChannel for bot count has been enabled.")
else:
await ctx.send("InfoChannel for bot count has been disabled.")
@infochannelset.command(name="onlinecount")
async def _infochannelset_onlinecount(self, ctx: commands.Context, enabled: bool = None):
"""
Toggle an infochannel that shows the amount of online users in the server
"""
guild = ctx.guild
if enabled is None:
enabled = not await self.config.guild(guild).online_count()
await self.config.guild(guild).online_count.set(enabled)
if enabled:
await ctx.send("InfoChannel for online user count has been enabled.")
else:
await ctx.send("InfoChannel for online user count has been disabled.")
async def make_infochannel(self, guild: discord.Guild):
botcount = await self.config.guild(guild).bot_count()
onlinecount = await self.config.guild(guild).online_count()
overwrites = {
guild.default_role: discord.PermissionOverwrite(connect=False),
guild.me: discord.PermissionOverwrite(manage_channels=True, connect=True),
}
channel = await guild.create_voice_channel(
"Placeholder", reason="InfoChannel make", overwrites=overwrites
)
await self.config.guild(guild).channel_id.set(channel.id)
if botcount:
botchannel = await guild.create_voice_channel(
"Placeholder", reason="InfoChannel botcount", overwrites=overwrites
)
await self.config.guild(guild).botchannel_id.set(botchannel.id)
if onlinecount:
onlinechannel = await guild.create_voice_channel(
"Placeholder", reason="InfoChannel onlinecount", overwrites=overwrites
)
await self.config.guild(guild).onlinechannel_id.set(onlinechannel.id)
await self.update_infochannel(guild)
async def delete_infochannel(self, guild: discord.Guild, channel: discord.VoiceChannel):
guild_data = await self.config.guild(guild).all()
botchannel_id = guild_data["botchannel_id"]
onlinechannel_id = guild_data["onlinechannel_id"]
botchannel: discord.VoiceChannel = guild.get_channel(botchannel_id)
onlinechannel: discord.VoiceChannel = guild.get_channel(onlinechannel_id)
channel_id = guild_data["channel_id"]
channel: discord.VoiceChannel = guild.get_channel(channel_id)
await channel.delete(reason="InfoChannel delete")
if botchannel_id is not None:
await botchannel.delete(reason="InfoChannel delete")
if onlinechannel_id is not None:
await onlinechannel.delete(reason="InfoChannel delete")
await self.config.guild(guild).clear()
async def update_infochannel(self, guild: discord.Guild):
guild_data = await self.config.guild(guild).all()
botcount = guild_data["bot_count"]
onlinecount = guild_data["online_count"]
# Gets count of bots
bots = lambda x: x.bot
num = len([m for m in guild.members if bots(m)])
bot_msg = f"Bots: {num}"
# Gets count of online users
members = guild.member_count
offline = len(list(filter(lambda m: m.status is discord.Status.offline, guild.members)))
num = members - offline
online_msg = f"Online: {num}"
# Gets count of actual users
total = lambda x: not x.bot
num = len([m for m in guild.members if total(m)])
human_msg = f"Total Humans: {num}"
channel_id = guild_data["channel_id"]
if channel_id is None:
return
botchannel_id = guild_data["botchannel_id"]
onlinechannel_id = guild_data["onlinechannel_id"]
channel_id = guild_data["channel_id"]
channel: discord.VoiceChannel = guild.get_channel(channel_id)
botchannel: discord.VoiceChannel = guild.get_channel(botchannel_id)
onlinechannel: discord.VoiceChannel = guild.get_channel(onlinechannel_id)
if guild_data["member_count"]:
name = "{} ".format(human_msg)
await channel.edit(reason="InfoChannel update", name=name)
if botcount:
name = "{} ".format(bot_msg)
await botchannel.edit(reason="InfoChannel update", name=name)
if onlinecount:
name = "{} ".format(online_msg)
await onlinechannel.edit(reason="InfoChannel update", name=name)
@listener()
async def on_member_join(self, member: discord.Member):
await self.update_infochannel(member.guild)
@listener()
async def on_member_remove(self, member: discord.Member):
await self.update_infochannel(member.guild)
@listener()
async def on_member_update(self, before: discord.Member, after: discord.Member):
onlinecount = await self.config.guild(after.guild).online_count()
if onlinecount:
if before.status != after.status:
await self.update_infochannel(after.guild)

@ -10,7 +10,6 @@
"description": "Keeps track of when people leave the server, and posts a message notifying", "description": "Keeps track of when people leave the server, and posts a message notifying",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing Leaver. Get started with `[p]load leaver`, then `[p]help Leaver`", "install_msg": "Thank you for installing Leaver. Get started with `[p]load leaver`, then `[p]help Leaver`",
"requirements": [],
"short": "Send message on leave", "short": "Send message on leave",
"tags": [ "tags": [
"bobloy", "bobloy",

@ -1,6 +1,7 @@
import discord import discord
from redbot.core import Config, checks, commands from redbot.core import Config, checks, commands
from redbot.core.bot import Red
from redbot.core.commands import Context from redbot.core.commands import Context
from typing import Any from typing import Any
@ -12,9 +13,11 @@ class Leaver(Cog):
Creates a goodbye message when people leave Creates a goodbye message when people leave
""" """
def __init__(self, bot): def __init__(self, bot: Red):
self.bot = bot self.bot = bot
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True) self.config = Config.get_conf(
self, identifier=9811198108111121, force_registration=True
)
default_guild = {"channel": ""} default_guild = {"channel": ""}
self.config.register_guild(**default_guild) self.config.register_guild(**default_guild)
@ -28,16 +31,28 @@ class Leaver(Cog):
@leaverset.command() @leaverset.command()
async def channel(self, ctx: Context): async def channel(self, ctx: Context):
"""Choose the channel to send leave messages to"""
guild = ctx.guild guild = ctx.guild
await self.config.guild(guild).channel.set(ctx.channel.id) await self.config.guild(guild).channel.set(ctx.channel.id)
await ctx.send("Channel set to " + ctx.channel.name) await ctx.send("Channel set to " + ctx.channel.name)
@commands.Cog.listener()
async def on_member_remove(self, member: discord.Member): async def on_member_remove(self, member: discord.Member):
guild = member.guild guild = member.guild
channel = await self.config.guild(guild).channel() channel = await self.config.guild(guild).channel()
if channel != "": if channel != "":
channel = guild.get_channel(channel) channel = guild.get_channel(channel)
await channel.send(str(member) + "(*" + str(member.nick) + "*) has left the server!") out = "{}{} has left the server".format(
member, member.nick if member.nick is not None else ""
)
if await self.bot.embed_requested(channel, member):
await channel.send(
embed=discord.Embed(
description=out, color=(await self.bot.get_embed_color(channel))
)
)
else:
await channel.send(out)
else: else:
pass pass

@ -30,7 +30,7 @@ class LoveCalculator(Cog):
soup_object = BeautifulSoup(await response.text(), "html.parser") soup_object = BeautifulSoup(await response.text(), "html.parser")
try: try:
description = ( description = (
soup_object.find("div", attrs={"class": "result score"}).get_text().strip() soup_object.find("div", attrs={"class": "result__score"}).get_text().strip()
) )
except: except:
description = "Dr. Love is busy right now" description = "Dr. Love is busy right now"

@ -24,7 +24,7 @@ class LastSeen(Cog):
self.bot = bot self.bot = bot
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True) self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
default_global = {} default_global = {}
default_guild = {"enabled": True} default_guild = {"enabled": False}
default_member = {"seen": None} default_member = {"seen": None}
self.config.register_global(**default_global) self.config.register_global(**default_global)
@ -74,6 +74,7 @@ class LastSeen(Cog):
embed = discord.Embed(timestamp=last_seen) embed = discord.Embed(timestamp=last_seen)
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.Cog.listener()
async def on_member_update(self, before: discord.Member, after: discord.Member): async def on_member_update(self, before: discord.Member, after: discord.Member):
if before.status != self.offline_status and after.status == self.offline_status: if before.status != self.offline_status and after.status == self.offline_status:
if not await self.config.guild(before.guild).enabled(): if not await self.config.guild(before.guild).enabled():

@ -1,5 +1,10 @@
from redbot.core import data_manager
from .planttycoon import PlantTycoon from .planttycoon import PlantTycoon
def setup(bot): async def setup(bot):
bot.add_cog(PlantTycoon(bot)) tycoon = PlantTycoon(bot)
data_manager.bundled_data_path(tycoon)
await tycoon._load_plants_products() # I can access protected members if I want, linter!!
bot.add_cog(tycoon)

@ -379,7 +379,7 @@
"rarity": "rare", "rarity": "rare",
"image": "http://i.imgur.com/hoh17hp.jpg", "image": "http://i.imgur.com/hoh17hp.jpg",
"health": 100, "health": 100,
"degradation": 1, "degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Sporadic", "badge": "Sporadic",
"reward": 2400 "reward": 2400
@ -391,7 +391,7 @@
"rarity": "rare", "rarity": "rare",
"image": "http://i.imgur.com/lhSjfQY.jpg", "image": "http://i.imgur.com/lhSjfQY.jpg",
"health": 100, "health": 100,
"degradation": 1, "degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Sporadic", "badge": "Sporadic",
"reward": 2400 "reward": 2400
@ -403,7 +403,7 @@
"rarity": "rare", "rarity": "rare",
"image": "http://i.imgur.com/Dhw9ync.jpg", "image": "http://i.imgur.com/Dhw9ync.jpg",
"health": 100, "health": 100,
"degradation": 1, "degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Sporadic", "badge": "Sporadic",
"reward": 2400 "reward": 2400
@ -415,7 +415,7 @@
"rarity": "rare", "rarity": "rare",
"image": "http://i.imgur.com/h4fJo2R.jpg", "image": "http://i.imgur.com/h4fJo2R.jpg",
"health": 100, "health": 100,
"degradation": 1, "degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Sporadic", "badge": "Sporadic",
"reward": 2400 "reward": 2400
@ -427,7 +427,7 @@
"rarity": "rare", "rarity": "rare",
"image": "http://i.imgur.com/NoSdxXh.jpg", "image": "http://i.imgur.com/NoSdxXh.jpg",
"health": 100, "health": 100,
"degradation": 1, "degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Sporadic", "badge": "Sporadic",
"reward": 2400 "reward": 2400
@ -439,7 +439,7 @@
"rarity": "rare", "rarity": "rare",
"image": "http://i.imgur.com/4ArSekX.jpg", "image": "http://i.imgur.com/4ArSekX.jpg",
"health": 100, "health": 100,
"degradation": 1, "degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Sporadic", "badge": "Sporadic",
"reward": 2400 "reward": 2400
@ -451,43 +451,19 @@
"rarity": "super-rare", "rarity": "super-rare",
"image": "http://i.imgur.com/ASZXr7C.png", "image": "http://i.imgur.com/ASZXr7C.png",
"health": 100, "health": 100,
"degradation": 1, "degradation": 2,
"threshold": 110,
"badge": "Odd-pod",
"reward": 3600
},
{
"name": "tba",
"article": "a",
"time": 9000,
"rarity": "super-rare",
"image": "tba",
"health": 100,
"degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Odd-pod", "badge": "Odd-pod",
"reward": 3600 "reward": 3600
}, },
{ {
"name": "Pirahna Plant", "name": "Piranha Plant",
"article": "a", "article": "a",
"time": 9000, "time": 9000,
"rarity": "super-rare", "rarity": "super-rare",
"image": "http://i.imgur.com/c03i9W7.jpg", "image": "http://i.imgur.com/c03i9W7.jpg",
"health": 100, "health": 100,
"degradation": 1.5, "degradation": 2,
"threshold": 110,
"badge": "Odd-pod",
"reward": 3600
},
{
"name": "tba",
"article": "a",
"time": 9000,
"rarity": "super-rare",
"image": "tba",
"health": 100,
"degradation": 1.5,
"threshold": 110, "threshold": 110,
"badge": "Odd-pod", "badge": "Odd-pod",
"reward": 3600 "reward": 3600
@ -499,19 +475,19 @@
"rarity": "super-rare", "rarity": "super-rare",
"image": "https://i.imgur.com/Vo4v2Ry.png", "image": "https://i.imgur.com/Vo4v2Ry.png",
"health": 100, "health": 100,
"degradation": 1.5, "degradation": 2,
"threshold": 110, "threshold": 110,
"badge": "Odd-pod", "badge": "Odd-pod",
"reward": 3600 "reward": 3600
}, },
{ {
"name": "tba", "name": "Eldergleam Tree",
"article": "a", "article": "a",
"time": 10800, "time": 10800,
"rarity": "epic", "rarity": "epic",
"image": "tba", "image": "https://i.imgur.com/pnZYKZc.jpg",
"health": 100, "health": 100,
"degradation": 2, "degradation": 2.5,
"threshold": 110, "threshold": 110,
"badge": "Greenfingers", "badge": "Greenfingers",
"reward": 5400 "reward": 5400
@ -523,7 +499,7 @@
"rarity": "epic", "rarity": "epic",
"image": "http://i.imgur.com/sizf7hE.png", "image": "http://i.imgur.com/sizf7hE.png",
"health": 100, "health": 100,
"degradation": 2, "degradation": 2.5,
"threshold": 110, "threshold": 110,
"badge": "Greenfingers", "badge": "Greenfingers",
"reward": 5400 "reward": 5400
@ -535,7 +511,7 @@
"rarity": "epic", "rarity": "epic",
"image": "http://i.imgur.com/9f5QzaW.jpg", "image": "http://i.imgur.com/9f5QzaW.jpg",
"health": 100, "health": 100,
"degradation": 2, "degradation": 2.5,
"threshold": 110, "threshold": 110,
"badge": "Greenfingers", "badge": "Greenfingers",
"reward": 5400 "reward": 5400
@ -547,7 +523,7 @@
"rarity": "epic", "rarity": "epic",
"image": "https://i.imgur.com/ExqLLHO.png", "image": "https://i.imgur.com/ExqLLHO.png",
"health": 100, "health": 100,
"degradation": 2, "degradation": 2.5,
"threshold": 110, "threshold": 110,
"badge": "Greenfingers", "badge": "Greenfingers",
"reward": 5400 "reward": 5400
@ -559,7 +535,7 @@
"rarity": "epic", "rarity": "epic",
"image": "https://i.imgur.com/tv2B72j.png", "image": "https://i.imgur.com/tv2B72j.png",
"health": 100, "health": 100,
"degradation": 2, "degradation": 2.5,
"threshold": 110, "threshold": 110,
"badge": "Greenfingers", "badge": "Greenfingers",
"reward": 5400 "reward": 5400
@ -571,7 +547,7 @@
"rarity": "legendary", "rarity": "legendary",
"image": "http://i.imgur.com/MIJQDLL.jpg", "image": "http://i.imgur.com/MIJQDLL.jpg",
"health": 100, "health": 100,
"degradation": 3, "degradation": 8,
"threshold": 110, "threshold": 110,
"badge": "Nobel Peas Prize", "badge": "Nobel Peas Prize",
"reward": 10800 "reward": 10800
@ -583,7 +559,7 @@
"rarity": "legendary", "rarity": "legendary",
"image": "http://i.imgur.com/cFSmaHH.png", "image": "http://i.imgur.com/cFSmaHH.png",
"health": 100, "health": 100,
"degradation": 3, "degradation": 8,
"threshold": 110, "threshold": 110,
"badge": "Nobel Peas Prize", "badge": "Nobel Peas Prize",
"reward": 10800 "reward": 10800
@ -595,7 +571,7 @@
"rarity": "legendary", "rarity": "legendary",
"image": "http://i.imgur.com/Ibwm2xY.jpg", "image": "http://i.imgur.com/Ibwm2xY.jpg",
"health": 100, "health": 100,
"degradation": 3, "degradation": 8,
"threshold": 110, "threshold": 110,
"badge": "Nobel Peas Prize", "badge": "Nobel Peas Prize",
"reward": 10800 "reward": 10800

File diff suppressed because it is too large Load Diff

@ -14,7 +14,7 @@ Cog: Any = getattr(commands, "Cog", object)
class QRInvite(Cog): class QRInvite(Cog):
""" """
V3 Cog Template Create custom QR codes for server invites
""" """
def __init__(self, bot: Red): def __init__(self, bot: Red):
@ -50,7 +50,7 @@ class QRInvite(Cog):
invite = invite.code invite = invite.code
if image_url is None: if image_url is None:
image_url = ctx.guild.icon_url image_url = str(ctx.guild.icon_url)
if image_url == "": # Still if image_url == "": # Still
await ctx.send( await ctx.send(
@ -72,9 +72,15 @@ class QRInvite(Cog):
file.write(image) file.write(image)
if extension == "webp": if extension == "webp":
new_path = convert_png(str(image_path)) new_path = convert_webp_to_png(str(image_path))
else: elif extension == "gif":
await ctx.send("gif is not supported yet, stay tuned")
return
elif extension == "png":
new_path = str(image_path) new_path = str(image_path)
else:
await ctx.send(f"{extension} is not supported yet, stay tuned")
return
myqr.run( myqr.run(
invite, invite,
@ -89,7 +95,7 @@ class QRInvite(Cog):
await ctx.send(file=discord.File(png_fp.read(), "qrcode.png")) await ctx.send(file=discord.File(png_fp.read(), "qrcode.png"))
def convert_png(path): def convert_webp_to_png(path):
im = Image.open(path) im = Image.open(path)
im.load() im.load()
alpha = im.split()[-1] alpha = im.split()[-1]

@ -1,10 +1,22 @@
{ {
"author" : ["Bobloy"], "author": [
"bot_version" : [3,0,0], "Bobloy"
],
"bot_version": [
3,
0,
0
],
"description": "Cog to prevent reactions on specific messages from certain users", "description": "Cog to prevent reactions on specific messages from certain users",
"hidden": true, "hidden": true,
"install_msg": "Thank you for installing ReactRestrict.", "install_msg": "Thank you for installing ReactRestrict.",
"requirements" : [],
"short": "[Incomplete] Prevent reactions", "short": "[Incomplete] Prevent reactions",
"tags" : ["react", "reaction", "restrict", "tools", "utils", "bobloy"] "tags": [
"react",
"reaction",
"restrict",
"tools",
"utils",
"bobloy"
]
} }

@ -139,7 +139,8 @@ class ReactRestrict(Cog):
return member return member
def _get_role(self, guild: discord.Guild, role_id: int) -> discord.Role: @staticmethod
def _get_role(guild: discord.Guild, role_id: int) -> discord.Role:
""" """
Gets a role object from the given guild with the given ID. Gets a role object from the given guild with the given ID.
@ -206,8 +207,7 @@ class ReactRestrict(Cog):
@reactrestrict.command() @reactrestrict.command()
async def add(self, ctx: commands.Context, message_id: int, *, role: discord.Role): async def add(self, ctx: commands.Context, message_id: int, *, role: discord.Role):
""" """
Adds a reaction|role combination to a registered message, don't use Adds a reaction|role combination to a registered message, don't use quotes for the role name.
quotes for the role name.
""" """
message = await self._get_message(ctx, message_id) message = await self._get_message(ctx, message_id)
if message is None: if message is None:
@ -248,6 +248,7 @@ class ReactRestrict(Cog):
await ctx.send("Reaction removed.") await ctx.send("Reaction removed.")
@commands.Cog.listener()
async def on_raw_reaction_add( async def on_raw_reaction_add(
self, emoji: discord.PartialEmoji, message_id: int, channel_id: int, user_id: int self, emoji: discord.PartialEmoji, message_id: int, channel_id: int, user_id: int
): ):

@ -5,5 +5,5 @@ from .recyclingplant import RecyclingPlant
def setup(bot): def setup(bot):
plant = RecyclingPlant(bot) plant = RecyclingPlant(bot)
data_manager.load_bundled_data(plant, __file__) data_manager.bundled_data_path(plant)
bot.add_cog(plant) bot.add_cog(plant)

@ -11,7 +11,6 @@
"description": "Apply for a job at the recycling plant! Sort out the garbage!", "description": "Apply for a job at the recycling plant! Sort out the garbage!",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing RecyclingPlant. Start recycling today with `[p]load recyclingplant`, then `[p]recyclingplant`", "install_msg": "Thank you for installing RecyclingPlant. Start recycling today with `[p]load recyclingplant`, then `[p]recyclingplant`",
"requirements": [],
"short": "Apply for a job at the recycling plant!", "short": "Apply for a job at the recycling plant!",
"tags": [ "tags": [
"bobloy", "bobloy",

@ -1,12 +1,11 @@
import asyncio import asyncio
import json import json
import random import random
from redbot.core import bank
from redbot.core import commands
from redbot.core.data_manager import cog_data_path
from typing import Any from typing import Any
from redbot.core import bank, commands
from redbot.core.data_manager import bundled_data_path
Cog: Any = getattr(commands, "Cog", object) Cog: Any = getattr(commands, "Cog", object)
@ -15,15 +14,19 @@ class RecyclingPlant(Cog):
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
self.path = str(cog_data_path(self)).replace("\\", "/") self.junk = None
self.junk_path = self.path + "/bundled_data/junk.json"
with open(self.junk_path) as json_data: def load_junk(self):
junk_path = bundled_data_path(self) / "junk.json"
with junk_path.open() as json_data:
self.junk = json.load(json_data) self.junk = json.load(json_data)
@commands.command(aliases=["recycle"]) @commands.command(aliases=["recycle"])
async def recyclingplant(self, ctx: commands.Context): async def recyclingplant(self, ctx: commands.Context):
"""Apply for a job at the recycling plant!""" """Apply for a job at the recycling plant!"""
if self.junk is None:
self.load_junk()
x = 0 x = 0
reward = 0 reward = 0
await ctx.send( await ctx.send(
@ -61,7 +64,7 @@ class RecyclingPlant(Cog):
used["object"] used["object"]
) )
) )
reward = reward + 50 reward += 50
x += 1 x += 1
elif answer.content.lower().strip() == opp: elif answer.content.lower().strip() == opp:
await ctx.send( await ctx.send(
@ -69,7 +72,7 @@ class RecyclingPlant(Cog):
ctx.author.display_name ctx.author.display_name
) )
) )
reward = reward - 50 reward -= 50
elif answer.content.lower().strip() == "exit": elif answer.content.lower().strip() == "exit":
await ctx.send( await ctx.send(
"{} has been relived of their duty.".format(ctx.author.display_name) "{} has been relived of their duty.".format(ctx.author.display_name)
@ -81,9 +84,9 @@ class RecyclingPlant(Cog):
) )
else: else:
if reward > 0: if reward > 0:
bank.deposit_credits(ctx.author, reward) await bank.deposit_credits(ctx.author, reward)
await ctx.send( await ctx.send(
"{} been given **{} {}s** for your services.".format( "{} been given **{} {}s** for your services.".format(
ctx.author.display_name, reward, bank.get_currency_name(ctx.guild) ctx.author.display_name, reward, await bank.get_currency_name(ctx.guild)
) )
) )

@ -10,7 +10,7 @@
], ],
"description": "Play Rock Papers Scissor Lizard Spock by Sam Kass in Discord!", "description": "Play Rock Papers Scissor Lizard Spock by Sam Kass in Discord!",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing RPSLP. Get started with `[p]load rpsls`, then `[p]rpsls`", "install_msg": "Thank you for installing RPSLS. Get started with `[p]load rpsls`, then `[p]rpsls`",
"requirements": [], "requirements": [],
"short": "Play Rock Papers Scissor Lizard Spock in Discord!", "short": "Play Rock Papers Scissor Lizard Spock in Discord!",
"tags": [ "tags": [

@ -9,7 +9,7 @@
], ],
"description": "Convert any website into text and post it in chat", "description": "Convert any website into text and post it in chat",
"hidden": true, "hidden": true,
"install_msg": "Thank you for installing SayUrl! Get started with `[p]load forcemention`, then `[p]help SayUrl", "install_msg": "Thank you for installing SayUrl! Get started with `[p]load sayurl`, then `[p]help SayUrl",
"requirements": ["html2text"], "requirements": ["html2text"],
"short": "Convert URL to text", "short": "Convert URL to text",
"tags": [ "tags": [

@ -16,19 +16,19 @@ class SCP(Cog):
"""Look up SCP articles. """Look up SCP articles.
Warning: Some of them may be too creepy or gruesome. Warning: Some of them may be too creepy or gruesome.
Reminder: You must specify a number between 1 and 4999. Reminder: You must specify a number between 1 and 5999.
""" """
# Thanks Shigbeard and Redjumpman for helping me! # Thanks Shigbeard and Redjumpman for helping me!
if 0 < num <= 4999: if 0 < num <= 5999:
msg = "http://www.scp-wiki.net/scp-{:03}".format(num) msg = "http://www.scp-wiki.net/scp-{:03}".format(num)
c = discord.Color.green() c = discord.Color.green()
else: else:
msg = "You must specify a number between 1 and 4999." msg = "You must specify a number between 1 and 5999."
c = discord.Color.red() c = discord.Color.red()
if ctx.embed_requested(): if await ctx.embed_requested():
await ctx.send(embed=discord.Embed(description=msg, color=c)) await ctx.send(embed=discord.Embed(description=msg, color=c))
else: else:
await ctx.maybe_send_embed(msg) await ctx.maybe_send_embed(msg)
@ -50,10 +50,11 @@ class SCP(Cog):
Warning: Some of them may be too creepy or gruesome.""" Warning: Some of them may be too creepy or gruesome."""
valid_archive = ( valid_archive = (
1,
13, 13,
48, 48,
49,
51, 51,
89,
91, 91,
112, 112,
132, 132,
@ -67,10 +68,9 @@ class SCP(Cog):
257, 257,
338, 338,
356, 356,
361,
400, 400,
406, 406,
503, 494,
515, 515,
517, 517,
578, 578,
@ -94,7 +94,7 @@ class SCP(Cog):
em = discord.Embed(title=ttl, description=msg, color=c) em = discord.Embed(title=ttl, description=msg, color=c)
if ctx.embed_requested(): if await ctx.embed_requested():
await ctx.send(embed=em) await ctx.send(embed=em)
else: else:
await ctx.maybe_send_embed(msg) await ctx.maybe_send_embed(msg)
@ -106,7 +106,37 @@ class SCP(Cog):
Warning: Some of them may be too creepy or gruesome. Warning: Some of them may be too creepy or gruesome.
""" """
valid_archive = (711, 920, 1841, 1851, 1974, 2600, 4023, 8900) valid_archive = (
1,
2,
67,
123,
445,
711,
888,
920,
1094,
1401,
1512,
1548,
1763,
1841,
1851,
1927,
1933,
1964,
1974,
1990,
2600,
2700,
3000,
4023,
4445,
4734,
5297,
5735,
8900,
)
if num in valid_archive: if num in valid_archive:
msg = "http://www.scp-wiki.net/scp-{:03}-ex".format(num) msg = "http://www.scp-wiki.net/scp-{:03}-ex".format(num)
c = discord.Color.green() c = discord.Color.green()
@ -118,7 +148,7 @@ class SCP(Cog):
em = discord.Embed(title=ttl, description=msg, color=c) em = discord.Embed(title=ttl, description=msg, color=c)
if ctx.embed_requested(): if await ctx.embed_requested():
await ctx.send(embed=em) await ctx.send(embed=em)
else: else:
await ctx.maybe_send_embed(msg) await ctx.maybe_send_embed(msg)

@ -106,6 +106,7 @@ class StealEmoji(Cog):
else: else:
await ctx.send("This server has been added to be an emoji bank") await ctx.send("This server has been added to be an emoji bank")
@commands.Cog.listener()
async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User): async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User):
"""Event handler for reaction watching""" """Event handler for reaction watching"""
if not reaction.custom_emoji: if not reaction.custom_emoji:

@ -9,8 +9,7 @@
], ],
"description": "Apply roles based on the # of days on server", "description": "Apply roles based on the # of days on server",
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing timerole.\nGet started with `[p]load timerole`. Configure with [p]timerole", "install_msg": "Thank you for installing timerole.\nGet started with `[p]load timerole`. Configure with `[p]timerole`",
"requirements": [],
"short": "Apply roles after # of days", "short": "Apply roles after # of days",
"tags": [ "tags": [
"bobloy", "bobloy",

@ -21,12 +21,20 @@ class Timerole(Cog):
self.config.register_global(**default_global) self.config.register_global(**default_global)
self.config.register_guild(**default_guild) self.config.register_guild(**default_guild)
self.updating = self.bot.loop.create_task(self.check_day())
def cog_unload(self):
self.updating.cancel()
@commands.command() @commands.command()
@checks.guildowner() @checks.guildowner()
@commands.guild_only() @commands.guild_only()
async def runtimerole(self, ctx: commands.Context): async def runtimerole(self, ctx: commands.Context):
"""Trigger the daily timerole""" """
Trigger the daily timerole
Useful for troubleshooting the initial setup
"""
await self.timerole_update() await self.timerole_update()
await ctx.send("Success") await ctx.send("Success")
@ -46,12 +54,34 @@ class Timerole(Cog):
"""Add a role to be added after specified time on server""" """Add a role to be added after specified time on server"""
guild = ctx.guild guild = ctx.guild
to_set = {"days": days} to_set = {"days": days, "remove": False}
if requiredroles: if requiredroles:
to_set["required"] = [r.id for r in requiredroles] to_set["required"] = [r.id for r in requiredroles]
await self.config.guild(guild).roles.set_raw(role.id, value=to_set) await self.config.guild(guild).roles.set_raw(role.id, value=to_set)
await ctx.send("Time Role for {0} set to {1} days".format(role.name, days)) await ctx.maybe_send_embed(
"Time Role for {0} set to {1} days until added".format(role.name, days)
)
@timerole.command()
async def removerole(
self, ctx: commands.Context, role: discord.Role, days: int, *requiredroles: discord.Role
):
"""
Add a role to be removed after specified time on server
Useful with an autorole cog
"""
guild = ctx.guild
to_set = {"days": days, "remove": True}
if requiredroles:
to_set["required"] = [r.id for r in requiredroles]
await self.config.guild(guild).roles.set_raw(role.id, value=to_set)
await ctx.maybe_send_embed(
"Time Role for {0} set to {1} days until removed".format(role.name, days)
)
@timerole.command() @timerole.command()
async def channel(self, ctx: commands.Context, channel: discord.TextChannel): async def channel(self, ctx: commands.Context, channel: discord.TextChannel):
@ -62,8 +92,8 @@ class Timerole(Cog):
await ctx.send("Announce channel set to {0}".format(channel.mention)) await ctx.send("Announce channel set to {0}".format(channel.mention))
@timerole.command() @timerole.command()
async def removerole(self, ctx: commands.Context, role: discord.Role): async def delrole(self, ctx: commands.Context, role: discord.Role):
"""Removes a role from being added after specified time""" """Deletes a role from being added/removed after specified time"""
guild = ctx.guild guild = ctx.guild
await self.config.guild(guild).roles.set_raw(role.id, value=None) await self.config.guild(guild).roles.set_raw(role.id, value=None)
@ -93,6 +123,7 @@ class Timerole(Cog):
async def timerole_update(self): async def timerole_update(self):
for guild in self.bot.guilds: for guild in self.bot.guilds:
addlist = [] addlist = []
removelist = []
role_dict = await self.config.guild(guild).roles() role_dict = await self.config.guild(guild).roles()
if not any(role_data for role_data in role_dict.values()): # No roles if not any(role_data for role_data in role_dict.values()): # No roles
@ -101,55 +132,70 @@ class Timerole(Cog):
for member in guild.members: for member in guild.members:
has_roles = [r.id for r in member.roles] has_roles = [r.id for r in member.roles]
get_roles = [int(rID) for rID, r_data in role_dict.items() if r_data is not None] add_roles = [
int(rID)
check_roles = set(get_roles) - set(has_roles) for rID, r_data in role_dict.items()
if r_data is not None and not r_data["remove"]
]
remove_roles = [
int(rID)
for rID, r_data in role_dict.items()
if r_data is not None and r_data["remove"]
]
for role_id in check_roles: check_add_roles = set(add_roles) - set(has_roles)
# Check for required role check_remove_roles = set(remove_roles) & set(has_roles)
if "required" in role_dict[str(role_id)]:
if not set(role_dict[str(role_id)]["required"]) & set(has_roles):
# Doesn't have required role
continue
if ( await self.check_required_and_date(
member.joined_at + timedelta(days=role_dict[str(role_id)]["days"]) addlist, check_add_roles, has_roles, member, role_dict
<= datetime.today() )
): await self.check_required_and_date(
# Qualifies removelist, check_remove_roles, has_roles, member, role_dict
addlist.append((member, role_id)) )
channel = await self.config.guild(guild).announce() channel = await self.config.guild(guild).announce()
if channel is not None: if channel is not None:
channel = guild.get_channel(channel) channel = guild.get_channel(channel)
title = "**These members have received the following roles**\n" title = "**These members have received the following roles**\n"
await self.announce_roles(title, addlist, channel, guild, to_add=True)
title = "**These members have lost the following roles**\n"
await self.announce_roles(title, removelist, channel, guild, to_add=False)
async def announce_roles(self, title, role_list, channel, guild, to_add: True):
results = "" results = ""
for member, role_id in addlist: for member, role_id in role_list:
role = discord.utils.get(guild.roles, id=role_id) role = discord.utils.get(guild.roles, id=role_id)
try:
if to_add:
await member.add_roles(role, reason="Timerole") await member.add_roles(role, reason="Timerole")
else:
await member.remove_roles(role, reason="Timerole")
except discord.Forbidden:
results += "{} : {} **(Failed)**\n".format(member.display_name, role.name)
else:
results += "{} : {}\n".format(member.display_name, role.name) results += "{} : {}\n".format(member.display_name, role.name)
if channel is not None and results: if channel is not None and results:
await channel.send(title) await channel.send(title)
for page in pagify(results, shorten_by=50): for page in pagify(results, shorten_by=50):
await channel.send(page) await channel.send(page)
async def check_day(self): async def check_required_and_date(self, role_list, check_roles, has_roles, member, role_dict):
while self is self.bot.get_cog("Timerole"): for role_id in check_roles:
tomorrow = datetime.now() + timedelta(days=1) # Check for required role
midnight = datetime( if "required" in role_dict[str(role_id)]:
year=tomorrow.year, if not set(role_dict[str(role_id)]["required"]) & set(has_roles):
month=tomorrow.month, # Doesn't have required role
day=tomorrow.day, continue
hour=0,
minute=0,
second=0,
)
await asyncio.sleep((midnight - datetime.now()).seconds) if (
member.joined_at + timedelta(days=role_dict[str(role_id)]["days"])
<= datetime.today()
):
# Qualifies
role_list.append((member, role_id))
async def check_day(self):
while self is self.bot.get_cog("Timerole"):
await self.timerole_update() await self.timerole_update()
await asyncio.sleep(86400)
await asyncio.sleep(3)
# then start loop over again

@ -8,9 +8,9 @@ import discord
# Import all roles here # Import all roles here
from redbot.core import commands from redbot.core import commands
from werewolf.roles.seer import Seer from .roles.seer import Seer
from werewolf.roles.vanillawerewolf import VanillaWerewolf from .roles.vanillawerewolf import VanillaWerewolf
from werewolf.roles.villager import Villager from .roles.villager import Villager
from redbot.core.utils.menus import menu, prev_page, next_page, close_menu from redbot.core.utils.menus import menu, prev_page, next_page, close_menu
# All roles in this list for iterating # All roles in this list for iterating

@ -5,10 +5,10 @@ from typing import List, Any, Dict, Set, Union
import discord import discord
from redbot.core import commands from redbot.core import commands
from werewolf.builder import parse_code from .builder import parse_code
from werewolf.player import Player from .player import Player
from werewolf.role import Role from .role import Role
from werewolf.votegroup import VoteGroup from .votegroup import VoteGroup
class Game: class Game:

@ -1,4 +1,4 @@
from werewolf.role import Role from .role import Role
def night_immune(role: Role): def night_immune(role: Role):

@ -1,5 +1,5 @@
from werewolf.night_powers import pick_target from ..night_powers import pick_target
from werewolf.role import Role from ..role import Role
class Seer(Role): class Seer(Role):

@ -1,5 +1,5 @@
from werewolf.night_powers import pick_target from ..night_powers import pick_target
from werewolf.role import Role from ..role import Role
class Shifter(Role): class Shifter(Role):

@ -1,6 +1,6 @@
from werewolf.role import Role from ..role import Role
from werewolf.votegroups.wolfvote import WolfVote from ..votegroups.wolfvote import WolfVote
class VanillaWerewolf(Role): class VanillaWerewolf(Role):

@ -1,4 +1,4 @@
from werewolf.role import Role from ..role import Role
class Villager(Role): class Villager(Role):

@ -1,6 +1,6 @@
import random import random
from werewolf.votegroup import VoteGroup from ..votegroup import VoteGroup
class WolfVote(VoteGroup): class WolfVote(VoteGroup):

@ -5,8 +5,8 @@ from redbot.core import Config, checks
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core import commands from redbot.core import commands
from werewolf.builder import GameBuilder, role_from_name, role_from_alignment, role_from_category, role_from_id from .builder import GameBuilder, role_from_name, role_from_alignment, role_from_category, role_from_id
from werewolf.game import Game from .game import Game
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
from typing import Any from typing import Any
@ -41,6 +41,11 @@ class Werewolf(Cog):
@commands.command() @commands.command()
async def buildgame(self, ctx: commands.Context): async def buildgame(self, ctx: commands.Context):
"""
Create game codes to run custom games.
Pick the roles or randomized roles you want to include in a game
"""
gb = GameBuilder() gb = GameBuilder()
code = await gb.build_game(ctx) code = await gb.build_game(ctx)
@ -92,7 +97,7 @@ class Werewolf(Cog):
@commands.guild_only() @commands.guild_only()
@wwset.command(name="category") @wwset.command(name="category")
async def wwset_category(self, ctx: commands.Context, category_id=None): async def wwset_category(self, ctx: commands.Context, category_id: int=None):
""" """
Assign the channel category Assign the channel category
""" """
@ -294,7 +299,7 @@ class Werewolf(Cog):
Find custom roles by name, alignment, category, or ID Find custom roles by name, alignment, category, or ID
""" """
if ctx.invoked_subcommand is None or ctx.invoked_subcommand == self.ww_search: if ctx.invoked_subcommand is None or ctx.invoked_subcommand == self.ww_search:
await ctx.send_help() pass
@ww_search.command(name="name") @ww_search.command(name="name")
async def ww_search_name(self, ctx: commands.Context, *, name): async def ww_search_name(self, ctx: commands.Context, *, name):

Loading…
Cancel
Save