Merge branch 'master' into cogguide_develop

cogguide_develop
bobloy 4 years ago
commit f6d3f2c4ea

@ -95,7 +95,8 @@ pip install --no-deps "chatterbot>=1.1"
#### Step 1: Built-in Downloader #### Step 1: Built-in Downloader
``` ```
[p]cog install <Fox> Chatter [p]repo add Fox https://github.com/bobloy/Fox-V3
[p]cog install Fox chatter
``` ```
#### Step 2: Install Requirements #### Step 2: Install Requirements

@ -10,7 +10,7 @@ from chatterbot import ChatBot
from chatterbot.comparisons import JaccardSimilarity, LevenshteinDistance, SpacySimilarity from chatterbot.comparisons import JaccardSimilarity, LevenshteinDistance, SpacySimilarity
from chatterbot.response_selection import get_random_response from chatterbot.response_selection import get_random_response
from chatterbot.trainers import ChatterBotCorpusTrainer, ListTrainer, UbuntuCorpusTrainer from chatterbot.trainers import ChatterBotCorpusTrainer, ListTrainer, UbuntuCorpusTrainer
from redbot.core import Config, commands from redbot.core import Config, checks, commands
from redbot.core.commands import Cog from redbot.core.commands import Cog
from redbot.core.data_manager import cog_data_path from redbot.core.data_manager import cog_data_path
from redbot.core.utils.predicates import MessagePredicate from redbot.core.utils.predicates import MessagePredicate
@ -159,7 +159,9 @@ class Chatter(Cog):
return out return out
def _train_ubuntu(self): def _train_ubuntu(self):
trainer = UbuntuCorpusTrainer(self.chatbot) trainer = UbuntuCorpusTrainer(
self.chatbot, ubuntu_corpus_data_directory=cog_data_path(self) / "ubuntu_data"
)
trainer.train() trainer.train()
return True return True
@ -191,6 +193,7 @@ class Chatter(Cog):
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
pass pass
@checks.admin()
@chatter.command(name="channel") @chatter.command(name="channel")
async def chatter_channel( async def chatter_channel(
self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None
@ -210,6 +213,7 @@ class Chatter(Cog):
await self.config.guild(ctx.guild).chatchannel.set(channel.id) await self.config.guild(ctx.guild).chatchannel.set(channel.id)
await ctx.maybe_send_embed(f"Chat channel is now {channel.mention}") await ctx.maybe_send_embed(f"Chat channel is now {channel.mention}")
@checks.is_owner()
@chatter.command(name="cleardata") @chatter.command(name="cleardata")
async def chatter_cleardata(self, ctx: commands.Context, confirm: bool = False): async def chatter_cleardata(self, ctx: commands.Context, confirm: bool = False):
""" """
@ -242,6 +246,7 @@ class Chatter(Cog):
await ctx.tick() await ctx.tick()
@checks.is_owner()
@chatter.command(name="algorithm", aliases=["algo"]) @chatter.command(name="algorithm", aliases=["algo"])
async def chatter_algorithm( async def chatter_algorithm(
self, ctx: commands.Context, algo_number: int, threshold: float = None self, ctx: commands.Context, algo_number: int, threshold: float = None
@ -275,6 +280,7 @@ class Chatter(Cog):
await ctx.tick() await ctx.tick()
@checks.is_owner()
@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):
""" """
@ -312,6 +318,7 @@ class Chatter(Cog):
f"Model has been switched to {self.tagger_language.ISO_639_1}" f"Model has been switched to {self.tagger_language.ISO_639_1}"
) )
@checks.is_owner()
@chatter.command(name="minutes") @chatter.command(name="minutes")
async def minutes(self, ctx: commands.Context, minutes: int): async def minutes(self, ctx: commands.Context, minutes: int):
""" """
@ -327,6 +334,7 @@ class Chatter(Cog):
await ctx.tick() await ctx.tick()
@checks.is_owner()
@chatter.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):
""" """
@ -341,6 +349,7 @@ class Chatter(Cog):
await self.config.guild(ctx.guild).days.set(days) await self.config.guild(ctx.guild).days.set(days)
await ctx.tick() await ctx.tick()
@checks.is_owner()
@chatter.command(name="backup") @chatter.command(name="backup")
async def backup(self, ctx, backupname): async def backup(self, ctx, backupname):
""" """
@ -362,6 +371,7 @@ class Chatter(Cog):
else: else:
await ctx.maybe_send_embed("Error occurred :(") await ctx.maybe_send_embed("Error occurred :(")
@checks.is_owner()
@chatter.command(name="trainubuntu") @chatter.command(name="trainubuntu")
async def chatter_train_ubuntu(self, ctx: commands.Context, confirmation: bool = False): async def chatter_train_ubuntu(self, ctx: commands.Context, confirmation: bool = False):
""" """
@ -383,6 +393,7 @@ class Chatter(Cog):
else: else:
await ctx.send("Error occurred :(") await ctx.send("Error occurred :(")
@checks.is_owner()
@chatter.command(name="trainenglish") @chatter.command(name="trainenglish")
async def chatter_train_english(self, ctx: commands.Context): async def chatter_train_english(self, ctx: commands.Context):
""" """
@ -396,6 +407,7 @@ class Chatter(Cog):
else: else:
await ctx.maybe_send_embed("Error occurred :(") await ctx.maybe_send_embed("Error occurred :(")
@checks.is_owner()
@chatter.command() @chatter.command()
async def train(self, ctx: commands.Context, channel: discord.TextChannel): async def train(self, ctx: commands.Context, channel: discord.TextChannel):
""" """

