Compare commits

..

1 Commits

Author SHA1 Message Date
bobloy af10493e81 Introduce mute method
4 years ago

@ -3,7 +3,6 @@ import logging
import re import re
import discord import discord
from discord.ext.commands import RoleConverter, Greedy, CommandError, ArgumentParsingError
from discord.ext.commands.view import StringView from discord.ext.commands.view import StringView
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
@ -14,38 +13,15 @@ log = logging.getLogger("red.fox_v3.ccrole")
async def _get_roles_from_content(ctx, content): async def _get_roles_from_content(ctx, content):
# greedy = Greedy[RoleConverter] content_list = content.split(",")
view = StringView(content) try:
rc = RoleConverter() role_list = [
discord.utils.get(ctx.guild.roles, name=role.strip(" ")).id for role in content_list
# "Borrowed" from discord.ext.commands.Command._transform_greedy_pos ]
result = [] except (discord.HTTPException, AttributeError): # None.id is attribute error
while not view.eof: return None
# for use with a manual undo else:
previous = view.index return role_list
view.skip_ws()
try:
argument = view.get_quoted_word()
value = await rc.convert(ctx, argument)
except (CommandError, ArgumentParsingError):
view.index = previous
break
else:
result.append(value)
return [r.id for r in result]
# Old method
# content_list = content.split(",")
# try:
# role_list = [
# discord.utils.get(ctx.guild.roles, name=role.strip(" ")).id for role in content_list
# ]
# except (discord.HTTPException, AttributeError): # None.id is attribute error
# return None
# else:
# return role_list
class CCRole(commands.Cog): class CCRole(commands.Cog):
@ -108,7 +84,7 @@ class CCRole(commands.Cog):
# Roles to add # Roles to add
await ctx.send( await ctx.send(
"What roles should it add?\n" "What roles should it add? (Must be **comma separated**)\n"
"Say `None` to skip adding roles" "Say `None` to skip adding roles"
) )
@ -130,7 +106,7 @@ class CCRole(commands.Cog):
# Roles to remove # Roles to remove
await ctx.send( await ctx.send(
"What roles should it remove?\n" "What roles should it remove? (Must be comma separated)\n"
"Say `None` to skip removing roles" "Say `None` to skip removing roles"
) )
try: try:
@ -148,7 +124,7 @@ class CCRole(commands.Cog):
# Roles to use # Roles to use
await ctx.send( await ctx.send(
"What roles are allowed to use this command?\n" "What roles are allowed to use this command? (Must be comma separated)\n"
"Say `None` to allow all roles" "Say `None` to allow all roles"
) )

@ -74,18 +74,12 @@ If you get an error at this step, stop and skip to one of the manual methods bel
#### Step 2: Install additional dependencies #### Step 2: Install additional dependencies
Here you need to decide which training models you want to have available to you. Assuming the previous commands had no error, you can now use `pipinstall` to add the remaining dependencies.
Shutdown the bot and run any number of these in the console: NOTE: This method is not the intended use case for `pipinstall` and may stop working in the future.
``` ```
python -m spacy download en_core_web_sm # ~15 MB [p]pipinstall --no-deps chatterbot>=1.1
python -m spacy download en_core_web_md # ~50 MB
python -m spacy download en_core_web_lg # ~750 MB (CPU Optimized)
python -m spacy download en_core_web_trf # ~500 MB (GPU Optimized)
``` ```
#### Step 3: Load the cog and get started #### Step 3: Load the cog and get started
@ -95,14 +89,62 @@ python -m spacy download en_core_web_trf # ~500 MB (GPU Optimized)
``` ```
### Windows - Manually ### Windows - Manually
Deprecated #### Step 1: Built-in Downloader
You need to get a copy of the requirements.txt provided with chatter, I recommend this method.
```
[p]repo add Fox https://github.com/bobloy/Fox-V3
```
#### Step 2: Install Requirements
Make sure you have your virtual environment that you installed Red on activated before starting this step. See the Red Docs for details on how.
In a terminal running as an admin, navigate to the directory containing this repo.
I've used my install directory as an example.
```
cd C:\Users\Bobloy\AppData\Local\Red-DiscordBot\Red-DiscordBot\data\bobbot\cogs\RepoManager\repos\Fox\chatter
pip install -r requirements.txt
pip install --no-deps "chatterbot>=1.1"
```
#### Step 3: Load Chatter
```
[p]repo add Fox https://github.com/bobloy/Fox-V3 # If you didn't already do this in step 1
[p]cog install Fox chatter
[p]load chatter
```
### Linux - Manually ### Linux - Manually
Deprecated
#### Step 1: Built-in Downloader
```
[p]repo add Fox https://github.com/bobloy/Fox-V3
[p]cog install Fox chatter
```
#### Step 2: Install Requirements
In your console with your virtual environment activated:
```
pip install --no-deps "chatterbot>=1.1"
```
### Step 3: Load Chatter
```
[p]load chatter
```
# Configuration # Configuration
Chatter works out the box without any training by learning as it goes, Chatter works out the the box without any training by learning as it goes,
but will have very poor and repetitive responses at first. but will have very poor and repetitive responses at first.
Initial training is recommended to speed up its learning. Initial training is recommended to speed up its learning.

@ -1,10 +1,8 @@
from .chat import Chatter from .chat import Chatter
async def setup(bot): def setup(bot):
cog = Chatter(bot) bot.add_cog(Chatter(bot))
await cog.initialize()
bot.add_cog(cog)
# __all__ = ( # __all__ = (

@ -19,7 +19,6 @@ from redbot.core.utils.predicates import MessagePredicate
from chatter.trainers import MovieTrainer, TwitterCorpusTrainer, UbuntuCorpusTrainer2 from chatter.trainers import MovieTrainer, TwitterCorpusTrainer, UbuntuCorpusTrainer2
chatterbot_log = logging.getLogger("red.fox_v3.chatterbot")
log = logging.getLogger("red.fox_v3.chatter") log = logging.getLogger("red.fox_v3.chatter")
@ -30,12 +29,6 @@ def my_local_get_prefix(prefixes, content):
return None return None
class ENG_TRF:
ISO_639_1 = "en_core_web_trf"
ISO_639 = "eng"
ENGLISH_NAME = "English"
class ENG_LG: class ENG_LG:
ISO_639_1 = "en_core_web_lg" ISO_639_1 = "en_core_web_lg"
ISO_639 = "eng" ISO_639 = "eng"
@ -59,15 +52,12 @@ class Chatter(Cog):
This cog trains a chatbot that will talk like members of your Guild This cog trains a chatbot that will talk like members of your Guild
""" """
models = [ENG_SM, ENG_MD, ENG_LG, ENG_TRF]
algos = [SpacySimilarity, JaccardSimilarity, LevenshteinDistance]
def __init__(self, bot): def __init__(self, bot):
super().__init__() super().__init__()
self.bot = bot self.bot = bot
self.config = Config.get_conf(self, identifier=6710497116116101114) self.config = Config.get_conf(self, identifier=6710497116116101114)
default_global = {"learning": True, "model_number": 0, "algo_number": 0, "threshold": 0.90} default_global = {"learning": True}
self.default_guild = { default_guild = {
"whitelist": None, "whitelist": None,
"days": 1, "days": 1,
"convo_delta": 15, "convo_delta": 15,
@ -80,16 +70,16 @@ class Chatter(Cog):
# TODO: Move training_model and similarity_algo to config # TODO: Move training_model and similarity_algo to config
# TODO: Add an option to see current settings # TODO: Add an option to see current settings
self.tagger_language = ENG_SM self.tagger_language = ENG_MD
self.similarity_algo = SpacySimilarity self.similarity_algo = SpacySimilarity
self.similarity_threshold = 0.90 self.similarity_threshold = 0.90
self.chatbot = None self.chatbot = self._create_chatbot()
# self.chatbot.set_trainer(ListTrainer) # self.chatbot.set_trainer(ListTrainer)
# self.trainer = ListTrainer(self.chatbot) # self.trainer = ListTrainer(self.chatbot)
self.config.register_global(**default_global) self.config.register_global(**default_global)
self.config.register_guild(**self.default_guild) self.config.register_guild(**default_guild)
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
@ -102,18 +92,6 @@ class Chatter(Cog):
"""Nothing to delete""" """Nothing to delete"""
return return
async def initialize(self):
all_config = dict(self.config.defaults["GLOBAL"])
all_config.update(await self.config.all())
model_number = all_config["model_number"]
algo_number = all_config["algo_number"]
threshold = all_config["threshold"]
self.tagger_language = self.models[model_number]
self.similarity_algo = self.algos[algo_number]
self.similarity_threshold = threshold
self.chatbot = self._create_chatbot()
def _create_chatbot(self): def _create_chatbot(self):
return ChatBot( return ChatBot(
@ -126,7 +104,7 @@ class Chatter(Cog):
logic_adapters=["chatterbot.logic.BestMatch"], logic_adapters=["chatterbot.logic.BestMatch"],
maximum_similarity_threshold=self.similarity_threshold, maximum_similarity_threshold=self.similarity_threshold,
tagger_language=self.tagger_language, tagger_language=self.tagger_language,
logger=chatterbot_log, logger=log,
) )
async def _get_conversation(self, ctx, in_channels: List[discord.TextChannel]): async def _get_conversation(self, ctx, in_channels: List[discord.TextChannel]):
@ -350,12 +328,15 @@ class Chatter(Cog):
self, ctx: commands.Context, algo_number: int, threshold: float = None self, ctx: commands.Context, algo_number: int, threshold: float = None
): ):
""" """
Switch the active logic algorithm to one of the three. Default is Spacy Switch the active logic algorithm to one of the three. Default after reload is Spacy
0: Spacy 0: Spacy
1: Jaccard 1: Jaccard
2: Levenshtein 2: Levenshtein
""" """
algos = [SpacySimilarity, JaccardSimilarity, LevenshteinDistance]
if algo_number < 0 or algo_number > 2: if algo_number < 0 or algo_number > 2:
await ctx.send_help() await ctx.send_help()
return return
@ -368,11 +349,8 @@ class Chatter(Cog):
return return
else: else:
self.similarity_threshold = threshold self.similarity_threshold = threshold
await self.config.threshold.set(self.similarity_threshold)
self.similarity_algo = self.algos[algo_number]
await self.config.algo_number.set(algo_number)
self.similarity_algo = algos[algo_number]
async with ctx.typing(): async with ctx.typing():
self.chatbot = self._create_chatbot() self.chatbot = self._create_chatbot()
@ -382,18 +360,20 @@ class Chatter(Cog):
@chatter.command(name="model") @chatter.command(name="model")
async def chatter_model(self, ctx: commands.Context, model_number: int): async def chatter_model(self, ctx: commands.Context, model_number: int):
""" """
Switch the active model to one of the three. Default is Small Switch the active model to one of the three. Default after reload is Medium
0: Small 0: Small
1: Medium (Requires additional setup) 1: Medium
2: Large (Requires additional setup) 2: Large (Requires additional setup)
3. Accurate (Requires additional setup)
""" """
if model_number < 0 or model_number > 3:
models = [ENG_SM, ENG_MD, ENG_LG]
if model_number < 0 or model_number > 2:
await ctx.send_help() await ctx.send_help()
return return
if model_number >= 0: if model_number == 2:
await ctx.maybe_send_embed( await ctx.maybe_send_embed(
"Additional requirements needed. See guide before continuing.\n" "Continue?" "Additional requirements needed. See guide before continuing.\n" "Continue?"
) )
@ -406,8 +386,7 @@ class Chatter(Cog):
if not pred.result: if not pred.result:
return return
self.tagger_language = self.models[model_number] self.tagger_language = models[model_number]
await self.config.model_number.set(model_number)
async with ctx.typing(): async with ctx.typing():
self.chatbot = self._create_chatbot() self.chatbot = self._create_chatbot()
@ -416,13 +395,7 @@ class Chatter(Cog):
) )
@commands.is_owner() @commands.is_owner()
@chatter.group(name="trainset") @chatter.command(name="minutes")
async def chatter_trainset(self, ctx: commands.Context):
"""Commands for configuring training"""
pass
@commands.is_owner()
@chatter_trainset.command(name="minutes")
async def minutes(self, ctx: commands.Context, minutes: int): async def minutes(self, ctx: commands.Context, minutes: int):
""" """
Sets the number of minutes the bot will consider a break in a conversation during training Sets the number of minutes the bot will consider a break in a conversation during training
@ -438,7 +411,7 @@ class Chatter(Cog):
await ctx.tick() await ctx.tick()
@commands.is_owner() @commands.is_owner()
@chatter_trainset.command(name="age") @chatter.command(name="age")
async def age(self, ctx: commands.Context, days: int): async def age(self, ctx: commands.Context, days: int):
""" """
Sets the number of days to look back Sets the number of days to look back
@ -657,7 +630,7 @@ class Chatter(Cog):
guild: discord.Guild = getattr(message, "guild", None) guild: discord.Guild = getattr(message, "guild", None)
if guild is None or await self.bot.cog_disabled_in_guild(self, guild): if await self.bot.cog_disabled_in_guild(self, guild):
return return
ctx: commands.Context = await self.bot.get_context(message) ctx: commands.Context = await self.bot.get_context(message)
@ -732,9 +705,7 @@ class Chatter(Cog):
) )
replying = None replying = None
if ( if self._guild_cache[guild.id]["reply"]:
"reply" not in self._guild_cache[guild.id] and self.default_guild["reply"]
) or self._guild_cache[guild.id]["reply"]:
if message != ctx.channel.last_message: if message != ctx.channel.last_message:
replying = message replying = message

