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 discord
from discord.ext.commands import RoleConverter, Greedy, CommandError, ArgumentParsingError
from discord.ext.commands.view import StringView
from redbot.core import Config, checks, commands
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):
# greedy = Greedy[RoleConverter]
view = StringView(content)
rc = RoleConverter()
# "Borrowed" from discord.ext.commands.Command._transform_greedy_pos
result = []
while not view.eof:
# for use with a manual undo
previous = view.index
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
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):
@ -108,7 +84,7 @@ class CCRole(commands.Cog):
# Roles to add
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"
)
@ -130,7 +106,7 @@ class CCRole(commands.Cog):
# Roles to remove
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"
)
try:
@ -148,7 +124,7 @@ class CCRole(commands.Cog):
# Roles to use
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"
)

@ -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
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
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)
[p]pipinstall --no-deps chatterbot>=1.1
```
#### 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
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
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
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.
Initial training is recommended to speed up its learning.

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

@ -19,7 +19,6 @@ from redbot.core.utils.predicates import MessagePredicate
from chatter.trainers import MovieTrainer, TwitterCorpusTrainer, UbuntuCorpusTrainer2
chatterbot_log = logging.getLogger("red.fox_v3.chatterbot")
log = logging.getLogger("red.fox_v3.chatter")
@ -30,12 +29,6 @@ def my_local_get_prefix(prefixes, content):
return None
class ENG_TRF:
ISO_639_1 = "en_core_web_trf"
ISO_639 = "eng"
ENGLISH_NAME = "English"
class ENG_LG:
ISO_639_1 = "en_core_web_lg"
ISO_639 = "eng"
@ -59,15 +52,12 @@ class Chatter(Cog):
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):
super().__init__()
self.bot = bot
self.config = Config.get_conf(self, identifier=6710497116116101114)
default_global = {"learning": True, "model_number": 0, "algo_number": 0, "threshold": 0.90}
self.default_guild = {
default_global = {"learning": True}
default_guild = {
"whitelist": None,
"days": 1,
"convo_delta": 15,
@ -80,16 +70,16 @@ class Chatter(Cog):
# TODO: Move training_model and similarity_algo to config
# TODO: Add an option to see current settings
self.tagger_language = ENG_SM
self.tagger_language = ENG_MD
self.similarity_algo = SpacySimilarity
self.similarity_threshold = 0.90
self.chatbot = None
self.chatbot = self._create_chatbot()
# self.chatbot.set_trainer(ListTrainer)
# self.trainer = ListTrainer(self.chatbot)
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()
@ -102,18 +92,6 @@ class Chatter(Cog):
"""Nothing to delete"""
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):
return ChatBot(
@ -126,7 +104,7 @@ class Chatter(Cog):
logic_adapters=["chatterbot.logic.BestMatch"],
maximum_similarity_threshold=self.similarity_threshold,
tagger_language=self.tagger_language,
logger=chatterbot_log,
logger=log,
)
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
):
"""
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
1: Jaccard
2: Levenshtein
"""
algos = [SpacySimilarity, JaccardSimilarity, LevenshteinDistance]
if algo_number < 0 or algo_number > 2:
await ctx.send_help()
return
@ -368,11 +349,8 @@ class Chatter(Cog):
return
else:
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():
self.chatbot = self._create_chatbot()
@ -382,18 +360,20 @@ class Chatter(Cog):
@chatter.command(name="model")
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
1: Medium (Requires additional setup)
1: Medium
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()
return
if model_number >= 0:
if model_number == 2:
await ctx.maybe_send_embed(
"Additional requirements needed. See guide before continuing.\n" "Continue?"
)
@ -406,8 +386,7 @@ class Chatter(Cog):
if not pred.result:
return
self.tagger_language = self.models[model_number]
await self.config.model_number.set(model_number)
self.tagger_language = models[model_number]
async with ctx.typing():
self.chatbot = self._create_chatbot()
@ -416,13 +395,7 @@ class Chatter(Cog):
)
@commands.is_owner()
@chatter.group(name="trainset")
async def chatter_trainset(self, ctx: commands.Context):
"""Commands for configuring training"""
pass
@commands.is_owner()
@chatter_trainset.command(name="minutes")
@chatter.command(name="minutes")
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
@ -438,7 +411,7 @@ class Chatter(Cog):
await ctx.tick()
@commands.is_owner()
@chatter_trainset.command(name="age")
@chatter.command(name="age")
async def age(self, ctx: commands.Context, days: int):
"""
Sets the number of days to look back
@ -657,7 +630,7 @@ class Chatter(Cog):
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
ctx: commands.Context = await self.bot.get_context(message)
@ -732,9 +705,7 @@ class Chatter(Cog):
)
replying = None
if (
"reply" not in self._guild_cache[guild.id] and self.default_guild["reply"]
) or self._guild_cache[guild.id]["reply"]:
if self._guild_cache[guild.id]["reply"]:
if message != ctx.channel.last_message:
replying = message

@ -7,10 +7,18 @@
"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`",
"requirements": [
"git+git://github.com/bobloy/ChatterBot@fox#egg=ChatterBot>=1.1.0.dev4",
"kaggle",
"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",
"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"
"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",
"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",
"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):
super(SQLStorageAdapter, self).__init__(**kwargs)
from sqlalchemy import create_engine, inspect
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
self.database_uri = kwargs.get("database_uri", False)
@ -18,7 +18,9 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter):
if not self.database_uri:
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://"):
from sqlalchemy.engine import Engine
@ -29,7 +31,7 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter):
dbapi_connection.execute("PRAGMA journal_mode=WAL")
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.Session = sessionmaker(bind=self.engine, expire_on_commit=True)

