You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Fox-V3/chatter/chat.py

209 lines
6.9 KiB

import asyncio
7 years ago
import pathlib
7 years ago
from datetime import datetime, timedelta
import discord
from redbot.core import Config
from redbot.core import commands
7 years ago
from redbot.core.data_manager import cog_data_path
7 years ago
from chatter.chatterbot import ChatBot
7 years ago
from chatter.chatterbot.comparisons import levenshtein_distance
6 years ago
from chatter.chatterbot.response_selection import get_first_response
7 years ago
from chatter.chatterbot.trainers import ListTrainer
from typing import Any
Cog: Any = getattr(commands, "Cog", object)
7 years ago
class Chatter(Cog):
"""
This cog trains a chatbot that will talk like members of your Guild
"""
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=6710497116116101114)
default_global = {}
6 years ago
default_guild = {"whitelist": None, "days": 1}
7 years ago
path: pathlib.Path = cog_data_path(self)
data_path = path / ("database.sqlite3")
7 years ago
self.chatbot = ChatBot(
"ChatterBot",
6 years ago
storage_adapter="chatter.chatterbot.storage.SQLStorageAdapter",
7 years ago
database=str(data_path),
6 years ago
statement_comparison_function=levenshtein_distance,
response_selection_method=get_first_response,
logic_adapters=[
6 years ago
"chatter.chatterbot.logic.BestMatch",
6 years ago
{
6 years ago
"import_path": "chatter.chatterbot.logic.LowConfidenceAdapter",
"threshold": 0.65,
"default_response": ":thinking:",
},
],
)
7 years ago
self.chatbot.set_trainer(ListTrainer)
self.config.register_global(**default_global)
self.config.register_guild(**default_guild)
7 years ago
7 years ago
self.loop = asyncio.get_event_loop()
7 years ago
async def _get_conversation(self, ctx, in_channel: discord.TextChannel = None):
"""
7 years ago
Compiles all conversation in the Guild this bot can get it's hands on
Currently takes a stupid long time
Returns a list of text
"""
7 years ago
out = [[]]
7 years ago
after = datetime.today() - timedelta(days=(await self.config.guild(ctx.guild).days()))
7 years ago
def new_message(msg, sent, out_in):
if sent is None:
return False
if len(out_in) < 2:
return False
6 years ago
return msg.created_at - sent >= timedelta(
hours=3
) # This should be configurable perhaps
7 years ago
7 years ago
for channel in ctx.guild.text_channels:
7 years ago
if in_channel:
channel = in_channel
await ctx.send("Gathering {}".format(channel.mention))
user = None
7 years ago
i = 0
send_time = None
try:
7 years ago
7 years ago
async for message in channel.history(limit=None, reverse=True, after=after):
7 years ago
# if message.author.bot: # Skip bot messages
# continue
if new_message(message, send_time, out[i]):
out.append([])
i += 1
user = None
else:
send_time = message.created_at + timedelta(seconds=1)
if user == message.author:
7 years ago
out[i][-1] += "\n" + message.clean_content
else:
user = message.author
7 years ago
out[i].append(message.clean_content)
except discord.Forbidden:
pass
except discord.HTTPException:
pass
7 years ago
7 years ago
if in_channel:
break
7 years ago
return out
7 years ago
7 years ago
def _train(self, data):
try:
7 years ago
for convo in data:
self.chatbot.train(convo)
except:
return False
return True
7 years ago
7 years ago
@commands.group(invoke_without_command=False)
async def chatter(self, ctx: commands.Context):
"""
Base command for this cog. Check help for the commands list.
"""
if ctx.invoked_subcommand is None:
7 years ago
pass
7 years ago
7 years ago
@chatter.command()
async def age(self, ctx: commands.Context, days: int):
7 years ago
"""
Sets the number of days to look back
Will train on 1 day otherwise
"""
7 years ago
7 years ago
await self.config.guild(ctx.guild).days.set(days)
await ctx.send("Success")
7 years ago
7 years ago
@chatter.command()
async def backup(self, ctx, backupname):
"""
Backup your training data to a json for later use
"""
7 years ago
await ctx.send("Backing up data, this may take a while")
6 years ago
future = await self.loop.run_in_executor(
None, self.chatbot.trainer.export_for_training, "./{}.json".format(backupname)
)
7 years ago
7 years ago
if future:
await ctx.send("Backup successful!")
else:
await ctx.send("Error occurred :(")
7 years ago
7 years ago
@chatter.command()
7 years ago
async def train(self, ctx: commands.Context, channel: discord.TextChannel):
"""
Trains the bot based on language in this guild
"""
7 years ago
await ctx.send("Warning: The cog may use significant RAM or CPU if trained on large data sets.\n"
"Additionally, large sets will use more disk space to save the trained data.\n\n"
"If you experience issues, clear your trained data and train again on a smaller scope.")
7 years ago
conversation = await self._get_conversation(ctx, channel)
7 years ago
if not conversation:
await ctx.send("Failed to gather training data")
return
7 years ago
6 years ago
await ctx.send(
"Gather successful! Training begins now\n(**This will take a long time, be patient**)"
)
7 years ago
embed = discord.Embed(title="Loading")
7 years ago
embed.set_image(url="http://www.loop.universaleverything.com/animations/1295.gif")
7 years ago
temp_message = await ctx.send(embed=embed)
7 years ago
future = await self.loop.run_in_executor(None, self._train, conversation)
7 years ago
7 years ago
try:
await temp_message.delete()
except:
pass
7 years ago
7 years ago
if future:
7 years ago
await ctx.send("Training successful!")
else:
7 years ago
await ctx.send("Error occurred :(")
7 years ago
7 years ago
async def on_message(self, message: discord.Message):
7 years ago
"""
Credit to https://github.com/Twentysix26/26-Cogs/blob/master/cleverbot/cleverbot.py
for on_message recognition of @bot
"""
author = message.author
7 years ago
try:
guild: discord.Guild = message.guild
except AttributeError: # Not a guild message
return
channel: discord.TextChannel = message.channel
7 years ago
7 years ago
if author.id != self.bot.user.id:
to_strip = "@" + guild.me.display_name + " "
7 years ago
text = message.clean_content
if not text.startswith(to_strip):
return
text = text.replace(to_strip, "", 1)
async with channel.typing():
7 years ago
future = await self.loop.run_in_executor(None, self.chatbot.get_response, text)
7 years ago
if future and str(future):
7 years ago
await channel.send(str(future))
else:
6 years ago
await channel.send(":thinking:")