@ -1,5 +1,6 @@
import itertools
import logging import logging
from datetime import datetime, timedelta, tzinfo from datetime import datetime, timedelta, tzinfo, MAXYEAR
from typing import Optional, Union from typing import Optional, Union
import discord import discord
@ -10,7 +11,7 @@ from apscheduler.schedulers.base import STATE_PAUSED, STATE_RUNNING
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 TimedeltaConverter from redbot.core.commands import TimedeltaConverter
from redbot.core.utils.chat_formatting import pagify from redbot.core.utils.chat_formatting import humanize_list, humanize_timedelta, pagify
from .datetime_cron_converters import CronConverter, DatetimeConverter, TimezoneConverter from .datetime_cron_converters import CronConverter, DatetimeConverter, TimezoneConverter
from .task import Task from .task import Task
@ -26,6 +27,7 @@ async def _execute_task(task_state):
task = Task(**task_state) task = Task(**task_state)
if await task.load_from_config(): if await task.load_from_config():
return await task.execute() return await task.execute()
log.warning(f"Failed to load data on {task_state=}")
return False return False
@ -37,6 +39,27 @@ def _disassemble_job_id(job_id: str):
return job_id.split("_") return job_id.split("_")
def _get_run_times(job: Job, now: datetime = None):
"""
Computes the scheduled run times between ``next_run_time`` and ``now`` (inclusive).
Modified to be asynchronous and yielding instead of all-or-nothing
"""
if not job.next_run_time:
raise StopIteration()
if now is None:
now = datetime(MAXYEAR, 12, 31, 23, 59, 59, 999999, tzinfo=job.next_run_time.tzinfo)
yield from _get_run_times(job, now)
raise StopIteration()
next_run_time = job.next_run_time
while next_run_time and next_run_time <= now:
yield next_run_time
next_run_time = job.trigger.get_next_fire_time(next_run_time, now)
class FIFO(commands.Cog): class FIFO(commands.Cog):
""" """
Simple Scheduling Cog Simple Scheduling Cog
@ -173,6 +196,30 @@ class FIFO(commands.Cog):
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
pass pass
@fifo.command(name="checktask", aliases=["checkjob", "check"])
async def fifo_checktask(self, ctx: commands.Context, task_name: str):
"""Returns the next 10 scheduled executions of the task"""
task = Task(task_name, ctx.guild.id, self.config, bot=self.bot)
await task.load_from_config()
if task.data is None:
await ctx.maybe_send_embed(
f"Task by the name of {task_name} is not found in this guild"
)
return
job = await self._get_job(task)
if job is None:
await ctx.maybe_send_embed("No job scheduled for this task")
return
now = datetime.now(job.next_run_time.tzinfo)
times = [
humanize_timedelta(timedelta=x - now)
for x in itertools.islice(_get_run_times(job), 10)
]
await ctx.maybe_send_embed("\n\n".join(times))
@fifo.command(name="set") @fifo.command(name="set")
async def fifo_set( async def fifo_set(
self, self,
@ -324,7 +371,7 @@ class FIFO(commands.Cog):
out = "" out = ""
all_tasks = await self.config.guild(ctx.guild).tasks() all_tasks = await self.config.guild(ctx.guild).tasks()
for task_name, task_data in all_tasks.items(): for task_name, task_data in all_tasks.items():
out += f"{task_name}: {task_data}\n" out += f"{task_name}: {task_data}\n\n"
if out: if out:
if len(out) > 2000: if len(out) > 2000:
@ -394,6 +441,7 @@ class FIFO(commands.Cog):
return return
await task.clear_triggers() await task.clear_triggers()
await self._remove_job(task)
await ctx.tick() await ctx.tick()
@fifo.group(name="addtrigger", aliases=["trigger"]) @fifo.group(name="addtrigger", aliases=["trigger"])

@ -39,7 +39,7 @@ def parse_triggers(data: Union[Dict, None]):
return None return None
if len(data["triggers"]) > 1: # Multiple triggers if len(data["triggers"]) > 1: # Multiple triggers
return OrTrigger(get_trigger(t_data) for t_data in data["triggers"]) return OrTrigger([get_trigger(t_data) for t_data in data["triggers"]])
return get_trigger(data["triggers"][0]) return get_trigger(data["triggers"][0])
@ -108,20 +108,6 @@ class Task:
"tzinfo": getattr(t["tzinfo"], "zone", None), "tzinfo": getattr(t["tzinfo"], "zone", None),
} }
) )
# triggers.append(
# {
# "type": t["type"],
# "time_data": {
# "year": dt.year,
# "month": dt.month,
# "day": dt.day,
# "hour": dt.hour,
# "minute": dt.minute,
# "second": dt.second,
# "tzinfo": dt.tzinfo,
# },
# }
# )
continue continue
if t["type"] == "cron": if t["type"] == "cron":
@ -239,20 +225,26 @@ class Task:
async def execute(self): async def execute(self):
if not self.data or not self.get_command_str(): if not self.data or not self.get_command_str():
log.warning(f"Could not execute task due to data problem: {self.data=}") log.warning(f"Could not execute Task[{self.name}] due to data problem: {self.data=}")
return False return False
guild: discord.Guild = self.bot.get_guild(self.guild_id) # used for get_prefix guild: discord.Guild = self.bot.get_guild(self.guild_id) # used for get_prefix
if guild is None: if guild is None:
log.warning(f"Could not execute task due to missing guild: {self.guild_id}") log.warning(
f"Could not execute Task[{self.name}] due to missing guild: {self.guild_id}"
)
return False return False
channel: discord.TextChannel = guild.get_channel(self.channel_id) channel: discord.TextChannel = guild.get_channel(self.channel_id)
if channel is None: if channel is None:
log.warning(f"Could not execute task due to missing channel: {self.channel_id}") log.warning(
f"Could not execute Task[{self.name}] due to missing channel: {self.channel_id}"
)
return False return False
author: discord.User = guild.get_member(self.author_id) author: discord.User = guild.get_member(self.author_id)
if author is None: if author is None:
log.warning(f"Could not execute task due to missing author: {self.author_id}") log.warning(
f"Could not execute Task[{self.name}] due to missing author: {self.author_id}"
)
return False return False
actual_message: discord.Message = channel.last_message actual_message: discord.Message = channel.last_message
@ -267,15 +259,15 @@ class Task:
actual_message = await author.history(limit=1).flatten() actual_message = await author.history(limit=1).flatten()
if not actual_message: # Okay, the *author* has never sent a message? if not actual_message: # Okay, the *author* has never sent a message?
log.warning("No message found in channel cache yet, skipping execution") log.warning("No message found in channel cache yet, skipping execution")
return return False
actual_message = actual_message[0] actual_message = actual_message[0]
message = FakeMessage(actual_message) message = FakeMessage(actual_message)
# message = FakeMessage2 # message = FakeMessage2
message.author = author message.author = author
message.guild = guild # Just in case we got desperate message.guild = guild # Just in case we got desperate, see above
message.channel = channel message.channel = channel
message.id = time_snowflake(datetime.now()) # Pretend to be now message.id = time_snowflake(datetime.utcnow(), high=False) # Pretend to be now
message = neuter_message(message) message = neuter_message(message)
# absolutely weird that this takes a message object instead of guild # absolutely weird that this takes a message object instead of guild
@ -287,15 +279,21 @@ class Task:
message.content = f"{prefix}{self.get_command_str()}" message.content = f"{prefix}{self.get_command_str()}"
if not message.guild or not message.author or not message.content: if (
log.warning(f"Could not execute task due to message problem: {message}") not message.guild
or not message.author
or not message.content
or message.content == prefix
):
log.warning(f"Could not execute Task[{self.name}] due to message problem: {message}")
return False return False
new_ctx: commands.Context = await self.bot.get_context(message) new_ctx: commands.Context = await self.bot.get_context(message)
new_ctx.assume_yes = True new_ctx.assume_yes = True
if not new_ctx.valid: if not new_ctx.valid:
log.warning( log.warning(
f"Could not execute Task[{self.name}] due invalid context: {new_ctx.invoked_with}" f"Could not execute Task[{self.name}] due invalid context: "
f"{new_ctx.invoked_with=} {new_ctx.prefix=} {new_ctx.command=}"
) )
return False return False