@ -1,6 +1,5 @@
import asyncio
import json
import logging
import os
import pathlib
from abc import ABC
@ -14,8 +13,6 @@ from redbot.core import Config, commands
from redbot.core.bot import Red
from redbot.core.data_manager import bundled_data_path, cog_data_path
log = logging.getLogger("red.fox_v3.conquest")
class Conquest(commands.Cog):
"""
@ -56,20 +53,14 @@ class Conquest(commands.Cog):
self.current_map = await self.config.current_map()
if self.current_map:
if not await self.current_map_load():
await self.config.current_map.clear()
await self.current_map_load()
async def current_map_load(self):
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:
self.map_data: dict = json.load(mapdata)
self.ext = self.map_data["extension"]
self.ext_format = "JPEG" if self.ext.upper() == "JPG" else self.ext.upper()
return True
@commands.group()
async def conquest(self, ctx: commands.Context):

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

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

@ -90,7 +90,6 @@ things_for_fakemessage_to_steal = [
"content",
"nonce",
"reference",
"_edited_timestamp" # New 7/23/21
]
things_fakemessage_sets_by_default = {
@ -143,7 +142,7 @@ class FakeMessage(discord.Message):
self._update(
{
"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 typing import Literal
from typing import Literal, Optional
import discord
from redbot.cogs.mutes import Mutes
from redbot.core import Config, checks, commands
from redbot.core.bot import Red
from redbot.core.commands import Cog
from redbot.core.utils import AsyncIter
from redbot.core.utils.chat_formatting import pagify
log = logging.getLogger("red.fox_v3.flag")
class Flag(Cog):
"""
@ -19,7 +23,7 @@ class Flag(Cog):
self.bot = bot
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
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_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)
)
@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
def _flag_template():
return {"reason": "", "expireyear": 0, "expiremonth": 0, "expireday": 0}
@ -91,7 +118,8 @@ class Flag(Cog):
await self._check_flags(guild)
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["expireyear"] = expire_date.year
@ -106,6 +134,12 @@ class Flag(Cog):
if str(member.id) not in flags:
flags[str(member.id)] = []
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)
@ -166,6 +200,17 @@ class Flag(Cog):
for page in pagify(out):
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):
"""Returns a pretty embed of flags on a member"""
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
# Gets count of actual users
human_num = members - bot_num
# count amount of premium subs/nitro subs.
boosters = guild.premium_subscription_count
return {
"members": members,
"humans": human_num,
"boosters": boosters,
"bots": bot_num,
"roles": roles_num,
"channels": channels_num,
@ -61,7 +58,6 @@ class InfoChannel(Cog):
self.default_channel_names = {
"members": "Members: {count}",
"humans": "Humans: {count}",
"boosters": "Boosters: {count}",
"bots": "Bots: {count}",
"roles": "Roles: {count}",
"channels": "Channels: {count}",
@ -174,7 +170,6 @@ class InfoChannel(Cog):
Valid Types are:
- `members`: Total members on the server
- `humans`: Total members that aren't bots
- `boosters`: Total amount of boosters
- `bots`: Total bots
- `roles`: Total number of roles
- `channels`: Total number of channels excluding infochannels,
@ -229,7 +224,6 @@ class InfoChannel(Cog):
Valid Types are:
- `members`: Total members on the server
- `humans`: Total members that aren't bots
- `boosters`: Total amount of boosters
- `bots`: Total bots
- `roles`: Total number of roles
- `channels`: Total number of channels excluding infochannels
@ -447,7 +441,6 @@ class InfoChannel(Cog):
guild,
members=True,
humans=True,
boosters=True,
bots=True,
roles=True,
channels=True,
@ -504,16 +497,14 @@ class InfoChannel(Cog):
guild_data = await self.config.guild(guild).all()
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
if to_update or extra_roles:
log.debug(f"{to_update=}\n"
f"{extra_roles=}")
log.debug(f"{to_update=}")
if to_update or extra_roles:
category = guild.get_channel(guild_data["category_id"])
if category is None:
log.debug('Channel category is missing, updating must be off')
return # Nothing to update, must be off
channel_data = await get_channel_counts(category, guild)

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

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

@ -32,7 +32,6 @@ class RecyclingPlant(Cog):
x = 0
reward = 0
timeoutcount = 0
await ctx.send(
"{0} has signed up for a shift at the Recycling Plant! Type ``exit`` to terminate it early.".format(
ctx.author.display_name
@ -54,25 +53,14 @@ class RecyclingPlant(Cog):
return m.author == ctx.author and m.channel == ctx.channel
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:
answer = None
if answer is None:
if timeoutcount == 2:
await ctx.send(
"{} 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
await ctx.send(
"``{}`` fell down the conveyor belt to be sorted again!".format(used["object"])
)
elif answer.content.lower().strip() == used["action"]:
await ctx.send(
"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"
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.bot import Red
from redbot.core.commands import Cog
from redbot.core.utils.chat_formatting import pagify
log = logging.getLogger("red.fox_v3.stealemoji")
# Replaced with discord.Asset.read()
@ -100,8 +99,7 @@ class StealEmoji(Cog):
await ctx.maybe_send_embed("No stolen emojis yet")
return
for page in pagify(emoj, delims=[" "]):
await ctx.maybe_send_embed(page)
await ctx.maybe_send_embed(emoj)
@checks.is_owner()
@stealemoji.command(name="notify")

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

@ -1,35 +1,11 @@
import io
import logging
from typing import Optional, TYPE_CHECKING
import discord
from discord.ext.commands import BadArgument, Converter
from gtts import gTTS
from gtts.lang import _fallback_deprecated_lang, tts_langs
from redbot.core import Config, commands
from redbot.core.bot import Red
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):
"""
@ -42,7 +18,7 @@ class TTS(Cog):
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
default_global = {}
default_guild = {"language": "en"}
default_guild = {}
self.config.register_global(**default_global)
self.config.register_guild(**default_guild)
@ -51,29 +27,13 @@ class TTS(Cog):
"""Nothing to delete"""
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"])
async def tts(
self, ctx: commands.Context, lang: Optional[ISO639Converter] = None, *, text: str
):
async def tts(self, ctx: commands.Context, *, text: str):
"""
Send Text to speech messages as an mp3
"""
if lang is None:
lang = await self.config.guild(ctx.guild).language()
mp3_fp = io.BytesIO()
tts = gTTS(text, lang=lang)
tts = gTTS(text, lang="en")
tts.write_to_fp(mp3_fp)
mp3_fp.seek(0)
await ctx.send(file=discord.File(mp3_fp, "text.mp3"))

Loading…
Cancel
Save