@ -7,10 +7,18 @@
"hidden": false, "hidden": false,
"install_msg": "Thank you for installing Chatter! Please make sure you check the install instructions at https://github.com/bobloy/Fox-V3/blob/master/chatter/README.md\nAfter that, get started ith `[p]load chatter` and `[p]help Chatter`", "install_msg": "Thank you for installing Chatter! Please make sure you check the install instructions at https://github.com/bobloy/Fox-V3/blob/master/chatter/README.md\nAfter that, get started ith `[p]load chatter` and `[p]help Chatter`",
"requirements": [ "requirements": [
"git+git://github.com/bobloy/ChatterBot@fox#egg=ChatterBot>=1.1.0.dev4", "git+git://github.com/gunthercox/chatterbot-corpus@master#egg=chatterbot_corpus",
"kaggle", "mathparse>=0.1,<0.2",
"https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.1.0/en_core_web_sm-3.1.0.tar.gz#egg=en_core_web_sm", "nltk>=3.2,<4.0",
"https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.1.0/en_core_web_md-3.1.0.tar.gz#egg=en_core_web_md" "pint>=0.8.1",
"python-dateutil>=2.8,<2.9",
"pyyaml>=5.3,<5.4",
"sqlalchemy>=1.3,<1.4",
"pytz",
"https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.3.1/en_core_web_sm-2.3.1.tar.gz#egg=en_core_web_sm",
"https://github.com/explosion/spacy-models/releases/download/en_core_web_md-2.3.1/en_core_web_md-2.3.1.tar.gz#egg=en_core_web_md",
"spacy>=2.3,<2.4",
"kaggle"
], ],
"short": "Local Chatbot run on machine learning", "short": "Local Chatbot run on machine learning",
"end_user_data_statement": "This cog only stores anonymous conversations data; no End User Data is stored.", "end_user_data_statement": "This cog only stores anonymous conversations data; no End User Data is stored.",

