commit
a671e81af8
@ -1,2 +1,5 @@
|
||||
.idea/
|
||||
*.pyc
|
||||
venv/
|
||||
v-data/
|
||||
database.sqlite3
|
||||
|
@ -0,0 +1,9 @@
|
||||
from redbot.core.bot import Red
|
||||
|
||||
from .announcedaily import AnnounceDaily
|
||||
|
||||
|
||||
def setup(bot: Red):
|
||||
daily = AnnounceDaily(bot)
|
||||
bot.add_cog(daily)
|
||||
bot.loop.create_task(daily.check_day())
|
@ -0,0 +1,253 @@
|
||||
import asyncio
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
import discord
|
||||
from redbot.core import Config, checks, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.utils.chat_formatting import pagify, box
|
||||
|
||||
DEFAULT_MESSAGES = [
|
||||
# "Example message. Uncomment and overwrite to use",
|
||||
# "Example message 2. Each message is in quotes and separated by a comma"
|
||||
]
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
class AnnounceDaily(Cog):
|
||||
"""
|
||||
Send daily announcements
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.path = str(cog_data_path(self)).replace('\\', '/')
|
||||
|
||||
self.image_path = self.path + "/"
|
||||
|
||||
self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
|
||||
default_global = {
|
||||
'messages': [],
|
||||
'images': [],
|
||||
'time': {'hour': 0, 'minute': 0, 'second': 0}
|
||||
}
|
||||
default_guild = {
|
||||
"channelid": None
|
||||
}
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
async def _get_msgs(self):
|
||||
return DEFAULT_MESSAGES + await self.config.messages()
|
||||
|
||||
@commands.group(name="announcedaily", aliases=['annd'])
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
@commands.guild_only()
|
||||
async def _ad(self, ctx: commands.Context):
|
||||
"""
|
||||
Base command for managing AnnounceDaily settings
|
||||
|
||||
Do `[p]help annd <subcommand>` for more details
|
||||
"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
pass
|
||||
|
||||
@commands.command()
|
||||
@checks.guildowner()
|
||||
@commands.guild_only()
|
||||
async def runannounce(self, ctx: commands.Context):
|
||||
"""Manually run the daily announcement"""
|
||||
|
||||
await self.send_announcements()
|
||||
await ctx.send("Success")
|
||||
|
||||
@_ad.command()
|
||||
async def setchannel(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||
"""
|
||||
Set the announcement channel for this server
|
||||
|
||||
Don't pass a channel to clear this server of receiving announcements
|
||||
"""
|
||||
if channel is not None:
|
||||
await self.config.guild(ctx.guild).channelid.set(channel.id)
|
||||
await ctx.send("Announcement channel has been set to {}".format(channel.mention))
|
||||
else:
|
||||
await self.config.guild(ctx.guild).channelid.set(None)
|
||||
await ctx.send("Announcement channel has been cleared")
|
||||
|
||||
@_ad.command()
|
||||
async def addmsg(self, ctx: commands.Context, *, msg):
|
||||
"""
|
||||
Add a message to the pool of announcement messages
|
||||
"""
|
||||
async with self.config.messages() as msgs:
|
||||
msgs.append(msg)
|
||||
|
||||
await ctx.send("Message successfully added!")
|
||||
|
||||
@_ad.command()
|
||||
async def addimg(self, ctx: commands.Context, filename=None):
|
||||
"""
|
||||
Add an image to the pool of announcement images
|
||||
|
||||
You must attach an image while executing this command
|
||||
"""
|
||||
if ctx.message.attachments:
|
||||
att_ = ctx.message.attachments[0]
|
||||
try:
|
||||
h = att_.height
|
||||
except AttributeError:
|
||||
await ctx.send("You must attach an image, no other file will be accepted")
|
||||
return
|
||||
|
||||
if filename is None:
|
||||
filename = att_.filename
|
||||
|
||||
try:
|
||||
# with open(self.image_path + filename, 'w') as f:
|
||||
# await att_.save(f)
|
||||
await att_.save(self.image_path + filename)
|
||||
except discord.NotFound:
|
||||
await ctx.send("Did you delete the message? Cause I couldn't download the attachment")
|
||||
except discord.HTTPException:
|
||||
await ctx.send("Failed to download the attachment, please try again")
|
||||
else:
|
||||
async with self.config.images() as images:
|
||||
if filename in images:
|
||||
await ctx.send("Image {} has been overwritten!".format(filename))
|
||||
else:
|
||||
images.append(filename)
|
||||
await ctx.send("Image {} has been added!".format(filename))
|
||||
else:
|
||||
await ctx.send("You must attach an image when sending this command")
|
||||
|
||||
@_ad.command()
|
||||
async def listmsg(self, ctx: commands.Context):
|
||||
"""
|
||||
List all registered announcement messages
|
||||
"""
|
||||
messages = await self.config.messages()
|
||||
for page in pagify("\n".join("{} - {}".format(key, image) for key, image in enumerate(messages))):
|
||||
await ctx.send(box(page))
|
||||
await ctx.send("Done!")
|
||||
|
||||
@_ad.command()
|
||||
async def listimg(self, ctx: commands.Context):
|
||||
"""
|
||||
List all registered announcement immages
|
||||
"""
|
||||
images = await self.config.images()
|
||||
for page in pagify("\n".join(images)):
|
||||
await ctx.send(box(page))
|
||||
await ctx.send("Done!")
|
||||
|
||||
@_ad.command()
|
||||
async def delmsg(self, ctx: commands.Context, index: int):
|
||||
"""
|
||||
Remove a message from the announcement pool
|
||||
|
||||
Must provide the index of the message, which can be found by using `[p]annd listmsg`
|
||||
"""
|
||||
async with self.config.messages() as messages:
|
||||
try:
|
||||
out = messages.pop(index)
|
||||
except IndexError:
|
||||
await ctx.send("Invalid index, check valid indexes with `listmsg` command")
|
||||
return
|
||||
|
||||
await ctx.send("The following message was removed:\n```{}```".format(out))
|
||||
|
||||
@_ad.command()
|
||||
async def delimg(self, ctx: commands.Context, filename: str):
|
||||
"""
|
||||
Remove an image from the announcement pool
|
||||
|
||||
Does not delete the file from the disk, so you may have to clean it up occasionally
|
||||
"""
|
||||
async with self.config.images() as images:
|
||||
if filename not in images:
|
||||
await ctx.send("This file doesn't exist")
|
||||
else:
|
||||
images.remove(filename)
|
||||
await ctx.send("Successfully removed {}".format(filename))
|
||||
|
||||
@_ad.command()
|
||||
async def settime(self, ctx: commands.Context, minutes_from_now: int):
|
||||
"""
|
||||
Set the daily announcement time
|
||||
|
||||
It will first announce at the time you provided, then it will repeat every 24 hours
|
||||
"""
|
||||
ann_time = datetime.now() + timedelta(minutes=minutes_from_now)
|
||||
|
||||
h = ann_time.hour
|
||||
m = ann_time.minute
|
||||
s = ann_time.second
|
||||
await self.config.time.set({'hour': h, 'minute': m, 'second': s})
|
||||
|
||||
await ctx.send("Announcements time has been set to {}::{}::{} every day\n"
|
||||
"**Changes will apply after next scheduled announcement or reload**".format(h, m, s))
|
||||
|
||||
async def send_announcements(self):
|
||||
messages = await self._get_msgs()
|
||||
images = await self.config.images()
|
||||
|
||||
total = len(messages) + len(images)
|
||||
if total < 1:
|
||||
return
|
||||
|
||||
x = random.randint(0, total - 1)
|
||||
|
||||
if x >= len(messages):
|
||||
x -= len(messages)
|
||||
choice = images[x]
|
||||
choice = open(self.image_path + choice, 'rb')
|
||||
is_image = True
|
||||
else:
|
||||
choice = messages[x]
|
||||
is_image = False
|
||||
|
||||
for guild in self.bot.guilds:
|
||||
channel = await self.config.guild(guild).channelid()
|
||||
if channel is None:
|
||||
continue
|
||||
channel = guild.get_channel(channel)
|
||||
if channel is None:
|
||||
continue
|
||||
|
||||
if is_image:
|
||||
await channel.send(file=discord.File(choice))
|
||||
else:
|
||||
await channel.send(choice)
|
||||
|
||||
async def check_day(self):
|
||||
while self is self.bot.get_cog("AnnounceDaily"):
|
||||
tomorrow = datetime.now() + timedelta(days=1)
|
||||
time = await self.config.time()
|
||||
h, m, s = time['hour'], time['minute'], time['second']
|
||||
midnight = datetime(year=tomorrow.year, month=tomorrow.month,
|
||||
day=tomorrow.day, hour=h, minute=m, second=s)
|
||||
|
||||
print("Sleeping for {} seconds".format((midnight - datetime.now()).seconds))
|
||||
await asyncio.sleep((midnight - datetime.now()).seconds)
|
||||
|
||||
if self is not self.bot.get_cog("AnnounceDaily"):
|
||||
print("Announce canceled, cog has been lost")
|
||||
return
|
||||
|
||||
await self.send_announcements()
|
||||
|
||||
await asyncio.sleep(3)
|
||||
|
||||
# [p]setchannel #channelname - Set the announcement channel per server
|
||||
# [p]addmsg <message goes here> - Adds a msg to the pool
|
||||
# [p]addimg http://imgurl.com/image.jpg - Adds an image to the pool
|
||||
# [p]listmsg - Lists all messages in the pool
|
||||
# [p]listimg - Unsure about this one, but would probably just post all the images
|
||||
# [p]delmsg - Remove msg from pool
|
||||
# [p]delimg - Remove image from pool
|
||||
# [p]settime <x> - S
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Send daily announcements to all servers at a specified times",
|
||||
"hidden": true,
|
||||
"install_msg": "Thank you for installing AnnounceDaily! Get started with `[p]load announcedaily` and `[p]help AnnounceDaily`",
|
||||
"requirements": [],
|
||||
"short": "Send daily announcements",
|
||||
"tags": [
|
||||
"bobloy"
|
||||
]
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
from redbot.core.bot import Red
|
||||
|
||||
from .audiotrivia import AudioTrivia
|
||||
|
||||
|
||||
async def setup(bot: Red):
|
||||
if bot.get_cog("Trivia"):
|
||||
print("Trivia is already loaded, attempting to unload it first")
|
||||
bot.remove_cog("Trivia")
|
||||
await bot.remove_loaded_package("trivia")
|
||||
bot.unload_extension("trivia")
|
||||
|
||||
bot.add_cog(AudioTrivia(bot))
|
@ -0,0 +1,74 @@
|
||||
"""Module to manage audio trivia sessions."""
|
||||
import asyncio
|
||||
|
||||
import lavalink
|
||||
from redbot.cogs.trivia import TriviaSession
|
||||
|
||||
|
||||
class AudioSession(TriviaSession):
|
||||
"""Class to run a session of audio trivia"""
|
||||
|
||||
def __init__(self, ctx, question_list: dict, settings: dict, player: lavalink.Player):
|
||||
super().__init__(ctx, question_list, settings)
|
||||
|
||||
self.player = player
|
||||
|
||||
@classmethod
|
||||
def start(cls, ctx, question_list, settings, player: lavalink.Player = None):
|
||||
session = cls(ctx, question_list, settings, player)
|
||||
loop = ctx.bot.loop
|
||||
session._task = loop.create_task(session.run())
|
||||
return session
|
||||
|
||||
async def run(self):
|
||||
"""Run the audio trivia session.
|
||||
|
||||
In order for the trivia session to be stopped correctly, this should
|
||||
only be called internally by `TriviaSession.start`.
|
||||
"""
|
||||
await self._send_startup_msg()
|
||||
max_score = self.settings["max_score"]
|
||||
delay = self.settings["delay"]
|
||||
timeout = self.settings["timeout"]
|
||||
for question, answers in self._iter_questions():
|
||||
async with self.ctx.typing():
|
||||
await asyncio.sleep(3)
|
||||
self.count += 1
|
||||
await self.player.stop()
|
||||
|
||||
msg = "**Question number {}!**\n\nName this audio!".format(self.count)
|
||||
await self.ctx.send(msg)
|
||||
# print("Audio question: {}".format(question))
|
||||
|
||||
# await self.ctx.invoke(self.audio.play(ctx=self.ctx, query=question))
|
||||
# ctx_copy = copy(self.ctx)
|
||||
|
||||
# await self.ctx.invoke(self.player.play, query=question)
|
||||
query = question.strip("<>")
|
||||
tracks = await self.player.get_tracks(query)
|
||||
seconds = tracks[0].length / 1000
|
||||
|
||||
if self.settings["repeat"] and seconds < delay:
|
||||
tot_length = seconds + 0
|
||||
while tot_length < delay:
|
||||
self.player.add(self.ctx.author, tracks[0])
|
||||
tot_length += seconds
|
||||
else:
|
||||
self.player.add(self.ctx.author, tracks[0])
|
||||
|
||||
if not self.player.current:
|
||||
await self.player.play()
|
||||
|
||||
continue_ = await self.wait_for_answer(answers, delay, timeout)
|
||||
if continue_ is False:
|
||||
break
|
||||
if any(score >= max_score for score in self.scores.values()):
|
||||
await self.end_game()
|
||||
break
|
||||
else:
|
||||
await self.ctx.send("There are no more questions!")
|
||||
await self.end_game()
|
||||
|
||||
async def end_game(self):
|
||||
await super().end_game()
|
||||
await self.player.disconnect()
|
@ -0,0 +1,212 @@
|
||||
import datetime
|
||||
import pathlib
|
||||
from typing import List
|
||||
|
||||
import lavalink
|
||||
import yaml
|
||||
from redbot.cogs.audio import Audio
|
||||
from redbot.cogs.trivia import LOG
|
||||
from redbot.cogs.trivia.trivia import InvalidListError, Trivia
|
||||
from redbot.core import commands, Config, checks
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
|
||||
from .audiosession import AudioSession
|
||||
|
||||
|
||||
class AudioTrivia(Trivia):
|
||||
"""
|
||||
Custom commands
|
||||
Creates commands used to display text and adjust roles
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.audio = None
|
||||
self.audioconf = Config.get_conf(self, identifier=651171001051118411410511810597, force_registration=True)
|
||||
|
||||
self.audioconf.register_guild(
|
||||
delay=30.0,
|
||||
repeat=True,
|
||||
)
|
||||
|
||||
@commands.group()
|
||||
@commands.guild_only()
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def atriviaset(self, ctx: commands.Context):
|
||||
"""Manage Audio Trivia settings."""
|
||||
audioset = self.audioconf.guild(ctx.guild)
|
||||
settings_dict = await audioset.all()
|
||||
msg = box(
|
||||
"**Audio settings**\n"
|
||||
"Answer time limit: {delay} seconds\n"
|
||||
"Repeat Short Audio: {repeat}"
|
||||
"".format(**settings_dict),
|
||||
lang="py",
|
||||
)
|
||||
await ctx.send(msg)
|
||||
|
||||
@atriviaset.command(name="delay")
|
||||
async def atriviaset_delay(self, ctx: commands.Context, seconds: float):
|
||||
"""Set the maximum seconds permitted to answer a question."""
|
||||
if seconds < 4.0:
|
||||
await ctx.send("Must be at least 4 seconds.")
|
||||
return
|
||||
settings = self.audioconf.guild(ctx.guild)
|
||||
await settings.delay.set(seconds)
|
||||
await ctx.send("Done. Maximum seconds to answer set to {}.".format(seconds))
|
||||
|
||||
@atriviaset.command(name="repeat")
|
||||
async def atriviaset_repeat(self, ctx: commands.Context, true_or_false: bool):
|
||||
"""Set whether or not short audio will be repeated"""
|
||||
settings = self.audioconf.guild(ctx.guild)
|
||||
await settings.repeat.set(true_or_false)
|
||||
await ctx.send("Done. Repeating short audio is now set to {}.".format(true_or_false))
|
||||
|
||||
@commands.group(invoke_without_command=True)
|
||||
@commands.guild_only()
|
||||
async def audiotrivia(self, ctx: commands.Context, *categories: str):
|
||||
"""Start trivia session on the specified category.
|
||||
|
||||
You may list multiple categories, in which case the trivia will involve
|
||||
questions from all of them.
|
||||
"""
|
||||
if not categories and ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
return
|
||||
|
||||
if self.audio is None:
|
||||
self.audio: Audio = self.bot.get_cog("Audio")
|
||||
|
||||
if self.audio is None:
|
||||
await ctx.send("Audio is not loaded. Load it and try again")
|
||||
return
|
||||
|
||||
categories = [c.lower() for c in categories]
|
||||
session = self._get_trivia_session(ctx.channel)
|
||||
if session is not None:
|
||||
await ctx.send("There is already an ongoing trivia session in this channel.")
|
||||
return
|
||||
|
||||
status = await self.audio.config.status()
|
||||
|
||||
if status:
|
||||
await ctx.send("I recommend disabling audio status with `{}audioset status`".format(ctx.prefix))
|
||||
|
||||
if not self.audio._player_check(ctx):
|
||||
try:
|
||||
if not ctx.author.voice.channel.permissions_for(ctx.me).connect or self.audio._userlimit(
|
||||
ctx.author.voice.channel
|
||||
):
|
||||
return await ctx.send("I don't have permission to connect to your channel."
|
||||
)
|
||||
await lavalink.connect(ctx.author.voice.channel)
|
||||
lavaplayer = lavalink.get_player(ctx.guild.id)
|
||||
lavaplayer.store("connect", datetime.datetime.utcnow())
|
||||
except AttributeError:
|
||||
return await ctx.send("Connect to a voice channel first.")
|
||||
|
||||
lavaplayer = lavalink.get_player(ctx.guild.id)
|
||||
lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno
|
||||
lavaplayer.store("guild", ctx.guild.id)
|
||||
|
||||
await self.audio._data_check(ctx)
|
||||
|
||||
if (
|
||||
not ctx.author.voice or ctx.author.voice.channel != lavaplayer.channel
|
||||
):
|
||||
return await ctx.send("You must be in the voice channel to use the audiotrivia command.")
|
||||
|
||||
trivia_dict = {}
|
||||
authors = []
|
||||
for category in reversed(categories):
|
||||
# We reverse the categories so that the first list's config takes
|
||||
# priority over the others.
|
||||
try:
|
||||
dict_ = self.get_audio_list(category)
|
||||
except FileNotFoundError:
|
||||
await ctx.send(
|
||||
"Invalid category `{0}`. See `{1}audiotrivia list`"
|
||||
" for a list of trivia categories."
|
||||
"".format(category, ctx.prefix)
|
||||
)
|
||||
except InvalidListError:
|
||||
await ctx.send(
|
||||
"There was an error parsing the trivia list for"
|
||||
" the `{}` category. It may be formatted"
|
||||
" incorrectly.".format(category)
|
||||
)
|
||||
else:
|
||||
trivia_dict.update(dict_)
|
||||
authors.append(trivia_dict.pop("AUTHOR", None))
|
||||
continue
|
||||
return
|
||||
if not trivia_dict:
|
||||
await ctx.send(
|
||||
"The trivia list was parsed successfully, however it appears to be empty!"
|
||||
)
|
||||
return
|
||||
settings = await self.conf.guild(ctx.guild).all()
|
||||
audiosettings = await self.audioconf.guild(ctx.guild).all()
|
||||
config = trivia_dict.pop("CONFIG", None)
|
||||
if config and settings["allow_override"]:
|
||||
settings.update(config)
|
||||
settings["lists"] = dict(zip(categories, reversed(authors)))
|
||||
|
||||
# Delay in audiosettings overwrites delay in settings
|
||||
combined_settings = {**settings, **audiosettings}
|
||||
session = AudioSession.start(ctx=ctx, question_list=trivia_dict, settings=combined_settings, player=lavaplayer)
|
||||
self.trivia_sessions.append(session)
|
||||
LOG.debug("New audio trivia session; #%s in %d", ctx.channel, ctx.guild.id)
|
||||
|
||||
@audiotrivia.command(name="list")
|
||||
@commands.guild_only()
|
||||
async def audiotrivia_list(self, ctx: commands.Context):
|
||||
"""List available trivia categories."""
|
||||
lists = set(p.stem for p in self._audio_lists())
|
||||
|
||||
msg = box("**Available trivia lists**\n\n{}".format(", ".join(sorted(lists))))
|
||||
if len(msg) > 1000:
|
||||
await ctx.author.send(msg)
|
||||
return
|
||||
await ctx.send(msg)
|
||||
|
||||
def get_audio_list(self, category: str) -> dict:
|
||||
"""Get the audiotrivia list corresponding to the given category.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
category : str
|
||||
The desired category. Case sensitive.
|
||||
|
||||
Returns
|
||||
-------
|
||||
`dict`
|
||||
A dict mapping questions (`str`) to answers (`list` of `str`).
|
||||
|
||||
"""
|
||||
try:
|
||||
path = next(p for p in self._audio_lists() if p.stem == category)
|
||||
except StopIteration:
|
||||
raise FileNotFoundError("Could not find the `{}` category.".format(category))
|
||||
|
||||
with path.open(encoding="utf-8") as file:
|
||||
try:
|
||||
dict_ = yaml.load(file)
|
||||
except yaml.error.YAMLError as exc:
|
||||
raise InvalidListError("YAML parsing failed.") from exc
|
||||
else:
|
||||
return dict_
|
||||
|
||||
def _audio_lists(self) -> List[pathlib.Path]:
|
||||
personal_lists = [p.resolve() for p in cog_data_path(self).glob("*.yaml")]
|
||||
|
||||
return personal_lists + get_core_lists()
|
||||
|
||||
|
||||
def get_core_lists() -> List[pathlib.Path]:
|
||||
"""Return a list of paths for all trivia lists packaged with the bot."""
|
||||
core_lists_path = pathlib.Path(__file__).parent.resolve() / "data/lists"
|
||||
return list(core_lists_path.glob("*.yaml"))
|
@ -0,0 +1,106 @@
|
||||
AUTHOR: bobloy
|
||||
https://www.youtube.com/watch?v=nfjiy-NX5b0:
|
||||
- flashbang
|
||||
https://www.youtube.com/watch?v=mJCE7s4W4IE:
|
||||
- starting round
|
||||
- round start
|
||||
- start round
|
||||
https://www.youtube.com/watch?v=XfLGi4cPu0Y:
|
||||
- select team
|
||||
- team select
|
||||
https://www.youtube.com/watch?v=b6ScVgFs-DQ:
|
||||
- desert eagle
|
||||
- deagle
|
||||
https://www.youtube.com/watch?v=JnHm-rn199Y:
|
||||
- planted bomb
|
||||
- bomb planted
|
||||
- bomb plant
|
||||
- plant bomb
|
||||
https://www.youtube.com/watch?v=3wztV24tbVU:
|
||||
- defusing bomb
|
||||
- defuse bomb
|
||||
- bomb defuse
|
||||
- bomb defusing
|
||||
https://www.youtube.com/watch?v=mpY9poBVje4:
|
||||
- lobby
|
||||
https://www.youtube.com/watch?v=zMT4ovCN7gk:
|
||||
- usp-s
|
||||
- usp s
|
||||
- usps
|
||||
https://www.youtube.com/watch?v=oI5Ww7y2aUQ:
|
||||
- gut knife
|
||||
https://www.youtube.com/watch?v=Dqmyxnx-OaQ:
|
||||
- ak47
|
||||
- ak 47
|
||||
https://www.youtube.com/watch?v=Ny4hGdziZP4:
|
||||
- hitmarker
|
||||
- hit
|
||||
- hitmaker
|
||||
- marker
|
||||
https://www.youtube.com/watch?v=vYUynDKM1Yw:
|
||||
- awp
|
||||
https://www.youtube.com/watch?v=52etXKmbQRM:
|
||||
- butterfly knife
|
||||
https://www.youtube.com/watch?v=99o4eyq0SzY:
|
||||
- won round
|
||||
- round won
|
||||
- win round
|
||||
- round win
|
||||
https://www.youtube.com/watch?v=V5tv1ZzqI_U:
|
||||
- lost round
|
||||
- round lost
|
||||
- lose round
|
||||
- round loss
|
||||
https://www.youtube.com/watch?v=1hI25OPdim0:
|
||||
- flashbang toss
|
||||
- toss flashbang
|
||||
- throwing flashbang
|
||||
- throw flashbang
|
||||
- flashbang throwing
|
||||
- flashbang throw
|
||||
- tossing flashbang
|
||||
- flashbang tossing
|
||||
https://www.youtube.com/watch?v=oML0z2Aj_D4:
|
||||
- firegrenade toss
|
||||
- toss firegrenade
|
||||
- throwing firegrenade
|
||||
- throw firegrenade
|
||||
- firegrenade throwing
|
||||
- firegrenade throw
|
||||
- tossing firegrenade
|
||||
- firegrenade tossing
|
||||
- fire grenade toss
|
||||
- toss fire grenade
|
||||
- throwing fire grenade
|
||||
- throw fire grenade
|
||||
- fire grenade throwing
|
||||
- fire grenade throw
|
||||
- tossing fire grenade
|
||||
- fire grenade tossing
|
||||
https://www.youtube.com/watch?v=9otQ9OLfaQc:
|
||||
- grenade out
|
||||
https://www.youtube.com/watch?v=tFA-8Vc32Kg:
|
||||
- famas
|
||||
https://www.youtube.com/watch?v=MdI1u8oXKZw:
|
||||
- awp zoom
|
||||
- zoom awp
|
||||
- awp scope
|
||||
- scope awp
|
||||
https://www.youtube.com/watch?v=6NiZhX4h32Q:
|
||||
- c4
|
||||
https://www.youtube.com/watch?v=3N0NxsyWPiY:
|
||||
- planting c4
|
||||
- c4 planting
|
||||
- plant c4
|
||||
- c4 plant
|
||||
https://www.youtube.com/watch?v=XLaJIXZ5QUc:
|
||||
- awp
|
||||
https://www.youtube.com/watch?v=DmuK9Wml88E:
|
||||
- P90
|
||||
https://www.youtube.com/watch?v=t1Ky_TbDXHY:
|
||||
- smoke
|
||||
https://www.youtube.com/watch?v=sJvdTbejDRY:
|
||||
- kill bonus
|
||||
https://www.youtube.com/watch?v=DYWi8qdvWCk:
|
||||
- AK47
|
||||
- AK 47
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,304 @@
|
||||
AUTHOR: bobloy
|
||||
https://www.youtube.com/watch?v=FrceWR4XnVU:
|
||||
- shovel knight
|
||||
https://www.youtube.com/watch?v=Fn0khIn2wfc:
|
||||
- super mario world
|
||||
https://www.youtube.com/watch?v=qkYSuWSPkHI:
|
||||
- the legend of zelda
|
||||
- legend of zelda
|
||||
- zelda
|
||||
https://www.youtube.com/watch?v=0hvlwLwxweI:
|
||||
- dragon quest ix
|
||||
- dragon quest 9
|
||||
https://www.youtube.com/watch?v=GxrKe9z4CCo:
|
||||
- chrono trigger
|
||||
https://www.youtube.com/watch?v=pz3BQFXjEOI:
|
||||
- super smash bros melee
|
||||
- super smash bros. melee
|
||||
- super smash brothers melee
|
||||
https://www.youtube.com/watch?v=l_ioujmtqjg:
|
||||
- super mario bros
|
||||
- super mario brothers
|
||||
- super mario bros.
|
||||
https://www.youtube.com/watch?v=zTztR_y9iHc:
|
||||
- banjo-kazooie
|
||||
- banjo kazooie
|
||||
https://www.youtube.com/watch?v=6gWyfQFdMJA:
|
||||
- metroid samus returns
|
||||
https://www.youtube.com/watch?v=0jXTBAGv9ZQ:
|
||||
- halo
|
||||
https://www.youtube.com/watch?v=Rhaq4JP_t6o:
|
||||
- the elder scrolls iii morrowind
|
||||
- morrowind
|
||||
- elder scrolls iii
|
||||
- elder scrolls 3
|
||||
https://www.youtube.com/watch?v=ZksNhHyEhE0:
|
||||
- sonic generations
|
||||
https://www.youtube.com/watch?v=lndBgOrTWxo:
|
||||
- donkey kong country 2
|
||||
- donkey kong country two
|
||||
https://www.youtube.com/watch?v=uTEMsmLoEA4:
|
||||
- mario kart 8
|
||||
- mario kart eight
|
||||
https://www.youtube.com/watch?v=WA2WjP6sgrc:
|
||||
- donkey kong country tropical freeze
|
||||
- tropical freeze
|
||||
https://www.youtube.com/watch?v=9wMjq58Fjvo:
|
||||
- castle crashers
|
||||
https://www.youtube.com/watch?v=sr2nK06zZkg:
|
||||
- shadow of the colossus
|
||||
https://www.youtube.com/watch?v=6CMTXyExkeI:
|
||||
- final fantasy v
|
||||
- final fantasy 5
|
||||
https://www.youtube.com/watch?v=nRbROTdOgj0:
|
||||
- legend of zelda skyward sword
|
||||
- skyward sword
|
||||
https://www.youtube.com/watch?v=LFcH84oNU6s:
|
||||
- skies of arcadia
|
||||
https://www.youtube.com/watch?v=VEIWhy-urqM:
|
||||
- super mario galaxy
|
||||
https://www.youtube.com/watch?v=IT12DW2Fm9M:
|
||||
- final fantasy iv
|
||||
- final fantasy 4
|
||||
https://www.youtube.com/watch?v=UZbqrZJ9VA4:
|
||||
- mother3
|
||||
- mother 3
|
||||
https://www.youtube.com/watch?v=o_ayLF9vdls:
|
||||
- dragon age origins
|
||||
https://www.youtube.com/watch?v=eVVXNDv8rY0:
|
||||
- the elder scrolls v skyrim
|
||||
- elder scrolls v
|
||||
- elder scrolls 5
|
||||
- the elder scrolls 5 skyrim
|
||||
- skyrim
|
||||
https://www.youtube.com/watch?v=kzvZE4BY0hY:
|
||||
- fallout 4
|
||||
https://www.youtube.com/watch?v=VTsD2FjmLsw:
|
||||
- mass effect 2
|
||||
https://www.youtube.com/watch?v=800be1ZmGd0:
|
||||
- world of warcraft
|
||||
https://www.youtube.com/watch?v=SXKrsJZWqK0:
|
||||
- batman arkham city
|
||||
- arkham city
|
||||
https://www.youtube.com/watch?v=BLEBtvOhGnM:
|
||||
- god of war iii
|
||||
- god of war 3
|
||||
https://www.youtube.com/watch?v=rxgTlQLm4Xg:
|
||||
- gears of war 3
|
||||
https://www.youtube.com/watch?v=QiPon8lr48U:
|
||||
- metal gear solid 2
|
||||
https://www.youtube.com/watch?v=qDnaIfiH37w:
|
||||
- super smash bros wii u
|
||||
- super smash bros. wii u
|
||||
- super smash brothers wii u
|
||||
- super smash bros wiiu
|
||||
- super smash bros. wiiu
|
||||
- super smash brothers wiiu
|
||||
https://www.youtube.com/watch?v=_Uzlm2MaCWw:
|
||||
- mega man maverick hunter x
|
||||
- megaman maverick hunter x
|
||||
- maverick hunter x
|
||||
https://www.youtube.com/watch?v=-8wo0KBQ3oI:
|
||||
- doom
|
||||
https://www.youtube.com/watch?v=TN36CetQw6I:
|
||||
- super smash bros brawl
|
||||
- super smash bros. brawl
|
||||
- super smash brothers brawl
|
||||
https://www.youtube.com/watch?v=01IEjvD5lss:
|
||||
- guilty gear
|
||||
https://www.youtube.com/watch?v=VXX4Ft1I0Dw:
|
||||
- dynasty warriors 6
|
||||
https://www.youtube.com/watch?v=liRMh4LzQQU:
|
||||
- doom 2016
|
||||
- doom
|
||||
https://www.youtube.com/watch?v=ouw3jLAUXWE:
|
||||
- devil may cry 3
|
||||
https://www.youtube.com/watch?v=B_MW65XxS7s:
|
||||
- final fantasy vii
|
||||
- final fantasy 7
|
||||
https://www.youtube.com/watch?v=viM0-3PXef0:
|
||||
- the witcher 3
|
||||
- witcher 3
|
||||
https://www.youtube.com/watch?v=WQYN2P3E06s:
|
||||
- civilization vi
|
||||
- civilization 6
|
||||
https://www.youtube.com/watch?v=qOMQxVtbkik:
|
||||
- guild wars 2
|
||||
- guild wars two
|
||||
https://www.youtube.com/watch?v=WwHrQdC02FY:
|
||||
- final fantasy vi
|
||||
- final fantasy 6
|
||||
https://www.youtube.com/watch?v=2_wkJ377LzU:
|
||||
- journey
|
||||
https://www.youtube.com/watch?v=IJiHDmyhE1A:
|
||||
- civilization iv
|
||||
- civilization 4
|
||||
https://www.youtube.com/watch?v=kN_LvY97Rco:
|
||||
- ori and the blind forest
|
||||
https://www.youtube.com/watch?v=TO7UI0WIqVw:
|
||||
- super smash bros brawl
|
||||
- super smash bros. brawl
|
||||
- super smash brothers brawl
|
||||
https://www.youtube.com/watch?v=s9XljBWGrRQ:
|
||||
- kingdom hearts
|
||||
https://www.youtube.com/watch?v=xkolWbZdGbM:
|
||||
- shenmue
|
||||
https://www.youtube.com/watch?v=h-0G_FI61a8:
|
||||
- final fantasy x
|
||||
- final fantasy 10
|
||||
https://www.youtube.com/watch?v=do5NTPLMqXQ:
|
||||
- fire emblem fates
|
||||
https://www.youtube.com/watch?v=eFVj0Z6ahcI:
|
||||
- persona 5
|
||||
- persona five
|
||||
https://www.youtube.com/watch?v=PhciLj5VzOk:
|
||||
- super mario odyssey
|
||||
https://www.youtube.com/watch?v=GBPbJyxqHV0:
|
||||
- super mario 64
|
||||
- mario 64
|
||||
https://www.youtube.com/watch?v=wRWq53IFXVQ:
|
||||
- the legend of zelda the wind waker
|
||||
- legend of zelda the wind waker
|
||||
- the legend of zelda wind waker
|
||||
- legend of zelda wind waker
|
||||
- wind waker
|
||||
https://www.youtube.com/watch?v=nkPF5UiDi4g:
|
||||
- uncharted 2
|
||||
https://www.youtube.com/watch?v=CdYen5UII0s:
|
||||
- battlefield 1
|
||||
- battlefield one
|
||||
https://www.youtube.com/watch?v=8yj-25MOgOM:
|
||||
- star fox zero
|
||||
- starfox zero
|
||||
https://www.youtube.com/watch?v=Z9dNrmGD7mU:
|
||||
- dark souls iii
|
||||
- dark souls 3
|
||||
https://www.youtube.com/watch?v=Bio99hoZVYI:
|
||||
- fire emblem awakening
|
||||
https://www.youtube.com/watch?v=4EcgruWlXnQ:
|
||||
- monty on the run
|
||||
https://www.youtube.com/watch?v=oEf8gPFFZ58:
|
||||
- mega man 3
|
||||
- megaman 3
|
||||
https://www.youtube.com/watch?v=ifbr2NQ3Js0:
|
||||
- castlevania
|
||||
https://www.youtube.com/watch?v=W7rhEKTX-sE:
|
||||
- shovel knight
|
||||
https://www.youtube.com/watch?v=as_ct9tgkZA:
|
||||
- mega man 2
|
||||
- megaman 2
|
||||
https://www.youtube.com/watch?v=FB9Pym-sdbs:
|
||||
- actraiser
|
||||
https://www.youtube.com/watch?v=G3zhZHU6B2M:
|
||||
- ogre battle
|
||||
https://www.youtube.com/watch?v=hlrOAEr6dXc:
|
||||
- metroid zero mission
|
||||
- zero mission
|
||||
https://www.youtube.com/watch?v=jl6kjAkVw_s:
|
||||
- sonic 2
|
||||
https://www.youtube.com/watch?v=K8GRDNU50b8:
|
||||
- the legend of zelda ocarina of time
|
||||
- legend of zelda ocarina of time
|
||||
- ocarina of time
|
||||
https://www.youtube.com/watch?v=dTZ8uhJ5hIE:
|
||||
- kirby's epic yarn
|
||||
- kirbys epic yarn
|
||||
https://www.youtube.com/watch?v=QaaD9CnWgig:
|
||||
- super smash bros brawl
|
||||
- super smash bros. brawl
|
||||
- super smash brothers brawl
|
||||
https://www.youtube.com/watch?v=JDqJa1RC3q8:
|
||||
- kid icarus uprising
|
||||
https://www.youtube.com/watch?v=MQurUl4Snio:
|
||||
- punch-out!!
|
||||
- punch-out
|
||||
- punch out
|
||||
- punchout
|
||||
https://www.youtube.com/watch?v=vlz6qgahnYQ:
|
||||
- super street fighter 2 turbo
|
||||
- super street fighter two turbo
|
||||
- street fighter 2 turbo
|
||||
- street fighter two turbo
|
||||
https://www.youtube.com/watch?v=FBLp-3Rw_u0:
|
||||
- mario & luigi bowser's inside story
|
||||
- mario and luigi bowser's inside story
|
||||
- mario & luigi bowsers inside story
|
||||
- mario and luigi bowsers inside story
|
||||
- bowser's inside story
|
||||
- bowsers inside story
|
||||
https://www.youtube.com/watch?v=jqE8M2ZnFL8:
|
||||
- grand theft auto 4
|
||||
- grand theft auto four
|
||||
https://www.youtube.com/watch?v=GQZLEegUK74:
|
||||
- goldeneye 007
|
||||
- goldeneye
|
||||
https://www.youtube.com/watch?v=nCe7W1ajzIE:
|
||||
- tmnt iv turtles in time
|
||||
- tmnt iv
|
||||
- tmnt 4 turtles in time
|
||||
- tmnt 4
|
||||
- turtles in time
|
||||
https://www.youtube.com/watch?v=YHEifuLCSIY:
|
||||
- ducktales
|
||||
https://www.youtube.com/watch?v=rXefFHRgyE0:
|
||||
- pokemon diamond
|
||||
- pokemon pearl
|
||||
- pokemon platinum
|
||||
https://www.youtube.com/watch?v=4jaIUlz-wNU:
|
||||
- warriors orochi 3
|
||||
- warriors orochi three
|
||||
https://www.youtube.com/watch?v=EAwWPadFsOA:
|
||||
- mortal kombat
|
||||
https://www.youtube.com/watch?v=XI1VpElKWF8:
|
||||
- metal gear solid
|
||||
https://www.youtube.com/watch?v=zz8m1oEkW5k:
|
||||
- tetris blitz
|
||||
https://www.youtube.com/watch?v=gMdX_Iloow8:
|
||||
- ultimate marvel vs capcom 3
|
||||
- marvel vs capcom 3
|
||||
- ultimate marvel vs. capcom 3
|
||||
- marvel vs. capcom 3
|
||||
https://www.youtube.com/watch?v=vRe3h1iQ1Os:
|
||||
- sonic the hedgehog 2006
|
||||
- sonic the hegehog
|
||||
https://www.youtube.com/watch?v=SYTS2sJWcIs:
|
||||
- pokemon heartgold
|
||||
- pokemon soulsilver
|
||||
https://www.youtube.com/watch?v=5-BIqqSe1nU:
|
||||
- red dead redemption
|
||||
https://www.youtube.com/watch?v=wp6QpMWaKpE:
|
||||
- bioshock
|
||||
https://www.youtube.com/watch?v=R9XdMnsKvUs:
|
||||
- call of duty 4 modern warfare
|
||||
- call of duty 4
|
||||
- modern warfare
|
||||
https://www.youtube.com/watch?v=f-sQhBDsjgE:
|
||||
- killzone 2
|
||||
https://www.youtube.com/watch?v=-_O6F5FwQ0s:
|
||||
- soul calibur v
|
||||
- sould calibur 5
|
||||
https://www.youtube.com/watch?v=MgK_OfW7nl4:
|
||||
- the legend of zelda breath of the wild
|
||||
- legend of zelda breath of the wild
|
||||
- breath of the wild
|
||||
https://www.youtube.com/watch?v=tz82xbLvK_k:
|
||||
- undertale
|
||||
https://www.youtube.com/watch?v=J46RY4PU8a8:
|
||||
- chrono cross
|
||||
https://www.youtube.com/watch?v=6LB7LZZGpkw:
|
||||
- silent hill 2
|
||||
https://www.youtube.com/watch?v=ya3yxTbkh5s:
|
||||
- Ōkami
|
||||
- okami
|
||||
- wolf
|
||||
https://www.youtube.com/watch?v=KGidvt4NTPI:
|
||||
- hikari 光
|
||||
- hikari
|
||||
- 光
|
||||
- light
|
||||
https://www.youtube.com/watch?v=JbXVNKtmWnc:
|
||||
- final fantasy vi
|
||||
- final fantasy 6
|
||||
https://www.youtube.com/watch?v=-jMDutXA4-M:
|
||||
- final fantasy iii
|
||||
- final fantasy 3
|
@ -0,0 +1,4 @@
|
||||
https://www.youtube.com/watch?v=hfyE220BsD0:
|
||||
- holiday
|
||||
https://www.youtube.com/watch?v=Hh3U9iPKeXQ:
|
||||
- sultans of swing
|
@ -0,0 +1,4 @@
|
||||
https://www.youtube.com/watch?v=Hi1kUdreiWk:
|
||||
- Jinx
|
||||
https://www.youtube.com/watch?v=PNYHFluhOGI:
|
||||
- Teemo
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Start an Audio Trivia game",
|
||||
"hidden": false,
|
||||
"install_msg": "Thank you for installing Audio trivia!\n You **MUST** unload trivia to use this (`[p]unload trivia`)\n Then you can get started with `[p]load audiotrivia` and `[p]help AudioTrivia`",
|
||||
"requirements": [],
|
||||
"short": "Start an Audio Trivia game",
|
||||
"tags": [
|
||||
"fox",
|
||||
"bobloy",
|
||||
"games"
|
||||
]
|
||||
}
|
@ -1,10 +1,22 @@
|
||||
{
|
||||
"author" : ["Bobloy"],
|
||||
"bot_version" : [3,0,0],
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "[Incomplete] Creates custom commands to adjust roles and send custom messages",
|
||||
"hidden": false,
|
||||
"install_msg" : "Thank you for installing Custom Commands w/ Roles.",
|
||||
"install_msg": "Thank you for installing Custom Commands w/ Roles. Get started with `[p]load ccrole` and `[p]help CCRole`",
|
||||
"requirements": [],
|
||||
"short": "[Incomplete] Creates commands that adjust roles",
|
||||
"tags" : ["fox", "bobloy", "utility", "tools", "roles"]
|
||||
"tags": [
|
||||
"fox",
|
||||
"bobloy",
|
||||
"utility",
|
||||
"tools",
|
||||
"roles"
|
||||
]
|
||||
}
|
@ -1,5 +1,11 @@
|
||||
from .chatter import Chatter
|
||||
from . import chatterbot
|
||||
from .chat import Chatter
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Chatter(bot))
|
||||
|
||||
|
||||
__all__ = (
|
||||
'chatterbot'
|
||||
)
|
||||
|
@ -0,0 +1,202 @@
|
||||
import asyncio
|
||||
import pathlib
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import discord
|
||||
from redbot.core import Config
|
||||
from redbot.core import commands
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
|
||||
from chatter.chatterbot import ChatBot
|
||||
from chatter.chatterbot.comparisons import levenshtein_distance
|
||||
from chatter.chatterbot.response_selection import get_first_response
|
||||
from chatter.chatterbot.trainers import ListTrainer
|
||||
from typing import Any
|
||||
|
||||
Cog: Any = getattr(commands, "Cog", object)
|
||||
|
||||
|
||||
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 = {}
|
||||
default_guild = {
|
||||
"whitelist": None,
|
||||
"days": 1
|
||||
}
|
||||
path: pathlib.Path = cog_data_path(self)
|
||||
data_path = path / ("database.sqlite3")
|
||||
|
||||
self.chatbot = ChatBot(
|
||||
"ChatterBot",
|
||||
storage_adapter='chatter.chatterbot.storage.SQLStorageAdapter',
|
||||
database=str(data_path),
|
||||
statement_comparison_function=levenshtein_distance,
|
||||
response_selection_method=get_first_response,
|
||||
logic_adapters=[
|
||||
'chatter.chatterbot.logic.BestMatch',
|
||||
{
|
||||
'import_path': 'chatter.chatterbot.logic.LowConfidenceAdapter',
|
||||
'threshold': 0.65,
|
||||
'default_response': ':thinking:'
|
||||
}
|
||||
]
|
||||
)
|
||||
self.chatbot.set_trainer(ListTrainer)
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
self.loop = asyncio.get_event_loop()
|
||||
|
||||
async def _get_conversation(self, ctx, in_channel: discord.TextChannel = None):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
out = [[]]
|
||||
after = datetime.today() - timedelta(days=(await self.config.guild(ctx.guild).days()))
|
||||
|
||||
def new_message(msg, sent, out_in):
|
||||
if sent is None:
|
||||
return False
|
||||
|
||||
if len(out_in) < 2:
|
||||
return False
|
||||
|
||||
return msg.created_at - sent >= timedelta(hours=3) # This should be configurable perhaps
|
||||
|
||||
for channel in ctx.guild.text_channels:
|
||||
if in_channel:
|
||||
channel = in_channel
|
||||
await ctx.send("Gathering {}".format(channel.mention))
|
||||
user = None
|
||||
i = 0
|
||||
send_time = None
|
||||
try:
|
||||
|
||||
async for message in channel.history(limit=None, reverse=True, after=after):
|
||||
# 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:
|
||||
out[i][-1] += "\n" + message.clean_content
|
||||
else:
|
||||
user = message.author
|
||||
out[i].append(message.clean_content)
|
||||
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
if in_channel:
|
||||
break
|
||||
|
||||
return out
|
||||
|
||||
def _train(self, data):
|
||||
try:
|
||||
for convo in data:
|
||||
self.chatbot.train(convo)
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
@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:
|
||||
pass
|
||||
|
||||
@chatter.command()
|
||||
async def age(self, ctx: commands.Context, days: int):
|
||||
"""
|
||||
Sets the number of days to look back
|
||||
Will train on 1 day otherwise
|
||||
"""
|
||||
|
||||
await self.config.guild(ctx.guild).days.set(days)
|
||||
await ctx.send("Success")
|
||||
|
||||
@chatter.command()
|
||||
async def backup(self, ctx, backupname):
|
||||
"""
|
||||
Backup your training data to a json for later use
|
||||
"""
|
||||
await ctx.send("Backing up data, this may take a while")
|
||||
future = await self.loop.run_in_executor(None, self.chatbot.trainer.export_for_training,
|
||||
'./{}.json'.format(backupname))
|
||||
|
||||
if future:
|
||||
await ctx.send("Backup successful!")
|
||||
else:
|
||||
await ctx.send("Error occurred :(")
|
||||
|
||||
@chatter.command()
|
||||
async def train(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
"""
|
||||
Trains the bot based on language in this guild
|
||||
"""
|
||||
|
||||
conversation = await self._get_conversation(ctx, channel)
|
||||
|
||||
if not conversation:
|
||||
await ctx.send("Failed to gather training data")
|
||||
return
|
||||
|
||||
await ctx.send("Gather successful! Training begins now\n(**This will take a long time, be patient**)")
|
||||
embed = discord.Embed(title="Loading")
|
||||
embed.set_image(url="http://www.loop.universaleverything.com/animations/1295.gif")
|
||||
temp_message = await ctx.send(embed=embed)
|
||||
future = await self.loop.run_in_executor(None, self._train, conversation)
|
||||
|
||||
try:
|
||||
await temp_message.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
if future:
|
||||
await ctx.send("Training successful!")
|
||||
else:
|
||||
await ctx.send("Error occurred :(")
|
||||
|
||||
async def on_message(self, message: discord.Message):
|
||||
"""
|
||||
Credit to https://github.com/Twentysix26/26-Cogs/blob/master/cleverbot/cleverbot.py
|
||||
for on_message recognition of @bot
|
||||
"""
|
||||
author = message.author
|
||||
try:
|
||||
guild: discord.Guild = message.guild
|
||||
except AttributeError: # Not a guild message
|
||||
return
|
||||
|
||||
channel: discord.TextChannel = message.channel
|
||||
|
||||
if author.id != self.bot.user.id:
|
||||
to_strip = "@" + guild.me.display_name + " "
|
||||
text = message.clean_content
|
||||
if not text.startswith(to_strip):
|
||||
return
|
||||
text = text.replace(to_strip, "", 1)
|
||||
async with channel.typing():
|
||||
future = await self.loop.run_in_executor(None, self.chatbot.get_response, text)
|
||||
|
||||
if future and str(future):
|
||||
await channel.send(str(future))
|
||||
else:
|
||||
await channel.send(':thinking:')
|
@ -1,141 +0,0 @@
|
||||
import asyncio
|
||||
from typing import List, Union
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
|
||||
from .source import ChatBot
|
||||
from .source.trainers import ListTrainer
|
||||
|
||||
from datetime import datetime,timedelta
|
||||
|
||||
class Chatter:
|
||||
"""
|
||||
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 = {}
|
||||
default_guild = {
|
||||
"whitelist": None,
|
||||
"days": 1
|
||||
}
|
||||
|
||||
self.chatbot = ChatBot("ChatterBot")
|
||||
self.chatbot.set_trainer(ListTrainer)
|
||||
|
||||
self.config.register_global(**default_global)
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
self.loop = asyncio.get_event_loop()
|
||||
|
||||
async def _get_conversation(self, ctx, in_channel: discord.TextChannel=None):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
out = []
|
||||
after = datetime.today() - timedelta(days=(await self.config.guild(ctx.guild).days()))
|
||||
|
||||
|
||||
for channel in ctx.guild.text_channels:
|
||||
if in_channel:
|
||||
channel = in_channel
|
||||
await ctx.send("Gathering {}".format(channel.mention))
|
||||
user = None
|
||||
try:
|
||||
async for message in channel.history(limit=None, reverse=True, after=after):
|
||||
if user == message.author:
|
||||
out[-1] += "\n"+message.clean_content
|
||||
else:
|
||||
user = message.author
|
||||
out.append(message.clean_content)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
if in_channel:
|
||||
break
|
||||
|
||||
return out
|
||||
|
||||
def _train(self, data):
|
||||
try:
|
||||
self.chatbot.train(data)
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
@commands.group()
|
||||
async def chatter(self, ctx: commands.Context):
|
||||
"""
|
||||
Base command for this cog. Check help for the commands list.
|
||||
"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
@chatter.command()
|
||||
async def age(self, ctx: commands.Context, days: int):
|
||||
"""
|
||||
Sets the number of days to look back
|
||||
Will train on 1 day otherwise
|
||||
"""
|
||||
|
||||
await self.config.guild(ctx.guild).days.set(days)
|
||||
await ctx.send("Success")
|
||||
|
||||
@chatter.command()
|
||||
async def train(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||
"""
|
||||
Trains the bot based on language in this guild
|
||||
"""
|
||||
|
||||
conversation = await self._get_conversation(ctx, channel)
|
||||
|
||||
if not conversation:
|
||||
await ctx.send("Failed to gather training data")
|
||||
return
|
||||
|
||||
await ctx.send("Gather successful! Training begins now\n(**This will take a long time, be patient**)")
|
||||
embed=discord.Embed(title="Loading")
|
||||
embed.set_image(url="http://www.loop.universaleverything.com/animations/1295.gif")
|
||||
temp_message = await ctx.send(embed=embed)
|
||||
future = await self.loop.run_in_executor(None, self._train, conversation)
|
||||
|
||||
try:
|
||||
await temp_message.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
if future:
|
||||
await ctx.send("Training successful!")
|
||||
else:
|
||||
await ctx.send("Error occurred :(")
|
||||
|
||||
async def on_message(self, message):
|
||||
"""
|
||||
Credit to https://github.com/Twentysix26/26-Cogs/blob/master/cleverbot/cleverbot.py
|
||||
for on_message recognition of @bot
|
||||
"""
|
||||
author = message.author
|
||||
channel = message.channel
|
||||
|
||||
if message.author.id != self.bot.user.id:
|
||||
to_strip = "@" + author.guild.me.display_name + " "
|
||||
text = message.clean_content
|
||||
if not text.startswith(to_strip):
|
||||
return
|
||||
text = text.replace(to_strip, "", 1)
|
||||
async with channel.typing():
|
||||
response = self.chatbot.get_response(text)
|
||||
if not response:
|
||||
response = ":thinking:"
|
||||
await channel.send(response)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import sys
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import importlib
|
||||
|
@ -1,12 +1,11 @@
|
||||
from .input_adapter import InputAdapter
|
||||
from .microsoft import Microsoft
|
||||
from .gitter import Gitter
|
||||
from .hipchat import HipChat
|
||||
from .mailgun import Mailgun
|
||||
from .microsoft import Microsoft
|
||||
from .terminal import TerminalAdapter
|
||||
from .variable_input_type_adapter import VariableInputTypeAdapter
|
||||
|
||||
|
||||
__all__ = (
|
||||
'InputAdapter',
|
||||
'Microsoft',
|
@ -1,7 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from time import sleep
|
||||
from . import InputAdapter
|
||||
from ..conversation import Statement
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.input import InputAdapter
|
||||
|
||||
|
||||
class Gitter(InputAdapter):
|
@ -1,7 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from time import sleep
|
||||
from . import InputAdapter
|
||||
from ..conversation import Statement
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.input import InputAdapter
|
||||
|
||||
|
||||
class HipChat(InputAdapter):
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..adapters import Adapter
|
||||
|
||||
from chatter.chatterbot.adapters import Adapter
|
||||
|
||||
|
||||
class InputAdapter(Adapter):
|
@ -1,7 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from . import InputAdapter
|
||||
from ..conversation import Statement
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.input import InputAdapter
|
||||
|
||||
|
||||
class Mailgun(InputAdapter):
|
@ -1,7 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from time import sleep
|
||||
from . import InputAdapter
|
||||
from ..conversation import Statement
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.input import InputAdapter
|
||||
|
||||
|
||||
class Microsoft(InputAdapter):
|
@ -1,7 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
from . import InputAdapter
|
||||
from ..conversation import Statement
|
||||
from ..utils import input_function
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.input import InputAdapter
|
||||
from chatter.chatterbot.utils import input_function
|
||||
|
||||
|
||||
class TerminalAdapter(InputAdapter):
|
@ -1,21 +1,17 @@
|
||||
from __future__ import unicode_literals
|
||||
from . import InputAdapter
|
||||
from ..conversation import Statement
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.input import InputAdapter
|
||||
|
||||
class VariableInputTypeAdapter(InputAdapter):
|
||||
|
||||
class VariableInputTypeAdapter(InputAdapter):
|
||||
JSON = 'json'
|
||||
TEXT = 'text'
|
||||
OBJECT = 'object'
|
||||
VALID_FORMATS = (JSON, TEXT, OBJECT,)
|
||||
|
||||
def detect_type(self, statement):
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
string_types = basestring # NOQA
|
||||
else:
|
||||
string_types = str
|
||||
|
||||
if hasattr(statement, 'text'):
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from .logic_adapter import LogicAdapter
|
||||
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class BestMatch(LogicAdapter):
|
@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversation import Statement
|
||||
from .best_match import BestMatch
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.logic import BestMatch
|
||||
|
||||
|
||||
class LowConfidenceAdapter(BestMatch):
|
@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from . import LogicAdapter
|
||||
from ..conversation import Statement
|
||||
|
||||
from chatter.chatterbot.conversation import Statement
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class MathematicalEvaluation(LogicAdapter):
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from .logic_adapter import LogicAdapter
|
||||
|
||||
from chatter.chatterbot.logic import LogicAdapter
|
||||
|
||||
|
||||
class NoKnowledgeAdapter(LogicAdapter):
|
@ -1,9 +1,9 @@
|
||||
from .output_adapter import OutputAdapter
|
||||
from .microsoft import Microsoft
|
||||
from .terminal import TerminalAdapter
|
||||
from .mailgun import Mailgun
|
||||
from .gitter import Gitter
|
||||
from .hipchat import HipChat
|
||||
from .mailgun import Mailgun
|
||||
from .microsoft import Microsoft
|
||||
from .terminal import TerminalAdapter
|
||||
|
||||
__all__ = (
|
||||
'OutputAdapter',
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from .output_adapter import OutputAdapter
|
||||
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class Gitter(OutputAdapter):
|
@ -1,6 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
from .output_adapter import OutputAdapter
|
||||
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class HipChat(OutputAdapter):
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from .output_adapter import OutputAdapter
|
||||
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class Mailgun(OutputAdapter):
|
@ -1,6 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
from .output_adapter import OutputAdapter
|
||||
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class Microsoft(OutputAdapter):
|
@ -1,4 +1,4 @@
|
||||
from ..adapters import Adapter
|
||||
from chatter.chatterbot.adapters import Adapter
|
||||
|
||||
|
||||
class OutputAdapter(Adapter):
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from .output_adapter import OutputAdapter
|
||||
|
||||
from chatter.chatterbot.output import OutputAdapter
|
||||
|
||||
|
||||
class TerminalAdapter(OutputAdapter):
|
@ -1,12 +1,9 @@
|
||||
from .storage_adapter import StorageAdapter
|
||||
from .django_storage import DjangoStorageAdapter
|
||||
from .mongodb import MongoDatabaseAdapter
|
||||
from .sql_storage import SQLStorageAdapter
|
||||
|
||||
|
||||
__all__ = (
|
||||
'StorageAdapter',
|
||||
'DjangoStorageAdapter',
|
||||
'MongoDatabaseAdapter',
|
||||
'SQLStorageAdapter',
|
||||
)
|
@ -1,10 +1,30 @@
|
||||
{
|
||||
"author" : ["Bobloy"],
|
||||
"bot_version" : [3,0,0],
|
||||
"author": [
|
||||
"Bobloy"
|
||||
],
|
||||
"bot_version": [
|
||||
3,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "Create an offline chatbot that talks like your average member using Machine Learning",
|
||||
"hidden": false,
|
||||
"install_msg" : "Thank you for installing Chatter!",
|
||||
"requirements" : ["sqlalchemy<1.3,>=1.2", "python-twitter<4.0,>=3.0", "python-dateutil<2.7,>=2.6", "pymongo<4.0,>=3.3", "nltk<4.0,>=3.2", "mathparse<0.2,>=0.1", "chatterbot-corpus<1.2,>=1.1"],
|
||||
"install_msg": "Thank you for installing Chatter! Get started ith `[p]load chatter` and `[p]help Chatter`",
|
||||
"requirements": [
|
||||
"sqlalchemy<1.3,>=1.2",
|
||||
"python-twitter<4.0,>=3.0",
|
||||
"python-dateutil<2.7,>=2.6",
|
||||
"pymongo<4.0,>=3.3",
|
||||
"nltk<4.0,>=3.2",
|
||||
"mathparse<0.2,>=0.1",
|
||||
"chatterbot-corpus<1.2,>=1.1"
|
||||
],
|
||||
"short": "Local Chatbot run on machine learning",
|
||||
"tags" : ["chat", "chatbot", "cleverbot", "clever","bobloy"]
|
||||
"tags": [
|
||||
"chat",
|
||||
"chatbot",
|
||||
"cleverbot",
|
||||
"clever",
|
||||
"bobloy"
|
||||
]
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
default_app_config = (
|
||||
'chatter.source.ext.django_chatterbot.apps.DjangoChatterBotConfig'
|
||||
)
|
@ -1,261 +0,0 @@
|
||||
from ...conversation import StatementMixin
|
||||
from ... import constants
|
||||
from django.db import models
|
||||
from django.apps import apps
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
DJANGO_APP_NAME = constants.DEFAULT_DJANGO_APP_NAME
|
||||
STATEMENT_MODEL = 'Statement'
|
||||
RESPONSE_MODEL = 'Response'
|
||||
|
||||
if hasattr(settings, 'CHATTERBOT'):
|
||||
"""
|
||||
Allow related models to be overridden in the project settings.
|
||||
Default to the original settings if one is not defined.
|
||||
"""
|
||||
DJANGO_APP_NAME = settings.CHATTERBOT.get(
|
||||
'django_app_name',
|
||||
DJANGO_APP_NAME
|
||||
)
|
||||
STATEMENT_MODEL = settings.CHATTERBOT.get(
|
||||
'statement_model',
|
||||
STATEMENT_MODEL
|
||||
)
|
||||
RESPONSE_MODEL = settings.CHATTERBOT.get(
|
||||
'response_model',
|
||||
RESPONSE_MODEL
|
||||
)
|
||||
|
||||
|
||||
class AbstractBaseStatement(models.Model, StatementMixin):
|
||||
"""
|
||||
The abstract base statement allows other models to
|
||||
be created using the attributes that exist on the
|
||||
default models.
|
||||
"""
|
||||
|
||||
text = models.CharField(
|
||||
unique=True,
|
||||
blank=False,
|
||||
null=False,
|
||||
max_length=constants.STATEMENT_TEXT_MAX_LENGTH
|
||||
)
|
||||
|
||||
extra_data = models.CharField(
|
||||
max_length=500,
|
||||
blank=True
|
||||
)
|
||||
|
||||
# This is the confidence with which the chat bot believes
|
||||
# this is an accurate response. This value is set when the
|
||||
# statement is returned by the chat bot.
|
||||
confidence = 0
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
if len(self.text.strip()) > 60:
|
||||
return '{}...'.format(self.text[:57])
|
||||
elif len(self.text.strip()) > 0:
|
||||
return self.text
|
||||
return '<empty>'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AbstractBaseStatement, self).__init__(*args, **kwargs)
|
||||
|
||||
# Responses to be saved if the statement is updated with the storage adapter
|
||||
self.response_statement_cache = []
|
||||
|
||||
@property
|
||||
def in_response_to(self):
|
||||
"""
|
||||
Return the response objects that are for this statement.
|
||||
"""
|
||||
ResponseModel = apps.get_model(DJANGO_APP_NAME, RESPONSE_MODEL)
|
||||
return ResponseModel.objects.filter(statement=self)
|
||||
|
||||
def add_extra_data(self, key, value):
|
||||
"""
|
||||
Add extra data to the extra_data field.
|
||||
"""
|
||||
import json
|
||||
|
||||
if not self.extra_data:
|
||||
self.extra_data = '{}'
|
||||
|
||||
extra_data = json.loads(self.extra_data)
|
||||
extra_data[key] = value
|
||||
|
||||
self.extra_data = json.dumps(extra_data)
|
||||
|
||||
def add_tags(self, tags):
|
||||
"""
|
||||
Add a list of strings to the statement as tags.
|
||||
(Overrides the method from StatementMixin)
|
||||
"""
|
||||
for tag in tags:
|
||||
self.tags.create(
|
||||
name=tag
|
||||
)
|
||||
|
||||
def add_response(self, statement):
|
||||
"""
|
||||
Add a response to this statement.
|
||||
"""
|
||||
self.response_statement_cache.append(statement)
|
||||
|
||||
def remove_response(self, response_text):
|
||||
"""
|
||||
Removes a response from the statement's response list based
|
||||
on the value of the response text.
|
||||
|
||||
:param response_text: The text of the response to be removed.
|
||||
:type response_text: str
|
||||
"""
|
||||
is_deleted = False
|
||||
response = self.in_response.filter(response__text=response_text)
|
||||
|
||||
if response.exists():
|
||||
is_deleted = True
|
||||
|
||||
return is_deleted
|
||||
|
||||
def get_response_count(self, statement):
|
||||
"""
|
||||
Find the number of times that the statement has been used
|
||||
as a response to the current statement.
|
||||
|
||||
:param statement: The statement object to get the count for.
|
||||
:type statement: chatterbot.conversation.Statement
|
||||
|
||||
:returns: Return the number of times the statement has been used as a response.
|
||||
:rtype: int
|
||||
"""
|
||||
return self.in_response.filter(response__text=statement.text).count()
|
||||
|
||||
def serialize(self):
|
||||
"""
|
||||
:returns: A dictionary representation of the statement object.
|
||||
:rtype: dict
|
||||
"""
|
||||
import json
|
||||
data = {}
|
||||
|
||||
if not self.extra_data:
|
||||
self.extra_data = '{}'
|
||||
|
||||
data['text'] = self.text
|
||||
data['in_response_to'] = []
|
||||
data['extra_data'] = json.loads(self.extra_data)
|
||||
|
||||
for response in self.in_response.all():
|
||||
data['in_response_to'].append(response.serialize())
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class AbstractBaseResponse(models.Model):
|
||||
"""
|
||||
The abstract base response allows other models to
|
||||
be created using the attributes that exist on the
|
||||
default models.
|
||||
"""
|
||||
|
||||
statement = models.ForeignKey(
|
||||
STATEMENT_MODEL,
|
||||
related_name='in_response',
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
response = models.ForeignKey(
|
||||
STATEMENT_MODEL,
|
||||
related_name='responses',
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(
|
||||
default=timezone.now,
|
||||
help_text='The date and time that this response was created at.'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@property
|
||||
def occurrence(self):
|
||||
"""
|
||||
Return a count of the number of times this response has occurred.
|
||||
"""
|
||||
ResponseModel = apps.get_model(DJANGO_APP_NAME, RESPONSE_MODEL)
|
||||
|
||||
return ResponseModel.objects.filter(
|
||||
statement__text=self.statement.text,
|
||||
response__text=self.response.text
|
||||
).count()
|
||||
|
||||
def __str__(self):
|
||||
statement = self.statement.text
|
||||
response = self.response.text
|
||||
return '{} => {}'.format(
|
||||
statement if len(statement) <= 20 else statement[:17] + '...',
|
||||
response if len(response) <= 40 else response[:37] + '...'
|
||||
)
|
||||
|
||||
def serialize(self):
|
||||
"""
|
||||
:returns: A dictionary representation of the statement object.
|
||||
:rtype: dict
|
||||
"""
|
||||
data = {}
|
||||
|
||||
data['text'] = self.response.text
|
||||
data['created_at'] = self.created_at.isoformat()
|
||||
data['occurrence'] = self.occurrence
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class AbstractBaseConversation(models.Model):
|
||||
"""
|
||||
The abstract base conversation allows other models to
|
||||
be created using the attributes that exist on the
|
||||
default models.
|
||||
"""
|
||||
|
||||
responses = models.ManyToManyField(
|
||||
RESPONSE_MODEL,
|
||||
related_name='conversations',
|
||||
help_text='The responses in this conversation.'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return str(self.id)
|
||||
|
||||
|
||||
class AbstractBaseTag(models.Model):
|
||||
"""
|
||||
The abstract base tag allows other models to
|
||||
be created using the attributes that exist on the
|
||||
default models.
|
||||
"""
|
||||
|
||||
name = models.SlugField(
|
||||
max_length=constants.TAG_NAME_MAX_LENGTH
|
||||
)
|
||||
|
||||
statements = models.ManyToManyField(
|
||||
STATEMENT_MODEL,
|
||||
related_name='tags'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
@ -1,31 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from .models import (
|
||||
Statement, Response, Conversation, Tag
|
||||
)
|
||||
|
||||
|
||||
class StatementAdmin(admin.ModelAdmin):
|
||||
list_display = ('text', )
|
||||
list_filter = ('text', )
|
||||
search_fields = ('text', )
|
||||
|
||||
|
||||
class ResponseAdmin(admin.ModelAdmin):
|
||||
list_display = ('statement', 'response', 'occurrence', )
|
||||
search_fields = ['statement__text', 'response__text']
|
||||
|
||||
|
||||
class ConversationAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', )
|
||||
|
||||
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', )
|
||||
list_filter = ('name', )
|
||||
search_fields = ('name', )
|
||||
|
||||
|
||||
admin.site.register(Statement, StatementAdmin)
|
||||
admin.site.register(Response, ResponseAdmin)
|
||||
admin.site.register(Conversation, ConversationAdmin)
|
||||
admin.site.register(Tag, TagAdmin)
|
@ -1,8 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DjangoChatterBotConfig(AppConfig):
|
||||
|
||||
name = 'chatter.source.ext.django_chatterbot'
|
||||
label = 'django_chatterbot'
|
||||
verbose_name = 'Django ChatterBot'
|
@ -1,42 +0,0 @@
|
||||
"""
|
||||
These factories are used to generate fake data for testing.
|
||||
"""
|
||||
import factory
|
||||
from . import models
|
||||
from ... import constants
|
||||
from factory.django import DjangoModelFactory
|
||||
|
||||
|
||||
class StatementFactory(DjangoModelFactory):
|
||||
|
||||
text = factory.Faker(
|
||||
'text',
|
||||
max_nb_chars=constants.STATEMENT_TEXT_MAX_LENGTH
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.Statement
|
||||
|
||||
|
||||
class ResponseFactory(DjangoModelFactory):
|
||||
|
||||
statement = factory.SubFactory(StatementFactory)
|
||||
|
||||
response = factory.SubFactory(StatementFactory)
|
||||
|
||||
class Meta:
|
||||
model = models.Response
|
||||
|
||||
|
||||
class ConversationFactory(DjangoModelFactory):
|
||||
|
||||
class Meta:
|
||||
model = models.Conversation
|
||||
|
||||
|
||||
class TagFactory(DjangoModelFactory):
|
||||
|
||||
name = factory.Faker('word')
|
||||
|
||||
class Meta:
|
||||
model = models.Tag
|
@ -1,29 +0,0 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
A Django management command for calling a
|
||||
chat bot's training method.
|
||||
"""
|
||||
|
||||
help = 'Trains the database used by the chat bot'
|
||||
can_import_settings = True
|
||||
|
||||
def handle(self, *args, **options):
|
||||
from ..... import ChatBot
|
||||
from ... import settings
|
||||
|
||||
chatterbot = ChatBot(**settings.CHATTERBOT)
|
||||
|
||||
chatterbot.train(chatterbot.training_data)
|
||||
|
||||
# Django 1.8 does not define SUCCESS
|
||||
if hasattr(self.style, 'SUCCESS'):
|
||||
style = self.style.SUCCESS
|
||||
else:
|
||||
style = self.style.NOTICE
|
||||
|
||||
self.stdout.write(style('Starting training...'))
|
||||
training_class = chatterbot.trainer.__class__.__name__
|
||||
self.stdout.write(style('ChatterBot trained using "%s"' % training_class))
|
@ -1,39 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Response',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('occurrence', models.PositiveIntegerField(default=0)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Statement',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', models.CharField(max_length=255, unique=True)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='response',
|
||||
name='response',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='django_chatterbot.Statement'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='response',
|
||||
name='statement',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response_to', to='django_chatterbot.Statement'),
|
||||
),
|
||||
]
|
@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-10-30 12:13
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='statement',
|
||||
name='extra_data',
|
||||
field=models.CharField(default='{}', max_length=500),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9 on 2016-12-12 00:06
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0002_statement_extra_data'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='response',
|
||||
name='occurrence',
|
||||
field=models.PositiveIntegerField(default=1),
|
||||
),
|
||||
]
|
@ -1,26 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.3 on 2016-12-04 23:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0003_change_occurrence_default'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='response',
|
||||
name='statement',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response', to='django_chatterbot.Statement'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='response',
|
||||
name='response',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='django_chatterbot.Statement'),
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-12-29 19:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0004_rename_in_response_to'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='statement',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now,
|
||||
help_text='The date and time that this statement was created at.'
|
||||
),
|
||||
),
|
||||
]
|
@ -1,33 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9 on 2017-01-17 07:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0005_statement_created_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Conversation',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='statement',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time that this statement was created at.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='conversation',
|
||||
name='statements',
|
||||
field=models.ManyToManyField(help_text='The statements in this conversation.', related_name='conversation', to='django_chatterbot.Statement'),
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-07-18 00:16
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0006_create_conversation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='response',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(
|
||||
default=django.utils.timezone.now,
|
||||
help_text='The date and time that this response was created at.'
|
||||
),
|
||||
),
|
||||
]
|
@ -1,32 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-07-18 11:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0007_response_created_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='conversation',
|
||||
name='statements',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='response',
|
||||
name='occurrence',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='statement',
|
||||
name='created_at',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='conversation',
|
||||
name='responses',
|
||||
field=models.ManyToManyField(help_text='The responses in this conversation.', related_name='conversations', to='django_chatterbot.Response'),
|
||||
),
|
||||
]
|
@ -1,35 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11a1 on 2017-07-07 00:12
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0008_update_conversations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Tag',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.SlugField()),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='statement',
|
||||
name='text',
|
||||
field=models.CharField(max_length=255, unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='statements',
|
||||
field=models.ManyToManyField(related_name='tags', to='django_chatterbot.Statement'),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-08-16 00:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0009_tags'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='statement',
|
||||
name='text',
|
||||
field=models.CharField(max_length=400, unique=True),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-08-20 13:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_chatterbot', '0010_statement_text'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='statement',
|
||||
name='extra_data',
|
||||
field=models.CharField(blank=True, max_length=500),
|
||||
),
|
||||
]
|
@ -1,34 +0,0 @@
|
||||
from .abstract_models import (
|
||||
AbstractBaseConversation, AbstractBaseResponse,
|
||||
AbstractBaseStatement, AbstractBaseTag
|
||||
)
|
||||
|
||||
|
||||
class Statement(AbstractBaseStatement):
|
||||
"""
|
||||
A statement represents a single spoken entity, sentence or
|
||||
phrase that someone can say.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Response(AbstractBaseResponse):
|
||||
"""
|
||||
A connection between a statement and anther statement
|
||||
that response to it.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Conversation(AbstractBaseConversation):
|
||||
"""
|
||||
A sequence of statements representing a conversation.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Tag(AbstractBaseTag):
|
||||
"""
|
||||
A label that categorizes a statement.
|
||||
"""
|
||||
pass
|
@ -1,19 +0,0 @@
|
||||
"""
|
||||
Default ChatterBot settings for Django.
|
||||
"""
|
||||
from django.conf import settings
|
||||
from ... import constants
|
||||
|
||||
|
||||
CHATTERBOT_SETTINGS = getattr(settings, 'CHATTERBOT', {})
|
||||
|
||||
CHATTERBOT_DEFAULTS = {
|
||||
'name': 'ChatterBot',
|
||||
'storage_adapter': 'chatter.source.storage.DjangoStorageAdapter',
|
||||
'input_adapter': 'chatter.source.input.VariableInputTypeAdapter',
|
||||
'output_adapter': 'chatter.source.output.OutputAdapter',
|
||||
'django_app_name': constants.DEFAULT_DJANGO_APP_NAME
|
||||
}
|
||||
|
||||
CHATTERBOT = CHATTERBOT_DEFAULTS.copy()
|
||||
CHATTERBOT.update(CHATTERBOT_SETTINGS)
|
@ -1,11 +0,0 @@
|
||||
from django.conf.urls import url
|
||||
from .views import ChatterBotView
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^$',
|
||||
ChatterBotView.as_view(),
|
||||
name='chatterbot',
|
||||
),
|
||||
]
|
@ -1,118 +0,0 @@
|
||||
import json
|
||||
from django.views.generic import View
|
||||
from django.http import JsonResponse
|
||||
from ... import ChatBot
|
||||
from . import settings
|
||||
|
||||
|
||||
class ChatterBotViewMixin(object):
|
||||
"""
|
||||
Subclass this mixin for access to the 'chatterbot' attribute.
|
||||
"""
|
||||
|
||||
chatterbot = ChatBot(**settings.CHATTERBOT)
|
||||
|
||||
def validate(self, data):
|
||||
"""
|
||||
Validate the data recieved from the client.
|
||||
|
||||
* The data should contain a text attribute.
|
||||
"""
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
if 'text' not in data:
|
||||
raise ValidationError('The attribute "text" is required.')
|
||||
|
||||
def get_conversation(self, request):
|
||||
"""
|
||||
Return the conversation for the session if one exists.
|
||||
Create a new conversation if one does not exist.
|
||||
"""
|
||||
from .models import Conversation, Response
|
||||
|
||||
class Obj(object):
|
||||
def __init__(self):
|
||||
self.id = None
|
||||
self.statements = []
|
||||
|
||||
conversation = Obj()
|
||||
|
||||
conversation.id = request.session.get('conversation_id', 0)
|
||||
existing_conversation = False
|
||||
try:
|
||||
Conversation.objects.get(id=conversation.id)
|
||||
existing_conversation = True
|
||||
|
||||
except Conversation.DoesNotExist:
|
||||
conversation_id = self.chatterbot.storage.create_conversation()
|
||||
request.session['conversation_id'] = conversation_id
|
||||
conversation.id = conversation_id
|
||||
|
||||
if existing_conversation:
|
||||
responses = Response.objects.filter(
|
||||
conversations__id=conversation.id
|
||||
)
|
||||
|
||||
for response in responses:
|
||||
conversation.statements.append(response.statement.serialize())
|
||||
conversation.statements.append(response.response.serialize())
|
||||
|
||||
return conversation
|
||||
|
||||
|
||||
class ChatterBotView(ChatterBotViewMixin, View):
|
||||
"""
|
||||
Provide an API endpoint to interact with ChatterBot.
|
||||
"""
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Return a response to the statement in the posted data.
|
||||
"""
|
||||
input_data = json.loads(request.read().decode('utf-8'))
|
||||
|
||||
self.validate(input_data)
|
||||
|
||||
conversation = self.get_conversation(request)
|
||||
|
||||
response = self.chatterbot.get_response(input_data, conversation.id)
|
||||
response_data = response.serialize()
|
||||
|
||||
return JsonResponse(response_data, status=200)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Return data corresponding to the current conversation.
|
||||
"""
|
||||
conversation = self.get_conversation(request)
|
||||
|
||||
data = {
|
||||
'detail': 'You should make a POST request to this endpoint.',
|
||||
'name': self.chatterbot.name,
|
||||
'conversation': conversation.statements
|
||||
}
|
||||
|
||||
# Return a method not allowed response
|
||||
return JsonResponse(data, status=405)
|
||||
|
||||
def patch(self, request, *args, **kwargs):
|
||||
"""
|
||||
The patch method is not allowed for this endpoint.
|
||||
"""
|
||||
data = {
|
||||
'detail': 'You should make a POST request to this endpoint.'
|
||||
}
|
||||
|
||||
# Return a method not allowed response
|
||||
return JsonResponse(data, status=405)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
"""
|
||||
The delete method is not allowed for this endpoint.
|
||||
"""
|
||||
data = {
|
||||
'detail': 'You should make a POST request to this endpoint.'
|
||||
}
|
||||
|
||||
# Return a method not allowed response
|
||||
return JsonResponse(data, status=405)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue