diff --git a/chatter/README.md b/chatter/README.md index 06331b2..bcd6cbc 100644 --- a/chatter/README.md +++ b/chatter/README.md @@ -74,77 +74,35 @@ If you get an error at this step, stop and skip to one of the manual methods bel #### Step 2: Install additional dependencies -Assuming the previous commands had no error, you can now use `pipinstall` to add the remaining dependencies. +Here you need to decide which training models you want to have available to you. -NOTE: This method is not the intended use case for `pipinstall` and may stop working in the future. +Shutdown the bot and run any number of these in the console: ``` -[p]pipinstall --no-deps chatterbot>=1.1 -``` - -#### Step 3: Load the cog and get started - -``` -[p]load chatter -``` +python -m spacy download en_core_web_sm # ~15 MB -### Windows - Manually -#### Step 1: Built-in Downloader +python -m spacy download en_core_web_md # ~50 MB -You need to get a copy of the requirements.txt provided with chatter, I recommend this method. +python -m spacy download en_core_web_lg # ~750 MB (CPU Optimized) -``` -[p]repo add Fox https://github.com/bobloy/Fox-V3 +python -m spacy download en_core_web_trf # ~500 MB (GPU Optimized) ``` -#### 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 +#### Step 3: Load the cog and get started ``` -[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 - -#### 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 +### Windows - Manually +Deprecated -``` -[p]load chatter -``` +### Linux - Manually +Deprecated # Configuration -Chatter works out the the box without any training by learning as it goes, +Chatter works out 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. diff --git a/chatter/__init__.py b/chatter/__init__.py index 9447c6a..663dadf 100644 --- a/chatter/__init__.py +++ b/chatter/__init__.py @@ -1,8 +1,10 @@ from .chat import Chatter -def setup(bot): - bot.add_cog(Chatter(bot)) +async def setup(bot): + cog = Chatter(bot) + await cog.initialize() + bot.add_cog(cog) # __all__ = ( diff --git a/chatter/chat.py b/chatter/chat.py index 5ad3efb..4655da8 100644 --- a/chatter/chat.py +++ b/chatter/chat.py @@ -19,6 +19,7 @@ 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") @@ -29,6 +30,12 @@ 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" @@ -52,12 +59,15 @@ 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} - default_guild = { + default_global = {"learning": True, "model_number": 0, "algo_number": 0, "threshold": 0.90} + self.default_guild = { "whitelist": None, "days": 1, "convo_delta": 15, @@ -70,16 +80,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_MD + self.tagger_language = ENG_SM self.similarity_algo = SpacySimilarity self.similarity_threshold = 0.90 - self.chatbot = self._create_chatbot() + self.chatbot = None # self.chatbot.set_trainer(ListTrainer) # self.trainer = ListTrainer(self.chatbot) self.config.register_global(**default_global) - self.config.register_guild(**default_guild) + self.config.register_guild(**self.default_guild) self.loop = asyncio.get_event_loop() @@ -92,6 +102,18 @@ 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( @@ -104,7 +126,7 @@ class Chatter(Cog): logic_adapters=["chatterbot.logic.BestMatch"], maximum_similarity_threshold=self.similarity_threshold, tagger_language=self.tagger_language, - logger=log, + logger=chatterbot_log, ) async def _get_conversation(self, ctx, in_channels: List[discord.TextChannel]): @@ -328,15 +350,12 @@ class Chatter(Cog): self, ctx: commands.Context, algo_number: int, threshold: float = None ): """ - Switch the active logic algorithm to one of the three. Default after reload is Spacy + Switch the active logic algorithm to one of the three. Default 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 @@ -349,8 +368,11 @@ 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() @@ -360,20 +382,18 @@ 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 after reload is Medium + Switch the active model to one of the three. Default is Small 0: Small - 1: Medium + 1: Medium (Requires additional setup) 2: Large (Requires additional setup) + 3. Accurate (Requires additional setup) """ - - models = [ENG_SM, ENG_MD, ENG_LG] - - if model_number < 0 or model_number > 2: + if model_number < 0 or model_number > 3: await ctx.send_help() return - if model_number == 2: + if model_number >= 0: await ctx.maybe_send_embed( "Additional requirements needed. See guide before continuing.\n" "Continue?" ) @@ -386,7 +406,8 @@ class Chatter(Cog): if not pred.result: return - self.tagger_language = models[model_number] + self.tagger_language = self.models[model_number] + await self.config.model_number.set(model_number) async with ctx.typing(): self.chatbot = self._create_chatbot() @@ -395,7 +416,13 @@ class Chatter(Cog): ) @commands.is_owner() - @chatter.command(name="minutes") + @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") 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 @@ -411,7 +438,7 @@ class Chatter(Cog): await ctx.tick() @commands.is_owner() - @chatter.command(name="age") + @chatter_trainset.command(name="age") async def age(self, ctx: commands.Context, days: int): """ Sets the number of days to look back @@ -630,7 +657,7 @@ class Chatter(Cog): guild: discord.Guild = getattr(message, "guild", None) - if await self.bot.cog_disabled_in_guild(self, guild): + if guild is None or await self.bot.cog_disabled_in_guild(self, guild): return ctx: commands.Context = await self.bot.get_context(message) @@ -705,7 +732,9 @@ class Chatter(Cog): ) replying = None - if self._guild_cache[guild.id]["reply"]: + if ( + "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: replying = message diff --git a/chatter/info.json b/chatter/info.json index fc31e7c..a9bb96b 100644 --- a/chatter/info.json +++ b/chatter/info.json @@ -7,17 +7,7 @@ "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/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", + "git+git://github.com/bobloy/ChatterBot@fox#egg=ChatterBot", "kaggle" ], "short": "Local Chatbot run on machine learning", diff --git a/chatter/requirements.txt b/chatter/requirements.txt deleted file mode 100644 index 88cd662..0000000 --- a/chatter/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -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 \ No newline at end of file diff --git a/chatter/storage_adapters.py b/chatter/storage_adapters.py index 4de2f00..706f96f 100644 --- a/chatter/storage_adapters.py +++ b/chatter/storage_adapters.py @@ -5,7 +5,7 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter): def __init__(self, **kwargs): super(SQLStorageAdapter, self).__init__(**kwargs) - from sqlalchemy import create_engine + from sqlalchemy import create_engine, inspect from sqlalchemy.orm import sessionmaker self.database_uri = kwargs.get("database_uri", False) @@ -18,9 +18,7 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter): if not self.database_uri: self.database_uri = "sqlite:///db.sqlite3" - self.engine = create_engine( - self.database_uri, convert_unicode=True, connect_args={"check_same_thread": False} - ) + self.engine = create_engine(self.database_uri, connect_args={"check_same_thread": False}) if self.database_uri.startswith("sqlite://"): from sqlalchemy.engine import Engine @@ -31,7 +29,7 @@ class MyDumbSQLStorageAdapter(SQLStorageAdapter): dbapi_connection.execute("PRAGMA journal_mode=WAL") dbapi_connection.execute("PRAGMA synchronous=NORMAL") - if not self.engine.dialect.has_table(self.engine, "Statement"): + if not inspect(self.engine).has_table("Statement"): self.create_database() self.Session = sessionmaker(bind=self.engine, expire_on_commit=True) diff --git a/recyclingplant/recyclingplant.py b/recyclingplant/recyclingplant.py index cc7bf57..3751648 100644 --- a/recyclingplant/recyclingplant.py +++ b/recyclingplant/recyclingplant.py @@ -32,6 +32,7 @@ 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 @@ -53,14 +54,25 @@ class RecyclingPlant(Cog): return m.author == ctx.author and m.channel == ctx.channel try: - answer = await self.bot.wait_for("message", timeout=120, check=check) + answer = await self.bot.wait_for("message", timeout=20, check=check) except asyncio.TimeoutError: answer = None if answer is None: - await ctx.send( - "``{}`` fell down the conveyor belt to be sorted again!".format(used["object"]) - ) + 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 elif answer.content.lower().strip() == used["action"]: await ctx.send( "Congratulations! You put ``{}`` down the correct chute! (**+50**)".format(