@ -0,0 +1,12 @@
git+git://github.com/gunthercox/chatterbot-corpus@master#egg=chatterbot_corpus
mathparse>=0.1,<0.2
nltk>=3.2,<4.0
pint>=0.8.1
python-dateutil>=2.8,<2.9
pyyaml>=5.3,<5.4
sqlalchemy>=1.3,<1.4
pytz
spacy>=2.3,<2.4
https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.3.1/en_core_web_sm-2.3.1.tar.gz#egg=en_core_web_sm
https://github.com/explosion/spacy-models/releases/download/en_core_web_md-2.3.1/en_core_web_md-2.3.1.tar.gz#egg=en_core_web_md
# https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-2.3.1/en_core_web_lg-2.3.1.tar.gz#egg=en_core_web_lg

@ -5,7 +5,7 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(SQLStorageAdapter, self).__init__(**kwargs) super(SQLStorageAdapter, self).__init__(**kwargs)
from sqlalchemy import create_engine, inspect from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
self.database_uri = kwargs.get("database_uri", False) self.database_uri = kwargs.get("database_uri", False)
@ -18,7 +18,9 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter):
if not self.database_uri: if not self.database_uri:
self.database_uri = "sqlite:///db.sqlite3" self.database_uri = "sqlite:///db.sqlite3"
self.engine = create_engine(self.database_uri, connect_args={"check_same_thread": False}) self.engine = create_engine(
self.database_uri, convert_unicode=True, connect_args={"check_same_thread": False}
)
if self.database_uri.startswith("sqlite://"): if self.database_uri.startswith("sqlite://"):
from sqlalchemy.engine import Engine from sqlalchemy.engine import Engine
@ -29,7 +31,7 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter):
dbapi_connection.execute("PRAGMA journal_mode=WAL") dbapi_connection.execute("PRAGMA journal_mode=WAL")
dbapi_connection.execute("PRAGMA synchronous=NORMAL") dbapi_connection.execute("PRAGMA synchronous=NORMAL")
if not inspect(self.engine).has_table("Statement"): if not self.engine.dialect.has_table(self.engine, "Statement"):
self.create_database() self.create_database()
self.Session = sessionmaker(bind=self.engine, expire_on_commit=True) self.Session = sessionmaker(bind=self.engine, expire_on_commit=True)

@ -1,6 +1,5 @@
import asyncio import asyncio
import json import json
import logging
import os import os
import pathlib import pathlib
from abc import ABC from abc import ABC
@ -14,8 +13,6 @@ from redbot.core import Config, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.data_manager import bundled_data_path, cog_data_path from redbot.core.data_manager import bundled_data_path, cog_data_path
log = logging.getLogger("red.fox_v3.conquest")
class Conquest(commands.Cog): class Conquest(commands.Cog):
""" """
@ -56,20 +53,14 @@ class Conquest(commands.Cog):
self.current_map = await self.config.current_map() self.current_map = await self.config.current_map()
if self.current_map: if self.current_map:
if not await self.current_map_load(): await self.current_map_load()
await self.config.current_map.clear()
async def current_map_load(self): async def current_map_load(self):
map_data_path = self.asset_path / self.current_map / "data.json" map_data_path = self.asset_path / self.current_map / "data.json"
if not map_data_path.exists():
log.warning(f"{map_data_path} does not exist. Clearing current map")
return False
with map_data_path.open() as mapdata: with map_data_path.open() as mapdata:
self.map_data: dict = json.load(mapdata) self.map_data: dict = json.load(mapdata)
self.ext = self.map_data["extension"] self.ext = self.map_data["extension"]
self.ext_format = "JPEG" if self.ext.upper() == "JPG" else self.ext.upper() self.ext_format = "JPEG" if self.ext.upper() == "JPG" else self.ext.upper()
return True
@commands.group() @commands.group()
async def conquest(self, ctx: commands.Context): async def conquest(self, ctx: commands.Context):