@ -8,9 +8,7 @@ from redbot.core.data_manager import cog_data_path
class Nudity(commands.Cog): class Nudity(commands.Cog):
""" """Monitor images for NSFW content and moves them to a nsfw channel if possible"""
V3 Cog Template
"""
def __init__(self, bot: Red): def __init__(self, bot: Red):
super().__init__() super().__init__()

@ -793,7 +793,7 @@ class PlantTycoon(commands.Cog):
pass pass
await asyncio.sleep(self.defaults["timers"]["notification"] * 60) await asyncio.sleep(self.defaults["timers"]["notification"] * 60)
def __unload(self): def cog_unload(self):
self.completion_task.cancel() self.completion_task.cancel()
# self.degradation_task.cancel() # self.degradation_task.cancel()
self.notification_task.cancel() self.notification_task.cancel()

@ -50,6 +50,7 @@ class StealEmoji(Cog):
default_global = { default_global = {
"stolemoji": {}, "stolemoji": {},
"guildbanks": [], "guildbanks": [],
"autobanked_guilds": [],
"on": False, "on": False,
"notify": 0, "notify": 0,
"autobank": False, "autobank": False,
@ -145,11 +146,54 @@ class StealEmoji(Cog):
await ctx.maybe_send_embed("AutoBanking is now " + str(not curr_setting)) await ctx.maybe_send_embed("AutoBanking is now " + str(not curr_setting))
@checks.is_owner()
@commands.guild_only()
@stealemoji.command(name="deleteserver", aliases=["deleteguild"])
async def se_deleteserver(self, ctx: commands.Context, guild_id=None):
"""Delete servers the bot is the owner of.
Useful for auto-generated guildbanks."""
if guild_id is None:
guild = ctx.guild
else:
guild = await self.bot.get_guild(guild_id)
if guild is None:
await ctx.maybe_send_embed("Failed to get guild, cancelling")
return
guild: discord.Guild
await ctx.maybe_send_embed(
f"Will attempt to delete {guild.name} ({guild.id})\n" f"Okay to continue? (yes/no)"
)
def check(m):
return m.author == ctx.author and m.channel == ctx.channel
try:
answer = await self.bot.wait_for("message", timeout=120, check=check)
except asyncio.TimeoutError:
await ctx.send("Timed out, canceling")
return
if answer.content.upper() not in ["Y", "YES"]:
await ctx.maybe_send_embed("Cancelling")
return
try:
await guild.delete()
except discord.Forbidden:
log.exception("No permission to delete. I'm probably not the guild owner")
await ctx.maybe_send_embed("No permission to delete. I'm probably not the guild owner")
except discord.HTTPException:
log.exception("Unexpected error when deleting guild")
await ctx.maybe_send_embed("Unexpected error when deleting guild")
else:
await self.bot.send_to_owners(f"Guild {guild.name} deleted")
@checks.is_owner() @checks.is_owner()
@commands.guild_only() @commands.guild_only()
@stealemoji.command(name="bank") @stealemoji.command(name="bank")
async def se_bank(self, ctx): async def se_bank(self, ctx):
"""Add current server as emoji bank""" """Add or remove current server as emoji bank"""
def check(m): def check(m):
return ( return (
@ -235,6 +279,9 @@ class StealEmoji(Cog):
return return
async with self.config.guildbanks() as guildbanks: async with self.config.guildbanks() as guildbanks:
guildbanks.append(guildbank.id) guildbanks.append(guildbank.id)
# Track generated guilds for easier deletion
async with self.config.autobanked_guilds() as autobanked_guilds:
autobanked_guilds.append(guildbank.id)
await asyncio.sleep(2) await asyncio.sleep(2)

@ -235,8 +235,8 @@ class Timerole(Cog):
has_roles = set(r.id for r in member.roles) has_roles = set(r.id for r in member.roles)
# Stop if they currently have or don't have the role, and mark had_role # Stop if they currently have or don't have the role, and mark had_role
if (role_id in has_roles and not role_data["remove"]) or ( if (int(role_id) in has_roles and not role_data["remove"]) or (
role_id not in has_roles and role_data["remove"] int(role_id) not in has_roles and role_data["remove"]
): ):
if not mr_dict["had_role"]: if not mr_dict["had_role"]:
await self.config.custom( await self.config.custom(

@ -56,7 +56,7 @@ class Werewolf(Cog):
"""Nothing to delete""" """Nothing to delete"""
return return
def __unload(self): def cog_unload(self):
log.debug("Unload called") log.debug("Unload called")
for game in self.games.values(): for game in self.games.values():
del game del game

Loading…
Cancel
Save