@ -1,7 +1,7 @@
{ {
"maps": [ "maps": [
"simple", "simple_blank_map",
"ck2", "test",
"HoI" "test2"
] ]
} }

@ -441,7 +441,6 @@ class FIFO(commands.Cog):
self.scheduler.print_jobs(out=cp) self.scheduler.print_jobs(out=cp)
out = cp.string out = cp.string
out=out.replace("*","\*")
if out: if out:
if len(out) > 2000: if len(out) > 2000:

@ -90,7 +90,6 @@ things_for_fakemessage_to_steal = [
"content", "content",
"nonce", "nonce",
"reference", "reference",
"_edited_timestamp" # New 7/23/21
] ]
things_fakemessage_sets_by_default = { things_fakemessage_sets_by_default = {
@ -143,7 +142,7 @@ class FakeMessage(discord.Message):
self._update( self._update(
{ {
"mention_roles": self.raw_role_mentions, "mention_roles": self.raw_role_mentions,
"mentions": [{"id": _id} for _id in self.raw_mentions], "mentions": self.raw_mentions,
} }
) )

@ -1,13 +1,17 @@
import logging
from datetime import date, timedelta from datetime import date, timedelta
from typing import Literal from typing import Literal, Optional
import discord import discord
from redbot.cogs.mutes import Mutes
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 redbot.core.commands import Cog from redbot.core.commands import Cog
from redbot.core.utils import AsyncIter from redbot.core.utils import AsyncIter
from redbot.core.utils.chat_formatting import pagify from redbot.core.utils.chat_formatting import pagify
log = logging.getLogger("red.fox_v3.flag")
class Flag(Cog): class Flag(Cog):
""" """
@ -19,7 +23,7 @@ class Flag(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 = {"days": 31, "dm": True, "flags": {}} default_guild = {"days": 31, "dm": True, "flags": {}, "mutethreshold": 0}
self.config.register_global(**default_global) self.config.register_global(**default_global)
self.config.register_guild(**default_guild) self.config.register_guild(**default_guild)
@ -78,6 +82,29 @@ class Flag(Cog):
"DM-ing members when they get a flag is now set to **{}**".format(not dm) "DM-ing members when they get a flag is now set to **{}**".format(not dm)
) )
@checks.admin_or_permissions(manage_guild=True)
@flagset.command(name="mute")
async def flagset_mute(self, ctx: commands.Context, count: int):
"""
Sets the threshold for flags to mute the user.
Only works when the core mute cog is configured properly.
Set to zero to disable.
"""
if count < 0:
await ctx.send_help()
return
await self.config.guild(ctx.guild).mutethreshold.set(count)
if count:
await ctx.maybe_send_embed(
"Will now attempt to mute people after **{}** flags".format(count)
)
else:
await ctx.maybe_send_embed("Muting disabled.")
@staticmethod @staticmethod
def _flag_template(): def _flag_template():
return {"reason": "", "expireyear": 0, "expiremonth": 0, "expireday": 0} return {"reason": "", "expireyear": 0, "expiremonth": 0, "expireday": 0}
@ -91,7 +118,8 @@ class Flag(Cog):
await self._check_flags(guild) await self._check_flags(guild)
flag = self._flag_template() flag = self._flag_template()
expire_date = date.today() + timedelta(days=await self.config.guild(guild).days()) mute_days = await self.config.guild(guild).days()
expire_date = date.today() + timedelta(mute_days)
flag["reason"] = reason flag["reason"] = reason
flag["expireyear"] = expire_date.year flag["expireyear"] = expire_date.year
@ -106,6 +134,12 @@ class Flag(Cog):
if str(member.id) not in flags: if str(member.id) not in flags:
flags[str(member.id)] = [] flags[str(member.id)] = []
flags[str(member.id)].append(flag) flags[str(member.id)].append(flag)
flag_count = len(flags[str(member.id)])
mute_threshold = await self.config.guild(guild).mutethreshold()
if 0 < mute_threshold <= flag_count:
able_to_mute = await self._attempt_mute(ctx, member, mute_days)
log.debug(f"Mute attempt: {able_to_mute}")
outembed = await self._list_flags(member) outembed = await self._list_flags(member)
@ -166,6 +200,17 @@ class Flag(Cog):
for page in pagify(out): for page in pagify(out):
await ctx.send(page) await ctx.send(page)
async def _attempt_mute(self, ctx, member: discord.Member, days):
mutes: Optional[Mutes] = self.bot.get_cog("Mutes")
if mutes is None:
log.info("Mutes cog not loaded, cannot mute")
return False
return await mutes.mute(ctx, member, f'{days} days [Flag] Exceeded mute threshold')
# return await mutes.mute_user(
# member.guild, author, member, until, "[Flag] Exceeded mute threshold"
# )
async def _list_flags(self, member: discord.Member): async def _list_flags(self, member: discord.Member):
"""Returns a pretty embed of flags on a member""" """Returns a pretty embed of flags on a member"""
flags = await self.config.guild(member.guild).flags.get_raw(str(member.id), default=[]) flags = await self.config.guild(member.guild).flags.get_raw(str(member.id), default=[])

@ -28,12 +28,9 @@ async def get_channel_counts(category, guild):
online_num = members - offline_num online_num = members - offline_num
# Gets count of actual users # Gets count of actual users
human_num = members - bot_num human_num = members - bot_num
# count amount of premium subs/nitro subs.
boosters = guild.premium_subscription_count
return { return {
"members": members, "members": members,
"humans": human_num, "humans": human_num,
"boosters": boosters,
"bots": bot_num, "bots": bot_num,
"roles": roles_num, "roles": roles_num,
"channels": channels_num, "channels": channels_num,
@ -61,7 +58,6 @@ class InfoChannel(Cog):
self.default_channel_names = { self.default_channel_names = {
"members": "Members: {count}", "members": "Members: {count}",
"humans": "Humans: {count}", "humans": "Humans: {count}",
"boosters": "Boosters: {count}",
"bots": "Bots: {count}", "bots": "Bots: {count}",
"roles": "Roles: {count}", "roles": "Roles: {count}",
"channels": "Channels: {count}", "channels": "Channels: {count}",
@ -174,7 +170,6 @@ class InfoChannel(Cog):
Valid Types are: Valid Types are:
- `members`: Total members on the server - `members`: Total members on the server
- `humans`: Total members that aren't bots - `humans`: Total members that aren't bots
- `boosters`: Total amount of boosters
- `bots`: Total bots - `bots`: Total bots
- `roles`: Total number of roles - `roles`: Total number of roles
- `channels`: Total number of channels excluding infochannels, - `channels`: Total number of channels excluding infochannels,
@ -229,7 +224,6 @@ class InfoChannel(Cog):
Valid Types are: Valid Types are:
- `members`: Total members on the server - `members`: Total members on the server
- `humans`: Total members that aren't bots - `humans`: Total members that aren't bots
- `boosters`: Total amount of boosters
- `bots`: Total bots - `bots`: Total bots
- `roles`: Total number of roles - `roles`: Total number of roles
- `channels`: Total number of channels excluding infochannels - `channels`: Total number of channels excluding infochannels
@ -447,7 +441,6 @@ class InfoChannel(Cog):
guild, guild,
members=True, members=True,
humans=True, humans=True,
boosters=True,
bots=True, bots=True,
roles=True, roles=True,
channels=True, channels=True,
@ -504,16 +497,14 @@ class InfoChannel(Cog):
guild_data = await self.config.guild(guild).all() guild_data = await self.config.guild(guild).all()
to_update = ( to_update = (
kwargs.keys() & [key for key, value in guild_data["enabled_channels"].items() if value] kwargs.keys() & guild_data["enabled_channels"].keys()
) # Value in kwargs doesn't matter ) # Value in kwargs doesn't matter
if to_update or extra_roles: log.debug(f"{to_update=}")
log.debug(f"{to_update=}\n"
f"{extra_roles=}")
if to_update or extra_roles:
category = guild.get_channel(guild_data["category_id"]) category = guild.get_channel(guild_data["category_id"])
if category is None: if category is None:
log.debug('Channel category is missing, updating must be off')
return # Nothing to update, must be off return # Nothing to update, must be off
channel_data = await get_channel_counts(category, guild) channel_data = await get_channel_counts(category, guild)

@ -40,20 +40,16 @@ class LoveCalculator(Cog):
log.debug(f"{resp=}") log.debug(f"{resp=}")
soup_object = BeautifulSoup(resp, "html.parser") soup_object = BeautifulSoup(resp, "html.parser")
description = soup_object.find("div", class_="result__score") description = soup_object.find("div", class_="result__score").get_text()
if description is None: if description is None:
description = "Dr. Love is busy right now" description = "Dr. Love is busy right now"
else: else:
description = description.get_text().strip() description = description.strip()
result_image = soup_object.find("img", class_="result__image").get("src") result_image = soup_object.find("img", class_="result__image").get("src")
result_text = soup_object.find("div", class_="result-text") result_text = soup_object.find("div", class_="result-text").get_text()
if result_text is None:
result_text = f"{x} and {y} aren't compatible 😔"
else:
result_text = result_text.get_text()
result_text = " ".join(result_text.split()) result_text = " ".join(result_text.split())
try: try:

@ -77,13 +77,11 @@ class LastSeen(Cog):
return return
last_seen = self.get_date_time(last_seen) last_seen = self.get_date_time(last_seen)
embed = discord.Embed( # embed = discord.Embed(
description="{} was last seen at this date and time".format(member.display_name), # description="{} was last seen at this date and time".format(member.display_name),
timestamp=last_seen, # timestamp=last_seen)
color=await self.bot.get_embed_color(ctx),
)
# embed = discord.Embed(timestamp=last_seen, color=await self.bot.get_embed_color(ctx)) embed = discord.Embed(timestamp=last_seen, color=await self.bot.get_embed_color(ctx))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.Cog.listener() @commands.Cog.listener()

@ -32,7 +32,6 @@ class RecyclingPlant(Cog):
x = 0 x = 0
reward = 0 reward = 0
timeoutcount = 0
await ctx.send( await ctx.send(
"{0} has signed up for a shift at the Recycling Plant! Type ``exit`` to terminate it early.".format( "{0} has signed up for a shift at the Recycling Plant! Type ``exit`` to terminate it early.".format(
ctx.author.display_name ctx.author.display_name
@ -54,25 +53,14 @@ class RecyclingPlant(Cog):
return m.author == ctx.author and m.channel == ctx.channel return m.author == ctx.author and m.channel == ctx.channel
try: try:
answer = await self.bot.wait_for("message", timeout=20, check=check) answer = await self.bot.wait_for("message", timeout=120, check=check)
except asyncio.TimeoutError: except asyncio.TimeoutError:
answer = None answer = None
if answer is None: if answer is None:
if timeoutcount == 2: await ctx.send(
await ctx.send( "``{}`` fell down the conveyor belt to be sorted again!".format(used["object"])
"{} slacked off at work, so they were sacked with no pay.".format( )
ctx.author.display_name
)
)
break
else:
await ctx.send(
"{} is slacking, and if they carry on not working, they'll be fired.".format(
ctx.author.display_name
)
)
timeoutcount += 1
elif answer.content.lower().strip() == used["action"]: elif answer.content.lower().strip() == used["action"]:
await ctx.send( await ctx.send(
"Congratulations! You put ``{}`` down the correct chute! (**+50**)".format( "Congratulations! You put ``{}`` down the correct chute! (**+50**)".format(

@ -177,3 +177,7 @@ class SCP(Cog):
msg = "http://www.scp-wiki.net/log-of-unexplained-locations" msg = "http://www.scp-wiki.net/log-of-unexplained-locations"
await ctx.maybe_send_embed(msg) await ctx.maybe_send_embed(msg)
def setup(bot):
bot.add_cog(SCP(bot))

@ -6,7 +6,6 @@ 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.bot import Red
from redbot.core.commands import Cog from redbot.core.commands import Cog
from redbot.core.utils.chat_formatting import pagify
log = logging.getLogger("red.fox_v3.stealemoji") log = logging.getLogger("red.fox_v3.stealemoji")
# Replaced with discord.Asset.read() # Replaced with discord.Asset.read()
@ -100,8 +99,7 @@ class StealEmoji(Cog):
await ctx.maybe_send_embed("No stolen emojis yet") await ctx.maybe_send_embed("No stolen emojis yet")
return return
for page in pagify(emoj, delims=[" "]): await ctx.maybe_send_embed(emoj)
await ctx.maybe_send_embed(page)
@checks.is_owner() @checks.is_owner()
@stealemoji.command(name="notify") @stealemoji.command(name="notify")

@ -37,7 +37,7 @@ class Timerole(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 = {"announce": None, "reapply": True, "roles": {}, "skipbots": True} default_guild = {"announce": None, "reapply": True, "roles": {}}
default_rolemember = {"had_role": False, "check_again_time": None} default_rolemember = {"had_role": False, "check_again_time": None}
self.config.register_global(**default_global) self.config.register_global(**default_global)
@ -92,9 +92,6 @@ class Timerole(Cog):
await ctx.maybe_send_embed("Error: Invalid time string.") await ctx.maybe_send_embed("Error: Invalid time string.")
return return
if parsed_time is None:
return await ctx.maybe_send_embed("Error: Invalid time string.")
days = parsed_time.days days = parsed_time.days
hours = parsed_time.seconds // 60 // 60 hours = parsed_time.seconds // 60 // 60
@ -154,14 +151,6 @@ class Timerole(Cog):
await self.config.guild(guild).reapply.set(not current_setting) await self.config.guild(guild).reapply.set(not current_setting)
await ctx.maybe_send_embed(f"Reapplying roles is now set to: {not current_setting}") await ctx.maybe_send_embed(f"Reapplying roles is now set to: {not current_setting}")
@timerole.command()
async def skipbots(self, ctx: commands.Context):
"""Toggle skipping bots when adding/removing roles. Defaults to True"""
guild = ctx.guild
current_setting = await self.config.guild(guild).skipbots()
await self.config.guild(guild).skipbots.set(not current_setting)
await ctx.maybe_send_embed(f"Skipping bots is now set to: {not current_setting}")
@timerole.command() @timerole.command()
async def delrole(self, ctx: commands.Context, role: discord.Role): async def delrole(self, ctx: commands.Context, role: discord.Role):
"""Deletes a role from being added/removed after specified time""" """Deletes a role from being added/removed after specified time"""
@ -210,7 +199,6 @@ class Timerole(Cog):
remove_results = "" remove_results = ""
reapply = all_guilds[guild_id]["reapply"] reapply = all_guilds[guild_id]["reapply"]
role_dict = all_guilds[guild_id]["roles"] role_dict = all_guilds[guild_id]["roles"]
skipbots = all_guilds[guild_id]["skipbots"]
if not any(role_dict.values()): # No roles if not any(role_dict.values()): # No roles
log.debug(f"No roles are configured for guild: {guild}") log.debug(f"No roles are configured for guild: {guild}")
@ -220,10 +208,6 @@ class Timerole(Cog):
# log.debug(f"{all_mr=}") # log.debug(f"{all_mr=}")
async for member in AsyncIter(guild.members, steps=10): async for member in AsyncIter(guild.members, steps=10):
if member.bot and skipbots:
continue
addlist = [] addlist = []
removelist = [] removelist = []
@ -311,11 +295,8 @@ class Timerole(Cog):
log.exception("Failed Adding Roles") log.exception("Failed Adding Roles")
add_results += f"{member.display_name} : **(Failed Adding Roles)**\n" add_results += f"{member.display_name} : **(Failed Adding Roles)**\n"
else: else:
add_results += ( add_results += " \n".join(
" \n".join( f"{member.display_name} : {role.name}" for role in add_roles
f"{member.display_name} : {role.name}" for role in add_roles
)
+ "\n"
) )
for role_id in addlist: for role_id in addlist:
await self.config.custom( await self.config.custom(
@ -329,11 +310,8 @@ class Timerole(Cog):
log.exception("Failed Removing Roles") log.exception("Failed Removing Roles")
remove_results += f"{member.display_name} : **(Failed Removing Roles)**\n" remove_results += f"{member.display_name} : **(Failed Removing Roles)**\n"
else: else:
remove_results += ( remove_results += " \n".join(
" \n".join( f"{member.display_name} : {role.name}" for role in remove_roles
f"{member.display_name} : {role.name}" for role in remove_roles
)
+ "\n"
) )
for role_id in removelist: for role_id in removelist:
await self.config.custom( await self.config.custom(

@ -1,35 +1,11 @@
import io import io
import logging
from typing import Optional, TYPE_CHECKING
import discord import discord
from discord.ext.commands import BadArgument, Converter
from gtts import gTTS from gtts import gTTS
from gtts.lang import _fallback_deprecated_lang, tts_langs
from redbot.core import Config, commands from redbot.core import Config, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.commands import Cog from redbot.core.commands import Cog
log = logging.getLogger("red.fox_v3.tts")
if TYPE_CHECKING:
ISO639Converter = str
else:
class ISO639Converter(Converter):
async def convert(self, ctx, argument) -> str:
lang = _fallback_deprecated_lang(argument)
try:
langs = tts_langs()
if lang not in langs:
raise BadArgument("Language not supported: %s" % lang)
except RuntimeError as e:
log.debug(str(e), exc_info=True)
log.warning(str(e))
return lang
class TTS(Cog): class TTS(Cog):
""" """
@ -42,7 +18,7 @@ class TTS(Cog):
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 = {"language": "en"} default_guild = {}
self.config.register_global(**default_global) self.config.register_global(**default_global)
self.config.register_guild(**default_guild) self.config.register_guild(**default_guild)
@ -51,29 +27,13 @@ class TTS(Cog):
"""Nothing to delete""" """Nothing to delete"""
return return
@commands.mod()
@commands.command()
async def ttslang(self, ctx: commands.Context, lang: ISO639Converter):
"""
Sets the default language for TTS in this guild.
Default is `en` for English
"""
await self.config.guild(ctx.guild).language.set(lang)
await ctx.send(f"Default tts language set to {lang}")
@commands.command(aliases=["t2s", "text2"]) @commands.command(aliases=["t2s", "text2"])
async def tts( async def tts(self, ctx: commands.Context, *, text: str):
self, ctx: commands.Context, lang: Optional[ISO639Converter] = None, *, text: str
):
""" """
Send Text to speech messages as an mp3 Send Text to speech messages as an mp3
""" """
if lang is None:
lang = await self.config.guild(ctx.guild).language()
mp3_fp = io.BytesIO() mp3_fp = io.BytesIO()
tts = gTTS(text, lang=lang) tts = gTTS(text, lang="en")
tts.write_to_fp(mp3_fp) tts.write_to_fp(mp3_fp)
mp3_fp.seek(0) mp3_fp.seek(0)
await ctx.send(file=discord.File(mp3_fp, "text.mp3")) await ctx.send(file=discord.File(mp3_fp, "text.mp3"))

Loading…
Cancel
Save